Skip to content

Commit 51782a3

Browse files
enrich equation factory
1 parent cc7840f commit 51782a3

File tree

10 files changed

+545
-132
lines changed

10 files changed

+545
-132
lines changed

docs/source/_rst/equation/equation_factory.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,29 @@ Equation Factory
1515
:show-inheritance:
1616

1717
.. autoclass:: FixedLaplacian
18+
:members:
19+
:show-inheritance:
20+
21+
.. autoclass:: Laplace
22+
:members:
23+
:show-inheritance:
24+
25+
.. autoclass:: AdvectionEquation
26+
:members:
27+
:show-inheritance:
28+
29+
.. autoclass:: AllenCahnEquation
30+
:members:
31+
:show-inheritance:
32+
33+
.. autoclass:: DiffusionReactionEquation
34+
:members:
35+
:show-inheritance:
36+
37+
.. autoclass:: HelmholtzEquation
38+
:members:
39+
:show-inheritance:
40+
41+
.. autoclass:: PoissonEquation
1842
:members:
1943
:show-inheritance:

pina/equation/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
"FixedGradient",
88
"FixedFlux",
99
"FixedLaplacian",
10+
"Laplace",
11+
"AdvectionEquation",
12+
"AllenCahnEquation",
13+
"DiffusionReactionEquation",
14+
"HelmholtzEquation",
15+
"PoissonEquation",
1016
]
1117

1218
from .equation import Equation
@@ -15,5 +21,11 @@
1521
FixedGradient,
1622
FixedLaplacian,
1723
FixedValue,
24+
Laplace,
25+
AdvectionEquation,
26+
AllenCahnEquation,
27+
DiffusionReactionEquation,
28+
HelmholtzEquation,
29+
PoissonEquation,
1830
)
1931
from .system_equation import SystemEquation

pina/equation/equation_factory.py

