Skip to content

Commit fd0125d

Browse files
committed
Flip modified and observed order; write docstring
1 parent eec24cf commit fd0125d

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
@@ -925,7 +926,18 @@ function compile_user_affect(affect::MutatingFunctionalAffect, sys, dvs, ps; kwa
925926
obs_fun(obs_component_array, integ.u, integ.p..., integ.t)
926927

927928
# let the user do their thing
928-
user_affect(upd_component_array, obs_component_array, integ, ctx)
929+
if applicable(user_affect, upd_component_array, obs_component_array, ctx, integ)
930+
user_affect(upd_component_array, obs_component_array, ctx, integ)
931+
elseif applicable(user_affect, upd_component_array, obs_component_array, ctx)
932+
user_affect(upd_component_array, obs_component_array, ctx)
933+
elseif applicable(user_affect, upd_component_array, obs_component_array)
934+
user_affect(upd_component_array, obs_component_array)
935+
elseif applicable(user_affect, upd_component_array)
936+
user_affect(upd_component_array)
937+
else
938+
@error "User affect function $user_affect needs to implement one of the supported MutatingFunctionalAffect callback forms; see the MutatingFunctionalAffect docstring for more details"
939+
user_affect(upd_component_array, obs_component_array, integ, ctx) # this WILL error but it'll give a more sensible message
940+
end
929941

930942
# write the new values back to the integrator
931943
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
@@ -250,19 +250,19 @@ end
250250
m = ModelingToolkit.MutatingFunctionalAffect(fmfa, (; x))
251251
@test m isa ModelingToolkit.MutatingFunctionalAffect
252252
@test m.f == fmfa
253-
@test isequal(m.obs, [x])
254-
@test m.obs_syms == [:x]
255-
@test m.modified == []
256-
@test m.mod_syms == []
253+
@test isequal(m.obs, [])
254+
@test m.obs_syms == []
255+
@test isequal(m.modified, [x])
256+
@test m.mod_syms == [:x]
257257
@test m.ctx === nothing
258258

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

268268
m = ModelingToolkit.MutatingFunctionalAffect(fmfa; observed=(; y=x))
@@ -1013,7 +1013,48 @@ end
10131013
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i, c
10141014
x.furnace_on = true
10151015
end)
1016-
1016+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
1017+
ss = structural_simplify(sys)
1018+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
1019+
sol = solve(prob, Tsit5(); dtmax=0.01)
1020+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
1021+
1022+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1023+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i
1024+
x.furnace_on = false
1025+
end)
1026+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1027+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o, i
1028+
x.furnace_on = true
1029+
end)
1030+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
1031+
ss = structural_simplify(sys)
1032+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
1033+
sol = solve(prob, Tsit5(); dtmax=0.01)
1034+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
1035+
1036+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1037+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o
1038+
x.furnace_on = false
1039+
end)
1040+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1041+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x, o
1042+
x.furnace_on = true
1043+
end)
1044+
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
1045+
ss = structural_simplify(sys)
1046+
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
1047+
sol = solve(prob, Tsit5(); dtmax=0.01)
1048+
@test all(sol[temp][sol.t .> 1.0] .<= 0.79) && all(sol[temp][sol.t .> 1.0] .>= 0.49)
1049+
1050+
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1051+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x
1052+
x.furnace_on = false
1053+
end)
1054+
furnace_enable = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_on_threshold],
1055+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on)) do x
1056+
x.furnace_on = true
1057+
end)
10171058
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off, furnace_enable])
10181059
ss = structural_simplify(sys)
10191060
prob = ODEProblem(ss, [temp => 0.0, furnace_on => true], (0.0, 100.0))
@@ -1029,7 +1070,7 @@ end
10291070
]
10301071

10311072
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1032-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on)) do x, o, i, c
1073+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on)) do x, o, c, i
10331074
x.furnace_on = false
10341075
end)
10351076
@named sys = ODESystem(eqs, t, [temp], params; continuous_events = [furnace_off])
@@ -1043,7 +1084,7 @@ end
10431084
]
10441085

10451086
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1046-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on, tempsq), observed=(; furnace_on)) do x, o, i, c
1087+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on, tempsq), observed=(; furnace_on)) do x, o, c, i
10471088
x.furnace_on = false
10481089
end)
10491090
@named sys = ODESystem(eqs, t, [temp, tempsq], params; continuous_events = [furnace_off])
@@ -1053,7 +1094,7 @@ end
10531094

10541095
@parameters not_actually_here
10551096
furnace_off = ModelingToolkit.SymbolicContinuousCallback([temp ~ furnace_off_threshold],
1056-
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on, not_actually_here)) do x, o, i, c
1097+
ModelingToolkit.MutatingFunctionalAffect(modified=(; furnace_on), observed=(; furnace_on, not_actually_here)) do x, o, c, i
10571098
x.furnace_on = false
10581099
end)
10591100
@named sys = ODESystem(eqs, t, [temp, tempsq], params; continuous_events = [furnace_off])
@@ -1081,26 +1122,26 @@ end
10811122
end
10821123
end
10831124
qAevt = ModelingToolkit.SymbolicContinuousCallback([cos(100 * theta) ~ 0],
1084-
ModelingToolkit.MutatingFunctionalAffect((; qB), (; qA, hA, hB, cnt)) do x, o, i, c
1125+
ModelingToolkit.MutatingFunctionalAffect((; qA, hA, hB, cnt), (; qB)) do x, o, i, c
10851126
x.hA = x.qA
10861127
x.hB = o.qB
10871128
x.qA = 1
10881129
x.cnt += decoder(x.hA, x.hB, x.qA, o.qB)
10891130
end,
1090-
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qB), (; qA, hA, hB, cnt)) do x, o, i, c
1131+
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qA, hA, hB, cnt), (; qB)) do x, o, c, i
10911132
x.hA = x.qA
10921133
x.hB = o.qB
10931134
x.qA = 0
10941135
x.cnt += decoder(x.hA, x.hB, x.qA, o.qB)
10951136
end; rootfind=SciMLBase.RightRootFind)
10961137
qBevt = ModelingToolkit.SymbolicContinuousCallback([cos(100 * theta - π/2) ~ 0],
1097-
ModelingToolkit.MutatingFunctionalAffect((; qA), (; qB, hA, hB, cnt)) do x, o, i, c
1138+
ModelingToolkit.MutatingFunctionalAffect((; qB, hA, hB, cnt), (; qA)) do x, o, i, c
10981139
x.hA = o.qA
10991140
x.hB = x.qB
11001141
x.qB = 1
11011142
x.cnt += decoder(x.hA, x.hB, o.qA, x.qB)
11021143
end,
1103-
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qA), (; qB, hA, hB, cnt)) do x, o, i, c
1144+
affect_neg = ModelingToolkit.MutatingFunctionalAffect((; qB, hA, hB, cnt), (; qA)) do x, o, c, i
11041145
x.hA = o.qA
11051146
x.hB = x.qB
11061147
x.qB = 0

0 commit comments

Comments
 (0)