Skip to content

Commit d03c503

Browse files
committed
workflow to export tutorials
1 parent da98d61 commit d03c503

File tree

16 files changed

+528
-659
lines changed

16 files changed

+528
-659
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: "Export Tutorials"
2+
3+
on:
4+
push:
5+
branches:
6+
- "**" # Run on push on all branches
7+
paths:
8+
- 'tutorials/**/*.ipynb'
9+
10+
jobs:
11+
export_tutorials:
12+
permissions: write-all
13+
runs-on: ubuntu-latest
14+
env:
15+
TUTORIAL_TIMEOUT: 1200s
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: 3.8
23+
24+
- name: Install dependencies
25+
run: |
26+
# Dependencies for tutorials
27+
python3 -m pip install --upgrade pip .[tutorial] black[jupyter]
28+
- name: Setup FFmpeg
29+
uses: FedericoCarboni/setup-ffmpeg@v2
30+
31+
- id: files
32+
uses: jitterbit/get-changed-files@v1
33+
with:
34+
token: ${{ secrets.GITHUB_TOKEN }}
35+
format: space-delimited
36+
37+
- name: Configure git
38+
run: |
39+
git config user.name "github-actions[bot]"
40+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
41+
- name: Export tutorials to .py and .html
42+
run: |
43+
set -x
44+
for file in ${{ steps.files.outputs.all }}; do
45+
if [[ $file == *.ipynb ]]; then
46+
filename=$(basename $file)
47+
pyfilename=$(echo ${filename%?????})py
48+
timeout --signal=SIGKILL $TUTORIAL_TIMEOUT python -Xfrozen_modules=off -m jupyter nbconvert --execute $file --to python --output $pyfilename --output-dir=$(dirname $file)
49+
htmlfilename=$(echo ${filename%?????} | sed -e 's/-//g')html
50+
htmldir="docs/source"/$(echo ${file%?????} | sed -e 's/-//g')html
51+
timeout --signal=SIGKILL $TUTORIAL_TIMEOUT python -Xfrozen_modules=off -m jupyter nbconvert --execute $file --to html --output $htmlfilename --output-dir=$htmldir
52+
fi
53+
done
54+
set +x
55+
56+
- name: Run formatter
57+
run: black tutorials/
58+
59+
- uses: benjlevesque/[email protected]
60+
id: short-sha
61+
62+
- name: Remove unwanted files
63+
run: |
64+
rm -rf build/
65+
- name: Create Pull Request
66+
uses: peter-evans/[email protected]
67+
with:
68+
labels: maintenance
69+
title: Export tutorial changed in ${{ steps.short-sha.outputs.sha }}
70+
branch: export-tutorial-${{ steps.short-sha.outputs.sha }}
71+
commit-message: export tutorials changed in ${{ steps.short-sha.outputs.sha }}
72+
delete-branch: true

tutorials/tutorial1/tutorial.ipynb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,25 +87,27 @@
8787
"source": [
8888
"## routine needed to run the notebook on Google Colab\n",
8989
"try:\n",
90-
" import google.colab\n",
91-
" IN_COLAB = True\n",
90+
" import google.colab\n",
91+
"\n",
92+
" IN_COLAB = True\n",
9293
"except:\n",
93-
" IN_COLAB = False\n",
94+
" IN_COLAB = False\n",
9495
"if IN_COLAB:\n",
95-
" !pip install \"pina-mathlab\"\n",
96+
" !pip install \"pina-mathlab\"\n",
9697
"\n",
9798
"import warnings\n",
9899
"\n",
99100
"from pina.problem import SpatialProblem, TimeDependentProblem\n",
100101
"from pina.domain import CartesianDomain\n",
101102
"\n",
102-
"warnings.filterwarnings('ignore')\n",
103+
"warnings.filterwarnings(\"ignore\")\n",
104+
"\n",
103105
"\n",
104106
"class TimeSpaceODE(SpatialProblem, TimeDependentProblem):\n",
105-
" \n",
106-
" output_variables = ['u']\n",
107-
" spatial_domain = CartesianDomain({'x': [0, 1]})\n",
108-
" temporal_domain = CartesianDomain({'t': [0, 1]})\n",
107+
"\n",
108+
" output_variables = [\"u\"]\n",
109+
" spatial_domain = CartesianDomain({\"x\": [0, 1]})\n",
110+
" temporal_domain = CartesianDomain({\"t\": [0, 1]})\n",
109111
"\n",
110112
" # other stuff ..."
111113
]
@@ -152,6 +154,7 @@
152154
"from pina.domain import CartesianDomain\n",
153155
"from pina.equation import Equation, FixedValue\n",
154156
"\n",
157+
"\n",
155158
"# defining the ode equation\n",
156159
"def ode_equation(input_, output_):\n",
157160
"\n",
@@ -164,6 +167,7 @@
164167
" # calculate the residual and return it\n",
165168
" return u_x - u\n",
166169
"\n",
170+
"\n",
167171
"class SimpleODE(SpatialProblem):\n",
168172
"\n",
169173
" output_variables = [\"u\"]\n",

