eos_density_point AND eos_specvol_derivs at the same point from
ONE evaluation of the active EOS. The isopycnal-slope and Redi
builders need both (drho/dX = -rho^2 * dSV/dX, locally
referenced); calling the two routines separately evaluated the EOS
twice – under Roquet, two full roquet_spv_point calls, the first
of which threw its derivatives away. Each branch uses the same
expressions as the two routines it fuses, so rho, dsv_dt and
dsv_ds are the numbers they return.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(eos_t), | intent(in) | :: | eos |
Shared EOS handle (variant + scalar coeffs), by value. |
||
| real(kind=wp), | intent(in) | :: | T |
Potential temperature (degC) and practical salinity (PSU). |
||
| real(kind=wp), | intent(in) | :: | S |
Potential temperature (degC) and practical salinity (PSU). |
||
| real(kind=wp), | intent(in) | :: | p |
Pressure (Pa). |
||
| real(kind=wp), | intent(out) | :: | rho |
In-situ density (kg/m^3), as |
||
| real(kind=wp), | intent(out) | :: | dsv_dt |
dSV/dT, dSV/dS, as |
||
| real(kind=wp), | intent(out) | :: | dsv_ds |
dSV/dT, dSV/dS, as |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | T_sq | ||||
| real(kind=wp), | private | :: | alpha_0 | ||||
| real(kind=wp), | private | :: | dlam_ds | ||||
| real(kind=wp), | private | :: | dlam_dt | ||||
| real(kind=wp), | private | :: | dp0_ds | ||||
| real(kind=wp), | private | :: | dp0_dt | ||||
| real(kind=wp), | private | :: | inv_p | ||||
| real(kind=wp), | private | :: | inv_p2 | ||||
| real(kind=wp), | private | :: | lambda | ||||
| real(kind=wp), | private | :: | p_0 | ||||
| real(kind=wp), | private | :: | p_plus_p0 | ||||
| real(kind=wp), | private | :: | sv_roq |
pure subroutine eos_density_specvol_derivs(eos, T, S, p, rho, dsv_dt, dsv_ds) !! `eos_density_point` AND `eos_specvol_derivs` at the same point from !! ONE evaluation of the active EOS. The isopycnal-slope and Redi !! builders need both (`drho/dX = -rho^2 * dSV/dX`, locally !! referenced); calling the two routines separately evaluated the EOS !! twice -- under Roquet, two full `roquet_spv_point` calls, the first !! of which threw its derivatives away. Each branch uses the same !! expressions as the two routines it fuses, so `rho`, `dsv_dt` and !! `dsv_ds` are the numbers they return. !$acc routine seq type(eos_t), intent(in) :: eos !! Shared EOS handle (variant + scalar coeffs), by value. real(wp), intent(in) :: T, S !! Potential temperature (degC) and practical salinity (PSU). real(wp), intent(in) :: p !! Pressure (Pa). real(wp), intent(out) :: rho !! In-situ density (kg/m^3), as `eos_density_point`. real(wp), intent(out) :: dsv_dt, dsv_ds !! dSV/dT, dSV/dS, as `eos_specvol_derivs`. real(wp) :: T_sq, alpha_0, p_0, lambda, p_plus_p0, inv_p, inv_p2 real(wp) :: dp0_dt, dlam_dt, dp0_ds, dlam_ds real(wp) :: sv_roq if (eos%variant == EOS_VARIANT_ROQUET_SPV) then call roquet_spv_point(T, S, p, sv_roq, dsv_dt, dsv_ds) rho = 1.0_wp/sv_roq else if (eos%variant == EOS_VARIANT_WRIGHT_97) then T_sq = T*T alpha_0 = WRIGHT_A0 + WRIGHT_A1*T + WRIGHT_A2*S p_0 = WRIGHT_B0 + WRIGHT_B1*T + WRIGHT_B2*T_sq + WRIGHT_B3*T_sq*T + & WRIGHT_B4*S + WRIGHT_B5*S*T lambda = WRIGHT_C0 + WRIGHT_C1*T + WRIGHT_C2*T_sq + WRIGHT_C3*T_sq*T + & WRIGHT_C4*S + WRIGHT_C5*S*T p_plus_p0 = p + p_0 rho = p_plus_p0/(lambda + alpha_0*p_plus_p0) dp0_dt = WRIGHT_B1 + 2.0_wp*WRIGHT_B2*T + 3.0_wp*WRIGHT_B3*T_sq + & WRIGHT_B5*S dlam_dt = WRIGHT_C1 + 2.0_wp*WRIGHT_C2*T + 3.0_wp*WRIGHT_C3*T_sq + & WRIGHT_C5*S dp0_ds = WRIGHT_B4 + WRIGHT_B5*T dlam_ds = WRIGHT_C4 + WRIGHT_C5*T inv_p = 1.0_wp/p_plus_p0 inv_p2 = inv_p*inv_p dsv_dt = WRIGHT_A1 + dlam_dt*inv_p - lambda*dp0_dt*inv_p2 dsv_ds = WRIGHT_A2 + dlam_ds*inv_p - lambda*dp0_ds*inv_p2 else rho = eos%rho0 + eos%beta_S*(S - eos%S_ref) - eos%alpha_T*(T - eos%T_ref) dsv_dt = eos%alpha_T/(eos%rho0*eos%rho0) dsv_ds = -eos%beta_S/(eos%rho0*eos%rho0) end if end subroutine eos_density_specvol_derivs