Automatize Tutorials html, py files creation (#496)

* workflow to export tutorials

---------
This commit is contained in:
Dario Coscia
2025-03-15 11:01:19 +01:00
committed by FilippoOlivo
parent 8dfc9d19db
commit 3ff9f0c9a2
51 changed files with 140529 additions and 440 deletions

View File

@@ -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",
"'''fit the model to pressure training data'''\n",
"pod_rbfp = PODRBF(pod_rank=20, rbf_kernel=\"thin_plate_spline\")\n",
"\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}\")"
]
},
{

View File

@@ -2,11 +2,11 @@
# 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.
#
# 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.
@@ -16,14 +16,15 @@
## 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"')
import google.colab
get_ipython().run_line_magic('matplotlib', 'inline')
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
@@ -33,11 +34,11 @@ import warnings
from pina.model.block import PODBlock, RBFBlock
from pina import LabelTensor
warnings.filterwarnings('ignore')
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]:
@@ -45,6 +46,7 @@ warnings.filterwarnings('ignore')
import smithers
from smithers.dataset import LidCavity
dataset = LidCavity()
@@ -54,13 +56,13 @@ dataset = LidCavity()
fig, axs = plt.subplots(1, 3, figsize=(14, 3))
for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['mag(v)'][: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}')
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]):
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}')
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:
@@ -68,15 +70,16 @@ for ax, par, u in zip(axs, dataset.params[:3], dataset.snapshots['p'][:3]):
# 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'])
"""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.
@@ -84,15 +87,16 @@ mu = LabelTensor(mu, labels=['mu'])
# In[5]:
'''number of snapshots'''
"""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'''
"""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:]
@@ -105,8 +109,9 @@ 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)
@@ -121,8 +126,9 @@ 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)
@@ -137,6 +143,7 @@ class PODRBF(torch.nn.Module):
"""
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
@@ -146,7 +153,6 @@ class PODRBF(torch.nn.Module):
"""
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:
@@ -154,13 +160,14 @@ class PODRBF(torch.nn.Module):
# In[8]:
'''create the model'''
pod_rbfu = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')
"""create the model"""
'''fit the model to velocity training data'''
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'''
"""predict the parameter using the fitted model"""
u_train_rbf = pod_rbfu(mu_train)
u_test_rbf = pod_rbfu(mu_test)
@@ -170,12 +177,12 @@ u_test_rbf = pod_rbfu(mu_test)
# 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)
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}')
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:
@@ -192,23 +199,32 @@ 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}')
relative_u_error_rbf = np.where(
u_test[idx] < 1e-7, 1e-7, relative_u_error_rbf / u_test[idx]
)
cm = axs[0, i].tricontourf(dataset.triang, rbf_.detach()) # POD-RBF prediction
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
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
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()
@@ -217,34 +233,37 @@ plt.show()
# In[11]:
'''create the model'''
pod_rbfp = PODRBF(pod_rank=20, rbf_kernel='thin_plate_spline')
"""create the model"""
'''fit the model to pressure training data'''
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'''
"""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)
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}')
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:
# 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]):
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}')
ax.set_title(f"$p$ field for $\mu$ = {par[0]:.4f}")
plt.tight_layout()
plt.show()
@@ -255,11 +274,13 @@ plt.show()
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]):
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()
ax.set_title(f"$p$ field for $\mu$ = {par[0]:.4f}")
plt.tight_layout()
plt.show()
@@ -268,45 +289,50 @@ plt.show()
# In[14]:
'''excluding problematic snapshots'''
"""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])])
"""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'])
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)
ratio = 0.9
new_train = int(newn * ratio)
new_p_train, new_p_test = newp[:new_train], newp[new_train:]
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 = 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)
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}')
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