diff --git a/.github/workflows/export_tutorials.yml b/.github/workflows/export_tutorials.yml new file mode 100644 index 000000000..6a7d0f375 --- /dev/null +++ b/.github/workflows/export_tutorials.yml @@ -0,0 +1,72 @@ +name: "Export Tutorials" + +on: + push: + branches: + - "**" # Run on push on all branches + paths: + - 'tutorials/**/*.ipynb' + +jobs: + export_tutorials: + permissions: write-all + runs-on: ubuntu-latest + env: + TUTORIAL_TIMEOUT: 1200s + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + # Dependencies for tutorials + python3 -m pip install --upgrade pip .[tutorial] black[jupyter] + - name: Setup FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v2 + + - id: files + uses: jitterbit/get-changed-files@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + format: space-delimited + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - name: Export tutorials to .py and .html + run: | + set -x + for file in ${{ steps.files.outputs.all }}; do + if [[ $file == *.ipynb ]]; then + filename=$(basename $file) + pyfilename=$(echo ${filename%?????})py + timeout --signal=SIGKILL $TUTORIAL_TIMEOUT python -Xfrozen_modules=off -m jupyter nbconvert --execute $file --to python --output $pyfilename --output-dir=$(dirname $file) + htmlfilename=$(echo ${filename%?????} | sed -e 's/-//g')html + htmldir="docs/source"/$(echo ${file%?????} | sed -e 's/-//g')html + timeout --signal=SIGKILL $TUTORIAL_TIMEOUT python -Xfrozen_modules=off -m jupyter nbconvert --execute $file --to html --output $htmlfilename --output-dir=$htmldir + fi + done + set +x + + - name: Run formatter + run: black tutorials/ + + - uses: benjlevesque/short-sha@v2.1 + id: short-sha + + - name: Remove unwanted files + run: | + rm -rf build/ + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5.0.2 + with: + labels: maintenance + title: Export tutorial changed in ${{ steps.short-sha.outputs.sha }} + branch: export-tutorial-${{ steps.short-sha.outputs.sha }} + commit-message: export tutorials changed in ${{ steps.short-sha.outputs.sha }} + delete-branch: true diff --git a/tutorials/tutorial1/tutorial.ipynb b/tutorials/tutorial1/tutorial.ipynb index 4a0d205c2..b2b7fbd84 100644 --- a/tutorials/tutorial1/tutorial.ipynb +++ b/tutorials/tutorial1/tutorial.ipynb @@ -87,25 +87,27 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import warnings\n", "\n", "from pina.problem import SpatialProblem, TimeDependentProblem\n", "from pina.domain import CartesianDomain\n", "\n", - "warnings.filterwarnings('ignore')\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", "\n", "class TimeSpaceODE(SpatialProblem, TimeDependentProblem):\n", - " \n", - " output_variables = ['u']\n", - " spatial_domain = CartesianDomain({'x': [0, 1]})\n", - " temporal_domain = CartesianDomain({'t': [0, 1]})\n", + "\n", + " output_variables = [\"u\"]\n", + " spatial_domain = CartesianDomain({\"x\": [0, 1]})\n", + " temporal_domain = CartesianDomain({\"t\": [0, 1]})\n", "\n", " # other stuff ..." ] @@ -152,6 +154,7 @@ "from pina.domain import CartesianDomain\n", "from pina.equation import Equation, FixedValue\n", "\n", + "\n", "# defining the ode equation\n", "def ode_equation(input_, output_):\n", "\n", @@ -164,6 +167,7 @@ " # calculate the residual and return it\n", " return u_x - u\n", "\n", + "\n", "class SimpleODE(SpatialProblem):\n", "\n", " output_variables = [\"u\"]\n", diff --git a/tutorials/tutorial10/tutorial.ipynb b/tutorials/tutorial10/tutorial.ipynb index 8f9baf797..748cb163a 100644 --- a/tutorials/tutorial10/tutorial.ipynb +++ b/tutorials/tutorial10/tutorial.ipynb @@ -25,16 +25,17 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", - " # get the data\n", - " !mkdir \"data\"\n", - " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS.mat\" -O \"data/Data_KS.mat\"\n", - " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS2.mat\" -O \"data/Data_KS2.mat\"\n", + " !pip install \"pina-mathlab\"\n", + " # get the data\n", + " !mkdir \"data\"\n", + " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS.mat\" -O \"data/Data_KS.mat\"\n", + " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS2.mat\" -O \"data/Data_KS2.mat\"\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", @@ -46,7 +47,7 @@ "from pina.solver import SupervisedSolver\n", "from pina.problem.zoo import SupervisedProblem\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -106,17 +107,24 @@ ], "source": [ "# load data\n", - "data=io.loadmat(\"data/Data_KS.mat\")\n", + "data = io.loadmat(\"data/Data_KS.mat\")\n", "\n", "# converting to label tensor\n", - "initial_cond_train = LabelTensor(torch.tensor(data['initial_cond_train'], dtype=torch.float), ['t','x','u0'])\n", - "initial_cond_test = LabelTensor(torch.tensor(data['initial_cond_test'], dtype=torch.float), ['t','x','u0'])\n", - "sol_train = LabelTensor(torch.tensor(data['sol_train'], dtype=torch.float), ['u'])\n", - "sol_test = LabelTensor(torch.tensor(data['sol_test'], dtype=torch.float), ['u'])\n", - "\n", - "print('Data Loaded')\n", - "print(f' shape initial condition: {initial_cond_train.shape}')\n", - "print(f' shape solution: {sol_train.shape}')" + "initial_cond_train = LabelTensor(\n", + " torch.tensor(data[\"initial_cond_train\"], dtype=torch.float),\n", + " [\"t\", \"x\", \"u0\"],\n", + ")\n", + "initial_cond_test = LabelTensor(\n", + " torch.tensor(data[\"initial_cond_test\"], dtype=torch.float), [\"t\", \"x\", \"u0\"]\n", + ")\n", + "sol_train = LabelTensor(\n", + " torch.tensor(data[\"sol_train\"], dtype=torch.float), [\"u\"]\n", + ")\n", + "sol_test = LabelTensor(torch.tensor(data[\"sol_test\"], dtype=torch.float), [\"u\"])\n", + "\n", + "print(\"Data Loaded\")\n", + "print(f\" shape initial condition: {initial_cond_train.shape}\")\n", + "print(f\" shape solution: {sol_train.shape}\")" ] }, { @@ -151,35 +159,58 @@ "# helper function\n", "def plot_trajectory(coords, real, no_sol=None):\n", " # find the x-t shapes\n", - " dim_x = len(torch.unique(coords.extract('x')))\n", - " dim_t = len(torch.unique(coords.extract('t')))\n", + " dim_x = len(torch.unique(coords.extract(\"x\")))\n", + " dim_t = len(torch.unique(coords.extract(\"t\")))\n", " # if we don't have the Neural Operator solution we simply plot the real one\n", " if no_sol is None:\n", " fig, axs = plt.subplots(1, 1, figsize=(15, 5), sharex=True, sharey=True)\n", - " c = axs.imshow(real.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n", - " axs.set_title('Real solution')\n", + " c = axs.imshow(\n", + " real.reshape(dim_t, dim_x).T.detach(),\n", + " extent=[0, 50, 0, 64],\n", + " cmap=\"PuOr_r\",\n", + " aspect=\"auto\",\n", + " )\n", + " axs.set_title(\"Real solution\")\n", " fig.colorbar(c, ax=axs)\n", - " axs.set_xlabel('t')\n", - " axs.set_ylabel('x')\n", + " axs.set_xlabel(\"t\")\n", + " axs.set_ylabel(\"x\")\n", " # otherwise we plot the real one, the Neural Operator one, and their difference\n", " else:\n", " fig, axs = plt.subplots(1, 3, figsize=(15, 5), sharex=True, sharey=True)\n", - " axs[0].imshow(real.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n", - " axs[0].set_title('Real solution')\n", - " axs[1].imshow(no_sol.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n", - " axs[1].set_title('NO solution')\n", - " c = axs[2].imshow((real - no_sol).abs().reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n", - " axs[2].set_title('Absolute difference')\n", + " axs[0].imshow(\n", + " real.reshape(dim_t, dim_x).T.detach(),\n", + " extent=[0, 50, 0, 64],\n", + " cmap=\"PuOr_r\",\n", + " aspect=\"auto\",\n", + " )\n", + " axs[0].set_title(\"Real solution\")\n", + " axs[1].imshow(\n", + " no_sol.reshape(dim_t, dim_x).T.detach(),\n", + " extent=[0, 50, 0, 64],\n", + " cmap=\"PuOr_r\",\n", + " aspect=\"auto\",\n", + " )\n", + " axs[1].set_title(\"NO solution\")\n", + " c = axs[2].imshow(\n", + " (real - no_sol).abs().reshape(dim_t, dim_x).T.detach(),\n", + " extent=[0, 50, 0, 64],\n", + " cmap=\"PuOr_r\",\n", + " aspect=\"auto\",\n", + " )\n", + " axs[2].set_title(\"Absolute difference\")\n", " fig.colorbar(c, ax=axs.ravel().tolist())\n", " for ax in axs:\n", - " ax.set_xlabel('t')\n", - " ax.set_ylabel('x')\n", + " ax.set_xlabel(\"t\")\n", + " ax.set_ylabel(\"x\")\n", " plt.show()\n", "\n", + "\n", "# a sample trajectory (we use the sample 5, feel free to change)\n", "sample_number = 20\n", - "plot_trajectory(coords=initial_cond_train[sample_number].extract(['x', 't']),\n", - " real=sol_train[sample_number].extract('u'))\n" + "plot_trajectory(\n", + " coords=initial_cond_train[sample_number].extract([\"x\", \"t\"]),\n", + " real=sol_train[sample_number].extract(\"u\"),\n", + ")" ] }, { @@ -300,7 +331,12 @@ ], "source": [ "# initialize problem\n", - "problem = SupervisedProblem(initial_cond_train, sol_train, input_variables=initial_cond_train.labels, output_variables=sol_train.labels)\n", + "problem = SupervisedProblem(\n", + " initial_cond_train,\n", + " sol_train,\n", + " input_variables=initial_cond_train.labels,\n", + " output_variables=sol_train.labels,\n", + ")\n", "# initialize solver\n", "solver = SupervisedSolver(problem=problem, model=model)\n", "# train, only CPU and avoid model summary at beginning of training (optional)\n", @@ -343,9 +379,11 @@ "source": [ "sample_number = 2\n", "no_sol = solver(initial_cond_test)\n", - "plot_trajectory(coords=initial_cond_test[sample_number].extract(['x', 't']),\n", - " real=sol_test[sample_number].extract('u'),\n", - " no_sol=no_sol[5])" + "plot_trajectory(\n", + " coords=initial_cond_test[sample_number].extract([\"x\", \"t\"]),\n", + " real=sol_test[sample_number].extract(\"u\"),\n", + " no_sol=no_sol[5],\n", + ")" ] }, { @@ -373,15 +411,19 @@ "source": [ "from pina.loss import PowerLoss\n", "\n", - "error_metric = PowerLoss(p=2) # we use the MSE loss\n", + "error_metric = PowerLoss(p=2) # we use the MSE loss\n", "\n", "with torch.no_grad():\n", " no_sol_train = solver(initial_cond_train)\n", - " err_train = error_metric(sol_train.extract('u'), no_sol_train).mean() # we average the error over trajectories\n", + " err_train = error_metric(\n", + " sol_train.extract(\"u\"), no_sol_train\n", + " ).mean() # we average the error over trajectories\n", " no_sol_test = solver(initial_cond_test)\n", - " err_test = error_metric(sol_test.extract('u'),no_sol_test).mean() # we average the error over trajectories\n", - " print(f'Training error: {float(err_train):.3f}')\n", - " print(f'Testing error: {float(err_test):.3f}')" + " err_test = error_metric(\n", + " sol_test.extract(\"u\"), no_sol_test\n", + " ).mean() # we average the error over trajectories\n", + " print(f\"Training error: {float(err_train):.3f}\")\n", + " print(f\"Testing error: {float(err_test):.3f}\")" ] }, { diff --git a/tutorials/tutorial11/tutorial.ipynb b/tutorials/tutorial11/tutorial.ipynb index 3506ca7ec..2a23eaecf 100644 --- a/tutorials/tutorial11/tutorial.ipynb +++ b/tutorials/tutorial11/tutorial.ipynb @@ -24,12 +24,13 @@ "outputs": [], "source": [ "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import torch\n", "import warnings\n", @@ -42,7 +43,7 @@ "from pina.domain import CartesianDomain\n", "from pina.equation import Equation, FixedValue\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -70,6 +71,7 @@ " # calculate the residual and return it\n", " return u_x - u\n", "\n", + "\n", "class SimpleODE(SpatialProblem):\n", "\n", " output_variables = [\"u\"]\n", @@ -101,7 +103,7 @@ " layers=[10, 10],\n", " func=torch.nn.Tanh,\n", " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", + " input_dimensions=len(problem.input_variables),\n", ")\n", "\n", "# create the PINN object\n", @@ -437,20 +439,22 @@ ], "source": [ "model = FeedForward(\n", - " layers=[10, 10],\n", - " func=torch.nn.Tanh,\n", - " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", - " )\n", + " layers=[10, 10],\n", + " func=torch.nn.Tanh,\n", + " output_dimensions=len(problem.output_variables),\n", + " input_dimensions=len(problem.input_variables),\n", + ")\n", "pinn = PINN(problem, model)\n", - "trainer = Trainer(solver=pinn,\n", - " accelerator='cpu',\n", - " logger=True,\n", - " callbacks=[NaiveMetricTracker()], # adding a callbacks\n", - " enable_model_summary=False,\n", - " train_size=1.0,\n", - " val_size=0.0,\n", - " test_size=0.0)\n", + "trainer = Trainer(\n", + " solver=pinn,\n", + " accelerator=\"cpu\",\n", + " logger=True,\n", + " callbacks=[NaiveMetricTracker()], # adding a callbacks\n", + " enable_model_summary=False,\n", + " train_size=1.0,\n", + " val_size=0.0,\n", + " test_size=0.0,\n", + ")\n", "trainer.train()" ] }, @@ -486,7 +490,7 @@ } ], "source": [ - "trainer.callbacks[0].saved_metrics[:3] # only the first three epochs" + "trainer.callbacks[0].saved_metrics[:3] # only the first three epochs" ] }, { @@ -615,19 +619,21 @@ "seed_everything(42, workers=True)\n", "\n", "model = FeedForward(\n", - " layers=[10, 10],\n", - " func=torch.nn.Tanh,\n", - " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", - " )\n", + " layers=[10, 10],\n", + " func=torch.nn.Tanh,\n", + " output_dimensions=len(problem.output_variables),\n", + " input_dimensions=len(problem.input_variables),\n", + ")\n", "\n", "pinn = PINN(problem, model)\n", - "trainer = Trainer(solver=pinn,\n", - " accelerator='cpu',\n", - " deterministic=True, # setting deterministic=True ensure reproducibility when a seed is imposed\n", - " max_epochs = 2000,\n", - " enable_model_summary=False,\n", - " callbacks=[Timer()]) # adding a callbacks\n", + "trainer = Trainer(\n", + " solver=pinn,\n", + " accelerator=\"cpu\",\n", + " deterministic=True, # setting deterministic=True ensure reproducibility when a seed is imposed\n", + " max_epochs=2000,\n", + " enable_model_summary=False,\n", + " callbacks=[Timer()],\n", + ") # adding a callbacks\n", "trainer.train()\n", "print(f'Total training time {trainer.callbacks[0].time_elapsed(\"train\"):.5f} s')" ] @@ -698,19 +704,20 @@ "seed_everything(42, workers=True)\n", "\n", "model = FeedForward(\n", - " layers=[10, 10],\n", - " func=torch.nn.Tanh,\n", - " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", - " )\n", + " layers=[10, 10],\n", + " func=torch.nn.Tanh,\n", + " output_dimensions=len(problem.output_variables),\n", + " input_dimensions=len(problem.input_variables),\n", + ")\n", "pinn = PINN(problem, model)\n", - "trainer = Trainer(solver=pinn,\n", - " accelerator='cpu',\n", - " deterministic=True,\n", - " max_epochs = 2000,\n", - " enable_model_summary=False,\n", - " callbacks=[Timer(),\n", - " StochasticWeightAveraging(swa_lrs=0.005)]) # adding StochasticWeightAveraging callbacks\n", + "trainer = Trainer(\n", + " solver=pinn,\n", + " accelerator=\"cpu\",\n", + " deterministic=True,\n", + " max_epochs=2000,\n", + " enable_model_summary=False,\n", + " callbacks=[Timer(), StochasticWeightAveraging(swa_lrs=0.005)],\n", + ") # adding StochasticWeightAveraging callbacks\n", "trainer.train()\n", "print(f'Total training time {trainer.callbacks[0].time_elapsed(\"train\"):.5f} s')" ] @@ -783,19 +790,20 @@ "seed_everything(42, workers=True)\n", "\n", "model = FeedForward(\n", - " layers=[10, 10],\n", - " func=torch.nn.Tanh,\n", - " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", - " )\n", + " layers=[10, 10],\n", + " func=torch.nn.Tanh,\n", + " output_dimensions=len(problem.output_variables),\n", + " input_dimensions=len(problem.input_variables),\n", + ")\n", "pinn = PINN(problem, model)\n", - "trainer = Trainer(solver=pinn,\n", - " accelerator='cpu',\n", - " max_epochs = 2000,\n", - " enable_model_summary=False,\n", - " gradient_clip_val=0.1, # clipping the gradient\n", - " callbacks=[Timer(),\n", - " StochasticWeightAveraging(swa_lrs=0.005)])\n", + "trainer = Trainer(\n", + " solver=pinn,\n", + " accelerator=\"cpu\",\n", + " max_epochs=2000,\n", + " enable_model_summary=False,\n", + " gradient_clip_val=0.1, # clipping the gradient\n", + " callbacks=[Timer(), StochasticWeightAveraging(swa_lrs=0.005)],\n", + ")\n", "trainer.train()\n", "print(f'Total training time {trainer.callbacks[0].time_elapsed(\"train\"):.5f} s')" ] diff --git a/tutorials/tutorial12/tutorial.ipynb b/tutorials/tutorial12/tutorial.ipynb index 77538395a..0223da5ae 100644 --- a/tutorials/tutorial12/tutorial.ipynb +++ b/tutorials/tutorial12/tutorial.ipynb @@ -53,16 +53,17 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import torch\n", "\n", - "#useful imports\n", + "# useful imports\n", "from pina import Condition\n", "from pina.problem import SpatialProblem, TimeDependentProblem\n", "from pina.equation import Equation, FixedValue\n", @@ -166,22 +167,23 @@ "outputs": [], "source": [ "class Burgers1DEquation(Equation):\n", - " \n", - " def __init__(self, nu = 0.):\n", + "\n", + " def __init__(self, nu=0.0):\n", " \"\"\"\n", " Burgers1D class. This class can be\n", " used to enforce the solution u to solve the viscous Burgers 1D Equation.\n", - " \n", + "\n", " :param torch.float32 nu: the viscosity coefficient. Default value is set to 0.\n", " \"\"\"\n", - " self.nu = nu \n", - " \n", + " self.nu = nu\n", + "\n", " def equation(input_, output_):\n", - " return grad(output_, input_, d='t') +\\\n", - " output_*grad(output_, input_, d='x') -\\\n", - " self.nu*laplacian(output_, input_, d='x')\n", + " return (\n", + " grad(output_, input_, d=\"t\")\n", + " + output_ * grad(output_, input_, d=\"x\")\n", + " - self.nu * laplacian(output_, input_, d=\"x\")\n", + " )\n", "\n", - " \n", " super().__init__(equation)" ] }, diff --git a/tutorials/tutorial13/tutorial.ipynb b/tutorials/tutorial13/tutorial.ipynb index 5bd059d56..9295835a8 100644 --- a/tutorials/tutorial13/tutorial.ipynb +++ b/tutorials/tutorial13/tutorial.ipynb @@ -25,12 +25,13 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", @@ -46,7 +47,7 @@ "from pina.model import FeedForward\n", "from pina.model.block import FourierFeatureEmbedding\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -312,9 +313,13 @@ "l2_loss = LpLoss(p=2, relative=False)\n", "\n", "# sample new test points\n", - "pts = pts = problem.spatial_domain.sample(100, 'grid')\n", - "print(f'Relative l2 error PINN {l2_loss(pinn(pts), problem.solution(pts)).item():.2%}')\n", - "print(f'Relative l2 error SAPINN {l2_loss(sapinn(pts), problem.solution(pts)).item():.2%}')" + "pts = pts = problem.spatial_domain.sample(100, \"grid\")\n", + "print(\n", + " f\"Relative l2 error PINN {l2_loss(pinn(pts), problem.solution(pts)).item():.2%}\"\n", + ")\n", + "print(\n", + " f\"Relative l2 error SAPINN {l2_loss(sapinn(pts), problem.solution(pts)).item():.2%}\"\n", + ")" ] }, { @@ -462,12 +467,14 @@ } ], "source": [ - "#plot solution obtained\n", - "plot_solution(multiscale_pinn, 'Multiscale PINN solution')\n", + "# plot solution obtained\n", + "plot_solution(multiscale_pinn, \"Multiscale PINN solution\")\n", "\n", "# sample new test points\n", - "pts = pts = problem.spatial_domain.sample(100, 'grid')\n", - "print(f'Relative l2 error PINN with MultiscaleFourierNet: {l2_loss(multiscale_pinn(pts), problem.solution(pts)).item():.2%}')" + "pts = pts = problem.spatial_domain.sample(100, \"grid\")\n", + "print(\n", + " f\"Relative l2 error PINN with MultiscaleFourierNet: {l2_loss(multiscale_pinn(pts), problem.solution(pts)).item():.2%}\"\n", + ")" ] }, { diff --git a/tutorials/tutorial14/tutorial.ipynb b/tutorials/tutorial14/tutorial.ipynb index 312e725d7..6301722e5 100644 --- a/tutorials/tutorial14/tutorial.ipynb +++ b/tutorials/tutorial14/tutorial.ipynb @@ -33,12 +33,13 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "%matplotlib inline\n", "\n", @@ -50,7 +51,7 @@ "from pina.model.block import PODBlock, RBFBlock\n", "from pina import LabelTensor\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -70,6 +71,7 @@ "source": [ "import smithers\n", "from smithers.dataset import LidCavity\n", + "\n", "dataset = LidCavity()" ] }, @@ -108,13 +110,13 @@ ], "source": [ "fig, axs = plt.subplots(1, 3, figsize=(14, 3))\n", - "for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['mag(v)'][:3]):\n", + "for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots[\"mag(v)\"][:3]):\n", " ax.tricontourf(dataset.triang, u, levels=16)\n", - " ax.set_title(f'$u$ field for $\\mu$ = {par[0]:.4f}')\n", + " ax.set_title(f\"$u$ field for $\\mu$ = {par[0]:.4f}\")\n", "fig, axs = plt.subplots(1, 3, figsize=(14, 3))\n", - "for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['p'][:3]):\n", + "for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots[\"p\"][:3]):\n", " ax.tricontourf(dataset.triang, u, levels=16)\n", - " ax.set_title(f'$p$ field for $\\mu$ = {par[0]:.4f}')" + " ax.set_title(f\"$p$ field for $\\mu$ = {par[0]:.4f}\")" ] }, { @@ -130,15 +132,16 @@ "metadata": {}, "outputs": [], "source": [ - "'''velocity magnitude data, 5041 for each snapshot'''\n", - "u=torch.tensor(dataset.snapshots['mag(v)']).float() \n", - "u = LabelTensor(u, labels=[f's{i}' for i in range(u.shape[1])])\n", - "'''pressure data, 5041 for each snapshot'''\n", - "p=torch.tensor(dataset.snapshots['p']).float()\n", - "p = LabelTensor(p, labels=[f's{i}' for i in range(p.shape[1])])\n", - "'''mu corresponding to each snapshot'''\n", - "mu=torch.tensor(dataset.params).float()\n", - "mu = LabelTensor(mu, labels=['mu'])\n" + "\"\"\"velocity magnitude data, 5041 for each snapshot\"\"\"\n", + "\n", + "u = torch.tensor(dataset.snapshots[\"mag(v)\"]).float()\n", + "u = LabelTensor(u, labels=[f\"s{i}\" for i in range(u.shape[1])])\n", + "\"\"\"pressure data, 5041 for each snapshot\"\"\"\n", + "p = torch.tensor(dataset.snapshots[\"p\"]).float()\n", + "p = LabelTensor(p, labels=[f\"s{i}\" for i in range(p.shape[1])])\n", + "\"\"\"mu corresponding to each snapshot\"\"\"\n", + "mu = torch.tensor(dataset.params).float()\n", + "mu = LabelTensor(mu, labels=[\"mu\"])" ] }, { @@ -154,15 +157,16 @@ "metadata": {}, "outputs": [], "source": [ - "'''number of snapshots'''\n", + "\"\"\"number of snapshots\"\"\"\n", + "\n", "n = u.shape[0]\n", - "'''training over total snapshots ratio and number of training snapshots'''\n", - "ratio = 0.9 \n", - "n_train = int(n*ratio)\n", - "'''split u and p data'''\n", - "u_train, u_test = u[:n_train], u[n_train:] #for mag(v)\n", - "p_train, p_test = p[:n_train], p[n_train:] #for p\n", - "'''split snapshots'''\n", + "\"\"\"training over total snapshots ratio and number of training snapshots\"\"\"\n", + "ratio = 0.9\n", + "n_train = int(n * ratio)\n", + "\"\"\"split u and p data\"\"\"\n", + "u_train, u_test = u[:n_train], u[n_train:] # for mag(v)\n", + "p_train, p_test = p[:n_train], p[n_train:] # for p\n", + "\"\"\"split snapshots\"\"\"\n", "mu_train, mu_test = mu[:n_train], mu[n_train:]" ] }, @@ -183,8 +187,9 @@ " \"\"\"\n", " Proper orthogonal decomposition with Radial Basis Function interpolation model.\n", " \"\"\"\n", + "\n", " def __init__(self, pod_rank, rbf_kernel):\n", - " \n", + "\n", " super().__init__()\n", " self.pod = PODBlock(pod_rank)\n", " self.rbf = RBFBlock(kernel=rbf_kernel)" @@ -207,8 +212,9 @@ " \"\"\"\n", " Proper orthogonal decomposition with Radial Basis Function interpolation model.\n", " \"\"\"\n", + "\n", " def __init__(self, pod_rank, rbf_kernel):\n", - " \n", + "\n", " super().__init__()\n", " self.pod = PODBlock(pod_rank)\n", " self.rbf = RBFBlock(kernel=rbf_kernel)\n", @@ -223,6 +229,7 @@ " \"\"\"\n", " coefficients = self.rbf(x)\n", " return self.pod.expand(coefficients)\n", + "\n", " def fit(self, p, x):\n", " \"\"\"\n", " Call the :meth:`pina.model.layers.PODBlock.fit` method of the\n", @@ -231,8 +238,7 @@ " :attr:`pina.model.layers.RBFBlock` attribute to fit the interpolation.\n", " \"\"\"\n", " self.pod.fit(x)\n", - " self.rbf.fit(p, self.pod.reduce(x))\n", - " " + " self.rbf.fit(p, self.pod.reduce(x))" ] }, { @@ -248,15 +254,16 @@ "metadata": {}, "outputs": [], "source": [ - "'''create the model'''\n", - "pod_rbfu = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')\n", + "\"\"\"create the model\"\"\"\n", "\n", - "'''fit the model to velocity training data'''\n", + "pod_rbfu = PODRBF(pod_rank=20, rbf_kernel=\"thin_plate_spline\")\n", + "\n", + "\"\"\"fit the model to velocity training data\"\"\"\n", "pod_rbfu.fit(mu_train, u_train)\n", "\n", - "'''predict the parameter using the fitted model'''\n", + "\"\"\"predict the parameter using the fitted model\"\"\"\n", "u_train_rbf = pod_rbfu(mu_train)\n", - "u_test_rbf = pod_rbfu(mu_test)\n" + "u_test_rbf = pod_rbfu(mu_test)" ] }, { @@ -282,12 +289,12 @@ } ], "source": [ - "relative_u_error_train = torch.norm(u_train_rbf - u_train)/torch.norm(u_train)\n", - "relative_u_error_test = torch.norm(u_test_rbf - u_test)/torch.norm(u_test)\n", + "relative_u_error_train = torch.norm(u_train_rbf - u_train) / torch.norm(u_train)\n", + "relative_u_error_test = torch.norm(u_test_rbf - u_test) / torch.norm(u_test)\n", "\n", - "print('Error summary for POD-RBF model:')\n", - "print(f' Train: {relative_u_error_train.item():e}')\n", - "print(f' Test: {relative_u_error_test.item():e}')" + "print(\"Error summary for POD-RBF model:\")\n", + "print(f\" Train: {relative_u_error_train.item():e}\")\n", + "print(f\" Test: {relative_u_error_test.item():e}\")" ] }, { @@ -323,23 +330,32 @@ "fig, axs = plt.subplots(3, 4, figsize=(14, 10))\n", "\n", "relative_u_error_rbf = np.abs(u_test[idx] - u_idx_rbf.detach())\n", - "relative_u_error_rbf = np.where(u_test[idx] < 1e-7, 1e-7, relative_u_error_rbf/u_test[idx])\n", - " \n", - "for i, (idx_, rbf_, rbf_err_) in enumerate(\n", - " zip(idx, u_idx_rbf, relative_u_error_rbf)):\n", - " axs[0, i].set_title('Prediction for ' f'$\\mu$ = {mu_test[idx_].item():.4f}')\n", - " axs[1, i].set_title('True snapshot for ' f'$\\mu$ = {mu_test[idx_].item():.4f}')\n", - " axs[2, i].set_title('Error for ' f'$\\mu$ = {mu_test[idx_].item():.4f}')\n", + "relative_u_error_rbf = np.where(\n", + " u_test[idx] < 1e-7, 1e-7, relative_u_error_rbf / u_test[idx]\n", + ")\n", "\n", - " cm = axs[0, i].tricontourf(dataset.triang, rbf_.detach()) # POD-RBF prediction\n", + "for i, (idx_, rbf_, rbf_err_) in enumerate(\n", + " zip(idx, u_idx_rbf, relative_u_error_rbf)\n", + "):\n", + " axs[0, i].set_title(\"Prediction for \" f\"$\\mu$ = {mu_test[idx_].item():.4f}\")\n", + " axs[1, i].set_title(\n", + " \"True snapshot for \" f\"$\\mu$ = {mu_test[idx_].item():.4f}\"\n", + " )\n", + " axs[2, i].set_title(\"Error for \" f\"$\\mu$ = {mu_test[idx_].item():.4f}\")\n", + "\n", + " cm = axs[0, i].tricontourf(\n", + " dataset.triang, rbf_.detach()\n", + " ) # POD-RBF prediction\n", " plt.colorbar(cm, ax=axs[0, i])\n", - " \n", - " cm = axs[1, i].tricontourf(dataset.triang, u_test[idx_].flatten()) # Truth\n", + "\n", + " cm = axs[1, i].tricontourf(dataset.triang, u_test[idx_].flatten()) # Truth\n", " plt.colorbar(cm, ax=axs[1, i])\n", "\n", - " cm = axs[2, i].tripcolor(dataset.triang, rbf_err_, norm=matplotlib.colors.LogNorm()) # Error for POD-RBF\n", + " cm = axs[2, i].tripcolor(\n", + " dataset.triang, rbf_err_, norm=matplotlib.colors.LogNorm()\n", + " ) # Error for POD-RBF\n", " plt.colorbar(cm, ax=axs[2, i])\n", - " \n", + "\n", "plt.show()" ] }, @@ -366,22 +382,23 @@ } ], "source": [ - "'''create the model'''\n", - "pod_rbfp = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')\n", + "\"\"\"create the model\"\"\"\n", + "\n", + "pod_rbfp = PODRBF(pod_rank=20, rbf_kernel=\"thin_plate_spline\")\n", "\n", - "'''fit the model to pressure training data'''\n", + "\"\"\"fit the model to pressure training data\"\"\"\n", "pod_rbfp.fit(mu_train, p_train)\n", "\n", - "'''predict the parameter using the fitted model'''\n", + "\"\"\"predict the parameter using the fitted model\"\"\"\n", "p_train_rbf = pod_rbfp(mu_train)\n", "p_test_rbf = pod_rbfp(mu_test)\n", "\n", - "relative_p_error_train = torch.norm(p_train_rbf - p_train)/torch.norm(p_train)\n", - "relative_p_error_test = torch.norm(p_test_rbf - p_test)/torch.norm(p_test)\n", + "relative_p_error_train = torch.norm(p_train_rbf - p_train) / torch.norm(p_train)\n", + "relative_p_error_test = torch.norm(p_test_rbf - p_test) / torch.norm(p_test)\n", "\n", - "print('Error summary for POD-RBF model:')\n", - "print(f' Train: {relative_p_error_train.item():e}')\n", - "print(f' Test: {relative_p_error_test.item():e}')" + "print(\"Error summary for POD-RBF model:\")\n", + "print(f\" Train: {relative_p_error_train.item():e}\")\n", + "print(f\" Test: {relative_p_error_test.item():e}\")" ] }, { @@ -409,10 +426,12 @@ ], "source": [ "fig, axs = plt.subplots(2, 3, figsize=(14, 6))\n", - "for ax, par, u in zip(axs.ravel(), dataset.params[66:72], dataset.snapshots['p'][66:72]):\n", + "for ax, par, u in zip(\n", + " axs.ravel(), dataset.params[66:72], dataset.snapshots[\"p\"][66:72]\n", + "):\n", " cm = ax.tricontourf(dataset.triang, u, levels=16)\n", " plt.colorbar(cm, ax=ax)\n", - " ax.set_title(f'$p$ field for $\\mu$ = {par[0]:.4f}')\n", + " ax.set_title(f\"$p$ field for $\\mu$ = {par[0]:.4f}\")\n", "plt.tight_layout()\n", "plt.show()" ] @@ -442,11 +461,13 @@ ], "source": [ "fig, axs = plt.subplots(2, 3, figsize=(14, 6))\n", - "for ax, par, u in zip(axs.ravel(), dataset.params[98:104], dataset.snapshots['p'][98:104]):\n", + "for ax, par, u in zip(\n", + " axs.ravel(), dataset.params[98:104], dataset.snapshots[\"p\"][98:104]\n", + "):\n", " cm = ax.tricontourf(dataset.triang, u, levels=16)\n", " plt.colorbar(cm, ax=ax)\n", - " ax.set_title(f'$p$ field for $\\mu$ = {par[0]:.4f}')\n", - "plt.tight_layout() \n", + " ax.set_title(f\"$p$ field for $\\mu$ = {par[0]:.4f}\")\n", + "plt.tight_layout()\n", "plt.show()" ] }, @@ -473,37 +494,42 @@ } ], "source": [ - "'''excluding problematic snapshots'''\n", + "\"\"\"excluding problematic snapshots\"\"\"\n", + "\n", "data = list(range(300))\n", "data_to_consider = data[:67] + data[71:100] + data[102:]\n", - "'''proceed as before'''\n", - "newp=torch.tensor(dataset.snapshots['p'][data_to_consider]).float()\n", - "newp = LabelTensor(newp, labels=[f's{i}' for i in range(newp.shape[1])])\n", + "\"\"\"proceed as before\"\"\"\n", + "newp = torch.tensor(dataset.snapshots[\"p\"][data_to_consider]).float()\n", + "newp = LabelTensor(newp, labels=[f\"s{i}\" for i in range(newp.shape[1])])\n", "\n", - "newmu=torch.tensor(dataset.params[data_to_consider]).float()\n", - "newmu = LabelTensor(newmu, labels=['mu'])\n", + "newmu = torch.tensor(dataset.params[data_to_consider]).float()\n", + "newmu = LabelTensor(newmu, labels=[\"mu\"])\n", "\n", "newn = newp.shape[0]\n", - "ratio = 0.9 \n", - "new_train = int(newn*ratio)\n", + "ratio = 0.9\n", + "new_train = int(newn * ratio)\n", "\n", - "new_p_train, new_p_test = newp[:new_train], newp[new_train:] \n", + "new_p_train, new_p_test = newp[:new_train], newp[new_train:]\n", "\n", "new_mu_train, new_mu_test = newmu[:new_train], newmu[new_train:]\n", "\n", - "new_pod_rbfp = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')\n", + "new_pod_rbfp = PODRBF(pod_rank=20, rbf_kernel=\"thin_plate_spline\")\n", "\n", "new_pod_rbfp.fit(new_mu_train, new_p_train)\n", "\n", "new_p_train_rbf = new_pod_rbfp(new_mu_train)\n", "new_p_test_rbf = new_pod_rbfp(new_mu_test)\n", "\n", - "new_relative_p_error_train = torch.norm(new_p_train_rbf - new_p_train)/torch.norm(new_p_train)\n", - "new_relative_p_error_test = torch.norm(new_p_test_rbf - new_p_test)/torch.norm(new_p_test)\n", + "new_relative_p_error_train = torch.norm(\n", + " new_p_train_rbf - new_p_train\n", + ") / torch.norm(new_p_train)\n", + "new_relative_p_error_test = torch.norm(\n", + " new_p_test_rbf - new_p_test\n", + ") / torch.norm(new_p_test)\n", "\n", - "print('Error summary for POD-RBF model:')\n", - "print(f' Train: {new_relative_p_error_train.item():e}')\n", - "print(f' Test: {new_relative_p_error_test.item():e}')" + "print(\"Error summary for POD-RBF model:\")\n", + "print(f\" Train: {new_relative_p_error_train.item():e}\")\n", + "print(f\" Test: {new_relative_p_error_test.item():e}\")" ] }, { diff --git a/tutorials/tutorial14/tutorial.py b/tutorials/tutorial14/tutorial.py deleted file mode 100644 index 97c0bccff..000000000 --- a/tutorials/tutorial14/tutorial.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# # Tutorial: Predicting Lid-driven cavity problem parameters with POD-RBF -# -# [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial14/tutorial.ipynb) - -# In this tutorial we will show how to use the **PINA** library to predict the distributions of velocity and pressure the Lid-driven Cavity problem, a benchmark in Computational Fluid Dynamics. The problem consists of a square cavity with a lid on top moving with tangential velocity (by convention to the right), with the addition of no-slip conditions on the walls of the cavity and null static pressure on the lower left angle. -# -# Our goal is to predict the distributions of velocity and pressure of the fluid inside the cavity as the Reynolds number of the inlet fluid varies. To do so we're using a Reduced Order Model (ROM) based on Proper Orthogonal Decomposition (POD). The parametric solution manifold is approximated here with Radial Basis Function (RBF) Interpolation, a common mesh-free interpolation method that doesn't require trainers or solvers as the found radial basis functions are used to interpolate new points. - -# Let's start with the necessary imports. We're particularly interested in the `PODBlock` and `RBFBlock` classes which will allow us to define the POD-RBF model. - -# In[1]: - - -## routine needed to run the notebook on Google Colab -try: - import google.colab - IN_COLAB = True -except: - IN_COLAB = False -if IN_COLAB: - get_ipython().system('pip install "pina-mathlab"') - -get_ipython().run_line_magic('matplotlib', 'inline') - -import matplotlib.pyplot as plt -import torch -import pina -import warnings - -from pina.model.block import PODBlock, RBFBlock -from pina import LabelTensor - -warnings.filterwarnings('ignore') - - -# In this tutorial we're gonna use the `LidCavity` class from the [Smithers](https://github.com/mathLab/Smithers) library, which contains a set of parametric solutions of the Lid-driven cavity problem in a square domain. The dataset consists of 300 snapshots of the parameter fields, which in this case are the magnitude of velocity and the pressure, and the corresponding parameter values $u$ and $p$. Each snapshot corresponds to a different value of the tangential velocity $\mu$ of the lid, which has been sampled uniformly between 0.01 m/s and 1 m/s. -# -# Let's start by importing the dataset: - -# In[2]: - - -import smithers -from smithers.dataset import LidCavity -dataset = LidCavity() - - -# Let's plot two the data points and the corresponding solution for both parameters at different snapshots, in order to better visualise the data we're using: - -# In[3]: - - -fig, axs = plt.subplots(1, 3, figsize=(14, 3)) -for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['mag(v)'][:3]): - ax.tricontourf(dataset.triang, u, levels=16) - ax.set_title(f'$u$ field for $\mu$ = {par[0]:.4f}') -fig, axs = plt.subplots(1, 3, figsize=(14, 3)) -for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['p'][:3]): - ax.tricontourf(dataset.triang, u, levels=16) - ax.set_title(f'$p$ field for $\mu$ = {par[0]:.4f}') - - -# To train the model we only need the snapshots for the two parameters. In order to be able to work with the snapshots in **PINA** we first need to assure they're in a compatible format, hence why we start by casting them into `LabelTensor` objects: - -# In[4]: - - -'''velocity magnitude data, 5041 for each snapshot''' -u=torch.tensor(dataset.snapshots['mag(v)']).float() -u = LabelTensor(u, labels=[f's{i}' for i in range(u.shape[1])]) -'''pressure data, 5041 for each snapshot''' -p=torch.tensor(dataset.snapshots['p']).float() -p = LabelTensor(p, labels=[f's{i}' for i in range(p.shape[1])]) -'''mu corresponding to each snapshot''' -mu=torch.tensor(dataset.params).float() -mu = LabelTensor(mu, labels=['mu']) - - -# The goal of our training is to be able to predict the solution for new test parameters. The first thing we need to do is validate the accuracy of the model, and in order to do so we split the 300 snapshots in training and testing dataset. In the example we set the training `ratio` to 0.9, which means that 90% of the total snapshots is used for training and the remaining 10% for testing. - -# In[5]: - - -'''number of snapshots''' -n = u.shape[0] -'''training over total snapshots ratio and number of training snapshots''' -ratio = 0.9 -n_train = int(n*ratio) -'''split u and p data''' -u_train, u_test = u[:n_train], u[n_train:] #for mag(v) -p_train, p_test = p[:n_train], p[n_train:] #for p -'''split snapshots''' -mu_train, mu_test = mu[:n_train], mu[n_train:] - - -# We now proceed by defining the model we intend to use. We inherit from the `torch.nn.Module` class, but in addition we require a `pod_rank` for the POD part and a function `rbf_kernel` in order to perform the RBF part: - -# In[6]: - - -class PODRBF(torch.nn.Module): - """ - Proper orthogonal decomposition with Radial Basis Function interpolation model. - """ - def __init__(self, pod_rank, rbf_kernel): - - super().__init__() - self.pod = PODBlock(pod_rank) - self.rbf = RBFBlock(kernel=rbf_kernel) - - -# We complete our model by adding two crucial methods. The first is `forward`, and it expands the input POD coefficients. After being expanded the POD layer needs to be fit, hence why we add a `fit` method that gives us the POD basis (current **PINA** default is by performing truncated Singular Value Decomposition). The same method then uses the basis to fit the RBF interpolation. Overall, the completed class looks like this: - -# In[7]: - - -class PODRBF(torch.nn.Module): - """ - Proper orthogonal decomposition with Radial Basis Function interpolation model. - """ - def __init__(self, pod_rank, rbf_kernel): - - super().__init__() - self.pod = PODBlock(pod_rank) - self.rbf = RBFBlock(kernel=rbf_kernel) - - def forward(self, x): - """ - Defines the computation performed at every call. - :param x: The tensor to apply the forward pass. - :type x: torch.Tensor - :return: the output computed by the model. - :rtype: torch.Tensor - """ - coefficients = self.rbf(x) - return self.pod.expand(coefficients) - def fit(self, p, x): - """ - Call the :meth:`pina.model.layers.PODBlock.fit` method of the - :attr:`pina.model.layers.PODBlock` attribute to perform the POD, - and the :meth:`pina.model.layers.RBFBlock.fit` method of the - :attr:`pina.model.layers.RBFBlock` attribute to fit the interpolation. - """ - self.pod.fit(x) - self.rbf.fit(p, self.pod.reduce(x)) - - - -# Now that we've built our class, we can fit the model and ask it to predict the parameters for the remaining snapshots. We remember that we don't need to train the model, as it doesn't involve any learnable parameter. The only things we have to set are the rank of the decomposition and the radial basis function (here we use thin plate). Here we focus on predicting the magnitude of velocity: - -# In[8]: - - -'''create the model''' -pod_rbfu = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline') - -'''fit the model to velocity training data''' -pod_rbfu.fit(mu_train, u_train) - -'''predict the parameter using the fitted model''' -u_train_rbf = pod_rbfu(mu_train) -u_test_rbf = pod_rbfu(mu_test) - - -# Finally we can calculate the relative error for our model: - -# In[9]: - - -relative_u_error_train = torch.norm(u_train_rbf - u_train)/torch.norm(u_train) -relative_u_error_test = torch.norm(u_test_rbf - u_test)/torch.norm(u_test) - -print('Error summary for POD-RBF model:') -print(f' Train: {relative_u_error_train.item():e}') -print(f' Test: {relative_u_error_test.item():e}') - - -# The results are promising! Now let's visualise them, comparing four random predicted snapshots to the true ones: - -# In[10]: - - -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - -idx = torch.randint(0, len(u_test), (4,)) -u_idx_rbf = pod_rbfu(mu_test[idx]) -fig, axs = plt.subplots(3, 4, figsize=(14, 10)) - -relative_u_error_rbf = np.abs(u_test[idx] - u_idx_rbf.detach()) -relative_u_error_rbf = np.where(u_test[idx] < 1e-7, 1e-7, relative_u_error_rbf/u_test[idx]) - -for i, (idx_, rbf_, rbf_err_) in enumerate( - zip(idx, u_idx_rbf, relative_u_error_rbf)): - axs[0, i].set_title('Prediction for ' f'$\mu$ = {mu_test[idx_].item():.4f}') - axs[1, i].set_title('True snapshot for ' f'$\mu$ = {mu_test[idx_].item():.4f}') - axs[2, i].set_title('Error for ' f'$\mu$ = {mu_test[idx_].item():.4f}') - - cm = axs[0, i].tricontourf(dataset.triang, rbf_.detach()) # POD-RBF prediction - plt.colorbar(cm, ax=axs[0, i]) - - cm = axs[1, i].tricontourf(dataset.triang, u_test[idx_].flatten()) # Truth - plt.colorbar(cm, ax=axs[1, i]) - - cm = axs[2, i].tripcolor(dataset.triang, rbf_err_, norm=matplotlib.colors.LogNorm()) # Error for POD-RBF - plt.colorbar(cm, ax=axs[2, i]) - -plt.show() - - -# Overall we have reached a good level of approximation while avoiding time-consuming training procedures. Let's try doing the same to predict the pressure snapshots: - -# In[11]: - - -'''create the model''' -pod_rbfp = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline') - -'''fit the model to pressure training data''' -pod_rbfp.fit(mu_train, p_train) - -'''predict the parameter using the fitted model''' -p_train_rbf = pod_rbfp(mu_train) -p_test_rbf = pod_rbfp(mu_test) - -relative_p_error_train = torch.norm(p_train_rbf - p_train)/torch.norm(p_train) -relative_p_error_test = torch.norm(p_test_rbf - p_test)/torch.norm(p_test) - -print('Error summary for POD-RBF model:') -print(f' Train: {relative_p_error_train.item():e}') -print(f' Test: {relative_p_error_test.item():e}') - - -# Unfortunately here we obtain a very high relative test error, although this is likely due to the nature of the available data. Looking at the plots we can see that the pressure field is subject to high variations between subsequent snapshots, especially here: - -# In[12]: - - -fig, axs = plt.subplots(2, 3, figsize=(14, 6)) -for ax, par, u in zip(axs.ravel(), dataset.params[66:72], dataset.snapshots['p'][66:72]): - cm = ax.tricontourf(dataset.triang, u, levels=16) - plt.colorbar(cm, ax=ax) - ax.set_title(f'$p$ field for $\mu$ = {par[0]:.4f}') -plt.tight_layout() -plt.show() - - -# Or here: - -# In[13]: - - -fig, axs = plt.subplots(2, 3, figsize=(14, 6)) -for ax, par, u in zip(axs.ravel(), dataset.params[98:104], dataset.snapshots['p'][98:104]): - cm = ax.tricontourf(dataset.triang, u, levels=16) - plt.colorbar(cm, ax=ax) - ax.set_title(f'$p$ field for $\mu$ = {par[0]:.4f}') -plt.tight_layout() -plt.show() - - -# Scrolling through the velocity snapshots we can observe a more regular behaviour, with no such variations in subsequent snapshots. Moreover, if we decide not to consider the abovementioned "problematic" snapshots, we can already observe a huge improvement: - -# In[14]: - - -'''excluding problematic snapshots''' -data = list(range(300)) -data_to_consider = data[:67] + data[71:100] + data[102:] -'''proceed as before''' -newp=torch.tensor(dataset.snapshots['p'][data_to_consider]).float() -newp = LabelTensor(newp, labels=[f's{i}' for i in range(newp.shape[1])]) - -newmu=torch.tensor(dataset.params[data_to_consider]).float() -newmu = LabelTensor(newmu, labels=['mu']) - -newn = newp.shape[0] -ratio = 0.9 -new_train = int(newn*ratio) - -new_p_train, new_p_test = newp[:new_train], newp[new_train:] - -new_mu_train, new_mu_test = newmu[:new_train], newmu[new_train:] - -new_pod_rbfp = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline') - -new_pod_rbfp.fit(new_mu_train, new_p_train) - -new_p_train_rbf = new_pod_rbfp(new_mu_train) -new_p_test_rbf = new_pod_rbfp(new_mu_test) - -new_relative_p_error_train = torch.norm(new_p_train_rbf - new_p_train)/torch.norm(new_p_train) -new_relative_p_error_test = torch.norm(new_p_test_rbf - new_p_test)/torch.norm(new_p_test) - -print('Error summary for POD-RBF model:') -print(f' Train: {new_relative_p_error_train.item():e}') -print(f' Test: {new_relative_p_error_test.item():e}') - - -# ## What's next? -# -# Congratulations on completing the **PINA** tutorial on building and using a custom POD class! Now you can try: -# -# 1. Varying the inputs of the model (for a list of the supported RB functions look at the `rbf_layer.py` file in `pina.layers`) -# -# 2. Changing the POD model, for example using Artificial Neural Networks. For a more in depth overview of POD-NN and a comparison with the POD-RBF model already shown, look at [Tutorial: Reduced order model (POD-RBF or POD-NN) for parametric problems](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial9/tutorial.ipynb) -# -# 3. Building your own classes or adapt the one shown to other datasets/problems diff --git a/tutorials/tutorial2/tutorial.ipynb b/tutorials/tutorial2/tutorial.ipynb index 6a1a86282..d0d891c77 100644 --- a/tutorials/tutorial2/tutorial.ipynb +++ b/tutorials/tutorial2/tutorial.ipynb @@ -23,12 +23,13 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", @@ -39,7 +40,7 @@ "from pina.solver import PINN\n", "from torch.nn import Softplus\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -186,8 +187,7 @@ " train_size=0.8, # set train size\n", " val_size=0.0, # set validation size\n", " test_size=0.2, # set testing size\n", - " shuffle=True, # shuffle the data\n", - " \n", + " shuffle=True, # shuffle the data\n", ")\n", "\n", "# train\n", @@ -348,7 +348,7 @@ "\n", " def forward(self, pts):\n", " x, y = pts.extract([\"x\"]), pts.extract([\"y\"])\n", - " f = 2 *torch.pi**2 * torch.sin(x * torch.pi) * torch.sin(y * torch.pi)\n", + " f = 2 * torch.pi**2 * torch.sin(x * torch.pi) * torch.sin(y * torch.pi)\n", " return LabelTensor(f, [\"feat\"])\n", "\n", "\n", @@ -487,28 +487,36 @@ "source": [ "class SinSinAB(torch.nn.Module):\n", " \"\"\" \"\"\"\n", + "\n", " def __init__(self):\n", " super().__init__()\n", " self.alpha = torch.nn.Parameter(torch.tensor([1.0]))\n", " self.beta = torch.nn.Parameter(torch.tensor([1.0]))\n", "\n", " def forward(self, x):\n", - " t = (\n", - " self.beta*torch.sin(self.alpha*x.extract(['x'])*torch.pi)*\n", - " torch.sin(self.alpha*x.extract(['y'])*torch.pi)\n", + " t = (\n", + " self.beta\n", + " * torch.sin(self.alpha * x.extract([\"x\"]) * torch.pi)\n", + " * torch.sin(self.alpha * x.extract([\"y\"]) * torch.pi)\n", " )\n", - " return LabelTensor(t, ['b*sin(a*x)sin(a*y)'])\n", + " return LabelTensor(t, [\"b*sin(a*x)sin(a*y)\"])\n", "\n", "\n", "# make model + solver + trainer\n", "model_learn = FeedForwardWithExtraFeatures(\n", - " input_dimensions=len(problem.input_variables) + 1, #we add one as also we consider the extra feature dimension\n", + " input_dimensions=len(problem.input_variables)\n", + " + 1, # we add one as also we consider the extra feature dimension\n", " output_dimensions=len(problem.output_variables),\n", " func=Softplus,\n", " layers=[10, 10],\n", - " extra_features=SinSinAB())\n", + " extra_features=SinSinAB(),\n", + ")\n", "\n", - "pinn_learn = PINN(problem, model_learn, optimizer=TorchOptimizer(torch.optim.Adam, lr=0.006,weight_decay=1e-8))\n", + "pinn_learn = PINN(\n", + " problem,\n", + " model_learn,\n", + " optimizer=TorchOptimizer(torch.optim.Adam, lr=0.006, weight_decay=1e-8),\n", + ")\n", "trainer_learn = Trainer(\n", " solver=pinn_learn, # setting the solver, i.e. PINN\n", " max_epochs=1000, # setting max epochs in training\n", @@ -649,14 +657,14 @@ ], "source": [ "# test error base pinn\n", - "print('PINN')\n", + "print(\"PINN\")\n", "trainer_base.test()\n", "# test error extra features pinn\n", "print(\"PINN with extra features\")\n", "trainer_feat.test()\n", "# test error learnable extra features pinn\n", "print(\"PINN with learnable extra features\")\n", - "_=trainer_learn.test()" + "_ = trainer_learn.test()" ] }, { diff --git a/tutorials/tutorial3/tutorial.ipynb b/tutorials/tutorial3/tutorial.ipynb index 5ffa55b9c..9962190d6 100644 --- a/tutorials/tutorial3/tutorial.ipynb +++ b/tutorials/tutorial3/tutorial.ipynb @@ -23,13 +23,14 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", - " \n", + " !pip install \"pina-mathlab\"\n", + "\n", "import torch\n", "import matplotlib.pyplot as plt\n", "import warnings\n", @@ -42,7 +43,7 @@ "from pina.equation import Equation, FixedValue\n", "from pina.callback import MetricTracker\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -261,7 +262,7 @@ " train_size=1.0,\n", " val_size=0.0,\n", " test_size=0.0,\n", - " callbacks=[MetricTracker(['train_loss', 'initial_loss','D_loss'])],\n", + " callbacks=[MetricTracker([\"train_loss\", \"initial_loss\", \"D_loss\"])],\n", ")\n", "trainer.train()" ] @@ -343,10 +344,10 @@ " \"True solution\": problem.solution(points),\n", " \"Absolute Difference\": torch.abs(\n", " solver(points) - problem.solution(points)\n", - " )\n", + " ),\n", " }\n", " # plot the solution\n", - " plt.suptitle(f'Solution for time {time.item()}')\n", + " plt.suptitle(f\"Solution for time {time.item()}\")\n", " for idx, (title, field) in enumerate(data.items()):\n", " plt.subplot(1, 3, idx + 1)\n", " plt.title(title)\n", diff --git a/tutorials/tutorial4/tutorial.ipynb b/tutorials/tutorial4/tutorial.ipynb index 9f3353801..f1df1b224 100644 --- a/tutorials/tutorial4/tutorial.ipynb +++ b/tutorials/tutorial4/tutorial.ipynb @@ -35,26 +35,27 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", - "import torch \n", - "import matplotlib.pyplot as plt \n", - "import torchvision # for MNIST dataset\n", + "import torch\n", + "import matplotlib.pyplot as plt\n", + "import torchvision # for MNIST dataset\n", "import warnings\n", "\n", "from pina import Trainer\n", "from pina.problem.zoo import SupervisedProblem\n", "from pina.solver import SupervisedSolver\n", "from pina.trainer import Trainer\n", - "from pina.model.block import ContinuousConvBlock \n", - "from pina.model import FeedForward # for building AE and MNIST classification\n", + "from pina.model.block import ContinuousConvBlock\n", + "from pina.model import FeedForward # for building AE and MNIST classification\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -517,7 +518,7 @@ "source": [ "# setting the problem\n", "problem = SupervisedProblem(\n", - " input_=train_data.train_data.unsqueeze(1), # adding channel dimension\n", + " input_=train_data.train_data.unsqueeze(1), # adding channel dimension\n", " output_=train_data.train_labels,\n", ")\n", "\n", @@ -568,7 +569,7 @@ "source": [ "correct = 0\n", "total = 0\n", - "trainer.data_module.setup('test')\n", + "trainer.data_module.setup(\"test\")\n", "with torch.no_grad():\n", " for data in trainer.data_module.test_dataloader():\n", " test_data = data[\"data\"]\n", @@ -580,9 +581,7 @@ " total += labels.size(0)\n", " correct += (predicted == labels).sum().item()\n", "\n", - "print(\n", - " f\"Accuracy of the network on the test images: {(correct / total):.3%}\"\n", - ")" + "print(f\"Accuracy of the network on the test images: {(correct / total):.3%}\")" ] }, { diff --git a/tutorials/tutorial5/tutorial.ipynb b/tutorials/tutorial5/tutorial.ipynb index 78d59afed..b02afd90c 100644 --- a/tutorials/tutorial5/tutorial.ipynb +++ b/tutorials/tutorial5/tutorial.ipynb @@ -33,15 +33,16 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", - " !pip install scipy\n", - " # get the data\n", - " !wget https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial5/Data_Darcy.mat\n", + " !pip install \"pina-mathlab\"\n", + " !pip install scipy\n", + " # get the data\n", + " !wget https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial5/Data_Darcy.mat\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", @@ -54,7 +55,7 @@ "from pina.solver import SupervisedSolver\n", "from pina.problem.zoo import SupervisedProblem\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -129,10 +130,10 @@ ], "source": [ "plt.subplot(1, 2, 1)\n", - "plt.title('permeability')\n", + "plt.title(\"permeability\")\n", "plt.imshow(k_train[0])\n", "plt.subplot(1, 2, 2)\n", - "plt.title('field solution')\n", + "plt.title(\"field solution\")\n", "plt.imshow(u_train[0])\n", "plt.show()" ] @@ -278,12 +279,10 @@ " )\n", " * 100\n", ")\n", - "print(f'Final error training {err:.2f}%')\n", + "print(f\"Final error training {err:.2f}%\")\n", "\n", "err = (\n", - " float(\n", - " metric_err(u_test.unsqueeze(-1), model(k_test.unsqueeze(-1))).mean()\n", - " )\n", + " float(metric_err(u_test.unsqueeze(-1), model(k_test.unsqueeze(-1))).mean())\n", " * 100\n", ")\n", "print(f\"Final error testing {err:.2f}%\")" diff --git a/tutorials/tutorial6/tutorial.ipynb b/tutorials/tutorial6/tutorial.ipynb index 563586178..522a9087f 100644 --- a/tutorials/tutorial6/tutorial.ipynb +++ b/tutorials/tutorial6/tutorial.ipynb @@ -26,21 +26,30 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import matplotlib.pyplot as plt\n", "\n", - "from pina.domain import EllipsoidDomain, Difference, CartesianDomain, Union, SimplexDomain, DomainInterface\n", + "from pina.domain import (\n", + " EllipsoidDomain,\n", + " Difference,\n", + " CartesianDomain,\n", + " Union,\n", + " SimplexDomain,\n", + " DomainInterface,\n", + ")\n", "from pina.label_tensor import LabelTensor\n", "\n", + "\n", "def plot_scatter(ax, pts, title):\n", " ax.title.set_text(title)\n", - " ax.scatter(pts.extract('x'), pts.extract('y'), color='blue', alpha=0.5)" + " ax.scatter(pts.extract(\"x\"), pts.extract(\"y\"), color=\"blue\", alpha=0.5)" ] }, { diff --git a/tutorials/tutorial7/tutorial.ipynb b/tutorials/tutorial7/tutorial.ipynb index 573f7954e..132578d11 100644 --- a/tutorials/tutorial7/tutorial.ipynb +++ b/tutorials/tutorial7/tutorial.ipynb @@ -75,17 +75,18 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", - " # get the data\n", - " !mkdir \"data\"\n", - " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial7/data/pinn_solution_0.5_0.5\" -O \"data/pinn_solution_0.5_0.5\"\n", - " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial7/data/pts_0.5_0.5\" -O \"data/pts_0.5_0.5\"\n", - " \n", + " !pip install \"pina-mathlab\"\n", + " # get the data\n", + " !mkdir \"data\"\n", + " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial7/data/pinn_solution_0.5_0.5\" -O \"data/pinn_solution_0.5_0.5\"\n", + " !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial7/data/pts_0.5_0.5\" -O \"data/pts_0.5_0.5\"\n", + "\n", "import matplotlib.pyplot as plt\n", "import torch\n", "import warnings\n", @@ -101,7 +102,7 @@ "from lightning.pytorch import seed_everything\n", "from lightning.pytorch.callbacks import Callback\n", "\n", - "warnings.filterwarnings('ignore')\n", + "warnings.filterwarnings(\"ignore\")\n", "seed_everything(883)" ] }, @@ -152,11 +153,11 @@ } ], "source": [ - "points = data_input.extract(['x', 'y']).detach().numpy()\n", + "points = data_input.extract([\"x\", \"y\"]).detach().numpy()\n", "truth = data_output.detach().numpy()\n", "\n", "plt.scatter(points[:, 0], points[:, 1], c=truth, s=8)\n", - "plt.axis('equal')\n", + "plt.axis(\"equal\")\n", "plt.colorbar()\n", "plt.show()" ] @@ -255,8 +256,8 @@ " layers=[20, 20, 20],\n", " func=torch.nn.Softplus,\n", " output_dimensions=len(problem.output_variables),\n", - " input_dimensions=len(problem.input_variables)\n", - " )" + " input_dimensions=len(problem.input_variables),\n", + ")" ] }, { diff --git a/tutorials/tutorial8/tutorial.ipynb b/tutorials/tutorial8/tutorial.ipynb index acc263d6c..b5273fcdb 100644 --- a/tutorials/tutorial8/tutorial.ipynb +++ b/tutorials/tutorial8/tutorial.ipynb @@ -38,12 +38,13 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "%matplotlib inline\n", "\n", @@ -60,7 +61,7 @@ "from pina.problem.zoo import SupervisedProblem\n", "from pina.model.block import PODBlock, RBFBlock\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -93,12 +94,13 @@ ], "source": [ "from smithers.dataset import NavierStokesDataset\n", + "\n", "dataset = NavierStokesDataset()\n", "\n", "fig, axs = plt.subplots(1, 4, figsize=(14, 3))\n", - "for ax, p, u in zip(axs, dataset.params[:4], dataset.snapshots['mag(v)'][:4]):\n", + "for ax, p, u in zip(axs, dataset.params[:4], dataset.snapshots[\"mag(v)\"][:4]):\n", " ax.tricontourf(dataset.triang, u, levels=16)\n", - " ax.set_title(f'$\\mu$ = {p[0]:.2f}')" + " ax.set_title(f\"$\\mu$ = {p[0]:.2f}\")" ] }, { @@ -118,7 +120,7 @@ "metadata": {}, "outputs": [], "source": [ - "u = torch.tensor(dataset.snapshots['mag(v)']).float()\n", + "u = torch.tensor(dataset.snapshots[\"mag(v)\"]).float()\n", "p = torch.tensor(dataset.params).float()\n", "problem = SupervisedProblem(input_=p, output_=u)" ] @@ -158,20 +160,18 @@ " \"\"\"\n", " Proper orthogonal decomposition with neural network model.\n", " \"\"\"\n", + "\n", " def __init__(self, pod_rank, layers, func):\n", - " \"\"\"\n", - " \n", - " \"\"\"\n", + " \"\"\" \"\"\"\n", " super().__init__()\n", - " \n", + "\n", " self.pod = PODBlock(pod_rank)\n", " self.nn = FeedForward(\n", " input_dimensions=1,\n", " output_dimensions=pod_rank,\n", " layers=layers,\n", - " func=func\n", + " func=func,\n", " )\n", - " \n", "\n", " def forward(self, x):\n", " \"\"\"\n", @@ -210,10 +210,11 @@ "source": [ "pod_nn = PODNN(pod_rank=20, layers=[10, 10, 10], func=torch.nn.Tanh)\n", "pod_nn_stokes = SupervisedSolver(\n", - " problem=problem, \n", - " model=pod_nn, \n", + " problem=problem,\n", + " model=pod_nn,\n", " optimizer=TorchOptimizer(torch.optim.Adam, lr=0.0001),\n", - " use_lt=False)" + " use_lt=False,\n", + ")" ] }, { @@ -278,14 +279,17 @@ " solver=pod_nn_stokes,\n", " max_epochs=1000,\n", " batch_size=None,\n", - " accelerator='cpu',\n", + " accelerator=\"cpu\",\n", " train_size=0.9,\n", " val_size=0.0,\n", - " test_size=0.1)\n", + " test_size=0.1,\n", + ")\n", "\n", "# fit the pod basis\n", - "trainer.data_module.setup(\"fit\") # set up the dataset\n", - "x_train = trainer.data_module.train_dataset.conditions_dict[\"data\"][\"target\"] # extract data for training\n", + "trainer.data_module.setup(\"fit\") # set up the dataset\n", + "x_train = trainer.data_module.train_dataset.conditions_dict[\"data\"][\n", + " \"target\"\n", + "] # extract data for training\n", "pod_nn.fit_pod(x=x_train)\n", "\n", "# now train\n", @@ -328,12 +332,12 @@ "u_test_nn = pod_nn_stokes(p_test)\n", "u_train_nn = pod_nn_stokes(p_train)\n", "\n", - "relative_error_train = torch.norm(u_train_nn - u_train)/torch.norm(u_train)\n", - "relative_error_test = torch.norm(u_test_nn - u_test)/torch.norm(u_test)\n", + "relative_error_train = torch.norm(u_train_nn - u_train) / torch.norm(u_train)\n", + "relative_error_test = torch.norm(u_test_nn - u_test) / torch.norm(u_test)\n", "\n", - "print('Error summary for POD-NN model:')\n", - "print(f' Train: {relative_error_train.item():e}')\n", - "print(f' Test: {relative_error_test.item():e}')" + "print(\"Error summary for POD-NN model:\")\n", + "print(f\" Train: {relative_error_train.item():e}\")\n", + "print(f\" Test: {relative_error_test.item():e}\")" ] }, { @@ -365,14 +369,11 @@ " \"\"\"\n", "\n", " def __init__(self, pod_rank, rbf_kernel):\n", - " \"\"\"\n", - " \n", - " \"\"\"\n", + " \"\"\" \"\"\"\n", " super().__init__()\n", - " \n", + "\n", " self.pod = PODBlock(pod_rank)\n", " self.rbf = RBFBlock(kernel=rbf_kernel)\n", - " \n", "\n", " def forward(self, x):\n", " \"\"\"\n", @@ -412,7 +413,7 @@ "metadata": {}, "outputs": [], "source": [ - "pod_rbf = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')\n", + "pod_rbf = PODRBF(pod_rank=20, rbf_kernel=\"thin_plate_spline\")\n", "pod_rbf.fit(p_train, u_train)" ] }, @@ -444,12 +445,12 @@ "u_test_rbf = pod_rbf(p_test)\n", "u_train_rbf = pod_rbf(p_train)\n", "\n", - "relative_error_train = torch.norm(u_train_rbf - u_train)/torch.norm(u_train)\n", - "relative_error_test = torch.norm(u_test_rbf - u_test)/torch.norm(u_test)\n", + "relative_error_train = torch.norm(u_train_rbf - u_train) / torch.norm(u_train)\n", + "relative_error_test = torch.norm(u_test_rbf - u_test) / torch.norm(u_test)\n", "\n", - "print('Error summary for POD-RBF model:')\n", - "print(f' Train: {relative_error_train.item():e}')\n", - "print(f' Test: {relative_error_test.item():e}')" + "print(\"Error summary for POD-RBF model:\")\n", + "print(f\" Train: {relative_error_train.item():e}\")\n", + "print(f\" Test: {relative_error_test.item():e}\")" ] }, { diff --git a/tutorials/tutorial9/tutorial.ipynb b/tutorials/tutorial9/tutorial.ipynb index 3f19c6c0e..88e1aee51 100644 --- a/tutorials/tutorial9/tutorial.ipynb +++ b/tutorials/tutorial9/tutorial.ipynb @@ -26,12 +26,13 @@ "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", - " import google.colab\n", - " IN_COLAB = True\n", + " import google.colab\n", + "\n", + " IN_COLAB = True\n", "except:\n", - " IN_COLAB = False\n", + " IN_COLAB = False\n", "if IN_COLAB:\n", - " !pip install \"pina-mathlab\"\n", + " !pip install \"pina-mathlab\"\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", @@ -47,7 +48,7 @@ "from pina.equation import Equation\n", "from pina.callback import MetricTracker\n", "\n", - "warnings.filterwarnings('ignore')" + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -111,6 +112,7 @@ " def solution(self, pts):\n", " return torch.sin(torch.pi * pts) * torch.cos(3.0 * torch.pi * pts)\n", "\n", + "\n", "problem = Helmholtz()\n", "\n", "# let's discretise the domain\n", @@ -234,7 +236,7 @@ " pinn,\n", " max_epochs=5000,\n", " accelerator=\"cpu\",\n", - " enable_model_summary=False, \n", + " enable_model_summary=False,\n", " callbacks=[MetricTracker()],\n", " train_size=1.0,\n", " val_size=0.0,\n", @@ -266,9 +268,9 @@ " range(len(trainer_metrics[\"train_loss\"])), trainer_metrics[\"train_loss\"]\n", ")\n", "# plotting\n", - "plt.xlabel('epoch')\n", - "plt.ylabel('loss')\n", - "plt.yscale('log') " + "plt.xlabel(\"epoch\")\n", + "plt.ylabel(\"loss\")\n", + "plt.yscale(\"log\")" ] }, { @@ -305,11 +307,11 @@ } ], "source": [ - "pts = pinn.problem.spatial_domain.sample(256, 'grid', variables='x')\n", - "predicted_output = pinn.forward(pts).extract('u').tensor.detach()\n", + "pts = pinn.problem.spatial_domain.sample(256, \"grid\", variables=\"x\")\n", + "predicted_output = pinn.forward(pts).extract(\"u\").tensor.detach()\n", "true_output = pinn.problem.solution(pts)\n", - "plt.plot(pts.extract(['x']), predicted_output, label='Neural Network solution')\n", - "plt.plot(pts.extract(['x']), true_output, label='True solution')\n", + "plt.plot(pts.extract([\"x\"]), predicted_output, label=\"Neural Network solution\")\n", + "plt.plot(pts.extract([\"x\"]), true_output, label=\"True solution\")\n", "plt.legend()" ] }, @@ -340,21 +342,21 @@ "# plotting solution\n", "with torch.no_grad():\n", " # Notice here we put [-4, 4]!!!\n", - " new_domain = CartesianDomain({'x' : [0, 4]})\n", - " x = new_domain.sample(1000, mode='grid')\n", + " new_domain = CartesianDomain({\"x\": [0, 4]})\n", + " x = new_domain.sample(1000, mode=\"grid\")\n", " fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n", " # Plot 1\n", - " axes[0].plot(x, problem.solution(x), label=r'$u(x)$', color='blue')\n", - " axes[0].set_title(r'True solution $u(x)$')\n", + " axes[0].plot(x, problem.solution(x), label=r\"$u(x)$\", color=\"blue\")\n", + " axes[0].set_title(r\"True solution $u(x)$\")\n", " axes[0].legend(loc=\"upper right\")\n", " # Plot 2\n", - " axes[1].plot(x, pinn(x), label=r'$u_{\\theta}(x)$', color='green')\n", - " axes[1].set_title(r'PINN solution $u_{\\theta}(x)$')\n", + " axes[1].plot(x, pinn(x), label=r\"$u_{\\theta}(x)$\", color=\"green\")\n", + " axes[1].set_title(r\"PINN solution $u_{\\theta}(x)$\")\n", " axes[1].legend(loc=\"upper right\")\n", " # Plot 3\n", " diff = torch.abs(problem.solution(x) - pinn(x))\n", - " axes[2].plot(x, diff, label=r'$|u(x) - u_{\\theta}(x)|$', color='red')\n", - " axes[2].set_title(r'Absolute difference $|u(x) - u_{\\theta}(x)|$')\n", + " axes[2].plot(x, diff, label=r\"$|u(x) - u_{\\theta}(x)|$\", color=\"red\")\n", + " axes[2].set_title(r\"Absolute difference $|u(x) - u_{\\theta}(x)|$\")\n", " axes[2].legend(loc=\"upper right\")\n", " # Adjust layout\n", " plt.tight_layout()\n",