Skip to content

Commit 5729f30

Browse files
authored
Merge pull request #182 from JuliaDynamics/hw/v092updates
fixes for minor release
2 parents cd31781 + c984e40 commit 5729f30

File tree

3 files changed

+68
-31
lines changed

3 files changed

+68
-31
lines changed

ext/MTKExt.jl

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using ModelingToolkit: Symbolic, iscall, operation, arguments, build_function
44
using ModelingToolkit: ModelingToolkit, Equation, ODESystem, Differential
55
using ModelingToolkit: full_equations, get_variables, structural_simplify, getname, unwrap
66
using ModelingToolkit: full_parameters, unknowns, independent_variables, observed, defaults
7-
using ModelingToolkit.Symbolics: Symbolics, fixpoint_sub
7+
using ModelingToolkit.Symbolics: Symbolics, fixpoint_sub, substitute
88
using RecursiveArrayTools: RecursiveArrayTools
99
using ArgCheck: @argcheck
1010
using LinearAlgebra: Diagonal, I
@@ -16,18 +16,20 @@ import NetworkDynamics: VertexModel, EdgeModel, AnnotatedSym
1616
include("MTKUtils.jl")
1717

1818
"""
19-
VertexModel(sys::ODESystem, inputs, outputs; kwargs...)
19+
VertexModel(sys::ODESystem, inputs, outputs; ff_to_constraint=true, kwargs...)
2020
2121
Create a `VertexModel` object from a given `ODESystem` created with ModelingToolkit.
2222
You need to provide 2 lists of symbolic names (`Symbol` or `Vector{Symbols}`):
2323
- `inputs`: names of variables in you equation representing the aggregated edge states
2424
- `outputs`: names of variables in you equation representing the node output
25+
26+
`ff_to_constraint` controlls, whether output transformations `g` which depend on inputs should be
2527
"""
26-
function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getname(sys), kwargs...)
28+
function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getname(sys), ff_to_constraint=true, kwargs...)
2729
warn_events(sys)
2830
inputs = inputs isa AbstractVector ? inputs : [inputs]
2931
outputs = outputs isa AbstractVector ? outputs : [outputs]
30-
gen = generate_io_function(sys, (inputs,), (outputs,); verbose)
32+
gen = generate_io_function(sys, (inputs,), (outputs,); verbose, ff_to_constraint)
3133

3234
f = gen.f
3335
g = gen.g
@@ -58,7 +60,7 @@ function VertexModel(sys::ODESystem, inputs, outputs; verbose=false, name=getnam
5860
end
5961

6062
"""
61-
EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); kwargs...)
63+
EdgeModel(sys::ODESystem, srcin, dstin, AntiSymmetric(dstout); ff_to_constraint=false, kwargs...)
6264
6365
Create a `EdgeModel` object from a given `ODESystem` created with ModelingToolkit.
6466
@@ -68,20 +70,26 @@ the symbol vector in
6870
- `AntiSymmetric(dstout)`,
6971
- `Symmetric(dstout)`, or
7072
- `Directed(dstout)`.
73+
74+
`ff_to_constraint` controlls, whether output transformations `g` which depend on inputs should be
75+
transformed into constraints.
7176
"""
7277
EdgeModel(sys::ODESystem, srcin, dstin, dstout; kwargs...) = EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
7378

7479
"""
75-
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; kwargs...)
80+
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout; ff_to_constraint=false, kwargs...)
7681
7782
Create a `EdgeModel` object from a given `ODESystem` created with ModelingToolkit.
7883
You need to provide 4 lists of symbolic names (`Symbol` or `Vector{Symbols}`):
7984
- `srcin`: names of variables in you equation representing the node state at the source
8085
- `dstin`: names of variables in you equation representing the node state at the destination
8186
- `srcout`: names of variables in you equation representing the output at the source
8287
- `dstout`: names of variables in you equation representing the output at the destination
88+
89+
`ff_to_constraint` controlls, whether output transformations `g` which depend on inputs should be
90+
transformed into constraints.
8391
"""
84-
function EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout; verbose=false, name=getname(sys), kwargs...)
92+
function EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout; verbose=false, name=getname(sys), ff_to_constraint=false, kwargs...)
8593
warn_events(sys)
8694
srcin = srcin isa AbstractVector ? srcin : [srcin]
8795
dstin = dstin isa AbstractVector ? dstin : [dstin]
@@ -103,7 +111,7 @@ function EdgeModel(sys::ODESystem, srcin, dstin, srcout, dstout; verbose=false,
103111
outs = (srcout, dstout)
104112
end
105113

106-
gen = generate_io_function(sys, (srcin, dstin), outs; verbose)
114+
gen = generate_io_function(sys, (srcin, dstin), outs; verbose, ff_to_constraint)
107115

108116
f = gen.f
109117
g = singlesided ? gwrap(gen.g; ff=gen.fftype) : gen.g
@@ -192,7 +200,8 @@ function _get_metadata(sys, name)
192200
end
193201

194202
function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
195-
expression=Val{false}, verbose=false)
203+
expression=Val{false}, verbose=false,
204+
ff_to_constraint)
196205
# TODO: scalarize vector symbolics/equations?
197206

