From 940e2ddfa29252af187dba8773f52c23cf28b0de Mon Sep 17 00:00:00 2001 From: Dario Coscia Date: Wed, 19 Feb 2025 11:12:48 +0100 Subject: [PATCH 01/14] adding problems --- pina/problem/zoo/diffusion_reaction.py | 46 ++++++++----- pina/problem/zoo/helmotz.py | 57 ++++++++++++++++ .../problem/zoo/inverse_diffusion_reaction.py | 67 ++++++++++++------- pina/problem/zoo/inverse_poisson_2d_square.py | 22 +++--- pina/problem/zoo/poisson_2d_square.py | 20 +++--- 5 files changed, 150 insertions(+), 62 deletions(-) create mode 100644 pina/problem/zoo/helmotz.py diff --git a/pina/problem/zoo/diffusion_reaction.py b/pina/problem/zoo/diffusion_reaction.py index e7bc6c2be..b383b445b 100644 --- a/pina/problem/zoo/diffusion_reaction.py +++ b/pina/problem/zoo/diffusion_reaction.py @@ -1,9 +1,9 @@ """Definition of the diffusion-reaction problem.""" import torch -from pina import Condition +from pina import Condition, LabelTensor from pina.problem import SpatialProblem, TimeDependentProblem -from pina.equation.equation import Equation +from pina.equation import Equation, FixedValue from pina.domain import CartesianDomain from pina.operator import grad @@ -25,31 +25,43 @@ def diffusion_reaction(input_, output_): ) return u_t - u_xx - r +def initial_condition(input_, output_): + t = input_.extract('t') + x = input_.extract('x') + u_0 = (torch.sin(x) + (1/2)*torch.sin(2*x) + + (1/3)*torch.sin(3*x) + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x)) + return output_ - u_0 + class DiffusionReactionProblem(TimeDependentProblem, SpatialProblem): """ Implementation of the diffusion-reaction problem on the spatial interval - [-pi, pi] and temporal interval [0,1]. + [-pi, pi] and temporal interval [0,1]. Taken from https://www.arxiv.org/pdf/2502.04917 """ output_variables = ["u"] spatial_domain = CartesianDomain({"x": [-torch.pi, torch.pi]}) temporal_domain = CartesianDomain({"t": [0, 1]}) + domains = { + 'D' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': [0, 1]}), + 'g1' : CartesianDomain({'x': -torch.pi, 't': [0, 1]}), + 'g2' : CartesianDomain({'x': torch.pi, 't': [0, 1]}), + 't0' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': 0}), + } conditions = { - "D": Condition( - domain=CartesianDomain({"x": [-torch.pi, torch.pi], "t": [0, 1]}), - equation=Equation(diffusion_reaction), - ) + + 'D': Condition(domain='D', equation=Equation(diffusion_reaction)), + 'g1' : Condition(domain='g1', equation=FixedValue(0)), + 'g2' : Condition(domain='g2', equation=FixedValue(0)), + 't0' : Condition(domain='t0', equation=Equation(initial_condition)), } - def _solution(self, pts): - t = pts.extract("t") - x = pts.extract("x") - return torch.exp(-t) * ( - torch.sin(x) - + (1 / 2) * torch.sin(2 * x) - + (1 / 3) * torch.sin(3 * x) - + (1 / 4) * torch.sin(4 * x) - + (1 / 8) * torch.sin(8 * x) - ) + def solution(self, pts): + t = pts.extract('t') + x = pts.extract('x') + return LabelTensor( + torch.exp(-t) * ( + torch.sin(x) + (1/2)*torch.sin(2*x) + (1/3)*torch.sin(3*x) + + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x) + ), labels=self.output_variables) diff --git a/pina/problem/zoo/helmotz.py b/pina/problem/zoo/helmotz.py new file mode 100644 index 000000000..add641c84 --- /dev/null +++ b/pina/problem/zoo/helmotz.py @@ -0,0 +1,57 @@ +""" Definition of the diffusion-reaction problem.""" + +import torch +from pina import Condition, LabelTensor +from pina.problem import SpatialProblem +from pina.equation import Equation, FixedValue +from pina.domain import CartesianDomain +from pina.operators import laplacian + + +class HelmotzEquation(Equation): + def __init__(self, alpha): + self.alpha = alpha + def equation(input_, output_): + x = input_.extract('x') + y = input_.extract('y') + laplacian_u = laplacian(output_, input_, components=['u']) + q = (1- 2 * (self.alpha * torch.pi)**2) * torch.sin( + self.alpha*torch.pi*x)*torch.sin(self.alpha*torch.pi*y) + return laplacian_u + output_ - q + super().__init__(equation) + +class HelmholtzProblem(SpatialProblem): + """ + Taken from https://www.arxiv.org/pdf/2502.04917 + """ + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [-1, 1], 'y' : [-1, 1]}) + + domains = { + 'D' : CartesianDomain({'x': [-1, 1], 'y' : [-1, 1]}), + 'g1': CartesianDomain({'x': [-1, 1], 'y': 1}), + 'g2': CartesianDomain({'x': [-1, 1], 'y': -1}), + 'g3': CartesianDomain({'x': 1, 'y': [-1, 1]}), + 'g4': CartesianDomain({'x': -1, 'y': [-1, 1]}), + } + + conditions = dict() + + def __init__(self, alpha=3): + super().__init__() + self.alpha = alpha + self.conditions = { + 'D': Condition(domain='D', equation=HelmotzEquation(self.alpha)), + 'g1' : Condition(domain='g1', equation=FixedValue(0)), + 'g2' : Condition(domain='g2', equation=FixedValue(0)), + 'g3' : Condition(domain='g1', equation=FixedValue(0)), + 'g4' : Condition(domain='g2', equation=FixedValue(0)), + } + + def solution(self, pts): + x = pts.extract('x') + y = pts.extract('y') + return LabelTensor( + torch.sin(self.alpha * torch.pi * x) * + torch.sin(self.alpha * torch.pi * y) + ,labels=self.output_variables) diff --git a/pina/problem/zoo/inverse_diffusion_reaction.py b/pina/problem/zoo/inverse_diffusion_reaction.py index 0a0560557..5cdae8558 100644 --- a/pina/problem/zoo/inverse_diffusion_reaction.py +++ b/pina/problem/zoo/inverse_diffusion_reaction.py @@ -3,7 +3,7 @@ import torch from pina import Condition, LabelTensor from pina.problem import SpatialProblem, TimeDependentProblem, InverseProblem -from pina.equation.equation import Equation +from pina.equation import Equation, FixedValue from pina.domain import CartesianDomain from pina.operator import grad @@ -26,13 +26,21 @@ def diffusion_reaction(input_, output_): return u_t - u_xx - r -class InverseDiffusionReactionProblem( - TimeDependentProblem, SpatialProblem, InverseProblem -): +def initial_condition(input_, output_): + t = input_.extract('t') + x = input_.extract('x') + u_0 = (torch.sin(x) + (1/2)*torch.sin(2*x) + + (1/3)*torch.sin(3*x) + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x)) + return output_ - u_0 + +class InverseDiffusionReactionProblem(TimeDependentProblem, + SpatialProblem, + InverseProblem): """ - Implementation of the diffusion-reaction inverse problem on the spatial - interval [-pi, pi] and temporal interval [0,1], with unknown parameters - in the interval [-1,1]. + Implementation of the diffusion-reaction inverse problem on the spatial + interval [-pi, pi] and temporal interval [0,1], with unknown parameters + in the interval [-1,1]. Taken from https://www.arxiv.org/pdf/2502.04917 +>>>>>>> e218673 (adding problems) """ output_variables = ["u"] @@ -40,24 +48,33 @@ class InverseDiffusionReactionProblem( temporal_domain = CartesianDomain({"t": [0, 1]}) unknown_parameter_domain = CartesianDomain({"mu": [-1, 1]}) + domains = { + 'D' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': [0, 1]}), + 'g1' : CartesianDomain({'x': -torch.pi, 't': [0, 1]}), + 'g2' : CartesianDomain({'x': torch.pi, 't': [0, 1]}), + 't0' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': 0}), + } conditions = { - "D": Condition( - domain=CartesianDomain({"x": [-torch.pi, torch.pi], "t": [0, 1]}), - equation=Equation(diffusion_reaction), - ), - "data": Condition( - input=LabelTensor(torch.randn(10, 2), ["x", "t"]), - target=LabelTensor(torch.randn(10, 1), ["u"]), - ), + 'D': Condition(domain='D', equation=Equation(diffusion_reaction)), + 'g1' : Condition(domain='g1', equation=FixedValue(0)), + 'g2' : Condition(domain='g2', equation=FixedValue(0)), + 't0' : Condition(domain='t0', equation=Equation(initial_condition)), + } - def _solution(self, pts): - t = pts.extract("t") - x = pts.extract("x") - return torch.exp(-t) * ( - torch.sin(x) - + (1 / 2) * torch.sin(2 * x) - + (1 / 3) * torch.sin(3 * x) - + (1 / 4) * torch.sin(4 * x) - + (1 / 8) * torch.sin(8 * x) - ) + def __init__(self): + super().__init__() + pts = self.spatial_domain.sample(100) + self.conditions['data'] = Condition( + input_points=pts, + output_points=self.solution(pts) + ) + + def solution(self, pts): + t = pts.extract('t') + x = pts.extract('x') + return LabelTensor( + torch.exp(-t) * ( + torch.sin(x) + (1/2)*torch.sin(2*x) + (1/3)*torch.sin(3*x) + + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x) + ), labels=self.output_variables) diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index 2d9bbe5ac..d244706cf 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -21,6 +21,11 @@ def laplace_equation(input_, output_, params_): return delta_u - force_term +data_output = torch.load( + '../../../tutorials/tutorial7/data/pinn_solution_0.5_0.5') +data_input = torch.load( + '../../../tutorials/tutorial7/data/pts_0.5_0.5').extract(['x', 'y']) + class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): """ Implementation of the inverse 2-dimensional Poisson problem @@ -44,13 +49,12 @@ class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): } conditions = { - "nil_g1": Condition(domain="g1", equation=FixedValue(0.0)), - "nil_g2": Condition(domain="g2", equation=FixedValue(0.0)), - "nil_g3": Condition(domain="g3", equation=FixedValue(0.0)), - "nil_g4": Condition(domain="g4", equation=FixedValue(0.0)), - "laplace_D": Condition(domain="D", equation=Equation(laplace_equation)), - "data": Condition( - input=data_input.extract(["x", "y"]), - target=data_output, - ), + 'nil_g1': Condition(domain='g1', equation=FixedValue(0.0)), + 'nil_g2': Condition(domain='g2', equation=FixedValue(0.0)), + 'nil_g3': Condition(domain='g3', equation=FixedValue(0.0)), + 'nil_g4': Condition(domain='g4', equation=FixedValue(0.0)), + 'laplace_D': Condition(domain='D', equation=Equation(laplace_equation)), + 'data': Condition( + input_points=data_input, + output_points=data_output) } diff --git a/pina/problem/zoo/poisson_2d_square.py b/pina/problem/zoo/poisson_2d_square.py index e65beb5bd..a77c49505 100644 --- a/pina/problem/zoo/poisson_2d_square.py +++ b/pina/problem/zoo/poisson_2d_square.py @@ -40,17 +40,15 @@ class Poisson2DSquareProblem(SpatialProblem): } conditions = { - "nil_g1": Condition(domain="g1", equation=FixedValue(0.0)), - "nil_g2": Condition(domain="g2", equation=FixedValue(0.0)), - "nil_g3": Condition(domain="g3", equation=FixedValue(0.0)), - "nil_g4": Condition(domain="g4", equation=FixedValue(0.0)), - "laplace_D": Condition(domain="D", equation=my_laplace), + + 'g1': Condition(domain='g1', equation=FixedValue(0.0)), + 'g2': Condition(domain='g2', equation=FixedValue(0.0)), + 'g3': Condition(domain='g3', equation=FixedValue(0.0)), + 'g4': Condition(domain='g4', equation=FixedValue(0.0)), + 'D': Condition(domain='D', equation=my_laplace), } - def poisson_sol(self, pts): - """TODO""" + def solution(self, pts): + return -(torch.sin(pts.extract(['x']) * torch.pi) * + torch.sin(pts.extract(['y']) * torch.pi)) - return -( - torch.sin(pts.extract(["x"]) * torch.pi) - * torch.sin(pts.extract(["y"]) * torch.pi) - ) From cf1c06f5b3f8682cf1dc016f224e01b60c1130f1 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 11:59:10 +0100 Subject: [PATCH 02/14] fixed poisson --- pina/problem/zoo/__init__.py | 8 +-- pina/problem/zoo/inverse_poisson_2d_square.py | 46 +++++++++------- pina/problem/zoo/poisson_2d_square.py | 52 ++++++++++++------- 3 files changed, 62 insertions(+), 44 deletions(-) diff --git a/pina/problem/zoo/__init__.py b/pina/problem/zoo/__init__.py index c18d649d7..833472fc0 100644 --- a/pina/problem/zoo/__init__.py +++ b/pina/problem/zoo/__init__.py @@ -1,15 +1,15 @@ """TODO""" __all__ = [ - "Poisson2DSquareProblem", "SupervisedProblem", - "InversePoisson2DSquareProblem", + "Poisson2DSquareProblem", "DiffusionReactionProblem", + "InversePoisson2DSquareProblem", "InverseDiffusionReactionProblem", ] -from .poisson_2d_square import Poisson2DSquareProblem from .supervised_problem import SupervisedProblem -from .inverse_poisson_2d_square import InversePoisson2DSquareProblem +from .poisson_2d_square import Poisson2DSquareProblem from .diffusion_reaction import DiffusionReactionProblem +from .inverse_poisson_2d_square import InversePoisson2DSquareProblem from .inverse_diffusion_reaction import InverseDiffusionReactionProblem diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index d244706cf..80810ad73 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -1,17 +1,22 @@ -"""Definition of the inverse Poisson problem on a square domain.""" +"""Formulation of the inverse Poisson problem in a square domain.""" import torch -from pina import Condition, LabelTensor -from pina.problem import SpatialProblem, InverseProblem -from pina.operator import laplacian -from pina.domain import CartesianDomain -from pina.equation.equation import Equation -from pina.equation.equation_factory import FixedValue +from ... import Condition +from ...operator import laplacian +from ...domain import CartesianDomain +from ...equation import Equation, FixedValue +from ...problem import SpatialProblem, InverseProblem def laplace_equation(input_, output_, params_): """ Implementation of the laplace equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :param dict params_: Parameters of the problem. + :return: The residual of the laplace equation. + :rtype: LabelTensor """ force_term = torch.exp( -2 * (input_.extract(["x"]) - params_["mu1"]) ** 2 @@ -21,22 +26,25 @@ def laplace_equation(input_, output_, params_): return delta_u - force_term +# Load data data_output = torch.load( - '../../../tutorials/tutorial7/data/pinn_solution_0.5_0.5') + f="../../../tutorials/tutorial7/data/pinn_solution_0.5_0.5", + weights_only=False, +) data_input = torch.load( - '../../../tutorials/tutorial7/data/pts_0.5_0.5').extract(['x', 'y']) + f="../../../tutorials/tutorial7/data/pts_0.5_0.5", weights_only=False +).extract(["x", "y"]) + class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): """ Implementation of the inverse 2-dimensional Poisson problem - on a square domain, with parameter domain [-1, 1] x [-1, 1]. + in a square domain, with parameter domain [-1, 1] x [-1, 1]. """ output_variables = ["u"] x_min, x_max = -2, 2 y_min, y_max = -2, 2 - data_input = LabelTensor(torch.rand(10, 2), ["x", "y"]) - data_output = LabelTensor(torch.rand(10, 1), ["u"]) spatial_domain = CartesianDomain({"x": [x_min, x_max], "y": [y_min, y_max]}) unknown_parameter_domain = CartesianDomain({"mu1": [-1, 1], "mu2": [-1, 1]}) @@ -49,12 +57,10 @@ class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): } conditions = { - 'nil_g1': Condition(domain='g1', equation=FixedValue(0.0)), - 'nil_g2': Condition(domain='g2', equation=FixedValue(0.0)), - 'nil_g3': Condition(domain='g3', equation=FixedValue(0.0)), - 'nil_g4': Condition(domain='g4', equation=FixedValue(0.0)), - 'laplace_D': Condition(domain='D', equation=Equation(laplace_equation)), - 'data': Condition( - input_points=data_input, - output_points=data_output) + "g1": Condition(domain="g1", equation=FixedValue(0.0)), + "g2": Condition(domain="g2", equation=FixedValue(0.0)), + "g3": Condition(domain="g3", equation=FixedValue(0.0)), + "g4": Condition(domain="g4", equation=FixedValue(0.0)), + "D": Condition(domain="D", equation=Equation(laplace_equation)), + "data": Condition(input=data_input, target=data_output), } diff --git a/pina/problem/zoo/poisson_2d_square.py b/pina/problem/zoo/poisson_2d_square.py index a77c49505..cc1cf0f87 100644 --- a/pina/problem/zoo/poisson_2d_square.py +++ b/pina/problem/zoo/poisson_2d_square.py @@ -1,31 +1,34 @@ -"""Definition of the Poisson problem on a square domain.""" +"""Formulation of the Poisson problem in a square domain.""" import torch -from ..spatial_problem import SpatialProblem -from ...operator import laplacian from ... import Condition +from ...operator import laplacian +from ...problem import SpatialProblem from ...domain import CartesianDomain -from ...equation.equation import Equation -from ...equation.equation_factory import FixedValue +from ...equation import Equation, FixedValue def laplace_equation(input_, output_): """ Implementation of the laplace equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the laplace equation. + :rtype: LabelTensor """ - force_term = torch.sin(input_.extract(["x"]) * torch.pi) * torch.sin( - input_.extract(["y"]) * torch.pi + force_term = ( + torch.sin(input_.extract(["x"]) * torch.pi) + * torch.sin(input_.extract(["y"]) * torch.pi) + * (2 * torch.pi**2) ) - delta_u = laplacian(output_.extract(["u"]), input_) + delta_u = laplacian(output_, input_, components=["u"], d=["x", "y"]) return delta_u - force_term -my_laplace = Equation(laplace_equation) - - class Poisson2DSquareProblem(SpatialProblem): """ - Implementation of the 2-dimensional Poisson problem on a square domain. + Implementation of the 2-dimensional Poisson problem in a square domain. """ output_variables = ["u"] @@ -40,15 +43,24 @@ class Poisson2DSquareProblem(SpatialProblem): } conditions = { - - 'g1': Condition(domain='g1', equation=FixedValue(0.0)), - 'g2': Condition(domain='g2', equation=FixedValue(0.0)), - 'g3': Condition(domain='g3', equation=FixedValue(0.0)), - 'g4': Condition(domain='g4', equation=FixedValue(0.0)), - 'D': Condition(domain='D', equation=my_laplace), + "g1": Condition(domain="g1", equation=FixedValue(0.0)), + "g2": Condition(domain="g2", equation=FixedValue(0.0)), + "g3": Condition(domain="g3", equation=FixedValue(0.0)), + "g4": Condition(domain="g4", equation=FixedValue(0.0)), + "D": Condition(domain="D", equation=Equation(laplace_equation)), } def solution(self, pts): - return -(torch.sin(pts.extract(['x']) * torch.pi) * - torch.sin(pts.extract(['y']) * torch.pi)) + """ + Implementation of the analytical solution of the Poisson problem. + :param LabelTensor pts: Points where the solution is evaluated. + :return: The analytical solution of the Poisson problem. + :rtype: LabelTensor + """ + sol = -( + torch.sin(pts.extract(["x"]) * torch.pi) + * torch.sin(pts.extract(["y"]) * torch.pi) + ) + sol.labels = self.output_variables + return sol From c50d85cd23090754b896488b416420632a2af244 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 12:10:40 +0100 Subject: [PATCH 03/14] fix doc in supervised problem --- pina/problem/zoo/supervised_problem.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pina/problem/zoo/supervised_problem.py b/pina/problem/zoo/supervised_problem.py index 1d4654945..8a414afe2 100644 --- a/pina/problem/zoo/supervised_problem.py +++ b/pina/problem/zoo/supervised_problem.py @@ -1,4 +1,4 @@ -"""TODO""" +"""Formulation of a Supervised Problem in PINA.""" from ..abstract_problem import AbstractProblem from ... import Condition @@ -7,11 +7,10 @@ class SupervisedProblem(AbstractProblem): """ - A problem definition for supervised learning in PINA. + Definition of a supervised learning problem in PINA. - This class allows an easy and straightforward definition of a - Supervised problem, based on a single condition of type - `InputTargetCondition` + This class provides a simple way to define a supervised problem + using a single condition of type `InputTargetCondition`. :Example: >>> import torch @@ -25,12 +24,11 @@ class SupervisedProblem(AbstractProblem): def __init__(self, input_, output_): """ - Initialize the SupervisedProblem class + Initialize the SupervisedProblem class. - :param input_: Input data of the problem + :param input_: Input data of the problem. :type input_: torch.Tensor | Graph - :param output_: Output data of the problem - :type output_: torch.Tensor + :param torch.Tensor output_: Output data of the problem. """ if isinstance(input_, Graph): input_ = input_.data From 1f2fc12f38f385ab74a6ebbc708d366500885e1b Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 14:06:11 +0100 Subject: [PATCH 04/14] fix diffusion-reaction --- pina/problem/zoo/diffusion_reaction.py | 90 +++++++++----- .../problem/zoo/inverse_diffusion_reaction.py | 112 +++++++++++------- pina/problem/zoo/inverse_poisson_2d_square.py | 13 +- pina/problem/zoo/poisson_2d_square.py | 8 +- 4 files changed, 142 insertions(+), 81 deletions(-) diff --git a/pina/problem/zoo/diffusion_reaction.py b/pina/problem/zoo/diffusion_reaction.py index b383b445b..8f5c83610 100644 --- a/pina/problem/zoo/diffusion_reaction.py +++ b/pina/problem/zoo/diffusion_reaction.py @@ -1,22 +1,26 @@ -"""Definition of the diffusion-reaction problem.""" +"""Formulation of the diffusion-reaction problem.""" import torch -from pina import Condition, LabelTensor -from pina.problem import SpatialProblem, TimeDependentProblem -from pina.equation import Equation, FixedValue -from pina.domain import CartesianDomain -from pina.operator import grad +from ... import Condition +from ...domain import CartesianDomain +from ...operator import grad, laplacian +from ...equation import Equation, FixedValue +from ...problem import SpatialProblem, TimeDependentProblem def diffusion_reaction(input_, output_): """ Implementation of the diffusion-reaction equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the diffusion-reaction equation. + :rtype: LabelTensor """ x = input_.extract("x") t = input_.extract("t") - u_t = grad(output_, input_, d="t") - u_x = grad(output_, input_, d="x") - u_xx = grad(u_x, input_, d="x") + u_t = grad(output_, input_, components=["u"], d=["t"]) + u_xx = laplacian(output_, input_, components=["u"], d=["x"]) r = torch.exp(-t) * ( 1.5 * torch.sin(2 * x) + (8 / 3) * torch.sin(3 * x) @@ -25,18 +29,32 @@ def diffusion_reaction(input_, output_): ) return u_t - u_xx - r + def initial_condition(input_, output_): - t = input_.extract('t') - x = input_.extract('x') - u_0 = (torch.sin(x) + (1/2)*torch.sin(2*x) + - (1/3)*torch.sin(3*x) + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x)) + """ + Definition of the initial condition of the diffusion-reaction problem. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the initial condition. + :rtype: LabelTensor + """ + x = input_.extract("x") + u_0 = ( + torch.sin(x) + + (1 / 2) * torch.sin(2 * x) + + (1 / 3) * torch.sin(3 * x) + + (1 / 4) * torch.sin(4 * x) + + (1 / 8) * torch.sin(8 * x) + ) return output_ - u_0 class DiffusionReactionProblem(TimeDependentProblem, SpatialProblem): """ Implementation of the diffusion-reaction problem on the spatial interval - [-pi, pi] and temporal interval [0,1]. Taken from https://www.arxiv.org/pdf/2502.04917 + [-pi, pi] and temporal interval [0,1]. + Reference: https://www.arxiv.org/pdf/2502.04917. """ output_variables = ["u"] @@ -44,24 +62,36 @@ class DiffusionReactionProblem(TimeDependentProblem, SpatialProblem): temporal_domain = CartesianDomain({"t": [0, 1]}) domains = { - 'D' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': [0, 1]}), - 'g1' : CartesianDomain({'x': -torch.pi, 't': [0, 1]}), - 'g2' : CartesianDomain({'x': torch.pi, 't': [0, 1]}), - 't0' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': 0}), + "D": CartesianDomain({"x": [-torch.pi, torch.pi], "t": [0, 1]}), + "g1": CartesianDomain({"x": -torch.pi, "t": [0, 1]}), + "g2": CartesianDomain({"x": torch.pi, "t": [0, 1]}), + "t0": CartesianDomain({"x": [-torch.pi, torch.pi], "t": 0.0}), } - conditions = { - 'D': Condition(domain='D', equation=Equation(diffusion_reaction)), - 'g1' : Condition(domain='g1', equation=FixedValue(0)), - 'g2' : Condition(domain='g2', equation=FixedValue(0)), - 't0' : Condition(domain='t0', equation=Equation(initial_condition)), + conditions = { + "D": Condition(domain="D", equation=Equation(diffusion_reaction)), + "g1": Condition(domain="g1", equation=FixedValue(0.0)), + "g2": Condition(domain="g2", equation=FixedValue(0.0)), + "t0": Condition(domain="t0", equation=Equation(initial_condition)), } def solution(self, pts): - t = pts.extract('t') - x = pts.extract('x') - return LabelTensor( - torch.exp(-t) * ( - torch.sin(x) + (1/2)*torch.sin(2*x) + (1/3)*torch.sin(3*x) + - (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x) - ), labels=self.output_variables) + """ + Implementation of the analytical solution of the diffusion-reaction + problem. + + :param LabelTensor pts: Points where the solution is evaluated. + :return: The analytical solution of the diffusion-reaction problem. + :rtype: LabelTensor + """ + t = pts.extract("t") + x = pts.extract("x") + sol = torch.exp(-t) * ( + torch.sin(x) + + (1 / 2) * torch.sin(2 * x) + + (1 / 3) * torch.sin(3 * x) + + (1 / 4) * torch.sin(4 * x) + + (1 / 8) * torch.sin(8 * x) + ) + sol.labels = self.output_variables + return sol diff --git a/pina/problem/zoo/inverse_diffusion_reaction.py b/pina/problem/zoo/inverse_diffusion_reaction.py index 5cdae8558..1ee747a73 100644 --- a/pina/problem/zoo/inverse_diffusion_reaction.py +++ b/pina/problem/zoo/inverse_diffusion_reaction.py @@ -1,22 +1,26 @@ -"""Definition of the diffusion-reaction problem.""" +"""Formulation of the inverse diffusion-reaction problem.""" import torch -from pina import Condition, LabelTensor -from pina.problem import SpatialProblem, TimeDependentProblem, InverseProblem -from pina.equation import Equation, FixedValue -from pina.domain import CartesianDomain -from pina.operator import grad +from ... import Condition +from ...domain import CartesianDomain +from ...operator import grad, laplacian +from ...equation import Equation, FixedValue +from ...problem import SpatialProblem, TimeDependentProblem, InverseProblem def diffusion_reaction(input_, output_): """ Implementation of the diffusion-reaction equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the diffusion-reaction equation. + :rtype: LabelTensor """ x = input_.extract("x") t = input_.extract("t") - u_t = grad(output_, input_, d="t") - u_x = grad(output_, input_, d="x") - u_xx = grad(u_x, input_, d="x") + u_t = grad(output_, input_, components=["u"], d=["t"]) + u_xx = laplacian(output_, input_, components=["u"], d=["x"]) r = torch.exp(-t) * ( 1.5 * torch.sin(2 * x) + (8 / 3) * torch.sin(3 * x) @@ -27,20 +31,32 @@ def diffusion_reaction(input_, output_): def initial_condition(input_, output_): - t = input_.extract('t') - x = input_.extract('x') - u_0 = (torch.sin(x) + (1/2)*torch.sin(2*x) + - (1/3)*torch.sin(3*x) + (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x)) + """ + Definition of the initial condition of the diffusion-reaction problem. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the initial condition. + :rtype: LabelTensor + """ + x = input_.extract("x") + u_0 = ( + torch.sin(x) + + (1 / 2) * torch.sin(2 * x) + + (1 / 3) * torch.sin(3 * x) + + (1 / 4) * torch.sin(4 * x) + + (1 / 8) * torch.sin(8 * x) + ) return output_ - u_0 -class InverseDiffusionReactionProblem(TimeDependentProblem, - SpatialProblem, - InverseProblem): + +class InverseDiffusionReactionProblem( + TimeDependentProblem, SpatialProblem, InverseProblem +): """ - Implementation of the diffusion-reaction inverse problem on the spatial - interval [-pi, pi] and temporal interval [0,1], with unknown parameters - in the interval [-1,1]. Taken from https://www.arxiv.org/pdf/2502.04917 ->>>>>>> e218673 (adding problems) + Implementation of the diffusion-reaction inverse problem on the spatial + interval [-pi, pi] and temporal interval [0,1], with unknown parameter + domain [-1,1]. """ output_variables = ["u"] @@ -49,32 +65,46 @@ class InverseDiffusionReactionProblem(TimeDependentProblem, unknown_parameter_domain = CartesianDomain({"mu": [-1, 1]}) domains = { - 'D' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': [0, 1]}), - 'g1' : CartesianDomain({'x': -torch.pi, 't': [0, 1]}), - 'g2' : CartesianDomain({'x': torch.pi, 't': [0, 1]}), - 't0' : CartesianDomain({'x': [-torch.pi, torch.pi], 't': 0}), + "D": CartesianDomain({"x": [-torch.pi, torch.pi], "t": [0, 1]}), + "g1": CartesianDomain({"x": -torch.pi, "t": [0, 1]}), + "g2": CartesianDomain({"x": torch.pi, "t": [0, 1]}), + "t0": CartesianDomain({"x": [-torch.pi, torch.pi], "t": 0.0}), } - conditions = { - 'D': Condition(domain='D', equation=Equation(diffusion_reaction)), - 'g1' : Condition(domain='g1', equation=FixedValue(0)), - 'g2' : Condition(domain='g2', equation=FixedValue(0)), - 't0' : Condition(domain='t0', equation=Equation(initial_condition)), + conditions = { + "D": Condition(domain="D", equation=Equation(diffusion_reaction)), + "g1": Condition(domain="g1", equation=FixedValue(0.0)), + "g2": Condition(domain="g2", equation=FixedValue(0.0)), + "t0": Condition(domain="t0", equation=Equation(initial_condition)), } def __init__(self): + """ + Initialization of the inverse diffusion-reaction problem. + """ super().__init__() pts = self.spatial_domain.sample(100) - self.conditions['data'] = Condition( - input_points=pts, - output_points=self.solution(pts) - ) - + self.conditions["data"] = Condition( + input=pts, target=self.solution(pts) + ) + def solution(self, pts): - t = pts.extract('t') - x = pts.extract('x') - return LabelTensor( - torch.exp(-t) * ( - torch.sin(x) + (1/2)*torch.sin(2*x) + (1/3)*torch.sin(3*x) + - (1/4)*torch.sin(4*x) + (1/8)*torch.sin(8*x) - ), labels=self.output_variables) + """ + Implementation of the analytical solution of the diffusion-reaction + problem. + + :param LabelTensor pts: Points where the solution is evaluated. + :return: The analytical solution of the diffusion-reaction problem. + :rtype: LabelTensor + """ + t = pts.extract("t") + x = pts.extract("x") + sol = torch.exp(-t) * ( + torch.sin(x) + + (1 / 2) * torch.sin(2 * x) + + (1 / 3) * torch.sin(3 * x) + + (1 / 4) * torch.sin(4 * x) + + (1 / 8) * torch.sin(8 * x) + ) + sol.labels = self.output_variables + return sol diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index 80810ad73..f6ad08ffd 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -27,19 +27,20 @@ def laplace_equation(input_, output_, params_): # Load data -data_output = torch.load( +input_data = torch.load( + f="../../../tutorials/tutorial7/data/pts_0.5_0.5", weights_only=False +).extract(["x", "y"]) + +output_data = torch.load( f="../../../tutorials/tutorial7/data/pinn_solution_0.5_0.5", weights_only=False, ) -data_input = torch.load( - f="../../../tutorials/tutorial7/data/pts_0.5_0.5", weights_only=False -).extract(["x", "y"]) class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): """ Implementation of the inverse 2-dimensional Poisson problem - in a square domain, with parameter domain [-1, 1] x [-1, 1]. + in a square domain, with unknown parameter domain [-1, 1] x [-1, 1]. """ output_variables = ["u"] @@ -62,5 +63,5 @@ class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): "g3": Condition(domain="g3", equation=FixedValue(0.0)), "g4": Condition(domain="g4", equation=FixedValue(0.0)), "D": Condition(domain="D", equation=Equation(laplace_equation)), - "data": Condition(input=data_input, target=data_output), + "data": Condition(input=input_data, target=output_data), } diff --git a/pina/problem/zoo/poisson_2d_square.py b/pina/problem/zoo/poisson_2d_square.py index cc1cf0f87..3964de0e0 100644 --- a/pina/problem/zoo/poisson_2d_square.py +++ b/pina/problem/zoo/poisson_2d_square.py @@ -36,10 +36,10 @@ class Poisson2DSquareProblem(SpatialProblem): domains = { "D": CartesianDomain({"x": [0, 1], "y": [0, 1]}), - "g1": CartesianDomain({"x": [0, 1], "y": 1}), - "g2": CartesianDomain({"x": [0, 1], "y": 0}), - "g3": CartesianDomain({"x": 1, "y": [0, 1]}), - "g4": CartesianDomain({"x": 0, "y": [0, 1]}), + "g1": CartesianDomain({"x": [0, 1], "y": 1.0}), + "g2": CartesianDomain({"x": [0, 1], "y": 0.0}), + "g3": CartesianDomain({"x": 1.0, "y": [0, 1]}), + "g4": CartesianDomain({"x": 0.0, "y": [0, 1]}), } conditions = { From 90fb81ad0c27b00079ae87398dd590ae6f0b9250 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 14:47:32 +0100 Subject: [PATCH 05/14] add helmholtz --- pina/problem/zoo/__init__.py | 2 + pina/problem/zoo/diffusion_reaction.py | 4 +- pina/problem/zoo/helmholtz.py | 92 +++++++++++++++++++ pina/problem/zoo/helmotz.py | 57 ------------ .../problem/zoo/inverse_diffusion_reaction.py | 6 +- pina/problem/zoo/inverse_poisson_2d_square.py | 4 +- pina/problem/zoo/poisson_2d_square.py | 3 +- 7 files changed, 103 insertions(+), 65 deletions(-) create mode 100644 pina/problem/zoo/helmholtz.py delete mode 100644 pina/problem/zoo/helmotz.py diff --git a/pina/problem/zoo/__init__.py b/pina/problem/zoo/__init__.py index 833472fc0..6b3964aff 100644 --- a/pina/problem/zoo/__init__.py +++ b/pina/problem/zoo/__init__.py @@ -2,6 +2,7 @@ __all__ = [ "SupervisedProblem", + "HelmholtzProblem", "Poisson2DSquareProblem", "DiffusionReactionProblem", "InversePoisson2DSquareProblem", @@ -9,6 +10,7 @@ ] from .supervised_problem import SupervisedProblem +from .helmholtz import HelmholtzProblem from .poisson_2d_square import Poisson2DSquareProblem from .diffusion_reaction import DiffusionReactionProblem from .inverse_poisson_2d_square import InversePoisson2DSquareProblem diff --git a/pina/problem/zoo/diffusion_reaction.py b/pina/problem/zoo/diffusion_reaction.py index 8f5c83610..31296e369 100644 --- a/pina/problem/zoo/diffusion_reaction.py +++ b/pina/problem/zoo/diffusion_reaction.py @@ -52,8 +52,8 @@ def initial_condition(input_, output_): class DiffusionReactionProblem(TimeDependentProblem, SpatialProblem): """ - Implementation of the diffusion-reaction problem on the spatial interval - [-pi, pi] and temporal interval [0,1]. + Implementation of the diffusion-reaction problem in the spatial interval + [-pi, pi] and temporal interval [0, 1]. Reference: https://www.arxiv.org/pdf/2502.04917. """ diff --git a/pina/problem/zoo/helmholtz.py b/pina/problem/zoo/helmholtz.py new file mode 100644 index 000000000..59f0c5764 --- /dev/null +++ b/pina/problem/zoo/helmholtz.py @@ -0,0 +1,92 @@ +"""Formulation of the Helmholtz problem.""" + +import torch +from ... import Condition +from ...problem import SpatialProblem +from ...equation import Equation, FixedValue +from ...domain import CartesianDomain +from ...operators import laplacian + + +class HelmholtzEquation(Equation): + """ + Implementation of the Helmholtz equation. + """ + + def __init__(self, alpha): + """ + Initialize the Helmholtz equation. + + :param float alpha: Parameter of the forcing term. + """ + self.alpha = alpha + + def equation(input_, output_): + """ + Implementation of the Helmholtz equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the Helmholtz equation. + :rtype: LabelTensor + """ + lap = laplacian(output_, input_, components=["u"], d=["x", "y"]) + q = ( + (1 - 2 * (self.alpha * torch.pi) ** 2) + * torch.sin(self.alpha * torch.pi * input_.extract("x")) + * torch.sin(self.alpha * torch.pi * input_.extract("y")) + ) + return lap + output_ - q + + super().__init__(equation) + + +class HelmholtzProblem(SpatialProblem): + """ + Implementation of the Helmholtz problem in the square domain + [-1, 1] x [-1, 1]. Reference: https://www.arxiv.org/pdf/2502.04917. + """ + + output_variables = ["u"] + spatial_domain = CartesianDomain({"x": [-1, 1], "y": [-1, 1]}) + + domains = { + "D": CartesianDomain({"x": [-1, 1], "y": [-1, 1]}), + "g1": CartesianDomain({"x": [-1, 1], "y": 1.0}), + "g2": CartesianDomain({"x": [-1, 1], "y": -1.0}), + "g3": CartesianDomain({"x": 1.0, "y": [-1, 1]}), + "g4": CartesianDomain({"x": -1.0, "y": [-1, 1]}), + } + + conditions = { + "g1": Condition(domain="g1", equation=FixedValue(0.0)), + "g2": Condition(domain="g2", equation=FixedValue(0.0)), + "g3": Condition(domain="g3", equation=FixedValue(0.0)), + "g4": Condition(domain="g4", equation=FixedValue(0.0)), + } + + def __init__(self, alpha=3.0): + """ + Initialize the Helmholtz problem. + + :param float alpha: Parameter of the forcing term. + """ + super().__init__() + self.alpha = alpha + self.conditions["D"] = Condition( + domain="D", equation=HelmholtzEquation(self.alpha) + ) + + def solution(self, pts): + """ + Implementation of the analytical solution of the Helmholtz problem. + + :param LabelTensor pts: Points where the solution is evaluated. + :return: The analytical solution of the Poisson problem. + :rtype: LabelTensor + """ + sol = torch.sin(self.alpha * torch.pi * pts.extract("x")) * torch.sin( + self.alpha * torch.pi * pts.extract("y") + ) + sol.labels = self.output_variables + return sol diff --git a/pina/problem/zoo/helmotz.py b/pina/problem/zoo/helmotz.py deleted file mode 100644 index add641c84..000000000 --- a/pina/problem/zoo/helmotz.py +++ /dev/null @@ -1,57 +0,0 @@ -""" Definition of the diffusion-reaction problem.""" - -import torch -from pina import Condition, LabelTensor -from pina.problem import SpatialProblem -from pina.equation import Equation, FixedValue -from pina.domain import CartesianDomain -from pina.operators import laplacian - - -class HelmotzEquation(Equation): - def __init__(self, alpha): - self.alpha = alpha - def equation(input_, output_): - x = input_.extract('x') - y = input_.extract('y') - laplacian_u = laplacian(output_, input_, components=['u']) - q = (1- 2 * (self.alpha * torch.pi)**2) * torch.sin( - self.alpha*torch.pi*x)*torch.sin(self.alpha*torch.pi*y) - return laplacian_u + output_ - q - super().__init__(equation) - -class HelmholtzProblem(SpatialProblem): - """ - Taken from https://www.arxiv.org/pdf/2502.04917 - """ - output_variables = ['u'] - spatial_domain = CartesianDomain({'x': [-1, 1], 'y' : [-1, 1]}) - - domains = { - 'D' : CartesianDomain({'x': [-1, 1], 'y' : [-1, 1]}), - 'g1': CartesianDomain({'x': [-1, 1], 'y': 1}), - 'g2': CartesianDomain({'x': [-1, 1], 'y': -1}), - 'g3': CartesianDomain({'x': 1, 'y': [-1, 1]}), - 'g4': CartesianDomain({'x': -1, 'y': [-1, 1]}), - } - - conditions = dict() - - def __init__(self, alpha=3): - super().__init__() - self.alpha = alpha - self.conditions = { - 'D': Condition(domain='D', equation=HelmotzEquation(self.alpha)), - 'g1' : Condition(domain='g1', equation=FixedValue(0)), - 'g2' : Condition(domain='g2', equation=FixedValue(0)), - 'g3' : Condition(domain='g1', equation=FixedValue(0)), - 'g4' : Condition(domain='g2', equation=FixedValue(0)), - } - - def solution(self, pts): - x = pts.extract('x') - y = pts.extract('y') - return LabelTensor( - torch.sin(self.alpha * torch.pi * x) * - torch.sin(self.alpha * torch.pi * y) - ,labels=self.output_variables) diff --git a/pina/problem/zoo/inverse_diffusion_reaction.py b/pina/problem/zoo/inverse_diffusion_reaction.py index 1ee747a73..4dfa882d8 100644 --- a/pina/problem/zoo/inverse_diffusion_reaction.py +++ b/pina/problem/zoo/inverse_diffusion_reaction.py @@ -54,9 +54,9 @@ class InverseDiffusionReactionProblem( TimeDependentProblem, SpatialProblem, InverseProblem ): """ - Implementation of the diffusion-reaction inverse problem on the spatial - interval [-pi, pi] and temporal interval [0,1], with unknown parameter - domain [-1,1]. + Implementation of the diffusion-reaction inverse problem in the spatial + interval [-pi, pi] and temporal interval [0, 1], with unknown parameter + domain [-1, 1]. """ output_variables = ["u"] diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index f6ad08ffd..5269c31fd 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -39,8 +39,8 @@ def laplace_equation(input_, output_, params_): class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): """ - Implementation of the inverse 2-dimensional Poisson problem - in a square domain, with unknown parameter domain [-1, 1] x [-1, 1]. + Implementation of the inverse 2-dimensional Poisson problem in the square + domain [0, 1] x [0, 1], with unknown parameter domain [-1, 1] x [-1, 1]. """ output_variables = ["u"] diff --git a/pina/problem/zoo/poisson_2d_square.py b/pina/problem/zoo/poisson_2d_square.py index 3964de0e0..f4f98cd01 100644 --- a/pina/problem/zoo/poisson_2d_square.py +++ b/pina/problem/zoo/poisson_2d_square.py @@ -28,7 +28,8 @@ def laplace_equation(input_, output_): class Poisson2DSquareProblem(SpatialProblem): """ - Implementation of the 2-dimensional Poisson problem in a square domain. + Implementation of the 2-dimensional Poisson problem in the square domain + [0, 1] x [0, 1]. """ output_variables = ["u"] From c28228c62a6a53a91ff7d3f29a26584b4739e2f7 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 15:03:14 +0100 Subject: [PATCH 06/14] add allen_cahn --- pina/problem/zoo/allen_cahn.py | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 pina/problem/zoo/allen_cahn.py diff --git a/pina/problem/zoo/allen_cahn.py b/pina/problem/zoo/allen_cahn.py new file mode 100644 index 000000000..b3f568470 --- /dev/null +++ b/pina/problem/zoo/allen_cahn.py @@ -0,0 +1,58 @@ +"""Formulation of the Allen Cahn problem.""" + +import torch +from ... import Condition +from ...operator import grad, laplacian +from ...equation import Equation +from ...domain import CartesianDomain +from ...problem import SpatialProblem, TimeDependentProblem + + +def allen_cahn_equation(input_, output_): + """ + Implementation of the Allen Cahn equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the Allen Cahn equation. + :rtype: LabelTensor + """ + u_t = grad(output_, input_, components=["u"], d=["t"]) + u_xx = laplacian(output_, input_, components=["u"], d=["x"]) + return u_t - 0.0001 * u_xx + 5 * output_**3 - 5 * output_ + + +def initial_condition(input_, output_): + """ + Definition of the initial condition of the Allen Cahn problem. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the initial condition. + :rtype: LabelTensor + """ + x = input_.extract("x") + u_0 = x**2 * torch.cos(torch.pi * x) + return output_ - u_0 + + +class AllenCahn(TimeDependentProblem, SpatialProblem): + """ + Implementation of the Allen Cahn problem in the spatial interval + [-1, 1] and temporal interval [0, 1]. + Reference: https://arxiv.org/pdf/2307.00379. + """ + + output_variables = ["u"] + spatial_domain = CartesianDomain({"x": [-1, 1]}) + temporal_domain = CartesianDomain({"t": [0, 1]}) + + domains = { + "D": CartesianDomain({"x": [-1, 1], "t": [0, 1]}), + "t0": CartesianDomain({"x": [-1, 1], "t": 0.0}), + } + + conditions = { + "D": Condition(domain="D", equation=Equation(allen_cahn_equation)), + "t0": Condition(domain="t0", equation=Equation(initial_condition)), + } From 69bea7e6912b9d58b69f57d9b0369b1350fee0c6 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 15:05:34 +0100 Subject: [PATCH 07/14] fix import for allen cahn --- pina/problem/zoo/__init__.py | 2 ++ pina/problem/zoo/allen_cahn.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pina/problem/zoo/__init__.py b/pina/problem/zoo/__init__.py index 6b3964aff..1a6de2c2d 100644 --- a/pina/problem/zoo/__init__.py +++ b/pina/problem/zoo/__init__.py @@ -3,6 +3,7 @@ __all__ = [ "SupervisedProblem", "HelmholtzProblem", + "AllenCahnProblem", "Poisson2DSquareProblem", "DiffusionReactionProblem", "InversePoisson2DSquareProblem", @@ -11,6 +12,7 @@ from .supervised_problem import SupervisedProblem from .helmholtz import HelmholtzProblem +from .allen_cahn import AllenCahnProblem from .poisson_2d_square import Poisson2DSquareProblem from .diffusion_reaction import DiffusionReactionProblem from .inverse_poisson_2d_square import InversePoisson2DSquareProblem diff --git a/pina/problem/zoo/allen_cahn.py b/pina/problem/zoo/allen_cahn.py index b3f568470..77c1308e3 100644 --- a/pina/problem/zoo/allen_cahn.py +++ b/pina/problem/zoo/allen_cahn.py @@ -36,7 +36,7 @@ def initial_condition(input_, output_): return output_ - u_0 -class AllenCahn(TimeDependentProblem, SpatialProblem): +class AllenCahnProblem(TimeDependentProblem, SpatialProblem): """ Implementation of the Allen Cahn problem in the spatial interval [-1, 1] and temporal interval [0, 1]. From bb43dfcc9973420eb979d88e246ea6e4f9ea8874 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 15:44:12 +0100 Subject: [PATCH 08/14] add advection --- pina/problem/zoo/__init__.py | 2 + pina/problem/zoo/advection.py | 94 ++++++++++++++++++++++++++ pina/problem/zoo/supervised_problem.py | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 pina/problem/zoo/advection.py diff --git a/pina/problem/zoo/__init__.py b/pina/problem/zoo/__init__.py index 1a6de2c2d..3969d203b 100644 --- a/pina/problem/zoo/__init__.py +++ b/pina/problem/zoo/__init__.py @@ -4,6 +4,7 @@ "SupervisedProblem", "HelmholtzProblem", "AllenCahnProblem", + "AdvectionProblem", "Poisson2DSquareProblem", "DiffusionReactionProblem", "InversePoisson2DSquareProblem", @@ -13,6 +14,7 @@ from .supervised_problem import SupervisedProblem from .helmholtz import HelmholtzProblem from .allen_cahn import AllenCahnProblem +from .advection import AdvectionProblem from .poisson_2d_square import Poisson2DSquareProblem from .diffusion_reaction import DiffusionReactionProblem from .inverse_poisson_2d_square import InversePoisson2DSquareProblem diff --git a/pina/problem/zoo/advection.py b/pina/problem/zoo/advection.py new file mode 100644 index 000000000..59f44a4f8 --- /dev/null +++ b/pina/problem/zoo/advection.py @@ -0,0 +1,94 @@ +"""Formulation of the advection problem.""" + +import torch +from ... import Condition +from ...operator import grad +from ...equation import Equation +from ...domain import CartesianDomain +from ...problem import SpatialProblem, TimeDependentProblem + + +class AdvectionEquation(Equation): + """ + Implementation of the advection equation. + """ + + def __init__(self, c): + """ + Initialize the advection equation. + + :param float c: The advection velocity parameter. + """ + self.c = c + + def equation(input_, output_): + """ + Implementation of the advection equation. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the advection equation. + :rtype: LabelTensor + """ + u_x = grad(output_, input_, components=["u"], d=["x"]) + u_t = grad(output_, input_, components=["u"], d=["t"]) + return u_t + self.c * u_x + + super().__init__(equation) + + +def initial_condition(input_, output_): + """ + Implementation of the initial condition. + + :param LabelTensor input_: Input data of the problem. + :param LabelTensor output_: Output data of the problem. + :return: The residual of the initial condition. + :rtype: LabelTensor + """ + return output_ - torch.sin(input_.extract("x")) + + +class AdvectionProblem(SpatialProblem, TimeDependentProblem): + """ + Implementation of the advection problem in the spatial interval [0, 2*pi] + and temporal interval [0, 1]. + Reference: https://arxiv.org/pdf/2308.08468. + """ + + output_variables = ["u"] + spatial_domain = CartesianDomain({"x": [0, 2 * torch.pi]}) + temporal_domain = CartesianDomain({"t": [0, 1]}) + + domains = { + "D": CartesianDomain({"x": [0, 2 * torch.pi], "t": [0, 1]}), + "t0": CartesianDomain({"x": [0, 2 * torch.pi], "t": 0.0}), + } + + conditions = { + "t0": Condition(domain="t0", equation=Equation(initial_condition)), + } + + def __init__(self, c=1.0): + """ + Initialize the advection problem. + + :param float c: The advection velocity parameter. + """ + super().__init__() + self.c = c + self.conditions["D"] = Condition( + domain="D", equation=AdvectionEquation(self.c) + ) + + def solution(self, pts): + """ + Implementation of the analytical solution of the advection problem. + + :param LabelTensor pts: Points where the solution is evaluated. + :return: The analytical solution of the advection problem. + :rtype: LabelTensor + """ + sol = torch.sin(pts.extract("x") - self.c * pts.extract("t")) + sol.labels = self.output_variables + return sol diff --git a/pina/problem/zoo/supervised_problem.py b/pina/problem/zoo/supervised_problem.py index 8a414afe2..846f9ef2d 100644 --- a/pina/problem/zoo/supervised_problem.py +++ b/pina/problem/zoo/supervised_problem.py @@ -9,7 +9,7 @@ class SupervisedProblem(AbstractProblem): """ Definition of a supervised learning problem in PINA. - This class provides a simple way to define a supervised problem + This class provides a simple way to define a supervised problem using a single condition of type `InputTargetCondition`. :Example: From ac4b06a7366650b690919778909d7499417a208a Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 16:50:39 +0100 Subject: [PATCH 09/14] fixing loading error --- pina/problem/zoo/helmholtz.py | 2 +- pina/problem/zoo/inverse_diffusion_reaction.py | 2 +- pina/problem/zoo/inverse_poisson_2d_square.py | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pina/problem/zoo/helmholtz.py b/pina/problem/zoo/helmholtz.py index 59f0c5764..266349afc 100644 --- a/pina/problem/zoo/helmholtz.py +++ b/pina/problem/zoo/helmholtz.py @@ -5,7 +5,7 @@ from ...problem import SpatialProblem from ...equation import Equation, FixedValue from ...domain import CartesianDomain -from ...operators import laplacian +from ...operator import laplacian class HelmholtzEquation(Equation): diff --git a/pina/problem/zoo/inverse_diffusion_reaction.py b/pina/problem/zoo/inverse_diffusion_reaction.py index 4dfa882d8..519a161a2 100644 --- a/pina/problem/zoo/inverse_diffusion_reaction.py +++ b/pina/problem/zoo/inverse_diffusion_reaction.py @@ -83,7 +83,7 @@ def __init__(self): Initialization of the inverse diffusion-reaction problem. """ super().__init__() - pts = self.spatial_domain.sample(100) + pts = self.domains["D"].sample(100) self.conditions["data"] = Condition( input=pts, target=self.solution(pts) ) diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index 5269c31fd..e72f187ec 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -1,5 +1,6 @@ """Formulation of the inverse Poisson problem in a square domain.""" +import os import torch from ... import Condition from ...operator import laplacian @@ -26,14 +27,21 @@ def laplace_equation(input_, output_, params_): return delta_u - force_term -# Load data +# Absolute path to the data directory +data_dir = os.path.abspath( + os.path.join( + os.path.dirname(__file__), "../../../tutorials/tutorial7/data/" + ) +) + +# Load input data input_data = torch.load( - f="../../../tutorials/tutorial7/data/pts_0.5_0.5", weights_only=False + f=os.path.join(data_dir, "pts_0.5_0.5"), weights_only=False ).extract(["x", "y"]) +# Load output data output_data = torch.load( - f="../../../tutorials/tutorial7/data/pinn_solution_0.5_0.5", - weights_only=False, + f=os.path.join(data_dir, "pinn_solution_0.5_0.5"), weights_only=False ) From b393dd3d12be6e1bcd98b9d874c6c47007d039d8 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 17:22:48 +0100 Subject: [PATCH 10/14] reduce test time --- tests/test_solver/test_causal_pinn.py | 6 ++++-- tests/test_solver/test_competitive_pinn.py | 11 +++++++++-- tests/test_solver/test_gradient_pinn.py | 11 +++++++++-- tests/test_solver/test_pinn.py | 11 +++++++++-- tests/test_solver/test_rba_pinn.py | 11 +++++++++-- tests/test_solver/test_self_adaptive_pinn.py | 11 +++++++++-- 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/tests/test_solver/test_causal_pinn.py b/tests/test_solver/test_causal_pinn.py index 107502f8a..a4da56e8b 100644 --- a/tests/test_solver/test_causal_pinn.py +++ b/tests/test_solver/test_causal_pinn.py @@ -28,12 +28,11 @@ class DummySpatialProblem(SpatialProblem): spatial_domain = None -# define problems and model +# define problems problem = DiffusionReactionProblem() problem.discretise_domain(50) inverse_problem = InverseDiffusionReactionProblem() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -42,6 +41,9 @@ class DummySpatialProblem(SpatialProblem): output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) @pytest.mark.parametrize("eps", [100, 100.1]) diff --git a/tests/test_solver/test_competitive_pinn.py b/tests/test_solver/test_competitive_pinn.py index c5f8017a2..64fb28058 100644 --- a/tests/test_solver/test_competitive_pinn.py +++ b/tests/test_solver/test_competitive_pinn.py @@ -17,12 +17,16 @@ from torch._dynamo.eval_frame import OptimizedModule -# define problems and model +# define problems problem = Poisson() problem.discretise_domain(50) inverse_problem = InversePoisson() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + +# reduce the number of data points to speed up testing +data_condition = inverse_problem.conditions["data"] +data_condition.input = data_condition.input[:10] +data_condition.target = data_condition.target[:10] # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -31,6 +35,9 @@ output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) @pytest.mark.parametrize("discr", [None, model]) diff --git a/tests/test_solver/test_gradient_pinn.py b/tests/test_solver/test_gradient_pinn.py index c572036ea..31666db3d 100644 --- a/tests/test_solver/test_gradient_pinn.py +++ b/tests/test_solver/test_gradient_pinn.py @@ -28,12 +28,16 @@ class DummyTimeProblem(TimeDependentProblem): conditions = {} -# define problems and model +# define problems problem = Poisson() problem.discretise_domain(50) inverse_problem = InversePoisson() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + +# reduce the number of data points to speed up testing +data_condition = inverse_problem.conditions["data"] +data_condition.input = data_condition.input[:10] +data_condition.target = data_condition.target[:10] # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -42,6 +46,9 @@ class DummyTimeProblem(TimeDependentProblem): output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) def test_constructor(problem): diff --git a/tests/test_solver/test_pinn.py b/tests/test_solver/test_pinn.py index 98d14389e..97511cb14 100644 --- a/tests/test_solver/test_pinn.py +++ b/tests/test_solver/test_pinn.py @@ -17,12 +17,16 @@ from torch._dynamo.eval_frame import OptimizedModule -# define problems and model +# define problems problem = Poisson() problem.discretise_domain(50) inverse_problem = InversePoisson() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + +# reduce the number of data points to speed up testing +data_condition = inverse_problem.conditions["data"] +data_condition.input = data_condition.input[:10] +data_condition.target = data_condition.target[:10] # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -31,6 +35,9 @@ output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) def test_constructor(problem): diff --git a/tests/test_solver/test_rba_pinn.py b/tests/test_solver/test_rba_pinn.py index ba74eba91..f355aab02 100644 --- a/tests/test_solver/test_rba_pinn.py +++ b/tests/test_solver/test_rba_pinn.py @@ -16,12 +16,16 @@ ) from torch._dynamo.eval_frame import OptimizedModule -# define problems and model +# define problems problem = Poisson() problem.discretise_domain(50) inverse_problem = InversePoisson() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + +# reduce the number of data points to speed up testing +data_condition = inverse_problem.conditions["data"] +data_condition.input = data_condition.input[:10] +data_condition.target = data_condition.target[:10] # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -30,6 +34,9 @@ output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) @pytest.mark.parametrize("eta", [1, 0.001]) diff --git a/tests/test_solver/test_self_adaptive_pinn.py b/tests/test_solver/test_self_adaptive_pinn.py index b42472df5..48e3d9f8b 100644 --- a/tests/test_solver/test_self_adaptive_pinn.py +++ b/tests/test_solver/test_self_adaptive_pinn.py @@ -17,12 +17,16 @@ from torch._dynamo.eval_frame import OptimizedModule -# make the problem +# define problems problem = Poisson() problem.discretise_domain(50) inverse_problem = InversePoisson() inverse_problem.discretise_domain(50) -model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + +# reduce the number of data points to speed up testing +data_condition = inverse_problem.conditions["data"] +data_condition.input = data_condition.input[:10] +data_condition.target = data_condition.target[:10] # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -31,6 +35,9 @@ output_pts = LabelTensor(output_pts, problem.output_variables) problem.conditions["data"] = Condition(input=input_pts, target=output_pts) +# define model +model = FeedForward(len(problem.input_variables), len(problem.output_variables)) + @pytest.mark.parametrize("problem", [problem, inverse_problem]) @pytest.mark.parametrize("weight_fn", [torch.nn.Sigmoid(), torch.nn.Tanh()]) From 77ec0095e79331347105b05695386f3fd4db6d02 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 18:02:08 +0100 Subject: [PATCH 11/14] add tests and checks --- pina/problem/zoo/advection.py | 11 +++++++++-- pina/problem/zoo/allen_cahn.py | 2 +- pina/problem/zoo/helmholtz.py | 15 +++++++++++---- tests/test_problem_zoo/test_advection.py | 17 +++++++++++++++++ tests/test_problem_zoo/test_allen_cahn.py | 12 ++++++++++++ .../test_problem_zoo/test_diffusion_reaction.py | 12 ++++++++++++ tests/test_problem_zoo/test_helmholtz.py | 16 ++++++++++++++++ .../test_inverse_diffusion_reaction.py | 13 +++++++++++++ .../test_inverse_poisson_2d_square.py | 12 ++++++++++++ .../test_problem_zoo/test_poisson_2d_square.py | 8 +++++++- 10 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 tests/test_problem_zoo/test_advection.py create mode 100644 tests/test_problem_zoo/test_allen_cahn.py create mode 100644 tests/test_problem_zoo/test_diffusion_reaction.py create mode 100644 tests/test_problem_zoo/test_helmholtz.py create mode 100644 tests/test_problem_zoo/test_inverse_diffusion_reaction.py create mode 100644 tests/test_problem_zoo/test_inverse_poisson_2d_square.py diff --git a/pina/problem/zoo/advection.py b/pina/problem/zoo/advection.py index 59f44a4f8..65b31bd4f 100644 --- a/pina/problem/zoo/advection.py +++ b/pina/problem/zoo/advection.py @@ -5,6 +5,7 @@ from ...operator import grad from ...equation import Equation from ...domain import CartesianDomain +from ...utils import check_consistency from ...problem import SpatialProblem, TimeDependentProblem @@ -17,9 +18,11 @@ def __init__(self, c): """ Initialize the advection equation. - :param float c: The advection velocity parameter. + :param c: The advection velocity parameter. + :type c: float | int """ self.c = c + check_consistency(self.c, (float, int)) def equation(input_, output_): """ @@ -73,10 +76,14 @@ def __init__(self, c=1.0): """ Initialize the advection problem. - :param float c: The advection velocity parameter. + :param c: The advection velocity parameter. + :type c: float | int """ super().__init__() + self.c = c + check_consistency(self.c, (float, int)) + self.conditions["D"] = Condition( domain="D", equation=AdvectionEquation(self.c) ) diff --git a/pina/problem/zoo/allen_cahn.py b/pina/problem/zoo/allen_cahn.py index 77c1308e3..3a9295486 100644 --- a/pina/problem/zoo/allen_cahn.py +++ b/pina/problem/zoo/allen_cahn.py @@ -2,9 +2,9 @@ import torch from ... import Condition -from ...operator import grad, laplacian from ...equation import Equation from ...domain import CartesianDomain +from ...operator import grad, laplacian from ...problem import SpatialProblem, TimeDependentProblem diff --git a/pina/problem/zoo/helmholtz.py b/pina/problem/zoo/helmholtz.py index 266349afc..d8d5c8a9b 100644 --- a/pina/problem/zoo/helmholtz.py +++ b/pina/problem/zoo/helmholtz.py @@ -2,10 +2,11 @@ import torch from ... import Condition +from ...operator import laplacian +from ...domain import CartesianDomain from ...problem import SpatialProblem +from ...utils import check_consistency from ...equation import Equation, FixedValue -from ...domain import CartesianDomain -from ...operator import laplacian class HelmholtzEquation(Equation): @@ -17,9 +18,11 @@ def __init__(self, alpha): """ Initialize the Helmholtz equation. - :param float alpha: Parameter of the forcing term. + :param alpha: Parameter of the forcing term. + :type alpha: float | int """ self.alpha = alpha + check_consistency(alpha, (int, float)) def equation(input_, output_): """ @@ -69,10 +72,14 @@ def __init__(self, alpha=3.0): """ Initialize the Helmholtz problem. - :param float alpha: Parameter of the forcing term. + :param alpha: Parameter of the forcing term. + :type alpha: float | int """ super().__init__() + self.alpha = alpha + check_consistency(alpha, (int, float)) + self.conditions["D"] = Condition( domain="D", equation=HelmholtzEquation(self.alpha) ) diff --git a/tests/test_problem_zoo/test_advection.py b/tests/test_problem_zoo/test_advection.py new file mode 100644 index 000000000..ac91452ce --- /dev/null +++ b/tests/test_problem_zoo/test_advection.py @@ -0,0 +1,17 @@ +import pytest +from pina.problem.zoo import AdvectionProblem +from pina.problem import SpatialProblem, TimeDependentProblem + + +@pytest.mark.parametrize("c", [1.5, 3]) +def test_constructor(c): + problem = AdvectionProblem(c=c) + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, SpatialProblem) + assert isinstance(problem, TimeDependentProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) + + with pytest.raises(TypeError): + AdvectionProblem(c="a") diff --git a/tests/test_problem_zoo/test_allen_cahn.py b/tests/test_problem_zoo/test_allen_cahn.py new file mode 100644 index 000000000..851348077 --- /dev/null +++ b/tests/test_problem_zoo/test_allen_cahn.py @@ -0,0 +1,12 @@ +from pina.problem.zoo import AllenCahnProblem +from pina.problem import SpatialProblem, TimeDependentProblem + + +def test_constructor(): + problem = AllenCahnProblem() + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, SpatialProblem) + assert isinstance(problem, TimeDependentProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) diff --git a/tests/test_problem_zoo/test_diffusion_reaction.py b/tests/test_problem_zoo/test_diffusion_reaction.py new file mode 100644 index 000000000..51709b29c --- /dev/null +++ b/tests/test_problem_zoo/test_diffusion_reaction.py @@ -0,0 +1,12 @@ +from pina.problem.zoo import DiffusionReactionProblem +from pina.problem import TimeDependentProblem, SpatialProblem + + +def test_constructor(): + problem = DiffusionReactionProblem() + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, TimeDependentProblem) + assert isinstance(problem, SpatialProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) diff --git a/tests/test_problem_zoo/test_helmholtz.py b/tests/test_problem_zoo/test_helmholtz.py new file mode 100644 index 000000000..71e81ec17 --- /dev/null +++ b/tests/test_problem_zoo/test_helmholtz.py @@ -0,0 +1,16 @@ +import pytest +from pina.problem.zoo import HelmholtzProblem +from pina.problem import SpatialProblem + + +@pytest.mark.parametrize("alpha", [1.5, 3]) +def test_constructor(alpha): + problem = HelmholtzProblem(alpha=alpha) + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, SpatialProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) + + with pytest.raises(TypeError): + HelmholtzProblem(alpha="a") diff --git a/tests/test_problem_zoo/test_inverse_diffusion_reaction.py b/tests/test_problem_zoo/test_inverse_diffusion_reaction.py new file mode 100644 index 000000000..43d148338 --- /dev/null +++ b/tests/test_problem_zoo/test_inverse_diffusion_reaction.py @@ -0,0 +1,13 @@ +from pina.problem.zoo import InverseDiffusionReactionProblem +from pina.problem import InverseProblem, SpatialProblem, TimeDependentProblem + + +def test_constructor(): + problem = InverseDiffusionReactionProblem() + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, InverseProblem) + assert isinstance(problem, SpatialProblem) + assert isinstance(problem, TimeDependentProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) diff --git a/tests/test_problem_zoo/test_inverse_poisson_2d_square.py b/tests/test_problem_zoo/test_inverse_poisson_2d_square.py new file mode 100644 index 000000000..20a60e636 --- /dev/null +++ b/tests/test_problem_zoo/test_inverse_poisson_2d_square.py @@ -0,0 +1,12 @@ +from pina.problem.zoo import InversePoisson2DSquareProblem +from pina.problem import InverseProblem, SpatialProblem + + +def test_constructor(): + problem = InversePoisson2DSquareProblem() + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, InverseProblem) + assert isinstance(problem, SpatialProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) diff --git a/tests/test_problem_zoo/test_poisson_2d_square.py b/tests/test_problem_zoo/test_poisson_2d_square.py index 272eb8c5a..ed7be0425 100644 --- a/tests/test_problem_zoo/test_poisson_2d_square.py +++ b/tests/test_problem_zoo/test_poisson_2d_square.py @@ -1,5 +1,11 @@ from pina.problem.zoo import Poisson2DSquareProblem +from pina.problem import SpatialProblem def test_constructor(): - Poisson2DSquareProblem() + problem = Poisson2DSquareProblem() + problem.discretise_domain(n=10, mode="random", domains="all") + assert problem.are_all_domains_discretised + assert isinstance(problem, SpatialProblem) + assert hasattr(problem, "conditions") + assert isinstance(problem.conditions, dict) From 36279cf7f4ae6fc75dd61f4c6868559aea4815a6 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 18:12:21 +0100 Subject: [PATCH 12/14] fix bugged tests --- tests/test_problem_zoo/test_advection.py | 3 ++- tests/test_problem_zoo/test_helmholtz.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_problem_zoo/test_advection.py b/tests/test_problem_zoo/test_advection.py index ac91452ce..db8505cb8 100644 --- a/tests/test_problem_zoo/test_advection.py +++ b/tests/test_problem_zoo/test_advection.py @@ -5,6 +5,7 @@ @pytest.mark.parametrize("c", [1.5, 3]) def test_constructor(c): + print(f"Testing with c = {c} (type: {type(c)})") problem = AdvectionProblem(c=c) problem.discretise_domain(n=10, mode="random", domains="all") assert problem.are_all_domains_discretised @@ -13,5 +14,5 @@ def test_constructor(c): assert hasattr(problem, "conditions") assert isinstance(problem.conditions, dict) - with pytest.raises(TypeError): + with pytest.raises(ValueError): AdvectionProblem(c="a") diff --git a/tests/test_problem_zoo/test_helmholtz.py b/tests/test_problem_zoo/test_helmholtz.py index 71e81ec17..ad8618a06 100644 --- a/tests/test_problem_zoo/test_helmholtz.py +++ b/tests/test_problem_zoo/test_helmholtz.py @@ -12,5 +12,5 @@ def test_constructor(alpha): assert hasattr(problem, "conditions") assert isinstance(problem.conditions, dict) - with pytest.raises(TypeError): + with pytest.raises(ValueError): HelmholtzProblem(alpha="a") From 0e46fc5f82b15124266e3d6bdf4a8a6b10e6ba68 Mon Sep 17 00:00:00 2001 From: giovanni Date: Tue, 11 Mar 2025 18:34:35 +0100 Subject: [PATCH 13/14] remove inverse diffusion-reaction --- pina/problem/zoo/__init__.py | 2 - .../problem/zoo/inverse_diffusion_reaction.py | 110 ------------------ .../test_inverse_diffusion_reaction.py | 13 --- tests/test_solver/test_causal_pinn.py | 17 +-- 4 files changed, 6 insertions(+), 136 deletions(-) delete mode 100644 pina/problem/zoo/inverse_diffusion_reaction.py delete mode 100644 tests/test_problem_zoo/test_inverse_diffusion_reaction.py diff --git a/pina/problem/zoo/__init__.py b/pina/problem/zoo/__init__.py index 3969d203b..6e3d58e52 100644 --- a/pina/problem/zoo/__init__.py +++ b/pina/problem/zoo/__init__.py @@ -8,7 +8,6 @@ "Poisson2DSquareProblem", "DiffusionReactionProblem", "InversePoisson2DSquareProblem", - "InverseDiffusionReactionProblem", ] from .supervised_problem import SupervisedProblem @@ -18,4 +17,3 @@ from .poisson_2d_square import Poisson2DSquareProblem from .diffusion_reaction import DiffusionReactionProblem from .inverse_poisson_2d_square import InversePoisson2DSquareProblem -from .inverse_diffusion_reaction import InverseDiffusionReactionProblem diff --git a/pina/problem/zoo/inverse_diffusion_reaction.py b/pina/problem/zoo/inverse_diffusion_reaction.py deleted file mode 100644 index 519a161a2..000000000 --- a/pina/problem/zoo/inverse_diffusion_reaction.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Formulation of the inverse diffusion-reaction problem.""" - -import torch -from ... import Condition -from ...domain import CartesianDomain -from ...operator import grad, laplacian -from ...equation import Equation, FixedValue -from ...problem import SpatialProblem, TimeDependentProblem, InverseProblem - - -def diffusion_reaction(input_, output_): - """ - Implementation of the diffusion-reaction equation. - - :param LabelTensor input_: Input data of the problem. - :param LabelTensor output_: Output data of the problem. - :return: The residual of the diffusion-reaction equation. - :rtype: LabelTensor - """ - x = input_.extract("x") - t = input_.extract("t") - u_t = grad(output_, input_, components=["u"], d=["t"]) - u_xx = laplacian(output_, input_, components=["u"], d=["x"]) - r = torch.exp(-t) * ( - 1.5 * torch.sin(2 * x) - + (8 / 3) * torch.sin(3 * x) - + (15 / 4) * torch.sin(4 * x) - + (63 / 8) * torch.sin(8 * x) - ) - return u_t - u_xx - r - - -def initial_condition(input_, output_): - """ - Definition of the initial condition of the diffusion-reaction problem. - - :param LabelTensor input_: Input data of the problem. - :param LabelTensor output_: Output data of the problem. - :return: The residual of the initial condition. - :rtype: LabelTensor - """ - x = input_.extract("x") - u_0 = ( - torch.sin(x) - + (1 / 2) * torch.sin(2 * x) - + (1 / 3) * torch.sin(3 * x) - + (1 / 4) * torch.sin(4 * x) - + (1 / 8) * torch.sin(8 * x) - ) - return output_ - u_0 - - -class InverseDiffusionReactionProblem( - TimeDependentProblem, SpatialProblem, InverseProblem -): - """ - Implementation of the diffusion-reaction inverse problem in the spatial - interval [-pi, pi] and temporal interval [0, 1], with unknown parameter - domain [-1, 1]. - """ - - output_variables = ["u"] - spatial_domain = CartesianDomain({"x": [-torch.pi, torch.pi]}) - temporal_domain = CartesianDomain({"t": [0, 1]}) - unknown_parameter_domain = CartesianDomain({"mu": [-1, 1]}) - - domains = { - "D": CartesianDomain({"x": [-torch.pi, torch.pi], "t": [0, 1]}), - "g1": CartesianDomain({"x": -torch.pi, "t": [0, 1]}), - "g2": CartesianDomain({"x": torch.pi, "t": [0, 1]}), - "t0": CartesianDomain({"x": [-torch.pi, torch.pi], "t": 0.0}), - } - - conditions = { - "D": Condition(domain="D", equation=Equation(diffusion_reaction)), - "g1": Condition(domain="g1", equation=FixedValue(0.0)), - "g2": Condition(domain="g2", equation=FixedValue(0.0)), - "t0": Condition(domain="t0", equation=Equation(initial_condition)), - } - - def __init__(self): - """ - Initialization of the inverse diffusion-reaction problem. - """ - super().__init__() - pts = self.domains["D"].sample(100) - self.conditions["data"] = Condition( - input=pts, target=self.solution(pts) - ) - - def solution(self, pts): - """ - Implementation of the analytical solution of the diffusion-reaction - problem. - - :param LabelTensor pts: Points where the solution is evaluated. - :return: The analytical solution of the diffusion-reaction problem. - :rtype: LabelTensor - """ - t = pts.extract("t") - x = pts.extract("x") - sol = torch.exp(-t) * ( - torch.sin(x) - + (1 / 2) * torch.sin(2 * x) - + (1 / 3) * torch.sin(3 * x) - + (1 / 4) * torch.sin(4 * x) - + (1 / 8) * torch.sin(8 * x) - ) - sol.labels = self.output_variables - return sol diff --git a/tests/test_problem_zoo/test_inverse_diffusion_reaction.py b/tests/test_problem_zoo/test_inverse_diffusion_reaction.py deleted file mode 100644 index 43d148338..000000000 --- a/tests/test_problem_zoo/test_inverse_diffusion_reaction.py +++ /dev/null @@ -1,13 +0,0 @@ -from pina.problem.zoo import InverseDiffusionReactionProblem -from pina.problem import InverseProblem, SpatialProblem, TimeDependentProblem - - -def test_constructor(): - problem = InverseDiffusionReactionProblem() - problem.discretise_domain(n=10, mode="random", domains="all") - assert problem.are_all_domains_discretised - assert isinstance(problem, InverseProblem) - assert isinstance(problem, SpatialProblem) - assert isinstance(problem, TimeDependentProblem) - assert hasattr(problem, "conditions") - assert isinstance(problem.conditions, dict) diff --git a/tests/test_solver/test_causal_pinn.py b/tests/test_solver/test_causal_pinn.py index a4da56e8b..4e72732d3 100644 --- a/tests/test_solver/test_causal_pinn.py +++ b/tests/test_solver/test_causal_pinn.py @@ -6,10 +6,7 @@ from pina.solver import CausalPINN from pina.trainer import Trainer from pina.model import FeedForward -from pina.problem.zoo import ( - DiffusionReactionProblem, - InverseDiffusionReactionProblem, -) +from pina.problem.zoo import DiffusionReactionProblem from pina.condition import ( InputTargetCondition, InputEquationCondition, @@ -31,8 +28,6 @@ class DummySpatialProblem(SpatialProblem): # define problems problem = DiffusionReactionProblem() problem.discretise_domain(50) -inverse_problem = InverseDiffusionReactionProblem() -inverse_problem.discretise_domain(50) # add input-output condition to test supervised learning input_pts = torch.rand(50, len(problem.input_variables)) @@ -45,7 +40,7 @@ class DummySpatialProblem(SpatialProblem): model = FeedForward(len(problem.input_variables), len(problem.output_variables)) -@pytest.mark.parametrize("problem", [problem, inverse_problem]) +@pytest.mark.parametrize("problem", [problem]) @pytest.mark.parametrize("eps", [100, 100.1]) def test_constructor(problem, eps): with pytest.raises(ValueError): @@ -59,7 +54,7 @@ def test_constructor(problem, eps): ) -@pytest.mark.parametrize("problem", [problem, inverse_problem]) +@pytest.mark.parametrize("problem", [problem]) @pytest.mark.parametrize("batch_size", [None, 1, 5, 20]) @pytest.mark.parametrize("compile", [True, False]) def test_solver_train(problem, batch_size, compile): @@ -79,7 +74,7 @@ def test_solver_train(problem, batch_size, compile): assert isinstance(solver.model, OptimizedModule) -@pytest.mark.parametrize("problem", [problem, inverse_problem]) +@pytest.mark.parametrize("problem", [problem]) @pytest.mark.parametrize("batch_size", [None, 1, 5, 20]) @pytest.mark.parametrize("compile", [True, False]) def test_solver_validation(problem, batch_size, compile): @@ -99,7 +94,7 @@ def test_solver_validation(problem, batch_size, compile): assert isinstance(solver.model, OptimizedModule) -@pytest.mark.parametrize("problem", [problem, inverse_problem]) +@pytest.mark.parametrize("problem", [problem]) @pytest.mark.parametrize("batch_size", [None, 1, 5, 20]) @pytest.mark.parametrize("compile", [True, False]) def test_solver_test(problem, batch_size, compile): @@ -119,7 +114,7 @@ def test_solver_test(problem, batch_size, compile): assert isinstance(solver.model, OptimizedModule) -@pytest.mark.parametrize("problem", [problem, inverse_problem]) +@pytest.mark.parametrize("problem", [problem]) def test_train_load_restore(problem): dir = "tests/test_solver/tmp" problem = problem From cd93a138372d0da0f384427b010e0e345a131e99 Mon Sep 17 00:00:00 2001 From: Dario Coscia Date: Wed, 12 Mar 2025 10:39:15 +0100 Subject: [PATCH 14/14] update doc + formatting --- pina/problem/zoo/advection.py | 14 ++++++++++---- pina/problem/zoo/allen_cahn.py | 14 +++++++++++--- pina/problem/zoo/diffusion_reaction.py | 10 +++++++--- pina/problem/zoo/helmholtz.py | 9 +++++++-- pina/problem/zoo/inverse_poisson_2d_square.py | 5 +++-- pina/problem/zoo/poisson_2d_square.py | 4 ++-- pina/problem/zoo/supervised_problem.py | 7 ++++--- tests/test_problem_zoo/test_advection.py | 2 +- 8 files changed, 45 insertions(+), 20 deletions(-) diff --git a/pina/problem/zoo/advection.py b/pina/problem/zoo/advection.py index 65b31bd4f..32c6afe78 100644 --- a/pina/problem/zoo/advection.py +++ b/pina/problem/zoo/advection.py @@ -53,10 +53,16 @@ def initial_condition(input_, output_): class AdvectionProblem(SpatialProblem, TimeDependentProblem): - """ - Implementation of the advection problem in the spatial interval [0, 2*pi] - and temporal interval [0, 1]. - Reference: https://arxiv.org/pdf/2308.08468. + r""" + Implementation of the advection problem in the spatial interval + :math:`[0, 2 \pi]` and temporal interval :math:`[0, 1]`. + + .. seealso:: + + **Original reference**: Wang, Sifan, et al. *An expert's guide to + training physics-informed neural networks*. + arXiv preprint arXiv:2308.08468 (2023). + DOI: `arXiv:2308.08468 `_. """ output_variables = ["u"] diff --git a/pina/problem/zoo/allen_cahn.py b/pina/problem/zoo/allen_cahn.py index 3a9295486..e4a9c4c41 100644 --- a/pina/problem/zoo/allen_cahn.py +++ b/pina/problem/zoo/allen_cahn.py @@ -37,10 +37,18 @@ def initial_condition(input_, output_): class AllenCahnProblem(TimeDependentProblem, SpatialProblem): - """ + r""" Implementation of the Allen Cahn problem in the spatial interval - [-1, 1] and temporal interval [0, 1]. - Reference: https://arxiv.org/pdf/2307.00379. + :math:`[-1, 1]` and temporal interval :math:`[0, 1]`. + + .. seealso:: + **Original reference**: Sokratis J. Anagnostopoulos, Juan D. Toscano, + Nikolaos Stergiopulos, and George E. Karniadakis. + *Residual-based attention and connection to information + bottleneck theory in PINNs*. + Computer Methods in Applied Mechanics and Engineering 421 (2024): 116805 + DOI: `10.1016/ + j.cma.2024.116805 `_. """ output_variables = ["u"] diff --git a/pina/problem/zoo/diffusion_reaction.py b/pina/problem/zoo/diffusion_reaction.py index 31296e369..6d6485dda 100644 --- a/pina/problem/zoo/diffusion_reaction.py +++ b/pina/problem/zoo/diffusion_reaction.py @@ -51,10 +51,14 @@ def initial_condition(input_, output_): class DiffusionReactionProblem(TimeDependentProblem, SpatialProblem): - """ + r""" Implementation of the diffusion-reaction problem in the spatial interval - [-pi, pi] and temporal interval [0, 1]. - Reference: https://www.arxiv.org/pdf/2502.04917. + :math:`[-\pi, \pi]` and temporal interval :math:`[0, 1]`. + + .. seealso:: + **Original reference**: Si, Chenhao, et al. *Complex Physics-Informed + Neural Network.* arXiv preprint arXiv:2502.04917 (2025). + DOI: `arXiv:2502.04917 `_. """ output_variables = ["u"] diff --git a/pina/problem/zoo/helmholtz.py b/pina/problem/zoo/helmholtz.py index d8d5c8a9b..8564d8200 100644 --- a/pina/problem/zoo/helmholtz.py +++ b/pina/problem/zoo/helmholtz.py @@ -45,9 +45,14 @@ def equation(input_, output_): class HelmholtzProblem(SpatialProblem): - """ + r""" Implementation of the Helmholtz problem in the square domain - [-1, 1] x [-1, 1]. Reference: https://www.arxiv.org/pdf/2502.04917. + :math:`[-1, 1] \times [-1, 1]`. + + .. seealso:: + **Original reference**: Si, Chenhao, et al. *Complex Physics-Informed + Neural Network.* arXiv preprint arXiv:2502.04917 (2025). + DOI: `arXiv:2502.04917 `_. """ output_variables = ["u"] diff --git a/pina/problem/zoo/inverse_poisson_2d_square.py b/pina/problem/zoo/inverse_poisson_2d_square.py index e72f187ec..16b4ec1d9 100644 --- a/pina/problem/zoo/inverse_poisson_2d_square.py +++ b/pina/problem/zoo/inverse_poisson_2d_square.py @@ -46,9 +46,10 @@ def laplace_equation(input_, output_, params_): class InversePoisson2DSquareProblem(SpatialProblem, InverseProblem): - """ + r""" Implementation of the inverse 2-dimensional Poisson problem in the square - domain [0, 1] x [0, 1], with unknown parameter domain [-1, 1] x [-1, 1]. + domain :math:`[0, 1] \times [0, 1]`, + with unknown parameter domain :math:`[-1, 1] \times [-1, 1]`. """ output_variables = ["u"] diff --git a/pina/problem/zoo/poisson_2d_square.py b/pina/problem/zoo/poisson_2d_square.py index f4f98cd01..fef0b2e61 100644 --- a/pina/problem/zoo/poisson_2d_square.py +++ b/pina/problem/zoo/poisson_2d_square.py @@ -27,9 +27,9 @@ def laplace_equation(input_, output_): class Poisson2DSquareProblem(SpatialProblem): - """ + r""" Implementation of the 2-dimensional Poisson problem in the square domain - [0, 1] x [0, 1]. + :math:`[0, 1] \times [0, 1]`. """ output_variables = ["u"] diff --git a/pina/problem/zoo/supervised_problem.py b/pina/problem/zoo/supervised_problem.py index 846f9ef2d..7e39a502a 100644 --- a/pina/problem/zoo/supervised_problem.py +++ b/pina/problem/zoo/supervised_problem.py @@ -10,7 +10,8 @@ class SupervisedProblem(AbstractProblem): Definition of a supervised learning problem in PINA. This class provides a simple way to define a supervised problem - using a single condition of type `InputTargetCondition`. + using a single condition of type + :class:`~pina.condition.input_target_condition.InputTargetCondition`. :Example: >>> import torch @@ -27,8 +28,8 @@ def __init__(self, input_, output_): Initialize the SupervisedProblem class. :param input_: Input data of the problem. - :type input_: torch.Tensor | Graph - :param torch.Tensor output_: Output data of the problem. + :param output_: Output data of the problem. + :type output_: torch.Tensor | Graph """ if isinstance(input_, Graph): input_ = input_.data diff --git a/tests/test_problem_zoo/test_advection.py b/tests/test_problem_zoo/test_advection.py index db8505cb8..4cfc27cd0 100644 --- a/tests/test_problem_zoo/test_advection.py +++ b/tests/test_problem_zoo/test_advection.py @@ -5,7 +5,7 @@ @pytest.mark.parametrize("c", [1.5, 3]) def test_constructor(c): - print(f"Testing with c = {c} (type: {type(c)})") + print(f"Testing with c = {c} (type: {type(c)})") problem = AdvectionProblem(c=c) problem.discretise_domain(n=10, mode="random", domains="all") assert problem.are_all_domains_discretised