Skip to content

frontx.models

frontx.models

Moisture diffusivity models.

LETd

Bases: _MoistureDiffusivityModel

LET moisture diffusivity on effective saturation.

Computes

.. math::

D(\theta) = D_{wt} \; \frac{Se^{L}}{Se^{L} + E (1-Se)^{T}},\quad
Se = \frac{\theta-\theta_r}{\theta_s-\theta_r}

where :math:\theta_r, \theta_s come from theta_range. All parameters can be floats or trainable :class:Param.

Attributes:

Name Type Description
L float | Param

Exponent for the wet-side shape.

E float | Param

Balance parameter between wet and dry branches.

T float | Param

Exponent for the dry-side shape.

Dwt float | Param

Scaling factor for the diffusivity (default 1).

theta_range tuple[float | Param, float | Param]

Tuple (theta_r, theta_s) used to compute Se.

Source code in frontx/models.py
35
36
37
38
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
class LETd(_MoistureDiffusivityModel):
    """LET moisture diffusivity on effective saturation.

    Computes

    .. math::

        D(\\theta) = D_{wt} \\; \\frac{Se^{L}}{Se^{L} + E (1-Se)^{T}},\\quad
        Se = \\frac{\\theta-\\theta_r}{\\theta_s-\\theta_r}

    where :math:`\\theta_r, \\theta_s` come from ``theta_range``.
    All parameters can be floats or trainable :class:`Param`.

    Attributes:
        L: Exponent for the wet-side shape.
        E: Balance parameter between wet and dry branches.
        T: Exponent for the dry-side shape.
        Dwt: Scaling factor for the diffusivity (default 1).
        theta_range: Tuple ``(theta_r, theta_s)`` used to compute ``Se``.
    """

    L: float | Param
    E: float | Param
    T: float | Param
    Dwt: float | Param = 1
    theta_range: tuple[float | Param, float | Param] = (0, 1)

    def __call__(
        self, theta: float | jax.Array | np.ndarray[Any, Any]
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = (theta - self.theta_range[0]) / (self.theta_range[1] - self.theta_range[0])  # noqa: N806
        return self.Dwt * Se**self.L / (Se**self.L + self.E * (1 - Se) ** self.T)

L: float | Param instance-attribute

E: float | Param instance-attribute

T: float | Param instance-attribute

Dwt: float | Param = 1 class-attribute instance-attribute

theta_range: tuple[float | Param, float | Param] = (0, 1) class-attribute instance-attribute

__call__(theta: float | jax.Array | np.ndarray[Any, Any]) -> float | jax.Array | np.ndarray[Any, Any]

Source code in frontx/models.py
62
63
64
65
66
def __call__(
    self, theta: float | jax.Array | np.ndarray[Any, Any]
) -> float | jax.Array | np.ndarray[Any, Any]:
    Se = (theta - self.theta_range[0]) / (self.theta_range[1] - self.theta_range[0])  # noqa: N806
    return self.Dwt * Se**self.L / (Se**self.L + self.E * (1 - Se) ** self.T)

BrooksAndCorey

Bases: _RichardsModel

Richards-based model using Brooks & Corey relations.

The model defines a relative conductivity :math:k_r(Se) and capillary pressure head :math:h(Se) following Brooks & Corey, and computes a diffusivity via :math:D(\theta) = K(\theta)/C(\theta) with :math:K = K_s k_r and :math:C = (\mathrm{d}h/\mathrm{d}\theta)^{-1}.

Set either Ks (saturated conductivity) or k (intrinsic permeability) together with fluid properties (rho, mu, g). If both are provided, a ValueError is raised.

Attributes:

Name Type Description
n float | Param

Pore-size index (Brooks and Corey exponent).

l float | Param

Mualem connectivity parameter (default 1).

Ks float | Param | None

Saturated hydraulic conductivity (optional if k is set).

k float | None

Intrinsic permeability (mutually exclusive with Ks).

g float | Param

Gravity acceleration (default 9.81).

rho float | Param

Fluid density (default 1e3).

mu float | Param

Dynamic viscosity (default 1e-3).

alpha float | Param

Scaling parameter for pressure head (1/length).

theta_range tuple[float | Param, float | Param]

Tuple (theta_r, theta_s) for effective saturation.

Source code in frontx/models.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
class BrooksAndCorey(_RichardsModel):
    """Richards-based model using Brooks & Corey relations.

    The model defines a relative conductivity :math:`k_r(Se)` and capillary
    pressure head :math:`h(Se)` following Brooks & Corey, and computes a
    diffusivity via :math:`D(\\theta) = K(\\theta)/C(\\theta)` with
    :math:`K = K_s k_r` and :math:`C = (\\mathrm{d}h/\\mathrm{d}\\theta)^{-1}`.

    Set either ``Ks`` (saturated conductivity) **or** ``k`` (intrinsic
    permeability) together with fluid properties (``rho``, ``mu``, ``g``).
    If both are provided, a ``ValueError`` is raised.

    Attributes:
        n: Pore-size index (Brooks and Corey exponent).
        l: Mualem connectivity parameter (default 1).
        Ks: Saturated hydraulic conductivity (optional if ``k`` is set).
        k: Intrinsic permeability (mutually exclusive with ``Ks``).
        g: Gravity acceleration (default 9.81).
        rho: Fluid density (default 1e3).
        mu: Dynamic viscosity (default 1e-3).
        alpha: Scaling parameter for pressure head (1/length).
        theta_range: Tuple ``(theta_r, theta_s)`` for effective saturation.
    """

    n: float | Param
    l: float | Param = 1  # noqa: E741
    Ks: float | Param | None = None
    k: float | None = None
    g: float | Param = 9.81
    rho: float | Param = 1e3
    mu: float | Param = 1e-3
    alpha: float | Param = 1
    theta_range: tuple[float | Param, float | Param] = (0, 1)

    def _h(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return -1 / (self.alpha * Se ** (1 / self.n))

    def _kr(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return Se ** (2 / self.n + self.l + 2)

n: float | Param instance-attribute

l: float | Param = 1 class-attribute instance-attribute

Ks: float | Param | None = None class-attribute instance-attribute

k: float | None = None class-attribute instance-attribute

g: float | Param = 9.81 class-attribute instance-attribute

rho: float | Param = 1000.0 class-attribute instance-attribute

mu: float | Param = 0.001 class-attribute instance-attribute

alpha: float | Param = 1 class-attribute instance-attribute

theta_range: tuple[float | Param, float | Param] = (0, 1) class-attribute instance-attribute

VanGenuchten

Bases: _RichardsModel

Richards-based model using van Genuchten-Mualem relations.

You must set either n or m (the other is inferred through m = 1 - 1/n). The model computes

.. math::

h(Se) = -\frac{1}{\alpha}\Big((Se^{-1/m}-1)^{1/n}\Big),\qquad
k_r(Se) = Se^l\big(1-(1-Se^{1/m})^m\big)^2

and returns :math:D(\theta)=K(\theta)/C(\theta) as in the base class.

Attributes:

Name Type Description
n float | Param | None

van Genuchten shape parameter (optional if m is set).

m float | Param | None

van Genuchten shape parameter (optional if n is set).

l float | Param

Mualem connectivity parameter (default 0.5).

Ks float | Param | None

Saturated hydraulic conductivity (optional if k is set).

k float | Param | None

Intrinsic permeability (mutually exclusive with Ks).

g float | Param

Gravity acceleration (default 9.81).

rho float | Param

Fluid density (default 1e3).

mu float | Param

Dynamic viscosity (default 1e-3).

alpha float | Param

Scaling parameter for pressure head (1/length).

theta_range tuple[float | Param, float | Param]

Tuple (theta_r, theta_s) for effective saturation.

Raises:

Type Description
ValueError

If neither n nor m is provided.

Source code in frontx/models.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
class VanGenuchten(_RichardsModel):
    """Richards-based model using van Genuchten-Mualem relations.

    You must set **either** ``n`` or ``m`` (the other is inferred through
    ``m = 1 - 1/n``). The model computes

    .. math::

        h(Se) = -\\frac{1}{\\alpha}\\Big((Se^{-1/m}-1)^{1/n}\\Big),\\qquad
        k_r(Se) = Se^l\\big(1-(1-Se^{1/m})^m\\big)^2

    and returns :math:`D(\\theta)=K(\\theta)/C(\\theta)` as in the base class.

    Attributes:
        n: van Genuchten shape parameter (optional if ``m`` is set).
        m: van Genuchten shape parameter (optional if ``n`` is set).
        l: Mualem connectivity parameter (default 0.5).
        Ks: Saturated hydraulic conductivity (optional if ``k`` is set).
        k: Intrinsic permeability (mutually exclusive with ``Ks``).
        g: Gravity acceleration (default 9.81).
        rho: Fluid density (default 1e3).
        mu: Dynamic viscosity (default 1e-3).
        alpha: Scaling parameter for pressure head (1/length).
        theta_range: Tuple ``(theta_r, theta_s)`` for effective saturation.

    Raises:
        ValueError: If neither ``n`` nor ``m`` is provided.
    """

    n: float | Param | None = None
    m: float | Param | None = None
    l: float | Param = 0.5  # noqa: E741
    Ks: float | Param | None = None
    k: float | Param | None = None
    g: float | Param = 9.81
    rho: float | Param = 1e3
    mu: float | Param = 1e-3
    alpha: float | Param = 1
    theta_range: tuple[float | Param, float | Param] = (0, 1)

    @property
    def _n(self) -> float | jax.Array | Param:
        if self.n is not None:
            return self.n

        if self.m is None:
            msg = "Either n or m must be set"
            raise ValueError(msg)

        return 1 / (1 - self.m)

    @property
    def _m(self) -> float | jax.Array | Param:
        if self.m is not None:
            return self.m

        if self.n is None:
            msg = "Either n or m must be set"
            raise ValueError(msg)

        return 1 - 1 / self.n

    def _h(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return -((1 / (Se ** (1 / self._m)) - 1) ** (1 / self._n)) / self.alpha

    def _kr(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return Se**self.l * (1 - (1 - Se ** (1 / self._m)) ** self._m) ** 2

n: float | Param | None = None class-attribute instance-attribute

m: float | Param | None = None class-attribute instance-attribute

l: float | Param = 0.5 class-attribute instance-attribute

Ks: float | Param | None = None class-attribute instance-attribute

k: float | Param | None = None class-attribute instance-attribute

g: float | Param = 9.81 class-attribute instance-attribute

rho: float | Param = 1000.0 class-attribute instance-attribute

mu: float | Param = 0.001 class-attribute instance-attribute

alpha: float | Param = 1 class-attribute instance-attribute

theta_range: tuple[float | Param, float | Param] = (0, 1) class-attribute instance-attribute

LETxs

Bases: _RichardsModel

Richards-based LET model with separate wet/dry shapes.

Uses LET-shaped functions both for relative conductivity and pressure head:

.. math::

k_r(Se) = \frac{Se^{L_w}}{Se^{L_w} + E_w (1-Se)^{T_w}},\qquad
h(Se) = -\frac{(1-Se)^{L_s}}{(1-Se)^{L_s} + E_s Se^{T_s}}\,\frac{1}{\alpha}

and returns :math:D(\theta)=K(\theta)/C(\theta).

Attributes:

Name Type Description
Lw float | Param

Wet-side exponent in k_r.

Ew float | Param

Balance parameter in k_r.

Tw float | Param

Dry-side exponent in k_r.

Ls float | Param

Dry-side exponent in h.

Es float | Param

Balance parameter in h.

Ts float | Param

Wet-side exponent in h.

Ks float | Param | None

Saturated hydraulic conductivity (optional if k is set).

k float | None

Intrinsic permeability (mutually exclusive with Ks).

g float | Param

Gravity acceleration (default 9.81).

rho float | Param

Fluid density (default 1e3).

mu float | Param

Dynamic viscosity (default 1e-3).

alpha float | Param

Scaling parameter for pressure head (1/length).

theta_range tuple[float | Param, float | Param]

Tuple (theta_r, theta_s) for effective saturation.

Source code in frontx/models.py
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
class LETxs(_RichardsModel):
    """Richards-based LET model with separate wet/dry shapes.

    Uses LET-shaped functions both for relative conductivity and pressure head:

    .. math::

        k_r(Se) = \\frac{Se^{L_w}}{Se^{L_w} + E_w (1-Se)^{T_w}},\\qquad
        h(Se) = -\\frac{(1-Se)^{L_s}}{(1-Se)^{L_s} + E_s Se^{T_s}}\\,\\frac{1}{\\alpha}

    and returns :math:`D(\\theta)=K(\\theta)/C(\\theta)`.

    Attributes:
        Lw: Wet-side exponent in ``k_r``.
        Ew: Balance parameter in ``k_r``.
        Tw: Dry-side exponent in ``k_r``.
        Ls: Dry-side exponent in ``h``.
        Es: Balance parameter in ``h``.
        Ts: Wet-side exponent in ``h``.
        Ks: Saturated hydraulic conductivity (optional if ``k`` is set).
        k: Intrinsic permeability (mutually exclusive with ``Ks``).
        g: Gravity acceleration (default 9.81).
        rho: Fluid density (default 1e3).
        mu: Dynamic viscosity (default 1e-3).
        alpha: Scaling parameter for pressure head (1/length).
        theta_range: Tuple ``(theta_r, theta_s)`` for effective saturation.
    """

    Lw: float | Param
    Ew: float | Param
    Tw: float | Param
    Ls: float | Param
    Es: float | Param
    Ts: float | Param
    Ks: float | Param | None = None
    k: float | None = None
    g: float | Param = 9.81
    rho: float | Param = 1e3
    mu: float | Param = 1e-3
    alpha: float | Param = 1
    theta_range: tuple[float | Param, float | Param] = (0, 1)

    def _kr(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return Se**self.Lw / (Se**self.Lw + self.Ew * (1 - Se) ** self.Tw)

    def _h(
        self,
        theta: float | jax.Array | np.ndarray[Any, Any],
        /,
    ) -> float | jax.Array | np.ndarray[Any, Any]:
        Se = self._Se(theta)  # noqa: N806
        return (
            -((1 - Se) ** self.Ls / ((1 - Se) ** self.Ls + self.Es * Se**self.Ts))
            / self.alpha
        )

Lw: float | Param instance-attribute

Ew: float | Param instance-attribute

Tw: float | Param instance-attribute

Ls: float | Param instance-attribute

Es: float | Param instance-attribute

Ts: float | Param instance-attribute

Ks: float | Param | None = None class-attribute instance-attribute

k: float | None = None class-attribute instance-attribute

g: float | Param = 9.81 class-attribute instance-attribute

rho: float | Param = 1000.0 class-attribute instance-attribute

mu: float | Param = 0.001 class-attribute instance-attribute

alpha: float | Param = 1 class-attribute instance-attribute

theta_range: tuple[float | Param, float | Param] = (0, 1) class-attribute instance-attribute