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