rdb_eos Module

Holds the EOS variant tag + scalar EOS coefficients (linear Boussinesq) and the kernels that convert (T, S) -> ρ on the multilayer C-grid. Coastal uses a sibling linear EOS in rdb_ml_eos; the ocean path needs a real nonlinear EOS along the FV pressure-gradient integration path. Default is Wright (1997) — the same EOS MOM6 uses by default — chosen for cost (one rational expression, no LUT) and accuracy in the open ocean. TEOS-10 is a future option.

Phase 5c status: linear branch live; Wright + LUT scratch still pending. The kernel follows the outer-shim + flat-impl pattern: an outer shim pulls the registered S, T tracer arrays off the owning state and forwards them as bare 3D arrays to the inner _impl routine. This avoids the NVHPC stdpar deep-deref issue with array-of-derived-types tracer registries (see CLAUDE.md feedback_nvhpc_impl_inlining). This module is regime-agnostic — it carries NO ocean-state dependency; the ocean C-grid shim ocean_eos_compute lives in rdb_ocean_eos_compute (core/ocean/state/), and the coastal shim in rdb_ml_eos.


Uses

  • module~~rdb_eos~~UsesGraph module~rdb_eos rdb_eos module~rdb_constants rdb_constants module~rdb_eos->module~rdb_constants module~rdb_grid rdb_grid module~rdb_eos->module~rdb_grid pic_types pic_types module~rdb_constants->pic_types module~rdb_grid->module~rdb_constants

