Skip to content

Commit 44d6eda

Browse files
committed
update explaination
1 parent 815e120 commit 44d6eda

File tree

1 file changed

+154
-38
lines changed

1 file changed

+154
-38
lines changed

docs/examples/init_tutorial.jl

Lines changed: 154 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
1-
# Tutorial on stepwise initialization of a complex model
1+
#=
2+
# Tutorial on Stepwise Initialization of a Complex Model
3+
4+
This example demonstrates how to initialize a complex network model with both static
5+
and dynamic components. We'll create a simple gas network model with three nodes
6+
and pipes connecting them, and show how to:
7+
8+
1. Create static models for initialization
9+
2. Find a steady-state solution
10+
3. Create corresponding dynamic models
11+
4. Initialize the dynamic models with the steady-state solution
12+
5. Simulate the system with dynamic behavior
13+
14+
This script can be downloaded as a normal Julia script [here](@__NAME__.jl). #md
15+
16+
First, let's import the necessary packages:
17+
=#
218

319
using NetworkDynamics
420
using ModelingToolkit
521
using ModelingToolkit: t_nounits as t, D_nounits as D
622
using OrdinaryDiffEqTsit5
723
using CairoMakie
24+
nothing #hide
825

26+
#=
927
## Node Models
10-
# template for commont states and equations in all gas nodes
28+
29+
We'll start by defining our node models using ModelingToolkit.
30+
First, let's create a template for common states and equations in all gas nodes:
31+
=#
1132
@mtkmodel GasNode begin
1233
@variables begin
1334
p(t), [description="Pressure"] # node output
@@ -20,7 +41,11 @@ using CairoMakie
2041
end
2142
nothing #hide
2243

23-
# node that forces pressure to be constant
44+
#=
45+
Now we'll define three specific node types:
46+
47+
1. A constant pressure node that forces pressure to maintain a specific value
48+
=#
2449
@mtkmodel ConstantPressureNode begin
2550
@extend GasNode()
2651
@parameters begin
@@ -32,7 +57,9 @@ nothing #hide
3257
end
3358
nothing #hide
3459

35-
# node which forces a certain flow (pressure fully implicit)
60+
#=
61+
2. A static prosumer node which forces a certain flow (pressure is fully implicit)
62+
=#
3663
@mtkmodel StaticProsumerNode begin
3764
@extend GasNode()
3865
@parameters begin
@@ -44,7 +71,9 @@ nothing #hide
4471
end
4572
nothing #hide
4673

47-
# dynamic prosumer node with compliance
74+
#=
75+
3. A dynamic prosumer node with compliance, which adds dynamics to the pressure state
76+
=#
4877
@mtkmodel DynamicProsumerNode begin
4978
@extend GasNode()
5079
@parameters begin
@@ -57,7 +86,9 @@ nothing #hide
5786
end
5887
nothing #hide
5988

60-
# node with a pressure controller
89+
#=
90+
4. A pressure control node that tries to maintain a set pressure by adjusting its injection
91+
=#
6192
@mtkmodel PressureControlNode begin
6293
@extend GasNode()
6394
@parameters begin
@@ -80,8 +111,11 @@ nothing #hide
80111
end
81112
nothing #hide
82113

83-
# ## Edge Models
84-
# template for pipe
114+
#=
115+
## Edge Models
116+
117+
Now we'll define our edge models, starting with a template for the pipe:
118+
=#
85119
@mtkmodel GasPipe begin
86120
@variables begin
87121
(t), [description="flow through pipe"] #output
@@ -91,7 +125,9 @@ nothing #hide
91125
end
92126
nothing #hide
93127

94-
# pipe with a simple delayed model
128+
#=
129+
Next, we define a dynamic pipe with inertia (a simple delayed model):
130+
=#
95131
@mtkmodel DynamicPipe begin
96132
@extend GasPipe()
97133
@parameters begin
@@ -104,7 +140,10 @@ nothing #hide
104140
end
105141
nothing #hide
106142

107-
# quasistatic pipe model for initialization (equals delayed model in steady state!)
143+
#=
144+
And finally a quasistatic pipe model for initialization purposes. This equals the
145+
dynamic model in steady state, making it ideal for finding initial conditions:
146+
=#
108147
@mtkmodel QuasistaticPipe begin
109148
@extend GasPipe()
110149
@parameters begin
@@ -117,36 +156,56 @@ end
117156
nothing #hide
118157