198207
# f_* may be given in namepsace version or as symbols, resolve to unnamespaced Symbolic
@@ -240,21 +249,6 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
240249
# end
241250
throw(ArgumentError(String(take!(buf))))
242251
end
243-
# generate mass matrix (this might change the equations)
244-
mass_matrix = begin
245-
# equations of form o = f(...) have to be transformed to 0 = f(...) - o
246-
for (i, eq) in enumerate(eqs)
247-
if eq_type(eq)[1] == :explicit_algebraic
248-
eqs[i] = 0 ~ eq.rhs - eq.lhs
249-
end
250-
end
251-
verbose && @info "Transformed algebraic eqs" eqs
252-
253-
# create massmatrix, we don't use the method provided by ODESystem because of reordering
254-
mm = generate_massmatrix(eqs)
255-
verbose && @info "Reordered by states and generated mass matrix" mm
256-
mm
257-
end
258252

259253
# extract observed equations. They might depend on eachother so resolve them
260254
obs_subs = Dict(eq.lhs => eq.rhs for eq in observed(sys))
@@ -268,22 +262,64 @@ function generate_io_function(_sys, inputss::Tuple, outputss::Tuple;
268262
@warn "obs_deps !⊆ parameters ∪ unknowns. Difference: $(setdiff(obs_deps, Set(allparams) Set(states)))"
269263
end
270264

271-
# find the output equations, this might remove the mfrom obseqs!
272-
outeqs = map(Iterators.flatten(outputss)) do out
265+
# if some states shadow outputs (out ~ state in observed)
266+
# switch their names. I.e. prioritize use of name `out`
267+
renamings = Dict()
268+
for eq in obseqs
269+
if eq.lhs Set(alloutputs) && iscall(eq.rhs) &&
270+
operation(eq.rhs) isa Symbolics.BasicSymbolic && eq.rhs Set(states)
271+
verbose && @info "Encountered trivial equation $eq. Swap out $(eq.lhs) <=> $(eq.rhs) everywhere."
272+
renamings[eq.lhs] = eq.rhs
273+
renamings[eq.rhs] = eq.lhs
274+
end
275+
end
276+
if !isempty(renamings)
277+
eqs = map(eq -> substitute(eq, renamings), eqs)
278+
obseqs = map(eq -> substitute(eq, renamings), obseqs)
279+
states = map(s -> substitute(s, renamings), states)
280+
verbose && @info "New States:" states
281+
end
282+
283+
# find the output equations, this might remove them from obseqs!
284+
outeqs = Equation[]
285+
for out in Iterators.flatten(outputss)
273286
if out Set(states)
274-
out ~ out
287+
push!(outeqs, out ~ out)
275288
else
276289
idx = findfirst(eq -> isequal(eq.lhs, out), obseqs)
277290
if isnothing(idx)
278291
throw(ArgumentError("Output $out was neither foundin states nor in observed equations."))
279292
end
280293
eq = obseqs[idx]
281294
deleteat!(obseqs, idx)
282-
obseqs
283-
eq
295+
296+
if ff_to_constraint && !isempty(get_variables(eq.rhs) allinputs)
297+
verbose && @info "Output $out would lead to FF in g, promote to state instead."
298+
push!(eqs, 0 ~ eq.lhs - eq.rhs)
299+
push!(states, eq.lhs)
300+
push!(outeqs, eq.lhs ~ eq.lhs)
301+
else
302+
push!(outeqs, eq)
303+
end
284304
end
285305
end
286306

307+
# generate mass matrix (this might change the equations)
308+
mass_matrix = begin
309+
# equations of form o = f(...) have to be transformed to 0 = f(...) - o
310+
for (i, eq) in enumerate(eqs)
311+
if eq_type(eq)[1] == :explicit_algebraic
312+
eqs[i] = 0 ~ eq.rhs - eq.lhs
313+
end
314+
end
315+
verbose && @info "Transformed algebraic eqs" eqs
316+
317+
# create massmatrix, we don't use the method provided by ODESystem because of reordering
318+
mm = generate_massmatrix(eqs)
319+
verbose && @info "Generated mass matrix" mm
320+
mm
321+
end
322+
287323
iv = only(independent_variables(sys))
288324
out_deps = _all_rhs_symbols(outeqs)
289325
fftype = _determine_fftype(out_deps, states, allinputs, params, iv)

src/component_functions.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,8 @@ function ff_to_constraint(v::VertexModel)
11451145
isnothing(v.obsf) || @warn "Observed function might be broke due to ff_to_constraint conversion."
11461146
newsym = vcat(sym(v), outsym(v))
11471147
VertexModel(; f=newf, g=newg, sym=newsym, mass_matrix, name=v.name,
1148-
psym=v.psym, obsf=v.obsf, obssym=v.obssym, metadata=v.metadata, symmetadata=v.symmetadata)
1148+
psym=v.psym, obsf=v.obsf, obssym=v.obssym, insym=v.insym,
1149+
metadata=v.metadata, symmetadata=v.symmetadata)
11491150
end
11501151

11511152
function _newf(::PureFeedForward, f, g, dim)

src/initialization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function initialization_problem(cf::T; t=NaN, verbose=true) where {T<:ComponentM
156156
# this fills the second half of the du buffer with the fixed and current outputs
157157
dunl_out .= RecursiveArrayTools.ArrayPartition(outbufs...)
158158
# execute fg to fill dunl and outputs
159-
fg(outbufs..., dunl, ubuf, inbufs..., pbuf, t)
159+
fg(outbufs..., dunl_fg, ubuf, inbufs..., pbuf, t)
160160

161161
# calculate the residual for the second half ov the dunl buf, the outputs
162162
dunl_out .= dunl_out .- RecursiveArrayTools.ArrayPartition(outbufs...)

0 commit comments

Comments
 (0)