diff --git a/docs/source/_rst/_tutorial.rst b/docs/source/_rst/_tutorial.rst index 020dbe2d0..9a72dd6aa 100644 --- a/docs/source/_rst/_tutorial.rst +++ b/docs/source/_rst/_tutorial.rst @@ -10,9 +10,11 @@ Getting started with PINA :titlesonly: Introduction to PINA for Physics Informed Neural Networks training + Introduction to PINA Equation class PINA and PyTorch Lightning, training tips and visualizations Building custom geometries with PINA Location class + Physics Informed Neural Networks -------------------------------- .. toctree:: diff --git a/docs/source/_rst/tutorials/tutorial12/tutorial.rst b/docs/source/_rst/tutorials/tutorial12/tutorial.rst new file mode 100644 index 000000000..8dd4dcf4f --- /dev/null +++ b/docs/source/_rst/tutorials/tutorial12/tutorial.rst @@ -0,0 +1,162 @@ +Tutorial: The ``Equation`` Class +================================ + +In this tutorial, we will show how to use the ``Equation`` Class in +PINA. Specifically, we will see how use the Class and its inherited +classes to enforce residuals minimization in PINNs. + +Example: The Burgers 1D equation +-------------------------------- + +We will start implementing the viscous Burgers 1D problem Class, +described as follows: + +.. math:: + + + \begin{equation} + \begin{cases} + \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\\ + u(x,0) &= -\sin (\pi x)\\ + u(x,t) &= 0 \quad x = \pm 1\\ + \end{cases} + \end{equation} + +where we set :math:`\nu = \frac{0.01}{\pi}` . + +In the class that models this problem we will see in action the +``Equation`` class and one of its inherited classes, the ``FixedValue`` +class. + +.. code:: ipython3 + + #useful imports + from pina.problem import SpatialProblem, TimeDependentProblem + from pina.equation import Equation, FixedValue, FixedGradient, FixedFlux + from pina.geometry import CartesianDomain + import torch + from pina.operators import grad, laplacian + from pina import Condition + + + +.. code:: ipython3 + + class Burgers1D(TimeDependentProblem, SpatialProblem): + + # define the burger equation + def burger_equation(input_, output_): + du = grad(output_, input_) + ddu = grad(du, input_, components=['dudx']) + return ( + du.extract(['dudt']) + + output_.extract(['u'])*du.extract(['dudx']) - + (0.01/torch.pi)*ddu.extract(['ddudxdx']) + ) + + # define initial condition + def initial_condition(input_, output_): + u_expected = -torch.sin(torch.pi*input_.extract(['x'])) + return output_.extract(['u']) - u_expected + + # assign output/ spatial and temporal variables + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [-1, 1]}) + temporal_domain = CartesianDomain({'t': [0, 1]}) + + # problem condition statement + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)), + 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)), + 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)), + 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Equation(burger_equation)), + } + +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``. + +The ``FixedValue`` class takes as input a value of same dimensions of +the output functions; this class can be used to enforced a fixed value +for a specific condition, e.g. Dirichlet boundary conditions, as it +happens for instance in our example. + +Once the equations are set as above in the problem conditions, the PINN +solver will aim to minimize the residuals described in each equation in +the training phase. + +Available classes of equations include also: - ``FixedGradient`` and +``FixedFlux``: they work analogously to ``FixedValue`` class, where we +can require a constant value to be enforced, respectively, on the +gradient of the solution or the divergence of the solution; - +``Laplace``: it can be used to enforce the laplacian of the solution to +be zero; - ``SystemEquation``: we can enforce multiple conditions on the +same subdomain through this class, passing a list of residual equations +defined in the problem. + +Defining a new Equation class +----------------------------- + +``Equation`` classes can be also inherited to define a new class. As +example, we can see how to rewrite the above problem introducing a new +class ``Burgers1D``; during the class call, we can pass the viscosity +parameter :math:`\nu`: + +.. code:: ipython3 + + class Burgers1DEquation(Equation): + + def __init__(self, nu = 0.): + """ + Burgers1D class. This class can be + used to enforce the solution u to solve the viscous Burgers 1D Equation. + + :param torch.float32 nu: the viscosity coefficient. Default value is set to 0. + """ + self.nu = nu + + def equation(input_, output_): + return grad(output_, input_, d='x') +\ + output_*grad(output_, input_, d='t') -\ + self.nu*laplacian(output_, input_, d='x') + + + super().__init__(equation) + +Now we can just pass the above class as input for the last condition, +setting :math:`\nu= \frac{0.01}{\pi}`: + +.. code:: ipython3 + + class Burgers1D(TimeDependentProblem, SpatialProblem): + + # define initial condition + def initial_condition(input_, output_): + u_expected = -torch.sin(torch.pi*input_.extract(['x'])) + return output_.extract(['u']) - u_expected + + # assign output/ spatial and temporal variables + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [-1, 1]}) + temporal_domain = CartesianDomain({'t': [0, 1]}) + + # problem condition statement + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)), + 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)), + 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)), + 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Burgers1DEquation(0.01/torch.pi)), + } + +What’s next? +------------ + +Congratulations on completing the ``Equation`` class tutorial of +**PINA**! As we have seen, you can build new classes that inherits +``Equation`` to store more complex equations, as the Burgers 1D +equation, only requiring to pass the characteristic coefficients of the +problem. From now on, you can: - define additional complex equation +classes (e.g. ``SchrodingerEquation``, ``NavierStokeEquation``..) - +define more ``FixedOperator`` (e.g. ``FixedCurl``) diff --git a/tutorials/README.md b/tutorials/README.md index 10ba0c69e..1dfe601e1 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -7,6 +7,7 @@ In this folder we collect useful tutorials in order to understand the principles | Description | Tutorial | |---------------|-----------| Introduction to PINA for Physics Informed Neural Networks training|[[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorials/tutorial1/tutorial.html)]| +Introduction to PINA `Equation` class|[[.ipynb](tutorial12/tutorial.ipynb), [.py](tutorial12/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorials/tutorial12/tutorial.html)]| PINA and PyTorch Lightning, training tips and visualizations|[[.ipynb](tutorial11/tutorial.ipynb), [.py](tutorial11/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorials/tutorial11/tutorial.html)]| Building custom geometries with PINA `Location` class|[[.ipynb](tutorial6/tutorial.ipynb), [.py](tutorial6/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorials/tutorial6/tutorial.html)]| diff --git a/tutorials/tutorial12/tutorial.ipynb b/tutorials/tutorial12/tutorial.ipynb new file mode 100644 index 000000000..7c3d6118c --- /dev/null +++ b/tutorials/tutorial12/tutorial.ipynb @@ -0,0 +1,235 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial: The `Equation` Class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we will show how to use the `Equation` Class in PINA. Specifically, we will see how use the Class and its inherited classes to enforce residuals minimization in PINNs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example: The Burgers 1D equation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will start implementing the viscous Burgers 1D problem Class, described as follows:\n", + "\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)\\\\\n", + "u(x,t) &= 0 \\quad x = \\pm 1\\\\\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": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#useful imports\n", + "from pina.problem import SpatialProblem, TimeDependentProblem\n", + "from pina.equation import Equation, FixedValue, FixedGradient, FixedFlux\n", + "from pina.geometry import CartesianDomain\n", + "import torch\n", + "from pina.operators import grad, laplacian\n", + "from pina import Condition\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "class Burgers1D(TimeDependentProblem, SpatialProblem):\n", + "\n", + " # define the burger equation\n", + " def burger_equation(input_, output_):\n", + " du = grad(output_, input_)\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", + " # 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", + " # problem condition statement\n", + " conditions = {\n", + " 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)),\n", + " 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)),\n", + " 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)),\n", + " 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Equation(burger_equation)),\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "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 same dimensions of the output functions; this class can be used to enforced a fixed value for a specific condition, e.g. Dirichlet boundary conditions, as it happens for instance 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 in the training phase. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Available classes of equations include also:\n", + "- `FixedGradient` and `FixedFlux`: they work analogously to `FixedValue` class, where we can require a constant value to be enforced, respectively, on the gradient of the solution or the divergence of the solution;\n", + "- `Laplace`: it can be used to enforce the laplacian of the solution to be zero;\n", + "- `SystemEquation`: we can enforce multiple conditions on the same subdomain through this class, passing a list of residual equations defined in the problem.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Defining a new Equation class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Equation` classes can be also inherited to define a new class. As example, we can see how to rewrite the above problem introducing a new class `Burgers1D`; during the class call, we can pass the viscosity parameter $\\nu$:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "class Burgers1DEquation(Equation):\n", + " \n", + " def __init__(self, nu = 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 grad(output_, input_, d='x') +\\\n", + " output_*grad(output_, input_, d='t') -\\\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": 14, + "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", + " # problem condition statement\n", + " conditions = {\n", + " 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)),\n", + " 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)),\n", + " 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)),\n", + " 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Burgers1DEquation(0.01/torch.pi)),\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What's next?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Congratulations on completing the `Equation` class tutorial of **PINA**! As we have seen, you can build new classes that inherits `Equation` to store more complex equations, as the Burgers 1D equation, only requiring to pass the characteristic coefficients of the problem. \n", + "From now on, you can:\n", + "- define additional complex equation classes (e.g. `SchrodingerEquation`, `NavierStokeEquation`..)\n", + "- define more `FixedOperator` (e.g. `FixedCurl`)" + ] + } + ], + "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.1.0" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/tutorial12/tutorial.py b/tutorials/tutorial12/tutorial.py new file mode 100644 index 000000000..9b71eb4cb --- /dev/null +++ b/tutorials/tutorial12/tutorial.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # Tutorial: The `Equation` Class + +# In this tutorial, we will show how to use the `Equation` Class in PINA. Specifically, we will see how use the Class and its inherited classes to enforce residuals minimization in PINNs. + +# # Example: The Burgers 1D equation + +# We will start implementing the viscous Burgers 1D problem Class, described as follows: +# +# +# $$ +# \begin{equation} +# \begin{cases} +# \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\\ +# u(x,0) &= -\sin (\pi x)\\ +# u(x,t) &= 0 \quad x = \pm 1\\ +# \end{cases} +# \end{equation} +# $$ +# +# where we set $ \nu = \frac{0.01}{\pi}$. +# +# In the class that models this problem we will see in action the `Equation` class and one of its inherited classes, the `FixedValue` class. + +# In[7]: + + +#useful imports +from pina.problem import SpatialProblem, TimeDependentProblem +from pina.equation import Equation, FixedValue, FixedGradient, FixedFlux +from pina.geometry import CartesianDomain +import torch +from pina.operators import grad, laplacian +from pina import Condition + + +# In[6]: + + +class Burgers1D(TimeDependentProblem, SpatialProblem): + + # define the burger equation + def burger_equation(input_, output_): + du = grad(output_, input_) + ddu = grad(du, input_, components=['dudx']) + return ( + du.extract(['dudt']) + + output_.extract(['u'])*du.extract(['dudx']) - + (0.01/torch.pi)*ddu.extract(['ddudxdx']) + ) + + # define initial condition + def initial_condition(input_, output_): + u_expected = -torch.sin(torch.pi*input_.extract(['x'])) + return output_.extract(['u']) - u_expected + + # assign output/ spatial and temporal variables + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [-1, 1]}) + temporal_domain = CartesianDomain({'t': [0, 1]}) + + # problem condition statement + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)), + 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)), + 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)), + 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Equation(burger_equation)), + } + + +# +# 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`. +# +# The `FixedValue` class takes as input a value of same dimensions of the output functions; this class can be used to enforced a fixed value for a specific condition, e.g. Dirichlet boundary conditions, as it happens for instance in our example. +# +# Once the equations are set as above in the problem conditions, the PINN solver will aim to minimize the residuals described in each equation in the training phase. + +# Available classes of equations include also: +# - `FixedGradient` and `FixedFlux`: they work analogously to `FixedValue` class, where we can require a constant value to be enforced, respectively, on the gradient of the solution or the divergence of the solution; +# - `Laplace`: it can be used to enforce the laplacian of the solution to be zero; +# - `SystemEquation`: we can enforce multiple conditions on the same subdomain through this class, passing a list of residual equations defined in the problem. +# + +# # Defining a new Equation class + +# `Equation` classes can be also inherited to define a new class. As example, we can see how to rewrite the above problem introducing a new class `Burgers1D`; during the class call, we can pass the viscosity parameter $\nu$: + +# In[13]: + + +class Burgers1DEquation(Equation): + + def __init__(self, nu = 0.): + """ + Burgers1D class. This class can be + used to enforce the solution u to solve the viscous Burgers 1D Equation. + + :param torch.float32 nu: the viscosity coefficient. Default value is set to 0. + """ + self.nu = nu + + def equation(input_, output_): + return grad(output_, input_, d='x') + output_*grad(output_, input_, d='t') - self.nu*laplacian(output_, input_, d='x') + + + super().__init__(equation) + + +# Now we can just pass the above class as input for the last condition, setting $\nu= \frac{0.01}{\pi}$: + +# In[14]: + + +class Burgers1D(TimeDependentProblem, SpatialProblem): + + # define initial condition + def initial_condition(input_, output_): + u_expected = -torch.sin(torch.pi*input_.extract(['x'])) + return output_.extract(['u']) - u_expected + + # assign output/ spatial and temporal variables + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [-1, 1]}) + temporal_domain = CartesianDomain({'t': [0, 1]}) + + # problem condition statement + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': -1, 't': [0, 1]}), equation=FixedValue(0.)), + 'gamma2': Condition(location=CartesianDomain({'x': 1, 't': [0, 1]}), equation=FixedValue(0.)), + 't0': Condition(location=CartesianDomain({'x': [-1, 1], 't': 0}), equation=Equation(initial_condition)), + 'D': Condition(location=CartesianDomain({'x': [-1, 1], 't': [0, 1]}), equation=Burgers1DEquation(0.01/torch.pi)), + } + + +# # What's next? + +# Congratulations on completing the `Equation` class tutorial of **PINA**! As we have seen, you can build new classes that inherits `Equation` to store more complex equations, as the Burgers 1D equation, only requiring to pass the characteristic coefficients of the problem. +# From now on, you can: +# - define additional complex equation classes (e.g. `SchrodingerEquation`, `NavierStokeEquation`..) +# - define more `FixedOperator` (e.g. `FixedCurl`)