Skip to content

Commit aff08c6

Browse files
feat: support initialize_save_discretes in symbolic callbacks
1 parent c208c11 commit aff08c6

3 files changed

Lines changed: 47 additions & 14 deletions

File tree

lib/ModelingToolkitBase/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ ReadOnlyDicts = "1.0.0"
151151
RecursiveArrayTools = "3.26"
152152
Reexport = "0.2, 1"
153153
RuntimeGeneratedFunctions = "0.5.12"
154-
SciMLBase = "2.144.0"
154+
SciMLBase = "2.149.0"
155155
SciMLPublic = "1.0.0"
156156
SciMLStructures = "1.7"
157157
Serialization = "1"

lib/ModelingToolkitBase/src/systems/callbacks.jl

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ const Affect = Union{AffectSystem, ImperativeAffect}
269269

270270
"""
271271
SymbolicContinuousCallback(eqs::Vector{Equation}, affect = nothing, iv = nothing;
272-
affect_neg = affect, initialize = nothing, finalize = nothing, rootfind = SciMLBase.LeftRootFind)
272+
affect_neg = affect, initialize = nothing, finalize = nothing,
273+
rootfind = SciMLBase.LeftRootFind, initialize_save_discretes = true)
273274
274275
A [`ContinuousCallback`](@ref SciMLBase.ContinuousCallback) specified symbolically. Takes a vector of equations `eq`
275276
as well as the positive-edge `affect` and negative-edge `affect_neg` that apply when *any* of `eq` are satisfied.
@@ -313,6 +314,9 @@ Affects (i.e. `affect` and `affect_neg`) can be specified as either:
313314
- Symbolic affects have reinitialization built in. In this case the algorithm will default to SciMLBase.NoInit(), and should **not** be provided.
314315
- Functional and imperative affects will default to SciMLBase.CheckInit(), which will error if the system is not properly reinitialized after the callback. If your system is a DAE, pass in an algorithm like SciMLBase.BrownBasicFullInit() to properly re-initialize.
315316
317+
`initialize_save_discretes` is a flag indicating whether the discrete variables modified by this
318+
callback should be saved at the start of the integration (when the `initialize` runs).
319+
316320
Initial and final affects can also be specified identically to positive and negative edge affects. Initialization affects
317321
will run as soon as the solver starts, while finalization affects will be executed after termination.
318322
"""
@@ -325,6 +329,7 @@ struct SymbolicContinuousCallback <: AbstractCallback
325329
rootfind::Union{Nothing, SciMLBase.RootfindOpt}
326330
reinitializealg::SciMLBase.DAEInitializationAlgorithm
327331
zero_crossing_id::Symbol
332+
initialize_save_discretes::Bool
328333
end
329334