tutorials/tutorial10/tutorial.ipynb

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@
2525
"source": [
2626
"## routine needed to run the notebook on Google Colab\n",
2727
"try:\n",
28-
" import google.colab\n",
29-
" IN_COLAB = True\n",
28+
" import google.colab\n",
29+
"\n",
30+
" IN_COLAB = True\n",
3031
"except:\n",
31-
" IN_COLAB = False\n",
32+
" IN_COLAB = False\n",
3233
"if IN_COLAB:\n",
33-
" !pip install \"pina-mathlab\"\n",
34-
" # get the data\n",
35-
" !mkdir \"data\"\n",
36-
" !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS.mat\" -O \"data/Data_KS.mat\"\n",
37-
" !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS2.mat\" -O \"data/Data_KS2.mat\"\n",
34+
" !pip install \"pina-mathlab\"\n",
35+
" # get the data\n",
36+
" !mkdir \"data\"\n",
37+
" !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS.mat\" -O \"data/Data_KS.mat\"\n",
38+
" !wget \"https://github.com/mathLab/PINA/raw/refs/heads/master/tutorials/tutorial10/data/Data_KS2.mat\" -O \"data/Data_KS2.mat\"\n",
3839
"\n",
3940
"import torch\n",
4041
"import matplotlib.pyplot as plt\n",
@@ -46,7 +47,7 @@
4647
"from pina.solver import SupervisedSolver\n",
4748
"from pina.problem.zoo import SupervisedProblem\n",
4849
"\n",
49-
"warnings.filterwarnings('ignore')"
50+
"warnings.filterwarnings(\"ignore\")"
5051
]
5152
},
5253
{
@@ -106,17 +107,24 @@
106107
],
107108
"source": [
108109
"# load data\n",
109-
"data=io.loadmat(\"data/Data_KS.mat\")\n",
110+
"data = io.loadmat(\"data/Data_KS.mat\")\n",
110111
"\n",
111112
"# converting to label tensor\n",
112-
"initial_cond_train = LabelTensor(torch.tensor(data['initial_cond_train'], dtype=torch.float), ['t','x','u0'])\n",
113-
"initial_cond_test = LabelTensor(torch.tensor(data['initial_cond_test'], dtype=torch.float), ['t','x','u0'])\n",
114-
"sol_train = LabelTensor(torch.tensor(data['sol_train'], dtype=torch.float), ['u'])\n",
115-
"sol_test = LabelTensor(torch.tensor(data['sol_test'], dtype=torch.float), ['u'])\n",
116-
"\n",
117-
"print('Data Loaded')\n",
118-
"print(f' shape initial condition: {initial_cond_train.shape}')\n",
119-
"print(f' shape solution: {sol_train.shape}')"
113+
"initial_cond_train = LabelTensor(\n",
114+
" torch.tensor(data[\"initial_cond_train\"], dtype=torch.float),\n",
115+
" [\"t\", \"x\", \"u0\"],\n",
116+
")\n",
117+
"initial_cond_test = LabelTensor(\n",
118+
" torch.tensor(data[\"initial_cond_test\"], dtype=torch.float), [\"t\", \"x\", \"u0\"]\n",
119+
")\n",
120+
"sol_train = LabelTensor(\n",
121+
" torch.tensor(data[\"sol_train\"], dtype=torch.float), [\"u\"]\n",
122+
")\n",
123+
"sol_test = LabelTensor(torch.tensor(data[\"sol_test\"], dtype=torch.float), [\"u\"])\n",
124+
"\n",
125+
"print(\"Data Loaded\")\n",
126+
"print(f\" shape initial condition: {initial_cond_train.shape}\")\n",
127+
"print(f\" shape solution: {sol_train.shape}\")"
120128
]
121129
},
122130
{
@@ -151,35 +159,58 @@
151159
"# helper function\n",
152160
"def plot_trajectory(coords, real, no_sol=None):\n",
153161
" # find the x-t shapes\n",
154-
" dim_x = len(torch.unique(coords.extract('x')))\n",
155-
" dim_t = len(torch.unique(coords.extract('t')))\n",
162+
" dim_x = len(torch.unique(coords.extract(\"x\")))\n",
163+
" dim_t = len(torch.unique(coords.extract(\"t\")))\n",
156164
" # if we don't have the Neural Operator solution we simply plot the real one\n",
157165
" if no_sol is None:\n",
158166
" fig, axs = plt.subplots(1, 1, figsize=(15, 5), sharex=True, sharey=True)\n",
159-
" c = axs.imshow(real.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n",
160-
" axs.set_title('Real solution')\n",
167+
" c = axs.imshow(\n",
168+
" real.reshape(dim_t, dim_x).T.detach(),\n",
169+
" extent=[0, 50, 0, 64],\n",
170+
" cmap=\"PuOr_r\",\n",
171+
" aspect=\"auto\",\n",
172+
" )\n",
173+
" axs.set_title(\"Real solution\")\n",
161174
" fig.colorbar(c, ax=axs)\n",
162-
" axs.set_xlabel('t')\n",
163-
" axs.set_ylabel('x')\n",
175+
" axs.set_xlabel(\"t\")\n",
176+
" axs.set_ylabel(\"x\")\n",
164177
" # otherwise we plot the real one, the Neural Operator one, and their difference\n",
165178
" else:\n",
166179
" fig, axs = plt.subplots(1, 3, figsize=(15, 5), sharex=True, sharey=True)\n",
167-
" axs[0].imshow(real.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n",
168-
" axs[0].set_title('Real solution')\n",
169-
" axs[1].imshow(no_sol.reshape(dim_t, dim_x).T.detach(),extent=[0, 50, 0, 64], cmap='PuOr_r', aspect='auto')\n",
170-
" axs[1].set_title('NO solution')\n",
171-
" 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",
172-
" axs[2].set_title('Absolute difference')\n",
180+
" axs[0].imshow(\n",
181+
" real.reshape(dim_t, dim_x).T.detach(),\n",
182+
" extent=[0, 50, 0, 64],\n",
183+
" cmap=\"PuOr_r\",\n",
184+
" aspect=\"auto\",\n",
185+
" )\n",
186+
" axs[0].set_title(\"Real solution\")\n",
187+
" axs[1].imshow(\n",
188+
" no_sol.reshape(dim_t, dim_x).T.detach(),\n",
189+
" extent=[0, 50, 0, 64],\n",
190+
" cmap=\"PuOr_r\",\n",
191+
" aspect=\"auto\",\n",
192+
" )\n",
193+
" axs[1].set_title(\"NO solution\")\n",
194+
" c = axs[2].imshow(\n",
195+
" (real - no_sol).abs().reshape(dim_t, dim_x).T.detach(),\n",
196+
" extent=[0, 50, 0, 64],\n",
197+
" cmap=\"PuOr_r\",\n",
198+
" aspect=\"auto\",\n",
199+
" )\n",
200+
" axs[2].set_title(\"Absolute difference\")\n",
173201
" fig.colorbar(c, ax=axs.ravel().tolist())\n",
174202
" for ax in axs:\n",
175-
" ax.set_xlabel('t')\n",
176-
" ax.set_ylabel('x')\n",
203+
" ax.set_xlabel(\"t\")\n",
204+
" ax.set_ylabel(\"x\")\n",
177205
" plt.show()\n",
178206
"\n",
207+
"\n",
179208
"# a sample trajectory (we use the sample 5, feel free to change)\n",
180209
"sample_number = 20\n",
181-
"plot_trajectory(coords=initial_cond_train[sample_number].extract(['x', 't']),\n",
182-
" real=sol_train[sample_number].extract('u'))\n"
210+
"plot_trajectory(\n",
211+
" coords=initial_cond_train[sample_number].extract([\"x\", \"t\"]),\n",
212+
" real=sol_train[sample_number].extract(\"u\"),\n",
213+
")"
183214
]
184215
},
185216
{
@@ -300,7 +331,12 @@
300331
],
301332
"source": [
302333
"# initialize problem\n",
303-
"problem = SupervisedProblem(initial_cond_train, sol_train, input_variables=initial_cond_train.labels, output_variables=sol_train.labels)\n",
334+
"problem = SupervisedProblem(\n",
335+
" initial_cond_train,\n",
336+
" sol_train,\n",
337+
" input_variables=initial_cond_train.labels,\n",
338+
" output_variables=sol_train.labels,\n",
339+
")\n",
304340
"# initialize solver\n",
305341
"solver = SupervisedSolver(problem=problem, model=model)\n",
306342
"# train, only CPU and avoid model summary at beginning of training (optional)\n",
@@ -343,9 +379,11 @@
343379
"source": [
344380
"sample_number = 2\n",
345381
"no_sol = solver(initial_cond_test)\n",
346-
"plot_trajectory(coords=initial_cond_test[sample_number].extract(['x', 't']),\n",
347-
" real=sol_test[sample_number].extract('u'),\n",
348-
" no_sol=no_sol[5])"
382+
"plot_trajectory(\n",
383+
" coords=initial_cond_test[sample_number].extract([\"x\", \"t\"]),\n",
384+
" real=sol_test[sample_number].extract(\"u\"),\n",
385+
" no_sol=no_sol[5],\n",
386+
")"
349387
]
350388
},
351389
{
@@ -373,15 +411,19 @@
373411
"source": [
374412
"from pina.loss import PowerLoss\n",
375413
"\n",
376-
"error_metric = PowerLoss(p=2) # we use the MSE loss\n",
414+
"error_metric = PowerLoss(p=2) # we use the MSE loss\n",
377415
"\n",
378416
"with torch.no_grad():\n",
379417
" no_sol_train = solver(initial_cond_train)\n",
380-
" err_train = error_metric(sol_train.extract('u'), no_sol_train).mean() # we average the error over trajectories\n",
418+
" err_train = error_metric(\n",
419+
" sol_train.extract(\"u\"), no_sol_train\n",
420+
" ).mean() # we average the error over trajectories\n",
381421
" no_sol_test = solver(initial_cond_test)\n",
382-
" err_test = error_metric(sol_test.extract('u'),no_sol_test).mean() # we average the error over trajectories\n",
383-
" print(f'Training error: {float(err_train):.3f}')\n",
384-
" print(f'Testing error: {float(err_test):.3f}')"
422+
" err_test = error_metric(\n",
423+
" sol_test.extract(\"u\"), no_sol_test\n",
424+
" ).mean() # we average the error over trajectories\n",
425+
" print(f\"Training error: {float(err_train):.3f}\")\n",
426+
" print(f\"Testing error: {float(err_test):.3f}\")"
385427
]
386428
},
387429
{

0 commit comments

Comments
 (0)