|
| 1 | +### Preparations ### |
| 2 | + |
| 3 | +# Fetch packages. |
| 4 | +using ModelingToolkit, Test |
| 5 | +using ModelingToolkit: t_nounits as t, D_nounits as D |
| 6 | +import ModelingToolkit: get_ps, get_unknowns, get_observed, get_eqs, get_continuous_events, |
| 7 | + get_discrete_events, namespace_equations |
| 8 | + |
| 9 | +# Creates helper functions. |
| 10 | +function all_sets_equal(args...) |
| 11 | + for arg in args[2:end] |
| 12 | + issetequal(args[1], arg) || return false |
| 13 | + end |
| 14 | + return true |
| 15 | +end |
| 16 | +function sym_issubset(set1, set2) |
| 17 | + for sym1 in set1 |
| 18 | + any(isequal(sym1, sym2) for sym2 in set2) || return false |
| 19 | + end |
| 20 | + return true |
| 21 | +end |
| 22 | + |
| 23 | +### Basic Tests ### |
| 24 | + |
| 25 | +# Checks `toplevel = false` argument for various accessors (currently only for `ODESystem`s). |
| 26 | +# Compares to `toplevel = true` version, and `get_` functions. |
| 27 | +# Checks accessors for parameters, unknowns, equations, observables, and events. |
| 28 | +let |
| 29 | + # Prepares model components. |
| 30 | + @parameters p_top p_mid1 p_mid2 p_bot d |
| 31 | + @variables X_top(t) X_mid1(t) X_mid2(t) X_bot(t) Y(t) O(t) |
| 32 | + |
| 33 | + # Creates the systems (individual and hierarchical). |
| 34 | + eqs_top = [ |
| 35 | + D(X_top) ~ p_top - d*X_top, |
| 36 | + D(Y) ~ log(X_top) - Y^2 + 3.0, |
| 37 | + O ~ (p_top + d)*X_top + Y |
| 38 | + ] |
| 39 | + eqs_mid1 = [ |
| 40 | + D(X_mid1) ~ p_mid1 - d*X_mid1^2, |
| 41 | + D(Y) ~ D(X_mid1) - Y^3, |
| 42 | + O ~ (p_mid1 + d)*X_mid1 + Y |
| 43 | + ] |
| 44 | + eqs_mid2 = [ |
| 45 | + D(X_mid2) ~ p_mid2 - d*X_mid2, |
| 46 | + X_mid2^3 ~ log(X_mid2 + Y) - Y^2 + 3.0, |
| 47 | + O ~ (p_mid2 + d)*X_mid2 + Y |
| 48 | + ] |
| 49 | + eqs_bot = [ |
| 50 | + D(X_bot) ~ p_bot - d*X_bot, |
| 51 | + D(Y) ~ -Y^3, |
| 52 | + O ~ (p_bot + d)*X_bot + Y |
| 53 | + ] |
| 54 | + cevs = [[t ~ 1.0] => [Y ~ Y + 2.0]] |
| 55 | + devs = [(t == 2.0) => [Y ~ Y + 2.0]] |
| 56 | + @named sys_bot = ODESystem(eqs_bot, t; systems = [], continuous_events = cevs, discrete_events = devs) |
| 57 | + @named sys_mid2 = ODESystem(eqs_mid2, t; systems = [], continuous_events = cevs, discrete_events = devs) |
| 58 | + @named sys_mid1 = ODESystem(eqs_mid1, t; systems = [sys_bot], continuous_events = cevs, discrete_events = devs) |
| 59 | + @named sys_top = ODESystem(eqs_top, t; systems = [sys_mid1, sys_mid2], continuous_events = cevs, discrete_events = devs) |
| 60 | + sys_bot_comp = complete(sys_bot) |
| 61 | + sys_mid2_comp = complete(sys_mid2) |
| 62 | + sys_mid1_comp = complete(sys_mid1) |
| 63 | + sys_top_comp = complete(sys_top) |
| 64 | + sys_bot_ss = structural_simplify(sys_bot) |
| 65 | + sys_mid2_ss = structural_simplify(sys_mid2) |
| 66 | + sys_mid1_ss = structural_simplify(sys_mid1) |
| 67 | + sys_top_ss = structural_simplify(sys_top) |
| 68 | + |
| 69 | + # Checks `parameters` for `toplevel = false`. |
| 70 | + @test all_sets_equal(parameters.([sys_bot, sys_bot_comp, sys_bot_ss])..., [d, p_bot]) |
| 71 | + @test all_sets_equal(parameters.([sys_mid1, sys_mid1_comp, sys_mid1_ss])..., [d, p_mid1, sys_bot.d, sys_bot.p_bot]) |
| 72 | + @test all_sets_equal(parameters.([sys_mid2, sys_mid2_comp, sys_mid2_ss])..., [d, p_mid2]) |
| 73 | + @test all_sets_equal(parameters.([sys_top, sys_top_comp, sys_top_ss])..., [d, p_top, sys_mid1.d, sys_mid1.p_mid1, sys_mid1.sys_bot.d, sys_mid1.sys_bot.p_bot, sys_mid2.d, sys_mid2.p_mid2]) |
| 74 | + |
| 75 | + # Checks `parameters`` for `toplevel = true`. Compares to known parameters and also checks that |
| 76 | + # these are subset of what `get_ps` returns. |
| 77 | + @test all_sets_equal(parameters.([sys_bot, sys_bot_comp, sys_bot_ss]; toplevel = true)..., [d, p_bot]) |
| 78 | + @test_broken all_sets_equal(parameters.([sys_mid1, sys_mid1_comp, sys_mid1_ss]; toplevel = true)..., [d, p_mid1]) |
| 79 | + @test all_sets_equal(parameters.([sys_mid2, sys_mid2_comp, sys_mid2_ss]; toplevel = true)..., [d, p_mid2]) |
| 80 | + @test_broken all_sets_equal(parameters.([sys_top, sys_top_comp, sys_top_ss]; toplevel = true)..., [d, p_top]) |
| 81 | + @test all(sym_issubset(parameters(sys; toplevel = true), get_ps(sys)) for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) |
| 82 | + |
| 83 | + # Checks `unknowns` for `toplevel = false`. O(t) is eliminated by `structural_simplify` and |
| 84 | + # must be considered separately. |
| 85 | + @test all_sets_equal(unknowns.([sys_bot, sys_bot_comp])..., [O, Y, X_bot]) |
| 86 | + @test all_sets_equal(unknowns.([sys_bot_ss])..., [Y, X_bot]) |
| 87 | + @test all_sets_equal(unknowns.([sys_mid1, sys_mid1_comp])..., [O, Y, X_mid1, sys_bot.Y, sys_bot.O, sys_bot.X_bot]) |
| 88 | + @test all_sets_equal(unknowns.([sys_mid1_ss])..., [Y, X_mid1, sys_bot.Y, sys_bot.X_bot]) |
| 89 | + @test all_sets_equal(unknowns.([sys_mid2, sys_mid2_comp])..., [O, Y, X_mid2]) |
| 90 | + @test all_sets_equal(unknowns.([sys_mid2_ss])..., [Y, X_mid2]) |
| 91 | + @test all_sets_equal(unknowns.([sys_top, sys_top_comp])..., [O, Y, X_top, sys_mid1.O, sys_mid1.Y, sys_mid1.X_mid1, sys_mid1.sys_bot.O, sys_mid1.sys_bot.Y, sys_mid1.sys_bot.X_bot, sys_mid2.O, sys_mid2.Y, sys_mid2.X_mid2]) |
| 92 | + @test all_sets_equal(unknowns.([sys_top_ss])..., [Y, X_top, sys_mid1.Y, sys_mid1.X_mid1, sys_mid1.sys_bot.Y, sys_mid1.sys_bot.X_bot, sys_mid2.Y, sys_mid2.X_mid2]) |
| 93 | + |
| 94 | + # Checks `unknowns` for `toplevel = true`. Note that O is not eliminated here (as we go back |
| 95 | + # to original parent system). Also checks that outputs are subsets of what `get_ps` returns.. |
| 96 | + @test all_sets_equal(unknowns.([sys_bot, sys_bot_comp, sys_bot_ss]; toplevel = true)..., [O, Y, X_bot]) |
| 97 | + @test all_sets_equal(unknowns.([sys_mid1, sys_mid1_comp]; toplevel = true)..., [O, Y, X_mid1]) |
| 98 | + @test all_sets_equal(unknowns.([sys_mid2, sys_mid2_comp]; toplevel = true)..., [O, Y, X_mid2]) |
| 99 | + @test all_sets_equal(unknowns.([sys_top, sys_top_comp]; toplevel = true)..., [O, Y, X_top]) |
| 100 | + @test all(sym_issubset(unknowns(sys; toplevel = true), get_unknowns(sys)) for sys in [sys_bot, sys_mid1, sys_mid2, sys_top]) |
| 101 | + |
| 102 | + # Checks `equations` for `toplevel = false`. Do not check ss equations as these might potentially |
| 103 | + # be structurally simplified to new equations. |
| 104 | + @test all_sets_equal(equations.([sys_bot, sys_bot_comp])..., eqs_bot) |
| 105 | + @test all_sets_equal(equations.([sys_mid1, sys_mid1_comp])..., [eqs_mid1; namespace_equations(sys_bot)]) |
| 106 | + @test all_sets_equal(equations.([sys_mid2, sys_mid2_comp])..., eqs_mid2) |
| 107 | + @test all_sets_equal(equations.([sys_top, sys_top_comp])..., [eqs_top; namespace_equations(sys_mid1); namespace_equations(sys_mid2)]) |
| 108 | + |
| 109 | + # Checks `equations` for `toplevel = true`. Do not check ss equations directly as these |
| 110 | + # might potentially be structurally simplified to new equations. |
| 111 | + @test all_sets_equal(equations.([sys_bot, sys_bot_comp]; toplevel = true)..., eqs_bot) |
| 112 | + @test all_sets_equal(equations.([sys_mid1, sys_mid1_comp]; toplevel = true)..., eqs_mid1) |
| 113 | + @test all_sets_equal(equations.([sys_mid2, sys_mid2_comp]; toplevel = true)..., eqs_mid2) |
| 114 | + @test all_sets_equal(equations.([sys_top, sys_top_comp]; toplevel = true)..., eqs_top) |
| 115 | + @test all(sym_issubset(equations(sys; toplevel = true), get_eqs(sys)) for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) |
| 116 | + |
| 117 | + # Checks `observed for `toplevel = false`. For non-ss systems, there are no observables. |
| 118 | + @test all_sets_equal(observed.([sys_bot, sys_bot_comp, sys_mid1, sys_mid1_comp, sys_mid2, sys_mid2_comp, sys_top, sys_top_comp])..., []) |
| 119 | + @test issetequal(observed(sys_bot_ss), eqs_bot[3:3]) |
| 120 | + @test issetequal(observed(sys_mid1_ss), [eqs_mid1[3:3]; namespace_equations(sys_bot)[3:3]]) |
| 121 | + @test issetequal(observed(sys_mid2_ss), eqs_mid2[3:3]) |
| 122 | + @test issetequal(observed(sys_top_ss), [eqs_top[3:3]; namespace_equations(sys_mid1)[3:3:6]; namespace_equations(sys_mid2)[3:3]]) |
| 123 | + |
| 124 | + # Checks `observed` for `toplevel = true`. Again, for non-ss systems, there are no observables. |
| 125 | + # Also checks that `toplevel = true` yields subset of `get_observed`. |
| 126 | + @test all_sets_equal(observed.([sys_bot, sys_bot_comp, sys_mid1, sys_mid1_comp, sys_mid2, sys_mid2_comp, sys_top, sys_top_comp]; toplevel = true)..., []) |
| 127 | + @test_broken issetequal(observed(sys_bot_ss; toplevel = true), eqs_bot[3:3]) |
| 128 | + @test_broken issetequal(observed(sys_mid1_ss; toplevel = true), eqs_mid1[3:3]) |
| 129 | + @test_broken issetequal(observed(sys_mid2_ss; toplevel = true), eqs_mid2[3:3]) |
| 130 | + @test_broken issetequal(observed(sys_top_ss; toplevel = true), eqs_top[3:3]) |
| 131 | + @test all(sym_issubset(observed(sys; toplevel = true), get_observed(sys)) for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) |
| 132 | + |
| 133 | + # Checks `continuous_events` and `discrete_events` for `toplevel = true` (more straightforward |
| 134 | + # as I stored the same singe event in all systems). Don't check for `toplevel = false` as |
| 135 | + # technically not needed for these tests and name spacing the events is a mess. |
| 136 | + mtk_cev = ModelingToolkit.SymbolicContinuousCallback.(cevs)[1] |
| 137 | + mtk_dev = ModelingToolkit.SymbolicDiscreteCallback.(devs)[1] |
| 138 | + @test all_sets_equal(continuous_events.([sys_bot, sys_bot_comp, sys_bot_ss, sys_mid1, sys_mid1_comp, sys_mid1_ss, sys_mid2, sys_mid2_comp, sys_mid2_ss, sys_top, sys_top_comp, sys_top_ss]; toplevel = true)..., [mtk_cev]) |
| 139 | + @test all_sets_equal(discrete_events.([sys_bot, sys_bot_comp, sys_bot_ss, sys_mid1, sys_mid1_comp, sys_mid1_ss, sys_mid2, sys_mid2_comp, sys_mid2_ss, sys_top, sys_top_comp, sys_top_ss]; toplevel = true)..., [mtk_dev]) |
| 140 | + @test all(sym_issubset(continuous_events(sys; toplevel = true), get_continuous_events(sys)) for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) |
| 141 | + @test all(sym_issubset(discrete_events(sys; toplevel = true), get_discrete_events(sys)) for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) |
| 142 | +end |
0 commit comments