Skip to content

Commit 4f83056

Browse files
committed
up
1 parent 96935e0 commit 4f83056

File tree

2 files changed

+26
-160
lines changed

2 files changed

+26
-160
lines changed

src/systems/callbacks.jl

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct SymbolicAffect
1111
end
1212

1313
function SymbolicAffect(affect::Vector{Equation}; alg_eqs = Equation[],
14-
discrete_parameters = infer_discrete_parameters(affect), kwargs...)
14+
discrete_parameters = Any[], kwargs...)
1515
if !(discrete_parameters isa AbstractVector)
1616
discrete_parameters = Any[discrete_parameters]
1717
elseif !(discrete_parameters isa Vector{Any})
@@ -31,38 +31,6 @@ function Symbolics.fast_substitute(aff::SymbolicAffect, rules)
3131
map(substituter, aff.discrete_parameters))
3232
end
3333

34-
# The discrete parameters (i.e. parameters that are updated in an event) can be inferred as
35-
# those that occur in an affect equation *outside* of a `Pre(...)` operator.
36-
function infer_discrete_parameters(affects)
37-
discrete_parameters = Set()
38-
for affect in affects
39-
if affect isa Equation
40-
infer_discrete_parameters!(discrete_parameters, affect.lhs)
41-
infer_discrete_parameters!(discrete_parameters, affect.rhs)
42-
elseif affect isa NamedTuple
43-
haskey(affect, :modified) && union!(discrete_parameters, affect.modified)
44-
end
45-
end
46-
return collect(discrete_parameters)
47-
end
48-
49-
# Find all `expr`'s parameters that occur *outside* of a Pre(...) statement. Add these to `discrete_parameters`.
50-
function infer_discrete_parameters!(discrete_parameters, expr)
51-
expr_pre_removed = Symbolics.replacenode(expr, precall_to_1)
52-
dynamic_symvars = Symbolics.get_variables(expr_pre_removed)
53-
# Change this coming line to a Symbolic append type of thing.
54-
union!(discrete_parameters, filter(ModelingToolkit.isparameter, dynamic_symvars))
55-
end
56-
57-
# When updating vector variables, the affect side can be a vector.
58-
function infer_discrete_parameters!(discrete_parameters, expr_vec::Vector)
59-
foreach(expr -> infer_discrete_parameters!(discrete_parameters, expr), expr_vec)
60-
end
61-
62-
# Functions for replacing a Pre-call with a `1.0` (removing its content from an expression).
63-
is_precall(expr) = iscall(expr) ? operation(expr) isa Pre : false
64-
precall_to_1(expr) = (is_precall(expr) ? 1.0 : expr)
65-
6634
struct AffectSystem
6735
"""The internal implicit discrete system whose equations are solved to obtain values after the affect."""
6836
system::AbstractSystem

test/symbolic_events.jl

Lines changed: 25 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,8 @@ end
13741374
@parameters p2(t) = 1.0
13751375
@variables x(t) = 0.0
13761376
@variables x2(t)
1377-
event = [0.5] => [p2 ~ Pre(t)]
1377+
event = ModelingToolkit.SymbolicDiscreteCallback(
1378+
[0.5] => [p2 ~ Pre(t)]; discrete_parameters = [p2])
13781379

