Skip to content

Commit 24257bb

Browse files
committed
update import functions
1 parent 4e94312 commit 24257bb

File tree

4 files changed

+172
-41
lines changed

4 files changed

+172
-41
lines changed

ext/NetworkDynamicsMTKExt.jl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,26 @@ the symbol vector in
9999
- `Directed(dstout)`.
100100
101101
Additional `kwargs` are the same as for the double-sided EdgeModel MTK constructor.
102+
103+
Instead of wrapping, you can also provide the annotation as keyword argument like `annotation=:AntiSymmetric`.
102104
"""
103-
EdgeModel(sys::ODESystem, srcin, dstin, dstout; kwargs...) = EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
105+
function EdgeModel(sys::ODESystem, srcin, dstin, dstout::AnnotatedSym; kwargs...)
106+
EdgeModel(sys, srcin, dstin, nothing, dstout; kwargs...)
107+
end
108+
function EdgeModel(sys::ODESystem, srcin, dstin, dstout; annotation=nothing, kwargs...)
109+
_dstout = if annotation==:AntiSymmetric
110+
AntiSymmetric(dstout)
111+
elseif annotation==:Symmetric
112+
Symmetric(dstout)
113+
elseif annotation==:Directed
114+
Directed(dstout)
115+
else
116+
throw(ArgumentError("When constructing single sided EdgeModel, you must either wrap \
117+
the output syms (e.g. AntiSymmetric(dstout)) orprovide the annotation as keyword \
118+
argument."))
119+
end
120+
EdgeModel(sys, srcin, dstin, nothing, _dstout; kwargs...)
121+
end
104122

105123
"""
106124
EdgeModel(sys::ODESystem, srcin, srcout, dstin, dstout;

src/importexport.jl

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,34 @@ function depth_first_flatten!(container::Union{OrderedDict,Vector})
103103
nothing
104104
end
105105

106+
widen_container_type(container::OrderedDict{K}) where {K} = convert(OrderedDict{K,Any}, container)
107+
widen_container_type(container::Vector) = convert(Vector{Any}, container)
108+
109+
replace_in_template!(template, placeholder, model) = template
110+
function replace_in_template!(container::Union{OrderedDict,Vector}, placeholder, model)
111+
container = widen_container_type(container)
112+
for (key, value) in pairs(container)
113+
if value == placeholder
114+
container[key] = deepcopy(model)
115+
else
116+
container[key] = replace_in_template!(value, placeholder, model)
117+
end
118+
end
119+
container
120+
end
121+
122+
apply_wrappers!(::Nothing) = nothing
123+
function apply_wrappers!(container::Union{OrderedDict,Vector})
124+
haskey(container, "WRAPPER") || return
125+
126+
wrapper = container["WRAPPER"]
127+
delete!(container, "WRAPPER")
128+
for (key, sub) in pairs(container)
129+
container[key] = replace_in_template!(wrapper, "MODEL", sub)
130+
end
131+
container
132+
end
133+
106134
function parse_network(file)
107135
data = YAML.load_file(file, dicttype=OrderedDict{Any,Any})
108136
dependencies = OrderedDict(keys(data["Models"]) .=> map(collect_model_dependencies, values(data["Models"])))
@@ -131,9 +159,13 @@ function parse_network(file)
131159
error("Could not resolve all dependencies")
132160
end
133161

162+
# apply wrappers to all models
163+
apply_wrappers!(data["VertexModels"])
164+
apply_wrappers!(data["EdgeModels"])
165+
134166
# with all deps resolve, we can replace the models edge and vertex list
135-
recursive_resolve!(data["Models"], data["Vertices"])
136-
recursive_resolve!(data["Models"], data["Edges"])
167+
recursive_resolve!(data["Models"], data["VertexModels"])
168+
recursive_resolve!(data["Models"], data["EdgeModels"])
137169

138170
# traverses the tree and flattens the model properties, i.e. merges
139171
# the local given properties with the parent/template model properties
@@ -144,24 +176,12 @@ end
144176
recursive_construct(constructors, data) = _depth_first_construct!(constructors, deepcopy(data))
145177
_depth_first_construct!(_, data) = data
146178
_depth_first_construct!(_, s::String) = startswith(s, ":") ? Symbol(s[2:end]) : s
147-
function _depth_first_construct!(constructors, data::Vector)
148-
if data isa Vector && eltype(data) !== Any
149-
data = convert(Vector{Any}, data)
150-
end
151-
for (key, value) in pairs(data)
152-
data[key] = _depth_first_construct!(constructors, value)
153-
end
154-
data
155-
end
156-
function _depth_first_construct!(constructors, data::OrderedDict)
157-
if data isa OrderedDict && eltype(values(data)) !== Any
158-
data = convert(OrderedDict{eltype(keys(data)), Any}, data)
159-
end
160-
179+
function _depth_first_construct!(constructors, data::Union{OrderedDict,Vector})
180+
data = widen_container_type(data)
161181
for (key, value) in pairs(data)
162182
data[key] = _depth_first_construct!(constructors, value)
163183
end
164-
if haskey(data, "CONSTRUCTOR")
184+
if data isa OrderedDict && haskey(data, "CONSTRUCTOR")
165185
if !haskey(constructors, data["CONSTRUCTOR"])
166186
error("No constructor found for $(data["CONSTRUCTOR"])")
167187
end
@@ -178,21 +198,26 @@ end
178198

179199
function build_network(data, constructors)
180200
vertexm = VertexModel[]
181-
for (k, v) in data["Vertices"]
201+
for (k, v) in data["VertexModels"]
182202
vm = recursive_construct(constructors, v)
183203
set_graphelement!(vm, k)
184204
push!(vertexm, vm)
185205
end
206+
186207
edgem = EdgeModel[]
187-
for (k, v) in data["Vertices"]
208+
for (k, v) in data["VertexModels"]
188209
vm = recursive_construct(constructors, v)
189210

190211
m = match(r"^(.*)=>(.*)$", k)
191212
isnothing(m) && error("Edge key must be of form 'src=>dst', got $k")
192213
src = tryparse(Int, m[1])
193214
dst = tryparse(Int, m[2])
194-
isnothing(src) && src = m[1]
195-
isnothing(dst) && src = m[1]
215+
if isnothing(src)
216+
src = m[1]
217+
end
218+
if isnothing(dst)
219+
dst = m[1]
220+
end
196221
set_graphelement!(vm, src, dst)
197222
push!(vertexm, vm)
198223
end

test/importexport_test.jl

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using NetworkDynamics
22
using ModelingToolkit
33
using ModelingToolkit: t_nounits as t, D_nounits as Dt
4+
using StableRNGs
45

56
@mtkmodel Swing begin
67
@variables begin
@@ -35,8 +36,8 @@ end
3536
end
3637

3738
@mtkmodel StaticPowerLine begin
38-
@structural_parameters begin
39-
K = 1.63 # structural parameter to keep them out of nw parameters
39+
@parameters begin
40+
K = 1.63, [description = "line conductance parameter"]
4041
end
4142
@variables begin
4243
srcθ(t), [description = "voltage angle at src end", input=true]
@@ -55,29 +56,59 @@ function CombinedModel(subsystems...; kwargs...)
5556
θ(t) = 0.0, [description = "voltage angle", output=true]
5657
Pel(t), [description = "Electical power flowing from network into node", input=true]
5758
end
58-
θ_eqs =~ sub.θ for sub in subsystes]
59-
Pel_eqs = [Pel ~ sub.Pel for sub in subsystems]
60-
ODESystem(; kwargs...)
59+
eqs = [
60+
~ sub.θ for sub in subsystems)...,
61+
Pel ~ +((sub.Pel for sub in subsystems)...) # kirchoff constaint
62+
]
63+
ODESystem(eqs, t, [θ, Pel], []; systems=collect(subsystems), kwargs...)
64+
end
65+
66+
v1 = let
67+
@named swing = Swing(; Pmech=1.0, M=1.0, D=1.0)
68+
VertexModel(swing, [:Pel], [], vidx=1)
69+
end
70+
71+
v2 = let
72+
@named load = Load(; Pd=-1.0)
73+
VertexModel(load, [:Pel], [], vidx=2)
74+
end
75+
76+
v3 = let
77+
@named swing = Swing(; Pmech=1.0, M=1.0, D=1.0)
78+
@named load = Load(; Pd=-2.0)
79+
@named swingload = CombinedModel(swing, load)
80+
VertexModel(swingload, [:Pel], [], vidx=3)
81+
end
82+
83+
v4 = let
84+
@named swing = Swing(; Pmech=2.0, M=1.0, D=1.0)
85+
@named load = Load(; Pd=-1.0)
86+
@named swingload2 = CombinedModel(swing, load)
87+
VertexModel(swingload2, [:Pel], [], vidx=4)
6188
end
6289

