Skip to content

Commit 773eb12

Browse files
authored
Update burgers_mol_vs_pinn.md
Updated with a comparison
1 parent 29f2190 commit 773eb12

File tree

1 file changed

+154
-7
lines changed

1 file changed

+154
-7
lines changed
Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,158 @@
1-
## 📝 Goal of the tutorial:
2-
This tutorial aims to document the performance trade-offs of using PINNs and high-fidelity traditional numerical solvers from the SciML ecosystem.
1+
# Trusting PINNs: Validating a Neural Solver against a standard numerical solver
32

4-
## 🧪 Scope of Comparison:
3+
## Dependencies
4+
These packages are required to run the following tutorial:
55

6-
The comparison will be using a multi parameter PDE like the Burgers' or Heat Equation, where a physical parameter is varied.
6+
```julia
7+
using Pkg
8+
Pkg.add(["NeuralPDE", "MethodOfLines", "OrdinaryDiffEq", "ModelingToolkit", "Lux", "Optimization", "OptimizationOptimJL", "OptimizationOptimisers", "DomainSets", "Plots", "Statistics"])
9+
```
710

8-
* Approach using PINNs : Train a single, continuous PINN model as a function of space, time, and the varied parameter.
9-
* Traditional Numerical Approach: Use an optimized solver to generate solutions across the same parameter range.
11+
## 1D Viscous Burgers' Equation:
1012

