Source code for fridom.framework.modules.advection.advection_base
import fridom.framework as fr
from abc import abstractmethod
from functools import partial
[docs]
@partial(fr.utils.jaxify, dynamic=("_scaling", "_background"))
class AdvectionBase(fr.modules.Module):
r"""
Base class for advection schemes.
Description
-----------
This class implements the base interface for 1D, 2D, and 3D advection schemes.
For that, it is assumed that the velocity field is stored in the state vector
as the components `u`, `v`, and `w`. Child classes must implement the `advection`
method to calculate the advection term:
.. math::
\mathcal{A}(\boldsymbol{v}, q) = -\boldsymbol{v} \cdot \nabla q
where :math:`q` is the quantity to be advected and :math:`\boldsymbol{v}` is
the velocity field, which is the sum of the velocity field in the state vector
and the background velocity field, stored in the `background` attribute.
This update routine of this module adds the advection term multiplied by the
nonlinear scaling factor to the tendency term of all fields that are not flagged
with `NO_ADV`:
.. math::
\partial_t q \leftarrow \partial_t q
+ \rho \mathcal{A}(\boldsymbol{v}, q)
where :math:`\rho` is the nonlinear scaling factor.
"""
name = "Advection Base"
[docs]
def __init__(self) -> None:
super().__init__()
self._scaling = 1
self._background = None
self._disable_nonlinear = False
return
[docs]
@abstractmethod
def advection(self,
velocity: 'tuple[fr.FieldVariable]',
quantity: 'fr.FieldVariable') -> 'fr.FieldVariable':
"""
Advect a quantity using the given velocity field.
"""
[docs]
@fr.utils.jaxjit
def advect_state(self, z: fr.StateBase, dz: fr.StateBase) -> fr.StateBase:
if self.background is None and self.disable_nonlinear:
return dz
if self.disable_nonlinear:
zf = self.background
else:
# Compute the full state vector (including the background)
if self.background is not None:
zf = z + self.background
else:
zf = z
# Compute the velocity field
if self.grid.n_dims == 1:
velocity = (zf.u,)
elif self.grid.n_dims == 2:
velocity = (zf.u, zf.v)
elif self.grid.n_dims == 3:
velocity = (zf.u, zf.v, zf.w)
else:
raise ValueError("Unsupported number of dimensions")
# calculate the advection term
for name, quantity in z.fields.items():
if quantity.flags["NO_ADV"]:
continue
dz.fields[name] += self.scaling * self.advection(velocity, quantity)
return dz
[docs]
@fr.modules.module_method
def update(self, mz: 'fr.ModelState') -> None:
mz.dz = self.advect_state(mz.z, mz.dz)
return mz
# ================================================================
# Properties
# ================================================================
@property
def disable_nonlinear(self):
"""
Whether to disable advection by the state vector itself.
Advection by the background state is still enabled.
"""
return self._disable_nonlinear
@disable_nonlinear.setter
def disable_nonlinear(self, value):
self._disable_nonlinear = value
@property
def scaling(self):
"""
A scaling factor for the nonlinear terms (default: 1.0)
Description
-----------
Some modules require to scale the nonlinear terms, as for example the
optimal balance projection
(:py:class:`fridom.framework.projection.OptimalBalance`). This parameter
provides an interface to set this scaling factor.
"""
return self._scaling
@scaling.setter
def scaling(self, value):
self._scaling = value
@property
def background(self) -> 'fr.StateBase':
"""The background state."""
return self._background
@background.setter
def background(self, value):
self._background = value