Source code for fridom.framework.modules.advection.advection_base
"""Base class for advection schemes."""
from __future__ import annotations
from abc import abstractmethod
from functools import partial
import fridom.framework as fr
[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
[docs]
@abstractmethod
def advection(self,
velocity: fr.VectorField,
quantity: fr.ScalarField) -> fr.ScalarField:
"""Advect a quantity using the given velocity field."""
[docs]
@fr.utils.jaxjit
def advect_state(self, z: fr.VectorField, dz: fr.VectorField) -> fr.VectorField:
"""Advect the state vector."""
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)
zf = z if self.background is None else z + self.background
# calculate the advection term
for field in z:
if field.flags["NO_ADV"]:
continue
dz[field.name] += self.scaling * self.advection(zf.velocity, field)
return dz
[docs]
@fr.modules.module_method
def update(self, mz: fr.ModelState) -> fr.ModelState: # noqa: D102
mz.dz = self.advect_state(mz.z, mz.dz)
return mz
# ================================================================
# Properties
# ================================================================
@property
def disable_nonlinear(self) -> bool:
"""
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: bool) -> None:
self._disable_nonlinear = value
@property
def scaling(self) -> float:
"""
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: float) -> None:
self._scaling = value
@property
def background(self) -> fr.VectorField:
"""The background state."""
return self._background
@background.setter
def background(self, value: fr.VectorField) -> None:
self._background = value