Files
PINA/tutorials/tutorial12/tutorial.ipynb
Dario Coscia 29b14ee9b6 Update Tutorials (#544)
* update tutorials
* tutorial guidelines
* doc
2025-04-23 18:53:30 +02:00

290 lines
12 KiB
Plaintext
Vendored

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tutorial: Introduction to PINA `Equation` class\n",
"\n",
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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
}