Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions src/systems/callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct SymbolicAffect
end

function SymbolicAffect(affect::Vector{Equation}; alg_eqs = Equation[],
discrete_parameters = Any[], kwargs...)
discrete_parameters = infer_discrete_parameters(affect), kwargs...)
if !(discrete_parameters isa AbstractVector)
discrete_parameters = Any[discrete_parameters]
elseif !(discrete_parameters isa Vector{Any})
Expand All @@ -31,6 +31,38 @@ function Symbolics.fast_substitute(aff::SymbolicAffect, rules)
map(substituter, aff.discrete_parameters))
end

# The discrete parameters (i.e. parameters that are updated in an event) can be inferred as
# those that occur in an affect equation *outside* of a `Pre(...)` operator.
function infer_discrete_parameters(affects)
discrete_parameters = Set()
for affect in affects
if affect isa Equation
infer_discrete_parameters!(discrete_parameters, affect.lhs)
infer_discrete_parameters!(discrete_parameters, affect.rhs)
elseif affect isa NamedTuple
haskey(affect, :modified) && union!(discrete_parameters, affect.modified)
end
end
return collect(discrete_parameters)
end

# Find all `expr`'s parameters that occur *outside* of a Pre(...) statement. Add these to `discrete_parameters`.
function infer_discrete_parameters!(discrete_parameters, expr)
expr_pre_removed = Symbolics.replacenode(expr, precall_to_1)
dynamic_symvars = Symbolics.get_variables(expr_pre_removed)
# Change this coming line to a Symbolic append type of thing.
union!(discrete_parameters, filter(ModelingToolkit.isparameter, dynamic_symvars))
end

# When updating vector variables, the affect side can be a vector.
function infer_discrete_parameters!(discrete_parameters, expr_vec::Vector)
foreach(expr -> infer_discrete_parameters!(discrete_parameters, expr), expr_vec)
end

# Functions for replacing a Pre-call with a `1.0` (removing its content from an expression).
is_precall(expr) = iscall(expr) ? operation(expr) isa Pre : false
precall_to_1(expr) = (is_precall(expr) ? 1.0 : expr)

struct AffectSystem
"""The internal implicit discrete system whose equations are solved to obtain values after the affect."""
system::AbstractSystem
Expand Down Expand Up @@ -438,8 +470,11 @@ function SymbolicDiscreteCallback(
condition::Union{Symbolic{Bool}, Number, Vector{<:Number}}, affect = nothing;
initialize = nothing, finalize = nothing,
reinitializealg = nothing, kwargs...)
c = is_timed_condition(condition) ? condition : value(scalarize(condition))
# Manual error check (to prevent events like `[X < 5.0] => [X ~ Pre(X) + 10.0]` from being created).
(condition isa Vector) && (eltype(condition) <: Num) &&
error("Vectors of symbolic conditions are not allowed for `SymbolicDiscreteCallback`.")

c = is_timed_condition(condition) ? condition : value(scalarize(condition))
if isnothing(reinitializealg)
if any(a -> a isa ImperativeAffect,
[affect, initialize, finalize])
Expand Down
15 changes: 15 additions & 0 deletions test/symbolic_events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1440,3 +1440,18 @@ end
@mtkcompile sys = MWE()
@test_nowarn ODEProblem(sys, [], (0.0, 1.0))
end

@testset "Test erroneously created events yields errors" begin
@parameters p(t) d
@variables X(t)
@test_throws "Vectors of symbolic conditions are not allowed" SymbolicDiscreteCallback([X <
5.0] => [X ~
Pre(X) +
10.0])
@test_throws "Vectors of symbolic conditions are not allowed" SymbolicDiscreteCallback([
X < 5.0, X > 10.0] => [X ~ Pre(X) + 10.0])
@test_throws "MethodError: no method" SymbolicContinuousCallback((X <
5.0) => [X ~
Pre(X) +
10.0])
end
Loading