Skip to content

Commit 4b77847

Browse files
committed
start tut on initialization
1 parent 50adfeb commit 4b77847

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

docs/examples/initialization.jl

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# Tutorial on stepwise initialization of a complex model
2+
3+
using NetworkDynamics
4+
using ModelingToolkit
5+
using ModelingToolkit: t_nounits as t, D_nounits as D
6+
using OrdinaryDiffEqTsit5
7+
using CairoMakie
8+
9+
## Node Models
10+
# template for commont states and equations in all gas nodes
11+
@mtkmodel GasNode begin
12+
@variables begin
13+
p(t), [description="Pressure"] # node output
14+
q̃_nw(t), [description="aggregated flow from pipes into node"] # node input
15+
q̃_inj(t), [description="flow injected into the network"]
16+
end
17+
@equations begin
18+
q̃_inj ~ -q̃_nw
19+
end
20+
end
21+
nothing #hide
22+
23+
# node that forces pressure to be constant
24+
@mtkmodel ConstantPressureNode begin
25+
@extend GasNode()
26+
@parameters begin
27+
p_set, [description="Constant pressure setpoint"]
28+
end
29+
@equations begin
30+
p ~ p_set
31+
end
32+
end
33+
nothing #hide
34+
35+
# node which forces a certain flow (pressure fully implicit)
36+
@mtkmodel StaticProsumerNode begin
37+
@extend GasNode()
38+
@parameters begin
39+
q̃_prosumer, [description="flow injected by prosumer"]
40+
end
41+
@equations begin
42+
-q̃_nw ~ q̃_prosumer
43+
end
44+
end
45+
nothing #hide
46+
47+
# dynamic prosumer node with compliance
48+
@mtkmodel DynamicProsumerNode begin
49+
@extend GasNode()
50+
@parameters begin
51+
q̃_prosumer, [description="flow injected by prosumer"]
52+
C=0.1, [description="Compliance"]
53+
end
54+
@equations begin
55+
C*D(p) ~ q̃_prosumer + q̃_nw
56+
end
57+
end
58+
nothing #hide
59+
60+
# node with a pressure controller
61+
@mtkmodel PressureControlNode begin
62+
@extend GasNode()
63+
@parameters begin
64+
p_set, [description="Pressure setpoint", guess=1]
65+
K_p=1, [description="Proportional gain"]
66+
K_i=1, [description="Integral gain"]
67+
C=0.1, [description="Compliance"]
68+
end
69+
@variables begin
70+
Δp(t), [description="Pressure error"]
71+
ξ(t), [description="Integral state", guess=0]
72+
q̃_prosumer(t), [description="flow injected by producer"]
73+
end
74+
@equations begin
75+
Δp ~ p_set - p
76+
D(ξ) ~ Δp
77+
q̃_prosumer ~ K_p*Δp + K_i*ξ
78+
C*D(p) ~ q̃_prosumer + q̃_nw
79+
end
80+
end
81+
nothing #hide
82+
83+
# ## Edge Models
84+
# template for pipe
85+
@mtkmodel GasPipe begin
86+
@variables begin
87+
(t), [description="flow through pipe"] #output
88+
p_src(t), [description="pressure at start of pipe"] #input
89+
p_dst(t), [description="pressure at end of pipe"] #input
90+
end
91+
end
92+
nothing #hide
93+
94+
# pipe with a simple delayed model
95+
@mtkmodel DynamicPipe begin
96+
@extend GasPipe()
97+
@parameters begin
98+
R=0.1, [description="Resistance"]
99+
M=0.1, [description="Inertia"]
100+
end
101+
@equations begin
102+
M*D(q̃) ~ (p_src - p_dst)/R -# some simple delayed model
103+
end
104+
end
105+
nothing #hide
106+
107+
# quasistatic pipe model for initialization (equals delayed model in steady state!)
108+
@mtkmodel QuasistaticPipe begin
109+
@extend GasPipe()
110+
@parameters begin
111+
R=0.1, [description="Resistance"]
112+
end
113+
@equations begin
114+
~ (p_src - p_dst)/R
115+
end
116+
end
117+
nothing #hide
118+
119+
#=
120+
## Define a static model
121+
=#
122+
123+
# step 1: definition of a static model
124+
# node 1 is our producer which will later be a controlled producer. For initialization we use a static model
125+
@named v1_mod_static = ConstantPressureNode(p_set=1)
126+
v1_static = VertexModel(v1_mod_static, [:q̃_nw], [:p], vidx=1)
127+
128+
# node 2 and 3 are consumers, there is no difference between static and dynamic model for them
129+
@named v2_mod_static = StaticProsumerNode(q̃_prosumer=-0.6) # consumer, initialize pressure
130+
v2_static = VertexModel(v2_mod, [:q̃_nw], [:p], vidx=2)
131+
132+
@named v3_mod_static = StaticProsumerNode(q̃_prosumer=-0.4) # consumer
133+
v3_static = VertexModel(v3_mod, [:q̃_nw], [:p], vidx=3)
134+
135+
# static pipes
136+
@named p_mod_static = QuasistaticPipe()
137+
p12_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=2)
138+
p13_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=3)
139+
p23_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=2, dst=3)
140+
141+
nw_static = Network([v1_static, v2_static, v3_static], [p12_static, p13_static, p23_static])
142+
143+
u_static_guess = NWState(nw_static)
144+
u_static_guess.v[2, :p] = 1.0
145+
u_static_guess.v[3, :p] = 1.0
146+
147+
u_static = find_fixpoint(nw_static, u_static_guess)
148+
149+
# ## Define a dynamic model
150+
@named v1_mod_dyn = PressureControlNode(;p_set=1)
151+
v1_dyn = VertexModel(v1_mod_dyn, [:q̃_nw], [:p], vidx=1)
152+
153+
@named v2_mod_dyn = DynamicProsumerNode(q̃_prosumer=-0.6)
154+
v2_dyn = VertexModel(v2_mod_dyn, [:q̃_nw], [:p], vidx=2)
155+
156+
@named v3_mod_dyn = DynamicProsumerNode(q̃_prosumer=-0.4)
157+
v3_dyn = VertexModel(v3_mod_dyn, [:q̃_nw], [:p], vidx=3)
158+
159+
@named p_mod_dyn = DynamicPipe()
160+
p12_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=2)
161+
p13_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=3)
162+
p23_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=2, dst=3)
163+
164+
nw_dyn = Network([v1_dyn, v2_dyn, v3_dyn], [p12_dyn, p13_dyn, p23_dyn])
165+
166+
# no we need to do some magic. we want to initialize the interface values (pressures and flow)
167+
# of the dynamic model with the results of the static model
168+
169+
set_default!(nw_dyn[VIndex(1)], :p, u_static.v[1, :p])
170+
set_default!(nw_dyn[VIndex(1)], :q̃_nw, u_static.v[1, :q̃_nw])
171+
v1_dyn
172+
# in the output you can see now that state ξ is "approx" 1 (guess value) while the rest is fixed
173+
# so we can initialize the ydnamic model
174+
initialize_component!(v1_dyn)
175+
v1_dyn
176+
177+
# the ther two vertices are simplere, the we need t oset the default values
178+
set_default!(nw_dyn[VIndex(2)], :p, u_static.v[2, :p])
179+
set_default!(nw_dyn[VIndex(3)], :p, u_static.v[3, :p])
180+
181+
# we can manually "initialize" the only open state of the dynamic line model
182+
set_default!(nw_dyn[EIndex(1)], :q̃, u_static.e[1, :q̃])
183+
set_default!(nw_dyn[EIndex(2)], :q̃, u_static.e[2, :q̃])
184+
set_default!(nw_dyn[EIndex(3)], :q̃, u_static.e[3, :q̃])
185+
186+
# we have set all the "default" values for all teh states now. so we call `NWState` on the
187+
# network we should get a fully initialized state
188+
u0_dyn = NWState(nw_dyn)
189+
190+
du = ones(dim(nw_dyn))
191+
nw_dyn(du, uflat(u0_dyn), pflat(u0_dyn), 0.0)
192+
extrema(du .- zeros(dim(nw_dyn))) # very close to zero, is steady state!
193+
194+
# now we can solve the dynamic model
195+
affect = ComponentAffect([], [:q̃_prosumer]) do u, p, ctx
196+
@info "Increas consumer at t=$(ctx.t)"
197+
p[:q̃_prosumer] -= 0.1
198+
end
199+
cb = PresetTimeComponentCallback([1.0], affect)
200+
set_callback!(nw_dyn[VIndex(2)], cb) # attach disturabnce to second node
201+
202+
prob = ODEProblem(nw_dyn, copy(uflat(u0_dyn)), (0, 7), copy(pflat(u0_dyn));
203+
callback=get_callbacks(nw_dyn))
204+
sol = solve(prob, Tsit5())
205+
206+
let
207+
fig = Figure(size=(1000,1000))
208+
ax = Axis(fig[1, 1]; title="Pressure at nodes")
209+
for i in 1:3
210+
lines!(ax, sol; idxs=VIndex(i, :p), label="Node $i", color=Cycled(i))
211+
end
212+
213+
ax = Axis(fig[2, 1]; title="Injection by producer")
214+
lines!(ax, sol; idxs=VIndex(1, :q̃_inj), label="Node 1", color=Cycled(1))
215+
216+
ax = Axis(fig[3, 1]; title="Draw by consumers")
217+
for i in 2:3
218+
lines!(ax, sol; idxs=@obsex(-1*VIndex(i, :q̃_inj)), label="Node $i", color=Cycled(i))
219+
lines!(ax, sol; idxs=@obsex(-1*VIndex(i, :q̃_prosumer)), label="Node $i", linestyle=:dash, color=Cycled(i))
220+
end
221+
222+
ax = Axis(fig[4, 1]; title="Flows through pipes")
223+
for i in 1:3
224+
lines!(ax, sol; idxs=@obsex(abs(EIndex(i, :q̃))), label="Pipe $i", color=Cycled(i))
225+
end
226+
227+
fig
228+
end
229+
230+
231+
# btw have you tried the GUi yet? :)
232+
# using NetworkDynamicsInspector
233+
# inspect(sol)

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ kwargs = (;
6464
"Cascading Failure" => "generated/cascading_failure.md",
6565
"Stress on Truss" => "generated/stress_on_truss.md",
6666
"Gas Network" => "generated/gas_network.md",
67+
"Initialization" => "generated/initialization.md",
6768
]
6869
],
6970
draft=false,

0 commit comments

Comments
 (0)