119158
#=
120-
## Define a static model
121-
=#
159+
## Defining a Static Model for Initialization
122160
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
161+
Our first step is to define a static model that we'll use to find the steady-state solution.
162+
This is a crucial step for initializing complex dynamic models.
163+
164+
Step 1: Define all the components of our static model
165+
First, node 1 is our producer which will later be a controlled producer. For initialization, we use a static model:
166+
=#
125167
@named v1_mod_static = ConstantPressureNode(p_set=1)
126168
v1_static = VertexModel(v1_mod_static, [:q̃_nw], [:p], vidx=1)
127169

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)
170+
## Nodes 2 and 3 are consumers. For them, we'll use static prosumer models:
171+
@named v2_mod_static = StaticProsumerNode(q̃_prosumer=-0.6) # consumer
172+
v2_static = VertexModel(v2_mod_static, [:q̃_nw], [:p], vidx=2)
131173

132174
@named v3_mod_static = StaticProsumerNode(q̃_prosumer=-0.4) # consumer
133-
v3_static = VertexModel(v3_mod, [:q̃_nw], [:p], vidx=3)
175+
v3_static = VertexModel(v3_mod_static, [:q̃_nw], [:p], vidx=3)
176+
nothing #hide
134177

135-
# static pipes
178+
#=
179+
Now we define the static pipe models connecting our nodes:
180+
=#
136181
@named p_mod_static = QuasistaticPipe()
137182
p12_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=2)
138183
p13_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=3)
139184
p23_static = EdgeModel(p_mod_static, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=2, dst=3)
140-
185+
nothing #hide
186+
#=
187+
Assemble all components into a static network:
188+
=#
141189
nw_static = Network([v1_static, v2_static, v3_static], [p12_static, p13_static, p23_static])
142190

191+
#=
192+
Create an initial guess for the steady state and modify it with reasonable values:
193+
=#
143194
u_static_guess = NWState(nw_static)
144195
u_static_guess.v[2, :p] = 1.0
145196
u_static_guess.v[3, :p] = 1.0
197+
nothing #hide
146198

199+
#=
200+
Find the steady-state solution using our initial guess:
201+
=#
147202
u_static = find_fixpoint(nw_static, u_static_guess)
148203

149-
# ## Define a dynamic model
204+
#=
205+
## Defining a Dynamic Model
206+
207+
Now we'll define our dynamic model using more complex components:
208+
=#
150209
@named v1_mod_dyn = PressureControlNode(;p_set=1)
151210
v1_dyn = VertexModel(v1_mod_dyn, [:q̃_nw], [:p], vidx=1)
152211

@@ -155,53 +214,104 @@ v2_dyn = VertexModel(v2_mod_dyn, [:q̃_nw], [:p], vidx=2)
155214

156215
@named v3_mod_dyn = DynamicProsumerNode(q̃_prosumer=-0.4)
157216
v3_dyn = VertexModel(v3_mod_dyn, [:q̃_nw], [:p], vidx=3)
217+
nothing #hide
158218

219+
#=
220+
Create dynamic pipe models with inertia:
221+
=#
159222
@named p_mod_dyn = DynamicPipe()
160223
p12_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=2)
161224
p13_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=1, dst=3)
162225
p23_dyn = EdgeModel(p_mod_dyn, [:p_src], [:p_dst], AntiSymmetric([:q̃]), src=2, dst=3)
226+
nothing #hide
163227

228+
#=
229+
Assemble the dynamic network:
230+
=#
164231
nw_dyn = Network([v1_dyn, v2_dyn, v3_dyn], [p12_dyn, p13_dyn, p23_dyn])
165232

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
233+
#=
234+
## Initializing the Dynamic Model with the Static Solution
235+
236+
Now comes the important part: we need to initialize the interface values (pressures and flows)
237+
of the dynamic model with the results from the static model.
238+
239+
First, let's handle node 1 (the pressure control node):
240+
=#
168241

