Skip to content
Merged
22 changes: 12 additions & 10 deletions src/systems/abstractsystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@
"""
Initial(x)

The `Initial` operator. Used by initializaton to store constant constraints on variables

Check warning on line 625 in src/systems/abstractsystem.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"initializaton" should be "initialization".
of a system. See the documentation section on initialization for more information.
"""
struct Initial <: Symbolics.Operator end
Expand Down Expand Up @@ -1293,10 +1293,10 @@

See also [`ModelingToolkit.get_unknowns`](@ref).
"""
function unknowns(sys::AbstractSystem)
function unknowns(sys::AbstractSystem; toplevel = false)
sts = get_unknowns(sys)
systems = get_systems(sys)
nonunique_unknowns = if isempty(systems)
nonunique_unknowns = if toplevel || isempty(systems)
sts
else
system_unknowns = reduce(vcat, namespace_variables.(systems))
Expand All @@ -1320,7 +1320,7 @@

See also [`@parameters`](@ref) and [`ModelingToolkit.get_ps`](@ref).
"""
function parameters(sys::AbstractSystem; initial_parameters = false)
function parameters(sys::AbstractSystem; initial_parameters = false, toplevel = false)
ps = get_ps(sys)
if ps == SciMLBase.NullParameters()
return []
Expand All @@ -1329,8 +1329,8 @@
ps = first.(ps)
end
systems = get_systems(sys)
result = unique(isempty(systems) ? ps :
[ps; reduce(vcat, namespace_parameters.(systems))])
result = unique(toplevel || isempty(systems) ?
ps : [ps; reduce(vcat, namespace_parameters.(systems))])
if !initial_parameters
if is_time_dependent(sys)
# time-dependent systems have `Initial` parameters for all their
Expand Down Expand Up @@ -1490,8 +1490,9 @@
isempty(systems) ? ctrls : [ctrls; reduce(vcat, namespace_controls.(systems))]
end

function observed(sys::AbstractSystem)
function observed(sys::AbstractSystem; toplevel = false)
obs = get_observed(sys)
toplevel && return obs
systems = get_systems(sys)
[obs;
reduce(vcat,
Expand All @@ -1510,7 +1511,7 @@

See also [`initialization_equations`](@ref), [`parameter_dependencies`](@ref) and [`ModelingToolkit.get_defaults`](@ref).
"""
function defaults(sys::AbstractSystem)
function defaults(sys::AbstractSystem; toplevel = false)
systems = get_systems(sys)
defs = get_defaults(sys)
# `mapfoldr` is really important!!! We should prefer the base model for
Expand All @@ -1519,7 +1520,8 @@
# `compose(ODESystem(...; defaults=defs), ...)`
#
# Thus, right associativity is required and crucial for correctness.
isempty(systems) ? defs : mapfoldr(namespace_defaults, merge, systems; init = defs)
(toplevel ||isempty(systems)) ?
defs : mapfoldr(namespace_defaults, merge, systems; init = defs)
end

function defaults_and_guesses(sys::AbstractSystem)
Expand Down Expand Up @@ -1549,10 +1551,10 @@

See also [`full_equations`](@ref) and [`ModelingToolkit.get_eqs`](@ref).
"""
function equations(sys::AbstractSystem)
function equations(sys::AbstractSystem; toplevel = false)
eqs = get_eqs(sys)
systems = get_systems(sys)
if isempty(systems)
if toplevel || isempty(systems)
return eqs
else
eqs = Equation[eqs;
Expand Down
32 changes: 17 additions & 15 deletions src/systems/callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand Down Expand Up @@ -327,14 +327,15 @@ The `SymbolicContinuousCallback`s in the returned vector are structs with two fi
`affect` which correspond to the first and second elements of a `Pair` used to define an event, i.e.
`eqs => affect`.
"""
function continuous_events(sys::AbstractSystem)
obs = get_continuous_events(sys)
filter(!isempty, obs)
function continuous_events(sys::AbstractSystem; toplevel = false)
cbs = get_continuous_events(sys)
filter(!isempty, cbs)
toplevel && return cbs

systems = get_systems(sys)
cbs = [obs;
cbs = [cbs;
Copy link
Member Author

Choose a reason for hiding this comment

The 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 observed getter (?), in which case it would make sense to use cbs and not obs throughout the function.

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)
Expand Down Expand Up @@ -482,10 +483,11 @@ The `SymbolicDiscreteCallback`s in the returned vector are structs with two fiel
`affect` which correspond to the first and second elements of a `Pair` used to define an event, i.e.
`condition => affect`.
"""
function discrete_events(sys::AbstractSystem)
obs = get_discrete_events(sys)
function discrete_events(sys::AbstractSystem; toplevel = false)
cbs = get_discrete_events(sys)
toplevel && return cbs
systems = get_systems(sys)
cbs = [obs;
cbs = [cbs;
reduce(vcat,
(map(o -> namespace_callback(o, s), discrete_events(s)) for s in systems),
init = SymbolicDiscreteCallback[])]
Expand Down Expand Up @@ -688,7 +690,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(
Expand Down
Loading