Source code for fridom.shallowwater.model_settings

"""Model settings for the 2D shallow water model."""
from __future__ import annotations

from typing import Literal

import fridom.framework as fr
import fridom.shallowwater as sw


[docs] class ModelSettings(fr.ModelSettingsBase): """ Model settings for the 2D shallow water model. Parameters ---------- grid : Grid The grid object. """ model_name = "ShallowWater"
[docs] def __init__(self, grid: fr.grid.GridBase, **kwargs: any) -> None: super().__init__(grid) # TODO(Silvano): rename variables to meaningful names # Set standard parameters self.tendencies = sw.modules.MainTendency() self._f0 = 1 # constant coriolis parameter f0 self._beta = 0 # beta term d(f)/dy self._f_coriolis = None # the coriolis parameter field self._csqr = 1 # speed of waves squared self._csqr_field = None # c² field (for varying depth) self._Ro = 1 # Rossby number # Finally, set attributes from keyword arguments self.set_attributes(**kwargs)
[docs] def setup_settings_parameters(self) -> None: # noqa: D102 # Coriolis parameter f_coriolis = fr.ScalarField(self, name="f", long_name="Coriolis parameter", units="1/s", position=self.grid.cell_center, topo=(True, True), # TODO(Silvano): don't need topo in x ) self._f_coriolis = f_coriolis self._update_coriolis() # Speed of waves squared csqr_field = fr.ScalarField( self, name="csqr", long_name="Speed of waves squared", units="m²/s²", position=self.grid.cell_center, ) self._csqr_field = csqr_field + self._csqr # make sure that the advection term is scaled by the Rossby number self.tendencies.advection.scaling = self.Ro
[docs] def state_constructor(self) -> sw.State: # noqa: D102 return sw.State(self, is_spectral=self.grid.spectral_grid)
# ================================================================ # Properties # ================================================================ @property def parameters(self) -> dict: # noqa: D102 res = super().parameters res["coriolis parameter f0"] = f"{self.f0} s⁻¹" res["beta term"] = f"{self.beta} m⁻¹ s⁻¹)" res["Phase velocity c²"] = f"{self._csqr} m²s⁻²" res["Rossby number Ro"] = f"{self.Ro}" return res @property def f0(self) -> float: """The constant term of the Coriolis parameter (f=f0 + beta*y).""" return self._f0 @f0.setter def f0(self, value: float) -> None: self._f0 = value self._update_coriolis() @property def beta(self) -> float: r""" The beta term of the Coriolis parameter (f=f0 + beta*y). Unscaled System --------------- For the unscaled system, the beta term is given by: .. math:: \beta = \frac{df}{dy} = \frac{2\Omega \cos(\phi)}{R} where :math:`\Omega` is the angular velocity of the Earth, :math:`\phi` is the latitude, and :math:`R` is the radius of the Earth. Scaled System ------------- In the scaled system, the beta term is given by: .. math:: \beta = \frac{\beta' L}{\Omega} where :math:`\beta'` is the unscaled beta term, :math:`L` is the domain extent in the y-direction, and :math:`\Omega` is the angular velocity of the Earth. We can rewrite the domain extent in terms of the latitude extent :math:`\Delta \phi`: .. math:: L = R \Delta \phi where :math:`R` is the radius of the Earth. Thus, the beta term in the scaled system is given by: .. math:: \beta = 2 \cos(\phi) \Delta \phi """ return self._beta @beta.setter def beta(self, value: float) -> None: self._beta = value self._update_coriolis() @property def f_coriolis(self) -> fr.ScalarField: """The Coriolis parameter (f=f0 + beta*y).""" return self._f_coriolis @f_coriolis.setter def f_coriolis(self, value: fr.ScalarField) -> None: if not isinstance(value, fr.ScalarField): msg = "The coriolis parameter must be a ScalarField." raise TypeError(msg) self._f_coriolis = value def _update_coriolis(self) -> None: if self._f_coriolis is None: # nothing to be updated if the coriolis parameter is not set return _x, y = self.f_coriolis.get_mesh() self._f_coriolis.arr = self.f0 + self.beta * y @property def csqr(self) -> float: """The phase speed of the gravity waves.""" return self._csqr @csqr.setter def csqr(self, value: float) -> None: self._csqr = value if self._csqr_field is not None: self.csqr_field *= 0 self.csqr_field += value @property def csqr_field(self) -> fr.ScalarField: """The variable c²(x,y) field.""" return self._csqr_field @csqr_field.setter def csqr_field(self, value: fr.ScalarField) -> None: if not isinstance(value, fr.ScalarField): msg = "Field must be a ScalarField." raise TypeError(msg) self._csqr_field = value @property def Ro(self) -> float: """The Rossby number.""" return self._Ro @Ro.setter def Ro(self, value: float) -> None: self._Ro = value # scale the advection term self.tendencies.advection.scaling = value