This example is also available as a Jupyter notebook: cs4b.ipynb
Case Studies: Case Study 4; Inaccurate continuum limit
This example shows how we obtained the results in the paper for the fourth case study, for the case that the continuum limit is inaccurate. Let us load in the packages we will need.
using StepwiseEQL
using CairoMakie
using EpithelialDynamics1D
using OrdinaryDiffEq
using Random
Simulating
Let us start by simulating the problem. We use the force law $F(\ell) = k(s-\ell)$ and the proliferation law $G(\ell) = \beta[1 - 1/(K\ell)]$.
final_time = 250.0
domain_length = 30.0
midpoint = domain_length / 2
initial_condition = [LinRange(0, 5, 30);] |> unique!
damping_constant = 1.0
resting_spring_length = 0.2
spring_constant = 1 / 5
k = spring_constant
η = damping_constant
s = resting_spring_length
force_law_parameters = (s=resting_spring_length, k=spring_constant)
force_law = (δ, p) -> p.k * (p.s - δ)
Δt = 1e-2
K = 15.0
β = 0.15
G = (δ, p) -> p.β * (one(δ) - inv(p.K * δ))
Gp = (β=β, K=K)
prob = CellProblem(;
final_time,
initial_condition,
damping_constant,
force_law,
force_law_parameters,
proliferation_law=G,
proliferation_period=Δt,
proliferation_law_parameters=Gp,
fix_right=false)
ens_prob = EnsembleProblem(prob)
Random.seed!(292919)
interval_1 = (0.0, 2, 20)
interval_2 = (2, 10, 200)
interval_3 = (10.0, 20, 200)
interval_4 = (20, 50, 200)
t = [0, 5, 25, 50, 100, 250]
saveat = [t
LinRange(interval_1...)
LinRange(interval_2...)
LinRange(interval_3...)
LinRange(interval_4...)] |> unique! |> sort!
esol = solve(ens_prob, Tsit5(), EnsembleSerial(); trajectories=1000, saveat=saveat)
Equation learning
We now learn the equations, applying our sequential procedure.
diffusion_basis = PolynomialBasis(-1, -3)
reaction_basis = PolynomialBasis(1, 5)
rhs_basis = PolynomialBasis(1, 5)
moving_boundary_basis = PolynomialBasis(-1, -3)
(::BasisSet{Tuple{StepwiseEQL.var"#52#54"{Int64}, StepwiseEQL.var"#52#54"{Int64}, StepwiseEQL.var"#52#54"{Int64}}}) (generic function with 3 methods)
eql_sol = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
reaction_theta=zeros(5), moving_boundary_theta=zeros(3), rhs_theta=zeros(5),
mesh_points=100,
num_knots=50, threshold_tol=(dt=0.4,),
initial=:none, time_range=(interval_1[1], interval_1[2]))
StepwiseEQL Solution.
D(q) = θ₂ᵈ ϕ₂ᵈ(q)
┌──────┬──────────────────┬────────┐
│ Step │ θ₁ᵈ θ₂ᵈ θ₃ᵈ │ Loss │
├──────┼──────────────────┼────────┤
│ 1 │ 0.00 0.00 0.00 │ -16.64 │
│ 2 │ 0.00 0.21 0.00 │ -15.64 │
└──────┴──────────────────┴────────┘
eql_sol_2 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta,
reaction_theta=zeros(5), rhs_theta=zeros(5),
mesh_points=100, threshold_tol=(dL=0.4,),
num_knots=100, initial=:none,
time_range=(interval_2[1], interval_2[2]))
┌──────┬──────────────────┬────────┐
│ Step │ θ₁ᵉ θ₂ᵉ θ₃ᵉ │ Loss │
├──────┼──────────────────┼────────┤
│ 1 │ 0.00 0.00 0.00 │ -11.24 │
│ 2 │ 0.00 0.23 0.00 │ -10.24 │
└──────┴──────────────────┴────────┘
eql_sol_3 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta, reaction_theta=zeros(5),
moving_boundary_theta=eql_sol_2.moving_boundary_theta,
mesh_points=100, num_knots=100,
initial=:none,
time_range=(interval_3[1], interval_3[2]))
StepwiseEQL Solution.
H(q) = θ₁ʰ ϕ₁ʰ(q) + θ₄ʰ ϕ₄ʰ(q)
┌──────┬────────────────────────────────┬────────┐
│ Step │ θ₁ʰ θ₂ʰ θ₃ʰ θ₄ʰ θ₅ʰ │ Loss │
├──────┼────────────────────────────────┼────────┤
│ 1 │ 0.00 0.00 0.00 0.00 0.00 │ -8.31 │
│ 2 │ 0.00 0.00 0.00 -0.01 0.00 │ -11.25 │
│ 3 │ -0.15 0.00 0.00 -0.01 0.00 │ -12.31 │
└──────┴────────────────────────────────┴────────┘
eql_sol_4 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta,
moving_boundary_theta=eql_sol_2.moving_boundary_theta,
rhs_theta=eql_sol_3.rhs_theta, mesh_points=1000,
num_knots=100,
threshold_tol=(q=0.3,),
initial=:none, time_range=(interval_4[1], interval_4[2]))
StepwiseEQL Solution.
R(q) = θ₁ʳ ϕ₁ʳ(q) + θ₂ʳ ϕ₂ʳ(q)
┌──────┬───────────────────────────────┬────────┐
│ Step │ θ₁ʳ θ₂ʳ θ₃ʳ θ₄ʳ θ₅ʳ │ Loss │
├──────┼───────────────────────────────┼────────┤
│ 1 │ 0.00 0.00 0.00 0.00 0.00 │ -9.40 │
│ 2 │ 0.00 0.00 0.00 0.00 0.00 │ -8.70 │
│ 3 │ 0.11 -0.01 0.00 0.00 0.00 │ -17.74 │
└──────┴───────────────────────────────┴────────┘
Plotting
Now we plot our results.
fig = Figure(fontsize=81, resolution=(3250, 2070))
ax_pde = Axis(fig[1, 1],
xlabel=L"x", ylabel=L"q(x, t)",
title=L"(a):$ $ PDE comparison",
titlealign=:left,
width=1200, height=400,
xticks=(0:5:40, [L"%$s" for s in 0:5:40]),
yticks=(0:5:15, [L"%$s" for s in 0:5:15])
)
xlims!(ax_pde, 0, 10)
ylims!(ax_pde, 0, 20)
ax_leading_edge = Axis(fig[1, 2],
xlabel=L"t", ylabel=L"L(t)",
title=L"(b):$ $ Leading edge comparison",
titlealign=:left,
width=1200, height=400,
xticks=(0:50:250, [L"%$s" for s in 0:50:250]),
yticks=(0:5:10, [L"%$s" for s in 0:5:10])
)
xlims!(ax_leading_edge, 0, 250)
ylims!(ax_leading_edge, 0, 10)
ax_dq = Axis(fig[2, 1],
xlabel=L"q", ylabel=L"D(q)",
title=L"(c): $D(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(0.0005:0.0025:0.01, [L"%$s" for s in 0.0005:0.0025:0.01])
)
q_min, q_max = extrema(stack(eql_sol_4.model.cell_sol.q))
q_min = 5
xlims!(ax_dq, q_min, q_max)
ylims!(ax_dq, 0.0005, 0.01)
ax_rq = Axis(fig[2, 2],
xlabel=L"q", ylabel=L"R(q)",
title=L"(d): $R(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(-0.25:0.25:0.75, [L"%$s" for s in -0.25:0.25:0.75])
)
xlims!(ax_rq, q_min, q_max)
ylims!(ax_rq, -0.25, 0.75)
ax_hq = Axis(fig[3, 1],
xlabel=L"q", ylabel=L"H(q)",
title=L"(e): $H(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(-24:6:0, [L"%$s" for s in -24:6:0])
)
xlims!(ax_hq, q_min, q_max)
ylims!(ax_hq, -24, 2)
ax_eq = Axis(fig[3, 2],
xlabel=L"q", ylabel=L"E(q)",
title=L"(f): $E(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(0.0005:0.0025:0.01, [L"%$s" for s in 0.0005:0.0025:0.01])
)
xlims!(ax_eq, q_min, q_max)
ylims!(ax_eq, 0.0005, 0.01)
time_indices = [findlast(≤(τ), esol[1].t) for τ in t]
colors = (:black, :red, :blue, :green, :orange, :purple, :brown)
pde_ξ = eql_sol_4.pde.geometry.mesh_points
pde_L = eql_sol_4.pde_sol[end, :]
pde_q = eql_sol_4.pde_sol[begin:(end-1), :]
cell_q = eql_sol_4.model.cell_sol.q
cell_r = eql_sol_4.model.cell_sol.u
cell_L = last.(eql_sol_4.model.cell_sol.u)
q_range = LinRange(q_min, q_max, 100)
D_cont_fnc = q -> (k / η) / q^2
R_cont_fnc = q -> β * q * (1 - q / K)
H_cont_fnc = q -> 2q^2 * (1 - q * s)
E_cont_fnc = D_cont_fnc
D_cont = D_cont_fnc.(q_range)
R_cont = R_cont_fnc.(q_range)
H_cont = H_cont_fnc.(q_range)
E_cont = E_cont_fnc.(q_range)
D_sol = diffusion_basis.(q_range, Ref(eql_sol.diffusion_theta), Ref(nothing))
R_sol = reaction_basis.(q_range, Ref(eql_sol_4.reaction_theta), Ref(nothing))
H_sol = rhs_basis.(q_range, Ref(eql_sol_3.rhs_theta), Ref(nothing))
E_sol = moving_boundary_basis.(q_range, Ref(eql_sol_2.moving_boundary_theta), Ref(nothing))
for (j, i) in enumerate(time_indices)
lines!(ax_pde, pde_ξ * pde_L[i], pde_q[:, i], color=colors[j], linestyle=:dash, linewidth=8)
lines!(ax_pde, cell_r[i], cell_q[i], color=colors[j], linewidth=4, label=L"%$(t[j])")
end
arrows!(ax_pde, [5.0], [3.0], [3.0], [0.0], color=:black, linewidth=12, arrowsize=57)
text!(ax_pde, [7.0], [3.5], text=L"t", fontsize=81)
lines!(ax_leading_edge, esol[1].t, pde_L, color=:red, linestyle=:dash, linewidth=5, label=L"$ $Learned")
lines!(ax_leading_edge, esol[1].t, cell_L, color=:black, linewidth=3, label=L"$ $Discrete")
axislegend(ax_leading_edge, position=:rb)
lines!(ax_dq, q_range, D_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_dq, q_range, D_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_dq, position=:rt)
lines!(ax_rq, q_range, R_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_rq, q_range, R_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
hlines!(ax_rq, [0.0], color=:grey, linewidth=12, linestyle=:dash)
axislegend(ax_rq, position=:lb)
lines!(ax_hq, q_range, H_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_hq, q_range, H_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_hq, position=:rt)
lines!(ax_eq, q_range, E_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_eq, q_range, E_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_eq, position=:rt)
fig

Just the code
An uncommented version of this example is given below. You can view the source code for this file here.
using StepwiseEQL
using CairoMakie
using EpithelialDynamics1D
using OrdinaryDiffEq
using Random
final_time = 250.0
domain_length = 30.0
midpoint = domain_length / 2
initial_condition = [LinRange(0, 5, 30);] |> unique!
damping_constant = 1.0
resting_spring_length = 0.2
spring_constant = 1 / 5
k = spring_constant
η = damping_constant
s = resting_spring_length
force_law_parameters = (s=resting_spring_length, k=spring_constant)
force_law = (δ, p) -> p.k * (p.s - δ)
Δt = 1e-2
K = 15.0
β = 0.15
G = (δ, p) -> p.β * (one(δ) - inv(p.K * δ))
Gp = (β=β, K=K)
prob = CellProblem(;
final_time,
initial_condition,
damping_constant,
force_law,
force_law_parameters,
proliferation_law=G,
proliferation_period=Δt,
proliferation_law_parameters=Gp,
fix_right=false)
ens_prob = EnsembleProblem(prob)
Random.seed!(292919)
interval_1 = (0.0, 2, 20)
interval_2 = (2, 10, 200)
interval_3 = (10.0, 20, 200)
interval_4 = (20, 50, 200)
t = [0, 5, 25, 50, 100, 250]
saveat = [t
LinRange(interval_1...)
LinRange(interval_2...)
LinRange(interval_3...)
LinRange(interval_4...)] |> unique! |> sort!
esol = solve(ens_prob, Tsit5(), EnsembleSerial(); trajectories=1000, saveat=saveat)
diffusion_basis = PolynomialBasis(-1, -3)
reaction_basis = PolynomialBasis(1, 5)
rhs_basis = PolynomialBasis(1, 5)
moving_boundary_basis = PolynomialBasis(-1, -3)
eql_sol = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
reaction_theta=zeros(5), moving_boundary_theta=zeros(3), rhs_theta=zeros(5),
mesh_points=100,
num_knots=50, threshold_tol=(dt=0.4,),
initial=:none, time_range=(interval_1[1], interval_1[2]))
eql_sol_2 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta,
reaction_theta=zeros(5), rhs_theta=zeros(5),
mesh_points=100, threshold_tol=(dL=0.4,),
num_knots=100, initial=:none,
time_range=(interval_2[1], interval_2[2]))
eql_sol_3 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta, reaction_theta=zeros(5),
moving_boundary_theta=eql_sol_2.moving_boundary_theta,
mesh_points=100, num_knots=100,
initial=:none,
time_range=(interval_3[1], interval_3[2]))
eql_sol_4 = stepwise_selection(esol; diffusion_basis, reaction_basis,
rhs_basis, moving_boundary_basis,
diffusion_theta=eql_sol.diffusion_theta,
moving_boundary_theta=eql_sol_2.moving_boundary_theta,
rhs_theta=eql_sol_3.rhs_theta, mesh_points=1000,
num_knots=100,
threshold_tol=(q=0.3,),
initial=:none, time_range=(interval_4[1], interval_4[2]))
fig = Figure(fontsize=81, resolution=(3250, 2070))
ax_pde = Axis(fig[1, 1],
xlabel=L"x", ylabel=L"q(x, t)",
title=L"(a):$ $ PDE comparison",
titlealign=:left,
width=1200, height=400,
xticks=(0:5:40, [L"%$s" for s in 0:5:40]),
yticks=(0:5:15, [L"%$s" for s in 0:5:15])
)
xlims!(ax_pde, 0, 10)
ylims!(ax_pde, 0, 20)
ax_leading_edge = Axis(fig[1, 2],
xlabel=L"t", ylabel=L"L(t)",
title=L"(b):$ $ Leading edge comparison",
titlealign=:left,
width=1200, height=400,
xticks=(0:50:250, [L"%$s" for s in 0:50:250]),
yticks=(0:5:10, [L"%$s" for s in 0:5:10])
)
xlims!(ax_leading_edge, 0, 250)
ylims!(ax_leading_edge, 0, 10)
ax_dq = Axis(fig[2, 1],
xlabel=L"q", ylabel=L"D(q)",
title=L"(c): $D(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(0.0005:0.0025:0.01, [L"%$s" for s in 0.0005:0.0025:0.01])
)
q_min, q_max = extrema(stack(eql_sol_4.model.cell_sol.q))
q_min = 5
xlims!(ax_dq, q_min, q_max)
ylims!(ax_dq, 0.0005, 0.01)
ax_rq = Axis(fig[2, 2],
xlabel=L"q", ylabel=L"R(q)",
title=L"(d): $R(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(-0.25:0.25:0.75, [L"%$s" for s in -0.25:0.25:0.75])
)
xlims!(ax_rq, q_min, q_max)
ylims!(ax_rq, -0.25, 0.75)
ax_hq = Axis(fig[3, 1],
xlabel=L"q", ylabel=L"H(q)",
title=L"(e): $H(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(-24:6:0, [L"%$s" for s in -24:6:0])
)
xlims!(ax_hq, q_min, q_max)
ylims!(ax_hq, -24, 2)
ax_eq = Axis(fig[3, 2],
xlabel=L"q", ylabel=L"E(q)",
title=L"(f): $E(q)$ comparison",
titlealign=:left,
width=1200, height=400,
xticks=(5:5:15, [L"%$s" for s in 5:5:15]),
yticks=(0.0005:0.0025:0.01, [L"%$s" for s in 0.0005:0.0025:0.01])
)
xlims!(ax_eq, q_min, q_max)
ylims!(ax_eq, 0.0005, 0.01)
time_indices = [findlast(≤(τ), esol[1].t) for τ in t]
colors = (:black, :red, :blue, :green, :orange, :purple, :brown)
pde_ξ = eql_sol_4.pde.geometry.mesh_points
pde_L = eql_sol_4.pde_sol[end, :]
pde_q = eql_sol_4.pde_sol[begin:(end-1), :]
cell_q = eql_sol_4.model.cell_sol.q
cell_r = eql_sol_4.model.cell_sol.u
cell_L = last.(eql_sol_4.model.cell_sol.u)
q_range = LinRange(q_min, q_max, 100)
D_cont_fnc = q -> (k / η) / q^2
R_cont_fnc = q -> β * q * (1 - q / K)
H_cont_fnc = q -> 2q^2 * (1 - q * s)
E_cont_fnc = D_cont_fnc
D_cont = D_cont_fnc.(q_range)
R_cont = R_cont_fnc.(q_range)
H_cont = H_cont_fnc.(q_range)
E_cont = E_cont_fnc.(q_range)
D_sol = diffusion_basis.(q_range, Ref(eql_sol.diffusion_theta), Ref(nothing))
R_sol = reaction_basis.(q_range, Ref(eql_sol_4.reaction_theta), Ref(nothing))
H_sol = rhs_basis.(q_range, Ref(eql_sol_3.rhs_theta), Ref(nothing))
E_sol = moving_boundary_basis.(q_range, Ref(eql_sol_2.moving_boundary_theta), Ref(nothing))
for (j, i) in enumerate(time_indices)
lines!(ax_pde, pde_ξ * pde_L[i], pde_q[:, i], color=colors[j], linestyle=:dash, linewidth=8)
lines!(ax_pde, cell_r[i], cell_q[i], color=colors[j], linewidth=4, label=L"%$(t[j])")
end
arrows!(ax_pde, [5.0], [3.0], [3.0], [0.0], color=:black, linewidth=12, arrowsize=57)
text!(ax_pde, [7.0], [3.5], text=L"t", fontsize=81)
lines!(ax_leading_edge, esol[1].t, pde_L, color=:red, linestyle=:dash, linewidth=5, label=L"$ $Learned")
lines!(ax_leading_edge, esol[1].t, cell_L, color=:black, linewidth=3, label=L"$ $Discrete")
axislegend(ax_leading_edge, position=:rb)
lines!(ax_dq, q_range, D_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_dq, q_range, D_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_dq, position=:rt)
lines!(ax_rq, q_range, R_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_rq, q_range, R_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
hlines!(ax_rq, [0.0], color=:grey, linewidth=12, linestyle=:dash)
axislegend(ax_rq, position=:lb)
lines!(ax_hq, q_range, H_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_hq, q_range, H_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_hq, position=:rt)
lines!(ax_eq, q_range, E_sol, linewidth=6, color=:red, linestyle=:solid, label=L"$ $Learned")
lines!(ax_eq, q_range, E_cont, linewidth=6, color=:black, linestyle=:dash, label=L"$ $Continuum limit")
axislegend(ax_eq, position=:rt)
fig
This page was generated using Literate.jl.