@@ -4,6 +4,15 @@ function has_functional_affect(cb)
4
4
affects (cb) isa ImperativeAffect
5
5
end
6
6
7
+ struct SymbolicAffect{A, K}
8
+ affect:: A
9
+ kwargs:: K
10
+ end
11
+
12
+ SymbolicAffect (affect:: Vector{Equation} ; kwargs... ) = SymbolicAffect (affect, kwargs)
13
+ SymbolicAffect (affect:: SymbolicAffect ; kwargs... ) = SymbolicAffect (affect. affect; affect. kwargs... , kwargs... )
14
+ SymbolicAffect (affect; kwargs... ) = affect
15
+
7
16
struct AffectSystem
8
17
""" The internal implicit discrete system whose equations are solved to obtain values after the affect."""
9
18
system:: AbstractSystem
@@ -15,6 +24,71 @@ struct AffectSystem
15
24
discretes:: Vector
16
25
end
17
26
27
+ function AffectSystem (spec:: SymbolicAffect ; iv = nothing , alg_eqs = Equation[], kwargs... )
28
+ AffectSystem (spec. affect; iv = something (iv, get (spec. kwargs, :iv , nothing ), Some (nothing )), alg_eqs = vcat (get (spec. kwargs, :alg_eqs , Equation[]), alg_eqs), kwargs... )
29
+ end
30
+
31
+ function AffectSystem (affect:: Vector{Equation} ; discrete_parameters = Any[],
32
+ iv = nothing , alg_eqs:: Vector{Equation} = Equation[], warn_no_algebraic = true , kwargs... )
33
+ isempty (affect) && return nothing
34
+ if isnothing (iv)
35
+ iv = t_nounits
36
+ @warn " No independent variable specified. Defaulting to t_nounits."
37
+ end
38
+
39
+ discrete_parameters isa AbstractVector || (discrete_parameters = [discrete_parameters])
40
+ discrete_parameters = unwrap .(discrete_parameters)
41
+
42
+ for p in discrete_parameters
43
+ occursin (unwrap (iv), unwrap (p)) ||
44
+ error (" Non-time dependent parameter $p passed in as a discrete. Must be declared as @parameters $p (t)." )
45
+ end
46
+
47
+ dvs = OrderedSet ()
48
+ params = OrderedSet ()
49
+ _varsbuf = Set ()
50
+ for eq in affect
51
+ if ! haspre (eq) && ! (symbolic_type (eq. rhs) === NotSymbolic () ||
52
+ symbolic_type (eq. lhs) === NotSymbolic ())
53
+ @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."
54
+ end
55
+ collect_vars! (dvs, params, eq, iv; op = Pre)
56
+ empty! (_varsbuf)
57
+ vars! (_varsbuf, eq; op = Pre)
58
+ filter! (x -> iscall (x) && operation (x) isa Pre, _varsbuf)
59
+ union! (params, _varsbuf)
60
+ diffvs = collect_applied_operators (eq, Differential)
61
+ union! (dvs, diffvs)
62
+ end
63
+ for eq in alg_eqs
64
+ collect_vars! (dvs, params, eq, iv)
65
+ end
66
+ pre_params = filter (haspre ∘ value, params)
67
+ sys_params = collect (setdiff (params, union (discrete_parameters, pre_params)))
68
+ discretes = map (tovar, discrete_parameters)
69
+ dvs = collect (dvs)
70
+ _dvs = map (default_toterm, dvs)
71
+
72
+ rev_map = Dict (zip (discrete_parameters, discretes))
73
+ subs = merge (rev_map, Dict (zip (dvs, _dvs)))
74
+ affect = Symbolics. fast_substitute (affect, subs)
75
+ alg_eqs = Symbolics. fast_substitute (alg_eqs, subs)
76
+
77
+ @named affectsys = System (
78
+ vcat (affect, alg_eqs), iv, collect (union (_dvs, discretes)),
79
+ collect (union (pre_params, sys_params)); is_discrete = true )
80
+ affectsys = mtkcompile (affectsys; fully_determined = nothing )
81
+ # get accessed parameters p from Pre(p) in the callback parameters
82
+ accessed_params = Vector {Any} (filter (isparameter, map (unPre, collect (pre_params))))
83
+ union! (accessed_params, sys_params)
84
+
85
+ # add scalarized unknowns to the map.
86
+ _dvs = reduce (vcat, map (scalarize, _dvs), init = Any[])
87
+
88
+ AffectSystem (affectsys, collect (_dvs), collect (accessed_params),
89
+ collect (discrete_parameters))
90
+ end
91
+
18
92
system (a:: AffectSystem ) = a. system
19
93
discretes (a:: AffectSystem ) = a. discretes
20
94
unknowns (a:: AffectSystem ) = a. unknowns
@@ -159,40 +233,40 @@ will run as soon as the solver starts, while finalization affects will be execut
159
233
"""
160
234
struct SymbolicContinuousCallback <: AbstractCallback
161
235
conditions:: Vector{Equation}
162
- affect:: Union{Affect, Nothing}
163
- affect_neg:: Union{Affect, Nothing}
164
- initialize:: Union{Affect, Nothing}
165
- finalize:: Union{Affect, Nothing}
236
+ affect:: Union{Affect, SymbolicAffect, Nothing}
237
+ affect_neg:: Union{Affect, SymbolicAffect, Nothing}
238
+ initialize:: Union{Affect, SymbolicAffect, Nothing}
239
+ finalize:: Union{Affect, SymbolicAffect, Nothing}
166
240
rootfind:: Union{Nothing, SciMLBase.RootfindOpt}
167
241
reinitializealg:: SciMLBase.DAEInitializationAlgorithm
242
+ end
168
243
169
- function SymbolicContinuousCallback (
170
- conditions:: Union{Equation, Vector{Equation}} ,
171
- affect = nothing ;
172
- affect_neg = affect,
173
- initialize = nothing ,
174
- finalize = nothing ,
175
- rootfind = SciMLBase. LeftRootFind,
176
- reinitializealg = nothing ,
177
- kwargs... )
178
- conditions = (conditions isa AbstractVector) ? conditions : [conditions]
179
-
180
- if isnothing (reinitializealg)
181
- if any (a -> a isa ImperativeAffect,
182
- [affect, affect_neg, initialize, finalize])
183
- reinitializealg = SciMLBase. CheckInit ()
184
- else
185
- reinitializealg = SciMLBase. NoInit ()
186
- end
244
+ function SymbolicContinuousCallback (
245
+ conditions:: Union{Equation, Vector{Equation}} ,
246
+ affect = nothing ;
247
+ affect_neg = affect,
248
+ initialize = nothing ,
249
+ finalize = nothing ,
250
+ rootfind = SciMLBase. LeftRootFind,
251
+ reinitializealg = nothing ,
252
+ kwargs... )
253
+ conditions = (conditions isa AbstractVector) ? conditions : [conditions]
254
+
255
+ if isnothing (reinitializealg)
256
+ if any (a -> a isa ImperativeAffect,
257
+ [affect, affect_neg, initialize, finalize])
258
+ reinitializealg = SciMLBase. CheckInit ()
259
+ else
260
+ reinitializealg = SciMLBase. NoInit ()
187
261
end
262
+ end
188
263
189
- new (conditions, make_affect (affect; kwargs... ),
190
- make_affect (affect_neg; kwargs... ),
191
- make_affect (initialize; kwargs... ), make_affect (
192
- finalize; kwargs... ),
193
- rootfind, reinitializealg)
194
- end # Default affect to nothing
195
- end
264
+ SymbolicContinuousCallback (conditions, SymbolicAffect (affect; kwargs... ),
265
+ SymbolicAffect (affect_neg; kwargs... ),
266
+ SymbolicAffect (initialize; kwargs... ), SymbolicAffect (
267
+ finalize; kwargs... ),
268
+ rootfind, reinitializealg)
269
+ end # Default affect to nothing
196
270
197
271
function SymbolicContinuousCallback (p:: Pair , args... ; kwargs... )
198
272
SymbolicContinuousCallback (p[1 ], p[2 ], args... ; kwargs... )
@@ -207,72 +281,18 @@ function SymbolicContinuousCallback(cb::Tuple, args...; kwargs...)
207
281
end
208
282
end
209
283
284
+ function complete (cb:: SymbolicContinuousCallback ; kwargs... )
285
+ SymbolicContinuousCallback (cb. conditions, make_affect (cb. affect; kwargs... ),
286
+ make_affect (cb. affect_neg; kwargs... ), make_affect (cb. initialize; kwargs... ),
287
+ make_affect (cb. finalize; kwargs... ), cb. rootfind, cb. reinitializealg)
288
+ end
289
+
290
+ make_affect (affect:: SymbolicAffect ; kwargs... ) = AffectSystem (affect; kwargs... )
210
291
make_affect (affect:: Nothing ; kwargs... ) = nothing
211
292
make_affect (affect:: Tuple ; kwargs... ) = ImperativeAffect (affect... )
212
293
make_affect (affect:: NamedTuple ; kwargs... ) = ImperativeAffect (; affect... )
213
294
make_affect (affect:: Affect ; kwargs... ) = affect
214
295
215
- function make_affect (affect:: Vector{Equation} ; discrete_parameters = Any[],
216
- iv = nothing , alg_eqs:: Vector{Equation} = Equation[], warn_no_algebraic = true , kwargs... )
217
- isempty (affect) && return nothing
218
- if isnothing (iv)
219
- iv = t_nounits
220
- @warn " No independent variable specified. Defaulting to t_nounits."
221
- end
222
-
223
- discrete_parameters isa AbstractVector || (discrete_parameters = [discrete_parameters])
224
- discrete_parameters = unwrap .(discrete_parameters)
225
-
226
- for p in discrete_parameters
227
- occursin (unwrap (iv), unwrap (p)) ||
228
- error (" Non-time dependent parameter $p passed in as a discrete. Must be declared as @parameters $p (t)." )
229
- end
230
-
231
- dvs = OrderedSet ()
232
- params = OrderedSet ()
233
- _varsbuf = Set ()
234
- for eq in affect
235
- if ! haspre (eq) && ! (symbolic_type (eq. rhs) === NotSymbolic () ||
236
- symbolic_type (eq. lhs) === NotSymbolic ())
237
- @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."
238
- end
239
- collect_vars! (dvs, params, eq, iv; op = Pre)
240
- empty! (_varsbuf)
241
- vars! (_varsbuf, eq; op = Pre)
242
- filter! (x -> iscall (x) && operation (x) isa Pre, _varsbuf)
243
- union! (params, _varsbuf)
244
- diffvs = collect_applied_operators (eq, Differential)
245
- union! (dvs, diffvs)
246
- end
247
- for eq in alg_eqs
248
- collect_vars! (dvs, params, eq, iv)
249
- end
250
- pre_params = filter (haspre ∘ value, params)
251
- sys_params = collect (setdiff (params, union (discrete_parameters, pre_params)))
252
- discretes = map (tovar, discrete_parameters)
253
- dvs = collect (dvs)
254
- _dvs = map (default_toterm, dvs)
255
-
256
- rev_map = Dict (zip (discrete_parameters, discretes))
257
- subs = merge (rev_map, Dict (zip (dvs, _dvs)))
258
- affect = Symbolics. fast_substitute (affect, subs)
259
- alg_eqs = Symbolics. fast_substitute (alg_eqs, subs)
260
-
261
- @named affectsys = System (
262
- vcat (affect, alg_eqs), iv, collect (union (_dvs, discretes)),
263
- collect (union (pre_params, sys_params)); is_discrete = true )
264
- affectsys = mtkcompile (affectsys; fully_determined = nothing )
265
- # get accessed parameters p from Pre(p) in the callback parameters
266
- accessed_params = Vector {Any} (filter (isparameter, map (unPre, collect (pre_params))))
267
- union! (accessed_params, sys_params)
268
-
269
- # add scalarized unknowns to the map.
270
- _dvs = reduce (vcat, map (scalarize, _dvs), init = Any[])
271
-
272
- AffectSystem (affectsys, collect (_dvs), collect (accessed_params),
273
- collect (discrete_parameters))
274
- end
275
-
276
296
function make_affect (affect; kwargs... )
277
297
error (" Malformed affect $(affect) . This should be a vector of equations or a tuple specifying a functional affect." )
278
298
end
@@ -374,30 +394,30 @@ Arguments:
374
394
"""
375
395
struct SymbolicDiscreteCallback <: AbstractCallback
376
396
conditions:: Union{Number, Vector{<:Number}, Symbolic{Bool}}
377
- affect:: Union{Affect, Nothing}
378
- initialize:: Union{Affect, Nothing}
379
- finalize:: Union{Affect, Nothing}
397
+ affect:: Union{Affect, SymbolicAffect, Nothing}
398
+ initialize:: Union{Affect, SymbolicAffect, Nothing}
399
+ finalize:: Union{Affect, SymbolicAffect, Nothing}
380
400
reinitializealg:: SciMLBase.DAEInitializationAlgorithm
401
+ end
381
402
382
- function SymbolicDiscreteCallback (
383
- condition:: Union{Symbolic{Bool}, Number, Vector{<:Number}} , affect = nothing ;
384
- initialize = nothing , finalize = nothing ,
385
- reinitializealg = nothing , kwargs... )
386
- c = is_timed_condition (condition) ? condition : value (scalarize (condition))
387
-
388
- if isnothing (reinitializealg)
389
- if any (a -> a isa ImperativeAffect,
390
- [affect, initialize, finalize])
391
- reinitializealg = SciMLBase. CheckInit ()
392
- else
393
- reinitializealg = SciMLBase. NoInit ()
394
- end
403
+ function SymbolicDiscreteCallback (
404
+ condition:: Union{Symbolic{Bool}, Number, Vector{<:Number}} , affect = nothing ;
405
+ initialize = nothing , finalize = nothing ,
406
+ reinitializealg = nothing , kwargs... )
407
+ c = is_timed_condition (condition) ? condition : value (scalarize (condition))
408
+
409
+ if isnothing (reinitializealg)
410
+ if any (a -> a isa ImperativeAffect,
411
+ [affect, initialize, finalize])
412
+ reinitializealg = SciMLBase. CheckInit ()
413
+ else
414
+ reinitializealg = SciMLBase. NoInit ()
395
415
end
396
- new (c, make_affect (affect; kwargs ... ),
397
- make_affect (initialize ; kwargs... ),
398
- make_affect (finalize ; kwargs... ), reinitializealg)
399
- end # Default affect to nothing
400
- end
416
+ end
417
+ SymbolicDiscreteCallback (c, SymbolicAffect (affect ; kwargs... ),
418
+ SymbolicAffect (initialize ; kwargs... ),
419
+ SymbolicAffect (finalize; kwargs ... ), reinitializealg)
420
+ end # Default affect to nothing
401
421
402
422
function SymbolicDiscreteCallback (p:: Pair , args... ; kwargs... )
403
423
SymbolicDiscreteCallback (p[1 ], p[2 ], args... ; kwargs... )
@@ -412,6 +432,10 @@ function SymbolicDiscreteCallback(cb::Tuple, args...; kwargs...)
412
432
end
413
433
end
414
434
435
+ function complete (cb:: SymbolicDiscreteCallback ; kwargs... )
436
+ SymbolicDiscreteCallback (cb. conditions, make_affect (cb. affect; kwargs... ), make_affect (cb. initialize; kwargs... ), make_affect (cb. finalize; kwargs... ), cb. reinitializealg)
437
+ end
438
+
415
439
function is_timed_condition (condition:: T ) where {T}
416
440
if T === Num
417
441
false
@@ -1060,12 +1084,8 @@ end
1060
1084
"""
1061
1085
Process the symbolic events of a system.
1062
1086
"""
1063
- function create_symbolic_events (cont_events, disc_events, sys_eqs, iv)
1064
- alg_eqs = filter (eq -> eq. lhs isa Union{Symbolic, Number} && ! is_diff_equation (eq),
1065
- sys_eqs)
1066
- cont_callbacks = to_cb_vector (cont_events; CB_TYPE = SymbolicContinuousCallback,
1067
- iv = iv, alg_eqs = alg_eqs, warn_no_algebraic = false )
1068
- disc_callbacks = to_cb_vector (disc_events; CB_TYPE = SymbolicDiscreteCallback,
1069
- iv = iv, alg_eqs = alg_eqs, warn_no_algebraic = false )
1087
+ function create_symbolic_events (cont_events, disc_events)
1088
+ cont_callbacks = to_cb_vector (cont_events; CB_TYPE = SymbolicContinuousCallback)
1089
+ disc_callbacks = to_cb_vector (disc_events; CB_TYPE = SymbolicDiscreteCallback)
1070
1090
cont_callbacks, disc_callbacks
1071
1091
end
0 commit comments