Skip to content

Commit b8b9998

Browse files
authored
Merge pull request #159 from SciML/bgc/hydraulic
Hydraulic library
2 parents 1e267db + 069e7c5 commit b8b9998

File tree

8 files changed

+562
-7
lines changed

8 files changed

+562
-7
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
1111

1212
[compat]
1313
IfElse = "0.1"
14-
ModelingToolkit = "8.48"
14+
ModelingToolkit = "8.50"
1515
Symbolics = "4.9, 5"
1616
julia = "1.6"
1717

src/Hydraulic/Hydraulic.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Library of hydrualic models.
3+
"""
4+
module Hydraulic
5+
6+
using ModelingToolkit
7+
8+
include("IsothermalCompressible/IsothermalCompressible.jl")
9+
10+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
Library to model iso-thermal compressible liquid fluid flow
3+
"""
4+
module IsothermalCompressible
5+
6+
using ModelingToolkit, Symbolics
7+
using ...Blocks: RealInput, RealOutput
8+
using ...Mechanical.Translational: MechanicalPort
9+
10+
@parameters t
11+
D = Differential(t)
12+
13+
export HydraulicPort
14+
include("utils.jl")
15+
16+
export Source, InputSource, FixedVolume, Pipe
17+
include("components.jl")
18+
19+
end
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
"""
2+
Source(; p, name)
3+
4+
Fixed pressure source
5+
6+
# Parameters:
7+
- `p`: [Pa] set pressure (set by `p` argument)
8+
9+
# Connectors:
10+
- `port`: hydraulic port
11+
"""
12+
@component function Source(; p, name)
13+
pars = @parameters begin p = p end
14+
15+
vars = []
16+
17+
systems = @named begin port = HydraulicPort(; p_int = p) end
18+
19+
eqs = [
20+
port.p ~ p,
21+
]
22+
23+
ODESystem(eqs, t, vars, pars; name, systems)
24+
end
25+
26+
"""
27+
InputSource(; p_int, name)
28+
29+
Fixed pressure source
30+
31+
# Parameters:
32+
- `p_int`: [Pa] initial pressure (set by `p_int` argument)
33+
34+
# Connectors:
35+
- `port`: hydraulic port
36+
"""
37+
@component function InputSource(; p_int, name)
38+
pars = @parameters begin p_int = p_int end
39+
40+
vars = []
41+
42+
systems = @named begin
43+
port = HydraulicPort(; p_int)
44+
input = RealInput()
45+
end
46+
47+
eqs = [
48+
port.p ~ input.u,
49+
]
50+
51+
ODESystem(eqs, t, vars, pars; name, systems)
52+
end
53+
54+
"""
55+
Cap(; p_int, name)
56+
57+
Caps a hydrualic port to prevent mass flow in or out.
58+
59+
# Parameters:
60+
- `p_int`: [Pa] initial pressure (set by `p_int` argument)
61+
62+
# Connectors:
63+
- `port`: hydraulic port
64+
"""
65+
@component function Cap(; p_int, name)
66+
pars = @parameters p_int = p_int
67+
68+
vars = @variables p(t) = p_int
69+
70+
systems = @named begin port = HydraulicPort(; p_int = p_int) end
71+
72+
eqs = [port.p ~ p
73+
port.dm ~ 0]
74+
75+
ODESystem(eqs, t, vars, pars; name, systems)
76+
end
77+
78+
"""
79+
FixedVolume(; vol, p_int, name)
80+
81+
Fixed fluid volume.
82+
83+
# Parameters:
84+
- `vol`: [m^3] fixed volume
85+
- `p_int`: [Pa] initial pressure
86+
87+
# Connectors:
88+
- `port`: hydraulic port
89+
"""
90+
@component function FixedVolume(; vol, p_int, name)
91+
pars = @parameters begin
92+
vol = vol
93+
p_int = p_int
94+
end
95+
96+
systems = @named begin port = HydraulicPort(; p_int) end
97+
98+
vars = @variables begin
99+
rho(t) = density(port, p_int)
100+
drho(t) = 0
101+
end
102+
103+
# let -------------------
104+
dm = port.dm
105+
106+
eqs = [D(rho) ~ drho
107+
rho ~ density(port, port.p)
108+
dm ~ drho * vol]
109+
110+
ODESystem(eqs, t, vars, pars; name, systems)
111+
end
112+
113+
"""
114+
PipeBase(; p_int, area, length, perimeter=2*sqrt(area*pi), shape_factor=64, name)
115+
116+
Pipe segement which models purely the fully developed flow friction, ignoring any compressibility.
117+
118+
# Parameters:
119+
- `p_int`: [Pa] initial pressure (set by `p_int` argument)
120+
- `area`: [m^2] tube cross sectional area (set by `area` argument)
121+
- `length`: [m] length of the pipe (set by `length` argument)
122+
- `perimeter`: [m] perimeter of the pipe cross section (set by optional `perimeter` argument, needed only for non-circular pipes)
123+
- `Φ`: shape factor, see `friction_factor` function (set by optional `shape_factor` argument, needed only for non-circular pipes).
124+
125+
# Connectors:
126+
- `port_a`: hydraulic port
127+
- `port_b`: hydraulic port
128+
"""
129+
@component function PipeBase(; p_int, area, length, perimeter = 2 * sqrt(area * pi),
130+
shape_factor = 64, name)
131+
pars = @parameters begin
132+
p_int = p_int
133+
area = area
134+
length = length
135+
perimeter = perimeter
136+
Φ = shape_factor
137+
end
138+
139+
vars = []
140+
141+
systems = @named begin
142+
port_a = HydraulicPort(; p_int)
143+
port_b = HydraulicPort(; p_int)
144+
end
145+
146+
# let ----------------------
147+
Δp = port_a.p - port_b.p
148+
dm = port_a.dm
149+
150+
d_h = 4 * area / perimeter
151+
152+
ρ = (density(port_a, port_a.p) + density(port_b, port_b.p)) / 2
153+
μ = viscosity(port_a)
154+
155+
f = friction_factor(dm, area, d_h, ρ, μ, Φ)
156+
u = dm /* area)
157+
158+
eqs = [Δp ~ 1 / 2 * ρ * u^2 * f * (length / d_h)
159+
0 ~ port_a.dm + port_b.dm]
160+
161+
ODESystem(eqs, t, vars, pars; name, systems)
162+
end
163+
164+
"""
165+
Pipe(N; p_int, area, length, perimeter=2*sqrt(area*pi), shape_factor=64, name)
166+
167+
Pipe modeled with `N` segements which models the fully developed flow friction and compressibility.
168+
169+
# Parameters:
170+
- `p_int`: [Pa] initial pressure (set by `p_int` argument)
171+
- `area`: [m^2] tube cross sectional area (set by `area` argument)
172+
- `length`: [m] length of the pipe (set by `length` argument)
173+
- `perimeter`: [m] perimeter of the pipe cross section (set by optional `perimeter` argument, needed only for non-circular pipes)
174+
- `Φ`: shape factor, see `friction_factor` function (set by optional `shape_factor` argument, needed only for non-circular pipes).
175+
176+
# Connectors:
177+
- `port_a`: hydraulic port
178+
- `port_b`: hydraulic port
179+
"""
180+
@component function Pipe(N; p_int, area, length, perimeter = 2 * sqrt(area * pi),
181+
shape_factor = 64, name)
182+
@assert(N>1,
183+
"the pipe component must be defined with more than 1 segment (i.e. N>1), found N=$N")
184+
185+
pars = @parameters begin
186+
p_int = p_int
187+
area = area
188+
length = length
189+
perimeter = perimeter
190+
Φ = shape_factor
191+
end
192+
193+
vars = []
194+
195+
ports = @named begin
196+
port_a = HydraulicPort(; p_int)
197+
port_b = HydraulicPort(; p_int)
198+
end
199+
200+
pipe_bases = []
201+
for i in 1:(N - 1)
202+
x = PipeBase(; name = Symbol("p$i"), shape_factor = ParentScope(Φ),
203+
p_int = ParentScope(p_int), area = ParentScope(area),
204+
length = ParentScope(length) / (N - 1),
205+
perimeter = ParentScope(perimeter))
206+
push!(pipe_bases, x)
207+
end
208+
209+
volumes = []
210+
for i in 1:N
211+
x = FixedVolume(; name = Symbol("v$i"),
212+
vol = ParentScope(area) * ParentScope(length) / N,
213+
p_int = ParentScope(p_int))
214+
push!(volumes, x)
215+
end
216+
217+
eqs = [connect(volumes[1].port, pipe_bases[1].port_a, port_a)
218+
connect(volumes[end].port, pipe_bases[end].port_b, port_b)]
219+
220+
for i in 2:(N - 1)
221+
eq = connect(volumes[i].port, pipe_bases[i - 1].port_b, pipe_bases[i].port_a)
222+
push!(eqs, eq)
223+
end
224+
225+
ODESystem(eqs, t, vars, pars; name, systems = [ports; pipe_bases; volumes])
226+
end
227+
228+
"""
229+
DynamicVolume(; p_int, x_int=0, area, dead_volume=0, direction=+1, name)
230+
231+
Volume with moving wall. The `direction` argument aligns the mechanical port with the hydraulic port, useful when connecting two dynamic volumes together in oppsing directions to create an actuator.
232+
```
233+
┌─────────────────┐ ───
234+
│ │ ▲
235+
│ │
236+
dm ────► dead volume │ │ area
237+
│ │
238+
│ │ ▼
239+
└─────────────────┤ ───
240+
241+
└─► x (= flange.v * direction)
242+
```
243+
244+
# Parameters:
245+
- `p_int`: [Pa] initial pressure (set by `p_int` argument)
246+
- `x_int`: [m] initial position of the moving wall (set by the `x_int` argument)
247+
- `area`: [m^2] moving wall area (set by the `area` argument)
248+
- `dead_volume`: [m^3] perimeter of the pipe cross section (set by optional `perimeter` argument, needed only for non-circular pipes)
249+
250+
# Connectors:
251+
- `port`: hydraulic port
252+
- `flange`: mechanical translational port
253+
"""
254+
@component function DynamicVolume(; p_int, x_int = 0, area, dead_volume = 0, direction = +1,
255+
name)
256+
@assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction"
257+
258+
pars = @parameters begin
259+
p_int = p_int
260+
x_int = x_int
261+
area = area
262+
dead_volume = dead_volume
263+
end
264+
265+
systems = @named begin
266+
port = HydraulicPort(; p_int)
267+
flange = MechanicalPort()
268+
end
269+
270+
vars = @variables begin
271+
x(t) = x_int
272+
dx(t) = 0
273+
rho(t) = density(port, p_int)
274+
drho(t) = 0
275+
end
276+
277+
# let -------------
278+
vol = dead_volume + area * x
279+
280+
eqs = [D(x) ~ dx
281+
D(rho) ~ drho
282+
dx ~ flange.v * direction
283+
rho ~ density(port, port.p)
284+
port.dm ~ drho * vol + rho * area * dx
285+
flange.f ~ -port.p * area * direction]
286+
287+
ODESystem(eqs, t, vars, pars; name, systems)
288+
end

0 commit comments

Comments
 (0)