From 75e5d40c45c85d262e7adb0ada2065b8cf9dfd59 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Mon, 19 Aug 2024 14:45:40 +0200 Subject: [PATCH 01/25] update tutorial Signed-off-by: JGoedeke --- .../tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb b/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb index 3f5348b5..5227352b 100644 --- a/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb +++ b/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350", "metadata": {}, "outputs": [], @@ -10,7 +10,8 @@ "import torchphysics as tp\n", "import numpy as np\n", "import torch\n", - "from matplotlib import pyplot as plt" + "from matplotlib import pyplot as plt\n", + "h=0" ] }, { @@ -483,7 +484,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.9.18" } }, "nbformat": 4, From db5d894e2c43f68c3ffe83298a9907ae61b66505 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Mon, 19 Aug 2024 14:48:20 +0200 Subject: [PATCH 02/25] update Signed-off-by: JGoedeke --- examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb b/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb index 5227352b..eccc65b1 100644 --- a/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb +++ b/examples/tutorial/Tutorial_PINNs_Parameter_Dependency.ipynb @@ -10,8 +10,7 @@ "import torchphysics as tp\n", "import numpy as np\n", "import torch\n", - "from matplotlib import pyplot as plt\n", - "h=0" + "from matplotlib import pyplot as plt" ] }, { From f36246903a616c72c06581ec8bbee572833319f0 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Mon, 19 Aug 2024 18:21:14 +0200 Subject: [PATCH 03/25] Updated trainer, removed some unneccessary cells Signed-off-by: JGoedeke --- .../Introduction_Tutorial_PINNs.ipynb | 76 +++++-------------- 1 file changed, 17 insertions(+), 59 deletions(-) diff --git a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb index a6095fb9..d77319bb 100644 --- a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb +++ b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb @@ -106,7 +106,7 @@ "metadata": {}, "source": [ "## Translating the PDE Problem into the Language of TorchPhysics\n", - "Translating the PDE problem into the framework of TorchPhysics works in a convenient and intuitive way, as the notation is close to the mathematical formulation. The general procedure can be devided into five steps. Also when solving other problems with TorchPhysics, such as parameter identification or variational problems, the same steps can be applied, see also the further [tutorials](https://torchphysics.readthedocs.io/en/latest/tutorial/tutorial_start.html) or [examples](https://torchphysics.readthedocs.io/en/latest/examples.html)." + "Translating the PDE problem into the framework of TorchPhysics works in a convenient and intuitive way, as the notation is close to the mathematical formulation. The general procedure can be devided into five steps. Also when solving other problems with TorchPhysics, such as parameter identification or variational problems, the same steps can be applied, see also the further [tutorials](https://boschresearch.github.io/torchphysics/tutorial/tutorial_start.html) or [examples](https://boschresearch.github.io/torchphysics/examples.html)." ] }, { @@ -118,7 +118,7 @@ "### Step 1: Specify spaces and domains\n", "The spatial domain $\\Omega$ is a subset of the space $\\mathbb{R}^2$, the time domain $I$ is a subset of $\\mathbb{R}$, whereas the temperature $u(x,t)$ attains values in $\\mathbb{R}$. First, we need to let TorchPhysics know which spaces and domains we are dealing with and how variables/elements within these spaces are denoted by.\n", "This is realized by generating objects of TorchPhysics' Space and Domain classes in \"tp.spaces\" and \"tp.domains\", respectively. \n", - "Some simple domains are already predefined, which will be sufficient for this tutorial. For creating complexer domains please have a look at the [domain-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/tutorial_domain_basics.html)." + "Some simple domains are already predefined, which will be sufficient for this tutorial. For creating complexer domains please have a look at the [domain-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_domain_basics.html)." ] }, { @@ -157,7 +157,7 @@ "metadata": {}, "source": [ "### Step 2: Define point samplers for different subsets of $\\overline{\\Omega\\times I}$\n", - "As mentioned in the PINN recall, it will be necessary to sample points in different subsets of the full domain $\\overline{\\Omega\\times I}$. TorchPhysics provides this functionality by sampler classes in \"tp.samplers\". For simplicity, we consider only Random Uniform Samplers for the subdomains. However, there are many more possibilities to sample points in TorchPhysics, see also [sampler-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/sampler_tutorial.html).\n", + "As mentioned in the PINN recall, it will be necessary to sample points in different subsets of the full domain $\\overline{\\Omega\\times I}$. TorchPhysics provides this functionality by sampler classes in \"tp.samplers\". For simplicity, we consider only Random Uniform Samplers for the subdomains. However, there are many more possibilities to sample points in TorchPhysics, see also [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", "\n", "The most important inputs of a sampler constructor are the \"domain\" from which points will be sampled, as well as the \"number of points\" drawn every time the sampler is called. It is reasonable to create different sampler objects for the different conditions of the pde problem, simply because the subdomains differ.\n", "\n", @@ -220,7 +220,7 @@ "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc", "metadata": {}, "source": [ - "For more detailed information on the functionality of TorchPysics samplers, please have a look at the [examples](https://torchphysics.readthedocs.io/en/latest/examples.html) or [sampler-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/sampler_tutorial.html).\n", + "For more detailed information on the functionality of TorchPysics samplers, please have a look at the [examples](https://boschresearch.github.io/torchphysics/examples.html) or [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", "\n", "Next, let us define samplers for the initial and boundary conditions. Regarding the initial condition the domain is $\\Omega \\times \\{0\\}$, so we need access to the left boundary of the time interval $I$. All tp.domains.Interval objects have the attribute \"left_boundary\", an instance of TorchPhysics BoundaryDomain class, a subclass of the Domain class." ] @@ -267,16 +267,6 @@ "plot = tp.utils.scatter(X, sampler_boundary_condition)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "661bcdbf", - "metadata": {}, - "outputs": [], - "source": [ - "plot" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -290,7 +280,7 @@ "\n", "More precisely, $u$ will be a torch.tensor of shape (n_points, 1), $x$ of shape (n_points, 2) and $t$ of shape (n_points, 1), where n_points is the number of triples $(u,x,t)$ for which the residual should be computed.\n", "\n", - "For the residual $R_1$ it is required to compute the laplacian of $u$ with respect to $x$, as well as the gradient with respect to $t$. These differential operators, among others - see [utils-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/differentialoperators.html), are pre-implemented and can be found in \"tp.utils\". The intern computation is build upon torch's autograd functionality." + "For the residual $R_1$ it is required to compute the laplacian of $u$ with respect to $x$, as well as the gradient with respect to $t$. These differential operators, among others - see [utils-tutorial](https://boschresearch.github.io/torchphysics/tutorial/differentialoperators.html), are pre-implemented and can be found in \"tp.utils\". The intern computation is build upon torch's autograd functionality." ] }, { @@ -409,7 +399,7 @@ "metadata": {}, "source": [ "### Step 4: Define Neural Network\n", - "At this point, let us define the model $u_\\theta:\\overline{\\Omega\\times I}\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/model_creation.html).\n", + "At this point, let us define the model $u_\\theta:\\overline{\\Omega\\times I}\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", "There are also a bunch of predefined neural networks or single layers available, e.g. fully connected networks (FCN) or normalization layers, which are subclasses of TorchPhysics' Model class. \n", "In this tutorial we consider a very simple neural network, constructed in the following way:\n", "\n", @@ -475,7 +465,7 @@ "Moreover, we have defined a neural network which will later be trained to fulfull each of these conditions.\n", "\n", "As a final step, we collect these constructions for each condition in an object of the TorchPhysics Condition class, contained in \"tp.conditions\". \n", - "Since we are interested in applying a PINN approach, we create objects of the subclass PINNCondition, which automatically contains the information that the residuals should be minimized in the squared $l_2$-norm, see again the PINN Recall. For other TorchPhysics Conditions one may need to specify which norm should be taken of the residuals, see [condition-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/condition_tutorial.html) for further information." + "Since we are interested in applying a PINN approach, we create objects of the subclass PINNCondition, which automatically contains the information that the residuals should be minimized in the squared $l_2$-norm, see again the PINN Recall. For other TorchPhysics Conditions one may need to specify which norm should be taken of the residuals, see [condition-tutorial](https://boschresearch.github.io/torchphysics/tutorial/condition_tutorial.html) for further information." ] }, { @@ -520,7 +510,7 @@ "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac", "metadata": {}, "source": [ - "The reason that also the model is required for initializing a Condition object is, that it could be desireable in some [cases](https://github.com/TomF98/torchphysics/blob/main/examples/pinn/interface-jump.ipynb) to train different networks for different conditions of the PDE problem." + "The reason that also the model is required for initializing a Condition object is, that it could be desireable in some [cases](https://github.com/boschresearch/torchphysics/blob/main/examples/pinn/interface-jump.ipynb) to train different networks for different conditions of the PDE problem." ] }, { @@ -543,8 +533,7 @@ "import pytorch_lightning as pl\n", "import os\n", "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\" if torch.cuda.is_available() else \"0\"\n", - "device = 1 if torch.cuda.is_available() else None\n", - "print('Training on', device)\n", + "\n", "print (\"GPU available: \" + str(torch.cuda.is_available()))" ] }, @@ -622,13 +611,12 @@ "outputs": [], "source": [ "# Start the training\n", - "trainer = pl.Trainer(\n", - " gpus=device, # or None if CPU is used\n", - " max_steps=5000, # number of training steps\n", - " logger=False,\n", - " benchmark=True,\n", - " # checkpoint_callback=False # Uncomment this for more verbose\n", - ")\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\", # what to use to solve problem and how many devices\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=5000, # number of training steps\n", + " logger=False, \n", + " enable_checkpointing=False)\n", "\n", "trainer.fit(solver) # start training" ] @@ -723,16 +711,6 @@ " vmin=vmin, vmax=vmax)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "47876679", - "metadata": {}, - "outputs": [], - "source": [ - "fig" - ] - }, { "cell_type": "code", "execution_count": null, @@ -746,16 +724,6 @@ " vmin=vmin, vmax=vmax)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "50a2e0a6", - "metadata": {}, - "outputs": [], - "source": [ - "fig" - ] - }, { "cell_type": "code", "execution_count": null, @@ -768,16 +736,6 @@ " vmin=vmin, vmax=vmax)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a163454", - "metadata": {}, - "outputs": [], - "source": [ - "fig" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -808,8 +766,8 @@ "id": "26d9c9ba-77fe-4c21-af35-12e1376b113e", "metadata": {}, "source": [ - "The TorchPhysics model cannot be directly evaluated at Pytorch Tensors. Tensors must first be transformed into TorchPhysics Points, which is easy to achieve. We only need to which space the \"tensors\" above belong to. In our case, it belongs to the space $X*T$. ATTENTION: Since the spatial coordinates has been fed into \"tensors\" first, it is important to define the space as $X*T$ and NOT $T*X$!\n", - "For more information on the Point class please have a look at [space- and point-tutorial](https://torchphysics.readthedocs.io/en/latest/tutorial/tutorial_spaces_and_points.html)." + "The TorchPhysics model cannot be directly evaluated at Pytorch Tensors. Tensors must first be transformed into TorchPhysics Points, which is easy to achieve. We only need to which space the \"tensors\" above belong to. In our case, it belongs to the space $X*T$. ATTENTION: Since the spatial coordinates have been fed into \"tensors\" first, it is important to define the space as $X*T$ and NOT $T*X$!\n", + "For more information on the Point class please have a look at the [space- and point-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_spaces_and_points.html)." ] }, { From a72a0947f64bd5f3cc4958d071bce6faf7b53be1 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Tue, 20 Aug 2024 13:01:10 +0200 Subject: [PATCH 04/25] Add loss to progress bar Signed-off-by: Tom Freudenberg --- src/torchphysics/solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/torchphysics/solver.py b/src/torchphysics/solver.py index 7cb8e681..96add9bf 100644 --- a/src/torchphysics/solver.py +++ b/src/torchphysics/solver.py @@ -88,7 +88,7 @@ def training_step(self, batch, batch_idx): self.log(f'train/{condition.name}', cond_loss) loss = loss + condition.weight*cond_loss - self.log('train/loss', loss) + self.log('train/loss', loss, prog_bar=True) self.n_training_step += 1 return loss From de46fd4f9200e6461c8a098c772675bce2c9b3bd Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Tue, 20 Aug 2024 14:35:33 +0200 Subject: [PATCH 05/25] Update Signed-off-by: JGoedeke --- .../Introduction_Tutorial_PINNs.ipynb | 2721 ++++++++++++----- 1 file changed, 1879 insertions(+), 842 deletions(-) diff --git a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb index d77319bb..495a0cad 100644 --- a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb +++ b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb @@ -1,844 +1,1881 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350", - "metadata": {}, - "outputs": [], - "source": [ - "import torchphysics as tp\n", - "import numpy as np\n", - "import torch\n", - "from matplotlib import pyplot as plt" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7cf51978-f0cb-4331-ba1c-9ee4ca6bf8f0", - "metadata": {}, - "source": [ - "# Physics Informed Neural Networks (PINNs) in TorchPhysics\n", - "In this tutorial we present a first basic example of solving a PDE with boundary constraints in TorchPhysics using a PINN approach.\n", - "You will also learn about the different components of this library and main steps for finding a neural network that approximates the solution of a PDE. \n", - "\n", - "We want to solve the time-dependent heat equation for a perfectly insulated room $\\Omega\\subset \\mathbb{R}^2$ in which a heater is turned on. \n", - "$$\n", - "\\begin{cases}\n", - "\\frac{\\partial}{\\partial t} u(x,t) &= \\Delta_x u(x,t) &&\\text{ on } \\Omega\\times I, \\\\\n", - "u(x, t) &= u_0 &&\\text{ on } \\Omega\\times \\{0\\},\\\\\n", - "u(x,t) &= h(t) &&\\text{ at } \\partial\\Omega_{heater}\\times I, \\\\\n", - "\\nabla_x u(x, t) \\cdot \\overset{\\rightarrow}{n}(x) &= 0 &&\\text{ at } (\\partial \\Omega \\setminus \\partial\\Omega_{heater}) \\times I.\n", - "\\end{cases}\n", - "$$\n", - "The initial room (and heater) temperature is $u_0 = 16$. The time domain is the interval $I = (0, 20)$, whereas the domain of the room is $\\Omega=(5,0) \\times (4,0)$. The heater is located at $\\partial\\Omega_{heater} = [1,3] \\times \\{4\\}$ and the temperature of the heater is described by the function $h$ defined below.\n", - "The normal vector at some $x\\in \\partial \\Omega$ is denoted by $\\overset{\\rightarrow}{n}(x)$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6b5fdd2-67c1-4f7e-a185-9d515fb9f3f8", - "metadata": {}, - "outputs": [], - "source": [ - "u_0 = 16 # initial temperature\n", - "u_heater_max = 40 # maximal temperature of the heater\n", - "t_heater_max = 5 # time at which the heater reaches its maximal temperature\n", - "\n", - "# heater temperature function\n", - "def h(t):\n", - " ht = u_0 + (u_heater_max - u_0) / t_heater_max * t\n", - " ht[t>t_heater_max] = u_heater_max\n", - " return ht\n", - "\n", - "# Visualize h(t)\n", - "t = np.linspace(0, 20, 200)\n", - "plt.plot(t, h(t))\n", - "plt.show()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8da6279e-83c2-41ed-a56b-453b21f05d11", - "metadata": {}, - "source": [ - "## Recall PINNs\n", - "The goal is to find a neural network $u_\\theta: \\overline{\\Omega\\times I} \\to \\mathbb{R}$, which approximately satisfies all four conditions of the PDE problem above, where $\\theta$ are the trainable parameters of the neural network.\n", - "Let us shortly recall the main idea behind PINNs.\n", - "\n", - "In our case, there is no data available (e.g. temperature measurements in $\\Omega$), which could be used for training the neural network. Hence, we can only exploit the four conditions listed above.\n", - "\n", - "The residuals are denoted by \n", - "$$\n", - "\\begin{align}\n", - "&\\text{1) Residual of pde condition: } &&R_1(u, x, t) := u(x, t) - \\Delta_x u(x,t) \\\\\n", - "&\\text{2) Residual of initial condition: } &&R_2(u, x) := u(x, 0) - u_0\\\\\n", - "&\\text{3) Residual of dirichlet boundary condition: } &&R_3(u, x, t) := u(x,t) - h(t)\\\\\n", - "&\\text{4) Residual of neumann boundary condition: } &&R_4(u, x, t) :=\\nabla_x u(x,t) \\cdot \\overset{\\rightarrow}{n}(x)\n", - "\\end{align}\n", - "$$\n", - "Continuing with the PINN approach, points are sampled in the domains corresponding to each condition. In our example points\n", - "$$\n", - "\\begin{align}\n", - "&\\text{1) } &&\\big(x^{(1)}_i, t_i^{(1)} \\big)_i &&&\\in \\Omega \\times I,\\\\\n", - "&\\text{2) } &&\\big(x^{(2)}_j, 0 \\big)_j &&&\\in \\Omega \\times \\{0\\},\\\\\n", - "&\\text{3) } &&\\big(x^{(3)}_k, t_k^{(3)} \\big)_k &&&\\in \\partial\\Omega_{heater} \\times I,\\\\\n", - "&\\text{4) } &&\\big(x^{(4)}_l, t_l^{(4)} \\big)_l &&&\\in (\\partial\\Omega \\setminus \\partial\\Omega_{heater}) \\times I.\n", - "\\end{align}\n", - "$$\n", - "Then, the network $u_\\theta$ is trained by solving the following minimization problem\n", - "$$\n", - "\\begin{align}\n", - "\\min_\\theta \\sum_{i} \\big\\vert R_1(u_\\theta, x^{(1)}_i, t_i^{(1)}) \\big \\vert^2 + \\sum_j \\big\\vert R_2(u_\\theta, x^{(2)}_j) \\big \\vert^2 + \\sum_k \\big\\vert R_3(u_\\theta, x^{(3)}_k, t_k^{(3)}) \\big \\vert^2 + \\sum_l \\big\\vert R_4(u_\\theta, x^{(4)}_l, t_l^{(4)}) \\big \\vert^2,\n", - "\\end{align}\n", - "$$\n", - "that is, the residuals are minimized with respect to the $l_2$-norm.\n", - "It is to be noted here that if data was available, one could simply add a data loss term to the loss function above." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8f0db4a0-cace-4d21-845f-f34680880d7d", - "metadata": {}, - "source": [ - "## Translating the PDE Problem into the Language of TorchPhysics\n", - "Translating the PDE problem into the framework of TorchPhysics works in a convenient and intuitive way, as the notation is close to the mathematical formulation. The general procedure can be devided into five steps. Also when solving other problems with TorchPhysics, such as parameter identification or variational problems, the same steps can be applied, see also the further [tutorials](https://boschresearch.github.io/torchphysics/tutorial/tutorial_start.html) or [examples](https://boschresearch.github.io/torchphysics/examples.html)." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e8fe0433-82b7-4093-8f6f-8adf7e46ff5b", - "metadata": {}, - "source": [ - "### Step 1: Specify spaces and domains\n", - "The spatial domain $\\Omega$ is a subset of the space $\\mathbb{R}^2$, the time domain $I$ is a subset of $\\mathbb{R}$, whereas the temperature $u(x,t)$ attains values in $\\mathbb{R}$. First, we need to let TorchPhysics know which spaces and domains we are dealing with and how variables/elements within these spaces are denoted by.\n", - "This is realized by generating objects of TorchPhysics' Space and Domain classes in \"tp.spaces\" and \"tp.domains\", respectively. \n", - "Some simple domains are already predefined, which will be sufficient for this tutorial. For creating complexer domains please have a look at the [domain-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_domain_basics.html)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6af0dba0-d481-4566-a8b7-244098eee713", - "metadata": {}, - "outputs": [], - "source": [ - "# Input and output spaces\n", - "X = tp.spaces.R2(variable_name='x')\n", - "T = tp.spaces.R1('t')\n", - "U = tp.spaces.R1('u')\n", - "\n", - "# Domains\n", - "Omega = tp.domains.Parallelogram(space=X, origin=[0,0], corner_1=[5,0], corner_2=[0,4])\n", - "I = tp.domains.Interval(space=T, lower_bound=0, upper_bound=20)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1efe92cb-daab-4d21-8a43-5008e3e9248a", - "metadata": {}, - "outputs": [], - "source": [ - "# The domain can be visualized by creating a sampler object, see also step 2, and use the scatter plot function from tp.utils. \n", - "Omega_sampler = tp.samplers.RandomUniformSampler(Omega, n_points=500)\n", - "plot = tp.utils.scatter(X, Omega_sampler)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "a1676bc3-8dab-4ce4-84ff-f8fc29e8b829", - "metadata": {}, - "source": [ - "### Step 2: Define point samplers for different subsets of $\\overline{\\Omega\\times I}$\n", - "As mentioned in the PINN recall, it will be necessary to sample points in different subsets of the full domain $\\overline{\\Omega\\times I}$. TorchPhysics provides this functionality by sampler classes in \"tp.samplers\". For simplicity, we consider only Random Uniform Samplers for the subdomains. However, there are many more possibilities to sample points in TorchPhysics, see also [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", - "\n", - "The most important inputs of a sampler constructor are the \"domain\" from which points will be sampled, as well as the \"number of points\" drawn every time the sampler is called. It is reasonable to create different sampler objects for the different conditions of the pde problem, simply because the subdomains differ.\n", - "\n", - "For instance, the pde condition 1) should hold for points in the domain $\\Omega \\times I$. We have already created $\\Omega$ and $I$ as TorchPhysics Domains in Step 1. Their cartesian product is simply obtained by the multiplication operator \"$*$\":" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d428cf7f-89ee-4f3f-a1bf-822b82550a7e", - "metadata": {}, - "outputs": [], - "source": [ - "domain_pde_condition = Omega * I" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8db04580-edb8-45ac-8f48-091450647377", - "metadata": {}, - "source": [ - "Having the relevant domain on hand, we initialize as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d020f7f4-c286-466f-928d-1f80ee64c53f", - "metadata": {}, - "outputs": [], - "source": [ - "sampler_pde_condition = tp.samplers.RandomUniformSampler(domain=domain_pde_condition, n_points=1500)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac69b667-1a77-4e8a-8a20-2e0b5a1de2a0", - "metadata": {}, - "source": [ - "There is an important alternative way of creating a sampler for a cartesian product of domains. Instead of defining the sampler on $\\Omega\\times I$, it is also possible to create samplers on $\\Omega$ and $I$ seperately, and multiply the samplers instead. This might be useful if different resolutions shall be considered, or when using other samplers in TorchPhysics such as a GridSampler, since a GridSampler cannot directly be created on a cartesian product in the way above." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a1ee851-1bd4-4ee2-83e4-7dca3f883c0f", - "metadata": {}, - "outputs": [], - "source": [ - "sampler_Omega = tp.samplers.GridSampler(domain=Omega, n_points=1000)\n", - "sampler_I = tp.samplers.RandomUniformSampler(domain=I, n_points=500)\n", - "alternative_sampler_pde_condition = sampler_Omega * sampler_I " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc", - "metadata": {}, - "source": [ - "For more detailed information on the functionality of TorchPysics samplers, please have a look at the [examples](https://boschresearch.github.io/torchphysics/examples.html) or [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", - "\n", - "Next, let us define samplers for the initial and boundary conditions. Regarding the initial condition the domain is $\\Omega \\times \\{0\\}$, so we need access to the left boundary of the time interval $I$. All tp.domains.Interval objects have the attribute \"left_boundary\", an instance of TorchPhysics BoundaryDomain class, a subclass of the Domain class." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e780f5fa-5ebf-4731-8568-77116ea039f6", - "metadata": {}, - "outputs": [], - "source": [ - "domain_initial_condition = Omega * I.boundary_left\n", - "sampler_initial_condition = tp.samplers.RandomUniformSampler(domain_initial_condition, 2500)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7750bf6b-30ec-4ca9-8f37-9699439d0d22", - "metadata": {}, - "source": [ - "Both the Dirichlet and Neumann boundary conditions should hold on subsets of the boundary $\\partial \\Omega \\times I$. It is easier to use a sampler for the whole boundary and determine later (in Step 3, the definition of the residual functions) whether a sampled point belongs to the domain $\\partial \\Omega_{heater}\\times I$ of the Dirichlet condition, or to the domain $(\\partial \\Omega \\setminus \\partial \\Omega_{heater}) \\times I$ of the Neumann condition." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b627951a-a12b-4333-b965-35a56b8fc396", - "metadata": {}, - "outputs": [], - "source": [ - "domain_boundary_condition = Omega.boundary * I\n", - "sampler_boundary_condition = tp.samplers.RandomUniformSampler(domain_boundary_condition, 2500)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c23a19e6-4167-4785-8323-984c319e2cb4", - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: Plot at two or three times\n", - "plot = tp.utils.scatter(X, sampler_boundary_condition)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "6b1b87f9-b6d6-44ec-8fb5-833ab466d89b", - "metadata": {}, - "source": [ - "### Step 3: Define residual functions\n", - "As mentioned in the PINNs Recall, we are looking for a neural network $u_\\theta$ for which all of the residual functions $R_1,...,R_4$ vanish.\n", - "\n", - "Let us have a look at $R_1$, the residual for the pde condition, the way it is defined in the PINNs Recall above. The inputs of $R_1$ are spatial and temporal coordinates $x\\in \\Omega$, $t\\in I$, but also the temperature $u_\\theta$, which is itself a function of $x$ and $t$. In TorchPhysics, the evaluation of the network $u_\\theta$ at $(x,t)$ is done before evaluating the residual functions. This means that from now on we consider $R_1$ as well as the other residuals to be functions, whose inputs are triples $(u, x, t)$, where $u:=u_\\theta(x,t)$.\n", - "\n", - "More precisely, $u$ will be a torch.tensor of shape (n_points, 1), $x$ of shape (n_points, 2) and $t$ of shape (n_points, 1), where n_points is the number of triples $(u,x,t)$ for which the residual should be computed.\n", - "\n", - "For the residual $R_1$ it is required to compute the laplacian of $u$ with respect to $x$, as well as the gradient with respect to $t$. These differential operators, among others - see [utils-tutorial](https://boschresearch.github.io/torchphysics/tutorial/differentialoperators.html), are pre-implemented and can be found in \"tp.utils\". The intern computation is build upon torch's autograd functionality." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c29f3f92-d613-470f-ab74-9369e071ea04", - "metadata": {}, - "outputs": [], - "source": [ - "def residual_pde_condition(u, x, t):\n", - " return tp.utils.laplacian(u, x) - tp.utils.grad(u, t)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e444a2e5-6fc6-4124-894c-1ba987153241", - "metadata": {}, - "source": [ - "For the computation of the residual $R_2$ of the initial condition, the coordinates $x$ and $t$ are not required, since $u$ is already the evaluation of the network at these points. Therefore, we can conveniently omit them as input parameters." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "65954de9-4c80-4d2a-be6e-0cd16ab82596", - "metadata": {}, - "outputs": [], - "source": [ - "def residual_initial_condition(u):\n", - " return u - u_0" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "97b9bfba-5cd3-400c-8c5a-4cd48b320c80", - "metadata": {}, - "source": [ - "In Step 2, we defined a boundary sampler for $\\partial \\Omega \\times I$, the domain for the boundary conditions. Hence, the sampler does not differ between the domain of the Dirichlet and Neumann boundary conditions. This is why we define a combined residual function $R_b$ for $R_3$ and $R_4$, which will output\n", - "$$\n", - "\\begin{align}\n", - "R_b(u, x, t) = \\begin{cases}\n", - "R_3(u, x, t) &\\text{ if } &&x \\in \\partial \\Omega_{heater},\\\\\n", - "R_4(u, x, t) &\\text{ if } &&x \\in \\partial \\Omega \\setminus \\partial \\Omega_{heater}.\n", - "\\end{cases}\n", - "\\end{align}\n", - "$$\n", - "Let us start with the defintion of the Dirichlet residual $R_3$:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c97e8bfe-1580-4bb8-bb1b-d4c874ef6244", - "metadata": {}, - "outputs": [], - "source": [ - "def residual_dirichlet_condition(u, t):\n", - " return u - h(t)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "de441693-0870-43db-8d8d-38777a075432", - "metadata": {}, - "source": [ - "For the Neumann residual $R_4$ we need the normal derivative of $u$ at $x$. This differential operator is also contained in \"tp.utils\", whereas the normal vectors at points $x\\in \\partial \\Omega$ are available by the attribute \"normal\" of the \"boundary\" of the domain $\\Omega$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17d5e293-57bd-4739-9518-a014f6df2b79", - "metadata": {}, - "outputs": [], - "source": [ - "def residual_neumann_condition(u, x):\n", - " normal_vectors = Omega.boundary.normal(x)\n", - " normal_derivative = tp.utils.normal_derivative(u, normal_vectors, x)\n", - " return normal_derivative " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "463e507e-d33b-4f8d-9149-c45356fdf236", - "metadata": {}, - "source": [ - "The combined boundary residual $R_b$ is then easily obtained as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4864c6ed-6f2b-4f80-bd6f-cd8ff3d8a809", - "metadata": {}, - "outputs": [], - "source": [ - "def residual_boundary_condition(u, x, t):\n", - " # Create boolean tensor indicating which points x belong to the dirichlet condition (heater location)\n", - " heater_location = (x[:, 0] >= 1 ) & (x[:, 0] <= 3) & (x[:, 1] >= 3.99) \n", - " # First compute Neumann residual everywhere, also at the heater position\n", - " residual = residual_neumann_condition(u, x)\n", - " # Now change residual at the heater to the Dirichlet residual\n", - " residual_h = residual_dirichlet_condition(u, t)\n", - " residual[heater_location] = residual_h[heater_location]\n", - " return residual" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "0cc89ada-310b-4a84-bcc0-77baa7afca2c", - "metadata": {}, - "source": [ - "### Step 4: Define Neural Network\n", - "At this point, let us define the model $u_\\theta:\\overline{\\Omega\\times I}\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", - "There are also a bunch of predefined neural networks or single layers available, e.g. fully connected networks (FCN) or normalization layers, which are subclasses of TorchPhysics' Model class. \n", - "In this tutorial we consider a very simple neural network, constructed in the following way:\n", - "\n", - "We start with a normalization layer, which maps points $(x,t)\\in \\overline{\\Omega\\times I}\\subset \\mathbb{R}^3$ into the cube $[-1, 1]^3$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bdef3d80-90e6-47aa-95ce-6d735fd03f36", - "metadata": {}, - "outputs": [], - "source": [ - "normalization_layer = tp.models.NormalizationLayer(Omega*I)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "75e0d506-13f0-4e39-882b-d752c89fe7fc", - "metadata": {}, - "source": [ - "Afterwards, the scaled points will be passed through a fully connected network. The constructor requires to include the input space $X\\times T$, output space $U$ and ouput dimensions of the hidden layers. Remember the definition of the TorchPyhsics spaces $X,T$ and $U$ from Step 1. Similar as for domains, the cartesian product of spaces is obtained by the multiplication operator \"$*$\". Here, we consider a fully connected network with four hidden layers, the latter consisting of $80, 50, 50$ and $50$ neurons, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa15606a-a2c7-40bf-9e41-920c8f6a1bc9", - "metadata": {}, - "outputs": [], - "source": [ - "fcn_layer = tp.models.FCN(input_space=X*T, output_space=U, hidden = (80,50,50,50))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "694d8666-170e-4c28-a87a-73aa329e2094", - "metadata": {}, - "source": [ - "Similar to Pytorch, the normalization layer and FCN can be concatenated by the class \"tp.models.Sequential\":" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b838d6f-1b90-4667-8ecb-9f54b4ec627e", - "metadata": {}, - "outputs": [], - "source": [ - "model = tp.models.Sequential(normalization_layer, fcn_layer)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "17e3f8ab-bd6c-4f4f-94a6-030930458c0c", - "metadata": {}, - "source": [ - "### Step 5: Create TorchPhysics Conditions\n", - "Let us sum up what we have done so far: For the pde, initial and combined boundary condition of the PDE problem, we constructed samplers and residuals on the corresponding domains.\n", - "Moreover, we have defined a neural network which will later be trained to fulfull each of these conditions.\n", - "\n", - "As a final step, we collect these constructions for each condition in an object of the TorchPhysics Condition class, contained in \"tp.conditions\". \n", - "Since we are interested in applying a PINN approach, we create objects of the subclass PINNCondition, which automatically contains the information that the residuals should be minimized in the squared $l_2$-norm, see again the PINN Recall. For other TorchPhysics Conditions one may need to specify which norm should be taken of the residuals, see [condition-tutorial](https://boschresearch.github.io/torchphysics/tutorial/condition_tutorial.html) for further information." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "008c09a7-81f8-41b5-8c10-3892812740ad", - "metadata": {}, - "outputs": [], - "source": [ - "pde_condition = tp.conditions.PINNCondition(module =model, \n", - " sampler =sampler_pde_condition,\n", - " residual_fn=residual_pde_condition)\n", - "\n", - "initial_condition = tp.conditions.PINNCondition(module =model, \n", - " sampler =sampler_initial_condition,\n", - " residual_fn=residual_initial_condition)\n", - "\n", - "boundary_condition = tp.conditions.PINNCondition(module =model, \n", - " sampler =sampler_boundary_condition,\n", - " residual_fn=residual_boundary_condition)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5cd77316-3c78-4bf1-b639-9ccb7070af2d", - "metadata": {}, - "source": [ - "It is to be noted that TorchPhysics' Condition class is a subclass of the torch.nn.Module class and its forward() method returns the current loss of the respective condition.\n", - "For example, calling forward() of the pde_condition at points $(x_i, t_i)_i$ in $\\Omega\\times I$ will return\n", - "$$\n", - "\\begin{align}\n", - "\\sum_i \\big \\vert R_1(u_\\theta, x_i, t_i) \\big \\vert^2,\n", - "\\end{align}\n", - "$$\n", - "where $R_1$ is the residual function for the pde condition defined in the PINN recall and $u_\\theta$ is the model defined in Step 4." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac", - "metadata": {}, - "source": [ - "The reason that also the model is required for initializing a Condition object is, that it could be desireable in some [cases](https://github.com/boschresearch/torchphysics/blob/main/examples/pinn/interface-jump.ipynb) to train different networks for different conditions of the PDE problem." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "31d80c43-5879-401c-8212-0e4a5fd6514c", - "metadata": {}, - "source": [ - "## Training based on Pytorch Lightning \n", - "In order to train a model, TorchPhysics makes use of the Pytorch Lightning library, which hence must be imported. Further, we import \"os\" so that GPUs can be used for the calculations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb76e892-bf53-4a01-adc5-74dddb770525", - "metadata": {}, - "outputs": [], - "source": [ - "import pytorch_lightning as pl\n", - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\" if torch.cuda.is_available() else \"0\"\n", - "\n", - "print (\"GPU available: \" + str(torch.cuda.is_available()))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "1639cf38-835b-4571-b0c5-7ef0d130c2df", - "metadata": {}, - "source": [ - "For the training process, i.e. the minimization of the loss function introduced in the PINN recall, TorchPhysics provides the Solver class. It inherits from the pl.LightningModule class and is compatible with the TorchPhysics library. The constructor requires a list of TorchPhysics Conditions, whose parameters should be optimized during the training." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea27b608-e319-4fac-85c1-5984f2d043c6", - "metadata": {}, - "outputs": [], - "source": [ - "training_conditions = [pde_condition, initial_condition, boundary_condition]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e024913e-e10e-4387-b390-165e77c8524b", - "metadata": {}, - "source": [ - "By default, the Solver uses the Adam Optimizer from Pytorch with learning rate $lr=0.001$ for optimizing the training_conditions. If a different optimizer or choice of its arguments shall be used, one can collect these information in an object of TorchPhysics' OptimizerSetting class. Here we choose the Adam Optimizer from Pytorch with a learning rate $lr=0.002$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1848d26-ea33-400c-84be-2291429e8065", - "metadata": {}, - "outputs": [], - "source": [ - "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.0005)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "efcd0c8c-1ef2-45a0-bf00-de88201f3d03", - "metadata": {}, - "source": [ - "Finally, we are able to create the Solver object, a Pytorch Lightning Module." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ea2cb3f-087c-4e03-aeb0-40318f556062", - "metadata": {}, - "outputs": [], - "source": [ - "solver = tp.solver.Solver(train_conditions=training_conditions, optimizer_setting=optim)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "53dec402-5dd2-40f9-a405-5170d0cfcbd7", - "metadata": {}, - "source": [ - "Now, as usual, the training is done with a Pytorch Lightning Trainer object and its fit() method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9ea9431a-9ea4-4312-8869-af4c8c4733a4", - "metadata": {}, - "outputs": [], - "source": [ - "# Start the training\n", - "trainer = pl.Trainer(devices=1, accelerator=\"gpu\", # what to use to solve problem and how many devices\n", - " num_sanity_val_steps=0,\n", - " benchmark=True,\n", - " max_steps=5000, # number of training steps\n", - " logger=False, \n", - " enable_checkpointing=False)\n", - "\n", - "trainer.fit(solver) # start training" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c2fa291a-73b1-476b-8302-3aa63c34c61a", - "metadata": {}, - "source": [ - "You can also re-run the last three blocks with a smaller learning rate to further decrease the loss.\n", - "\n", - "Of course, the state dictionary of the model can be saved in the common way: torch.save(model.state_dict(), 'sd')" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "bac7c186-2be3-4ce0-a252-527ae5083019", - "metadata": {}, - "source": [ - "## Visualization\n", - "Torchphysics provides built-in functionalities for visualizing the outcome of the neural network.\n", - "As a first step, for the 2D heat equation example one might be interested in creating a contour plot for the heat distribution inside of the room at some fixed time.\n", - "\n", - "For this purpose, we use the plot() function from \"tp.utils\", which is built on the Matplotlib library. The most important inputs are:\n", - "1) model: The neural network whose output shall be visualized.\n", - "2) point_function: Will be applied to the model's output before visualization. E.g. if the output was two-dimensional, the plot_function $u\\mapsto u[:, 0]$ could be used for showing only its first coordinate.\n", - "3) plot_sampler: A sampler creating points the neural network will be evaluated at for creating the plot.\n", - "4) plot_type: Specify what kind of plot should be created. \n", - "\n", - "Let us start with the sampler. The samplers we have seen so far (RandomUniformSampler, GridSampler) plot either on the interior or the boundary of their domain.\n", - "However, it is desirable to consider both the interior and the boundary points in the visualization. For this, one can use a PlotSampler, which is desined for harmonizing with plotting duties.\n", - "\n", - "We wish to visualize the heat distribution in $\\overline{\\Omega}$ at some fixed time $t'$. The latter can be added to the attribute \"data_for_other_variables\" of the PlotSampler." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00c3d1e0-aeda-4e15-9ca5-67bbb953bd73", - "metadata": {}, - "outputs": [], - "source": [ - "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':0.})" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5f9efe1d-cf26-4274-9ac0-1bba28e04827", - "metadata": {}, - "source": [ - "In our case, the model's output is a scalar and we do not want to modify it before plotting. Hence, plot_function should be the identity mapping. As we wish to use a colormap/contour plot to visualize the heat in $\\Omega$, we specify the plot_type as 'contour_surface'.\n", - "\n", - "Finally, we obtain the desired plot at time $t'=0$ by" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3b514990-7c54-4896-b391-9275011df402", - "metadata": {}, - "outputs": [], - "source": [ - "vmin = 15 # limits for the axes\n", - "vmax = 42\n", - "fig = tp.utils.plot(model =model, plot_function=lambda u : u, \n", - " point_sampler=plot_sampler, plot_type ='contour_surface',\n", - " vmin=vmin, vmax=vmin)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "54c7788a-d7a0-438c-821e-bef10f3f780f", - "metadata": {}, - "source": [ - "Let us visualize the solution of the PDE at further time points." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e9e54d6e-f7a2-4746-a05e-681e3dbee8b7", - "metadata": {}, - "outputs": [], - "source": [ - "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':4.})\n", - "fig = tp.utils.plot(model, lambda u : u, \n", - " plot_sampler, plot_type='contour_surface',\n", - " vmin=vmin, vmax=vmax)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "10a7c785-90da-4b62-964f-af7d816ed1bd", - "metadata": {}, - "outputs": [], - "source": [ - "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':8.})\n", - "fig = tp.utils.plot(model, lambda u : u, \n", - " plot_sampler, plot_type='contour_surface',\n", - " vmin=vmin, vmax=vmax)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3e6a8cf-6bd5-42d6-a3ac-16c4a64eb22b", - "metadata": {}, - "outputs": [], - "source": [ - "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':12.})\n", - "fig = tp.utils.plot(model, lambda u : u, plot_sampler, plot_type='contour_surface',\n", - " vmin=vmin, vmax=vmax)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "9d58e206-c27f-4ee6-8f4d-ddb1415c7221", - "metadata": {}, - "source": [ - "It is also possible to evaluate the model manually at torch Tensors. Say, we want to evaluate it on a spatial grid at some fixed time $t'= 6$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9ccbb9b3-6f6a-4a29-8dc7-c2360b2df7c9", - "metadata": {}, - "outputs": [], - "source": [ - "x_coords = torch.linspace(0, 5, 100)\n", - "y_coords = torch.linspace(0, 4, 80)\n", - "t_coords = torch.linspace(6, 6 , 1)\n", - "#t_coords = torch.linspace(0, 20, 120)\n", - "xs, ys, ts = torch.meshgrid([x_coords, y_coords, t_coords])\n", - "tensors = torch.stack([xs.flatten(), ys.flatten(), ts.flatten()], dim=1)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "26d9c9ba-77fe-4c21-af35-12e1376b113e", - "metadata": {}, - "source": [ - "The TorchPhysics model cannot be directly evaluated at Pytorch Tensors. Tensors must first be transformed into TorchPhysics Points, which is easy to achieve. We only need to which space the \"tensors\" above belong to. In our case, it belongs to the space $X*T$. ATTENTION: Since the spatial coordinates have been fed into \"tensors\" first, it is important to define the space as $X*T$ and NOT $T*X$!\n", - "For more information on the Point class please have a look at the [space- and point-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_spaces_and_points.html)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67c99cdd-70db-4465-9ec0-8278b7381fa6", - "metadata": {}, - "outputs": [], - "source": [ - "points = tp.spaces.Points(tensors, space=X*T)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ce94a359-75dd-41e7-85b3-2000b2065054", - "metadata": {}, - "source": [ - "Now the model can be evaluated at those points by its forward() method. In order to use e.g. \"plt.imshow()\", we need to transform the output into a numpy array." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "854b969a-96f2-4088-b045-d1ca5cf0db64", - "metadata": {}, - "outputs": [], - "source": [ - "output = model.forward(tp.spaces.Points(tensors, space=X*T))\n", - "output = output.as_tensor.reshape(100, 80, 1).detach().numpy()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "70d30023-ca42-460a-9906-2bcc736016ce", - "metadata": {}, - "outputs": [], - "source": [ - "plt.imshow(np.rot90(output[:, :]), 'gray', vmin=vmin, vmax=vmax)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9840aad9", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350", + "metadata": { + "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350" + }, + "outputs": [], + "source": [ + "import torchphysics as tp\n", + "import numpy as np\n", + "import torch\n", + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "7cf51978-f0cb-4331-ba1c-9ee4ca6bf8f0", + "metadata": { + "id": "7cf51978-f0cb-4331-ba1c-9ee4ca6bf8f0" + }, + "source": [ + "# Physics Informed Neural Networks (PINNs) in TorchPhysics\n", + "In this tutorial we present a first basic example of solving a PDE with boundary constraints in TorchPhysics using a PINN approach.\n", + "You will also learn about the different components of this library and main steps for finding a neural network that approximates the solution of a PDE.\n", + "\n", + "We want to solve the time-dependent heat equation for a perfectly insulated room $\\Omega\\subset \\mathbb{R}^2$ in which a heater is turned on.\n", + "$$\n", + "\\begin{cases}\n", + "\\frac{\\partial}{\\partial t} u(x,t) &= \\Delta_x u(x,t) &&\\text{ on } \\Omega\\times I, \\\\\n", + "u(x, t) &= u_0 &&\\text{ on } \\Omega\\times \\{0\\},\\\\\n", + "u(x,t) &= h(t) &&\\text{ at } \\partial\\Omega_{heater}\\times I, \\\\\n", + "\\nabla_x u(x, t) \\cdot \\overset{\\rightarrow}{n}(x) &= 0 &&\\text{ at } (\\partial \\Omega \\setminus \\partial\\Omega_{heater}) \\times I.\n", + "\\end{cases}\n", + "$$\n", + "The initial room (and heater) temperature is $u_0 = 16$. The time domain is the interval $I = (0, 20)$, whereas the domain of the room is $\\Omega=(5,0) \\times (4,0)$. The heater is located at $\\partial\\Omega_{heater} = [1,3] \\times \\{4\\}$ and the temperature of the heater is described by the function $h$ defined below.\n", + "The normal vector at some $x\\in \\partial \\Omega$ is denoted by $\\overset{\\rightarrow}{n}(x)$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d6b5fdd2-67c1-4f7e-a185-9d515fb9f3f8", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 430 + }, + "id": "d6b5fdd2-67c1-4f7e-a185-9d515fb9f3f8", + "outputId": "b6f2faea-b0e8-4af0-dc49-e8312374062b" + }, + "outputs": [ + { + "data": { + "image/png": "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PubPn29+/etfe78f6HkcLMfX3t5uvvWtb5kJEyaYw4cP93heulyuPo9voL/zYDm+5uZm8/d///dm37595vTp02bnzp3mhhtuMNddd51pa2vr8/iC6fwZM/DfqDHGNDU1mfj4eLNhw4Ze9xGs53AwrwsPPfSQycnJMbt37zYffPCBKSgoMAUFBT32M336dPPKK694vx/M83a4wip8GGPMr3/9a5OTk2NiY2PNN77xDbN//37vz2699VZz33339dj+D3/4g5k2bZqJjY01M2fONCUlJQGeeHAk9fq1ceNG7zZfPb7HHnvM+/9FRkaGWbp0qTl48GDghx+ku+++24wfP97Exsaaa665xtx9993m5MmT3p+H8vnzKC0tNZJMVVXVVT8LxfP35ptv9vp36TkOt9ttfvKTn5iMjAxjt9vNggULrjr2iRMnmscff7zHY/09jwOpv+M7ffp0n8/LN99807uPrx7fQH/ngdTf8bW2tpqFCxeacePGmZiYGDNx4kTz4IMPXhUigvn8GTPw36gxxjz77LNm9OjRprGxsdd9BOs5HMzrwqVLl8zf/u3fmrFjx5r4+Hjz53/+56a2tvaq/Vz5O4N53g6XrfsfDAAAEBBh85kPAAAQGggfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoAgfAAAgoP5/M6SenHABuB4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "u_0 = 16 # initial temperature\n", + "u_heater_max = 40 # maximal temperature of the heater\n", + "t_heater_max = 5 # time at which the heater reaches its maximal temperature\n", + "\n", + "# heater temperature function\n", + "def h(t):\n", + " ht = u_0 + (u_heater_max - u_0) / t_heater_max * t\n", + " ht[t>t_heater_max] = u_heater_max\n", + " return ht\n", + "\n", + "# Visualize h(t)\n", + "t = np.linspace(0, 20, 200)\n", + "plt.plot(t, h(t))\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8da6279e-83c2-41ed-a56b-453b21f05d11", + "metadata": { + "id": "8da6279e-83c2-41ed-a56b-453b21f05d11" + }, + "source": [ + "## Recall PINNs\n", + "The goal is to find a neural network $u_\\theta: \\overline{\\Omega\\times I} \\to \\mathbb{R}$, which approximately satisfies all four conditions of the PDE problem above, where $\\theta$ are the trainable parameters of the neural network.\n", + "Let us shortly recall the main idea behind PINNs.\n", + "\n", + "In our case, there is no data available (e.g. temperature measurements in $\\Omega$), which could be used for training the neural network. Hence, we can only exploit the four conditions listed above.\n", + "\n", + "The residuals are denoted by\n", + "$$\n", + "\\begin{align}\n", + "&\\text{1) Residual of pde condition: } &&R_1(u, x, t) := u(x, t) - \\Delta_x u(x,t) \\\\\n", + "&\\text{2) Residual of initial condition: } &&R_2(u, x) := u(x, 0) - u_0\\\\\n", + "&\\text{3) Residual of dirichlet boundary condition: } &&R_3(u, x, t) := u(x,t) - h(t)\\\\\n", + "&\\text{4) Residual of neumann boundary condition: } &&R_4(u, x, t) :=\\nabla_x u(x,t) \\cdot \\overset{\\rightarrow}{n}(x)\n", + "\\end{align}\n", + "$$\n", + "Continuing with the PINN approach, points are sampled in the domains corresponding to each condition. In our example points\n", + "$$\n", + "\\begin{align}\n", + "&\\text{1) } &&\\big(x^{(1)}_i, t_i^{(1)} \\big)_i &&&\\in \\Omega \\times I,\\\\\n", + "&\\text{2) } &&\\big(x^{(2)}_j, 0 \\big)_j &&&\\in \\Omega \\times \\{0\\},\\\\\n", + "&\\text{3) } &&\\big(x^{(3)}_k, t_k^{(3)} \\big)_k &&&\\in \\partial\\Omega_{heater} \\times I,\\\\\n", + "&\\text{4) } &&\\big(x^{(4)}_l, t_l^{(4)} \\big)_l &&&\\in (\\partial\\Omega \\setminus \\partial\\Omega_{heater}) \\times I.\n", + "\\end{align}\n", + "$$\n", + "Then, the network $u_\\theta$ is trained by solving the following minimization problem\n", + "$$\n", + "\\begin{align}\n", + "\\min_\\theta \\sum_{i} \\big\\vert R_1(u_\\theta, x^{(1)}_i, t_i^{(1)}) \\big \\vert^2 + \\sum_j \\big\\vert R_2(u_\\theta, x^{(2)}_j) \\big \\vert^2 + \\sum_k \\big\\vert R_3(u_\\theta, x^{(3)}_k, t_k^{(3)}) \\big \\vert^2 + \\sum_l \\big\\vert R_4(u_\\theta, x^{(4)}_l, t_l^{(4)}) \\big \\vert^2,\n", + "\\end{align}\n", + "$$\n", + "that is, the residuals are minimized with respect to the $l_2$-norm.\n", + "It is to be noted here that if data was available, one could simply add a data loss term to the loss function above." + ] + }, + { + "cell_type": "markdown", + "id": "8f0db4a0-cace-4d21-845f-f34680880d7d", + "metadata": { + "id": "8f0db4a0-cace-4d21-845f-f34680880d7d" + }, + "source": [ + "## Translating the PDE Problem into the Language of TorchPhysics\n", + "Translating the PDE problem into the framework of TorchPhysics works in a convenient and intuitive way, as the notation is close to the mathematical formulation. The general procedure can be devided into five steps. Also when solving other problems with TorchPhysics, such as parameter identification or variational problems, the same steps can be applied, see also the further [tutorials](https://boschresearch.github.io/torchphysics/tutorial/tutorial_start.html) or [examples](https://boschresearch.github.io/torchphysics/examples.html)." + ] + }, + { + "cell_type": "markdown", + "id": "e8fe0433-82b7-4093-8f6f-8adf7e46ff5b", + "metadata": { + "id": "e8fe0433-82b7-4093-8f6f-8adf7e46ff5b" + }, + "source": [ + "### Step 1: Specify spaces and domains\n", + "The spatial domain $\\Omega$ is a subset of the space $\\mathbb{R}^2$, the time domain $I$ is a subset of $\\mathbb{R}$, whereas the temperature $u(x,t)$ attains values in $\\mathbb{R}$. First, we need to let TorchPhysics know which spaces and domains we are dealing with and how variables/elements within these spaces are denoted by.\n", + "This is realized by generating objects of TorchPhysics' Space and Domain classes in \"tp.spaces\" and \"tp.domains\", respectively.\n", + "Some simple domains are already predefined, which will be sufficient for this tutorial. For creating complexer domains please have a look at the [domain-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_domain_basics.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6af0dba0-d481-4566-a8b7-244098eee713", + "metadata": { + "id": "6af0dba0-d481-4566-a8b7-244098eee713" + }, + "outputs": [], + "source": [ + "# Input and output spaces\n", + "X = tp.spaces.R2(variable_name='x')\n", + "T = tp.spaces.R1('t')\n", + "U = tp.spaces.R1('u')\n", + "\n", + "# Domains\n", + "Omega = tp.domains.Parallelogram(space=X, origin=[0,0], corner_1=[5,0], corner_2=[0,4])\n", + "I = tp.domains.Interval(space=T, lower_bound=0, upper_bound=20)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "1efe92cb-daab-4d21-8a43-5008e3e9248a", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 449 + }, + "id": "1efe92cb-daab-4d21-8a43-5008e3e9248a", + "outputId": "cdb09abd-279c-42b4-b96c-7f244d36d305" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The domain can be visualized by creating a sampler object, see also step 2, and use the scatter plot function from tp.utils.\n", + "Omega_sampler = tp.samplers.RandomUniformSampler(Omega, n_points=500)\n", + "plot = tp.utils.scatter(X, Omega_sampler)" + ] + }, + { + "cell_type": "markdown", + "id": "a1676bc3-8dab-4ce4-84ff-f8fc29e8b829", + "metadata": { + "id": "a1676bc3-8dab-4ce4-84ff-f8fc29e8b829" + }, + "source": [ + "### Step 2: Define point samplers for different subsets of $\\overline{\\Omega\\times I}$\n", + "As mentioned in the PINN recall, it will be necessary to sample points in different subsets of the full domain $\\overline{\\Omega\\times I}$. TorchPhysics provides this functionality by sampler classes in \"tp.samplers\". For simplicity, we consider only Random Uniform Samplers for the subdomains. However, there are many more possibilities to sample points in TorchPhysics, see also [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", + "\n", + "The most important inputs of a sampler constructor are the \"domain\" from which points will be sampled, as well as the \"number of points\" drawn every time the sampler is called. It is reasonable to create different sampler objects for the different conditions of the pde problem, simply because the subdomains differ.\n", + "\n", + "For instance, the pde condition 1) should hold for points in the domain $\\Omega \\times I$. We have already created $\\Omega$ and $I$ as TorchPhysics Domains in Step 1. Their cartesian product is simply obtained by the multiplication operator \"$*$\":" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d428cf7f-89ee-4f3f-a1bf-822b82550a7e", + "metadata": { + "id": "d428cf7f-89ee-4f3f-a1bf-822b82550a7e" + }, + "outputs": [], + "source": [ + "domain_pde_condition = Omega * I" + ] + }, + { + "cell_type": "markdown", + "id": "8db04580-edb8-45ac-8f48-091450647377", + "metadata": { + "id": "8db04580-edb8-45ac-8f48-091450647377" + }, + "source": [ + "Having the relevant domain on hand, we initialize as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d020f7f4-c286-466f-928d-1f80ee64c53f", + "metadata": { + "id": "d020f7f4-c286-466f-928d-1f80ee64c53f" + }, + "outputs": [], + "source": [ + "sampler_pde_condition = tp.samplers.RandomUniformSampler(domain=domain_pde_condition, n_points=1500)" + ] + }, + { + "cell_type": "markdown", + "id": "ac69b667-1a77-4e8a-8a20-2e0b5a1de2a0", + "metadata": { + "id": "ac69b667-1a77-4e8a-8a20-2e0b5a1de2a0" + }, + "source": [ + "There is an important alternative way of creating a sampler for a cartesian product of domains. Instead of defining the sampler on $\\Omega\\times I$, it is also possible to create samplers on $\\Omega$ and $I$ seperately, and multiply the samplers instead. This might be useful if different resolutions shall be considered, or when using other samplers in TorchPhysics such as a GridSampler, since a GridSampler cannot directly be created on a cartesian product in the way above." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3a1ee851-1bd4-4ee2-83e4-7dca3f883c0f", + "metadata": { + "id": "3a1ee851-1bd4-4ee2-83e4-7dca3f883c0f" + }, + "outputs": [], + "source": [ + "sampler_Omega = tp.samplers.GridSampler(domain=Omega, n_points=1000)\n", + "sampler_I = tp.samplers.RandomUniformSampler(domain=I, n_points=500)\n", + "alternative_sampler_pde_condition = sampler_Omega * sampler_I" + ] + }, + { + "cell_type": "markdown", + "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc", + "metadata": { + "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc" + }, + "source": [ + "For more detailed information on the functionality of TorchPysics samplers, please have a look at the [examples](https://boschresearch.github.io/torchphysics/examples.html) or [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", + "\n", + "Next, let us define samplers for the initial and boundary conditions. Regarding the initial condition the domain is $\\Omega \\times \\{0\\}$, so we need access to the left boundary of the time interval $I$. All tp.domains.Interval objects have the attribute \"left_boundary\", an instance of TorchPhysics BoundaryDomain class, a subclass of the Domain class." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e780f5fa-5ebf-4731-8568-77116ea039f6", + "metadata": { + "id": "e780f5fa-5ebf-4731-8568-77116ea039f6" + }, + "outputs": [], + "source": [ + "domain_initial_condition = Omega * I.boundary_left\n", + "sampler_initial_condition = tp.samplers.RandomUniformSampler(domain_initial_condition, 2500)" + ] + }, + { + "cell_type": "markdown", + "id": "7750bf6b-30ec-4ca9-8f37-9699439d0d22", + "metadata": { + "id": "7750bf6b-30ec-4ca9-8f37-9699439d0d22" + }, + "source": [ + "Both the Dirichlet and Neumann boundary conditions should hold on subsets of the boundary $\\partial \\Omega \\times I$. It is easier to use a sampler for the whole boundary and determine later (in Step 3, the definition of the residual functions) whether a sampled point belongs to the domain $\\partial \\Omega_{heater}\\times I$ of the Dirichlet condition, or to the domain $(\\partial \\Omega \\setminus \\partial \\Omega_{heater}) \\times I$ of the Neumann condition." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b627951a-a12b-4333-b965-35a56b8fc396", + "metadata": { + "id": "b627951a-a12b-4333-b965-35a56b8fc396" + }, + "outputs": [], + "source": [ + "domain_boundary_condition = Omega.boundary * I\n", + "sampler_boundary_condition = tp.samplers.RandomUniformSampler(domain_boundary_condition, 2500)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "c23a19e6-4167-4785-8323-984c319e2cb4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 449 + }, + "id": "c23a19e6-4167-4785-8323-984c319e2cb4", + "outputId": "f47cb4e2-7c58-41e3-8d5b-445b02ec45fa" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGwCAYAAABVdURTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABLS0lEQVR4nO3de3xU9Z0//teZ+0ySyYWQBEhAEORquIoGW28VEFhs7K4PF9zFUnW7fcCulq18S9cLlFZs1SpbvGBti+23WSx+F/pYi2KKAsuCSpBYwIqCQrjkCslMMpOcOTPn/P7gR2KahHzC+eQMZ3w9Hw8e7Jy8JvPZd6TnnXM+5/NRDMMwQERERJQiHMkeABEREZFMbG6IiIgopbC5ISIiopTC5oaIiIhSCpsbIiIiSilsboiIiCilsLkhIiKilOJK9gCspus6zpw5g4yMDCiKkuzhEBERkQDDMNDc3IzBgwfD4bj4tZkvXXNz5swZFBUVJXsYREREdAlOnjyJwsLCi2a+dM1NRkYGgPPFCQaDUr+3pml46623MGvWLLjdbqnfmzqwztZgna3BOluHtbZGf9U5HA6jqKio/Tx+MV+65ubCrahgMNgvzU0gEEAwGOQ/nH7EOluDdbYG62wd1toa/V1nkSklnFBMREREKYXNDREREaUUNjdERESUUtjcEBERUUphc0NEREQphc0NERERpRQ2N0RERJRS2NwQERFRSmFzQ0RERCnlS7dCcX9pjSWw5o8fYaoDmLd2FwzFiaDfjVH5acj0eVATVlETbsWnNc2IxBJQALicQMDjBmAAhoEEFAS9TkRjCSQMwOlUMCY/HQ5FwYmGKE42tSKuAy4HkJfuRtDvRVRLoC2eQG6aFxOLsjB7bAGaVA1ZPjfeOlyDP58JId3nQmGWH583RGAYCq7MCyCixvFJTQsiagKxRAIuh4LhA9Nw3RU5KP+4HqFWDVcODGBghhcVJ0JwO4GpQ7MxbVgO8oI+HDrZhM0fnkEsoWNothf1LRoaWmJI87pw69g85Af9yA648UFVIz46E8LJc1EkdAMOhwIYQCyhw+10YHR+OgqzA9hz7CzORmJwOxWMGxREus+FtriOgNsJv9uJ6rCKwZlejCrIQMVnDfjGQODOF/dgSJYf56IazrZoMBTAqQD1LTHEdR0F6R6oCQPVIRW6ARRkuHF1USZcTjccClCQ6UNOwIOzzSp2H6tHfbMK3VBQmOPHVfnpaNMMtGpx5Gf4MH5wBsoP1+DAyRDUuI7cdA8GZ/kRi+tQ4zq8LgeGZAWQ7nOiNtSKz862Is3jxKAsP2aNy0dTVMOh0yH8pTqMqBZHQdCHr43JhwJg+5E66LoOA4Df40LA48QVuWk4fDqMk+eicDqAEQPTMLogE4oCNLdqUBQFVwxIwz+WXAEAeGXPcbz32VnUhNvgcSrwuhzQ9QSONUQRjSUAKMhN92BkXhquys+EQ1FwLqKi4vg5NMcSGJDmwbAcP06cjeJcVEPQ58L8q/MxFMDf/Px/kDAcMAwDbVoccUPBuIJ0DMryI+Bx4U8f1eJsRIUCIC/ox5hBGQh4HDhaG0Vds4qBGW6Mys+AqhmIanFk+d04fKoJp8Mq3A4FuWkeJABk+NwYnZ+ODL8bh0+F0KrFcTaiAYYORXFgRF46Bmf6MHloDvLSvfioJoyK442IxDTAMOD3OKEoDuRleAEA4VYNH9eEUdeswu10YFiOH1OH58DjOP9vMxTVcCbUisFZflw/MhcTBmfie5sqceJcFBleF/526hB8dCaM6lAbakNtUBM6dN1AuDUGAwryM32YM7YAn9a3oOpcBGfCbdATBvIz/fjuLaPg87lwpjGKrYeqUVnVhBY1DrcDGDogDYpDgVNxYNzgDLgMA9d6gNnP7sSo/Awcq29FOKqiLW4AMBDTgcFBDwpz0nGsrhlqXMeQLD98HidqQq1oVROI6QZ8bgduvCoPs8bk478PVeN4fRjV4Rh0A8hN92JgmgNH6tvgAHDj6DzMGVeAhoiKiqpzOFLTgogax1X5GbgyL4AtB6oRbtNwxYAA1v/DNdh84BTePFSN2mYVuq4j1KohFjfgdzvg8zgRjydgQIFhAAnDgM/tRGbAg7x0D/KCPhRmB3DdiAHQdQP/74OT+EtNGAoUZAc8aFHjaGnT0BrXYegGWlQNTocCt9OJ/KAX8YSOuAG4nQ4UZvtRkOmHy6FgclE2BmX5cfWQTDzxxl9QebIRCd1AJBZHpC0Bp1PB1YMzMX3EAFw5MB2/+t/PcLKhBd8bB/zt87vREEmcH6vLgew0D/xeF3L8LnxeH8HZ1jhcDgWZPhc8TgfihoH8DC90w8CpxlY0qwn43A6MGJCGgUEfqs5FUN3UBsPQ4XY60RrXAcNAXoYXQZ8Ln9a1oFmNw+d2Iuhzo1U7fw7ID3oxtiATE4dmofJkEz6uDqMurMIAkJvuQUSNI6qdH2eGxwXDMBDXDbRqCbTFdfjcDuSm+5Dpc8LvOX86PxeN4VRjK9I9TlyZl4Fbx+bho+pmxBI6jlSHcfxsFK1aAm4HcOXAdIweFMSpxlY0tWpQ1Ti0hI7GVg0uhxMD0j0oyvKhMRpDfUsMiqJgQLoHdaE2nIvGoDgcuCLbh6w0L85FYkj3ujB6UAamFWXBBSChG0jWOtCKYRhGkj67kyeeeAIrVqzAAw88gGeffbbH3KZNm/DII4/g+PHjGDVqFH7yk59g7ty5wp8TDoeRmZmJUCgkbfuF+3+zD+Uf1cHrNPDT6Qksf98JNcEdx/sL69xBAdBf/4BZZ2uwztZhra1xoc6rP/Ri5e3F+JtJg6V8376cvy+L21L79u3D+vXrUVxcfNHcnj17sGDBAtx77704cOAASktLUVpaikOHDlk00q4uNDZEyXBZ/GZCRNSNZjWOpRsP4P7f7LP8s5Pe3LS0tODuu+/GL37xC2RnZ180u3btWtx222146KGHMHbsWKxevRpTpkzBunXrLBptZ62xBBsbIiKiiyj/qA4//uNHln5m0ufcLFmyBPPmzcOtt96KH/3oRxfN7t27F8uWLet0bPbs2diyZUuP71FVFaqqtr8Oh8MAzu9aqmnapQ8cwI//+zC8zo7fnb0Oo9Pf1D9YZ2uwztZgna3DWlujuzr/Zs9nePCWK+FxXfo1lb6cs5Pa3GzcuBEffPAB9u0Tu2RVU1OD/Pz8Tsfy8/NRU1PT43vWrFmDVatWdTn+1ltvIRAI9G3Af+UaF3DN9K7HV0/TTX1fEsM6W4N1tgbrbB3W2hp/Xec/vfWmqe8XjUaFs0lrbk6ePIkHHngA5eXl8Pl8/fY5K1as6HS1JxwOo6ioCLNmzTI9ofi6x/+Ellii/bXXYWD1NB2PVDig6pys1l9YZ2uwztZgna3DWlujpzpPKcrEb+697pK/74U7LyKS1tzs378fdXV1mDJlSvuxRCKBXbt2Yd26dVBVFU6ns9N7CgoKUFtb2+lYbW0tCgoKevwcr9cLr9fb5bjb7Ybbbe4hNa/bhbOtXX8DUHWFM/EtwDpbg3W2ButsHdbaGn9dZzWhmDrv9uW9SZtQ/LWvfQ0HDx5EZWVl+59p06bh7rvvRmVlZZfGBgBKSkqwffv2TsfKy8tRUlJi1bA7yU7zJOVziYiI7CbTb92qN0m7cpORkYEJEyZ0OpaWloYBAwa0H1+0aBGGDBmCNWvWAAAeeOAB3HjjjXj66acxb948bNy4ERUVFXjppZcsHz8A5Gf6cbi6JSmfTQT07zo3REQyhVrNPcTTF0l/Wupiqqqq4HB0XFyaMWMGysrK8PDDD+MHP/gBRo0ahS1btnRpkqySl971dld3RuelYcygTK5QLHGFYqAeYwsyuEJxP69QjOgnuGJAgCsU9/MKxUAVhmT5uEKxBSsUA1GMzkvjCsUSVyg+1hCFJjBHu65Z7T0kyWWzQrFVZK5QvPClPdjzWWP7655Wv5wxIhtl/zTD1GdRB03TsHXrVsydO9f0vCnqGetsDdbZOqx1/7j5p9vx+bm29tc9nQuH5/jwzvKvXfLn2G6FYruqDsek5oiIiOxGV8QmZ4vmZGBzY0JBUGxCsWiOiIjIbgKCC/OJ5mRgc2NCUY7YIoCiOSIiIrsJtcWl5mRgc2PCp7ViT0qJ5oiIiOwmFhdb8Vk0JwObGxNOnmuVmiMiIrId0ak0Fq6byObGBIci9qCZaI6IiMhupg/NkpqTgc2NCV7ByVGiOSIiIrvJTBPbH1I0JwPPuiZo8UTvoT7kiIiI7OZ0o9i8UtGcDGxuTFAF50aJ5oiIiOzm/RMhqTkZ2NyYkJsmtv2CaI6IiMhuNJG9F/qQk4HNjQkTC7Ok5oiIiOzG7RJ7DEo0JwObGxMO14hdYhPNERER2c3ovHSpORnY3JhQHxbb4VQ0R0REZDfpfrEthkRzMrC5McEhWD3RHBERkd1k+Z1SczLwtGuCLjg3SjRHRERkNyfOtUnNycDmhoiIiC6ZAbFV+EVzMrC5MaG4MCg1R0REZDdep+Bq/YI5GdjcmHD39Cuk5oiIiOwm0++SmpOBzY0J7x0/JzVHRERkN7XNYk8Ei+ZkYHNjwlsf1UjNERER2U0oqknNycDmxoRzgj8o0RwREZHduF1irYRoTgY2Nya4FcElpwVzREREdjN8QEBqTgY2NyZcmeeXmiMiIrKbdJ/gCsWCORnY3JjgVMRWWxTNERER2U2blpCak4HNjQlnIzGpOSIiIrtJ6GKL84nmZGBzY4JhCK7KKJgjIiKym3MtrVJzMrC5McEhuJS0aI6IiMhuGiJxqTkZ2NyYUB0WW5BINEdERETmsbkxoTUutt23aI6IiMhu8oJeqTkZ2NyY4HOKrV8jmiMiIrKbmeMKpOZkYHNjwpAssfVrRHNERER2MyYvQ2pOhqQ2Ny+88AKKi4sRDAYRDAZRUlKCN954o8f8hg0boChKpz8+n8/CEXfm84jtcCqaIyIispvNlaek5mRI6lm3sLAQTzzxBEaNGgXDMPDKK6/g61//Og4cOIDx48d3+55gMIgjR460v1aSuLWBJjiVRjRHRERkNx+cbJKakyGpzc38+fM7vf7xj3+MF154Ae+++26PzY2iKCgosO6+3cW4Ba97ieaIiIjsRnQNfivX6r9s7pckEgls2rQJkUgEJSUlPeZaWlowbNgw6LqOKVOm4PHHH++xEQIAVVWhqh2PYofDYQCApmnQNHO7dUdaVXidHWvYeB1Gp7+/mDP7WdThQi1Z0/7FOluDdbYOa90/Am5F6FwYcCumat+X9ypGkpfPPXjwIEpKStDW1ob09HSUlZVh7ty53Wb37t2LTz/9FMXFxQiFQnjqqaewa9cuHD58GIWFhd2+Z+XKlVi1alWX42VlZQgErNuhlIiIiC5dNBrFwoULEQqFEAwGL5pNenMTi8VQVVWFUCiE1157DS+//DJ27tyJcePG9fpeTdMwduxYLFiwAKtXr+42092Vm6KiIjQ0NPRanN7c+NO3cTba0Ul6HQZWT9PxSIUDqt4xF2hAwI2dy28x9VnUQdM0lJeXY+bMmXC73ckeTspina3BOluHte4fNz/5NuojvZ8LB6a58c5Dl34uDIfDyM3NFWpukn5byuPxYOTIkQCAqVOnYt++fVi7di3Wr1/f63vdbjcmT56Mo0eP9pjxer3wersuHOR2u03/x604HFATXSc0q7rS6bjicPAfUj+Q8TOk3rHO1mCdrcNay9UWN4TOhW1xw1Td+/Ley26qq67rna60XEwikcDBgwcxaNCgfh5V9/KDYuvXiOaIiIjsJib4RLBoToakXrlZsWIF5syZg6FDh6K5uRllZWXYsWMHtm3bBgBYtGgRhgwZgjVr1gAAfvjDH+K6667DyJEj0dTUhCeffBInTpzAfffdl5Txj8rPwAcnQ0I5IiKiVOQQXJJFNCdDUpuburo6LFq0CNXV1cjMzERxcTG2bduGmTNnAgCqqqrgcHRcXGpsbMT999+PmpoaZGdnY+rUqdizZ4/Q/Jz+EFXFZm6L5oiIiOzG5RBrWkRzMiS1ufnlL3950a/v2LGj0+tnnnkGzzzzTD+OqG+O1Eak5oiIiOymMNuPhkjvv8QXZls3ReOym3NjJwnBB81Ec0RERHaTHfBIzcnA5saEUKRNao6IiMhuGiNiDwGJ5mRgc2NCi5qQmiMiIrKb+khMak4GNjcmOB1i5RPNERER2U3ALTZ9VzQnA8+6JqT5xH5QojkiIiK7yQt2XSjXTE4GNjcm+F1i5RPNERER2c3VQ7Kk5mTgWdcEXfApKNEcERGR3aR7nVJzMrC5MSEaE5soLJojIiKymzcP10rNycDmxgRNcJ8M0RwREZHdNEXFnoISzcnA5saEwUGxBYlEc0RERHbjd4vdbhLNycDmxoTJQ3Ok5oiIiOwmL13sF3jRnAxsbkxwOgXXuRHMERER2c3+k01SczLwrGuClhCbTCOaIyIishstIfZEsGhOBjY3Jhw63SQ1R0REZDcZggvViuZkYHNjQl2L2Mxv0RwREZHdLL5+uNScDGxuTPC5FKk5IiIiu8kP+qTmZGBzY0Ka4GNtojkiIiK7+f2+Kqk5GdjcmKAKLjwsmiMiIrKbE2cjUnMysLkxYXCW2CU20RwREZHdhNviUnMysLkx4doRYovzieaIiIjsxu0Um1cqmpOBzY0J733WKDVHRERkN5l+t9ScDGxuTDh1Tuz+oWiOiIjIbkblp0vNycDmxoRQq9j6NaI5IiIiu6luUqXmZGBzY4IOsfuHojkiIiK7aRacKCyak4HNjQkZPrH7h6I5IiIiuzEMsf0TRXMysLkx4Ssjc6XmiIiI7CYnzSM1JwObGxOmDM2SmiMiIrKbQVkBqTkZ2NyYcOhMWGqOiIjIbgIesS2GRHMysLkxQTfk5oiIiOzmdGNUak4GNjcmJHSxyVGiOSIiIrs5GxVb7kQ0JwObGxM+PCm28rBojoiIyG5UTewXeNGcDEltbl544QUUFxcjGAwiGAyipKQEb7zxxkXfs2nTJowZMwY+nw9XX301tm7datFouzrV2Co1R0REZDcuh1grIZqTIanNTWFhIZ544gns378fFRUVuOWWW/D1r38dhw8f7ja/Z88eLFiwAPfeey8OHDiA0tJSlJaW4tChQxaP/LyEYBMqmiMiIrKbITk+qTkZktrczJ8/H3PnzsWoUaNw1VVX4cc//jHS09Px7rvvdptfu3YtbrvtNjz00EMYO3YsVq9ejSlTpmDdunUWj/y8dJ9Lao6IiMhunIpYKyGak+GyOesmEgls2rQJkUgEJSUl3Wb27t2LZcuWdTo2e/ZsbNmypcfvq6oqVLVjP4tw+Pxj2ZqmQdM0U2PO9CgIOTsehfI6jE5/fzFn9rOow4Vasqb9i3W2ButsHda6f4RaWuEVOBeGWlpN1b4v71UMw0jqg8oHDx5ESUkJ2trakJ6ejrKyMsydO7fbrMfjwSuvvIIFCxa0H3v++eexatUq1NbWdvuelStXYtWqVV2Ol5WVIRCwbkEhIiIiunTRaBQLFy5EKBRCMBi8aDbpV25Gjx6NyspKhEIhvPbaa7jnnnuwc+dOjBs3Tsr3X7FiRaerPeFwGEVFRZg1a1avxenN8k0fYuvhmvbXXoeB1dN0PFLhgKp3bJY5d3wBfnrnRFOfRR00TUN5eTlmzpwJt5v7dvUX1tkarLN1WOv+cc8v92L/yY7Fans6F04tCuKVe7u/MyPiwp0XEUlvbjweD0aOHAkAmDp1Kvbt24e1a9di/fr1XbIFBQVdrtDU1taioKCgx+/v9Xrh9Xq7HHe73ab/43Z7XFATXXf8VnWl03G3x8V/SP1Axs+Qesc6W4N1tg5rLdeZ5rjQufBMc9xU3fvy3stunRtd1zvNkfmikpISbN++vdOx8vLyHufo9LcDJ5qk5oiIiOxGjcWl5mRI6pWbFStWYM6cORg6dCiam5tRVlaGHTt2YNu2bQCARYsWYciQIVizZg0A4IEHHsCNN96Ip59+GvPmzcPGjRtRUVGBl156KSnjb1bFflCiOSIiIrtxOJwAep/sez5njaQ2N3V1dVi0aBGqq6uRmZmJ4uJibNu2DTNnzgQAVFVVwfGFRX9mzJiBsrIyPPzww/jBD36AUaNGYcuWLZgwYUJSxu8WvO4lmiMiIrKbTL8bp0NtQjmrJLW5+eUvf3nRr+/YsaPLsTvvvBN33nlnP42ob7i3FBERfdkZhtg5TjQnA68pmJAQLJ9ojoiIyG4isYTUnAw865rgc4mVTzRHRERkNxHBeaWiORl41jUhS3BbBdEcERGR3bRpYldkRHMysLkxISTYhYrmiIiI7EYX3OdANCcDmxsTAm6xx9pEc0RERHbj94jdnRDNycDmxgSf4DPeojkiIiK7uXVcntScDDzrmpDuFetCRXNERER2c8tV+VJzMrC5MeHDk01Sc0RERHazbsdRqTkZ2NyY0BITW5BINEdERGQ3tc29r07cl5wMbG5MuBxniBMREVkpL90rNScDmxsTvIIPQYnmiIiI7Gb2BLG5NKI5GdjcmMCnpYiI6MvuWF1Uak4GnnVNcApu3y6aIyIisptwmyo1JwObGxMSgjuciuaIiIjs5uQ5sYnCojkZ2NyY4FLEyieaIyIispvGiFjTIpqTgWddEzL9YovzieaIiIjsRhO8OSGak4HNjQlewcegRHNERER2M1DwEW/RnAxsbkwoyPBLzREREdkNN85MMYoiN0dERGQ3eekeqTkZ2NyYYOhiNxBFc0RERHYj+syMlc/WsLkxobY5JjVHRERkN5fjVkRsbkzQEmI/KdEcERGR3dQ3iy3OJ5qTgc2NCRk+sclRojkiIiK7cQrOKxXNycDmxoRZ4wuk5oiIiOwm1JaQmpOBzY0Ji68fLjVHRERkN4YhNvVCNCcDmxsTEoKzo0RzREREdnM5LovC5saEH//xsNQcERGR3QwfEJCak4HNjQkfngpJzREREdmNwyHWSojmZGBzY0K6V+wpKNEcERGR3SiC95tEczKwuTEh4BYrn2iOiIjIbgKCe0aJ5mTgWdeEZlWTmiMiIrKbDI9Tak6GpDY3a9aswTXXXIOMjAzk5eWhtLQUR44cueh7NmzYAEVROv3x+XwWjbizY/VRqTkiIiK70RWxJ4JFczIktbnZuXMnlixZgnfffRfl5eXQNA2zZs1CJBK56PuCwSCqq6vb/5w4ccKiEXcWi4ttiCmaIyIispvapjapORmSOtP1zTff7PR6w4YNyMvLw/79+3HDDTf0+D5FUVBQkPxVfy/HSVRERERWOtXUKjUnw2X1GE8odP6R6ZycnIvmWlpaMGzYMOi6jilTpuDxxx/H+PHju82qqgpV7disKxwOAwA0TYOmmZsLc9OV2dj2cV37a6/D6PT3F3NmP4s6XKgla9q/WGdrsM7WYa37hwIdXmfHea+nc6EC3VTt+/JexbByPeSL0HUdt99+O5qamrB79+4ec3v37sWnn36K4uJihEIhPPXUU9i1axcOHz6MwsLCLvmVK1di1apVXY6XlZUhELBuQSEiIiK6dNFoFAsXLkQoFEIwGLxo9rJpbr7zne/gjTfewO7du7ttUnqiaRrGjh2LBQsWYPXq1V2+3t2Vm6KiIjQ0NPRanN689PZR/MeuY+2vvQ4Dq6fpeKTCAVXvuBX1rzdciX+6ZaSpz6IOmqahvLwcM2fOhNvtTvZwUhbrbA3W2Tqsdf94dMtB/FflmfbXPZ0LvzFpMH5YevUlf044HEZubq5Qc3NZ3JZaunQpXn/9dezatatPjQ0AuN1uTJ48GUePHu32616vF16vt9v3mf2P+9f7TkFNdJ1Po+pKp+O/3ncKS2aPNfVZ1JWMnyH1jnW2ButsHdZarnePh4TOhe8eD5mqe1/em9SnpQzDwNKlS7F582a8/fbbGD6877tnJxIJHDx4EIMGDeqHEV5cS5vY/T/RHBERkd2oWkJqToakXrlZsmQJysrK8Ic//AEZGRmoqakBAGRmZsLv9wMAFi1ahCFDhmDNmjUAgB/+8Ie47rrrMHLkSDQ1NeHJJ5/EiRMncN9991k+ftH7eZfFfT8iIqJ+ENfFmhbRnAxJbW5eeOEFAMBNN93U6fivf/1rfPOb3wQAVFVVddpsq7GxEffffz9qamqQnZ2NqVOnYs+ePRg3bpxVw243IODGmXBMKEdERJSKHBBb7kQ0J0NSmxuRucw7duzo9PqZZ57BM888008j6pu8oE+ouckLJmcFZSIiov4WF7w9IZqTgXtLmSC6TYaF22kQERFZKlPw7oRoTgY2NyZ8Uiu2Z5RojoiIyG5UTWyLIdGcDGxuTNASYj8o0RwREZHdFGaJTb0QzcnA5sYEp2D1RHNERER2k+73SM3JwNOuCX6P2Hxs0RwREZHdjMhNk5qTgc2NCcMFf1CiOSIiIrupDbdJzcnA5saEoTliG2+K5oiIiOymvlntPdSHnAxsbkz4tK5Zao6IiMhu6lsEmxvBnAxsbkyob+59Ab++5IiIiOzG0MVW5xPNycDmxgRd8AclmiMiIrIbj0uslRDNycDmxgS/R2yfDNEcERGR3fjcYsvwi+ZkYHNjQkIXa1pEc0RERHbD5ibFBHxi69eI5oiIiOxmQJrYnlGiORnY3JjgE1x6WDRHRERkNw0RsYdmRHMy8KxrwsiCdKk5IiIiu6kPCz45LJiTgc2NCV6n2P1D0RwREZHdNEbF1q8RzcnA5saEiYVZUnNERER2o8Z1qTkZ2NyY0CC4lLRojoiIyG4UQ6xpEc3JwObGhP/5tF5qjoiIyG4MwVZCNCcDmxsTzjS1Ss0RERHZjeIQW8tNNCcDmxsTEobYtgqiOSIiIrsJeAXXfBPMycDmxoSIGpeaIyIisptpQzOl5mRgc2OC6MRvCyeIExERWWpwVkBqTgY2NyZk+sXWrxHNERER2U3FiXNSczKwuTFhUKZfao6IiMhu6prFVh4WzcnA5sYE3pYiIqIvu7gudpITzcnA5saEy3GbdyIiIisFnGKPeIvmZOhTc/Phhx/iRz/6EZ5//nk0NDR0+lo4HMa3vvUtqYO73BUXic38Fs0RERHZzdmoJjUng3Bz89Zbb2H69OnYuHEjfvKTn2DMmDF455132r/e2tqKV155pV8GebmaMSJXao6IiMhuRFdys3LFN+HmZuXKlfje976HQ4cO4fjx41i+fDluv/12vPnmm/05vsuarov9qERzREREdmPrRfwOHz7cfttJURQsX74c69evx9/93d/h9ddf77cBXs62VJ6WmiMiIrKbsQUZUnMyCDc3Xq8XTU1NnY4tXLgQL7/8Mu666y5s3ry5zx++Zs0aXHPNNcjIyEBeXh5KS0tx5MiRXt+3adMmjBkzBj6fD1dffTW2bt3a58+WoblN7P6haI6IiMhubH1batKkSZ3m2Fzw93//93j55Zfxr//6r33+8J07d2LJkiV49913UV5eDk3TMGvWLEQikR7fs2fPHixYsAD33nsvDhw4gNLSUpSWluLQoUN9/nyzzkXEntkXzREREdnNpzUtUnMyCN8A+853voNdu3Z1+7UFCxbAMAz84he/6NOH//V8nQ0bNiAvLw/79+/HDTfc0O171q5di9tuuw0PPfQQAGD16tUoLy/HunXr8OKLL/bp883yuMQeaxPNERER2c1ZwV/gRXMyCDc3d9xxB+64444ev75w4UIsXLiw/fV//ud/4vbbb0daWprwYEKhEAAgJyenx8zevXuxbNmyTsdmz56NLVu2dJtXVRWqqra/DofDAABN06Bp5m4XGYkEvM6OC21eh9Hp7y/mzH4WdbhQS9a0f7HO1mCdrcNa9480N/DFm049nQvT3Iqp2vflvYphGP1yGywYDKKyshIjRowQyuu6jttvvx1NTU3YvXt3jzmPx4NXXnkFCxYsaD/2/PPPY9WqVaitre2SX7lyJVatWtXleFlZGQIB6zbxIiIioksXjUaxcOFChEIhBIPBi2b77bmsvvZMS5YswaFDhy7a2FyKFStWdLrSEw6HUVRUhFmz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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualization of sampled points at the boundary\n", + "plot = tp.utils.scatter(X, sampler_boundary_condition)" + ] + }, + { + "cell_type": "markdown", + "id": "6b1b87f9-b6d6-44ec-8fb5-833ab466d89b", + "metadata": { + "id": "6b1b87f9-b6d6-44ec-8fb5-833ab466d89b" + }, + "source": [ + "### Step 3: Define residual functions\n", + "As mentioned in the PINNs Recall, we are looking for a neural network $u_\\theta$ for which all of the residual functions $R_1,...,R_4$ vanish.\n", + "\n", + "Let us have a look at $R_1$, the residual for the pde condition, the way it is defined in the PINNs Recall above. The inputs of $R_1$ are spatial and temporal coordinates $x\\in \\Omega$, $t\\in I$, but also the temperature $u_\\theta$, which is itself a function of $x$ and $t$. In TorchPhysics, the evaluation of the network $u_\\theta$ at $(x,t)$ is done before evaluating the residual functions. This means that from now on we consider $R_1$ as well as the other residuals to be functions, whose inputs are triples $(u, x, t)$, where $u:=u_\\theta(x,t)$.\n", + "\n", + "More precisely, $u$ will be a torch.tensor of shape (n_points, 1), $x$ of shape (n_points, 2) and $t$ of shape (n_points, 1), where n_points is the number of triples $(u,x,t)$ for which the residual should be computed.\n", + "\n", + "For the residual $R_1$ it is required to compute the laplacian of $u$ with respect to $x$, as well as the gradient with respect to $t$. These differential operators, among others - see [utils-tutorial](https://boschresearch.github.io/torchphysics/tutorial/differentialoperators.html), are pre-implemented and can be found in \"tp.utils\". The intern computation is build upon torch's autograd functionality." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c29f3f92-d613-470f-ab74-9369e071ea04", + "metadata": { + "id": "c29f3f92-d613-470f-ab74-9369e071ea04" + }, + "outputs": [], + "source": [ + "def residual_pde_condition(u, x, t):\n", + " return tp.utils.laplacian(u, x) - tp.utils.grad(u, t)" + ] + }, + { + "cell_type": "markdown", + "id": "e444a2e5-6fc6-4124-894c-1ba987153241", + "metadata": { + "id": "e444a2e5-6fc6-4124-894c-1ba987153241" + }, + "source": [ + "For the computation of the residual $R_2$ of the initial condition, the coordinates $x$ and $t$ are not required, since $u$ is already the evaluation of the network at these points. Therefore, we can conveniently omit them as input parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "65954de9-4c80-4d2a-be6e-0cd16ab82596", + "metadata": { + "id": "65954de9-4c80-4d2a-be6e-0cd16ab82596" + }, + "outputs": [], + "source": [ + "def residual_initial_condition(u):\n", + " return u - u_0" + ] + }, + { + "cell_type": "markdown", + "id": "97b9bfba-5cd3-400c-8c5a-4cd48b320c80", + "metadata": { + "id": "97b9bfba-5cd3-400c-8c5a-4cd48b320c80" + }, + "source": [ + "In Step 2, we defined a boundary sampler for $\\partial \\Omega \\times I$, the domain for the boundary conditions. Hence, the sampler does not differ between the domain of the Dirichlet and Neumann boundary conditions. This is why we define a combined residual function $R_b$ for $R_3$ and $R_4$, which will output\n", + "$$\n", + "\\begin{align}\n", + "R_b(u, x, t) = \\begin{cases}\n", + "R_3(u, x, t) &\\text{ if } &&x \\in \\partial \\Omega_{heater},\\\\\n", + "R_4(u, x, t) &\\text{ if } &&x \\in \\partial \\Omega \\setminus \\partial \\Omega_{heater}.\n", + "\\end{cases}\n", + "\\end{align}\n", + "$$\n", + "Let us start with the defintion of the Dirichlet residual $R_3$:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "c97e8bfe-1580-4bb8-bb1b-d4c874ef6244", + "metadata": { + "id": "c97e8bfe-1580-4bb8-bb1b-d4c874ef6244" + }, + "outputs": [], + "source": [ + "def residual_dirichlet_condition(u, t):\n", + " return u - h(t)" + ] + }, + { + "cell_type": "markdown", + "id": "de441693-0870-43db-8d8d-38777a075432", + "metadata": { + "id": "de441693-0870-43db-8d8d-38777a075432" + }, + "source": [ + "For the Neumann residual $R_4$ we need the normal derivative of $u$ at $x$. This differential operator is also contained in \"tp.utils\", whereas the normal vectors at points $x\\in \\partial \\Omega$ are available by the attribute \"normal\" of the \"boundary\" of the domain $\\Omega$." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "17d5e293-57bd-4739-9518-a014f6df2b79", + "metadata": { + "id": "17d5e293-57bd-4739-9518-a014f6df2b79" + }, + "outputs": [], + "source": [ + "def residual_neumann_condition(u, x):\n", + " normal_vectors = Omega.boundary.normal(x)\n", + " normal_derivative = tp.utils.normal_derivative(u, normal_vectors, x)\n", + " return normal_derivative" + ] + }, + { + "cell_type": "markdown", + "id": "463e507e-d33b-4f8d-9149-c45356fdf236", + "metadata": { + "id": "463e507e-d33b-4f8d-9149-c45356fdf236" + }, + "source": [ + "The combined boundary residual $R_b$ is then easily obtained as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "4864c6ed-6f2b-4f80-bd6f-cd8ff3d8a809", + "metadata": { + "id": "4864c6ed-6f2b-4f80-bd6f-cd8ff3d8a809" + }, + "outputs": [], + "source": [ + "def residual_boundary_condition(u, x, t):\n", + " # Create boolean tensor indicating which points x belong to the dirichlet condition (heater location)\n", + " heater_location = (x[:, 0] >= 1 ) & (x[:, 0] <= 3) & (x[:, 1] >= 3.99)\n", + " # First compute Neumann residual everywhere, also at the heater position\n", + " residual = residual_neumann_condition(u, x)\n", + " # Now change residual at the heater to the Dirichlet residual\n", + " residual_h = residual_dirichlet_condition(u, t)\n", + " residual[heater_location] = residual_h[heater_location]\n", + " return residual" + ] + }, + { + "cell_type": "markdown", + "id": "0cc89ada-310b-4a84-bcc0-77baa7afca2c", + "metadata": { + "id": "0cc89ada-310b-4a84-bcc0-77baa7afca2c" + }, + "source": [ + "### Step 4: Define Neural Network\n", + "At this point, let us define the model $u_\\theta:\\overline{\\Omega\\times I}\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", + "There are also a bunch of predefined neural networks or single layers available, e.g. fully connected networks (FCN) or normalization layers, which are subclasses of TorchPhysics' Model class.\n", + "In this tutorial we consider a very simple neural network, constructed in the following way:\n", + "\n", + "We start with a normalization layer, which maps points $(x,t)\\in \\overline{\\Omega\\times I}\\subset \\mathbb{R}^3$ into the cube $[-1, 1]^3$." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "bdef3d80-90e6-47aa-95ce-6d735fd03f36", + "metadata": { + "id": "bdef3d80-90e6-47aa-95ce-6d735fd03f36" + }, + "outputs": [], + "source": [ + "normalization_layer = tp.models.NormalizationLayer(Omega*I)" + ] + }, + { + "cell_type": "markdown", + "id": "75e0d506-13f0-4e39-882b-d752c89fe7fc", + "metadata": { + "id": "75e0d506-13f0-4e39-882b-d752c89fe7fc" + }, + "source": [ + "Afterwards, the scaled points will be passed through a fully connected network. The constructor requires to include the input space $X\\times T$, output space $U$ and ouput dimensions of the hidden layers. Remember the definition of the TorchPyhsics spaces $X,T$ and $U$ from Step 1. Similar as for domains, the cartesian product of spaces is obtained by the multiplication operator \"$*$\". Here, we consider a fully connected network with four hidden layers, the latter consisting of $80, 50, 50$ and $50$ neurons, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "fa15606a-a2c7-40bf-9e41-920c8f6a1bc9", + "metadata": { + "id": "fa15606a-a2c7-40bf-9e41-920c8f6a1bc9" + }, + "outputs": [], + "source": [ + "fcn_layer = tp.models.FCN(input_space=X*T, output_space=U, hidden = (80,50,50,50))" + ] + }, + { + "cell_type": "markdown", + "id": "694d8666-170e-4c28-a87a-73aa329e2094", + "metadata": { + "id": "694d8666-170e-4c28-a87a-73aa329e2094" + }, + "source": [ + "Similar to Pytorch, the normalization layer and FCN can be concatenated by the class \"tp.models.Sequential\":" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "9b838d6f-1b90-4667-8ecb-9f54b4ec627e", + "metadata": { + "id": "9b838d6f-1b90-4667-8ecb-9f54b4ec627e" + }, + "outputs": [], + "source": [ + "model = tp.models.Sequential(normalization_layer, fcn_layer)" + ] + }, + { + "cell_type": "markdown", + "id": "17e3f8ab-bd6c-4f4f-94a6-030930458c0c", + "metadata": { + "id": "17e3f8ab-bd6c-4f4f-94a6-030930458c0c" + }, + "source": [ + "### Step 5: Create TorchPhysics Conditions\n", + "Let us sum up what we have done so far: For the pde, initial and combined boundary condition of the PDE problem, we constructed samplers and residuals on the corresponding domains.\n", + "Moreover, we have defined a neural network which will later be trained to fulfull each of these conditions.\n", + "\n", + "As a final step, we collect these constructions for each condition in an object of the TorchPhysics Condition class, contained in \"tp.conditions\".\n", + "Since we are interested in applying a PINN approach, we create objects of the subclass PINNCondition, which automatically contains the information that the residuals should be minimized in the squared $l_2$-norm, see again the PINN Recall. For other TorchPhysics Conditions one may need to specify which norm should be taken of the residuals, see [condition-tutorial](https://boschresearch.github.io/torchphysics/tutorial/condition_tutorial.html) for further information." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "008c09a7-81f8-41b5-8c10-3892812740ad", + "metadata": { + "id": "008c09a7-81f8-41b5-8c10-3892812740ad" + }, + "outputs": [], + "source": [ + "pde_condition = tp.conditions.PINNCondition(module =model,\n", + " sampler =sampler_pde_condition,\n", + " residual_fn=residual_pde_condition)\n", + "\n", + "initial_condition = tp.conditions.PINNCondition(module =model,\n", + " sampler =sampler_initial_condition,\n", + " residual_fn=residual_initial_condition)\n", + "\n", + "boundary_condition = tp.conditions.PINNCondition(module =model,\n", + " sampler =sampler_boundary_condition,\n", + " residual_fn=residual_boundary_condition)" + ] + }, + { + "cell_type": "markdown", + "id": "5cd77316-3c78-4bf1-b639-9ccb7070af2d", + "metadata": { + "id": "5cd77316-3c78-4bf1-b639-9ccb7070af2d" + }, + "source": [ + "It is to be noted that TorchPhysics' Condition class is a subclass of the torch.nn.Module class and its forward() method returns the current loss of the respective condition.\n", + "For example, calling forward() of the pde_condition at points $(x_i, t_i)_i$ in $\\Omega\\times I$ will return\n", + "$$\n", + "\\begin{align}\n", + "\\sum_i \\big \\vert R_1(u_\\theta, x_i, t_i) \\big \\vert^2,\n", + "\\end{align}\n", + "$$\n", + "where $R_1$ is the residual function for the pde condition defined in the PINN recall and $u_\\theta$ is the model defined in Step 4." + ] + }, + { + "cell_type": "markdown", + "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac", + "metadata": { + "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac" + }, + "source": [ + "The reason that also the model is required for initializing a Condition object is, that it could be desireable in some [cases](https://github.com/boschresearch/torchphysics/blob/main/examples/pinn/interface-jump.ipynb) to train different networks for different conditions of the PDE problem." + ] + }, + { + "cell_type": "markdown", + "id": "31d80c43-5879-401c-8212-0e4a5fd6514c", + "metadata": { + "id": "31d80c43-5879-401c-8212-0e4a5fd6514c" + }, + "source": [ + "## Training based on Pytorch Lightning\n", + "In order to train a model, TorchPhysics makes use of the Pytorch Lightning library, which hence must be imported. Further, we import \"os\" so that GPUs can be used for the calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "bb76e892-bf53-4a01-adc5-74dddb770525", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bb76e892-bf53-4a01-adc5-74dddb770525", + "outputId": "90104470-f0c5-40ab-e922-3c9915202fd6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GPU available: False\n" + ] + } + ], + "source": [ + "import pytorch_lightning as pl\n", + "import os\n", + "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\" if torch.cuda.is_available() else \"0\"\n", + "\n", + "print (\"GPU available: \" + str(torch.cuda.is_available()))" + ] + }, + { + "cell_type": "markdown", + "id": "1639cf38-835b-4571-b0c5-7ef0d130c2df", + "metadata": { + "id": "1639cf38-835b-4571-b0c5-7ef0d130c2df" + }, + "source": [ + "For the training process, i.e. the minimization of the loss function introduced in the PINN recall, TorchPhysics provides the Solver class. It inherits from the pl.LightningModule class and is compatible with the TorchPhysics library. The constructor requires a list of TorchPhysics Conditions, whose parameters should be optimized during the training." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "ea27b608-e319-4fac-85c1-5984f2d043c6", + "metadata": { + "id": "ea27b608-e319-4fac-85c1-5984f2d043c6" + }, + "outputs": [], + "source": [ + "training_conditions = [pde_condition, initial_condition, boundary_condition]" + ] + }, + { + "cell_type": "markdown", + "id": "e024913e-e10e-4387-b390-165e77c8524b", + "metadata": { + "id": "e024913e-e10e-4387-b390-165e77c8524b" + }, + "source": [ + "By default, the Solver uses the Adam Optimizer from Pytorch with learning rate $lr=0.001$ for optimizing the training_conditions. If a different optimizer or choice of its arguments shall be used, one can collect these information in an object of TorchPhysics' OptimizerSetting class. Here we choose the Adam Optimizer from Pytorch with a learning rate $lr=0.002$." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "b1848d26-ea33-400c-84be-2291429e8065", + "metadata": { + "id": "b1848d26-ea33-400c-84be-2291429e8065" + }, + "outputs": [], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.0005)" + ] + }, + { + "cell_type": "markdown", + "id": "efcd0c8c-1ef2-45a0-bf00-de88201f3d03", + "metadata": { + "id": "efcd0c8c-1ef2-45a0-bf00-de88201f3d03" + }, + "source": [ + "Finally, we are able to create the Solver object, a Pytorch Lightning Module." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "4ea2cb3f-087c-4e03-aeb0-40318f556062", + "metadata": { + "id": "4ea2cb3f-087c-4e03-aeb0-40318f556062" + }, + "outputs": [], + "source": [ + "solver = tp.solver.Solver(train_conditions=training_conditions, optimizer_setting=optim)" + ] + }, + { + "cell_type": "markdown", + "id": "53dec402-5dd2-40f9-a405-5170d0cfcbd7", + "metadata": { + "id": "53dec402-5dd2-40f9-a405-5170d0cfcbd7" + }, + "source": [ + "Now, as usual, the training is done with a Pytorch Lightning Trainer object and its fit() method." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "9ea9431a-9ea4-4312-8869-af4c8c4733a4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 327, + "referenced_widgets": [ + "c0c85ee450fa4382b8ba6d076bbe15ab", + "b51fd3c0f34740719b885c8931acb307", + "b0f8c43fb244435fa250e90aa431a910", + "b78a8f1dc2f34425b15c9f4b785e59c7", + "c45982b9a83b47779e614305dbbfc2ac", + "cafdac53e72d48bba71a7c98538c5354", + "51d400ec3dab4d3db89d4ae1212e0cce", + "2a351ae394444086b648b09ffa58c06d", + "f8c630032aea45f6b3f97e838d7b74de", + "d66ce69c61594d8c972a49763e91d599", + "acd31f72a0184f859d18ad08567a61aa", + "8c926f8700a64539818ed8fe7b54bdea", + "30a318be130447cea4842d46d887360f", + "367e26e3fcff4add996e6b9c39cce77c", + "4a071277015d42e2876abd2fcec77f60", + "d7106f73c09d4ffc94f9146307913a93", + "436ad1ff35d841408e32383cd6ce6cb7", + "9e32d83238be4720909a1606779ce4a1", + "600a325e05fd4ead94806e30de2dfe91", + "d8765650eeba4c84be216cf3ffd27b0d", + "b026dd8e870a4557bc5583b5a509f59e", + "5a685209ec8e4496831015718305bacb" + ] + }, + "id": "9ea9431a-9ea4-4312-8869-af4c8c4733a4", + "outputId": "6e93ce9c-379c-4f4f-b946-16cf8b9652a5" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pytorch_lightning.utilities.rank_zero:GPU available: False, used: False\n", + "INFO:pytorch_lightning.utilities.rank_zero:TPU available: False, using: 0 TPU cores\n", + "INFO:pytorch_lightning.utilities.rank_zero:HPU available: False, using: 0 HPUs\n", + "INFO:pytorch_lightning.callbacks.model_summary:\n", + " | Name | Type | Params | Mode \n", + "--------------------------------------------------------\n", + "0 | train_conditions | ModuleList | 9.5 K | train\n", + "1 | val_conditions | ModuleList | 0 | train\n", + "--------------------------------------------------------\n", + "9.5 K Trainable params\n", + "0 Non-trainable params\n", + "9.5 K Total params\n", + "0.038 Total estimated model params size (MB)\n", + "20 Modules in train mode\n", + "0 Modules in eval mode\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c0c85ee450fa4382b8ba6d076bbe15ab", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: | | 0/? [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "vmin = 15 # limits for the axes\n", + "vmax = 42\n", + "fig = tp.utils.plot(model =model, plot_function=lambda u : u,\n", + " point_sampler=plot_sampler, plot_type ='contour_surface',\n", + " vmin=vmin, vmax=vmin)" + ] + }, + { + "cell_type": "markdown", + "id": "54c7788a-d7a0-438c-821e-bef10f3f780f", + "metadata": { + "id": "54c7788a-d7a0-438c-821e-bef10f3f780f" + }, + "source": [ + "Let us visualize the solution of the PDE at further time points." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e9e54d6e-f7a2-4746-a05e-681e3dbee8b7", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 449 + }, + "id": "e9e54d6e-f7a2-4746-a05e-681e3dbee8b7", + "outputId": "bc9feda3-d8bc-4e0a-8e50-7be9a6d8487d" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnoAAAGwCAYAAAA+MchDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACIs0lEQVR4nO2de3wU9b33P5tkdxMgiUVMAiZcTrVcBQSRBi1iJUHkoFAfj0ctoCjWPgkKafEYawW8NHi8AE+lQK0Be2wePNoTtFoDWzBQC6gEcwoejZUDwlEC8lgTyGV3s7vPH5vZzM7O/T6z3/frta9kZ37zm+9MZnfe+f4u44nFYjEQBEEQBEEQriPD6gAIgiAIgiAIYyDRIwiCIAiCcCkkegRBEARBEC6FRI8gCIIgCMKlkOgRBEEQBEG4FBI9giAIgiAIl0KiRxAEQRAE4VKyrA7AbKLRKL788kvk5ubC4/FYHQ5BEARhY2KxGM6dO4chQ4YgI8O43Eh3dzdCoZDmenw+H7Kzs3WIiHALaSd6X375JUpKSqwOgyAIgnAQJ0+eRHFxsSF1d3d3Y8SFOWjt1F5XUVERjh07RrJHJEg70cvNzQUQ/9Dm5eWpriccDmPnzp0oLy+H1+vVKzzXQOdHGjpH4tD5EYfOjzh6nZ/29naUlJQk7h1GEAqF0NoJnLwTyPOpr6c9BJRsbUUoFCLRIxKknegxzbV5eXmaRa9fv37Iy8ujL1ke6PxIQ+dIHDo/4tD5EUfv82NGV588nzbRIwg+aDAGQRAEQRCESyHRIwiCIAiCcClp13RLEARBELbEB8BvdRCE26CMHkEQBEEQhEsh0SMIgiAIgnApJHoEQRAEQRAuhUSPIAiCIAjCpdBgDIIgCIKwA1nQdleO6BUI4SYoo0cQBEEQBOFSSPQIgiAIgiBcim1Eb82aNfB4PFi2bJlouVdffRWjRo1CdnY2LrvsMvzxj380J0CCIAiCIAiHYQvR++CDD7B582aMHz9etNy+fftw22234e6778aHH36IefPmYd68eThy5IhJkRIEQRAEQTgHy0Xv/PnzuOOOO/DCCy/gW9/6lmjZ9evX4/rrr8eKFSswevRoPP7445g0aRKef/55k6IlCIIgCIPw6/AiCA6Wj7qtqKjAnDlzMHPmTDzxxBOiZffv34+qqqqkZbNmzcL27dsFtwkGgwgGg4n37e3tAIBwOIxwOKw6bmZbdh2rsEZ1fVJ4ERJcl4mo4Do/+o6dXYef9buPU7eXtY0fYVa5YFJ5Zr98+8gKA8C/4HB4GjIQTNTJ1MfUkfgZ6a0z0hN/H4zXncVULfWzu/cncyg9rAMKctZxy7KXscuLleGu64F8eusPZ+YA02sRfqgIiHQJlxf68ub79Pp4lmXzrPOLrOMuY8pmcZZns9axf/b+3tP7M+SP/z8ZycxCKDO+cQh9P4PwAgDC8CPIWh4L+wAsRyA8Hx6EEen9vzQEH0K9O2Fvy66XqSeceB9fH0FGyrJQbx3x35Pr4dsHu352uSgyWeuT/2jsfXC3C3HKBjlluftm8IcjWANgdOh/0B3t23coyHcRsPbdLb6eoSecKV3IQv5e0E90Pd93tBq0bk8QVmOp6G3btg2HDh3CBx98IKt8a2srCgsLk5YVFhaitbVVcJuamhqsXr06ZfnOnTvRr5/4F4UcAoFA4vcrId707BYinJ/CCgqcDDxicDQK8HJ+2oTA9FqrQ+An1vuTK7gmEw4sSXqfCSCn9/eclNJmExH4XexToS/P/umvpu3LTsjtnc3+jlZDZ2enpu0JwmosE72TJ0/igQceQCAQQHZ2tvQGKqmurk7KAra3t6OkpATl5eXIy8tTXW84HEYgEEBZWRm8Xq+h2TxA/4xefB1/Vk8qo8cuz83qsTN6ZwL/goKyp5DjPZ9Ur2FZPUA4syeU1WMv4y5n1ytWTs49nSfjF87MQWBqLcreWwyvWEYPEP6kimXw+MpxM3nsdWKZPxOyeUA8U5ZYHx6A84EKeMteQNQb4WybmtGTk83jW85k29iZtb66+v4r0CujF+L80dRm9DJCmXj2T3/FT2aOR3eWMzJ60VDyvjN82oVYKLPH/Y5WC9MKRBBOxTLRa2pqwpkzZzBp0qTEskgkgr179+L5559HMBhEZmbyF01RURFOnz6dtOz06dMoKioS3I/f74ffn/ol6fV6NX34ufVERGRLDyLISmliZYgCyBSYKTPKWh5EZlIdMZZ9sH8PITMhdUF4WELYJ32e3t+ZfYfggR9B9CAubj29N7MMbwgZ3nhdmb119iAumZHeshHERTHq7ZU9L5DZ04NoFuAPRoGsXtljQuyHuIQx3+9MT1NmWTfiIhJEPHvXw3rPvRS60Scv7PXs5cw6L+t3H6uc0KeILYoenvW9y7yeLng9EqIX4YkdiP8BuGIX5SnLxBvrLR8Dv/Rx5S2LZ102Urdlle/xx09V0J+BLACRrCxEMz3IQheC8CMT3QjBh0x0IxNRhOBHBoIIwocMBJGBuAx5eq87jzcMjzfSK1fhXjEK977v6X3fg1jvhRDrFS7m2u/7GYf5rPb9jKcuexIpzL7f2X7OXP1B1oUR7v0jRpD8XRVi/cG5YhfkdI0Osd4HkSphXBFkyOhtru3OykS3lyWZEeGv9VC3T/a3fk9MX9GLBv0At8pI6ndwhp/7H5Y42V+HECnsL7he63e9HvcJ2fjB/48aQWjAssEY1113HQ4fPozm5ubE64orrsAdd9yB5ubmFMkDgNLSUuzatStpWSAQQGlpqVlhOw5uZkFoHfdmxIfQDUc6huQsDHuZIviEhL1cRD6QheTOyuyy7PLc5XzrhMpxv6ClOk1LnQKhTtZ8+xQqyxcrwH+uhCSPWwc4v7PWMZm8IJPJy+rL5PVl0JIzb+zfk6+R5Ewce73c66evjkzOvlP74vXtQ55sCX0exD5zcuuwGr2zedGg/OOMBv1JLzlknu5QGxpBuB7LRC83Nxfjxo1LevXv3x8XXnghxo0bBwBYuHAhqqurE9s88MADaGhowLPPPotPPvkEq1atwsGDB1FZWWnVYZiKmIxxMwpq6kguJy5l7GXMvsVunnz1sssmtu2VgkhW3DIYYegRkhUhuWPKsctkccqISZDUOr5y7LJSLzY+pIqakEiKiZ1UHewyfNLHrGM/hkmoDpmSxyAkecnNpcLXj5DYcbeRcw3yoUTOtKDqHxweQlH+eoLdwscht8lWb5RIntD2csSPZE85GzduxPjx45GXl4e8vDyUlpbi7bffTqyfMWMGPB5P0uu+++6TrPfjjz/GjTfeiPz8fPTv3x9TpkzBiRMnjDwUQgTLp1cR48SJEzh16lTi/bRp01BXV4df//rXmDBhAl577TVs3749IYZW8RBSB3sYhRrZS+0v5ONdp2dWL5y4kadmb7hZGcWyB/Bn9/jkji+7x34PnvJSWT72cjHJEsqyceuSEkKhutSIIfeccLfnZvGEJBFIrQ+pkhfK9AnKF192jnstsJelXjfaBELqepf6B0ePfdgRPbN5WiVPqE6hekn2lFFcXIw1a9agqakJBw8exPe//33cdNNN+OijjxJllixZglOnTiVe//qv/ypa59GjR3H11Vdj1KhRaGxsxF//+lf8/Oc/N7QvPiGO5dOrsGlsbBR9DwC33HILbrnlFnMC0hk5WQP24AkrCMGX6McXgj9pAAaXIHxg+u9FkIlMRBCEX/AY2OWZupllzH6Z7UOZvsTgDCAuEP5gNCF7WUH0CUY3+qSDWc4s43vPd9Vz++9J1Q+edfwH3Qe3HNONywewuocJw7cfoe9OPrHkW88VPPYyrtDxLet93+OXL3lammz5EMrmSTXbitWVvEz8cys3m64GuzbtSmGE4PHtQ2l/vnSBO4BEqK/63Llzk94/+eST2LhxIw4cOICxY8cCAPr16yfaD57Lz372M9xwww1JQvjtb39bSfiEztg6o+d0gvAnveRuI4XeTbhy9imUyZCT4dDSNy+p+c/fd7nKyu4x79mZKW72ipvB4mbLAOHsGnud2n56csqI7U9oe3DKi5WTyuLx1QNokjypJlvh7LRPcBu1qGm2NaJ/ntZmXT2abfXI5pkheWL7cnRWT+53gcT3RElJCfLz8xOvmpoayV1HIhFs27YNHR0dSf3ef/e732HQoEEYN24cqqurRaebiUajeOutt/Cd73wHs2bNQkFBAaZOnSo61y1hPLbK6LkJLX1+xLJiZtQpldUTyvSxs3o+ToqKvU1fFk9eVo+Rh8yenkRmD4gLRmLqFb7sGzjL+LJ7QN+ngBliyc7eKcnkcWORQurPITdbp6Q8+3e1WTzWMiHJ44MraXKabDMT5cOiIqT0Hykzmm313p5BqH+eHTBT8tj75Gb2Mk93oHugfc+T0Zw8eTJp+jC+bB7D4cOHUVpaiu7ubgwYMAD19fUYM2YMAOD222/HsGHDMGTIEPz1r3/Fv/zLv6ClpQX/8R//wVvXmTNncP78eaxZswZPPPEEnnrqKTQ0NOAHP/gB3nnnHVxzzTX6HighCxI9jXDnz9OrU7eUmLFljAsjXFKw65AjgnxNr9zl7PiBeF89sW2UNOFGsrISsgcgtSkX6BM5QFju2O/7Ak7+NPSAX/iY91ypkmrCZbZjw+6jJ9R0q0XqhJaJCZ6CZeysKlfypAZfiDfZimeVxLJ5Us224vUqb7ZVWp+d0ZrNs0Ly2Pvmyt63znTidxbFYzXM4Ao5jBw5Es3NzWhra8Nrr72GRYsWYc+ePRgzZgzuvffeRLnLLrsMgwcPxnXXXYejR4/yNsdGo/F/wm+66SYsX74cADBx4kTs27cPmzZtItGzCGq61RG9R+5pqY/vZqmsWUldkyzffoUe9yR8808dnMEdoAFINOUKDRjga55kL2c3Z3KnZeFrauXuU0kzLnNahEbditXPVx83cyd0POCskxqRy9NUyx5hq1Xy+JpsudkvOdk8NWKltdmW+ufFsVLy2DHYIQ6n4fP5cMkll2Dy5MmoqanBhAkTsH79et6yU6dOBQB89tlnvOsHDRqErKysREaQYfTo0TTq1kIoo6cDRk7NIJZpE8vq6bkvqeydHJQM8hCKSagpF5DI7kk15bKXg1Wer2mXu07oT6+kGVduU6/QvoSWcz/d3HJSfRt5yglNoaJU8hj4mmzFpE5oOhU2aubOS97e+GZbvSVOa/88Ldk8u8kVDdLQRjQaTXo+PJvm5mYAwODBg3nX+3w+TJkyBS0tLUnLP/30UwwbNkzXOAn5kOhpJH7zMPapGGplT04Trtj2QuukmnAjyGQ9rMKP7ETZVNkTa8IFkGjGBSDYlAtAft89saZbsT547KbdHkgLnpx7X6z35QP/kzP4EKuX79MsJnfs9TKbadlZVC2SJ6fJNgQ/cgAEex9vomTkrVKsGtTRt63M+S1t1j/PbpLHEA36EY04sMGKye6rReGtqLq6GrNnz8bQoUNx7tw51NXVobGxETt27MDRo0dRV1eHG264ARdeeCH++te/Yvny5Zg+fTrGj+97rvuoUaNQU1OD+fPnAwBWrFiBW2+9FdOnT8e1116LhoYG/OEPf+CdRYMwBxK9NEOqL56crJ6cdZGkxzqxsnI88igke9zy7H57ANRn97hTpwDypU9oehZAXAD5iLHqlit6ENk/BPYvJHfcdTzL2U3ifBMha5E8sSZbNdOpsJHTpColZunSP09NNs+ugkco48yZM1i4cCFOnTqF/Px8jB8/Hjt27EBZWRlOnjyJP/3pT1i3bh06OjpQUlKCm2++GY888khSHS0tLWhra0u8nz9/PjZt2oSamhrcf//9GDlyJH7/+9/j6quvNvvwiF5I9ByCkVk9sX0JNbkKNeEKL0+NX2pwBgDe7B63KRdQkN2TmhMPSG2eFRvRyy4nJoBaEKpX7F4rJnfc9TKbaQH+kbVaJE9uFkzOdCp8dSl95JlUs61Z/eaU7CcU9NE3OaGKF198UXBdSUkJ9uzZI1lHLJY6kmzx4sVYvHixptgI/aCvhzSEK11q+/qJNeEy06tEkJFykSmZvoUvu8dtygUUZPeEpE1O0y3fSFtAuhlX6FMWY62XyugpkTqxbRQIHiAviwdokzyhbF4QXuQIHJqcbJ5SKWQjR7SMHIihF1L98yibRxDuh0TPQdgtq5caQ986Zg409v6l6kzO4sVvJnKacgH52T2AR/jiO2UOPI6cOfOEBEtKABmY/jQ+yB//LjZwQ2h/Mptu+QQPkJfFiy9TL3lihOFHvI+e/AEYfOvUDMKQi5g46jUQw2798wiCcAYkeg5Dr1G4crJ6appw5UymrKROoaZcIdkDxLN7gIDwAfxZPCBVCgHxJly5o2gZ0cuG8omOlGb4RJpupQQPEM/ixZdrkzyp0bl8iImf3D5yRsif0v55Ro/I1RPK5hkM37RMSpDzKEUi7XDgsCRC6bM6AXmZE6kblNR8eHzr+Dvc868PwS9jhGbfXHvsG31SnazMk9C8e0DyfHAApOfWUzpPnp73RLF6hWLiK88po1XygvCJDqCQNx2KcQMw+DOPwjGIxaFm/jy9noahBr2bbUnyCMKZUEbPRLg3Jy1z4OmR2ZP3NAzhzJ9UZi8nUUffOqnMHrteblOuWL89bqzcplwgObsHpGb4AIEsXzwI9knpg++/b6nMH0OUVcbMplsoEzxAvD9efBmfjAtLntDEyOxl4aTt+Ue08GUH5fbNk3oShlxJM+P5ttRsSxCEWkj0DEbspsNep24whDLZkzuvHjce8f3Im0xZjuyx9ys0IjdeJrXfHrOt0EANIFn4AIhKH+88fInCvAfYh9ymF+ZP4QdU9etX2HzbwykvR/AAef3x2MvYy8Uyb3zllTwBQ0qw9OybJ7c5Veu0Kno121I2jyAIBhI9g1D6hc8nWHLQKnvC8+b5BGVPKAMXL8fXZy+cso5P9rh1K+m3x91WqO8ekCw1QtInmuVjI3fkrVbUjLpFqtwBygUP0C55QiNsufWwl7EHIsttsjUjm6e22VZtNk+IUJB/ezlPwlACSR5BOBsSPZ3Qa4JUNVk+rc24xsheTGRdquwB4M3uCWfx5DXlJtXLET5AXPoEs3xstDTdqs3oaRC7xK6zkj/6Us208eXymmpTt5OWPLnZQKE6hfapZt681PJiI2qNlyC9m22VZPNI8kxGa99eYx/SRDgUEj2NhOBFxKChTkqkT4ns8TXhyumvxy0nJpHcOdCkZI8vBqVTsPQtT86OCgkfIC59Ylk+Lllqmm6Zbmd+qPokisUjR+4AeYIXXydPzrjbqpW81H1HRJtspQdbiN899cjmiQmknCZZJc22wW4/cnj6LeqdzSMIwvmQ6DkEOU27cmUNkC97aqdd4T8GebIHQLJ+udk9QFj4gNTmSiX9+djwSRdvBlAlYlIH8IsdIC13gHLBE1ouJFt8ciRHFqXmkOabGFm6edYe2TyrBmFYms3r1ni7ydb6iBmCSE9I9ByGVFOs3GZYwBzZC8ILf28fPe56Idnjqx/oa8qVI3sAJIWPQUj82PPxAfzSxwffaF4+enrvuT1+wKPgkyi2fz6xA1LlDlAneOx1Ulk8QP5ceUJ1cZcpmWiZLx5uLNx969E3Tw16DMLQM5tnO8lj10HCRxCKINFzIGbIHsARIMWyx5Y24UmV5coeex/CTbZ9y5m6mXXM9gzcvocMfNLHN4iDgZG/pPMiIYIMPZnxciF/BqJZ6qa0FBI7QFruAHWCx10nNn0Kd1tlzb7MNCvxplslTbZS2Tw5KMnmKWm21ZrNC3arkzC52TxdJU8PwROq043C54e2CZOpjx7BA4meQ1Ere3zIfUSa1D7Z68MCNzep5lZGELQ25QKpwpe8f/nSJ9SnDxAXLT4JVIvYfgB+qQOk+7P1lVMveNz98GXx2NvKFUahplslTbZOyOa5FiMkj69+NwofQegIiZ6JiDUZqkGN7Alto7YZV+pRamH44WV1GpfTt44vHrmPTgOQInyAcJaPqY8NXxMvn/RxUSKBABCJxddHMrPgkSjLRiwGQJ3cxcvIEzxufULNqXKmUBGqC2AyeeL74G6jJMMnhthTMKSyokL1KCmjNJun17x5umTzjBY8of2R8BEELyR6BiJ3QldAvfTZVfb8rDaE1H578vrWiWX3hKZgYdfDrkusDLscEz+DnH59ie0EBIwrgEzZnt6beSjTh2imdEaVD6WPw+PLWmkVPEA6iye0XEzMMpKWJ58ftdMZqcnmqa1fzXor0Sx5Zgue0P5J+AgiCRI9HdE2Ck+99KmVPSB1FK+espdcXp7scdfxxSSW3YsfU3JdgHiWj12OXZZ7HEJNvHyIjexVg5xrS0h+5Mgdt5xcwQOMkTw+1DbZ2jmbx7udmmyeyDe5nGye4yWPDROLNyxejiDSBBt9Op1JCH706DyPnpomXr0HaAAQHRjBt61Uv0Ap2QOEm1j5sntij09jkJPlY5fjlk0uL5Cx4znvcsQsAmaAgg8RBb2o5YiL0sydUBkjBE+sXiVTtCiVPL2zeWIDWfiQK9yOwk6Cx8XOsQnhh7YJk9U1DBAux4GfhPSDbxQsH1Jz7SmRPbnby8nssfvoSWXvlGT3hJ6oAaRKn1iWj12ub3vpbB/7GMUQOrfMdiH4kKlyuJyUXKiRO3Zs8W3kCR63Lq2SxzTdRpABIKopkyd7pKuCbJ7cepRgxUhb1dk8J4oUQaQh9El1EEqETw/Z05LZy2Ztw12vp+wx+wNSp4MB5Gf54tvzi59Ytk9oezZq+5Nx0TLBr96Cx61T60hdqZGyUuuVIEdGuSiZlkZNHFZBkkcQ7oc+rQ5EztQpYtk9pZk9uWKVLHvcTI+07MXrVNaUC6QO1mDHxncelPfT4y/PRenNPgJv73ZeVRk9OaKgtHm2bzuxzJa4HOkheRFkItab04uPvo2mbCe/TmOnU5HzuDMtgzCMHmmrGJI8gnAU9Il1KFqze1plj68OJfPsxd/zN6sqye4xsQGpmUdAXPjYMfDFwSDWxMtGTAK1olbqxLaXEjxAfjOt1HqlkseHnpLHRa8BGHJRMqWKGgxrsiXJIwjHQZ9akxC7OcjpHyeEHOFTI3t8camRvSB88CX10UuNRU1TLpDaVKpE+PiOT+ngDC5Km+KiveXD8CvuQ62lGdcqwePuR0ry+ObREzsGJRjVZGtmNi8H6qcRIcmzKT5oG4xBM8sQPNAn1yCU3IiEJuxVgvSIV2Wyx46LvZ2cfnvxOdAyk9YD8ppTpSY/lpN1kyN87DgY5DTvcvfLRkgAtaJF6vrWS8sdoL4fHl8ZLSNr4wNU2GWiSeuEjsPMJlsuciVPbjbPVk22JHkE4VjUPWBTJzZu3Ijx48cjLy8PeXl5KC0txdtvvy1YfuvWrfB4PEmv7GwtDwbUjxB8SS8r6grCL9pRXXieNeXb8TVvKZkgmqmXb7695G1S1wvdUJkXO0YlcQqdc2afQvvmi0HuS852qXH6ZcUkdDx854B7rtj75u6DLy5uGa2SJ4RWyUutT32TrR0nRzakyZYkjyAcjaWf4OLiYqxZswaXXnopYrEYXnrpJdx000348MMPMXbsWN5t8vLy0NLSknjv8Qg9EdMcQvDpPo8e3z4Y5E6DAgg8vUEgs8dsJ5bdU9OUG4YPWegS3YfQCFnxx5ZJZ9zkDtxgEMr2iTXxctG7n568R2hJC4Wc7F28nLzpUoTiU9pUy41DbHStWN1yERJArZInt9neyGweZfIIguDD0k/x3Llzk94/+eST2LhxIw4cOCAoeh6PB0VFRWaEZ0uk5spjo2bePClJ5O5bSPZyRPrlCe1D6chcdrl4XOLCx8QLICVmdlzc2PhESnhuPPnyEU1kzHyyU+tyM7xiEmSG4HFjUCN57CjlCiQ3XjVTqeiBXR91piibR5JHEK7ANp/kSCSCV199FR0dHSgtLRUsd/78eQwbNgzRaBSTJk3CL37xC0EpBIBgMIhgsO+m3d7eDgAIh8MIh9U/IofZNitsTUYxmpAbceGLILu3XJCznNme/xz0IAcAv/BFkA0va7/MNBjsKULC4fjUIRm9WYYe5CRtAwAh9OvdR9/yMLKSjince4l6k4Sv77L1s+IP9U5XEj8udnlv77K+entYZbmxA0A350bNPQ9SN3LusfIRC/sSPzV12JeQloiARvLJWWrTefJ5Cveu9yTK+5Lex7dJXhZG8nNr2RMhZ7DKsPvkZQLIDMfXRsI+ZPZmzZn4mCsgBF/Sl1gQ3qT37Fx7NGl5jLU8M1E/s38f630IXvg4fQS5z3L2p0yPk/qPRBjJ80sC8WxeyrIg/0AL7gCM7J6exM+ecGbSxOR8REM+QODznkJ3lvyyStCS8Fbo5jmRePxavuf12F4RWdB2V7bNHZ2wE55YLGZsu6MEhw8fRmlpKbq7uzFgwADU1dXhhhtu4C27f/9+/O1vf8P48ePR1taGZ555Bnv37sVHH32E4uJi3m1WrVqF1atXpyyvq6tDv379dD0WgiAIwl10dnbi9ttvR1tbG/Ly8gzZR3t7O/Lz89H2BpDXX0M9HUD+jZAd68aNG7Fx40YcP34cADB27Fg8+uijmD17dlK5WCyGG264AQ0NDaivr8e8efME64zFYli5ciVeeOEFfPPNN7jqqquwceNGXHrppeoPjNCE5aIXCoVw4sQJtLW14bXXXsNvfvMb7NmzB2PGjJHcNhwOY/To0bjtttvw+OOP85bhy+iVlJTg7Nmzmj604XAYgUAAb5WdRY9X3inkZkcY/Dr+5yyV4RPrQyaU3WMQ6r/HzV5lIoqMcAbGBa7AkbKD8Hq7ZG0X30fqMqFj8grEI3Q+hY5dTjO42keUCZ2zIPyIhb0IB5bAW/YCPCofwC6UrWMj1NzLP6iFP4MnVhc3GxkWaWYFkmMWmkIlBC8ywx7MCIxAY9kxdHm5TcvcfQjHndxkm7xdVLJvnpfzXs50Kqmfc+55BPj75oWCAn8rnr552T092PzBftxz+dXoyhRP5cSzeTLQu7nWuKkl44hk+XIiYdQeDaCsrAxeL/93rxza29sxaNAgV4reH/7wB2RmZib1k3/66adT+smvXbsWgUAAb7/9tqToPfXUU6ipqcFLL72EESNG4Oc//zkOHz6M//qv/7LN4Ml0w/JEr8/nwyWXXAIAmDx5Mj744AOsX78emzdvltzW6/Xi8ssvx2effSZYxu/3w+9P/Tbwer2aPvwMPd4Yr+gpaYrjNiMyqJmuo4enmTJ5vU9QPrp6LwehbTvhFWjKTW5ujQLw9opR1BtFlzdeb2rzZ2bK/rqRyVMugzeuUGJ7bvm+hsTkARwZvOVDSG1+5557rubx9fHjg69uAPCw6vd4w/B4pZrg+Tvae3j7GnKvPXbztp93ed82PZxyzPvk9fFtmDLJj7tjv4+X6XsfP44oqyz7iRfs5X2fqS6vL+kzxl0vNTAinPR739/DiAEYiqZT4fmTBiOpX8mhbp/oN3VXZha6soQLRIN+3n2l0J0lr5wcunt/Gj2vA/PHFfEHrd/1etwn7IqcfvLNzc149tlncfDgQQwePFi0vlgshnXr1uGRRx7BTTfdBAD47W9/i8LCQmzfvh3//M//bMyBEKJYLnpcotFoUgZOjEgkgsOHDws29ZpBULJnjJa65U3Uy4faARfMtoC2x6fxZZrEBmGw9yc1KELuRMfxusRH7PJtw96uLw7+yZm1wPRtjCADHhX1aZ1fT49BFkLlpJ6TK2f6FT6UypdRo2z5kCt5gtsLjLTVguzBF3pl8rqlixgCs19KGAHo64vOIJTwYMPXT55ptt6wYYOsQZDHjh1Da2srZs6cmViWn5+PqVOnYv/+/SR6FmGp6FVXV2P27NkYOnQozp07h7q6OjQ2NmLHjh0AgIULF+Liiy9GTU0NAOCxxx7Dd7/7XVxyySX45ptv8PTTT+Pzzz/HPffcY+VhmIbQqFMhpEboyhE+Idnj245/CpYMRCE9ebHQ9lITHQtJn9gkx0KjdvvqlBY/obqMQm6GWKnYCW1nhuBxy0s1ixoleWqQN+2NwBQpCh51pmU6FVMlzyrB48KOw4mJOD90eTJGSUlJ0uKVK1di1apVvJtw+8nX19cnuk4tX74c06ZNS2TnpGhtbQUAFBYWJi0vLCxMrCPMx1LRO3PmDBYuXIhTp04hPz8f48ePx44dO1BWVgYAOHHiBDIy+jJDf//737FkyRK0trbiW9/6FiZPnox9+/bJ6s/nJtQIn1g/NKVPxxDbjimfzclzCk3DAsiTOLnZwL7l0lm+eH2px8V3gxbq36dtGg0vMsEcg7LR23IzRULxaXk8Gl85OfP0KZsixZ/05aRE8pTOcWdUNo8PIckzIpsnC62SZxfB48Po/oE25uTJk0l99MSyeSNHjkRzc3Oin/yiRYuwZ88efPbZZ9i9ezc+/PBDM0ImDMRS0XvxxRdF1zc2Nia9X7t2LdauXWtgRM5CifCZnd3jdsgHhOewUyJxcua6Uyt9ffXKkz+hOvVGUfOfgqxd33IB+ZAheHzllAie/PX8ffKUPHeXLzY1kqclm8eH2smRxTqNqHqWrRLsLHhE4mlTchDqJ5+Tk4OjR4/iggsuSCp/880343vf+17K/RlAonn39OnTSf35Tp8+jYkTJ6o6FkI7tuujRyjHLOFT03cvPj9a8l1BD+GTU55vGzHp66tXPOPHRf3ku17kJLZX3mtdax89swWPbxslT7tQ0lTLt15K8uQgZwCG65tsSfJcDdNPfvXq1Sndoi677DKsXbs2ZRAHw4gRI1BUVIRdu3YlxK69vR3vvfcefvzjHxsdOiEAiZ4JyBUBrZkhM4VPad89vn0ZLXxi28TXqe+bF9+fOf3zpOLgopfcCZXX2kwrt16hZ9fG12mTPD70GmXLW7dJTbYkeQ7HD20DShSODBTrJ19UVMQ7AGPo0KEYMWJE4v2oUaNQU1OD+fPnw+PxYNmyZXjiiSdw6aWXJqZXGTJkiOiULISxkOjpiNbHKunVPKgkIyVH+NQ8Ri2TJ0NllvCJbcO3XV8ZafFL3p+8mzzzN0gtn4UcMPOuqXvCipZn4YpltIwSPDl1M+uzEuu4c9lplzw7j7JVm80jySOUItVPXg4tLS1oa2tLvH/wwQfR0dGBe++9F9988w2uvvpqNDQ00Bx6FkKip5Ew9JzuWBg5TY58yJU+KXFTmi1jb5uFLlnb6SV8Ytuwt2NQ8txaNVlXvZ57qkQixKYpMULw+LbVmsXjj88aybPDKFtdIMkjWEj1k+fC93wF7jKPx4PHHnsMjz32mKbYCP0g0XMgQlOISCHVtKtVnJQ2A+stfHx1iWX5+LbnqyO5rHzZEvv7hOCHp/fjF4YfMQ1TfkjJEaCsaVZsGzWCp76MPTJ5RvTLU9NkqzmbR5JHEGkJiZ6DsUr45AgT30PZlcyfBygXPjmxs5ErfkL1SaG1KZ+/TvkZHzVyJ7adHoMthOrnE67k6VXsK3lCKMnkARqabEM+6SdakOQRRNpCoucCtDbrigmf0uxe37bCs5WKzb8HKM/w8cUip0+eXPHj1ieFOinU1mQnNYJUrdwB5mbx4uWSl3GfE2uE5PGh+RFnAijN5kmNsjUMkjzz8UPbhMlm9CMiHAeJnolIZQL0GMWpRvrEhE9uhkw4O+ZFBDHBJlUlWTn2DVxNlo9BrvgJ1SeFXGljmm7j5eUPl1MyLYiagRiA8ChVPadMkcqocQWPr4wayeNDz+fYiu5HxQAMzSjN5pHgEYSrINEzCDUd8PWeykOp9AXhU5zd69tWPMun99QoarJ83HoZ5ByXHNRIodZ9clE7CAMQn4LEyGbaeLlU0eLmhPWSPDUjbPnQq1+epiZbKewkeV3SRUTJ0SUKgkg7SPQ0EoTPlGy50kl8ucjtz6c2u9e3fXw//RT2/5ObkZPbrMuuk8HoPnlKBc3TG3sQPsWDMeRmDfWWO6E69WqmjZdLbarlfsaMlDwnDL6QhR0kT6vcCdVF0kcQsiHRcyBKn3XLRm6WT47wxeuQPzUJXx1KR8mqadblq1uofu5++FAjgVpQ2n9PaX87uftS+8QMYWHUZxoTsyVPMC4VkieUzZP39AuRfzGVSJ7dBU+sfhI+gpCERM/BcG9ASsVPTpZPrDk3Xod4li8EP3p6n1OqdpSs2mZdBrniJ7QvoX1rJaM3zjB8iArEKIWcLKLecie0jZ5ZPLnl5DzWzOgYxDBO8kSwSvKMljupfbpB+vzQ9mQMGoxB8ECi5yLUNu9KCZ+cDKKSUa5apkVRM7pWrvhx98VGr/53alHaLGyE3Altp0Wu4mX5R9V6RcoA9pI8vaZScaTkWSF4fLhN+ghCJ0j0LEDt/HdKUJPtkyt8UvWF4ENU5JFeeky+zLe93EEWfBIkJn/c/SqFiZNbR0aij55fdUYPUPccVy5K5U5oG63NtPzlUpc5QfL0HmGrq+TpgV0Ej48uAFGrgyAIe0CiZyDS/dTky4NWKVTSr09uk66c+kLwIUNQHNUNwJCzPbcOsXoAYVmSEkA5aJFENnKEjo0WuRPbXllzrr794ABrJE8Is0bY6o7WbJ6dJY8giCRI9HTAiCcgSO1DrfgZJXxidap9RBp3e6E6jBxdq1SulIhhBBmJn1qTD3qNwlUz756WZlqhsiH4EUMMYJ1PtYMuxPYhF7365QnhmCZbEjyCcBwkehrRq2O+8v0qmyOPixrhk9qXlkerxbeXPwee3LnvpPrW6TW1CoMSMcxQuQ89R+FK1afuGbnypUpuWbMkT2uTrWWDL+RediR59sYPbU/GMHdCAMIhkOi5AC3ZPqUDOORm+cTyWnrImtqnXWgZVWv3aVUY5DQVq23WVSp48W209YGLkuTpA0keQaQlJHouRK34KZE++RMwe5EpkeGL16P+qRtq6mIjd0St3tnbzN6cXhg+RFQ23urxODQ5dSkZZNG3jfz+b3xl44In/OQTqfhcL3lmNNeS4BGE4yHRSwPUjPKVP8JWullX6fQs8brUPVtXbl1C9bKxekoVLmoGdciRUzMET2gbsWbazN75F6XidKLkSSFP8mROmkaSRxBpDYmewSjtzC+G1hGgavv1KZU+LcLXV5f2/nzcusTqk9oHGyMEUI+RuUoyjmrkLr6dPoInVF6oL15IYLoep0qe6DNsKZNHEISOkOjphJ5CJ3cfWsRP7Vx+8rJzfk4uhr8OqXridcmfZoWNkpG3YnXL3Z8amKbbeH3Kmm7VNCNrG5ShXPCEthMqr+QzZMR8faL702lCZMDmkkeCZy3ZoCdjELpDoqeRKDIREZkc2EjkPN9VCq1ZPilRC8MPj8zpWaTqU5KZU5Lx49bNxuwBGHxYNShDrdyJbaskixefXiX5/Osx2lfpcZk1V56lAy9I8AjCtZDouQQ9JvxVM4hDboZPaX3SdUqP3O2rU/nIW+5+pFAihEydmb3/IITgRUQ0ByqO0gyj9KAM/bJ3UtvJnTYlXoey/oJaB4MANpM8udk8u0meXj0djJ+ulCBcCYmey9GS9VOS7ZM3cbKy7KGcbJ+aefCM6Htn1nyKRg3IiNetTIKkthPbVpngeRESmHXQDZInCzmSZ6emWiPGMTF1kvARhCJI9NKIvtGMypt5lUqfVI8zo7KH8brVTYIsJlFmjsDV2gdQ2aAM/bN3UtvKn/zYq2r/TpM8Xfrl2UHyzPqIsPfjMunr8QE9Go6px14TBRA2gUTPBPTqvA/oIxxaB3Uolb4MWaNs9c0eptavbuRt3z71v6NkJZpu/ehR0XSrbkCG9DZWC168Hh/vvwtGj/oFHCh5atBT8qyUCxdLH0HoBYmejhghA3L2oVX+tIifXs/D5auTQW7d8usXvjHbYfAFH+oHZMjbzky566tX3tMtpOLQa9Qv4FDJC0LZ8/T0kjy7ZY+oaZcgeCHR04jYHF9mxsCHWgFU069P6fNwGfR81i5f/XL30bcvcakxQgRD8KnK6HFRPGWIhr53UtsrFbx4fUIi50NQwGT0yuIBDpU8pegheXYTPC5MfNo/UgThCkj0XIzSKUb4UCt98akxpMtryfapebSb3P0I71+/QRdRDf8gKJU6QFrs5NQrVYeegqc2Dr2aagGbS57Sj3Q6SB5BECmQ6KUJWqYYYVAnfT6Ee4VGaTZO7vN22agZzctFiwjqjRqhY5AjdnL2IaceNYIXr1tsyhT+IT1Ks3hi2+gteVLoJnlKBl+Q4DmGoN+DoF/DP4D+GCiVSXBR0rNDdzZu3Ijx48cjLy8PeXl5KC0txdtvvy26zauvvopRo0YhOzsbl112Gf74xz+aFK27CMKf9FJKBJmJl1xC8Cde8mL0JV5q9iF3P2L71foSr9fbu9wra3slxy2V8ZKzD6l6pK4BsWsrBB+v5InFJBaPmm3EmmrVSp4pT70wU/KCIMlzMWL34K+//hpLly7FyJEjkZOTg6FDh+L+++9HW1ub7Prvu+8+eDwerFu3zqAjIORgaUavuLgYa9aswaWXXopYLIaXXnoJN910Ez788EOMHTs2pfy+fftw2223oaamBv/4j/+Iuro6zJs3D4cOHcK4ceMsOAJ5GDHHmt59xbRk/NQM5jBiTj2p/SjZn15oycgJoUZglUqjGFJyr/dzdOOypmwePWY7wXUK++MBNmmuNVvyCFcjdg+OxWL48ssv8cwzz2DMmDH4/PPPcd999+HLL7/Ea6+9Jll3fX09Dhw4gCFDhphwJIQYlore3Llzk94/+eST2LhxIw4cOMAreuvXr8f111+PFStWAAAef/xxBAIBPP/889i0aZMpMUth1sS5Rj+yS0v/vggykSmzjx5gfr87MQEwUwKlUJuRZNBjxC2DnMyt3oInvT9zmmoBgyVPLmZJnn0+AoTBiN2D7777bvz+979PrPv2t7+NJ598Ej/84Q/R09ODrCxhffjiiy+wdOlS7NixA3PmzDEsfkIetumjF4lE8Oqrr6KjowOlpaW8Zfbv34+qqqqkZbNmzcL27dsF6w0GgwgG+7652tvbAQDhcBjhsPonQCe2DWcBrE710lMFGw3/n9Sn4WnXsaQBE/LuAt5wvJ9IZjgjMeggQ4b4sfelRFqjGkfb9u1f2U3Zq/CuGGbVHwszP/28vWqEpwtOhU96xLZni5dXpE9PNDHJNn+ZYJKkp5ZhJj3m+1wwMfh51/nhCceX+8PJ65kmbz/P9cSc32yedaGoj3c5AISCPuSgh39dt/C6nnAmvALrACAa8kHySfPdWdJleC6znGg46WcCtZJnn66pupDT+wHT8j2vx/ZWwNznGPx+P/x+iUy9jHtwW1sb8vLyRCUvGo1iwYIFWLFiBW/ChjAfy0Xv8OHDKC0tRXd3NwYMGID6+nqMGTOGt2xraysKCwuTlhUWFqK1tVWw/pqaGqxevTpl+c6dO9GvXz9twQNYHvi75jrczJKAli9JjY+NQo7G7c3Zx60BM+LkIneuRKlyxtvBI4GThu/DydSeDFgdgq0JBLSdn87OTp0ikSaSlYVIlvrBGJGsGIAwSkpKkpavXLkSq1at4t1G7j347NmzePzxx3HvvfeKxvDUU08hKysL999/v9rDIHTGctEbOXIkmpub0dbWhtdeew2LFi3Cnj17BGVPKdXV1UlZwPb2dpSUlKC8vBx5eXmq6w2HwwgEAlhb9i2EvNbOo6cVLdk+Nuxsnzccw5JAGC+UeRGWOD9yMn186NVMbdUo26xwXPJeKetCD0/6TWv/PiXdCKIypVpq4I7YY8ukYuI2t/rDUTwSOImfl/0Dgl7xcWNhFU21QDyTJ7hOS3NtSMa5l9MvTyRhnBMNo/ZkAItLytAVVJL/ZeGyLB6bnFgYtW0BlJWVwetVeX6Qmh1zAidPnky6v4ll8+Tcg9vb2zFnzhyMGTNGUBgBoKmpCevXr8ehQ4fg8Tj7vugmLBc9n8+HSy65BAAwefJkfPDBB1i/fj02b96cUraoqAinT59OWnb69GkUFRUJ1i+UsvZ6vZo+/AwhrwchiZuQ3RHqz6RUpELIZm0b/88y7PXIEOG+y1DJUzmEbu5K+9mFZQqRXkLICBxz9XV4fQhrvxQV9elTMlpazqhsKalUO2gCAILeDAS9/PFKDrgQOMxEfzyBb8BQt09wXU84U3Bdoj+e1OntzpJRBrLmRegKetGVoeICCgKmz/XO7WeYzVtKV7R+1+txnzAbZhStHKTuwefOncP111+P3Nxc1NfXi56PP//5zzhz5gyGDh2aWBaJRPCTn/wE69atw/Hjx9UfFKEay0WPSzQaTepTx6a0tBS7du3CsmXLEssCgYBgfwI7YcToS8DYbBRz89aSOWM/OURO/z41c/VxMWqkrVF/QzWoGahhttzF61EnePHthP/+UsevdtAFIJzJ023QhVQmT8mgC7WYNeBC6liUHKsJUkgk34Pb29sxa9Ys+P1+vPHGG8jOFv8jLFiwADNnzkxaNmvWLCxYsAB33XWXYTET4lgqetXV1Zg9ezaGDh2Kc+fOoa6uDo2NjdixYwcAYOHChbj44otRU1MDAHjggQdwzTXX4Nlnn8WcOXOwbds2HDx4EL/+9a+tPIwkzJYBOfvTKoMhlQMkuCgdyavlGbxcrJ5mRS1aR97qLXeAGYIntm8ZAqpi6pTEtk6SPLWDLoy87I2UVKZuEj7dELsHt7e3o7y8HJ2dnXj55ZfR3t6eaMa+6KKLkJkZ/0yMGjUKNTU1mD9/Pi688EJceOGFSfvwer0oKirCyJEjTT8+Io6lonfmzBksXLgQp06dQn5+PsaPH48dO3agrKwMAHDixAlkZPS1XUybNg11dXV45JFH8PDDD+PSSy/F9u3bLZ1DT+w5nHZB6WPGxOC7yauRPzXTtwhJi56ZPy5GyCCzX+Yxceynh2jFCLEDtMtdXz3qJE+L4AHqs3iATpInpz+e0ZJnhOCZkX0U26fLpC+c6UMoU/13QTgz3mVGLmL34MbGRrz33nsAkGjaZTh27BiGDx8OAGhpaVE0iTJhPpaK3osvvii6vrGxMWXZLbfcgltuucWgiNwP381Ub/lTKn5an8krJjdasoCA9oyaUSgROjZ6y128TuMEDxDui5moW2LAhVGSp1sWD3Ce5FkheHxQlk8TYvfgGTNmIBaTfpyaVBnql2c9tuujR5iP0I1WrQCGWM8pDcGLEDJky58ez+RlwydEWuXPbNRKHaBM7ADz5E6qjhD8kqOxpQQPUD8JMqCD5Ml92oUSrJY8uwgeFxdn+QhCKyR6NsUOfcqMbPJVK3598ag/F3LEyWgZZD89JIpMRHRqujVK7OJ1a5c7qXq0NtEm9qFB8AATJc/ITF46CB4fTKzSySiCSAtI9ExCjyZAuXUYIYR6N/lqHeBhhACy0ZJFMwulUsegt9zF67S/4AHasniARZJnZRbPSYJHEAQvJHoGYHW/LrOe5aqX/Gnt45cck/Cx6yWBZqNW6BiUPn/ZLLmTW08o6kMoKi5gWgUPcInkkeA5miB88GkY3BdEFECHfgERroBETyMh+BGy+ahbNsKTI+tzh+i7sat/5q9eI3u5SAmT2SIYjyeUNNegHigVu3gs+smd3Pq0zIWX2I+GKVMYbCl4gDWSR4JHEK6DRI8AYJwAcqef0TbAIxW9HoMGaM+cKcWnsRORGqFjUDLfo5lyB/QJXrZIP0kzBA9wSBYP0C55JHgE4VpI9AhRuDdmreLHlQEj5vUD9BVAu6BF7ADlk3mbLXiA9qlSAH2aaQEHSV4I6h9lRoJHEK6HRM8B6D39iRaMFj+GdBRArSLHRc1TWvSUOyX12aWZFnDQ/HhaL2WSPNsRgk9TV6CQhi4zhHsh0TMRvR+PpqQ+o6TQrOfK6hW/GplSK4fS+0qea1ALWq4tJYOH9BQ8OXIHAKGgD8GI+FeVaYMtGPSUPLObaknwCCKtINEzCLOfeSuFEU/EECIEPzwJifFDySN5hDAzfi56Z9q0ovXaUjMq3CrBkyxjZjMtYH1TLfXFIwhCISR6OmA3qZOLVNz6ZdH0be5lsFOTtpHocX05Re6AvibaHPQI1ydD8AAds3hKnnJhR8mzWvDk7p+eakEQukOip5EgvFaHYBjGNZ8aO8+fWQKrF9yRyfrUaZzYKdmHGsETrU8nwQMclMUD1EueFYKnZZ9i25IEEoQqSPQI2ZjRfGr0PH+AnTKw2jtOa52c26iRuHoLHkBZPMUYLXlmSySzPxcLn9Z/+mgwBsEHiZ5DkHODNftZuIBY86m+dwE7PPvXDujx1BWj5A6QL3hy5Q6IC14oS/qryvQpUxLlZZYzS/KMFDCrm4CB5BhcLH0EoRckehZg1CPS7CSDTJN2EF4EkWlIc6lb5U/v68PIaVYAY+QO0G+gBZAmggcYJ2J2EDw+0iDLRxBaIdEzGKufe8tFKh6jRMmsgRNqzreZcsiOjz0yWa/H6Jk1zQpgXPaOQWwwhlzBAwyQPDs+xswIEbOr3PFBWT6CEIRET0fsJnVqMKOPHBs7DJxw6t/NitG4VmbvAIsFD7Cv5On3qGRnCR4fTPzO/FgThO6Q6GkkDD9CkH/zcSpGj5QVIl2mUJFCrwEkaqTWiIEVgHy5A0jweNFbyJwueFwceDzxJ2Oov5+ERJ4PTaQvJHqEZvjkwY9OQ/cpJj5Ok0CmH6MRqM1WKpE7wDjBA3olT8Y3laWjaRmcKHkOFCKCIORDoucQlN54AcCXYZ3whHsFgy/jaXSfOD2yX2pkUfl+9fvvW2vzs5rry+jsXU9PTLKs7o8tS5SVXxQACR5BELaFRM9C1NxcjajfbCG0qhlYCfaZay8VPfoUqr32DM/cycSxggdYL3kkeASRVpDomYTRUqcFObGZJYNWjQq2I3oOEjFD7BL7MqjvHWCg4AHuz+KR4BFEWkKiZwB2ljq1iB2TmRlBN4mgWPO2Hmi5Do0aNcuQVoIHkOQRsgjCB6+G74IgDcYgeCDR0wE3ip0S+I4/I2rNF47WLJhaUVSz32w9++hpvAaNztwxKBU8AIiGfJB17zNa8ADK4hEE4ThI9DQSisq8CaUpoagPoSj/CbJysIgQTphTT69/LNTIHWCO4EWDfkQjMieRpgyecfXoCXMecyyNgiDSDhI9wjKEhMWOAmglVosdYGL2zulNtIA6yXOD4Mk9X0LlSAAJwhBI9GyElhuxnvizre3nZpf+gGYhlvXUitlyBxjc/w4gwTOyHjmoPT9a6iUJJAjVkOiZjF1kTgw5MVolg0qyW1ZLoVSsRvRj1Hp92VbuAGOnSQG0CYzVkqfnI9D4MEru1OzfxdKndWBWmAZjEDzo8yR1QpBgtz/p5Ra4x2XH44xnyqx7GYle5z3U7Ut6yaUnnJn0kks06FeXwZMred1Ql8HTksVTKnlqYjSiDim6oO3cGIUdY3IoGzduxPjx45GXl4e8vDyUlpbi7bffTqzv7u5GRUUFLrzwQgwYMAA333wzTp8+LVrn+fPnUVlZieLiYuTk5GDMmDHYtGmT0YdCiEAZPR2xi+DYgVDQl/gZjKReZlY3DzsNva4ttRk7QF2fOwbK4EEfMTND7pxCmmT5jKS4uBhr1qzBpZdeilgshpdeegk33XQTPvzwQ4wdOxbLly/HW2+9hVdffRX5+fmorKzED37wA/zlL38RrLOqqgq7d+/Gyy+/jOHDh2Pnzp343//7f2PIkCG48cYbTTw6goFETyNCIkOIIyYu6SqBRv2j4Fy5C8sor3wXpgseoE8GzyicJHdC0IjeJNrb25Pe+/1++P2pn8e5c+cmvX/yySexceNGHDhwAMXFxXjxxRdRV1eH73//+wCALVu2YPTo0Thw4AC++93v8u573759WLRoEWbMmAEAuPfee7F582a8//77JHoWYWnTbU1NDaZMmYLc3FwUFBRg3rx5aGlpEd1m69at8Hg8Sa/s7GyTIibMQKpZ2G5NxHLgiz0p66njsahtjmWjtEmWgWmaVTx6VknzLKC+6dLMJlpAexOr0U20bpA8Ng5v1g3BixB8Gl5eAEBJSQny8/MTr5qaGsl9RyIRbNu2DR0dHSgtLUVTUxPC4TBmzpyZKDNq1CgMHToU+/fvF6xn2rRpeOONN/DFF18gFovhnXfewaeffory8nLtJ4hQhaWpqD179qCiogJTpkxBT08PHn74YZSXl+O//uu/0L9/f8Ht8vLykoTQ4zG6J7I1aMnE6IEv2/4jXJ0ke0ag1zVieuYOUD56FjBf7rRAWTzrYI4vTXMAJ0+eRF5eXuI9XzaP4fDhwygtLUV3dzcGDBiA+vp6jBkzBs3NzfD5fLjggguSyhcWFqK1tVWwvl/+8pe49957UVxcjKysLGRkZOCFF17A9OnTNR8XoQ5LRa+hoSHp/datW1FQUICmpibRi8Lj8aCoqMjo8EzBapkTQ0lsTpBCp6PntaJF7ACT5Q4gwdMLtwseAQCJwRVyGDlyJJqbm9HW1obXXnsNixYtwp49e1Tv+5e//CUOHDiAN954A8OGDcPevXtRUVGBIUOGJGUHCfOwVeeytrY2AMDAgQNFy50/fx7Dhg1DNBrFpEmT8Itf/AJjx47lLRsMBhEM9n1zM30XwuEwwmEZfYAEYLbN7umRvQ3fjToH8re3NeeTewFk9MTfZ5zPQGZW8jqSwjjMtcO+hsRkTsu1whU7r8K6oiFuXAo+O0r73fWS0xXu+ym3k4kWkdF6WZoseDm95zIHYSCmX71ugTk/Wr7n9dje7vh8PlxyySUAgMmTJ+ODDz7A+vXrceuttyIUCuGbb75JyuqdPn1aMNHS1dWFhx9+GPX19ZgzZw4AYPz48WhubsYzzzxDomcRthG9aDSKZcuW4aqrrsK4ceMEy40cORK1tbUYP3482tra8Mwzz2DatGn46KOPUFxcnFK+pqYGq1evTlm+c+dO9OvXT3Pcv/xzk+Y63MzmD4T7chBx6BoSp/ZkwOoQbE0tJM5PmjZfMgQC2q6fzs5OnSJxBtFoFMFgEJMnT4bX68WuXbtw8803AwBaWlpw4sQJlJaW8m7LJFAyMpL/M8vMzEQ0GjU8doIf24heRUUFjhw5gnfffVe0XGlpadJFNm3aNIwePRqbN2/G448/nlK+uroaVVVVifft7e0oKSlBeXm57NQ2H+FwGIFAAEu/NxndWfHTaOdmWLPJ7unB5g/240dTShPnxwqMyB7q9XfW6xxpbYZlk5q5U4DaZlmBptKcaBi1JwNYXFKGrgwvfyG1GTyrs3c61JGDMGoRwGKUoau3E366Zu/4yEEYtdkBlJWVwesVuH5kwB3BaiRB+JGl4bYcVDjZcnV1NWbPno2hQ4fi3LlzqKurQ2NjI3bs2IH8/HzcfffdqKqqwsCBA5GXl4elS5eitLQ0acTtqFGjUFNTg/nz5yMvLw/XXHMNVqxYgZycHAwbNgx79uzBb3/7Wzz33HOqj4vQhi1Er7KyEm+++Sb27t3Lm5UTw+v14vLLL8dnn33Gu15oWLnX69X04Wdo7+mHLuY02uJs2ovurCx0WSh6XT0G7FvnKpWcI16p0xBPSl87pc7IljvF2/b+lGiW7crwJoseW+6UzhvASKXa8VtMzFrGf+lRB5Boru2CF13d2r/L3IrW73o97hN25cyZM1i4cCFOnTqF/Px8jB8/Hjt27EBZWRkAYO3atcjIyMDNN9+MYDCIWbNm4Ve/+lVSHS0tLYluVwCwbds2VFdX44477sDXX3+NYcOG4cknn8R9991n6rERfViqJrFYDEuXLkV9fT0aGxsxYsQIxXVEIhEcPnwYN9xwgwER2g89szdyyPLSI3Wswqi/teqBFAxqM3eAtoyTFfPfMdj5mbSUxSNU8uKLL4quz87OxoYNG7BhwwbBMrFYcgfRoqIibNmyRZf4CH2wVPQqKipQV1eH119/Hbm5uYkh2/n5+cjJic98uXDhQlx88cWJeYAee+wxfPe738Ull1yCb775Bk8//TQ+//xz3HPPPZYdh96YLXNiqI2lp0esdzjBpieciZ6YcX9zzWIHaJM7QLvgqZ3x062CZ/fpQ6T+P7TPVxxBuB5LRW/jxo0AkJhBm2HLli248847AQAnTpxI6tj597//HUuWLEFrayu+9a1vYfLkydi3bx/GjBljVti6YSehMwq5EuPGzKHU39coGdZF7ADr5U5L3207CJ4RmTY7TI+ix0eVrw73fx0ShCVY3nQrRWNjY9L7tWvXYu3atQZFZCzpIHZq0evcGCWMdv7b6SZ2gHa5A9QLjh4SY7XgGdWMaqXgmfU/GMkfoshERMNBR0Xn2SHSFRo+YCB2lgO34vZzrqvUMVgpd4B2idHj0ch2FLx0kDsp2HG4+6NNEIZBoqcjbpcMwjwMETo2VjbLAvaQO4AEzy5CJweSPoJQBYmeRozuSK83bIHI8Ot1tyTUwMxZFw35EI0YPIWD1Vk7BitHzgL2HVwBmCd4TpI7IZhjcM5XL0FYBomeA9Er22No1ijTJY92U4m8c2vgo5X0EDtAP6HR+mgyrXPOpbvguUHu+CDhIwhJSPRsiuFNdwajNVtlp2yj7f8Wekldoj4d6tCjaVaPfuXpLHhulTs+XCJ8QXiRCfXZ/aDm/4gIN0KiZzG2lwiLoPMigN5Sl6hXhzrs0u8OsK/gUfbOWKgfH0GkQKJnIiQvhGyMErpE/TrVY4cpUQB9hYzkzh3QOSEIACR6hkNyR/DSnQVEYn2/ZzpE7AB3yp3e9dHIWYIgbAKJns6Q2BGGZ+NkxaBjXXpJi93kzon97hhI7giCkIkN7kjOxpSpMbRipHhku3x0rR2kTQo7C4td5rvTux42JHeEToTgR5aGwRgh1Q+FJtyMA+5iRAp2kg+hWMxslkwXjHq8FoNd5a4b9plehQ2NmiUIwgHQHdjO2EnoCPMwWugY9BQVu2Xu9K6LgQZWEAThMMgk7AJJXfoRZP00o8VFb0khudMPp8id1vObrUsUBEEogOzCCkjq0gezsnNC2FXuAHv3uwPSW/CMOqd89ZL8JQjBp2nC5BBNmEzwQMZhFiR37sJqgePDKDGxo9zpXRdDusmdHa5jbgwkfgShK2QfRkFi5wzscKNTg9FCkk5yB6SX4Nn9mifxIwhdIRvREzfInV43gWjvT7P6n7kdM0RE78cL213w0mnUrN3lTgwmdhI+QiUnTpzA2bNnrQ7DdAYNGoShQ4eS6GnGTtOHOPnLnIhj5hMV9BY7wBi502N6FYZ0kTs3fheQ8BEqOHHiBEaPHo3Ozk6rQzGdfv364eOPPybRcxRu/PJORxjZiLLem5X1tHPWzqg63S53QQAxi2MwExcLX3wwhk/D9jQYg8vZs2fR2dmJl19+GaNHj7Y6HNP4+OOP8cMf/hBnz54l0bM1JHbOxMysnBhGZOwAez9jFnB/fzvmfHkA9LcwDqtxsfAR+jN69GhMmjTJ6jAsgUTPLpDUOQO7SBwXo6QOcEbWDnB35o6+H4Qh4SMIUUj0rIC+tO2BXaVNCiOljsHMedS04ObsHX1PKIOEjyB4IdEzC/rSVo5TRUxvzBA7gOSODcmdcyHhI4gkSPSMxA1f2mpvqlYMNHAyZskcF6c0ywLUNEsoIwjH9WHUPhiDIFKhW7DedLNedqNLxYvQlyDrxXwrm/Xt3M3zMqJuvemGMddihPMyE6P+DgShgJqaGkyZMgW5ubkoKCjAvHnz0NLSklTm6NGjmD9/Pi666CLk5eXhn/7pn3D69GnN9RLmQaKnB3b4wiZps56gjJeZGC0TRtTNvl6NiJnEjiAS7NmzBxUVFThw4AACgQDC4TDKy8vR0dEBAOjo6EB5eTk8Hg92796Nv/zlLwiFQpg7dy6i0ajqeglzIdHTipk3b5I467CbxHExMlsntA+9MPo6tkLuSOwIB9DQ0IA777wTY8eOxYQJE7B161acOHECTU1NAIC//OUvOH78OLZu3YrLLrsMl112GV566SUcPHgQu3fvVl2v3ZgxYwaWLVtm6j7vu+8+eDwerFu3TrLshg0bMHz4cGRnZ2Pq1Kl4//33Fe1Lkej953/+J5544gn86le/SnmcSHt7OxYvXqxo54QIJHPGIkfc7CJxXMyQOr596YmR17VVTbJuEju+a0zOi7AF7e3tSa9gUN6XWFtbGwBg4MCBAIBgMAiPxwO/358ok52djYyMDLz77ruy4+HWm+7U19fjwIEDGDJkiGTZV155BVVVVVi5ciUOHTqECRMmYNasWThz5ozs/ckWvZ07d+LKK6/Etm3b8NRTT2HUqFF45513Euu7urrw0ksvyd4xwYGkThtOFjcpzL6ZOlHuAOubZJ2CkcJGIqiJEPwIaniFEBeykpIS5OfnJ141NTWS+45Go1i2bBmuuuoqjBs3DgDw3e9+F/3798e//Mu/oLOzEx0dHfjpT3+KSCSCU6dOyTomvnrtxJ133ok9e/Zg/fr18Hg88Hg8OH78uGH7++KLL7B06VL87ne/g9frlSz/3HPPYcmSJbjrrrswZswYbNq0Cf369UNtba3sfcoWvVWrVuGnP/0pjhw5guPHj+PBBx/EjTfeiIaGBtk7I1iQ2KWiVNacKm5CWHVzNHJ/bm+WtTN2lC27xOFyTp48iba2tsSrurpacpuKigocOXIE27ZtSyy76KKL8Oqrr+IPf/gDBgwYgPz8fHzzzTeYNGkSMjLk6QNfvXZi/fr1KC0txZIlS3Dq1CmcOnUKJSUlvGXvu+8+DBgwQPQlRjQaxYIFC7BixQqMHTtWMrZQKISmpibMnDkzsSwjIwMzZ87E/v37ZR+j7OlVPvroI/zbv/0bAMDj8eDBBx9EcXEx/tf/+l/Ytm0bpkyZInunaYubhY4rW8yzN0PQ74H0boG5ycVY7808R0bfZN02DYoTpMQJMXJhx5xtWRSuJC8vD3l5ebLLV1ZW4s0338TevXtRXFyctK68vBxHjx6NPzM1KwsXXHABioqK8A//8A+a6rUL+fn58Pl86NevH4qKikTLPvbYY/jpT3+qel9PPfUUsrKycP/998sqf/bsWUQiERQWFiYtLywsxCeffCJ7v7JFz+/345tvvkladvvttyMjIwO33nornn32Wdk7ZaipqcF//Md/4JNPPkFOTg6mTZuGp556CiNHjhTd7tVXX8XPf/5zHD9+HJdeeimeeuop3HDDDYr3bwpOlDu3ZMisxk43XzNiMfJaNztj5wS6OT+dDEmfJcRiMSxduhT19fVobGzEiBEjBMsOGjQIALB7926cOXMGN954oy71OomCggIUFBSo2rapqQnr16/HoUOH4PGYm/2Q3XQ7ceLEpD55DP/8z/+M3/zmN7INlY2aIdj79u3Dbbfdhrvvvhsffvgh5s2bh3nz5uHIkSOK928Ydm6STZdmUDOwc38ks2Ix41o3Q/Ls8ncTw07Xl5Gky3HagIqKCrz88suoq6tDbm4uWltb0draiq6uvg/0li1bcODAARw9ehQvv/wybrnlFixfvjwpIXPdddfh+eefV1SvE9HSdPvnP/8ZZ86cwdChQ5GVlYWsrCx8/vnn+MlPfoLhw4fzbjNo0CBkZmamzFt4+vRpyewjG9kZvR//+MfYu3cv77rbbrsNsVgML7zwguwdA0jp37d161YUFBSgqakJ06dP591m/fr1uP7667FixQoAwOOPP45AIIDnn38emzZtUrR/XbHb9UvCpg9OudGYGadbmmbt+re1a1xWwZyPNMj0heBFhqYnY8SkC7HYuHEjgPj0Imy2bNmCO++8EwDQ0tKC6upqfP311xg+fDh+9rOfYfny5UnlmaZdJfXaCZ/Ph0hE+otHS9PtggULkvraAcCsWbOwYMEC3HXXXYJxTZ48Gbt27cK8efMAxPv57dq1C5WVlbL3LVv05s+fj/nz5wuuv/3223H77bcn3v/f//t/ceONN6J/f/nPoJEzBHv//v2oqqpKWjZr1ixs376dt3wwGEwaWt7e3g4ACIfDCIfDsmPjwmyb0xG2djZCmz7zJicWTvppO2xwM1V9jqyI3eh98nzH5iCc9FMX2P8A2aHvKN95lRlXjiec9NP1MH87mcLHXDdavuf12N7OxGLSYrhmzRqsWbNGtAx3lKqceu3E8OHD8d577+H48eMYMGAABg4cyDvYREvT7YUXXogLL7wwaZnX60VRUVFKdnT+/PkJkauqqsKiRYtwxRVX4Morr8S6devQ0dEhKId8GPas2x/96EeYOnWqrA6bgPwh2K2trbwdE1tbW3nL19TUYPXq1SnLd+7ciX79+smKTYzaswHNdbiZ2jY6P1LUBh1wjizMptT21/H8OOzZp3KoHeyA68dCAgFt56ezs1OnSAi78tOf/hSLFi3CmDFj0NXVhWPHjgk2pxoNNzt666234quvvsKjjz6K1tZWTJw4EQ0NDSkeJIZhoqfU6Jkh2EomYZRDdXV1Ugawvb0dJSUlKC8vVzQqiUs4HEYgEMDiQWXoypCeC0cVNs3WySEnFkZtWwCL88vQ5dFwfmyQeTOKnFgYtcEAFvvL0BU06BqSixnnWWGTbA7CqO0fwOKOMnRBwfmxU7cFA89rjieM2sEBLD5Vhq6YxdePVYj8A5KDMGoHBlBWViZrvjIhmFYgwr185zvfUTRdiV7wzdfHt6yyslJRUy0Xw0RPCUqGYBcVFSnqmOj3+5Nm9Wbwer2aPvwMXRle/USPe4OyQ9OSRro8Xn7Rk3sDdME5SIJ93L3/C3UFBc6RkZjVp1SH/nZd8EqLnp3+ITA5lq6YN31Fj7mO+YSv97tD63e9HvcJuQThhwep9yv52xNEKpY+6zYWi6GyshL19fXYvXu3rCHYpaWl2LVrV9KyQCCA0tJSo8I0DjeMdpUadWrHEalGY8eRuGZO0G3WJMZ2OK8MdoolHaFzTxCCWJrRq6ioQF1dHV5//fXEEGwgPoFhTk4OAGDhwoW4+OKLE49weeCBB3DNNdfg2WefxZw5c7Bt2zYcPHgQv/71ry07DkU4Tejoy5Mfu58Xs0eBp9NIWTvEQPDTjbQYnUsQSrBU9OQMwT5x4kTS6Jdp06ahrq4OjzzyCB5++GFceuml2L59uy2foZfAznJHNy1hnHRurJjexyy5CwIKZ43QFyddB0Tf3yvH0igIwjYoFr133nkH1157Le+6zZs340c/+hEAYNiwYZJ9G+QM2GhsbExZdsstt+CWW26RDtZK7CZ3dLNKxsnnw6o5G81+OoUH1oySdfK1QfRBf0eCAKCijx4zWTF7bqGzZ89i7ty5eOihhxLLjhw5IvhgYFdjh/52duobZgVST6xwyvng9quz4mkrVvS3M/vv48Rrg3AlUWQiouEVRabVh0DYEFUZvYULFyIQCKCurg7Hjh3D3XffjZEjR6K5udmAEB2CFXKXjjclNx6znY4pHZ4pa6fzrTdWHBv1iSMIW6NY9KZNm4bm5mbcd999mDRpEqLRKB5//HE8+OCDpj+o1xaEYN4UIG6+QTG4/Ri5GTk73CRJ7pwFcywe1ns79mG0w7VNEL18/PHHVodgKuzjVTUY49NPP8XBgwdRXFyML7/8Ei0tLejs7FT0uDNCJm66QQHuOx4h7PbsYzZmih2DGx/bZiROjp2BewwkfoQFDBo0CP369cMPf/hDq0MxnX79+mHQoEHKRW/NmjVYuXIl7r33Xjz99NP47LPPsGDBAowfPx4vv/yyM+ezsxtO/5JnZxiYTvVuxs5SB1gjdgBl75TgxJiVwj5Gkj7CJIYOHYqPP/446bFi6cKgQYMwdOhQ5aK3fv16bN++HbNnzwYAjBs3Du+//z4efvhhzJgxA8Gg1SMRHIhTv+SdGrca7C5zbKwSO4DkTg5OitUomHNAwpdECF544NOwfVTHaNzD0KFDMXToUKvDsAzFonf48GEMGjQoaZnX68XTTz+Nf/zHf9QtMNfjlC97p8SpF04SOsBaqQNI7MRwQoxWQ1k+gjAcxaLHlTw211xzjaZg0gI7f/nbOTa9cZrQMVgtdgD1t+PD7vE5AcryEYQhWPpkjLTBjjcBO8ZkBE4VOgY7iB1grdzZ8Vq1Y0xugYSPIHSFRM9I7HIzsEscRuB0kWNjF6ljsDpzZ6dBPG7+DNkVatYlCF0g0dMbO9wQ7BCDHrhJ4thwhc4ugkf97fqwY0zpTDfSQvaC8APwa9jeygkVCbtCoqcXVt4Y3HJTcqPY2UXihKDrNo6dYiH4oSZdglAFiZ5WrJgnzsk3JTfKHGB/oWOw+tqxev8MdomDUE6aZPcIQi9I9JyAE29KzJexE2MXwylCB9jn3FMchN5Qdo8gZEOiZ1ecdFNyY5bOSULHYJdrhuIgzIKyewQhCYmenbD7jYmEzj7Y7VqxSzx2iYMwDxdl98LwaXoyRpiejEHwQKJnNXa9MblN6pwqdIA9rxG7xGSXOOxAuk9HQtk9guCFRM8K7HhzcovYOVnoAHteG4B94rJLHEYi9xgzen8GgZREjtbz5FRhItkjiBRI9MzETjcpN4id06WOIQjYcvorO12vdopFL+x8TGKx2V2kmNhzLI2CIGwDiZ5ZWP2l7mSxc4vQca8BD4D+VgQigtXXKRs7PwJNDk6NWwqnNBE78PwH4UNMw4TJIeqjR/BAomc0Vn7ZOFHu3CJ1gDNuNHaL0a6PQJPCbufRLJwifQSRxmRIFyFU0Q1rvvy7WC8nEOG8nEo3z8uu2C1Gu8UjhZP+1mZC58Nx1NTUYMqUKcjNzUVBQQHmzZuHlpaWxPrjx4/D4/Hwvl599VXRuj/++GPceOONyM/PR//+/TFlyhScOHHC6EMieCDR0xuzv+i64By540qdU8XOaTd6O8Zqt3jEsOP5szt0vhzBnj17UFFRgQMHDiAQCCAcDqO8vBwdHR0AgJKSEpw6dSrptXr1agwYMACzZ88WrPfo0aO4+uqrMWrUKDQ2NuKvf/0rfv7znyM7m9K+VkBNt3ph9heaE6SO/dOpOPVGZce47RgTH06J0ylQ867ptLe3J733+/3w+1P7/jU0NCS937p1KwoKCtDU1ITp06cjMzMTRUVFSWXq6+vxT//0TxgwYIDg/n/2s5/hhhtuwL/+678mln37299WcyiEDlBGTytm/tdq58wdZeqsxa5x2zEmPpwSp9OhcyxKCD7NLyCeicvPz0+8ampqZO2/ra0NADBw4EDe9U1NTWhubsbdd98tWEc0GsVbb72F73znO5g1axYKCgowdepUbN++XdnJIHSDMnp2x45Sx+BUoWNw8g3HzrHbOTbA/vGlAy56moUdOXnyJPLy8hLv+bJ5XKLRKJYtW4arrroK48aN4y3z4osvYvTo0Zg2bZpgPWfOnMH58+exZs0aPPHEE3jqqafQ0NCAH/zgB3jnnXdwzTXXKD8gQhMkenbFjoLnZLFz8s3d7rFTfIRaaIJjQ8jLy0sSPTlUVFTgyJEjePfdd3nXd3V1oa6uDj//+c9F64lG41O83HTTTVi+fDkAYOLEidi3bx82bdpEomcBJHp2wy6CR1JnHU6I384x2jk2IhXK7llOZWUl3nzzTezduxfFxcW8ZV577TV0dnZi4cKFonUNGjQIWVlZGDNmTNLy0aNHC0okYSwkenaA5E49bripd8OeT8ZgY/fzbPf4CGlI+EwnFoth6dKlqK+vR2NjI0aMGCFY9sUXX8SNN96Iiy66SLROn8+HKVOmJE3TAgCffvophg0bpkvchDJI9KzEDoLnJLlzw83cSRMC2/182z0+Qh1pLHwh+BDrHVChhrDCL/SKigrU1dXh9ddfR25uLlpbWwEA+fn5yMnpe4bcZ599hr179+KPf/wjbz2jRo1CTU0N5s+fDwBYsWIFbr31VkyfPh3XXnstGhoa8Ic//AGNjY3qDozQBIme2ZDcycMNN3EnHoPdY7Z7fIR+pLHwmcXGjRsBADNmzEhavmXLFtx5552J97W1tSguLkZ5eTlvPS0tLYkRuwAwf/58bNq0CTU1Nbj//vsxcuRI/P73v8fVV1+t+zEQ0lg6vcrevXsxd+5cDBkyBB6PR3L4dWNjI+8M3cx/IbbGymlRnDD1iV2nB5GLU6dmcULMQdg7PsJY6O9uGLFYjPfFljwA+MUvfoETJ04gI4NfGfi2Wbx4Mf72t7+hq6sLzc3NuOmmmww6CkIKSzN6HR0dmDBhAhYvXowf/OAHsrdraWlJGlFUUFBgRHj6YKXc2Rmnf3k7NX4nxM3ESLN8EgyU3SMI1VgqerNnzxZ9jIoQBQUFuOCCC/QPSE9I8JJxgmAIQbEbixNiJOwBTcdCEIpxZB+9iRMnIhgMYty4cVi1ahWuuuoqwbLBYBDBYDDxnnk0TDgcRjgcVh0Ds20OwqkjJs2+cdlQ7nKCYaB/70/A/gMP2HD/fgbFnuMJJ/3UBZNi10yQ9btA5i4nI5z00zXo9P2QOD+hMBBlrXC7CIUASM//m/hcafme12N7JcQHY8g4OAGUDsYg0gNHid7gwYOxadMmXHHFFQgGg/jNb36DGTNm4L333sOkSZN4t6mpqcHq1atTlu/cuRP9+vXTHFMtAqkL3f5FK4f+8R+1A3nOD5FE7WA6R2LUjqTzI0btBDo/YgQC2s5PZ2enTpEQhDV4YrGYLWbw8ng8qK+vx7x58xRtd80112Do0KH4t3/7N971fBm9kpISnD17VvHM4WzC4TACgQAWowxd3V7V9ajCTv+0BfkX5yCM2oEBLP66DF0w+fxIYZOmwhxPGLWDA1h8qgxdMQXnyCbxy0bgGpEiJyOM2pEBLG4pQ1fUZtcQYPnfIScjjNoJASz+Tx3Oj1P/ORVJfuV4wqgdEUBZWRm8XvXnp729HYMGDUJbW5ume4bUPvLz8zGn7UV489QnIMLtnXgr/25DYyWch6MyenxceeWVorNt+/1+3uf8eb1eTR9+hq5ur3kiYxfBk3OD620y7IJXmcQYhY3lqCsm4xzZOH5edIy3K+q1XvRsfP51OT98SSsnyF8XJOPU+l2vx32CIKzE8aLX3NyMwYMHWx2GsdhB8Gx8o+PFafFycWL8ToxZCDcdi1rY58DO0kcDNAhCFEtF7/z58/jss88S748dO4bm5mYMHDgQQ4cORXV1Nb744gv89re/BQCsW7cOI0aMwNixY9Hd3Y3f/OY32L17N3bu3GnVIRiL1YLnpJudk2IVwonH4MSYhXDTseiN3ac3cYnsBeFDVNOTMXp0jIZwC5aK3sGDB3Httdcm3ldVVQEAFi1ahK1bt+LUqVM4ceJEYn0oFMJPfvITfPHFF+jXrx/Gjx+PP/3pT0l1uAIrBc8pNzunxClGN+unLXrKysRN555Qhp2FzyWyRxB6Y6nozZgxA2JjQbZu3Zr0/sEHH8SDDz5ocFQWQoInjhNiFMMpU5/w4bZzT2jDrsJHskcQKTi+j54rsErw7H7zs3t8UlD81uL0+J2AHYWPZI8gkiDRsxISvGTsGpdcKH7rccMxOBG7CV83gByrg1BOGH5NEyb3WN6xm7AjJHpWQILXhx1jUoKT43dy7AxuOAY3YSfhUzl/I0G4DRI9M7FC8Ox2I7RbPEqh+K3F6fGnC3YSPoJIc0j0zMJsybPTDdFOsSjFybEDzo8fiB9DVLIUYUdI+AjCckj0jCZdBa+b89MpOC1ePpx+DEz8GZZGQegJCR9BWAaJnpGYKXl2ubkzcThl6hC7nDct0DEQToFGxIoSQQY8yNS0PUFwIdEzgnQTPDvEIBcnxSqGG47DDcdAKIeyewRhKiR6emOW5NnhJmmHGOTglDilcMNxuOEYCH2g7B5BmAKJnl6Q4NkLJ8QoBzcchxuOgTAGkj2CMBwSPT1IB8lzws3aCTHKgY6DSCdI9gjCUEj0tGKG5JHgieOEGKVwwzEA7jkOwlxI9gAAQfgR0fRkjB4doyHcAome3bHqxmn3G7bd45NDkPPTqbjhb0FYD8keQRgCiZ5dIcFLxc6xyYV9DE6eCcENfwvCfpDsEYTukOjZEStuona9cds1LiW44RgA9xyHEXSZtJ8ck/ZjJSR7BKErJHp2ggQvjh1jUoobjgFwz3HogVkyJwY7BmZe3W4APgtiMRKSPYLQDRI9u0CSZ794lOL0+Nm46ViUYgehUwo7Zrdk/dJQ9sLwIarB2iMI6xgN4Rac3EvIHXTD/JuqFfsUohv2ikcpTo+fjZuORS5dPC+n46ZjSadr0QJqamowZcoU5ObmoqCgAPPmzUNLS0tKuf379+P73/8++vfvj7y8PEyfPh1dXeIX2IYNGzB8+HBkZ2dj6tSpeP/99406DEICEj0rIcFzJm4SIjcdi1zcJEJSuOFY0+naNJk9e/agoqICBw4cQCAQQDgcRnl5OTo6OhJl9u/fj+uvvx7l5eV4//338cEHH6CyshIZGcL68Morr6CqqgorV67EoUOHMGHCBMyaNQtnzpwx47AIDtR0awVWCJ4dsEscanBy7FzcdCxycLLk6ImTm3fTsBnXDBoaGpLeb926FQUFBWhqasL06dMBAMuXL8f999+Phx56KFFu5MiRovU+99xzWLJkCe666y4AwKZNm/DWW2+htrY2qR7CHCijZzbpKHlOzRi5KdvlpmORgxsyWUZC58fVtLe3J72CQXmTdba1tQEABg4cCAA4c+YM3nvvPRQUFGDatGkoLCzENddcg3fffVewjlAohKamJsycOTOxLCMjAzNnzsT+/fs1HBWhFhI9MzHzJmuXm7odYlCCm4TITccihdv62ZmJU85ZGlzHQXgRhE/DywsAKCkpQX5+fuJVU1Mjue9oNIply5bhqquuwrhx4wAA//3f/w0AWLVqFZYsWYKGhgZMmjQJ1113Hf72t7/x1nP27FlEIhEUFhYmLS8sLERra6uW00OohJpuzcBswbMSZv8eS6NQhtXnTE/cdCxiOEFMnAZzTu3crEtNuLI4efIk8vLyEu/9funHqlVUVODIkSNJ2bpoNAoA+NGPfpRohr388suxa9cu1NbWyhJIwnpI9IwmXSTPaYLhtHjFcNOxiEFyZw52Fz6SPUny8vKSRE+KyspKvPnmm9i7dy+Ki4sTywcPHgwAGDNmTFL50aNH48SJE7x1DRo0CJmZmTh9+nTS8tOnT6OoqEh2TIR+UNOtkZh1A7a6ec5JomH1udITNx2LGN0gybMCOueuJxaLobKyEvX19di9ezdGjBiRtH748OEYMmRIypQrn376KYYNG8Zbp8/nw+TJk7Fr167Esmg0il27dqG0tFT/gyAkoYyeUZgpeVbhFMlwSpxycdvx8NGFvic/ENbRBXtm9lya1QvBj0xIN7MKoXTC5IqKCtTV1eH1119Hbm5uog9dfn4+cnJy4PF4sGLFCqxcuRITJkzAxIkT8dJLL+GTTz7Ba6+9lqjnuuuuw/z581FZWQkAqKqqwqJFi3DFFVfgyiuvxLp169DR0ZFo/iXMhUTPCEjy7IETYlSC246HC2WQ7Indm3IJ1WzcuBEAMGPGjKTlW7ZswZ133gkAWLZsGbq7u7F8+XJ8/fXXmDBhAgKBAL797W8nyh89ehRnz55NvL/11lvx1Vdf4dFHH0VraysmTpyIhoaGlAEahDmQ6OmN2yXPCbLhhBiV4Lbj4UKC5wzslt1zaVbPTGKxmKxyDz30kOj8d8ePH09ZVllZmcjwEdZCoqcnJHnWYvf4lOK242FDcudMSPYIwnGQ6OmFGTdlErxU7BybWtx4TAwkeM7HbrJHEIQoJHp6QJJnPnaNSwtuPCYGEjx3YSfZc1FWLwQfMuBTvX1Uw7aEe7F0epW9e/di7ty5GDJkCDweD7Zv3y65TWNjIyZNmgS/349LLrkEW7duNTxOUeQ9WUYbVgiAXafusGtcWnDjMQH0pAq3Q39XgnAElopeR0cHJkyYgA0bNsgqf+zYMcyZMwfXXnstmpubsWzZMtxzzz3YsWOHwZFaiFWSZzfcJkNuOx4Gkrv0wi5/Zzd+lghCJyxtup09ezZmz54tu/ymTZswYsQIPPvsswDis3O/++67WLt2LWbNmmVUmNZh9peXHb8s7RiTFtx2PAx2ueET5mOnZlyCIFJwVB+9/fv3Y+bMmUnLZs2ahWXLlgluEwwGEQz2ta+2t7cDAMLhMMJhZZNLsmG2zUHYmOe6dsPc58XqvL8cTzjpp2KYP5lbnt3CCB7reHIywkk/HQdbWg2Y3DgnM5z0k0jGVufHDp/TEJL66jGfKy3f83psTxBW4yjRa21tTZlwsbCwEO3t7ejq6kJOTuq/lTU1NVi9enXK8p07d6Jfv36aY6odGNBch5upHUHnR4raCXSOxKi9ks6PGHR+xAkEtJ2fzs5OnSKRJj4YQ/2TMaII6RgN4RYcJXpqqK6uRlVVVeJ9e3s7SkpKUF5eruihz1zC4TACgQAWf12GLnj1CNVVTbU5njBqRwSw+FgZumIyzo8Zg1rMRMa5zckIo3ZCAIv/swxdUZ2uISMx+frMyQyj9soAFr9fhq6IA86Pydjy/Fg9+pWT0asdGUBZWRm8XvXnh2kFIgin4ijRKyoqwunTp5OWnT59Gnl5ebzZPADw+/3w+1P/Q/J6vZo+/Axd8MoTGSlcJHlsumIS58dNfdZUHktX1Gtf0bNB37uuiNc+ImNDbHV+OmBtf71OpMim1u96Pe4TBGElduhZIZvS0lLs2rUraVkgEEBpaalFEemESyVPErvEoRU3jqClkbOEWui6IQhbYanonT9/Hs3NzWhubgYQnz6lubkZJ06cABBvdl24cGGi/H333Yf//u//xoMPPohPPvkEv/rVr/Dv//7vWL58uRXh64OZgmAXIbFLHFpxy3Ew0NQohF5YeQ256TNJEDpgadPtwYMHce211ybeM33pFi1ahK1bt+LUqVMJ6QOAESNG4K233sLy5cuxfv16FBcX4ze/+Y1zp1ZJxy8ktxyzW46DgeSOICwnFPUhI6rhyRgatiXci6WiN2PGDMRiMcH1fE+9mDFjBj788EMDozKJdGuutXr/euGW42BDkkcYAc2vRxC2wFGDMQiVWC0nVu9fD9xwDFxI8Ai30g1A++xZBOEKHDUYwzWY3S/PStwwbYrV59AISPIIM6DrjCAshzJ6ZpNOkud03Hr+6OZLpANu/fwShEJI9MwknSTP6Zk8q8+fEZDgEVZAffVkE+r2w+NT/2SMWDc9GYNIhUSP0J9uOLdTgBsFDyDJIwiCSFOcejt2HumQzXP6vHJOjl0MkjzCaugaJAjLoIyeGaSL5DkVJ8cuBt1cCYIg0h4SPTdBkqcMp8YtB5I8gnAcoW4vPD71kx7Huum5vEQq1HRrNGbJBEmeMpwatxT0CDPCrtB1SRCWQBk9IyHJsx9OjFkudCM1BrUjyNUPniQIgtANEj2jcLNQAM48PifGLBeSPH3Qc1ogbl0kfjTVCkFYAIme07FCXpwmTE6LVykkedowa85H9n5I+giCMAkSPSNwc5Otk6TJSbGqgQRPGXaaxJuyfQQPPT2Z8IQzVW8f61G/LeFeaDCG3rhZ8pyE288PSZ48gqyXnXFKnHpA165tqKmpwZQpU5Cbm4uCggLMmzcPLS0tvGVjsRhmz54Nj8eD7du3y97HfffdB4/Hg3Xr1ukTNKEYEj1CPk6RJ6fESRhHukgTQWhgz549qKiowIEDBxAIBBAOh1FeXo6Ojo6UsuvWrYPH41FUf319PQ4cOIAhQ4boFTKhAmq6dSIkMsKkw7mhjIgwJHcEIZuGhoak91u3bkVBQQGampowffr0xPLm5mY8++yzOHjwIAYPHiyr7i+++AJLly7Fjh07MGfOHF3jJpRBoqcnbpYMJxybE2IkjMMtkhcE9dkjNNHe3p703u/3w++Xvqja2toAAAMHDkws6+zsxO23344NGzagqKhI1v6j0SgWLFiAFStWYOzYsQoiJ4yAmm6dBg3ASG8om5cKNdMSLiEW9COq4RULxmWupKQE+fn5iVdNTY3kvqPRKJYtW4arrroK48aNSyxfvnw5pk2bhptuukn2cTz11FPIysrC/fffr/wkELpDGT29IBmyFjr/6QkJHkGkcPLkSeTl5SXey8nmVVRU4MiRI3j33XcTy9544w3s3r0bH374oex9NzU1Yf369Th06JDiPn2EMVBGz0lQNo8fJ8SoB5TN68PtWTw3HxthOHl5eUkvKdGrrKzEm2++iXfeeQfFxcWJ5bt378bRo0dxwQUXICsrC1lZ8dzQzTffjBkzZvDW9ec//xlnzpzB0KFDE9t8/vnn+MlPfoLhw4frdYiEAiijRzibdJE8og+SIILQhVgshqVLl6K+vh6NjY0YMWJE0vqHHnoI99xzT9Kyyy67DGvXrsXcuXN561ywYAFmzpyZtGzWrFlYsGAB7rrrLn0PgJAFiZ4euFU23HpcToSyeXFI8ghCNyoqKlBXV4fXX38dubm5aG1tBQDk5+cjJycHRUVFvAMwhg4dmiSFo0aNQk1NDebPn48LL7wQF154YVJ5r9eLoqIijBw50tgDIngh0XMKJF2p0DlJL9JN8mj0bfoRzAK8Gm7LQWXbbty4EQBSmmG3bNmCO++8U3Y9LS0tiRG7hP0g0SP4sbtE2T0+PaFsXvpJHkGYQCwW02UbqXqOHz+ueD+EftBgDK2YIRzpJDVyoPORXqSz5KXzsRMEoQskekQqJFL2IZ2zeSQ5BEEQmiHRszskXcmk0/kgySPcSjpf2wRhMtRHj0jGziJl59gIfSDBI9KZbmi7K9N3JMEDZfQIwo6kY8aDJI8fOi8EQWiARM/O0H9nfdC5cDckM8K4cYqVHKsDIIj0gUSPIAjCrrhR8giCMBVbiN6GDRswfPhwZGdnY+rUqXj//fcFy27duhUejyfplZ2dbWK0LoayZgRBENbRjXi3DbUv+g4neLBc9F555RVUVVVh5cqVOHToECZMmIBZs2bhzJkzgtvk5eXh1KlTidfnn39uYsQEYQLUtEVQNo8gCB2wXPSee+45LFmyBHfddRfGjBmDTZs2oV+/fqitrRXcxuPxJJ7BV1RUhMLCQhMjJgiCMBiSPIIgdMLS6VVCoRCamppQXV2dWJaRkYGZM2di//79gtudP38ew4YNQzQaxaRJk/CLX/wCY8eO5S0bDAYRDPb19G5vbwcAhMNhhMNh1bEz2+Z41Nchice4qnnRUftzMsJJPzVj+b8k+iN5jjJNDMZqeI41JzOc9DNtkCl5jj0/JvW0YT5XWr7n9dieIKzGUtE7e/YsIpFISkausLAQn3zyCe82I0eORG1tLcaPH4+2tjY888wzmDZtGj766CMUFxenlK+pqcHq1atTlu/cuRP9+vXTfAy1gwOa63AztSPp/EhRO4HOkRi136PzI0btlXR+xAgEtJ2fzs5OnSIhCGtw3ITJpaWlKC0tTbyfNm0aRo8ejc2bN+Pxxx9PKV9dXY2qqqrE+/b2dpSUlKC8vBx5eXmq4wiHwwgEAlh8qgxdMa/qekQxu2OtjlNc5GSEUTsygMUtZeiK6nB+XNjJOCcjjNoJASz+T4Fz5MJjFoVz/eVkhlH7vQAW/7kMXRGDPmN2Q0GTbU5mGLVXBrD4fYedHxMzerUTAigrK4PXq/78MK1AphCEtrsyTVNE8GCp6A0aNAiZmZk4ffp00vLTp0+jqKhIVh1erxeXX345PvvsM971fr8ffn/qt6fX69X04WfoinmNET0rbvJR/avsinr1ET0DYrMLgufIh/SaODnCv7gr4nWWyKjFD8FzIIbjzo/Jn2Wt3/V63CcIwkos7fnk8/kwefJk7Nq1K7EsGo1i165dSVk7MSKRCA4fPozBgwcbFSZBEISxpMvgCxpNThCmY3nTbVVVFRYtWoQrrrgCV155JdatW4eOjg7cddddAICFCxfi4osvRk1NDQDgsccew3e/+11ccskl+Oabb/D000/j888/xz333GPlYRAEQagjXSSPIAhLsFz0br31Vnz11Vd49NFH0draiokTJ6KhoSExQOPEiRPIyOhLPP7973/HkiVL0Nraim9961uYPHky9u3bhzFjxlh1CARBEOogySMIwmAsFz0AqKysRGVlJe+6xsbGpPdr167F2rVrTYiKIAhT8SO9OpOnm+RRs6003dA2rVK6DeAiZOHC2ckIwkXQzdGdpJvkEQRhGbbI6BEEQaQFJHgEQZgMZfQIgiDMgCSPIAgLINEjnIFJk6zaknRqvvXDfULkxmNSSjpdwwRhM6jp1q5kw/yOtVbsUwl2j89IcpBekyf74fxn/aa73DGQ5MknCG3XfToNZiJkQ6JHOIt0lr10xQ/ACY8bJbEjCMKGUNMtkYwTmkidEKMRpHNmxI7Nn37Oi0glna9ZgrAJlNGzM5S9EiZdz026NeFy4QqVmU1VJHMEQTgQEj2CcBrpLntsxORLrQSS0OmD1dm8dM38EwQHEj0iFadky5wSpxGQ7ElDwmYdVkueU+kC4NG4PUFwoD56hLOh/9oJguBC3wuyqKmpwZQpU5Cbm4uCggLMmzcPLS0tifVff/01li5dipEjRyInJwdDhw7F/fffj7a2NtF6z58/j8rKShQXFyMnJwdjxozBpk2bjD4cQgASPbtj1ReWk74onRSrnlDWhLAjdF06hj179qCiogIHDhxAIBBAOBxGeXk5Ojo6AABffvklvvzySzzzzDM4cuQItm7dioaGBtx9992i9VZVVaGhoQEvv/wyPv74YyxbtgyVlZV44403zDgsggM13RLuIF2bcakJl7ATdpC8dP3Hj0V7e3vSe7/fD78/tS9DQ0ND0vutW7eioKAATU1NmD59OsaNG4ff//73ifXf/va38eSTT+KHP/whenp6kJXFrxD79u3DokWLMGPGDADAvffei82bN+P999/HjTfeqPHoCKVQRo8QxmlfmE6LVy/scHMlCLoOtRPR4QWgpKQE+fn5iVdNTY2s3TNNsgMHDhQtk5eXJyh5ADBt2jS88cYb+OKLLxCLxfDOO+/g008/RXl5uaw4CH2hjJ4TSNdslRrS9VxRZo8g0vefPQ4nT55EXl5e4j1fNo9LNBrFsmXLcNVVV2HcuHG8Zc6ePYvHH38c9957r2hdv/zlL3HvvfeiuLgYWVlZyMjIwAsvvIDp06crOxBCF0j0CPeRrrJHEFZB2TxbkZeXlyR6cqioqMCRI0fw7rvv8q5vb2/HnDlzMGbMGKxatUq0rl/+8pc4cOAA3njjDQwbNgx79+5FRUUFhgwZgpkzZyqKi9AOiR4hjlOlyalxa4GyeoQV2EXyKJunmsrKSrz55pvYu3cviouLU9afO3cO119/PXJzc1FfXw+v1ytYV1dXFx5++GHU19djzpw5AIDx48ejubkZzzzzDImeBVAfPadAX2LKyUb6nTe73HSJ9ICuN0cTi8VQWVmJ+vp67N69GyNGjEgp097ejvLycvh8PrzxxhvIzhb/Ug2HwwiHw8jISNaLzMxMRKNRXeMn5EEZPUIap2fHnB6/UiizR5iBnSTPLf/QBaEt/aLwaTAVFRWoq6vD66+/jtzcXLS2tgIA8vPzkZOTk5C8zs5OvPzyy2hvb0+M6L3ooouQmZkJABg1ahRqamowf/585OXl4ZprrsGKFSuQk5ODYcOGYc+ePfjtb3+L5557TsPBEWoh0dNKNsy7qVopLE6XJeZG4ORjUALJHmEkdpI8QjUbN24EgMQ0KAxbtmzBnXfeiUOHDuG9994DAFxyySVJZY4dO4bhw4cDAFpaWpImUd62bRuqq6txxx134Ouvv8awYcPw5JNP4r777jPuYAhBSPQI+Thd9oD0Ej6SPcII7CZ5bsnmWUAsFhNdP2PGDMkyfPUUFRVhy5YtmmIj9IP66OmBmV80Vn+pWb1/vUiX/ns5sN+NmXAudC0RhOMg0XMiVguK1fvXk3QSPoJQi13/YUiHzy5BaISabvXC7GZNq5tRrd6/3rBvGG46LjbUlEuowY6C51a6AXg0bk8QHCij52Ss/m/W6v0bhZuzfHbNzBD2xM7Xils/owShMyR6epKOXzxuPma3Cx9BCGH3fwjc+rkkCAMg0XM6dvjCs0MMRuJW4bPzjZywDrtfF278LBKEgZDo6Y0VX0J2+OJzqwyxyYb7jtPuN3XCPOyexSMIQhU0GMMt2GVwhF3iMBo3zcdHgzQIpwiem/7J4iMIbYMxFD4Zg0gPKKNnBG7/MpIinY7fLRk+p9zoCX1xUhbPDZ8zgrAAEj2jSNcmXAa/1QGYjBuEz0k3fUIbTvtbK/xsxR4G2n5iTCgE4TRsIXobNmzA8OHDkZ2djalTp+L9998XLf/qq69i1KhRyM7OxmWXXYY//vGPJkXqAJwuG07HLcJHuBOnCZ5CYg/HXwRB9GG56L3yyiuoqqrCypUrcejQIUyYMAGzZs3CmTNneMvv27cPt912G+6++258+OGHmDdvHubNm4cjR46YHLkMrLrh20k00i2zx+B04XOxDKQtTv2byvgckeARhDCWi95zzz2HJUuW4K677sKYMWOwadMm9OvXD7W1tbzl169fj+uvvx4rVqzA6NGj8fjjj2PSpEl4/vnnTY5cJiR7zpceLTj5uF2e/UkbnPx3FPn8MHLnKsHr1uFFEBwsHXUbCoXQ1NSE6urqxLKMjAzMnDkT+/fv591m//79qKqqSlo2a9YsbN++nbd8MBhEMNg3FKm9vR0AEA6HEQ6HVcfObJuDsPQoqRxY8wG0ar8AcjzhpJ+JeNJxVFi/3p+cv0VORjjpp23pD0uuo5zMcNJPIhnJ8+PkfzIAwfiZvndSX9/Md7SW73k9ticIq7FU9M6ePYtIJILCwsKk5YWFhfjkk094t2ltbeUt39raylu+pqYGq1evTlm+c+dO9OvXj2cLZdQODGiuw83UjqDzI0XtBDpHYtReSedHjHQ7P0q7ZAcC2s5PZ2enpu0JwmpcP49edXV1Ugawvb0dJSUlKC8vR15enup6w+EwAoEAysrK4PV69QjVVdD5kYbOkTh0fsSh8yOOXueHaQUiCKdiqegNGjQImZmZOH36dNLy06dPo6ioiHeboqIiReX9fj/8/tQRAV6vV5cvR73qcSt0fqShcyQOnR9x6PyIo/X8mHputXZtSceuMYQklg7G8Pl8mDx5Mnbt2pVYFo1GsWvXLpSWlvJuU1pamlQeiKfmhcoTBEEQBEGkK5Y33VZVVWHRokW44oorcOWVV2LdunXo6OjAXXfdBQBYuHAhLr74YtTU1AAAHnjgAVxzzTV49tlnMWfOHGzbtg0HDx7Er3/9aysPgyAIgiAIwnZYLnq33norvvrqKzz66KNobW3FxIkT0dDQkBhwceLECWRk9CUep02bhrq6OjzyyCN4+OGHcemll2L79u0YN26cVYdAEARBEARhSywXPQCorKxEZWUl77rGxsaUZbfccgtuueUWg6MiCIIgCIJwNrYQPYIgCIJIe7oARDVsT4MxCB4sfzIGQRAEQRAEYQwkegRBEARBEC6FRI8gCIIgCMKlkOgRBEEQBEG4FBqMQRAEQRB2IGTx9oQroYweQRAEQaQhNTU1mDJlCnJzc1FQUIB58+ahpaUlqcyvf/1rzJgxA3l5efB4PPjmm290qZcwDxI9giAIgkhD9uzZg4qKChw4cACBQADhcBjl5eXo6OhIlOns7MT111+Phx9+WNd6CfOgpluCIAiCSEMaGhqS3m/duhUFBQVoamrC9OnTAQDLli0DwP/wAi31EuaRdqIXi8UAAO3t7ZrqCYfD6OzsRHt7O7xerx6huQo6P9LQORKHzo84dH7E0ev8MPcK5t5hKCFt9yVme+79ze/3w+/3S27e1tYGABg4cKC2OEyql5BH2oneuXPnAAAlJSUWR0IQBEE4hXPnziE/P9+Qun0+H4qKitC6Rft9acCAASn3t5UrV2LVqlWi20WjUSxbtgxXXXWVrs+ON6peQj5pJ3pDhgzByZMnkZubC4/Ho7qe9vZ2lJSU4OTJk8jLy9MxQndA50caOkfi0PkRh86POHqdn1gshnPnzmHIkCE6RpdMdnY2jh07hlBI+7DZWCyWcm+Tk82rqKjAkSNH8O6772qOwYx6CfmknehlZGSguLhYt/ry8vLoS1YEOj/S0DkSh86POHR+xNHj/BiVyWOTnZ2N7Oxsw/fDR2VlJd58803s3btX1/ujUfUSykg70SMIgiAIIp79W7p0Kerr69HY2IgRI0bYul5CHTS9CkEQBEGkIRUVFXj55ZdRV1eH3NxctLa2orW1FV1dXYkyra2taG5uxmeffQYAOHz4MJqbm/H1118nylx33XV4/vnnFdVLmAeJnkr8fj9Wrlwpq+9DOkLnRxo6R+LQ+RGHzo84dH6k2bhxI9ra2jBjxgwMHjw48XrllVcSZTZt2oTLL78cS5YsAQBMnz4dl19+Od54441EmaNHj+Ls2bOK6iXMwxMzZcw4QRAEQRAEYTaU0SMIgiAIgnApJHoEQRAEQRAuhUSPIAiCIAjCpZDoEQRBEARBuBQSPZVs2LABw4cPR3Z2NqZOnYr333/f6pBsw969ezF37lwMGTIEHo8H27dvtzok21BTU4MpU6YgNzcXBQUFmDdvHlpaWqwOyzZs3LgR48ePT0xyW1pairffftvqsGzLmjVr4PF4Eg+eJ4BVq1bB4/EkvUaNGmV1WARhGSR6KnjllVdQVVWFlStX4tChQ5gwYQJmzZqFM2fOWB2aLejo6MCECROwYcMGq0OxHXv27EFFRQUOHDiAQCCAcDiM8vJydHR0WB2aLSguLsaaNWvQ1NSEgwcP4vvf/z5uuukmfPTRR1aHZjs++OADbN68GePHj7c6FNsxduxYnDp1KvGix28R6QxNr6KCqVOnYsqUKYkJIqPRKEpKSrB06VI89NBDFkdnLzweD+rr6zFv3jyrQ7ElX331FQoKCrBnzx5Mnz7d6nBsycCBA/H000/j7rvvtjoU23D+/HlMmjQJv/rVr/DEE09g4sSJWLdundVh2YJVq1Zh+/btaG5utjoUgrAFlNFTSCgUQlNTE2bOnJlYlpGRgZkzZ2L//v0WRkY4kba2NgBxmSGSiUQi2LZtGzo6OlBaWmp1OLaioqICc+bMSfoeIvr429/+hiFDhuAf/uEfcMcdd+DEiRNWh0QQlkHPulXI2bNnEYlEUFhYmLS8sLAQn3zyiUVREU4kGo1i2bJluOqqqzBu3Dirw7ENhw8fRmlpKbq7uzFgwADU19djzJgxVodlG7Zt24ZDhw7hgw8+sDoUWzJ16lRs3boVI0eOxKlTp7B69Wp873vfw5EjR5Cbm2t1eARhOiR6BGERFRUVOHLkCPUf4jBy5Eg0Nzejra0Nr732GhYtWoQ9e/aQ7AE4efIkHnjgAQQCAWRnZ1sdji2ZPXt24vfx48dj6tSpGDZsGP793/+dmv+JtIRETyGDBg1CZmYmTp8+nbT89OnTKCoqsigqwmlUVlbizTffxN69e1FcXGx1OLbC5/PhkksuAQBMnjwZH3zwAdavX4/NmzdbHJn1NDU14cyZM5g0aVJiWSQSwd69e/H8888jGAwiMzPTwgjtxwUXXIDvfOc7+Oyzz6wOhSAsgfroKcTn82Hy5MnYtWtXYlk0GsWuXbuoHxEhSSwWQ2VlJerr67F7926MGDHC6pBsTzQaRTAYtDoMW3Ddddfh8OHDaG5uTryuuOIK3HHHHWhubibJ4+H8+fM4evQoBg8ebHUoBGEJlNFTQVVVFRYtWoQrrrgCV155JdatW4eOjg7cddddVodmC86fP5/03/OxY8fQ3NyMgQMHYujQoRZGZj0VFRWoq6vD66+/jtzcXLS2tgIA8vPzkZOTY3F01lNdXY3Zs2dj6NChOHfuHOrq6tDY2IgdO3ZYHZotyM3NTenP2b9/f1x44YXUz7OXn/70p5g7dy6GDRuGL7/8EitXrkRmZiZuu+02q0MjCEsg0VPBrbfeiq+++gqPPvooWltbMXHiRDQ0NKQM0EhXDh48iGuvvTbxvqqqCgCwaNEibN261aKo7MHGjRsBADNmzEhavmXLFtx5553mB2Qzzpw5g4ULF+LUqVPIz8/H+PHjsWPHDpSVlVkdGuEQ/ud//ge33XYb/t//+3+46KKLcPXVV+PAgQO46KKLrA6NICyB5tEjCIIgCIJwKdRHjyAIgiAIwqWQ6BEEQRAEQbgUEj2CIAiCIAiXQqJHEARBEAThUkj0CIIgCIIgXAqJHkEQBEEQhEsh0SMIgiAIgnApJHoEQRAEQRAuhUSPIAiCIAjCpZDoEQTBy6lTp3D77bfjO9/5DjIyMrBs2TKrQyIIgiAUQqJHEAQvwWAQF110ER555BFMmDDB6nAIgiAIFZDoEYSL+eqrr1BUVIRf/OIXiWX79u2Dz+fDrl27RLcdPnw41q9fj4ULFyI/P9/oUAmCIAgDyLI6AIIgjOOiiy5CbW0t5s2bh/LycowcORILFixAZWUlrrvuOqvDIwiCIAyGRI8gXM4NN9yAJUuW4I477sAVV1yB/v37o6amxuqwCIIgCBOgpluCSAOeeeYZ9PT04NVXX8Xvfvc7+P1+q0MiCIIgTIBEjyDSgKNHj+LLL79ENBrF8ePHrQ6HIAiCMAlquiUIlxMKhfDDH/4Qt956K0aOHIl77rkHhw8fRkFBgdWhEQRBEAZDokcQLudnP/sZ2tra8H/+z//BgAED8Mc//hGLFy/Gm2++Kbltc3MzAOD8+fP46quv0NzcDJ/PhzFjxhgcNUEQBKEHnlgsFrM6CIIgjKGxsRFlZWV45513cPXVVwMAjh8/jgkTJmDNmjX48Y9/LLq9x+NJWTZs2DBq/iUIgnAIJHoEQRAEQRAuhQZjEARBEARBuBQSPYJIU8aOHYsBAwbwvn73u99ZHR5BEAShA9R0SxBpyueff45wOMy7rrCwELm5uSZHRBAEQegNiR5BEARBEIRLoaZbgiAIgiAIl0KiRxAEQRAE4VJI9AiCIAiCIFwKiR5BEARBEIRLIdEjCIIgCIJwKSR6BEEQBEEQLoVEjyAIgiAIwqX8f02B1eo9zLVuAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':4.})\n", + "fig = tp.utils.plot(model, lambda u : u,\n", + " plot_sampler, plot_type='contour_surface',\n", + " vmin=vmin, vmax=vmax)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "10a7c785-90da-4b62-964f-af7d816ed1bd", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 449 + }, + "id": "10a7c785-90da-4b62-964f-af7d816ed1bd", + "outputId": "249db53f-3a95-4bca-aeff-019f9a9d4dce" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':8.})\n", + "fig = tp.utils.plot(model, lambda u : u,\n", + " plot_sampler, plot_type='contour_surface',\n", + " vmin=vmin, vmax=vmax)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "c3e6a8cf-6bd5-42d6-a3ac-16c4a64eb22b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 449 + }, + "id": "c3e6a8cf-6bd5-42d6-a3ac-16c4a64eb22b", + "outputId": "ca72f816-5fba-4edb-8f3c-62a8b624cae6" + }, + "outputs": [ + { + "data": { + "image/png": "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du1y/V5dXY1Dhw7BarUyPBP+qKmpwZ133onXXntN8Pfz58/j/PnzWL58OY4dO4aNGzdi586dePTRR33aZbE6W3ST8sknn2DChAno0aMHLl26hBdffBFbtmzBXXfdBQCoq6vDpk2bkJubK7pwQiFaCSQtOkieUeofT0PAcu3rPRys1nxAQLsooJ75vUGCiS+UCn2J89ezsrIwduxYJCQk4NKlS8jLy0NhYSEKCgpgMpkwf/58LFq0CP369UP//v2xadMmnDx5Elu3bnXZGDlyJCZPnozMzEwA11coL126FAkJCejduzeOHj2Kt956yxUsMplMmDNnDl555RX06NED3bp1w8KFC9G5c2dMmjRJwcnL4+GHH8aePXuwZ88erFq1CsD1RS5du3ZlXtZDDz0EADhz5ozg73369MEHH3zg+t69e3csXboUDz74IK5du4awsOY3B6vV2aJvu8WLF+N3v/sdli5dCofDgTfeeAN33303tmzZgjFjxog1Q0hFa6EU7MIP0Ef8sSiXdRl6C0CptgNNBKpRpyT+iCZUVlYiPT0d5eXliI6ORkpKCgoKCpCamgoAmDNnDq5cuYK5c+fixx9/RL9+/WCz2dC9e3eXjdOnT7uNFP7xj3/EwoUL8d///d+orKxE586dMWvWLLz44ouuNM8++yxqamrw+OOP4+eff8add96JnTt3okWLFtqd/C+sWrUKX331Ffr06YOXX34ZANChQwfBtE888QTeeecdn/YuX77M1L+qqipYLBZBIQj4X53NXAweP34cf/vb3wBcV/bPPvss4uLi8F//9V/YvHkzBg0aJNYUIYTRJ4D7KoP3maks6l5JXYZB3D6DSuxLgQcRKNUuT6uDeYsCkgAkvLB+/Xq/aRYsWOC2z6AnnlGu1q1bY+XKlVi5cqXXPCaTCS+//LJLfOlJdHQ0wsPDERUV5XfO4ssvv4zf/e53Gnl2/b3KS5YsweOPP+41DavV2aKbnYiICPz8889uxx544AGEhITgvvvuw5tvvim6UCfZ2dn4xz/+gZMnTyIyMhJDhgzBa6+95ndTyy1btmDhwoU4c+YMevTogddeew3jxo2TXL7m6BUV0yMCosa7UlnB6jooFYBq1xGJQHd4FoEs65PEH0GoQseOHdGxY0dNyqqursb48eORnJzsdVsgloiO2fTv3x+fffZZs+P3338//vKX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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_sampler = tp.samplers.PlotSampler(plot_domain=Omega, n_points=600, data_for_other_variables={'t':12.})\n", + "fig = tp.utils.plot(model, lambda u : u, plot_sampler, plot_type='contour_surface',\n", + " vmin=vmin, vmax=vmax)" + ] + }, + { + "cell_type": "markdown", + "id": "c0cbc22a", + "metadata": { + "id": "c0cbc22a" + }, + "source": [ + "## Manual Visualization" + ] + }, + { + "cell_type": "markdown", + "id": "9d58e206-c27f-4ee6-8f4d-ddb1415c7221", + "metadata": { + "id": "9d58e206-c27f-4ee6-8f4d-ddb1415c7221" + }, + "source": [ + "It is also possible to evaluate the model manually at torch Tensors. Say, we want to evaluate it on a spatial grid at some fixed time $t'= 6$." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "9ccbb9b3-6f6a-4a29-8dc7-c2360b2df7c9", + "metadata": { + "id": "9ccbb9b3-6f6a-4a29-8dc7-c2360b2df7c9" + }, + "outputs": [], + "source": [ + "x_coords = torch.linspace(0, 5, 100)\n", + "y_coords = torch.linspace(0, 4, 80)\n", + "t_coords = torch.linspace(6, 6 , 1)\n", + "\n", + "xs, ys, ts = torch.meshgrid([x_coords, y_coords, t_coords])\n", + "tensors = torch.stack([xs.flatten(), ys.flatten(), ts.flatten()], dim=1)" + ] + }, + { + "cell_type": "markdown", + "id": "26d9c9ba-77fe-4c21-af35-12e1376b113e", + "metadata": { + "id": "26d9c9ba-77fe-4c21-af35-12e1376b113e" + }, + "source": [ + "The TorchPhysics model cannot be directly evaluated at Pytorch Tensors. Tensors must first be transformed into TorchPhysics Points, which is easy to achieve. We only need to which space the \"tensors\" above belong to. In our case, it belongs to the space $X*T$. ATTENTION: Since the spatial coordinates have been fed into \"tensors\" first, it is important to define the space as $X*T$ and NOT $T*X$!\n", + "For more information on the Point class please have a look at the [space- and point-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_spaces_and_points.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "67c99cdd-70db-4465-9ec0-8278b7381fa6", + "metadata": { + "id": "67c99cdd-70db-4465-9ec0-8278b7381fa6" + }, + "outputs": [], + "source": [ + "points = tp.spaces.Points(tensors, space=X*T)" + ] + }, + { + "cell_type": "markdown", + "id": "ce94a359-75dd-41e7-85b3-2000b2065054", + "metadata": { + "id": "ce94a359-75dd-41e7-85b3-2000b2065054" + }, + "source": [ + "Now the model can be evaluated at those points by its forward() method. In order to use e.g. \"plt.imshow()\", we need to transform the output into a numpy array." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "854b969a-96f2-4088-b045-d1ca5cf0db64", + "metadata": { + "id": "854b969a-96f2-4088-b045-d1ca5cf0db64" + }, + "outputs": [], + "source": [ + "output = model.forward(tp.spaces.Points(tensors, space=X*T))\n", + "output = output.as_tensor.reshape(100, 80, 1).detach().numpy()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "70d30023-ca42-460a-9906-2bcc736016ce", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 433 + }, + "id": "70d30023-ca42-460a-9906-2bcc736016ce", + "outputId": "a7a3ce2b-18e1-4fce-e1c5-e370e258cc85" + }, + "outputs": [ + { + "data": { + "image/png": "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zp49O+78t7a2qrm5mfM/CZ2dnbrlllvGnVeJ8+2Hb37zm1qyZInuuOMOzZkzR9dee6127NiRf/7EiRNKpVLjznkymdTSpUs55x7ceOON6u/v1xtvvCFJ+t73vqfvfOc7WrVqlSTOd9REbpW9H/3oRzp37pzq6+vHba+vr9f3v//9kEZVnnK5nDZs2KDly5frqquukiSlUilVVVWptrZ23L719fVKpVIhjDL+9uzZo+9+97s6cuTIhOc43+a99dZb2r59u7q7u/X5z39eR44c0Wc+8xlVVVVp7dq1+fNa7DuGc166z33uc8pkMmptbdWUKVN07tw5Pfzww1qzZo0kcb4jJnJBH8Hp7OzUa6+9pu985zthD6VsDQ8P67777tNzzz2n6urqsIdjhVwupyVLluiRRx6RJF177bV67bXX9Pjjj2vt2rUhj678fO1rX9NTTz2l3bt368orr9SxY8e0YcMGNTY2cr4jKHK/3r/00ks1ZcqUCdXL6XRaDQ0NIY2q/HR1demZZ57RP/7jP2revHn57Q0NDRobG9PIyMi4/Tn/3gwODurdd9/Vddddp6lTp2rq1Kk6ePCgHnvsMU2dOlX19fWcb8Pmzp2rK664Yty2hQsX6uTJk5KUP698x5jxB3/wB/rc5z6nO++8U1dffbV+93d/Vxs3blRvb68kznfURC7oV1VVafHixerv789vy+Vy6u/vV1tbW4gjKw+O46irq0tPP/20XnjhBbW0tIx7fvHixZo2bdq48z80NKSTJ09y/j24+eab9eqrr+rYsWP5x5IlS7RmzZr8vznfZi1fvnzCn6G+8cYbmj9/viSppaVFDQ0N4855JpPRK6+8wjn34Mc//rEqK8eHkilTpiiXy0nifEdO2JWExezZs8dJJBLOk08+6bz++uvOPffc49TW1jqpVCrsocXepz71KSeZTDovvvii88477+QfP/7xj/P73HvvvU5zc7PzwgsvOEePHnXa2tqctra2EEddXn6+et9xON+mHT582Jk6darz8MMPO8ePH3eeeuopZ8aMGc5Xv/rV/D6bNm1yamtrnW984xvOv/zLvzi33XYbf0Lm0dq1a51f+IVfyP/J3te//nXn0ksvde6///78Ppzv6Ihk0Hccx/nLv/xLp7m52amqqnJuuOEG59ChQ2EPqSxIKvro6+vL7/OTn/zE+fSnP+1ccsklzowZM5zf/u3fdt55553wBl1mCoM+59u8b33rW85VV13lJBIJp7W11XniiSfGPZ/L5ZwHHnjAqa+vdxKJhHPzzTc7Q0NDIY023jKZjHPfffc5zc3NTnV1tfP+97/f+cM//EMnm83m9+F8R0eF4/zctEkAAKBsRS6nDwAA/EHQBwDAEgR9AAAsQdAHAMASBH0AACxB0AcAwBIEfQAALEHQBwDAEgR9AAAsQdAHAMASBH0AACzx/wDJNtwJIg1tKAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(np.rot90(output[:, :]), 'gray', vmin=vmin, vmax=vmax)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "9840aad9", + "metadata": { + "id": "9840aad9" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.19" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "2a351ae394444086b648b09ffa58c06d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "30a318be130447cea4842d46d887360f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_436ad1ff35d841408e32383cd6ce6cb7", + "placeholder": "​", + "style": "IPY_MODEL_9e32d83238be4720909a1606779ce4a1", + "value": "Validation DataLoader 0: 100%" + } + }, + "367e26e3fcff4add996e6b9c39cce77c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_600a325e05fd4ead94806e30de2dfe91", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_d8765650eeba4c84be216cf3ffd27b0d", + "value": 1 + } + }, + "436ad1ff35d841408e32383cd6ce6cb7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4a071277015d42e2876abd2fcec77f60": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b026dd8e870a4557bc5583b5a509f59e", + "placeholder": "​", + "style": "IPY_MODEL_5a685209ec8e4496831015718305bacb", + "value": " 1/1 [00:00<00:00, 318.04it/s]" + } + }, + "51d400ec3dab4d3db89d4ae1212e0cce": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "5a685209ec8e4496831015718305bacb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "600a325e05fd4ead94806e30de2dfe91": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8c926f8700a64539818ed8fe7b54bdea": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_30a318be130447cea4842d46d887360f", + "IPY_MODEL_367e26e3fcff4add996e6b9c39cce77c", + "IPY_MODEL_4a071277015d42e2876abd2fcec77f60" + ], + "layout": "IPY_MODEL_d7106f73c09d4ffc94f9146307913a93" + } + }, + "9e32d83238be4720909a1606779ce4a1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "acd31f72a0184f859d18ad08567a61aa": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "b026dd8e870a4557bc5583b5a509f59e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b0f8c43fb244435fa250e90aa431a910": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_2a351ae394444086b648b09ffa58c06d", + "max": 5000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_f8c630032aea45f6b3f97e838d7b74de", + "value": 5000 + } + }, + "b51fd3c0f34740719b885c8931acb307": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cafdac53e72d48bba71a7c98538c5354", + "placeholder": "​", + "style": "IPY_MODEL_51d400ec3dab4d3db89d4ae1212e0cce", + "value": "Epoch 0: 100%" + } + }, + "b78a8f1dc2f34425b15c9f4b785e59c7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d66ce69c61594d8c972a49763e91d599", + "placeholder": "​", + "style": "IPY_MODEL_acd31f72a0184f859d18ad08567a61aa", + "value": " 5000/5000 [08:16<00:00, 10.06it/s, train/loss=0.837]" + } + }, + "c0c85ee450fa4382b8ba6d076bbe15ab": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b51fd3c0f34740719b885c8931acb307", + "IPY_MODEL_b0f8c43fb244435fa250e90aa431a910", + "IPY_MODEL_b78a8f1dc2f34425b15c9f4b785e59c7" + ], + "layout": "IPY_MODEL_c45982b9a83b47779e614305dbbfc2ac" + } + }, + "c45982b9a83b47779e614305dbbfc2ac": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + }, + "cafdac53e72d48bba71a7c98538c5354": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d66ce69c61594d8c972a49763e91d599": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d7106f73c09d4ffc94f9146307913a93": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "d8765650eeba4c84be216cf3ffd27b0d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "f8c630032aea45f6b3f97e838d7b74de": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + } + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 } From 758c06ea2c7cec4139aec9efb00537aae4121ca4 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Tue, 20 Aug 2024 17:18:50 +0200 Subject: [PATCH 06/25] Simple ODE as a first-step tutorial Signed-off-by: JGoedeke --- examples/tutorial/Tutorial_Simple_ODE.ipynb | 1434 +++++++++++++++++++ 1 file changed, 1434 insertions(+) create mode 100644 examples/tutorial/Tutorial_Simple_ODE.ipynb diff --git a/examples/tutorial/Tutorial_Simple_ODE.ipynb b/examples/tutorial/Tutorial_Simple_ODE.ipynb new file mode 100644 index 00000000..633d7396 --- /dev/null +++ b/examples/tutorial/Tutorial_Simple_ODE.ipynb @@ -0,0 +1,1434 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "xSO5tTpnr652", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "collapsed": true, + "id": "xSO5tTpnr652", + "outputId": "1ef26626-7f7f-49df-d88f-fe8c9b8d893c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting git+https://github.com/TomF98/torchphysics\n", + " Cloning https://github.com/TomF98/torchphysics to /tmp/pip-req-build-z9tkrdb2\n", + " Running command git clone --filter=blob:none --quiet https://github.com/TomF98/torchphysics /tmp/pip-req-build-z9tkrdb2\n", + " Resolved https://github.com/TomF98/torchphysics to commit b5e89acb2132bb5543a7fa1d7212aa4bf9327b73\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1+cu121)\n", + "Requirement already satisfied: pytorch-lightning>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.4.0)\n", + "Requirement already satisfied: numpy>=1.20.2 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.26.4)\n", + "Requirement already satisfied: matplotlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (3.7.1)\n", + "Requirement already satisfied: scipy>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.1)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.2.1)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.53.1)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.5)\n", + "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.1)\n", + "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (9.4.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.2)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.8.2)\n", + "Requirement already satisfied: tqdm>=4.57.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.66.5)\n", + "Requirement already satisfied: PyYAML>=5.4 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.2)\n", + "Requirement already satisfied: fsspec>=2022.5.0 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2024.6.1)\n", + "Requirement already satisfied: torchmetrics>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", + "Requirement already satisfied: typing-extensions>=4.4.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.12.2)\n", + "Requirement already satisfied: lightning-utilities>=0.10.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.11.6)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.15.4)\n", + "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.2)\n", + "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.3)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.4)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (8.9.2.26)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.3.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.0.2.54)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (10.3.2.106)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.4.5.107)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.0.106)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.20.5 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.20.5)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", + "Requirement already satisfied: triton==2.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12 in /usr/local/lib/python3.10/dist-packages (from nvidia-cusolver-cu12==11.4.5.107->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.6.20)\n", + "Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.10.3)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from lightning-utilities>=0.10.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (71.0.4)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.16.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.1.5)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.0)\n", + "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.5)\n", + "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.1)\n", + "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.2.0)\n", + "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", + "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.5)\n", + "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.9.4)\n", + "Requirement already satisfied: async-timeout<5.0,>=4.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.0.3)\n", + "Requirement already satisfied: idna>=2.0 in /usr/local/lib/python3.10/dist-packages (from yarl<2.0,>=1.0->aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.7)\n" + ] + } + ], + "source": [ + "!pip install git+https://github.com/TomF98/torchphysics" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350", + "metadata": { + "id": "1ef6d147-2dd4-4547-9fb6-79b3758d7350" + }, + "outputs": [], + "source": [ + "import torchphysics as tp\n", + "import numpy as np\n", + "import torch\n", + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "bTGz6NV4_Jzb", + "metadata": { + "id": "bTGz6NV4_Jzb" + }, + "source": [ + "# Physics Informed Neural Networks (PINNs) in TorchPhysics\n", + "In this tutorial we present a first basic example of solving a simple ODE with an initial condition in TorchPhysics using a PINN approach.\n", + "You will also learn about the different components of this library and main steps for finding a neural network that approximates the solution of a PDE.\n", + "\n", + "We consider the simple ODE:\n", + "$$\n", + "\\begin{cases}\n", + "\\frac{\\partial}{\\partial t} u(t) &= u(t) &&\\text{ on the interval } (0, 2), \\\\\n", + "u(t) &= u_0 &&\\text{ for } t\\in \\{ 0\\}.\n", + "\\end{cases}\n", + "$$\n", + "In the following we want to learn the solution function $u(t) = u_0 e^t$, where we set the initial value to $u_0=2$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d6b5fdd2-67c1-4f7e-a185-9d515fb9f3f8", + "metadata": { + "id": "d6b5fdd2-67c1-4f7e-a185-9d515fb9f3f8" + }, + "outputs": [], + "source": [ + "u_0 = 2 # initial value" + ] + }, + { + "cell_type": "markdown", + "id": "8da6279e-83c2-41ed-a56b-453b21f05d11", + "metadata": { + "id": "8da6279e-83c2-41ed-a56b-453b21f05d11" + }, + "source": [ + "## Recall PINNs\n", + "The goal is to find a neural network $u_\\theta:[0, 2]\\to \\mathbb{R}$, which approximately satisfies the two conditions of the ODE problem above, where $\\theta$ are the trainable parameters of the neural network.\n", + "Let us shortly recall the main idea behind PINNs.\n", + "\n", + "The residuals are denoted by\n", + "$$\n", + "\\begin{align}\n", + "&\\text{1) Residual of the ODE condition: } &&R_1(u, t) := \\frac{\\partial^2}{\\partial t^2} u(t) - u(t) &&&\\text{ for } t\\in (0,2),\\\\\n", + "&\\text{2) Residual of the initial condition: } &&R_2(u, t) := u(t) - u_0 &&& \\text{ for } t\\in \\{0\\}.\n", + "\\end{align}\n", + "$$\n", + "Continuing with the PINN approach, points are sampled in the domains corresponding to each condition. In our example:\n", + "$$\n", + "\\begin{align}\n", + "&\\text{1) } &&\\big(t_i^{(1)} \\big)_i &&&\\in (0, 2),\\\\\n", + "&\\text{2) } &&\\big(t_j^{(2)} \\big)_j &&&\\in \\{0\\}.\n", + "\\end{align}\n", + "$$\n", + "Then, the network $u_\\theta$ is trained by solving the following minimization problem\n", + "$$\n", + "\\begin{align}\n", + "\\min_\\theta \\sum_{i} \\big\\vert R_1(u_\\theta, t_i^{(1)}) \\big \\vert^2 + \\sum_j \\big\\vert R_2(u_\\theta, t_j^{(2)}) \\big \\vert^2,\n", + "\\end{align}\n", + "$$\n", + "that is, the residuals are minimized with respect to the $l_2$-norm." + ] + }, + { + "cell_type": "markdown", + "id": "8f0db4a0-cace-4d21-845f-f34680880d7d", + "metadata": { + "id": "8f0db4a0-cace-4d21-845f-f34680880d7d" + }, + "source": [ + "## Translating the PDE Problem into the Language of TorchPhysics\n", + "Translating the PDE problem into the framework of TorchPhysics works in a convenient and intuitive way, as the notation is close to the mathematical formulation. The general procedure can be devided into five steps. Also when solving other problems with TorchPhysics, such as parameter identification or variational problems, the same steps can be applied, see also the further [tutorials](https://boschresearch.github.io/torchphysics/tutorial/tutorial_start.html) or [examples](https://boschresearch.github.io/torchphysics/examples.html)." + ] + }, + { + "cell_type": "markdown", + "id": "e8fe0433-82b7-4093-8f6f-8adf7e46ff5b", + "metadata": { + "id": "e8fe0433-82b7-4093-8f6f-8adf7e46ff5b" + }, + "source": [ + "### Step 1: Specify spaces and domains\n", + "The domain $I=(0, 2)$ is a subset of the space $\\mathbb{R}$, whereas the range space of the solution function $u$ is $\\mathbb{R}$. First, we need to let TorchPhysics know which spaces and domains we are dealing with and how variables/elements within these spaces are denoted by.\n", + "This is realized by generating objects of TorchPhysics' Space and Domain classes in \"tp.spaces\" and \"tp.domains\", respectively.\n", + "Some simple domains are already predefined, which will be sufficient for this tutorial. We need an interval domain, which is an open interval by default. For creating complexer domains please have a look at the [domain-tutorial](https://boschresearch.github.io/torchphysics/tutorial/tutorial_domain_basics.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6af0dba0-d481-4566-a8b7-244098eee713", + "metadata": { + "id": "6af0dba0-d481-4566-a8b7-244098eee713" + }, + "outputs": [], + "source": [ + "# Input and output spaces\n", + "T = tp.spaces.R1('t')\n", + "U = tp.spaces.R1('u')\n", + "\n", + "# Domain\n", + "I = tp.domains.Interval(space=T, lower_bound=0, upper_bound=2)" + ] + }, + { + "cell_type": "markdown", + "id": "a1676bc3-8dab-4ce4-84ff-f8fc29e8b829", + "metadata": { + "id": "a1676bc3-8dab-4ce4-84ff-f8fc29e8b829" + }, + "source": [ + "### Step 2: Define point samplers for different subsets of [0, 2]\n", + "As mentioned in the PINN recall, it will be necessary to sample points in different subsets of the full domain $I$. TorchPhysics provides this functionality by sampler classes in \"tp.samplers\". For simplicity, we consider only Random Uniform Samplers for the subdomains. However, there are many more possibilities to sample points in TorchPhysics, see also [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html).\n", + "\n", + "The most important inputs of a sampler constructor are the \"domain\" from which points will be sampled, as well as the \"number of points\" drawn every time the sampler is called. It is reasonable to create different sampler objects for the different conditions of the pde problem, simply because the subdomains differ.\n", + "\n", + "The ODE condition 1) should hold for points in the domain $I=(0, 2)$. We have defined this domain already in Step 1, so that we can define a point sampler:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d020f7f4-c286-466f-928d-1f80ee64c53f", + "metadata": { + "id": "d020f7f4-c286-466f-928d-1f80ee64c53f" + }, + "outputs": [], + "source": [ + "sampler_ode_condition = tp.samplers.RandomUniformSampler(domain=I, n_points=150)" + ] + }, + { + "cell_type": "markdown", + "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc", + "metadata": { + "id": "c9f72b70-0e87-466f-a7c0-0e1f194745cc" + }, + "source": [ + "Next, let us define samplers for the initial condition. This condition should hold on the domain $\\{0\\}$, which is the left boundary of the interval $I=(0,2)$. All tp.domains.Interval objects have the attribute \"left_boundary\", an instance of TorchPhysics BoundaryDomain class, a subclass of the Domain class. This allows to construct a sampler for this initial condition." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e780f5fa-5ebf-4731-8568-77116ea039f6", + "metadata": { + "id": "e780f5fa-5ebf-4731-8568-77116ea039f6" + }, + "outputs": [], + "source": [ + "domain_initial_condition = I.boundary_left\n", + "sampler_initial_condition = tp.samplers.RandomUniformSampler(domain_initial_condition, 50)" + ] + }, + { + "cell_type": "markdown", + "id": "7750bf6b-30ec-4ca9-8f37-9699439d0d22", + "metadata": { + "id": "7750bf6b-30ec-4ca9-8f37-9699439d0d22" + }, + "source": [ + "For more detailed information on the functionality of TorchPysics samplers, please have a look at the further [tutorials](https://torchphysics.de/tutorial), [examples](https://boschresearch.github.io/torchphysics/examples.html) or the in-depth [sampler-tutorial](https://boschresearch.github.io/torchphysics/tutorial/sampler_tutorial.html)." + ] + }, + { + "cell_type": "markdown", + "id": "6b1b87f9-b6d6-44ec-8fb5-833ab466d89b", + "metadata": { + "id": "6b1b87f9-b6d6-44ec-8fb5-833ab466d89b" + }, + "source": [ + "### Step 3: Define residual functions\n", + "As mentioned in the PINNs Recall, we are looking for a neural network $u_\\theta$ for which all of the residual functions $R_1$ and $R_2$ vanish.\n", + "\n", + "Let us have a look at $R_1$, the residual for the ODE condition, the way it is defined in the PINNs recall above. The inputs of $R_1$ are the coordinate $t\\in(0,2)$, but also $u_\\theta$, which is itself a function of $t$. In TorchPhysics, the evaluation of the network $u_\\theta$ at $t$ is done before evaluating the residual functions. This means that from now on we consider $R_1$ as well as the other residuals to be functions, whose inputs are tuples $(u, t)$, where $u:=u_\\theta(t)$.\n", + "\n", + "More precisely, $u$ will be a torch.tensor of shape (n_points, 1) and $t$ of shape (n_points, 1), where n_points is the number of triples $(u,t)$ for which the residual should be computed.\n", + "\n", + "For the residual $R_1$ it is required to compute the first derivative (gradient) of $u$ with respect to $t$. This differential operator, among others - see the [utils-tutorial](https://boschresearch.github.io/torchphysics/tutorial/differentialoperators.html), are pre-implemented and can be found in \"tp.utils\". The intern computation is build upon torch's autograd functionality." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c29f3f92-d613-470f-ab74-9369e071ea04", + "metadata": { + "id": "c29f3f92-d613-470f-ab74-9369e071ea04" + }, + "outputs": [], + "source": [ + "def residual_ode_condition(u, t):\n", + " return u - tp.utils.grad(u, t)" + ] + }, + { + "cell_type": "markdown", + "id": "e444a2e5-6fc6-4124-894c-1ba987153241", + "metadata": { + "id": "e444a2e5-6fc6-4124-894c-1ba987153241" + }, + "source": [ + "For the computation of the residual $R_2$ of the initial condition, the coordinate $t\\in \\{0\\}$ is not required, since $u$ is already the evaluation of the network at these sampling points. Therefore, we can conveniently omit them as input parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "65954de9-4c80-4d2a-be6e-0cd16ab82596", + "metadata": { + "id": "65954de9-4c80-4d2a-be6e-0cd16ab82596" + }, + "outputs": [], + "source": [ + "def residual_initial_condition(u):\n", + " return u - u_0" + ] + }, + { + "cell_type": "markdown", + "id": "0cc89ada-310b-4a84-bcc0-77baa7afca2c", + "metadata": { + "id": "0cc89ada-310b-4a84-bcc0-77baa7afca2c" + }, + "source": [ + "### Step 4: Define Neural Network\n", + "At this point, let us define the model $u_\\theta:[0,2]\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", + "There are also a bunch of predefined neural networks or single layers available. In this tutorial we consider a very simple neural network, a FNO consisting of four hidden layers with $80, 50, 50$ and $50$ neurons, respectively.:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bdef3d80-90e6-47aa-95ce-6d735fd03f36", + "metadata": { + "id": "bdef3d80-90e6-47aa-95ce-6d735fd03f36" + }, + "outputs": [], + "source": [ + "model = tp.models.FCN(input_space=T, output_space=U, hidden = (50,50,50))" + ] + }, + { + "cell_type": "markdown", + "id": "17e3f8ab-bd6c-4f4f-94a6-030930458c0c", + "metadata": { + "id": "17e3f8ab-bd6c-4f4f-94a6-030930458c0c" + }, + "source": [ + "### Step 5: Create TorchPhysics Conditions\n", + "Let us sum up what we have done so far: For the ODE and initial condition, we constructed samplers and residuals on the corresponding domains.\n", + "Moreover, we have defined a neural network which will later be trained to fulfull each of these conditions.\n", + "\n", + "As a final step, we collect these constructions for each condition in an object of the TorchPhysics Condition class, contained in \"tp.conditions\".\n", + "Since we are interested in applying a PINN approach, we create objects of the subclass PINNCondition, which automatically contains the information that the residuals should be minimized in the squared $l_2$-norm, see again the PINN Recall. For other TorchPhysics Conditions one may need to specify which norm should be taken of the residuals, see [condition-tutorial](https://boschresearch.github.io/torchphysics/tutorial/condition_tutorial.html) for further information." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "008c09a7-81f8-41b5-8c10-3892812740ad", + "metadata": { + "id": "008c09a7-81f8-41b5-8c10-3892812740ad" + }, + "outputs": [], + "source": [ + "ode_condition = tp.conditions.PINNCondition(module =model,\n", + " sampler =sampler_ode_condition,\n", + " residual_fn=residual_ode_condition)\n", + "\n", + "initial_condition = tp.conditions.PINNCondition(module =model,\n", + " sampler =sampler_initial_condition,\n", + " residual_fn=residual_initial_condition)" + ] + }, + { + "cell_type": "markdown", + "id": "5cd77316-3c78-4bf1-b639-9ccb7070af2d", + "metadata": { + "id": "5cd77316-3c78-4bf1-b639-9ccb7070af2d" + }, + "source": [ + "It is to be noted that TorchPhysics' Condition class is a subclass of the torch.nn.Module class and its forward() method returns the current loss of the respective condition.\n", + "For example, calling forward() of the ode_condition at points $(t_i)_i$ in $I=(0,2)$ will return\n", + "$$\n", + "\\begin{align}\n", + "\\sum_i \\big \\vert R_1(u_\\theta, t_i) \\big \\vert^2,\n", + "\\end{align}\n", + "$$\n", + "where $R_1$ is the residual function for the ODE condition defined in the PINN recall and $u_\\theta$ is the model defined in Step 4." + ] + }, + { + "cell_type": "markdown", + "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac", + "metadata": { + "id": "2e0fad4c-2cfd-4c10-8e2f-0a3702a2eeac" + }, + "source": [ + "The reason that also the model is required for initializing a Condition object is, that it could be desireable in some [cases](https://github.com/boschresearch/torchphysics/blob/main/examples/pinn/interface-jump.ipynb) to train different networks for different conditions of the PDE problem." + ] + }, + { + "cell_type": "markdown", + "id": "31d80c43-5879-401c-8212-0e4a5fd6514c", + "metadata": { + "id": "31d80c43-5879-401c-8212-0e4a5fd6514c" + }, + "source": [ + "## Training based on Pytorch Lightning\n", + "In order to train a model, TorchPhysics makes use of the Pytorch Lightning library, which hence must be imported. Further, we import \"os\" so that GPUs can be used for the calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "bb76e892-bf53-4a01-adc5-74dddb770525", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bb76e892-bf53-4a01-adc5-74dddb770525", + "outputId": "ca750735-3e1e-4fcc-ca59-e020d6131434" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GPU available: False\n" + ] + } + ], + "source": [ + "import pytorch_lightning as pl\n", + "import os\n", + "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\" if torch.cuda.is_available() else \"0\"\n", + "\n", + "print (\"GPU available: \" + str(torch.cuda.is_available()))" + ] + }, + { + "cell_type": "markdown", + "id": "1639cf38-835b-4571-b0c5-7ef0d130c2df", + "metadata": { + "id": "1639cf38-835b-4571-b0c5-7ef0d130c2df" + }, + "source": [ + "For the training process, i.e. the minimization of the loss function introduced in the PINN recall, TorchPhysics provides the Solver class. It inherits from the pl.LightningModule class and is compatible with the TorchPhysics library. The constructor requires a list of TorchPhysics Conditions, whose parameters should be optimized during the training." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ea27b608-e319-4fac-85c1-5984f2d043c6", + "metadata": { + "id": "ea27b608-e319-4fac-85c1-5984f2d043c6" + }, + "outputs": [], + "source": [ + "training_conditions = [ode_condition, initial_condition]" + ] + }, + { + "cell_type": "markdown", + "id": "e024913e-e10e-4387-b390-165e77c8524b", + "metadata": { + "id": "e024913e-e10e-4387-b390-165e77c8524b" + }, + "source": [ + "By default, the Solver uses the Adam Optimizer from Pytorch with learning rate $lr=0.001$ for optimizing the training_conditions. If a different optimizer or choice of its arguments shall be used, one can collect these information in an object of TorchPhysics' OptimizerSetting class." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b1848d26-ea33-400c-84be-2291429e8065", + "metadata": { + "id": "b1848d26-ea33-400c-84be-2291429e8065" + }, + "outputs": [], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.001)" + ] + }, + { + "cell_type": "markdown", + "id": "efcd0c8c-1ef2-45a0-bf00-de88201f3d03", + "metadata": { + "id": "efcd0c8c-1ef2-45a0-bf00-de88201f3d03" + }, + "source": [ + "Finally, we are able to create the Solver object, a Pytorch Lightning Module." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "4ea2cb3f-087c-4e03-aeb0-40318f556062", + "metadata": { + "id": "4ea2cb3f-087c-4e03-aeb0-40318f556062" + }, + "outputs": [], + "source": [ + "solver = tp.solver.Solver(train_conditions=training_conditions, optimizer_setting=optim)" + ] + }, + { + "cell_type": "markdown", + "id": "53dec402-5dd2-40f9-a405-5170d0cfcbd7", + "metadata": { + "id": "53dec402-5dd2-40f9-a405-5170d0cfcbd7" + }, + "source": [ + "Now, as usual, the training is done with a Pytorch Lightning Trainer object and its fit() method." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "9ea9431a-9ea4-4312-8869-af4c8c4733a4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 347, + "referenced_widgets": [ + "e9e9c3f6752640deb4d7f2791a8a329e", + "3b582cb05b834f60b6311fdbde240407", + "099fcd830e64454093b45f9ec3a9cc4a", + "d8e62d4fa51c4cedbff580f154dd3a17", + "fd6826c5f1a54e93ae8b6ab4d3d6e421", + "5f06b3f84db348c8b32ee48089e01f01", + "f3ebae78a3674c6c93fd64b3ed3e7542", + "cd0ca01f50124df0ac6c951d6d31ab85", + "58dbae958d11454e88b91efedb750d65", + "8ed5e770686f4cb78a0bc85a39ae4157", + "c733319a53f94defb4948ab02d05837c", + "fcd564594edd4d9582d3a4b3e212a3f2", + "6553e0de7efa42728366a465d83a948f", + "79b83887a2bb4248ae991970d6a274b1", + "e0ba190eae4e4d5192b5e1ce501af014", + "96e31c05ff4c4a87aef91967cf4292e1", + "1b5ba0b918de430da29cfca2f2a7afbf", + "b6390043fdd048849050dc7526dbe7df", + "b321cd7f2e454f21a4e1ea33c7ff4ce3", + "4250369b168442daa28a85e459fd788e", + "8f703e85f7aa4729a04a8f8e41a11b5a", + "4107618f389d48359082d1b9b55d0ef9" + ] + }, + "id": "9ea9431a-9ea4-4312-8869-af4c8c4733a4", + "outputId": "061449fa-76e8-48c2-dbc3-66800d039674" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pytorch_lightning.utilities.rank_zero:GPU available: False, used: False\n", + "INFO:pytorch_lightning.utilities.rank_zero:TPU available: False, using: 0 TPU cores\n", + "INFO:pytorch_lightning.utilities.rank_zero:HPU available: False, using: 0 HPUs\n", + "INFO:pytorch_lightning.callbacks.model_summary:\n", + " | Name | Type | Params | Mode \n", + "--------------------------------------------------------\n", + "0 | train_conditions | ModuleList | 5.3 K | train\n", + "1 | val_conditions | ModuleList | 0 | train\n", + "--------------------------------------------------------\n", + "5.3 K Trainable params\n", + "0 Non-trainable params\n", + "5.3 K Total params\n", + "0.021 Total estimated model params size (MB)\n", + "13 Modules in train mode\n", + "0 Modules in eval mode\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e9e9c3f6752640deb4d7f2791a8a329e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: | | 0/? [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = tp.utils.plot(model=model, plot_function=lambda u : u, point_sampler=plot_sampler, label='Learned solution')\n", + "# plot also groundtruth\n", + "sampling_points = np.linspace(0, 2, 100)\n", + "plt.plot(sampling_points, 2*np.exp(sampling_points), label=f'Groundtruth $u(t) = u_0e^t$')\n", + "fig.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "9840aad9", + "metadata": { + "id": "9840aad9" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.19" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "099fcd830e64454093b45f9ec3a9cc4a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cd0ca01f50124df0ac6c951d6d31ab85", + "max": 1000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_58dbae958d11454e88b91efedb750d65", + "value": 1000 + } + }, + "1b5ba0b918de430da29cfca2f2a7afbf": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3b582cb05b834f60b6311fdbde240407": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_5f06b3f84db348c8b32ee48089e01f01", + "placeholder": "​", + "style": "IPY_MODEL_f3ebae78a3674c6c93fd64b3ed3e7542", + "value": "Epoch 0: 100%" + } + }, + "4107618f389d48359082d1b9b55d0ef9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "4250369b168442daa28a85e459fd788e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "58dbae958d11454e88b91efedb750d65": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "5f06b3f84db348c8b32ee48089e01f01": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6553e0de7efa42728366a465d83a948f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1b5ba0b918de430da29cfca2f2a7afbf", + "placeholder": "​", + "style": "IPY_MODEL_b6390043fdd048849050dc7526dbe7df", + "value": "Validation DataLoader 0: 100%" + } + }, + "79b83887a2bb4248ae991970d6a274b1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b321cd7f2e454f21a4e1ea33c7ff4ce3", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_4250369b168442daa28a85e459fd788e", + "value": 1 + } + }, + "8ed5e770686f4cb78a0bc85a39ae4157": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8f703e85f7aa4729a04a8f8e41a11b5a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "96e31c05ff4c4a87aef91967cf4292e1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "b321cd7f2e454f21a4e1ea33c7ff4ce3": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b6390043fdd048849050dc7526dbe7df": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "c733319a53f94defb4948ab02d05837c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "cd0ca01f50124df0ac6c951d6d31ab85": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d8e62d4fa51c4cedbff580f154dd3a17": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8ed5e770686f4cb78a0bc85a39ae4157", + "placeholder": "​", + "style": "IPY_MODEL_c733319a53f94defb4948ab02d05837c", + "value": " 1000/1000 [00:37<00:00, 26.83it/s, train/loss=0.000856]" + } + }, + "e0ba190eae4e4d5192b5e1ce501af014": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8f703e85f7aa4729a04a8f8e41a11b5a", + "placeholder": "​", + "style": "IPY_MODEL_4107618f389d48359082d1b9b55d0ef9", + "value": " 1/1 [00:00<00:00, 259.10it/s]" + } + }, + "e9e9c3f6752640deb4d7f2791a8a329e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_3b582cb05b834f60b6311fdbde240407", + "IPY_MODEL_099fcd830e64454093b45f9ec3a9cc4a", + "IPY_MODEL_d8e62d4fa51c4cedbff580f154dd3a17" + ], + "layout": "IPY_MODEL_fd6826c5f1a54e93ae8b6ab4d3d6e421" + } + }, + "f3ebae78a3674c6c93fd64b3ed3e7542": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "fcd564594edd4d9582d3a4b3e212a3f2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_6553e0de7efa42728366a465d83a948f", + "IPY_MODEL_79b83887a2bb4248ae991970d6a274b1", + "IPY_MODEL_e0ba190eae4e4d5192b5e1ce501af014" + ], + "layout": "IPY_MODEL_96e31c05ff4c4a87aef91967cf4292e1" + } + }, + "fd6826c5f1a54e93ae8b6ab4d3d6e421": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + } + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From be393b32f287731b4f18b3bb4f30cf8fac807b88 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Tue, 20 Aug 2024 17:20:38 +0200 Subject: [PATCH 07/25] Update Signed-off-by: JGoedeke --- examples/tutorial/Tutorial_Simple_ODE.ipynb | 80 --------------------- 1 file changed, 80 deletions(-) diff --git a/examples/tutorial/Tutorial_Simple_ODE.ipynb b/examples/tutorial/Tutorial_Simple_ODE.ipynb index 633d7396..6281f6a1 100644 --- a/examples/tutorial/Tutorial_Simple_ODE.ipynb +++ b/examples/tutorial/Tutorial_Simple_ODE.ipynb @@ -1,85 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "xSO5tTpnr652", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "xSO5tTpnr652", - "outputId": "1ef26626-7f7f-49df-d88f-fe8c9b8d893c" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting git+https://github.com/TomF98/torchphysics\n", - " Cloning https://github.com/TomF98/torchphysics to /tmp/pip-req-build-z9tkrdb2\n", - " Running command git clone --filter=blob:none --quiet https://github.com/TomF98/torchphysics /tmp/pip-req-build-z9tkrdb2\n", - " Resolved https://github.com/TomF98/torchphysics to commit b5e89acb2132bb5543a7fa1d7212aa4bf9327b73\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1+cu121)\n", - "Requirement already satisfied: pytorch-lightning>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.4.0)\n", - "Requirement already satisfied: numpy>=1.20.2 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.26.4)\n", - "Requirement already satisfied: matplotlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (3.7.1)\n", - "Requirement already satisfied: scipy>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.1)\n", - "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.2.1)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.12.1)\n", - "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.53.1)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.5)\n", - "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.1)\n", - "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (9.4.0)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.2)\n", - "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.8.2)\n", - "Requirement already satisfied: tqdm>=4.57.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.66.5)\n", - "Requirement already satisfied: PyYAML>=5.4 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.2)\n", - "Requirement already satisfied: fsspec>=2022.5.0 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2024.6.1)\n", - "Requirement already satisfied: torchmetrics>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", - "Requirement already satisfied: typing-extensions>=4.4.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.12.2)\n", - "Requirement already satisfied: lightning-utilities>=0.10.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.11.6)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.15.4)\n", - "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.2)\n", - "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.3)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.4)\n", - "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (8.9.2.26)\n", - "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.3.1)\n", - "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.0.2.54)\n", - "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (10.3.2.106)\n", - "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.4.5.107)\n", - "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.0.106)\n", - "Requirement already satisfied: nvidia-nccl-cu12==2.20.5 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.20.5)\n", - "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: triton==2.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1)\n", - "Requirement already satisfied: nvidia-nvjitlink-cu12 in /usr/local/lib/python3.10/dist-packages (from nvidia-cusolver-cu12==11.4.5.107->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.6.20)\n", - "Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.10.3)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from lightning-utilities>=0.10.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (71.0.4)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.16.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.1.5)\n", - "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.0)\n", - "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.5)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.1)\n", - "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.2.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.5)\n", - "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.9.4)\n", - "Requirement already satisfied: async-timeout<5.0,>=4.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.0.3)\n", - "Requirement already satisfied: idna>=2.0 in /usr/local/lib/python3.10/dist-packages (from yarl<2.0,>=1.0->aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.7)\n" - ] - } - ], - "source": [ - "!pip install git+https://github.com/TomF98/torchphysics" - ] - }, { "cell_type": "code", "execution_count": 2, From ca255595b54b22d45b7219006b392526a1802f13 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Tue, 20 Aug 2024 17:20:38 +0200 Subject: [PATCH 08/25] update --- examples/tutorial/Tutorial_Simple_ODE.ipynb | 82 +-------------------- 1 file changed, 1 insertion(+), 81 deletions(-) diff --git a/examples/tutorial/Tutorial_Simple_ODE.ipynb b/examples/tutorial/Tutorial_Simple_ODE.ipynb index 633d7396..d823d3b2 100644 --- a/examples/tutorial/Tutorial_Simple_ODE.ipynb +++ b/examples/tutorial/Tutorial_Simple_ODE.ipynb @@ -1,85 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "xSO5tTpnr652", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "xSO5tTpnr652", - "outputId": "1ef26626-7f7f-49df-d88f-fe8c9b8d893c" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting git+https://github.com/TomF98/torchphysics\n", - " Cloning https://github.com/TomF98/torchphysics to /tmp/pip-req-build-z9tkrdb2\n", - " Running command git clone --filter=blob:none --quiet https://github.com/TomF98/torchphysics /tmp/pip-req-build-z9tkrdb2\n", - " Resolved https://github.com/TomF98/torchphysics to commit b5e89acb2132bb5543a7fa1d7212aa4bf9327b73\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1+cu121)\n", - "Requirement already satisfied: pytorch-lightning>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (2.4.0)\n", - "Requirement already satisfied: numpy>=1.20.2 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.26.4)\n", - "Requirement already satisfied: matplotlib>=3.0.0 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (3.7.1)\n", - "Requirement already satisfied: scipy>=1.6.3 in /usr/local/lib/python3.10/dist-packages (from torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.1)\n", - "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.2.1)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.12.1)\n", - "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.53.1)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.5)\n", - "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.1)\n", - "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (9.4.0)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.2)\n", - "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.8.2)\n", - "Requirement already satisfied: tqdm>=4.57.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.66.5)\n", - "Requirement already satisfied: PyYAML>=5.4 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.2)\n", - "Requirement already satisfied: fsspec>=2022.5.0 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2024.6.1)\n", - "Requirement already satisfied: torchmetrics>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", - "Requirement already satisfied: typing-extensions>=4.4.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.12.2)\n", - "Requirement already satisfied: lightning-utilities>=0.10.0 in /usr/local/lib/python3.10/dist-packages (from pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (0.11.6)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.15.4)\n", - "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.13.2)\n", - "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.3)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.1.4)\n", - "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (8.9.2.26)\n", - "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.3.1)\n", - "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.0.2.54)\n", - "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (10.3.2.106)\n", - "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (11.4.5.107)\n", - "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.0.106)\n", - "Requirement already satisfied: nvidia-nccl-cu12==2.20.5 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.20.5)\n", - "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.1.105)\n", - "Requirement already satisfied: triton==2.3.1 in /usr/local/lib/python3.10/dist-packages (from torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.1)\n", - "Requirement already satisfied: nvidia-nvjitlink-cu12 in /usr/local/lib/python3.10/dist-packages (from nvidia-cusolver-cu12==11.4.5.107->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (12.6.20)\n", - "Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.10.3)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from lightning-utilities>=0.10.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (71.0.4)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib>=3.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.16.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.1.5)\n", - "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy->torch>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.0)\n", - "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (2.3.5)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.3.1)\n", - "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (24.2.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.4.1)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (6.0.5)\n", - "Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (1.9.4)\n", - "Requirement already satisfied: async-timeout<5.0,>=4.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (4.0.3)\n", - "Requirement already satisfied: idna>=2.0 in /usr/local/lib/python3.10/dist-packages (from yarl<2.0,>=1.0->aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]>=2022.5.0->pytorch-lightning>=2.0.0->torchphysics==0.0.post1.dev510+gb5e89ac) (3.7)\n" - ] - } - ], - "source": [ - "!pip install git+https://github.com/TomF98/torchphysics" - ] - }, { "cell_type": "code", "execution_count": 2, @@ -646,7 +566,7 @@ "Torchphysics provides built-in functionalities for visualizing the outcome of the neural network. We use the plot() function from \"tp.utils\", which is built on the Matplotlib library. The most important inputs are:\n", "1) model: The neural network whose output shall be visualized.\n", "2) plot_function: Will be applied to the model's output before visualization. E.g. if the output was two-dimensional, the plot_function $u\\mapsto u[:, 0]$ could be used for showing only its first coordinate.\n", - "3) point_sampler: A sampler creating points the neural network will be evaluated at for creating the plot. More information is provided in the further [tutorials](https://torchphysics.de/tutorial) or the in-depth [plot-tutorial](https://boschresearch.github.io/torchphysics/tutorial/plotting.html).\n", + "3) point_sampler: A sampler creating points the neural network will be evaluated at for creating the plot. More information is provided in the the in-depth [plot-tutorial](https://boschresearch.github.io/torchphysics/tutorial/plotting.html).\n", "\n", "Let us start with the sampler. The samplers we have seen so far (RandomUniformSampler, GridSampler) plot either on the interior or the boundary of their domain.\n", "However, it is desirable to consider both the interior and the boundary points in the visualization. For this, one can use a PlotSampler, which is desined for harmonizing with plotting duties." From 45a35e4c68ec9acc17ff199f6bcdefe58098b777 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Wed, 21 Aug 2024 12:57:22 +0200 Subject: [PATCH 09/25] update Signed-off-by: JGoedeke --- examples/tutorial/Introduction_Tutorial_PINNs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb index 495a0cad..08c99c3e 100644 --- a/examples/tutorial/Introduction_Tutorial_PINNs.ipynb +++ b/examples/tutorial/Introduction_Tutorial_PINNs.ipynb @@ -868,7 +868,7 @@ "For this purpose, we use the plot() function from \"tp.utils\", which is built on the Matplotlib library. The most important inputs are:\n", "1) model: The neural network whose output shall be visualized.\n", "2) plot_function: Will be applied to the model's output before visualization. E.g. if the output was two-dimensional, the plot_function $u\\mapsto u[:, 0]$ could be used for showing only its first coordinate.\n", - "3) point_sampler: A sampler creating points the neural network will be evaluated at for creating the plot.\n", + "3) point_sampler: A sampler creating points the neural network will be evaluated at for creating the plot. More information is provided in the the in-depth [plot-tutorial](https://boschresearch.github.io/torchphysics/tutorial/plotting.html).\n", "4) plot_type: Specify what kind of plot should be created.\n", "\n", "Let us start with the sampler. The samplers we have seen so far (RandomUniformSampler, GridSampler) plot either on the interior or the boundary of their domain.\n", From 8d0b4fd97ee3055d24dc2e9c932067c997371b08 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Thu, 22 Aug 2024 14:47:15 +0200 Subject: [PATCH 10/25] Tutorial for physics-informed DeepONets Signed-off-by: JGoedeke --- examples/tutorial/Tutorial_PIDeepONet.ipynb | 2131 +++++++++++++++++++ 1 file changed, 2131 insertions(+) create mode 100644 examples/tutorial/Tutorial_PIDeepONet.ipynb diff --git a/examples/tutorial/Tutorial_PIDeepONet.ipynb b/examples/tutorial/Tutorial_PIDeepONet.ipynb new file mode 100644 index 00000000..280601cf --- /dev/null +++ b/examples/tutorial/Tutorial_PIDeepONet.ipynb @@ -0,0 +1,2131 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "CzanhsB3YLBe" + }, + "source": [ + "# Physics-informed DeepONet: Solving a ODE for different right hand sides\n", + "In this notebook, we present an introduction to the physics-informed DeepONet [(paper)](https://arxiv.org/abs/2103.10974) utilities of TorchPhysics.\n", + "As an example, we try to learn the integral operator of the ODE:\n", + "\\begin{align*}\n", + " \\partial_t u(t) &= f(t), \\text{ in } [0, 1] \\\\\n", + " u(0) &= 0\n", + "\\end{align*}\n", + "for different functions $f$. Before describing the implementation in TorchPhysics we give a short recall of DeepONets.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bKeJnQz4Yftw" + }, + "source": [ + "# Recall of Deep Operator Networks (DeepONets)\n", + "\n", + "We wish to learn the solution $u_f:[0,1]\\to \\mathbb{R}$ for many different choices of functions $f:[0,1]\\to \\mathbb{R}$ by a DeepONet $\\varphi_{\\theta}.$\n", + "The construction of DeepOnets is described in what follows:\n", + "\n", + "1) The basic idea is to represent $u_f$ as a linear combination of neural networks $\\varphi_i:[0,1]\\to \\mathbb{R}$\n", + "$$\n", + "u_f(t) \\approx \\sum_{i=1}^n c_i(f) \\varphi_i(t),\n", + "$$\n", + "where the coefficients $c_i(f)$ depend on the parameter function $f$ that leads to the solution $u_f$ of the ODE above.\n", + "\n", + "2) We would wish to replace $c_i(f)$ by a neural network, too. However, we cannot use a function $f$ as an input of a neural network. Instead, we collect finitely many information about $f$ by sampling $f$ at sampling points $t_1, ..., t_k$. These serve as inputs of neural networks $\\psi_i$ for approximating the coefficients\n", + "$$\n", + "c_i(f) \\approx \\psi_i( f(t_1), ..., f(t_k) ).\n", + "$$\n", + "\n", + "3) A DeepONet $\\varphi_\\theta$ is hence defined as\n", + "$$\n", + "\\varphi_\\theta(f,t) := \\sum_{i=1}^n \\psi_i( f(t_1),...,f(t_k) ) \\varphi_i(t) ≈u_f(t).\n", + "$$\n", + "Typically, all $\\psi_i$ are collected within a single neural network, the so-called branch net $\\varphi_{branch}:[0,1] \\to \\mathbb{R}^n$, whose output coordinates just represent the $\\psi_i$. Similarly, the $\\varphi_i$ are represented by a so-called trunk net $\\varphi_{trunk}:[0,1] \\to \\mathbb{R}^n$.\n", + "So the branch net receives only information about the parameter function $f$, whereas the input of the trunk net is the coordinate $t$ at which the solution $u_f$ is to be approximated.\n", + "Note that the sampling points $t_1, ..., t_k$ needs to be fixed beforehang and should not be changed for different $f$, simply to not confuse the branch net $\\varphi_{branch}$." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mdaVk2063YCG" + }, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ulenDc9tYfv2" + }, + "source": [ + "# Physics-informed DeepONets in TorchPhysics\n", + "\n", + "The general structure of TorchPhysics still holds for DeepONet problems, so we can follow the same step-by-step recipe as in the [PINN-tutorials](https://torchphysics.de/tutorial).\n", + "However, we need some new concepts to define training functions of $f$. Here we show, how to:\n", + "\n", + "- create a function space for different training functions\n", + "- define a DeepONet-neural-network consisting of trunk and branch net\n", + "\n", + "and some additional details one has to consider." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "ge4lD3QIYLBf" + }, + "outputs": [], + "source": [ + "import os\n", + "import torch\n", + "import torchphysics as tp\n", + "import pytorch_lightning as pl" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-TnwJNYRs0nA" + }, + "source": [ + "## Step 1: Specify spaces and domains\n", + "As in the PINN tutorials we need to specify the input and output spaces of the solution functions $u_f:[0,1]\\to \\mathbb{R}$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "0ewY_QuetJmo" + }, + "outputs": [], + "source": [ + "# Input and output spaces\n", + "T = tp.spaces.R1('t') # input variable\n", + "U = tp.spaces.R1('u') # output variable\n", + "\n", + "# Domain of u_f\n", + "T_int = tp.domains.Interval(T, 0, 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8zhCYNVZtg-5" + }, + "source": [ + "The DeepONet tries to learn many different $u_f$ for parameter functions $f$. Therefore, we need to define a set of functions $f$ that will be used for training the DeepONet.\n", + "We consider the function set that consist of the following parameterized functions for $k\\in [0,6]$\n", + "$$\n", + "f_1(t, k) = kt,\\\\\n", + "f_2(t, k) = kt^2,\\\\\n", + "f_3(t, k) = k\\cos(kt).\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "LoCmJVNd9vvc" + }, + "outputs": [], + "source": [ + "def f1(k, t):\n", + " return k*t\n", + "def f2(k, t):\n", + " return k*t**2\n", + "def f3(k, t):\n", + " return k*torch.cos(k*t)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PzMYI0iX9sXr" + }, + "source": [ + "In order to construct this function set in TorchPhysics, we need to create input and output spaces for these functions $f_i$, similar to what we did with $u_f$. Here, also the parameter $k$ is treated as an input. Note that for the input variable $t$ we do not specify a space and domain again, as we already did that above." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "UzKytihtYLBg" + }, + "outputs": [], + "source": [ + "# Input and output spaces for the parameterized functions f_1, f_2, f_3\n", + "K = tp.spaces.R1('k') # Parameter\n", + "F = tp.spaces.R1('f') # Function output space name\n", + "# Domains\n", + "K_int = tp.domains.Interval(K, 0, 6) # Parameters will be scalar values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lF6ufyUE8B4M" + }, + "source": [ + "If different ranges for $k$ were to be used for different $f_i$, one would have needed to create multiple parameter spaces and domains.\n", + "\n", + "The function set of all $f_i$ for all $k\\in [0,6]$ is considered as a domain object in TorchPhysics, simply because this set serves as the input for the branch net of the DeepONet. The constructor of CustomFunctionSet(...) receives three inputs:\n", + "- A function space that specifies the domain and output space of the functions $f_i:[0,1] \\to \\mathbb{R}$;\n", + "- A sampler for providing different parameters $k$;\n", + "- The parameterized function $f_i$ that represents the function set.\n", + "\n", + "We first create function sets for all $f_i$ individually and afterwards obtain the union by the \"+\" operator." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "kNyDfWJ3YLBh" + }, + "outputs": [], + "source": [ + "# Defining function set\n", + "Fn_space = tp.spaces.FunctionSpace(T_int, F)\n", + "\n", + "param_sampler = tp.samplers.RandomUniformSampler(K_int, n_points=40)\n", + "\n", + "Fn_set_1 = tp.domains.CustomFunctionSet(Fn_space, param_sampler, f1)\n", + "Fn_set_2 = tp.domains.CustomFunctionSet(Fn_space, param_sampler, f2)\n", + "Fn_set_3 = tp.domains.CustomFunctionSet(Fn_space, param_sampler, f3)\n", + "\n", + "Fn_set = Fn_set_1 + Fn_set_2 + Fn_set_3 # \"+\" computes the union of function sets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n4e7zKDW-5dz" + }, + "source": [ + "Mathematically, Fn_set represents the function set\n", + "$$\n", + "\\{ f_i(\\cdot , k): \\text{for } i=1,2,3 \\text{ and } k\\in [0,6] \\}.\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fM4ALPl13dQW" + }, + "source": [ + "## Step 2: Define point samplers\n", + "As in the PINN tutorials we need to define samplers for the ODE condtion and the initial condition." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "QVYBUZM0AEjL" + }, + "outputs": [], + "source": [ + "sampler_ode_condition = tp.samplers.RandomUniformSampler(T_int, 1000)\n", + "\n", + "sampler_initial_condition = tp.samplers.RandomUniformSampler(T_int.boundary_left, 500)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HOJ31RPpAYd7" + }, + "source": [ + "## Step 3: Define residual functions\n", + "Similar to $u$, the parameter function $f$ occuring in the ODE condition will already have been sampled when the residual functions are called. Therefore, we define the residual functions as:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "qhtoY7msAffq" + }, + "outputs": [], + "source": [ + "def residual_ode_condition(u, t, f):\n", + " return tp.utils.grad(u, t) - f\n", + "\n", + "def residual_initial_condition(u):\n", + " return u" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PWt7y2vLA2wK" + }, + "source": [ + "\n", + "## Step 4: Define DeepONet\n", + "\n", + "The branch net in TorchPhysics needs to know the function space the parameter functions $f$ belong to. Further, it needs to know the points $t_1,.,,,t_k$ at which these $f$ are to be sampled. These sampling points can just be obtained by for example a GridSampler. As mentioned in the recall, these sampling points should be fixed and never changed. Therefore, we need to make this sampler static." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "hTW3VG7iYLBh" + }, + "outputs": [], + "source": [ + "# Branch net\n", + "branch_input_sampler = tp.samplers.GridSampler(T_int, 50).make_static() # hence, there will be 50 sampling points $t_j$\n", + "# We choose a fully connected network for the branch net, in TorchPhysics: FCBranchNet(...)\n", + "branch_net = tp.models.FCBranchNet(Fn_space, hidden=(50, 50), discretization_sampler=branch_input_sampler)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F_nMeGdYD1rd" + }, + "source": [ + "Also for the trunk net we choose a fully connected network." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "d1PDR5qIDdmr" + }, + "outputs": [], + "source": [ + "trunk_net = tp.models.FCTrunkNet(T, hidden=(30, 30))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_RFcpvASD-qy" + }, + "source": [ + "The constructor of the DeepONet receives just the trunk net, the branch net, the output space of the solution functions $u_f$, as well as the output dimension $n$ of the branch and trunk nets. We set the output dimension to $50$. Having a look at the DeepONet recall again, this means that the DeepONet can represent a function vector space of dimension $\\leq 50$." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "HgFlwDuBD9aT" + }, + "outputs": [], + "source": [ + "model = tp.models.DeepONet(trunk_net, branch_net, U, output_neurons=50)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yCeOL7jFA9sr" + }, + "source": [ + "## Step 5: Create TorchPhysics Conditions\n", + "Almost done: We only need to define the TorchPhysics conditions for the ODE and initial condition. Since the DeepONet is to be trained physics-informed, we need to define PIDeepONetConditions." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "jOUqZr8vYLBi" + }, + "outputs": [], + "source": [ + "# Conditions for the ODE problem\n", + "ode_condition = tp.conditions.PIDeepONetCondition(deeponet_model=model,\n", + " function_set=Fn_set,\n", + " input_sampler=sampler_ode_condition,\n", + " name='ode_condition', # By specifying a name\n", + " residual_fn=residual_ode_condition)\n", + "\n", + "initial_condition = tp.conditions.PIDeepONetCondition(deeponet_model=model,\n", + " function_set=Fn_set,\n", + " input_sampler=sampler_initial_condition,\n", + " residual_fn=residual_initial_condition)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7ImcV0W8BZxj" + }, + "source": [ + "# Training" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 344, + "referenced_widgets": [ + "eab3e0cf876e42a9bd7118bbf01417b3", + "1fbef4dff38943dd9334bcf8f98b9025", + "96df4b367e0147b4b0873178c2c6ddee", + "febbf813f7344eea93d68337a3fb3ce4", + "accbf70574ba461ea61bb62e4e649841", + "bf5c85326d0746f1a75f5ba124b04b2b", + "42e76e3b8ed3484f92869e7253b6f65b", + "035d64de4ffe4480a693d787e4cf3372", + "b63aa5f44deb4e35bcc74e26f581bb08", + "bea3f5a98c8d4a3a97b8b946e0cd4d57", + "d12decf7d62840b79bc0dd22184d2260", + "cd63d468874441ada619c005aea50780", + "c18d459cfb3e4e158e7f2944b667144e", + "40845e140d324680a70af6ff099c5fd0", + "539d5cad38824eaca6c808263ff39248", + "2bb0e93f1fcc4557a9e28acbfb1ca5ff", + "0b5a790579e3404680f9e0eee3a8cff2", + "cd11a8ea139a42249b0907737559a3ad", + "0d2180d103794cc8a90a46884fc402cc", + "e72a8bd17e234b979a6423515ec8676d", + "392f12bd38d54b40a0a06309f3ddd7fd", + "e91087442a97426892598e42e19a0368" + ] + }, + "id": "CmoY6mXyYLBj", + "outputId": "e309c69b-b892-4a69-9f5c-f2bae80bfbf2", + "tags": [ + "outputPrepend" + ] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pytorch_lightning.utilities.rank_zero:GPU available: True (cuda), used: True\n", + "INFO:pytorch_lightning.utilities.rank_zero:TPU available: False, using: 0 TPU cores\n", + "INFO:pytorch_lightning.utilities.rank_zero:HPU available: False, using: 0 HPUs\n", + "INFO:pytorch_lightning.accelerators.cuda:LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]\n", + "INFO:pytorch_lightning.callbacks.model_summary:\n", + " | Name | Type | Params | Mode \n", + "--------------------------------------------------------\n", + "0 | train_conditions | ModuleList | 10.2 K | train\n", + "1 | val_conditions | ModuleList | 0 | train\n", + "--------------------------------------------------------\n", + "10.2 K Trainable params\n", + "0 Non-trainable params\n", + "10.2 K Total params\n", + "0.041 Total estimated model params size (MB)\n", + "19 Modules in train mode\n", + "0 Modules in eval mode\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "eab3e0cf876e42a9bd7118bbf01417b3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: | | 0/? [00:00" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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ffQRlStX5rXXXrvo/mzYsIG2bdvi6+tLWFgY48ePJzs72/l6fjMckZGRTJ061fk6wI033ojFYnE+vxDj888/7zzVfdCgQZw+fdo5Tn6HsPr378/w4cMvmk8RkTLr0Gb47g2s8ZPwdpzzWBjFevPDkuqc3UHTyZ945Gfvmh5DgE/B026z2XjiiSe49dZbuf/++7niiivy9Nm/fz+9e/dm5syZLF26lJMnTzqvlfPSSy/xzjvv0KJFC0aPHs2oUaOAnMMlXbp0ITExkYEDB/L777+ze/du/P392bNnD40bN2bDhg20adOGgIAADMNwFisbNmwgOzube++9l8GDB+e69s6+fft4++23eeedd7DZbHliff3117n77rt5/fXXue666/Ld56NHj9KnTx+GDx/O8uXL2bNnD6NGjcLPz89ZkFzO1q1bqVGjBi+99BK9e/fOFcu+fft44403+OCDD0hLS2PkyJGMGTPmkgXUX+WXTxGRssrY9CxWYINvd06YlT0WR7ksWEqbG2+8kcjISKZMmcKSJUvyvB4XF8dtt93mnBVo2LAhzz77LF27dmXhwoVUrVoVm81GpUqVct2kslu3bjz//PMAfP7557Rs2ZLQ0FASExNp3LgxiYmJdO3aFYCEhAS+++47Dhw4QEREBADLly/nqquuYuvWrbRp0wbIOQy0fPlyqlevnifO+fPnM3HiRD744APnuPlZsGABERERzJs3D4vFQuPGjTl27BiPPvookydPLtA1SS78/KCgoDw35jx//jzLly+nZs2aADz33HP07duXp59+Ot+beP7dxfIpIlLm/Lofy96PAFiU1ZubPVg1lMuCxd/bxq7pMZftZxgGZ9LOUCmwUpFduMvfO++sQ0E8+eSTdO/enYcffjjPa99++y07d+7MNUNgmqbztgRNmjTJd8yuXbvywAMPcPLkSTZs2EC3bt2cBcvIkSP58ssvGTduHAC7d+8mIiLCWawANG3alKCgIHbv3u0sWGrXrp1vsfLWW29x4sQJNm3a5Ox7Mbt376Z9+/a5DrV07NiRs2fPcuTIEWrVqnXJ7S+nVq1azmIFoH379hiGkevu4SIiAuaXz2HBJMHRks5dOuJ1ZrfHYimXa1gsFgsBPl4Fevj72ArctyCPwq536NKlCzExMUyYMCHPa2fPnuXf//43O3bscD6+/fZbfvrpJ+rXr3/RMZs1a0bVqlXZsGGDs2Dp1q0bGzZsYOvWrdjtdjp06OBSnBUqVMi3vWXLllSvXp2lS5e6vI4nP1arNc84drv9H49b3GOLiJQaZ09gfJPzH+Hl1n7c0ibvkgR3KpczLKXVrFmziIyMpFGjRrnaW7Vqxa5du2jQoMFFt/Xx8cHhcORqs1gsdO7cmffee48ffviBTp06ERAQQGZmJs8//zxRUVHOAqRJkyYcPnyYw4cPO2dZdu3axalTp2jatOllY69fvz5PP/003bp1w2azMW/evIv2bdKkCW+//TamaToLvE2bNlGpUiXnGp7q1atz/Phx5zZpaWkcOHAg1zje3t559hlyFg8fO3aM8PBwAL766iusVqszr38f2+Fw8P3333PNNdc42/LLp4hImbLlBWxGFjuM+lzZtheV/Lw9Gk65nGEprZo1a8Ztt93Gs88+m6v90Ucf5csvv2Ts2LHs2LGDn376iffeey/XDSrr1KnD559/ztGjR0lNTXW2d+vWjRUrVhAZGem8U3KXLl147bXXcq0ziY6Odv787du3s2XLFoYOHUrXrl2JiooqUPxXXnkln332GW+//fYlLyQ3ZswYDh8+zH333ceePXt47733mDJlCrGxsc5Dc927d+eVV17hiy++4LvvvmPYsGF5FvnWqVOHhIQEkpOTc50W7ufnx7Bhw/j222/54osvuP/++xk0aJDzcFD37t356KOP+Oijj9izZw/33HMPp06dyjN2fvkUESkTMs+SvfkFABY7rmdEp3oeDkgFS6kzffp0DMPI1da8eXM2bNjAjz/+SOfOnWnZsiWTJ092ziBc2O7gwYPUr18/1xqTrl274nA46Natm7OtW7duedosFgvvvfceVapUoUuXLkRHR1OvXj1WrVrlUvyNGjVi/fr1rFixgoceeijfPjVr1mTNmjVs2bKFFi1acPfddzNy5EgmTZrk7DNhwgS6du3KddddR9++fenfv3+ew19PP/008fHxRERE0LJlS2d7gwYNuOmmm+jTpw+9evWiefPmLFiwwPn6nXfeybBhw5wFWb169XLNrlwqnyIiZcI3r+KVeZoDRgh+zW4gPMjf0xFhMYtiQYGHpaWlUblyZU6fPk1gYGCu186fP8+BAwcueV2QizEMg7S0NAIDA3W33GLmrlxPnTqV1atXs2PHjmL7GcXhn7yP/8put7NmzRr6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43h7ezu/v/BBeaEAePjhh4mPj+epp56iQYMG+Pv7M3DgQLKysi76s4cOHcr48eNJSkriyy+/pG7dunTu3Nml+P39L7146/rrr6d27dosXryY0NBQ0tLS6NChwyXj+qtKlSqxfft2EhMTWbduHZMnT2bq1KmXXKdyKVarFfNva4zsdnuhxiqIv/7OIOf3duF3JiJSbEyT7M+fxgt42RHDyO7NPB1RgZTPRbelQHZ2NsuXL+fpp59mx44dzse3335LeHg4K1asKPBYmzZtYvjw4dx44400a9aM0NDQPOtN/q5atWr079+fl156iZdffpkRI0bket3Hx8e5FuVimjdvTkJCQr6v/frrr+zdu5dJkybRo0cPmjRpUqjroXh5eREdHc3s2bPZuXMnBw8eZP369dSvXx8fHx/nOh7IKT62bt1K06ZN8x2revXqJCcn5ypa/n5tlYLsd5MmTUhKSso1zqZNm6hUqRJXXHGFy/soIlKk9n2K14nvSTd92VrjZjrUr+bpiAqkUAXL/PnzqVOnDn5+frRr144tW7ZctO8777xDVFQUQUFBVKhQgcjISF555ZVcfYYPH+486+LCo3fv3oUJrcz48MMP+f333xk5ciRXX311rseAAQNYsmRJgcdq2LAh77zzjrPgufXWWwv0P/m77rqLZcuWsXv3boYNG5brtTp16rB582YOHjyY63DOX02YMIGtW7cyZswYdu7cyZ49e1i4cCGpqalUqVKFatWq8cILL7Bv3z7Wr1/PpEmTCrxPkJOjZ599lh07dvDLL7+wfPlyDMOgUaNGVKhQgXvuuYdHHnmEtWvXsmvXLkaNGkVGRgYjR47Md7xu3bpx8uRJZs+ezf79+5k/fz4ff/xxnv3euXMne/fuJTU1Nd8ZmDFjxnD48GHuu+8+9uzZw3vvvceUKVOIjY0tukOLIiKF5Pj8aQBed/Tg9mtalppLUbj813PVqlXExsYyZcoUtm/fTosWLYiJibnosfeqVasyceJEkpKS2LlzJyNGjGDEiBF88sknufr17t2b48ePOx+uzCCURUuWLCE6Ojrfi7ANGDCAr7/+mp07dxZorDlz5lClShU6dOjA9ddfT0xMDK1atbrsdtHR0YSFhRETE5PrsBTkHGay2Ww0bdqU6tWr57se5sorr2TdunV8++23tG3blvbt2/Pee+/h5eWF1Wpl5cqV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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "k0 = 1.16\n", + "def f(t):\n", + " return k0 * t**2\n", + "\n", + "def F(t): # ground truth solution u_f\n", + " return k0/3.0 * t**3\n", + "\n", + "model.fix_branch_input(f)\n", + "grid_sampler = tp.samplers.GridSampler(T_int, 500)\n", + "grid_points = grid_sampler.sample_points().as_tensor\n", + "out = model(tp.spaces.Points(grid_points, T)).as_tensor.detach()[0]\n", + "\n", + "grid_p = grid_points\n", + "plt.plot(grid_p, out)\n", + "plt.plot(grid_p, F(grid_p))\n", + "plt.grid()\n", + "plt.legend(['Network output', 'Analytical solution'])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 447 + }, + "id": "5lqtQM9WYLBl", + "outputId": "24c26ba5-8390-48ef-b2e4-27f6f5b84aca" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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9YABA5Q5c+Yohw4dUk6cOKEMGTJE8fLyUi5fvmzjyIsWc/v5rujoaCUoKEhp1aqV0qtXL9sEW8SZ29fp6elKaGio0rVrV2XHjh1KdHS0EhERoURGRto48qLF3H5evHix4uzsrCxevFiJjo5WNmzYoAQGBiqjR4+2ceRFz7p165S33npLWblypQIoq1atum/58+fPK25ubkp4eLgSFRWlfPbZZ4pOp1PWr19vlfiKbTLStGlTZcSIEVnfG41GpVy5csq0adNyLN+vXz+lW7du2c41a9ZMeeGFF6waZ3Fgbl//V2ZmpuLh4aF8++231gqxWChIP2dmZirNmzdXvvrqK2Xw4MGSjOSTuX09b948pXLlykpGRoatQiwWzO3nESNGKO3bt892Ljw8XGnRooVV4yxu8pOMjB07VqlTp062c/3791c6d+5slZiK5TBNRkYGBw4coEOHDlnntFotHTp0YPfu3TnW2b17d7byAJ07d861vFAVpK//KyUlBYPBQJkyZawVZpFX0H5+99138fPzY9iwYbYIs1goSF//8ssvhIWFMWLECPz9/albty5Tp07FaDTaKuwipyD93Lx5cw4cOJA1lHP+/HnWrVtH165dbRJzSWLrz8QisVGeua5fv47RaMTf3z/beX9/f06ePJljnbi4uBzLx8XFWS3O4qAgff1f48aNo1y5cve88MU/CtLPO3bsYOHChURGRtogwuKjIH19/vx5tmzZwsCBA1m3bh1nz57l5ZdfxmAwMHHiRFuEXeQUpJ8HDBjA9evXadmyJYqikJmZyYsvvsibb75pi5BLlNw+E/V6Pampqbi6ulr08YrllRFRdEyfPp0lS5awatUqXFxc7B1OsZGYmMigQYNYsGABPj4+9g6n2DOZTPj5+fHll1/SpEkT+vfvz1tvvcX8+fPtHVqxEhERwdSpU5k7dy4HDx5k5cqVrF27lilTptg7NPGAiuWVER8fH3Q6HfHx8dnOx8fHExAQkGOdgIAAs8oLVUH6+q6PPvqI6dOn88cff1C/fn1rhlnkmdvP586d48KFC/To0SPrnMlkAsDBwYFTp05RpUoV6wZdRBXkNR0YGIijoyM6nS7rXK1atYiLiyMjIwMnJyerxlwUFaSf33nnHQYNGsRzzz0HQL169UhOTub555/nrbfeQquVv68tJbfPRE9PT4tfFYFiemXEycmJJk2asHnz5qxzJpOJzZs3ExYWlmOdsLCwbOUBNm3alGt5oSpIXwPMmDGDKVOmsH79ekJDQ20RapFmbj/XrFmTo0ePEhkZmfXVs2dP2rVrR2RkJMHBwbYMv0gpyGu6RYsWnD17NivhAzh9+jSBgYGSiOSiIP2ckpJyT8JxNwFUZJs1i7L5Z6JVpsUWAkuWLFGcnZ2Vb775RomKilKef/55xdvbW4mLi1MURVEGDRqkvPHGG1nld+7cqTg4OCgfffSRcuLECWXixIlya28+mdvX06dPV5ycnJTly5crsbGxWV+JiYn2egpFgrn9/F9yN03+mdvXMTExioeHhzJy5Ejl1KlTym+//ab4+fkp7733nr2eQpFgbj9PnDhR8fDwUH766Sfl/PnzysaNG5UqVaoo/fr1s9dTKDISExOVQ4cOKYcOHVIAZdasWcqhQ4eUixcvKoqiKG+88YYyaNCgrPJ3b+0dM2aMcuLECWXOnDlya29BffbZZ0qFChUUJycnpWnTpsqePXuyftamTRtl8ODB2covW7ZMqV69uuLk5KTUqVNHWbt2rY0jLrrM6euKFSsqwD1fEydOtH3gRYy5r+l/k2TEPOb29a5du5RmzZopzs7OSuXKlZX3339fyczMtHHURY85/WwwGJRJkyYpVapUUVxcXJTg4GDl5ZdfVm7dumX7wIuYrVu35vi+e7d/Bw8erLRp0+aeOg0bNlScnJyUypUrK19//bXV4tMoilzbEkIIIYT9FMs5I0IIIYQoOiQZEUIIIYRdSTIihBBCCLuSZEQIIYQQdiXJiBBCCCHsSpIRIYQQQtiVJCNCCCGEsCtJRoQQQghhV5KMCCGEEMKuJBkRQgghhF1JMiKEEEIIu5JkRAghhBB29X9XRRVuBx3DLgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "k0 = 2.14\n", + "def f(t):\n", + " return k0 * torch.cos(k0*t)\n", + "\n", + "def F(t): # ground truth solution u_f\n", + " return torch.sin(k0*t)\n", + "\n", + "model.fix_branch_input(f)\n", + "grid_sampler = tp.samplers.GridSampler(T_int, 500)\n", + "grid_points = grid_sampler.sample_points().as_tensor\n", + "out = model(tp.spaces.Points(grid_points, T)).as_tensor.detach()[0]\n", + "\n", + "grid_p = grid_points\n", + "plt.plot(grid_p, out)\n", + "plt.plot(grid_p, F(grid_p))\n", + "plt.grid()\n", + "plt.legend(['Network output', 'Analytical solution'])" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "interpreter": { + "hash": "fb770cb910411e790a99fd848f827dc995ac53be5098d939fbaa56bcec3c9277" + }, + "kernelspec": { + "display_name": "Python 3", + "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.15" + }, + "orig_nbformat": 4, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "035d64de4ffe4480a693d787e4cf3372": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "06660a07aca242108507da32e1d49d1f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "068b4483fbb54147bb9753ffdd51c1c1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0b5a790579e3404680f9e0eee3a8cff2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0d2180d103794cc8a90a46884fc402cc": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "13e8e28e8b9d4cab9fe843a12e596b57": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "1fbef4dff38943dd9334bcf8f98b9025": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_bf5c85326d0746f1a75f5ba124b04b2b", + "placeholder": "​", + "style": "IPY_MODEL_42e76e3b8ed3484f92869e7253b6f65b", + "value": "Epoch 0: 100%" + } + }, + "2a9cce56300a4a3eb8ddc041286ab630": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "2bb0e93f1fcc4557a9e28acbfb1ca5ff": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "3179d2e823eb46b9ae9cb5b5b6de0172": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "32eb8b6c120549069bdc2a2ab5570f0c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "392f12bd38d54b40a0a06309f3ddd7fd": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "40845e140d324680a70af6ff099c5fd0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0d2180d103794cc8a90a46884fc402cc", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e72a8bd17e234b979a6423515ec8676d", + "value": 1 + } + }, + "42e76e3b8ed3484f92869e7253b6f65b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "52174dc0567f4d4ea3465918b86d211e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "539d5cad38824eaca6c808263ff39248": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_392f12bd38d54b40a0a06309f3ddd7fd", + "placeholder": "​", + "style": "IPY_MODEL_e91087442a97426892598e42e19a0368", + "value": " 1/1 [00:00<00:00, 660.10it/s]" + } + }, + "55eb40429c8d42679f56acda13dbfd66": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_32eb8b6c120549069bdc2a2ab5570f0c", + "placeholder": "​", + "style": "IPY_MODEL_aa7c70173ccd4ec988d6469cbee6fd12", + "value": "Epoch 0: 100%" + } + }, + "63ec80f6968844948e2a580fe6c7085c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cdf450aa05064cf3bcd184e70c67b83c", + "placeholder": "​", + "style": "IPY_MODEL_13e8e28e8b9d4cab9fe843a12e596b57", + "value": "Validation DataLoader 0: 100%" + } + }, + "661570365b4c4ec99f4272987d1beb8c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "6711697a1654483d890f42156b351fad": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_068b4483fbb54147bb9753ffdd51c1c1", + "max": 2000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_2a9cce56300a4a3eb8ddc041286ab630", + "value": 2000 + } + }, + "6919e8904d0a4574b49e08468e95a4b6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_63ec80f6968844948e2a580fe6c7085c", + "IPY_MODEL_b0a3ba48d59b42b49310e4f5aeaf1c82", + "IPY_MODEL_f2db35b38aec40768d37881d8c929a91" + ], + "layout": "IPY_MODEL_661570365b4c4ec99f4272987d1beb8c" + } + }, + "6ab2a2dcd4854df3b2dfa4d63ebe966c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e39eddbafb9e4fafbfa570935d22a069", + "placeholder": "​", + "style": "IPY_MODEL_fd85806fa29d482398fac840754b79b1", + "value": " 2000/2000 [02:40<00:00, 12.48it/s, train/loss=0.0479]" + } + }, + "78db7ec96e2f499ab2cfc4b64eb9cc10": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_55eb40429c8d42679f56acda13dbfd66", + "IPY_MODEL_6711697a1654483d890f42156b351fad", + "IPY_MODEL_6ab2a2dcd4854df3b2dfa4d63ebe966c" + ], + "layout": "IPY_MODEL_8d95454822ce431f900fe4a9abe1e888" + } + }, + "8d95454822ce431f900fe4a9abe1e888": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + }, + "96df4b367e0147b4b0873178c2c6ddee": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_035d64de4ffe4480a693d787e4cf3372", + "max": 3000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b63aa5f44deb4e35bcc74e26f581bb08", + "value": 3000 + } + }, + "aa7c70173ccd4ec988d6469cbee6fd12": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "accbf70574ba461ea61bb62e4e649841": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + }, + "b0a3ba48d59b42b49310e4f5aeaf1c82": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_3179d2e823eb46b9ae9cb5b5b6de0172", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_06660a07aca242108507da32e1d49d1f", + "value": 1 + } + }, + "b63aa5f44deb4e35bcc74e26f581bb08": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "bea3f5a98c8d4a3a97b8b946e0cd4d57": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "bf5c85326d0746f1a75f5ba124b04b2b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c18d459cfb3e4e158e7f2944b667144e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0b5a790579e3404680f9e0eee3a8cff2", + "placeholder": "​", + "style": "IPY_MODEL_cd11a8ea139a42249b0907737559a3ad", + "value": "Validation DataLoader 0: 100%" + } + }, + "ca253e0ded924533a3adbcec0093ca32": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cd11a8ea139a42249b0907737559a3ad": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "cd63d468874441ada619c005aea50780": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_c18d459cfb3e4e158e7f2944b667144e", + "IPY_MODEL_40845e140d324680a70af6ff099c5fd0", + "IPY_MODEL_539d5cad38824eaca6c808263ff39248" + ], + "layout": "IPY_MODEL_2bb0e93f1fcc4557a9e28acbfb1ca5ff" + } + }, + "cdf450aa05064cf3bcd184e70c67b83c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d12decf7d62840b79bc0dd22184d2260": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "e39eddbafb9e4fafbfa570935d22a069": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e72a8bd17e234b979a6423515ec8676d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e91087442a97426892598e42e19a0368": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "eab3e0cf876e42a9bd7118bbf01417b3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_1fbef4dff38943dd9334bcf8f98b9025", + "IPY_MODEL_96df4b367e0147b4b0873178c2c6ddee", + "IPY_MODEL_febbf813f7344eea93d68337a3fb3ce4" + ], + "layout": "IPY_MODEL_accbf70574ba461ea61bb62e4e649841" + } + }, + "f2db35b38aec40768d37881d8c929a91": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ca253e0ded924533a3adbcec0093ca32", + "placeholder": "​", + "style": "IPY_MODEL_52174dc0567f4d4ea3465918b86d211e", + "value": " 1/1 [00:00<00:00, 496.13it/s]" + } + }, + "fd85806fa29d482398fac840754b79b1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "febbf813f7344eea93d68337a3fb3ce4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_bea3f5a98c8d4a3a97b8b946e0cd4d57", + "placeholder": "​", + "style": "IPY_MODEL_d12decf7d62840b79bc0dd22184d2260", + "value": " 3000/3000 [01:03<00:00, 47.27it/s, train/loss=1.770]" + } + } + } + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From 200f9aae205454aeefcc16d47f0d4ffed42d38d7 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Thu, 22 Aug 2024 14:52:19 +0200 Subject: [PATCH 11/25] update Signed-off-by: JGoedeke --- examples/tutorial/Tutorial_PIDeepONet.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorial/Tutorial_PIDeepONet.ipynb b/examples/tutorial/Tutorial_PIDeepONet.ipynb index 280601cf..cf39faeb 100644 --- a/examples/tutorial/Tutorial_PIDeepONet.ipynb +++ b/examples/tutorial/Tutorial_PIDeepONet.ipynb @@ -350,8 +350,8 @@ "ode_condition = tp.conditions.PIDeepONetCondition(deeponet_model=model,\n", " function_set=Fn_set,\n", " input_sampler=sampler_ode_condition,\n", - " name='ode_condition', # By specifying a name\n", " residual_fn=residual_ode_condition)\n", + "# Note: If logging via Tensorboard is desired, just add name='ode_condition' as an input of the PIDeepONetCondition. Works also for all other conditions, such as PINNCondition. \n", "\n", "initial_condition = tp.conditions.PIDeepONetCondition(deeponet_model=model,\n", " function_set=Fn_set,\n", From 7a37baefacd122f924220739a6111fce40914f72 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 2 Sep 2024 17:50:56 +0200 Subject: [PATCH 12/25] Update tests and fix callback Signed-off-by: Tom Freudenberg --- src/torchphysics/utils/callbacks.py | 101 ++++++++++++------ tests/tests_utils/test_callbacks.py | 80 ++++++++++++++ tests/{ => tests_utils}/test_data_utils.py | 0 .../test_differentialoperators.py | 0 tests/{ => tests_utils}/test_user_function.py | 0 5 files changed, 146 insertions(+), 35 deletions(-) create mode 100644 tests/tests_utils/test_callbacks.py rename tests/{ => tests_utils}/test_data_utils.py (100%) rename tests/{ => tests_utils}/test_differentialoperators.py (100%) rename tests/{ => tests_utils}/test_user_function.py (100%) diff --git a/src/torchphysics/utils/callbacks.py b/src/torchphysics/utils/callbacks.py index bee78fe8..79a4a62b 100644 --- a/src/torchphysics/utils/callbacks.py +++ b/src/torchphysics/utils/callbacks.py @@ -28,8 +28,16 @@ class WeightSaveCallback(Callback): save_final_model: True Whether the model should always be saved after the last iteration. """ - def __init__(self, model, path, name, check_interval, - save_initial_model=False, save_final_model=True): + + def __init__( + self, + model, + path, + name, + check_interval, + save_initial_model=False, + save_final_model=True, + ): super().__init__() self.model = model self.path = path @@ -38,32 +46,40 @@ def __init__(self, model, path, name, check_interval, self.save_initial_model = save_initial_model self.save_final_model = save_final_model - self.current_loss = float('inf') + self.current_loss = float("inf") def on_train_start(self, trainer, pl_module): if self.save_initial_model: - torch.save(self.model.state_dict(), self.path+'/' + self.name + '_init.pt') - + torch.save( + self.model.state_dict(), self.path + "/" + self.name + "_init.pt" + ) + def on_train_batch_start(self, trainer, pl_module, batch, batch_idx, dataloader_idx): - if (self.check_interval > 0 and batch_idx > 0) and ((batch_idx-1) % self.check_interval == 0): - if trainer.logged_metrics['train/loss'] < self.current_loss: - self.current_loss = trainer.logged_metrics['train/loss'] - torch.save(self.model.state_dict(), - self.path+'/' + self.name + '_min_loss.pt') + if (self.check_interval > 0 and batch_idx > 0) and ( + (batch_idx - 1) % self.check_interval == 0 + ): + if trainer.logged_metrics["train/loss"] < self.current_loss: + self.current_loss = trainer.logged_metrics["train/loss"] + torch.save( + self.model.state_dict(), + self.path + "/" + self.name + "_min_loss.pt", + ) def on_train_end(self, trainer, pl_module): if self.save_final_model: - torch.save(self.model.state_dict(), self.path+'/' + self.name + '_final.pt') + torch.save( + self.model.state_dict(), self.path + "/" + self.name + "_final.pt" + ) class PlotterCallback(Callback): - '''Object for plotting (logging plots) inside of tensorboard. + """Object for plotting (logging plots) inside of tensorboard. Can be passed to the pytorch lightning trainer. Parameters ---------- plot_function : callable - A function that specfices the part of the model that should be plotted. + A function that specfices the part of the model that should be plotted. point_sampler : torchphysics.samplers.PlotSampler A sampler that creates the points that should be used for the plot. log_interval : str, optional @@ -79,12 +95,22 @@ class PlotterCallback(Callback): Additional arguments to specify different parameters/behaviour of the plot. See https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.html for possible arguments of each underlying object. - ''' - def __init__(self, model, plot_function, point_sampler, log_name='plot', - check_interval=200, angle=[30, 30], plot_type='', **kwargs): + """ + + def __init__( + self, + model, + plot_function, + point_sampler, + log_name="plot", + check_interval=200, + angle=[30, 30], + plot_type="", + **kwargs + ): super().__init__() self.model = model - self.check_interval=check_interval + self.check_interval = check_interval self.plot_function = UserFunction(plot_function) self.log_name = log_name self.point_sampler = point_sampler @@ -94,17 +120,21 @@ def __init__(self, model, plot_function, point_sampler, log_name='plot', def on_train_start(self, trainer, pl_module): self.point_sampler.sample_points(device=pl_module.device) - - def on_train_batch_end(self, trainer, pl_module, outputs, batch, - batch_idx, dataloader_idx): + + def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): if batch_idx % self.check_interval == 0: - fig = plot(model=self.model, plot_function=self.plot_function, - point_sampler=self.point_sampler, - angle=self.angle, plot_type=self.plot_type, - device=pl_module.device, **self.kwargs) - pl_module.logger.experiment.add_figure(tag=self.log_name, - figure=fig, - global_step=batch_idx) + fig = plot( + model=self.model, + plot_function=self.plot_function, + point_sampler=self.point_sampler, + angle=self.angle, + plot_type=self.plot_type, + device=pl_module.device, + **self.kwargs + ) + pl_module.logger.experiment.add_figure( + tag=self.log_name, figure=fig, global_step=batch_idx + ) def on_train_end(self, trainer, pl_module): return @@ -112,7 +142,7 @@ def on_train_end(self, trainer, pl_module): class TrainerStateCheckpoint(Callback): """ - A callback to saves the current state of the trainer (a PyTorch Lightning checkpoint), + A callback to save the current state of the trainer (a PyTorch Lightning checkpoint), if the training has to be resumed at a later point in time. Parameters @@ -128,24 +158,25 @@ class TrainerStateCheckpoint(Callback): Note ---- - To continue from the checkpoint, use `resume_from_checkpoint ="path_to_ckpt_file"` as an - argument in the initialization of the trainer. + To continue from the checkpoint, use ckpt_path="some/path/to/my_checkpoint.ckpt" as + argument in the fit command of the trainer. + - The PyTorch Lightning checkpoint would save the current epoch and restart from it. + The PyTorch Lightning checkpoint would save the current epoch and restart from it. In TorchPhysics we dont use multiple epochs, instead we train with multiple iterations inside "one giant epoch". If the training is restarted, the trainer will always start from iteration 0 (essentially the last completed epoch). But all other states (model, optimizer, ...) will be correctly restored. """ - def __init__(self, path, name, check_interval=200, weights_only = False): + def __init__(self, path, name, check_interval=200, weights_only=False): super().__init__() self.path = path self.name = name self.check_interval = check_interval self.weights_only = weights_only - def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): if batch_idx % self.check_interval == 0: - trainer.save_checkpoint(self.path + '/' + self.name + ".ckpt", - weights_only=self.weights_only) + trainer.save_checkpoint( + self.path + "/" + self.name + ".ckpt", weights_only=self.weights_only + ) \ No newline at end of file diff --git a/tests/tests_utils/test_callbacks.py b/tests/tests_utils/test_callbacks.py new file mode 100644 index 00000000..2a60befa --- /dev/null +++ b/tests/tests_utils/test_callbacks.py @@ -0,0 +1,80 @@ +import os +import torch +import pytest +import pytorch_lightning as pl +import torchphysics as tp + + +def helper_setup(): + X = tp.spaces.R1("x") + U = tp.spaces.R1("u") + + x_smapler = tp.samplers.RandomUniformSampler( + tp.domains.Interval(X, 0, 1), 10 + ) + + model = tp.models.FCN(X, U) + + def test_cond(u): + return u + + cond = tp.conditions.PINNCondition(model, x_smapler, test_cond) + + optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.01) + solver = tp.solver.Solver([cond], optimizer_setting=optim) + + return model, solver, x_smapler.domain + + +def helper_cleaner(path_to_file): + try: + os.remove(path_to_file) + except OSError: + raise AssertionError(f"File {path_to_file} does not exist! Callback did not work") + + +def test_weight_save_callback(): + model, solver, _ = helper_setup() + save_callback = tp.WeightSaveCallback(model, "./tests", "test_weight", + 10, + save_final_model=False) + trainer = pl.Trainer( max_steps=2, callbacks=[save_callback]) + trainer.fit(solver) + helper_cleaner("./tests/test_weight_min_loss.pt") + + +def test_weight_save_callback_end(): + model, solver, _ = helper_setup() + save_callback = tp.WeightSaveCallback(model, "./tests", "test_weight", + 10) + trainer = pl.Trainer( max_steps=2, callbacks=[save_callback]) + trainer.fit(solver) + helper_cleaner("./tests/test_weight_min_loss.pt") + helper_cleaner("./tests/test_weight_final.pt") + + +def test_weight_save_callback_start(): + model, solver, _ = helper_setup() + save_callback = tp.WeightSaveCallback(model, "./tests", "test_weight", + 10, True, False) + trainer = pl.Trainer( max_steps=2, callbacks=[save_callback]) + trainer.fit(solver) + helper_cleaner("./tests/test_weight_min_loss.pt") + helper_cleaner("./tests/test_weight_init.pt") + + +def test_plotter_callback(): + model, solver, domain = helper_setup() + plot_sampler = tp.samplers.PlotSampler(domain, 100) + plot_callback = tp.callbacks.PlotterCallback(model, lambda u : u, + plot_sampler) + trainer = pl.Trainer( max_steps=2, callbacks=[plot_callback]) + trainer.fit(solver) + + +def test_state_checkpoint(): + _, solver, _ = helper_setup() + state_callback = tp.callbacks.TrainerStateCheckpoint("./tests", name="state") + trainer = pl.Trainer( max_steps=2, callbacks=[state_callback]) + trainer.fit(solver) + helper_cleaner("./tests/state.ckpt") \ No newline at end of file diff --git a/tests/test_data_utils.py b/tests/tests_utils/test_data_utils.py similarity index 100% rename from tests/test_data_utils.py rename to tests/tests_utils/test_data_utils.py diff --git a/tests/test_differentialoperators.py b/tests/tests_utils/test_differentialoperators.py similarity index 100% rename from tests/test_differentialoperators.py rename to tests/tests_utils/test_differentialoperators.py diff --git a/tests/test_user_function.py b/tests/tests_utils/test_user_function.py similarity index 100% rename from tests/test_user_function.py rename to tests/tests_utils/test_user_function.py From 2dcf4112f2a3ea9cd3505a7828b29fcd5a976c93 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 2 Sep 2024 18:03:40 +0200 Subject: [PATCH 13/25] Update callbacks Signed-off-by: Tom Freudenberg --- src/torchphysics/utils/callbacks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/torchphysics/utils/callbacks.py b/src/torchphysics/utils/callbacks.py index 79a4a62b..e34cb01a 100644 --- a/src/torchphysics/utils/callbacks.py +++ b/src/torchphysics/utils/callbacks.py @@ -54,7 +54,7 @@ def on_train_start(self, trainer, pl_module): self.model.state_dict(), self.path + "/" + self.name + "_init.pt" ) - def on_train_batch_start(self, trainer, pl_module, batch, batch_idx, dataloader_idx): + def on_train_batch_start(self, trainer, pl_module, batch, batch_idx, dataloader_idx=0): if (self.check_interval > 0 and batch_idx > 0) and ( (batch_idx - 1) % self.check_interval == 0 ): @@ -121,7 +121,7 @@ def __init__( def on_train_start(self, trainer, pl_module): self.point_sampler.sample_points(device=pl_module.device) - def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): + def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx=0): if batch_idx % self.check_interval == 0: fig = plot( model=self.model, @@ -175,7 +175,7 @@ def __init__(self, path, name, check_interval=200, weights_only=False): self.check_interval = check_interval self.weights_only = weights_only - def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx): + def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx=0): if batch_idx % self.check_interval == 0: trainer.save_checkpoint( self.path + "/" + self.name + ".ckpt", weights_only=self.weights_only From 110b12983217149d75e21354da3d54f05c4f0ea3 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 2 Sep 2024 18:31:42 +0200 Subject: [PATCH 14/25] fix test Signed-off-by: Tom Freudenberg --- tests/tests_utils/test_callbacks.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/tests_utils/test_callbacks.py b/tests/tests_utils/test_callbacks.py index 2a60befa..2bf07c70 100644 --- a/tests/tests_utils/test_callbacks.py +++ b/tests/tests_utils/test_callbacks.py @@ -1,9 +1,9 @@ import os import torch -import pytest import pytorch_lightning as pl import torchphysics as tp - +from pytorch_lightning import loggers as pl_loggers +import shutil def helper_setup(): X = tp.spaces.R1("x") @@ -26,9 +26,12 @@ def test_cond(u): return model, solver, x_smapler.domain -def helper_cleaner(path_to_file): +def helper_cleaner(path_to_file, delete_dict=False): try: - os.remove(path_to_file) + if delete_dict: + shutil.rmtree(path_to_file) + else: + os.remove(path_to_file) except OSError: raise AssertionError(f"File {path_to_file} does not exist! Callback did not work") @@ -64,12 +67,14 @@ def test_weight_save_callback_start(): def test_plotter_callback(): + tensorboard_logger = pl_loggers.TensorBoardLogger('./tests/logdata') model, solver, domain = helper_setup() plot_sampler = tp.samplers.PlotSampler(domain, 100) plot_callback = tp.callbacks.PlotterCallback(model, lambda u : u, plot_sampler) - trainer = pl.Trainer( max_steps=2, callbacks=[plot_callback]) + trainer = pl.Trainer( max_steps=2, callbacks=[plot_callback], logger=tensorboard_logger) trainer.fit(solver) + helper_cleaner("./tests/logdata", True) def test_state_checkpoint(): From 23a37eefbf997d2d9d487db719a5c6005aadf7f5 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 2 Sep 2024 18:36:59 +0200 Subject: [PATCH 15/25] add tensorboard to test suite Signed-off-by: Tom Freudenberg --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 589acf50..6a255a42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ shapely>=1.7.1 rtree>=0.9.7 scipy>=1.6.3 networkx>=2.5.1 +tensorboard >= 2.4.1 \ No newline at end of file From 1cefbb1d5a015183cf53edf8518985fb63d61776 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Tue, 3 Sep 2024 15:39:43 +0200 Subject: [PATCH 16/25] Remove os line to set GPU in examples Signed-off-by: Tom Freudenberg --- .../oscillator-checkpoint.ipynb | 464 ------------------ examples/deeponet/inverse_ode.ipynb | 2 - examples/deeponet/ode.ipynb | 2 - examples/deeponet/oscillator.ipynb | 2 - examples/deepritz/corner_pde.ipynb | 2 - examples/deepritz/poisson-equation.ipynb | 2 - examples/pinn/exp-function-with-param.ipynb | 4 +- examples/pinn/hard-constrains.ipynb | 2 - examples/pinn/heat-equation.ipynb | 2 - examples/pinn/interface-jump.ipynb | 6 +- .../inverse-heat-equation-D-function.ipynb | 3 - examples/pinn/inverse-heat-equation.ipynb | 2 - examples/pinn/periodic-boundary-problem.ipynb | 4 +- examples/pinn/poisson-with-input-params.ipynb | 6 +- examples/pinn/singular-boundary-problem.ipynb | 2 - .../solid_mechanics/mechanic_cube_FCN.ipynb | 5 +- 16 files changed, 5 insertions(+), 505 deletions(-) delete mode 100644 examples/deeponet/.ipynb_checkpoints/oscillator-checkpoint.ipynb diff --git a/examples/deeponet/.ipynb_checkpoints/oscillator-checkpoint.ipynb b/examples/deeponet/.ipynb_checkpoints/oscillator-checkpoint.ipynb deleted file mode 100644 index 5d242f2c..00000000 --- a/examples/deeponet/.ipynb_checkpoints/oscillator-checkpoint.ipynb +++ /dev/null @@ -1,464 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import torch\n", - "import torchphysics as tp\n", - "import pytorch_lightning as pl" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Training on cuda:1\n", - "GPU available: True\n" - ] - } - ], - "source": [ - "device = torch.device(\"cuda:1\" if torch.cuda.is_available() else \"cpu\")\n", - "print('Training on', device)\n", - "print (\"GPU available: \" + str(torch.cuda.is_available()))" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# torch.backends.cuda.matmul.allow_tf32 = False\n", - "# torch.backends.cudnn.allow_tf32 = False" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# Problem params:\n", - "x0 = [0.0, 0.5] # inital position/speed\n", - "t_end = 10 # sec\n", - "D = 1\n", - "omega_0 = 1\n", - "mu = 0.75\n", - "# Spaces \n", - "T = tp.spaces.R1('t') # input variable\n", - "U = tp.spaces.R1('u') # output variable\n", - "K1 = tp.spaces.R1('k1') # parameter\n", - "K2 = tp.spaces.R1('k2') # parameter\n", - "F = tp.spaces.R1('f') # function output space name\n", - "# Domains\n", - "A_t = tp.domains.Interval(T, 0.0, t_end)\n", - "K_int = tp.domains.Interval(K1, 0, 2)\n", - "K_int2 = tp.domains.Interval(K2, 5, 10)\n", - "#Sampler (for inner sampler add the left boundary, else delta right-hand side can't be learned)\n", - "inner_sampler = tp.samplers.RandomUniformSampler(A_t, n_points = 4000) + tp.samplers.GridSampler(A_t.boundary_left, n_points = 1).make_static()\n", - "initial_u_sampler = tp.samplers.GridSampler(A_t.boundary_left, n_points = 1).make_static()\n", - "initial_v_sampler = tp.samplers.GridSampler(A_t.boundary_left, n_points = 1).make_static()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Defining function set\n", - "Fn_space = tp.spaces.FunctionSpace(A_t, F)\n", - "\n", - "def sine(k1, t):\n", - " return torch.sin(k1*t)\n", - "\n", - "def delta(k2, t):\n", - " return k2 * torch.isclose(t, torch.tensor(0.0))\n", - "\n", - "def wn(k1, t):\n", - " return k1 * torch.randn(t.shape).cuda()\n", - "\n", - "param_sampler = tp.samplers.RandomUniformSampler(K_int, n_points=100)\n", - "param_sampler_delta = tp.samplers.RandomUniformSampler(K_int2, n_points=50)\n", - "Fn_set_1 = tp.domains.CustomFunctionSet(Fn_space, param_sampler, sine)\n", - "Fn_set_2 = tp.domains.CustomFunctionSet(Fn_space, param_sampler_delta, delta)\n", - "Fn_set_3 = tp.domains.CustomFunctionSet(Fn_space, param_sampler, wn)\n", - "Fn_set = Fn_set_1 + Fn_set_2 + Fn_set_3" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - ">" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Fn_space.input_domain.boundary_left.sample_grid" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# custom convolution that should be used for the branch,\n", - "# can also apply some pooling, or something else, if needed.\n", - "# Just the output dimension and input of the linear layers has to fit\n", - "class ConvolutionLayers(torch.nn.Module):\n", - " def __init__(self):\n", - " super().__init__()\n", - " self.conv1 = torch.nn.Conv1d(1, 1, 51, padding=25)\n", - " self.activation = torch.nn.Tanh()\n", - "\n", - " def forward(self, x):\n", - " return self.activation(self.conv1(x))\n", - "\n", - "# custom activation, from old notebook\n", - "class DampedSine(torch.nn.Module):\n", - " def __init__(self):\n", - " super().__init__()\n", - " pass\n", - "\n", - " def forward(self, Tensor):\n", - " alpha = 0.1\n", - " return torch.exp(-alpha * Tensor) * torch.sin(Tensor)\n", - "\n", - "# Model\n", - "dis_sampler = (tp.samplers.GridSampler(A_t.boundary_left, n_points = 1)\n", - " + tp.samplers.GridSampler(A_t, n_points = 800)).make_static()\n", - "trunk_net = tp.models.FCTrunkNet(T, U, hidden=(50, 50), output_neurons=80,\n", - " xavier_gains=[3/5, 3/5, 0.0])\n", - "branch_net = tp.models.ConvBranchNet1D(Fn_space, U, output_neurons=80, \n", - " convolutional_network=ConvolutionLayers(),\n", - " hidden=(600, 500, 250), discretization_sampler=dis_sampler)\n", - "model = tp.models.DeepONet(trunk_net, branch_net)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def u_constrain(u, t):\n", - " return u * t **2 / t_end**2 + x0[1]*t + x0[0]\n", - "\n", - "def ode_residual(u, f, t):\n", - " u_con = u_constrain(u, t)\n", - " u_t = tp.utils.grad(u_con, t)\n", - " lhs = tp.utils.grad(u_t, t) + 2*D*u_t + omega_0**2 * (u_con + mu*u_con**3) \n", - " return lhs - f\n", - "\n", - "ode_cond = tp.conditions.PIDeepONetCondition(deeponet_model=model, \n", - " function_set=Fn_set, \n", - " input_sampler=inner_sampler, \n", - " name='ode_condition',\n", - " residual_fn=ode_residual)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'\\ndef initial_residual(u):\\n return u - x0[0]\\n\\ninitial_cond = tp.conditions.PIDeepONetCondition(deeponet_model=model, \\n function_set=Fn_set, \\n input_sampler=initial_u_sampler, \\n residual_fn=initial_residual, weight=1000)\\n\\ndef initial_speed_residual(u, t):\\n return tp.utils.grad(u, t) - x0[1]\\n\\ninitial_speed_cond = tp.conditions.PIDeepONetCondition(deeponet_model=model, \\n function_set=Fn_set, \\n input_sampler=initial_u_sampler, \\n residual_fn=initial_residual, weight=1000)\\n'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\"\"\"\n", - "def initial_residual(u):\n", - " return u - x0[0]\n", - "\n", - "initial_cond = tp.conditions.PIDeepONetCondition(deeponet_model=model, \n", - " function_set=Fn_set, \n", - " input_sampler=initial_u_sampler, \n", - " residual_fn=initial_residual, weight=1000)\n", - "\n", - "def initial_speed_residual(u, t):\n", - " return tp.utils.grad(u, t) - x0[1]\n", - "\n", - "initial_speed_cond = tp.conditions.PIDeepONetCondition(deeponet_model=model, \n", - " function_set=Fn_set, \n", - " input_sampler=initial_u_sampler, \n", - " residual_fn=initial_residual, weight=1000)\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "GPU available: True, used: True\n", - "TPU available: False, using: 0 TPU cores\n", - "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [MIG-GPU-15d4fb6a-2099-9d5c-d5b5-39d64ae60897/13/0]\n", - "\n", - " | Name | Type | Params\n", - "------------------------------------------------\n", - "0 | train_conditions | ModuleList | 933 K \n", - "1 | val_conditions | ModuleList | 0 \n", - "------------------------------------------------\n", - "933 K Trainable params\n", - "0 Non-trainable params\n", - "933 K Total params\n", - "3.735 Total estimated model params size (MB)\n", - "/home/krd2rng/.conda/envs/pytorch-physics/lib/python3.9/site-packages/pytorch_lightning/utilities/distributed.py:69: UserWarning: The dataloader, train dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 64 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", - " warnings.warn(*args, **kwargs)\n", - "/home/krd2rng/.conda/envs/pytorch-physics/lib/python3.9/site-packages/pytorch_lightning/utilities/distributed.py:69: UserWarning: The dataloader, val dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 64 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", - " warnings.warn(*args, **kwargs)\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "34b26404678f402ebbfc0cd1c0deab4b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Training: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "solver = tp.solver.Solver([ode_cond])\n", - "trainer = pl.Trainer(gpus = 1 if torch.cuda.is_available() else None,\n", - " num_sanity_val_steps=0,\n", - " benchmark=True,\n", - " max_steps=1000,\n", - " logger=False,\n", - " checkpoint_callback=False\n", - " )\n", - "\n", - "trainer.fit(solver)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Validating: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "optim = tp.OptimizerSetting(optimizer_class=torch.optim.LBFGS, lr=0.2, \n", - " optimizer_args={'max_iter':5, 'history_size': 100})\n", - "\n", - "# here now use grid points:\n", - "ode_cond.input_sampler = tp.samplers.GridSampler(A_t, n_points=4000).make_static()\n", - "# also fix parameters for input functions and take some more:\n", - "Fn_set_1.parameter_sampler.n_points = 120\n", - "Fn_set_1.parameter_sampler = Fn_set_1.parameter_sampler.make_static()\n", - "Fn_set_2.parameter_sampler.n_points = 80\n", - "Fn_set_2.parameter_sampler = Fn_set_2.parameter_sampler.make_static()\n", - "Fn_set_3.parameter_sampler.n_points = 60\n", - "Fn_set_3.parameter_sampler = Fn_set_3.parameter_sampler.make_static()\n", - "\n", - "solver = tp.solver.Solver(train_conditions=[ode_cond], optimizer_setting=optim)\n", - "\n", - "trainer = pl.Trainer(gpus=1,\n", - " max_steps=150, \n", - " logger=False,\n", - " benchmark=True,\n", - " checkpoint_callback=False)\n", - " \n", - "trainer.fit(solver)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", - "\n", - "k0 = 1.8\n", - "def f(t):\n", - " a = torch.sin(k0*t)\n", - " return a.unsqueeze(0)\n", - "\n", - "\n", - "model.fix_branch_input(f)\n", - "grid_sampler = tp.samplers.GridSampler(A_t, 2000)\n", - "grid_points = grid_sampler.sample_points()\n", - "out = u_constrain(model(grid_points).as_tensor.detach()[0], grid_points.as_tensor)\n", - "# euler:\n", - "grid_p = grid_points.as_tensor\n", - "dis_f = f(grid_p).squeeze(0)\n", - "delta_t = grid_p[1] - grid_p[0]\n", - "u, v = torch.zeros_like(dis_f), torch.zeros_like(dis_f)\n", - "u[0] = x0[0]\n", - "v[0] = x0[1]\n", - "for i in range(len(u)-1):\n", - " v[i+1] = v[i] + delta_t * (dis_f[i] - 2*D*v[i] - omega_0**2*(u[i] + mu * u[i]**3))\n", - " u[i+1] = u[i] + delta_t * v[i]\n", - "plt.plot(grid_p, out)\n", - "plt.plot(grid_p, u)\n", - "plt.grid()\n", - "plt.legend(['Network output', 'Euler solution'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "k0 = 5.0\n", - "def f(t):\n", - " a = k0 * torch.isclose(t, torch.tensor(0.0))\n", - " return a.unsqueeze(0)\n", - "\n", - "\n", - "model.fix_branch_input(f)\n", - "grid_sampler = tp.samplers.GridSampler(A_t.boundary_left, 1) + tp.samplers.GridSampler(A_t, 2000)\n", - "grid_points = grid_sampler.sample_points()\n", - "out = u_constrain(model(grid_points).as_tensor.detach()[0], grid_points.as_tensor)\n", - "# euler:\n", - "grid_p = grid_points.as_tensor\n", - "dis_f = f(grid_p).squeeze(0)\n", - "delta_t = grid_p[1] - grid_p[0]\n", - "u, v = torch.zeros_like(dis_f), torch.zeros_like(dis_f)\n", - "u[0] = x0[0]\n", - "v[0] = x0[1]\n", - "for i in range(len(u)-1):\n", - " v[i+1] = v[i] + delta_t * (dis_f[i] - 2*D*v[i] - omega_0**2*(u[i] + mu * u[i]**3))\n", - " u[i+1] = u[i] + delta_t * v[i]\n", - "plt.plot(grid_p, out)\n", - "plt.plot(grid_p, u)\n", - "plt.grid()\n", - "plt.legend(['Network output', 'Euler solution'])" - ] - } - ], - "metadata": { - "interpreter": { - "hash": "fb770cb910411e790a99fd848f827dc995ac53be5098d939fbaa56bcec3c9277" - }, - "kernelspec": { - "display_name": "Python [conda env:.conda-pytorch-physics]", - "language": "python", - "name": "conda-env-.conda-pytorch-physics-py" - }, - "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.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/deeponet/inverse_ode.ipynb b/examples/deeponet/inverse_ode.ipynb index 025df2da..3aa01211 100644 --- a/examples/deeponet/inverse_ode.ipynb +++ b/examples/deeponet/inverse_ode.ipynb @@ -28,8 +28,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\"\n", "import torch\n", "import numpy as np\n", "import torchphysics as tp\n", diff --git a/examples/deeponet/ode.ipynb b/examples/deeponet/ode.ipynb index 217392e9..9f9fad6f 100644 --- a/examples/deeponet/ode.ipynb +++ b/examples/deeponet/ode.ipynb @@ -27,8 +27,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\"\n", "import torch\n", "import torchphysics as tp\n", "import pytorch_lightning as pl" diff --git a/examples/deeponet/oscillator.ipynb b/examples/deeponet/oscillator.ipynb index c0687033..cf76ce97 100644 --- a/examples/deeponet/oscillator.ipynb +++ b/examples/deeponet/oscillator.ipynb @@ -6,8 +6,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"3\"\n", "import torchphysics as tp\n", "import torch\n", "import pytorch_lightning as pl" diff --git a/examples/deepritz/corner_pde.ipynb b/examples/deepritz/corner_pde.ipynb index f2197ab3..39f9e112 100644 --- a/examples/deepritz/corner_pde.ipynb +++ b/examples/deepritz/corner_pde.ipynb @@ -22,8 +22,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\"\n", "import torchphysics as tp \n", "import torch\n", "X = tp.spaces.R1('x') \n", diff --git a/examples/deepritz/poisson-equation.ipynb b/examples/deepritz/poisson-equation.ipynb index c1a0da31..be69a9a5 100644 --- a/examples/deepritz/poisson-equation.ipynb +++ b/examples/deepritz/poisson-equation.ipynb @@ -14,8 +14,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"\n", "import torch\n", "import torchphysics as tp\n", "import pytorch_lightning as pl" diff --git a/examples/pinn/exp-function-with-param.ipynb b/examples/pinn/exp-function-with-param.ipynb index 38d09ccb..8c5cc547 100644 --- a/examples/pinn/exp-function-with-param.ipynb +++ b/examples/pinn/exp-function-with-param.ipynb @@ -25,9 +25,7 @@ "source": [ "import torchphysics as tp\n", "import pytorch_lightning as pl\n", - "import torch\n", - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\" # select GPUs to use" + "import torch" ] }, { diff --git a/examples/pinn/hard-constrains.ipynb b/examples/pinn/hard-constrains.ipynb index 117bd874..ea97feb9 100644 --- a/examples/pinn/hard-constrains.ipynb +++ b/examples/pinn/hard-constrains.ipynb @@ -28,8 +28,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\" # select GPUs to use\n", "import torchphysics as tp\n", "import pytorch_lightning as pl\n", "import torch\n", diff --git a/examples/pinn/heat-equation.ipynb b/examples/pinn/heat-equation.ipynb index 1460a41f..94d6a4dc 100644 --- a/examples/pinn/heat-equation.ipynb +++ b/examples/pinn/heat-equation.ipynb @@ -14,8 +14,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"\n", "import torch\n", "import torchphysics as tp\n", "import math" diff --git a/examples/pinn/interface-jump.ipynb b/examples/pinn/interface-jump.ipynb index af74a537..21852cd4 100644 --- a/examples/pinn/interface-jump.ipynb +++ b/examples/pinn/interface-jump.ipynb @@ -30,10 +30,6 @@ "metadata": {}, "outputs": [], "source": [ - "# set GPU:\n", - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\"\n", - "\n", "import torchphysics as tp\n", "import torch" ] @@ -465,7 +461,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.15" }, "orig_nbformat": 4 }, diff --git a/examples/pinn/inverse-heat-equation-D-function.ipynb b/examples/pinn/inverse-heat-equation-D-function.ipynb index 6aba070c..c5cf4455 100644 --- a/examples/pinn/inverse-heat-equation-D-function.ipynb +++ b/examples/pinn/inverse-heat-equation-D-function.ipynb @@ -27,9 +27,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\" # select GPUs to use\n", - "\n", "import torchphysics as tp\n", "import pytorch_lightning as pl\n", "import torch" diff --git a/examples/pinn/inverse-heat-equation.ipynb b/examples/pinn/inverse-heat-equation.ipynb index 3b00fca9..640f71c4 100644 --- a/examples/pinn/inverse-heat-equation.ipynb +++ b/examples/pinn/inverse-heat-equation.ipynb @@ -15,8 +15,6 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\" # select GPUs to use\n", "import torch\n", "import torchphysics as tp\n", "import math" diff --git a/examples/pinn/periodic-boundary-problem.ipynb b/examples/pinn/periodic-boundary-problem.ipynb index f4e0f7b5..ed7a4719 100644 --- a/examples/pinn/periodic-boundary-problem.ipynb +++ b/examples/pinn/periodic-boundary-problem.ipynb @@ -24,9 +24,7 @@ "import os\n", "import pytorch_lightning as pl\n", "import torchphysics as tp\n", - "import numpy as np\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\" # select GPUs to use" + "import numpy as np" ] }, { diff --git a/examples/pinn/poisson-with-input-params.ipynb b/examples/pinn/poisson-with-input-params.ipynb index 608347e6..d5cf49dd 100644 --- a/examples/pinn/poisson-with-input-params.ipynb +++ b/examples/pinn/poisson-with-input-params.ipynb @@ -24,11 +24,7 @@ "source": [ "import torchphysics as tp\n", "import numpy as np\n", - "import torch\n", - "\n", - "# set GPU:\n", - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"2\"" + "import torch" ] }, { diff --git a/examples/pinn/singular-boundary-problem.ipynb b/examples/pinn/singular-boundary-problem.ipynb index 1aebf025..d481b421 100644 --- a/examples/pinn/singular-boundary-problem.ipynb +++ b/examples/pinn/singular-boundary-problem.ipynb @@ -29,8 +29,6 @@ "import os\n", "import pytorch_lightning as pl\n", "import torchphysics as tp\n", - "\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\" # select GPUs to use\n", "torch.cuda.is_available()" ] }, diff --git a/examples/solid_mechanics/mechanic_cube_FCN.ipynb b/examples/solid_mechanics/mechanic_cube_FCN.ipynb index dcd9f162..764f2da8 100644 --- a/examples/solid_mechanics/mechanic_cube_FCN.ipynb +++ b/examples/solid_mechanics/mechanic_cube_FCN.ipynb @@ -10,10 +10,7 @@ "import torchphysics as tp\n", "import pytorch_lightning as pl\n", "import itertools\n", - "import math\n", - "import os\n", - "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"3\"\n", - "%matplotlib inline" + "import math" ] }, { From 207ccecc3ecd560f233bf505e0f54f5b52d940f3 Mon Sep 17 00:00:00 2001 From: JGoedeke Date: Mon, 9 Sep 2024 18:48:19 +0200 Subject: [PATCH 17/25] typo --- examples/tutorial/Tutorial_Simple_ODE.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/tutorial/Tutorial_Simple_ODE.ipynb b/examples/tutorial/Tutorial_Simple_ODE.ipynb index d823d3b2..6cd23050 100644 --- a/examples/tutorial/Tutorial_Simple_ODE.ipynb +++ b/examples/tutorial/Tutorial_Simple_ODE.ipynb @@ -244,8 +244,8 @@ }, "source": [ "### Step 4: Define Neural Network\n", - "At this point, let us define the model $u_\\theta:[0,2]\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", - "There are also a bunch of predefined neural networks or single layers available. In this tutorial we consider a very simple neural network, a FNO consisting of four hidden layers with $80, 50, 50$ and $50$ neurons, respectively.:" + "At this point, let us define the model $u_\\theta:[0,2]\\to \\mathbb{R}$. This task is handled by the TorchPhysics Model class, which is contained in \"tp.models\". It inherits from the torch.nn.Module class from Pytorch, which means that building own models can be achieved in a very similar way, see the [model-tutorial](https://boschresearch.github.io/torchphysics/tutorial/model_creation.html).\n", + "There are also a bunch of predefined neural networks or single layers available. In this tutorial we consider a very simple neural network, a FNO consisting of three hidden layers with $50, 50$ and $50$ neurons, respectively.:" ] }, { From 55dd44a56ba6eabfe5f66afade1f533fab504fdb Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Wed, 25 Sep 2024 15:36:54 +0200 Subject: [PATCH 18/25] Small updates and comments Signed-off-by: Tom Freudenberg --- src/torchphysics/models/FNO.py | 3 + src/torchphysics/models/__init__.py | 2 +- src/torchphysics/models/fcn.py | 57 +++++ .../problem/conditions/__init__.py | 4 +- .../conditions/variational_condition.py | 41 ++++ src/torchphysics/problem/domains/__init__.py | 4 +- .../domains/functionsets/FE_functionset.py | 203 ++++++++++++++++++ .../domains/functionsets/functionset.py | 45 +++- .../functionsets/harmonic_functionset.py | 79 +++++++ 9 files changed, 434 insertions(+), 4 deletions(-) create mode 100644 src/torchphysics/problem/conditions/variational_condition.py create mode 100644 src/torchphysics/problem/domains/functionsets/FE_functionset.py create mode 100644 src/torchphysics/problem/domains/functionsets/harmonic_functionset.py diff --git a/src/torchphysics/models/FNO.py b/src/torchphysics/models/FNO.py index 01f54e4d..b750c364 100644 --- a/src/torchphysics/models/FNO.py +++ b/src/torchphysics/models/FNO.py @@ -1,3 +1,6 @@ +""" +Work in progress! +""" import torch import torch.nn as nn from .model import Model diff --git a/src/torchphysics/models/__init__.py b/src/torchphysics/models/__init__.py index 4ba520e1..1653df32 100644 --- a/src/torchphysics/models/__init__.py +++ b/src/torchphysics/models/__init__.py @@ -13,7 +13,7 @@ from .parameter import Parameter from .model import (Model, NormalizationLayer, AdaptiveWeightLayer, Sequential, Parallel) -from .fcn import FCN, Harmonic_FCN +from .fcn import FCN, Harmonic_FCN, Polynomial_FCN from .deepritz import DeepRitzNet from .qres import QRES from .activation_fn import (AdaptiveActivationFunction, ReLUn, Sinus) diff --git a/src/torchphysics/models/fcn.py b/src/torchphysics/models/fcn.py index c9f2f207..fde8e760 100644 --- a/src/torchphysics/models/fcn.py +++ b/src/torchphysics/models/fcn.py @@ -137,3 +137,60 @@ def forward(self, points): points_list.append(torch.sin((i+1) * math.pi * points)) points = torch.cat(points_list, dim=-1) return Points(self.sequential(points), self.output_space) + + + +class Polynomial_FCN(Model): + """ + + """ + def __init__(self, input_space, output_space, polynomial_degree=1, + hidden=(20,20,20), activation=nn.Tanh(), xavier_gains=5/3, + res_connection=False): + super().__init__(input_space, output_space) + self._construct_polynom_layers(hidden, polynomial_degree, xavier_gains) + self.activation_fn = activation + self.polynomial_degree = polynomial_degree + self.res_con = res_connection + + + def forward(self, points): + points = self._fix_points_order(points).as_tensor + batch_dim = len(points) + for i in range(len(self.layers)): + points = points.unsqueeze(-1) + out = self.layers[i][:, :, self.polynomial_degree-1] / self.polynomial_degree + out = out.unsqueeze(0).expand((batch_dim, self.layers[i].shape[0], self.layers[i].shape[1])) + + for j in range(self.polynomial_degree, 0, -1): + reshaped_layer = self.layers[i][:, :, j-1].unsqueeze(0).expand( + (batch_dim, self.layers[i].shape[0], self.layers[i].shape[1])) + out = (points * out + reshaped_layer / (max(1, j-1))) + + + out = torch.sum(out, dim=1) + if self.res_con and i < len(self.layers) - 1 and i > 0: + points = self.activation_fn(out) + points.squeeze(-1) + else: + points = self.activation_fn(out) + + return Points(out, self.output_space) + + def _construct_polynom_layers(self, hidden, polynomial_degree, xavier_gains): + if not isinstance(xavier_gains, (list, tuple)): + xavier_gains = len(hidden) * [xavier_gains] + + self.layers = torch.nn.ParameterList() + self._build_one_layer(self.input_space.dim, hidden[0], + polynomial_degree, xavier_gains[0]) + for i in range(len(hidden)-1): + self._build_one_layer(hidden[i], hidden[i+1], + polynomial_degree, xavier_gains[0]) + self._build_one_layer(hidden[-1], self.output_space.dim, + polynomial_degree, xavier_gains[0]) + + + def _build_one_layer(self, input_dim, output_dim, polynomial_degree, xavier_gain): + in_layer = torch.empty((input_dim, output_dim, polynomial_degree)) + self.layers.append(nn.Parameter(in_layer)) + torch.nn.init.xavier_normal_(self.layers[-1], gain=xavier_gain) diff --git a/src/torchphysics/problem/conditions/__init__.py b/src/torchphysics/problem/conditions/__init__.py index 09a3a4c7..aa3a5088 100644 --- a/src/torchphysics/problem/conditions/__init__.py +++ b/src/torchphysics/problem/conditions/__init__.py @@ -20,4 +20,6 @@ from .deeponet_condition import (DeepONetSingleModuleCondition, PIDeepONetCondition, - DeepONetDataCondition) \ No newline at end of file + DeepONetDataCondition) + +from .variational_condition import VariationalPINNCondition \ No newline at end of file diff --git a/src/torchphysics/problem/conditions/variational_condition.py b/src/torchphysics/problem/conditions/variational_condition.py new file mode 100644 index 00000000..dc0e6a46 --- /dev/null +++ b/src/torchphysics/problem/conditions/variational_condition.py @@ -0,0 +1,41 @@ +import torch +from .condition import SingleModuleCondition, SquaredError +from ...models import Parameter + + +class VariationalPINNCondition(SingleModuleCondition): + + def __init__(self, module, residual_fn, sampler, test_fn_set, track_gradients=True, + data_functions={}, parameter=Parameter.empty(), name='pinncondition', + weight=1.0): + super().__init__(module, sampler, residual_fn, error_fn=SquaredError(), + reduce_fn=torch.mean, name=name, track_gradients=track_gradients, + data_functions=data_functions, parameter=parameter, weight=weight) + self.test_fn_set = test_fn_set + + + def _move_static_data(self, device): + super()._move_static_data(device) + self.test_fn_set.to(device) + + + def forward(self, device='cpu', iteration=None): + x = self.sampler.sample_points(device=device) + x_coordinates, x = x.track_coord_gradients() + + y = self.module(x) + + data = {} + for fun in self.data_functions: + data[fun] = self.data_functions[fun](x_coordinates) + + test_fn = self.test_fn_set(x_coordinates) + + test_space_parameters = {"quad_weights": self.test_fn_set.get_quad_weights()} + + unreduced_loss = self.error_fn(self.residual_fn({**y.coordinates, + **x_coordinates, + **test_space_parameters, + **test_fn.coordinates, + **data})) + return self.reduce_fn(unreduced_loss) \ No newline at end of file diff --git a/src/torchphysics/problem/domains/__init__.py b/src/torchphysics/problem/domains/__init__.py index 62a42377..547fc6d6 100644 --- a/src/torchphysics/problem/domains/__init__.py +++ b/src/torchphysics/problem/domains/__init__.py @@ -33,7 +33,9 @@ from .domain3D.sphere import Sphere #from .domain3D.trimesh_polyhedron import TrimeshPolyhedron # Function domains: -from .functionsets.functionset import FunctionSet, CustomFunctionSet +from .functionsets.functionset import FunctionSet, CustomFunctionSet, TestFunctionSet +from .functionsets.FE_functionset import FEFunctionSet +from .functionsets.harmonic_functionset import HarmonicFunctionSet1D # Domain transforms: from .domainoperations.translate import Translate from .domainoperations.rotate import Rotate \ No newline at end of file diff --git a/src/torchphysics/problem/domains/functionsets/FE_functionset.py b/src/torchphysics/problem/domains/functionsets/FE_functionset.py new file mode 100644 index 00000000..c9e2a710 --- /dev/null +++ b/src/torchphysics/problem/domains/functionsets/FE_functionset.py @@ -0,0 +1,203 @@ +import torch + +from ...spaces.points import Points +from .functionset import TestFunctionSet + +class FEFunctionSet(TestFunctionSet): + + def __init__(self, function_space, order, mesh_vertices, mesh_triangles=None): + super().__init__(function_space=function_space) + if len(mesh_vertices.shape) == 1: + mesh_vertices = mesh_vertices.unsqueeze(-1) + dim = len(mesh_vertices[0]) + if dim == 1 and order == 1: + self.finite_elements = LinearFE1D(mesh_vertices) + if dim == 2 and order == 1: + self.finite_elements = LinearFE2D(mesh_vertices, mesh_triangles) + else: + AssertionError(f"FE Space not implemented for dimension {dim} and order {order}.") + + + def __call__(self, x=None): + if self.finite_elements.quadrature_mode_on: + helper_out = self.eval_fn_helper.apply(x[self.function_space.input_space.variables.pop()], + self.finite_elements.basis_at_quadrature, + self.finite_elements.grad_at_quadrature) + + return Points(helper_out, self.function_space.output_space) + else: + Points(self.finite_elements(x), self.function_space.output_space) + + + def get_quad_weights(self): + return self.finite_elements.quadrature_weights_per_dof + + + def get_quadrature_points(self): + return Points(self.finite_elements.quadrature_points_per_dof, self.function_space.input_space) + + + def to(self, device): + self.finite_elements.to(device) + + + def switch_quadrature_mode_on(self, set_on : bool): + self.finite_elements.switch_quadrature_mode_on(set_on) + + +class LinearFE2D(): + + def __init__(self, mesh_vertices : torch.tensor, mesh_triangles : torch.tensor): + self.mesh_vertices = mesh_vertices + self.mesh_triangles = mesh_triangles + self.quadrature_mode_on = True + + self.dim = 2 + self.basis_dim = len(self.mesh_vertices) + + center_points, triangle_volume = self.compute_center_and_volume() + ## Next find which vertex belongs to which triangle, so we can find + ## where each basis function is not zero and construct the corresponding + ## quadrature: + vertex_to_triangle_map_h = [[] for _ in range(self.basis_dim)] + for i in range(len(self.mesh_triangles)): + for k in self.mesh_triangles[i]: + vertex_to_triangle_map_h[k].append(i) + + # Pad the mapping with -1 to save as one big tensor: + max_triangles_per_vertex = max(len(vertex_to_triangle_map_h[i]) for i in range(self.basis_dim)) + self.vertex_to_triangle_map = -1 * torch.ones((self.basis_dim, + max_triangles_per_vertex), dtype=torch.long) + for i in range(self.basis_dim): + self.vertex_to_triangle_map[i, :len(vertex_to_triangle_map_h[i])] = \ + torch.tensor(vertex_to_triangle_map_h[i]) + + + # We need basis with compact support -> no functions at the boundary: + self.find_boundary_dofs() + use_index = torch.ones(self.basis_dim, dtype=bool) + use_index[self.boundary_dofs] = False + self.quadrature_weights_per_dof = triangle_volume[self.vertex_to_triangle_map[use_index]] + self.quadrature_weights_per_dof *= (self.vertex_to_triangle_map[use_index] >= 0) + self.quadrature_weights_per_dof = self.quadrature_weights_per_dof.unsqueeze(-1) + self.quadrature_points_per_dof = center_points[self.vertex_to_triangle_map[use_index]] + self.basis_at_quadrature = torch.tensor([1.0/3.0], requires_grad=True) + + self.compute_grad_per_dof(triangle_volume, use_index, max_triangles_per_vertex) + + def switch_quadrature_mode_on(self, set_on : bool): + self.quadrature_mode_on = set_on + if not set_on: + AssertionError("Arbritrary evaluation not implemented!") + + def to(self, device): + self.quadrature_points_per_dof = self.quadrature_points_per_dof.to(device) + self.quadrature_weights_per_dof = self.quadrature_weights_per_dof.to(device) + self.basis_at_quadrature = self.basis_at_quadrature.to(device) + self.grad_at_quadrature = self.grad_at_quadrature.to(device) + + + def compute_center_and_volume(self): + ## Find center of each triangle + center_points = torch.zeros((len(self.mesh_triangles), self.dim)) + for i in range(self.dim+1): + center_points += self.mesh_vertices[self.mesh_triangles[:, i]] + center_points /= (self.dim+1) + + ## Compute volume of each triangle + vec_1 = self.mesh_vertices[self.mesh_triangles[:, 1]] \ + - self.mesh_vertices[self.mesh_triangles[:, 0]] + vec_2 = self.mesh_vertices[self.mesh_triangles[:, 2]] \ + - self.mesh_vertices[self.mesh_triangles[:, 0]] + triangle_volume = 0.5 *(vec_1[:, 0]*vec_2[:, 1] - vec_1[:, 1]*vec_2[:, 0]) + self.triangle_rot = torch.sign(triangle_volume) + triangle_volume *= self.triangle_rot + return center_points, triangle_volume + + + def find_boundary_dofs(self): + ## Not the most efficient way... + boundary_edge_list = torch.zeros((self.basis_dim, self.basis_dim), dtype=torch.int8) + for triangle in self.mesh_triangles: + v = triangle.sort()[0] + boundary_edge_list[v[0], v[1]] += 1 + boundary_edge_list[v[0], v[2]] += 1 + boundary_edge_list[v[1], v[2]] += 1 + self.boundary_dofs = torch.unique(torch.cat(torch.where(boundary_edge_list == 1))) + + + def compute_grad_per_dof(self, triangle_volume, inner_dofs, max_triangles_per_vertex): + inner_idx = torch.where(inner_dofs)[0] + self.grad_at_quadrature = torch.zeros((len(inner_idx), max_triangles_per_vertex, 2)) + for c, i in enumerate(inner_idx): + for j, k in enumerate(self.vertex_to_triangle_map[i]): + if k == -1: # -1 mapping means not further triangles are neighbours + break + remove_i = (self.mesh_triangles[k] != i) + neighbour_dofs = self.mesh_triangles[k][remove_i] + edge_vec = self.mesh_vertices[neighbour_dofs[1]] - self.mesh_vertices[neighbour_dofs[0]] + if not remove_i[1]: + # if we removed the second vertex from the triangle, we need to multiple + # by -1 to fix the direction of the edge + edge_vec *= -1 + edge_vec = torch.tensor([-edge_vec[1], edge_vec[0]]) # rotate + edge_vec /= (2*triangle_volume[k]) + self.grad_at_quadrature[c, j, :] = self.triangle_rot[k] * edge_vec + + def __call__(self, x): + pass + + +class LinearFE1D(): + + def __init__(self, mesh_vertices : torch.tensor): + self.mesh_vertices, _ = torch.sort(mesh_vertices) + self.quadrature_mode_on = True # if evaluation only happens at all quadrature points + + self.dim = 1 + self.basis_dim = len(self.mesh_vertices) + + center_points, interval_length = self.compute_center_and_volume() + + + # We need basis with compact support -> no functions at the boundary: + self.quadrature_weights_per_dof = torch.column_stack((interval_length[:-1], interval_length[1:])) + self.quadrature_weights_per_dof = self.quadrature_weights_per_dof.unsqueeze(-1) + + self.quadrature_points_per_dof = torch.column_stack((center_points[:-1], center_points[1:])) + self.quadrature_points_per_dof = self.quadrature_points_per_dof.unsqueeze(-1) + self.basis_at_quadrature = torch.tensor([1.0/2.0]) + + self.compute_grad_per_dof(interval_length) + + + def switch_quadrature_mode_on(self, set_on : bool): + self.quadrature_mode_on = set_on + if not set_on: + AssertionError("Arbritrary evaluation not implemented!") + + + def to(self, device): + self.quadrature_points_per_dof = self.quadrature_points_per_dof.to(device) + self.quadrature_weights_per_dof = self.quadrature_weights_per_dof.to(device) + self.basis_at_quadrature = self.basis_at_quadrature.to(device) + self.grad_at_quadrature = self.grad_at_quadrature.to(device) + + + def compute_center_and_volume(self): + ## Find center of each triangle + center_points = self.mesh_vertices[:-1] + self.mesh_vertices[1:] + center_points /= 2.0 + + interval_length = self.mesh_vertices[:-1] - self.mesh_vertices[1:] + return center_points, interval_length + + + def compute_grad_per_dof(self, interval_length): + self.grad_at_quadrature = torch.zeros((self.basis_dim - 2, 2, 1)) + self.grad_at_quadrature[:, :1, 0] = 1.0/interval_length[:-1] + self.grad_at_quadrature[:, 1:, 0] = -1.0/interval_length[:-1] + + + def __call__(self, x): + pass \ No newline at end of file diff --git a/src/torchphysics/problem/domains/functionsets/functionset.py b/src/torchphysics/problem/domains/functionsets/functionset.py index ca629648..a674f48b 100644 --- a/src/torchphysics/problem/domains/functionsets/functionset.py +++ b/src/torchphysics/problem/domains/functionsets/functionset.py @@ -179,4 +179,47 @@ def __init__(self, function_space, parameter_sampler, custom_fn): self.custom_fn = custom_fn def _evaluate_function(self, param_point_meshgrid): - return self.custom_fn(param_point_meshgrid) \ No newline at end of file + return self.custom_fn(param_point_meshgrid) + + + +class TestFunctionHelper(torch.autograd.Function): + + @staticmethod + def forward(ctx, x, expected_out, grad_out): + ctx.save_for_backward(grad_out) + x_ten = torch.sum(x, dim=-1, keepdim=True) + return expected_out + 0.0 * x_ten# <- hack to build graph to allow for precomputed gradient + + @staticmethod + def backward(ctx, grad_output): + grad_out, = ctx.saved_tensors + return grad_out * grad_output, None, None + + +class TestFunctionSet(FunctionSet): + + def __init__(self, function_space): + super().__init__(function_space=function_space, parameter_sampler=None) + self.eval_fn_helper = TestFunctionHelper() + self.quadrature_mode_on = True + + @abc.abstractmethod + def switch_quadrature_mode_on(self, set_on : bool): + raise NotImplementedError + + @abc.abstractmethod + def __call__(self, x): + raise NotImplementedError + + @abc.abstractmethod + def to(self, device): + raise NotImplementedError + + @abc.abstractmethod + def get_quad_weights(self): + raise NotImplementedError + + @abc.abstractmethod + def get_quadrature_points(self): + raise NotImplementedError \ No newline at end of file diff --git a/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py b/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py new file mode 100644 index 00000000..b9c4e0b9 --- /dev/null +++ b/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py @@ -0,0 +1,79 @@ +import torch +import math + +from ...spaces.points import Points +from .functionset import TestFunctionSet +from ..domain1D import Interval + + +class HarmonicFunctionSet1D(TestFunctionSet): + + def __init__(self, function_space, interval : Interval, frequence, + samples_per_max_frequence : int = 5): + super().__init__(function_space=function_space) + self.interval = interval + self.samples_max = samples_per_max_frequence + + if isinstance(frequence, list): + self.basis_dim = max(frequence) + self.frequence_list = frequence + else: + self.basis_dim = frequence + self.frequence_list = torch.arange(1, frequence+1, 1) + + quad_points = torch.linspace(self.interval.lower_bound(), self.interval.upper_bound(), + self.basis_dim * self.samples_max + 2)[1:-1] + self.quadrature_points_per_dof = quad_points.repeat((self.basis_dim, 1)).unsqueeze(-1) + self.quadrature_weights_per_dof = quad_points[1] - quad_points[0] + + self.compute_basis_at_quadrature_points() + + + def switch_quadrature_mode_on(self, set_on : bool): + self.quadrature_mode_on = set_on + if not set_on: + AssertionError("Arbritrary evaluation not implemented!") + + + def to(self, device): + self.quadrature_points_per_dof = self.quadrature_points_per_dof.to(device) + self.quadrature_weigths_per_dof = self.quadrature_weights_per_dof.to(device) + self.basis_at_quadrature = self.basis_at_quadrature.to(device) + self.grad_at_quadrature = self.grad_at_quadrature.to(device) + + + def compute_basis_at_quadrature_points(self): + self.basis_at_quadrature = torch.zeros_like(self.quadrature_points_per_dof) + self.grad_at_quadrature = torch.zeros_like(self.quadrature_points_per_dof) + + int_size = self.interval.upper_bound() - self.interval.lower_bound() + for i, n in enumerate(self.frequence_list): + self.basis_at_quadrature[i] = \ + torch.sin(n*math.pi/(int_size) * \ + (self.quadrature_points_per_dof[i] - self.interval.lower_bound())) + self.grad_at_quadrature[i] = -n*math.pi/(int_size) * \ + torch.cos(n*math.pi/(int_size) * \ + (self.quadrature_points_per_dof[i] - self.interval.lower_bound())) + + + def __call__(self, x=None): + if self.quadrature_mode_on: + input_variable_name = self.function_space.input_space.variables.pop() + return Points(self.eval_fn_helper.apply(x[input_variable_name], + self.basis_at_quadrature, + self.grad_at_quadrature), + self.function_space.output_space) + else: + raise NotImplementedError + + + def grad(self, x=None): + if self.quadrature_mode_on or x == None: + return self.grad_at_quadrature + + + def get_quad_weights(self): + return self.quadrature_weights_per_dof + + def get_quadrature_points(self): + return Points(self.quadrature_points_per_dof, self.function_space.input_space) \ No newline at end of file From 69098669f8f6308deb05ba6715f2df78732a5072 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Tue, 12 Nov 2024 10:49:26 +0100 Subject: [PATCH 19/25] uniform title size in examples Signed-off-by: Tom Freudenberg --- examples/deeponet/inverse_ode.ipynb | 2 +- examples/deeponet/ode.ipynb | 2 +- examples/pinn/exp-function-with-param.ipynb | 3 +-- examples/pinn/hard-constrains.ipynb | 2 +- examples/pinn/heat-equation.ipynb | 2 +- examples/pinn/interface-jump.ipynb | 2 +- examples/pinn/inverse-heat-equation-D-function.ipynb | 3 +-- examples/pinn/inverse-heat-equation.ipynb | 2 +- examples/pinn/moving-heat-equation.ipynb | 3 +-- examples/pinn/periodic-boundary-problem.ipynb | 3 +-- examples/pinn/poisson-equation.ipynb | 2 +- examples/pinn/poisson-with-input-params.ipynb | 2 +- examples/pinn/signorini-equation.ipynb | 3 +-- examples/pinn/singular-boundary-problem.ipynb | 3 +-- 14 files changed, 14 insertions(+), 20 deletions(-) diff --git a/examples/deeponet/inverse_ode.ipynb b/examples/deeponet/inverse_ode.ipynb index 3aa01211..3d877c17 100644 --- a/examples/deeponet/inverse_ode.ipynb +++ b/examples/deeponet/inverse_ode.ipynb @@ -5,7 +5,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Inverse DeepONet\n", + "### Inverse DeepONet\n", "In this notebook, we present the learning of the inverse operator, that maps solution data to some input data. \n", "To keep things simple we again consider the ODE:\n", "\\begin{align*}\n", diff --git a/examples/deeponet/ode.ipynb b/examples/deeponet/ode.ipynb index 9f9fad6f..c983f464 100644 --- a/examples/deeponet/ode.ipynb +++ b/examples/deeponet/ode.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Physics-informed DeepONet: Solving a ODE for different right hand sides\n", + "### Physics-informed DeepONet: Solving a ODE for different right hand sides\n", "In this notebook, we present an introduction to the physics-informed DeepONet [(paper)](https://arxiv.org/abs/2103.10974) utilities of TorchPhysics. \n", "As an example, we try to learn the integral operator of the ODE:\n", "\\begin{align*}\n", diff --git a/examples/pinn/exp-function-with-param.ipynb b/examples/pinn/exp-function-with-param.ipynb index 8c5cc547..1110c404 100644 --- a/examples/pinn/exp-function-with-param.ipynb +++ b/examples/pinn/exp-function-with-param.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Exponential growth with variable rates\n", - "======================================\n", + "### Exponential growth with variable rates\n", "Here we want to train a parameter dependency of the PDE. To this end we consider:\n", "\n", "\\begin{align*}\n", diff --git a/examples/pinn/hard-constrains.ipynb b/examples/pinn/hard-constrains.ipynb index ea97feb9..b1c95d4e 100644 --- a/examples/pinn/hard-constrains.ipynb +++ b/examples/pinn/hard-constrains.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Applying hard constraints \n", + "### Applying hard constraints \n", "\n", "For some problems, it is advantageous to apply prior knowledge about the solution in the network architecture, instead of the training process via additional loss terms. For example, in simple domains a Dirichlet boundary condition could be added to the network output with a corresponding characteristic function. Since the boundary condition is then naturally fulfilled, one has to consider fewer terms in the final loss and the optimization may become easier.\n", "\n", diff --git a/examples/pinn/heat-equation.ipynb b/examples/pinn/heat-equation.ipynb index 94d6a4dc..ad130cae 100644 --- a/examples/pinn/heat-equation.ipynb +++ b/examples/pinn/heat-equation.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# PINN: Heat equation with variable diffusion\n", + "### PINN: Heat equation with variable diffusion\n", "Solving the heat equation in 2D for variable diffusion D using the PINN-concept." ] }, diff --git a/examples/pinn/interface-jump.ipynb b/examples/pinn/interface-jump.ipynb index 21852cd4..1ee700e4 100644 --- a/examples/pinn/interface-jump.ipynb +++ b/examples/pinn/interface-jump.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## PDE with a jump at a interface\n", + "### PDE with a jump at a interface\n", "\n", "In this Notebook we want to solve a simple Poisson Problem with a Robin-Condition at a interface. \n", "\n", diff --git a/examples/pinn/inverse-heat-equation-D-function.ipynb b/examples/pinn/inverse-heat-equation-D-function.ipynb index c5cf4455..ee4839a4 100644 --- a/examples/pinn/inverse-heat-equation-D-function.ipynb +++ b/examples/pinn/inverse-heat-equation-D-function.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Inverse heat equation with space dependent diffusion $D$\n", - "========================================================\n", + "### Inverse heat equation with space dependent diffusion $D$\n", "In this example we want to solve a inverse problem where the parameter we are looking for is also a function.\n", "We consider, $\\Omega = [0, 10] \\times [0, 10]$ and:\n", "\\begin{align*}\n", diff --git a/examples/pinn/inverse-heat-equation.ipynb b/examples/pinn/inverse-heat-equation.ipynb index 640f71c4..28823b79 100644 --- a/examples/pinn/inverse-heat-equation.ipynb +++ b/examples/pinn/inverse-heat-equation.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Solving a inverse heat equation\n", + "### Solving a inverse heat equation\n", "\n", "We want to solve a inverse 2D heat equation and find the heat diffusion coefficent $D$." ] diff --git a/examples/pinn/moving-heat-equation.ipynb b/examples/pinn/moving-heat-equation.ipynb index df3a5af2..0af80955 100644 --- a/examples/pinn/moving-heat-equation.ipynb +++ b/examples/pinn/moving-heat-equation.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Heat equation on moving Domain\n", - "==============================\n", + "### Heat equation on moving Domain\n", "In this example we show how to solve a PDE on a changing domain. We consider a simple heat equation of the form\n", "\\begin{align*}\n", " \\partial_t u - D\\Delta u &= 0 \\text{ in } \\Omega \\times [0, T] \\\\\n", diff --git a/examples/pinn/periodic-boundary-problem.ipynb b/examples/pinn/periodic-boundary-problem.ipynb index ed7a4719..7fabb7cb 100644 --- a/examples/pinn/periodic-boundary-problem.ipynb +++ b/examples/pinn/periodic-boundary-problem.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Periodic Boundary Problem\n", - "=========================\n", + "### Periodic Boundary Problem\n", "Here we want to solve a problem with a periodic boundary condition. We consider:\n", "\\begin{align*}\n", " - \\Delta u(x, y) &= f(x, y), \\text{ for } (x, y) \\in [0, 1] \\times [0, 1]\\\\\n", diff --git a/examples/pinn/poisson-equation.ipynb b/examples/pinn/poisson-equation.ipynb index 5edc2b01..60498174 100644 --- a/examples/pinn/poisson-equation.ipynb +++ b/examples/pinn/poisson-equation.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# PINN: Poisson\n", + "### PINN: Poisson\n", "Solving the poisson equation in 2D using the PINN-concept." ] }, diff --git a/examples/pinn/poisson-with-input-params.ipynb b/examples/pinn/poisson-with-input-params.ipynb index d5cf49dd..7b259b48 100644 --- a/examples/pinn/poisson-with-input-params.ipynb +++ b/examples/pinn/poisson-with-input-params.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Complexer Poisson Example\n", + "### Complexer Poisson Example\n", "In this example a poisson equation, with local forces on a complex domain will be solved.\n", "The equation is:\n", "\\begin{align*}\n", diff --git a/examples/pinn/signorini-equation.ipynb b/examples/pinn/signorini-equation.ipynb index c0818315..81782d4d 100644 --- a/examples/pinn/signorini-equation.ipynb +++ b/examples/pinn/signorini-equation.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Simple Signorini problem with variable forces\n", - "=============================================\n", + "### Simple Signorini problem with variable forces\n", "In this example we a solve a obstacle PDE. The special point in this example are the inequalyties in the corresponding differentialequation. On the unit square $\\Omega = (0, 1) \\times (0, 1)$, we consider the problem:\n", "\\begin{align*}\n", " -\\Delta u &= f \\text{ in } \\Omega \\\\\n", diff --git a/examples/pinn/singular-boundary-problem.ipynb b/examples/pinn/singular-boundary-problem.ipynb index d481b421..5996ac4d 100644 --- a/examples/pinn/singular-boundary-problem.ipynb +++ b/examples/pinn/singular-boundary-problem.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Boundary Value Problem\n", - "======================" + "### Boundary Value Problem" ] }, { From 2fc74642e3ae998ec973cb05d8efcb57ba290b79 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Tue, 12 Nov 2024 14:17:03 +0100 Subject: [PATCH 20/25] First FNO implementation Signed-off-by: Tom Freudenberg --- examples/fno/integrator(+batchnorm).ipynb | 380 ++++++++++++++++++ examples/fno/integrator.ipynb | 376 +++++++++++++++++ src/torchphysics/models/FNO.py | 244 ++++++++--- src/torchphysics/models/__init__.py | 5 +- .../conditions/variational_condition.py | 2 +- .../domains/functionsets/FE_functionset.py | 5 +- .../domains/functionsets/functionset.py | 7 +- .../functionsets/harmonic_functionset.py | 6 +- .../problem/samplers/data_samplers.py | 35 +- src/torchphysics/utils/data/dataloader.py | 8 +- tests/tests_models/test_fno.py | 96 +++++ 11 files changed, 1094 insertions(+), 70 deletions(-) create mode 100644 examples/fno/integrator(+batchnorm).ipynb create mode 100644 examples/fno/integrator.ipynb create mode 100644 tests/tests_models/test_fno.py diff --git a/examples/fno/integrator(+batchnorm).ipynb b/examples/fno/integrator(+batchnorm).ipynb new file mode 100644 index 00000000..4e43b64e --- /dev/null +++ b/examples/fno/integrator(+batchnorm).ipynb @@ -0,0 +1,380 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fourier Neural Operator (FNO): Solving a ODE for different right hand sides\n", + "In this notebook, we present the same problem as in [this notebook](https://arxiv.org/abs/2010.08895), but also add a batch normalization in space which leads to smoother learned solutions. \n", + "As an example, we try to learn the integral operator of the ODE:\n", + "\\begin{align*}\n", + " \\partial_t u(t) &= f(t), \\text{ in } [0, 1] \\\\\n", + " u(0) &= 0\n", + "\\end{align*}\n", + "for different functions $f$. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch \n", + "import torchphysics as tp\n", + "import pytorch_lightning as pl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First create the data set we need for training the network.\n", + "Here we randomly create some oscillating functions and use an explicit euler to compute our training data." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "N_batch = 5000 # Data in trainig set\n", + "N_time = 100 # resolution of time (or space) interval\n", + "dt = 1/N_time\n", + "\n", + "input_data = torch.zeros((N_batch, N_time, 1))\n", + "random_data = torch.zeros((N_batch, 3))\n", + "random_data[:, :1] = torch.randint(0, 24, (N_batch, 1))\n", + "random_data[:, 1:2] = torch.randint(0, 12, (N_batch, 1))\n", + "random_data[:, 2:] = torch.randint(0, 6, (N_batch, 1))\n", + "\n", + "output_data = torch.zeros((N_batch, N_time, 1))\n", + "\n", + "t = 0.0\n", + "input_data[:, 0, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + "for i in range(1, N_time):\n", + " t += dt\n", + " input_data[:, i, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + " output_data[:, i, 0] = output_data[:, i-1, 0] + dt * input_data[:, i, 0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In TorchPhysics we have to define the input and output space like always:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "F = tp.spaces.R1(\"f\")\n", + "U = tp.spaces.R1(\"u\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we create the network that learns the mapping:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "hidden_channels = 8\n", + "\n", + "model = tp.models.FNO(F, U, \n", + " fourier_layers=3, \n", + " hidden_channels=hidden_channels, \n", + " fourier_modes=8, \n", + " skip_connections=True, \n", + " channel_down_sample_network=torch.nn.Sequential(\n", + " torch.nn.Linear(hidden_channels, hidden_channels),\n", + " torch.nn.Tanh(),\n", + " torch.nn.Linear(hidden_channels, U.dim)\n", + " ),\n", + " space_resolution=100) # by setting the resolution of the space, we enable batch norm computations.\n", + "# Note: This (currently) disables the super resolution property of the FNO, so the input always needs to be\n", + "# on the same grid " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we create a data condition to fit the FNO to the data." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Transform data to points\n", + "input_data_points = tp.spaces.Points(input_data, F)\n", + "output_data_points = tp.spaces.Points(output_data, U)\n", + "\n", + "dataloader = tp.utils.PointsDataLoader((input_data_points, output_data_points),\n", + " batch_size=N_batch)\n", + "\n", + "data_condition = tp.conditions.DataCondition(model, dataloader, norm=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start training:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "You are using a CUDA device ('GeForce RTX 3090') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n", + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 1.1 K \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "1.1 K Trainable params\n", + "0 Non-trainable params\n", + "1.1 K Total params\n", + "0.004 Total estimated model params size (MB)\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'val_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 5000/5000 [00:53<00:00, 94.12it/s, train/loss=1.28e-5] " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "`Trainer.fit` stopped: `max_steps=5000` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 5000/5000 [00:53<00:00, 94.11it/s, train/loss=1.28e-5]\n" + ] + } + ], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.01)\n", + "solver = tp.solver.Solver([data_condition], optimizer_setting=optim)\n", + "\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\",\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=5000, \n", + " logger=False, \n", + " enable_checkpointing=False)\n", + "\n", + "trainer.fit(solver)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Decrease learning rate and fine tune some more:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n", + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 1.1 K \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "1.1 K Trainable params\n", + "0 Non-trainable params\n", + "1.1 K Total params\n", + "0.004 Total estimated model params size (MB)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 0%| | 0/8000 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "example_plot = 78\n", + "t = torch.linspace(0, 1, N_time)\n", + "plt.plot(t, model_output[example_plot, :, 0].detach().cpu())\n", + "plt.plot(t, output_data_test[example_plot, :, 0].detach().cpu())\n", + "plt.legend([\"FNO Solution\", \"Correct Solution\"])\n", + "plt.grid()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "bosch", + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/fno/integrator.ipynb b/examples/fno/integrator.ipynb new file mode 100644 index 00000000..f83e7bb2 --- /dev/null +++ b/examples/fno/integrator.ipynb @@ -0,0 +1,376 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fourier Neural Operator (FNO): Solving a ODE for different right hand sides\n", + "In this notebook, we present an introduction to the FNO [(paper)](https://arxiv.org/abs/2010.08895) utilities of TorchPhysics. \n", + "As an example, we try to learn the integral operator of the ODE:\n", + "\\begin{align*}\n", + " \\partial_t u(t) &= f(t), \\text{ in } [0, 1] \\\\\n", + " u(0) &= 0\n", + "\\end{align*}\n", + "for different functions $f$. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch \n", + "import torchphysics as tp\n", + "import pytorch_lightning as pl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First create the data set we need for training the network.\n", + "Here we randomly create some oscillating functions and use an explicit euler to compute our training data." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "N_batch = 5000 # Data in trainig set\n", + "N_time = 100 # resolution of time (or space) interval\n", + "dt = 1/N_time\n", + "\n", + "input_data = torch.zeros((N_batch, N_time, 1))\n", + "random_data = torch.zeros((N_batch, 3))\n", + "random_data[:, :1] = torch.randint(0, 24, (N_batch, 1))\n", + "random_data[:, 1:2] = torch.randint(0, 12, (N_batch, 1))\n", + "random_data[:, 2:] = torch.randint(0, 6, (N_batch, 1))\n", + "\n", + "output_data = torch.zeros((N_batch, N_time, 1))\n", + "\n", + "t = 0.0\n", + "input_data[:, 0, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + "for i in range(1, N_time):\n", + " t += dt\n", + " input_data[:, i, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + " output_data[:, i, 0] = output_data[:, i-1, 0] + dt * input_data[:, i, 0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In TorchPhysics we have to define the input and output space like always:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "F = tp.spaces.R1(\"f\")\n", + "U = tp.spaces.R1(\"u\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we create the network that learns the mapping:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "hidden_channels = 8\n", + "\n", + "model = tp.models.FNO(F, U, \n", + " fourier_layers=3, \n", + " hidden_channels=hidden_channels, \n", + " fourier_modes=8, \n", + " skip_connections=True, \n", + " channel_down_sample_network=torch.nn.Sequential(\n", + " torch.nn.Linear(hidden_channels, hidden_channels),\n", + " torch.nn.Tanh(),\n", + " torch.nn.Linear(hidden_channels, U.dim)\n", + " ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we create a data condition to fit the FNO to the data." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Transform data to points\n", + "input_data_points = tp.spaces.Points(input_data, F)\n", + "output_data_points = tp.spaces.Points(output_data, U)\n", + "\n", + "dataloader = tp.utils.PointsDataLoader((input_data_points, output_data_points),\n", + " batch_size=N_batch)\n", + "\n", + "data_condition = tp.conditions.DataCondition(model, dataloader, norm=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start training:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "You are using a CUDA device ('GeForce RTX 3090') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n", + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 505 \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "505 Trainable params\n", + "0 Non-trainable params\n", + "505 Total params\n", + "0.002 Total estimated model params size (MB)\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'val_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 5000/5000 [00:47<00:00, 104.95it/s, train/loss=0.00127] " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "`Trainer.fit` stopped: `max_steps=5000` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 5000/5000 [00:47<00:00, 104.95it/s, train/loss=0.00127]\n" + ] + } + ], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.01)\n", + "solver = tp.solver.Solver([data_condition], optimizer_setting=optim)\n", + "\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\",\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=5000, \n", + " logger=False, \n", + " enable_checkpointing=False)\n", + "\n", + "trainer.fit(solver)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Decrease learning rate and fine tune some more:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 505 \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "505 Trainable params\n", + "0 Non-trainable params\n", + "505 Total params\n", + "0.002 Total estimated model params size (MB)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [01:31<00:00, 108.78it/s, train/loss=0.000454]" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "`Trainer.fit` stopped: `max_steps=10000` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [01:31<00:00, 108.78it/s, train/loss=0.000454]\n" + ] + } + ], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.001)\n", + "solver = tp.solver.Solver([data_condition], optimizer_setting=optim)\n", + "\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\",\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=10000, \n", + " logger=False, \n", + " enable_checkpointing=False)\n", + "\n", + "trainer.fit(solver)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we test the network on unseen data. Therefore we create inputs as before and evaluate the model:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Relative error: 11.572426795959473 %\n" + ] + } + ], + "source": [ + "N_test = 100\n", + "N_time = 100\n", + "dt = 1/N_time\n", + "\n", + "test_data = torch.zeros((N_test, N_time, 1))\n", + "output_data_test = torch.zeros((N_test, N_time, 1))\n", + "\n", + "random_data = torch.zeros((N_test, 3))\n", + "random_data[:, :1] = torch.randint(0, 24, (N_test, 1))\n", + "random_data[:, 1:2] = torch.randint(0, 12, (N_test, 1))\n", + "random_data[:, 2:] = torch.randint(0, 6, (N_test, 1))\n", + "\n", + "t = 0.0\n", + "test_data[:, 0, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + "for i in range(1, N_time):\n", + " t += dt\n", + " test_data[:, i, 0] = torch.sin(t * random_data[:, 0]) + 0.5 * torch.cos(t * random_data[:, 1]) \\\n", + " + 2.0 * torch.sin(t * random_data[:, 2])\n", + " output_data_test[:, i, 0] = output_data_test[:, i-1, 0] + dt * test_data[:, i, 0]\n", + "\n", + "model_output = model(tp.spaces.Points(test_data, F)).as_tensor\n", + "\n", + "rel_error = torch.max(torch.abs(model_output - output_data_test)) / torch.max(output_data_test)\n", + "\n", + "print(f\"Relative error: {rel_error*100} %\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Ro0aMXnyZOLj4+nfvz8Affv2JTQ0lIkTJwLw9NNP07JlS9555x3uvvtu5s2bxx9//MGnn36ava/kFny3/TQjvtkBOI6IPNumooKIiIiIk2Q5jHTv3p2zZ88yatQooqOjqVOnDitWrEgdpHr8+HHM5r8PuDRt2pS5c+cycuRIXn75ZSpWrMiSJUuoUaNG9r2KW7DjgomZG3ZiGND7ttI6IiIiIuJkNzWAdejQoQwdOjTDx6KiotIt69q1K127dr2Zp8pRqw+eZ+Z+MzbD4IF6oYy7t4aCiIiIiJMV2Bm8EpJTeO7r7dgME+2rBfJml1q6akZERMQFCmwY8fFw46MedahXzM67XWvhpnvNiIiIuESB/gauX6YI/SrZNcW7iIiIC+lbWERERFxKYURERERcSmFEREREXEphRERERFxKYURERERcSmFEREREXEphRERERFxKYURERERcSmFEREREXEphRERERFxKYURERERcSmFEREREXEphRERERFzKzdUFZIZhGADExsZm636tVisJCQnExsbi7u6erfuWv6nPzqNeO4f67Bzqs3PkZJ+vfW9f+x6/njwRRuLi4gAICwtzcSUiIiKSVXFxcQQEBFz3cZPxX3ElF7Db7Zw6dQo/Pz9MJlO27Tc2NpawsDBOnDiBv79/tu1X0lKfnUe9dg712TnUZ+fIyT4bhkFcXBwlS5bEbL7+yJA8cWTEbDZTqlSpHNu/v7+/3uhOoD47j3rtHOqzc6jPzpFTfb7REZFrNIBVREREXEphRERERFyqQIcRT09PRo8ejaenp6tLydfUZ+dRr51DfXYO9dk5ckOf88QAVhEREcm/CvSREREREXE9hRERERFxKYURERERcSmFEREREXGpfB9GpkyZQnh4OF5eXjRu3JiNGzfecP2FCxdSpUoVvLy8qFmzJsuXL3dSpXlbVvo8bdo0br/9dooUKUKRIkVo06bNf/69yN+y+p6+Zt68eZhMJjp37pyzBeYTWe3zpUuXGDJkCCEhIXh6elKpUiV9fmRCVvs8efJkKleujLe3N2FhYTz77LMkJiY6qdq8adWqVXTq1ImSJUtiMplYsmTJf24TFRVFvXr18PT0pEKFCsyaNStnizTysXnz5hkeHh7GjBkzjF27dhmDBg0yChcubMTExGS4/po1awyLxWK8+eabxu7du42RI0ca7u7uxo4dO5xced6S1T737NnTmDJlirFlyxZjz549xsMPP2wEBAQYJ0+edHLleU9We33NkSNHjNDQUOP222837rvvPucUm4dltc9JSUlGgwYNjI4dOxqrV682jhw5YkRFRRlbt251cuV5S1b7PGfOHMPT09OYM2eOceTIEePHH380QkJCjGeffdbJlecty5cvN1555RVj8eLFBmB88803N1z/8OHDho+PjzFs2DBj9+7dxgcffGBYLBZjxYoVOVZjvg4jjRo1MoYMGZL6u81mM0qWLGlMnDgxw/W7detm3H333WmWNW7c2HjsscdytM68Lqt9/reUlBTDz8/PmD17dk6VmG/cTK9TUlKMpk2bGp999pnRr18/hZFMyGqfP/74Y6NcuXJGcnKys0rMF7La5yFDhhh33HFHmmXDhg0zmjVrlqN15ieZCSMvvviiUb169TTLunfvbrRv3z7H6sq3p2mSk5PZtGkTbdq0SV1mNptp06YN69aty3CbdevWpVkfoH379tddX26uz/+WkJCA1WqlaNGiOVVmvnCzvR43bhyBgYEMGDDAGWXmeTfT52+//ZYmTZowZMgQgoKCqFGjBhMmTMBmszmr7DznZvrctGlTNm3alHoq5/DhwyxfvpyOHTs6peaCwhXfhXniRnk349y5c9hsNoKCgtIsDwoKYu/evRluEx0dneH60dHROVZnXnczff63l156iZIlS6Z780taN9Pr1atXM336dLZu3eqECvOHm+nz4cOH+fnnn+nVqxfLly/n4MGDDB48GKvVyujRo51Rdp5zM33u2bMn586do3nz5hiGQUpKCo8//jgvv/yyM0ouMK73XRgbG8vVq1fx9vbO9ufMt0dGJG+YNGkS8+bN45tvvsHLy8vV5eQrcXFx9OnTh2nTplG8eHFXl5Ov2e12AgMD+fTTT6lfvz7du3fnlVdeYerUqa4uLV+JiopiwoQJfPTRR2zevJnFixezbNkyxo8f7+rS5Bbl2yMjxYsXx2KxEBMTk2Z5TEwMwcHBGW4THBycpfXl5vp8zdtvv82kSZP46aefqFWrVk6WmS9ktdeHDh3i6NGjdOrUKXWZ3W4HwM3NjX379lG+fPmcLToPupn3dEhICO7u7lgsltRlVatWJTo6muTkZDw8PHK05rzoZvr86quv0qdPHwYOHAhAzZo1iY+P59FHH+WVV17BbNb/r7PD9b4L/f39c+SoCOTjIyMeHh7Ur1+fyMjI1GV2u53IyEiaNGmS4TZNmjRJsz5ARETEddeXm+szwJtvvsn48eNZsWIFDRo0cEapeV5We12lShV27NjB1q1bU//ce++9tG7dmq1btxIWFubM8vOMm3lPN2vWjIMHD6aGPYD9+/cTEhKiIHIdN9PnhISEdIHjWgA0dJu1bOOS78IcGxqbC8ybN8/w9PQ0Zs2aZezevdt49NFHjcKFCxvR0dGGYRhGnz59jOHDh6euv2bNGsPNzc14++23jT179hijR4/Wpb2ZkNU+T5o0yfDw8DC+/vpr4/Tp06l/4uLiXPUS8oys9vrfdDVN5mS1z8ePHzf8/PyMoUOHGvv27TO+//57IzAw0Hjttddc9RLyhKz2efTo0Yafn5/x1VdfGYcPHzZWrlxplC9f3ujWrZurXkKeEBcXZ2zZssXYsmWLARjvvvuusWXLFuPYsWOGYRjG8OHDjT59+qSuf+3S3hdeeMHYs2ePMWXKFF3ae6s++OADo3Tp0oaHh4fRqFEjY/369amPtWzZ0ujXr1+a9RcsWGBUqlTJ8PDwMKpXr24sW7bMyRXnTVnpc5kyZQwg3Z/Ro0c7v/A8KKvv6X9SGMm8rPZ57dq1RuPGjQ1PT0+jXLlyxuuvv26kpKQ4ueq8Jyt9tlqtxpgxY4zy5csbXl5eRlhYmDF48GDj4sWLzi88D/nll18y/My91tt+/foZLVu2TLdNnTp1DA8PD6NcuXLGzJkzc7RGk2Ho2JaIiIi4Tr4dMyIiIiJ5g8KIiIiIuJTCiIiIiLiUwoiIiIi4lMKIiIiIuJTCiIiIiLiUwoiIiIi4lMKIiIiIuJTCiIiIiLiUwoiIiIi4lMKIiIiIuJTCiIiIiLjU/wH837TfVbjVrwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "example_plot = 6\n", + "t = torch.linspace(0, 1, N_time)\n", + "plt.plot(t, model_output[example_plot, :, 0].detach().cpu())\n", + "plt.plot(t, output_data_test[example_plot, :, 0].detach().cpu())\n", + "plt.legend([\"FNO Solution\", \"Correct Solution\"])\n", + "plt.grid()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "bosch", + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/torchphysics/models/FNO.py b/src/torchphysics/models/FNO.py index b750c364..d049f745 100644 --- a/src/torchphysics/models/FNO.py +++ b/src/torchphysics/models/FNO.py @@ -1,78 +1,210 @@ -""" -Work in progress! -""" import torch import torch.nn as nn from .model import Model -from ..problem.spaces import Points +from ..problem.spaces import Points -class _FourierLayer(nn.Model): - """Implements a single fourier layer of the FNO from [1]. Is of the form: - Parameters - ---------- - mode_num : int, tuple - The number of modes that should be used. For resolutions with higher - frequenzies, the layer will discard everything above `mode_num` and - in the inverse Fourier transform append zeros. In higher dimensional - data, a tuple can be passed in with len(mode_num) = dimension. - in_features : int - size of each input sample. - - Notes - ----- - .. [1] +class _FourierLayer(nn.Module): + """Implements a single Fourier layer of the FNO. For the parameter description see + the FNO documentation. """ - def __init__(self, mode_num, in_features, xavier_gain): + def __init__(self, channels, mode_num, + linear_connection : bool = False, skip_connection : bool = False, + bias : bool = False, xavier_gain=5.0/3.0, space_res=None): # Transform mode_num to tuple: if isinstance(mode_num, int): mode_num = (mode_num, ) super().__init__() + + self.channels = channels + self.skip_connection : bool = skip_connection + # Values for Fourier transformation self.mode_num = torch.tensor(mode_num) - self.in_features = in_features - #self.linear_weights = torch.nn.Linear(in_features=in_features, - # out_features=in_features, - # bias=False) + self.data_dim = len(mode_num) + self.fourier_dims = list(range(1, self.data_dim+1)) + + # Learnable parameters + self.fourier_kernel = torch.nn.Parameter( + torch.empty((*self.mode_num, self.channels), dtype=torch.cfloat)) + torch.nn.init.xavier_normal_(self.fourier_kernel, gain=xavier_gain) + + self.linear_connection : bool = linear_connection + if self.linear_connection: + self.linear_transform = nn.Linear(channels, channels, bias=bias) + + self.use_bn : bool = False + if space_res: + self.use_bn = True + if self.data_dim == 1: + self.bn = torch.nn.BatchNorm1d(space_res) + else: + raise NotImplementedError(f"Dimension {self.data_dim} currently not \ + supported for batch normalization") - self.fourier_weights = torch.nn.Parameter( - torch.empty((in_features, *self.mode_num)), dtype=torch.complex32) - torch.nn.init.xavier_normal_(self.fourier_weights, gain=xavier_gain) - def forward(self, points): - ### Linear skip connection - #linear_out = self.linear_weights(points) - ### Fourier part - # Computing how much each dimension has to cut/padded: - # Here we need that points.shape = (batch, data_dim, resolution) - padding = torch.zeros(2*len(self.mode_num), device=points.device, - dtype=torch.int32) - padding[1::2] = torch.flip((self.mode_num - torch.tensor(points.shape[2:])), - dims=(0,)) - fft = torch.nn.functional.pad( - torch.fft.fftn(points, dim=len(self.mode_num), norm="ortho"), - padding.tolist()) # here remove to high freq. - weighted_fft = self.fourier_weights * fft - ifft = torch.fft.ifftn( - torch.nn.functional.pad(weighted_fft, (-padding).tolist()), # here add high freq. - dim=len(self.mode_num), norm="ortho") - ### Connect linear and fourier output - return ifft + fft = torch.fft.rfftn(points, dim=self.fourier_dims) + + # Next add zeros or remove fourier modes to fit input for wanted freq. + original_fft_shape = torch.tensor(fft.shape[1:-1]) + # padding needs to extra values, since the torch.nn.functional.pad starts + # from the last dimension (the channels in our case), there we dont need to + # change anything so only zeros in the padding. + padding = torch.zeros(2*self.data_dim + 2, device=points.device, dtype=torch.int32) + padding[3::2] = torch.flip((self.mode_num - original_fft_shape), dims=(0,)) + + fft = torch.nn.functional.pad(fft, padding.tolist()) + + fft *= self.fourier_kernel + + ifft = torch.fft.irfftn(fft, s=points.shape[1:-1], dim=self.fourier_dims) - @property - def in_features(self): - return self.in_features + if self.linear_connection: + ifft += self.linear_transform(points) - @property - def out_features(self): - return self.in_features + if self.skip_connection: + ifft += points + + if self.use_bn: + return self.bn(ifft) + + return ifft class FNO(Model): + """ The Fourier Neural Operator original developed in [1]. - def __init__(self, input_space, output_space, - upscale_size, fourier_layers, fourier_modes, - activations, xavier_gains): - super().__init__(input_space, output_space) \ No newline at end of file + Parameters + ---------- + input_space : Space + The space of the points the can be put into this model. + output_space : Space + The space of the points returned by this model. + fourier_layers : int + The number of fourier layers of this network. Each fourier layer consists + of a spectral convolution with learnable kernels. See [1] for an overview + of the model. Linear transformations and skip connections can be enabled + in each layer as well. + hidden_channles : int + The number of hidden channels. + fourier_modes : int or list, tuple + The number of Fourier modes that will be used for the spectral convolution + in each layer. Modes over the given value will be truncated, and in case + of not enough modes they are padded with 0. + In case of a 1D space domain you can pass in one integer or a list of + integers, such that in each layer a different amount of modes is used. + In case of a N-dimensional space domain a list (or tuple) of N numbers + must be passed in (Setting the modes for each direction), or again + a list of list containig each N numbers to vary the modes per layer. + activations : torch.nn or list, tuple + The activation function after each Fourier layer. + Default is torch.nn.Tanh() + skip_connections : bool or list, tuple + If a skip connection is enabled in each Fourier layer, adding the original + input of the layer to the output without any transformations. + linear_connection : bool or list, tuple + If the input of each Fourier layer should also be transformed by a + (learnable) linear mapping and added to the output. + bias : bool or list, tuple + If the above linear connection should include a (learnable) bias vector. + channel_up_sample_network : torch.nn + The network that transforms the input channel dimension to the + hidden channel dimension. (The mapping P in [1], Figure 2) + Default is a linear mapping. + channel_down_sample_network : torch.nn + The network that transforms the hidden channel dimension to the + output channel dimension. (The mapping Q in [1], Figure 2) + Default is a linear mapping. + xavier_gains : int or list, tuple + For the weight initialization a Xavier/Glorot algorithm will be used. + The gain can be specified over this value. + Default is 5/3. + space_resolution : int or None + The resolution of the space grid used for training. This value is optional. + If specified, a batch normalization over the space dimension will be applied + in each Fourier layer. This leads to smoother solutions and better local + approximations. But (currently) removes the super resolution property of the + FNO. This is currently only possible for 1D space dimensions. + + Notes + ----- + The FNO assumes that the data is of the shape + (batch, space_dim_1, ..., space_dim_n, channels). + E.g. for a one dimensional problem we have (batch, grid points, channels). + Additionally, the data needs to exists on a uniform grid to accurately + compute the Fourier transformation. + + Note, this networks assumes that the input and output are real numbers. + It does not work in the case of complex numbers. + + .. [1] Zong-Yi Li et al., "Fourier Neural Operator for Parametric Partial + Differential Equations", 2020 + """ + def __init__(self, input_space, output_space, fourier_layers : int, + hidden_channels : int = 16, fourier_modes = 16, activations=torch.nn.Tanh(), + skip_connections = False, linear_connections = True, bias = True, + channel_up_sample_network = None, channel_down_sample_network = None, + xavier_gains=5.0/3, space_resolution = None): + super().__init__(input_space, output_space) + + # Transform data to list values for each layer: + skip_connections = self._extend_data(fourier_layers, skip_connections) + bias = self._extend_data(fourier_layers, bias) + linear_connections = self._extend_data(fourier_layers, linear_connections) + activations = self._extend_data(fourier_layers, activations) + xavier_gains = self._extend_data(fourier_layers, xavier_gains) + + if isinstance(fourier_modes, int): + fourier_modes = fourier_layers * [fourier_modes] + elif isinstance(fourier_modes, (list, tuple)): + if len(fourier_modes) < fourier_layers: + fourier_modes = fourier_layers * [fourier_modes] + else: + raise ValueError(f"Invalid input for fourier modes") + + # Define network architecture + layers = [] + + in_channels = self.input_space.dim + out_channels = self.output_space.dim + + if not channel_up_sample_network: + self.channel_up_sampling = nn.Linear(in_channels, + hidden_channels, + bias=True) + else: + self.channel_up_sampling = channel_up_sample_network + + if not channel_down_sample_network: + self.channel_down_sampling = nn.Linear(hidden_channels, + out_channels, + bias=True) + else: + self.channel_down_sampling = channel_down_sample_network + + for i in range(fourier_layers): + new_layer = _FourierLayer(hidden_channels, fourier_modes[i], + linear_connections[i], + skip_connections[i], bias[i], + xavier_gains[i], + space_res=space_resolution) + layers.append(new_layer) + layers.append(activations[i]) + + self.fourier_sequential = nn.Sequential(*layers) + + + def _extend_data(self, fourier_layers, skip_connections): + if not isinstance(skip_connections, (list, tuple)): + skip_connections = fourier_layers * [skip_connections] + return skip_connections + + + def forward(self, points): + points = self._fix_points_order(points) + points_up_sampled = self.channel_up_sampling(points) + fourier_points = self.fourier_sequential(points_up_sampled) + output = self.channel_down_sampling(fourier_points) + return Points(output, self.output_space) diff --git a/src/torchphysics/models/__init__.py b/src/torchphysics/models/__init__.py index 1653df32..e75feb13 100644 --- a/src/torchphysics/models/__init__.py +++ b/src/torchphysics/models/__init__.py @@ -22,4 +22,7 @@ from .deeponet.deeponet import DeepONet from .deeponet.branchnets import (BranchNet, FCBranchNet, ConvBranchNet1D) from .deeponet.trunknets import (TrunkNet, FCTrunkNet) -from .deeponet.layers import TrunkLinear \ No newline at end of file +from .deeponet.layers import TrunkLinear + +# FNO: +from .FNO import FNO, _FourierLayer \ No newline at end of file diff --git a/src/torchphysics/problem/conditions/variational_condition.py b/src/torchphysics/problem/conditions/variational_condition.py index dc0e6a46..fac216d3 100644 --- a/src/torchphysics/problem/conditions/variational_condition.py +++ b/src/torchphysics/problem/conditions/variational_condition.py @@ -31,7 +31,7 @@ def forward(self, device='cpu', iteration=None): test_fn = self.test_fn_set(x_coordinates) - test_space_parameters = {"quad_weights": self.test_fn_set.get_quad_weights()} + test_space_parameters = {"quad_weights": self.test_fn_set.get_quad_weights(len(y.as_tensor))} unreduced_loss = self.error_fn(self.residual_fn({**y.coordinates, **x_coordinates, diff --git a/src/torchphysics/problem/domains/functionsets/FE_functionset.py b/src/torchphysics/problem/domains/functionsets/FE_functionset.py index c9e2a710..604baf4e 100644 --- a/src/torchphysics/problem/domains/functionsets/FE_functionset.py +++ b/src/torchphysics/problem/domains/functionsets/FE_functionset.py @@ -29,8 +29,9 @@ def __call__(self, x=None): Points(self.finite_elements(x), self.function_space.output_space) - def get_quad_weights(self): - return self.finite_elements.quadrature_weights_per_dof + def get_quad_weights(self, n): + repeats = n // len(self.finite_elements.quadrature_weights_per_dof) + return self.finite_elements.quadrature_weights_per_dof.repeat((repeats, 1, 1)) def get_quadrature_points(self): diff --git a/src/torchphysics/problem/domains/functionsets/functionset.py b/src/torchphysics/problem/domains/functionsets/functionset.py index a674f48b..9005ecc3 100644 --- a/src/torchphysics/problem/domains/functionsets/functionset.py +++ b/src/torchphysics/problem/domains/functionsets/functionset.py @@ -194,7 +194,10 @@ def forward(ctx, x, expected_out, grad_out): @staticmethod def backward(ctx, grad_output): grad_out, = ctx.saved_tensors - return grad_out * grad_output, None, None + repeats = grad_output.shape[0] // grad_out.shape[0] + # Assumes the original data to be repeated along the first axis + # TODO: Can be done nicer??? + return grad_out.repeat((repeats, 1, 1)) * grad_output, None, None class TestFunctionSet(FunctionSet): @@ -217,7 +220,7 @@ def to(self, device): raise NotImplementedError @abc.abstractmethod - def get_quad_weights(self): + def get_quad_weights(self, n): raise NotImplementedError @abc.abstractmethod diff --git a/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py b/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py index b9c4e0b9..1c75b8d8 100644 --- a/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py +++ b/src/torchphysics/problem/domains/functionsets/harmonic_functionset.py @@ -72,8 +72,10 @@ def grad(self, x=None): return self.grad_at_quadrature - def get_quad_weights(self): - return self.quadrature_weights_per_dof + def get_quad_weights(self, n): + repeats = n // len(self.quadrature_weights_per_dof) + return self.quadrature_weights_per_dof.repeat((repeats, 1, 1)) + def get_quadrature_points(self): return Points(self.quadrature_points_per_dof, self.function_space.input_space) \ No newline at end of file diff --git a/src/torchphysics/problem/samplers/data_samplers.py b/src/torchphysics/problem/samplers/data_samplers.py index 0b24dfc7..9edaccd3 100644 --- a/src/torchphysics/problem/samplers/data_samplers.py +++ b/src/torchphysics/problem/samplers/data_samplers.py @@ -1,10 +1,12 @@ """File with samplers that handle external created data. E.g. measurements or validation data computed with other methods. """ +import torch from .sampler_base import PointSampler from ..spaces import Points +import time class DataSampler(PointSampler): """A sampler that processes external created data points. @@ -25,9 +27,38 @@ def __init__(self, points): self.points = Points.from_coordinates(points) else: raise TypeError("points should be one of Points or dict.") - n = len(points) + n = len(self.points.as_tensor) super().__init__(n_points=n) def sample_points(self, params=Points.empty(), device='cpu'): self.points = self.points.to(device) - return self.points \ No newline at end of file + + # If sampler not coupled to other samplers or parameters + # we can return: + if params.isempty: + return self.points + + # Maybe given data has more dimensions than batch and space + # (For example evaluation on quadrature points) + # TODO: Make more general. What happends when parameters have higher dimension? + # What when multiple dimension in both that do not fit? + start_time = time.time() + if len(self.points.as_tensor.shape) > 2: + repeated_tensor = params.as_tensor + for i in range(1, len(self.points.as_tensor.shape)-1): + repeated_tensor = torch.repeat_interleave(repeated_tensor.unsqueeze(-1), + self.points.as_tensor.shape[i], + dim=i) + + repeated_params = Points(repeated_tensor, params.space) + print("Dimension thing took", time.time() - start_time) + + # else we have to repeat data (meshgrid of both) and join the tensors together: + start_time = time.time() + repeated_params = self._repeat_params(repeated_params, len(self)) + print("Repeating params took", time.time() - start_time) + start_time = time.time() + repeated_points = self.points.repeat(len(params)) + print("Repeating points took", time.time() - start_time) + + return repeated_points.join(repeated_params) \ No newline at end of file diff --git a/src/torchphysics/utils/data/dataloader.py b/src/torchphysics/utils/data/dataloader.py index 9bca3327..7a0625dd 100644 --- a/src/torchphysics/utils/data/dataloader.py +++ b/src/torchphysics/utils/data/dataloader.py @@ -27,7 +27,7 @@ def __init__(self, data_points, batch_size, shuffle=False, drop_last=False): assert isinstance(data_points, (tuple, list)) self.data_points = list(data_points) if shuffle: - perm = torch.randperm(len(self.data_points[0])) + perm = torch.randperm(len(self.data_points[0].as_tensor)) for i in range(len(self.data_points)): self.data_points[i] = self.data_points[i][perm] @@ -38,9 +38,9 @@ def __len__(self): """Returns the number of points of this dataset. """ if self.drop_last: - return len(self.data_points[0]) // self.batch_size + return len(self.data_points[0].as_tensor) // self.batch_size else: - return math.ceil(len(self.data_points[0]) / self.batch_size) + return math.ceil(len(self.data_points[0].as_tensor) / self.batch_size) def __getitem__(self, idx): """Returns the item at the given index. @@ -50,7 +50,7 @@ def __getitem__(self, idx): idx : int The index of the desired point. """ - l = len(self.data_points[0]) + l = len(self.data_points[0].as_tensor) out = [] for points in self.data_points: out.append(points[idx*self.batch_size:min((idx+1)*self.batch_size, l), :]) diff --git a/tests/tests_models/test_fno.py b/tests/tests_models/test_fno.py new file mode 100644 index 00000000..0a0e22b0 --- /dev/null +++ b/tests/tests_models/test_fno.py @@ -0,0 +1,96 @@ +import torch +import pytest + +from torchphysics.models.FNO import FNO, _FourierLayer +from torchphysics.problem.spaces import Points, R1, R2 + + +def test_create_fourier_layer(): + fourier_layer = _FourierLayer(4, 4) + assert fourier_layer.data_dim == 1 + assert fourier_layer.fourier_kernel.shape == (4, 4) + + +def test_create_fourier_layer_higher_dim(): + fourier_layer = _FourierLayer(8, (4, 6)) + assert fourier_layer.data_dim == 2 + assert fourier_layer.fourier_kernel.shape == (4, 6, 8) + + +def test_create_fourier_layer_with_linear_transform(): + fourier_layer = _FourierLayer(8, 4, linear_connection=True) + assert isinstance(fourier_layer.linear_transform, torch.nn.Linear) + assert fourier_layer.linear_transform.weight.shape == (8, 8) + + +def test_create_fourier_layer_with_batchnorm(): + fourier_layer = _FourierLayer(8, 4, space_res=10) + assert fourier_layer.use_bn + with pytest.raises(NotImplementedError): + fourier_layer = _FourierLayer(8, (4, 4), space_res=10) + + +def test_forward_fourier_layer(): + fourier_layer = _FourierLayer(1, 10) + input_data = torch.linspace(0, 1, 10).reshape(1, 10, 1) + output_data = fourier_layer(input_data) + assert output_data.shape[0] == 1 + assert output_data.shape[1] == 10 + assert output_data.shape[2] == 1 + + +def test_forward_fourier_layer_with_multiple_transforms(): + fourier_layer = _FourierLayer(1, 10, linear_connection=True, + skip_connection=True, + space_res=10) + input_data = torch.linspace(0, 1, 10).reshape(1, 10, 1) + input_data = torch.repeat_interleave(input_data, 3, 0) + output_data = fourier_layer(input_data) + assert output_data.shape[0] == 3 + assert output_data.shape[1] == 10 + assert output_data.shape[2] == 1 + + + +def test_create_fno_default(): + fno = FNO(input_space=R2('f'), output_space=R1('u'), fourier_layers=4, + activations=torch.nn.Tanh()) + assert isinstance(fno.fourier_sequential, torch.nn.Sequential) + for i in range(0, 8, 2): + assert isinstance(fno.fourier_sequential[i], _FourierLayer) + for i in range(1, 8, 2): + assert isinstance(fno.fourier_sequential[i], torch.nn.Tanh) + assert isinstance(fno.channel_down_sampling, torch.nn.Module) + assert isinstance(fno.channel_up_sampling, torch.nn.Module) + + +def test_create_fno_optional(): + in_network = torch.nn.Linear(2, 10) + out_network = torch.nn.Linear(10, 1) + fno = FNO(input_space=R2('f'), output_space=R1('u'), fourier_layers=2, + fourier_modes=([3, 3], [4, 4]), linear_connections=False, hidden_channels=10, + bias=[False, False], + channel_up_sample_network=in_network, + channel_down_sample_network=out_network, + activations=torch.nn.Tanh()) + assert fno.channel_up_sampling == in_network + assert fno.channel_down_sampling == out_network + + +def test_create_fno_set_modes(): + FNO(input_space=R2('f'), output_space=R1('u'), fourier_layers=4, + fourier_modes=[3, 3]) + with pytest.raises(ValueError): + FNO(input_space=R2('f'), output_space=R1('u'), fourier_layers=4, + fourier_modes="a") + + +def test_forward_fno(): + fno = FNO(input_space=R1('f'), output_space=R2('u'), fourier_layers=4, + activations=torch.nn.Tanh(), hidden_channels=10) + input_data = torch.linspace(0, 1, 10).reshape(1, 10, 1) + input_data = torch.repeat_interleave(input_data, 3, 0) + output_data = fno(Points(input_data, R1("f"))).as_tensor + assert output_data.shape[0] == 3 + assert output_data.shape[1] == 10 + assert output_data.shape[2] == 2 \ No newline at end of file From 826f46bdd73d195607be8539a8008bf8c489a397 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Tue, 12 Nov 2024 14:39:56 +0100 Subject: [PATCH 21/25] FNO 2d diffusion example Signed-off-by: Tom Freudenberg --- examples/fno/diffusion_2D.ipynb | 376 ++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 examples/fno/diffusion_2D.ipynb diff --git a/examples/fno/diffusion_2D.ipynb b/examples/fno/diffusion_2D.ipynb new file mode 100644 index 00000000..c4f5abd0 --- /dev/null +++ b/examples/fno/diffusion_2D.ipynb @@ -0,0 +1,376 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### FNO: Solving a diffusion equation for different conductivty\n", + "We want to solve the following equation\n", + "\\begin{align*}\n", + " -\\text{div}(\\kappa(x) \\nabla u(x)) &= 10 &&\\text{ in } \\Omega, \\\\\n", + " u &= 0 &&\\text{ on } \\partial \\Omega,\n", + "\\end{align*}\n", + "for functions $\\kappa: \\Omega \\to \\mathbb{R}$. As a domain we consider the unit square $\\Omega = [0, 1]^2$.\n", + "\n", + "The possible $\\kappa$ are created from [perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) and the dataset was created with the finite element method. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch \n", + "import torchphysics as tp\n", + "import pytorch_lightning as pl\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we load the data. The complete dataset consists of 6000 instances of pairs $(\\kappa, u)$. \n", + "5000 will be used in the training process, while the remaining once are later used to test the FNO on unseen data." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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DLsOY8z26zDbz69T7DqMYddhjMN1HbrOCdAwDMMwjMNP11oADMMwDMM4fNgHgGEYhmH0IPYBYBiGYRg9iH0AGIZhGEYP0pUfAFu2bMGrX/1q9PX1YeXKlXjwwQePdJcOK5s3b8Y555yD+fPn4/jjj8dFF12EnTt3ijajo6NYt24dFi1ahHnz5mHNmjUYGRk5Qj2eGa677jpEUYQrrriitaxXrsOvfvUrfOADH8CiRYvQ39+P1772tXjooYda651zuOqqq3DCCSegv78fq1atwhNPPHEEezx1bNzbuAds3M/ouHddxu233+4qlYr76le/6n7605+63//933cLFy50IyMjR7prh43Vq1e7rVu3uscee8w98sgj7m1ve5tbunSp279/f6vNRz7yEbdkyRK3bds299BDD7lzzz3XnXfeeUew14eXBx980L361a92Z555prv88stby3vhOrz00kvupJNOch/84AfdAw884H7xi1+4u+++2z355JOtNtddd50bGBhw3/jGN9yPf/xj9853vtMtW7bMHTp06Aj2fPLYuLdx75yN+5ke9133AfCGN7zBrVu3rjWfpqlbvHix27x58xHs1czy/PPPOwDunnvucc45t3v3blcul90dd9zRavN//s//cQDc9u3bj1Q3Dxv79u1zp5xyivvOd77j3vSmN7VeBL1yHT7xiU+4N77xjd71WZa5oaEh96d/+qetZbt373bVatX99//+32eii9OOjXsb9zbuZ37cd5UEUK/XsWPHDqxataq1LI5jrFq1Ctu3bz+CPZtZ9uzZAwA45phjAAA7duxAo9EQ12X58uVYunTprLwu69atw9vf/nZxvkDvXIe//du/xdlnn413v/vdOP7443HWWWfhK1/5Smv9U089hV27donrMDAwgJUrVx6V18HG/Rg27m3cz/S476oPgBdffBFpmmJwcFAsHxwcxK5du45Qr2aWLMtwxRVX4Pzzz8cZZ5wBANi1axcqlQoWLlwo2s7G63L77bfj4YcfxubNm3PreuU6/OIXv8CNN96IU045BXfffTc++tGP4uMf/zhuvfVWAGid62wZJzbubdzbuD8y477rqgH2OuvWrcNjjz2G++6770h3ZcZ55plncPnll+M73/kO+vr6jnR3jhhZluHss8/G5z73OQDAWWedhcceewxf/vKXsXbt2iPcO+NwYOPexv2RGPddZQE49thjkSRJzrtzZGQEQ0NDR6hXM8f69evxrW99C//wD/+AE088sbV8aGgI9Xodu3fvFu1n23XZsWMHnn/+ebz+9a9HqVRCqVTCPffcgxtuuAGlUgmDg4M9cR1OOOEEnHbaaWLZqaeeiqeffhoAWuc6W8aJjXsb9zbuj8y476oPgEqlghUrVmDbtm2tZVmWYdu2bRgeHj6CPTu8OOewfv16fP3rX8f3vvc9LFu2TKxfsWIFyuWyuC47d+7E008/Pauuy1vf+lY8+uijeOSRR1p/Z599Nt7//ve3pnvhOpx//vm5cLCf//znOOmkkwAAy5Ytw9DQkLgOe/fuxQMPPHBUXgcb9zbubdwfoXE/KdfBw8jtt9/uqtWqu+WWW9zjjz/uLrvsMrdw4UK3a9euI921w8ZHP/pRNzAw4L7//e+75557rvV38ODBVpuPfOQjbunSpe573/uee+ihh9zw8LAbHh4+gr2eGdgb2LneuA4PPvigK5VK7rOf/ax74okn3F/+5V+6OXPmuL/4i79otbnuuuvcwoUL3Te/+U33k5/8xF144YVHfRigjXsb9+PYuJ+Zcd91HwDOOfdnf/ZnbunSpa5Sqbg3vOEN7v777z/SXTqsACj827p1a6vNoUOH3Mc+9jH3qle9ys2ZM8f9zu/8jnvuueeOXKdnCP0i6JXr8Hd/93fujDPOcNVq1S1fvtzddNNNYn2WZe5Tn/qUGxwcdNVq1b31rW91O3fuPEK9nR5s3Nu4H8fG/cyMeysHbBiGYRg9SFf5ABiGYRiGMTPYB4BhGIZh9CD2AWAYhmEYPYh9ABiGYRhGD2IfAIZhGIbRg9gHgGEYhmH0IPYBYBiGYRg9iH0AGIZhGEYPYh8AhmEYhtGD2AeAYRiGYfQg9gFgGIZhGD2IfQAYhmEYRg/y/wE96iSTBHTdywAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "input_data = torch.tensor(np.load(\"datasets/diffusion_input.npy\"), dtype=torch.float32)\n", + "output_data = torch.tensor(np.load(\"datasets/diffusion_output.npy\"), dtype=torch.float32)\n", + "\n", + "train_batch_size = 5000\n", + "train_input = input_data[:train_batch_size]\n", + "train_output = output_data[:train_batch_size]\n", + "\n", + "# Plot one example of the data\n", + "f, axarr = plt.subplots(1,2, figsize=(6, 10))\n", + "plot_idx = 1402\n", + "axarr[0].imshow(train_input[plot_idx])\n", + "axarr[0].title.set_text(r\"Example $\\kappa$\")\n", + "axarr[1].imshow(train_output[plot_idx])\n", + "axarr[1].title.set_text(r\"Example solution $u$\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In TorchPhysics we have to define the input and output space like always:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "K = tp.spaces.R1(\"k\")\n", + "U = tp.spaces.R1(\"u\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we create the network that learns the mapping:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "hidden_channels = 12\n", + "\n", + "model = tp.models.FNO(K, U, \n", + " fourier_layers=4, \n", + " hidden_channels=hidden_channels, \n", + " fourier_modes=(12, 12), # Here two modes need to be set (one for each space direction) \n", + " skip_connections=True, \n", + " channel_down_sample_network=torch.nn.Sequential(\n", + " torch.nn.Linear(hidden_channels, hidden_channels),\n", + " torch.nn.Tanh(),\n", + " torch.nn.Linear(hidden_channels, U.dim)\n", + " ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we create a data condition to fit the FNO to the data." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "input_data_points = tp.spaces.Points(train_input, K)\n", + "output_data_points = tp.spaces.Points(train_output, U)\n", + "\n", + "batch_size = train_batch_size // 4\n", + "\n", + "dataloader = tp.utils.PointsDataLoader((input_data_points, output_data_points),\n", + " batch_size=batch_size)\n", + "\n", + "data_condition = tp.conditions.DataCondition(model, dataloader, norm=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start training:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "You are using a CUDA device ('GeForce RTX 3090') that has Tensor Cores. To properly utilize them, you should set `torch.set_float32_matmul_precision('medium' | 'high')` which will trade-off precision for performance. For more details, read https://pytorch.org/docs/stable/generated/torch.set_float32_matmul_precision.html#torch.set_float32_matmul_precision\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n", + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 7.7 K \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "7.7 K Trainable params\n", + "0 Non-trainable params\n", + "7.7 K Total params\n", + "0.031 Total estimated model params size (MB)\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n", + "/home/tomfre/miniconda3/envs/tp_version2/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:441: The 'val_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=254` in the `DataLoader` to improve performance.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [16:23<00:00, 10.17it/s, train/loss=0.00143]" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "`Trainer.fit` stopped: `max_steps=10000` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [16:23<00:00, 10.17it/s, train/loss=0.00143]\n" + ] + } + ], + "source": [ + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.005)\n", + "solver = tp.solver.Solver([data_condition], optimizer_setting=optim)\n", + "\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\",\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=10000, \n", + " logger=False, \n", + " enable_checkpointing=False)\n", + "\n", + "trainer.fit(solver)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "GPU available: True (cuda), used: True\n", + "TPU available: False, using: 0 TPU cores\n", + "IPU available: False, using: 0 IPUs\n", + "HPU available: False, using: 0 HPUs\n", + "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6]\n", + "\n", + " | Name | Type | Params\n", + "------------------------------------------------\n", + "0 | train_conditions | ModuleList | 7.7 K \n", + "1 | val_conditions | ModuleList | 0 \n", + "------------------------------------------------\n", + "7.7 K Trainable params\n", + "0 Non-trainable params\n", + "7.7 K Total params\n", + "0.031 Total estimated model params size (MB)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [32:14<00:00, 5.17it/s, train/loss=0.00101]" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "`Trainer.fit` stopped: `max_steps=10000` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 10000/10000 [32:14<00:00, 5.17it/s, train/loss=0.00101]\n" + ] + } + ], + "source": [ + "batch_size = train_batch_size // 2\n", + "\n", + "dataloader = tp.utils.PointsDataLoader((input_data_points, output_data_points),\n", + " batch_size=batch_size)\n", + "\n", + "data_condition = tp.conditions.DataCondition(model, dataloader, norm=2)\n", + "\n", + "optim = tp.OptimizerSetting(optimizer_class=torch.optim.Adam, lr=0.001)\n", + "solver = tp.solver.Solver([data_condition], optimizer_setting=optim)\n", + "\n", + "trainer = pl.Trainer(devices=1, accelerator=\"gpu\",\n", + " num_sanity_val_steps=0,\n", + " benchmark=True,\n", + " max_steps=10000, \n", + " logger=False, \n", + " enable_checkpointing=False)\n", + "\n", + "trainer.fit(solver)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One could train further and try to optimize the network architecture as well as the training procedure some more. But for this example we are fine with this result. We obtain:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Relative error: 16.17975616455078 %\n" + ] + } + ], + "source": [ + "test_input = input_data[train_batch_size:]\n", + "test_output = output_data[train_batch_size:]\n", + "model_output = model(tp.spaces.Points(test_input, K)).as_tensor\n", + "\n", + "rel_error = torch.max(torch.abs(model_output - test_output)) / torch.max(test_output)\n", + "\n", + "print(f\"Relative error: {rel_error*100} %\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB7gAAAHHCAYAAAAlC4fkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeZgddZk+/LuqzunubJ2YBDpkwQSihDVhkRDEEZw4ARWNK4OyiICixh8QB19QICijcRm20SCiIi6DIC6oAwYxGJQhoICRRYJEiSRAdwiYpZN09zlV9f4RaO3UfTddSXVyTuf+XFdfV/L0009t36qnzqlzqoI0TVOYmZmZmZmZmZmZmZmZmZnVuHBnz4CZmZmZmZmZmZmZmZmZmVlf+AK3mZmZmZmZmZmZmZmZmZnVBV/gNjMzMzMzMzMzMzMzMzOzuuAL3GZmZmZmZmZmZmZmZmZmVhd8gdvMzMzMzMzMzMzMzMzMzOqCL3CbmZmZmZmZmZmZmZmZmVld8AVuMzMzMzMzMzMzMzMzMzOrC6WdPQNmZmZb6+joQFdXV6E1Gxoa0NTUVGhNMzMzc982MzOrJ+7bZmZm9cN9W/MFbjMzqykdHR2Y9MqhaF0dF1p3zJgxePLJJwdE8zYzM6sV7ttmZmb1w33bzMysfnR0dGC3QYPQXnDdgdK3fYHbzMxqSldXF1pXx/jbAxPRPKyYJ2ms35DglYeuQFdXV903bjMzs1rivm1mZlY/3LfNzMzqR1dXF9oBnAugsaCanQCuaG0dEH3bF7jNzKwmDR0WYOiwoJBaCYqpY2ZmZpz7tpmZWf1w3zYzM6sfQwAUdSl6IF0UHkjLYmZmA0icJojT4mqZmZlZ/3HfNjMzqx/u22ZmZvWj/OJPEYp9SMnOVcy9aMzMzMzMzMzMzMzMzMzMzPqZv8FtZmY1KUGKBMV8pLyoOmZmZsa5b5uZmdUP920zM7P6UUJxF3MH0kVhf4PbzMzMzMzMzMzMzMzMzMzqwkC6WG9mZgNIggRFPcmruEpmZmbGuG+bmZnVD/dtMzOz+lFCcc/grhZUpxb4AreZmdWkOE0Rp8Xc6qyoOmZmZsa5b5uZmdUP920zM7P64VuUc75FuZmZmZmZmZmZmZmZmZmZ1YWBdLHezMwGkAQpEhTzSfCi6piZmRnnvm1mZlY/3LfNzMzqRxm+RTnjb3CbmZmZme1El1xyCYIg2NmzYWZmZmZmZrsQ9lq0Wq3iE5/4BCZMmIAwDDF79mwAQHt7O8444wyMGTMGQRDgnHPO2fEzbGb2T3yB23Z5QRD06Wfx4sWFTveee+7BJZdcgrVr1xZa12ygSJAiLujHnyg323muv/76Xvvrvffeu7NnsU/ct816575tZr3p7Xzg/PPP75HT1NSEp59+OlPj6KOPxgEHHJCJP/roozjppJMwbtw4NDY2YuzYsXjf+96HRx99tN+Xy6xeuW+bDUxb99umpiaMHTsWs2bNwn//939jw4YNL1vjuuuuw5e+9CW8613vwre//W2ce+65AIDPfe5zuP766/HhD38Y3/3ud3HyySf39+KY2YtKBf8MFANpWcy2yXe/+90e///Od76DO+64IxPfd999C53uPffcg09/+tN4//vfjxEjRhRa22wg8C3TzAaWz3zmM5g0aVImPnny5J0wN/m5b5v1zn3bzPqCnQ9sfdG6s7MTn//85/HlL3/5Zev9+Mc/xoknnoiRI0fi9NNPx6RJk7BixQp885vfxA9/+EPceOONePvb317oMpgNBO7bZgPbS/22UqmgtbUVixcvxjnnnIPLL78cP/vZz3DQQQcBAC688MLuD5q95M4778S4ceNwxRVXZOJHHHEE5s2bt8OWw8y2KKG4W5RXCqpTC3yB23Z5J510Uo//33vvvbjjjjsycTMzM9t2xx13HA477LCdPRtmZma2E/XlfGDatGn4+te/jgsuuABjx46VeX/5y19w8sknY6+99sJvfvMb7Lbbbt2/O/vss/G6170OJ598Mh566CHstddehS2DmZlZrdu6315wwQW488478Za3vAVvfetb8dhjj2HQoEEolUoolXpeIlq9ejX9UPfq1aux3377FTaPSZKgq6sLTU1NhdU0s12Lb1FultPTTz+ND3zgA2hpaUFjYyP2339/XHfddT1yNmzYgHPOOQcTJ05EY2Mjdt99d7zxjW/Egw8+CGDL803OO+88AMCkSZO6bxuzYsUKOd0TTzwR48eP7/5/pVLBKaecgtGjR+Oee+4pfkHNdrI4TQv9MbPatXnzZkyZMgVTpkzB5s2bu+MvvPAC9thjDxx55JGI4xjAP54RtmzZMrznPe9Bc3MzRo0ahbPPPhsdHR096valZ/9z7umnn46xY8eisbERkyZNwoc//GF0dXW9bN/OM527774br3nNa9DU1IS9994bX/va1/q8nnbbbTfMmTMnEz/ssMPw5je/uc91zPqD+7aZFeWTn/wk4jjG5z//+V7zvvSlL2HTpk249tpre1zcBoDRo0fja1/7GjZu3IgvfvGLfZqu+6ztSty3zXY9b3jDG3DRRRfhb3/7G773ve8B6PkM7hUrViAIAvz617/Go48+2uOxnUEQ4Mknn8Stt96aeT3c2dmJefPmYfLkyWhsbMSECRPwiU98Ap2dnT2mHwQB5syZg//5n//B/vvvj8bGRixcuBBA315TvzQfP/jBD/DZz34W48ePR1NTE/71X/8Vy5cvzyzvfffdhze96U14xStegSFDhuCggw7CVVdd1SNn2bJleNe73oWRI0eiqakJhx12GH72s58Vsr7NiuRblHMDaVnM+l1bWxuOOOKI7oa822674Re/+AVOP/10rF+/Hueccw4A4KyzzsIPf/hDzJkzB/vttx+ef/553H333XjsscdwyCGH4B3veAf+/Oc/4/vf/z6uuOIKjB49GgAyL8r/2cMPP9x9+5gNGzbgne98J5544gn83//9H/bZZ59+X3YzM7PtsW7dOqxZs6ZHLAgCjBo1CoMGDcK3v/1tvPa1r8WnPvUpXH755QCAj370o1i3bh2uv/56RFHU42/f8573YOLEiZg/fz7uvfde/Pd//zf+/ve/4zvf+Q6AvvdsAHjmmWdw+OGHY+3atfjgBz+IKVOm4Omnn8YPf/hDbNq0qde+nWc6Dz/8MP7t3/4Nu+22Gy655BJUq1XMmzcPLS0tL7v+nnnmGaxZswZTp07tEY/jGI8++ije+MY39nlbmJmZ7SzsfOClvvqSSZMm4ZRTTsHXv/51nH/++fJb3D//+c8xceJEvO51r6O//5d/+RdMnDgRt95668vOl/usmZntCk4++WR88pOfxC9/+UuceeaZPX6322674bvf/S4++9nPor29HfPnzwew5bGd3/3ud3Huuedi/Pjx+PjHP96dnyQJ3vrWt+Luu+/GBz/4Qey77754+OGHccUVV+DPf/4zbrnllh7TuPPOO/GDH/wAc+bMwejRozFx4sRcr6kB4POf/zzCMMR//Md/YN26dfjiF7+I973vfbjvvvu6c+644w685S1vwR577IGzzz4bY8aMwWOPPYb//d//xdlnnw0AePTRR/Ha174W48aNw/nnn48hQ4bgBz/4AWbPno0f/ehHfsSJWR3wBW6zHD71qU8hjmM8/PDDGDVqFIAtF7NPPPFEXHLJJfjQhz6EQYMG4dZbb8WZZ56Jyy67rPtvP/GJT3T/+6CDDsIhhxyC73//+5g9ezYmTpzY63S7urrw+OOP4y1veQtaW1vxpje9CUEQYMmSJRgzZky/LKvZzpa8+FNULTPbuWbOnJmJNTY2dn/revr06fjEJz6BL3zhC3j729+OtrY23Hjjjbjyyivx6le/OvO3kyZNwk9/+lMAWy6ENzc34+qrr8Z//Md/4KCDDupzzwa23K6ttbUV9913X4/buH3mM59Bmqa99u2zzz67z9O5+OKLkaYpfvvb32LPPfcEALzzne/EgQce+LLr76GHHgKAzBvvy5YtQ0dHR59qmPUn920z6wt2PpCSb39+6lOfwne+8x184QtfyHzbCthyofyZZ57B2972tl6nd9BBB+FnP/sZNmzYgGHDhsk891nb1bhvm+2axo8fj+HDh+Mvf/lL5ndDhgzBSSedhG984xuIoqjH4ztPOukkXHjhhRg3blyP+Pe+9z386le/wl133YWjjjqqO37AAQfgrLPOwj333IMjjzyyO/7444/j4Ycf7nGr8zPOOKPPr6kBoKOjA0uXLkVDQwMA4BWveAXOPvtsPPLIIzjggAMQxzE+9KEPYY899sDSpUt73G79n885zj77bOy55574/e9/j8bGRgDARz7yERx11FH4//6//88XuK2mlFHcM7iLqlMLfItysz5K0xQ/+tGPcPzxxyNNU6xZs6b7Z9asWVi3bl33LchHjBiB++67D88880wh0162bBmq1SoaGhowY8YMDBkyBHfddZcvbtuAFiMt9MfMdq4FCxbgjjvu6PHzi1/8okfOJZdcgv333x+nnnoqPvKRj+D1r389/t//+3+03kc/+tEe///Yxz4GALjtttty9ewkSXDLLbfg+OOPp88Efel2bUye6cRxjNtvvx2zZ8/uvrgNbPk0/KxZs152/T300EMIwxAHHHBAj/gf//hHAPAb77bTuW+bWV+w8wFmr732wsknn4xrr70Wzz77bOb3GzZsAIBeL1r/8+/Xr1/fa577rO1q3LfNdl1Dhw7t7qPb6+abb8a+++6LKVOm9Hg9/IY3vAEA8Otf/7pH/utf//oeF7fzvKZ+yWmnndZ9cRtA951c/vrXvwIA/vCHP+DJJ5/EOeeck3mW+Euv71944QXceeedeM973oMNGzZ0T/P555/HrFmz8MQTT+Dpp58uZB2ZFaFc8M9A4W9wm/XRc889h7Vr1+Laa6/FtddeS3NWr14NAPjiF7+IU089FRMmTMChhx6KN73pTTjllFOw1157bdO0H374YQDAZz/7WURRhGHDhmHIkCHbtiBmZmY7weGHH04vIP+zhoYGXHfddd3PqP7Wt74lLzC/6lWv6vH/vffeG2EYYsWKFbl69nPPPYf169dn3tDui7zT2bx5c2a+AWCfffbBbbfd1uu0/vjHP2Ly5MkYPHhwj/jSpUtRLpcxZcqU3PNvZma2o/XlfOAlF154Ib773e/i85//fOZb3C9duH65N+j7eiHcfdbMzHYV7e3t2H333Qup9cQTT+Cxxx6Tj9186fXwSyZNmtTj/3leU7/knz8wDmz5BjcA/P3vfweA7m+n9/Yaf/ny5UjTFBdddBEuuugiOd1x48bJGma28/kCt1kfJcmWmy6ddNJJOPXUU2nOS8/Ifs973oPXve51+MlPfoJf/vKX+NKXvoQvfOEL+PGPf4zjjjsu97Rful3avHnzMHjwYJx33nn46U9/itmzZ2/bwpjVgTjd8lNULTOrD7fffjuALbcde+KJJzIvgJV/vhCep2dvjx01HWDLh922vm0qAPz+97/HPvvsg3J5IH0G1+qR+7aZFW2vvfbCSSedhGuvvRbnn39+j98NHz4ce+yxR/drZeWhhx7CuHHj0Nzc3Gue+6ztaty3zXZNq1atwrp16zB58uRC6iVJggMPPBCXX345/f2ECRN6/P+fbzX+0t8D+V5TR1FE89gjT5SXpvsf//Ef8o5qRa0jsyKUUNzF3IF0UXggLYtZv9ptt90wbNgwxHFMnxu2tT322AMf+chH8JGPfASrV6/GIYccgs9+9rPdF7h7u+Xp1l56NsnFF1+M9vZ2fO5zn8N//ud/+gK3mZkNKA899BA+85nP4LTTTsPSpUtxxhln4OGHH8bw4cMzuVtf/F6+fDmSJMHEiRNz9ezddtsNzc3NeOSRR3rNY30773QGDRqEJ554IvO7xx9/vNe/TZIEjz/+eOYZYKtXr8bdd9+N97znPb3+vZmZWb268MIL8b3vfQ9f+MIXMr97y1vegq9//eu4++67ezz38yW//e1vsWLFCnzoQx/qdRrus2Zmtqv47ne/CwB9ekxWX+y999744x//iH/913/N9V73S/K+397XeQKARx55RNZ86S6r5XK5sOma2Y7nZ3Cb9VEURXjnO9+JH/3oR/RN8Oeeew7Almdsrlu3rsfvdt99d4wdOxadnZ3dsZduMb527dqXnfbDDz/c/Wm1oUOH4mMf+xgeeOCBl72dqVk9Swr+MbPaVqlU8P73vx9jx47FVVddheuvvx5tbW0499xzaf6CBQt6/P/LX/4yAOC4447rc88GgDAMMXv2bPz85z/H/fffn8l96VPgrG/nmU4URZg1axZuueUWPPXUU93xxx57rPtb60ocx6hUKti0aVN3rFqt4kMf+hCq1aqfC2o1wX3bzPrD3nvvjZNOOglf+9rX0Nra2uN35513HgYNGoQPfehDeP7553v87oUXXsBZZ53VfQe03rjP2q7Ifdts13PnnXfi0ksvxaRJk/C+972vkJrvec978PTTT+PrX/965nebN2/Gxo0be/37PK+p++qQQw7BpEmTcOWVV2bed3/p9f3uu++Oo48+Gl/72tfw7LPPFjJds/5UQnHP3x5I33oeSMti1u8+//nP49e//jWmT5+OM888E/vttx9eeOEFPPjgg/jVr36FF154ARs2bMD48ePxrne9C1OnTsXQoUPxq1/9Cr///e9x2WWXddc69NBDAQCf+tSn8O///u8ol8s4/vjjM8/WXrt2LVatWtXjdixnn302LrvsMlx66aV405vetGMW3mwHSxAgRv5Pf6paZrZz/eIXv8CyZcsy8SOPPBJ77bUX/vM//xNLly7FokWLMGzYMBx00EG4+OKLceGFF+Jd73pXpt89+eSTeOtb34pjjz0WS5Yswfe+9z28973v7b69aF969ks+97nP4Ze//CVe//rX44Mf/CD23XdfPPvss7j55ptx9913Y8SIEbJv55nOpz/9aSxcuBCve93r8JGPfATVahVf/vKXsf/++/d6i9VyuYyDDjoIX/3qVzFo0CAMGjQIN998c/ft3fzGu9UC920z6y+f+tSn8N3vfhePP/449t9//+74q171Knz729/G+973Phx44IE4/fTTMWnSJKxYsQLf/OY3sWbNGnz/+9/v/iaX4j5ruyL3bbOB7aXX39VqFW1tbbjzzjtxxx134JWvfCV+9rOfoampqZDpnHzyyfjBD36As846C7/+9a/x2te+FnEcY9myZfjBD36A22+/HYcddlivNfK8pu6LMAzx1a9+FccffzymTZuG0047DXvssQeWLVuGRx99tPsD5gsWLMBRRx2FAw88EGeeeSb22msvtLW1YcmSJVi1ahX++Mc/bvN6MSuab1HODaRlMet3LS0t+N3vfofPfOYz+PGPf4yrr74ao0aNwv777999y7TBgwfjIx/5CH75y1/ixz/+MZIkweTJk3H11Vfjwx/+cHet17zmNbj00ktxzTXXYOHChUiSBE8++WTmAvfDDz8MoOfzRkaOHIkPfehDuPzyy3HHHXfgjW984w5YejMzs2138cUX0/i3vvUtrF27Fp/73OcwZ84cHHPMMd2/O//88/HTn/4UZ555Jh599FGMGDGi+3c33XQTLr74Ypx//vkolUqYM2cOvvSlL3X/vi89+yXjxo3Dfffdh4suugj/8z//g/Xr12PcuHE47rjjMHjwYAC6b0+cOLHP0znooINw++23Y+7cubj44osxfvx4fPrTn8azzz77ss8Q/da3voUzzzwTX/rSl7D33nvj7LPPRhRFuOeee/zGu5mZDWiTJ0/GSSedhG9/+9uZ37373e/GlClTMH/+/O6L2qNGjcIxxxyDT37ykzjggAP6NA33WTMzG0heev3d0NCAkSNH4sADD8SVV16J0047DcOGDStsOmEY4pZbbsEVV1yB73znO/jJT36CwYMHY6+99sLZZ5+NV7/61S9bI89r976aNWsWfv3rX+PTn/40LrvsMiRJgr333htnnnlmd85+++2H+++/H5/+9Kdx/fXX4/nnn8fuu++Ogw8+WL5/YWa1JUhfui+DmZlZDVi/fj2GDx+O+x9twdBhxTxJo31DgsP2b8O6devQ3NxcSE0z2zkuueQSfPrTn8Zzzz2H0aNH7+zZMdvluW+bmZnVD/dtMzOz+vFS374dwJCXze6bjQBmAQOib/sZ3GZmZmZmZmZmZmZmZmZmlrFgwQJMnDgRTU1NmD59On73u9/1mn/zzTdjypQpaGpqwoEHHojbbrstk/PYY4/hrW99K4YPH44hQ4bgNa95DZ566qk+z5MvcJuZWU2KX3wmWFE/ZmZm1n/ct83MzOqH+7aZmVn9KBX8k9dNN92EuXPnYt68eXjwwQcxdepUzJo1C6tXr6b599xzD0488UScfvrp+MMf/oDZ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Nvr/X5fH0qtw3f6joq2f63ax16/h6jYqL/3k1VZjqMsb3FZsF198MTZt2jTgOxsawRve8AZs3LgRu3fvxo9+9CNceOGFuP/++0e6W7nZsWMHLrnkEtx3331oa2sb6e7UxHvf+97+f5922mlYuHAhjj/+ePzwhz/EqFGjRrBn+YjjGPPnz8dVV10FAHjTm96ETZs2YdWqVbjwwgtHuHdDkLz0BJbc2jIzplHrNtDYtdt123W77rhumw0L1+1ict123a47rttmw8J1u5hct12360/1pZ+82moMdfmI8smTJ6NUKqGzs3NAvLOzE1OnTh2hXtXOy+vUCOu7fPly/OQnP8EvfvELHHvssf3xqVOnolwuo6ura0B+kdaxpaUFJ554IubNm4err74ac+bMwTe+8Y2GWDeg79EjO3fuxOmnn46mpiY0NTXh/vvvxze/+U00NTWhvb29IdbzlSZOnIjXv/712Lp1a0Psx2OOOQYnn3zygNhJJ53U/3iZRpprrL64bqP//0Vb30au20Bj127X7eLvQ9dtGymu2+j/f9HW13W7a0B+kdbPdbv4+9B120aK6zb6/1+09XXd7hqQX6T1c90u/j503T4y1OUN7paWFsybNw9r1qzpj8VxjDVr1mDRokUj2LPaOOGEEzB16tQB69vd3Y2HH364MOubJAmWL1+OH//4x/i3f/s3nHDCCQN+P2/ePDQ3Nw9Yxy1btmD79u2FWceDxXGM3t7ehlm3d77znXjsscewcePG/p/58+fj/PPP7/93I6znK+3duxe/+c1vcMwxxzTEfjzzzDOxZcuWAbEnn3wSxx9/PIDizTVJHOf6Y7Xjul3fxxJzJNZtoLFqt+t28feh67br9khx3a7vY4lx3S7++rluF38fum67bo8U1+36PpYY1+3ir5/rdvH3YaPV7b5PXVdy+mmcT3DX7SPKV6xYgQsvvBDz58/HggULcP3112Pfvn1YtmzZSHdtSPbu3YutW7f2///pp5/Gxo0bcfTRR+O4447Dpz71KXz5y1/GrFmzcMIJJ+Dzn/88pk2bhqVLl45cpzO4+OKLcdttt+F//+//jXHjxvV/T8GECRMwatQoTJgwARdddBFWrFiBo48+GuPHj8cnP/lJLFq0CGecccYI9/7VXX755Xjve9+L4447Dnv27MFtt92GtWvX4t577y38ur1s3Lhx/d8F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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot one example of data\n", + "plot_idx = 103 # <- value between 0 and 999\n", + "\n", + "f, axarr = plt.subplots(1,4, figsize=(20, 6))\n", + "img = axarr[0].imshow(test_input[plot_idx])\n", + "axarr[0].title.set_text(r\"Test $\\kappa$\")\n", + "plt.colorbar(img, ax=axarr[0], shrink=0.75)\n", + "\n", + "sol_min = torch.min(test_output[plot_idx])\n", + "sol_max = torch.max(test_output[plot_idx])\n", + "img = axarr[1].imshow(test_output[plot_idx], vmin=sol_min, vmax=sol_max)\n", + "axarr[1].title.set_text(r\"Expected $u$\")\n", + "plt.colorbar(img, ax=axarr[1], shrink=0.75)\n", + "\n", + "img = axarr[2].imshow(model_output[plot_idx].detach(), vmin=sol_min, vmax=sol_max)\n", + "axarr[2].title.set_text(r\"FNO $u$\")\n", + "plt.colorbar(img, ax=axarr[2], shrink=0.75)\n", + "\n", + "error = torch.abs(model_output[plot_idx].detach() - test_output[plot_idx])\n", + "img = axarr[3].imshow(error, cmap='jet')\n", + "axarr[3].title.set_text(r\"Difference\")\n", + "plt.colorbar(img, ax=axarr[3], shrink=0.75)\n", + "\n", + "plt.tight_layout()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tp_version2", + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From ef18b746048cbf266221413507e1e8544f530e19 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 18 Nov 2024 16:04:07 +0100 Subject: [PATCH 22/25] fix callback Signed-off-by: Tom Freudenberg --- src/torchphysics/utils/callbacks.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/torchphysics/utils/callbacks.py b/src/torchphysics/utils/callbacks.py index 5d757c3a..0f7dddd4 100644 --- a/src/torchphysics/utils/callbacks.py +++ b/src/torchphysics/utils/callbacks.py @@ -28,17 +28,6 @@ class WeightSaveCallback(Callback): save_final_model: True Whether the model should always be saved after the last iteration. """ - - def __init__( - self, - model, - path, - name, - check_interval, - save_initial_model=False, - save_final_model=True, - ): - def __init__( self, model, @@ -113,20 +102,6 @@ class PlotterCallback(Callback): the plot. See https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.html for possible arguments of each underlying object. """ - - def __init__( - self, - model, - plot_function, - point_sampler, - log_name="plot", - check_interval=200, - angle=[30, 30], - plot_type="", - **kwargs - ): - """ - def __init__( self, model, From 524d15b1ec473de4f8e9080d7c9992e9cb9415ed Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 18 Nov 2024 16:59:23 +0100 Subject: [PATCH 23/25] fix callbacks Signed-off-by: Tom Freudenberg --- src/torchphysics/utils/callbacks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/torchphysics/utils/callbacks.py b/src/torchphysics/utils/callbacks.py index 0f7dddd4..42626f09 100644 --- a/src/torchphysics/utils/callbacks.py +++ b/src/torchphysics/utils/callbacks.py @@ -55,7 +55,7 @@ def on_train_start(self, trainer, pl_module): ) def on_train_batch_start( - self, trainer, pl_module, batch, batch_idx, dataloader_idx + self, trainer, pl_module, batch, batch_idx, dataloader_idx=0 ): if (self.check_interval > 0 and batch_idx > 0) and ( (batch_idx - 1) % self.check_interval == 0 @@ -128,7 +128,7 @@ def on_train_start(self, trainer, pl_module): self.point_sampler.sample_points(device=pl_module.device) def on_train_batch_end( - self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx + self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx=0 ): if batch_idx % self.check_interval == 0: fig = plot( @@ -185,7 +185,7 @@ def __init__(self, path, name, check_interval=200, weights_only=False): self.weights_only = weights_only def on_train_batch_end( - self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx + self, trainer, pl_module, outputs, batch, batch_idx, dataloader_idx=0 ): if batch_idx % self.check_interval == 0: trainer.save_checkpoint( From 9313b4837b891fded4ae896d3b21091fb0d9ebc6 Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 18 Nov 2024 17:07:49 +0100 Subject: [PATCH 24/25] update setup file and dont test optional tensorboard Signed-off-by: Tom Freudenberg --- setup.cfg | 8 ++++---- tests/tests_utils/test_callbacks.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/setup.cfg b/setup.cfg index 7f36c77f..b99499a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -46,15 +46,12 @@ package_dir = # new major versions. This works if the required packages follow Semantic Versioning. # For more information, check out https://semver.org/. install_requires = - torch>=2.0.0, <2.4 + torch>=2.0.0 pytorch-lightning>=2.0.0 numpy>=1.20.2, <2.0 matplotlib>=3.0.0 scipy>=1.6.3 importlib-metadata - trimesh>=3.9.19 - shapely>=1.7.1 - rtree>=0.9.7 jupyter [options.packages.find] @@ -70,6 +67,9 @@ exclude = # `pip install torchphysics[all]` like: all = networkx>=2.5.1 + trimesh>=3.9.19 + shapely>=1.7.1 + rtree>=0.9.7 # Add here test requirements (semicolon/line-separated) testing = diff --git a/tests/tests_utils/test_callbacks.py b/tests/tests_utils/test_callbacks.py index 2bf07c70..2b89a73f 100644 --- a/tests/tests_utils/test_callbacks.py +++ b/tests/tests_utils/test_callbacks.py @@ -66,15 +66,15 @@ def test_weight_save_callback_start(): helper_cleaner("./tests/test_weight_init.pt") -def test_plotter_callback(): - tensorboard_logger = pl_loggers.TensorBoardLogger('./tests/logdata') - model, solver, domain = helper_setup() - plot_sampler = tp.samplers.PlotSampler(domain, 100) - plot_callback = tp.callbacks.PlotterCallback(model, lambda u : u, - plot_sampler) - trainer = pl.Trainer( max_steps=2, callbacks=[plot_callback], logger=tensorboard_logger) - trainer.fit(solver) - helper_cleaner("./tests/logdata", True) +# def test_plotter_callback(): +# tensorboard_logger = pl_loggers.TensorBoardLogger('./tests/logdata') +# model, solver, domain = helper_setup() +# plot_sampler = tp.samplers.PlotSampler(domain, 100) +# plot_callback = tp.callbacks.PlotterCallback(model, lambda u : u, +# plot_sampler) +# trainer = pl.Trainer( max_steps=2, callbacks=[plot_callback], logger=tensorboard_logger) +# trainer.fit(solver) +# helper_cleaner("./tests/logdata", True) def test_state_checkpoint(): From a9d4f3a344c35ce9c5ebdffa920274c1ade4d1af Mon Sep 17 00:00:00 2001 From: Tom Freudenberg Date: Mon, 18 Nov 2024 17:12:33 +0100 Subject: [PATCH 25/25] try to fix github testing Signed-off-by: Tom Freudenberg --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 218b449f..7c7708a6 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -29,7 +29,7 @@ jobs: python -m pip install --upgrade pip pip install -U setuptools setuptools_scm wheel python -m pip install flake8 pytest pytest-cov - python -m pip install -e . + python -m pip install -e .[all] if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with flake8 run: |