← back to index

src/equation_of_state/MOM_EOS_Wright.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> The equation of state using a poor implementation (missing parenthesis and bugs) of the
6!! reduced range Wright 1997 expressions
7module MOM_EOS_Wright
8
9use MOM_EOS_base_type, only : EOS_base
10use MOM_hor_index, only : hor_index_type
11
12implicit none ; private
13
14public buggy_Wright_EOS
15public int_density_dz_wright, int_spec_vol_dp_wright
16public avg_spec_vol_buggy_Wright
17public set_params_buggy_Wright
18
19!>@{ Parameters in the Wright equation of state using the reduced range formula, which is a fit to the UNESCO
20! equation of state for the restricted range: -2 < theta < 30 [degC], 28 < S < 38 [PSU], 0 < p < 5e7 [Pa].
21
22 ! Note that a0/a1 ~= 2028 [degC] ; a0/a2 ~= -6343 [PSU]
23 ! b0/b1 ~= 165 [degC] ; b0/b4 ~= 974 [PSU]
24 ! c0/c1 ~= 216 [degC] ; c0/c4 ~= -740 [PSU]
25real, parameter :: a0 = 7.057924e-4 ! A parameter in the Wright alpha_0 fit [m3 kg-1]
26real, parameter :: a1 = 3.480336e-7 ! A parameter in the Wright alpha_0 fit [m3 kg-1 degC-1]
27real, parameter :: a2 = -1.112733e-7 ! A parameter in the Wright alpha_0 fit [m3 kg-1 PSU-1]
28real, parameter :: b0 = 5.790749e8 ! A parameter in the Wright p_0 fit [Pa]
29real, parameter :: b1 = 3.516535e6 ! A parameter in the Wright p_0 fit [Pa degC-1]
30real, parameter :: b2 = -4.002714e4 ! A parameter in the Wright p_0 fit [Pa degC-2]
31real, parameter :: b3 = 2.084372e2 ! A parameter in the Wright p_0 fit [Pa degC-3]
32real, parameter :: b4 = 5.944068e5 ! A parameter in the Wright p_0 fit [Pa PSU-1]
33real, parameter :: b5 = -9.643486e3 ! A parameter in the Wright p_0 fit [Pa degC-1 PSU-1]
34real, parameter :: c0 = 1.704853e5 ! A parameter in the Wright lambda fit [m2 s-2]
35real, parameter :: c1 = 7.904722e2 ! A parameter in the Wright lambda fit [m2 s-2 degC-1]
36real, parameter :: c2 = -7.984422 ! A parameter in the Wright lambda fit [m2 s-2 degC-2]
37real, parameter :: c3 = 5.140652e-2 ! A parameter in the Wright lambda fit [m2 s-2 degC-3]
38real, parameter :: c4 = -2.302158e2 ! A parameter in the Wright lambda fit [m2 s-2 PSU-1]
39real, parameter :: c5 = -3.079464 ! A parameter in the Wright lambda fit [m2 s-2 degC-1 PSU-1]
40!>@}
41
42!> The EOS_base implementation of the Wright 1997 equation of state with some bugs
43type, extends (EOS_base) :: buggy_Wright_EOS
44
45 real :: three = 3.0 !< A constant that can be adjusted to recreate some bugs [nondim]
46
47contains
48 !> Implementation of the in-situ density as an elemental function [kg m-3]
49 procedure :: density_elem => density_elem_buggy_Wright
50 !> Implementation of the in-situ density anomaly as an elemental function [kg m-3]
51 procedure :: density_anomaly_elem => density_anomaly_elem_buggy_Wright
52 !> Implementation of the in-situ specific volume as an elemental function [m3 kg-1]
53 procedure :: spec_vol_elem => spec_vol_elem_buggy_Wright
54 !> Implementation of the in-situ specific volume anomaly as an elemental function [m3 kg-1]
55 procedure :: spec_vol_anomaly_elem => spec_vol_anomaly_elem_buggy_Wright
56 !> Implementation of the calculation of derivatives of density
57 procedure :: calculate_density_derivs_elem => calculate_density_derivs_elem_buggy_Wright
58 !> Implementation of the calculation of second derivatives of density
59 procedure :: calculate_density_second_derivs_elem => calculate_density_second_derivs_elem_buggy_Wright
60 !> Implementation of the calculation of derivatives of specific volume
61 procedure :: calculate_specvol_derivs_elem => calculate_specvol_derivs_elem_buggy_Wright
62 !> Implementation of the calculation of compressibility
63 procedure :: calculate_compress_elem => calculate_compress_elem_buggy_Wright
64 !> Implementation of the range query function
65 procedure :: EOS_fit_range => EOS_fit_range_buggy_Wright
66
67 !> Instance specific function to set internal parameters
68 procedure :: set_params_buggy_Wright => set_params_buggy_Wright
69
70 !> Local implementation of generic calculate_density_array for efficiency
71 procedure :: calculate_density_array => calculate_density_array_buggy_Wright
72 !> Local implementation of generic calculate_density_array_2d for efficiency
73 procedure :: calculate_density_array_2d => calculate_density_array_2d_buggy_Wright
74 !> Local implementation of generic calculate_density_array_3d for efficiency
75 procedure :: calculate_density_array_3d => calculate_density_array_3d_buggy_Wright
76 !> Local implementation of generic calculate_spec_vol_array for efficiency
77 procedure :: calculate_spec_vol_array => calculate_spec_vol_array_buggy_Wright
78 !> Local implementation of generic calculate_density_derivs_2d for efficiency
79 procedure :: calculate_density_derivs_2d => calculate_density_derivs_2d_buggy_Wright
80 !> Local implementation of generic calculate_density_derivs_3d for efficiency
81 procedure :: calculate_density_derivs_3d => calculate_density_derivs_3d_buggy_Wright
82
83end type buggy_Wright_EOS
84
85contains
86
87!> In situ density of sea water using a buggy implementation of Wright, 1997 [kg m-3]
88!!
89!! This is an elemental function that can be applied to any combination of scalar and array inputs.
907065260real elemental function density_elem_buggy_Wright_loc(T, S, pressure)
91 real, intent(in) :: T !< potential temperature relative to the surface [degC].
92 real, intent(in) :: S !< salinity [PSU].
93 real, intent(in) :: pressure !< pressure [Pa].
94
95 ! Local variables
96 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
97 real :: p0 ! The pressure offset in the Wright EOS [Pa]
98 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
99
1007065260 al0 = (a0 + a1*T) +a2*S
1017065260 p0 = (b0 + b4*S) + T * (b1 + T*(b2 + b3*T) + b5*S)
1027065260 lambda = (c0 +c4*S) + T * (c1 + T*(c2 + c3*T) + c5*S)
1037065260 density_elem_buggy_Wright_loc = (pressure + p0) / (lambda + al0*(pressure + p0))
104
1057065260end function density_elem_buggy_Wright_loc
106
107!> Wrapper for density_elem_buggy_Wright_loc created to preserve API while calling
108!! density_elem_buggy_Wright without "this" variable that causes runtime errors on
109!! gpu runs with nvfortran.
1102real elemental function density_elem_buggy_Wright(this, T, S, pressure)
111 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
112 real, intent(in) :: T !< potential temperature relative to the surface [degC].
113 real, intent(in) :: S !< salinity [PSU].
114 real, intent(in) :: pressure !< pressure [Pa].
115
1162 density_elem_buggy_Wright = density_elem_buggy_Wright_loc(T, S, pressure)
1172end function density_elem_buggy_Wright
118
119!> In situ density anomaly of sea water using a buggy implementation of Wright, 1997 [kg m-3]
120!!
121!! This is an elemental function that can be applied to any combination of scalar and array inputs.
1220real elemental function density_anomaly_elem_buggy_Wright(this, T, S, pressure, rho_ref)
123 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
124 real, intent(in) :: T !< potential temperature relative to the surface [degC].
125 real, intent(in) :: S !< salinity [PSU].
126 real, intent(in) :: pressure !< pressure [Pa].
127 real, intent(in) :: rho_ref !< A reference density [kg m-3].
128
129 ! Local variables
130 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
131 real :: p0 ! The pressure offset in the Wright EOS [Pa]
132 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
133 real :: al_TS ! The contributions of temperature and salinity to al0 [m3 kg-1]
134 real :: p_TSp ! A combination of the pressure and the temperature and salinity contributions to p0 [Pa]
135 real :: lam_TS ! The contributions of temperature and salinity to lambda [m2 s-2]
136 real :: pa_000 ! A corrected offset to the pressure, including contributions from rho_ref [Pa]
137
1380 pa_000 = (b0*(1.0 - a0*rho_ref) - rho_ref*c0)
1390 al_TS = a1*T +a2*S
1400 al0 = a0 + al_TS
1410 p_TSp = pressure + (b4*S + T * (b1 + (T*(b2 + b3*T) + b5*S)))
1420 lam_TS = c4*S + T * (c1 + (T*(c2 + c3*T) + c5*S))
143
144 ! The following two expressions are mathematically equivalent.
145 ! wright_density = (b0 + p0_TSp) / ((c0 + lam_TS) + al0*(b0 + p0_TSp)) - rho_ref
146 density_anomaly_elem_buggy_Wright = &
147 (pa_000 + (p_TSp - rho_ref*(p_TSp*al0 + (b0*al_TS + lam_TS)))) / &
1480 ( (c0 + lam_TS) + al0*(b0 + p_TSp) )
149
1500end function density_anomaly_elem_buggy_Wright
151
152!> In situ specific volume of sea water using a buggy implementation of Wright, 1997 [kg m-3]
153!!
154!! This is an elemental function that can be applied to any combination of scalar and array inputs.
1550real elemental function spec_vol_elem_buggy_Wright(this, T, S, pressure)
156 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
157 real, intent(in) :: T !< potential temperature relative to the surface [degC].
158 real, intent(in) :: S !< salinity [PSU].
159 real, intent(in) :: pressure !< pressure [Pa].
160
161 ! Local variables
162 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
163 real :: p0 ! The pressure offset in the Wright EOS [Pa]
164 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
165
1660 al0 = (a0 + a1*T) +a2*S
1670 p0 = (b0 + b4*S) + T * (b1 + T*((b2 + b3*T)) + b5*S)
1680 lambda = (c0 +c4*S) + T * (c1 + T*((c2 + c3*T)) + c5*S)
169
1700 spec_vol_elem_buggy_Wright = (lambda + al0*(pressure + p0)) / (pressure + p0)
171
1720end function spec_vol_elem_buggy_Wright
173
174!> In situ specific volume anomaly of sea water using a buggy implementation of Wright, 1997 [kg m-3]
175!!
176!! This is an elemental function that can be applied to any combination of scalar and array inputs.
1770real elemental function spec_vol_anomaly_elem_buggy_Wright(this, T, S, pressure, spv_ref)
178 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
179 real, intent(in) :: T !< potential temperature relative to the surface [degC].
180 real, intent(in) :: S !< salinity [PSU].
181 real, intent(in) :: pressure !< pressure [Pa].
182 real, intent(in) :: spv_ref !< A reference specific volume [m3 kg-1].
183
184 ! Local variables
185 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
186 real :: p0 ! The pressure offset in the Wright EOS [Pa]
187 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
188
1890 al0 = (a0 + a1*T) +a2*S
1900 p0 = (b0 + b4*S) + T * (b1 + T*((b2 + b3*T)) + b5*S)
1910 lambda = (c0 +c4*S) + T * (c1 + T*((c2 + c3*T)) + c5*S)
192
1930 spec_vol_anomaly_elem_buggy_Wright = (lambda + (al0 - spv_ref)*(pressure + p0)) / (pressure + p0)
194
1950end function spec_vol_anomaly_elem_buggy_Wright
196
197!> Calculate the partial derivatives of density with potential temperature and salinity
198!! using the buggy implementation of the equation of state, as fit by Wright, 1997
19910966107elemental subroutine calculate_density_derivs_elem_buggy_Wright_loc( T, S, pressure, drho_dT, drho_dS)
200 real, intent(in) :: T !< Potential temperature relative to the surface [degC]
201 real, intent(in) :: S !< Salinity [PSU]
202 real, intent(in) :: pressure !< Pressure [Pa]
203 real, intent(out) :: drho_dT !< The partial derivative of density with potential
204 !! temperature [kg m-3 degC-1]
205 real, intent(out) :: drho_dS !< The partial derivative of density with salinity,
206 !! in [kg m-3 PSU-1]
207 ! Local variables
208 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
209 real :: p0 ! The pressure offset in the Wright EOS [Pa]
210 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
211 real :: I_denom2 ! The inverse of the square of the denominator of density in the Wright EOS [s4 m-4]
212
21310966107 al0 = (a0 + a1*T) + a2*S
21410966107 p0 = (b0 + b4*S) + T * (b1 + T*((b2 + b3*T)) + b5*S)
21510966107 lambda = (c0 +c4*S) + T * (c1 + T*((c2 + c3*T)) + c5*S)
216
21710966107 I_denom2 = 1.0 / (lambda + al0*(pressure + p0))
21810966107 I_denom2 = I_denom2 *I_denom2
219 drho_dT = I_denom2 * &
220 (lambda* (b1 + T*(2.0*b2 + 3.0*b3*T) + b5*S) - &
221 (pressure+p0) * ( (pressure+p0)*a1 + &
22210966107 (c1 + T*(c2*2.0 + c3*3.0*T) + c5*S) ))
223 drho_dS = I_denom2 * (lambda* (b4 + b5*T) - &
22410966107 (pressure+p0) * ( (pressure+p0)*a2 + (c4 + c5*T) ))
225
22610966107end subroutine calculate_density_derivs_elem_buggy_Wright_loc
227
228!> Wrapper for density_elem_buggy_Wright_loc created to preserve API while calling
229!! density_elem_buggy_Wright without "this" variable that causes runtime errors on
230!! gpu runs with nvfortran.
2315431851elemental subroutine calculate_density_derivs_elem_buggy_Wright(this, T, S, pressure, drho_dT, drho_dS)
232 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
233 real, intent(in) :: T !< Potential temperature relative to the surface [degC]
234 real, intent(in) :: S !< Salinity [PSU]
235 real, intent(in) :: pressure !< Pressure [Pa]
236 real, intent(out) :: drho_dT !< The partial derivative of density with potential
237 !! temperature [kg m-3 degC-1]
238 real, intent(out) :: drho_dS !< The partial derivative of density with salinity,
239 !! in [kg m-3 PSU-1]
240
2415431851 call calculate_density_derivs_elem_buggy_Wright_loc(T, S, pressure, drho_dT, drho_dS)
242
2435431851end subroutine calculate_density_derivs_elem_buggy_Wright
244
245!> Second derivatives of density with respect to temperature, salinity, and pressure,
246!! using the poor implementation of the equation of state, as fit by Wright, 1997
2470elemental subroutine calculate_density_second_derivs_elem_buggy_Wright(this, T, S, pressure, &
248 drho_ds_ds, drho_ds_dt, drho_dt_dt, drho_ds_dp, drho_dt_dp)
249 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
250 real, intent(in) :: T !< Potential temperature referenced to 0 dbar [degC]
251 real, intent(in) :: S !< Salinity [PSU]
252 real, intent(in) :: pressure !< Pressure [Pa]
253 real, intent(inout) :: drho_ds_ds !< Partial derivative of beta with respect
254 !! to S [kg m-3 PSU-2]
255 real, intent(inout) :: drho_ds_dt !< Partial derivative of beta with respect
256 !! to T [kg m-3 PSU-1 degC-1]
257 real, intent(inout) :: drho_dt_dt !< Partial derivative of alpha with respect
258 !! to T [kg m-3 degC-2]
259 real, intent(inout) :: drho_ds_dp !< Partial derivative of beta with respect
260 !! to pressure [kg m-3 PSU-1 Pa-1] = [s2 m-2 PSU-1]
261 real, intent(inout) :: drho_dt_dp !< Partial derivative of alpha with respect
262 !! to pressure [kg m-3 degC-1 Pa-1] = [s2 m-2 degC-1]
263 ! Local variables
264 real :: z0, z1 ! Local work variables [Pa]
265 real :: z2, z4 ! Local work variables [m2 s-2]
266 real :: z3, z5 ! Local work variables [Pa degC-1]
267 real :: z6, z8 ! Local work variables [m2 s-2 degC-1]
268 real :: z7 ! A local work variable [m2 s-2 PSU-1]
269 real :: z9 ! A local work variable [m3 kg-1]
270 real :: z10 ! A local work variable [Pa PSU-1]
271 real :: z11 ! A local work variable [Pa m2 s-2 PSU-1] = [kg m s-4 PSU-1]
272 real :: z2_2 ! A local work variable [m4 s-4]
273 real :: z2_3 ! A local work variable [m6 s-6]
274 real :: six ! A constant that can be adjusted from 6. to 4. to recreate a bug [nondim]
275
276 ! Based on the above expression with common terms factored, there probably exists a more numerically stable
277 ! and/or efficient expression
278
2790 six = 2.0*this%three ! When recreating a bug from the original version of this routine, six = 4.
280
2810 z0 = T*(b1 + b5*S + T*(b2 + b3*T))
2820 z1 = (b0 + pressure + b4*S + z0)
2830 z3 = (b1 + b5*S + T*(2.*b2 + this%three*b3*T)) ! When recreating a bug here this%three = 2.
2840 z4 = (c0 + c4*S + T*(c1 + c5*S + T*(c2 + c3*T)))
2850 z5 = (b1 + b5*S + T*(b2 + b3*T) + T*(b2 + 2.*b3*T))
2860 z6 = c1 + c5*S + T*(c2 + c3*T) + T*(c2 + 2.*c3*T)
2870 z7 = (c4 + c5*T + a2*z1)
2880 z8 = (c1 + c5*S + T*(2.*c2 + 3.*c3*T) + a1*z1)
2890 z9 = (a0 + a2*S + a1*T)
2900 z10 = (b4 + b5*T)
2910 z11 = (z10*z4 - z1*z7)
2920 z2 = (c0 + c4*S + T*(c1 + c5*S + T*(c2 + c3*T)) + z9*z1)
2930 z2_2 = z2*z2
2940 z2_3 = z2_2*z2
295
2960 drho_ds_ds = (z10*(c4 + c5*T) - a2*z10*z1 - z10*z7)/z2_2 - (2.*(c4 + c5*T + z9*z10 + a2*z1)*z11)/z2_3
2970 drho_ds_dt = (z10*z6 - z1*(c5 + a2*z5) + b5*z4 - z5*z7)/z2_2 - (2.*(z6 + z9*z5 + a1*z1)*z11)/z2_3
298 drho_dt_dt = (z3*z6 - z1*(2.*c2 + 6.*c3*T + a1*z5) + (2.*b2 + six*b3*T)*z4 - z5*z8)/z2_2 - &
2990 (2.*(z6 + z9*z5 + a1*z1)*(z3*z4 - z1*z8))/z2_3
3000 drho_ds_dp = (-c4 - c5*T - 2.*a2*z1)/z2_2 - (2.*z9*z11)/z2_3
3010 drho_dt_dp = (-c1 - c5*S - T*(2.*c2 + 3.*c3*T) - 2.*a1*z1)/z2_2 - (2.*z9*(z3*z4 - z1*z8))/z2_3
302
3030end subroutine calculate_density_second_derivs_elem_buggy_Wright
304
305!> Calculate the partial derivatives of specific volume with temperature and salinity
306!! using the poor implementation of the equation of state, as fit by Wright, 1997
3070elemental subroutine calculate_specvol_derivs_elem_buggy_Wright(this, T, S, pressure, dSV_dT, dSV_dS)
308 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
309 real, intent(in) :: T !< Potential temperature [degC]
310 real, intent(in) :: S !< Salinity [PSU]
311 real, intent(in) :: pressure !< Pressure [Pa]
312 real, intent(inout) :: dSV_dT !< The partial derivative of specific volume with
313 !! potential temperature [m3 kg-1 degC-1]
314 real, intent(inout) :: dSV_dS !< The partial derivative of specific volume with
315 !! salinity [m3 kg-1 PSU-1]
316 ! Local variables
317 real :: p0 ! The pressure offset in the Wright EOS [Pa]
318 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
319 real :: I_denom ! The inverse of the denominator of specific volume in the Wright EOS [Pa-1]
320
321! al0 = (a0 + a1*T) + a2*S
3220 p0 = (b0 + b4*S) + T * (b1 + T*((b2 + b3*T)) + b5*S)
3230 lambda = (c0 +c4*S) + T * (c1 + T*((c2 + c3*T)) + c5*S)
324
325 ! SV = al0 + lambda / (pressure + p0)
326
3270 I_denom = 1.0 / (pressure + p0)
328 dSV_dT = (a1 + I_denom * (c1 + T*((2.0*c2 + 3.0*c3*T)) + c5*S)) - &
3290 (I_denom**2 * lambda) * (b1 + T*((2.0*b2 + 3.0*b3*T)) + b5*S)
330 dSV_dS = (a2 + I_denom * (c4 + c5*T)) - &
3310 (I_denom**2 * lambda) * (b4 + b5*T)
332
3330end subroutine calculate_specvol_derivs_elem_buggy_Wright
334
335!> Compute the in situ density of sea water (rho) and the compressibility (drho/dp == C_sound^-2)
336!! at the given salinity, potential temperature and pressure
337!! using the poor implementation of the equation of state, as fit by Wright, 1997
3380elemental subroutine calculate_compress_elem_buggy_Wright(this, T, S, pressure, rho, drho_dp)
339 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
340 real, intent(in) :: T !< Potential temperature relative to the surface [degC]
341 real, intent(in) :: S !< Salinity [PSU]
342 real, intent(in) :: pressure !< Pressure [Pa]
343 real, intent(out) :: rho !< In situ density [kg m-3]
344 real, intent(out) :: drho_dp !< The partial derivative of density with pressure
345 !! (also the inverse of the square of sound speed)
346 !! [s2 m-2].
347
348 ! Local variables
349 real :: al0 ! The specific volume at 0 lambda in the Wright EOS [m3 kg-1]
350 real :: p0 ! The pressure offset in the Wright EOS [Pa]
351 real :: lambda ! The sound speed squared at 0 alpha in the Wright EOS [m2 s-2]
352 real :: I_denom ! The inverse of the denominator of density in the Wright EOS [s2 m-2]
353
3540 al0 = (a0 + a1*T) +a2*S
3550 p0 = (b0 + b4*S) + T * (b1 + T*((b2 + b3*T)) + b5*S)
3560 lambda = (c0 +c4*S) + T * (c1 + T*((c2 + c3*T)) + c5*S)
357
3580 I_denom = 1.0 / (lambda + al0*(pressure + p0))
3590 rho = (pressure + p0) * I_denom
3600 drho_dp = lambda * I_denom * I_denom
361
3620end subroutine calculate_compress_elem_buggy_Wright
363
364!> Calculates analytical and nearly-analytical integrals, in pressure across layers, to determine
365!! the layer-average specific volumes. There are essentially no free assumptions, apart from a
366!! truncation in the series for log(1-eps/1+eps) that assumes that |eps| < 0.34.
3670subroutine avg_spec_vol_buggy_Wright(T, S, p_t, dp, SpV_avg, start, npts)
368 real, dimension(:), intent(in) :: T !< Potential temperature relative to the surface
369 !! [degC].
370 real, dimension(:), intent(in) :: S !< Salinity [PSU].
371 real, dimension(:), intent(in) :: p_t !< Pressure at the top of the layer [Pa]
372 real, dimension(:), intent(in) :: dp !< Pressure change in the layer [Pa]
373 real, dimension(:), intent(inout) :: SpV_avg !< The vertical average specific volume
374 !! in the layer [m3 kg-1]
375 integer, intent(in) :: start !< the starting point in the arrays.
376 integer, intent(in) :: npts !< the number of values to calculate.
377
378 ! Local variables
379 real :: al0 ! A term in the Wright EOS [m3 kg-1]
380 real :: p0 ! A term in the Wright EOS [Pa]
381 real :: lambda ! A term in the Wright EOS [m2 s-2]
382 real :: eps2 ! The square of a nondimensional ratio [nondim]
383 real :: I_pterm ! The inverse of p0 plus p_ave [Pa-1].
384 real, parameter :: C1_3 = 1.0/3.0, C1_7 = 1.0/7.0, C1_9 = 1.0/9.0 ! Rational constants [nondim]
385 integer :: j
386
387 ! alpha(j) = al0 + lambda / (pressure(j) + p0)
3880 do j=start,start+npts-1
3890 al0 = a0 + (a1*T(j) + a2*S(j))
3900 p0 = b0 + ( b4*S(j) + T(j) * (b1 + (T(j)*(b2 + b3*T(j)) + b5*S(j))) )
3910 lambda = c0 + ( c4*S(j) + T(j) * (c1 + (T(j)*(c2 + c3*T(j)) + c5*S(j))) )
392
3930 I_pterm = 1.0 / (p0 + (p_t(j) + 0.5*dp(j)))
3940 eps2 = (0.5 * dp(j) * I_pterm)**2
395 SpV_avg(j) = al0 + (lambda * I_pterm) * &
3960 (1.0 + eps2*(C1_3 + eps2*(0.2 + eps2*(C1_7 + eps2*C1_9))))
397 enddo
3980end subroutine avg_spec_vol_buggy_Wright
399
400!> Return the range of temperatures, salinities and pressures for which the reduced-range equation
401!! of state from Wright (1997) has been fitted to observations. Care should be taken when applying
402!! this equation of state outside of its fit range.
4030subroutine EoS_fit_range_buggy_Wright(this, T_min, T_max, S_min, S_max, p_min, p_max)
404 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
405 real, optional, intent(out) :: T_min !< The minimum potential temperature over which this EoS is fitted [degC]
406 real, optional, intent(out) :: T_max !< The maximum potential temperature over which this EoS is fitted [degC]
407 real, optional, intent(out) :: S_min !< The minimum practical salinity over which this EoS is fitted [PSU]
408 real, optional, intent(out) :: S_max !< The maximum practical salinity over which this EoS is fitted [PSU]
409 real, optional, intent(out) :: p_min !< The minimum pressure over which this EoS is fitted [Pa]
410 real, optional, intent(out) :: p_max !< The maximum pressure over which this EoS is fitted [Pa]
411
4120 if (present(T_min)) T_min = -2.0
4130 if (present(T_max)) T_max = 30.0
4140 if (present(S_min)) S_min = 28.0
4150 if (present(S_max)) S_max = 38.0
4160 if (present(p_min)) p_min = 0.0
4170 if (present(p_max)) p_max = 5.0e7
418
4190end subroutine EoS_fit_range_buggy_Wright
420
421!> Calculates analytical and nearly-analytical integrals, in geopotential across layers, of pressure
422!! anomalies, which are required for calculating the finite-volume form pressure accelerations in a
423!! Boussinesq model. There are essentially no free assumptions, apart from the use of Boole's rule
424!! rule to do the horizontal integrals, and from a truncation in the series for log(1-eps/1+eps)
425!! that assumes that |eps| < 0.34.
426176subroutine int_density_dz_wright(T, S, z_t, z_b, rho_ref, rho_0, G_e, HI, &
427440 dpa, intz_dpa, intx_dpa, inty_dpa, bathyT, SSH, dz_neglect, &
42888 MassWghtInterp, rho_scale, pres_scale, temp_scale, saln_scale, Z_0p)
429 type(hor_index_type), intent(in) :: HI !< The horizontal index type for the arrays.
430 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
431 intent(in) :: T !< Potential temperature relative to the surface
432 !! [C ~> degC].
433 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
434 intent(in) :: S !< Salinity [S ~> PSU].
435 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
436 intent(in) :: z_t !< Height at the top of the layer in depth units [Z ~> m].
437 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
438 intent(in) :: z_b !< Height at the top of the layer [Z ~> m].
439 real, intent(in) :: rho_ref !< A mean density [R ~> kg m-3], that is subtracted
440 !! out to reduce the magnitude of each of the integrals.
441 !! (The pressure is calculated as p~=-z*rho_0*G_e.)
442 real, intent(in) :: rho_0 !< Density [R ~> kg m-3], that is used
443 !! to calculate the pressure (as p~=-z*rho_0*G_e)
444 !! used in the equation of state.
445 real, intent(in) :: G_e !< The Earth's gravitational acceleration
446 !! [L2 Z-1 T-2 ~> m s-2].
447 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
448 intent(inout) :: dpa !< The change in the pressure anomaly across the
449 !! layer [R L2 T-2 ~> Pa].
450 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
451 optional, intent(inout) :: intz_dpa !< The integral through the thickness of the layer
452 !! of the pressure anomaly relative to the anomaly
453 !! at the top of the layer [R Z L2 T-2 ~> Pa m].
454 real, dimension(HI%IsdB:HI%IedB,HI%jsd:HI%jed), &
455 optional, intent(inout) :: intx_dpa !< The integral in x of the difference between the
456 !! pressure anomaly at the top and bottom of the
457 !! layer divided by the x grid spacing [R L2 T-2 ~> Pa].
458 real, dimension(HI%isd:HI%ied,HI%JsdB:HI%JedB), &
459 optional, intent(inout) :: inty_dpa !< The integral in y of the difference between the
460 !! pressure anomaly at the top and bottom of the
461 !! layer divided by the y grid spacing [R L2 T-2 ~> Pa].
462 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
463 optional, intent(in) :: bathyT !< The depth of the bathymetry [Z ~> m].
464 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
465 optional, intent(in) :: SSH !< The sea surface height [Z ~> m]
466 real, optional, intent(in) :: dz_neglect !< A miniscule thickness change [Z ~> m].
467 integer, optional, intent(in) :: MassWghtInterp !< A flag indicating whether and how to use
468 !! mass weighting to interpolate T/S in integrals
469 real, optional, intent(in) :: rho_scale !< A multiplicative factor by which to scale density
470 !! from kg m-3 to the desired units [R m3 kg-1 ~> 1]
471 real, optional, intent(in) :: pres_scale !< A multiplicative factor to convert pressure
472 !! into Pa [Pa T2 R-1 L-2 ~> 1].
473 real, optional, intent(in) :: temp_scale !< A multiplicative factor by which to scale
474 !! temperature into degC [degC C-1 ~> 1]
475 real, optional, intent(in) :: saln_scale !< A multiplicative factor to convert pressure
476 !! into PSU [PSU S-1 ~> 1].
477 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
478 optional, intent(in) :: Z_0p !< The height at which the pressure is 0 [Z ~> m]
479
480 ! Local variables
481176 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: al0_2d ! A term in the Wright EOS [m3 kg-1]
482176 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: p0_2d ! A term in the Wright EOS [Pa]
483176 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: lambda_2d ! A term in the Wright EOS [m2 s-2]
484176 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: z0pres ! The height at which the pressure is zero [Z ~> m]
485 real :: al0 ! A term in the Wright EOS [m3 kg-1]
486 real :: p0 ! A term in the Wright EOS [Pa]
487 real :: lambda ! A term in the Wright EOS [m2 s-2]
488 real :: rho_anom ! The density anomaly from rho_ref [kg m-3].
489 real :: eps, eps2 ! A nondimensional ratio and its square [nondim]
490 real :: rem ! [kg m-1 s-2]
491 real :: GxRho ! The gravitational acceleration times density and unit conversion factors [Pa Z-1 ~> kg m-2 s-2]
492 real :: g_Earth ! The gravitational acceleration [m2 Z-1 s-2 ~> m s-2]
493 real :: I_Rho ! The inverse of the Boussinesq density [m3 kg-1]
494 real :: rho_ref_mks ! The reference density in MKS units [kg m-3]
495 real :: p_ave ! The layer averaged pressure [Pa]
496 real :: I_al0 ! The inverse of al0 [kg m-3]
497 real :: I_Lzz ! The inverse of the denominator [Pa-1]
498 real :: dz ! The layer thickness [Z ~> m].
499 real :: hWght ! A pressure-thickness below topography [Z ~> m].
500 real :: hL, hR ! Pressure-thicknesses of the columns to the left and right [Z ~> m].
501 real :: iDenom ! The inverse of the denominator in the weights [Z-2 ~> m-2].
502 real :: hWt_LL, hWt_LR ! hWt_LA is the weighted influence of A on the left column [nondim].
503 real :: hWt_RL, hWt_RR ! hWt_RA is the weighted influence of A on the right column [nondim].
504 real :: wt_L, wt_R ! The linear weights of the left and right columns [nondim].
505 real :: wtT_L, wtT_R ! The weights for tracers from the left and right columns [nondim].
506 real :: intz(5) ! The gravitational acceleration times the integrals of density
507 ! with height at the 5 sub-column locations [R L2 T-2 ~> Pa].
508 real :: Pa_to_RL2_T2 ! A conversion factor of pressures from Pa to the output units indicated by
509 ! pres_scale [R L2 T-2 Pa-1 ~> 1].
510 real :: a1s ! Partly rescaled version of a1 [m3 kg-1 C-1 ~> m3 kg-1 degC-1]
511 real :: a2s ! Partly rescaled version of a2 [m3 kg-1 S-1 ~> m3 kg-1 PSU-1]
512 real :: b1s ! Partly rescaled version of b1 [Pa C-1 ~> Pa degC-1]
513 real :: b2s ! Partly rescaled version of b2 [Pa C-2 ~> Pa degC-2]
514 real :: b3s ! Partly rescaled version of b3 [Pa C-3 ~> Pa degC-3]
515 real :: b4s ! Partly rescaled version of b4 [Pa S-1 ~> Pa PSU-1]
516 real :: b5s ! Partly rescaled version of b5 [Pa C-1 S-1 ~> Pa degC-1 PSU-1]
517 real :: c1s ! Partly rescaled version of c1 [m2 s-2 C-1 ~> m2 s-2 degC-1]
518 real :: c2s ! Partly rescaled version of c2 [m2 s-2 C-2 ~> m2 s-2 degC-2]
519 real :: c3s ! Partly rescaled version of c3 [m2 s-2 C-3 ~> m2 s-2 degC-3]
520 real :: c4s ! Partly rescaled version of c4 [m2 s-2 S-1 ~> m2 s-2 PSU-1]
521 real :: c5s ! Partly rescaled version of c5 [m2 s-2 C-1 S-1 ~> m2 s-2 degC-1 PSU-1]
522 logical :: do_massWeight ! Indicates whether to do mass weighting.
523 logical :: top_massWeight ! Indicates whether to do mass weighting the sea surface
524 real, parameter :: C1_3 = 1.0/3.0, C1_7 = 1.0/7.0 ! Rational constants [nondim]
525 real, parameter :: C1_9 = 1.0/9.0, C1_90 = 1.0/90.0 ! Rational constants [nondim]
526 integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, i, j, m
527
528 ! These array bounds work for the indexing convention of the input arrays, but
529 ! on the computational domain defined for the output arrays.
53088 Isq = HI%IscB ; Ieq = HI%IecB
53188 Jsq = HI%JscB ; Jeq = HI%JecB
53288 is = HI%isc ; ie = HI%iec
53388 js = HI%jsc ; je = HI%jec
534
53588 if (present(pres_scale)) then
5360 GxRho = pres_scale * G_e * rho_0 ; g_Earth = pres_scale * G_e
5370 Pa_to_RL2_T2 = 1.0 / pres_scale
538 else
53988 GxRho = G_e * rho_0 ; g_Earth = G_e
54088 Pa_to_RL2_T2 = 1.0
541 endif
54288 if (present(rho_scale)) then
5430 g_Earth = g_Earth * rho_scale
5440 rho_ref_mks = rho_ref / rho_scale ; I_Rho = rho_scale / rho_0
545 else
54688 rho_ref_mks = rho_ref ; I_Rho = 1.0 / rho_0
547 endif
548 !$omp target enter data map(alloc: z0pres, al0_2d, p0_2d, lambda_2d, intz)
54988 if (present(Z_0p)) then
55031944 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
5515829736 z0pres(i,j) = Z_0p(i,j)
552 enddo
553 else
5540 do concurrent (j=HI%jsd:HI%jed, i=HI%isd:HI%ied)
5550 z0pres(i,j) = 0.0
556 enddo
557 endif
558
55988 a1s = a1 ; a2s = a2
56088 b1s = b1 ; b2s = b2 ; b3s = b3 ; b4s = b4 ; b5s = b5
56188 c1s = c1 ; c2s = c2 ; c3s = c3 ; c4s = c4 ; c5s = c5
562
56388 if (present(temp_scale)) then ; if (temp_scale /= 1.0) then
5640 a1s = a1s * temp_scale
5650 b1s = b1s * temp_scale ; b2s = b2s * temp_scale**2
5660 b3s = b3s * temp_scale**3 ; b5s = b5s * temp_scale
5670 c1s = c1s * temp_scale ; c2s = c2s * temp_scale**2
5680 c3s = c3s * temp_scale**3 ; c5s = c5s * temp_scale
569 endif ; endif
570
57188 if (present(saln_scale)) then ; if (saln_scale /= 1.0) then
5720 a2s = a2s * saln_scale
5730 b4s = b4s * saln_scale ; b5s = b5s * saln_scale
5740 c4s = c4s * saln_scale ; c5s = c5s * saln_scale
575 endif ; endif
576
57788 do_massWeight = .false. ; top_massWeight = .false.
57888 if (present(MassWghtInterp)) then
57988 do_massWeight = BTEST(MassWghtInterp, 0) ! True for odd values
58088 top_massWeight = BTEST(MassWghtInterp, 1) ! True if the 2 bit is set
581 endif
582
58331944 do concurrent (j=Jsq:Jeq+1, i=Isq:Ieq+1)
5845797792 al0_2d(i,j) = (a0 + a1s*T(i,j)) + a2s*S(i,j)
5855797792 p0_2d(i,j) = (b0 + b4s*S(i,j)) + T(i,j) * (b1s + T(i,j)*((b2s + b3s*T(i,j))) + b5s*S(i,j))
5865797792 lambda_2d(i,j) = (c0 +c4s*S(i,j)) + T(i,j) * (c1s + T(i,j)*((c2s + c3s*T(i,j))) + c5s*S(i,j))
587
5885797792 al0 = al0_2d(i,j) ; p0 = p0_2d(i,j) ; lambda = lambda_2d(i,j)
589
5905797792 dz = z_t(i,j) - z_b(i,j)
5915797792 p_ave = -GxRho*(0.5*(z_t(i,j)+z_b(i,j)) - z0pres(i,j))
592
5935797792 I_al0 = 1.0 / al0
5945797792 I_Lzz = 1.0 / (p0 + (lambda * I_al0) + p_ave)
5955797792 eps = 0.5*GxRho*dz*I_Lzz ; eps2 = eps*eps
596
597! rho(j) = (pressure(j) + p0) / (lambda + al0*(pressure(j) + p0))
598
5995797792 rho_anom = (p0 + p_ave)*(I_Lzz*I_al0) - rho_ref_mks
600 rem = I_Rho * (lambda * I_al0**2) * eps2 * &
6015797792 (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2)))
6025797792 dpa(i,j) = Pa_to_RL2_T2 * (g_Earth*rho_anom*dz - 2.0*eps*rem)
6035797792 if (present(intz_dpa)) &
6045829736 intz_dpa(i,j) = Pa_to_RL2_T2 * (0.5*g_Earth*rho_anom*dz**2 - dz*(1.0+eps)*rem)
605 enddo
606
60788 if (present(intx_dpa)) then
608 !$omp target teams loop collapse(2) &
609 !$omp private(hWght, hL, hR, iDenom, hWt_LL, hWt_LR, hWt_RR, hWt_RL, m, wt_L, wt_R, wtT_L, &
610 !$omp wtT_R, al0, p0, lambda, dz, p_ave, I_al0, I_Lzz, eps, eps2, intz)
6115734168 do j=js,je ; do I=Isq,Ieq
612 ! hWght is the distance measure by which the cell is violation of
613 ! hydrostatic consistency. For large hWght we bias the interpolation of
614 ! T & S along the top and bottom integrals, akin to thickness weighting.
6155718240 hWght = 0.0
6165718240 if (do_massWeight) &
6170 hWght = max(0., -bathyT(i,j)-z_t(i+1,j), -bathyT(i+1,j)-z_t(i,j))
6185718240 if (top_massWeight) &
6190 hWght = max(hWght, z_b(i+1,j)-SSH(i,j), z_b(i,j)-SSH(i+1,j))
6205718240 if (hWght > 0.) then
6210 hL = (z_t(i,j) - z_b(i,j)) + dz_neglect
6220 hR = (z_t(i+1,j) - z_b(i+1,j)) + dz_neglect
6230 hWght = hWght * ( (hL-hR)/(hL+hR) )**2
6240 iDenom = 1.0 / ( hWght*(hR + hL) + hL*hR )
6250 hWt_LL = (hWght*hL + hR*hL) * iDenom ; hWt_LR = (hWght*hR) * iDenom
6260 hWt_RR = (hWght*hR + hR*hL) * iDenom ; hWt_RL = (hWght*hL) * iDenom
627 else
6285718240 hWt_LL = 1.0 ; hWt_LR = 0.0 ; hWt_RR = 1.0 ; hWt_RL = 0.0
629 endif
630
6315718240 intz(1) = dpa(i,j) ; intz(5) = dpa(i+1,j)
63222872960 do m=2,4
63317154720 wt_L = 0.25*real(5-m) ; wt_R = 1.0-wt_L
63417154720 wtT_L = (wt_L*hWt_LL) + (wt_R*hWt_RL) ; wtT_R = (wt_L*hWt_LR) + (wt_R*hWt_RR)
635
63617154720 al0 = (wtT_L*al0_2d(i,j)) + (wtT_R*al0_2d(i+1,j))
63717154720 p0 = (wtT_L*p0_2d(i,j)) + (wtT_R*p0_2d(i+1,j))
63817154720 lambda = (wtT_L*lambda_2d(i,j)) + (wtT_R*lambda_2d(i+1,j))
639
64017154720 dz = (wt_L*(z_t(i,j) - z_b(i,j))) + (wt_R*(z_t(i+1,j) - z_b(i+1,j)))
641 p_ave = -GxRho*((wt_L * (0.5*(z_t(i,j)+z_b(i,j)) - z0pres(i,j))) + &
64217154720 (wt_R * (0.5*(z_t(i+1,j)+z_b(i+1,j)) - z0pres(i+1,j))))
643
64417154720 I_al0 = 1.0 / al0
64517154720 I_Lzz = 1.0 / (p0 + (lambda * I_al0) + p_ave)
64617154720 eps = 0.5*GxRho*dz*I_Lzz ; eps2 = eps*eps
647
648 intz(m) = Pa_to_RL2_T2 * ( g_Earth*dz*((p0 + p_ave)*(I_Lzz*I_al0) - rho_ref_mks) - 2.0*eps * &
64922872960 I_Rho * (lambda * I_al0**2) * eps2 * (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2))) )
650 enddo
651 ! Use Boole's rule to integrate the values.
6525734080 intx_dpa(i,j) = C1_90*(7.0*(intz(1)+intz(5)) + 32.0*(intz(2)+intz(4)) + 12.0*intz(3))
653 enddo ; enddo
654 endif
655
65688 if (present(inty_dpa)) then
657 !$omp target teams loop collapse(2) &
658 !$omp private(hWght, hL, hR, iDenom, hWt_LL, hWt_LR, hWt_RR, hWt_RL, m, wt_L, wt_R, wtT_L, &
659 !$omp wtT_R, al0, p0, lambda, dz, p_ave, I_al0, I_Lzz, eps, eps2, intz)
6605750096 do J=Jsq,Jeq ; do i=is,ie
661 ! hWght is the distance measure by which the cell is violation of
662 ! hydrostatic consistency. For large hWght we bias the interpolation of
663 ! T & S along the top and bottom integrals, akin to thickness weighting.
6645734080 hWght = 0.0
6655734080 if (do_massWeight) &
6660 hWght = max(0., -bathyT(i,j)-z_t(i,j+1), -bathyT(i,j+1)-z_t(i,j))
6675734080 if (top_massWeight) &
6680 hWght = max(hWght, z_b(i,j+1)-SSH(i,j), z_b(i,j)-SSH(i,j+1))
6695734080 if (hWght > 0.) then
6700 hL = (z_t(i,j) - z_b(i,j)) + dz_neglect
6710 hR = (z_t(i,j+1) - z_b(i,j+1)) + dz_neglect
6720 hWght = hWght * ( (hL-hR)/(hL+hR) )**2
6730 iDenom = 1.0 / ( hWght*(hR + hL) + hL*hR )
6740 hWt_LL = (hWght*hL + hR*hL) * iDenom ; hWt_LR = (hWght*hR) * iDenom
6750 hWt_RR = (hWght*hR + hR*hL) * iDenom ; hWt_RL = (hWght*hL) * iDenom
676 else
6775734080 hWt_LL = 1.0 ; hWt_LR = 0.0 ; hWt_RR = 1.0 ; hWt_RL = 0.0
678 endif
679
6805734080 intz(1) = dpa(i,j) ; intz(5) = dpa(i,j+1)
68122936320 do m=2,4
68217202240 wt_L = 0.25*real(5-m) ; wt_R = 1.0-wt_L
68317202240 wtT_L = (wt_L*hWt_LL) + (wt_R*hWt_RL) ; wtT_R = (wt_L*hWt_LR) + (wt_R*hWt_RR)
684
68517202240 al0 = (wtT_L*al0_2d(i,j)) + (wtT_R*al0_2d(i,j+1))
68617202240 p0 = (wtT_L*p0_2d(i,j)) + (wtT_R*p0_2d(i,j+1))
68717202240 lambda = (wtT_L*lambda_2d(i,j)) + (wtT_R*lambda_2d(i,j+1))
688
68917202240 dz = (wt_L*(z_t(i,j) - z_b(i,j))) + (wt_R*(z_t(i,j+1) - z_b(i,j+1)))
690 p_ave = -GxRho*((wt_L*(0.5*(z_t(i,j)+z_b(i,j))-z0pres(i,j))) + &
69117202240 (wt_R*(0.5*(z_t(i,j+1)+z_b(i,j+1))-z0pres(i,j+1))))
692
69317202240 I_al0 = 1.0 / al0
69417202240 I_Lzz = 1.0 / (p0 + (lambda * I_al0) + p_ave)
69517202240 eps = 0.5*GxRho*dz*I_Lzz ; eps2 = eps*eps
696
697 intz(m) = Pa_to_RL2_T2 * ( g_Earth*dz*((p0 + p_ave)*(I_Lzz*I_al0) - rho_ref_mks) - 2.0*eps * &
69822936320 I_Rho * (lambda * I_al0**2) * eps2 * (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2))) )
699 enddo
700 ! Use Boole's rule to integrate the values.
7015750008 inty_dpa(i,j) = C1_90*(7.0*(intz(1)+intz(5)) + 32.0*(intz(2)+intz(4)) + 12.0*intz(3))
702 enddo ; enddo
703 endif
704 !$omp target exit data map(release: z0pres, al0_2d, p0_2d, lambda_2d, intz)
705
706528end subroutine int_density_dz_wright
707
708!> Calculates analytical and nearly-analytical integrals, in pressure across layers, of geopotential
709!! anomalies, which are required for calculating the finite-volume form pressure accelerations in a
710!! non-Boussinesq model. There are essentially no free assumptions, apart from the use of Boole's
711!! rule to do the horizontal integrals, and from a truncation in the series for log(1-eps/1+eps)
712!! that assumes that |eps| < 0.34.
7130subroutine int_spec_vol_dp_wright(T, S, p_t, p_b, spv_ref, HI, dza, &
7140 intp_dza, intx_dza, inty_dza, halo_size, bathyP, P_surf, dP_neglect, &
715 MassWghtInterp, SV_scale, pres_scale, temp_scale, saln_scale)
716 type(hor_index_type), intent(in) :: HI !< The ocean's horizontal index type.
717 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
718 intent(in) :: T !< Potential temperature relative to the surface
719 !! [C ~> degC].
720 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
721 intent(in) :: S !< Salinity [S ~> PSU].
722 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
723 intent(in) :: p_t !< Pressure at the top of the layer [R L2 T-2 ~> Pa]
724 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
725 intent(in) :: p_b !< Pressure at the top of the layer [R L2 T-2 ~> Pa]
726 real, intent(in) :: spv_ref !< A mean specific volume that is subtracted out
727 !! to reduce the magnitude of each of the integrals [R-1 ~> m3 kg-1].
728 !! The calculation is mathematically identical with different values of
729 !! spv_ref, but this reduces the effects of roundoff.
730 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
731 intent(inout) :: dza !< The change in the geopotential anomaly across
732 !! the layer [L2 T-2 ~> m2 s-2].
733 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
734 optional, intent(inout) :: intp_dza !< The integral in pressure through the layer of
735 !! the geopotential anomaly relative to the anomaly
736 !! at the bottom of the layer [R L4 T-4 ~> Pa m2 s-2]
737 real, dimension(HI%IsdB:HI%IedB,HI%jsd:HI%jed), &
738 optional, intent(inout) :: intx_dza !< The integral in x of the difference between the
739 !! geopotential anomaly at the top and bottom of
740 !! the layer divided by the x grid spacing
741 !! [L2 T-2 ~> m2 s-2].
742 real, dimension(HI%isd:HI%ied,HI%JsdB:HI%JedB), &
743 optional, intent(inout) :: inty_dza !< The integral in y of the difference between the
744 !! geopotential anomaly at the top and bottom of
745 !! the layer divided by the y grid spacing
746 !! [L2 T-2 ~> m2 s-2].
747 integer, optional, intent(in) :: halo_size !< The width of halo points on which to calculate
748 !! dza.
749 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
750 optional, intent(in) :: bathyP !< The pressure at the bathymetry [R L2 T-2 ~> Pa]
751 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed), &
752 optional, intent(in) :: P_surf !< The pressure at the ocean surface [R L2 T-2 ~> Pa]
753 real, optional, intent(in) :: dP_neglect !< A miniscule pressure change with
754 !! the same units as p_t [R L2 T-2 ~> Pa]
755 integer, optional, intent(in) :: MassWghtInterp !< A flag indicating whether and how to use
756 !! mass weighting to interpolate T/S in integrals
757 real, optional, intent(in) :: SV_scale !< A multiplicative factor by which to scale specific
758 !! volume from m3 kg-1 to the desired units [kg m-3 R-1 ~> 1]
759 real, optional, intent(in) :: pres_scale !< A multiplicative factor to convert pressure
760 !! into Pa [Pa T2 R-1 L-2 ~> 1].
761 real, optional, intent(in) :: temp_scale !< A multiplicative factor by which to scale
762 !! temperature into degC [degC C-1 ~> 1]
763 real, optional, intent(in) :: saln_scale !< A multiplicative factor to convert pressure
764 !! into PSU [PSU S-1 ~> 1].
765
766 ! Local variables
7670 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: al0_2d ! A term in the Wright EOS [R-1 ~> m3 kg-1]
7680 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: p0_2d ! A term in the Wright EOS [R L2 T-2 ~> Pa]
7690 real, dimension(HI%isd:HI%ied,HI%jsd:HI%jed) :: lambda_2d ! A term in the Wright EOS [L2 T-2 ~> m2 s-2]
770 real :: al0 ! A term in the Wright EOS [R-1 ~> m3 kg-1]
771 real :: p0 ! A term in the Wright EOS [R L2 T-2 ~> Pa]
772 real :: lambda ! A term in the Wright EOS [L2 T-2 ~> m2 s-2]
773 real :: al0_scale ! Scaling factor to convert al0 from MKS units [kg m-3 R-1 ~> 1]
774 real :: p0_scale ! Scaling factor to convert p0 from MKS units [R L2 T-2 Pa-1 ~> 1]
775 real :: lam_scale ! Scaling factor to convert lambda from MKS units [L2 s2 T-2 m-2 ~> 1]
776 real :: p_ave ! The layer average pressure [R L2 T-2 ~> Pa]
777 real :: rem ! [L2 T-2 ~> m2 s-2]
778 real :: eps, eps2 ! A nondimensional ratio and its square [nondim]
779 real :: alpha_anom ! The depth averaged specific volume anomaly [R-1 ~> m3 kg-1].
780 real :: dp ! The pressure change through a layer [R L2 T-2 ~> Pa].
781 real :: hWght ! A pressure-thickness below topography [R L2 T-2 ~> Pa].
782 real :: hL, hR ! Pressure-thicknesses of the columns to the left and right [R L2 T-2 ~> Pa].
783 real :: iDenom ! The inverse of the denominator in the weights [T4 R-2 L-4 ~> Pa-2].
784 real :: hWt_LL, hWt_LR ! hWt_LA is the weighted influence of A on the left column [nondim].
785 real :: hWt_RL, hWt_RR ! hWt_RA is the weighted influence of A on the right column [nondim].
786 real :: wt_L, wt_R ! The linear weights of the left and right columns [nondim].
787 real :: wtT_L, wtT_R ! The weights for tracers from the left and right columns [nondim].
788 real :: intp(5) ! The integrals of specific volume with pressure at the
789 ! 5 sub-column locations [L2 T-2 ~> m2 s-2].
790 real :: a1s ! Partly rescaled version of a1 [m3 kg-1 C-1 ~> m3 kg-1 degC-1]
791 real :: a2s ! Partly rescaled version of a2 [m3 kg-1 S-1 ~> m3 kg-1 PSU-1]
792 real :: b1s ! Partly rescaled version of b1 [Pa C-1 ~> Pa degC-1]
793 real :: b2s ! Partly rescaled version of b2 [Pa C-2 ~> Pa degC-2]
794 real :: b3s ! Partly rescaled version of b3 [Pa C-3 ~> Pa degC-3]
795 real :: b4s ! Partly rescaled version of b4 [Pa S-1 ~> Pa PSU-1]
796 real :: b5s ! Partly rescaled version of b5 [Pa C-1 S-1 ~> Pa degC-1 PSU-1]
797 real :: c1s ! Partly rescaled version of c1 [m2 s-2 C-1 ~> m2 s-2 degC-1]
798 real :: c2s ! Partly rescaled version of c2 [m2 s-2 C-2 ~> m2 s-2 degC-2]
799 real :: c3s ! Partly rescaled version of c3 [m2 s-2 C-3 ~> m2 s-2 degC-3]
800 real :: c4s ! Partly rescaled version of c4 [m2 s-2 S-1 ~> m2 s-2 PSU-1]
801 real :: c5s ! Partly rescaled version of c5 [m2 s-2 C-1 S-1 ~> m2 s-2 degC-1 PSU-1]
802 logical :: do_massWeight ! Indicates whether to do mass weighting.
803 logical :: top_massWeight ! Indicates whether to do mass weighting the sea surface
804 logical :: massWeight_bug ! If true, use an incorrect expression to determine where to apply mass weighting
805 real, parameter :: C1_3 = 1.0/3.0, C1_7 = 1.0/7.0 ! Rational constants [nondim]
806 real, parameter :: C1_9 = 1.0/9.0, C1_90 = 1.0/90.0 ! Rational constants [nondim]
807 integer :: Isq, Ieq, Jsq, Jeq, ish, ieh, jsh, jeh, i, j, m, halo
808
8090 Isq = HI%IscB ; Ieq = HI%IecB ; Jsq = HI%JscB ; Jeq = HI%JecB
8100 halo = 0 ; if (present(halo_size)) halo = MAX(halo_size,0)
8110 ish = HI%isc-halo ; ieh = HI%iec+halo ; jsh = HI%jsc-halo ; jeh = HI%jec+halo
8120 if (present(intx_dza)) then ; ish = MIN(Isq,ish) ; ieh = MAX(Ieq+1,ieh) ; endif
8130 if (present(inty_dza)) then ; jsh = MIN(Jsq,jsh) ; jeh = MAX(Jeq+1,jeh) ; endif
814
815
8160 al0_scale = 1.0 ; if (present(SV_scale)) al0_scale = SV_scale
8170 p0_scale = 1.0
8180 if (present(pres_scale)) then ; if (pres_scale /= 1.0) then
8190 p0_scale = 1.0 / pres_scale
820 endif ; endif
8210 lam_scale = al0_scale * p0_scale
822
8230 a1s = a1 ; a2s = a2
8240 b1s = b1 ; b2s = b2 ; b3s = b3 ; b4s = b4 ; b5s = b5
8250 c1s = c1 ; c2s = c2 ; c3s = c3 ; c4s = c4 ; c5s = c5
826
8270 if (present(temp_scale)) then ; if (temp_scale /= 1.0) then
8280 a1s = a1s * temp_scale
8290 b1s = b1s * temp_scale ; b2s = b2s * temp_scale**2
8300 b3s = b3s * temp_scale**3 ; b5s = b5s * temp_scale
8310 c1s = c1s * temp_scale ; c2s = c2s * temp_scale**2
8320 c3s = c3s * temp_scale**3 ; c5s = c5s * temp_scale
833 endif ; endif
834
8350 if (present(saln_scale)) then ; if (saln_scale /= 1.0) then
8360 a2s = a2s * saln_scale
8370 b4s = b4s * saln_scale ; b5s = b5s * saln_scale
8380 c4s = c4s * saln_scale ; c5s = c5s * saln_scale
839 endif ; endif
840
8410 do_massWeight = .false. ; massWeight_bug = .false. ; top_massWeight = .false.
8420 if (present(MassWghtInterp)) then
8430 do_massWeight = BTEST(MassWghtInterp, 0) ! True for odd values
8440 top_massWeight = BTEST(MassWghtInterp, 1) ! True if the 2 bit is set
8450 massWeight_bug = BTEST(MassWghtInterp, 3) ! True if the 8 bit is set
846 endif
847
848 ! alpha(j) = (lambda + al0*(pressure(j) + p0)) / (pressure(j) + p0)
8490 do j=jsh,jeh ; do i=ish,ieh
8500 al0_2d(i,j) = al0_scale * ( (a0 + a1s*T(i,j)) + a2s*S(i,j) )
8510 p0_2d(i,j) = p0_scale * ( (b0 + b4s*S(i,j)) + T(i,j) * (b1s + T(i,j)*((b2s + b3s*T(i,j))) + b5s*S(i,j)) )
8520 lambda_2d(i,j) = lam_scale * ( (c0 + c4s*S(i,j)) + T(i,j) * (c1s + T(i,j)*((c2s + c3s*T(i,j))) + c5s*S(i,j)) )
853
8540 al0 = al0_2d(i,j) ; p0 = p0_2d(i,j) ; lambda = lambda_2d(i,j)
8550 dp = p_b(i,j) - p_t(i,j)
8560 p_ave = 0.5*(p_t(i,j)+p_b(i,j))
857
8580 eps = 0.5 * dp / (p0 + p_ave) ; eps2 = eps*eps
8590 alpha_anom = al0 + lambda / (p0 + p_ave) - spv_ref
8600 rem = lambda * eps2 * (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2)))
8610 dza(i,j) = alpha_anom*dp + 2.0*eps*rem
8620 if (present(intp_dza)) &
8630 intp_dza(i,j) = 0.5*alpha_anom*dp**2 - dp*(1.0-eps)*rem
864 enddo ; enddo
865
8660 if (present(intx_dza)) then ; do j=HI%jsc,HI%jec ; do I=Isq,Ieq
867 ! hWght is the distance measure by which the cell is violation of
868 ! hydrostatic consistency. For large hWght we bias the interpolation of
869 ! T & S along the top and bottom integrals, akin to thickness weighting.
8700 hWght = 0.0
8710 if (do_massWeight .and. massWeight_bug) then
8720 hWght = max(0., bathyP(i,j)-p_t(i+1,j), bathyP(i+1,j)-p_t(i,j))
8730 elseif (do_massWeight) then
8740 hWght = max(0., p_t(i+1,j)-bathyP(i,j), p_t(i,j)-bathyP(i+1,j))
875 endif
8760 if (top_massWeight) &
8770 hWght = max(hWght, P_surf(i,j)-p_b(i+1,j), P_surf(i+1,j)-p_b(i,j))
8780 if (hWght > 0.) then
8790 hL = (p_b(i,j) - p_t(i,j)) + dP_neglect
8800 hR = (p_b(i+1,j) - p_t(i+1,j)) + dP_neglect
8810 hWght = hWght * ( (hL-hR)/(hL+hR) )**2
8820 iDenom = 1.0 / ( hWght*(hR + hL) + hL*hR )
8830 hWt_LL = (hWght*hL + hR*hL) * iDenom ; hWt_LR = (hWght*hR) * iDenom
8840 hWt_RR = (hWght*hR + hR*hL) * iDenom ; hWt_RL = (hWght*hL) * iDenom
885 else
8860 hWt_LL = 1.0 ; hWt_LR = 0.0 ; hWt_RR = 1.0 ; hWt_RL = 0.0
887 endif
888
8890 intp(1) = dza(i,j) ; intp(5) = dza(i+1,j)
8900 do m=2,4
8910 wt_L = 0.25*real(5-m) ; wt_R = 1.0-wt_L
8920 wtT_L = (wt_L*hWt_LL) + (wt_R*hWt_RL) ; wtT_R = (wt_L*hWt_LR) + (wt_R*hWt_RR)
893
894 ! T, S and p are interpolated in the horizontal. The p interpolation
895 ! is linear, but for T and S it may be thickness weighted.
8960 al0 = (wtT_L*al0_2d(i,j)) + (wtT_R*al0_2d(i+1,j))
8970 p0 = (wtT_L*p0_2d(i,j)) + (wtT_R*p0_2d(i+1,j))
8980 lambda = (wtT_L*lambda_2d(i,j)) + (wtT_R*lambda_2d(i+1,j))
899
9000 dp = (wt_L*(p_b(i,j) - p_t(i,j))) + (wt_R*(p_b(i+1,j) - p_t(i+1,j)))
9010 p_ave = 0.5*((wt_L*(p_t(i,j)+p_b(i,j))) + (wt_R*(p_t(i+1,j)+p_b(i+1,j))))
902
9030 eps = 0.5 * dp / (p0 + p_ave) ; eps2 = eps*eps
904 intp(m) = (al0 + lambda / (p0 + p_ave) - spv_ref)*dp + 2.0*eps* &
9050 lambda * eps2 * (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2)))
906 enddo
907 ! Use Boole's rule to integrate the values.
908 intx_dza(i,j) = C1_90*(7.0*(intp(1)+intp(5)) + 32.0*(intp(2)+intp(4)) + &
9090 12.0*intp(3))
910 enddo ; enddo ; endif
911
9120 if (present(inty_dza)) then ; do J=Jsq,Jeq ; do i=HI%isc,HI%iec
913 ! hWght is the distance measure by which the cell is violation of
914 ! hydrostatic consistency. For large hWght we bias the interpolation of
915 ! T & S along the top and bottom integrals, akin to thickness weighting.
9160 hWght = 0.0
9170 if (do_massWeight .and. massWeight_bug) then
9180 hWght = max(0., bathyP(i,j)-p_t(i,j+1), bathyP(i,j+1)-p_t(i,j))
9190 elseif (do_massWeight) then
9200 hWght = max(0., p_t(i,j+1)-bathyP(i,j), p_t(i,j)-bathyP(i,j+1))
921 endif
9220 if (top_massWeight) &
9230 hWght = max(hWght, P_surf(i,j)-p_b(i,j+1), P_surf(i,j+1)-p_b(i,j))
9240 if (hWght > 0.) then
9250 hL = (p_b(i,j) - p_t(i,j)) + dP_neglect
9260 hR = (p_b(i,j+1) - p_t(i,j+1)) + dP_neglect
9270 hWght = hWght * ( (hL-hR)/(hL+hR) )**2
9280 iDenom = 1.0 / ( hWght*(hR + hL) + hL*hR )
9290 hWt_LL = (hWght*hL + hR*hL) * iDenom ; hWt_LR = (hWght*hR) * iDenom
9300 hWt_RR = (hWght*hR + hR*hL) * iDenom ; hWt_RL = (hWght*hL) * iDenom
931 else
9320 hWt_LL = 1.0 ; hWt_LR = 0.0 ; hWt_RR = 1.0 ; hWt_RL = 0.0
933 endif
934
9350 intp(1) = dza(i,j) ; intp(5) = dza(i,j+1)
9360 do m=2,4
9370 wt_L = 0.25*real(5-m) ; wt_R = 1.0-wt_L
9380 wtT_L = (wt_L*hWt_LL) + (wt_R*hWt_RL) ; wtT_R = (wt_L*hWt_LR) + (wt_R*hWt_RR)
939
940 ! T, S and p are interpolated in the horizontal. The p interpolation
941 ! is linear, but for T and S it may be thickness weighted.
9420 al0 = (wt_L*al0_2d(i,j)) + (wt_R*al0_2d(i,j+1))
9430 p0 = (wt_L*p0_2d(i,j)) + (wt_R*p0_2d(i,j+1))
9440 lambda = (wt_L*lambda_2d(i,j)) + (wt_R*lambda_2d(i,j+1))
945
9460 dp = (wt_L*(p_b(i,j) - p_t(i,j))) + (wt_R*(p_b(i,j+1) - p_t(i,j+1)))
9470 p_ave = 0.5*((wt_L*(p_t(i,j)+p_b(i,j))) + (wt_R*(p_t(i,j+1)+p_b(i,j+1))))
948
9490 eps = 0.5 * dp / (p0 + p_ave) ; eps2 = eps*eps
950 intp(m) = (al0 + lambda / (p0 + p_ave) - spv_ref)*dp + 2.0*eps* &
9510 lambda * eps2 * (C1_3 + eps2*(0.2 + eps2*(C1_7 + C1_9*eps2)))
952 enddo
953 ! Use Boole's rule to integrate the values.
954 inty_dza(i,j) = C1_90*(7.0*(intp(1)+intp(5)) + 32.0*(intp(2)+intp(4)) + &
9550 12.0*intp(3))
956 enddo ; enddo ; endif
9570end subroutine int_spec_vol_dp_wright
958
959!> Calculate the in-situ density for 1D arraya inputs and outputs.
96017393subroutine calculate_density_array_buggy_Wright(this, T, S, pressure, rho, start, npts, rho_ref)
961 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
962 real, dimension(:), intent(in) :: T !< Potential temperature relative to the surface [degC]
963 real, dimension(:), intent(in) :: S !< Salinity [PSU]
964 real, dimension(:), intent(in) :: pressure !< Pressure [Pa]
965 real, dimension(:), intent(out) :: rho !< In situ density [kg m-3]
966 integer, intent(in) :: start !< The starting index for calculations
967 integer, intent(in) :: npts !< The number of values to calculate
968 real, optional, intent(in) :: rho_ref !< A reference density [kg m-3]
969
970 ! Local variables
971 integer :: j
972
97317393 if (present(rho_ref)) then
9740 do j = start, start+npts-1
9750 rho(j) = density_anomaly_elem_buggy_Wright(this, T(j), S(j), pressure(j), rho_ref)
976 enddo
977 else
9786278843 do j = start, start+npts-1
9796278843 rho(j) = density_elem_buggy_Wright_loc(T(j), S(j), pressure(j))
980 enddo
981 endif
98217393end subroutine calculate_density_array_buggy_Wright
983
984!> Calculate the in-situ density for 2D arraya inputs and outputs.
98512subroutine calculate_density_array_2d_buggy_Wright(this, T, S, pressure, rho, &
986 dom, rho_ref)
987 class(buggy_Wright_EOS), intent(in) :: this
988 !< This EOS
989 real, intent(in) :: T(:,:)
990 !< Potential temperature relative to the surface [degC]
991 real, intent(in) :: S(:,:)
992 !< Salinity [PSU]
993 real, intent(in) :: pressure(:,:)
994 !< Pressure [Pa]
995 real, intent(out) :: rho(:,:)
996 !< In situ density [kg m-3]
997 integer, intent(in) :: dom(2,2)
998 !< Index bounds of domain. First index is rank, second is bounds
999 real, optional, intent(in) :: rho_ref
1000 !< A reference density [kg m-3]
1001
1002 integer :: is, ie, js, je
1003 integer :: i, j
1004
100512 is = dom(1,1) ; ie = dom(1,2)
100612 js = dom(2,1) ; je = dom(2,2)
1007
1008 ! NOTE: There is an implicit copy of `this` which cannot yet be prevented.
1009 ! Possibly because Nvidia cannot associate `this` with `EOS%type`.
1010
101112 if (present(rho_ref)) then
10120 do concurrent (j=js:je, i=is:ie)
1013 rho(i,j) = density_anomaly_elem_buggy_Wright(this, T(i,j), S(i,j), &
10140 pressure(i,j), rho_ref)
1015 enddo
1016 else
101712 do concurrent (j=js:je, i=is:ie)
1018808188 rho(i,j) = density_elem_buggy_Wright_loc( T(i,j), S(i,j), pressure(i,j))
1019 enddo
1020 endif
102112end subroutine calculate_density_array_2d_buggy_Wright
1022
1023!> Calculate the in-situ density for 3D array inputs and outputs.
10240subroutine calculate_density_array_3d_buggy_Wright(this, T, S, pressure, rho, &
1025 dom, rho_ref)
1026 class(buggy_Wright_EOS), intent(in) :: this
1027 !< This EOS
1028 real, intent(in) :: T(:,:,:)
1029 !< Potential temperature relative to the surface [degC]
1030 real, intent(in) :: S(:,:,:)
1031 !< Salinity [PSU]
1032 real, intent(in) :: pressure(:,:,:)
1033 !< Pressure [Pa]
1034 real, intent(out) :: rho(:,:,:)
1035 !< In situ density [kg m-3]
1036 integer, intent(in) :: dom(3,2)
1037 !< Index bounds of domain. First index is rank, second is bounds
1038 real, optional, intent(in) :: rho_ref
1039 !< A reference density [kg m-3]
1040
1041 integer :: is, ie, js, je, ks, ke
1042 integer :: i, j, k
1043
10440 is = dom(1,1) ; ie = dom(1,2)
10450 js = dom(2,1) ; je = dom(2,2)
10460 ks = dom(3,1) ; ke = dom(3,2)
1047
1048 ! NOTE: There is an implicit copy of `this` which cannot yet be prevented.
1049 ! Possibly because Nvidia cannot associate `this` with `EOS%type`.
1050
10510 if (present(rho_ref)) then
10520 do concurrent (k=ks:ke, j=js:je, i=is:ie)
1053 rho(i,j,k) = density_anomaly_elem_buggy_Wright(this, T(i,j,k), S(i,j,k), &
10540 pressure(i,j,k), rho_ref)
1055 enddo
1056 else
10570 do concurrent (k=ks:ke, j=js:je, i=is:ie)
10580 rho(i,j,k) = density_elem_buggy_Wright_loc( T(i,j,k), S(i,j,k), pressure(i,j,k))
1059 enddo
1060 endif
10610end subroutine calculate_density_array_3d_buggy_Wright
1062
1063!> Calculate the in-situ specific volume for 1D array inputs and outputs.
10640subroutine calculate_spec_vol_array_buggy_Wright(this, T, S, pressure, specvol, start, npts, spv_ref)
1065 class(buggy_Wright_EOS), intent(in) :: this !< This EOS
1066 real, dimension(:), intent(in) :: T !< Potential temperature relative to the surface [degC]
1067 real, dimension(:), intent(in) :: S !< Salinity [PSU]
1068 real, dimension(:), intent(in) :: pressure !< Pressure [Pa]
1069 real, dimension(:), intent(out) :: specvol !< In situ specific volume [m3 kg-1]
1070 integer, intent(in) :: start !< The starting index for calculations
1071 integer, intent(in) :: npts !< The number of values to calculate
1072 real, optional, intent(in) :: spv_ref !< A reference specific volume [m3 kg-1]
1073
1074 ! Local variables
1075 integer :: j
1076
10770 if (present(spv_ref)) then
10780 do j = start, start+npts-1
10790 specvol(j) = spec_vol_anomaly_elem_buggy_Wright(this, T(j), S(j), pressure(j), spv_ref)
1080 enddo
1081 else
10820 do j = start, start+npts-1
10830 specvol(j) = spec_vol_elem_buggy_Wright(this, T(j), S(j), pressure(j) )
1084 enddo
1085 endif
1086
10870end subroutine calculate_spec_vol_array_buggy_Wright
1088
1089
1090!> Calculate the in-situ density derivatives for 2D array inputs and outputs.
10910subroutine calculate_density_derivs_2d_buggy_Wright(this, T, S, pressure, &
10920 drho_dT, drho_dS, dom)
1093 class(buggy_Wright_EOS), intent(in) :: this
1094 !< This EOS
1095 real, intent(in) :: T(:,:)
1096 !< Potential temperature relative to the surface [degC]
1097 real, intent(in) :: S(:,:)
1098 !< Salinity [PSU]
1099 real, intent(in) :: pressure(:,:)
1100 !< Pressure [Pa]
1101 real, intent(out) :: drho_dT(:,:)
1102 !< Partial derivative of density with potential temperature [kg m-3 degC-1]
1103 real, intent(out) :: drho_dS(:,:)
1104 !< Partial derivative of density with salinity [kg m-3 PSU-1]
1105 integer, intent(in) :: dom(2,2)
1106 !< Index bounds of domain. First index is rank, second is bounds
1107
1108 integer :: is, ie, js, je
1109 integer :: i, j
1110
11110 is = dom(1,1) ; ie = dom(1,2)
11120 js = dom(2,1) ; je = dom(2,2)
1113
1114 ! NOTE: There is an implicit copy of `this` which cannot yet be prevented.
1115
11160 do concurrent (j=js:je, i=is:ie)
1117 call calculate_density_derivs_elem_buggy_Wright_loc( T(i,j), S(i,j), &
11180 pressure(i,j), drho_dT(i,j), drho_dS(i,j))
1119 enddo
11200end subroutine calculate_density_derivs_2d_buggy_Wright
1121
1122!> Calculate the in-situ density derivatives for 3D array inputs and outputs.
112384subroutine calculate_density_derivs_3d_buggy_Wright(this, T, S, pressure, &
112484 drho_dT, drho_dS, dom)
1125 class(buggy_Wright_EOS), intent(in) :: this
1126 !< This EOS
1127 real, intent(in) :: T(:,:,:)
1128 !< Potential temperature relative to the surface [degC]
1129 real, intent(in) :: S(:,:,:)
1130 !< Salinity [PSU]
1131 real, intent(in) :: pressure(:,:,:)
1132 !< Pressure [Pa]
1133 real, intent(out) :: drho_dT(:,:,:)
1134 !< Partial derivative of density with potential temperature [kg m-3 degC-1]
1135 real, intent(out) :: drho_dS(:,:,:)
1136 !< Partial derivative of density with salinity [kg m-3 PSU-1]
1137 integer, intent(in) :: dom(3,2)
1138 !< Index bounds of domain. First index is rank, second is bounds
1139
1140 integer :: is, ie, js, je, ks, ke
1141 integer :: i, j, k
1142
114384 is = dom(1,1) ; ie = dom(1,2)
114484 js = dom(2,1) ; je = dom(2,2)
114584 ks = dom(3,1) ; ke = dom(3,2)
1146
1147 ! NOTE: There is an implicit copy of `this` which cannot yet be prevented.
1148
114984 do concurrent (k=ks:ke, j=js:je, i=is:ie)
1150 call calculate_density_derivs_elem_buggy_Wright_loc( T(i,j,k), S(i,j,k), &
115111099004 pressure(i,j,k), drho_dT(i,j,k), drho_dS(i,j,k))
1152 enddo
115384end subroutine calculate_density_derivs_3d_buggy_Wright
1154
1155!> Set coefficients that can correct bugs un the buggy Wright equation of state.
11562subroutine set_params_buggy_Wright(this, use_Wright_2nd_deriv_bug)
1157 class(buggy_Wright_EOS), intent(inout) :: this !< This EOS
1158 logical, optional, intent(in) :: use_Wright_2nd_deriv_bug !< If true, use a buggy
1159 !! buggy version of the calculations of the second
1160 !! derivative of density with temperature and with temperature and
1161 !! pressure. This bug is corrected in the default version.
1162
11632 this%three = 3.0
11642 if (present(use_Wright_2nd_deriv_bug)) then
11651 if (use_Wright_2nd_deriv_bug) then ; this%three = 2.0
11661 else ; this%three = 3.0 ; endif
1167 endif
1168
11692end subroutine set_params_buggy_Wright
1170
1171
1172!> \namespace mom_eos_wright
1173!!
1174!! \section section_EOS_Wright Wright equation of state
1175!!
1176!! Wright, 1997, provide an approximation for the in situ density as a function of
1177!! potential temperature, salinity and pressure. The formula follow the Tumlirz
1178!! equation of state which are easier to evaluate and make efficient.
1179!!
1180!! Two ranges are provided by Wright: a "full" range and "reduced" range. The version in this
1181!! module uses the reduced range.
1182!!
1183!! Originally coded in 2000 by R. Hallberg.
1184!! Anomaly form coded in 3/18.
1185!!
1186!! \subsection section_EOS_Wright_references References
1187!!
1188!! Wright, D., 1997: An Equation of State for Use in Ocean Models: Eckart's Formula Revisited.
1189!! J. Ocean. Atmosph. Tech., 14 (3), 735-740.
1190!! https://journals.ametsoc.org/doi/abs/10.1175/1520-0426%281997%29014%3C0735%3AAEOSFU%3E2.0.CO%3B2
1191
11922end module MOM_EOS_Wright