Example using different components of EarthSciMLBase together

This example shows how to define and couple different components of EarthSciMLBase together to create a more complex model. First, we create several model components.

Our first example system is a simple reaction system:

using EarthSciMLBase
using ModelingToolkit, Catalyst, DomainSets, MethodOfLines, DifferentialEquations
using Plots

# Create our independent variable `t` and our partially-independent variables `x` and `y`.
@parameters t x y

function ExampleSys1(t)
    @species c₁(t)=5.0 c₂(t)=5.0
    ReactionSystem(
        [Reaction(2.0, [c₁], [c₂])],
        t; name=:Docs₊Sys1, combinatoric_ratelaws=false)
end

ExampleSys1(t)

\[ \begin{align*} \mathrm{c_1} &\xrightarrow{2.0} \mathrm{c_2} \end{align*} \]

Our second example system is a simple ODE system, with the same two variables.

function ExampleSys2(t)
    @variables c₁(t)=5.0 c₂(t)=5.0
    @parameters p₁=1.0 p₂=0.5
    D = Differential(t)
    ODESystem(
        [D(c₁) ~ -p₁, D(c₂) ~ p₂],
        t; name=:Docs₊Sys2)
end

ExampleSys2(t)

\[ \begin{align} \frac{\mathrm{d} c_1\left( t \right)}{\mathrm{d}t} =& - p_1 \\ \frac{\mathrm{d} c_2\left( t \right)}{\mathrm{d}t} =& p_2 \end{align} \]

Now, we specify what should happen when we couple the two systems together. In this case, we want the the derivative of the composed system to be equal to the sum of the derivatives of the two systems. We can do that using the operator_compose function from this package.

register_coupling(ExampleSys1(t), ExampleSys2(t)) do sys1, sys2
    sys1 = convert(ODESystem, sys1)
    operator_compose(sys1, sys2)
end

Once we specify all of the above, it is simple to create our two individual systems and then couple them together.

sys1 = ExampleSys1(t)
sys2 = ExampleSys2(t)
sys = couple(sys1, sys2)

get_mtk(sys)

\[ \begin{align} Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) \\ Docs_{+}Sys1_{+}c_1\left( t \right) =& Docs_{+}Sys2_{+}c_1\left( t \right) \\ Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) \\ Docs_{+}Sys1_{+}c_2\left( t \right) =& Docs_{+}Sys2_{+}c_2\left( t \right) \\ \frac{\mathrm{d} Docs_{+}Sys1_{+}c_1\left( t \right)}{\mathrm{d}t} =& - 2 Docs_{+}Sys1_{+}c_1\left( t \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) \\ \frac{\mathrm{d} Docs_{+}Sys1_{+}c_2\left( t \right)}{\mathrm{d}t} =& 2 Docs_{+}Sys1_{+}c_1\left( t \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) =& - Docs_{+}Sys2_{+}p_1 \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}p_2 \end{align} \]

At this point we have an ODE system that is composed of two other ODE systems. We can inspect its observed variables using the observed function.

simplified_sys = structural_simplify(get_mtk(sys))

\[ \begin{align} \frac{\mathrm{d} Docs_{+}Sys1_{+}c_1\left( t \right)}{\mathrm{d}t} =& - 2 Docs_{+}Sys1_{+}c_1\left( t \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) \\ \frac{\mathrm{d} Docs_{+}Sys1_{+}c_2\left( t \right)}{\mathrm{d}t} =& 2 Docs_{+}Sys1_{+}c_1\left( t \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) \end{align} \]

observed(simplified_sys)

\[ \begin{align} Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) =& - Docs_{+}Sys2_{+}p_1 \\ Docs_{+}Sys2_{+}c_1\left( t \right) =& Docs_{+}Sys1_{+}c_1\left( t \right) \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}p_2 \\ Docs_{+}Sys2_{+}c_2\left( t \right) =& Docs_{+}Sys1_{+}c_2\left( t \right) \\ Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t \right) \\ Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t \right) \end{align} \]

We can also run simulations using this system:

odeprob = ODEProblem(simplified_sys, [], (0.0,10.0), [])
odesol = solve(odeprob)
plot(odesol)
Example block output

Once we've confirmed that our model works in a 0D "box model" setting, we can expand it to 1, 2, or 3 dimensions using by adding in initial and boundary conditions. We will also add in advection using constant-velocity wind fields add the same time.

x_min = y_min = t_min = 0.0
x_max = y_max = t_max = 1.0
domain = DomainInfo(
    constIC(4.0, t ∈ Interval(t_min, t_max)),
    periodicBC(x ∈ Interval(x_min, x_max)),
    zerogradBC(y ∈ Interval(y_min, y_max)),
)

sys_pde = couple(sys, domain, ConstantWind(t, 1.0, 1.0), Advection())

sys_pde_mtk = get_mtk(sys_pde)

