Analytic specific-volume sensitivities dSV/dT and dSV/dS (SV = 1/rho) at a point. Needed by the EPBL energy bookkeeping (pressure-weighted PE-per-unit-tracer-change weights) and kappa-shear buoyancy — dSV/dX = -(1/rho^2) d(rho)/dX.
Takes the shared eos_t handle BY VALUE (flat POD,
no allocatable) so the device copy is register-resident.
The select case (eos%variant) body is warp-uniform (one
variant per run) — ~free.
Linear variant: rho = rho0 + beta_S (S - S_ref) - alpha_T (T - T_ref) gives constant dSV/dT = +alpha_T/rho0^2 and dSV/dS = -beta_S/rho0^2 (evaluated at the reference density, consistent with the Boussinesq weights that consume them).
Wright (1997) variant: SV = alpha_0(T,S) + lambda(T,S)/P with
P = p + p_0(T,S), differentiable in closed form from the
Table A1 polynomials:
dSV/dX = d(alpha_0)/dX + d(lambda)/dX / P
- lambda * d(p_0)/dX / P^2.
The else is unreachable-by-contract: eos_validate
guarantees eos%variant is in the device-callable set at
configure time (device code cannot error stop).
| 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 |
In-situ temperature (degC) and salinity (PSU). |
||
| real(kind=wp), | intent(in) | :: | S |
In-situ temperature (degC) and salinity (PSU). |
||
| real(kind=wp), | intent(in) | :: | p |
Pressure (Pa), hydrostatic surface-relative. |
||
| real(kind=wp), | intent(out) | :: | dsv_dt |
dSV/dT (m^3/kg/degC); > 0 for warm-expands water. |
||
| real(kind=wp), | intent(out) | :: | dsv_ds |
dSV/dS (m^3/kg/PSU); < 0 (salt contracts). |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | T_sq | ||||
| real(kind=wp), | private | :: | big_p | ||||
| 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 | :: | sv_roq |
pure subroutine eos_specvol_derivs(eos, T, S, p, dsv_dt, dsv_ds) !! Analytic specific-volume sensitivities dSV/dT and dSV/dS !! (SV = 1/rho) at a point. Needed by the EPBL energy !! bookkeeping (pressure-weighted PE-per-unit-tracer-change !! weights) and kappa-shear buoyancy — dSV/dX = -(1/rho^2) !! d(rho)/dX. !! !! Takes the shared `eos_t` handle BY VALUE (flat POD, !! no allocatable) so the device copy is register-resident. !! The `select case (eos%variant)` body is warp-uniform (one !! variant per run) — ~free. !! !! Linear variant: rho = rho0 + beta_S (S - S_ref) - alpha_T !! (T - T_ref) gives constant dSV/dT = +alpha_T/rho0^2 and !! dSV/dS = -beta_S/rho0^2 (evaluated at the reference density, !! consistent with the Boussinesq weights that consume them). !! !! Wright (1997) variant: SV = alpha_0(T,S) + lambda(T,S)/P with !! P = p + p_0(T,S), differentiable in closed form from the !! Table A1 polynomials: !! dSV/dX = d(alpha_0)/dX + d(lambda)/dX / P !! - lambda * d(p_0)/dX / P^2. !! The `else` is unreachable-by-contract: `eos_validate` !! guarantees `eos%variant` is in the device-callable set at !! configure time (device code cannot `error stop`). !$acc routine seq type(eos_t), intent(in) :: eos !! Shared EOS handle (variant + scalar coeffs), by value. real(wp), intent(in) :: T, S !! In-situ temperature (degC) and salinity (PSU). real(wp), intent(in) :: p !! Pressure (Pa), hydrostatic surface-relative. real(wp), intent(out) :: dsv_dt !! dSV/dT (m^3/kg/degC); > 0 for warm-expands water. real(wp), intent(out) :: dsv_ds !! dSV/dS (m^3/kg/PSU); < 0 (salt contracts). real(wp) :: T_sq, p_0, lambda, big_p, 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) else if (eos%variant == EOS_VARIANT_WRIGHT_97) then T_sq = T*T 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 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 big_p = p + p_0 inv_p = 1.0_wp/big_p 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 dsv_dt = eos%alpha_T/(eos%rho0*eos%rho0) dsv_ds = -eos%beta_S/(eos%rho0*eos%rho0) end if end subroutine eos_specvol_derivs