63-
mtk_models = [
64-
Swing(Pmech=1, M=1, D=1.0; name=:G1),
65-
Load(Pload; name=:L2),
66-
Swing(Pmech=1, M=1, D=1.0; name=:G2),
67-
Load(Pload; name=:L3),
68-
Swing(Pmech=1, M=1, D=1.0; name=:G3),
69-
Load(Pload; name=:L4),
70-
];
90+
e1 = let
91+
@named line = StaticPowerLine(; K=0.5)
92+
EdgeModel(line, [:srcθ], [:dstθ], [:P]; annotation=:AntiSymmetric, src=1, dst=2)
93+
end
94+
e2 = let
95+
@named line = StaticPowerLine(; K=1.0)
96+
EdgeModel(line, [:srcθ], [:dstθ], [:P]; annotation=:AntiSymmetric, src=2, dst=3)
97+
end
98+
e3 = let
99+
@named line = StaticPowerLine()
100+
EdgeModel(line, [:srcθ], [:dstθ], [:P]; annotation=:AntiSymmetric, src=3, dst=4)
101+
end
71102

72-
vertexmodels = map(mtk_models) do mtk_model
73-
VertexModel(mtk_model, [:Pel], [])
74-
end;
75-
vertexmodels2 = map(mtk_models2) do mtk_model
76-
VertexModel(mtk_model, [:Pel], [])
77-
end;
103+
nw = Network([v1,v2,v3,v4],[e1,e2,e3])
78104

