290 lines
12 KiB
Plaintext
Vendored
290 lines
12 KiB
Plaintext
Vendored
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Tutorial: Introduction to PINA `Equation` class\n",
|
|
"\n",
|
|
"[](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial12/tutorial.ipynb)\n",
|
|
"\n",
|
|
"\n",
|
|
"In this tutorial, we will explore how to use the `Equation` class in **PINA**. We will focus on how to leverage this class, along with its inherited subclasses, to enforce residual minimization in **Physics-Informed Neural Networks (PINNs)**.\n",
|
|
"\n",
|
|
"By the end of this guide, you'll understand how to integrate physical laws and constraints directly into your model training, ensuring that the solution adheres to the underlying differential equations.\n",
|
|
"\n",
|
|
"\n",
|
|
"## Example: The Burgers 1D equation\n",
|
|
"We will start implementing the viscous Burgers 1D problem Class, described as follows:\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
"\\begin{cases}\n",
|
|
"\\frac{\\partial u}{\\partial t} + u \\frac{\\partial u}{\\partial x} &= \\nu \\frac{\\partial^2 u}{ \\partial x^2}, \\quad x\\in(0,1), \\quad t>0\\\\\n",
|
|
"u(x,0) &= -\\sin (\\pi x), \\quad x\\in(0,1)\\\\\n",
|
|
"u(x,t) &= 0, \\quad x = \\pm 1, \\quad t>0\\\\\n",
|
|
"\\end{cases}\n",
|
|
"\\end{equation}\n",
|
|
"$$\n",
|
|
"\n",
|
|
"where we set $ \\nu = \\frac{0.01}{\\pi}$.\n",
|
|
"\n",
|
|
"In the class that models this problem we will see in action the `Equation` class and one of its inherited classes, the `FixedValue` class."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"## routine needed to run the notebook on Google Colab\n",
|
|
"try:\n",
|
|
" import google.colab\n",
|
|
"\n",
|
|
" IN_COLAB = True\n",
|
|
"except:\n",
|
|
" IN_COLAB = False\n",
|
|
"if IN_COLAB:\n",
|
|
" !pip install \"pina-mathlab[tutorial]\"\n",
|
|
"\n",
|
|
"import torch\n",
|
|
"\n",
|
|
"# useful imports\n",
|
|
"from pina import Condition\n",
|
|
"from pina.problem import SpatialProblem, TimeDependentProblem\n",
|
|
"from pina.equation import Equation, FixedValue\n",
|
|
"from pina.domain import CartesianDomain\n",
|
|
"from pina.operator import grad, fast_grad, laplacian"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let's begin by defining the Burgers equation and its initial condition as Python functions. These functions will take the model's `input` (spatial and temporal coordinates) and `output` (predicted solution) as arguments. The goal is to compute the residuals for the Burgers equation, which we will minimize during training."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# define the burger equation\n",
|
|
"def burger_equation(input_, output_):\n",
|
|
" du = fast_grad(output_, input_, components=[\"u\"], d=[\"x\"])\n",
|
|
" ddu = grad(du, input_, components=[\"dudx\"])\n",
|
|
" return (\n",
|
|
" du.extract([\"dudt\"])\n",
|
|
" + output_.extract([\"u\"]) * du.extract([\"dudx\"])\n",
|
|
" - (0.01 / torch.pi) * ddu.extract([\"ddudxdx\"])\n",
|
|
" )\n",
|
|
"\n",
|
|
"\n",
|
|
"# define initial condition\n",
|
|
"def initial_condition(input_, output_):\n",
|
|
" u_expected = -torch.sin(torch.pi * input_.extract([\"x\"]))\n",
|
|
" return output_.extract([\"u\"]) - u_expected"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Above we use the `grad` operator from `pina.operator` to compute the gradient. In PINA each differential operator takes the following inputs:\n",
|
|
"- `output_`: A tensor on which the operator is applied.\n",
|
|
"- `input_`: A tensor with respect to which the operator is computed.\n",
|
|
"- `components`: The names of the output variables for which the operator is evaluated.\n",
|
|
"- `d`: The names of the variables with respect to which the operator is computed.\n",
|
|
"\n",
|
|
"Each differential operator has its **fast** version, which performs no internal checks on input and output tensors. For these methods, the user is always required to specify both ``components`` and ``d`` as lists of strings.\n",
|
|
"\n",
|
|
"Let's define now the problem!\n",
|
|
"\n",
|
|
"> **👉 Do you want to learn more on Problems? Check the dedicated [tutorial](https://mathlab.github.io/PINA/tutorial16/tutorial.html) to learn how to build a Problem from scratch.**"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Burgers1D(TimeDependentProblem, SpatialProblem):\n",
|
|
"\n",
|
|
" # assign output/ spatial and temporal variables\n",
|
|
" output_variables = [\"u\"]\n",
|
|
" spatial_domain = CartesianDomain({\"x\": [-1, 1]})\n",
|
|
" temporal_domain = CartesianDomain({\"t\": [0, 1]})\n",
|
|
"\n",
|
|
" domains = {\n",
|
|
" \"bound_cond1\": CartesianDomain({\"x\": -1, \"t\": [0, 1]}),\n",
|
|
" \"bound_cond2\": CartesianDomain({\"x\": 1, \"t\": [0, 1]}),\n",
|
|
" \"time_cond\": CartesianDomain({\"x\": [-1, 1], \"t\": 0}),\n",
|
|
" \"phys_cond\": CartesianDomain({\"x\": [-1, 1], \"t\": [0, 1]}),\n",
|
|
" }\n",
|
|
" # problem condition statement\n",
|
|
" conditions = {\n",
|
|
" \"bound_cond1\": Condition(\n",
|
|
" domain=\"bound_cond1\", equation=FixedValue(0.0)\n",
|
|
" ),\n",
|
|
" \"bound_cond2\": Condition(\n",
|
|
" domain=\"bound_cond2\", equation=FixedValue(0.0)\n",
|
|
" ),\n",
|
|
" \"time_cond\": Condition(\n",
|
|
" domain=\"time_cond\", equation=Equation(initial_condition)\n",
|
|
" ),\n",
|
|
" \"phys_cond\": Condition(\n",
|
|
" domain=\"phys_cond\", equation=Equation(burger_equation)\n",
|
|
" ),\n",
|
|
" }"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The `Equation` class takes as input a function (in this case it happens twice, with `initial_condition` and `burger_equation`) which computes a residual of an equation, such as a PDE. In a problem class such as the one above, the `Equation` class with such a given input is passed as a parameter in the specified `Condition`. \n",
|
|
"\n",
|
|
"The `FixedValue` class takes as input a value of the same dimensions as the output functions. This class can be used to enforce a fixed value for a specific condition, such as Dirichlet boundary conditions, as demonstrated in our example.\n",
|
|
"\n",
|
|
"Once the equations are set as above in the problem conditions, the PINN solver will aim to minimize the residuals described in each equation during the training phase. \n",
|
|
"\n",
|
|
"### Available classes of equations:\n",
|
|
"- `FixedGradient` and `FixedFlux`: These work analogously to the `FixedValue` class, where we can enforce a constant value on the gradient or the divergence of the solution, respectively.\n",
|
|
"- `Laplace`: This class can be used to enforce that the Laplacian of the solution is zero.\n",
|
|
"- `SystemEquation`: This class allows you to enforce multiple conditions on the same subdomain by passing a list of residual equations defined in the problem.\n",
|
|
"\n",
|
|
"## Defining a new Equation class\n",
|
|
"`Equation` classes can also be inherited to define a new class. For example, we can define a new class `Burgers1D` to represent the Burgers equation. During the class call, we can pass the viscosity parameter $\\nu$:\n",
|
|
"\n",
|
|
"```python\n",
|
|
"class Burgers1D(Equation):\n",
|
|
" def __init__(self, nu):\n",
|
|
" self.nu = nu\n",
|
|
"\n",
|
|
" def equation(self, input_, output_):\n",
|
|
" ...\n",
|
|
"```\n",
|
|
"In this case, the `Burgers1D` class will inherit from the `Equation` class and compute the residual of the Burgers equation. The viscosity parameter $\\nu$ is passed when instantiating the class and used in the residual calculation. Let's see it in more details:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Burgers1DEquation(Equation):\n",
|
|
"\n",
|
|
" def __init__(self, nu=0.0):\n",
|
|
" \"\"\"\n",
|
|
" Burgers1D class. This class can be\n",
|
|
" used to enforce the solution u to solve the viscous Burgers 1D Equation.\n",
|
|
"\n",
|
|
" :param torch.float32 nu: the viscosity coefficient. Default value is set to 0.\n",
|
|
" \"\"\"\n",
|
|
" self.nu = nu\n",
|
|
"\n",
|
|
" def equation(input_, output_):\n",
|
|
" return (\n",
|
|
" grad(output_, input_, d=\"t\")\n",
|
|
" + output_ * grad(output_, input_, d=\"x\")\n",
|
|
" - self.nu * laplacian(output_, input_, d=\"x\")\n",
|
|
" )\n",
|
|
"\n",
|
|
" super().__init__(equation)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we can just pass the above class as input for the last condition, setting $\\nu= \\frac{0.01}{\\pi}$:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Burgers1D(TimeDependentProblem, SpatialProblem):\n",
|
|
"\n",
|
|
" # define initial condition\n",
|
|
" def initial_condition(input_, output_):\n",
|
|
" u_expected = -torch.sin(torch.pi * input_.extract([\"x\"]))\n",
|
|
" return output_.extract([\"u\"]) - u_expected\n",
|
|
"\n",
|
|
" # assign output/ spatial and temporal variables\n",
|
|
" output_variables = [\"u\"]\n",
|
|
" spatial_domain = CartesianDomain({\"x\": [-1, 1]})\n",
|
|
" temporal_domain = CartesianDomain({\"t\": [0, 1]})\n",
|
|
"\n",
|
|
" domains = {\n",
|
|
" \"bound_cond1\": CartesianDomain({\"x\": -1, \"t\": [0, 1]}),\n",
|
|
" \"bound_cond2\": CartesianDomain({\"x\": 1, \"t\": [0, 1]}),\n",
|
|
" \"time_cond\": CartesianDomain({\"x\": [-1, 1], \"t\": 0}),\n",
|
|
" \"phys_cond\": CartesianDomain({\"x\": [-1, 1], \"t\": [0, 1]}),\n",
|
|
" }\n",
|
|
" # problem condition statement\n",
|
|
" conditions = {\n",
|
|
" \"bound_cond1\": Condition(\n",
|
|
" domain=\"bound_cond1\", equation=FixedValue(0.0)\n",
|
|
" ),\n",
|
|
" \"bound_cond2\": Condition(\n",
|
|
" domain=\"bound_cond2\", equation=FixedValue(0.0)\n",
|
|
" ),\n",
|
|
" \"time_cond\": Condition(\n",
|
|
" domain=\"time_cond\", equation=Equation(initial_condition)\n",
|
|
" ),\n",
|
|
" \"phys_cond\": Condition(\n",
|
|
" domain=\"phys_cond\", equation=Burgers1DEquation(nu=0.01 / torch.pi)\n",
|
|
" ),\n",
|
|
" }"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## What's Next?\n",
|
|
"\n",
|
|
"Congratulations on completing the `Equation` class tutorial of **PINA**! As we've seen, you can build new classes that inherit from `Equation` to store more complex equations, such as the 1D Burgers equation, by simply passing the characteristic coefficients of the problem.\n",
|
|
"\n",
|
|
"From here, you can:\n",
|
|
"\n",
|
|
"- **Define Additional Complex Equation Classes**: Create your own equation classes, such as `SchrodingerEquation`, `NavierStokesEquation`, etc.\n",
|
|
"- **Define More `FixedOperator` Classes**: Implement operators like `FixedCurl`, `FixedDivergence`, and others for more advanced simulations.\n",
|
|
"- **Integrate Custom Equations and Operators**: Combine your custom equations and operators into larger systems for more complex simulations.\n",
|
|
"- **and many more!**: Explore for example different residual minimization techniques to improve the performance and accuracy of your models.\n",
|
|
"\n",
|
|
"For more resources and tutorials, check out the [PINA Documentation](https://mathlab.github.io/PINA/)."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "pina",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.9.21"
|
|
},
|
|
"orig_nbformat": 4
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|