eos_specvol_derivs Subroutine

public 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).

Arguments

Type IntentOptional 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).


Calls

proc~~eos_specvol_derivs~~CallsGraph proc~eos_specvol_derivs eos_specvol_derivs proc~roquet_spv_point roquet_spv_point proc~eos_specvol_derivs->proc~roquet_spv_point

Called by

proc~~eos_specvol_derivs~~CalledByGraph proc~eos_specvol_derivs eos_specvol_derivs proc~epbl_column_kernel epbl_column_kernel proc~epbl_column_kernel->proc~eos_specvol_derivs proc~ks_solve_column ks_solve_column proc~ks_solve_column->proc~eos_specvol_derivs proc~tidal_mixing_column_kernel tidal_mixing_column_kernel proc~tidal_mixing_column_kernel->proc~eos_specvol_derivs proc~epbl_compute epbl_compute proc~epbl_compute->proc~epbl_column_kernel proc~kappa_shear_column_kernel kappa_shear_column_kernel proc~kappa_shear_column_kernel->proc~ks_solve_column proc~kappa_shear_vertex_kernel kappa_shear_vertex_kernel proc~kappa_shear_vertex_kernel->proc~ks_solve_column proc~tidal_mixing_compute tidal_mixing_compute proc~tidal_mixing_compute->proc~tidal_mixing_column_kernel proc~kappa_shear_compute kappa_shear_compute proc~kappa_shear_compute->proc~kappa_shear_column_kernel proc~kappa_shear_compute->proc~kappa_shear_vertex_kernel proc~vmix_apply_in_stage vmix_apply_in_stage proc~vmix_apply_in_stage->proc~epbl_compute proc~vmix_apply_in_stage->proc~tidal_mixing_compute proc~vmix_apply_in_stage->proc~kappa_shear_compute proc~run_stage run_stage proc~run_stage->proc~vmix_apply_in_stage proc~run_stage_split run_stage_split proc~run_stage_split->proc~vmix_apply_in_stage proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split

Variables

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

Source Code

   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