From f686a3c3d0b543ba2849e339c7e2183ffa115cc8 Mon Sep 17 00:00:00 2001 From: vyudu Date: Thu, 16 Oct 2025 11:48:09 -0400 Subject: [PATCH 1/6] add error for discrete parameter update, do not add observed to algebraic equations --- src/systems/abstractsystem.jl | 4 ++-- src/systems/callbacks.jl | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 51a495e4ff..1682a08917 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -648,12 +648,12 @@ function complete( if has_continuous_events(sys) && is_time_dependent(sys) @set! sys.continuous_events = complete.( get_continuous_events(sys); iv = get_iv(sys), - alg_eqs = [alg_equations(sys); observed(sys)]) + alg_eqs = alg_equations(sys)) end if has_discrete_events(sys) && is_time_dependent(sys) @set! sys.discrete_events = complete.( get_discrete_events(sys); iv = get_iv(sys), - alg_eqs = [alg_equations(sys); observed(sys)]) + alg_eqs = alg_equations(sys)) end end if split && has_index_cache(sys) diff --git a/src/systems/callbacks.jl b/src/systems/callbacks.jl index c94166103b..0f4b75a135 100644 --- a/src/systems/callbacks.jl +++ b/src/systems/callbacks.jl @@ -31,6 +31,8 @@ function Symbolics.fast_substitute(aff::SymbolicAffect, rules) map(substituter, aff.discrete_parameters)) end +all_equations(a::SymbolicAffect) = [a.affect; a.alg_eqs] + struct AffectSystem """The internal implicit discrete system whose equations are solved to obtain values after the affect.""" system::AbstractSystem @@ -84,6 +86,9 @@ function AffectSystem(affect::Vector{Equation}; discrete_parameters = Any[], symbolic_type(eq.lhs) === NotSymbolic()) @warn "Affect equation $eq has no `Pre` operator. As such it will be interpreted as an algebraic equation to be satisfied after the callback. If you intended to use the value of a variable x before the affect, use Pre(x). Errors may be thrown if there is no `Pre` and the algebraic equation is unsatisfiable, such as X ~ X + 1." end + if ModelingToolkit.isparameter(eq.lhs) && (eq.lhs ∉ OrderedSet(discrete_parameters)) + error("Detected explicit update to parameter $(eq.lhs), but it has not been passed in as a `discrete_parameters` to the callback constructor, for example: \n\n(SymbolicDiscreteCallback(cond => aff, discrete_parameters = [$(eq.lhs)])\n\nAs a result, the parameter update will fail.") + end collect_vars!(dvs, params, eq, iv; op = Pre) empty!(_varsbuf) vars!(_varsbuf, eq; op = Pre) From c1978a5df3d5fbff39c223a3810e58898b5d264b Mon Sep 17 00:00:00 2001 From: vyudu Date: Thu, 16 Oct 2025 11:50:15 -0400 Subject: [PATCH 2/6] precompute ordered set of discrete parameters --- src/systems/callbacks.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/systems/callbacks.jl b/src/systems/callbacks.jl index 0f4b75a135..2393cd96bc 100644 --- a/src/systems/callbacks.jl +++ b/src/systems/callbacks.jl @@ -31,8 +31,6 @@ function Symbolics.fast_substitute(aff::SymbolicAffect, rules) map(substituter, aff.discrete_parameters)) end -all_equations(a::SymbolicAffect) = [a.affect; a.alg_eqs] - struct AffectSystem """The internal implicit discrete system whose equations are solved to obtain values after the affect.""" system::AbstractSystem @@ -80,13 +78,14 @@ function AffectSystem(affect::Vector{Equation}; discrete_parameters = Any[], dvs = OrderedSet() params = OrderedSet() + dp_set = OrderedSet(discrete_parameters) _varsbuf = Set() for eq in affect if !haspre(eq) && !(symbolic_type(eq.rhs) === NotSymbolic() || symbolic_type(eq.lhs) === NotSymbolic()) @warn "Affect equation $eq has no `Pre` operator. As such it will be interpreted as an algebraic equation to be satisfied after the callback. If you intended to use the value of a variable x before the affect, use Pre(x). Errors may be thrown if there is no `Pre` and the algebraic equation is unsatisfiable, such as X ~ X + 1." end - if ModelingToolkit.isparameter(eq.lhs) && (eq.lhs ∉ OrderedSet(discrete_parameters)) + if ModelingToolkit.isparameter(eq.lhs) && (eq.lhs ∉ dp_set) error("Detected explicit update to parameter $(eq.lhs), but it has not been passed in as a `discrete_parameters` to the callback constructor, for example: \n\n(SymbolicDiscreteCallback(cond => aff, discrete_parameters = [$(eq.lhs)])\n\nAs a result, the parameter update will fail.") end collect_vars!(dvs, params, eq, iv; op = Pre) From e2fb2cf4af94c58450a7223889b3f055db2af7a5 Mon Sep 17 00:00:00 2001 From: vyudu Date: Mon, 20 Oct 2025 15:34:42 -0400 Subject: [PATCH 3/6] ftest: fix discrete parameter in test --- test/symbolic_events.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/symbolic_events.jl b/test/symbolic_events.jl index 5f37e524e1..bf694908f4 100644 --- a/test/symbolic_events.jl +++ b/test/symbolic_events.jl @@ -874,10 +874,9 @@ end # Test Simulation @mtkcompile sys = TestSystem() - - # Test Simulation prob = ODEProblem(sys, [], (0.0, 150.0)) sol = solve(prob) + # This is singular at the second event, but the derivatives are zero so it's # constant after that point anyway. Just make sure it hits the last event and # had the correct `u`. @@ -1374,7 +1373,7 @@ end @parameters p2(t) = 1.0 @variables x(t) = 0.0 @variables x2(t) - event = [0.5] => [p2 ~ Pre(t)] + event = SymbolicDiscreteCallback([0.5] => [p2 ~ Pre(t)], discrete_parameters = p2) eq = [ D(x) ~ p2, From 090c1583feb088f2f0e28081049466c113ff9286 Mon Sep 17 00:00:00 2001 From: vyudu Date: Mon, 20 Oct 2025 15:35:31 -0400 Subject: [PATCH 4/6] revert remove observed --- src/systems/abstractsystem.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systems/abstractsystem.jl b/src/systems/abstractsystem.jl index 1682a08917..51a495e4ff 100644 --- a/src/systems/abstractsystem.jl +++ b/src/systems/abstractsystem.jl @@ -648,12 +648,12 @@ function complete( if has_continuous_events(sys) && is_time_dependent(sys) @set! sys.continuous_events = complete.( get_continuous_events(sys); iv = get_iv(sys), - alg_eqs = alg_equations(sys)) + alg_eqs = [alg_equations(sys); observed(sys)]) end if has_discrete_events(sys) && is_time_dependent(sys) @set! sys.discrete_events = complete.( get_discrete_events(sys); iv = get_iv(sys), - alg_eqs = alg_equations(sys)) + alg_eqs = [alg_equations(sys); observed(sys)]) end end if split && has_index_cache(sys) From d21154edaf6eee91d8a30a03cfefe2ed97dfaeb6 Mon Sep 17 00:00:00 2001 From: vyudu Date: Tue, 21 Oct 2025 11:08:13 -0400 Subject: [PATCH 5/6] fix incorrect test value --- test/symbolic_events.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/symbolic_events.jl b/test/symbolic_events.jl index bf694908f4..a18b9923cf 100644 --- a/test/symbolic_events.jl +++ b/test/symbolic_events.jl @@ -1384,7 +1384,7 @@ end prob = ODEProblem(sys, [], (0.0, 1.0)) sol = solve(prob) @test SciMLBase.successful_retcode(sol) - @test sol[x, end]≈1.0 atol=1e-6 + @test sol[x, end] ≈ 0.75 atol=1e-6 end @testset "Symbolic affects are compiled in `complete`" begin From 101e0b53ec8a91915d99322d7680870bd855297b Mon Sep 17 00:00:00 2001 From: vyudu Date: Fri, 24 Oct 2025 14:11:28 -0400 Subject: [PATCH 6/6] test: add error if parameter not declared as discrete --- test/symbolic_events.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/symbolic_events.jl b/test/symbolic_events.jl index a18b9923cf..77f3193f63 100644 --- a/test/symbolic_events.jl +++ b/test/symbolic_events.jl @@ -1374,11 +1374,13 @@ end @variables x(t) = 0.0 @variables x2(t) event = SymbolicDiscreteCallback([0.5] => [p2 ~ Pre(t)], discrete_parameters = p2) + event_broken = [0.5] => [p2 ~ Pre(t)] eq = [ D(x) ~ p2, x2 ~ p_1(x) ] + @test_throws ErrorException @mtkcompile sys = System(eq, t, [x, x2], [p_1, p2], discrete_events = [event_broken]) @mtkcompile sys = System(eq, t, [x, x2], [p_1, p2], discrete_events = [event]) prob = ODEProblem(sys, [], (0.0, 1.0))