xeos 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xeos/__init__.py +37 -0
- xeos/api.py +34 -0
- xeos/backends/__init__.py +17 -0
- xeos/backends/_jmd95.py +86 -0
- xeos/backends/_linear.py +52 -0
- xeos/backends/_mdjwf.py +59 -0
- xeos/backends/_mpas.py +103 -0
- xeos/backends/_roquet.py +162 -0
- xeos/backends/_roquet_idealized.py +81 -0
- xeos/backends/_roquet_spv.py +175 -0
- xeos/backends/_teos10.py +61 -0
- xeos/backends/_unesco.py +68 -0
- xeos/backends/_wright.py +87 -0
- xeos/conventions.py +104 -0
- xeos/eos.py +147 -0
- xeos/models.py +123 -0
- xeos/registry.py +64 -0
- xeos/tests/reference/.gitignore +18 -0
- xeos/tests/reference/README.md +84 -0
- xeos/tests/reference/_build_mitgcm_fortran.py +281 -0
- xeos/tests/reference/_build_mpas_eos_fortran.py +224 -0
- xeos/tests/reference/_build_roquet_spv_fortran.py +130 -0
- xeos/tests/reference/_build_seawaterpolynomials_julia.py +128 -0
- xeos/tests/reference/_build_unesco_fortran.py +147 -0
- xeos/tests/reference/_build_wright_fortran.py +157 -0
- xeos/tests/reference/environment.yml +31 -0
- xeos/tests/reference/generate_truth.py +199 -0
- xeos/tests/reference/truth.json +2243 -0
- xeos/tests/test_api.py +177 -0
- xeos/tests/test_backends.py +69 -0
- xeos/tests/test_models.py +66 -0
- xeos/version.py +3 -0
- xeos/xarray_utils.py +38 -0
- xeos-0.1.0.dist-info/METADATA +135 -0
- xeos-0.1.0.dist-info/RECORD +37 -0
- xeos-0.1.0.dist-info/WHEEL +4 -0
- xeos-0.1.0.dist-info/licenses/LICENSE +21 -0
xeos/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""xeos: lightweight, xarray-enabled wrappers for seawater equations of state.
|
|
2
|
+
|
|
3
|
+
Pick the EOS that matches your ocean-model run — by the model's own selector
|
|
4
|
+
string — and apply it to xarray/dask data with a uniform API::
|
|
5
|
+
|
|
6
|
+
import xeos
|
|
7
|
+
eos = xeos.from_model("MOM6", "WRIGHT_FULL")
|
|
8
|
+
density = eos.rho(theta, salt, pressure) # theta, salt, pressure as DataArrays
|
|
9
|
+
|
|
10
|
+
See :func:`xeos.list_eos` for the available equations of state.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from . import backends # noqa: F401 (registers all built-in EOS at import time)
|
|
14
|
+
|
|
15
|
+
from .eos import EquationOfState
|
|
16
|
+
from .models import from_model, equation_of_state, MODEL_SELECTORS
|
|
17
|
+
from .api import rho, alpha, beta, specific_volume
|
|
18
|
+
from .registry import list_eos, get_backend
|
|
19
|
+
from .conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
20
|
+
from .version import __version__
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"EquationOfState",
|
|
24
|
+
"from_model",
|
|
25
|
+
"equation_of_state",
|
|
26
|
+
"MODEL_SELECTORS",
|
|
27
|
+
"rho",
|
|
28
|
+
"alpha",
|
|
29
|
+
"beta",
|
|
30
|
+
"specific_volume",
|
|
31
|
+
"list_eos",
|
|
32
|
+
"get_backend",
|
|
33
|
+
"TemperatureKind",
|
|
34
|
+
"SalinityKind",
|
|
35
|
+
"PressureUnit",
|
|
36
|
+
"__version__",
|
|
37
|
+
]
|
xeos/api.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Functional shims: ``xeos.rho(t, s, p, eos=...)`` and friends.
|
|
2
|
+
|
|
3
|
+
Thin wrappers over :class:`~xeos.eos.EquationOfState` for one-off calls. For
|
|
4
|
+
repeated use, build an ``EquationOfState`` once (via :func:`xeos.equation_of_state`
|
|
5
|
+
or :func:`xeos.from_model`) and call its methods.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .models import equation_of_state
|
|
9
|
+
|
|
10
|
+
__all__ = ["rho", "alpha", "beta", "specific_volume"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _eos(eos, pressure_input_unit, params):
|
|
14
|
+
return equation_of_state(eos, pressure_input_unit=pressure_input_unit, **params)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def rho(t, s, p, eos, pressure_input_unit="dbar", **params):
|
|
18
|
+
"""In-situ density [kg m-3] from the named ``eos``."""
|
|
19
|
+
return _eos(eos, pressure_input_unit, params).rho(t, s, p)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def alpha(t, s, p, eos, pressure_input_unit="dbar", **params):
|
|
23
|
+
"""Thermal expansion coefficient [degC-1] from the named ``eos``."""
|
|
24
|
+
return _eos(eos, pressure_input_unit, params).alpha(t, s, p)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def beta(t, s, p, eos, pressure_input_unit="dbar", **params):
|
|
28
|
+
"""Haline contraction coefficient [(salt unit)-1] from the named ``eos``."""
|
|
29
|
+
return _eos(eos, pressure_input_unit, params).beta(t, s, p)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def specific_volume(t, s, p, eos, pressure_input_unit="dbar", **params):
|
|
33
|
+
"""Specific volume [m3 kg-1] from the named ``eos``."""
|
|
34
|
+
return _eos(eos, pressure_input_unit, params).specific_volume(t, s, p)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Importing this package registers all built-in EOS backends.
|
|
2
|
+
|
|
3
|
+
Each module calls :func:`xeos.registry.register` at import time, so importing
|
|
4
|
+
the package populates the global registry. New schemes are added by dropping a
|
|
5
|
+
module here and importing it below.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from . import _linear # noqa: F401
|
|
9
|
+
from . import _wright # noqa: F401
|
|
10
|
+
from . import _jmd95 # noqa: F401
|
|
11
|
+
from . import _unesco # noqa: F401
|
|
12
|
+
from . import _mdjwf # noqa: F401
|
|
13
|
+
from . import _roquet # noqa: F401
|
|
14
|
+
from . import _roquet_spv # noqa: F401
|
|
15
|
+
from . import _roquet_idealized # noqa: F401
|
|
16
|
+
from . import _mpas # noqa: F401
|
|
17
|
+
from . import _teos10 # noqa: F401
|
xeos/backends/_jmd95.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Jackett & McDougall (1995) equation of state (the MITgcm / ECCO standard).
|
|
2
|
+
|
|
3
|
+
Jackett, D.R. & T.J. McDougall (1995): "Minimal adjustment of hydrographic
|
|
4
|
+
profiles to achieve static stability." J. Atmos. Oceanic Technol., 12, 381-389.
|
|
5
|
+
|
|
6
|
+
Coefficients ported verbatim from the MITgcm reference implementation (also used
|
|
7
|
+
by ``fastjmd95``). This same fit is what MOM6 calls ``UNESCO`` / ``JACKETT_MCD``
|
|
8
|
+
(``MOM_EOS_UNESCO.F90``), so those MOM6 selectors resolve here too. State
|
|
9
|
+
variables: potential temperature [degC], practical
|
|
10
|
+
salinity [PSU], sea pressure [dbar]. Density derivatives are left to the
|
|
11
|
+
facade's centred finite-difference fallback (the reference analytic derivatives
|
|
12
|
+
carry a known typo); the fallback is accurate to ~1e-6 (O(h^2) truncation).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
18
|
+
from ..registry import EOSBackend, register
|
|
19
|
+
|
|
20
|
+
# Density of fresh / sea water at p = 0.
|
|
21
|
+
_CFW = np.array([999.842594, 6.793952e-02, -9.095290e-03,
|
|
22
|
+
1.001685e-04, -1.120083e-06, 6.536332e-09])
|
|
23
|
+
_CSW = np.array([8.244930e-01, -4.089900e-03, 7.643800e-05, -8.246700e-07,
|
|
24
|
+
5.387500e-09, -5.724660e-03, 1.022700e-04, -1.654600e-06,
|
|
25
|
+
4.831400e-04])
|
|
26
|
+
# Secant bulk modulus coefficients.
|
|
27
|
+
_CKFW = np.array([1.965933e04, 1.444304e02, -1.706103e00,
|
|
28
|
+
9.648704e-03, -4.190253e-05])
|
|
29
|
+
_CKSW = np.array([5.284855e01, -3.101089e-01, 6.283263e-03, -5.084188e-05,
|
|
30
|
+
3.886640e-01, 9.085835e-03, -4.619924e-04])
|
|
31
|
+
_CKP = np.array([3.186519e00, 2.212276e-02, -2.984642e-04, 1.956415e-06,
|
|
32
|
+
6.704388e-03, -1.847318e-04, 2.059331e-07, 1.480266e-04,
|
|
33
|
+
2.102898e-04, -1.202016e-05, 1.394680e-07, -2.040237e-06,
|
|
34
|
+
6.128773e-08, 6.207323e-10])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _rho_surface(s, t):
|
|
38
|
+
t2 = t * t
|
|
39
|
+
t3 = t2 * t
|
|
40
|
+
t4 = t3 * t
|
|
41
|
+
s3o2 = s * np.sqrt(s)
|
|
42
|
+
rho_fw = (_CFW[0] + _CFW[1] * t + _CFW[2] * t2 + _CFW[3] * t3
|
|
43
|
+
+ _CFW[4] * t4 + _CFW[5] * t4 * t)
|
|
44
|
+
return (rho_fw
|
|
45
|
+
+ s * (_CSW[0] + _CSW[1] * t + _CSW[2] * t2 + _CSW[3] * t3 + _CSW[4] * t4)
|
|
46
|
+
+ s3o2 * (_CSW[5] + _CSW[6] * t + _CSW[7] * t2)
|
|
47
|
+
+ _CSW[8] * s * s)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _bulk_modulus(s, t, p_bar):
|
|
51
|
+
t2 = t * t
|
|
52
|
+
t3 = t2 * t
|
|
53
|
+
t4 = t3 * t
|
|
54
|
+
s3o2 = s * np.sqrt(s)
|
|
55
|
+
p = p_bar
|
|
56
|
+
p2 = p * p
|
|
57
|
+
bm = _CKFW[0] + _CKFW[1] * t + _CKFW[2] * t2 + _CKFW[3] * t3 + _CKFW[4] * t4
|
|
58
|
+
bm = (bm
|
|
59
|
+
+ s * (_CKSW[0] + _CKSW[1] * t + _CKSW[2] * t2 + _CKSW[3] * t3)
|
|
60
|
+
+ s3o2 * (_CKSW[4] + _CKSW[5] * t + _CKSW[6] * t2))
|
|
61
|
+
bm = (bm
|
|
62
|
+
+ p * (_CKP[0] + _CKP[1] * t + _CKP[2] * t2 + _CKP[3] * t3)
|
|
63
|
+
+ p * s * (_CKP[4] + _CKP[5] * t + _CKP[6] * t2)
|
|
64
|
+
+ p * s3o2 * _CKP[7]
|
|
65
|
+
+ p2 * (_CKP[8] + _CKP[9] * t + _CKP[10] * t2)
|
|
66
|
+
+ p2 * s * (_CKP[11] + _CKP[12] * t + _CKP[13] * t2))
|
|
67
|
+
return bm
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def density(t, s, p_dbar):
|
|
71
|
+
"""In-situ density [kg m-3] from potential temp, practical salinity, dbar."""
|
|
72
|
+
p_bar = 0.1 * p_dbar
|
|
73
|
+
rho_s = _rho_surface(s, t)
|
|
74
|
+
bulk = _bulk_modulus(s, t, p_bar)
|
|
75
|
+
return rho_s / (1.0 - p_bar / bulk)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
register(EOSBackend(
|
|
79
|
+
id="jmd95",
|
|
80
|
+
density=density,
|
|
81
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
82
|
+
salinity=SalinityKind.PRACTICAL,
|
|
83
|
+
pressure_unit=PressureUnit.DBAR,
|
|
84
|
+
reference="Jackett & McDougall (1995), J. Atmos. Oceanic Technol., 12, 381-389.",
|
|
85
|
+
description="UNESCO/EOS-80 refit; MITgcm JMD95Z/JMD95P, ECCO standard.",
|
|
86
|
+
))
|
xeos/backends/_linear.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Linear equation of state: rho = rho0 + drho_dT * T + drho_dS * S.
|
|
2
|
+
|
|
3
|
+
Used by all three target models (MOM6 ``LINEAR``, MITgcm ``LINEAR``,
|
|
4
|
+
Oceananigans ``LinearEquationOfState``). The coefficients are configurable;
|
|
5
|
+
the registered default uses MITgcm-style values.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
9
|
+
from ..registry import EOSBackend, register
|
|
10
|
+
|
|
11
|
+
# MITgcm-style defaults (rhoNil, tAlpha, sBeta).
|
|
12
|
+
DEFAULT_RHO0 = 999.8
|
|
13
|
+
DEFAULT_TALPHA = 2.0e-4 # thermal expansion [degC-1]
|
|
14
|
+
DEFAULT_SBETA = 7.4e-4 # haline contraction [PSU-1]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def make_linear(rho0=DEFAULT_RHO0, talpha=DEFAULT_TALPHA, sbeta=DEFAULT_SBETA,
|
|
18
|
+
eos_id="linear"):
|
|
19
|
+
"""Build a linear :class:`EOSBackend` from expansion/contraction coefficients.
|
|
20
|
+
|
|
21
|
+
``rho = rho0 * (1 - talpha * T + sbeta * S)``, so ``drho/dT = -rho0*talpha``
|
|
22
|
+
and ``drho/dS = rho0*sbeta`` are exact constants.
|
|
23
|
+
"""
|
|
24
|
+
drdt = -rho0 * talpha
|
|
25
|
+
drds = rho0 * sbeta
|
|
26
|
+
|
|
27
|
+
def density(t, s, p):
|
|
28
|
+
return rho0 + drdt * t + drds * s
|
|
29
|
+
|
|
30
|
+
def drho_dt(t, s, p):
|
|
31
|
+
return drdt + 0.0 * t
|
|
32
|
+
|
|
33
|
+
def drho_ds(t, s, p):
|
|
34
|
+
return drds + 0.0 * s
|
|
35
|
+
|
|
36
|
+
return EOSBackend(
|
|
37
|
+
id=eos_id,
|
|
38
|
+
density=density,
|
|
39
|
+
drho_dt=drho_dt,
|
|
40
|
+
drho_ds=drho_ds,
|
|
41
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
42
|
+
salinity=SalinityKind.PRACTICAL,
|
|
43
|
+
pressure_unit=PressureUnit.DBAR,
|
|
44
|
+
reference="Linear EOS (configurable thermal/haline coefficients).",
|
|
45
|
+
description=(
|
|
46
|
+
f"rho0={rho0}, talpha={talpha}, sbeta={sbeta}; "
|
|
47
|
+
"pressure-independent (incompressible)."
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
register(make_linear())
|
xeos/backends/_mdjwf.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""McDougall, Jackett, Wright & Feistel (2003) equation of state.
|
|
2
|
+
|
|
3
|
+
McDougall, T.J., D.R. Jackett, D.G. Wright & R. Feistel (2003): "Accurate and
|
|
4
|
+
computationally efficient algorithms for potential temperature and density of
|
|
5
|
+
seawater." J. Atmos. Oceanic Technol., 20, 730-741.
|
|
6
|
+
|
|
7
|
+
A rational-function (numerator / denominator) fit; MITgcm ``eosType='MDJWF'``.
|
|
8
|
+
Coefficients ported from the MITgcm reference (``MITgcmutils.density.mdjwf``).
|
|
9
|
+
|
|
10
|
+
State variables: potential temperature [degC], practical salinity [PSU], sea
|
|
11
|
+
pressure [dbar].
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
17
|
+
from ..registry import EOSBackend, register
|
|
18
|
+
|
|
19
|
+
_NUM = np.array([7.35212840e+00, -5.45928211e-02, 3.98476704e-04, 2.96938239e+00,
|
|
20
|
+
-7.23268813e-03, 2.12382341e-03, 1.04004591e-02, 1.03970529e-07,
|
|
21
|
+
5.18761880e-06, -3.24041825e-08, -1.23869360e-11, 9.99843699e+02])
|
|
22
|
+
_DEN = np.array([7.28606739e-03, -4.60835542e-05, 3.68390573e-07, 1.80809186e-10,
|
|
23
|
+
2.14691708e-03, -9.27062484e-06, -1.78343643e-10, 4.76534122e-06,
|
|
24
|
+
1.63410736e-09, 5.30848875e-06, -3.03175128e-16, -1.27934137e-17,
|
|
25
|
+
1.00000000e+00])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def density(t, s, p_dbar):
|
|
29
|
+
"""In-situ density [kg m-3] from potential temp, practical salinity, dbar."""
|
|
30
|
+
t1 = t
|
|
31
|
+
t2 = t1 * t1
|
|
32
|
+
s1 = s
|
|
33
|
+
p1 = p_dbar
|
|
34
|
+
sp5 = np.sqrt(s1)
|
|
35
|
+
p1t1 = p1 * t1
|
|
36
|
+
|
|
37
|
+
num = (_NUM[11]
|
|
38
|
+
+ t1 * (_NUM[0] + t1 * (_NUM[1] + _NUM[2] * t1))
|
|
39
|
+
+ s1 * (_NUM[3] + _NUM[4] * t1 + _NUM[5] * s1)
|
|
40
|
+
+ p1 * (_NUM[6] + _NUM[7] * t2 + _NUM[8] * s1
|
|
41
|
+
+ p1 * (_NUM[9] + _NUM[10] * t2)))
|
|
42
|
+
den = (_DEN[12]
|
|
43
|
+
+ t1 * (_DEN[0] + t1 * (_DEN[1] + t1 * (_DEN[2] + t1 * _DEN[3])))
|
|
44
|
+
+ s1 * (_DEN[4] + t1 * (_DEN[5] + _DEN[6] * t2)
|
|
45
|
+
+ sp5 * (_DEN[7] + _DEN[8] * t2))
|
|
46
|
+
+ p1 * (_DEN[9] + p1t1 * (_DEN[10] * t2 + _DEN[11] * p1)))
|
|
47
|
+
return num / den
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
register(EOSBackend(
|
|
51
|
+
id="mdjwf",
|
|
52
|
+
density=density,
|
|
53
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
54
|
+
salinity=SalinityKind.PRACTICAL,
|
|
55
|
+
pressure_unit=PressureUnit.DBAR,
|
|
56
|
+
reference="McDougall, Jackett, Wright & Feistel (2003), "
|
|
57
|
+
"J. Atmos. Oceanic Technol., 20, 730-741.",
|
|
58
|
+
description="Rational-function EOS; MITgcm MDJWF.",
|
|
59
|
+
))
|
xeos/backends/_mpas.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""MPAS-Ocean (MPAS-O) equations of state.
|
|
2
|
+
|
|
3
|
+
MPAS-O (the ocean component of E3SM) selects its EOS with the namelist option
|
|
4
|
+
``config_eos_type``, which accepts exactly three values -- ``linear``, ``jm`` and
|
|
5
|
+
``wright`` -- implemented in
|
|
6
|
+
``components/mpas-ocean/src/shared/mpas_ocn_equation_of_state_{linear,jm,wright}.F``.
|
|
7
|
+
The ``jm`` (Jackett & McDougall 1995) and ``wright`` (Wright 1997) kernels are
|
|
8
|
+
byte-for-byte identical -- same coefficients and same algebraic form -- to xeos's
|
|
9
|
+
existing :mod:`._jmd95` and :mod:`._wright` (reduced-range) kernels, so this module
|
|
10
|
+
*reuses those kernel functions* rather than re-vendoring the coefficients; the
|
|
11
|
+
MPAS-O truth fixtures (``mpas-jm`` / ``mpas-wright``) validate that reuse against
|
|
12
|
+
MPAS-O's own compiled Fortran. ``mpas-linear`` is the linear form with MPAS-O's
|
|
13
|
+
namelist defaults baked in.
|
|
14
|
+
|
|
15
|
+
State variables (all three): potential temperature [degC], practical salinity
|
|
16
|
+
[PSU]; ``jm`` takes sea pressure [dbar], ``wright`` pressure [Pa], ``linear`` is
|
|
17
|
+
pressure-independent.
|
|
18
|
+
|
|
19
|
+
Two MPAS-O specifics are *documented here but intentionally not exposed* (the
|
|
20
|
+
facade takes pressure as an input, and the validation grid is in-range):
|
|
21
|
+
|
|
22
|
+
* **T/S clamping** before the polynomial -- ``jm`` clamps to T in [-2, 40] degC and
|
|
23
|
+
S in [0, 42] PSU; ``wright`` to T in [-3, 30] degC and S in [28, 38] PSU.
|
|
24
|
+
* **depth -> pressure** -- ``jm`` derives a per-layer reference pressure in bars
|
|
25
|
+
from depth via a POP/Levitus polynomial
|
|
26
|
+
``P_bar(z) = 0.059808*(exp(-0.025*z) - 1) + 0.100766*z + 2.28405e-7*z**2``;
|
|
27
|
+
``wright`` uses the Boussinesq linearisation ``p = -rho0*g*z``.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
31
|
+
from ..registry import EOSBackend, register
|
|
32
|
+
from ._jmd95 import density as _jmd95_density
|
|
33
|
+
from ._wright import REDUCED as _WRIGHT_REDUCED, _make as _wright_make
|
|
34
|
+
|
|
35
|
+
# --- mpas-linear: rho = RhoRef - Alpha*(T - Tref) + Beta*(S - Sref) -----------
|
|
36
|
+
# MPAS-O Registry defaults (config_eos_linear_*): a *decrease* with temperature
|
|
37
|
+
# (-Alpha) and *increase* with salinity (+Beta); Alpha/Beta are dimensional
|
|
38
|
+
# (kg m-3 per degC / per PSU), not normalised by density.
|
|
39
|
+
_RHO_REF = 1000.0 # config_eos_linear_densityref [kg m-3]
|
|
40
|
+
_ALPHA = 0.2 # config_eos_linear_alpha [kg m-3 degC-1]
|
|
41
|
+
_BETA = 0.8 # config_eos_linear_beta [kg m-3 PSU-1]
|
|
42
|
+
_T_REF = 5.0 # config_eos_linear_Tref [degC]
|
|
43
|
+
_S_REF = 35.0 # config_eos_linear_Sref [PSU]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _mpas_linear_density(t, s, p):
|
|
47
|
+
return _RHO_REF - _ALPHA * (t - _T_REF) + _BETA * (s - _S_REF)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _mpas_linear_drho_dt(t, s, p):
|
|
51
|
+
return -_ALPHA + 0.0 * t
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _mpas_linear_drho_ds(t, s, p):
|
|
55
|
+
return _BETA + 0.0 * s
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# --- mpas-wright: reuse the reduced-range Wright (1997) kernel -----------------
|
|
59
|
+
_wright_density, _wright_drho_dt, _wright_drho_ds = _wright_make(_WRIGHT_REDUCED)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
register(EOSBackend(
|
|
63
|
+
id="mpas-linear",
|
|
64
|
+
density=_mpas_linear_density,
|
|
65
|
+
drho_dt=_mpas_linear_drho_dt,
|
|
66
|
+
drho_ds=_mpas_linear_drho_ds,
|
|
67
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
68
|
+
salinity=SalinityKind.PRACTICAL,
|
|
69
|
+
pressure_unit=PressureUnit.DBAR,
|
|
70
|
+
reference="MPAS-O config_eos_type='linear' (mpas_ocn_equation_of_state_linear.F).",
|
|
71
|
+
description=(
|
|
72
|
+
f"rho = {_RHO_REF} - {_ALPHA}*(T-{_T_REF}) + {_BETA}*(S-{_S_REF}); "
|
|
73
|
+
"MPAS-O namelist defaults; pressure-independent (incompressible)."
|
|
74
|
+
),
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
register(EOSBackend(
|
|
78
|
+
id="mpas-jm",
|
|
79
|
+
density=_jmd95_density,
|
|
80
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
81
|
+
salinity=SalinityKind.PRACTICAL,
|
|
82
|
+
pressure_unit=PressureUnit.DBAR,
|
|
83
|
+
reference=("MPAS-O config_eos_type='jm' (mpas_ocn_equation_of_state_jm.F); "
|
|
84
|
+
"Jackett & McDougall (1995), J. Atmos. Oceanic Technol., 12, 381-389."),
|
|
85
|
+
description=("Jackett-McDougall (1995) UNESCO/EOS-80 refit; identical kernel to "
|
|
86
|
+
"xeos 'jmd95'. MPAS-O clamps T,S and derives pressure from depth "
|
|
87
|
+
"(documented in this module; not applied here)."),
|
|
88
|
+
))
|
|
89
|
+
|
|
90
|
+
register(EOSBackend(
|
|
91
|
+
id="mpas-wright",
|
|
92
|
+
density=_wright_density,
|
|
93
|
+
drho_dt=_wright_drho_dt,
|
|
94
|
+
drho_ds=_wright_drho_ds,
|
|
95
|
+
temperature=TemperatureKind.POTENTIAL,
|
|
96
|
+
salinity=SalinityKind.PRACTICAL,
|
|
97
|
+
pressure_unit=PressureUnit.PASCAL,
|
|
98
|
+
reference=("MPAS-O config_eos_type='wright' (mpas_ocn_equation_of_state_wright.F); "
|
|
99
|
+
"Wright (1997), J. Atmos. Oceanic Technol., 14, 735-740."),
|
|
100
|
+
description=("Wright (1997) reduced-range coefficients (Table 1, last column); "
|
|
101
|
+
"identical kernel to xeos 'wright97-reduced'. MPAS-O clamps T,S and "
|
|
102
|
+
"uses Boussinesq pressure p=-rho0*g*z (documented; not applied here)."),
|
|
103
|
+
))
|
xeos/backends/_roquet.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Roquet et al. (2015) 55-term polynomial approximation to TEOS-10.
|
|
2
|
+
|
|
3
|
+
Roquet, F., Madec, G., McDougall, T.J., Barker, P.M. (2015): "Accurate
|
|
4
|
+
polynomial expressions for the density and specific volume of seawater using the
|
|
5
|
+
TEOS-10 standard." Ocean Modelling, 90, 29-43.
|
|
6
|
+
|
|
7
|
+
This is the Boussinesq in-situ density form (``polyTEOS10_bsq``) used by
|
|
8
|
+
Oceananigans / SeawaterPolynomials.jl ``TEOS10EquationOfState``. Coefficients
|
|
9
|
+
ported verbatim from Fabien Roquet's reference ``polyTEOS10.py``.
|
|
10
|
+
|
|
11
|
+
State variables: conservative temperature [degC], absolute salinity [g/kg], and
|
|
12
|
+
sea pressure [dbar] (numerically ~ geopotential depth in m).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
16
|
+
from ..registry import EOSBackend, register
|
|
17
|
+
|
|
18
|
+
# Reduced-variable normalisation.
|
|
19
|
+
_SAu = 40.0 * 35.16504 / 35.0
|
|
20
|
+
_CTu = 40.0
|
|
21
|
+
_Zu = 1.0e4
|
|
22
|
+
_dS = 32.0
|
|
23
|
+
|
|
24
|
+
# Vertical reference profile of density r0(pp).
|
|
25
|
+
_R0 = (4.6494977072e+01, -5.2099962525e+00, 2.2601900708e-01,
|
|
26
|
+
6.4326772569e-02, 1.5616995503e-02, -1.7243708991e-03)
|
|
27
|
+
|
|
28
|
+
# Density-anomaly coefficients R_{ijk} (i: salinity, j: temperature, k: pressure).
|
|
29
|
+
R000 = 8.0189615746e+02; R100 = 8.6672408165e+02; R200 = -1.7864682637e+03
|
|
30
|
+
R300 = 2.0375295546e+03; R400 = -1.2849161071e+03; R500 = 4.3227585684e+02
|
|
31
|
+
R600 = -6.0579916612e+01; R010 = 2.6010145068e+01; R110 = -6.5281885265e+01
|
|
32
|
+
R210 = 8.1770425108e+01; R310 = -5.6888046321e+01; R410 = 1.7681814114e+01
|
|
33
|
+
R510 = -1.9193502195e+00; R020 = -3.7074170417e+01; R120 = 6.1548258127e+01
|
|
34
|
+
R220 = -6.0362551501e+01; R320 = 2.9130021253e+01; R420 = -5.4723692739e+00
|
|
35
|
+
R030 = 2.1661789529e+01; R130 = -3.3449108469e+01; R230 = 1.9717078466e+01
|
|
36
|
+
R330 = -3.1742946532e+00; R040 = -8.3627885467e+00; R140 = 1.1311538584e+01
|
|
37
|
+
R240 = -5.3563304045e+00; R050 = 5.4048723791e-01; R150 = 4.8169980163e-01
|
|
38
|
+
R060 = -1.9083568888e-01; R001 = 1.9681925209e+01; R101 = -4.2549998214e+01
|
|
39
|
+
R201 = 5.0774768218e+01; R301 = -3.0938076334e+01; R401 = 6.6051753097e+00
|
|
40
|
+
R011 = -1.3336301113e+01; R111 = -4.4870114575e+00; R211 = 5.0042598061e+00
|
|
41
|
+
R311 = -6.5399043664e-01; R021 = 6.7080479603e+00; R121 = 3.5063081279e+00
|
|
42
|
+
R221 = -1.8795372996e+00; R031 = -2.4649669534e+00; R131 = -5.5077101279e-01
|
|
43
|
+
R041 = 5.5927935970e-01; R002 = 2.0660924175e+00; R102 = -4.9527603989e+00
|
|
44
|
+
R202 = 2.5019633244e+00; R012 = 2.0564311499e+00; R112 = -2.1311365518e-01
|
|
45
|
+
R022 = -1.2419983026e+00; R003 = -2.3342758797e-02; R103 = -1.8507636718e-02
|
|
46
|
+
R013 = 3.7969820455e-01
|
|
47
|
+
|
|
48
|
+
# Thermal-expansion coefficients (a = -dr/dCT).
|
|
49
|
+
ALP000 = -6.5025362670e-01; ALP100 = 1.6320471316e+00; ALP200 = -2.0442606277e+00
|
|
50
|
+
ALP300 = 1.4222011580e+00; ALP400 = -4.4204535284e-01; ALP500 = 4.7983755487e-02
|
|
51
|
+
ALP010 = 1.8537085209e+00; ALP110 = -3.0774129064e+00; ALP210 = 3.0181275751e+00
|
|
52
|
+
ALP310 = -1.4565010626e+00; ALP410 = 2.7361846370e-01; ALP020 = -1.6246342147e+00
|
|
53
|
+
ALP120 = 2.5086831352e+00; ALP220 = -1.4787808849e+00; ALP320 = 2.3807209899e-01
|
|
54
|
+
ALP030 = 8.3627885467e-01; ALP130 = -1.1311538584e+00; ALP230 = 5.3563304045e-01
|
|
55
|
+
ALP040 = -6.7560904739e-02; ALP140 = -6.0212475204e-02; ALP050 = 2.8625353333e-02
|
|
56
|
+
ALP001 = 3.3340752782e-01; ALP101 = 1.1217528644e-01; ALP201 = -1.2510649515e-01
|
|
57
|
+
ALP301 = 1.6349760916e-02; ALP011 = -3.3540239802e-01; ALP111 = -1.7531540640e-01
|
|
58
|
+
ALP211 = 9.3976864981e-02; ALP021 = 1.8487252150e-01; ALP121 = 4.1307825959e-02
|
|
59
|
+
ALP031 = -5.5927935970e-02; ALP002 = -5.1410778748e-02; ALP102 = 5.3278413794e-03
|
|
60
|
+
ALP012 = 6.2099915132e-02; ALP003 = -9.4924551138e-03
|
|
61
|
+
|
|
62
|
+
# Haline-contraction coefficients (b = dr/dSA, before /ss).
|
|
63
|
+
BET000 = 1.0783203594e+01; BET100 = -4.4452095908e+01; BET200 = 7.6048755820e+01
|
|
64
|
+
BET300 = -6.3944280668e+01; BET400 = 2.6890441098e+01; BET500 = -4.5221697773e+00
|
|
65
|
+
BET010 = -8.1219372432e-01; BET110 = 2.0346663041e+00; BET210 = -2.1232895170e+00
|
|
66
|
+
BET310 = 8.7994140485e-01; BET410 = -1.1939638360e-01; BET020 = 7.6574242289e-01
|
|
67
|
+
BET120 = -1.5019813020e+00; BET220 = 1.0872489522e+00; BET320 = -2.7233429080e-01
|
|
68
|
+
BET030 = -4.1615152308e-01; BET130 = 4.9061350869e-01; BET230 = -1.1847737788e-01
|
|
69
|
+
BET040 = 1.4073062708e-01; BET140 = -1.3327978879e-01; BET050 = 5.9929880134e-03
|
|
70
|
+
BET001 = -5.2937873009e-01; BET101 = 1.2634116779e+00; BET201 = -1.1547328025e+00
|
|
71
|
+
BET301 = 3.2870876279e-01; BET011 = -5.5824407214e-02; BET111 = 1.2451933313e-01
|
|
72
|
+
BET211 = -2.4409539932e-02; BET021 = 4.3623149752e-02; BET121 = -4.6767901790e-02
|
|
73
|
+
BET031 = -6.8523260060e-03; BET002 = -6.1618945251e-02; BET102 = 6.2255521644e-02
|
|
74
|
+
BET012 = -2.6514181169e-03; BET003 = -2.3025968587e-04
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _reduced(ct, sa, p_dbar):
|
|
78
|
+
import numpy as np
|
|
79
|
+
ss = np.sqrt((sa + _dS) / _SAu)
|
|
80
|
+
tt = ct / _CTu
|
|
81
|
+
pp = p_dbar / _Zu
|
|
82
|
+
return ss, tt, pp
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _r0(pp):
|
|
86
|
+
R00, R01, R02, R03, R04, R05 = _R0
|
|
87
|
+
return (((((R05 * pp + R04) * pp + R03) * pp + R02) * pp + R01) * pp + R00) * pp
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _anomaly(ss, tt, pp):
|
|
91
|
+
rz3 = R013 * tt + R103 * ss + R003
|
|
92
|
+
rz2 = (R022 * tt + R112 * ss + R012) * tt + (R202 * ss + R102) * ss + R002
|
|
93
|
+
rz1 = ((((R041 * tt + R131 * ss + R031) * tt
|
|
94
|
+
+ (R221 * ss + R121) * ss + R021) * tt
|
|
95
|
+
+ ((R311 * ss + R211) * ss + R111) * ss + R011) * tt
|
|
96
|
+
+ (((R401 * ss + R301) * ss + R201) * ss + R101) * ss + R001)
|
|
97
|
+
rz0 = (((((R060 * tt + R150 * ss + R050) * tt
|
|
98
|
+
+ (R240 * ss + R140) * ss + R040) * tt
|
|
99
|
+
+ ((R330 * ss + R230) * ss + R130) * ss + R030) * tt
|
|
100
|
+
+ (((R420 * ss + R320) * ss + R220) * ss + R120) * ss + R020) * tt
|
|
101
|
+
+ ((((R510 * ss + R410) * ss + R310) * ss + R210) * ss + R110) * ss + R010) * tt \
|
|
102
|
+
+ (((((R600 * ss + R500) * ss + R400) * ss + R300) * ss + R200) * ss + R100) * ss + R000
|
|
103
|
+
return ((rz3 * pp + rz2) * pp + rz1) * pp + rz0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def density(ct, sa, p_dbar):
|
|
107
|
+
"""In-situ density [kg m-3] (conservative temp, absolute salinity, dbar)."""
|
|
108
|
+
ss, tt, pp = _reduced(ct, sa, p_dbar)
|
|
109
|
+
return _r0(pp) + _anomaly(ss, tt, pp)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _thermal_expansion_a(ct, sa, p_dbar):
|
|
113
|
+
"""Boussinesq thermal expansion a = -dr/dCT [kg m-3 K-1]."""
|
|
114
|
+
ss, tt, pp = _reduced(ct, sa, p_dbar)
|
|
115
|
+
return ((ALP003 * pp
|
|
116
|
+
+ ALP012 * tt + ALP102 * ss + ALP002) * pp
|
|
117
|
+
+ ((ALP031 * tt + ALP121 * ss + ALP021) * tt
|
|
118
|
+
+ (ALP211 * ss + ALP111) * ss + ALP011) * tt
|
|
119
|
+
+ ((ALP301 * ss + ALP201) * ss + ALP101) * ss + ALP001) * pp \
|
|
120
|
+
+ ((((ALP050 * tt + ALP140 * ss + ALP040) * tt
|
|
121
|
+
+ (ALP230 * ss + ALP130) * ss + ALP030) * tt
|
|
122
|
+
+ ((ALP320 * ss + ALP220) * ss + ALP120) * ss + ALP020) * tt
|
|
123
|
+
+ (((ALP410 * ss + ALP310) * ss + ALP210) * ss + ALP110) * ss + ALP010) * tt \
|
|
124
|
+
+ ((((ALP500 * ss + ALP400) * ss + ALP300) * ss + ALP200) * ss + ALP100) * ss + ALP000
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _haline_contraction_b(ct, sa, p_dbar):
|
|
128
|
+
"""Boussinesq haline contraction b = dr/dSA [kg m-3 (g/kg)-1]."""
|
|
129
|
+
ss, tt, pp = _reduced(ct, sa, p_dbar)
|
|
130
|
+
b = ((BET003 * pp
|
|
131
|
+
+ BET012 * tt + BET102 * ss + BET002) * pp
|
|
132
|
+
+ ((BET031 * tt + BET121 * ss + BET021) * tt
|
|
133
|
+
+ (BET211 * ss + BET111) * ss + BET011) * tt
|
|
134
|
+
+ ((BET301 * ss + BET201) * ss + BET101) * ss + BET001) * pp \
|
|
135
|
+
+ ((((BET050 * tt + BET140 * ss + BET040) * tt
|
|
136
|
+
+ (BET230 * ss + BET130) * ss + BET030) * tt
|
|
137
|
+
+ ((BET320 * ss + BET220) * ss + BET120) * ss + BET020) * tt
|
|
138
|
+
+ (((BET410 * ss + BET310) * ss + BET210) * ss + BET110) * ss + BET010) * tt \
|
|
139
|
+
+ ((((BET500 * ss + BET400) * ss + BET300) * ss + BET200) * ss + BET100) * ss + BET000
|
|
140
|
+
return b / ss
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def drho_dt(ct, sa, p_dbar):
|
|
144
|
+
return -_thermal_expansion_a(ct, sa, p_dbar)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def drho_ds(ct, sa, p_dbar):
|
|
148
|
+
return _haline_contraction_b(ct, sa, p_dbar)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
register(EOSBackend(
|
|
152
|
+
id="teos10-poly55",
|
|
153
|
+
density=density,
|
|
154
|
+
drho_dt=drho_dt,
|
|
155
|
+
drho_ds=drho_ds,
|
|
156
|
+
temperature=TemperatureKind.CONSERVATIVE,
|
|
157
|
+
salinity=SalinityKind.ABSOLUTE,
|
|
158
|
+
pressure_unit=PressureUnit.DBAR,
|
|
159
|
+
reference="Roquet et al. (2015), Ocean Modelling, 90, 29-43.",
|
|
160
|
+
description="55-term Boussinesq polynomial approx to TEOS-10 "
|
|
161
|
+
"(Oceananigans / SeawaterPolynomials.jl; MOM6 ROQUET_RHO family).",
|
|
162
|
+
))
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Idealized second-order Roquet equations of state (Oceananigans).
|
|
2
|
+
|
|
3
|
+
Roquet, F., G. Madec, L. Brodeau, J. Nycander (2015): "Defining a Simplified
|
|
4
|
+
yet 'Realistic' Equation of State for Seawater." J. Phys. Oceanogr., 45, Table 3.
|
|
5
|
+
(doi:10.1175/JPO-D-15-0080.1)
|
|
6
|
+
|
|
7
|
+
These are the ``RoquetSeawaterPolynomial(:Linear | :Cabbeling | ...)`` options in
|
|
8
|
+
SeawaterPolynomials.jl / Oceananigans. Each is a second-order polynomial of the
|
|
9
|
+
density anomaly in conservative temperature, absolute salinity, and geopotential
|
|
10
|
+
height Z::
|
|
11
|
+
|
|
12
|
+
rho = rho_ref + R100*S + R010*T + R020*T^2 - R011*T*Z + R200*S^2
|
|
13
|
+
- R101*S*Z + R110*S*T
|
|
14
|
+
|
|
15
|
+
Coefficients ported verbatim from SeawaterPolynomials.jl. Geopotential height is
|
|
16
|
+
taken as ``Z = -p`` (sea pressure in dbar ~ depth in m, positive down), matching
|
|
17
|
+
the convention of the 55-term ``teos10-poly55`` backend. There is no standalone
|
|
18
|
+
Python reference for these, so they are validated structurally (exact analytic
|
|
19
|
+
derivatives checked against finite differences; literal check values).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from ..conventions import TemperatureKind, SalinityKind, PressureUnit
|
|
23
|
+
from ..registry import EOSBackend, register
|
|
24
|
+
|
|
25
|
+
DEFAULT_REFERENCE_DENSITY = 1024.6 # kg m-3 (Oceananigans / Roquet default)
|
|
26
|
+
|
|
27
|
+
# (R100, R010, R020, R011, R200, R101, R110) for each coefficient set.
|
|
28
|
+
COEFFICIENTS = {
|
|
29
|
+
"roquet-linear": dict(R100=7.718e-1, R010=-1.775e-1),
|
|
30
|
+
"roquet-cabbeling": dict(R100=7.718e-1, R010=-0.844e-1, R020=-4.561e-3),
|
|
31
|
+
"roquet-cabbeling-thermobaricity": dict(
|
|
32
|
+
R100=7.718e-1, R010=-0.651e-1, R020=-5.027e-3, R011=-2.5681e-5),
|
|
33
|
+
"roquet-freezing": dict(
|
|
34
|
+
R100=7.718e-1, R010=-0.491e-1, R020=-5.027e-3, R011=-2.5681e-5),
|
|
35
|
+
"roquet-second-order": dict(
|
|
36
|
+
R100=8.078e-1, R010=0.182e-1, R020=-4.937e-3, R011=-2.4677e-5,
|
|
37
|
+
R200=-1.115e-4, R101=-8.241e-6, R110=-2.446e-3),
|
|
38
|
+
# Simplest-realistic (Roquet 2015 eq. 17): Cb=0.011, Th=2.5e-5, b0=0.77, T0=-4.5.
|
|
39
|
+
# The constant R000 = -Cb*T0^2/2 (~-0.11 kg m-3) is omitted (no dynamical effect),
|
|
40
|
+
# matching SeawaterPolynomials.jl; this offsets the absolute density by that constant.
|
|
41
|
+
"roquet-simplest-realistic": dict(
|
|
42
|
+
R100=0.77, R010=0.011 * -4.5, R020=-0.011 / 2, R011=-2.5e-5),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def make_roquet_idealized(eos_id, reference_density=DEFAULT_REFERENCE_DENSITY, **coeffs):
|
|
47
|
+
c = {k: 0.0 for k in ("R100", "R010", "R020", "R011", "R200", "R101", "R110")}
|
|
48
|
+
c.update(coeffs)
|
|
49
|
+
R100, R010, R020 = c["R100"], c["R010"], c["R020"]
|
|
50
|
+
R011, R200, R101, R110 = c["R011"], c["R200"], c["R101"], c["R110"]
|
|
51
|
+
|
|
52
|
+
def density(t, s, p):
|
|
53
|
+
z = -p # geopotential height [m] ~ -(sea pressure in dbar)
|
|
54
|
+
anomaly = (R100 * s + R010 * t + R020 * t * t - R011 * t * z
|
|
55
|
+
+ R200 * s * s - R101 * s * z + R110 * s * t)
|
|
56
|
+
return reference_density + anomaly
|
|
57
|
+
|
|
58
|
+
def drho_dt(t, s, p):
|
|
59
|
+
z = -p
|
|
60
|
+
return R010 + 2.0 * R020 * t - R011 * z + R110 * s
|
|
61
|
+
|
|
62
|
+
def drho_ds(t, s, p):
|
|
63
|
+
z = -p
|
|
64
|
+
return R100 + 2.0 * R200 * s - R101 * z + R110 * t
|
|
65
|
+
|
|
66
|
+
return EOSBackend(
|
|
67
|
+
id=eos_id,
|
|
68
|
+
density=density,
|
|
69
|
+
drho_dt=drho_dt,
|
|
70
|
+
drho_ds=drho_ds,
|
|
71
|
+
temperature=TemperatureKind.CONSERVATIVE,
|
|
72
|
+
salinity=SalinityKind.ABSOLUTE,
|
|
73
|
+
pressure_unit=PressureUnit.DBAR,
|
|
74
|
+
reference="Roquet et al. (2015), J. Phys. Oceanogr., 45, Table 3.",
|
|
75
|
+
description=f"Idealized Roquet EOS '{eos_id.split('-', 1)[1]}' "
|
|
76
|
+
f"(Oceananigans; rho_ref={reference_density}).",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
for _eos_id, _coeffs in COEFFICIENTS.items():
|
|
81
|
+
register(make_roquet_idealized(_eos_id, **_coeffs))
|