330335
function SymbolicContinuousCallback(
@@ -336,6 +341,7 @@ function SymbolicContinuousCallback(
336341
rootfind = SciMLBase.LeftRootFind,
337342
reinitializealg = nothing,
338343
zero_crossing_id = gensym(),
344+
initialize_save_discretes = true,
339345
kwargs...
340346
)
341347
conditions = (conditions isa AbstractVector) ? conditions : [conditions]
@@ -357,7 +363,7 @@ function SymbolicContinuousCallback(
357363
SymbolicAffect(initialize; kwargs...), SymbolicAffect(
358364
finalize; kwargs...
359365
),
360-
rootfind, reinitializealg, zero_crossing_id
366+
rootfind, reinitializealg, zero_crossing_id, initialize_save_discretes
361367
)
362368
end # Default affect to nothing
363369

@@ -378,7 +384,8 @@ function complete(cb::SymbolicContinuousCallback; kwargs...)
378384
return SymbolicContinuousCallback(
379385
cb.conditions, make_affect(cb.affect; kwargs...),
380386
make_affect(cb.affect_neg; kwargs...), make_affect(cb.initialize; kwargs...),
381-
make_affect(cb.finalize; kwargs...), cb.rootfind, cb.reinitializealg, cb.zero_crossing_id
387+
make_affect(cb.finalize; kwargs...), cb.rootfind, cb.reinitializealg,
388+
cb.zero_crossing_id, cb.initialize_save_discretes
382389
)
383390
end
384391

@@ -489,12 +496,13 @@ struct SymbolicDiscreteCallback <: AbstractCallback
489496
initialize::Union{Affect, SymbolicAffect, Nothing}
490497
finalize::Union{Affect, SymbolicAffect, Nothing}
491498
reinitializealg::SciMLBase.DAEInitializationAlgorithm
499+
initialize_save_discretes::Bool
492500
end
493501

494502
function SymbolicDiscreteCallback(
495503
condition::Union{SymbolicT, Number, Vector{<:Number}}, affect = nothing;
496504
initialize = nothing, finalize = nothing,
497-
reinitializealg = nothing, kwargs...
505+
reinitializealg = nothing, initialize_save_discretes = true, kwargs...
498506
)
499507
# Manual error check (to prevent events like `[X < 5.0] => [X ~ Pre(X) + 10.0]` from being created).
500508
(condition isa Vector) && (eltype(condition) <: Num) &&
@@ -516,7 +524,8 @@ function SymbolicDiscreteCallback(
516524
return SymbolicDiscreteCallback(
517525
c, SymbolicAffect(affect; kwargs...),
518526
SymbolicAffect(initialize; kwargs...),
519-
SymbolicAffect(finalize; kwargs...), reinitializealg
527+
SymbolicAffect(finalize; kwargs...), reinitializealg,
528+
initialize_save_discretes
520529
)
521530
end # Default affect to nothing
522531

@@ -537,7 +546,8 @@ function complete(cb::SymbolicDiscreteCallback; kwargs...)
537546
return SymbolicDiscreteCallback(
538547
cb.conditions, make_affect(cb.affect; kwargs...),
539548
make_affect(cb.initialize; kwargs...),
540-
make_affect(cb.finalize; kwargs...), cb.reinitializealg
549+
make_affect(cb.finalize; kwargs...), cb.reinitializealg,
550+
cb.initialize_save_discretes
541551
)
542552
end
543553

@@ -758,15 +768,15 @@ function generate_continuous_callbacks(
758768
cbs = continuous_events(sys)
759769
isempty(cbs) && return nothing
760770
cb_classes = Dict{
761-
Tuple{SciMLBase.RootfindOpt, SciMLBase.DAEInitializationAlgorithm},
771+
Tuple{SciMLBase.RootfindOpt, SciMLBase.DAEInitializationAlgorithm, Bool},
762772
Vector{SymbolicContinuousCallback},
763773
}()
764774

765775
# Sort the callbacks by their rootfinding method
766776
for cb in cbs
767777
_cbs = get!(
768778
() -> SymbolicContinuousCallback[],
769-
cb_classes, (cb.rootfind, cb.reinitializealg)
779+
cb_classes, (cb.rootfind, cb.reinitializealg, cb.initialize_save_discretes)
770780
)
771781
push!(_cbs, cb)
772782
end
@@ -876,7 +886,7 @@ function generate_callback(cbs::Vector{SymbolicContinuousCallback}, sys; kwargs.
876886
return VectorContinuousCallback(
877887
trigger, affect, affect_neg, length(eqs); initialize, finalize,
878888
rootfind = cbs[1].rootfind, initializealg = cbs[1].reinitializealg,
879-
saved_clock_partitions
889+
saved_clock_partitions, initialize_save_discretes = cbs[1].initialize_save_discretes
880890
)
881891
end
882892

@@ -913,23 +923,26 @@ function generate_callback(cb, sys; kwargs...)
913923
if is_timed && conditions(cb) isa AbstractVector
914924
return PresetTimeCallback(
915925
trigger, affect; initialize,
916-
finalize, initializealg = cb.reinitializealg, saved_clock_partitions
926+
finalize, initializealg = cb.reinitializealg, saved_clock_partitions,
927+
initialize_save_discretes = cb.initialize_save_discretes
917928
)
918929
elseif is_timed
919930
return PeriodicCallback(
920931
affect, trigger; initialize, finalize, initializealg = cb.reinitializealg,
921-
saved_clock_partitions
932+
saved_clock_partitions, initialize_save_discretes = cb.initialize_save_discretes
922933
)
923934
else
924935
return DiscreteCallback(
925936
trigger, affect; initialize,
926-
finalize, initializealg = cb.reinitializealg, saved_clock_partitions
937+
finalize, initializealg = cb.reinitializealg, saved_clock_partitions,
938+
initialize_save_discretes = cb.initialize_save_discretes
927939
)
928940
end
929941
else
930942
return ContinuousCallback(
931943
trigger, affect, affect_neg; initialize, finalize,
932-
rootfind = cb.rootfind, initializealg = cb.reinitializealg, saved_clock_partitions
944+
rootfind = cb.rootfind, initializealg = cb.reinitializealg, saved_clock_partitions,
945+
initialize_save_discretes = cb.initialize_save_discretes
933946
)
934947
end
935948
end

lib/ModelingToolkitBase/test/symbolic_events.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,3 +1875,23 @@ if @isdefined(ModelingToolkit)
18751875
@test_nowarn mtkcompile(sys)
18761876
end
18771877
end
1878+
1879+
if !@isdefined(ModelingToolkit)
1880+
@testset "`initialize_save_discretes` support" begin
1881+
@variables x(t)
1882+
@discretes d1(t) d2(t)
1883+
cevt = SymbolicContinuousCallback(
1884+
[x ~ 0.5], [d1 ~ Pre(d1) + 0.1]; discrete_parameters = [d1], initialize_save_discretes = false
1885+
)
1886+
devt = SymbolicDiscreteCallback(
1887+
0.1, [d2 ~ Pre(d2) + 0.1]; discrete_parameters = [d2], initialize_save_discretes = false
1888+
)
1889+
@mtkcompile sys = System(
1890+
[D(x) ~ sin(d1 * d2 * t)], t; continuous_events = [cevt], discrete_events = [devt]
1891+
)
1892+
prob = ODEProblem(sys, [x => 0.0, d1 => 1.0, d2 => 1.0], (0.0, 10.0))
1893+
sol = solve(prob, Tsit5())
1894+
@test sol.discretes[1].t[1] > 0.0
1895+
@test sol.discretes[2].t[1] > 0.0
1896+
end
1897+
end

0 commit comments

Comments
 (0)