13791380
eq = [
13801381
D(x) ~ p2,
@@ -1536,132 +1537,29 @@ end
15361537
@test M_sol[M] [Mini, -Mini, Mini]
15371538
end
15381539

1539-
@testset "Automatic inference of `discrete_parameters`" begin
1540-
# Basic case, checks for both types of events (in combination and isolation).
1541-
let
1542-
# Creates models with continuous, discrete, or both types of events
1543-
@variables X(t) Y(t)
1544-
@parameters p1(t) p2(t) d1 d2
1545-
eqs = [
1546-
D(X) ~ p1 - d1*X,
1547-
D(Y) ~ p2 - d2*Y
1548-
]
1549-
cevent = [[t ~ 1.0] => [p1 ~ 2 * Pre(p1)]]
1550-
devent = [[1.0] => [p2 ~ 2 * Pre(p2)]]
1551-
@mtkcompile sys_c = System(eqs, t; continuous_events = cevent)
1552-
@mtkcompile sys_d = System(eqs, t; discrete_events = devent)
1553-
@mtkcompile sys_cd = System(eqs, t; discrete_events = devent, continuous_events = cevent)
1554-
1555-
# Simulates them all. They should start at steady states (X = Y = 1).
1556-
# If event triggers, the variable should become 2 (after some wait).
1557-
sim_cond = [X => 1.0, Y => 1.0, p1 => 1.0, p2 => 1.0, d1 => 1.0, d2 => 1.0]
1558-
prob_c = ODEProblem(sys_c, sim_cond, 100.0)
1559-
prob_d = ODEProblem(sys_d, sim_cond, 100.0)
1560-
prob_cd = ODEProblem(sys_cd, sim_cond, 100.0)
1561-
sol_c = solve(prob_c, Rosenbrock23())
1562-
sol_d = solve(prob_d, Rosenbrock23())
1563-
sol_cd = solve(prob_cd, Rosenbrock23())
1564-
@test sol_c[X][end]2.0 atol=1e-3 rtol=1e-3
1565-
@test sol_c[Y][end]1.0 atol=1e-3 rtol=1e-3
1566-
@test sol_d[X][end]1.0 atol=1e-3 rtol=1e-3
1567-
@test sol_d[Y][end]2.0 atol=1e-3 rtol=1e-3
1568-
@test sol_cd[Y][end]2.0 atol=1e-3 rtol=1e-3
1569-
@test sol_cd[Y][end]2.0 atol=1e-3 rtol=1e-3
1540+
@testset "Check array parameter and variables event" begin
1541+
# Creates the model using the macro.
1542+
us = @variables begin
1543+
X(t)[1:2] = [4.0, 4.0]
15701544
end
1571-
1572-
# Complicated and multiple events.
1573-
# All should trigger. Modified parameters (and only those) should get non-zero values.
1574-
let
1575-
# Declares the model. `k` parameters depend on time, but should not actually be updated.
1576-
us = @variables X1(t) X2(t) X3(t) X4(t) X5(t)
1577-
ps = @parameters p1(t) p2(t) p3(t) p4(t) p5(t) k1(t) k2(t) k3(t) k4(t) k5(t) d1 d2 d3 d4 d5
1578-
eqs = [
1579-
D(X1) ~ p1 + k1 - d1*X1,
1580-
D(X2) ~ p2 + k2 - d2*X2,
1581-
D(X3) ~ p3 + k3 - d3*X3,
1582-
D(X4) ~ p4 + k4 - d4*X4,
1583-
D(X5) ~ p4 + k4 - d4*X5
1584-
]
1585-
cevents = [[t + d1 + k1 ~
1586-
0.5] => [Pre(X1)*(p1 + 5 + Pre(X2)) + Pre(k1) ~ Pre(3X2 + k2)]]
1587-
devents = [
1588-
2.0 => [exp(p2 + Pre(p2)) ~ 5.0],
1589-
[1.0] => [(4 + Pre(k2) + Pre(k4) + Pre(k3))^3 + exp(1 + Pre(k3)) ~
1590-
(3 + p3 + Pre(k2))^3],
1591-
(t ==
1592-
1.5) => [
1593-
Pre(k2) + Pre(k3) ~ p4 * (2 + Pre(k1)) + 3,
1594-
Pre(p5) + 2 + 3Pre(k4) + Pre(p5) ~ exp(p5)
1595-
]
1596-
]
1597-
@mtkcompile sys = System(
1598-
eqs, t, us, ps; continuous_events = cevents, discrete_events = devents)
1599-
1600-
# Simulates system so that all events trigger.
1601-
sim_cond = [
1602-
X1 => 1.0, X2 => 1.0, X3 => 1.0, X4 => 1.0, X5 => 1.0,
1603-
p1 => 0.0, p2 => 0.0, p3 => 0.0, p4 => 0.0, p5 => 0.0,
1604-
k1 => 0.0, k2 => 0.0, k3 => 0.0, k4 => 0.0, k5 => 0.0,
1605-
d1 => 0.0, d2 => 0.0, d3 => 0.0, d4 => 0.0, d5 => 0.0
1606-
]
1607-
prob = ODEProblem(sys, sim_cond, 3.0)
1608-
sol = solve(prob, tstops = [1.5])
1609-
1610-
# Check that the correct parameters have been modified.
1611-
@test sol.ps[p1][end] != 0.0
1612-
@test sol.ps[p2][end] != 0.0
1613-
@test sol.ps[p3][end] != 0.0
1614-
@test sol.ps[p4][end] != 0.0
1615-
@test sol.ps[p5][end] != 0.0
1616-
@test sol.ps[k1] == sol.ps[k2] == sol.ps[k3] == sol.ps[k4] == sol.ps[k5] == 0.0
1617-
@test sol.ps[d1] == sol.ps[d2] == sol.ps[d3] == sol.ps[d4] == sol.ps[d5] == 0.0
1618-
end
1619-
1620-
# Checks that everything works for vector-valued parameters and variables.
1621-
let
1622-
# Creates the model using the macro.
1623-
@mtkmodel VectorParams begin
1624-
@parameters begin
1625-
k(t)[1:2] = [1, 1]
1626-
kup = 2.0
1627-
end
1628-
@variables begin
1629-
X(t)[1:2] = [4.0, 4.0]
1630-
end
1631-
@equations begin
1632-
D(X[1]) ~ -k[1]*X[1] + k[2]*X[2]
1633-
D(X[2]) ~ k[1]*X[1] - k[2]*X[2]
1634-
end
1635-
@continuous_events begin
1636-
(k[2] ~ t) => [k[1] ~ Pre(k[1] + kup)]
1637-
end
1638-
end
1639-
@mtkcompile model = VectorParams()
1640-
1641-
# Simulates the model. Checks that the correct values are achieved.
1642-
prob = ODEProblem(model, [], (0.0, 100.0))
1643-
sol = solve(prob, Rosenbrock23())
1644-
@test sol.ps[model.kup] == 2.0
1645-
@test sol.ps[model.k[1]] == 3.0
1646-
@test sol.ps[model.k[2]] == 1.0
1647-
@test sol[model.X[1]][end]2.0 atol=1e-8 rtol=1e-8
1648-
@test sol[model.X[2]][end]6.0 atol=1e-8 rtol=1e-8
1649-
end
1650-
1651-
# Checks for a functional affect.
1652-
let
1653-
# Creates model.
1654-
@variables X(t) = 5.0
1655-
@parameters p=2.0 d(t)=1.0
1656-
eqs = [D(X) ~ p - d * X]
1657-
affect!(mod, obs, ctx, integ) = return (; d = 2.0)
1658-
cevent = [t ~ 1.0] => (f = affect!, modified = (; d))
1659-
@mtkcompile sys = System(eqs, t; continuous_events = [cevent])
1660-
1661-
# Simulates the model and checks that values is correct.
1662-
sol = solve(ODEProblem(sys, [], (0.0, 100.0)), Rosenbrock23())
1663-
@test sol[X][end]1.0 atol=1e-8 rtol=1e-8
1664-
@test sol.ps[p] == 2.0
1665-
@test sol.ps[d] == [1.0, 2.0]
1545+
ps = @parameters begin
1546+
k(t)[1:2] = [1, 1]
1547+
kup = 2.0
16661548
end
1549+
eqs = [
1550+
D(X[1]) ~ -k[1]*X[1] + k[2]*X[2]
1551+
D(X[2]) ~ k[1]*X[1] - k[2]*X[2]
1552+
]
1553+
c_event = SymbolicContinuousCallback(
1554+
(k[2] ~ t) => [k[1] ~ Pre(k[1] + kup)]; discrete_parameters = [k[1]])
1555+
@mtkcompile model = System(eqs, t, us, ps; continuous_events = [c_event])
1556+
1557+
# Simulates the model. Checks that the correct values are achieved.
1558+
prob = ODEProblem(model, [], (0.0, 100.0))
1559+
sol = solve(prob, Rosenbrock23())
1560+
@test sol.ps[model.kup] == 2.0
1561+
@test sol.ps[model.k[1]][end] == 3.0
1562+
@test sol.ps[model.k[2]][end] == 1.0
1563+
@test sol[model.X[1]][end]2.0 atol=1e-8 rtol=1e-8
1564+
@test sol[model.X[2]][end]6.0 atol=1e-8 rtol=1e-8
16671565
end

0 commit comments

Comments
 (0)