diff --git a/src/ALE/MOM_hybgen_regrid.F90 b/src/ALE/MOM_hybgen_regrid.F90 index 524f9b8ff2..491693549f 100644 --- a/src/ALE/MOM_hybgen_regrid.F90 +++ b/src/ALE/MOM_hybgen_regrid.F90 @@ -41,8 +41,10 @@ module MOM_hybgen_regrid dp0k, & !< minimum deep z-layer separation [H ~> m or kg m-2] ds0k !< minimum shallow z-layer separation [H ~> m or kg m-2] - real :: coord_scale = 1.0 !< A scaling factor to restores the depth coordinates to values in m - real :: Rho_coord_scale = 1.0 !< A scaling factor to restores the denesity coordinates to values in kg m-3 + real :: coord_scale = 1.0 !< A scaling factor to restores the depth coordinates to + !! values in m [m H-1 ~> 1 or m3 kg-1] + real :: Rho_coord_scale = 1.0 !< A scaling factor to restores the denesity coordinates to + !! values in kg m-3 [kg m-3 R-1 ~> 1] real :: dpns !< depth to start terrain following [H ~> m or kg m-2] real :: dsns !< depth to stop terrain following [H ~> m or kg m-2] @@ -68,7 +70,7 @@ module MOM_hybgen_regrid !! the bottom that certain adjustments can be made in the Hybgen regridding !! code [H ~> m or kg m-2]. In Hycom, this is set to onem (nominally 1 m). real :: h_thin !< A layer thickness below which a layer is considered to be too thin for - !! certain adjustments to be made in the Hybgen regridding code. + !! certain adjustments to be made in the Hybgen regridding code [H ~> m or kg m-2]. !! In Hycom, this is set to onemm (nominally 0.001 m). real :: rho_eps !< A small nonzero density that is used to prevent division by zero @@ -284,7 +286,7 @@ subroutine get_hybgen_regrid_params(CS, nk, ref_pressure, hybiso, nsigma, dp00i, real, optional, intent(out) :: ref_pressure !< Reference pressure for density calculations [R L2 T-2 ~> Pa] real, optional, intent(out) :: hybiso !< Hybgen uses PCM if layer is within hybiso of target density [R ~> kg m-3] integer, optional, intent(out) :: nsigma !< Number of sigma levels used by HYBGEN - real, optional, intent(out) :: dp00i !< Deep isopycnal spacing minimum thickness (m) + real, optional, intent(out) :: dp00i !< Deep isopycnal spacing minimum thickness [H ~> m or kg m-2] real, optional, intent(out) :: qhybrlx !< Fractional relaxation amount per timestep, 0 < qyhbrlx <= 1 [nondim] real, optional, intent(out) :: dp0k(:) !< minimum deep z-layer separation [H ~> m or kg m-2] real, optional, intent(out) :: ds0k(:) !< minimum shallow z-layer separation [H ~> m or kg m-2] @@ -687,8 +689,8 @@ real function cushn(delp, dp0) ! These are derivative nondimensional parameters. ! real, parameter :: cusha = qqmn**2 * (qqmx-1.0) / (qqmx-qqmn)**2 ! real, parameter :: I_qqmn = 1.0 / qqmn - real, parameter :: qq_scale = (qqmx-1.0) / (qqmx-qqmn)**2 - real, parameter :: I_qqmx = 1.0 / qqmx + real, parameter :: qq_scale = (qqmx-1.0) / (qqmx-qqmn)**2 ! A scaling factor based on qqmn and qqmx [nondim] + real, parameter :: I_qqmx = 1.0 / qqmx ! The inverse of qqmx [nondim] ! --- if delp >= qqmx*dp0 >> dp0, cushn returns delp. ! --- if delp <= qqmn*dp0 << -dp0, cushn returns dp0. diff --git a/src/ALE/MOM_hybgen_remap.F90 b/src/ALE/MOM_hybgen_remap.F90 index 5ab3e162db..f97b0e9c62 100644 --- a/src/ALE/MOM_hybgen_remap.F90 +++ b/src/ALE/MOM_hybgen_remap.F90 @@ -126,7 +126,7 @@ subroutine hybgen_ppm_coefs(s, h_src, edges, nk, ns, thin, PCM_lay) real :: da ! Difference between the unlimited scalar edge value estimates [A] real :: a6 ! Scalar field differences that are proportional to the curvature [A] real :: slk, srk ! Differences between adjacent cell averages of scalars [A] - real :: sck ! Scalar differences across a cell. + real :: sck ! Scalar differences across a cell [A] real :: as(nk) ! Scalar field difference across each cell [A] real :: al(nk), ar(nk) ! Scalar field at the left and right edges of a cell [A] real :: h112(nk+1), h122(nk+1) ! Combinations of thicknesses [H ~> m or kg m-2] diff --git a/src/ALE/MOM_remapping.F90 b/src/ALE/MOM_remapping.F90 index eeb4590a08..abcd821790 100644 --- a/src/ALE/MOM_remapping.F90 +++ b/src/ALE/MOM_remapping.F90 @@ -1723,7 +1723,7 @@ logical function test_interp(verbose, msg, nsrc, h_src, u_src, ndest, h_dest, u_ ! Local variables real, dimension(ndest+1) :: u_dest ! Interpolated value at destination cell interfaces [A] integer :: k - real :: error + real :: error ! The difference between the evaluated and expected solutions [A] ! Interpolate from src to dest call interpolate_column(nsrc, h_src, u_src, ndest, h_dest, u_dest, .true.) @@ -1760,7 +1760,7 @@ logical function test_reintegrate(verbose, msg, nsrc, h_src, uh_src, ndest, h_de ! Local variables real, dimension(ndest) :: uh_dest ! Reintegrated value on destination cells [A H] integer :: k - real :: error + real :: error ! The difference between the evaluated and expected solutions [A H] ! Interpolate from src to dest call reintegrate_column(nsrc, h_src, uh_src, ndest, h_dest, uh_dest) diff --git a/src/ALE/P1M_functions.F90 b/src/ALE/P1M_functions.F90 index b17b35c85c..7889966135 100644 --- a/src/ALE/P1M_functions.F90 +++ b/src/ALE/P1M_functions.F90 @@ -36,7 +36,7 @@ subroutine P1M_interpolation( N, h, u, edge_values, ppoly_coef, h_neglect, answe ! Local variables integer :: k ! loop index - real :: u0_l, u0_r ! edge values (left and right) + real :: u0_l, u0_r ! edge values (left and right) [A] ! Bound edge values (routine found in 'edge_values.F90') call bound_edge_values( N, h, u, edge_values, h_neglect, answer_date=answer_date ) @@ -74,10 +74,10 @@ subroutine P1M_boundary_extrapolation( N, h, u, edge_values, ppoly_coef ) real, dimension(:,:), intent(inout) :: ppoly_coef !< coefficients of piecewise polynomials, mainly [A] ! Local variables - real :: u0, u1 ! cell averages - real :: h0, h1 ! corresponding cell widths - real :: slope ! retained PLM slope - real :: u0_l, u0_r ! edge values + real :: u0, u1 ! cell averages [A] + real :: h0, h1 ! corresponding cell widths [H] + real :: slope ! retained PLM slope [A] + real :: u0_l, u0_r ! edge values [A] ! ----------------------------------------- ! Left edge value in the left boundary cell diff --git a/src/ALE/PCM_functions.F90 b/src/ALE/PCM_functions.F90 index 4f64e4a96d..f5899339e4 100644 --- a/src/ALE/PCM_functions.F90 +++ b/src/ALE/PCM_functions.F90 @@ -17,11 +17,11 @@ module PCM_functions !! defining 'grid' and 'ppoly'. No consistency check is performed. subroutine PCM_reconstruction( N, u, edge_values, ppoly_coef ) integer, intent(in) :: N !< Number of cells - real, dimension(:), intent(in) :: u !< cell averages + real, dimension(:), intent(in) :: u !< cell averages in arbitrary units [A] real, dimension(:,:), intent(inout) :: edge_values !< Edge value of polynomial, - !! with the same units as u. + !! with the same units as u [A]. real, dimension(:,:), intent(inout) :: ppoly_coef !< Coefficients of polynomial, - !! with the same units as u. + !! with the same units as u [A]. ! Local variables integer :: k diff --git a/src/ALE/PLM_functions.F90 b/src/ALE/PLM_functions.F90 index bc7f100a04..c0c4516fe2 100644 --- a/src/ALE/PLM_functions.F90 +++ b/src/ALE/PLM_functions.F90 @@ -16,20 +16,21 @@ module PLM_functions contains -!> Returns a limited PLM slope following White and Adcroft, 2008. [units of u] +!> Returns a limited PLM slope following White and Adcroft, 2008, in the same arbitrary +!! units [A] as the input values. !! Note that this is not the same as the Colella and Woodward method. real elemental pure function PLM_slope_wa(h_l, h_c, h_r, h_neglect, u_l, u_c, u_r) - real, intent(in) :: h_l !< Thickness of left cell [units of grid thickness] - real, intent(in) :: h_c !< Thickness of center cell [units of grid thickness] - real, intent(in) :: h_r !< Thickness of right cell [units of grid thickness] - real, intent(in) :: h_neglect !< A negligible thickness [units of grid thickness] - real, intent(in) :: u_l !< Value of left cell [units of u] - real, intent(in) :: u_c !< Value of center cell [units of u] - real, intent(in) :: u_r !< Value of right cell [units of u] + real, intent(in) :: h_l !< Thickness of left cell in arbitrary grid thickness units [H] + real, intent(in) :: h_c !< Thickness of center cell in arbitrary grid thickness units [H] + real, intent(in) :: h_r !< Thickness of right cell in arbitrary grid thickness units [H] + real, intent(in) :: h_neglect !< A negligible thickness [H] + real, intent(in) :: u_l !< Value of left cell in arbitrary units [A] + real, intent(in) :: u_c !< Value of center cell in arbitrary units [A] + real, intent(in) :: u_r !< Value of right cell in arbitrary units [A] ! Local variables real :: sigma_l, sigma_c, sigma_r ! Left, central and right slope estimates as - ! differences across the cell [units of u] - real :: u_min, u_max ! Minimum and maximum value across cell [units of u] + ! differences across the cell [A] + real :: u_min, u_max ! Minimum and maximum value across cell [A] ! Side differences sigma_r = u_r - u_c @@ -63,20 +64,21 @@ real elemental pure function PLM_slope_wa(h_l, h_c, h_r, h_neglect, u_l, u_c, u_ end function PLM_slope_wa -!> Returns a limited PLM slope following Colella and Woodward 1984. +!> Returns a limited PLM slope following Colella and Woodward 1984, in the same +!! arbitrary units as the input values [A]. real elemental pure function PLM_slope_cw(h_l, h_c, h_r, h_neglect, u_l, u_c, u_r) - real, intent(in) :: h_l !< Thickness of left cell [units of grid thickness] - real, intent(in) :: h_c !< Thickness of center cell [units of grid thickness] - real, intent(in) :: h_r !< Thickness of right cell [units of grid thickness] - real, intent(in) :: h_neglect !< A negligible thickness [units of grid thickness] - real, intent(in) :: u_l !< Value of left cell [units of u] - real, intent(in) :: u_c !< Value of center cell [units of u] - real, intent(in) :: u_r !< Value of right cell [units of u] + real, intent(in) :: h_l !< Thickness of left cell in arbitrary grid thickness units [H] + real, intent(in) :: h_c !< Thickness of center cell in arbitrary grid thickness units [H] + real, intent(in) :: h_r !< Thickness of right cell in arbitrary grid thickness units [H] + real, intent(in) :: h_neglect !< A negligible thickness [H] + real, intent(in) :: u_l !< Value of left cell in arbitrary units [A] + real, intent(in) :: u_c !< Value of center cell in arbitrary units [A] + real, intent(in) :: u_r !< Value of right cell in arbitrary units [A] ! Local variables real :: sigma_l, sigma_c, sigma_r ! Left, central and right slope estimates as - ! differences across the cell [units of u] - real :: u_min, u_max ! Minimum and maximum value across cell [units of u] - real :: h_cn ! Thickness of center cell [units of grid thickness] + ! differences across the cell [A] + real :: u_min, u_max ! Minimum and maximum value across cell [A] + real :: h_cn ! Thickness of center cell [H] h_cn = h_c + h_neglect @@ -117,18 +119,19 @@ real elemental pure function PLM_slope_cw(h_l, h_c, h_r, h_neglect, u_l, u_c, u_ end function PLM_slope_cw -!> Returns a limited PLM slope following Colella and Woodward 1984. +!> Returns a limited PLM slope following Colella and Woodward 1984, in the same +!! arbitrary units as the input values [A]. real elemental pure function PLM_monotonized_slope(u_l, u_c, u_r, s_l, s_c, s_r) - real, intent(in) :: u_l !< Value of left cell [units of u] - real, intent(in) :: u_c !< Value of center cell [units of u] - real, intent(in) :: u_r !< Value of right cell [units of u] - real, intent(in) :: s_l !< PLM slope of left cell [units of u] - real, intent(in) :: s_c !< PLM slope of center cell [units of u] - real, intent(in) :: s_r !< PLM slope of right cell [units of u] + real, intent(in) :: u_l !< Value of left cell in arbitrary units [A] + real, intent(in) :: u_c !< Value of center cell in arbitrary units [A] + real, intent(in) :: u_r !< Value of right cell in arbitrary units [A] + real, intent(in) :: s_l !< PLM slope of left cell [A] + real, intent(in) :: s_c !< PLM slope of center cell [A] + real, intent(in) :: s_r !< PLM slope of right cell [A] ! Local variables - real :: e_r, e_l, edge ! Right, left and temporary edge values [units of u] - real :: almost_two ! The number 2, almost. - real :: slp ! Magnitude of PLM central slope [units of u] + real :: e_r, e_l, edge ! Right, left and temporary edge values [A] + real :: almost_two ! The number 2, almost [nondim] + real :: slp ! Magnitude of PLM central slope [A] almost_two = 2. * ( 1. - epsilon(s_c) ) @@ -155,17 +158,18 @@ real elemental pure function PLM_monotonized_slope(u_l, u_c, u_r, s_l, s_c, s_r) end function PLM_monotonized_slope -!> Returns a PLM slope using h2 extrapolation from a cell to the left. +!> Returns a PLM slope using h2 extrapolation from a cell to the left, in the same +!! arbitrary units as the input values [A]. !! Use the negative to extrapolate from the cell to the right. real elemental pure function PLM_extrapolate_slope(h_l, h_c, h_neglect, u_l, u_c) - real, intent(in) :: h_l !< Thickness of left cell [units of grid thickness] - real, intent(in) :: h_c !< Thickness of center cell [units of grid thickness] - real, intent(in) :: h_neglect !< A negligible thickness [units of grid thickness] - real, intent(in) :: u_l !< Value of left cell [units of u] - real, intent(in) :: u_c !< Value of center cell [units of u] + real, intent(in) :: h_l !< Thickness of left cell in arbitrary grid thickness units [H] + real, intent(in) :: h_c !< Thickness of center cell in arbitrary grid thickness units [H] + real, intent(in) :: h_neglect !< A negligible thickness [H] + real, intent(in) :: u_l !< Value of left cell in arbitrary units [A] + real, intent(in) :: u_c !< Value of center cell in arbitrary units [A] ! Local variables - real :: left_edge ! Left edge value [units of u] - real :: hl, hc ! Left and central cell thicknesses [units of grid thickness] + real :: left_edge ! Left edge value [A] + real :: hl, hc ! Left and central cell thicknesses [H] ! Avoid division by zero for vanished cells hl = h_l + h_neglect @@ -185,24 +189,26 @@ end function PLM_extrapolate_slope !! defining 'grid' and 'ppoly'. No consistency check is performed here. subroutine PLM_reconstruction( N, h, u, edge_values, ppoly_coef, h_neglect ) integer, intent(in) :: N !< Number of cells - real, dimension(:), intent(in) :: h !< cell widths (size N) - real, dimension(:), intent(in) :: u !< cell averages (size N) + real, dimension(:), intent(in) :: h !< cell widths (size N) [H] + real, dimension(:), intent(in) :: u !< cell averages (size N) in arbitrary units [A] real, dimension(:,:), intent(inout) :: edge_values !< edge values of piecewise polynomials, - !! with the same units as u. + !! with the same units as u [A]. real, dimension(:,:), intent(inout) :: ppoly_coef !< coefficients of piecewise polynomials, mainly - !! with the same units as u. + !! with the same units as u [A]. real, optional, intent(in) :: h_neglect !< A negligibly small width for !! the purpose of cell reconstructions - !! in the same units as h + !! in the same units as h [H] ! Local variables - integer :: k ! loop index - real :: u_l, u_r ! left and right cell averages - real :: slope ! retained PLM slope - real :: e_r, edge - real :: almost_one - real, dimension(N) :: slp, mslp - real :: hNeglect + integer :: k ! loop index + real :: u_l, u_r ! left and right cell averages [A] + real :: slope ! retained PLM slope for a normalized cell width [A] + real :: e_r ! The edge value in the neighboring cell [A] + real :: edge ! The projected edge value in the cell [A] + real :: almost_one ! A value that is slightly smaller than 1 [nondim] + real, dimension(N) :: slp ! The first guess at the normalized tracer slopes [A] + real, dimension(N) :: mslp ! The monotonized normalized tracer slopes [A] + real :: hNeglect ! A negligibly small width used in cell reconstructions [H] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect @@ -265,18 +271,18 @@ end subroutine PLM_reconstruction !! defining 'grid' and 'ppoly'. No consistency check is performed here. subroutine PLM_boundary_extrapolation( N, h, u, edge_values, ppoly_coef, h_neglect ) integer, intent(in) :: N !< Number of cells - real, dimension(:), intent(in) :: h !< cell widths (size N) - real, dimension(:), intent(in) :: u !< cell averages (size N) + real, dimension(:), intent(in) :: h !< cell widths (size N) [H] + real, dimension(:), intent(in) :: u !< cell averages (size N) in arbitrary units [A] real, dimension(:,:), intent(inout) :: edge_values !< edge values of piecewise polynomials, - !! with the same units as u. + !! with the same units as u [A]. real, dimension(:,:), intent(inout) :: ppoly_coef !< coefficients of piecewise polynomials, mainly - !! with the same units as u. + !! with the same units as u [A]. real, optional, intent(in) :: h_neglect !< A negligibly small width for !! the purpose of cell reconstructions - !! in the same units as h + !! in the same units as h [H] ! Local variables - real :: slope ! retained PLM slope - real :: hNeglect + real :: slope ! retained PLM slope for a normalized cell width [A] + real :: hNeglect ! A negligibly small width used in cell reconstructions [H] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect diff --git a/src/ALE/PPM_functions.F90 b/src/ALE/PPM_functions.F90 index 805a70d502..ef6841f635 100644 --- a/src/ALE/PPM_functions.F90 +++ b/src/ALE/PPM_functions.F90 @@ -28,7 +28,7 @@ module PPM_functions subroutine PPM_reconstruction( N, h, u, edge_values, ppoly_coef, h_neglect, answer_date) integer, intent(in) :: N !< Number of cells real, dimension(N), intent(in) :: h !< Cell widths [H] - real, dimension(N), intent(in) :: u !< Cell averages [A] + real, dimension(N), intent(in) :: u !< Cell averages in arbitrary coordinates [A] real, dimension(N,2), intent(inout) :: edge_values !< Edge values [A] real, dimension(N,3), intent(inout) :: ppoly_coef !< Polynomial coefficients, mainly [A] real, optional, intent(in) :: h_neglect !< A negligibly small width [H] @@ -36,7 +36,7 @@ subroutine PPM_reconstruction( N, h, u, edge_values, ppoly_coef, h_neglect, answ ! Local variables integer :: k ! Loop index - real :: edge_l, edge_r ! Edge values (left and right) + real :: edge_l, edge_r ! Edge values (left and right) [A] ! PPM limiter call PPM_limiter_standard( N, h, u, edge_values, h_neglect, answer_date=answer_date ) @@ -69,9 +69,9 @@ subroutine PPM_limiter_standard( N, h, u, edge_values, h_neglect, answer_date ) ! Local variables integer :: k ! Loop index - real :: u_l, u_c, u_r ! Cell averages (left, center and right) - real :: edge_l, edge_r ! Edge values (left and right) - real :: expr1, expr2 + real :: u_l, u_c, u_r ! Cell averages (left, center and right) [A] + real :: edge_l, edge_r ! Edge values (left and right) [A] + real :: expr1, expr2 ! Temporary expressions [A2] ! Bound edge values call bound_edge_values( N, h, u, edge_values, h_neglect, answer_date=answer_date ) @@ -135,8 +135,8 @@ subroutine PPM_monotonicity( N, u, edge_values ) real, dimension(:,:), intent(inout) :: edge_values !< Potentially modified edge values [A] ! Local variables - integer :: k ! Loop index - real :: a6,da ! scalar temporaries + integer :: k ! Loop index + real :: a6, da ! Normalized scalar curvature and slope [A] ! Loop on interior cells to impose monotonicity ! Eq. 1.10 of (Colella & Woodward, JCP 84) @@ -195,14 +195,16 @@ subroutine PPM_boundary_extrapolation( N, h, u, edge_values, ppoly_coef, h_negle ! Local variables integer :: i0, i1 - real :: u0, u1 - real :: h0, h1 - real :: a, b, c - real :: u0_l, u0_r - real :: u1_l, u1_r - real :: slope - real :: exp1, exp2 - real :: hNeglect + real :: u0, u1 ! Average concentrations in the two neighboring cells [A] + real :: h0, h1 ! Thicknesses of the two neighboring cells [H] + real :: a, b, c ! An edge value, normalized slope and normalized curvature + ! of a reconstructed distribution [A] + real :: u0_l, u0_r ! Edge values of a neighboring cell [A] + real :: u1_l, u1_r ! Neighboring cell slopes renormalized by the thickness of + ! the cell being worked on [A] + real :: slope ! The normalized slope [A] + real :: exp1, exp2 ! Temporary expressions [A2] + real :: hNeglect ! A negligibly small width used in cell reconstructions [H] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect diff --git a/src/ALE/PQM_functions.F90 b/src/ALE/PQM_functions.F90 index 1159652858..ef42fb9f01 100644 --- a/src/ALE/PQM_functions.F90 +++ b/src/ALE/PQM_functions.F90 @@ -30,10 +30,10 @@ subroutine PQM_reconstruction( N, h, u, edge_values, edge_slopes, ppoly_coef, h_ ! Local variables integer :: k ! loop index - real :: h_c ! cell width + real :: h_c ! cell width [H] real :: u0_l, u0_r ! edge values (left and right) [A] real :: u1_l, u1_r ! edge slopes (left and right) [A H-1] - real :: a, b, c, d, e ! parabola coefficients + real :: a, b, c, d, e ! quartic fit coefficients [A] ! PQM limiter call PQM_limiter( N, h, u, edge_values, edge_slopes, h_neglect, answer_date=answer_date ) @@ -90,14 +90,15 @@ subroutine PQM_limiter( N, h, u, edge_values, edge_slopes, h_neglect, answer_dat real :: u1_l, u1_r ! edge slopes [A H-1] real :: u_l, u_c, u_r ! left, center and right cell averages [A] real :: h_l, h_c, h_r ! left, center and right cell widths [H] - real :: sigma_l, sigma_c, sigma_r ! left, center and right van Leer slopes - real :: slope ! retained PLM slope - real :: a, b, c, d, e - real :: alpha1, alpha2, alpha3 - real :: rho, sqrt_rho - real :: gradient1, gradient2 - real :: x1, x2 - real :: hNeglect + real :: sigma_l, sigma_c, sigma_r ! left, center and right van Leer slopes [A H-1] + real :: slope ! retained PLM slope [A H-1] + real :: a, b, c, d, e ! quartic fit coefficients [A] + real :: alpha1, alpha2, alpha3 ! Normalized second derivative coefficients [A] + real :: rho ! A temporary expression [A2] + real :: sqrt_rho ! The square root of rho [A] + real :: gradient1, gradient2 ! Normalized gradients [A] + real :: x1, x2 ! Fractional inflection point positions in a cell [nondim] + real :: hNeglect ! A negligibly small width for the purpose of cell reconstructions [H] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect @@ -359,13 +360,13 @@ subroutine PQM_boundary_extrapolation( N, h, u, edge_values, ppoly_coef ) real, dimension(:,:), intent(inout) :: ppoly_coef !< Coefficients of polynomial, mainly [A] ! Local variables integer :: i0, i1 - real :: u0, u1 - real :: h0, h1 - real :: a, b, c, d, e - real :: u0_l, u0_r - real :: u1_l, u1_r - real :: slope - real :: exp1, exp2 + real :: u0, u1 ! Successive cell averages [A] + real :: h0, h1 ! Successive cell thicknesses [H] + real :: a, b, c, d, e ! quartic fit coefficients [A] + real :: u0_l, u0_r ! Edge values [A] + real :: u1_l, u1_r ! Edge slopes [A H-1] + real :: slope ! The integrated slope across the cell [A] + real :: exp1, exp2 ! Two temporary expressions [A2] ! ----- Left boundary ----- i0 = 1 @@ -511,19 +512,21 @@ subroutine PQM_boundary_extrapolation_v1( N, h, u, edge_values, edge_slopes, ppo integer :: i0, i1 integer :: inflexion_l integer :: inflexion_r - real :: u0, u1, um - real :: h0, h1 - real :: a, b, c, d, e - real :: ar, br, beta - real :: u0_l, u0_r - real :: u1_l, u1_r - real :: u_plm - real :: slope - real :: alpha1, alpha2, alpha3 - real :: rho, sqrt_rho - real :: gradient1, gradient2 - real :: x1, x2 - real :: hNeglect + real :: u0, u1, um ! Successive cell averages [A] + real :: h0, h1 ! Successive cell thicknesses [H] + real :: a, b, c, d, e ! quartic fit coefficients [A] + real :: ar, br ! Temporary variables in [A] + real :: beta ! A rational function coefficient [nondim] + real :: u0_l, u0_r ! Edge values [A] + real :: u1_l, u1_r ! Edge slopes [A H-1] + real :: u_plm ! The integrated piecewise linear method slope [A] + real :: slope ! The integrated slope across the cell [A] + real :: alpha1, alpha2, alpha3 ! Normalized second derivative coefficients [A] + real :: rho ! A temporary expression [A2] + real :: sqrt_rho ! The square root of rho [A] + real :: gradient1, gradient2 ! Normalized gradients [A] + real :: x1, x2 ! Fractional inflection point positions in a cell [nondim] + real :: hNeglect ! A negligibly small width for the purpose of cell reconstructions [H] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect diff --git a/src/ALE/coord_zlike.F90 b/src/ALE/coord_zlike.F90 index f2ed7f0035..7f284217b2 100644 --- a/src/ALE/coord_zlike.F90 +++ b/src/ALE/coord_zlike.F90 @@ -67,13 +67,15 @@ subroutine build_zstar_column(CS, depth, total_thickness, zInterface, & !! output units), units may be [Z ~> m] or [H ~> m or kg m-2] real, intent(in) :: total_thickness !< Column thickness (positive definite in the same !! units as depth) [Z ~> m] or [H ~> m or kg m-2] - real, dimension(CS%nk+1), intent(inout) :: zInterface !< Absolute positions of interfaces + real, dimension(CS%nk+1), intent(inout) :: zInterface !< Absolute positions of interfaces (in the same + !! units as depth) [Z ~> m] or [H ~> m or kg m-2] real, optional, intent(in) :: z_rigid_top !< The height of a rigid top (positive upward in the same !! units as depth) [Z ~> m] or [H ~> m or kg m-2] - real, optional, intent(in) :: eta_orig !< The actual original height of the top in the same + real, optional, intent(in) :: eta_orig !< The actual original height of the top (in the same !! units as depth) [Z ~> m] or [H ~> m or kg m-2] real, optional, intent(in) :: zScale !< Scaling factor from the target coordinate resolution - !! in Z to desired units for zInterface, perhaps Z_to_H + !! in Z to desired units for zInterface, perhaps Z_to_H, + !! often [nondim] or [H Z-1 ~> 1 or kg m-3] ! Local variables real :: eta ! Free surface height [Z ~> m] or [H ~> m or kg m-2] real :: stretching ! A stretching factor for the coordinate [nondim] diff --git a/src/ALE/polynomial_functions.F90 b/src/ALE/polynomial_functions.F90 index e5c90fe31d..b01e097b83 100644 --- a/src/ALE/polynomial_functions.F90 +++ b/src/ALE/polynomial_functions.F90 @@ -9,7 +9,7 @@ module polynomial_functions contains -!> Pointwise evaluation of a polynomial at x +!> Pointwise evaluation of a polynomial in arbitrary units [A] at x !! !! The polynomial is defined by the coefficients contained in the !! array of the same name, as follows: C(1) + C(2)x + C(3)x^2 + C(4)x^3 + ... @@ -17,12 +17,14 @@ module polynomial_functions !! The number of coefficients is given by ncoef and x !! is the coordinate where the polynomial is to be evaluated. real function evaluation_polynomial( coeff, ncoef, x ) - real, dimension(:), intent(in) :: coeff !< The coefficients of the polynomial + real, dimension(:), intent(in) :: coeff !< The coefficients of the polynomial, in units that + !! vary with the index k as [A H^(k-1)] integer, intent(in) :: ncoef !< The number of polynomial coefficients real, intent(in) :: x !< The position at which to evaluate the polynomial + !! in arbitrary thickness units [H] ! Local variables integer :: k - real :: f ! value of polynomial at x + real :: f ! value of polynomial at x in arbitrary units [A] f = 0.0 do k = 1,ncoef @@ -33,7 +35,8 @@ real function evaluation_polynomial( coeff, ncoef, x ) end function evaluation_polynomial -!> Calculates the first derivative of a polynomial evaluated at a point x +!> Calculates the first derivative of a polynomial evaluated in arbitrary units of [A H-1] +!! at a point x !! !! The polynomial is defined by the coefficients contained in the !! array of the same name, as follows: C(1) + C(2)x + C(3)x^2 + C(4)x^3 + ... @@ -41,12 +44,14 @@ end function evaluation_polynomial !! The number of coefficients is given by ncoef and x !! is the coordinate where the polynomial's derivative is to be evaluated. real function first_derivative_polynomial( coeff, ncoef, x ) - real, dimension(:), intent(in) :: coeff !< The coefficients of the polynomial + real, dimension(:), intent(in) :: coeff !< The coefficients of the polynomial, in units that + !! vary with the index k as [A H^(k-1)] integer, intent(in) :: ncoef !< The number of polynomial coefficients real, intent(in) :: x !< The position at which to evaluate the derivative + !! in arbitrary thickness units [H] ! Local variables integer :: k - real :: f ! value of polynomial at x + real :: f ! value of the derivative at x in [A H-1] f = 0.0 do k = 2,ncoef @@ -57,17 +62,20 @@ real function first_derivative_polynomial( coeff, ncoef, x ) end function first_derivative_polynomial -!> Exact integration of polynomial of degree npoly +!> Exact integration of polynomial of degree npoly in arbitrary units of [A H] !! !! The array of coefficients (Coeff) must be of size npoly+1. real function integration_polynomial( xi0, xi1, Coeff, npoly ) - real, intent(in) :: xi0 !< The lower bound of the integral - real, intent(in) :: xi1 !< The lower bound of the integral - real, dimension(:), intent(in) :: Coeff !< The coefficients of the polynomial + real, intent(in) :: xi0 !< The lower bound of the integral in arbitrary + !! thickness units [H] + real, intent(in) :: xi1 !< The upper bound of the integral in arbitrary + !! thickness units [H] + real, dimension(:), intent(in) :: Coeff !< The coefficients of the polynomial, in units that + !! vary with the index k as [A H^(k-1)] integer, intent(in) :: npoly !< The degree of the polynomial ! Local variables - integer :: k - real :: integral + integer :: k + real :: integral ! The integral of the polynomial over the specified range in [A H] integral = 0.0 diff --git a/src/ALE/regrid_edge_values.F90 b/src/ALE/regrid_edge_values.F90 index 9b574348af..0814c6a907 100644 --- a/src/ALE/regrid_edge_values.F90 +++ b/src/ALE/regrid_edge_values.F90 @@ -27,7 +27,7 @@ module regrid_edge_values !! thickness for sum(h) in edge value inversions real, parameter :: hNeglect_dflt = 1.e-30 !< The default value for cut-off minimum !! thickness for sum(h) in other calculations -real, parameter :: hMinFrac = 1.e-5 !< A minimum fraction for min(h)/sum(h) +real, parameter :: hMinFrac = 1.e-5 !< A minimum fraction for min(h)/sum(h) [nondim] contains @@ -119,7 +119,7 @@ subroutine average_discontinuous_edge_values( N, edge_val ) !! second index is for the two edges of each cell. ! Local variables integer :: k ! loop index - real :: u0_avg ! avg value at given edge + real :: u0_avg ! avg value at given edge [A] ! Loop on interior edges do k = 1,N-1 @@ -231,19 +231,24 @@ subroutine edge_values_explicit_h4( N, h, u, edge_val, h_neglect, answer_date ) ! Local variables real :: h0, h1, h2, h3 ! temporary thicknesses [H] real :: h_min ! A minimal cell width [H] - real :: f1, f2, f3 ! auxiliary variables with various units + real :: f1 ! An auxiliary variable [H] + real :: f2 ! An auxiliary variable [A H] + real :: f3 ! An auxiliary variable [H-1] real :: et1, et2, et3 ! terms the expression for edge values [A H] real :: I_h12 ! The inverse of the sum of the two central thicknesses [H-1] real :: I_h012, I_h123 ! Inverses of sums of three successive thicknesses [H-1] real :: I_den_et2, I_den_et3 ! Inverses of denominators in edge value terms [H-2] - real, dimension(5) :: x ! Coordinate system with 0 at edges [H] - real, dimension(4) :: dz ! A temporary array of limited layer thicknesses [H] - real, dimension(4) :: u_tmp ! A temporary array of cell average properties [A] - real, parameter :: C1_12 = 1.0 / 12.0 - real :: dx ! Difference of successive values of x [H] - real, dimension(4,4) :: A ! values near the boundaries - real, dimension(4) :: B, C - real :: hNeglect ! A negligible thickness in the same units as h. + real, dimension(5) :: x ! Coordinate system with 0 at edges [H] + real, dimension(4) :: dz ! A temporary array of limited layer thicknesses [H] + real, dimension(4) :: u_tmp ! A temporary array of cell average properties [A] + real, parameter :: C1_12 = 1.0 / 12.0 ! A rational constant [nondim] + real :: dx ! Difference of successive values of x [H] + real, dimension(4,4) :: A ! Differences in successive positions raised to various powers, + ! in units that vary with the second (j) index as [H^j] + real, dimension(4) :: B ! The right hand side of the system to solve for C [A H] + real, dimension(4) :: C ! The coefficients of a fit polynomial in units that vary + ! with the index (j) as [A H^(j-1)] + real :: hNeglect ! A negligible thickness in the same units as h [H]. integer :: i, j logical :: use_2018_answers ! If true use older, less accurate expressions. @@ -383,11 +388,11 @@ subroutine edge_values_explicit_h4cw( N, h, u, edge_val, h_neglect ) ! Local variables real :: dp(N) ! Input grid layer thicknesses, but with a minimum thickness [H ~> m or kg m-2] - real :: hNeglect ! A negligible thickness in the same units as h + real :: hNeglect ! A negligible thickness in the same units as h [H] real :: da ! Difference between the unlimited scalar edge value estimates [A] real :: a6 ! Scalar field differences that are proportional to the curvature [A] real :: slk, srk ! Differences between adjacent cell averages of scalars [A] - real :: sck ! Scalar differences across a cell. + real :: sck ! Scalar differences across a cell [A] real :: au(N) ! Scalar field difference across each cell [A] real :: al(N), ar(N) ! Scalar field at the left and right edges of a cell [A] real :: h112(N+1), h122(N+1) ! Combinations of thicknesses [H ~> m or kg m-2] @@ -496,22 +501,26 @@ subroutine edge_values_implicit_h4( N, h, u, edge_val, h_neglect, answer_date ) integer :: i, j ! loop indexes real :: h0, h1 ! cell widths [H] real :: h_min ! A minimal cell width [H] - real :: h0_2, h1_2, h0h1 - real :: h0ph1_2, h0ph1_4 + real :: h0_2, h1_2, h0h1 ! Squares or products of thicknesses [H2] + real :: h0ph1_2 ! The square of a sum of thicknesses [H2] + real :: h0ph1_4 ! The fourth power of a sum of thicknesses [H4] real :: alpha, beta ! stencil coefficients [nondim] real :: I_h2, abmix ! stencil coefficients [nondim] - real :: a, b + real :: a, b ! Combinations of stencil coefficients [nondim] real, dimension(5) :: x ! Coordinate system with 0 at edges [H] - real, parameter :: C1_12 = 1.0 / 12.0 - real, parameter :: C1_3 = 1.0 / 3.0 + real, parameter :: C1_12 = 1.0 / 12.0 ! A rational constant [nondim] + real, parameter :: C1_3 = 1.0 / 3.0 ! A rational constant [nondim] real, dimension(4) :: dz ! A temporary array of limited layer thicknesses [H] real, dimension(4) :: u_tmp ! A temporary array of cell average properties [A] real :: dx ! Differences and averages of successive values of x [H] - real, dimension(4,4) :: Asys ! boundary conditions - real, dimension(4) :: Bsys, Csys + real, dimension(4,4) :: Asys ! Differences in successive positions raised to various powers, + ! in units that vary with the second (j) index as [H^j] + real, dimension(4) :: Bsys ! The right hand side of the system to solve for C [A H] + real, dimension(4) :: Csys ! The coefficients of a fit polynomial in units that vary + ! with the index (j) as [A H^(j-1)] real, dimension(N+1) :: tri_l, & ! tridiagonal system (lower diagonal) [nondim] tri_d, & ! tridiagonal system (middle diagonal) [nondim] - tri_c, & ! tridiagonal system central value, with tri_d = tri_c+tri_l+tri_u + tri_c, & ! tridiagonal system central value [nondim], with tri_d = tri_c+tri_l+tri_u tri_u, & ! tridiagonal system (upper diagonal) [nondim] tri_b, & ! tridiagonal system (right hand side) [A] tri_x ! tridiagonal system (solution vector) [A] @@ -667,7 +676,7 @@ subroutine end_value_h4(dz, u, Csys) real :: I_denom ! The inverse of the denominator some expressions [H-3] real :: I_denB3 ! The inverse of the product of three sums of thicknesses [H-3] real :: min_frac = 1.0e-6 ! The square of min_frac should be much larger than roundoff [nondim] - real, parameter :: C1_3 = 1.0 / 3.0 + real, parameter :: C1_3 = 1.0 / 3.0 ! A rational parameter [nondim] ! These are only used for code verification ! real, dimension(4) :: Atest ! The coefficients of an expression that is being tested. @@ -810,17 +819,21 @@ subroutine edge_slopes_implicit_h3( N, h, u, edge_slopes, h_neglect, answer_date real :: I_h ! Inverses of thicknesses [H-1] real :: alpha, beta ! stencil coefficients [nondim] real :: a, b ! weights of cells [H-1] - real, parameter :: C1_12 = 1.0 / 12.0 + real, parameter :: C1_12 = 1.0 / 12.0 ! A rational parameter [nondim] real, dimension(4) :: dz ! A temporary array of limited layer thicknesses [H] real, dimension(4) :: u_tmp ! A temporary array of cell average properties [A] - real, dimension(5) :: x ! Coordinate system with 0 at edges [H] - real :: dx ! Differences and averages of successive values of x [H] - real, dimension(4,4) :: Asys ! matrix used to find boundary conditions - real, dimension(4) :: Bsys, Csys - real, dimension(3) :: Dsys + real, dimension(5) :: x ! Coordinate system with 0 at edges [H] + real :: dx ! Differences and averages of successive values of x [H] + real, dimension(4,4) :: Asys ! Differences in successive positions raised to various powers, + ! in units that vary with the second (j) index as [H^j] + real, dimension(4) :: Bsys ! The right hand side of the system to solve for C [A H] + real, dimension(4) :: Csys ! The coefficients of a fit polynomial in units that vary with the + ! index (j) as [A H^(j-1)] + real, dimension(3) :: Dsys ! The coefficients of the first derivative of the fit polynomial + ! in units that vary with the index (j) as [A H^(j-2)] real, dimension(N+1) :: tri_l, & ! tridiagonal system (lower diagonal) [nondim] tri_d, & ! tridiagonal system (middle diagonal) [nondim] - tri_c, & ! tridiagonal system central value, with tri_d = tri_c+tri_l+tri_u + tri_c, & ! tridiagonal system central value [nondim], with tri_d = tri_c+tri_l+tri_u tri_u, & ! tridiagonal system (upper diagonal) [nondim] tri_b, & ! tridiagonal system (right hand side) [A H-1] tri_x ! tridiagonal system (solution vector) [A H-1] @@ -1009,23 +1022,27 @@ subroutine edge_slopes_implicit_h5( N, h, u, edge_slopes, h_neglect, answer_date real :: h01, h01_2 ! Summed thicknesses to various powers [H^n ~> m^n or kg^n m-2n] real :: h23, h23_2 ! Summed thicknesses to various powers [H^n ~> m^n or kg^n m-2n] real :: hNeglect ! A negligible thickness [H]. - real :: h1_2, h2_2 ! the coefficients of the - real :: h1_3, h2_3 ! tridiagonal system - real :: h1_4, h2_4 ! ... - real :: h1_5, h2_5 ! ... - real :: alpha, beta ! stencil coefficients - real, dimension(7) :: x ! Coordinate system with 0 at edges [same units as h] - real, parameter :: C1_12 = 1.0 / 12.0 - real, parameter :: C5_6 = 5.0 / 6.0 - real :: dx, xavg ! Differences and averages of successive values of x [same units as h] - real, dimension(6,6) :: Asys ! matrix used to find boundary conditions - real, dimension(6) :: Bsys, Csys ! ... - real, dimension(N+1) :: tri_l, & ! trid. system (lower diagonal) - tri_d, & ! trid. system (middle diagonal) - tri_u, & ! trid. system (upper diagonal) - tri_b, & ! trid. system (unknowns vector) - tri_x ! trid. system (rhs) - real :: h_Min_Frac = 1.0e-4 + real :: h1_2, h2_2 ! Squares of thicknesses [H2] + real :: h1_3, h2_3 ! Cubes of thicknesses [H3] + real :: h1_4, h2_4 ! Fourth powers of thicknesses [H4] + real :: h1_5, h2_5 ! Fifth powers of thicknesses [H5] + real :: alpha, beta ! stencil coefficients [nondim] + real, dimension(7) :: x ! Coordinate system with 0 at edges in the same units as h [H] + real, parameter :: C1_12 = 1.0 / 12.0 ! A rational parameter [nondim] + real, parameter :: C5_6 = 5.0 / 6.0 ! A rational parameter [nondim] + real :: dx, xavg ! Differences and averages of successive values of x [same units as h] + real, dimension(6,6) :: Asys ! The matrix that is being inverted for a solution, + ! in units that might vary with the second (j) index as [H^j] + real, dimension(6) :: Bsys ! The right hand side of the system to solve for C in various + ! units that sometimes vary with the intex (j) as [H^(j-1)] or [H^j] + ! or might be [A] + real, dimension(6) :: Csys ! The solution to a matrix equation usually [nondim] in this routine. + real, dimension(N+1) :: tri_l, & ! trid. system (lower diagonal) [nondim] + tri_d, & ! trid. system (middle diagonal) [nondim] + tri_u, & ! trid. system (upper diagonal) [nondim] + tri_b, & ! trid. system (rhs) [A H-1] + tri_x ! trid. system (unknowns vector) [A H-1] + real :: h_Min_Frac = 1.0e-4 ! A minimum fractional thickness [nondim] integer :: i, k ! loop indexes hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect @@ -1249,18 +1266,24 @@ subroutine edge_values_implicit_h6( N, h, u, edge_val, h_neglect, answer_date ) real :: hNeglect ! A negligible thickness [H]. real :: h1_2, h2_2, h1_3, h2_3 ! Cell widths raised to the 2nd and 3rd powers [H2] or [H3] real :: h1_4, h2_4, h1_5, h2_5 ! Cell widths raised to the 4th and 5th powers [H4] or [H5] - real :: alpha, beta ! stencil coefficients - real, dimension(7) :: x ! Coordinate system with 0 at edges [same units as h] - real, parameter :: C1_12 = 1.0 / 12.0 - real, parameter :: C5_6 = 5.0 / 6.0 - real :: dx, xavg ! Differences and averages of successive values of x [same units as h] - real, dimension(6,6) :: Asys ! boundary conditions - real, dimension(6) :: Bsys, Csys ! ... - real, dimension(N+1) :: tri_l, & ! trid. system (lower diagonal) - tri_d, & ! trid. system (middle diagonal) - tri_u, & ! trid. system (upper diagonal) - tri_b, & ! trid. system (unknowns vector) - tri_x ! trid. system (rhs) + real :: alpha, beta ! stencil coefficients [nondim] + real, dimension(7) :: x ! Coordinate system with 0 at edges in the same units as h [H] + real, parameter :: C1_12 = 1.0 / 12.0 ! A rational parameter [nondim] + real, parameter :: C5_6 = 5.0 / 6.0 ! A rational parameter [nondim] + real :: dx, xavg ! Differences and averages of successive values of x [H] + real, dimension(6,6) :: Asys ! The matrix that is being inverted for a solution, + ! in units that might vary with the second (j) index as [H^j] + real, dimension(6) :: Bsys ! The right hand side of the system to solve for C in various + ! units that sometimes vary with the intex (j) as [H^(j-1)] or [H^j] + ! or might be [A] + real, dimension(6) :: Csys ! The solution to a matrix equation, which might be [nondim] or the + ! coefficients of a fit polynomial in units that vary with the + ! index (j) as [A H^(j-1)] + real, dimension(N+1) :: tri_l, & ! trid. system (lower diagonal) [nondim] + tri_d, & ! trid. system (middle diagonal) [nondim] + tri_u, & ! trid. system (upper diagonal) [nondim] + tri_b, & ! trid. system (rhs) [A] + tri_x ! trid. system (unknowns vector) [A] integer :: i, k ! loop indexes hNeglect = hNeglect_edge_dflt ; if (present(h_neglect)) hNeglect = h_neglect @@ -1432,16 +1455,16 @@ end subroutine edge_values_implicit_h6 !> Test that A*C = R to within a tolerance, issuing a fatal error with an explanatory message if they do not. subroutine test_line(msg, N, A, C, R, mag, tol) - real, intent(in) :: mag !< The magnitude of leading order terms in this line - integer, intent(in) :: N !< The number of points in the system - real, dimension(4), intent(in) :: A !< One of the two vectors being multiplied - real, dimension(4), intent(in) :: C !< One of the two vectors being multiplied - real, intent(in) :: R !< The expected solution of the equation character(len=*), intent(in) :: msg !< An identifying message for this test - real, optional, intent(in) :: tol !< The fractional tolerance for the two solutions - - real :: sum, sum_mag - real :: tolerance + integer, intent(in) :: N !< The number of points in the system + real, dimension(4), intent(in) :: A !< One of the two vectors being multiplied in arbitrary units [A] + real, dimension(4), intent(in) :: C !< One of the two vectors being multiplied in arbitrary units [B] + real, intent(in) :: R !< The expected solution of the equation [A B] + real, intent(in) :: mag !< The magnitude of leading order terms in this line [A B] + real, optional, intent(in) :: tol !< The fractional tolerance for the sums [nondim] + + real :: sum, sum_mag ! The sum of the products and their magnitude in arbitrary units [A B] + real :: tolerance ! The fractional tolerance for the sums [nondim] character(len=128) :: mesg2 integer :: i diff --git a/src/ALE/regrid_solvers.F90 b/src/ALE/regrid_solvers.F90 index 0655d31062..6e5b3a0cb0 100644 --- a/src/ALE/regrid_solvers.F90 +++ b/src/ALE/regrid_solvers.F90 @@ -18,15 +18,19 @@ module regrid_solvers !! The matrix A must be square, with the first index varing down the column. subroutine solve_linear_system( A, R, X, N, answer_date ) integer, intent(in) :: N !< The size of the system - real, dimension(N,N), intent(inout) :: A !< The matrix being inverted [nondim] - real, dimension(N), intent(inout) :: R !< system right-hand side [A] - real, dimension(N), intent(inout) :: X !< solution vector [A] + real, dimension(N,N), intent(inout) :: A !< The matrix being inverted in arbitrary units [A] on + !! input, but internally modified to become nondimensional + !! during the solver. + real, dimension(N), intent(inout) :: R !< system right-hand side in arbitrary units [A B] on + !! input, but internally modified to have units of [B] + !! during the solver + real, dimension(N), intent(inout) :: X !< solution vector in arbitrary units [B] integer, optional, intent(in) :: answer_date !< The vintage of the expressions to use ! Local variables - real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed - real :: factor ! The factor that eliminates the leading nonzero element in a row. - real :: pivot, I_pivot ! The pivot value and its reciprocal [nondim] - real :: swap_a, swap_b + real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed [A] + real :: factor ! The factor that eliminates the leading nonzero element in a row [A-1] + real :: pivot, I_pivot ! The pivot value and its reciprocal, in [A] and [A-1] + real :: swap_a, swap_b ! Swap space in various units [various] logical :: found_pivot ! If true, a pivot has been found logical :: old_answers ! If true, use expressions that give the original (2008 through 2018) MOM6 answers integer :: i, j, k @@ -110,15 +114,18 @@ end subroutine solve_linear_system !! The matrix A must be square, with the first index varing along the row. subroutine linear_solver( N, A, R, X ) integer, intent(in) :: N !< The size of the system - real, dimension(N,N), intent(inout) :: A !< The matrix being inverted [nondim] - real, dimension(N), intent(inout) :: R !< system right-hand side [A] - real, dimension(N), intent(inout) :: X !< solution vector [A] + real, dimension(N,N), intent(inout) :: A !< The matrix being inverted in arbitrary units [A] on + !! input, but internally modified to become nondimensional + !! during the solver. + real, dimension(N), intent(inout) :: R !< system right-hand side in [A B] on input, but internally + !! modified to have units of [B] during the solver + real, dimension(N), intent(inout) :: X !< solution vector [B] ! Local variables - real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed - real :: factor ! The factor that eliminates the leading nonzero element in a row. - real :: I_pivot ! The reciprocal of the pivot value [inverse of the input units of a row of A] - real :: swap + real, parameter :: eps = 0.0 ! Minimum pivot magnitude allowed [A] + real :: factor ! The factor that eliminates the leading nonzero element in a row [A-1]. + real :: I_pivot ! The reciprocal of the pivot value [A-1] + real :: swap ! Swap space used in various units [various] integer :: i, j, k ! Loop on rows to transform the problem into multiplication by an upper-right matrix. @@ -175,16 +182,17 @@ end subroutine linear_solver !! (A is made up of lower, middle and upper diagonals) subroutine solve_tridiagonal_system( Al, Ad, Au, R, X, N, answer_date ) integer, intent(in) :: N !< The size of the system - real, dimension(N), intent(in) :: Ad !< Matrix center diagonal - real, dimension(N), intent(in) :: Al !< Matrix lower diagonal - real, dimension(N), intent(in) :: Au !< Matrix upper diagonal - real, dimension(N), intent(in) :: R !< system right-hand side - real, dimension(N), intent(out) :: X !< solution vector + real, dimension(N), intent(in) :: Ad !< Matrix center diagonal in arbitrary units [A] + real, dimension(N), intent(in) :: Al !< Matrix lower diagonal [A] + real, dimension(N), intent(in) :: Au !< Matrix upper diagonal [A] + real, dimension(N), intent(in) :: R !< system right-hand side in arbitrary units [A B] + real, dimension(N), intent(out) :: X !< solution vector in arbitrary units [B] integer, optional, intent(in) :: answer_date !< The vintage of the expressions to use ! Local variables - real, dimension(N) :: pivot, Al_piv - real, dimension(N) :: c1 ! Au / pivot for the backward sweep - real :: I_pivot ! The inverse of the most recent pivot + real, dimension(N) :: pivot ! The pivot value [A] + real, dimension(N) :: Al_piv ! The lower diagonal divided by the pivot value [nondim] + real, dimension(N) :: c1 ! Au / pivot for the backward sweep [nondim] + real :: I_pivot ! The inverse of the most recent pivot [A-1] integer :: k ! Loop index logical :: old_answers ! If true, use expressions that give the original (2008 through 2018) MOM6 answers @@ -237,16 +245,16 @@ end subroutine solve_tridiagonal_system !! roundoff compared with (Al+Au), the answers are prone to inaccuracy. subroutine solve_diag_dominant_tridiag( Al, Ac, Au, R, X, N ) integer, intent(in) :: N !< The size of the system - real, dimension(N), intent(in) :: Ac !< Matrix center diagonal offset from Al + Au - real, dimension(N), intent(in) :: Al !< Matrix lower diagonal - real, dimension(N), intent(in) :: Au !< Matrix upper diagonal - real, dimension(N), intent(in) :: R !< system right-hand side - real, dimension(N), intent(out) :: X !< solution vector + real, dimension(N), intent(in) :: Ac !< Matrix center diagonal offset from Al + Au in arbitrary units [A] + real, dimension(N), intent(in) :: Al !< Matrix lower diagonal [A] + real, dimension(N), intent(in) :: Au !< Matrix upper diagonal [A] + real, dimension(N), intent(in) :: R !< system right-hand side in arbitrary units [A B] + real, dimension(N), intent(out) :: X !< solution vector in arbitrary units [B] ! Local variables - real, dimension(N) :: c1 ! Au / pivot for the backward sweep - real :: d1 ! The next value of 1.0 - c1 - real :: I_pivot ! The inverse of the most recent pivot - real :: denom_t1 ! The first term in the denominator of the inverse of the pivot. + real, dimension(N) :: c1 ! Au / pivot for the backward sweep [nondim] + real :: d1 ! The next value of 1.0 - c1 [nondim] + real :: I_pivot ! The inverse of the most recent pivot [A-1] + real :: denom_t1 ! The first term in the denominator of the inverse of the pivot [A] integer :: k ! Loop index ! Factorization and forward sweep, in a form that will never give a division by a diff --git a/src/ALE/remapping_attic.F90 b/src/ALE/remapping_attic.F90 index 534428aaed..be20a27466 100644 --- a/src/ALE/remapping_attic.F90 +++ b/src/ALE/remapping_attic.F90 @@ -46,11 +46,13 @@ module remapping_attic function isPosSumErrSignificant(n1, sum1, n2, sum2) integer, intent(in) :: n1 !< Number of values in sum1 integer, intent(in) :: n2 !< Number of values in sum2 - real, intent(in) :: sum1 !< Sum of n1 values [A] + real, intent(in) :: sum1 !< Sum of n1 values in arbitrary units [A] real, intent(in) :: sum2 !< Sum of n2 values [A] logical :: isPosSumErrSignificant !< True if difference in sums is large ! Local variables - real :: sumErr, allowedErr, eps + real :: sumErr ! The absolutde difference in the sums [A] + real :: allowedErr ! The tolerance for the integrated reconstruction [A] + real :: eps ! A tiny fractional error [nondim] if (sum1<0.) call MOM_error(FATAL,'isPosSumErrSignificant: sum1<0 is not allowed!') if (sum2<0.) call MOM_error(FATAL,'isPosSumErrSignificant: sum2<0 is not allowed!') @@ -73,22 +75,22 @@ end function isPosSumErrSignificant subroutine remapByProjection( n0, h0, u0, ppoly0_E, ppoly0_coefs, & n1, h1, method, u1, h_neglect ) integer, intent(in) :: n0 !< Number of cells in source grid - real, intent(in) :: h0(:) !< Source grid widths (size n0) - real, intent(in) :: u0(:) !< Source cell averages (size n0) - real, intent(in) :: ppoly0_E(:,:) !< Edge value of polynomial - real, intent(in) :: ppoly0_coefs(:,:) !< Coefficients of polynomial + real, intent(in) :: h0(:) !< Source grid widths (size n0) in thickness units [H] + real, intent(in) :: u0(:) !< Source cell averages (size n0) in arbitrary units [A] + real, intent(in) :: ppoly0_E(:,:) !< Edge value of polynomial [A] + real, intent(in) :: ppoly0_coefs(:,:) !< Coefficients of polynomial [A] integer, intent(in) :: n1 !< Number of cells in target grid - real, intent(in) :: h1(:) !< Target grid widths (size n1) + real, intent(in) :: h1(:) !< Target grid widths (size n1) [H] integer, intent(in) :: method !< Remapping scheme to use - real, intent(out) :: u1(:) !< Target cell averages (size n1) + real, intent(out) :: u1(:) !< Target cell averages (size n1) [A] real, optional, intent(in) :: h_neglect !< A negligibly small width for the !! purpose of cell reconstructions - !! in the same units as h. + !! in the same units as h [H]. ! Local variables integer :: iTarget - real :: xL, xR ! coordinates of target cell edges + real :: xL, xR ! coordinates of target cell edges [H] integer :: jStart ! Used by integrateReconOnInterval() - real :: xStart ! Used by integrateReconOnInterval() + real :: xStart ! Used by integrateReconOnInterval() [H] ! Loop on cells in target grid (grid1). For each target cell, we need to find ! in which source cells the target cell edges lie. The associated indexes are @@ -120,27 +122,32 @@ end subroutine remapByProjection subroutine remapByDeltaZ( n0, h0, u0, ppoly0_E, ppoly0_coefs, n1, dx1, & method, u1, h1, h_neglect ) integer, intent(in) :: n0 !< Number of cells in source grid - real, dimension(:), intent(in) :: h0 !< Source grid sizes (size n0) - real, dimension(:), intent(in) :: u0 !< Source cell averages (size n0) - real, dimension(:,:), intent(in) :: ppoly0_E !< Edge value of polynomial - real, dimension(:,:), intent(in) :: ppoly0_coefs !< Coefficients of polynomial + real, dimension(:), intent(in) :: h0 !< Source grid sizes (size n0) in thickness units [H] + real, dimension(:), intent(in) :: u0 !< Source cell averages (size n0) in arbitrary units [A] + real, dimension(:,:), intent(in) :: ppoly0_E !< Edge value of polynomial [A] + real, dimension(:,:), intent(in) :: ppoly0_coefs !< Coefficients of polynomial [A] integer, intent(in) :: n1 !< Number of cells in target grid - real, dimension(:), intent(in) :: dx1 !< Target grid edge positions (size n1+1) + real, dimension(:), intent(in) :: dx1 !< Target grid edge positions (size n1+1) [H] integer, intent(in) :: method !< Remapping scheme to use - real, dimension(:), intent(out) :: u1 !< Target cell averages (size n1) + real, dimension(:), intent(out) :: u1 !< Target cell averages (size n1) [A] real, dimension(:), & - optional, intent(out) :: h1 !< Target grid widths (size n1) + optional, intent(out) :: h1 !< Target grid widths (size n1) [H] real, optional, intent(in) :: h_neglect !< A negligibly small width for the !! purpose of cell reconstructions - !! in the same units as h. + !! in the same units as h [H]. ! Local variables integer :: iTarget - real :: xL, xR ! coordinates of target cell edges - real :: xOld, hOld, uOld - real :: xNew, hNew, h_err - real :: uhNew, hFlux, uAve, fluxL, fluxR + real :: xL, xR ! Coordinates of target cell edges [H] + real :: xOld, xNew ! Edge positions on the old and new grids [H] + real :: hOld, hNew ! Cell thicknesses on the old and new grids [H] + real :: uOld ! A source cell average of u [A] + real :: h_err ! An estimate of the error in the reconstructed thicknesses [H] + real :: uhNew ! Cell integrated u on the new grid [A H] + real :: hFlux ! Width of the remapped volume [H] + real :: uAve ! Target cell average of u [A] + real :: fluxL, fluxR ! Fluxes of u through the two cell faces [A H] integer :: jStart ! Used by integrateReconOnInterval() - real :: xStart ! Used by integrateReconOnInterval() + real :: xStart ! Used by integrateReconOnInterval() [H] ! Loop on cells in target grid. For each cell, iTarget, the left flux is ! the right flux of the cell to the left, iTarget-1. @@ -198,36 +205,35 @@ end subroutine remapByDeltaZ subroutine integrateReconOnInterval( n0, h0, u0, ppoly0_E, ppoly0_coefs, method, & xL, xR, hC, uAve, jStart, xStart, h_neglect ) integer, intent(in) :: n0 !< Number of cells in source grid - real, dimension(:), intent(in) :: h0 !< Source grid sizes (size n0) - real, dimension(:), intent(in) :: u0 !< Source cell averages - real, dimension(:,:), intent(in) :: ppoly0_E !< Edge value of polynomial - real, dimension(:,:), intent(in) :: ppoly0_coefs !< Coefficients of polynomial + real, dimension(:), intent(in) :: h0 !< Source grid sizes (size n0) in thickness units [H] + real, dimension(:), intent(in) :: u0 !< Source cell averages in arbitrary units [A] + real, dimension(:,:), intent(in) :: ppoly0_E !< Edge value of polynomial [A] + real, dimension(:,:), intent(in) :: ppoly0_coefs !< Coefficients of polynomial [A] integer, intent(in) :: method !< Remapping scheme to use - real, intent(in) :: xL !< Left edges of target cell - real, intent(in) :: xR !< Right edges of target cell - real, intent(in) :: hC !< Cell width hC = xR - xL - real, intent(out) :: uAve !< Average value on target cell + real, intent(in) :: xL !< Left edges of target cell [H] + real, intent(in) :: xR !< Right edges of target cell [H] + real, intent(in) :: hC !< Cell width hC = xR - xL [H] + real, intent(out) :: uAve !< Average value on target cell [A] integer, intent(inout) :: jStart !< The index of the cell to start searching from !< On exit, contains index of last cell used - real, intent(inout) :: xStart !< The left edge position of cell jStart + real, intent(inout) :: xStart !< The left edge position of cell jStart [H] !< On first entry should be 0. real, optional, intent(in) :: h_neglect !< A negligibly small width for the !! purpose of cell reconstructions - !! in the same units as h + !! in the same units as h [H] ! Local variables integer :: j, k - integer :: jL, jR ! indexes of source cells containing target - ! cell edges - real :: q ! complete integration - real :: xi0, xi1 ! interval of integration (local -- normalized - ! -- coordinates) - real :: x0jLl, x0jLr ! Left/right position of cell jL - real :: x0jRl, x0jRr ! Left/right position of cell jR + integer :: jL, jR ! indexes of source cells containing target cell edges + real :: q ! complete integration [A H] + real :: xi0, xi1 ! interval of integration (local -- normalized -- coordinates) [nondim] + real :: x0jLl, x0jLr ! Left/right position of cell jL [H] + real :: x0jRl, x0jRr ! Left/right position of cell jR [H] real :: hAct ! The distance actually used in the integration - ! (notionally xR - xL) which differs due to roundoff. - real :: x0_2, x1_2, x02px12, x0px1 ! Used in evaluation of integrated polynomials - real :: hNeglect ! A negligible thickness in the same units as h - real, parameter :: r_3 = 1.0/3.0 ! Used in evaluation of integrated polynomials + ! (notionally xR - xL) which differs due to roundoff [H]. + real :: x0_2, x1_2 ! Squares of normalized positions used to evaluate polynomials [nondim] + real :: x0px1, x02px12 ! Sums of normalized positions and their squares [nondim] + real :: hNeglect ! A negligible thickness in the same units as h [H] + real, parameter :: r_3 = 1.0/3.0 ! Used in evaluation of integrated polynomials [nondim] hNeglect = hNeglect_dflt ; if (present(h_neglect)) hNeglect = h_neglect @@ -281,7 +287,7 @@ subroutine integrateReconOnInterval( n0, h0, u0, ppoly0_E, ppoly0_coefs, method, if ( h0(jL) == 0.0 ) then uAve = 0.5 * ( ppoly0_E(jL,1) + ppoly0_E(jL,2) ) else - !### WHY IS THIS NOT WRITTEN AS xi0 = ( xL - x0jLl ) / h0(jL) ---AJA + ! WHY IS THIS NOT WRITTEN AS xi0 = ( xL - x0jLl ) / h0(jL) ---AJA xi0 = xL / ( h0(jL) + hNeglect ) - x0jLl / ( h0(jL) + hNeglect ) select case ( method ) @@ -540,17 +546,24 @@ logical function remapping_attic_unit_tests(verbose) logical, intent(in) :: verbose !< If true, write results to stdout ! Local variables integer, parameter :: n0 = 4, n1 = 3, n2 = 6 - real :: h0(n0), x0(n0+1), u0(n0) - real :: h1(n1), x1(n1+1), u1(n1), hn1(n1), dx1(n1+1) - real :: h2(n2), x2(n2+1), u2(n2), hn2(n2), dx2(n2+1) + real :: h0(n0), x0(n0+1) ! Test cell widths and edge coordinates [H] + real :: u0(n0) ! Test values for remapping in arbitrary units [A] + real :: h1(n1), x1(n1+1) ! Test cell widths and edge coordinates [H] + real :: u1(n1) ! Test values for remapping [A] + real :: h2(n2), x2(n2+1) ! Test cell widths and edge coordinates [H] + real :: u2(n2) ! Test values for remapping [A] + real :: hn1(n1), hn2(n2) ! Updated grid thicknesses [H] + real :: dx1(n1+1), dx2(n2+1) ! Differences in interface positions [H] data u0 /9., 3., -3., -9./ ! Linear profile, 4 at surface to -4 at bottom data h0 /4*0.75/ ! 4 uniform layers with total depth of 3 data h1 /3*1./ ! 3 uniform layers with total depth of 3 data h2 /6*0.5/ ! 6 uniform layers with total depth of 3 - real, allocatable, dimension(:,:) :: ppoly0_E, ppoly0_S, ppoly0_coefs + real, allocatable, dimension(:,:) :: ppoly0_E, ppoly0_S ! Polynomial edge values [A] + real, allocatable, dimension(:,:) :: ppoly0_coefs ! Polynomial reconstruction coefficients [A] integer :: answer_date ! The vintage of the expressions to test integer :: i, degree - real :: err, h_neglect, h_neglect_edge + real :: err ! Difference between a remapped value and its expected value [A] + real :: h_neglect, h_neglect_edge ! Negligible thicknesses used in remapping [H] logical :: thisTest, v v = verbose diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 53f20bb10b..595b730e0f 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -2904,6 +2904,11 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & if (CS%rotate_index) then G_in%ke = GV%ke + ! Allocate the auxiliary non-symmetric domain for debugging or I/O purposes. + if (CS%debug .or. G_in%symmetric) then + call clone_MOM_domain(G_in%Domain, G_in%Domain_aux, symmetric=.false.) + else ; G_in%Domain_aux => G_in%Domain ; endif + allocate(u_in(G_in%IsdB:G_in%IedB, G_in%jsd:G_in%jed, nz), source=0.0) allocate(v_in(G_in%isd:G_in%ied, G_in%JsdB:G_in%JedB, nz), source=0.0) allocate(h_in(G_in%isd:G_in%ied, G_in%jsd:G_in%jed, nz), source=GV%Angstrom_H) @@ -3117,6 +3122,29 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & endif if ( CS%use_ALE_algorithm ) call ALE_updateVerticalGridType( CS%ALE_CSp, GV ) + ! The basic state variables have now been fully initialized, so update their halos and + ! calculate any derived thermodynmics quantities. + + !--- set up group pass for u,v,T,S and h. pass_uv_T_S_h also is used in step_MOM + call cpu_clock_begin(id_clock_pass_init) + dynamics_stencil = min(3, G%Domain%nihalo, G%Domain%njhalo) + call create_group_pass(pass_uv_T_S_h, CS%u, CS%v, G%Domain, halo=dynamics_stencil) + if (use_temperature) then + call create_group_pass(pass_uv_T_S_h, CS%tv%T, G%Domain, halo=dynamics_stencil) + call create_group_pass(pass_uv_T_S_h, CS%tv%S, G%Domain, halo=dynamics_stencil) + endif + call create_group_pass(pass_uv_T_S_h, CS%h, G%Domain, halo=dynamics_stencil) + + call do_group_pass(pass_uv_T_S_h, G%Domain) + if (associated(CS%tv%p_surf)) call pass_var(CS%tv%p_surf, G%Domain, halo=dynamics_stencil) + call cpu_clock_end(id_clock_pass_init) + + ! Update derived thermodynamic quantities. + if (allocated(CS%tv%SpV_avg)) then + call calc_derived_thermo(CS%tv, CS%h, G, GV, US, halo=dynamics_stencil, debug=CS%debug) + endif + + diag => CS%diag ! Initialize the diag mediator. call diag_mediator_init(G, GV, US, GV%ke, param_file, diag, doc_file_dir=dirs%output_directory) @@ -3129,15 +3157,15 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & ! Set up pointers within diag mediator control structure, ! this needs to occur _after_ CS%h etc. have been allocated. - call diag_set_state_ptrs(CS%h, CS%T, CS%S, CS%tv%eqn_of_state, diag) + call diag_set_state_ptrs(CS%h, CS%tv, diag) ! This call sets up the diagnostic axes. These are needed, ! e.g. to generate the target grids below. call set_axes_info(G, GV, US, param_file, diag) ! Whenever thickness/T/S changes let the diag manager know, target grids - ! for vertical remapping may need to be regenerated. - ! FIXME: are h, T, S updated at the same time? Review these for T, S updates. + ! for vertical remapping may need to be regenerated. In non-Boussinesq mode, + ! calc_derived_thermo needs to be called before diag_update_remap_grids. call diag_update_remap_grids(diag) ! Setup the diagnostic grid storage types @@ -3287,30 +3315,13 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call ALE_register_diags(Time, G, GV, US, diag, CS%ALE_CSp) endif - !--- set up group pass for u,v,T,S and h. pass_uv_T_S_h also is used in step_MOM + ! Do any necessary halo updates on any auxiliary variables that have been initialized. call cpu_clock_begin(id_clock_pass_init) - dynamics_stencil = min(3, G%Domain%nihalo, G%Domain%njhalo) - call create_group_pass(pass_uv_T_S_h, CS%u, CS%v, G%Domain, halo=dynamics_stencil) - if (use_temperature) then - call create_group_pass(pass_uv_T_S_h, CS%tv%T, G%Domain, halo=dynamics_stencil) - call create_group_pass(pass_uv_T_S_h, CS%tv%S, G%Domain, halo=dynamics_stencil) - endif - call create_group_pass(pass_uv_T_S_h, CS%h, G%Domain, halo=dynamics_stencil) - - call do_group_pass(pass_uv_T_S_h, G%Domain) - - ! Update derived thermodynamic quantities. - if (associated(CS%tv%p_surf)) call pass_var(CS%tv%p_surf, G%Domain, halo=dynamics_stencil) - if (allocated(CS%tv%SpV_avg)) then - call calc_derived_thermo(CS%tv, CS%h, G, GV, US, halo=dynamics_stencil, debug=CS%debug) - endif - if (associated(CS%visc%Kv_shear)) & call pass_var(CS%visc%Kv_shear, G%Domain, To_All+Omit_Corners, halo=1) if (associated(CS%visc%Kv_slow)) & call pass_var(CS%visc%Kv_slow, G%Domain, To_All+Omit_Corners, halo=1) - call cpu_clock_end(id_clock_pass_init) ! This subroutine initializes any tracer packages. diff --git a/src/core/MOM_dynamics_split_RK2.F90 b/src/core/MOM_dynamics_split_RK2.F90 index 11b1eb5c16..0557ec7cd5 100644 --- a/src/core/MOM_dynamics_split_RK2.F90 +++ b/src/core/MOM_dynamics_split_RK2.F90 @@ -851,7 +851,7 @@ subroutine step_MOM_dyn_split_RK2(u_inst, v_inst, h, tv, visc, Time_local, dt, f call horizontal_viscosity(u_av, v_av, h_av, CS%diffu, CS%diffv, & MEKE, Varmix, G, GV, US, CS%hor_visc, & OBC=CS%OBC, BT=CS%barotropic_CSp, TD=thickness_diffuse_CSp, & - ADp=CS%ADp) + ADp=CS%ADp, hu_cont=CS%BT_cont%h_u, hv_cont=CS%BT_cont%h_v) call cpu_clock_end(id_clock_horvisc) if (showCallTree) call callTree_wayPoint("done with horizontal_viscosity (step_MOM_dyn_split_RK2)") @@ -1518,7 +1518,8 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param if (.not. query_initialized(CS%diffu, "diffu", restart_CS) .or. & .not. query_initialized(CS%diffv, "diffv", restart_CS)) then call horizontal_viscosity(u, v, h, CS%diffu, CS%diffv, MEKE, VarMix, G, GV, US, CS%hor_visc, & - OBC=CS%OBC, BT=CS%barotropic_CSp, TD=thickness_diffuse_CSp) + OBC=CS%OBC, BT=CS%barotropic_CSp, TD=thickness_diffuse_CSp, & + hu_cont=CS%BT_cont%h_u, hv_cont=CS%BT_cont%h_v) call set_initialized(CS%diffu, "diffu", restart_CS) call set_initialized(CS%diffv, "diffv", restart_CS) endif diff --git a/src/core/MOM_forcing_type.F90 b/src/core/MOM_forcing_type.F90 index 98cfdb3f17..452161c6ca 100644 --- a/src/core/MOM_forcing_type.F90 +++ b/src/core/MOM_forcing_type.F90 @@ -6,6 +6,7 @@ module MOM_forcing_type use MOM_array_transform, only : rotate_array, rotate_vector, rotate_array_pair use MOM_coupler_types, only : coupler_2d_bc_type, coupler_type_destructor use MOM_coupler_types, only : coupler_type_increment_data, coupler_type_initialized +use MOM_coupler_types, only : coupler_type_copy_data use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_ROUTINE use MOM_debugging, only : hchksum, uvchksum use MOM_diag_mediator, only : post_data, register_diag_field, register_scalar_field @@ -3702,9 +3703,11 @@ subroutine rotate_forcing(fluxes_in, fluxes, turns) if (associated(fluxes_in%ustar_tidal)) & call rotate_array(fluxes_in%ustar_tidal, turns, fluxes%ustar_tidal) - ! TODO: tracer flux rotation - if (coupler_type_initialized(fluxes%tr_fluxes)) & - call MOM_error(FATAL, "Rotation of tracer BC fluxes not yet implemented.") + ! NOTE: Tracer fields are handled by FMS, so are left unrotated. Any + ! reads/writes to tr_fields must be appropriately rotated. + if (coupler_type_initialized(fluxes%tr_fluxes)) then + call coupler_type_copy_data(fluxes_in%tr_fluxes, fluxes%tr_fluxes) + endif ! Scalars and flags fluxes%accumulate_p_surf = fluxes_in%accumulate_p_surf @@ -3757,10 +3760,18 @@ subroutine rotate_mech_forcing(forces_in, turns, forces) endif if (do_press) then - ! NOTE: p_surf_SSH either points to p_surf or p_surf_full call rotate_array(forces_in%p_surf, turns, forces%p_surf) call rotate_array(forces_in%p_surf_full, turns, forces%p_surf_full) call rotate_array(forces_in%net_mass_src, turns, forces%net_mass_src) + + ! p_surf_SSH points to either p_surf or p_surf_full + if (associated(forces_in%p_surf_SSH, forces_in%p_surf)) then + forces%p_surf_SSH => forces%p_surf + else if (associated(forces_in%p_surf_SSH, forces_in%p_surf_full)) then + forces%p_surf_SSH => forces%p_surf_full + else + forces%p_surf_SSH => null() + endif endif if (do_iceberg) then diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index e212581993..8f872ceb15 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -168,9 +168,10 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) if (CS%debug) then call uvchksum("Interface height used by porous barrier for layer weights", & - eta_u, eta_v, G%HI, haloshift=0) + eta_u, eta_v, G%HI, haloshift=0, scalar_pair=.true.) call uvchksum("Porous barrier layer-averaged weights: por_face_area[UV]", & - pbv%por_face_areaU, pbv%por_face_areaV, G%HI, haloshift=0) + pbv%por_face_areaU, pbv%por_face_areaV, G%HI, haloshift=0, & + scalar_pair=.true.) endif if (CS%id_por_face_areaU > 0) call post_data(CS%id_por_face_areaU, pbv%por_face_areaU, CS%diag) @@ -256,9 +257,10 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) if (CS%debug) then call uvchksum("Interface height used by porous barrier for interface weights", & - eta_u, eta_v, G%HI, haloshift=0) + eta_u, eta_v, G%HI, haloshift=0, scalar_pair=.true.) call uvchksum("Porous barrier weights at the layer-interface: por_layer_width[UV]", & - pbv%por_layer_widthU, pbv%por_layer_widthV, G%HI, haloshift=0) + pbv%por_layer_widthU, pbv%por_layer_widthV, G%HI, & + haloshift=0, scalar_pair=.true.) endif if (CS%id_por_layer_widthU > 0) call post_data(CS%id_por_layer_widthU, pbv%por_layer_widthU, CS%diag) diff --git a/src/core/MOM_variables.F90 b/src/core/MOM_variables.F90 index 0eab1a5b17..cb20837d3b 100644 --- a/src/core/MOM_variables.F90 +++ b/src/core/MOM_variables.F90 @@ -6,6 +6,7 @@ module MOM_variables use MOM_array_transform, only : rotate_array, rotate_vector use MOM_coupler_types, only : coupler_1d_bc_type, coupler_2d_bc_type use MOM_coupler_types, only : coupler_type_spawn, coupler_type_destructor, coupler_type_initialized +use MOM_coupler_types, only : coupler_type_copy_data use MOM_debugging, only : hchksum use MOM_domains, only : MOM_domain_type, get_domain_extent, group_pass_type use MOM_EOS, only : EOS_type @@ -499,9 +500,11 @@ subroutine rotate_surface_state(sfc_state_in, sfc_state, G, turns) sfc_state%T_is_conT = sfc_state_in%T_is_conT sfc_state%S_is_absS = sfc_state_in%S_is_absS - ! TODO: tracer field rotation - if (coupler_type_initialized(sfc_state_in%tr_fields)) & - call MOM_error(FATAL, "Rotation of surface state tracers is not yet implemented.") + ! NOTE: Tracer fields are handled by FMS, so are left unrotated. Any + ! reads/writes to tr_fields must be appropriately rotated. + if (coupler_type_initialized(sfc_state_in%tr_fields)) then + call coupler_type_copy_data(sfc_state_in%tr_fields, sfc_state%tr_fields) + endif end subroutine rotate_surface_state !> Allocates the arrays contained within a BT_cont_type and initializes them to 0. diff --git a/src/framework/MOM_diag_mediator.F90 b/src/framework/MOM_diag_mediator.F90 index 2c71a93e42..eeb239859d 100644 --- a/src/framework/MOM_diag_mediator.F90 +++ b/src/framework/MOM_diag_mediator.F90 @@ -21,15 +21,18 @@ module MOM_diag_mediator use MOM_diag_remap, only : diag_remap_configure_axes, diag_remap_axes_configured use MOM_diag_remap, only : diag_remap_diag_registration_closed, diag_remap_set_active use MOM_EOS, only : EOS_type -use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe, assert +use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe, assert, callTree_showQuery +use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint use MOM_file_parser, only : get_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type +use MOM_interface_heights, only : thickness_to_dz use MOM_io, only : slasher, vardesc, query_vardesc, MOM_read_data use MOM_io, only : get_filename_appendix use MOM_safe_alloc, only : safe_alloc_ptr, safe_alloc_alloc use MOM_string_functions, only : lowercase use MOM_time_manager, only : time_type use MOM_unit_scaling, only : unit_scale_type +use MOM_variables, only : thermo_var_ptrs use MOM_verticalGrid, only : verticalGrid_type implicit none ; private @@ -134,7 +137,7 @@ module MOM_diag_mediator !> Contains an array to store a diagnostic target grid type, private :: diag_grids_type - real, dimension(:,:,:), allocatable :: h !< Target grid for remapped coordinate + real, dimension(:,:,:), allocatable :: h !< Target grid for remapped coordinate [H ~> m or kg m-2] or [Z ~> m] end type diag_grids_type !> Stores all the remapping grids and the model's native space thicknesses @@ -238,6 +241,7 @@ module MOM_diag_mediator integer :: chksum_iounit = -1 !< The unit number of a diagnostic documentation file. !! This file is open if available_diag_doc_unit is > 0. logical :: diag_as_chksum !< If true, log chksums in a text file instead of posting diagnostics + logical :: show_call_tree !< Display the call tree while running. Set by VERBOSITY level. logical :: grid_space_axes !< If true, diagnostic horizontal coordinates axes are in grid space. ! The following fields are used for the output of the data. integer :: is !< The start i-index of cell centers within the computational domain @@ -311,7 +315,9 @@ module MOM_diag_mediator real, dimension(:,:,:), pointer :: h => null() !< The thicknesses needed for remapping [H ~> m or kg m-2] real, dimension(:,:,:), pointer :: T => null() !< The temperatures needed for remapping [C ~> degC] real, dimension(:,:,:), pointer :: S => null() !< The salinities needed for remapping [S ~> ppt] - type(EOS_type), pointer :: eqn_of_state => null() !< The equation of state type + type(EOS_type), pointer :: eqn_of_state => null() !< The equation of state type + type(thermo_var_ptrs), pointer :: tv => null() !< A sturcture with thermodynamic variables that are + !! are used to convert thicknesses to vertical extents type(ocean_grid_type), pointer :: G => null() !< The ocean grid type type(verticalGrid_type), pointer :: GV => null() !< The model's vertical ocean grid type(unit_scale_type), pointer :: US => null() !< A dimensional unit scaling type @@ -329,7 +335,7 @@ module MOM_diag_mediator integer :: num_chksum_diags real, dimension(:,:,:), allocatable :: h_begin !< Layer thicknesses at the beginning of the timestep used - !! for remapping of extensive variables + !! for remapping of extensive variables [H ~> m or kg m-2] end type diag_ctrl @@ -866,43 +872,51 @@ subroutine set_masks_for_axes_dsamp(G, diag_cs) do c=1, diag_cs%num_diag_coords ! Level/layer h-points in diagnostic coordinate axes => diag_cs%remap_axesTL(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTL(c)%dsamp(dl)%mask3d, dl,G%isc, G%jsc, & + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTL(c)%dsamp(dl)%mask3d, & + dl, G%isc, G%jsc, G%isd, G%jsd, & G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed) diag_cs%dsamp(dl)%remap_axesTL(c)%mask3d => axes%mask3d !set non-downsampled mask ! Level/layer u-points in diagnostic coordinate axes => diag_cs%remap_axesCuL(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCuL(c)%dsamp(dl)%mask3d, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%jsc, G%HId2%jec,G%HId2%IsdB,G%HId2%IedB,G%HId2%jsd, G%HId2%jed) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCuL(c)%dsamp(dl)%mask3d, & + dl, G%IscB, G%jsc, G%IsdB, G%jsd, & + G%HId2%IscB, G%HId2%IecB, G%HId2%jsc, G%HId2%jec, G%HId2%IsdB, G%HId2%IedB, G%HId2%jsd, G%HId2%jed) diag_cs%dsamp(dl)%remap_axesCul(c)%mask3d => axes%mask3d !set non-downsampled mask ! Level/layer v-points in diagnostic coordinate axes => diag_cs%remap_axesCvL(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvL(c)%dsamp(dl)%mask3d, dl,G%isc ,G%JscB, & - G%HId2%isc ,G%HId2%iec, G%HId2%JscB,G%HId2%JecB,G%HId2%isd ,G%HId2%ied, G%HId2%JsdB,G%HId2%JedB) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvL(c)%dsamp(dl)%mask3d, & + dl, G%isc, G%JscB, G%isd, G%JsdB, & + G%HId2%isc, G%HId2%iec, G%HId2%JscB, G%HId2%JecB, G%HId2%isd, G%HId2%ied, G%HId2%JsdB, G%HId2%JedB) diag_cs%dsamp(dl)%remap_axesCvL(c)%mask3d => axes%mask3d !set non-downsampled mask ! Level/layer q-points in diagnostic coordinate axes => diag_cs%remap_axesBL(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBL(c)%dsamp(dl)%mask3d, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%JscB,G%HId2%JecB,G%HId2%IsdB,G%HId2%IedB,G%HId2%JsdB,G%HId2%JedB) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBL(c)%dsamp(dl)%mask3d, & + dl, G%IscB, G%JscB, G%IsdB, G%JsdB, & + G%HId2%IscB, G%HId2%IecB, G%HId2%JscB, G%HId2%JecB, G%HId2%IsdB, G%HId2%IedB, G%HId2%JsdB, G%HId2%JedB) diag_cs%dsamp(dl)%remap_axesBL(c)%mask3d => axes%mask3d !set non-downsampled mask ! Interface h-points in diagnostic coordinate (w-point) axes => diag_cs%remap_axesTi(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTi(c)%dsamp(dl)%mask3d, dl,G%isc, G%jsc, & + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesTi(c)%dsamp(dl)%mask3d, & + dl, G%isc, G%jsc, G%isd, G%jsd, & G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed) diag_cs%dsamp(dl)%remap_axesTi(c)%mask3d => axes%mask3d !set non-downsampled mask ! Interface u-points in diagnostic coordinate axes => diag_cs%remap_axesCui(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCui(c)%dsamp(dl)%mask3d, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%jsc, G%HId2%jec,G%HId2%IsdB,G%HId2%IedB,G%HId2%jsd, G%HId2%jed) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCui(c)%dsamp(dl)%mask3d, & + dl, G%IscB, G%jsc, G%IsdB, G%jsd, & + G%HId2%IscB, G%HId2%IecB, G%HId2%jsc, G%HId2%jec, G%HId2%IsdB, G%HId2%IedB, G%HId2%jsd, G%HId2%jed) diag_cs%dsamp(dl)%remap_axesCui(c)%mask3d => axes%mask3d !set non-downsampled mask ! Interface v-points in diagnostic coordinate axes => diag_cs%remap_axesCvi(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvi(c)%dsamp(dl)%mask3d, dl,G%isc ,G%JscB, & - G%HId2%isc ,G%HId2%iec, G%HId2%JscB,G%HId2%JecB,G%HId2%isd ,G%HId2%ied, G%HId2%JsdB,G%HId2%JedB) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesCvi(c)%dsamp(dl)%mask3d, & + dl, G%isc, G%JscB, G%isd, G%JsdB, & + G%HId2%isc, G%HId2%iec, G%HId2%JscB, G%HId2%JecB, G%HId2%isd, G%HId2%ied, G%HId2%JsdB, G%HId2%JedB) diag_cs%dsamp(dl)%remap_axesCvi(c)%mask3d => axes%mask3d !set non-downsampled mask ! Interface q-points in diagnostic coordinate axes => diag_cs%remap_axesBi(c) - call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBi(c)%dsamp(dl)%mask3d, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%JscB,G%HId2%JecB,G%HId2%IsdB,G%HId2%IedB,G%HId2%JsdB,G%HId2%JedB) + call downsample_mask(axes%mask3d, diag_cs%dsamp(dl)%remap_axesBi(c)%dsamp(dl)%mask3d, & + dl, G%IscB, G%JscB, G%IsdB, G%JsdB, & + G%HId2%IscB, G%HId2%IecB, G%HId2%JscB, G%HId2%JecB, G%HId2%IsdB, G%HId2%IedB, G%HId2%JsdB, G%HId2%JedB) diag_cs%dsamp(dl)%remap_axesBi(c)%mask3d => axes%mask3d !set non-downsampled mask enddo enddo @@ -1525,13 +1539,15 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) ! Local variables type(diag_type), pointer :: diag => null() real, dimension(:,:,:), allocatable :: remapped_field - logical :: staggered_in_x, staggered_in_y + logical :: staggered_in_x, staggered_in_y, dz_diag_needed, dz_begin_needed real, dimension(:,:,:), pointer :: h_diag => NULL() + real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: & + dz_diag, & ! Layer vertical extents for remapping [Z ~> m] + dz_begin ! Layer vertical extents for remapping extensive quantities [Z ~> m] if (id_clock_diag_mediator>0) call cpu_clock_begin(id_clock_diag_mediator) - ! For intensive variables only, we can choose to use a different diagnostic grid - ! to map to + ! For intensive variables only, we can choose to use a different diagnostic grid to map to if (present(alt_h)) then h_diag => alt_h else @@ -1542,6 +1558,32 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) ! grids, and post each. call assert(diag_field_id < diag_cs%next_free_diag_id, & 'post_data_3d: Unregistered diagnostic id') + + if (diag_cs%show_call_tree) & + call callTree_enter("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")") + + ! Find out whether there are any z-based diagnostics + diag => diag_cs%diags(diag_field_id) + dz_diag_needed = .false. ; dz_begin_needed = .false. + do while (associated(diag)) + if (diag%v_extensive .and. .not.diag%axes%is_native) then + if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) & + dz_begin_needed = .true. + elseif (diag%axes%needs_remapping .or. diag%axes%needs_interpolating) then + if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) & + dz_diag_needed = .true. + endif + diag => diag%next + enddo + + ! Determine the diagnostic grid spacing in height units, if it is needed. + if (dz_diag_needed) then + call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1) + endif + if (dz_begin_needed) then + call thickness_to_dz(diag_cs%h_begin, diag_cs%tv, dz_begin, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1) + endif + diag => diag_cs%diags(diag_field_id) do while (associated(diag)) call assert(associated(diag%axes), 'post_data_3d: axes is not associated') @@ -1557,11 +1599,17 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap) allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz)) - call vertically_reintegrate_diag_field( & - diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, & - diag_cs%h_begin, & - diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, & - staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field) + if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then + call vertically_reintegrate_diag_field( & + diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, & + dz_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, & + staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field) + else + call vertically_reintegrate_diag_field( & + diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), diag_cs%G, & + diag_cs%h_begin, diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%h_extensive, & + staggered_in_x, staggered_in_y, diag%axes%mask3d, field, remapped_field) + endif if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap) if (associated(diag%axes%mask3d)) then ! Since 3d masks do not vary in the vertical, just use as much as is @@ -1582,9 +1630,15 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap) allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz)) - call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), & - diag_cs%G, diag_cs%GV, h_diag, staggered_in_x, staggered_in_y, & - diag%axes%mask3d, field, remapped_field) + if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then + call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), & + diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, staggered_in_x, staggered_in_y, & + diag%axes%mask3d, field, remapped_field) + else + call diag_remap_do_remap(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), & + diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, staggered_in_x, staggered_in_y, & + diag%axes%mask3d, field, remapped_field) + endif if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap) if (associated(diag%axes%mask3d)) then ! Since 3d masks do not vary in the vertical, just use as much as is @@ -1605,10 +1659,15 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) if (id_clock_diag_remap>0) call cpu_clock_begin(id_clock_diag_remap) allocate(remapped_field(size(field,1), size(field,2), diag%axes%nz+1)) - call vertically_interpolate_diag_field(diag_cs%diag_remap_cs( & - diag%axes%vertical_coordinate_number), & - diag_cs%G, h_diag, staggered_in_x, staggered_in_y, & - diag%axes%mask3d, field, remapped_field) + if (diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number)%Z_based_coord) then + call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), & + diag_cs%G, dz_diag, staggered_in_x, staggered_in_y, & + diag%axes%mask3d, field, remapped_field) + else + call vertically_interpolate_diag_field(diag_cs%diag_remap_cs(diag%axes%vertical_coordinate_number), & + diag_cs%G, h_diag, staggered_in_x, staggered_in_y, & + diag%axes%mask3d, field, remapped_field) + endif if (id_clock_diag_remap>0) call cpu_clock_end(id_clock_diag_remap) if (associated(diag%axes%mask3d)) then ! Since 3d masks do not vary in the vertical, just use as much as is @@ -1628,6 +1687,9 @@ subroutine post_data_3d(diag_field_id, field, diag_cs, is_static, mask, alt_h) enddo if (id_clock_diag_mediator>0) call cpu_clock_end(id_clock_diag_mediator) + if (diag_cs%show_call_tree) & + call callTree_leave("post_data_3d("//trim(diag_cs%diags(diag_field_id)%debug_str)//")") + end subroutine post_data_3d !> Make a real 3-d array diagnostic available for averaging or output @@ -1926,8 +1988,7 @@ subroutine post_xy_average(diag_cs, diag, field) call horizontally_average_diag_field(diag_cs%G, diag_cs%GV, diag_cs%h, & staggered_in_x, staggered_in_y, & diag%axes%is_layer, diag%v_extensive, & - field, & - averaged_field, averaged_mask) + field, averaged_field, averaged_mask) else nz = size(field, 3) coord = diag%axes%vertical_coordinate_number @@ -3168,6 +3229,8 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) call initialize_diag_type(diag_cs%diags(i)) enddo + diag_cs%show_call_tree = callTree_showQuery() + ! Read all relevant parameters and write them to the model log. call log_version(param_file, mdl, version, "") @@ -3207,7 +3270,7 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) allocate(diag_cs%diag_remap_cs(diag_cs%num_diag_coords)) ! Initialize each diagnostic vertical coordinate do i=1, diag_cs%num_diag_coords - call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i), answer_date=remap_answer_date) + call diag_remap_init(diag_cs%diag_remap_cs(i), diag_coords(i), answer_date=remap_answer_date, GV=GV) enddo deallocate(diag_coords) endif @@ -3223,7 +3286,7 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) if (diag_cs%diag_as_chksum) & diag_cs%num_chksum_diags = 0 - ! Keep pointers grid, h, T, S needed diagnostic remapping + ! Keep pointers to the grid, h, T, S needed for diagnostic remapping diag_cs%G => G diag_cs%GV => GV diag_cs%US => US @@ -3231,6 +3294,7 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) diag_cs%T => null() diag_cs%S => null() diag_cs%eqn_of_state => null() + diag_cs%tv => null() allocate(diag_cs%h_begin(G%isd:G%ied,G%jsd:G%jed,nz)) #if defined(DEBUG) || defined(__DO_SAFETY_CHECKS__) @@ -3343,18 +3407,18 @@ subroutine diag_mediator_init(G, GV, US, nz, param_file, diag_cs, doc_file_dir) end subroutine diag_mediator_init !> Set pointers to the default state fields used to remap diagnostics. -subroutine diag_set_state_ptrs(h, T, S, eqn_of_state, diag_cs) +subroutine diag_set_state_ptrs(h, tv, diag_cs) real, dimension(:,:,:), target, intent(in ) :: h !< the model thickness array [H ~> m or kg m-2] - real, dimension(:,:,:), target, intent(in ) :: T !< the model temperature array [C ~> degC] - real, dimension(:,:,:), target, intent(in ) :: S !< the model salinity array [S ~> ppt] - type(EOS_type), target, intent(in ) :: eqn_of_state !< Equation of state structure + type(thermo_var_ptrs), target, intent(in ) :: tv !< A sturcture with thermodynamic variables that are + !! are used to convert thicknesses to vertical extents type(diag_ctrl), intent(inout) :: diag_cs !< diag mediator control structure ! Keep pointers to h, T, S needed for the diagnostic remapping diag_cs%h => h - diag_cs%T => T - diag_cs%S => S - diag_cs%eqn_of_state => eqn_of_state + diag_cs%T => tv%T + diag_cs%S => tv%S + diag_cs%eqn_of_state => tv%eqn_of_state + diag_cs%tv => tv end subroutine @@ -3374,11 +3438,15 @@ subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S, update_intensiv logical, optional, intent(in ) :: update_extensive !< If true (not default), update the grids used for !! intensive diagnostics ! Local variables - integer :: i + integer :: m real, dimension(:,:,:), pointer :: h_diag => NULL() ! The layer thickneses for diagnostics [H ~> m or kg m-2] real, dimension(:,:,:), pointer :: T_diag => NULL() ! The layer temperatures for diagnostics [C ~> degC] real, dimension(:,:,:), pointer :: S_diag => NULL() ! The layer salinities for diagnostics [S ~> ppt] - logical :: update_intensive_local, update_extensive_local + real, dimension(diag_cs%G%isd:diag_cS%G%ied, diag_cs%G%jsd:diag_cS%G%jed, diag_cs%GV%ke) :: & + dz_diag ! Layer vertical extents for remapping [Z ~> m] + logical :: update_intensive_local, update_extensive_local, dz_diag_needed + + if (diag_cs%show_call_tree) call callTree_enter("diag_update_remap_grids()") ! Set values based on optional input arguments if (present(alt_h)) then @@ -3415,17 +3483,38 @@ subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S, update_intensiv "diagnostic structure have been overridden") endif + ! Determine the diagnostic grid spacing in height units, if it is needed. + dz_diag_needed = .false. + if (update_intensive_local .or. update_extensive_local) then + do m=1, diag_cs%num_diag_coords + if (diag_cs%diag_remap_cs(m)%Z_based_coord) dz_diag_needed = .true. + enddo + endif + if (dz_diag_needed) then + call thickness_to_dz(h_diag, diag_cs%tv, dz_diag, diag_cs%G, diag_cs%GV, diag_cs%US, halo_size=1) + endif + if (update_intensive_local) then - do i=1, diag_cs%num_diag_coords - call diag_remap_update(diag_cs%diag_remap_cs(i), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, T_diag, S_diag, & - diag_cs%eqn_of_state, diag_cs%diag_remap_cs(i)%h) + do m=1, diag_cs%num_diag_coords + if (diag_cs%diag_remap_cs(m)%Z_based_coord) then + call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, T_diag, S_diag, & + diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h) + else + call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, T_diag, S_diag, & + diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h) + endif enddo endif if (update_extensive_local) then diag_cs%h_begin(:,:,:) = diag_cs%h(:,:,:) - do i=1, diag_cs%num_diag_coords - call diag_remap_update(diag_cs%diag_remap_cs(i), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, T_diag, S_diag, & - diag_cs%eqn_of_state, diag_cs%diag_remap_cs(i)%h_extensive) + do m=1, diag_cs%num_diag_coords + if (diag_cs%diag_remap_cs(m)%Z_based_coord) then + call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, dz_diag, T_diag, S_diag, & + diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive) + else + call diag_remap_update(diag_cs%diag_remap_cs(m), diag_cs%G, diag_cs%GV, diag_cs%US, h_diag, T_diag, S_diag, & + diag_cs%eqn_of_state, diag_cs%diag_remap_cs(m)%h_extensive) + endif enddo endif @@ -3437,6 +3526,8 @@ subroutine diag_update_remap_grids(diag_cs, alt_h, alt_T, alt_S, update_intensiv if (id_clock_diag_grid_updates>0) call cpu_clock_end(id_clock_diag_grid_updates) + if (diag_cs%show_call_tree) call callTree_leave("diag_update_remap_grids()") + end subroutine diag_update_remap_grids !> Sets up the 2d and 3d masks for native diagnostics @@ -3915,13 +4006,13 @@ subroutine downsample_diag_masks_set(G, nz, diag_cs) do dl=2,MAX_DSAMP_LEV ! 2d mask - call downsample_mask(G%mask2dT, diag_cs%dsamp(dl)%mask2dT, dl,G%isc, G%jsc, & + call downsample_mask(G%mask2dT, diag_cs%dsamp(dl)%mask2dT, dl, G%isc, G%jsc, G%isd, G%jsd, & G%HId2%isc, G%HId2%iec, G%HId2%jsc, G%HId2%jec, G%HId2%isd, G%HId2%ied, G%HId2%jsd, G%HId2%jed) - call downsample_mask(G%mask2dBu,diag_cs%dsamp(dl)%mask2dBu, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%JscB,G%HId2%JecB,G%HId2%IsdB,G%HId2%IedB,G%HId2%JsdB,G%HId2%JedB) - call downsample_mask(G%mask2dCu,diag_cs%dsamp(dl)%mask2dCu, dl,G%IscB,G%JscB, & - G%HId2%IscB,G%HId2%IecB,G%HId2%jsc, G%HId2%jec,G%HId2%IsdB,G%HId2%IedB,G%HId2%jsd, G%HId2%jed) - call downsample_mask(G%mask2dCv,diag_cs%dsamp(dl)%mask2dCv, dl,G%isc ,G%JscB, & + call downsample_mask(G%mask2dBu, diag_cs%dsamp(dl)%mask2dBu, dl,G%IscB, G%JscB, G%IsdB, G%JsdB, & + G%HId2%IscB,G%HId2%IecB, G%HId2%JscB,G%HId2%JecB,G%HId2%IsdB,G%HId2%IedB,G%HId2%JsdB,G%HId2%JedB) + call downsample_mask(G%mask2dCu, diag_cs%dsamp(dl)%mask2dCu, dl, G%IscB, G%jsc, G%IsdB, G%jsd, & + G%HId2%IscB,G%HId2%IecB, G%HId2%jsc, G%HId2%jec,G%HId2%IsdB,G%HId2%IedB,G%HId2%jsd, G%HId2%jed) + call downsample_mask(G%mask2dCv, diag_cs%dsamp(dl)%mask2dCv, dl,G %isc ,G%JscB, G%isd, G%JsdB, & G%HId2%isc ,G%HId2%iec, G%HId2%JscB,G%HId2%JecB,G%HId2%isd ,G%HId2%ied, G%HId2%JsdB,G%HId2%JedB) ! 3d native masks are needed by diag_manager but the native variables ! can only be masked 2d - for ocean points, all layers exists. @@ -4182,6 +4273,7 @@ subroutine downsample_field_3d(field_in, field_out, dl, method, mask, diag_cs, d allocate(field_out(1:f1,1:f2,ks:ke)) ! Fill the down sampled field on the down sampled diagnostics (almost always compuate) domain + !### The averaging used here is not rotationally invariant. if (method == MMM) then do k=ks,ke ; do j=jsv_d,jev_d ; do i=isv_d,iev_d i0 = isv_o+dl*(i-isv_d) @@ -4433,10 +4525,12 @@ end subroutine downsample_field_2d !> Allocate and compute the 2d down sampled mask !! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1) !! if at least one of the sub-cells are open, otherwise it's closed (0) -subroutine downsample_mask_2d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_d, jsc_d, jec_d, & - isd_d, ied_d, jsd_d, jed_d) - real, dimension(:,:), intent(in) :: field_in !< Original field to be down sampled - real, dimension(:,:), pointer :: field_out !< Down sampled field +subroutine downsample_mask_2d(field_in, field_out, dl, isc_o, jsc_o, isd_o, jsd_o, & + isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d) + integer, intent(in) :: isd_o !< Original data domain i-start index + integer, intent(in) :: jsd_o !< Original data domain j-start index + real, dimension(isd_o:,jsd_o:), intent(in) :: field_in !< Original field to be down sampled in arbitrary units [A] + real, dimension(:,:), pointer :: field_out !< Down sampled field mask [nondim] integer, intent(in) :: dl !< Level of down sampling integer, intent(in) :: isc_o !< Original i-start index integer, intent(in) :: jsc_o !< Original j-start index @@ -4444,13 +4538,13 @@ subroutine downsample_mask_2d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_ integer, intent(in) :: iec_d !< Computational i-end index of down sampled data integer, intent(in) :: jsc_d !< Computational j-start index of down sampled data integer, intent(in) :: jec_d !< Computational j-end index of down sampled data - integer, intent(in) :: isd_d !< Computational i-start index of down sampled data - integer, intent(in) :: ied_d !< Computational i-end index of down sampled data - integer, intent(in) :: jsd_d !< Computational j-start index of down sampled data - integer, intent(in) :: jed_d !< Computational j-end index of down sampled data + integer, intent(in) :: isd_d !< Data domain i-start index of down sampled data + integer, intent(in) :: ied_d !< Data domain i-end index of down sampled data + integer, intent(in) :: jsd_d !< Data domain j-start index of down sampled data + integer, intent(in) :: jed_d !< Data domain j-end index of down sampled data ! Locals integer :: i,j,ii,jj,i0,j0 - real :: tot_non_zero + real :: tot_non_zero ! The sum of values in the down-scaled cell [A] ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1 allocate(field_out(isd_d:ied_d,jsd_d:jed_d)) field_out(:,:) = 0.0 @@ -4468,10 +4562,12 @@ end subroutine downsample_mask_2d !> Allocate and compute the 3d down sampled mask !! The masks are down sampled based on a minority rule, i.e., a coarse cell is open (1) !! if at least one of the sub-cells are open, otherwise it's closed (0) -subroutine downsample_mask_3d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_d, jsc_d, jec_d, & - isd_d, ied_d, jsd_d, jed_d) - real, dimension(:,:,:), intent(in) :: field_in !< Original field to be down sampled - real, dimension(:,:,:), pointer :: field_out !< down sampled field +subroutine downsample_mask_3d(field_in, field_out, dl, isc_o, jsc_o, isd_o, jsd_o, & + isc_d, iec_d, jsc_d, jec_d, isd_d, ied_d, jsd_d, jed_d) + integer, intent(in) :: isd_o !< Original data domain i-start index + integer, intent(in) :: jsd_o !< Original data domain j-start index + real, dimension(isd_o:,jsd_o:,:), intent(in) :: field_in !< Original field to be down sampled in arbitrary units [A] + real, dimension(:,:,:), pointer :: field_out !< down sampled field mask [nondim] integer, intent(in) :: dl !< Level of down sampling integer, intent(in) :: isc_o !< Original i-start index integer, intent(in) :: jsc_o !< Original j-start index @@ -4485,7 +4581,7 @@ subroutine downsample_mask_3d(field_in, field_out, dl, isc_o, jsc_o, isc_d, iec_ integer, intent(in) :: jed_d !< Computational j-end index of down sampled data ! Locals integer :: i,j,ii,jj,i0,j0,k,ks,ke - real :: tot_non_zero + real :: tot_non_zero ! The sum of values in the down-scaled cell [A] ! down sampled mask = 0 unless the mask value of one of the down sampling cells is 1 ks = lbound(field_in,3) ; ke = ubound(field_in,3) allocate(field_out(isd_d:ied_d,jsd_d:jed_d,ks:ke)) diff --git a/src/framework/MOM_diag_remap.F90 b/src/framework/MOM_diag_remap.F90 index 0e2e594403..ace50242a5 100644 --- a/src/framework/MOM_diag_remap.F90 +++ b/src/framework/MOM_diag_remap.F90 @@ -15,42 +15,14 @@ !! the diagnostic is written out. -! NOTE: In the following functions, the fields are passed using 1-based -! indexing, which requires special handling within the grid index loops. +! NOTE: In the following functions, the fields are initially passed using 1-based +! indexing, which are then passed to separate private internal routines that shift +! the indexing to use the same indexing conventions used elsewhere in the MOM6 code. ! -! * diag_remap_do_remap -! * vertically_reintegrate_diag_field -! * vertically_interpolate_diag_field -! * horizontally_average_diag_field -! -! Symmetric grids add an additional row of western and southern points to u- -! and v-grids. Non-symmetric grids are 1-based and symmetric grids are -! zero-based, allowing the same expressions to be used when accessing the -! fields. But if u- or v-points become 1-indexed, as in these functions, then -! the stencils must be re-assessed. -! -! For interpolation between h and u grids, we use the following relations: -! -! h->u: f_u(ig) = 0.5 * (f_h( ig ) + f_h(ig+1)) -! f_u(i1) = 0.5 * (f_h(i1-1) + f_h( i1 )) -! -! u->h: f_h(ig) = 0.5 * (f_u(ig-1) + f_u( ig )) -! f_h(i1) = 0.5 * (f_u( i1 ) + f_u(i1+1)) -! -! where ig is the grid index and i1 is the 1-based index. That is, a 1-based -! u-point is ahead of its matching h-point in non-symmetric mode, but behind -! its matching h-point in non-symmetric mode. -! -! We can combine these expressions by applying to ig a -1 shift on u-grids and -! a +1 shift on h-grids in symmetric mode. -! -! We do not adjust the h-point indices, since they are assumed to be 1-based. -! This is only correct when global indexing is disabled. If global indexing is -! enabled, then all indices will need to be defined relative to the data -! domain. -! -! Finally, note that the mask input fields are pointers to arrays which are -! zero-indexed, and do not need any corrections over grid index loops. +! * diag_remap_do_remap, which calls do_remap +! * vertically_reintegrate_diag_field, which calls vertically_reintegrate_field +! * vertically_interpolate_diag_field, which calls vertically_interpolate_field +! * horizontally_average_diag_field, which calls horizontally_average_field module MOM_diag_remap @@ -70,10 +42,9 @@ module MOM_diag_remap use MOM_EOS, only : EOS_type use MOM_remapping, only : remapping_CS, initialize_remapping, remapping_core_h use MOM_remapping, only : interpolate_column, reintegrate_column -use MOM_regridding, only : regridding_CS, initialize_regridding -use MOM_regridding, only : end_regridding +use MOM_regridding, only : regridding_CS, initialize_regridding, end_regridding use MOM_regridding, only : set_regrid_params, get_regrid_size -use MOM_regridding, only : getCoordinateInterfaces +use MOM_regridding, only : getCoordinateInterfaces, set_h_neglect, set_dz_neglect use MOM_regridding, only : get_zlike_CS, get_sigma_CS, get_rho_CS use regrid_consts, only : coordinateMode use coord_zlike, only : build_zstar_column @@ -83,6 +54,8 @@ module MOM_diag_remap implicit none ; private +#include "MOM_memory.h" + public diag_remap_ctrl public diag_remap_init, diag_remap_end, diag_remap_update, diag_remap_do_remap public diag_remap_configure_axes, diag_remap_axes_configured @@ -104,14 +77,19 @@ module MOM_diag_remap logical :: used = .false. !< Whether this coordinate actually gets used. integer :: vertical_coord = 0 !< The vertical coordinate that we remap to character(len=10) :: vertical_coord_name ='' !< The coordinate name as understood by ALE + logical :: Z_based_coord = .false. !< If true, this coordinate is based on remapping of + !! geometric distances across layers (in [Z ~> m]) rather + !! than layer thicknesses (in [H ~> m or kg m-2]). This + !! distinction only matters in non-Boussinesq mode. character(len=16) :: diag_coord_name = '' !< A name for the purpose of run-time parameters character(len=8) :: diag_module_suffix = '' !< The suffix for the module to appear in diag_table type(remapping_CS) :: remap_cs !< Remapping control structure use for this axes type(regridding_CS) :: regrid_cs !< Regridding control structure that defines the coordinates for this axes integer :: nz = 0 !< Number of vertical levels used for remapping - real, dimension(:,:,:), allocatable :: h !< Remap grid thicknesses [H ~> m or kg m-2] - real, dimension(:,:,:), allocatable :: h_extensive !< Remap grid thicknesses for extensive - !! variables [H ~> m or kg m-2] + real, dimension(:,:,:), allocatable :: h !< Remap grid thicknesses in [H ~> m or kg m-2] or + !! vertical extents in [Z ~> m], depending on the setting of Z_based_coord. + real, dimension(:,:,:), allocatable :: h_extensive !< Remap grid thicknesses in [H ~> m or kg m-2] or + !! vertical extents in [Z ~> m] for remapping extensive variables integer :: interface_axes_id = 0 !< Vertical axes id for remapping at interfaces integer :: layer_axes_id = 0 !< Vertical axes id for remapping on layers integer :: answer_date !< The vintage of the order of arithmetic and expressions @@ -124,19 +102,28 @@ module MOM_diag_remap contains !> Initialize a diagnostic remapping type with the given vertical coordinate. -subroutine diag_remap_init(remap_cs, coord_tuple, answer_date) - type(diag_remap_ctrl), intent(inout) :: remap_cs !< Diag remapping control structure +subroutine diag_remap_init(remap_cs, coord_tuple, answer_date, GV) + type(diag_remap_ctrl), intent(inout) :: remap_cs !< Diag remapping control structure character(len=*), intent(in) :: coord_tuple !< A string in form of !! MODULE_SUFFIX PARAMETER_SUFFIX COORDINATE_NAME integer, intent(in) :: answer_date !< The vintage of the order of arithmetic and expressions !! to use for remapping. Values below 20190101 recover !! the answers from 2018, while higher values use more !! robust forms of the same remapping expressions. + type(verticalGrid_type), intent(in) :: GV !< The ocean vertical grid structure, used here to evaluate + !! whether the model is in non-Boussinesq mode. remap_cs%diag_module_suffix = trim(extractWord(coord_tuple, 1)) remap_cs%diag_coord_name = trim(extractWord(coord_tuple, 2)) remap_cs%vertical_coord_name = trim(extractWord(coord_tuple, 3)) remap_cs%vertical_coord = coordinateMode(remap_cs%vertical_coord_name) + remap_cs%Z_based_coord = .false. + if (.not.(GV%Boussinesq .or. GV%semi_Boussinesq) .and. & + ((remap_cs%vertical_coord == coordinateMode('ZSTAR')) .or. & + (remap_cs%vertical_coord == coordinateMode('SIGMA')) .or. & + (remap_cs%vertical_coord == coordinateMode('RHO'))) ) & + remap_cs%Z_based_coord = .true. + remap_cs%configured = .false. remap_cs%initialized = .false. remap_cs%used = .false. @@ -274,31 +261,46 @@ subroutine diag_remap_update(remap_cs, G, GV, US, h, T, S, eqn_of_state, h_targe type(ocean_grid_type), pointer :: G !< The ocean's grid type type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - real, dimension(:,:,:), intent(in) :: h !< New thickness [H ~> m or kg m-2] - real, dimension(:,:,:), intent(in) :: T !< New temperatures [C ~> degC] - real, dimension(:,:,:), intent(in) :: S !< New salinities [S ~> ppt] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & + intent(in) :: h !< New thickness in [H ~> m or kg m-2] or [Z ~> m], depending + !! on the value of remap_cs%Z_based_coord + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & + intent(in) :: T !< New temperatures [C ~> degC] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & + intent(in) :: S !< New salinities [S ~> ppt] type(EOS_type), intent(in) :: eqn_of_state !< A pointer to the equation of state - real, dimension(:,:,:), intent(inout) :: h_target !< The new diagnostic thicknesses [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),remap_cs%nz), & + intent(inout) :: h_target !< The new diagnostic thicknesses in [H ~> m or kg m-2] + !! or [Z ~> m], depending on the value of remap_cs%Z_based_coord ! Local variables - real, dimension(remap_cs%nz + 1) :: zInterfaces ! Interface positions [H ~> m or kg m-2] - real :: h_neglect, h_neglect_edge ! Negligible thicknesses [H ~> m or kg m-2] - integer :: i, j, k, nz - - ! Note that coordinateMode('LAYER') is never 'configured' so will - ! always return here. - if (.not. remap_cs%configured) then - return - endif - - if (remap_cs%answer_date >= 20190101) then - h_neglect = GV%H_subroundoff ; h_neglect_edge = GV%H_subroundoff - elseif (GV%Boussinesq) then - h_neglect = GV%m_to_H*1.0e-30 ; h_neglect_edge = GV%m_to_H*1.0e-10 + real, dimension(remap_cs%nz + 1) :: zInterfaces ! Interface positions [H ~> m or kg m-2] or [Z ~> m] + real :: h_neglect, h_neglect_edge ! Negligible thicknesses [H ~> m or kg m-2] or [Z ~> m] + real :: bottom_depth(SZI_(G),SZJ_(G)) ! The depth of the bathymetry in [H ~> m or kg m-2] or [Z ~> m] + real :: h_tot(SZI_(G),SZJ_(G)) ! The total thickness of the water column [H ~> m or kg m-2] or [Z ~> m] + real :: Z_unit_scale ! A conversion factor from Z-units the internal work units in this routine, + ! in units of [H Z-1 ~> 1 or kg m-3] or [nondim], depending on remap_cs%Z_based_coord. + integer :: i, j, k, is, ie, js, je, nz + + ! Note that coordinateMode('LAYER') is never 'configured' so will always return here. + if (.not. remap_cs%configured) return + + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + ! Set the bottom depth and negligible thicknesses used in the coordinate remapping in the right units. + if (remap_cs%Z_based_coord) then + h_neglect = set_dz_neglect(GV, US, remap_cs%answer_date, h_neglect_edge) + Z_unit_scale = 1.0 + do j=js-1,je+1 ; do i=is-1,ie+1 + bottom_depth(i,j) = G%bathyT(i,j) + G%Z_ref + enddo ; enddo else - h_neglect = GV%kg_m2_to_H*1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H*1.0e-10 + h_neglect = set_h_neglect(GV, remap_cs%answer_date, h_neglect_edge) + Z_unit_scale = GV%Z_to_H ! This branch is not used in fully non-Boussinesq mode. + do j=js-1,je+1 ; do i=is-1,ie+1 + bottom_depth(i,j) = GV%Z_to_H * (G%bathyT(i,j) + G%Z_ref) + enddo ; enddo endif - nz = remap_cs%nz if (.not. remap_cs%initialized) then ! Initialize remapping and regridding on the first call @@ -307,145 +309,203 @@ subroutine diag_remap_update(remap_cs, G, GV, US, h, T, S, eqn_of_state, h_targe remap_cs%initialized = .true. endif + ! Calculate the total thickness of the water column, if it is needed, + if ((remap_cs%vertical_coord == coordinateMode('ZSTAR')) .or. & + (remap_cs%vertical_coord == coordinateMode('SIGMA'))) then + if (remap_CS%answer_date >= 20240201) then + ! Avoid using sum to have a specific order for the vertical sums. + ! For some compilers, the explicit expression gives the same answers as the sum function. + h_tot(:,:) = 0.0 + do k=1,GV%ke ; do j=js-1,je+1 ; do i=is-1,ie+1 + h_tot(i,j) = h_tot(i,j) + h(i,j,k) + enddo ; enddo ; enddo + else + do j=js-1,je+1 ; do i=is-1,ie+1 + h_tot(i,j) = sum(h(i,j,:)) + enddo ; enddo + endif + endif + ! Calculate remapping thicknesses for different target grids based on ! nominal/target interface locations. This happens for every call on the ! assumption that h, T, S has changed. - do j=G%jsc-1, G%jec+1 ; do i=G%isc-1, G%iec+1 - if (G%mask2dT(i,j)==0.) then - h_target(i,j,:) = 0. - cycle - endif + h_target(:,:,:) = 0.0 - if (remap_cs%vertical_coord == coordinateMode('ZSTAR')) then + nz = remap_cs%nz + if (remap_cs%vertical_coord == coordinateMode('ZSTAR')) then + do j=js-1,je+1 ; do i=is-1,ie+1 ; if (G%mask2dT(i,j) > 0.0) then + ! This function call can work with the last 4 arguments all in units of [Z ~> m] or [H ~> kg m-2]. call build_zstar_column(get_zlike_CS(remap_cs%regrid_cs), & - GV%Z_to_H*(G%bathyT(i,j)+G%Z_ref), sum(h(i,j,:)), & - zInterfaces, zScale=GV%Z_to_H) - elseif (remap_cs%vertical_coord == coordinateMode('SIGMA')) then + bottom_depth(i,j), h_tot(i,j), zInterfaces, zScale=Z_unit_scale) + do k=1,nz ; h_target(i,j,k) = zInterfaces(K) - zInterfaces(K+1) ; enddo + endif ; enddo ; enddo + elseif (remap_cs%vertical_coord == coordinateMode('SIGMA')) then + do j=js-1, je+1 ; do i=is-1,ie+1 ; if (G%mask2dT(i,j) > 0.0) then + ! This function call can work with the last 3 arguments all in units of [Z ~> m] or [H ~> kg m-2]. call build_sigma_column(get_sigma_CS(remap_cs%regrid_cs), & - GV%Z_to_H*(G%bathyT(i,j)+G%Z_ref), sum(h(i,j,:)), zInterfaces) - elseif (remap_cs%vertical_coord == coordinateMode('RHO')) then + bottom_depth(i,j), h_tot(i,j), zInterfaces) + do k=1,nz ; h_target(i,j,k) = zInterfaces(K) - zInterfaces(K+1) ; enddo + endif ; enddo ; enddo + elseif (remap_cs%vertical_coord == coordinateMode('RHO')) then + do j=js-1,je+1 ; do i=is-1,ie+1 ; if (G%mask2dT(i,j) > 0.0) then + ! This function call can work with 5 arguments in units of [Z ~> m] or [H ~> kg m-2]. call build_rho_column(get_rho_CS(remap_cs%regrid_cs), GV%ke, & - GV%Z_to_H*(G%bathyT(i,j)+G%Z_ref), h(i,j,:), T(i,j,:), S(i,j,:), & + bottom_depth(i,j), h(i,j,:), T(i,j,:), S(i,j,:), & eqn_of_state, zInterfaces, h_neglect=h_neglect, h_neglect_edge=h_neglect_edge) - elseif (remap_cs%vertical_coord == coordinateMode('HYCOM1')) then -! call build_hycom1_column(remap_cs%regrid_cs, nz, & -! GV%Z_to_H*(G%bathyT(i,j)+G%Z_ref), sum(h(i,j,:)), zInterfaces) - call MOM_error(FATAL,"diag_remap_update: HYCOM1 coordinate not coded for diagnostics yet!") - endif - do k = 1,nz - h_target(i,j,k) = zInterfaces(k) - zInterfaces(k+1) - enddo - enddo ; enddo + do k=1,nz ; h_target(i,j,k) = zInterfaces(K) - zInterfaces(K+1) ; enddo + endif ; enddo ; enddo + elseif (remap_cs%vertical_coord == coordinateMode('HYCOM1')) then + call MOM_error(FATAL,"diag_remap_update: HYCOM1 coordinate not coded for diagnostics yet!") +! do j=js-1,je+1 ; do i=is-1,ie+1 ; if (G%mask2dT(i,j) > 0.0) then +! call build_hycom1_column(remap_cs%regrid_cs, nz, & +! bottom_depth(i,j), h_tot(i,j), zInterfaces) +! do k=1,nz ; h_target(i,j,k) = zInterfaces(K) - zInterfaces(K+1) ; enddo +! endif ; enddo ; enddo + endif end subroutine diag_remap_update !> Remap diagnostic field to alternative vertical grid. -subroutine diag_remap_do_remap(remap_cs, G, GV, h, staggered_in_x, staggered_in_y, & +subroutine diag_remap_do_remap(remap_cs, G, GV, US, h, staggered_in_x, staggered_in_y, & mask, field, remapped_field) - type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coodinate control structure - type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure - real, dimension(:,:,:), intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] - logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points - logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points - real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim] - real, dimension(:,:,:), intent(in) :: field(:,:,:) !< The diagnostic field to be remapped [A] - real, dimension(:,:,:), intent(inout) :: remapped_field !< Field remapped to new coordinate [A] + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(:,:,:), intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] or [Z ~> m], + !! depending on the value of remap_CS%Z_based_coord + logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points + real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim]. + real, dimension(:,:,:), intent(in) :: field(:,:,:) !< The diagnostic field to be remapped [A] + real, dimension(:,:,:), intent(out) :: remapped_field !< Field remapped to new coordinate [A] + ! Local variables - real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] - real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] - real :: h_neglect, h_neglect_edge ! Negligible thicknesses [H ~> m or kg m-2] - integer :: nz_src, nz_dest - integer :: i, j !< Grid index - integer :: i1, j1 !< 1-based index - integer :: i_lo, i_hi, j_lo, j_hi !< (uv->h) interpolation indices - integer :: shift !< Symmetric offset for 1-based indexing + integer :: isdf, jsdf !< The starting i- and j-indices in memory for field call assert(remap_cs%initialized, 'diag_remap_do_remap: remap_cs not initialized.') call assert(size(field, 3) == size(h, 3), & 'diag_remap_do_remap: Remap field and thickness z-axes do not match.') - if (remap_cs%answer_date >= 20190101) then - h_neglect = GV%H_subroundoff ; h_neglect_edge = GV%H_subroundoff - elseif (GV%Boussinesq) then - h_neglect = GV%m_to_H*1.0e-30 ; h_neglect_edge = GV%m_to_H*1.0e-10 + isdf = G%isd ; if (staggered_in_x) Isdf = G%IsdB + jsdf = G%jsd ; if (staggered_in_y) Jsdf = G%JsdB + + if (associated(mask)) then + call do_remap(remap_cs, G, GV, US, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, remapped_field, mask(:,:,1)) else - h_neglect = GV%kg_m2_to_H*1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H*1.0e-10 + call do_remap(remap_cs, G, GV, US, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, remapped_field) + endif + +end subroutine diag_remap_do_remap + +!> The internal routine to remap a diagnostic field to an alternative vertical grid. +subroutine do_remap(remap_cs, G, GV, US, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, remapped_field, mask) + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + integer, intent(in) :: isdf !< The starting i-index in memory for field + integer, intent(in) :: jsdf !< The starting j-index in memory for field + real, dimension(G%isd:,G%jsd:,:), & + intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] or [Z ~> m], + !! depending on the value of remap_CS%Z_based_coord + logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points + real, dimension(isdf:,jsdf:,:), & + intent(in) :: field !< The diagnostic field to be remapped [A] + real, dimension(isdf:,jsdf:,:), & + intent(out) :: remapped_field !< Field remapped to new coordinate [A] + real, dimension(isdf:,jsdf:), & + optional, intent(in) :: mask !< A mask for the field [nondim] + + ! Local variables + real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] or [Z ~> m] + real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] or [Z ~> m] + real :: h_neglect, h_neglect_edge ! Negligible thicknesses [H ~> m or kg m-2] or [Z ~> m] + integer :: nz_src, nz_dest ! The number of layers on the native and remapped grids + integer :: i, j ! Grid index + + if (remap_cs%Z_based_coord) then + h_neglect = set_dz_neglect(GV, US, remap_cs%answer_date, h_neglect_edge) + else + h_neglect = set_h_neglect(GV, remap_cs%answer_date, h_neglect_edge) endif nz_src = size(field,3) nz_dest = remap_cs%nz remapped_field(:,:,:) = 0. - ! Symmetric grid offset under 1-based indexing; see header for details. - shift = 0 ; if (G%symmetric) shift = 1 - if (staggered_in_x .and. .not. staggered_in_y) then ! U-points - do j=G%jsc, G%jec - do I=G%iscB, G%iecB - I1 = I - G%isdB + 1 - i_lo = I1 - shift; i_hi = i_lo + 1 - if (associated(mask)) then - if (mask(I,j,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i_lo,j,:) + h(i_hi,j,:)) - h_dest(:) = 0.5 * (remap_cs%h(i_lo,j,:) + remap_cs%h(i_hi,j,:)) - call remapping_core_h(remap_cs%remap_cs, & - nz_src, h_src(:), field(I1,j,:), & - nz_dest, h_dest(:), remapped_field(I1,j,:), & - h_neglect, h_neglect_edge) - enddo - enddo + if (present(mask)) then + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB ; if (mask(I,j) > 0.) then + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i+1,j,:)) + call remapping_core_h(remap_cs%remap_cs, nz_src, h_src(:), field(I,j,:), & + nz_dest, h_dest(:), remapped_field(I,j,:), h_neglect, h_neglect_edge) + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i+1,j,:)) + call remapping_core_h(remap_cs%remap_cs, nz_src, h_src(:), field(I,j,:), & + nz_dest, h_dest(:), remapped_field(I,j,:), h_neglect, h_neglect_edge) + enddo ; enddo + endif elseif (staggered_in_y .and. .not. staggered_in_x) then ! V-points - do J=G%jscB, G%jecB - J1 = J - G%jsdB + 1 - j_lo = J1 - shift; j_hi = j_lo + 1 - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,J,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i,j_lo,:) + h(i,j_hi,:)) - h_dest(:) = 0.5 * (remap_cs%h(i,j_lo,:) + remap_cs%h(i,j_hi,:)) - call remapping_core_h(remap_cs%remap_cs, & - nz_src, h_src(:), field(i,J1,:), & - nz_dest, h_dest(:), remapped_field(i,J1,:), & - h_neglect, h_neglect_edge) - enddo - enddo + if (present(mask)) then + do J=G%jscB,G%jecB ; do i=G%isc,G%iec ; if (mask(i,j) > 0.) then + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i,j+1,:)) + call remapping_core_h(remap_cs%remap_cs, nz_src, h_src(:), field(i,J,:), & + nz_dest, h_dest(:), remapped_field(i,J,:), h_neglect, h_neglect_edge) + endif ; enddo ; enddo + else + do J=G%jscB,G%jecB ; do i=G%isc,G%iec + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i,j+1,:)) + call remapping_core_h(remap_cs%remap_cs, nz_src, h_src(:), field(i,J,:), & + nz_dest, h_dest(:), remapped_field(i,J,:), h_neglect, h_neglect_edge) + enddo ; enddo + endif elseif ((.not. staggered_in_x) .and. (.not. staggered_in_y)) then ! H-points - do j=G%jsc, G%jec - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,j,1) == 0.) cycle - endif - h_src(:) = h(i,j,:) - h_dest(:) = remap_cs%h(i,j,:) - call remapping_core_h(remap_cs%remap_cs, & - nz_src, h_src(:), field(i,j,:), & - nz_dest, h_dest(:), remapped_field(i,j,:), & + if (present(mask)) then + do j=G%jsc,G%jec ; do i=G%isc,G%iec ; if (mask(i,j) > 0.) then + call remapping_core_h(remap_cs%remap_cs, nz_src, h(i,j,:), field(i,j,:), & + nz_dest, remap_cs%h(i,j,:), remapped_field(i,j,:), & h_neglect, h_neglect_edge) - enddo - enddo + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do i=G%isc,G%iec + call remapping_core_h(remap_cs%remap_cs, nz_src, h(i,j,:), field(i,j,:), & + nz_dest, remap_cs%h(i,j,:), remapped_field(i,j,:), & + h_neglect, h_neglect_edge) + enddo ; enddo + endif else call assert(.false., 'diag_remap_do_remap: Unsupported axis combination') endif -end subroutine diag_remap_do_remap +end subroutine do_remap !> Calculate masks for target grid subroutine diag_remap_calc_hmask(remap_cs, G, mask) - type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coodinate control structure + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - real, dimension(:,:,:), intent(out) :: mask !< h-point mask for target grid [nondim] + real, dimension(G%isd:,G%jsd:,:), & + intent(out) :: mask !< h-point mask for target grid [nondim] + ! Local variables - real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] + real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] or [Z ~> m] integer :: i, j, k logical :: mask_vanished_layers - real :: h_tot ! Sum of all thicknesses [H ~> m or kg m-2] - real :: h_err ! An estimate of a negligible thickness [H ~> m or kg m-2] + real :: h_tot ! Sum of all thicknesses [H ~> m or kg m-2] or [Z ~> m] + real :: h_err ! An estimate of a negligible thickness [H ~> m or kg m-2] or [Z ~> m] call assert(remap_cs%initialized, 'diag_remap_calc_hmask: remap_cs not initialized.') @@ -453,7 +513,7 @@ subroutine diag_remap_calc_hmask(remap_cs, G, mask) mask_vanished_layers = (remap_cs%vertical_coord == coordinateMode('ZSTAR')) mask(:,:,:) = 0. - do j=G%jsc-1, G%jec+1 ; do i=G%isc-1, G%iec+1 + do j=G%jsc-1,G%jec+1 ; do i=G%isc-1,G%iec+1 if (G%mask2dT(i,j)>0.) then if (mask_vanished_layers) then h_dest(:) = remap_cs%h(i,j,:) @@ -482,166 +542,239 @@ end subroutine diag_remap_calc_hmask !> Vertically re-grid an already vertically-integrated diagnostic field to alternative vertical grid. subroutine vertically_reintegrate_diag_field(remap_cs, G, h, h_target, staggered_in_x, staggered_in_y, & mask, field, reintegrated_field) - type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coodinate control structure - type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - real, dimension(:,:,:), intent(in) :: h !< The thicknesses of the source grid [H ~> m or kg m-2] - real, dimension(:,:,:), intent(in) :: h_target !< The thicknesses of the target grid [H ~> m or kg m-2] - logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points - logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points - real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim] - real, dimension(:,:,:), intent(in) :: field !< The diagnostic field to be remapped [A] - real, dimension(:,:,:), intent(inout) :: reintegrated_field !< Field argument remapped to alternative coordinate [A] + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + real, dimension(:,:,:), intent(in) :: h !< The thicknesses of the source grid [H ~> m or kg m-2] or [Z ~> m] + real, dimension(:,:,:), intent(in) :: h_target !< The thicknesses of the target grid [H ~> m or kg m-2] or [Z ~> m] + logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points + real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim]. Note that because this + !! is a pointer it retains its declared indexing conventions. + real, dimension(:,:,:), intent(in) :: field !< The diagnostic field to be remapped [A] + real, dimension(:,:,:), intent(out) :: reintegrated_field !< Field argument remapped to alternative coordinate [A] + ! Local variables - real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] - real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] - integer :: nz_src, nz_dest - integer :: i, j !< Grid index - integer :: i1, j1 !< 1-based index - integer :: i_lo, i_hi, j_lo, j_hi !< (uv->h) interpolation indices - integer :: shift !< Symmetric offset for 1-based indexing + integer :: isdf, jsdf !< The starting i- and j-indices in memory for field call assert(remap_cs%initialized, 'vertically_reintegrate_diag_field: remap_cs not initialized.') call assert(size(field, 3) == size(h, 3), & 'vertically_reintegrate_diag_field: Remap field and thickness z-axes do not match.') + isdf = G%isd ; if (staggered_in_x) Isdf = G%IsdB + jsdf = G%jsd ; if (staggered_in_y) Jsdf = G%JsdB + + if (associated(mask)) then + call vertically_reintegrate_field(remap_cs, G, isdf, jsdf, h, h_target, staggered_in_x, staggered_in_y, & + field, reintegrated_field, mask(:,:,1)) + else + call vertically_reintegrate_field(remap_cs, G, isdf, jsdf, h, h_target, staggered_in_x, staggered_in_y, & + field, reintegrated_field) + endif + +end subroutine vertically_reintegrate_diag_field + +!> The internal routine to vertically re-grid an already vertically-integrated diagnostic field to +!! an alternative vertical grid. +subroutine vertically_reintegrate_field(remap_cs, G, isdf, jsdf, h, h_target, staggered_in_x, staggered_in_y, & + field, reintegrated_field, mask) + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + integer, intent(in) :: isdf !< The starting i-index in memory for field + integer, intent(in) :: jsdf !< The starting j-index in memory for field + real, dimension(G%isd:,G%jsd:,:), & + intent(in) :: h !< The thicknesses of the source grid [H ~> m or kg m-2] or [Z ~> m] + real, dimension(G%isd:,G%jsd:,:), & + intent(in) :: h_target !< The thicknesses of the target grid [H ~> m or kg m-2] or [Z ~> m] + logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points + real, dimension(isdf:,jsdf:,:), & + intent(in) :: field !< The diagnostic field to be remapped [A] + real, dimension(isdf:,jsdf:,:), & + intent(out) :: reintegrated_field !< Field argument remapped to alternative coordinate [A] + real, dimension(isdf:,jsdf:), & + optional, intent(in) :: mask !< A mask for the field [nondim] + + ! Local variables + real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] or [Z ~> m] + real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] or [Z ~> m] + integer :: nz_src, nz_dest ! The number of layers on the native and remapped grids + integer :: i, j ! Grid index + nz_src = size(field,3) nz_dest = remap_cs%nz reintegrated_field(:,:,:) = 0. - ! Symmetric grid offset under 1-based indexing; see header for details. - shift = 0 ; if (G%symmetric) shift = 1 - if (staggered_in_x .and. .not. staggered_in_y) then ! U-points - do j=G%jsc, G%jec - do I=G%iscB, G%iecB - I1 = I - G%isdB + 1 - i_lo = I1 - shift; i_hi = i_lo + 1 - if (associated(mask)) then - if (mask(I,j,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i_lo,j,:) + h(i_hi,j,:)) - h_dest(:) = 0.5 * (h_target(i_lo,j,:) + h_target(i_hi,j,:)) - call reintegrate_column(nz_src, h_src, field(I1,j,:), & - nz_dest, h_dest, reintegrated_field(I1,j,:)) - enddo - enddo + if (present(mask)) then + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB ; if (mask(I,j) > 0.0) then + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (h_target(i,j,:) + h_target(i+1,j,:)) + call reintegrate_column(nz_src, h_src, field(I,j,:), & + nz_dest, h_dest, reintegrated_field(I,j,:)) + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (h_target(i,j,:) + h_target(i+1,j,:)) + call reintegrate_column(nz_src, h_src, field(I,j,:), & + nz_dest, h_dest, reintegrated_field(I,j,:)) + enddo ; enddo + endif elseif (staggered_in_y .and. .not. staggered_in_x) then ! V-points - do J=G%jscB, G%jecB - J1 = J - G%jsdB + 1 - j_lo = J1 - shift; j_hi = j_lo + 1 - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,J,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i,j_lo,:) + h(i,j_hi,:)) - h_dest(:) = 0.5 * (h_target(i,j_lo,:) + h_target(i,j_hi,:)) - call reintegrate_column(nz_src, h_src, field(i,J1,:), & - nz_dest, h_dest, reintegrated_field(i,J1,:)) - enddo - enddo + if (present(mask)) then + do J=G%jscB,G%jecB ; do i=G%isc,G%iec ; if (mask(i,J) > 0.0) then + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (h_target(i,j,:) + h_target(i,j+1,:)) + call reintegrate_column(nz_src, h_src, field(i,J,:), & + nz_dest, h_dest, reintegrated_field(i,J,:)) + endif ; enddo ; enddo + else + do J=G%jscB,G%jecB ; do i=G%isc,G%iec + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (h_target(i,j,:) + h_target(i,j+1,:)) + call reintegrate_column(nz_src, h_src, field(i,J,:), & + nz_dest, h_dest, reintegrated_field(i,J,:)) + enddo ; enddo + endif elseif ((.not. staggered_in_x) .and. (.not. staggered_in_y)) then ! H-points - do j=G%jsc, G%jec - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,j,1) == 0.) cycle - endif - h_src(:) = h(i,j,:) - h_dest(:) = h_target(i,j,:) - call reintegrate_column(nz_src, h_src, field(i,j,:), & - nz_dest, h_dest, reintegrated_field(i,j,:)) - enddo - enddo + if (present(mask)) then + do j=G%jsc,G%jec ; do i=G%isc,G%iec ; if (mask(i,J) > 0.0) then + call reintegrate_column(nz_src, h(i,j,:), field(i,j,:), & + nz_dest, h_target(i,j,:), reintegrated_field(i,j,:)) + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do i=G%isc,G%iec + call reintegrate_column(nz_src, h(i,j,:), field(i,j,:), & + nz_dest, h_target(i,j,:), reintegrated_field(i,j,:)) + enddo ; enddo + endif else call assert(.false., 'vertically_reintegrate_diag_field: Q point remapping is not coded yet.') endif -end subroutine vertically_reintegrate_diag_field +end subroutine vertically_reintegrate_field !> Vertically interpolate diagnostic field to alternative vertical grid. subroutine vertically_interpolate_diag_field(remap_cs, G, h, staggered_in_x, staggered_in_y, & mask, field, interpolated_field) - type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coodinate control structure + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - real, dimension(:,:,:), intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] + real, dimension(:,:,:), intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] or [Z ~> m], + !! depending on the value of remap_cs%Z_based_coord logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points - real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim] + real, dimension(:,:,:), pointer :: mask !< A mask for the field [nondim]. Note that because this + !! is a pointer it retains its declared indexing conventions. real, dimension(:,:,:), intent(in) :: field !< The diagnostic field to be remapped [A] real, dimension(:,:,:), intent(inout) :: interpolated_field !< Field argument remapped to alternative coordinate [A] + ! Local variables - real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] - real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] - integer :: nz_src, nz_dest - integer :: i, j !< Grid index - integer :: i1, j1 !< 1-based index - integer :: i_lo, i_hi, j_lo, j_hi !< (uv->h) interpolation indices - integer :: shift !< Symmetric offset for 1-based indexing + integer :: isdf, jsdf !< The starting i- and j-indices in memory for field call assert(remap_cs%initialized, 'vertically_interpolate_diag_field: remap_cs not initialized.') call assert(size(field, 3) == size(h, 3)+1, & 'vertically_interpolate_diag_field: Remap field and thickness z-axes do not match.') + isdf = G%isd ; if (staggered_in_x) Isdf = G%IsdB + jsdf = G%jsd ; if (staggered_in_y) Jsdf = G%JsdB + + if (associated(mask)) then + call vertically_interpolate_field(remap_cs, G, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, interpolated_field, mask(:,:,1)) + else + call vertically_interpolate_field(remap_cs, G, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, interpolated_field) + endif + +end subroutine vertically_interpolate_diag_field + +!> Internal routine to vertically interpolate a diagnostic field to an alternative vertical grid. +subroutine vertically_interpolate_field(remap_cs, G, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + field, interpolated_field, mask) + type(diag_remap_ctrl), intent(in) :: remap_cs !< Diagnostic coordinate control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + integer, intent(in) :: isdf !< The starting i-index in memory for field + integer, intent(in) :: jsdf !< The starting j-index in memory for field + real, dimension(G%isd:,G%jsd:,:), & + intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] or [Z ~> m], + !! depending on the value of remap_cs%Z_based_coord + logical, intent(in) :: staggered_in_x !< True is the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True is the y-axis location is at v or q points + real, dimension(isdf:,jsdf:,:), & + intent(in) :: field !< The diagnostic field to be remapped [A] + real, dimension(isdf:,jsdf:,:), & + intent(out) :: interpolated_field !< Field argument remapped to alternative coordinate [A] + real, dimension(isdf:,jsdf:), & + optional, intent(in) :: mask !< A mask for the field [nondim] + + ! Local variables + real, dimension(remap_cs%nz) :: h_dest ! Destination thicknesses [H ~> m or kg m-2] or [Z ~> m] + real, dimension(size(h,3)) :: h_src ! A column of source thicknesses [H ~> m or kg m-2] or [Z ~> m] + integer :: nz_src, nz_dest ! The number of layers on the native and remapped grids + integer :: i, j !< Grid index + interpolated_field(:,:,:) = 0. nz_src = size(h,3) nz_dest = remap_cs%nz - ! Symmetric grid offset under 1-based indexing; see header for details. - shift = 0 ; if (G%symmetric) shift = 1 - if (staggered_in_x .and. .not. staggered_in_y) then ! U-points - do j=G%jsc, G%jec - do I=G%iscB, G%iecB - I1 = I - G%isdB + 1 - i_lo = I1 - shift; i_hi = i_lo + 1 - if (associated(mask)) then - if (mask(I,j,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i_lo,j,:) + h(i_hi,j,:)) - h_dest(:) = 0.5 * (remap_cs%h(i_lo,j,:) + remap_cs%h(i_hi,j,:)) - call interpolate_column(nz_src, h_src, field(I1,j,:), & - nz_dest, h_dest, interpolated_field(I1,j,:), .true.) - enddo - enddo + if (present(mask)) then + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB ; if (mask(I,j) > 0.0) then + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i+1,j,:)) + call interpolate_column(nz_src, h_src, field(I,j,:), & + nz_dest, h_dest, interpolated_field(I,j,:), .true.) + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do I=G%IscB,G%IecB + h_src(:) = 0.5 * (h(i,j,:) + h(i+1,j,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i+1,j,:)) + call interpolate_column(nz_src, h_src, field(I,j,:), & + nz_dest, h_dest, interpolated_field(I,j,:), .true.) + enddo ; enddo + endif elseif (staggered_in_y .and. .not. staggered_in_x) then ! V-points - do J=G%jscB, G%jecB - J1 = J - G%jsdB + 1 - j_lo = J1 - shift; j_hi = j_lo + 1 - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,J,1) == 0.) cycle - endif - h_src(:) = 0.5 * (h(i,j_lo,:) + h(i,j_hi,:)) - h_dest(:) = 0.5 * (remap_cs%h(i,j_lo,:) + remap_cs%h(i,j_hi,:)) - call interpolate_column(nz_src, h_src, field(i,J1,:), & - nz_dest, h_dest, interpolated_field(i,J1,:), .true.) - enddo - enddo + if (present(mask)) then + do J=G%jscB,G%jecB ; do i=G%isc,G%iec ; if (mask(I,j) > 0.0) then + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i,j+1,:)) + call interpolate_column(nz_src, h_src, field(i,J,:), & + nz_dest, h_dest, interpolated_field(i,J,:), .true.) + endif ; enddo ; enddo + else + do J=G%jscB,G%jecB ; do i=G%isc,G%iec + h_src(:) = 0.5 * (h(i,j,:) + h(i,j+1,:)) + h_dest(:) = 0.5 * (remap_cs%h(i,j,:) + remap_cs%h(i,j+1,:)) + call interpolate_column(nz_src, h_src, field(i,J,:), & + nz_dest, h_dest, interpolated_field(i,J,:), .true.) + enddo ; enddo + endif elseif ((.not. staggered_in_x) .and. (.not. staggered_in_y)) then ! H-points - do j=G%jsc, G%jec - do i=G%isc, G%iec - if (associated(mask)) then - if (mask(i,j,1) == 0.) cycle - endif - h_src(:) = h(i,j,:) - h_dest(:) = remap_cs%h(i,j,:) - call interpolate_column(nz_src, h_src, field(i,j,:), & - nz_dest, h_dest, interpolated_field(i,j,:), .true.) - enddo - enddo + if (present(mask)) then + do j=G%jsc,G%jec ; do i=G%isc,G%iec ; if (mask(i,j) > 0.0) then + call interpolate_column(nz_src, h(i,j,:), field(i,j,:), & + nz_dest, remap_cs%h(i,j,:), interpolated_field(i,j,:), .true.) + endif ; enddo ; enddo + else + do j=G%jsc,G%jec ; do i=G%isc,G%iec + call interpolate_column(nz_src, h(i,j,:), field(i,j,:), & + nz_dest, remap_cs%h(i,j,:), interpolated_field(i,j,:), .true.) + enddo ; enddo + endif else call assert(.false., 'vertically_interpolate_diag_field: Q point remapping is not coded yet.') endif -end subroutine vertically_interpolate_diag_field +end subroutine vertically_interpolate_field -!> Horizontally average field +!> Horizontally average a diagnostic field subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_in_y, & is_layer, is_extensive, & field, averaged_field, & @@ -654,8 +787,37 @@ subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_i logical, intent(in) :: is_layer !< True if the z-axis location is at h points logical, intent(in) :: is_extensive !< True if the z-direction is spatially integrated (over layers) real, dimension(:,:,:), intent(in) :: field !< The diagnostic field to be remapped [A] - real, dimension(:), intent(inout) :: averaged_field !< Field argument horizontally averaged [A] - logical, dimension(:), intent(inout) :: averaged_mask !< Mask for horizontally averaged field [nondim] + real, dimension(:), intent(out) :: averaged_field !< Field argument horizontally averaged [A] + logical, dimension(:), intent(out) :: averaged_mask !< Mask for horizontally averaged field [nondim] + + ! Local variables + integer :: isdf, jsdf !< The starting i- and j-indices in memory for field + + isdf = G%isd ; if (staggered_in_x) Isdf = G%IsdB + jsdf = G%jsd ; if (staggered_in_y) Jsdf = G%JsdB + + call horizontally_average_field(G, GV, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + is_layer, is_extensive, field, averaged_field, averaged_mask) + +end subroutine horizontally_average_diag_field + +!> Horizontally average a diagnostic field +subroutine horizontally_average_field(G, GV, isdf, jsdf, h, staggered_in_x, staggered_in_y, & + is_layer, is_extensive, field, averaged_field, averaged_mask) + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< The ocean vertical grid structure + integer, intent(in) :: isdf !< The starting i-index in memory for field + integer, intent(in) :: jsdf !< The starting j-index in memory for field + real, dimension(G%isd:,G%jsd:,:), & + intent(in) :: h !< The current thicknesses [H ~> m or kg m-2] + logical, intent(in) :: staggered_in_x !< True if the x-axis location is at u or q points + logical, intent(in) :: staggered_in_y !< True if the y-axis location is at v or q points + logical, intent(in) :: is_layer !< True if the z-axis location is at h points + logical, intent(in) :: is_extensive !< True if the z-direction is spatially integrated (over layers) + real, dimension(isdf:,jsdf:,:), & + intent(in) :: field !< The diagnostic field to be remapped [A] + real, dimension(:), intent(out) :: averaged_field !< Field argument horizontally averaged [A] + logical, dimension(:), intent(out) :: averaged_mask !< Mask for horizontally averaged field [nondim] ! Local variables real :: volume(G%isc:G%iec, G%jsc:G%jec, size(field,3)) ! The area [m2], volume [m3] or mass [kg] of each cell. @@ -670,7 +832,6 @@ subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_i type(EFP_type), dimension(2*size(field,3)) :: sums_EFP ! Sums of volume or stuff by layer real :: height ! An average thickness attributed to an velocity point [H ~> m or kg m-2] integer :: i, j, k, nz - integer :: i1, j1 !< 1-based index nz = size(field, 3) @@ -686,26 +847,23 @@ subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_i stuff_sum(k) = 0. if (is_extensive) then do j=G%jsc, G%jec ; do I=G%isc, G%iec - I1 = I - G%isdB + 1 volume(I,j,k) = (G%US%L_to_m**2 * G%areaCu(I,j)) * G%mask2dCu(I,j) - stuff(I,j,k) = volume(I,j,k) * field(I1,j,k) + stuff(I,j,k) = volume(I,j,k) * field(I,j,k) enddo ; enddo else ! Intensive do j=G%jsc, G%jec ; do I=G%isc, G%iec - I1 = i - G%isdB + 1 height = 0.5 * (h(i,j,k) + h(i+1,j,k)) volume(I,j,k) = (G%US%L_to_m**2 * G%areaCu(I,j)) & * (GV%H_to_MKS * height) * G%mask2dCu(I,j) - stuff(I,j,k) = volume(I,j,k) * field(I1,j,k) + stuff(I,j,k) = volume(I,j,k) * field(I,j,k) enddo ; enddo endif enddo else ! Interface do k=1,nz do j=G%jsc, G%jec ; do I=G%isc, G%iec - I1 = I - G%isdB + 1 volume(I,j,k) = (G%US%L_to_m**2 * G%areaCu(I,j)) * G%mask2dCu(I,j) - stuff(I,j,k) = volume(I,j,k) * field(I1,j,k) + stuff(I,j,k) = volume(I,j,k) * field(I,j,k) enddo ; enddo enddo endif @@ -715,26 +873,23 @@ subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_i do k=1,nz if (is_extensive) then do J=G%jsc, G%jec ; do i=G%isc, G%iec - J1 = J - G%jsdB + 1 volume(i,J,k) = (G%US%L_to_m**2 * G%areaCv(i,J)) * G%mask2dCv(i,J) - stuff(i,J,k) = volume(i,J,k) * field(i,J1,k) + stuff(i,J,k) = volume(i,J,k) * field(i,J,k) enddo ; enddo else ! Intensive do J=G%jsc, G%jec ; do i=G%isc, G%iec - J1 = J - G%jsdB + 1 height = 0.5 * (h(i,j,k) + h(i,j+1,k)) volume(i,J,k) = (G%US%L_to_m**2 * G%areaCv(i,J)) & * (GV%H_to_MKS * height) * G%mask2dCv(i,J) - stuff(i,J,k) = volume(i,J,k) * field(i,J1,k) + stuff(i,J,k) = volume(i,J,k) * field(i,J,k) enddo ; enddo endif enddo else ! Interface do k=1,nz do J=G%jsc, G%jec ; do i=G%isc, G%iec - J1 = J - G%jsdB + 1 volume(i,J,k) = (G%US%L_to_m**2 * G%areaCv(i,J)) * G%mask2dCv(i,J) - stuff(i,J,k) = volume(i,J,k) * field(i,J1,k) + stuff(i,J,k) = volume(i,J,k) * field(i,J,k) enddo ; enddo enddo endif @@ -794,6 +949,6 @@ subroutine horizontally_average_diag_field(G, GV, h, staggered_in_x, staggered_i endif enddo -end subroutine horizontally_average_diag_field +end subroutine horizontally_average_field end module MOM_diag_remap diff --git a/src/framework/MOM_io_file.F90 b/src/framework/MOM_io_file.F90 index 3159a635fa..6697e56f68 100644 --- a/src/framework/MOM_io_file.F90 +++ b/src/framework/MOM_io_file.F90 @@ -1327,7 +1327,13 @@ subroutine open_file_nc(handle, filename, action, MOM_domain, threading, fileset if (present(MOM_domain)) then handle%domain_decomposed = .true. - call hor_index_init(MOM_domain, handle%HI) + + ! Input files use unrotated indexing. + if (associated(MOM_domain%domain_in)) then + call hor_index_init(MOM_domain%domain_in, handle%HI) + else + call hor_index_init(MOM_domain, handle%HI) + endif endif call handle%axes%init() diff --git a/src/initialization/MOM_shared_initialization.F90 b/src/initialization/MOM_shared_initialization.F90 index 46d0448699..821232b80d 100644 --- a/src/initialization/MOM_shared_initialization.F90 +++ b/src/initialization/MOM_shared_initialization.F90 @@ -256,8 +256,8 @@ subroutine apply_topography_edits_from_file(D, G, param_file, US) call close_file_to_read(ncid, topo_edits_file) do n = 1, n_edits - i = ig(n) - G%isd_global + 2 ! +1 for python indexing and +1 for ig-isd_global+1 - j = jg(n) - G%jsd_global + 2 + i = ig(n) - G%idg_offset + 1 ! +1 for python indexing + j = jg(n) - G%jdg_offset + 1 if (i>=G%isc .and. i<=G%iec .and. j>=G%jsc .and. j<=G%jec) then if (new_depth(n) /= mask_depth) then write(stdout,'(a,3i5,f8.2,a,f8.2,2i4)') & diff --git a/src/parameterizations/lateral/MOM_hor_visc.F90 b/src/parameterizations/lateral/MOM_hor_visc.F90 index e3249afb73..6f707f9e87 100644 --- a/src/parameterizations/lateral/MOM_hor_visc.F90 +++ b/src/parameterizations/lateral/MOM_hor_visc.F90 @@ -111,7 +111,7 @@ module MOM_hor_visc !! limit the grid Reynolds number [L2 T-1 ~> m2 s-1] real :: min_grid_Ah !< Minimun horizontal biharmonic viscosity used to !! limit grid Reynolds number [L4 T-1 ~> m4 s-1] - + logical :: use_cont_thick !< If true, thickness at velocity points adopts h[uv] in BT_cont from continuity solver. type(ZB2020_CS) :: ZB2020 !< Zanna-Bolton 2020 control structure. logical :: use_ZB2020 !< If true, use Zanna-Bolton 2020 parameterization. @@ -239,7 +239,7 @@ module MOM_hor_visc !! v[is-2:ie+2,js-2:je+2] !! h[is-1:ie+1,js-1:je+1] subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, & - CS, OBC, BT, TD, ADp) + CS, OBC, BT, TD, ADp, hu_cont, hv_cont) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & @@ -263,6 +263,10 @@ subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, type(barotropic_CS), intent(in), optional :: BT !< Barotropic control structure type(thickness_diffuse_CS), intent(in), optional :: TD !< Thickness diffusion control structure type(accel_diag_ptrs), intent(in), optional :: ADp !< Acceleration diagnostics + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & + optional, intent(in) :: hu_cont !< Layer thickness at u-points [H ~> m or kg m-2]. + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), & + optional, intent(in) :: hv_cont !< Layer thickness at v-points [H ~> m or kg m-2]. ! Local variables real, dimension(SZIB_(G),SZJ_(G)) :: & @@ -391,6 +395,7 @@ subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, logical :: apply_OBC = .false. logical :: use_MEKE_Ku logical :: use_MEKE_Au + logical :: use_cont_huv integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz integer :: i, j, k, n real :: inv_PI3, inv_PI2, inv_PI6 ! Powers of the inverse of pi [nondim] @@ -445,6 +450,8 @@ subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, use_MEKE_Ku = allocated(MEKE%Ku) use_MEKE_Au = allocated(MEKE%Au) + use_cont_huv = CS%use_cont_thick .and. present(hu_cont) .and. present(hv_cont) + rescale_Kh = .false. if (VarMix%use_variable_mixing) then rescale_Kh = VarMix%Resoln_scaled_Kh @@ -554,12 +561,12 @@ subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, !$OMP CS, G, GV, US, OBC, VarMix, MEKE, u, v, h, & !$OMP is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, & !$OMP apply_OBC, rescale_Kh, legacy_bound, find_FrictWork, & - !$OMP use_MEKE_Ku, use_MEKE_Au, & + !$OMP use_MEKE_Ku, use_MEKE_Au, use_cont_huv, & !$OMP backscat_subround, GME_effic_h, GME_effic_q, & !$OMP h_neglect, h_neglect3, inv_PI3, inv_PI6, & !$OMP diffu, diffv, Kh_h, Kh_q, Ah_h, Ah_q, FrictWork, FrictWork_GME, & !$OMP div_xx_h, sh_xx_h, vort_xy_q, sh_xy_q, GME_coeff_h, GME_coeff_q, & - !$OMP KH_u_GME, KH_v_GME, grid_Re_Kh, grid_Re_Ah, NoSt, ShSt & + !$OMP KH_u_GME, KH_v_GME, grid_Re_Kh, grid_Re_Ah, NoSt, ShSt, hu_cont, hv_cont & !$OMP ) & !$OMP private( & !$OMP i, j, k, n, & @@ -658,6 +665,16 @@ subroutine horizontal_viscosity(u, v, h, diffu, diffv, MEKE, VarMix, G, GV, US, enddo ; enddo endif + ! The following should obviously be combined with the previous block if adopted. + if (use_cont_huv) then + do j=js-2,je+2 ; do I=Isq-1,Ieq+1 + h_u(I,j) = hu_cont(I,j,k) + enddo ; enddo + do J=Jsq-1,Jeq+1 ; do i=is-2,ie+2 + h_v(i,J) = hv_cont(i,J,k) + enddo ; enddo + endif + ! Adjust contributions to shearing strain and interpolated values of ! thicknesses on open boundaries. if (apply_OBC) then ; do n=1,OBC%number_of_segments @@ -1969,6 +1986,9 @@ subroutine hor_visc_init(Time, G, GV, US, param_file, diag, CS, ADp) if (.not.GV%Boussinesq) CS%answer_date = max(CS%answer_date, 20230701) call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.) + call get_param(param_file, mdl, "USE_CONT_THICKNESS", CS%use_cont_thick, & + "If true, use thickness at velocity points from continuity solver. This option"//& + "currently only works with split mode.", default=.false.) call get_param(param_file, mdl, "LAPLACIAN", CS%Laplacian, & "If true, use a Laplacian horizontal viscosity.", & default=.false.) diff --git a/src/parameterizations/vertical/MOM_diabatic_aux.F90 b/src/parameterizations/vertical/MOM_diabatic_aux.F90 index 6fdfdd5936..1ba5aef392 100644 --- a/src/parameterizations/vertical/MOM_diabatic_aux.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_aux.F90 @@ -643,7 +643,7 @@ subroutine set_pen_shortwave(optics, fluxes, G, GV, US, CS, opacity, tracer_flow if (CS%chl_from_file) then ! Only the 2-d surface chlorophyll can be read in from a file. The ! same value is assumed for all layers. - call time_interp_external(CS%sbc_chl, CS%Time, chl_2d) + call time_interp_external(CS%sbc_chl, CS%Time, chl_2d, turns=G%HI%turns) do j=js,je ; do i=is,ie if ((G%mask2dT(i,j) > 0.0) .and. (chl_2d(i,j) < 0.0)) then write(mesg,'(" Time_interp negative chl of ",(1pe12.4)," at i,j = ",& @@ -1899,7 +1899,11 @@ subroutine diabatic_aux_init(Time, G, GV, US, param_file, diag, CS, useALEalgori call log_param(param_file, mdl, "INPUTDIR/CHL_FILE", chl_filename) call get_param(param_file, mdl, "CHL_VARNAME", chl_varname, & "Name of CHL_A variable in CHL_FILE.", default='CHL_A') - CS%sbc_chl = init_external_field(chl_filename, trim(chl_varname), MOM_domain=G%Domain) + if (modulo(G%Domain%turns, 4) /= 0) then + CS%sbc_chl = init_external_field(chl_filename, trim(chl_varname), MOM_domain=G%Domain%domain_in) + else + CS%sbc_chl = init_external_field(chl_filename, trim(chl_varname), MOM_domain=G%Domain) + endif endif CS%id_chl = register_diag_field('ocean_model', 'Chl_opac', diag%axesT1, Time, & diff --git a/src/parameterizations/vertical/MOM_set_diffusivity.F90 b/src/parameterizations/vertical/MOM_set_diffusivity.F90 index 3de5ad1162..ef2e4ed5f6 100644 --- a/src/parameterizations/vertical/MOM_set_diffusivity.F90 +++ b/src/parameterizations/vertical/MOM_set_diffusivity.F90 @@ -613,7 +613,8 @@ subroutine set_diffusivity(u, v, h, u_h, v_h, tv, fluxes, optics, visc, dt, Kd_i endif if (allocated(visc%Ray_u) .and. allocated(visc%Ray_v)) then - call uvchksum("Ray_[uv]", visc%Ray_u, visc%Ray_v, G%HI, 0, symmetric=.true., scale=GV%H_to_m*US%s_to_T) + call uvchksum("Ray_[uv]", visc%Ray_u, visc%Ray_v, G%HI, 0, & + symmetric=.true., scale=GV%H_to_m*US%s_to_T, scalar_pair=.true.) endif endif diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index 60298f9226..e601fdf2f7 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -1072,7 +1072,8 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) if (CS%debug) then if (allocated(visc%Ray_u) .and. allocated(visc%Ray_v)) & - call uvchksum("Ray [uv]", visc%Ray_u, visc%Ray_v, G%HI, haloshift=0, scale=GV%H_to_m*US%s_to_T) + call uvchksum("Ray [uv]", visc%Ray_u, visc%Ray_v, G%HI, haloshift=0, & + scale=GV%H_to_m*US%s_to_T, scalar_pair=.true.) if (allocated(visc%kv_bbl_u) .and. allocated(visc%kv_bbl_v)) & call uvchksum("kv_bbl_[uv]", visc%kv_bbl_u, visc%kv_bbl_v, G%HI, & haloshift=0, scale=GV%HZ_T_to_m2_s, scalar_pair=.true.) diff --git a/src/tracer/MOM_generic_tracer.F90 b/src/tracer/MOM_generic_tracer.F90 index 7f550d8de5..f430e94515 100644 --- a/src/tracer/MOM_generic_tracer.F90 +++ b/src/tracer/MOM_generic_tracer.F90 @@ -414,7 +414,8 @@ subroutine initialize_MOM_generic_tracer(restart, day, G, GV, US, h, tv, param_f endif call g_tracer_get_obc_segment_props(g_tracer,g_tracer_name,obc_has ) - if(obc_has .and. g_tracer_is_prog(g_tracer)) call fill_obgc_segments(G, GV, OBC, tr_ptr, g_tracer_name) + if(obc_has .and. g_tracer_is_prog(g_tracer) .and. .not.restart) & + call fill_obgc_segments(G, GV, OBC, tr_ptr, g_tracer_name) !traverse the linked list till hit NULL call g_tracer_get_next(g_tracer, g_tracer_next) if (.NOT. associated(g_tracer_next)) exit diff --git a/src/tracer/MOM_neutral_diffusion.F90 b/src/tracer/MOM_neutral_diffusion.F90 index 87a8881b10..b64c665c87 100644 --- a/src/tracer/MOM_neutral_diffusion.F90 +++ b/src/tracer/MOM_neutral_diffusion.F90 @@ -119,6 +119,10 @@ module MOM_neutral_diffusion !! for remapping. Values below 20190101 recover the remapping !! answers from 2018, while higher values use more robust !! forms of the same remapping expressions. + integer :: ndiff_answer_date !< The vintage of the order of arithmetic to use for the neutral + !! diffusion. Values of 20240330 or below recover the answers + !! from the original form of this code, while higher values use + !! mathematically equivalent expressions that recover rotational symmetry. type(KPP_CS), pointer :: KPP_CSp => NULL() !< KPP control structure needed to get BLD type(energetic_PBL_CS), pointer :: energetic_PBL_CSp => NULL()!< ePBL control structure needed to get MLD end type neutral_diffusion_CS @@ -200,6 +204,16 @@ logical function neutral_diffusion_init(Time, G, GV, US, param_file, diag, EOS, "transports that were unmasked, as used prior to Jan 2018. This is not "//& "recommended.", default=.false.) + call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & + "This sets the default value for the various _ANSWER_DATE parameters.", & + default=99991231) + call get_param(param_file, mdl, "NDIFF_ANSWER_DATE", CS%ndiff_answer_date, & + "The vintage of the order of arithmetic to use for the neutral diffusion. "//& + "Values of 20240330 or below recover the answers from the original form of the "//& + "neutral diffusion code, while higher values use mathematically equivalent "//& + "expressions that recover rotational symmetry.", & + default=20240101) !### Change this default later to default_answer_date. + ! Initialize and configure remapping if ( .not.CS%continuous_reconstruction ) then call get_param(param_file, mdl, "NDIFF_BOUNDARY_EXTRAP", boundary_extrap, & @@ -211,9 +225,6 @@ logical function neutral_diffusion_init(Time, G, GV, US, param_file, diag, EOS, "for vertical remapping for all variables. "//& "It can be one of the following schemes: "//& trim(remappingSchemesDoc), default=remappingDefaultScheme) - call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & - "This sets the default value for the various _ANSWER_DATE parameters.", & - default=99991231) call get_param(param_file, mdl, "REMAPPING_ANSWER_DATE", CS%remap_answer_date, & "The vintage of the expressions and order of arithmetic to use for remapping. "//& "Values below 20190101 result in the use of older, less accurate expressions "//& @@ -623,6 +634,18 @@ subroutine neutral_diffusion(G, GV, h, Coef_x, Coef_y, dt, Reg, US, CS) real, dimension(SZK_(GV)) :: dTracer ! Change in tracer concentration due to neutral diffusion ! [H L2 conc ~> m3 conc or kg conc]. For temperature ! these units are [C H L2 ~> degC m3 or degC kg]. + real, dimension(SZK_(GV)) :: dTracer_N ! Change in tracer concentration due to neutral diffusion + ! into a cell via its logically northern face, in + ! [H L2 conc ~> m3 conc or kg conc]. + real, dimension(SZK_(GV)) :: dTracer_S ! Change in tracer concentration due to neutral diffusion + ! into a cell via its logically southern face, in + ! [H L2 conc ~> m3 conc or kg conc]. + real, dimension(SZK_(GV)) :: dTracer_E ! Change in tracer concentration due to neutral diffusion + ! into a cell via its logically eastern face, in + ! [H L2 conc ~> m3 conc or kg conc]. + real, dimension(SZK_(GV)) :: dTracer_W ! Change in tracer concentration due to neutral diffusion + ! into a cell via its logically western face, in + ! [H L2 conc ~> m3 conc or kg conc]. real :: normalize ! normalization used for averaging Coef_x and Coef_y to t-points [nondim]. type(tracer_type), pointer :: Tracer => NULL() ! Pointer to the current tracer @@ -800,21 +823,39 @@ subroutine neutral_diffusion(G, GV, h, Coef_x, Coef_y, dt, Reg, US, CS) endif endif - ! Update the tracer concentration from divergence of neutral diffusive flux components + ! Update the tracer concentration from divergence of neutral diffusive flux components, noting + ! that uFlx and vFlx use an unexpected sign convention. if (CS%KhTh_use_ebt_struct) then do j = G%jsc,G%jec ; do i = G%isc,G%iec if (G%mask2dT(i,j)>0.) then - dTracer(:) = 0. - do ks = 1,CS%nsurf-1 - k = CS%uKoL(I,j,ks) - dTracer(k) = dTracer(k) + uFlx(I,j,ks) - k = CS%uKoR(I-1,j,ks) - dTracer(k) = dTracer(k) - uFlx(I-1,j,ks) - k = CS%vKoL(i,J,ks) - dTracer(k) = dTracer(k) + vFlx(i,J,ks) - k = CS%vKoR(i,J-1,ks) - dTracer(k) = dTracer(k) - vFlx(i,J-1,ks) - enddo + if (CS%ndiff_answer_date <= 20240330) then + dTracer(:) = 0. + do ks = 1,CS%nsurf-1 + k = CS%uKoL(I,j,ks) + dTracer(k) = dTracer(k) + uFlx(I,j,ks) + k = CS%uKoR(I-1,j,ks) + dTracer(k) = dTracer(k) - uFlx(I-1,j,ks) + k = CS%vKoL(i,J,ks) + dTracer(k) = dTracer(k) + vFlx(i,J,ks) + k = CS%vKoR(i,J-1,ks) + dTracer(k) = dTracer(k) - vFlx(i,J-1,ks) + enddo + else ! This form recovers rotational symmetry. + dTracer_N(:) = 0.0 ; dTracer_S(:) = 0.0 ; dTracer_E(:) = 0.0 ; dTracer_W(:) = 0.0 + do ks = 1,CS%nsurf-1 + k = CS%uKoL(I,j,ks) + dTracer_E(k) = dTracer_E(k) + uFlx(I,j,ks) + k = CS%uKoR(I-1,j,ks) + dTracer_W(k) = dTracer_W(k) - uFlx(I-1,j,ks) + k = CS%vKoL(i,J,ks) + dTracer_N(k) = dTracer_N(k) + vFlx(i,J,ks) + k = CS%vKoR(i,J-1,ks) + dTracer_S(k) = dTracer_S(k) - vFlx(i,J-1,ks) + enddo + do k = 1, GV%ke + dTracer(k) = (dTracer_N(k) + dTracer_S(k)) + (dTracer_E(k) + dTracer_W(k)) + enddo + endif do k = 1, GV%ke tracer%t(i,j,k) = tracer%t(i,j,k) + dTracer(k) * & ( G%IareaT(i,j) / ( h(i,j,k) + GV%H_subroundoff ) ) @@ -832,17 +873,34 @@ subroutine neutral_diffusion(G, GV, h, Coef_x, Coef_y, dt, Reg, US, CS) else do j = G%jsc,G%jec ; do i = G%isc,G%iec if (G%mask2dT(i,j)>0.) then - dTracer(:) = 0. - do ks = 1,CS%nsurf-1 - k = CS%uKoL(I,j,ks) - dTracer(k) = dTracer(k) + Coef_x(I,j,1) * uFlx(I,j,ks) - k = CS%uKoR(I-1,j,ks) - dTracer(k) = dTracer(k) - Coef_x(I-1,j,1) * uFlx(I-1,j,ks) - k = CS%vKoL(i,J,ks) - dTracer(k) = dTracer(k) + Coef_y(i,J,1) * vFlx(i,J,ks) - k = CS%vKoR(i,J-1,ks) - dTracer(k) = dTracer(k) - Coef_y(i,J-1,1) * vFlx(i,J-1,ks) - enddo + if (CS%ndiff_answer_date <= 20240330) then + dTracer(:) = 0. + do ks = 1,CS%nsurf-1 + k = CS%uKoL(I,j,ks) + dTracer(k) = dTracer(k) + Coef_x(I,j,1) * uFlx(I,j,ks) + k = CS%uKoR(I-1,j,ks) + dTracer(k) = dTracer(k) - Coef_x(I-1,j,1) * uFlx(I-1,j,ks) + k = CS%vKoL(i,J,ks) + dTracer(k) = dTracer(k) + Coef_y(i,J,1) * vFlx(i,J,ks) + k = CS%vKoR(i,J-1,ks) + dTracer(k) = dTracer(k) - Coef_y(i,J-1,1) * vFlx(i,J-1,ks) + enddo + else ! This form recovers rotational symmetry. + dTracer_N(:) = 0.0 ; dTracer_S(:) = 0.0 ; dTracer_E(:) = 0.0 ; dTracer_W(:) = 0.0 + do ks = 1,CS%nsurf-1 + k = CS%uKoL(I,j,ks) + dTracer_E(k) = dTracer_E(k) + Coef_x(I,j,1) * uFlx(I,j,ks) + k = CS%uKoR(I-1,j,ks) + dTracer_W(k) = dTracer_W(k) - Coef_x(I-1,j,1) * uFlx(I-1,j,ks) + k = CS%vKoL(i,J,ks) + dTracer_N(k) = dTracer_N(k) + Coef_y(i,J,1) * vFlx(i,J,ks) + k = CS%vKoR(i,J-1,ks) + dTracer_S(k) = dTracer_S(k) - Coef_y(i,J-1,1) * vFlx(i,J-1,ks) + enddo + do k = 1, GV%ke + dTracer(k) = (dTracer_N(k) + dTracer_S(k)) + (dTracer_E(k) + dTracer_W(k)) + enddo + endif do k = 1, GV%ke tracer%t(i,j,k) = tracer%t(i,j,k) + dTracer(k) * & ( G%IareaT(i,j) / ( h(i,j,k) + GV%H_subroundoff ) ) diff --git a/src/user/MOM_wave_interface.F90 b/src/user/MOM_wave_interface.F90 index fa7b567845..9a951cf655 100644 --- a/src/user/MOM_wave_interface.F90 +++ b/src/user/MOM_wave_interface.F90 @@ -673,7 +673,7 @@ subroutine Update_Surface_Waves(G, GV, US, Time_present, dt, CS, forces) type(mech_forcing), intent(in), optional :: forces !< MOM_forcing_type ! Local variables type(time_type) :: Stokes_Time - integer :: ii, jj, b + integer :: i, j, b if (CS%WaveMethod == TESTPROF) then ! Do nothing @@ -701,37 +701,37 @@ subroutine Update_Surface_Waves(G, GV, US, Time_present, dt, CS, forces) do b=1,CS%NumBands CS%WaveNum_Cen(b) = forces%stk_wavenumbers(b) !Interpolate from a grid to c grid - do jj=G%jsc,G%jec - do II=G%iscB,G%iecB - CS%STKx0(II,jj,b) = 0.5*(forces%UStkb(ii,jj,b)+forces%UStkb(ii+1,jj,b)) + do j=G%jsc,G%jec + do I=G%iscB,G%iecB + CS%STKx0(I,j,b) = 0.5*(forces%UStkb(i,j,b)+forces%UStkb(i+1,j,b)) enddo enddo - do JJ=G%jscB, G%jecB - do ii=G%isc,G%iec - CS%STKY0(ii,JJ,b) = 0.5*(forces%VStkb(ii,jj,b)+forces%VStkb(ii,jj+1,b)) + do J=G%jscB,G%jecB + do i=G%isc,G%iec + CS%STKY0(i,J,b) = 0.5*(forces%VStkb(i,j,b)+forces%VStkb(i,j+1,b)) enddo enddo call pass_vector(CS%STKx0(:,:,b),CS%STKy0(:,:,b), G%Domain) enddo - do jj=G%jsc,G%jec - do ii=G%isc,G%iec - !CS%Omega_w2x(ii,jj) = forces%omega_w2x(ii,jj) + do j=G%jsc,G%jec + do i=G%isc,G%iec + !CS%Omega_w2x(i,j) = forces%omega_w2x(i,j) do b=1,CS%NumBands - CS%UStk_Hb(ii,jj,b) = US%m_s_to_L_T*forces%UStkb(ii,jj,b) - CS%VStk_Hb(ii,jj,b) = US%m_s_to_L_T*forces%VStkb(ii,jj,b) + CS%UStk_Hb(i,j,b) = US%m_s_to_L_T*forces%UStkb(i,j,b) + CS%VStk_Hb(i,j,b) = US%m_s_to_L_T*forces%VStkb(i,j,b) enddo enddo enddo elseif (CS%DataSource == INPUT) then do b=1,CS%NumBands - do jj=G%jsd,G%jed - do II=G%isdB,G%iedB - CS%STKx0(II,jj,b) = CS%PrescribedSurfStkX(b) + do j=G%jsd,G%jed + do I=G%isdB,G%iedB + CS%STKx0(I,j,b) = CS%PrescribedSurfStkX(b) enddo enddo - do JJ=G%jsdB, G%jedB - do ii=G%isd,G%ied - CS%STKY0(ii,JJ,b) = CS%PrescribedSurfStkY(b) + do J=G%jsdB, G%jedB + do i=G%isd,G%ied + CS%STKY0(i,J,b) = CS%PrescribedSurfStkY(b) enddo enddo enddo @@ -763,7 +763,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) real :: UStokes ! A Stokes drift velocity [L T-1 ~> m s-1] real :: PI ! 3.1415926535... [nondim] real :: La ! The local Langmuir number [nondim] - integer :: ii, jj, kk, b, iim1, jjm1 + integer :: i, j, k, b real :: I_dt ! The inverse of the time step [T-1 ~> s-1] if (CS%WaveMethod==EFACTOR) return @@ -777,29 +777,27 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) if (CS%WaveMethod==TESTPROF) then PI = 4.0*atan(1.0) DecayScale = 4.*PI / CS%TP_WVL !4pi - do jj = G%jsc,G%jec - do II = G%iscB,G%iecB - IIm1 = max(1,II-1) + do j=G%jsc,G%jec + do I=G%iscB,G%iecB Bottom = 0.0 MidPoint = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - MidPoint = Bottom - 0.25*(dz(II,jj,kk)+dz(IIm1,jj,kk)) - Bottom = Bottom - 0.5*(dz(II,jj,kk)+dz(IIm1,jj,kk)) - CS%Us_x(II,jj,kk) = CS%TP_STKX0*exp(MidPoint*DecayScale) + MidPoint = Bottom - 0.25*(dz(I,j,k)+dz(I-1,j,k)) + Bottom = Bottom - 0.5*(dz(I,j,k)+dz(I-1,j,k)) + CS%Us_x(I,j,k) = CS%TP_STKX0*exp(MidPoint*DecayScale) enddo enddo enddo - do JJ = G%jscB,G%jecB - do ii = G%isc,G%iec - JJm1 = max(1,JJ-1) + do J=G%jscB,G%jecB + do i=G%isc,G%iec Bottom = 0.0 MidPoint = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - MidPoint = Bottom - 0.25*(dz(ii,JJ,kk)+dz(ii,JJm1,kk)) - Bottom = Bottom - 0.5*(dz(ii,JJ,kk)+dz(ii,JJm1,kk)) - CS%Us_y(ii,JJ,kk) = CS%TP_STKY0*exp(MidPoint*DecayScale) + MidPoint = Bottom - 0.25*(dz(i,J,k)+dz(i,J-1,k)) + Bottom = Bottom - 0.5*(dz(i,J,k)+dz(i,J-1,k)) + CS%Us_y(i,J,k) = CS%TP_STKY0*exp(MidPoint*DecayScale) enddo enddo enddo @@ -813,19 +811,18 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) CS%Us0_x(:,:) = 0.0 CS%Us0_y(:,:) = 0.0 ! Computing X direction Stokes drift - do jj = G%jsc,G%jec - do II = G%iscB,G%iecB + do j=G%jsc,G%jec + do I=G%iscB,G%iecB ! 1. First compute the surface Stokes drift ! by summing over the partitions. do b = 1,CS%NumBands - CS%US0_x(II,jj) = CS%US0_x(II,jj) + CS%STKx0(II,jj,b) + CS%US0_x(I,j) = CS%US0_x(I,j) + CS%STKx0(I,j,b) enddo ! 2. Second compute the level averaged Stokes drift bottom = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - IIm1 = max(II-1,1) - level_thick = 0.5*(dz(II,jj,kk)+dz(IIm1,jj,kk)) + level_thick = 0.5*(dz(I,j,k)+dz(I-1,j,k)) MidPoint = Top - 0.5*level_thick Bottom = Top - level_thick @@ -840,7 +837,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) WN = CS%Freq_Cen(b)**2 * CS%I_g_Earth CMN_FAC = exp(2.*WN*Top) * one_minus_exp_x(2.*WN*level_thick) endif - CS%US_x(II,jj,kk) = CS%US_x(II,jj,kk) + CS%STKx0(II,jj,b)*CMN_FAC + CS%US_x(I,j,k) = CS%US_x(I,j,k) + CS%STKx0(I,j,b)*CMN_FAC enddo elseif (level_thick > CS%Stokes_min_thick_avg) then @@ -855,7 +852,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) WN = CS%Freq_Cen(b)**2 / CS%g_Earth !bgr bug-fix missing g CMN_FAC = (exp(2.*WN*Top)-exp(2.*WN*Bottom)) / (2.*WN*(Top-Bottom)) endif - CS%US_x(II,jj,kk) = CS%US_x(II,jj,kk) + CS%STKx0(II,jj,b)*CMN_FAC + CS%US_x(I,j,k) = CS%US_x(I,j,k) + CS%STKx0(I,j,b)*CMN_FAC enddo else ! Take the value at the midpoint do b = 1,CS%NumBands @@ -864,7 +861,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) else CMN_FAC = exp(MidPoint * 2. * CS%Freq_Cen(b)**2 / CS%g_Earth) endif - CS%US_x(II,jj,kk) = CS%US_x(II,jj,kk) + CS%STKx0(II,jj,b)*CMN_FAC + CS%US_x(I,j,k) = CS%US_x(I,j,k) + CS%STKx0(I,j,b)*CMN_FAC enddo endif enddo @@ -872,18 +869,17 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) enddo ! Computing Y direction Stokes drift - do JJ = G%jscB,G%jecB - do ii = G%isc,G%iec + do J=G%jscB,G%jecB + do i=G%isc,G%iec ! Set the surface value to that at z=0 do b = 1,CS%NumBands - CS%US0_y(ii,JJ) = CS%US0_y(ii,JJ) + CS%STKy0(ii,JJ,b) + CS%US0_y(i,J) = CS%US0_y(i,J) + CS%STKy0(i,J,b) enddo ! Compute the level averages. bottom = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - JJm1 = max(JJ-1,1) - level_thick = 0.5*(dz(ii,JJ,kk)+dz(ii,JJm1,kk)) + level_thick = 0.5*(dz(i,J,k)+dz(i,J-1,k)) MidPoint = Top - 0.5*level_thick Bottom = Top - level_thick @@ -898,7 +894,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) WN = CS%Freq_Cen(b)**2 * CS%I_g_Earth CMN_FAC = exp(2.*WN*Top) * one_minus_exp_x(2.*WN*level_thick) endif - CS%US_y(ii,JJ,kk) = CS%US_y(ii,JJ,kk) + CS%STKy0(ii,JJ,b)*CMN_FAC + CS%US_y(i,J,k) = CS%US_y(i,J,k) + CS%STKy0(i,J,b)*CMN_FAC enddo elseif (level_thick > CS%Stokes_min_thick_avg) then ! -> Stokes drift in thin layers not averaged. @@ -912,7 +908,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) WN = CS%Freq_Cen(b)**2 / CS%g_Earth !bgr bug-fix missing g CMN_FAC = (exp(2.*WN*Top)-exp(2.*WN*Bottom)) / (2.*WN*(Top-Bottom)) endif - CS%US_y(ii,JJ,kk) = CS%US_y(ii,JJ,kk) + CS%STKy0(ii,JJ,b)*CMN_FAC + CS%US_y(i,J,k) = CS%US_y(i,J,k) + CS%STKy0(i,J,b)*CMN_FAC enddo else ! Take the value at the midpoint do b = 1,CS%NumBands @@ -921,7 +917,7 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) else CMN_FAC = exp(MidPoint * 2. * CS%Freq_Cen(b)**2 / CS%g_Earth) endif - CS%US_y(ii,JJ,kk) = CS%US_y(ii,JJ,kk) + CS%STKy0(ii,JJ,b)*CMN_FAC + CS%US_y(i,J,k) = CS%US_y(i,J,k) + CS%STKy0(i,J,b)*CMN_FAC enddo endif enddo @@ -931,39 +927,37 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) call pass_vector(CS%Us0_x(:,:),CS%Us0_y(:,:), G%Domain) elseif (CS%WaveMethod == DHH85) then if (.not.(CS%StaticWaves .and. CS%DHH85_is_set)) then - do jj = G%jsc,G%jec - do II = G%iscB,G%iecB + do j=G%jsc,G%jec + do I=G%iscB,G%iecB bottom = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - IIm1 = max(II-1,1) - MidPoint = Top - 0.25*(dz(II,jj,kk)+dz(IIm1,jj,kk)) - Bottom = Top - 0.5*(dz(II,jj,kk)+dz(IIm1,jj,kk)) - !bgr note that this is using a u-point ii on h-point ustar + MidPoint = Top - 0.25*(dz(I,j,k)+dz(I-1,j,k)) + Bottom = Top - 0.5*(dz(I,j,k)+dz(I-1,j,k)) + !bgr note that this is using a u-point I on h-point ustar ! this code has only been previous used for uniform ! grid cases. This needs fixed if DHH85 is used for non ! uniform cases. call DHH85_mid(GV, US, CS, MidPoint, UStokes) ! Putting into x-direction (no option for direction - CS%US_x(II,jj,kk) = UStokes + CS%US_x(I,j,k) = UStokes enddo enddo enddo - do JJ = G%jscB,G%jecB - do ii = G%isc,G%iec + do J=G%jscB,G%jecB + do i=G%isc,G%iec Bottom = 0.0 - do kk=1, GV%ke + do k = 1,GV%ke Top = Bottom - JJm1 = max(JJ-1,1) - MidPoint = Bottom - 0.25*(dz(ii,JJ,kk)+dz(ii,JJm1,kk)) - Bottom = Bottom - 0.5*(dz(ii,JJ,kk)+dz(ii,JJm1,kk)) - !bgr note that this is using a v-point jj on h-point ustar + MidPoint = Bottom - 0.25*(dz(i,J,k)+dz(i,J-1,k)) + Bottom = Bottom - 0.5*(dz(i,J,k)+dz(i,J-1,k)) + !bgr note that this is using a v-point J on h-point ustar ! this code has only been previous used for uniform ! grid cases. This needs fixed if DHH85 is used for non ! uniform cases. ! call DHH85_mid(GV, US, CS, Midpoint, UStokes) ! Putting into x-direction, so setting y direction to 0 - CS%US_y(ii,JJ,kk) = 0.0 + CS%US_y(i,J,k) = 0.0 ! For rotational symmetry there should be the option for this to become = UStokes ! bgr - see note above, but this is true ! if this is used for anything @@ -975,28 +969,18 @@ subroutine Update_Stokes_Drift(G, GV, US, CS, dz, ustar, dt, dynamics_step) endif call pass_vector(CS%Us_x(:,:,:),CS%Us_y(:,:,:), G%Domain) else! Keep this else, fallback to 0 Stokes drift - do kk= 1,GV%ke - do jj = G%jsd,G%jed - do II = G%isdB,G%iedB - CS%Us_x(II,jj,kk) = 0. - enddo - enddo - do JJ = G%jsdB,G%jedB - do ii = G%isd,G%ied - CS%Us_y(ii,JJ,kk) = 0. - enddo - enddo - enddo + CS%Us_x(:,:,:) = 0. + CS%Us_y(:,:,:) = 0. endif ! Turbulent Langmuir number is computed here and available to use anywhere. ! SL Langmuir number requires mixing layer depth, and therefore is computed ! in the routine it is needed by (e.g. KPP or ePBL). - do jj = G%jsc, G%jec - do ii = G%isc,G%iec - call get_Langmuir_Number( La, G, GV, US, dz(ii,jj,1), ustar(ii,jj), ii, jj, & - dz(ii,jj,:), CS, Override_MA=.false.) - CS%La_turb(ii,jj) = La + do j=G%jsc, G%jec + do i=G%isc,G%iec + call get_Langmuir_Number( La, G, GV, US, dz(i,j,1), ustar(i,j), i, j, & + dz(i,j,:), CS, Override_MA=.false.) + CS%La_turb(i,j) = La enddo enddo @@ -1197,7 +1181,7 @@ subroutine get_Langmuir_Number( LA, G, GV, US, HBL, ustar, i, j, dz, Waves, & logical :: ContinueLoop, USE_MA real, dimension(SZK_(GV)) :: US_H, VS_H ! Profiles of Stokes velocities [L T-1 ~> m s-1] real, allocatable :: StkBand_X(:), StkBand_Y(:) ! Stokes drifts by band [L T-1 ~> m s-1] - integer :: KK, BB + integer :: k, BB ! Compute averaging depth for Stokes drift (negative) Dpt_LASL = -1.0*max(Waves%LA_FracHBL*HBL, Waves%LA_HBL_min) @@ -1211,24 +1195,24 @@ subroutine get_Langmuir_Number( LA, G, GV, US, HBL, ustar, i, j, dz, Waves, & "Get_LA_waves requested to consider misalignment, but velocities were not provided.") ContinueLoop = .true. bottom = 0.0 - do kk = 1,GV%ke + do k = 1,GV%ke Top = Bottom - MidPoint = Bottom + 0.5*dz(kk) - Bottom = Bottom + dz(kk) + MidPoint = Bottom + 0.5*dz(k) + Bottom = Bottom + dz(k) !### Given the sign convention that Dpt_LASL is negative, the next line seems to have a bug. ! To correct this bug, this line should be changed to: - ! if (MidPoint > abs(Dpt_LASL) .and. (kk > 1) .and. ContinueLoop) then - if (MidPoint > Dpt_LASL .and. kk > 1 .and. ContinueLoop) then - ShearDirection = atan2(V_H(1)-V_H(kk),U_H(1)-U_H(kk)) + ! if (MidPoint > abs(Dpt_LASL) .and. (k > 1) .and. ContinueLoop) then + if (MidPoint > Dpt_LASL .and. k > 1 .and. ContinueLoop) then + ShearDirection = atan2(V_H(1)-V_H(k), U_H(1)-U_H(k)) ContinueLoop = .false. endif enddo endif if (Waves%WaveMethod==TESTPROF) then - do kk = 1,GV%ke - US_H(kk) = 0.5*(Waves%US_X(I,j,kk)+Waves%US_X(I-1,j,kk)) - VS_H(kk) = 0.5*(Waves%US_Y(i,J,kk)+Waves%US_Y(i,J-1,kk)) + do k = 1,GV%ke + US_H(k) = 0.5*(Waves%US_X(I,j,k)+Waves%US_X(I-1,j,k)) + VS_H(k) = 0.5*(Waves%US_Y(i,J,k)+Waves%US_Y(i,J-1,k)) enddo call Get_SL_Average_Prof( GV, Dpt_LASL, dz, US_H, LA_STKx) call Get_SL_Average_Prof( GV, Dpt_LASL, dz, VS_H, LA_STKy) @@ -1245,9 +1229,9 @@ subroutine get_Langmuir_Number( LA, G, GV, US, HBL, ustar, i, j, dz, Waves, & deallocate(StkBand_X, StkBand_Y) elseif (Waves%WaveMethod==DHH85) then ! Temporarily integrating profile rather than spectrum for simplicity - do kk = 1,GV%ke - US_H(kk) = 0.5*(Waves%US_X(I,j,kk)+Waves%US_X(I-1,j,kk)) - VS_H(kk) = 0.5*(Waves%US_Y(i,J,kk)+Waves%US_Y(i,J-1,kk)) + do k = 1,GV%ke + US_H(k) = 0.5*(Waves%US_X(I,j,k)+Waves%US_X(I-1,j,k)) + VS_H(k) = 0.5*(Waves%US_Y(i,J,k)+Waves%US_Y(i,J-1,k)) enddo call Get_SL_Average_Prof( GV, Dpt_LASL, dz, US_H, LA_STKx) call Get_SL_Average_Prof( GV, Dpt_LASL, dz, VS_H, LA_STKy) @@ -1451,20 +1435,20 @@ subroutine Get_SL_Average_Prof( GV, AvgDepth, dz, Profile, Average ) !Local variables real :: Top, Bottom ! Depths, negative downward [Z ~> m] real :: Sum ! The depth weighted vertical sum of a quantity [A Z ~> A m] - integer :: kk + integer :: k ! Initializing sum Sum = 0.0 ! Integrate bottom = 0.0 - do kk = 1, GV%ke + do k = 1, GV%ke Top = Bottom - Bottom = Bottom - dz(kk) + Bottom = Bottom - dz(k) if (AvgDepth < Bottom) then ! The whole cell is within H_LA - Sum = Sum + Profile(kk) * dz(kk) + Sum = Sum + Profile(k) * dz(k) elseif (AvgDepth < Top) then ! A partial cell is within H_LA - Sum = Sum + Profile(kk) * (Top-AvgDepth) + Sum = Sum + Profile(k) * (Top-AvgDepth) exit else exit