Skip to content

Commit 1b98b86

Browse files
authored
Merge pull request #1575 from SciML/myb/pss
Add the dummy derivative pass
2 parents 6f49685 + a267e2d commit 1b98b86

21 files changed

+438
-72
lines changed

docs/src/mtkitize_tutorials/modelingtoolkitize_index_reduction.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ p = [9.8, 1]
2929
tspan = (0, 10.0)
3030
pendulum_prob = ODEProblem(pendulum_fun!, u0, tspan, p)
3131
traced_sys = modelingtoolkitize(pendulum_prob)
32-
pendulum_sys = structural_simplify(traced_sys)
33-
prob = ODAEProblem(pendulum_sys, Pair[], tspan)
32+
pendulum_sys = structural_simplify(dae_index_lowering(traced_sys))
33+
prob = ODAEProblem(pendulum_sys, [], tspan)
3434
sol = solve(prob, Tsit5(),abstol=1e-8,reltol=1e-8)
3535
plot(sol, vars=states(traced_sys))
3636
```

src/ModelingToolkit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export ControlSystem
175175
export alias_elimination, flatten
176176
export connect, @connector, Connection, Flow, Stream, instream
177177
export isinput, isoutput, getbounds, hasbounds, isdisturbance, istunable, getdist, hasdist, tunable_parameters
178-
export ode_order_lowering, liouville_transform
178+
export ode_order_lowering, dae_order_lowering, liouville_transform
179179
export runge_kutta_discretize
180180
export PDESystem
181181
export Differential, expand_derivatives, @derivatives

src/bipartite_graph.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,13 @@ Base.in(edge::BipartiteEdge, g::BipartiteGraph) = Graphs.has_edge(g, edge)
234234

235235
### Maximal matching
236236
"""
237-
construct_augmenting_path!(m::Matching, g::BipartiteGraph, vsrc, dstfilter, vcolor=falses(ndsts(g)), ecolor=falses(nsrcs(g))) -> path_found::Bool
237+
construct_augmenting_path!(m::Matching, g::BipartiteGraph, vsrc, dstfilter, vcolor=falses(ndsts(g)), ecolor=nothing) -> path_found::Bool
238238
239239
Try to construct an augmenting path in matching and if such a path is found,
240240
update the matching accordingly.
241241
"""
242-
function construct_augmenting_path!(matching::Matching, g::BipartiteGraph, vsrc, dstfilter, dcolor=falses(ndsts(g)), scolor=falses(nsrcs(g)))
243-
scolor[vsrc] = true
242+
function construct_augmenting_path!(matching::Matching, g::BipartiteGraph, vsrc, dstfilter, dcolor=falses(ndsts(g)), scolor=nothing)
243+
scolor === nothing || (scolor[vsrc] = true)
244244

245245
# if a `vdst` is unassigned and the edge `vsrc <=> vdst` exists
246246
for vdst in 𝑠neighbors(g, vsrc)

src/inputoutput.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ function generate_control_function(
210210
inputs = map(x->time_varying_as_func(value(x), sys), ctrls)
211211

212212
eqs = [eq for eq in equations(sys) if !isdifferenceeq(eq)]
213-
foreach(check_derivative_variables, eqs)
213+
check_operator_variables(eqs, Differential)
214214
# substitute x(t) by just x
215215
rhss = implicit_dae ? [_iszero(eq.lhs) ? eq.rhs : eq.rhs - eq.lhs for eq in eqs] :
216216
[eq.rhs for eq in eqs]
@@ -244,5 +244,5 @@ function toparam(sys, ctrls::AbstractVector)
244244
eqs = map(eqs) do eq
245245
substitute(eq.lhs, subs) ~ substitute(eq.rhs, subs)
246246
end
247-
ODESystem(eqs, name=sys.name)
248-
end
247+
ODESystem(eqs, name=nameof(sys))
248+
end

src/structural_transformation/StructuralTransformations.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ using ModelingToolkit.BipartiteGraphs
2727
import .BipartiteGraphs: invview
2828
using Graphs
2929
using ModelingToolkit.SystemStructures
30-
using ModelingToolkit.SystemStructures: algeqs
30+
using ModelingToolkit.SystemStructures: algeqs, EquationsView
3131

3232
using ModelingToolkit.DiffEqBase
3333
using ModelingToolkit.StaticArrays
@@ -40,6 +40,7 @@ using SparseArrays
4040
using NonlinearSolve
4141

4242
export tearing, partial_state_selection, dae_index_lowering, check_consistency
43+
export dummy_derivative
4344
export build_torn_function, build_observed_function, ODAEProblem
4445
export sorted_incidence_matrix, pantelides!, tearing_reassemble, find_solvables!
4546
export tearing_assignments, tearing_substitution

src/structural_transformation/bareiss.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ function nullspace(A; col_order=nothing)
222222
@swap(col_order[i],col_order[cp])
223223
end
224224
end
225-
225+
226226
N = ModelingToolkit.reduced_echelon_nullspace(rank, B)
227227
apply_inv_pivot_rows!(N, column_pivots)
228228
end

src/structural_transformation/bipartite_tearing/modia_tearing.jl

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1-
# This code is from the Modia project and is licensed as follows:
1+
# This code is derived from the Modia project and is licensed as follows:
22
# https://github.com/ModiaSim/Modia.jl/blob/b61daad643ef7edd0c1ccce6bf462c6acfb4ad1a/LICENSE
33

4-
################################################
5-
#
6-
# Functions to tear systems of equations
7-
#
8-
# Author: Martin Otter, DLR-SR (first version: Jan. 14, 2017)
9-
#
10-
# Details are described in the paper:
11-
# Otter, Elmqvist (2017): Transformation of Differential Algebraic Array Equations to
12-
# Index One Form. Modelica'2017 Conference.
13-
#
14-
################################################
15-
164
function try_assign_eq!(ict::IncrementalCycleTracker, vj::Integer, eq::Integer)
175
G = ict.graph
186
add_edge_checked!(ict, Iterators.filter(!=(vj), 𝑠neighbors(G.graph, eq)), vj) do G
@@ -21,19 +9,6 @@ function try_assign_eq!(ict::IncrementalCycleTracker, vj::Integer, eq::Integer)
219
end
2210
end
2311

24-
"""
25-
(eSolved, vSolved, eResidue, vTear) = tearEquations!(td, Gsolvable, es, vs)
26-
27-
Equations es shall be solved with respect to variables vs. The function returns
28-
the teared equation so that if vTear is given, vSolved can be computed from eSolved
29-
in a forward sequence (so solving eSolved[1] for vSolved[1], eSolved[2] for vSolved[2],
30-
and so on). vTear must be selected, so that the equations eResidues are fulfilled.
31-
Equations es are the union of eSolved and eResidue.
32-
Variables vs are the union of vSolved and vTear.
33-
34-
Gsolvable defines the variables that can be explicitly solved in every equation without influencing the solution space
35-
(= rank preserving operation).
36-
"""
3712
function tearEquations!(ict::IncrementalCycleTracker, Gsolvable, es::Vector{Int}, vs::Vector{Int})
3813
G = ict.graph
3914
vActive = BitSet(vs)
@@ -59,13 +34,8 @@ function tear_graph_block_modia!(var_eq_matching, graph, solvable_graph, eqs, va
5934
return nothing
6035
end
6136

62-
"""
63-
tear_graph_modia(sys) -> sys
64-
65-
Tear the bipartite graph in a system. End users are encouraged to call [`structural_simplify`](@ref)
66-
instead, which calls this function internally.
67-
"""
68-
function tear_graph_modia(graph::BipartiteGraph, solvable_graph::BipartiteGraph; varfilter=v->true, eqfilter=eq->true)
37+
function tear_graph_modia(structure::SystemStructure; varfilter=v->true, eqfilter=eq->true)
38+
@unpack graph, solvable_graph = structure
6939
var_eq_matching = complete(maximal_matching(graph, eqfilter, varfilter))
7040
var_sccs::Vector{Union{Vector{Int}, Int}} = find_var_sccs(graph, var_eq_matching)
7141

src/structural_transformation/codegen.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,11 @@ function ODAEProblem{iip}(
524524
parammap = DiffEqBase.NullParameters();
525525
callback = nothing,
526526
use_union = false,
527+
check = true,
527528
kwargs...
528529
) where {iip}
530+
eqs = equations(sys)
531+
check && ModelingToolkit.check_operator_variables(eqs, Differential)
529532
fun, dvs = build_torn_function(sys; kwargs...)
530533
ps = parameters(sys)
531534
defs = defaults(sys)
@@ -535,7 +538,7 @@ function ODAEProblem{iip}(
535538
u0 = ModelingToolkit.varmap_to_vars(u0map, dvs; defaults=defs, tofloat=true)
536539
p = ModelingToolkit.varmap_to_vars(parammap, ps; defaults=defs, tofloat=!use_union, use_union)
537540

538-
has_difference = any(isdifferenceeq, equations(sys))
541+
has_difference = any(isdifferenceeq, eqs)
539542
if has_continuous_events(sys)
540543
event_cb = generate_rootfinding_callback(sys; kwargs...)
541544
else

src/structural_transformation/partial_state_selection.jl

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,98 @@ function partial_state_selection_graph!(structure::SystemStructure, var_eq_match
139139

140140
var_eq_matching
141141
end
142+
143+
function dummy_derivative_graph!(state::TransformationState, jac=nothing)
144+
var_eq_matching = complete(pantelides!(state))
145+
complete!(state.structure)
146+
dummy_derivative_graph!(state.structure, var_eq_matching, jac)
147+
end
148+
149+
function dummy_derivative_graph!(structure::SystemStructure, var_eq_matching, jac)
150+
@unpack eq_to_diff, var_to_diff, graph = structure
151+
diff_to_eq = invview(eq_to_diff)
152+
diff_to_var = invview(var_to_diff)
153+
invgraph = invview(graph)
154+
155+
neqs = nsrcs(graph)
156+
eqlevel = zeros(Int, neqs)
157+
maxlevel = 0
158+
for i in 1:neqs
159+
level = 0
160+
eq = i
161+
while diff_to_eq[eq] !== nothing
162+
eq = diff_to_eq[eq]
163+
level += 1
164+
end
165+
maxlevel = max(maxlevel, level)
166+
eqlevel[i] = level
167+
end
168+
169+
nvars = ndsts(graph)
170+
varlevel = zeros(Int, nvars)
171+
for i in 1:nvars
172+
level = 0
173+
var = i
174+
while diff_to_var[var] !== nothing
175+
var = diff_to_var[var]
176+
level += 1
177+
end
178+
maxlevel = max(maxlevel, level)
179+
varlevel[i] = level
180+
end
181+
182+
var_sccs = find_var_sccs(graph, var_eq_matching)
183+
eqcolor = falses(neqs)
184+
dummy_derivatives = Int[]
185+
col_order = Int[]
186+
for vars in var_sccs
187+
eqs = [var_eq_matching[var] for var in vars if var_eq_matching[var] !== unassigned]
188+
isempty(eqs) && continue
189+
maxlevel = maximum(map(x->eqlevel[x], eqs))
190+
iszero(maxlevel) && continue
191+
192+
rank_matching = Matching(nvars)
193+
for level in maxlevel:-1:1
194+
eqs = filter(eq->diff_to_eq[eq] !== nothing, eqs)
195+
nrows = length(eqs)
196+
iszero(nrows) && break
197+
eqs_set = BitSet(eqs)
198+
199+
# TODO: making the algorithm more robust
200+
# 1. If the Jacobian is a integer matrix, use Bareiss to check
201+
# linear independence. (done)
202+
#
203+
# 2. If the Jacobian is a single row, generate pivots. (Dynamic
204+
# state selection.)
205+
#
206+
# 3. If the Jacobian is a polynomial matrix, use Gröbner basis (?)
207+
if jac !== nothing && (_J = jac(eqs, vars); all(x->unwrap(x) isa Integer, _J))
208+
J = Int.(unwrap.(_J))
209+
N = ModelingToolkit.nullspace(J; col_order) # modifies col_order
210+
rank = length(col_order)-size(N, 2)
211+
for i in 1:rank
212+
push!(dummy_derivatives, vars[col_order[i]])
213+
end
214+
else
215+
rank = 0
216+
for var in vars
217+
pathfound = construct_augmenting_path!(rank_matching, invgraph, var, eq->eq in eqs_set, eqcolor)
218+
pathfound || continue
219+
push!(dummy_derivatives, var)
220+
rank += 1
221+
rank == nrows && break
222+
end
223+
fill!(rank_matching, unassigned)
224+
end
225+
if rank != nrows
226+
@warn "The DAE system is structurally singular!"
227+
end
228+
229+
# prepare the next iteration
230+
eqs = map(eq->diff_to_eq[eq], eqs)
231+
vars = [diff_to_var[var] for var in vars if diff_to_var[var] !== nothing]
232+
end
233+
end
234+
235+
dummy_derivatives
236+
end

src/structural_transformation/symbolics_tearing.jl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ function tearing(state::TearingState; kwargs...)
248248
@unpack graph, solvable_graph = state.structure
249249
algvars = BitSet(findall(v->isalgvar(state.structure, v), 1:ndsts(graph)))
250250
aeqs = algeqs(state.structure)
251-
var_eq_matching = Matching{Union{Unassigned, SelectedState}}(tear_graph_modia(graph, solvable_graph;
251+
var_eq_matching = Matching{Union{Unassigned, SelectedState}}(tear_graph_modia(state.structure;
252252
varfilter=var->var in algvars, eqfilter=eq->eq in aeqs))
253253
for var in 1:ndsts(graph)
254254
if isdiffvar(state.structure, var)
@@ -282,3 +282,21 @@ function partial_state_selection(sys; simplify=false)
282282

283283
tearing_reassemble(state, var_eq_matching; simplify=simplify)
284284
end
285+
286+
"""
287+
dummy_derivative(sys)
288+
289+
Perform index reduction and use the dummy derivative techinque to ensure that
290+
the system is balanced.
291+
"""
292+
function dummy_derivative(sys, state=TearingState(sys))
293+
function jac(eqs, vars)
294+
symeqs = EquationsView(state)[eqs]
295+
Symbolics.jacobian((x->x.rhs).(symeqs), state.fullvars[vars])
296+
end
297+
dds = dummy_derivative_graph!(state, jac)
298+
symdds = Symbolics.diff2term.(state.fullvars[dds])
299+
subs = Dict(state.fullvars[dd] => symdds[i] for (i, dd) in enumerate(dds))
300+
@set! sys.eqs = substitute.(EquationsView(state), (subs,))
301+
@set! sys.states = [states(sys); symdds]
302+
end

0 commit comments

Comments
 (0)