From 9fc3741d98193c8a613998a1b7d1f0c1cd931676 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Fri, 6 Dec 2024 07:18:33 -0500 Subject: [PATCH 1/4] +Add optional unscale argument to reproducing_sum Added the new optional unscale argument to reproducing_sum() and reproducing_sum_EFP(). When this is used, the resulting real sum is restored to the same scaling as the input arrays, but when there is an EFP_type returned it is the same as it would have been had the input array been rescaled before it was passed in. All answers are bitwise identical, but thre is a new optional argument to a two publicly visible interfaces. --- src/framework/MOM_coms.F90 | 169 ++++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 59 deletions(-) diff --git a/src/framework/MOM_coms.F90 b/src/framework/MOM_coms.F90 index e4f5235da8..8471dc0b29 100644 --- a/src/framework/MOM_coms.F90 +++ b/src/framework/MOM_coms.F90 @@ -51,7 +51,8 @@ module MOM_coms logical :: NaN_error = .false. !< This becomes true if a NaN is encountered. logical :: debug = .false. !< Making this true enables debugging output. -!> Find an accurate and order-invariant sum of a distributed 2d or 3d field +!> Find an accurate and order-invariant sum of a distributed 2d or 3d field, in some cases after +!! undoing the scaling of the input array and restoring that scaling in the returned value interface reproducing_sum module procedure reproducing_sum_2d, reproducing_sum_3d end interface reproducing_sum @@ -91,8 +92,9 @@ module MOM_coms !! the result returned as an extended fixed point type that can be converted back to a real number !! using EFP_to_real. This technique is described in Hallberg & Adcroft, 2014, Parallel Computing, !! doi:10.1016/j.parco.2014.04.007. -function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, only_on_PE) result(EFP_sum) - real, dimension(:,:), intent(in) :: array !< The array to be summed in arbitrary units [a] +function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, only_on_PE, unscale) result(EFP_sum) + real, dimension(:,:), intent(in) :: array !< The array to be summed in arbitrary units [a], or in + !! arbitrary scaled units [A ~> a] if unscale is present integer, optional, intent(in) :: isr !< The starting i-index of the sum, noting !! that the array indices starts at 1 integer, optional, intent(in) :: ier !< The ending i-index of the sum, noting @@ -102,15 +104,17 @@ function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, integer, optional, intent(in) :: jer !< The ending j-index of the sum, noting !! that the array indices starts at 1 logical, optional, intent(in) :: overflow_check !< If present and false, disable - !! checking for overflows in incremental results. - !! This can speed up calculations if the number - !! of values being summed is small enough - integer, optional, intent(out) :: err !< If present, return an error code instead of - !! triggering any fatal errors directly from - !! this routine. + !! checking for overflows in incremental results. + !! This can speed up calculations if the number + !! of values being summed is small enough + integer, optional, intent(out) :: err !< If present, return an error code instead of + !! triggering any fatal errors directly from + !! this routine. logical, optional, intent(in) :: only_on_PE !< If present and true, do not do the sum - !! across processors, only reporting the local sum - type(EFP_type) :: EFP_sum !< The result in extended fixed point format + !! across processors, only reporting the local sum + real, optional, intent(in) :: unscale !< A factor that is used to undo scaling of array before it is + !! summed, often to compensate for the scaling in [a A-1 ~> 1] + type(EFP_type) :: EFP_sum !< The result in extended fixed point format ! This subroutine uses a conversion to an integer representation ! of real numbers to give order-invariant sums that will reproduce @@ -120,7 +124,8 @@ function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, integer(kind=int64) :: ival, prec_error real :: rs ! The remaining value to add, in arbitrary units [a] real :: max_mag_term ! A running maximum magnitude of the values in arbitrary units [a] - logical :: over_check, do_sum_across_PEs + real :: descale ! A local copy of unscale if it is present [a A-1 ~> 1] or 1 + logical :: over_check, do_sum_across_PEs, do_unscale character(len=256) :: mesg integer :: i, j, n, is, ie, js, je, sgn @@ -130,7 +135,7 @@ function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, prec_error = ((2_int64)**62 + ((2_int64)**62 - 1)) / num_PEs() - is = 1 ; ie = size(array,1) ; js = 1 ; je = size(array,2 ) + is = 1 ; ie = size(array,1) ; js = 1 ; je = size(array,2) if (present(isr)) then if (isr < is) call MOM_error(FATAL, "Value of isr too small in reproducing_EFP_sum_2d.") is = isr @@ -150,32 +155,40 @@ function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, over_check = .true. ; if (present(overflow_check)) over_check = overflow_check do_sum_across_PEs = .true. ; if (present(only_on_PE)) do_sum_across_PEs = .not.only_on_PE + do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0) + descale = 1.0 ; if (do_unscale) descale = unscale overflow_error = .false. ; NaN_error = .false. ; max_mag_term = 0.0 ints_sum(:) = 0 if (over_check) then if ((je+1-js)*(ie+1-is) < max_count_prec) then - do j=js,je ; do i=is,ie - call increment_ints_faster(ints_sum, array(i,j), max_mag_term) - enddo ; enddo + ! This is the most common case, so handle the do_unscale case separately for efficiency. + if (do_unscale) then + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sum, unscale*array(i,j), max_mag_term) + enddo ; enddo + else + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sum, array(i,j), max_mag_term) + enddo ; enddo + endif call carry_overflow(ints_sum, prec_error) elseif ((ie+1-is) < max_count_prec) then do j=js,je do i=is,ie - call increment_ints_faster(ints_sum, array(i,j), max_mag_term) + call increment_ints_faster(ints_sum, descale*array(i,j), max_mag_term) enddo call carry_overflow(ints_sum, prec_error) enddo else do j=js,je ; do i=is,ie - call increment_ints(ints_sum, real_to_ints(array(i,j), prec_error), & - prec_error) + call increment_ints(ints_sum, real_to_ints(descale*array(i,j), prec_error), prec_error) enddo ; enddo endif else do j=js,je ; do i=is,ie sgn = 1 ; if (array(i,j)<0.0) sgn = -1 - rs = abs(array(i,j)) + rs = abs(descale*array(i,j)) do n=1,ni ival = int(rs*I_pr(n), kind=int64) rs = rs - ival*pr(n) @@ -213,13 +226,15 @@ function reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, end function reproducing_EFP_sum_2d + !> This subroutine uses a conversion to an integer representation of real numbers to give an !! order-invariant sum of distributed 2-D arrays that reproduces across domain decomposition. !! This technique is described in Hallberg & Adcroft, 2014, Parallel Computing, !! doi:10.1016/j.parco.2014.04.007. function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & - overflow_check, err, only_on_PE) result(sum) - real, dimension(:,:), intent(in) :: array !< The array to be summed in arbitrary units [a] + overflow_check, err, only_on_PE, unscale) result(sum) + real, dimension(:,:), intent(in) :: array !< The array to be summed in arbitrary units [a], or in + !! arbitrary scaled units [A ~> a] if unscale is present integer, optional, intent(in) :: isr !< The starting i-index of the sum, noting !! that the array indices starts at 1 integer, optional, intent(in) :: ier !< The ending i-index of the sum, noting @@ -228,7 +243,7 @@ function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & !! that the array indices starts at 1 integer, optional, intent(in) :: jer !< The ending j-index of the sum, noting !! that the array indices starts at 1 - type(EFP_type), optional, intent(out) :: EFP_sum !< The result in extended fixed point format + type(EFP_type), optional, intent(out) :: EFP_sum !< The result in extended fixed point format logical, optional, intent(in) :: reproducing !< If present and false, do the sum !! using the naive non-reproducing approach logical, optional, intent(in) :: overflow_check !< If present and false, disable @@ -240,16 +255,18 @@ function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & !! this routine. logical, optional, intent(in) :: only_on_PE !< If present and true, do not do the sum !! across processors, only reporting the local sum - real :: sum !< The sum of the values in array in arbitrary units [a] - - ! This subroutine uses a conversion to an integer representation - ! of real numbers to give order-invariant sums that will reproduce - ! across PE count. This idea comes from R. Hallberg and A. Adcroft. + real, optional, intent(in) :: unscale !< A factor that is used to undo scaling of array before it is + !! summed, often to compensate for the scaling in [a A-1 ~> 1] + real :: sum !< The sum of the values in array in the same + !! arbitrary units as array [a] or [A ~> a] + ! Local variables integer(kind=int64), dimension(ni) :: ints_sum integer(kind=int64) :: prec_error - real :: rsum(1) ! The running sum, in arbitrary units [a] - logical :: repro, do_sum_across_PEs + real :: rsum(1) ! The running sum, in arbitrary units [a] + real :: descale ! A local copy of unscale if it is present [a A-1 ~> 1] or 1 + real :: I_unscale ! The reciprocal of unscale [A a-1 ~> 1] + logical :: repro, do_sum_across_PEs, do_unscale character(len=256) :: mesg type(EFP_type) :: EFP_val ! An extended fixed point version of the sum integer :: i, j, is, ie, js, je @@ -260,7 +277,7 @@ function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & prec_error = ((2_int64)**62 + ((2_int64)**62 - 1)) / num_PEs() - is = 1 ; ie = size(array,1) ; js = 1 ; je = size(array,2 ) + is = 1 ; ie = size(array,1) ; js = 1 ; je = size(array,2) if (present(isr)) then if (isr < is) call MOM_error(FATAL, "Value of isr too small in reproducing_sum_2d.") is = isr @@ -280,19 +297,25 @@ function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & repro = .true. ; if (present(reproducing)) repro = reproducing do_sum_across_PEs = .true. ; if (present(only_on_PE)) do_sum_across_PEs = .not.only_on_PE + do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0) + descale = 1.0 ; I_unscale = 1.0 + if (do_unscale) then + descale = unscale + if (abs(unscale) > 0.0) I_unscale = 1.0 / unscale + endif if (repro) then - EFP_val = reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, only_on_PE) - sum = ints_to_real(EFP_val%v) + EFP_val = reproducing_EFP_sum_2d(array, isr, ier, jsr, jer, overflow_check, err, only_on_PE, unscale) + sum = ints_to_real(EFP_val%v) * I_unscale if (present(EFP_sum)) EFP_sum = EFP_val if (debug) ints_sum(:) = EFP_sum%v(:) else rsum(1) = 0.0 do j=js,je ; do i=is,ie - rsum(1) = rsum(1) + array(i,j) + rsum(1) = rsum(1) + descale*array(i,j) enddo ; enddo if (do_sum_across_PEs) call sum_across_PEs(rsum,1) - sum = rsum(1) + sum = rsum(1) * I_unscale if (present(err)) then ; err = 0 ; endif @@ -312,7 +335,7 @@ function reproducing_sum_2d(array, isr, ier, jsr, jer, EFP_sum, reproducing, & endif if (debug) then - write(mesg,'("2d RS: ", ES24.16, 6 Z17.16)') sum, ints_sum(1:ni) + write(mesg,'("2d RS: ", ES24.16, 6 Z17.16)') sum*descale, ints_sum(1:ni) call MOM_mesg(mesg, 3) endif @@ -322,9 +345,10 @@ end function reproducing_sum_2d !! order-invariant sum of distributed 3-D arrays that reproduces across domain decomposition. !! This technique is described in Hallberg & Adcroft, 2014, Parallel Computing, !! doi:10.1016/j.parco.2014.04.007. -function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_sums, err, only_on_PE) & +function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_sums, err, only_on_PE, unscale) & result(sum) - real, dimension(:,:,:), intent(in) :: array !< The array to be summed in arbitrary units [a] + real, dimension(:,:,:), intent(in) :: array !< The array to be summed in arbitrary units [a], or in + !! arbitrary scaled units [A ~> a] if unscale is present integer, optional, intent(in) :: isr !< The starting i-index of the sum, noting !! that the array indices starts at 1 integer, optional, intent(in) :: ier !< The ending i-index of the sum, noting @@ -333,28 +357,31 @@ function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_su !! that the array indices starts at 1 integer, optional, intent(in) :: jer !< The ending j-index of the sum, noting !! that the array indices starts at 1 - real, dimension(:), optional, intent(out) :: sums !< The sums by vertical layer in abitrary units [a] + real, dimension(:), optional, intent(out) :: sums !< The sums by vertical layer in the same + !! abitrary units as array [a] or [A ~> a] type(EFP_type), optional, intent(out) :: EFP_sum !< The result in extended fixed point format type(EFP_type), dimension(:), & optional, intent(out) :: EFP_lay_sums !< The sums by vertical layer in EFP format - integer, optional, intent(out) :: err !< If present, return an error code instead of - !! triggering any fatal errors directly from - !! this routine. + integer, optional, intent(out) :: err !< If present, return an error code instead of + !! triggering any fatal errors directly from + !! this routine. logical, optional, intent(in) :: only_on_PE !< If present and true, do not do the sum - !! across processors, only reporting the local sum - real :: sum !< The sum of the values in array in arbitrary units [a] - - ! This subroutine uses a conversion to an integer representation - ! of real numbers to give order-invariant sums that will reproduce - ! across PE count. This idea comes from R. Hallberg and A. Adcroft. + !! across processors, only reporting the local sum + real, optional, intent(in) :: unscale !< A factor that is used to undo scaling of array before it is + !! summed, often to compensate for the scaling in [a A-1 ~> 1] + real :: sum !< The sum of the values in array in the same + !! arbitrary units as array [a] or [A ~> a] + ! Local variables real :: val ! The real number that is extracted in arbitrary units [a] real :: max_mag_term ! A running maximum magnitude of the val's in arbitrary units [a] + real :: descale ! A local copy of unscale if it is present [a A-1 ~> 1] or 1 + real :: I_unscale ! The Adcroft reciprocal of unscale [A a-1 ~> 1] integer(kind=int64), dimension(ni) :: ints_sum integer(kind=int64), dimension(ni,size(array,3)) :: ints_sums integer(kind=int64) :: prec_error character(len=256) :: mesg - logical :: do_sum_across_PEs + logical :: do_sum_across_PEs, do_unscale integer :: i, j, k, is, ie, js, je, ke, isz, jsz, n if (num_PEs() > max_count_prec) call MOM_error(FATAL, & @@ -384,6 +411,7 @@ function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_su jsz = je+1-js; isz = ie+1-is do_sum_across_PEs = .true. ; if (present(only_on_PE)) do_sum_across_PEs = .not.only_on_PE + do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0) if (present(sums) .or. present(EFP_lay_sums)) then if (present(sums)) then ; if (size(sums) < ke) then @@ -396,22 +424,28 @@ function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_su overflow_error = .false. ; NaN_error = .false. ; max_mag_term = 0.0 if (jsz*isz < max_count_prec) then do k=1,ke - do j=js,je ; do i=is,ie - call increment_ints_faster(ints_sums(:,k), array(i,j,k), max_mag_term) - enddo ; enddo + if (do_unscale) then + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sums(:,k), unscale*array(i,j,k), max_mag_term) + enddo ; enddo + else + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sums(:,k), array(i,j,k), max_mag_term) + enddo ; enddo + endif call carry_overflow(ints_sums(:,k), prec_error) enddo elseif (isz < max_count_prec) then do k=1,ke ; do j=js,je do i=is,ie - call increment_ints_faster(ints_sums(:,k), array(i,j,k), max_mag_term) + call increment_ints_faster(ints_sums(:,k), descale*array(i,j,k), max_mag_term) enddo call carry_overflow(ints_sums(:,k), prec_error) enddo ; enddo else do k=1,ke ; do j=js,je ; do i=is,ie call increment_ints(ints_sums(:,k), & - real_to_ints(array(i,j,k), prec_error), prec_error) + real_to_ints(descale*array(i,j,k), prec_error), prec_error) enddo ; enddo ; enddo endif if (present(err)) then @@ -458,21 +492,27 @@ function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_su overflow_error = .false. ; NaN_error = .false. ; max_mag_term = 0.0 if (jsz*isz < max_count_prec) then do k=1,ke - do j=js,je ; do i=is,ie - call increment_ints_faster(ints_sum, array(i,j,k), max_mag_term) - enddo ; enddo + if (do_unscale) then + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sum, unscale*array(i,j,k), max_mag_term) + enddo ; enddo + else + do j=js,je ; do i=is,ie + call increment_ints_faster(ints_sum, array(i,j,k), max_mag_term) + enddo ; enddo + endif call carry_overflow(ints_sum, prec_error) enddo elseif (isz < max_count_prec) then do k=1,ke ; do j=js,je do i=is,ie - call increment_ints_faster(ints_sum, array(i,j,k), max_mag_term) + call increment_ints_faster(ints_sum, descale*array(i,j,k), max_mag_term) enddo call carry_overflow(ints_sum, prec_error) enddo ; enddo else do k=1,ke ; do j=js,je ; do i=is,ie - call increment_ints(ints_sum, real_to_ints(array(i,j,k), prec_error), & + call increment_ints(ints_sum, real_to_ints(descale*array(i,j,k), prec_error), & prec_error) enddo ; enddo ; enddo endif @@ -504,6 +544,15 @@ function reproducing_sum_3d(array, isr, ier, jsr, jer, sums, EFP_sum, EFP_lay_su endif endif + if (do_unscale) then + ! Revise the sum to restore the scaling of input array before it is returned + I_unscale = 0.0 ; if (abs(unscale) > 0.0) I_unscale = 1.0 / unscale + sum = sum * I_unscale + if (present(sums)) then + do k=1,ke ; sums(k) = sums(k) * I_unscale ; enddo + endif + endif + end function reproducing_sum_3d !> Convert a real number into the array of integers constitute its extended-fixed-point representation @@ -516,9 +565,11 @@ function real_to_ints(r, prec_error, overflow) result(ints) logical, optional, intent(inout) :: overflow !< Returns true if the conversion is being !! done on a value that is too large to be represented integer(kind=int64), dimension(ni) :: ints + ! This subroutine converts a real number to an equivalent representation ! using several long integers. + ! Local variables real :: rs ! The remaining value to add, in arbitrary units [a] character(len=80) :: mesg integer(kind=int64) :: ival, prec_err From 5d0bf79e2b6377483e0cdea8a13bc7bb89832de8 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Wed, 11 Dec 2024 18:03:54 -0500 Subject: [PATCH 2/4] +Add RZL2_to_kg element to unit_scale_type Added the new element RZL2_to_kg to the unit_scale_type for convenience when rescaling masses and other globally integrated variables. All answers are bitwise identical, but there is a new element in a transparent type. --- src/framework/MOM_unit_scaling.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/framework/MOM_unit_scaling.F90 b/src/framework/MOM_unit_scaling.F90 index 868352102e..8b4f9266a8 100644 --- a/src/framework/MOM_unit_scaling.F90 +++ b/src/framework/MOM_unit_scaling.F90 @@ -47,6 +47,7 @@ module MOM_unit_scaling real :: QRZ_T_to_W_m2 !< Convert heat fluxes from Q R Z T-1 to W m-2 [W T Q-1 R-1 Z-1 m-2 ~> 1] ! Not used enough: real :: kg_m2_to_RZ !< Convert mass loads from kg m-2 to R Z [R Z m2 kg-1 ~> 1] real :: RZ_to_kg_m2 !< Convert mass loads from R Z to kg m-2 [kg R-1 Z-1 m-2 ~> 1] + real :: RZL2_to_kg !< Convert masses from R Z L2 to kg [kg R-1 Z-1 L-2 ~> 1] real :: kg_m2s_to_RZ_T !< Convert mass fluxes from kg m-2 s-1 to R Z T-1 [R Z m2 s T-1 kg-1 ~> 1] real :: RZ_T_to_kg_m2s !< Convert mass fluxes from R Z T-1 to kg m-2 s-1 [T kg R-1 Z-1 m-2 s-1 ~> 1] real :: RZ3_T3_to_W_m2 !< Convert turbulent kinetic energy fluxes from R Z3 T-3 to W m-2 [W T3 R-1 Z-3 m-2 ~> 1] @@ -224,6 +225,8 @@ subroutine set_unit_scaling_combos(US) ! Wind stresses: US%RLZ_T2_to_Pa = US%R_to_kg_m3 * US%L_T_to_m_s**2 * US%Z_to_L US%Pa_to_RLZ_T2 = US%kg_m3_to_R * US%m_s_to_L_T**2 * US%L_to_Z + ! Masses: + US%RZL2_to_kg = US%R_to_kg_m3 * US%Z_to_m * US%L_to_m**2 end subroutine set_unit_scaling_combos From c618090b2f4434ee4c74a678dbfd04702d31be15 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Wed, 11 Dec 2024 18:04:37 -0500 Subject: [PATCH 3/4] Work in rescaled units in write_energy Revised write_energy() and accumulate_net_input() to work more extensively in dimensionally rescaled variables by using the new unscale arguments to the reproducing_sum functions. As a result of these changes, 15 rescaling factors were eliminated or moved toward the end of write_energy(). All answers are bitwise identical. --- src/diagnostics/MOM_sum_output.F90 | 144 ++++++++++++++--------------- 1 file changed, 68 insertions(+), 76 deletions(-) diff --git a/src/diagnostics/MOM_sum_output.F90 b/src/diagnostics/MOM_sum_output.F90 index f5ff19630b..85029c5152 100644 --- a/src/diagnostics/MOM_sum_output.F90 +++ b/src/diagnostics/MOM_sum_output.F90 @@ -340,8 +340,8 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci real :: areaTm(SZI_(G),SZJ_(G)) ! A masked version of areaT [L2 ~> m2]. real :: KE(SZK_(GV)) ! The total kinetic energy of a layer [J]. real :: PE(SZK_(GV)+1)! The available potential energy of an interface [J]. - real :: KE_tot ! The total kinetic energy [J]. - real :: PE_tot ! The total available potential energy [J]. + real :: KE_tot ! The total kinetic energy [R Z L4 T-2 ~> J]. + real :: PE_tot ! The total available potential energy [R Z L4 T-2 ~> J]. real :: Z_0APE(SZK_(GV)+1) ! The uniform depth which overlies the same ! volume as is below an interface [Z ~> m]. real :: H_0APE(SZK_(GV)+1) ! A version of Z_0APE, converted to m, usually positive [m]. @@ -351,9 +351,10 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci ! the total mass of the ocean [m2 s-2]. real :: vol_lay(SZK_(GV)) ! The volume of fluid in a layer [Z L2 ~> m3]. real :: volbelow ! The volume of all layers beneath an interface [Z L2 ~> m3]. - real :: mass_lay(SZK_(GV)) ! The mass of fluid in a layer [kg]. + real :: mass_lay(SZK_(GV)) ! The mass of fluid in a layer [R Z L2 ~> kg]. + real :: mass_tot_RZL2 ! The total mass of the ocean [R Z L2 ~> kg]. real :: mass_tot ! The total mass of the ocean [kg]. - real :: vol_tot ! The total ocean volume [m3]. + real :: vol_tot ! The total ocean volume [Z L2 ~> m3]. real :: mass_chg ! The change in total ocean mass of fresh water since ! the last call to this subroutine [kg]. real :: mass_anom ! The change in fresh water that cannot be accounted for @@ -399,14 +400,11 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & tmp1 ! A temporary array used in reproducing sums [various] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: & - PE_pt ! The potential energy at each point [J]. + PE_pt ! The potential energy at each point [R Z L4 T-2 ~> J]. real, dimension(SZI_(G),SZJ_(G)) :: & - Temp_int, Salt_int ! Layer and cell integrated heat and salt [J] and [g Salt]. - real :: HL2_to_kg ! A conversion factor from a thickness-volume to mass [kg H-1 L-2 ~> kg m-3 or 1] - real :: KE_scale_factor ! The combination of unit rescaling factors in the kinetic energy - ! calculation [kg T2 H-1 L-2 s-2 ~> kg m-3 or 1] - real :: PE_scale_factor ! The combination of unit rescaling factors in the potential energy - ! calculation [kg T2 R-1 Z-1 L-2 s-2 ~> 1] + Temp_int, Salt_int ! Layer and cell integrated heat and salt [Q R Z L2 ~> J] and [g Salt]. + real :: RZL4_to_J ! The combination of unit rescaling factors to convert the spatially integrated + ! kinetic or potential energies into mks units [T2 kg m2 R-1 Z-1 L-4 s-2 ~> 1] integer :: num_nc_fields ! The number of fields that will actually go into ! the NetCDF file. integer :: i, j, k, is, ie, js, je, nz, m, Isq, Ieq, Jsq, Jeq, isr, ier, jsr, jer @@ -532,7 +530,7 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB isr = is - (G%isd-1) ; ier = ie - (G%isd-1) ; jsr = js - (G%jsd-1) ; jer = je - (G%jsd-1) - HL2_to_kg = GV%H_to_kg_m2*US%L_to_m**2 + RZL4_to_J = US%RZL2_to_kg*US%L_T_to_m_s**2 ! Used to unscale energies if (.not.associated(CS)) call MOM_error(FATAL, & "write_energy: Module must be initialized before it is used.") @@ -544,28 +542,21 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci areaTm(i,j) = G%mask2dT(i,j)*G%areaT(i,j) enddo ; enddo - if (GV%Boussinesq) then - tmp1(:,:,:) = 0.0 - do k=1,nz ; do j=js,je ; do i=is,ie - tmp1(i,j,k) = h(i,j,k) * (HL2_to_kg*areaTm(i,j)) - enddo ; enddo ; enddo + tmp1(:,:,:) = 0.0 + do k=1,nz ; do j=js,je ; do i=is,ie + tmp1(i,j,k) = h(i,j,k) * (GV%H_to_RZ*areaTm(i,j)) + enddo ; enddo ; enddo + mass_tot_RZL2 = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=mass_lay, EFP_sum=mass_EFP, unscale=US%RZL2_to_kg) - mass_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=mass_lay, EFP_sum=mass_EFP) - do k=1,nz ; vol_lay(k) = (US%m_to_L**2*GV%H_to_Z/GV%H_to_kg_m2)*mass_lay(k) ; enddo + if (GV%Boussinesq) then + do k=1,nz ; vol_lay(k) = (1.0 / GV%Rho0) * mass_lay(k) ; enddo else - tmp1(:,:,:) = 0.0 - do k=1,nz ; do j=js,je ; do i=is,ie - tmp1(i,j,k) = HL2_to_kg * h(i,j,k) * areaTm(i,j) - enddo ; enddo ; enddo - mass_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=mass_lay, EFP_sum=mass_EFP) - if (CS%do_APE_calc) then call find_eta(h, tv, G, GV, US, eta, dZref=G%Z_ref) do k=1,nz ; do j=js,je ; do i=is,ie - tmp1(i,j,k) = US%Z_to_m*US%L_to_m**2*(eta(i,j,K)-eta(i,j,K+1)) * areaTm(i,j) + tmp1(i,j,k) = (eta(i,j,K)-eta(i,j,K+1)) * areaTm(i,j) enddo ; enddo ; enddo - vol_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=vol_lay) - do k=1,nz ; vol_lay(k) = US%m_to_Z*US%m_to_L**2 * vol_lay(k) ; enddo + vol_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=vol_lay, unscale=US%Z_to_m*US%L_to_m**2) endif endif ! Boussinesq @@ -692,7 +683,6 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci ! Calculate the Available Potential Energy integrated over each interface. With a nonlinear ! equation of state or with a bulk mixed layer this calculation is only approximate. ! With an ALE model this does not make sense and should be revisited. - PE_scale_factor = US%RZ_to_kg_m2*US%L_to_m**2*US%L_T_to_m_s**2 PE_pt(:,:,:) = 0.0 if (GV%Boussinesq) then do j=js,je ; do i=is,ie @@ -702,7 +692,7 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci hint = Z_0APE(K) + (hbelow - (G%bathyT(i,j) + G%Z_ref)) hbot = Z_0APE(K) - (G%bathyT(i,j) + G%Z_ref) hbot = (hbot + ABS(hbot)) * 0.5 - PE_pt(i,j,K) = (0.5 * PE_scale_factor * areaTm(i,j)) * (GV%Rho0*GV%g_prime(K)) * & + PE_pt(i,j,K) = (0.5 * areaTm(i,j)) * (GV%Rho0*GV%g_prime(K)) * & (hint * hint - hbot * hbot) enddo enddo ; enddo @@ -711,7 +701,7 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci do K=nz,1,-1 hint = Z_0APE(K) + eta(i,j,K) ! eta and H_0 have opposite signs. hbot = max(Z_0APE(K) - (G%bathyT(i,j) + G%Z_ref), 0.0) - PE_pt(i,j,K) = (0.5 * PE_scale_factor * areaTm(i,j) * (GV%Rho0*GV%g_prime(K))) * & + PE_pt(i,j,K) = (0.5 * areaTm(i,j) * (GV%Rho0*GV%g_prime(K))) * & (hint * hint - hbot * hbot) enddo enddo ; enddo @@ -720,47 +710,44 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci do K=nz,2,-1 hint = Z_0APE(K) + eta(i,j,K) ! eta and H_0 have opposite signs. hbot = max(Z_0APE(K) - (G%bathyT(i,j) + G%Z_ref), 0.0) - PE_pt(i,j,K) = (0.25 * PE_scale_factor * areaTm(i,j) * & + PE_pt(i,j,K) = (0.25 * areaTm(i,j) * & ((GV%Rlay(k)+GV%Rlay(k-1))*GV%g_prime(K))) * & (hint * hint - hbot * hbot) enddo hint = Z_0APE(1) + eta(i,j,1) ! eta and H_0 have opposite signs. hbot = max(Z_0APE(1) - (G%bathyT(i,j) + G%Z_ref), 0.0) - PE_pt(i,j,1) = (0.5 * PE_scale_factor * areaTm(i,j) * (GV%Rlay(1)*GV%g_prime(1))) * & + PE_pt(i,j,1) = (0.5 * areaTm(i,j) * (GV%Rlay(1)*GV%g_prime(1))) * & (hint * hint - hbot * hbot) enddo ; enddo endif - PE_tot = reproducing_sum(PE_pt, isr, ier, jsr, jer, sums=PE) + PE_tot = reproducing_sum(PE_pt, isr, ier, jsr, jer, sums=PE, unscale=RZL4_to_J) do k=1,nz+1 ; H_0APE(K) = US%Z_to_m*Z_0APE(K) ; enddo else PE_tot = 0.0 do k=1,nz+1 ; PE(K) = 0.0 ; H_0APE(K) = 0.0 ; enddo endif -! Calculate the Kinetic Energy integrated over each layer. - KE_scale_factor = HL2_to_kg*US%L_T_to_m_s**2 + ! Calculate the Kinetic Energy integrated over each layer. tmp1(:,:,:) = 0.0 do k=1,nz ; do j=js,je ; do i=is,ie - tmp1(i,j,k) = (0.25 * KE_scale_factor * (areaTm(i,j) * h(i,j,k))) * & + tmp1(i,j,k) = (0.25 * GV%H_to_RZ*(areaTm(i,j) * h(i,j,k))) * & (((u(I-1,j,k)**2) + (u(I,j,k)**2)) + ((v(i,J-1,k)**2) + (v(i,J,k)**2))) enddo ; enddo ; enddo - KE_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=KE) + KE_tot = reproducing_sum(tmp1, isr, ier, jsr, jer, sums=KE, unscale=RZL4_to_J) - toten = KE_tot + PE_tot - - Salt = 0.0 ; Heat = 0.0 + ! Use reproducing sums to do global integrals relate to the heat, salinity and water budgets. if (CS%use_temperature) then Temp_int(:,:) = 0.0 ; Salt_int(:,:) = 0.0 do k=1,nz ; do j=js,je ; do i=is,ie - Salt_int(i,j) = Salt_int(i,j) + US%S_to_ppt*tv%S(i,j,k) * & - (h(i,j,k)*(HL2_to_kg * areaTm(i,j))) - Temp_int(i,j) = Temp_int(i,j) + (US%Q_to_J_kg*tv%C_p * tv%T(i,j,k)) * & - (h(i,j,k)*(HL2_to_kg * areaTm(i,j))) + Salt_int(i,j) = Salt_int(i,j) + tv%S(i,j,k) * (h(i,j,k)*(GV%H_to_RZ * areaTm(i,j))) + Temp_int(i,j) = Temp_int(i,j) + (tv%C_p * tv%T(i,j,k)) * (h(i,j,k)*(GV%H_to_RZ * areaTm(i,j))) enddo ; enddo ; enddo - salt_EFP = reproducing_sum_EFP(Salt_int, isr, ier, jsr, jer, only_on_PE=.true.) - heat_EFP = reproducing_sum_EFP(Temp_int, isr, ier, jsr, jer, only_on_PE=.true.) + salt_EFP = reproducing_sum_EFP(Salt_int, isr, ier, jsr, jer, only_on_PE=.true., & + unscale=US%RZL2_to_kg*US%S_to_ppt) + heat_EFP = reproducing_sum_EFP(Temp_int, isr, ier, jsr, jer, only_on_PE=.true., & + unscale=US%RZL2_to_kg*US%Q_to_J_kg) ! Combining the sums avoids multiple blocking all-PE updates. EFP_list(1) = salt_EFP ; EFP_list(2) = heat_EFP ; EFP_list(3) = CS%fresh_water_in_EFP @@ -770,13 +757,11 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci salt_EFP = EFP_list(1) ; heat_EFP = EFP_list(2) ; CS%fresh_water_in_EFP = EFP_list(3) CS%net_salt_in_EFP = EFP_list(4) ; CS%net_heat_in_EFP = EFP_list(5) - Salt = EFP_to_real(salt_EFP) - Heat = EFP_to_real(heat_EFP) else call EFP_sum_across_PEs(CS%fresh_water_in_EFP) endif -! Calculate the maximum CFL numbers. + ! Calculate the maximum CFL numbers. max_CFL(1:2) = 0.0 do k=1,nz ; do j=js,je ; do I=Isq,Ieq CFL_Iarea = G%IareaT(i,j) @@ -803,7 +788,12 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci call max_across_PEs(max_CFL, 2) + ! From this point onward, many of the calculations set or use variables in unscaled (mks) units. + + Salt = 0.0 ; Heat = 0.0 if (CS%use_temperature) then + Salt = EFP_to_real(salt_EFP) + Heat = EFP_to_real(heat_EFP) if (CS%previous_calls == 0) then CS%salt_prev_EFP = salt_EFP ; CS%heat_prev_EFP = heat_EFP endif @@ -826,6 +816,7 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci endif mass_chg = EFP_to_real(mass_chg_EFP) + mass_tot = US%RZL2_to_kg * mass_tot_RZL2 if (CS%use_temperature) then salin = Salt / mass_tot salin_anom = Salt_anom / mass_tot @@ -833,6 +824,7 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci temp = heat / (mass_tot*US%Q_to_J_kg*US%degC_to_C*tv%C_p) temp_anom = Heat_anom / (mass_tot*US%Q_to_J_kg*US%degC_to_C*tv%C_p) endif + toten = RZL4_to_J * (KE_tot + PE_tot) En_mass = toten / mass_tot call get_time(day, start_of_day, num_days) @@ -952,9 +944,12 @@ subroutine write_energy(u, v, h, tv, day, n, G, GV, US, CS, tracer_CSp, dt_forci call CS%fileenergy_nc%write_field(CS%fields(1), real(CS%ntrunc), reday) call CS%fileenergy_nc%write_field(CS%fields(2), toten, reday) + do k=1,nz+1 ; PE(K) = RZL4_to_J*PE(K) ; enddo call CS%fileenergy_nc%write_field(CS%fields(3), PE, reday) + do k=1,nz ; KE(k) = RZL4_to_J*KE(k) ; enddo call CS%fileenergy_nc%write_field(CS%fields(4), KE, reday) call CS%fileenergy_nc%write_field(CS%fields(5), H_0APE, reday) + do k=1,nz ; mass_lay(k) = US%RZL2_to_kg*mass_lay(k) ; enddo call CS%fileenergy_nc%write_field(CS%fields(6), mass_lay, reday) call CS%fileenergy_nc%write_field(CS%fields(7), mass_tot, reday) @@ -1018,19 +1013,17 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS) !! to MOM_sum_output_init. ! Local variables real, dimension(SZI_(G),SZJ_(G)) :: & - FW_in, & ! The net fresh water input, integrated over a timestep [kg]. + FW_in, & ! The net fresh water input, integrated over a timestep [R Z L2 ~> kg]. salt_in, & ! The total salt added by surface fluxes, integrated ! over a time step [ppt kg]. heat_in ! The total heat added by surface fluxes, integrated - ! over a time step [J]. - real :: RZL2_to_kg ! A combination of scaling factors for mass [kg R-1 Z-1 L-2 ~> 1] - real :: QRZL2_to_J ! A combination of scaling factors for heat [J Q-1 R-1 Z-1 L-2 ~> 1] + ! over a time step [Q R Z L2 ~> J]. type(EFP_type) :: & FW_in_EFP, & ! The net fresh water input, integrated over a timestep ! and summed over space [kg]. salt_in_EFP, & ! The total salt added by surface fluxes, integrated - ! over a time step and summed over space [ppt kg]. + ! over a time step and summed over space [R Z L2 ~> ppt kg]. heat_in_EFP ! The total heat added by boundary fluxes, integrated ! over a time step and summed over space [J]. @@ -1038,14 +1031,11 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS) is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec - RZL2_to_kg = US%L_to_m**2*US%RZ_to_kg_m2 - QRZL2_to_J = RZL2_to_kg*US%Q_to_J_kg - FW_in(:,:) = 0.0 if (associated(fluxes%evap)) then if (associated(fluxes%lprec) .and. associated(fluxes%fprec)) then do j=js,je ; do i=is,ie - FW_in(i,j) = RZL2_to_kg * dt*G%areaT(i,j)*(fluxes%evap(i,j) + & + FW_in(i,j) = dt*G%areaT(i,j)*(fluxes%evap(i,j) + & (((fluxes%lprec(i,j) + fluxes%vprec(i,j)) + fluxes%lrunoff(i,j)) + & (fluxes%fprec(i,j) + fluxes%frunoff(i,j)))) enddo ; enddo @@ -1056,27 +1046,26 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS) endif if (associated(fluxes%seaice_melt)) then ; do j=js,je ; do i=is,ie - FW_in(i,j) = FW_in(i,j) + RZL2_to_kg*dt * & - G%areaT(i,j) * fluxes%seaice_melt(i,j) + FW_in(i,j) = FW_in(i,j) + dt * G%areaT(i,j) * fluxes%seaice_melt(i,j) enddo ; enddo ; endif salt_in(:,:) = 0.0 ; heat_in(:,:) = 0.0 if (CS%use_temperature) then if (associated(fluxes%sw)) then ; do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + dt * QRZL2_to_J * G%areaT(i,j) * (fluxes%sw(i,j) + & + heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * (fluxes%sw(i,j) + & (fluxes%lw(i,j) + (fluxes%latent(i,j) + fluxes%sens(i,j)))) enddo ; enddo ; endif if (associated(fluxes%seaice_melt_heat)) then ; do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + dt * QRZL2_to_J * G%areaT(i,j) * & + heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * & fluxes%seaice_melt_heat(i,j) enddo ; enddo ; endif ! smg: new code ! include heat content from water transport across ocean surface ! if (associated(fluxes%heat_content_lprec)) then ; do j=js,je ; do i=is,ie -! heat_in(i,j) = heat_in(i,j) + dt * QRZL2_to_J * G%areaT(i,j) * & +! heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * & ! (fluxes%heat_content_lprec(i,j) + (fluxes%heat_content_fprec(i,j) & ! + (fluxes%heat_content_lrunoff(i,j) + (fluxes%heat_content_frunoff(i,j) & ! + (fluxes%heat_content_cond(i,j) + (fluxes%heat_content_vprec(i,j) & @@ -1086,41 +1075,41 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS) ! smg: old code if (associated(fluxes%heat_content_evap)) then do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + dt * QRZL2_to_J * G%areaT(i,j) * & + heat_in(i,j) = heat_in(i,j) + dt * G%areaT(i,j) * & (fluxes%heat_content_evap(i,j) + fluxes%heat_content_lprec(i,j) + & fluxes%heat_content_cond(i,j) + fluxes%heat_content_fprec(i,j) + & fluxes%heat_content_lrunoff(i,j) + fluxes%heat_content_frunoff(i,j)) enddo ; enddo elseif (associated(tv%TempxPmE)) then do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + (tv%C_p * QRZL2_to_J*G%areaT(i,j)) * tv%TempxPmE(i,j) + heat_in(i,j) = heat_in(i,j) + (tv%C_p * G%areaT(i,j)) * tv%TempxPmE(i,j) enddo ; enddo elseif (associated(fluxes%evap)) then do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + (US%Q_to_J_kg*tv%C_p * sfc_state%SST(i,j)) * FW_in(i,j) + heat_in(i,j) = heat_in(i,j) + (tv%C_p * sfc_state%SST(i,j)) * FW_in(i,j) enddo ; enddo endif ! The following heat sources may or may not be used. if (associated(tv%internal_heat)) then do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + (tv%C_p * QRZL2_to_J*G%areaT(i,j)) * tv%internal_heat(i,j) + heat_in(i,j) = heat_in(i,j) + (tv%C_p * G%areaT(i,j)) * tv%internal_heat(i,j) enddo ; enddo endif if (associated(tv%frazil)) then ; do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + QRZL2_to_J * G%areaT(i,j) * tv%frazil(i,j) + heat_in(i,j) = heat_in(i,j) + G%areaT(i,j) * tv%frazil(i,j) enddo ; enddo ; endif if (associated(fluxes%heat_added)) then ; do j=js,je ; do i=is,ie - heat_in(i,j) = heat_in(i,j) + QRZL2_to_J * dt*G%areaT(i,j) * fluxes%heat_added(i,j) + heat_in(i,j) = heat_in(i,j) + dt*G%areaT(i,j) * fluxes%heat_added(i,j) enddo ; enddo ; endif ! if (associated(sfc_state%sw_lost)) then ; do j=js,je ; do i=is,ie -! heat_in(i,j) = heat_in(i,j) - US%L_to_m**2*G%areaT(i,j) * sfc_state%sw_lost(i,j) +! sfc_state%sw_lost must be in units of [Q R Z ~> J m-2] +! heat_in(i,j) = heat_in(i,j) - G%areaT(i,j) * sfc_state%sw_lost(i,j) ! enddo ; enddo ; endif if (associated(fluxes%salt_flux)) then ; do j=js,je ; do i=is,ie ! integrate salt_flux in [R Z T-1 ~> kgSalt m-2 s-1] to give [ppt kg] - salt_in(i,j) = RZL2_to_kg * dt * & - G%areaT(i,j)*(1000.0*fluxes%salt_flux(i,j)) + salt_in(i,j) = dt * G%areaT(i,j)*(1000.0*fluxes%salt_flux(i,j)) enddo ; enddo ; endif endif @@ -1129,9 +1118,12 @@ subroutine accumulate_net_input(fluxes, sfc_state, tv, dt, G, US, CS) ! The on-PE sums are stored here, but the sums across PEs are deferred to ! the next call to write_energy to avoid extra barriers. isr = is - (G%isd-1) ; ier = ie - (G%isd-1) ; jsr = js - (G%jsd-1) ; jer = je - (G%jsd-1) - FW_in_EFP = reproducing_sum_EFP(FW_in, isr, ier, jsr, jer, only_on_PE=.true.) - heat_in_EFP = reproducing_sum_EFP(heat_in, isr, ier, jsr, jer, only_on_PE=.true.) - salt_in_EFP = reproducing_sum_EFP(salt_in, isr, ier, jsr, jer, only_on_PE=.true.) + FW_in_EFP = reproducing_sum_EFP(FW_in, isr, ier, jsr, jer, only_on_PE=.true., & + unscale=US%RZL2_to_kg) + heat_in_EFP = reproducing_sum_EFP(heat_in, isr, ier, jsr, jer, only_on_PE=.true., & + unscale=US%RZL2_to_kg*US%Q_to_J_kg) + salt_in_EFP = reproducing_sum_EFP(salt_in, isr, ier, jsr, jer, only_on_PE=.true., & + unscale=US%RZL2_to_kg) CS%fresh_water_in_EFP = CS%fresh_water_in_EFP + FW_in_EFP CS%net_salt_in_EFP = CS%net_salt_in_EFP + salt_in_EFP From 03ed72642cdf8813532ff2ba7f3ad8361bf1c748 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Wed, 11 Dec 2024 18:05:26 -0500 Subject: [PATCH 4/4] Use reproducing_sum with unscale in 5 places Use reproducing_sum with unscale to calculate the global mass diagnostic, globally integrated ocean surface area, offline tracer transport residuals and in calculating the OBC inflow area in tidal_bay_set_OBC_data(). This change allows for the elimination or replacement of 6 rescaling factors and one added instance with a consistent conversion factor and diagnostic units occur on the same line. All answers are bitwise identical. --- src/diagnostics/MOM_diagnostics.F90 | 9 +++---- .../MOM_shared_initialization.F90 | 9 +++---- src/tracer/MOM_offline_main.F90 | 24 ++++++++----------- src/user/tidal_bay_initialization.F90 | 10 +++----- 4 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/diagnostics/MOM_diagnostics.F90 b/src/diagnostics/MOM_diagnostics.F90 index abce27909b..de4e9190df 100644 --- a/src/diagnostics/MOM_diagnostics.F90 +++ b/src/diagnostics/MOM_diagnostics.F90 @@ -332,9 +332,9 @@ subroutine calculate_diagnostic_fields(u, v, h, uh, vh, tv, ADp, CDp, p_surf, & if (CS%id_masso > 0) then mass_cell(:,:) = 0.0 do k=1,nz ; do j=js,je ; do i=is,ie - mass_cell(i,j) = mass_cell(i,j) + (GV%H_to_kg_m2*h(i,j,k)) * US%L_to_m**2*G%areaT(i,j) + mass_cell(i,j) = mass_cell(i,j) + (GV%H_to_RZ*h(i,j,k)) * G%areaT(i,j) enddo ; enddo ; enddo - masso = reproducing_sum(mass_cell) + masso = reproducing_sum(mass_cell, unscale=US%RZL2_to_kg) call post_data(CS%id_masso, masso, CS%diag) endif @@ -1644,8 +1644,9 @@ subroutine MOM_diagnostics_init(MIS, ADp, CDp, Time, G, GV, US, param_file, diag Time, 'Mass per unit area of liquid ocean grid cell', 'kg m-2', conversion=GV%H_to_kg_m2, & standard_name='sea_water_mass_per_unit_area', v_extensive=.true.) - CS%id_masso = register_scalar_field('ocean_model', 'masso', Time, & - diag, 'Mass of liquid ocean', 'kg', standard_name='sea_water_mass') + CS%id_masso = register_scalar_field('ocean_model', 'masso', Time, diag, & + 'Mass of liquid ocean', units='kg', conversion=US%RZL2_to_kg, & + standard_name='sea_water_mass') CS%id_thkcello = register_diag_field('ocean_model', 'thkcello', diag%axesTL, Time, & long_name='Cell Thickness', standard_name='cell_thickness', & diff --git a/src/initialization/MOM_shared_initialization.F90 b/src/initialization/MOM_shared_initialization.F90 index eabd376512..0a257b6ca1 100644 --- a/src/initialization/MOM_shared_initialization.F90 +++ b/src/initialization/MOM_shared_initialization.F90 @@ -1314,17 +1314,14 @@ subroutine compute_global_grid_integrals(G, US) ! Local variables real, dimension(G%isc:G%iec, G%jsc:G%jec) :: tmpForSumming ! Masked and unscaled cell areas [m2] - real :: area_scale ! A scaling factor for area into MKS units [m2 L-2 ~> 1] - integer :: i,j - - area_scale = US%L_to_m**2 + integer :: i, j tmpForSumming(:,:) = 0. G%areaT_global = 0.0 ; G%IareaT_global = 0.0 do j=G%jsc,G%jec ; do i=G%isc,G%iec - tmpForSumming(i,j) = area_scale*G%areaT(i,j) * G%mask2dT(i,j) + tmpForSumming(i,j) = G%areaT(i,j) * G%mask2dT(i,j) enddo ; enddo - G%areaT_global = reproducing_sum(tmpForSumming) + G%areaT_global = US%L_to_m**2 * reproducing_sum(tmpForSumming, unscale=US%L_to_m**2) if (G%areaT_global == 0.0) & call MOM_error(FATAL, "compute_global_grid_integrals: zero ocean area (check topography?)") diff --git a/src/tracer/MOM_offline_main.F90 b/src/tracer/MOM_offline_main.F90 index f772e0bc8a..cd9c05de9e 100644 --- a/src/tracer/MOM_offline_main.F90 +++ b/src/tracer/MOM_offline_main.F90 @@ -625,27 +625,24 @@ real function remaining_transport_sum(G, GV, US, uhtr, vhtr, h_new) ! Local variables real, dimension(SZI_(G),SZJ_(G)) :: trans_rem_col !< The vertical sum of the absolute value of - !! transports through the faces of a column, in MKS units [kg]. + !! transports through the faces of a column [R Z L2 ~> kg]. real :: trans_cell !< The sum of the absolute value of the remaining transports through the faces !! of a tracer cell [H L2 ~> m3 or kg] - real :: HL2_to_kg_scale !< Unit conversion factor to cell mass [kg H-1 L-2 ~> kg m-3 or 1] integer :: i, j, k, is, ie, js, je, nz is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke - HL2_to_kg_scale = GV%H_to_kg_m2 * US%L_to_m**2 - trans_rem_col(:,:) = 0.0 do k=1,nz ; do j=js,je ; do i=is,ie trans_cell = (ABS(uhtr(I-1,j,k)) + ABS(uhtr(I,j,k))) + & (ABS(vhtr(i,J-1,k)) + ABS(vhtr(i,J,k))) if (trans_cell > max(1.0e-16*h_new(i,j,k), GV%H_subroundoff) * G%areaT(i,j)) & - trans_rem_col(i,j) = trans_rem_col(i,j) + HL2_to_kg_scale * trans_cell + trans_rem_col(i,j) = trans_rem_col(i,j) + GV%H_to_RZ * trans_cell enddo ; enddo ; enddo ! The factor of 0.5 here is to avoid double-counting because two cells share a face. - remaining_transport_sum = 0.5 * GV%kg_m2_to_H*US%m_to_L**2 * & - reproducing_sum(trans_rem_col, is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd)) + remaining_transport_sum = 0.5 * GV%RZ_to_H * reproducing_sum(trans_rem_col, & + is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd), unscale=US%RZL2_to_kg) end function remaining_transport_sum @@ -876,8 +873,8 @@ subroutine offline_advection_layer(fluxes, Time_start, time_interval, G, GV, US, real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: vhtr_sub ! Remaining meridional mass transports [H L2 ~> m3 or kg] real, dimension(SZI_(G),SZJB_(G)) :: rem_col_flux ! The summed absolute value of the remaining - ! fluxes through the faces of a column or within a column, in mks units [kg] - real :: sum_flux ! Globally summed absolute value of fluxes in mks units [kg], which is + ! mass fluxes through the faces of a column or within a column [R Z L2 ~> kg] + real :: sum_flux ! Globally summed absolute value of fluxes [R Z L2 ~> kg], which is ! used to keep track of how close to convergence we are. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & @@ -890,7 +887,6 @@ subroutine offline_advection_layer(fluxes, Time_start, time_interval, G, GV, US, ! Work arrays for temperature and salinity integer :: iter real :: dt_iter ! The timestep of each iteration [T ~> s] - real :: HL2_to_kg_scale ! Unit conversion factors to cell mass [kg H-1 L-2 ~> kg m-3 or 1] character(len=160) :: mesg ! The text of an error message integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz integer :: IsdB, IedB, JsdB, JedB @@ -993,22 +989,22 @@ subroutine offline_advection_layer(fluxes, Time_start, time_interval, G, GV, US, call pass_vector(uhtr,vhtr,G%Domain) ! Calculate how close we are to converging by summing the remaining fluxes at each point - HL2_to_kg_scale = US%L_to_m**2*GV%H_to_kg_m2 rem_col_flux(:,:) = 0.0 do k=1,nz ; do j=js,je ; do i=is,ie - rem_col_flux(i,j) = rem_col_flux(i,j) + HL2_to_kg_scale * & + rem_col_flux(i,j) = rem_col_flux(i,j) + GV%H_to_RZ * & ( (abs(eatr(i,j,k)) + abs(ebtr(i,j,k))) + & ((abs(uhtr(I-1,j,k)) + abs(uhtr(I,j,k))) + & (abs(vhtr(i,J-1,k)) + abs(vhtr(i,J,k))) ) ) enddo ; enddo ; enddo - sum_flux = reproducing_sum(rem_col_flux, is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd)) + sum_flux = reproducing_sum(rem_col_flux, is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd), & + unscale=US%RZL2_to_kg) if (sum_flux==0) then write(mesg,*) 'offline_advection_layer: Converged after iteration', iter call MOM_mesg(mesg) exit else - write(mesg,*) "offline_advection_layer: Iteration ", iter, " remaining total fluxes: ", sum_flux + write(mesg,*) "offline_advection_layer: Iteration ", iter, " remaining total fluxes: ", sum_flux*US%RZL2_to_kg call MOM_mesg(mesg) endif diff --git a/src/user/tidal_bay_initialization.F90 b/src/user/tidal_bay_initialization.F90 index c2ca2565ce..5b300a4d05 100644 --- a/src/user/tidal_bay_initialization.F90 +++ b/src/user/tidal_bay_initialization.F90 @@ -78,8 +78,7 @@ subroutine tidal_bay_set_OBC_data(OBC, CS, G, GV, US, h, Time) real :: my_flux ! The vlume flux through the face [L2 Z T-1 ~> m3 s-1] real :: total_area ! The total face area of the OBCs [L Z ~> m2] real :: PI ! The ratio of the circumference of a circle to its diameter [nondim] - real :: flux_scale ! A scaling factor for the areas [m2 H-1 L-1 ~> nondim or m3 kg-1] - real, allocatable :: my_area(:,:) ! The total OBC inflow area [m2] + real, allocatable :: my_area(:,:) ! The total OBC inflow area [L Z ~> m2] integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz, n integer :: IsdB, IedB, JsdB, JedB type(OBC_segment_type), pointer :: segment => NULL() @@ -94,8 +93,6 @@ subroutine tidal_bay_set_OBC_data(OBC, CS, G, GV, US, h, Time) allocate(my_area(1:1,js:je)) - flux_scale = GV%H_to_m*US%L_to_m - time_sec = US%s_to_T*time_type_to_real(Time) cff_eta = CS%tide_ssh_amp * sin(2.0*PI*time_sec / CS%tide_period) my_area = 0.0 @@ -105,12 +102,11 @@ subroutine tidal_bay_set_OBC_data(OBC, CS, G, GV, US, h, Time) do j=segment%HI%jsc,segment%HI%jec ; do I=segment%HI%IscB,segment%HI%IecB if (OBC%segnum_u(I,j) /= OBC_NONE) then do k=1,nz - ! This area has to be in MKS units to work with reproducing_sum. - my_area(1,j) = my_area(1,j) + h(I,j,k)*flux_scale*G%dyCu(I,j) + my_area(1,j) = my_area(1,j) + h(I,j,k)*(GV%H_to_m*US%m_to_Z)*G%dyCu(I,j) enddo endif enddo ; enddo - total_area = US%m_to_Z*US%m_to_L * reproducing_sum(my_area) + total_area = reproducing_sum(my_area, unscale=US%Z_to_m*US%L_to_m) my_flux = - CS%tide_flow * SIN(2.0*PI*time_sec / CS%tide_period) do n = 1, OBC%number_of_segments