Used by

  • module~~rdb_eos~~UsedByGraph module~rdb_eos rdb_eos module~rdb_ice_basal_flux rdb_ice_basal_flux module~rdb_ice_basal_flux->module~rdb_eos module~rdb_ice_frazil rdb_ice_frazil module~rdb_ice_frazil->module~rdb_eos module~rdb_ice_frazil_uptake rdb_ice_frazil_uptake module~rdb_ice_frazil_uptake->module~rdb_eos module~rdb_ice_thermo_driver rdb_ice_thermo_driver module~rdb_ice_thermo_driver->module~rdb_eos module~rdb_ocean_cavity_flux rdb_ocean_cavity_flux module~rdb_ocean_cavity_flux->module~rdb_eos module~rdb_ocean_cavity_melt rdb_ocean_cavity_melt module~rdb_ocean_cavity_flux->module~rdb_ocean_cavity_melt module~rdb_ocean_cavity_melt->module~rdb_eos module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_diag_fills->module~rdb_eos module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_diag_fills->module~rdb_ocean_state module~rdb_ocean_vcoord rdb_ocean_vcoord module~rdb_ocean_diag_fills->module~rdb_ocean_vcoord module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_eos module~rdb_ocean_dyn->module~rdb_ocean_cavity_flux module~rdb_ocean_eos_compute rdb_ocean_eos_compute module~rdb_ocean_dyn->module~rdb_ocean_eos_compute module~rdb_ocean_epbl rdb_ocean_epbl module~rdb_ocean_dyn->module~rdb_ocean_epbl module~rdb_ocean_isopycnal_slopes rdb_ocean_isopycnal_slopes module~rdb_ocean_dyn->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_kappa_shear rdb_ocean_kappa_shear module~rdb_ocean_dyn->module~rdb_ocean_kappa_shear module~rdb_ocean_pressure_force rdb_ocean_pressure_force module~rdb_ocean_dyn->module~rdb_ocean_pressure_force module~rdb_ocean_redi rdb_ocean_redi module~rdb_ocean_dyn->module~rdb_ocean_redi module~rdb_ocean_remap rdb_ocean_remap module~rdb_ocean_dyn->module~rdb_ocean_remap module~rdb_ocean_tidal_mixing rdb_ocean_tidal_mixing module~rdb_ocean_dyn->module~rdb_ocean_tidal_mixing module~rdb_ocean_dyn->module~rdb_ocean_vcoord module~rdb_ocean_vdiff rdb_ocean_vdiff module~rdb_ocean_dyn->module~rdb_ocean_vdiff module~rdb_ocean_vmix rdb_ocean_vmix module~rdb_ocean_dyn->module~rdb_ocean_vmix module~rdb_barotropic_coupling rdb_barotropic_coupling module~rdb_ocean_dyn->module~rdb_barotropic_coupling module~rdb_ocean_bt_budget_probe rdb_ocean_bt_budget_probe module~rdb_ocean_dyn->module~rdb_ocean_bt_budget_probe module~rdb_ocean_gm rdb_ocean_gm module~rdb_ocean_dyn->module~rdb_ocean_gm module~rdb_ocean_mle rdb_ocean_mle module~rdb_ocean_dyn->module~rdb_ocean_mle module~rdb_ocean_varmix rdb_ocean_varmix module~rdb_ocean_dyn->module~rdb_ocean_varmix module~rdb_continuity rdb_continuity module~rdb_ocean_dyn->module~rdb_continuity module~rdb_ocean_meke rdb_ocean_meke module~rdb_ocean_dyn->module~rdb_ocean_meke module~rdb_ocean_eos_compute->module~rdb_eos module~rdb_ocean_epbl->module~rdb_eos module~rdb_ocean_isopycnal_slopes->module~rdb_eos module~rdb_ocean_kappa_shear->module~rdb_eos module~rdb_ocean_pgf_reconstruct rdb_ocean_pgf_reconstruct module~rdb_ocean_pgf_reconstruct->module~rdb_eos module~rdb_ocean_pressure_force->module~rdb_eos module~rdb_ocean_pressure_force->module~rdb_ocean_pgf_reconstruct module~rdb_ocean_redi->module~rdb_eos module~rdb_ocean_remap->module~rdb_eos module~rdb_ocean_remap->module~rdb_ocean_vcoord module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_setup->module~rdb_eos module~rdb_ocean_setup->module~rdb_ocean_cavity_melt module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_ocean_epbl module~rdb_ocean_setup->module~rdb_ocean_pressure_force module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_setup->module~rdb_ocean_vcoord module~rdb_ocean_setup->module~rdb_ocean_vdiff module~rdb_ocean_setup->module~rdb_ocean_vmix module~rdb_ocean_state->module~rdb_eos module~rdb_ocean_state->module~rdb_ocean_cavity_flux module~rdb_ocean_state->module~rdb_ocean_dyn module~rdb_ocean_state->module~rdb_ocean_epbl module~rdb_ocean_state->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_state->module~rdb_ocean_kappa_shear module~rdb_ocean_state->module~rdb_ocean_pressure_force module~rdb_ocean_state->module~rdb_ocean_redi module~rdb_ocean_state->module~rdb_ocean_tidal_mixing module~rdb_ocean_state->module~rdb_ocean_vcoord module~rdb_ocean_state->module~rdb_ocean_vdiff module~rdb_ocean_state->module~rdb_ocean_vmix module~rdb_ocean_state->module~rdb_ocean_gm module~rdb_ocean_state->module~rdb_ocean_mle module~rdb_ocean_state->module~rdb_ocean_varmix module~rdb_ocean_state->module~rdb_continuity module~rdb_ocean_state->module~rdb_ocean_meke module~rdb_ocean_tidal_mixing->module~rdb_eos module~rdb_ocean_vcoord->module~rdb_eos module~rdb_ocean_vdiff->module~rdb_eos module~rdb_ocean_vmix->module~rdb_eos proc~tfreeze_ib_impl tfreeze_ib_impl proc~tfreeze_ib_impl->module~rdb_eos proc~thermal_driving_impl thermal_driving_impl proc~thermal_driving_impl->module~rdb_eos proc~validate_config validate_config proc~validate_config->module~rdb_eos proc~validate_config->module~rdb_ocean_cavity_melt proc~validate_config->module~rdb_ocean_pressure_force proc~validate_config->module~rdb_ocean_tidal_mixing proc~validate_config->module~rdb_ocean_vmix module~rdb_barotropic_coupling->module~rdb_ocean_pressure_force module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_dyn module~rdb_driver->module~rdb_ocean_state module~rdb_ocean_engine rdb_ocean_engine module~rdb_driver->module~rdb_ocean_engine module~rdb_handle rdb_handle module~rdb_handle->module~rdb_ocean_state module~rdb_handle->module~rdb_ocean_engine module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_dyn module~rdb_ocean_api->module~rdb_ocean_eos_compute module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_bt_budget_probe->module~rdb_ocean_pressure_force module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ice_basal_flux module~rdb_ocean_engine->module~rdb_ice_frazil module~rdb_ocean_engine->module~rdb_ice_frazil_uptake module~rdb_ocean_engine->module~rdb_ice_thermo_driver module~rdb_ocean_engine->module~rdb_ocean_cavity_flux module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_engine->module~rdb_ocean_dyn module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_vcoord module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ice_transport rdb_ice_transport module~rdb_ocean_engine->module~rdb_ice_transport module~rdb_ocean_gm->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_mle->module~rdb_ocean_epbl module~rdb_ocean_varmix->module~rdb_ocean_isopycnal_slopes proc~configure_ocean_cavity_melt configure_ocean_cavity_melt proc~configure_ocean_cavity_melt->module~rdb_ocean_cavity_melt module~rdb_continuity->module~rdb_ocean_gm module~rdb_continuity->module~rdb_ocean_mle module~rdb_ocean_meke->module~rdb_ocean_gm module~rdb_ocean_meke->module~rdb_ocean_varmix module~rdb_ice_transport->module~rdb_continuity

Variables

Type Visibility Attributes Name Initial
integer, public, parameter :: EOS_VARIANT_LINEAR = 1

Linear T/S (debug / lock-exchange / Eady).

integer, public, parameter :: EOS_VARIANT_ROQUET_SPV = 4

Roquet et al. (2015) “Accurate polynomial expressions for the density and specific volume of seawater using the TEOS-10 standard.” Ocean Modelling 90:29-43 — the specific-volume (SpV) polynomial variant (NEMO / MOM6 Roquet_SpV). ~75-term polynomial in (CT, SA, p) giving TEOS-10-class accuracy as self-contained parameter arithmetic (no GSW LUT, two sqrt, no iteration). Device-clean. The coefficient assembly + the PT->CT conversion poly were transcribed from the validated Python prototype (local_archive/prototypes/roquet_spv_eos.py, 128/128 identical to the published MOM6 MOM_EOS_Roquet_SpV transcription); MOM6 is credited as the published-coefficient reference. CONVENTION (pinned 2026-06-16): consumers work in model (PT, SP); this branch converts SR = SP·(35.16504/35) for Reference Salinity and CT = ct_from_pt(SR, PT) locally, so it stays device-callable and returns derivatives w.r.t. the model (PT, SP) via the analytic chain rule.

integer, public, parameter :: EOS_VARIANT_TEOS10 = 3

