Ready-made models¶
ps.models builds the systems of the SNAPE papers and a few more, with sensible starting
values. Pass one to fit in place of an equation string; unknowns= still overrides the
starting values.
| Builder | Equation | Coordinates | True values of the matching data |
|---|---|---|---|
burgers() |
u_t + θ1 u u_x + θ2 u_xx = 0 |
x, t | (1, −0.1) |
kdv() |
u_t + θ1 u u_x + θ2 u_xxx = 0 |
x, t | (6, 1) |
ks() |
u_t + θ1 u u_x + θ2 u_xx + θ3 u_xxxx = 0 |
x, t | (1, 1, 1) |
duffing() |
x_tt + θ1 x_t + θ2 x + θ3 x³ = p |
t | (0.5, −1, 1) |
vanderpol() |
x_tt + θ1 x_t + θ2 x² x_t + θ3 x = 0 |
t | (−8, 8, 1) |
wave2d() |
u_tt − θ1 u_xx − θ2 u_yy = 0 |
x, y, t | (1, 1) |
mdof_chain(n) |
a chain of n masses fixed at one end |
t | k = 12 … 4, c = 0.6 … 0.2 |
The coefficients are named theta1, theta2, …, except in mdof_chain, whose springs and
dampers are k1 … kn and c1 … cn. ps.models.TRUTH holds the true values above.
Data for each¶
| Model | Data |
|---|---|
burgers, kdv, ks |
ps.datasets.load(...): the PDE-FIND data (Data) |
duffing |
ode.duffing(): the forced oscillator of the paper, with the force as input p |
vanderpol |
ode.vanderpol(): μ = 8 by default, with sharp fronts |
wave2d |
pde2d.wave(): the paper's 2-D wave, exact to about 1e-11 |
mdof_chain |
ode.mdof_chain(): displacements or accelerations |
The generators live in pysnape.data.generators and integrate to tight tolerances, so that
any error in a fit comes from the noise and the method, not from the data.
The MDOF chain¶
ps.models.mdof_chain(n, masses=1.0) writes one force balance per mass, for a chain fixed
at its first spring and free at its last mass:
import pysnape as ps
model = ps.models.mdof_chain(3)
for eq in model.equations:
print(eq.text)
# m1*x1_tt + c1*x1_t + k1*x1 - c2*(x2_t - x1_t) - k2*(x2 - x1) = f1
# m2*x2_tt + c2*(x2_t - x1_t) + k2*(x2 - x1) - c3*(x3_t - x2_t) - k3*(x3 - x2) = f2
# m3*x3_tt + c3*(x3_t - x2_t) + k3*(x3 - x2) = f3
The masses are known; f1 … fn are the applied forces, zero where a mass is not driven.
Each spring and damper appears in two equations and is estimated from both, and all are kept
non-negative. ode.mdof_chain(n_dof=5) makes matching data: unit masses, a broadband shaker
on the free end, displacements or (with observe="acceleration") accelerations.
Measured on five masses, ten unknowns, with the default settings: from 5 % noisy accelerations, every spring within 0.2 % and every damper within 3.6 %.
The 2-D wave¶
pde2d.wave() reproduces the paper's setup: u_tt = c (u_xx + u_yy) on the square
[−1, 1]², fixed at x = ±1, free at y = ±1, with its initial displacement and velocity, on a
50 × 50 × 100 grid over t from 0 to 10. It is solved by expanding in the natural modes of
those edges, so it is exact to about 1e-11.