11-
**Status: Work In Progress (WIP)**
13+
```math
14+
\frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} - \nu \frac{\partial^2 u}{\partial x^2} = 0
15+
```
16+
17+
Where we set $\nu = 0.02$ as the viscosity coefficient. The problem is defined on the domain $t \in [0, 1]$ and $x \in [-1, 1]$ with:
18+
* **Initial Condition:** $u(0, x) = -\sin(\pi x)$
19+
* **Boundary Conditions:** $u(t, -1) = u(t, 1) = 0$
20+
21+
Implementing this using`ModelingToolkit.jl`:
22+
23+
```@example burgers_comp
24+
using NeuralPDE, MethodOfLines, OrdinaryDiffEq, ModelingToolkit
25+
using Lux, Optimization, OptimizationOptimJL, OptimizationOptimisers
26+
using DomainSets, Plots, Random, Statistics
27+
28+
# Setting seed to ensure reproducibility:
29+
rng = Random.default_rng()
30+
Random.seed!(rng, 42)
31+
32+
# Statistics Tracker Training:
33+
mutable struct TrainingStats
34+
losses::Vector{Float64}
35+
best_loss::Float64
36+
end
37+
38+
@parameters t x
39+
@variables u(..)
40+
Dt = Differential(t)
41+
Dx = Differential(x)
42+
Dxx = Differential(x)^2
43+
44+
# Setting values of constants:
45+
ν = 0.02
46+
t_max = 1.0
47+
x_min = -1.0
48+
x_max = 1.0
49+
50+
# Defining the 1D Viscous Burgers' Equation:
51+
eq = Dt(u(t,x)) + u(t,x)*Dx(u(t,x)) - ν*Dxx(u(t,x)) ~ 0
52+
53+
# Setting Boundary Conditions:
54+
bcs = [u(0.0, x) ~ -sin(π * x),
55+
u(t, x_min) ~ 0.0,
56+
u(t, x_max) ~ 0.0]
57+
domains = [t ∈ Interval(0.0, t_max), x ∈ Interval(x_min, x_max)]
58+
59+
# Defining the PDE System:
60+
@named pde_system = PDESystem(eq, bcs, domains, [t, x], [u(t, x)])
61+
```
62+
63+
## Generating the ground truth: (MethodOfLines)
64+
We discretize the spatial domain using a finite difference scheme and solve the resulting ODE system with a high-order solver (`Tsit5`). This is done to generate a ground truth solution to compare the results of the neural network to.
65+
66+
```@example burgers_comp
67+
68+
dx_mol = 0.01
69+
discretization_mol = MOLFiniteDifference([x => dx_mol], t)
70+
prob_mol = discretize(pde_system, discretization_mol)
71+
@time sol_mol = solve(prob_mol, Tsit5(), saveat=0.01)
72+
```
73+
74+
## Training a PINN:
75+
We use a grid training strategy to train a deep neural network. The best loss achieved is stored.
76+
77+
```@example burgers_comp
78+
# Defining the architecture of the Neural Network:
79+
chain = Lux.Chain(
80+
Dense(2, 20, Lux.tanh),
81+
Dense(20, 30, Lux.tanh),
82+
Dense(30, 30, Lux.tanh),
83+
Dense(30, 20, Lux.tanh),
84+
Dense(20, 20, Lux.tanh),
85+
Dense(20, 1)
86+
)
87+
strategy = GridTraining(0.05)
88+
discretization_pinn = PhysicsInformedNN(chain, strategy)
89+
prob_pinn = discretize(pde_system, discretization_pinn)
90+
stats = TrainingStats([], Inf)
91+
callback = function (p, l)
92+
push!(stats.losses, l)
93+
if l < stats.best_loss
94+
stats.best_loss = l
95+
end
96+
97+
# In case we get NaN anywhere:
98+
if isnan(l)
99+
println("!!! WARNING: Loss became NaN. Aborting training.")
100+
return true
101+
end
102+
103+
if length(stats.losses) % 500 == 0
104+
println("Iter: $(length(stats.losses)) | Current: $l | Best: $(stats.best_loss)")
105+
end
106+
return false
107+
end
108+
```
109+
110+
## Training strategy
111+
The training of the neural network consists of 2 stages:
112+
1. **Adam**: Rapidly descends the loss landscape to find a rough solution (2000 iterations).
113+
2. **BFGS**: A second-order optimizer used to fine-tune the solution to high precision (1000 iterations).
114+
115+
```@example burgers_comp
116+
# Use of Adam optimizer:
117+
res_adam = Optimization.solve(prob_pinn, OptimizationOptimisers.Adam(0.01); callback=callback, maxiters=2000)
118+
# Use of the BFGS Optimizer:
119+
prob_bfgs = remake(prob_pinn, u0=res_adam.u)
120+
@time res_pinn = Optimization.solve(prob_bfgs, BFGS(); callback=callback, maxiters=1000)
121+
```
122+
123+
## Results Analysis:
124+
Generating an animation to visually compare the prediction from the PINN against the ground truth generated by the numerical solver and calculating the global MSE:
125+
```@example burgers_comp
126+
ts_mol = sol_mol[t]
127+
xs_mol = sol_mol[x]
128+
u_truth_matrix = sol_mol[u(t, x)]
129+
phi = discretization_pinn.phi
130+
u_predict_matrix = [first(phi([t_val, x_val], res_pinn.u)) for t_val in ts_mol, x_val in xs_mol]
131+
mse_global = mean(abs2, u_truth_matrix .- u_predict_matrix)
132+
println("FINAL GLOBAL MSE: $mse_global")
133+
anim = @animate for (i, t_val) in enumerate(ts_mol)
134+
truth_slice = u_truth_matrix[i, :]
135+
pred_slice = u_predict_matrix[i, :]
136+
137+
p1 = plot(xs_mol, truth_slice, label="MOL Ground Truth",
138+
lw=3, color=:blue, ylims=(-1.2, 1.2), legend=:topright,
139+
title="Burgers' (MOL) vs PINN (t = $(round(t_val, digits=2)))")
140+
plot!(p1, xs_mol, pred_slice, label="PINN Prediction",
141+
ls=:dash, lw=3, color=:orange)
142+
143+
err_slice = abs.(truth_slice .- pred_slice)
144+
p2 = plot(xs_mol, err_slice, label="Abs Error",
145+
color=:red, fill=(0, 0.2, :red), ylims=(0, 0.2),
146+
xlabel="x", ylabel="Error")
147+
148+
plot(p1, p2, layout=(2, 1))
149+
end
150+
151+
# Saving the gif animation:
152+
mkpath("assets")
153+
gif(anim, "assets/burgers_mol_pinn_evolution.gif", fps=15)
154+
```
155+
![Burgers Evolution](assets/burgers_mol_pinn_evolution.gif)
156+
157+
## Performance Summary
158+
While MethodOfLines.jl approach is significantly faster than for this equation, we have demonstrated the ability of a PINN to learn the solution without any mesh generation. This may be useful for solving PDEs in higher dimensions or inverse problems.

0 commit comments

Comments
 (0)