TEOS-10 (future).

integer, public, parameter :: EOS_VARIANT_WRIGHT_97 = 2

Wright (1997) “An Equation of State for Use in Ocean Models: Eckart’s Formula Revisited” (JAOT 14:735-740). Rational form: ρ = (P + p_0) / (λ + α_0*(P + p_0)) with cubic / linear polynomials in (T, S). Matches the MOM6 default EOS. Phase Tier-1 evaluates at P = eos%p_ref (0 by default → surface ρ); Phase 5d adds the FV-PGF integration path with in-situ pressure.

real(kind=wp), public, parameter :: ROQ_CP0 = 3991.86795711963_wp
real(kind=wp), public, parameter :: ROQ_CT_SFAC = 0.0248826675584615_wp

(35.16504/35)/40 [(g/kg)^-1] — normalises SR for the poly.

real(kind=wp), public, parameter :: ROQ_R1_S0 = 0.875_wp/35.16504_wp

Inverse plausible salinity range (kg/g).

real(kind=wp), public, parameter :: ROQ_RDELTAS = 24.0_wp

Salinity offset before the sqrt (g/kg).

real(kind=wp), public, parameter :: ROQ_SR_FACTOR = 35.16504_wp/35.0_wp

SP -> SR (Reference Salinity) conversion factor.

