Skip to content

Scalar equation

scalar_equation

Scalar terms and equations (e.g. for temperature and salinity transport).

All terms are considered as if they were on the right-hand side of the equation, leading to the following UFL expression returned by the residual method:

\[ (dq)/dt = sum "term.residual()" \]

This sign convention ensures compatibility with Thetis's time integrators. In general, however, we like to think about the terms as they are on the left-hand side. Therefore, in the residual methods below, we first sum the terms in the variable F as if they were on the left-hand side, i.e.

\[ (dq)/dt + F(q) = 0, \]

and then return -F.

Users should not interact with these classes; instead, please use the solver provided in the stokes_integrators module.

ScalarAdvectionTerm(test_space, trial_space, dx, ds, dS, **kwargs)

Bases: BaseTerm

Scalar advection term (non-conservative): u \dot \div(q).

Source code in g-adopt/gadopt/equations.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    dx: firedrake.Measure,
    ds: firedrake.Measure,
    dS: firedrake.Measure,
    **kwargs,
):
    self.test_space = test_space
    self.trial_space = trial_space

    self.dx = dx
    self.ds = ds
    self.dS = dS

    self.mesh = test_space.mesh()
    self.dim = self.mesh.geometric_dimension()
    self.n = firedrake.FacetNormal(self.mesh)

    self.term_kwargs = kwargs

ScalarDiffusionTerm(test_space, trial_space, dx, ds, dS, **kwargs)

Bases: BaseTerm

Scalar diffusion term \(-nabla * (kappa grad q)\).

Using the symmetric interior penalty method, the weak form becomes

\[ {:( -int_Omega nabla * (kappa grad q) phi dx , = , int_Omega kappa (grad phi) * (grad q) dx ), ( , - , int_(cc"I" uu cc"I"_v) "jump"(phi bb n) * "avg"(kappa grad q) dS - int_(cc"I" uu cc"I"_v) "jump"(q bb n) * "avg"(kappa grad phi) dS ), ( , + , int_(cc"I" uu cc"I"_v) sigma "avg"(kappa) "jump"(q bb n) * "jump"(phi bb n) dS ) :} \]

where σ is a penalty parameter (see Epshteyn and Riviere, 2007).

Epshteyn, Y., & Rivière, B. (2007). Estimation of penalty parameters for symmetric interior penalty Galerkin methods. Journal of Computational and Applied Mathematics, 206(2), 843-872.

Source code in g-adopt/gadopt/equations.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    dx: firedrake.Measure,
    ds: firedrake.Measure,
    dS: firedrake.Measure,
    **kwargs,
):
    self.test_space = test_space
    self.trial_space = trial_space

    self.dx = dx
    self.ds = ds
    self.dS = dS

    self.mesh = test_space.mesh()
    self.dim = self.mesh.geometric_dimension()
    self.n = firedrake.FacetNormal(self.mesh)

    self.term_kwargs = kwargs

ScalarSourceTerm(test_space, trial_space, dx, ds, dS, **kwargs)

Bases: BaseTerm

Scalar source term s_T.

Source code in g-adopt/gadopt/equations.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    dx: firedrake.Measure,
    ds: firedrake.Measure,
    dS: firedrake.Measure,
    **kwargs,
):
    self.test_space = test_space
    self.trial_space = trial_space

    self.dx = dx
    self.ds = ds
    self.dS = dS

    self.mesh = test_space.mesh()
    self.dim = self.mesh.geometric_dimension()
    self.n = firedrake.FacetNormal(self.mesh)

    self.term_kwargs = kwargs

ScalarAbsorptionTerm(test_space, trial_space, dx, ds, dS, **kwargs)

Bases: BaseTerm

Scalar absorption term \alpha_T T.

Source code in g-adopt/gadopt/equations.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    dx: firedrake.Measure,
    ds: firedrake.Measure,
    dS: firedrake.Measure,
    **kwargs,
):
    self.test_space = test_space
    self.trial_space = trial_space

    self.dx = dx
    self.ds = ds
    self.dS = dS

    self.mesh = test_space.mesh()
    self.dim = self.mesh.geometric_dimension()
    self.n = firedrake.FacetNormal(self.mesh)

    self.term_kwargs = kwargs

ScalarAdvectionEquation(test_space, trial_space, quad_degree=None, **kwargs)

Bases: BaseEquation

Scalar advection equation with source and absorption terms.