105+
rng = StableRNG(1)
106+
u = randn(rng, dim(nw))
107+
du1 = [NaN for _ in 1:dim(nw)]
108+
nw(du1, u, pflat(NWState(nw)), NaN)
79109

80110
data = NetworkDynamics.parse_network("testgrid.yaml")
111+
data["VertexModels"][1]
81112

82113
constructors = Dict("Swing"=>Swing, "VertexModel"=>VertexModel, "Load"=>Load)
83114
NetworkDynamics.build_network(data, constructors)

test/testgrid.yaml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
title: Power Grid Example
2+
version: "1.0"
3+
description: 6 Bus Powergrid Toy Example
4+
5+
# special words are "MODEL", "CONSTRUCTOR", "ARGS", "WRAPPER"
6+
Models:
7+
Swing:
8+
CONSTRUCTOR: Swing
9+
name: :swing
10+
Pmech: 1
11+
M: 1
12+
D: 1.0
13+
14+
Load: # base models have known constructor
15+
CONSTRUCTOR: Load
16+
name: :load
17+
Pd: -1.0
18+
19+
DerivedLoad:
20+
MODEL: Models.Load
21+
Pd: 2
22+
23+
Swing_And_Load:
24+
CONSTRUCTOR: CombinedModel
25+
ARGS:
26+
- Models.Swing
27+
- MODEL: Models.Load
28+
Pd: 2
29+
name: "combined model"
30+
31+
DerivedSwing_And_Load:
32+
MODEL: Models.Swing_And_Load
33+
ARGS[1].Pd: 3
34+
35+
DerivedSwing_And_Load2:
36+
MODEL: Models.Swing_And_Load
37+
ARGS[1]:
38+
Pd: 4
39+
name: :foo
40+
41+
StaticPowerLine:
42+
CONSTRUCTOR: StaticPowerLine
43+
44+
VertexModels:
45+
WRAPPER:
46+
CONSTRUCTOR: VertexModel
47+
ARGS:
48+
- MODEL
49+
- [":Pel"]
50+
- [":θ"]
51+
1:
52+
MODEL: Models.Swing
53+
2:
54+
MODEL: Models.Load
55+
Pd: 1
56+
57+
EdgeModels:

0 commit comments

Comments
 (0)