real(kind=wp), public, parameter :: ROQ_V00 = -4.4015007269e-05_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: ROQ_V01 = 6.9232335784e-06_wp*ROQ_PA2KB**2
real(kind=wp), public, parameter :: ROQ_V02 = -7.5004675975e-07_wp*ROQ_PA2KB**3
real(kind=wp), public, parameter :: ROQ_V03 = 1.7009109288e-08_wp*ROQ_PA2KB**4
real(kind=wp), public, parameter :: ROQ_V04 = -1.6884162004e-08_wp*ROQ_PA2KB**5
real(kind=wp), public, parameter :: ROQ_V05 = 1.9613503930e-09_wp*ROQ_PA2KB**6
real(kind=wp), public, parameter :: SPV000 = 1.0772899069e-03_wp
real(kind=wp), public, parameter :: SPV001 = -1.6889436589e-05_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: SPV002 = 1.0500241168e-06_wp*ROQ_PA2KB**2
real(kind=wp), public, parameter :: SPV003 = -4.1503454190e-07_wp*ROQ_PA2KB**3
real(kind=wp), public, parameter :: SPV010 = -1.4949652640e-05_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV011 = 1.5355844621e-05_wp*(ROQ_I_TS*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV012 = -3.8541359685e-06_wp*(ROQ_I_TS*ROQ_PA2KB**2)
real(kind=wp), public, parameter :: SPV013 = -1.1293871415e-07_wp*(ROQ_I_TS*ROQ_PA2KB**3)
real(kind=wp), public, parameter :: SPV020 = 2.7546851539e-05_wp*ROQ_I_TS**2
real(kind=wp), public, parameter :: SPV021 = -9.6659393016e-06_wp*(ROQ_I_TS**2*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV022 = 1.7178343158e-06_wp*(ROQ_I_TS**2*ROQ_PA2KB**2)
real(kind=wp), public, parameter :: SPV030 = -1.6506828994e-05_wp*ROQ_I_TS**3
real(kind=wp), public, parameter :: SPV031 = 3.1134283336e-06_wp*(ROQ_I_TS**3*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV040 = 6.7896174634e-06_wp*ROQ_I_TS**4
real(kind=wp), public, parameter :: SPV041 = -5.6590253863e-07_wp*(ROQ_I_TS**4*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV050 = -7.2535743349e-07_wp*ROQ_I_TS**5
real(kind=wp), public, parameter :: SPV060 = 1.9041365570e-07_wp*ROQ_I_TS**6
real(kind=wp), public, parameter :: SPV100 = -3.1263658781e-04_wp
real(kind=wp), public, parameter :: SPV101 = 2.1106556158e-05_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: SPV102 = 1.9600661704e-06_wp*ROQ_PA2KB**2
real(kind=wp), public, parameter :: SPV103 = 3.5627020989e-07_wp*ROQ_PA2KB**3
real(kind=wp), public, parameter :: SPV110 = 3.1866349188e-05_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV111 = 2.0914122241e-06_wp*(ROQ_I_TS*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV112 = 1.0157632247e-06_wp*(ROQ_I_TS*ROQ_PA2KB**2)
real(kind=wp), public, parameter :: SPV120 = -3.6597334199e-05_wp*ROQ_I_TS**2
real(kind=wp), public, parameter :: SPV121 = -7.0686982208e-07_wp*(ROQ_I_TS**2*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV130 = 2.4412359055e-05_wp*ROQ_I_TS**3
real(kind=wp), public, parameter :: SPV131 = 7.9562529879e-08_wp*(ROQ_I_TS**3*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV140 = -8.7951832993e-06_wp*ROQ_I_TS**4
real(kind=wp), public, parameter :: SPV150 = -3.4680559205e-07_wp*ROQ_I_TS**5
real(kind=wp), public, parameter :: SPV200 = 6.7615860683e-04_wp
real(kind=wp), public, parameter :: SPV201 = -2.1322804368e-05_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: SPV202 = -2.1666693382e-06_wp*ROQ_PA2KB**2
real(kind=wp), public, parameter :: SPV210 = -3.8070687610e-05_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV211 = -5.7751479725e-06_wp*(ROQ_I_TS*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV220 = 3.4489154625e-05_wp*ROQ_I_TS**2
real(kind=wp), public, parameter :: SPV221 = 1.4488066593e-06_wp*(ROQ_I_TS**2*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV230 = -1.4606740723e-05_wp*ROQ_I_TS**3
real(kind=wp), public, parameter :: SPV240 = 4.4249040774e-06_wp*ROQ_I_TS**4
real(kind=wp), public, parameter :: SPV300 = -8.6127884515e-04_wp
real(kind=wp), public, parameter :: SPV301 = 1.7347655458e-05_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: SPV310 = 2.9818473563e-05_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV311 = 1.0767234341e-06_wp*(ROQ_I_TS*ROQ_PA2KB)
real(kind=wp), public, parameter :: SPV320 = -1.7663254122e-05_wp*ROQ_I_TS**2
real(kind=wp), public, parameter :: SPV330 = 2.3293406656e-06_wp*ROQ_I_TS**3
real(kind=wp), public, parameter :: SPV400 = 5.9010812596e-04_wp
real(kind=wp), public, parameter :: SPV401 = -4.3209400767e-06_wp*ROQ_PA2KB
real(kind=wp), public, parameter :: SPV410 = -1.0011321965e-05_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV420 = 3.5965131935e-06_wp*ROQ_I_TS**2
real(kind=wp), public, parameter :: SPV500 = -2.1503943538e-04_wp
real(kind=wp), public, parameter :: SPV510 = 1.0751931163e-06_wp*ROQ_I_TS
real(kind=wp), public, parameter :: SPV600 = 3.2678954455e-05_wp
integer, public, parameter :: TFREEZE_SET_INVALID = 0

Unrecognised tfreeze_set string — validate_config fails loud on this rather than falling back to a default (a mistyped liquidus is a 0.03 °C physics change with no symptom).

integer, public, parameter :: TFREEZE_SET_ISOMIP = 2

ISOMIP+ ice-shelf-cavity liquidus (TFR_ISOMIP_* below).

integer, public, parameter :: TFREEZE_SET_SEAICE = 1

SIS2/MOM6 sea-ice linear liquidus (TFR_*_COEFF below). The DEFAULT — every run that predates this knob is bit-identical.

real(kind=wp), public, parameter :: TFR_0_COEFF = 0.0_wp

Liquidus intercept (degC). EXACTLY zero for this set — the SIS2 form has no constant term. Named (rather than left implicit) so the handle’s default and eos_freezing_point’s bit-identity contract cannot drift apart.

real(kind=wp), public, parameter :: TFR_ISOMIP_0_COEFF = 0.0832_wp

ISOMIP+ λ2, liquidus intercept (degC).

real(kind=wp), public, parameter :: TFR_ISOMIP_P_COEFF = -7.53e-8_wp

ISOMIP+ λ3, liquidus pressure coefficient (degC/Pa). Numerically equal to TFR_P_COEFF; kept as its own named constant so the two sets stay independently editable.

real(kind=wp), public, parameter :: TFR_ISOMIP_S_COEFF = -0.0573_wp

ISOMIP+ λ1, liquidus slope (degC per PSU).

real(kind=wp), public, parameter :: TFR_P_COEFF = -7.53e-8_wp

Pressure depression dT_f/dp (degC/Pa) — MOM6 DTFREEZE_DP reference value (−7.53e-8 °C/Pa ≈ −0.75 °C per 1000 dbar).

real(kind=wp), public, parameter :: TFR_S_COEFF = -0.054_wp

Liquidus slope dT_f/dS (degC per g/kg) — SIS2’s T_Freeze μ = −0.054 °C/(g/kg); T_f(S=35) = −1.89 °C.

integer, public, parameter :: TS_CONS_ABS = 2

(CT, SA) — for a future TEOS-10/Roquet-CT branch that does the CT/SA->PT/SP conversion locally inside its acc routine seq body. Not yet device-callable; consumers always pass the model-prognostic (T, S) regardless of convention.

integer, public, parameter :: TS_POT_PRAC = 1

Tracer T/S convention: potential temperature + practical salinity (the model-prognostic pair). Identity for the linear and Wright (1997) branches — they consume (PT, SP) directly. Default.

real(kind=wp), public, parameter :: WRIGHT_A0 = 7.057924e-4_wp
real(kind=wp), public, parameter :: WRIGHT_A1 = 3.480336e-7_wp
real(kind=wp), public, parameter :: WRIGHT_A2 = -1.112733e-7_wp
real(kind=wp), public, parameter :: WRIGHT_B0 = 5.790749e8_wp
real(kind=wp), public, parameter :: WRIGHT_B1 = 3.516535e6_wp
real(kind=wp), public, parameter :: WRIGHT_B2 = -4.002714e4_wp
real(kind=wp), public, parameter :: WRIGHT_B3 = 2.084372e2_wp
real(kind=wp), public, parameter :: WRIGHT_B4 = 5.944068e5_wp
real(kind=wp), public, parameter :: WRIGHT_B5 = -9.643486e3_wp
real(kind=wp), public, parameter :: WRIGHT_C0 = 1.704853e5_wp
real(kind=wp), public, parameter :: WRIGHT_C1 = 7.904722e2_wp
real(kind=wp), public, parameter :: WRIGHT_C2 = -7.984422e0_wp
real(kind=wp), public, parameter :: WRIGHT_C3 = 5.140652e-2_wp
real(kind=wp), public, parameter :: WRIGHT_C4 = -2.302158e2_wp
real(kind=wp), public, parameter :: WRIGHT_C5 = -3.079464e0_wp
real(kind=wp), private, parameter :: ALP000 = SPV010
real(kind=wp), private, parameter :: ALP001 = SPV011
real(kind=wp), private, parameter :: ALP002 = SPV012
real(kind=wp), private, parameter :: ALP003 = SPV013
real(kind=wp), private, parameter :: ALP010 = 2.0_wp*SPV020
real(kind=wp), private, parameter :: ALP011 = 2.0_wp*SPV021
real(kind=wp), private, parameter :: ALP012 = 2.0_wp*SPV022
real(kind=wp), private, parameter :: ALP020 = 3.0_wp*SPV030
real(kind=wp), private, parameter :: ALP021 = 3.0_wp*SPV031
real(kind=wp), private, parameter :: ALP030 = 4.0_wp*SPV040
real(kind=wp), private, parameter :: ALP031 = 4.0_wp*SPV041
real(kind=wp), private, parameter :: ALP040 = 5.0_wp*SPV050
real(kind=wp), private, parameter :: ALP050 = 6.0_wp*SPV060
real(kind=wp), private, parameter :: ALP100 = SPV110
real(kind=wp), private, parameter :: ALP101 = SPV111
real(kind=wp), private, parameter :: ALP102 = SPV112
real(kind=wp), private, parameter :: ALP110 = 2.0_wp*SPV120
real(kind=wp), private, parameter :: ALP111 = 2.0_wp*SPV121
real(kind=wp), private, parameter :: ALP120 = 3.0_wp*SPV130
real(kind=wp), private, parameter :: ALP121 = 3.0_wp*SPV131
real(kind=wp), private, parameter :: ALP130 = 4.0_wp*SPV140
real(kind=wp), private, parameter :: ALP140 = 5.0_wp*SPV150
real(kind=wp), private, parameter :: ALP200 = SPV210
real(kind=wp), private, parameter :: ALP201 = SPV211
real(kind=wp), private, parameter :: ALP210 = 2.0_wp*SPV220
real(kind=wp), private, parameter :: ALP211 = 2.0_wp*SPV221
real(kind=wp), private, parameter :: ALP220 = 3.0_wp*SPV230
real(kind=wp), private, parameter :: ALP230 = 4.0_wp*SPV240
real(kind=wp), private, parameter :: ALP300 = SPV310
real(kind=wp), private, parameter :: ALP301 = SPV311
real(kind=wp), private, parameter :: ALP310 = 2.0_wp*SPV320
real(kind=wp), private, parameter :: ALP320 = 3.0_wp*SPV330
real(kind=wp), private, parameter :: ALP400 = SPV410
real(kind=wp), private, parameter :: ALP410 = 2.0_wp*SPV420
real(kind=wp), private, parameter :: ALP500 = SPV510
real(kind=wp), private, parameter :: BET000 = 0.5_wp*SPV100*ROQ_R1_S0
real(kind=wp), private, parameter :: BET001 = 0.5_wp*SPV101*ROQ_R1_S0
real(kind=wp), private, parameter :: BET002 = 0.5_wp*SPV102*ROQ_R1_S0
real(kind=wp), private, parameter :: BET003 = 0.5_wp*SPV103*ROQ_R1_S0
real(kind=wp), private, parameter :: BET010 = 0.5_wp*SPV110*ROQ_R1_S0
real(kind=wp), private, parameter :: BET011 = 0.5_wp*SPV111*ROQ_R1_S0
real(kind=wp), private, parameter :: BET012 = 0.5_wp*SPV112*ROQ_R1_S0
real(kind=wp), private, parameter :: BET020 = 0.5_wp*SPV120*ROQ_R1_S0
real(kind=wp), private, parameter :: BET021 = 0.5_wp*SPV121*ROQ_R1_S0
real(kind=wp), private, parameter :: BET030 = 0.5_wp*SPV130*ROQ_R1_S0
real(kind=wp), private, parameter :: BET031 = 0.5_wp*SPV131*ROQ_R1_S0
real(kind=wp), private, parameter :: BET040 = 0.5_wp*SPV140*ROQ_R1_S0
real(kind=wp), private, parameter :: BET050 = 0.5_wp*SPV150*ROQ_R1_S0
real(kind=wp), private, parameter :: BET100 = SPV200*ROQ_R1_S0
real(kind=wp), private, parameter :: BET101 = SPV201*ROQ_R1_S0
real(kind=wp), private, parameter :: BET102 = SPV202*ROQ_R1_S0
real(kind=wp), private, parameter :: BET110 = SPV210*ROQ_R1_S0
real(kind=wp), private, parameter :: BET111 = SPV211*ROQ_R1_S0
real(kind=wp), private, parameter :: BET120 = SPV220*ROQ_R1_S0
real(kind=wp), private, parameter :: BET121 = SPV221*ROQ_R1_S0
real(kind=wp), private, parameter :: BET130 = SPV230*ROQ_R1_S0
real(kind=wp), private, parameter :: BET140 = SPV240*ROQ_R1_S0
real(kind=wp), private, parameter :: BET200 = 1.5_wp*SPV300*ROQ_R1_S0
real(kind=wp), private, parameter :: BET201 = 1.5_wp*SPV301*ROQ_R1_S0
real(kind=wp), private, parameter :: BET210 = 1.5_wp*SPV310*ROQ_R1_S0
real(kind=wp), private, parameter :: BET211 = 1.5_wp*SPV311*ROQ_R1_S0
real(kind=wp), private, parameter :: BET220 = 1.5_wp*SPV320*ROQ_R1_S0
real(kind=wp), private, parameter :: BET230 = 1.5_wp*SPV330*ROQ_R1_S0
real(kind=wp), private, parameter :: BET300 = 2.0_wp*SPV400*ROQ_R1_S0
real(kind=wp), private, parameter :: BET301 = 2.0_wp*SPV401*ROQ_R1_S0
real(kind=wp), private, parameter :: BET310 = 2.0_wp*SPV410*ROQ_R1_S0
real(kind=wp), private, parameter :: BET320 = 2.0_wp*SPV420*ROQ_R1_S0
real(kind=wp), private, parameter :: BET400 = 2.5_wp*SPV500*ROQ_R1_S0
real(kind=wp), private, parameter :: BET410 = 2.5_wp*SPV510*ROQ_R1_S0
real(kind=wp), private, parameter :: BET500 = 3.0_wp*SPV600*ROQ_R1_S0
real(kind=wp), private, parameter :: ROQ_I_TS = 0.025_wp

Inverse plausible temperature range (1/degC).

real(kind=wp), private, parameter :: ROQ_PA2KB = 1.0e-8_wp

Pa -> kbar.


Derived Types

type, public ::  eos_t

Components

Type Visibility Attributes Name Initial
real(kind=wp), public :: S_ref = 35.0_wp

Reference salinity for linear EOS (PSU).

real(kind=wp), public :: T_ref = 10.0_wp

Reference temperature for linear EOS (degC).

real(kind=wp), public :: alpha_T = 1.7e-4_wp

Thermal expansion coeff (linear EOS), kg/m^3 per degC. NOTE: this default is ~1000× smaller than the standard seawater value (~0.17 kg/m³/K). The small value suppresses baroclinic feedback from PPM round-off in tests that don’t care about realistic density gradients. Density-driven tests (e.g. lock_exchange_2layer) MUST override to the realistic value.

real(kind=wp), public :: beta_S = 7.6e-4_wp

Haline contraction coeff (linear EOS), kg/m^3 per PSU. See alpha_T for the rationale on this small default.

logical, public :: is_init = .false.

True between init and destroy. Prefer this to allocated(...) — tracks GPU device attachment too.

real(kind=wp), public :: p_ref = 0.0_wp

Reference pressure (Pa) at which eos_compute_arrays evaluates ms%rho_layer — i.e. the pressure the model’s POTENTIAL density is referenced to. Set from &ocean_eos_nml p_ref (default 0 ⇒ surface/potential density, bit-identical to every run before the knob existed). The Wright and Roquet branches read it; the linear branch has no pressure dependence and ignores it.

Read more…
real(kind=wp), public :: rho0 = 1035.0_wp

Boussinesq reference density (kg/m^3).

real(kind=wp), public :: tfr_0 = TFR_0_COEFF

Liquidus intercept λ2 (degC). Exactly 0.0 for the default sea-ice set — see eos_freezing_point for the (signed-zero only) bit-identity argument this exactness underwrites.

real(kind=wp), public :: tfr_p = TFR_P_COEFF

Liquidus pressure coefficient λ3 (degC/Pa).

real(kind=wp), public :: tfr_s = TFR_S_COEFF

Liquidus slope λ1 (degC per PSU) — the S coefficient of eos_freezing_point. Selected as a NAMED SET by &ocean_eos_nml tfreeze_set (eos_apply_tfreeze_set), never knob-by-knob: the three numbers are a fitted triple and mixing λ1 from one source with λ2 from another is a silent physics error. Default = the SIS2 sea-ice set ⇒ bit-identical to every run before the knob existed.

integer, public :: ts_convention = TS_POT_PRAC

Tracer T/S convention this EOS expects (TS_POT_PRAC / TS_CONS_ABS). Identity for linear + Wright. This type is a flat POD — NO allocatable component — so it copies into registers for free when passed by value into the device point routines (!$acc routine seq).

integer, public :: variant = EOS_VARIANT_LINEAR

Active EOS variant. Defaults to linear two-tracer (the simplest implemented branch). Wright (1997) rational EOS is also shipped — opt in by setting variant = EOS_VARIANT_WRIGHT_97. TEOS-10 reserved.

Type-Bound Procedures

procedure, public, non_overridable :: destroy => eos_destroy
procedure, public, non_overridable :: init => eos_init

Functions

public pure function eos_density_point(eos, T, S, p) result(rho)

Scalar density evaluation at a point — the same formulas the 3D eos_*_impl kernels apply, exposed for finite-difference verification of eos_specvol_derivs and for host-side diagnostics. Takes the shared eos_t handle by value. The else is unreachable-by-contract (see eos_validate).

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: T
real(kind=wp), intent(in) :: S
real(kind=wp), intent(in) :: p

Return Value real(kind=wp)

public pure elemental function eos_freezing_point(eos, S, p) result(T_f)

Seawater freezing point T_f (degC) at salinity S and pressure p — the ocean-side prerequisite for the sea-ice port (PLAN_SEA_ICE.md, PR 1). Same point-function style as eos_density_point (flat-POD eos_t by value, device- callable), plus elemental so callers can evaluate whole salinity arrays in one reference.

Read more…

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos

Shared EOS handle, by value — carries the liquidus coefficient set (tfr_s/tfr_0/tfr_p) written at configure by eos_apply_tfreeze_set, plus the variant tag the future nonlinear-form branch will read.

real(kind=wp), intent(in) :: S

Salinity (PSU / g/kg).

real(kind=wp), intent(in) :: p

Pressure (Pa), hydrostatic surface-relative (0 at the surface — the frazil kernel’s use case).

Return Value real(kind=wp)

public pure function parse_eos_variant(name) result(code)

Translate a &ocean_eos_nml eos=... string into an EOS_VARIANT_* code. Unrecognised values fall back to EOS_VARIANT_LINEAR (the default = bit-identical to runs that omit the knob). Membership in the device-callable set is enforced separately by eos_validate at configure time.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: name

Return Value integer

public pure function parse_tfreeze_set(name) result(code)

Translate a &ocean_eos_nml tfreeze_set=... string into a TFREEZE_SET_* code. Unlike parse_eos_variant this one does NOT fall back to a default on a typo: it returns TFREEZE_SET_INVALID and validate_config aborts. Silently defaulting would turn a mistyped liquidus into a 0.03 °C shift in the freezing point — a melt-rate sign change at the margin, with no run-time symptom at all.

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: name

Return Value integer

public pure function roquet_spv_value(T_pt, S_sp, p) result(sv)

VALUE-ONLY Roquet et al. (2015) specific volume (m^3/kg) at a point, model variables (PT degC, SP PSU, p Pa). The same number as roquet_spv_point’s sv without the dSV/dT, dSV/dS and PT->CT chain-rule work that a density-only consumer (rho_layer, eos_density_point) would discard – roughly half the arithmetic.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: T_pt
real(kind=wp), intent(in) :: S_sp
real(kind=wp), intent(in) :: p

Return Value real(kind=wp)


Subroutines

public pure subroutine eos_apply_tfreeze_set(eos, code)

Write the named liquidus coefficient SET onto the EOS handle. Called once, at configure time, from the earliest configure_ocean_* stage — before the flat-POD handle is copied onto the vmix / EPBL / kappa-shear / tidal-mixing slots and before ocean_state_enter_data, so every copy and every device kernel taking eos_t by value sees the configured set (same contract as rho0 / p_ref; no !$acc update device is owed).

Read more…

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(inout) :: eos
integer, intent(in) :: code

public pure elemental subroutine eos_buoyancy_coeffs(eos, T, S, p, alpha_T, beta_S)

Thermal-expansion and haline-contraction coefficients of the ACTIVE equation of state at a point, in the SAME DIMENSIONAL convention the eos_t members alpha_T / beta_S carry:

Read more…

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

Potential temperature (degC) and practical salinity (PSU) — the model-prognostic pair, per the TS_POT_PRAC convention.

real(kind=wp), intent(in) :: S

Potential temperature (degC) and practical salinity (PSU) — the model-prognostic pair, per the TS_POT_PRAC convention.

real(kind=wp), intent(in) :: p

Pressure (Pa) at which to evaluate. Which pressure is the CONSUMER’s choice and is documented at each call site: a surface buoyancy flux wants the top of the column (multilayer_state_t%p_top under &ocean_psurf_nml in_eos, else eos%p_ref); an interior-interface closure wants the true in-situ hydrostatic pressure there. Never a horizontally-varying value fed back into ms%rho_layer — see the p_top seam contract in src/core/ocean/README.md.

real(kind=wp), intent(out) :: alpha_T

−∂ρ/∂T (kg/m³ per degC).

real(kind=wp), intent(out) :: beta_S

+∂ρ/∂S (kg/m³ per PSU).

public subroutine eos_compute_arrays(eos, h_layer, hS_layer, hT_layer, rho_layer, nx, ny, nz)

Variant dispatch on bare 3D arrays — the state-agnostic body of ocean_eos_compute (the shim hosted in rdb_ocean_eos_compute), which forwards the multilayer state’s registry arrays here. Kept free of state-type dependencies so the dispatch stays callable from a bare array context. All impls evaluate at the SINGLE, HORIZONTALLY UNIFORM reference pressure eos%p_ref (&ocean_eos_nml p_ref, default 0 ⇒ surface/potential density) with the H_VANISHED vanishing-layer fallback to eos%rho0.

Read more…

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hS_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hT_layer(nx,ny,nz)
real(kind=wp), intent(out) :: rho_layer(nx,ny,nz)
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

public pure elemental subroutine eos_density_derivs(eos, T, S, p, drho_dt, drho_ds)

Density sensitivities ∂ρ/∂T and ∂ρ/∂S of the ACTIVE equation of state at a point — the signed twin of eos_buoyancy_coeffs, which is where the per-variant closed forms live:

Read more…

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: T
real(kind=wp), intent(in) :: S
real(kind=wp), intent(in) :: p
real(kind=wp), intent(out) :: drho_dt

∂ρ/∂T (kg/m³ per degC).

real(kind=wp), intent(out) :: drho_ds

∂ρ/∂S (kg/m³ per PSU).

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

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

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 eos_density_point.

real(kind=wp), intent(out) :: dsv_dt

dSV/dT, dSV/dS, as eos_specvol_derivs.

real(kind=wp), intent(out) :: dsv_ds

dSV/dT, dSV/dS, as eos_specvol_derivs.

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.

Read more…

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

public subroutine eos_validate(eos, ierr)

Host-side fail-loud gate over the device-supported variant set. Device point routines (!$acc routine seq) cannot error stop, so membership in the device-callable set is guaranteed HERE at configure time; the device else branch is then unreachable-by-contract.

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos
integer, intent(out), optional :: ierr

OCEAN_STATUS_ERR_SETUP on an unsupported/unknown EOS variant when present; absent behaves as today (error stop).

public pure subroutine eos_wright_pgf_column_sweep_impl(h_layer, hS_layer, hT_layer, rho_layer_seed, p_top, p_edge_out, rho_insitu_out, gravity, rho_0, nx, ny, nz)

FV-Wright PGF column sweep. Top-down per-column traversal that simultaneously produces the hydrostatic pressure stack p_edge_out and the in-situ density rho_insitu_out at each layer centre.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hS_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hT_layer(nx,ny,nz)
real(kind=wp), intent(in) :: rho_layer_seed(nx,ny,nz)
real(kind=wp), intent(in) :: p_top(nx,ny)

Top-of-column pressure (Pa, >= 0) the EOS argument is measured down from. Does NOT enter p_edge_out.

real(kind=wp), intent(out) :: p_edge_out(nx,ny,nz+1)
real(kind=wp), intent(out) :: rho_insitu_out(nx,ny,nz)
real(kind=wp), intent(in) :: gravity
real(kind=wp), intent(in) :: rho_0
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

public pure subroutine roquet_spv_ts_coeffs(T_pt, S_sp, sv0, sv1, sv2, sv3)

The (T, S)-dependent coefficients of the Roquet et al. (2015) SpV polynomial viewed as a polynomial in PRESSURE, in model variables (potential temperature T_pt degC, practical salinity S_sp PSU):

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: T_pt

Potential temperature (degC).

real(kind=wp), intent(in) :: S_sp

Practical salinity (PSU).

real(kind=wp), intent(out) :: sv0

Pressure-independent part, sv_ts0 + sv_0s0 (m^3/kg).

real(kind=wp), intent(out) :: sv1

Coefficients of p, p^2, p^3 (Pa-powers folded in).

real(kind=wp), intent(out) :: sv2

Coefficients of p, p^2, p^3 (Pa-powers folded in).

real(kind=wp), intent(out) :: sv3

Coefficients of p, p^2, p^3 (Pa-powers folded in).

private subroutine eos_destroy(this)

Arguments

Type IntentOptional Attributes Name
class(eos_t), intent(inout) :: this

private subroutine eos_init(this, grid)

Arguments

Type IntentOptional Attributes Name
class(eos_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid

private pure subroutine eos_linear_impl(h_layer, hS_layer, hT_layer, rho_layer, rho_0, beta_S, S_ref, alpha_T, T_ref, nx, ny, nz)

Linear two-tracer EOS, flat-impl form:

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hS_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hT_layer(nx,ny,nz)
real(kind=wp), intent(out) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(in) :: rho_0
real(kind=wp), intent(in) :: beta_S
real(kind=wp), intent(in) :: S_ref
real(kind=wp), intent(in) :: alpha_T
real(kind=wp), intent(in) :: T_ref
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine eos_roquet_spv_impl(h_layer, hS_layer, hT_layer, rho_layer, rho_0, p_ref, nx, ny, nz)

Roquet et al. (2015) SpV EOS evaluated at a single reference pressure p_ref (a SCALAR by design — see the horizontal-uniformity contract in eos_compute_arrays). Same outer-shim signature as eos_linear_impl/eos_wright_impl — bare 3D arrays, model (PT, SP) tracers, vanishing-layer fallback to rho_0.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hS_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hT_layer(nx,ny,nz)
real(kind=wp), intent(out) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(in) :: rho_0
real(kind=wp), intent(in) :: p_ref
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine eos_wright_impl(h_layer, hS_layer, hT_layer, rho_layer, rho_0, p_ref, nx, ny, nz)

Wright (1997) rational EOS evaluated at a single reference pressure p_ref, which is a SCALAR by design — see the horizontal-uniformity contract in eos_compute_arrays. Same outer-shim signature as eos_linear_impl — bare 3D arrays, vanishing-layer fallback to rho_0.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: h_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hS_layer(nx,ny,nz)
real(kind=wp), intent(in) :: hT_layer(nx,ny,nz)
real(kind=wp), intent(out) :: rho_layer(nx,ny,nz)
real(kind=wp), intent(in) :: rho_0
real(kind=wp), intent(in) :: p_ref
integer, intent(in) :: nx
integer, intent(in) :: ny
integer, intent(in) :: nz

private pure subroutine roquet_spv_point(T_pt, S_sp, p, sv, dsv_dt_model, dsv_ds_model)

Fused Roquet et al. (2015) SpV evaluation at a point, in MODEL variables (potential temperature T_pt degC, practical salinity S_sp PSU, pressure p Pa). Returns specific volume sv (m^3/kg) and the analytic sensitivities w.r.t. the MODEL variables (dsv_dt_model = dSV/dPT, dsv_ds_model = dSV/dSP) so the variant-agnostic consumers (which work in PT, SP) get the correct chain-ruled derivatives.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: T_pt
real(kind=wp), intent(in) :: S_sp
real(kind=wp), intent(in) :: p
real(kind=wp), intent(out) :: sv
real(kind=wp), intent(out) :: dsv_dt_model
real(kind=wp), intent(out) :: dsv_ds_model