- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 233
add toplevel arguments to various getters #3462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
186a7d3
              1d872e0
              0641937
              7867202
              3e07adf
              04d8dda
              9e459e2
              e151999
              be8d20d
              56ec622
              4536f98
              9a19c1d
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -94,17 +94,17 @@ A [`ContinuousCallback`](@ref SciMLBase.ContinuousCallback) specified symbolical | |
| as well as the positive-edge `affect` and negative-edge `affect_neg` that apply when *any* of `eq` are satisfied. | ||
| By default `affect_neg = affect`; to only get rising edges specify `affect_neg = nothing`. | ||
|  | ||
| Assume without loss of generality that the equation is of the form `c(u,p,t) ~ 0`; we denote the integrator state as `i.u`. | ||
| Assume without loss of generality that the equation is of the form `c(u,p,t) ~ 0`; we denote the integrator state as `i.u`. | ||
| For compactness, we define `prev_sign = sign(c(u[t-1], p[t-1], t-1))` and `cur_sign = sign(c(u[t], p[t], t))`. | ||
| A condition edge will be detected and the callback will be invoked iff `prev_sign * cur_sign <= 0`. | ||
| A condition edge will be detected and the callback will be invoked iff `prev_sign * cur_sign <= 0`. | ||
| The positive edge `affect` will be triggered iff an edge is detected and if `prev_sign < 0`; similarly, `affect_neg` will be | ||
| triggered iff an edge is detected and `prev_sign > 0`. | ||
| triggered iff an edge is detected and `prev_sign > 0`. | ||
|  | ||
| Inter-sample condition activation is not guaranteed; for example if we use the dirac delta function as `c` to insert a | ||
| Inter-sample condition activation is not guaranteed; for example if we use the dirac delta function as `c` to insert a | ||
| sharp discontinuity between integrator steps (which in this example would not normally be identified by adaptivity) then the condition is not | ||
| guaranteed to be triggered. | ||
|  | ||
| Once detected the integrator will "wind back" through a root-finding process to identify the point when the condition became active; the method used | ||
| Once detected the integrator will "wind back" through a root-finding process to identify the point when the condition became active; the method used | ||
| is specified by `rootfind` from [`SciMLBase.RootfindOpt`](@ref). If we denote the time when the condition becomes active as `tc`, | ||
| the value in the integrator after windback will be: | ||
| * `u[tc-epsilon], p[tc-epsilon], tc` if `LeftRootFind` is used, | ||
|  | @@ -116,7 +116,7 @@ it passed through 0. | |
|  | ||
| Multiple callbacks in the same system with different `rootfind` operations will be grouped | ||
| by their `rootfind` value into separate VectorContinuousCallbacks in the enumeration order of `SciMLBase.RootfindOpt`. This may cause some callbacks to not fire if several become | ||
| active at the same instant. See the `SciMLBase` documentation for more information on the semantic rules. | ||
| active at the same instant. See the `SciMLBase` documentation for more information on the semantic rules. | ||
|  | ||
| Affects (i.e. `affect` and `affect_neg`) can be specified as either: | ||
| * A list of equations that should be applied when the callback is triggered (e.g. `x ~ 3, y ~ 7`) which must be of the form `unknown ~ observed value` where each `unknown` appears only once. Equations will be applied in the order that they appear in the vector; parameters and state updates will become immediately visible to following equations. | ||
|  | @@ -328,13 +328,13 @@ The `SymbolicContinuousCallback`s in the returned vector are structs with two fi | |
| `eqs => affect`. | ||
| """ | ||
| function continuous_events(sys::AbstractSystem) | ||
| obs = get_continuous_events(sys) | ||
| filter(!isempty, obs) | ||
| cbs = get_continuous_events(sys) | ||
| filter(!isempty, cbs) | ||
|  | ||
| systems = get_systems(sys) | ||
| cbs = [obs; | ||
| cbs = [cbs; | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a leftover from the code being copied from the  | ||
| reduce(vcat, | ||
| (map(o -> namespace_callback(o, s), continuous_events(s)) | ||
| (map(cb -> namespace_callback(cb, s), continuous_events(s)) | ||
| for s in systems), | ||
| init = SymbolicContinuousCallback[])] | ||
| filter(!isempty, cbs) | ||
|  | @@ -356,6 +356,21 @@ function vars!(vars, cb::SymbolicContinuousCallback; op = Differential) | |
| return vars | ||
| end | ||
|  | ||
| """ | ||
| continuous_events_toplevel(sys::AbstractSystem) | ||
|  | ||
| Replicates the behaviour of `continuous_events`, but ignores events of subsystems. | ||
|  | ||
| Notes: | ||
| - Cannot be applied to non-complete systems. | ||
| """ | ||
| function continuous_events_toplevel(sys::AbstractSystem) | ||
| if has_parent(sys) && (parent = get_parent(sys)) !== nothing | ||
| return continuous_events_toplevel(parent) | ||
| end | ||
| return get_continuous_events(sys) | ||
| end | ||
|  | ||
| #################################### discrete events ##################################### | ||
|  | ||
| struct SymbolicDiscreteCallback | ||
|  | @@ -483,11 +498,11 @@ The `SymbolicDiscreteCallback`s in the returned vector are structs with two fiel | |
| `condition => affect`. | ||
| """ | ||
| function discrete_events(sys::AbstractSystem) | ||
| obs = get_discrete_events(sys) | ||
| cbs = get_discrete_events(sys) | ||
| systems = get_systems(sys) | ||
| cbs = [obs; | ||
| cbs = [cbs; | ||
| reduce(vcat, | ||
| (map(o -> namespace_callback(o, s), discrete_events(s)) for s in systems), | ||
| (map(cb -> namespace_callback(cb, s), discrete_events(s)) for s in systems), | ||
| init = SymbolicDiscreteCallback[])] | ||
| cbs | ||
| end | ||
|  | @@ -514,6 +529,21 @@ function vars!(vars, cb::SymbolicDiscreteCallback; op = Differential) | |
| return vars | ||
| end | ||
|  | ||
| """ | ||
| discrete_events_toplevel(sys::AbstractSystem) | ||
|  | ||
| Replicates the behaviour of `discrete_events`, but ignores events of subsystems. | ||
|  | ||
| Notes: | ||
| - Cannot be applied to non-complete systems. | ||
| """ | ||
| function discrete_events_toplevel(sys::AbstractSystem) | ||
| if has_parent(sys) && (parent = get_parent(sys)) !== nothing | ||
| return discrete_events_toplevel(parent) | ||
| end | ||
| return get_discrete_events(sys) | ||
| end | ||
|  | ||
| ################################# compilation functions #################################### | ||
|  | ||
| # handles ensuring that affect! functions work with integrator arguments | ||
|  | @@ -688,7 +718,7 @@ function generate_rootfinding_callback(sys::AbstractTimeDependentSystem, | |
| generate_rootfinding_callback(cbs, sys, dvs, ps; kwargs...) | ||
| end | ||
| """ | ||
| Generate a single rootfinding callback; this happens if there is only one equation in `cbs` passed to | ||
| Generate a single rootfinding callback; this happens if there is only one equation in `cbs` passed to | ||
| generate_rootfinding_callback and thus we can produce a ContinuousCallback instead of a VectorContinuousCallback. | ||
| """ | ||
| function generate_single_rootfinding_callback( | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| ### Preparations ### | ||
|  | ||
| # Fetch packages. | ||
| using ModelingToolkit, Test | ||
| using ModelingToolkit: t_nounits as t, D_nounits as D | ||
| import ModelingToolkit: get_ps, get_unknowns, get_observed, get_eqs, get_continuous_events, | ||
| get_discrete_events, namespace_equations | ||
| import ModelingToolkit: parameters_toplevel, unknowns_toplevel, equations_toplevel, | ||
| continuous_events_toplevel, discrete_events_toplevel | ||
|  | ||
| # Creates helper functions. | ||
| function all_sets_equal(args...) | ||
| for arg in args[2:end] | ||
| issetequal(args[1], arg) || return false | ||
| end | ||
| return true | ||
| end | ||
| function sym_issubset(set1, set2) | ||
| for sym1 in set1 | ||
| any(isequal(sym1, sym2) for sym2 in set2) || return false | ||
| end | ||
| return true | ||
| end | ||
|  | ||
| ### Basic Tests ### | ||
|  | ||
| # Checks `toplevel = false` argument for various accessors (currently only for `ODESystem`s). | ||
| # Compares to `` version, and `get_` functions. | ||
| # Checks accessors for parameters, unknowns, equations, observables, and events. | ||
| # Some tests looks funny (caused by the formatter). | ||
| let | ||
| # Prepares model components. | ||
| @parameters p_top p_mid1 p_mid2 p_bot d | ||
| @variables X_top(t) X_mid1(t) X_mid2(t) X_bot(t) Y(t) O(t) | ||
|  | ||
| # Creates the systems (individual and hierarchical). | ||
| eqs_top = [ | ||
| D(X_top) ~ p_top - d * X_top, | ||
| D(Y) ~ log(X_top) - Y^2 + 3.0, | ||
| O ~ (p_top + d) * X_top + Y | ||
| ] | ||
| eqs_mid1 = [ | ||
| D(X_mid1) ~ p_mid1 - d * X_mid1^2, | ||
| D(Y) ~ D(X_mid1) - Y^3, | ||
| O ~ (p_mid1 + d) * X_mid1 + Y | ||
| ] | ||
| eqs_mid2 = [ | ||
| D(X_mid2) ~ p_mid2 - d * X_mid2, | ||
| X_mid2^3 ~ log(X_mid2 + Y) - Y^2 + 3.0, | ||
| O ~ (p_mid2 + d) * X_mid2 + Y | ||
| ] | ||
| eqs_bot = [ | ||
| D(X_bot) ~ p_bot - d * X_bot, | ||
| D(Y) ~ -Y^3, | ||
| O ~ (p_bot + d) * X_bot + Y | ||
| ] | ||
| cevs = [[t ~ 1.0] => [Y ~ Y + 2.0]] | ||
| devs = [(t == 2.0) => [Y ~ Y + 2.0]] | ||
| @named sys_bot = ODESystem( | ||
| eqs_bot, t; systems = [], continuous_events = cevs, discrete_events = devs) | ||
| @named sys_mid2 = ODESystem( | ||
| eqs_mid2, t; systems = [], continuous_events = cevs, discrete_events = devs) | ||
| @named sys_mid1 = ODESystem( | ||
| eqs_mid1, t; systems = [sys_bot], continuous_events = cevs, discrete_events = devs) | ||
| @named sys_top = ODESystem(eqs_top, t; systems = [sys_mid1, sys_mid2], | ||
| continuous_events = cevs, discrete_events = devs) | ||
| sys_bot_comp = complete(sys_bot) | ||
| sys_mid2_comp = complete(sys_mid2) | ||
| sys_mid1_comp = complete(sys_mid1) | ||
| sys_top_comp = complete(sys_top) | ||
| sys_bot_ss = structural_simplify(sys_bot) | ||
| sys_mid2_ss = structural_simplify(sys_mid2) | ||
| sys_mid1_ss = structural_simplify(sys_mid1) | ||
| sys_top_ss = structural_simplify(sys_top) | ||
|  | ||
| # Checks `parameters1. | ||
| @test all_sets_equal(parameters.([sys_bot, sys_bot_comp, sys_bot_ss])..., [d, p_bot]) | ||
| @test all_sets_equal(parameters.([sys_mid1, sys_mid1_comp, sys_mid1_ss])..., | ||
| [d, p_mid1, sys_bot.d, sys_bot.p_bot]) | ||
| @test all_sets_equal( | ||
| parameters.([sys_mid2, sys_mid2_comp, sys_mid2_ss])..., [d, p_mid2]) | ||
| @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]) | ||
|  | ||
| # Checks `parameters_toplevel`. Compares to known parameters and also checks that | ||
| # these are subset of what `get_ps` returns. | ||
| @test all_sets_equal( | ||
| parameters_toplevel.([sys_bot, sys_bot_comp, sys_bot_ss])..., [d, p_bot]) | ||
| @test_broken all_sets_equal( | ||
| parameters_toplevel.([sys_mid1, sys_mid1_comp, sys_mid1_ss])..., | ||
| [d, p_mid1]) | ||
| @test all_sets_equal( | ||
| parameters_toplevel.([sys_mid2, sys_mid2_comp, sys_mid2_ss])..., | ||
| [d, p_mid2]) | ||
| @test_broken all_sets_equal( | ||
|          | ||
| parameters_toplevel.([sys_top, sys_top_comp, sys_top_ss])..., [d, p_top]) | ||
| @test all(sym_issubset(parameters_toplevel(sys), get_ps(sys)) | ||
| for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) | ||
|  | ||
| # Checks `unknowns`. O(t) is eliminated by `structural_simplify` and | ||
| # must be considered separately. | ||
| @test all_sets_equal(unknowns.([sys_bot, sys_bot_comp])..., [O, Y, X_bot]) | ||
| @test all_sets_equal(unknowns.([sys_bot_ss])..., [Y, X_bot]) | ||
| @test all_sets_equal(unknowns.([sys_mid1, sys_mid1_comp])..., | ||
| [O, Y, X_mid1, sys_bot.Y, sys_bot.O, sys_bot.X_bot]) | ||
| @test all_sets_equal(unknowns.([sys_mid1_ss])..., [Y, X_mid1, sys_bot.Y, sys_bot.X_bot]) | ||
| @test all_sets_equal(unknowns.([sys_mid2, sys_mid2_comp])..., [O, Y, X_mid2]) | ||
| @test all_sets_equal(unknowns.([sys_mid2_ss])..., [Y, X_mid2]) | ||
| @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]) | ||
| @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]) | ||
|  | ||
| # Checks `unknowns_toplevel`. Note that O is not eliminated here (as we go back | ||
| # to original parent system). Also checks that outputs are subsets of what `get_unknowns` returns. | ||
| @test all_sets_equal( | ||
| unknowns_toplevel.([sys_bot, sys_bot_comp, sys_bot_ss])..., [O, Y, X_bot]) | ||
| @test all_sets_equal( | ||
| unknowns_toplevel.([sys_mid1, sys_mid1_comp])..., [O, Y, X_mid1]) | ||
| @test all_sets_equal( | ||
| unknowns_toplevel.([sys_mid2, sys_mid2_comp])..., [O, Y, X_mid2]) | ||
| @test all_sets_equal( | ||
| unknowns_toplevel.([sys_top, sys_top_comp])..., [O, Y, X_top]) | ||
| @test all(sym_issubset(unknowns_toplevel(sys), get_unknowns(sys)) | ||
| for sys in [sys_bot, sys_mid1, sys_mid2, sys_top]) | ||
|  | ||
| # Checks `equations`. Do not check ss equations as these might potentially | ||
| # be structurally simplified to new equations. | ||
| @test all_sets_equal(equations.([sys_bot, sys_bot_comp])..., eqs_bot) | ||
| @test all_sets_equal( | ||
| equations.([sys_mid1, sys_mid1_comp])..., [eqs_mid1; namespace_equations(sys_bot)]) | ||
| @test all_sets_equal(equations.([sys_mid2, sys_mid2_comp])..., eqs_mid2) | ||
| @test all_sets_equal(equations.([sys_top, sys_top_comp])..., | ||
| [eqs_top; namespace_equations(sys_mid1); namespace_equations(sys_mid2)]) | ||
|  | ||
| # Checks `equations_toplevel`. Do not check ss equations directly as these | ||
| # might potentially be structurally simplified to new equations. Do not check | ||
| @test all_sets_equal(equations_toplevel.([sys_bot])..., eqs_bot) | ||
| @test all_sets_equal( | ||
| equations_toplevel.([sys_mid1])..., eqs_mid1) | ||
| @test all_sets_equal( | ||
| equations_toplevel.([sys_mid2])..., eqs_mid2) | ||
| @test all_sets_equal(equations_toplevel.([sys_top])..., eqs_top) | ||
| @test all(sym_issubset(equations_toplevel(sys), get_eqs(sys)) | ||
| for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) | ||
|  | ||
| # Checks `continuous_events_toplevel` and `discrete_events_toplevel` (straightforward | ||
| # as I stored the same singe event in all systems). Don't check for non-toplevel cases as | ||
| # technically not needed for these tests and name spacing the events is a mess. | ||
| mtk_cev = ModelingToolkit.SymbolicContinuousCallback.(cevs)[1] | ||
| mtk_dev = ModelingToolkit.SymbolicDiscreteCallback.(devs)[1] | ||
| @test all_sets_equal( | ||
| continuous_events_toplevel.( | ||
| [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])..., | ||
| [mtk_cev]) | ||
| @test all_sets_equal( | ||
| discrete_events_toplevel.( | ||
| [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])..., | ||
| [mtk_dev]) | ||
| @test all(sym_issubset( | ||
| continuous_events_toplevel(sys), get_continuous_events(sys)) | ||
| for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) | ||
| @test all(sym_issubset(discrete_events_toplevel(sys), get_discrete_events(sys)) | ||
| for sys in [sys_bot, sys_mid2, sys_mid1, sys_top]) | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.