@@ -7,9 +7,11 @@ module MOM_spatial_means
7
7
use MOM_coms, only : EFP_to_real, real_to_EFP, EFP_sum_across_PEs
8
8
use MOM_coms, only : reproducing_sum, reproducing_sum_EFP, EFP_to_real
9
9
use MOM_coms, only : query_EFP_overflow_error, reset_EFP_overflow_error
10
+ use MOM_coms, only : max_across_PEs, min_across_PEs
10
11
use MOM_error_handler, only : MOM_error, NOTE, WARNING, FATAL, is_root_pe
11
12
use MOM_file_parser, only : get_param, log_version, param_file_type
12
13
use MOM_grid, only : ocean_grid_type
14
+ use MOM_hor_index, only : hor_index_type
13
15
use MOM_verticalGrid, only : verticalGrid_type
14
16
15
17
implicit none ; private
@@ -21,6 +23,7 @@ module MOM_spatial_means
21
23
public :: global_area_integral
22
24
public :: global_volume_mean, global_mass_integral, global_mass_int_EFP
23
25
public :: adjust_area_mean_to_zero
26
+ public :: array_global_min_max
24
27
25
28
! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
26
29
! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
@@ -701,4 +704,192 @@ subroutine adjust_area_mean_to_zero(array, G, scaling, unit_scale, unscale)
701
704
702
705
end subroutine adjust_area_mean_to_zero
703
706
707
+
708
+ ! > Find the global maximum and minimum of a tracer array and return the locations of the extrema.
709
+ ! ! When there multiple cells with the same extreme values, the reported locations are from the
710
+ ! ! uppermost layer where they occur, and then from the logically northernmost and then eastermost
711
+ ! ! such location on the unrotated version of the grid within that layer. Only ocean points (as
712
+ ! ! indicated by a positive value of G%mask2dT) are evaluated, and if there are no ocean points
713
+ ! ! anywhere in the domain, the reported extrema and their locations are all returned as 0.
714
+ subroutine array_global_min_max (tr_array , G , nk , g_min , g_max , &
715
+ xgmin , ygmin , zgmin , xgmax , ygmax , zgmax , unscale )
716
+ integer , intent (in ) :: nk ! < The number of vertical levels
717
+ type (ocean_grid_type), intent (in ) :: G ! < The ocean's grid structure
718
+ real , dimension (SZI_(G),SZJ_(G),nk), intent (in ) :: tr_array ! < The tracer array to search for
719
+ ! ! extrema in arbitrary concentration units [CU ~> conc]
720
+ real , intent (out ) :: g_min ! < The global minimum of tr_array, either in
721
+ ! ! the same units as tr_array [CU ~> conc] or in
722
+ ! ! unscaled units if unscale is present [conc]
723
+ real , intent (out ) :: g_max ! < The global maximum of tr_array, either in
724
+ ! ! the same units as tr_array [CU ~> conc] or in
725
+ ! ! unscaled units if unscale is present [conc]
726
+ real , optional , intent (out ) :: xgmin ! < The x-position of the global minimum in the
727
+ ! ! units of G%geoLonT, often [degrees_E] or [km] or [m]
728
+ real , optional , intent (out ) :: ygmin ! < The y-position of the global minimum in the
729
+ ! ! units of G%geoLatT, often [degrees_N] or [km] or [m]
730
+ real , optional , intent (out ) :: zgmin ! < The z-position of the global minimum [layer]
731
+ real , optional , intent (out ) :: xgmax ! < The x-position of the global maximum in the
732
+ ! ! units of G%geoLonT, often [degrees_E] or [km] or [m]
733
+ real , optional , intent (out ) :: ygmax ! < The y-position of the global maximum in the
734
+ ! ! units of G%geoLatT, often [degrees_N] or [km] or [m]
735
+ real , optional , intent (out ) :: zgmax ! < The z-position of the global maximum [layer]
736
+ real , optional , intent (in ) :: unscale ! < A factor to use to undo any scaling of
737
+ ! ! the input tracer array [conc CU-1 ~> 1]
738
+
739
+ ! Local variables
740
+ real :: tmax, tmin ! Maximum and minimum tracer values, in the same units as tr_array [CU ~> conc]
741
+ integer :: ijk_min_max(2 ) ! Integers encoding the global grid positions of the global minimum and maximum values
742
+ real :: xyz_min_max(6 ) ! A single array with the x-, y- and z-positions of the minimum and
743
+ ! maximum values in units that vary between the array elements [various]
744
+ logical :: valid_PE ! True if there are any valid points on the local PE.
745
+ logical :: find_location ! If true, report the locations of the extrema
746
+ integer :: ijk_loc_max ! An integer encoding the global grid position of the maximum tracer value on this PE
747
+ integer :: ijk_loc_min ! An integer encoding the global grid position of the minimum tracer value on this PE
748
+ integer :: ijk_loc_here ! An integer encoding the global grid position of the current grid point
749
+ integer :: itmax, jtmax, ktmax, itmin, jtmin, ktmin
750
+ integer :: i, j, k, isc, iec, jsc, jec
751
+
752
+ isc = G% isc ; iec = G% iec ; jsc = G% jsc ; jec = G% jec
753
+
754
+ find_location = (present (xgmin) .or. present (ygmin) .or. present (zgmin) .or. &
755
+ present (xgmax) .or. present (ygmax) .or. present (zgmax))
756
+
757
+ ! The initial values set here are never used if there are any valid points.
758
+ tmax = - huge (tmax) ; tmin = huge (tmin)
759
+
760
+ if (find_location) then
761
+ ! Find the maximum and minimum tracer values on this PE and their locations.
762
+ valid_PE = .false.
763
+ itmax = 0 ; jtmax = 0 ; ktmax = 0 ; ijk_loc_max = 0
764
+ itmin = 0 ; jtmin = 0 ; ktmin = 0 ; ijk_loc_min = 0
765
+ do k= 1 ,nk ; do j= jsc,jec ; do i= isc,iec ; if (G% mask2dT(i,j) > 0.0 ) then
766
+ valid_PE = .true.
767
+ if (tr_array(i,j,k) > tmax) then
768
+ tmax = tr_array(i,j,k)
769
+ itmax = i ; jtmax = j ; ktmax = k
770
+ ijk_loc_max = ijk_loc(i, j, k, nk, G% HI)
771
+ elseif ((tr_array(i,j,k) == tmax) .and. (k <= ktmax)) then
772
+ ijk_loc_here = ijk_loc(i, j, k, nk, G% HI)
773
+ if (ijk_loc_here > ijk_loc_max) then
774
+ itmax = i ; jtmax = j ; ktmax = k
775
+ ijk_loc_max = ijk_loc_here
776
+ endif
777
+ endif
778
+ if (tr_array(i,j,k) < tmin) then
779
+ tmin = tr_array(i,j,k)
780
+ itmin = i ; jtmin = j ; ktmin = k
781
+ ijk_loc_min = ijk_loc(i, j, k, nk, G% HI)
782
+ elseif ((tr_array(i,j,k) == tmin) .and. (k <= ktmin)) then
783
+ ijk_loc_here = ijk_loc(i, j, k, nk, G% HI)
784
+ if (ijk_loc_here > ijk_loc_min) then
785
+ itmin = i ; jtmin = j ; ktmin = k
786
+ ijk_loc_min = ijk_loc_here
787
+ endif
788
+ endif
789
+ endif ; enddo ; enddo ; enddo
790
+ else
791
+ ! Only the maximum and minimum values are needed, and not their positions.
792
+ do k= 1 ,nk ; do j= jsc,jec ; do i= isc,iec ; if (G% mask2dT(i,j) > 0.0 ) then
793
+ if (tr_array(i,j,k) > tmax) tmax = tr_array(i,j,k)
794
+ if (tr_array(i,j,k) < tmin) tmin = tr_array(i,j,k)
795
+ endif ; enddo ; enddo ; enddo
796
+ endif
797
+
798
+ ! Find the global maximum and minimum tracer values.
799
+ g_max = tmax ; g_min = tmin
800
+ call max_across_PEs(g_max)
801
+ call min_across_PEs(g_min)
802
+
803
+ if (find_location) then
804
+ if (g_max < g_min) then
805
+ ! This only occurs if there are no unmasked points anywhere in the domain.
806
+ xyz_min_max(:) = 0.0
807
+ else
808
+ ! Find the global indices of the maximum and minimum locations. This can
809
+ ! occur on multiple PEs.
810
+ ijk_min_max(1 :2 ) = 0
811
+ if (valid_PE) then
812
+ if (g_min == tmin) ijk_min_max(1 ) = ijk_loc_min
813
+ if (g_max == tmax) ijk_min_max(2 ) = ijk_loc_max
814
+ endif
815
+ ! If MOM6 supported taking maxima on arrays of integers, these could be combined as:
816
+ ! call max_across_PEs(ijk_min_max, 2)
817
+ call max_across_PEs(ijk_min_max(1 ))
818
+ call max_across_PEs(ijk_min_max(2 ))
819
+
820
+ ! Set the positions of the extrema if they occur on this PE. This will only
821
+ ! occur on a single PE.
822
+ xyz_min_max(1 :6 ) = - huge (xyz_min_max) ! These huge negative values are never selected by max_across_PEs.
823
+ if (valid_PE) then
824
+ if (ijk_min_max(1 ) == ijk_loc_min) then
825
+ xyz_min_max(1 ) = G% geoLonT(itmin,jtmin)
826
+ xyz_min_max(2 ) = G% geoLatT(itmin,jtmin)
827
+ xyz_min_max(3 ) = real (ktmin)
828
+ endif
829
+ if (ijk_min_max(2 ) == ijk_loc_max) then
830
+ xyz_min_max(4 ) = G% geoLonT(itmax,jtmax)
831
+ xyz_min_max(5 ) = G% geoLatT(itmax,jtmax)
832
+ xyz_min_max(6 ) = real (ktmax)
833
+ endif
834
+ endif
835
+
836
+ call max_across_PEs(xyz_min_max, 6 )
837
+ endif
838
+
839
+ if (present (xgmin)) xgmin = xyz_min_max(1 )
840
+ if (present (ygmin)) ygmin = xyz_min_max(2 )
841
+ if (present (zgmin)) zgmin = xyz_min_max(3 )
842
+ if (present (xgmax)) xgmax = xyz_min_max(4 )
843
+ if (present (ygmax)) ygmax = xyz_min_max(5 )
844
+ if (present (zgmax)) zgmax = xyz_min_max(6 )
845
+ endif
846
+
847
+ if (g_max < g_min) then
848
+ ! There are no unmasked points anywhere in the domain.
849
+ g_max = 0.0 ; g_min = 0.0
850
+ endif
851
+
852
+ if (present (unscale)) then
853
+ ! Rescale g_min and g_max, perhaps changing their units from [CU ~> conc] to [conc]
854
+ g_max = unscale * g_max
855
+ g_min = unscale * g_min
856
+ endif
857
+
858
+ end subroutine array_global_min_max
859
+
860
+ ! Return a positive integer encoding the rotationally invariant global position of a tracer cell
861
+ function ijk_loc (i , j , k , nk , HI )
862
+ integer , intent (in ) :: i ! < Local i-index
863
+ integer , intent (in ) :: j ! < Local j-index
864
+ integer , intent (in ) :: k ! < Local k-index
865
+ integer , intent (in ) :: nk ! < Range of k-index, used to pick out a low-k position.
866
+ type (hor_index_type), intent (in ) :: HI ! < Horizontal index ranges
867
+ integer :: ijk_loc ! An integer encoding the cell position in the global grid.
868
+
869
+ ! Local variables
870
+ integer :: ig, jg ! Global index values with a global computational domain start value of 1.
871
+ integer :: ij_loc ! The encoding of the horizontal position
872
+ integer :: qturns ! The number of counter-clockwise quarter turns of the grid that have to be undone
873
+
874
+ ! These global i-grid positions run from 1 to HI%niglobal, and analogously for jg.
875
+ ig = i + HI% idg_offset + (1 - HI% isg)
876
+ jg = j + HI% jdg_offset + (1 - HI% jsg)
877
+
878
+ ! Compensate for the rotation of the model grid to give a rotationally invariant encoding.
879
+ qturns = modulo (HI% turns, 4 )
880
+ if (qturns == 0 ) then
881
+ ij_loc = ig + HI% niglobal * jg
882
+ elseif (qturns == 1 ) then
883
+ ij_loc = jg + HI% njglobal * ((HI% niglobal+1 )- ig)
884
+ elseif (qturns == 2 ) then
885
+ ij_loc = ((HI% niglobal+1 )- ig) + HI% niglobal * ((HI% njglobal+1 )- jg)
886
+ elseif (qturns == 3 ) then
887
+ ij_loc = ((HI% njglobal+1 )- jg) + HI% njglobal * ig
888
+ endif
889
+
890
+ ijk_loc = ij_loc + (HI% niglobal* HI% njglobal) * (nk- k)
891
+
892
+ end function ijk_loc
893
+
894
+
704
895
end module MOM_spatial_means
0 commit comments