Lines changed: 298 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Module for defining various general equations."""
2-
2+
from typing import Callable
3+
import torch
34
from .equation import Equation
45
from ..operator import grad, div, laplacian
6+
from ..utils import check_consistency
57

68

79
class FixedValue(Equation):
@@ -121,7 +123,7 @@ def __init__(self, value, components=None, d=None):
121123
122124
:param float value: The fixed value to be enforced to the laplacian.
123125
:param list[str] components: The name of the output variables for which
124-
the null laplace condition is applied. It should be a subset of the
126+
the fixed laplace condition is applied. It should be a subset of the
125127
output labels. If ``None``, all output variables are considered.
126128
Default is ``None``.
127129
:param list[str] d: The name of the input variables on which the
@@ -146,3 +148,297 @@ def equation(input_, output_):
146148
)
147149

148150
super().__init__(equation)
151+
152+
153+
class Laplace(FixedLaplacian):
154+
"""
155+
Equation to enforce a null laplacian for a specific condition.
156+
"""
157+
158+
def __init__(self, components=None, d=None):
159+
"""
160+
Initialization of the :class:`Laplace` class.
161+
162+
:param list[str] components: The name of the output variables for which
163+
the null laplace condition is applied. It should be a subset of the
164+
output labels. If ``None``, all output variables are considered.
165+
Default is ``None``.
166+
:param list[str] d: The name of the input variables on which the
167+
laplacian is computed. It should be a subset of the input labels.
168+
If ``None``, all the input variables are considered.
169+
Default is ``None``.
170+
"""
171+
super().__init__(0.0, components=components, d=d)
172+
173+
174+
class AdvectionEquation(Equation):
175+
r"""
176+
Implementation of the N-dimensional advection equation with constant
177+
velocity parameter. The equation is defined as follows:
178+
179+
.. math::
180+
181+
\frac{\partial u}{\partial t} + c \cdot \nabla u = 0
182+
183+
Here, :math:`c` is the advection velocity parameter.
184+
"""
185+
186+
def __init__(self, c):
187+
"""
188+
Initialization of the :class:`AdvectionEquation`.
189+
190+
:param c: The advection velocity. If a scalar is provided, the same
191+
velocity is applied to all spatial dimensions. If a list is
192+
provided, it must contain one value per spatial dimension.
193+
:type c: float | int | List[float] | List[int]
194+
:raises ValueError: If ``c`` is an empty list.
195+
"""
196+
# Check consistency
197+
check_consistency(c, (float, int, list))
198+
if isinstance(c, list):
199+
all(check_consistency(ci, (float, int)) for ci in c)
200+
if len(c) < 1:
201+
raise ValueError("'c' cannot be an empty list.")
202+
else:
203+
c = [c]
204+
205+
# Store advection velocity parameter
206+
self.c = torch.tensor(c).unsqueeze(0)
207+
208+
def equation(input_, output_):
209+
"""
210+
Implementation of the advection equation.
211+
212+
:param LabelTensor input_: The input data of the problem.
213+
:param LabelTensor output_: The output data of the problem.
214+
:return: The residual of the advection equation.
215+
:rtype: LabelTensor
216+
:raises ValueError: If the ``input_`` labels do not contain the time
217+
variable 't'.
218+
:raises ValueError: If ``c`` is a list and its length is not
219+
consistent with the number of spatial dimensions.
220+
"""
221+
# Store labels
222+
input_lbl = input_.labels
223+
spatial_d = [di for di in input_lbl if di != "t"]
224+
225+
# Ensure time is passed as input
226+
if "t" not in input_lbl:
227+
raise ValueError(
228+
"The ``input_`` labels must contain the time 't' variable."
229+
)
230+
231+
# Ensure consistency of c length
232+
if len(self.c) != (len(input_lbl) - 1) and len(self.c) > 1:
233+
raise ValueError(
234+
"If 'c' is passed as a list, its length must be equal to "
235+
"the number of spatial dimensions."
236+
)
237+
238+
# Repeat c to ensure consistent shape for advection
239+
self.c = self.c.repeat(output_.shape[0], 1)
240+
if self.c.shape[1] != (len(input_lbl) - 1):
241+
self.c = self.c.repeat(1, len(input_lbl) - 1)
242+
243+
# Add a dimension to c for the following operations
244+
self.c = self.c.unsqueeze(-1)
245+
246+
# Compute the time derivative and the spatial gradient
247+
time_der = grad(output_, input_, components=None, d="t")
248+
grads = grad(output_=output_, input_=input_, d=spatial_d)
249+
250+
# Reshape and transpose
251+
tmp = grads.reshape(*output_.shape, len(spatial_d))
252+
tmp = tmp.transpose(-1, -2)
253+
254+
# Compute advection term
255+
adv = (tmp * self.c).sum(dim=tmp.tensor.ndim - 2)
256+
257+
return time_der + adv
258+
259+
super().__init__(equation)
260+
261+
262+
class AllenCahnEquation(Equation):
263+
r"""
264+
Implementation of the N-dimensional Allen-Cahn equation, defined as follows:
265+
266+
.. math::
267+
268+
\frac{\partial u}{\partial t} - \alpha \Delta u + \beta(u^3 - u) = 0
269+
270+
Here, :math:`\alpha` and :math:`\beta` are parameters of the equation.
271+
"""
272+
273+
def __init__(self, alpha, beta):
274+
"""
275+
Initialization of the :class:`AllenCahnEquation`.
276+
277+
:param alpha: The diffusion coefficient.
278+
:type alpha: float | int
279+
:param beta: The reaction coefficient.
280+
:type beta: float | int
281+
"""
282+
check_consistency(alpha, (float, int))
283+
check_consistency(beta, (float, int))
284+
self.alpha = alpha
285+
self.beta = beta
286+
287+
def equation(input_, output_):
288+
"""
289+
Implementation of the Allen-Cahn equation.
290+
291+
:param LabelTensor input_: The input data of the problem.
292+
:param LabelTensor output_: The output data of the problem.
293+
:return: The residual of the Allen-Cahn equation.
294+
:rtype: LabelTensor
295+
:raises ValueError: If the ``input_`` labels do not contain the time
296+
variable 't'.
297+
"""
298+
# Ensure time is passed as input
299+
if "t" not in input_.labels:
300+
raise ValueError(
301+
"The ``input_`` labels must contain the time 't' variable."
302+
)
303+
304+
# Compute the time derivative and the spatial laplacian
305+
u_t = grad(output_, input_, d=["t"])
306+
u_xx = laplacian(
307+
output_, input_, d=[di for di in input_.labels if di != "t"]
308+
)
309+
310+
return u_t - self.alpha * u_xx + self.beta * (output_**3 - output_)
311+
312+
super().__init__(equation)
313+
314+
315+
class DiffusionReactionEquation(Equation):
316+
r"""
317+
Implementation of the N-dimensional Diffusion-Reaction equation,
318+
defined as follows:
319+
320+
.. math::
321+
322+
\frac{\partial u}{\partial t} - \alpha \Delta u - f = 0
323+
324+
Here, :math:`\alpha` is a parameter of the equation, while :math:`f` is the
325+
reaction term.
326+
"""
327+
328+
def __init__(self, alpha, forcing_term):
329+
"""
330+
Initialization of the :class:`DiffusionReactionEquation`.
331+
332+
:param alpha: The diffusion coefficient.
333+
:type alpha: float | int
334+
:param Callable forcing_term: The forcing field function, taking as
335+
input the points on which evaluation is required.
336+
"""
337+
check_consistency(alpha, (float, int))
338+
check_consistency(forcing_term, (Callable))
339+
self.alpha = alpha
340+
self.forcing_term = forcing_term
341+
342+
def equation(input_, output_):
343+
"""
344+
Implementation of the Diffusion-Reaction equation.
345+
346+
:param LabelTensor input_: The input data of the problem.
347+
:param LabelTensor output_: The output data of the problem.
348+
:return: The residual of the Diffusion-Reaction equation.
349+
:rtype: LabelTensor
350+
:raises ValueError: If the ``input_`` labels do not contain the time
351+
variable 't'.
352+
"""
353+
# Ensure time is passed as input
354+
if "t" not in input_.labels:
355+
raise ValueError(
356+
"The ``input_`` labels must contain the time 't' variable."
357+
)
358+
359+
# Compute the time derivative and the spatial laplacian
360+
u_t = grad(output_, input_, d=["t"])
361+
u_xx = laplacian(
362+
output_, input_, d=[di for di in input_.labels if di != "t"]
363+
)
364+
365+
return u_t - self.alpha * u_xx - self.forcing_term(input_)
366+
367+
super().__init__(equation)
368+
369+
370+
class HelmholtzEquation(Equation):
371+
r"""
372+
Implementation of the Helmholtz equation, defined as follows:
373+
374+
.. math::
375+
376+
\Delta u + k u - f = 0
377+
378+
Here, :math:`k` is a parameter of the equation, while :math:`f` is the
379+
forcing term.
380+
"""
381+
382+
def __init__(self, k, forcing_term):
383+
"""
384+
Initialization of the :class:`HelmholtzEquation` class.
385+
386+
:param k: The parameter of the equation.
387+
:type k: float | int
388+
:param Callable forcing_term: The forcing field function, taking as
389+
input the points on which evaluation is required.
390+
"""
391+
check_consistency(k, (int, float))
392+
check_consistency(forcing_term, (Callable))
393+
self.k = k
394+
self.forcing_term = forcing_term
395+
396+
def equation(input_, output_):
397+
"""
398+
Implementation of the Helmholtz equation.
399+
400+
:param LabelTensor input_: The input data of the problem.
401+
:param LabelTensor output_: The output data of the problem.
402+
:return: The residual of the Helmholtz equation.
403+
:rtype: LabelTensor
404+
"""
405+
lap = laplacian(output_, input_)
406+
return lap + self.k * output_ - self.forcing_term(input_)
407+
408+
super().__init__(equation)
409+
410+
411+
class PoissonEquation(Equation):
412+
r"""
413+
Implementation of the Poisson equation, defined as follows:
414+
415+
.. math::
416+
417+
\Delta u - f = 0
418+
419+
Here, :math:`f` is the forcing term.
420+
"""
421+
422+
def __init__(self, forcing_term):
423+
"""
424+
Initialization of the :class:`PoissonEquation` class.
425+
426+
:param Callable forcing_term: The forcing field function, taking as
427+
input the points on which evaluation is required.
428+
"""
429+
check_consistency(forcing_term, (Callable))
430+
self.forcing_term = forcing_term
431+
432+
def equation(input_, output_):
433+
"""
434+
Implementation of the Poisson equation.
435+
436+
:param LabelTensor input_: The input data of the problem.
437+
:param LabelTensor output_: The output data of the problem.
438+
:return: The residual of the Poisson equation.
439+
:rtype: LabelTensor
440+
"""
441+
lap = laplacian(output_, input_)
442+
return lap - self.forcing_term(input_)
443+
444+
super().__init__(equation)

0 commit comments

Comments
 (0)