Getting Started with EasyModelAnalysis

EasyModelAnalysis.jl is made to work with a ModelingToolkit.jl model. If one is unfamiliar with ModelingToolkit, check out its tutorial before getting started. We will start by defining our model as the ModelingToolkit's README example:

using EasyModelAnalysis, Plots

@parameters t σ ρ β
@variables x(t) y(t) z(t)
D = Differential(t)

eqs = [D(D(x)) ~ σ * (y - x),
    D(y) ~ x * (ρ - z) - y,
    D(z) ~ x * y - β * z]

@named sys = ODESystem(eqs)
sys = structural_simplify(sys)

u0 = [D(x) => 2.0,
    x => 1.0,
    y => 0.0,
    z => 0.0]

p = [σ => 28.0,
    ρ => 10.0,
    β => 8 / 3]

tspan = (0.0, 100.0)
prob = ODEProblem(sys, u0, tspan, p, jac = true)
sol = solve(prob)
retcode: Success
Interpolation: specialized 4th order "free" interpolation, specialized 2nd order "free" stiffness-aware interpolation
t: 1495-element Vector{Float64}:
   0.0
   0.00014131524176735154
   0.0015544676594408668
   0.012956381792766277
   0.038266700044138006
   0.07852291796751046
   0.12210864065288088
   0.17155375443120538
   0.2311290630460472
   0.30252383872495503
   ⋮
  99.50500396864174
  99.57665667367455
  99.64283640514518
  99.72166966002771
  99.79967562312255
  99.86801143860899
  99.93304884850775
  99.99013919862291
 100.0
u: 1495-element Vector{Vector{Float64}}:
 [2.0, 0.0, 0.0, 1.0]
 [1.9960454103704428, 0.0014132521265988688, 9.986094350108765e-8, 1.0002823510089436]
 [1.956746201469907, 0.01555657123298723, 1.2096178746316906e-5, 1.0030752466310615]
 [1.6563910957343149, 0.13029732534611171, 0.0008464888325952064, 1.023645017393648]
 [1.1011942420041494, 0.3874130558030291, 0.007445311135321615, 1.0582098436702096]
 [0.5557298463522196, 0.7959541011040255, 0.031202921104640697, 1.0901348104540605]
 [0.44950102412404563, 1.229007863529698, 0.07390400544865926, 1.1102092717261387]
 [0.9266726317320947, 1.7062381668292075, 0.14159577334379553, 1.1416871365805596]
 [2.2781005968110417, 2.2773793271385414, 0.25114094267068454, 1.233177108242911]
 [4.867419855792264, 3.014693593518013, 0.44047994481421116, 1.4824570579833207]
 ⋮
 [80.19625687470715, 0.22319498068561278, 6.377998459496999, -10.92101238125288]
 [93.91126047193579, -1.9435112337378833, 5.7144265707275865, -4.580907294954086]
 [92.47053186268388, -2.2128845219633724, 4.966589670343976, 1.653117805222435]
 [78.34378276619968, 0.2736929875090918, 3.6720087645277166, 8.448758815091098]
 [59.61988722700268, 5.207887350906977, 5.364596386122552, 13.834326658988568]
 [42.06608771509131, 6.848721336825014, 10.923029356998175, 17.327760613533872]
 [17.746361864729497, 2.561462421673927, 14.710142641007621, 19.336065831253446]
 [-13.425204706776736, -1.9987153748274389, 12.635789734327103, 19.49627693453924]
 [-19.39944052157736, -2.411052627709995, 11.88863957641005, 19.334495160672596]

EasyModelAnalysis.jl then makes it easy to do complex queries about the model with simple one-line commands. For example, let's evaluate the values of x at times [0.0, 1.0, 2.0]:

get_timeseries(prob, x, [0.0, 1.0, 2.0])
3-element Vector{Float64}:
  1.0
 11.352378507900013
 11.818298591583103

That's too simple, so now let's grab the time points where x achieves its maximum and minimum:

xmin, xminval = get_min_t(prob, x)
(5.367888369569736, -18.631902241638347)
xmax, xmaxval = get_max_t(prob, x)
(98.6971839863247, 19.64868962568079)

Was that simple? Let's see what x looks like:

phaseplot_extrema(prob, x, (x, y))
Example block output
plot_extrema(prob, x)
Example block output

and boom, it grabbed the correct value in something that's relatively difficult. That's the core of EasyModelAnalysis!

Lazily Defining Observables

One thing to note about EasyModelAnalysis is that algebraically defined observables can be used in place of any state definition. As an example, let's say we wished to get the time series points for abs(x+y)^2. To do that, we would just make that be our observable:

get_timeseries(prob, abs(x + y)^2, [0.0, 1.0, 2.0])
3-element Vector{Float64}:
   1.0
  80.34060709018749
 249.44063778749026

and similarly for extrema:

xmin, xminval = get_min_t(prob, abs(x + y)^2)
(41.879660481309145, 7.576220133742597e-27)

This applies to data fitting, sensitivity analysis, thresholds, and everything!