|  | 
|  | 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