\[ \begin{align} Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys1_{+}c_1\left( t, x, y \right) =& Docs_{+}Sys2_{+}c_1\left( t, x, y \right) \\ Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) =& Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys1_{+}c_2\left( t, x, y \right) =& Docs_{+}Sys2_{+}c_2\left( t, x, y \right) \\ EarthSciMLBase_{+}MeanWind_{+}v_{x}\left( t, x, y \right) =& EarthSciMLBase_{+}ConstantWind_{+}v_{1}\left( t, x, y \right) \\ EarthSciMLBase_{+}MeanWind_{+}v_{y}\left( t, x, y \right) =& EarthSciMLBase_{+}ConstantWind_{+}v_{2}\left( t, x, y \right) \\ \frac{\mathrm{d}}{\mathrm{d}t} Docs_{+}Sys1_{+}c_1\left( t, x, y \right) =& - 2 Docs_{+}Sys1_{+}c_1\left( t, x, y \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}x} Docs_{+}Sys1_{+}c_1\left( t, x, y \right) EarthSciMLBase_{+}MeanWind_{+}v_{x}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_1\left( t, x, y \right) EarthSciMLBase_{+}MeanWind_{+}v_{y}\left( t, x, y \right) \\ \frac{\mathrm{d}}{\mathrm{d}t} Docs_{+}Sys1_{+}c_2\left( t, x, y \right) =& 2 Docs_{+}Sys1_{+}c_1\left( t, x, y \right) + Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}x} Docs_{+}Sys1_{+}c_2\left( t, x, y \right) EarthSciMLBase_{+}MeanWind_{+}v_{x}\left( t, x, y \right) - \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_2\left( t, x, y \right) EarthSciMLBase_{+}MeanWind_{+}v_{y}\left( t, x, y \right) \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) =& - Docs_{+}Sys2_{+}p_1 \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) =& Docs_{+}Sys2_{+}p_2 \\ EarthSciMLBase_{+}ConstantWind_{+}v_{1}\left( t, x, y \right) =& EarthSciMLBase_{+}ConstantWind_{+}c_{v1} \\ EarthSciMLBase_{+}ConstantWind_{+}v_{2}\left( t, x, y \right) =& EarthSciMLBase_{+}ConstantWind_{+}c_{v2} \end{align} \]

Now we can inspect this new system that we've created:

sys_pde_mtk.dvs

\[ \begin{equation} \left[ \begin{array}{c} Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{1}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys1_{+}c_1\left( t, x, y \right) \\ Docs_{+}Sys2_{+}c_1\left( t, x, y \right) \\ Docs_{+}Sys1_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys2_{+}Docs_{+}Sys2_{ddt\_c_{2}ˍt}\left( t, x, y \right) \\ Docs_{+}Sys1_{+}c_2\left( t, x, y \right) \\ Docs_{+}Sys2_{+}c_2\left( t, x, y \right) \\ EarthSciMLBase_{+}MeanWind_{+}v_{x}\left( t, x, y \right) \\ EarthSciMLBase_{+}ConstantWind_{+}v_{1}\left( t, x, y \right) \\ EarthSciMLBase_{+}MeanWind_{+}v_{y}\left( t, x, y \right) \\ EarthSciMLBase_{+}ConstantWind_{+}v_{2}\left( t, x, y \right) \\ \end{array} \right] \end{equation} \]

sys_pde_mtk.bcs

\[ \begin{align} Docs_{+}Sys1_{+}c_1\left( 0, x, y \right) =& 4 \\ Docs_{+}Sys1_{+}c_2\left( 0, x, y \right) =& 4 \\ Docs_{+}Sys1_{+}c_1\left( t, 0, y \right) =& Docs_{+}Sys1_{+}c_1\left( t, 1, y \right) \\ Docs_{+}Sys1_{+}c_2\left( t, 0, y \right) =& Docs_{+}Sys1_{+}c_2\left( t, 1, y \right) \\ \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_1\left( t, x, 0 \right) =& 0 \\ \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_1\left( t, x, 1 \right) =& 0 \\ \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_2\left( t, x, 0 \right) =& 0 \\ \frac{\mathrm{d}}{\mathrm{d}y} Docs_{+}Sys1_{+}c_2\left( t, x, 1 \right) =& 0 \end{align} \]

Finally, we can run a simulation using this system:

discretization = MOLFiniteDifference([x=>10, y=>10], t, approx_order=2)
@time pdeprob = discretize(sys_pde_mtk, discretization)
@time pdesol = solve(pdeprob, Tsit5(), saveat=0.1)

# Plot the solution.
discrete_x, discrete_y, discrete_t = pdesol[x], pdesol[y], pdesol[t]
@variables Docs₊Sys1₊c₁(..) Docs₊Sys1₊c₂(..)
solc1, solc2 = pdesol[Docs₊Sys1₊c₁(t, x, y)], pdesol[Docs₊Sys1₊c₂(t, x, y)]
anim = @animate for k in 1:length(discrete_t)
    p1 = heatmap(solc1[k, 1:end-1, 1:end-1], title="c₁ t=\$(discrete_t[k])", clim=(0,4.0), lab=:none)
    p2 = heatmap(solc2[k, 1:end-1, 1:end-1], title="c₂ t=\$(discrete_t[k])", clim=(0,7.0), lab=:none)
    plot(p1, p2, layout=(1,2), size=(800,400))
end
gif(anim, fps = 8)
Example block output

Because our system is a system of ordinary differential equations rather than partial differential equations, all of the grid cells in the animation above have the same value. Refer to the advection example for an example of a system of partial differential equations.