Source code in g-adopt/gadopt/equations.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    quad_degree: Optional[int] = None,
    **kwargs
):
    self.test_space = test_space
    self.trial_space = trial_space
    self.mesh = trial_space.mesh()

    p = trial_space.ufl_element().degree()
    if isinstance(p, int):  # isotropic element
        if quad_degree is None:
            quad_degree = 2*p + 1
    else:  # tensorproduct element
        p_h, p_v = p
        if quad_degree is None:
            quad_degree = 2*max(p_h, p_v) + 1

    if trial_space.extruded:
        # Create surface measures that treat the bottom and top boundaries similarly
        # to lateral boundaries. This way, integration using the ds and dS measures
        # occurs over both horizontal and vertical boundaries, and we can also use
        # "bottom" and "top" as surface identifiers, for example, ds("top").
        self.ds = CombinedSurfaceMeasure(self.mesh, quad_degree)
        self.dS = (
            firedrake.dS_v(domain=self.mesh, degree=quad_degree) +
            firedrake.dS_h(domain=self.mesh, degree=quad_degree)
        )
    else:
        self.ds = firedrake.ds(domain=self.mesh, degree=quad_degree)
        self.dS = firedrake.dS(domain=self.mesh, degree=quad_degree)

    self.dx = firedrake.dx(domain=self.mesh, degree=quad_degree)

    # self._terms stores the actual instances of the BaseTerm-classes in self.terms
    self._terms = []
    for TermClass in self.terms:
        self._terms.append(TermClass(test_space, trial_space, self.dx, self.ds, self.dS, **kwargs))

ScalarAdvectionDiffusionEquation(test_space, trial_space, quad_degree=None, **kwargs)

Bases: BaseEquation

Scalar advection-diffusion equation with source and absorption terms.

Source code in g-adopt/gadopt/equations.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def __init__(
    self,
    test_space: firedrake.functionspaceimpl.WithGeometry,
    trial_space: firedrake.functionspaceimpl.WithGeometry,
    quad_degree: Optional[int] = None,
    **kwargs
):
    self.test_space = test_space
    self.trial_space = trial_space
    self.mesh = trial_space.mesh()

    p = trial_space.ufl_element().degree()
    if isinstance(p, int):  # isotropic element
        if quad_degree is None:
            quad_degree = 2*p + 1
    else:  # tensorproduct element
        p_h, p_v = p
        if quad_degree is None:
            quad_degree = 2*max(p_h, p_v) + 1

    if trial_space.extruded:
        # Create surface measures that treat the bottom and top boundaries similarly
        # to lateral boundaries. This way, integration using the ds and dS measures
        # occurs over both horizontal and vertical boundaries, and we can also use
        # "bottom" and "top" as surface identifiers, for example, ds("top").
        self.ds = CombinedSurfaceMeasure(self.mesh, quad_degree)
        self.dS = (
            firedrake.dS_v(domain=self.mesh, degree=quad_degree) +
            firedrake.dS_h(domain=self.mesh, degree=quad_degree)
        )
    else:
        self.ds = firedrake.ds(domain=self.mesh, degree=quad_degree)
        self.dS = firedrake.dS(domain=self.mesh, degree=quad_degree)

    self.dx = firedrake.dx(domain=self.mesh, degree=quad_degree)

    # self._terms stores the actual instances of the BaseTerm-classes in self.terms
    self._terms = []
    for TermClass in self.terms:
        self._terms.append(TermClass(test_space, trial_space, self.dx, self.ds, self.dS, **kwargs))

EnergyEquation(test_space, trial_space, rhocp=None, quad_degree=None)

Bases: ScalarAdvectionDiffusionEquation

Energy equation defined as an advection-diffusion equation.

Source code in g-adopt/gadopt/scalar_equation.py
232
233
234
235
236
237
238
239
240
def __init__(
    self,
    test_space: fd.functionspaceimpl.WithGeometry,
    trial_space: fd.functionspaceimpl.WithGeometry,
    rhocp: Optional[fd.ufl.core.expr.Expr] = None,
    quad_degree: Optional[int] = None,
):
    self.rhocp = rhocp
    super().__init__(test_space, trial_space, quad_degree=quad_degree)

mass_term(test, trial)

UFL expression for the mass term used in the time discretisation.

Parameters:

Name Type Description Default
test Argument

Firedrake test function

required
trial Argument | Function

Firedrake trial function

required

Returns:

Type Description
Expr

The UFL expression associated with the mass term of the equation.

Source code in g-adopt/gadopt/scalar_equation.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def mass_term(
    self,
    test: fd.ufl_expr.Argument,
    trial: fd.ufl_expr.Argument | fd.Function,
) -> fd.ufl.core.expr.Expr:
    """UFL expression for the mass term used in the time discretisation.

    Arguments:
      test: Firedrake test function
      trial: Firedrake trial function

    Returns:
      The UFL expression associated with the mass term of the equation.

    """
    return self.rhocp * dot(test, trial) * self.dx