Skip to content

Commit 8413edd

Browse files
committed
Flip modified and observed order; write docstring
1 parent d5da20d commit 8413edd

File tree

2 files changed

+80
-27
lines changed

2 files changed

+80
-27
lines changed

src/systems/callbacks.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,16 @@ function namespace_affect(affect::FunctionalAffect, s)
7272
end
7373

7474
"""
75-
MutatingFunctionalAffect(f::Function; observed::NamedTuple, modified::NamedTuple, ctx)
75+
MutatingFunctionalAffect(f::Function; modified::NamedTuple, observed::NamedTuple, ctx)
7676
7777
`MutatingFunctionalAffect` is a helper for writing affect functions that will compute observed values and
7878
ensure that modified values are correctly written back into the system. The affect function `f` needs to have
79-
one of three signatures:
80-
* `f(observed::ComponentArray)` if the function only reads observed values back from the system,
81-
* `f(observed::ComponentArray, modified::ComponentArray)` if the function also writes values (unknowns or parameters) into the system,
82-
* `f(observed::ComponentArray, modified::ComponentArray, ctx)` if the function needs the user-defined context,
83-
* `f(observed::ComponentArray, modified::ComponentArray, ctx, integrator)` if the function needs the low-level integrator.
79+
one of four signatures:
80+
* `f(modified::ComponentArray)` if the function only writes values (unknowns or parameters) to the system,
81+
* `f(modified::ComponentArray, observed::ComponentArray)` if the function also reads observed values from the system,
82+
* `f(modified::ComponentArray, observed::ComponentArray, ctx)` if the function needs the user-defined context,
83+
* `f(modified::ComponentArray, observed::ComponentArray, ctx, integrator)` if the function needs the low-level integrator.
84+
These will be checked in reverse order (that is, the four-argument version first, than the 3, etc).
8485
8586
The function `f` will be called with `observed` and `modified` `ComponentArray`s that are derived from their respective `NamedTuple` definitions.
8687
Each `NamedTuple` should map an expression to a symbol; for example if we pass `observed=(; x = a + b)` this will alias the result of executing `a+b` in the system as `x`
@@ -90,7 +91,7 @@ so the value of `a + b` will be accessible as `observed.x` in `f`. `modified` cu
9091
Both `observed` and `modified` will be automatically populated with the current values of their corresponding expressions on function entry.
9192
The values in `modified` will be written back to the system after `f` returns. For example, if we want to update the value of `x` to be the result of `x + y` we could write
9293
93-
MutatingFunctionalAffect(observed=(; x_plus_y = x + y), modified=(; x)) do o, m
94+
MutatingFunctionalAffect(observed=(; x_plus_y = x + y), modified=(; x)) do m, o
9495
m.x = o.x_plus_y
9596
end
9697
@@ -109,11 +110,11 @@ MutatingFunctionalAffect(f::Function;
109110
observed::NamedTuple = NamedTuple{()}(()),
110111
modified::NamedTuple = NamedTuple{()}(()),
111112
ctx=nothing) = MutatingFunctionalAffect(f, collect(values(observed)), collect(keys(observed)), collect(values(modified)), collect(keys(modified)), ctx)
112-
MutatingFunctionalAffect(f::Function, observed::NamedTuple; modified::NamedTuple = NamedTuple{()}(()), ctx=nothing) =
113+
MutatingFunctionalAffect(f::Function, modified::NamedTuple; observed::NamedTuple = NamedTuple{()}(()), ctx=nothing) =
113114
MutatingFunctionalAffect(f, observed=observed, modified=modified, ctx=ctx)
114-
MutatingFunctionalAffect(f::Function, observed::NamedTuple, modified::NamedTuple; ctx=nothing) =
115+
MutatingFunctionalAffect(f::Function, modified::NamedTuple, observed::NamedTuple; ctx=nothing) =
115116
MutatingFunctionalAffect(f, observed=observed, modified=modified, ctx=ctx)
116-
MutatingFunctionalAffect(f::Function, observed::NamedTuple, modified::NamedTuple, ctx) =
117+
MutatingFunctionalAffect(f::Function, modified::NamedTuple, observed::NamedTuple, ctx) =
117118
MutatingFunctionalAffect(f, observed=observed, modified=modified, ctx=ctx)
118119

119120
func(f::MutatingFunctionalAffect) = f.f
@@ -851,7 +852,18 @@ function compile_user_affect(affect::MutatingFunctionalAffect, sys, dvs, ps; kwa
851852
obs_fun(obs_component_array, integ.u, integ.p..., integ.t)
852853

853854
# let the user do their thing
854-
user_affect(upd_component_array, obs_component_array, integ, ctx)
855+
if applicable(user_affect, upd_component_array, obs_component_array, ctx, integ)
856+
user_affect(upd_component_array, obs_component_array, ctx, integ)
857+
elseif applicable(user_affect, upd_component_array, obs_component_array, ctx)
858+
user_affect(upd_component_array, obs_component_array, ctx)
859+
elseif applicable(user_affect, upd_component_array, obs_component_array)
860+
user_affect(upd_component_array, obs_component_array)
861+
elseif applicable(user_affect, upd_component_array)
862+
user_affect(upd_component_array)
863+
else
864+
@error "User affect function $user_affect needs to implement one of the supported MutatingFunctionalAffect callback forms; see the MutatingFunctionalAffect docstring for more details"
865+
user_affect(upd_component_array, obs_component_array, integ, ctx) # this WILL error but it'll give a more sensible message
866+
end
855867

856868
# write the new values back to the integrator
857869
upd_params_fun(integ, upd_params_view)

test/symbolic_events.jl

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,19 @@ end
249249
m = ModelingToolkit.MutatingFunctionalAffect(fmfa, (; x))
250250
@test m isa ModelingToolkit.MutatingFunctionalAffect
251251
@test m.f == fmfa
252-
@test isequal(m.obs, [x])
253-
@test m.obs_syms == [:x]
254-
@test m.modified == []
255-
@test m.mod_syms == []
252+
@test isequal(m.obs, [])
253+
@test m.obs_syms == []
254+
@test isequal(m.modified, [x])
255+
@test m.mod_syms == [:x]
256256
@test m.ctx === nothing
257257

258258
m = ModelingToolkit.MutatingFunctionalAffect(fmfa, (; y=x))
259259
@test m isa ModelingToolkit.MutatingFunctionalAffect
260260
@test m.f == fmfa
261-
@test isequal(m.obs, [x])
262-
@test m.obs_syms == [:y]
263-
@test m.modified == []
264-
@test m.mod_syms == []
261+
@test isequal(m.obs, [])
262+
@test m.obs_syms == []
263+
@test isequal(m.modified, [x])
264+
@test m.mod_syms == [:y]
265265
@test m.ctx === nothing
266266

267267
m = ModelingToolkit.MutatingFunctionalAffect(fmfa; observed=(; y=x))
@@ -991,7 +991,48 @@ end
991991
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i, c
992992
x.furnace_on = true
993993
end)
994-
994+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
995+
ss = structural_simplify(sys)
996+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
997+
sol = solve(prob, Tsit5(); dtmax=0.01)
998+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
999+
1000+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1001+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i
1002+
x.furnace_on = false
1003+
end)
1004+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1005+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i
1006+
x.furnace_on = true
1007+
end)
1008+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
1009+
ss = structural_simplify(sys)
1010+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
1011+
sol = solve(prob, Tsit5(); dtmax=0.01)
1012+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
1013+
1014+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1015+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o
1016+
x.furnace_on = false
1017+
end)
1018+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1019+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o
1020+
x.furnace_on = true
1021+
end)
1022+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
1023+
ss = structural_simplify(sys)
1024+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
1025+
sol = solve(prob, Tsit5(); dtmax=0.01)
1026+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
1027+
1028+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1029+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x
1030+
x.furnace_on = false
1031+
end)
1032+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1033+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x
1034+
x.furnace_on = true
1035+
end)
9951036
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
9961037
ss = structural_simplify(sys)
9971038
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
@@ -1007,7 +1048,7 @@ end
10071048
]
10081049

10091050
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1010-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on)) do x, o, i, c
1051+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on)) do x, o, c, i
10111052
x.furnace_on = false
10121053
end)
10131054
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off])
@@ -1021,7 +1062,7 @@ end
10211062
]
10221063

10231064
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1024-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on, tempsq), observed=(; furnace_on)) do x, o, i, c
1065+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on, tempsq), observed=(; furnace_on)) do x, o, c, i
10251066
x.furnace_on = false
10261067
end)
10271068
@named sys = ODESystem(eqs, t, [temp, tempsq], params; continuous_events = [furnace_off])
@@ -1031,7 +1072,7 @@ end
10311072

10321073
@parameters not_actually_here
10331074
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1034-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on, not_actually_here)) do x, o, i, c
1075+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on, not_actually_here)) do x, o, c, i
10351076
x.furnace_on = false
10361077
end)
10371078
@named sys = ODESystem(eqs, t, [temp, tempsq], params; continuous_events = [furnace_off])
@@ -1059,26 +1100,26 @@ end
10591100
end
10601101
end
10611102
qAevt = ModelingToolkit.SymbolicContinuousCallback([cos(100 * theta) ~ 0],
1062-
ModelingToolkit.MutatingFunctionalAffect((; qB), (; qA, hA, hB, cnt)) do x, o, i, c
1103+
ModelingToolkit.MutatingFunctionalAffect((; qA, hA, hB, cnt), (; qB)) do x, o, i, c
10631104
x.hA = x.qA
10641105
x.hB = o.qB
10651106
x.qA = 1
10661107
x.cnt += decoder(x.hA, x.hB, x.qA, o.qB)
10671108
end,
1068-
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qB), (; qA, hA, hB, cnt)) do x, o, i, c
1109+
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qA, hA, hB, cnt), (; qB)) do x, o, c, i
10691110
x.hA = x.qA
10701111
x.hB = o.qB
10711112
x.qA = 0
10721113
x.cnt += decoder(x.hA, x.hB, x.qA, o.qB)
10731114
end; rootfind=SciMLBase.RightRootFind)
10741115
qBevt = ModelingToolkit.SymbolicContinuousCallback([cos(100 * theta - π/2) ~ 0],
1075-
ModelingToolkit.MutatingFunctionalAffect((; qA), (; qB, hA, hB, cnt)) do x, o, i, c
1116+
ModelingToolkit.MutatingFunctionalAffect((; qB, hA, hB, cnt), (; qA)) do x, o, i, c
10761117
x.hA = o.qA
10771118
x.hB = x.qB
10781119
x.qB = 1
10791120
x.cnt += decoder(x.hA, x.hB, o.qA, x.qB)
10801121
end,
1081-
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qA), (; qB, hA, hB, cnt)) do x, o, i, c
1122+
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qB, hA, hB, cnt), (; qA)) do x, o, c, i
10821123
x.hA = o.qA
10831124
x.hB = x.qB
10841125
x.qB = 0

0 commit comments

Comments
 (0)