169242
set_default!(nw_dyn[VIndex(1)], :p, u_static.v[1, :p])
170243
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
244+
v1_dyn # hide
245+
#=
246+
In the output, you can see that state ξ is "approx" 1 (guess value) while the rest is fixed.
247+
Now we can initialize the dynamic model's internal states:
248+
=#
174249
initialize_component!(v1_dyn)
175-
v1_dyn
250+
v1_dyn #hide
176251

177-
# the ther two vertices are simplere, the we need t oset the default values
252+
#=
253+
For the other two vertices (which are simpler), we just need to set the default values:
254+
=#
178255
set_default!(nw_dyn[VIndex(2)], :p, u_static.v[2, :p])
179256
set_default!(nw_dyn[VIndex(3)], :p, u_static.v[3, :p])
257+
nothing #hide
180258

181-
# we can manually "initialize" the only open state of the dynamic line model
259+
#=
260+
For the pipe models, we manually initialize the flow state of the dynamic line model:
261+
=#
182262
set_default!(nw_dyn[EIndex(1)], :q̃, u_static.e[1, :q̃])
183263
set_default!(nw_dyn[EIndex(2)], :q̃, u_static.e[2, :q̃])
184264
set_default!(nw_dyn[EIndex(3)], :q̃, u_static.e[3, :q̃])
265+
nothing #hide
185266

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
267+
#=
268+
Now that we've set all the "default" values for all the states, we can call `NWState` on the
269+
network to get a fully initialized state vector:
270+
=#
188271
u0_dyn = NWState(nw_dyn)
189272

273+
#=
274+
Let's verify that our initialization is correct by checking that the derivatives are close to zero:
275+
=#
190276
du = ones(dim(nw_dyn))
191277
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!
278+
extrema(du .- zeros(dim(nw_dyn))) # very close to zero, confirming we have a steady state!
193279

194-
# now we can solve the dynamic model
280+
#=
281+
## Simulating the Dynamic Model
282+
283+
Now we can solve the dynamic model and add a disturbance to see how the system responds:
284+
=#
195285
affect = ComponentAffect([], [:q̃_prosumer]) do u, p, ctx
196-
@info "Increas consumer at t=$(ctx.t)"
286+
@info "Increase consumer demand at t=$(ctx.t)"
197287
p[:q̃_prosumer] -= 0.1
198288
end
199289
cb = PresetTimeComponentCallback([1.0], affect)
200-
set_callback!(nw_dyn[VIndex(2)], cb) # attach disturabnce to second node
290+
set_callback!(nw_dyn[VIndex(2)], cb) # attach disturbance to second node
291+
nothing #hide
201292

293+
#=
294+
Create and solve the ODE problem with the callback:
295+
=#
202296
prob = ODEProblem(nw_dyn, copy(uflat(u0_dyn)), (0, 7), copy(pflat(u0_dyn));
203297
callback=get_callbacks(nw_dyn))
204298
sol = solve(prob, Tsit5())
299+
nothing #hide
300+
301+
#=
302+
## Visualizing the Results
303+
304+
Finally, let's visualize the results of our simulation.
305+
The plots show how our gas network responds to the increased consumer demand at t=1:
306+
307+
1. **Pressure at nodes**: We see a pressure drop at all nodes after the disturbance before the pressure is stabilized by the controller.
308+
309+
2. **Injection by producer**: Node 1 increases its injection to compensate for the higher demand.
310+
311+
3. **Draw by consumers**: The solid lines show the actual flows at nodes 2 and 3, while the dashed lines show the set consumer demands. At t=1, we see the step change in consumer demand at node 2.
312+
313+
4. **Flows through pipes**: Shows how the flows in all pipes adjust to the new demand pattern.
314+
=#
205315

206316
let
207317
fig = Figure(size=(1000,1000))
@@ -227,7 +337,13 @@ let
227337
fig
228338
end
229339

340+
#=
341+
## Interactive Visualization
230342
231-
# btw have you tried the GUi yet? :)
232-
# using NetworkDynamicsInspector
233-
# inspect(sol)
343+
You can also visualize the results interactively using NetworkDynamicsInspector:
344+
345+
```julia
346+
using NetworkDynamicsInspector
347+
inspect(sol)
348+
```
349+
=#

0 commit comments

Comments
 (0)