Skip to content

Commit 1b8f638

Browse files
authored
Merge pull request #160 from SciML/bgc/hyd_fixes
Added `Valve` component and `minimum_volume` feature to the `DynamicVolume` component
2 parents fe49350 + 9a09986 commit 1b8f638

File tree

4 files changed

+156
-85
lines changed

4 files changed

+156
-85
lines changed

src/Hydraulic/IsothermalCompressible/IsothermalCompressible.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ Library to model iso-thermal compressible liquid fluid flow
33
"""
44
module IsothermalCompressible
55

6-
using ModelingToolkit, Symbolics
6+
using ModelingToolkit, Symbolics, IfElse
77
using ...Blocks: RealInput, RealOutput
88
using ...Mechanical.Translational: MechanicalPort
99

1010
@parameters t
1111
D = Differential(t)
1212

13-
export HydraulicPort
13+
export HydraulicPort, HydraulicFluid
1414
include("utils.jl")
1515

16-
export Source, InputSource, FixedVolume, Pipe
16+
export Source, InputSource, Cap, Pipe, FixedVolume, DynamicVolume
1717
include("components.jl")
1818

1919
end

src/Hydraulic/IsothermalCompressible/components.jl

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ Pipe modeled with `N` segements which models the fully developed flow friction a
226226
end
227227

228228
"""
229-
DynamicVolume(; p_int, x_int=0, area, dead_volume=0, direction=+1, name)
229+
DynamicVolume(; p_int, x_int=0, area, dead_volume=0, direction=+1, minimum_volume=0, name)
230230
231231
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.
232232
```
@@ -242,17 +242,18 @@ dm ────► dead volume │ │ area
242242
```
243243
244244
# 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)
245+
- `p_int`: [Pa] initial pressure
246+
- `x_int`: [m] initial position of the moving wall
247+
- `area`: [m^2] moving wall area
248+
- `dead_volume`: [m^3] perimeter of the pipe cross section
249+
- `minimum_volume`: [m^3] if `x`*`area` <= `minimum_volume` then mass flow `dm` shuts off
249250
250251
# Connectors:
251252
- `port`: hydraulic port
252253
- `flange`: mechanical translational port
253254
"""
254255
@component function DynamicVolume(; p_int, x_int = 0, area, dead_volume = 0, direction = +1,
255-
name)
256+
minimum_volume = 0, name)
256257
@assert (direction == +1)||(direction == -1) "direction arument must be +/-1, found $direction"
257258

258259
pars = @parameters begin
@@ -272,17 +273,62 @@ dm ────► dead volume │ │ area
272273
dx(t) = 0
273274
rho(t) = density(port, p_int)
274275
drho(t) = 0
276+
vol(t) = dead_volume + area * x_int
277+
p(t) = p_int
275278
end
276279

277-
# let -------------
278-
vol = dead_volume + area * x
279-
280-
eqs = [D(x) ~ dx
280+
eqs = [0 ~ IfElse.ifelse(vol >= minimum_volume, p - port.p, port.dm)
281+
vol ~ dead_volume + area * x
282+
D(x) ~ dx
281283
D(rho) ~ drho
282284
dx ~ flange.v * direction
283-
rho ~ density(port, port.p)
285+
rho ~ density(port, p)
284286
port.dm ~ drho * vol + rho * area * dx
285-
flange.f ~ -port.p * area * direction]
287+
flange.f ~ -p * area * direction] #TODO: update to dynamic pressure
286288

287-
ODESystem(eqs, t, vars, pars; name, systems)
289+
ODESystem(eqs, t, vars, pars; name, systems, defaults = [flange.v => 0])
290+
end
291+
292+
"""
293+
Valve(; p_a_int, p_b_int, area_int, Cd, name)
294+
295+
Valve with area input and discharge coefficient `Cd` defined by https://en.wikipedia.org/wiki/Discharge_coefficient
296+
297+
# Parameters:
298+
- `p_a_int`: [Pa] initial pressure for `port_a`
299+
- `p_b_int`: [Pa] initial pressure for `port_b`
300+
- `area_int`: [m^2] initial valve opening
301+
- `Cd`: discharge coefficient
302+
303+
# Connectors:
304+
- `port_a`: hydraulic port
305+
- `port_b`: hydraulic port
306+
- `input`: real input setting the valve `area`. Note: absolute value taken
307+
"""
308+
@component function Valve(; p_a_int, p_b_int, area_int, Cd, name)
309+
pars = @parameters begin
310+
p_a_int = p_a_int
311+
p_b_int = p_b_int
312+
area_int = area_int
313+
Cd = Cd
314+
end
315+
316+
systems = @named begin
317+
port_a = HydraulicPort(; p_int = p_a_int)
318+
port_b = HydraulicPort(; p_int = p_b_int)
319+
input = RealInput()
320+
end
321+
322+
vars = []
323+
324+
# let
325+
ρ = (density(port_a, port_a.p) + density(port_b, port_b.p)) / 2
326+
Δp = port_a.p - port_b.p
327+
dm = port_a.dm
328+
area = abs(input.u)
329+
330+
eqs = [sign(Δp) * dm ~ sqrt(2 * abs(Δp) * ρ / Cd) * area
331+
0 ~ port_a.dm + port_b.dm]
332+
333+
ODESystem(eqs, t, vars, pars; name, systems, defaults = [input.u => area_int])
288334
end

test/Hydraulic/isothermal_compressible.jl

Lines changed: 91 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import ModelingToolkitStandardLibrary.Mechanical.Translational as T
66
@parameters t
77
D = Differential(t)
88

9-
function system(N; bulk_modulus, name)
9+
# ------------------------------------------------------------
10+
# Test fluid domain and Pipe
11+
function System(N; bulk_modulus, name)
1012
pars = @parameters begin bulk_modulus = bulk_modulus end
1113

1214
systems = @named begin
@@ -32,9 +34,9 @@ function system(N; bulk_modulus, name)
3234
ODESystem(eqs, t, [], pars; name, systems)
3335
end
3436

35-
@named sys1_2 = system(1; bulk_modulus = 2e9)
36-
@named sys1_1 = system(1; bulk_modulus = 1e9)
37-
@named sys5_1 = system(5; bulk_modulus = 1e9)
37+
@named sys1_2 = System(1; bulk_modulus = 2e9)
38+
@named sys1_1 = System(1; bulk_modulus = 1e9)
39+
@named sys5_1 = System(5; bulk_modulus = 1e9)
3840

3941
NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 100, relax = 4 // 10)
4042

@@ -53,77 +55,97 @@ s5_1 = complete(sys5_1)
5355
# N=5 pipe is compressible, will pressurize more slowly
5456
@test sols[2][s1_1.vol.port.p][end] > sols[3][s5_1.vol.port.p][end]
5557

56-
# using CairoMakie
57-
# fig = Figure()
58-
# ax = Axis(fig[1,1])
59-
# lines!(ax, sols[1].t, sols[1][s1_2.src.port.p]; label="sorce", color=:black)
60-
# # lines!(ax, sols[2].t, sols[2][s1_1.src.port.p])
61-
# scatterlines!(ax, sols[1].t, sols[1][s1_2.vol.port.p]; label="bulk=2e9")
62-
# scatterlines!(ax, sols[2].t, sols[2][s1_1.vol.port.p]; label="bulk=1e9")
63-
# scatterlines!(ax, sols[3].t, sols[3][s5_1.vol.port.p]; label="bulk=1e9, N=5")
64-
# Legend(fig[1,2], ax)
65-
# fig
66-
67-
function system(; bulk_modulus, name)
68-
pars = @parameters begin bulk_modulus = bulk_modulus end
58+
# ------------------------------------------------------------
59+
# Test the Valve
60+
function System(; name)
61+
pars = []
6962

7063
systems = @named begin
71-
fluid = IC.HydraulicFluid(; bulk_modulus)
72-
stp = B.Step(; height = 10e5, offset = 0, start_time = 0.05, duration = Inf,
73-
smooth = 0.001)
74-
src = IC.InputSource(; p_int = 0)
75-
vol1 = IC.DynamicVolume(; p_int = 0, area = 0.01, direction = +1)
76-
vol2 = IC.DynamicVolume(; p_int = 0, area = 0.01, direction = -1, x_int = 1.0)
77-
mass = T.Mass(; m = 1000, s_0 = 0)
78-
res = IC.PipeBase(; p_int = 0, area = 0.001, length = 50.0)
79-
cap = IC.Cap(; p_int = 0)
64+
fluid = IC.HydraulicFluid()
65+
sink = IC.Source(; p = 10e5)
66+
vol = IC.FixedVolume(; vol = 0.1, p_int = 100e5)
67+
valve = IC.Valve(; p_a_int = 10e5, p_b_int = 100e5, area_int = 0, Cd = 1e6)
68+
ramp = B.Ramp(; height = 1, duration = 0.001, offset = 0, start_time = 0.001,
69+
smooth = true)
8070
end
8171

82-
eqs = [connect(stp.output, src.input)
83-
connect(fluid, src.port, cap.port)
84-
connect(src.port, res.port_a)
85-
connect(res.port_b, vol1.port)
86-
connect(vol1.flange, mass.flange, vol2.flange)
87-
connect(vol2.port, cap.port)]
72+
eqs = [connect(fluid, sink.port)
73+
connect(sink.port, valve.port_a)
74+
connect(valve.port_b, vol.port)
75+
connect(valve.input, ramp.output)]
8876

8977
ODESystem(eqs, t, [], pars; name, systems)
9078
end
9179

92-
@named sys1 = system(; bulk_modulus = 1e9)
93-
@named sys2 = system(; bulk_modulus = 2e9)
80+
@named valve_system = System()
81+
s = complete(valve_system)
82+
sys = structural_simplify(valve_system)
83+
prob = ODEProblem(sys, [], (0, 0.01))
84+
sol = solve(prob, ImplicitEuler(nlsolve = NEWTON); adaptive = false, dt = 1e-4,
85+
initializealg = NoInit())
9486

95-
syss = structural_simplify.([sys1, sys2])
96-
probs = [ODEProblem(sys, [], (0, 0.2)) for sys in syss];
97-
dt = 1e-4
98-
sols = [solve(prob, ImplicitEuler(nlsolve = NEWTON); adaptive = false, dt,
99-
initializealg = NoInit()) for prob in probs];
100-
101-
s1 = complete(sys1)
102-
s2 = complete(sys2)
103-
104-
# less stiff will compress more
105-
@test maximum(sols[1][s1.mass.s]) > maximum(sols[2][s2.mass.s])
106-
107-
# fig = Figure()
108-
# ax = Axis(fig[1,1])
109-
# lines!(ax, sols[1].t, sols[1][s1.src.port.p]; label="sorce", color=:black)
110-
# # lines!(ax, sols[2].t, sols[2][s1_1.src.port.p])
111-
# scatterlines!(ax, sols[1].t, sols[1][s1.vol1.port.p]; label="bulk=1e9")
112-
# scatterlines!(ax, sols[2].t, sols[2][s2.vol1.port.p]; label="bulk=2e9")
113-
# Legend(fig[1,2], ax)
114-
# fig
115-
116-
# fig = Figure()
117-
# ax = Axis(fig[1,1])
118-
# scatterlines!(ax, sols[1].t, sols[1][s1.mass.s]; label="bulk=1e9")
119-
# scatterlines!(ax, sols[2].t, sols[2][s2.mass.s]; label="bulk=2e9")
120-
# Legend(fig[1,2], ax)
121-
# fig
122-
123-
# fig = Figure()
124-
# ax = Axis(fig[1,1])
125-
# lines!(ax, sols[1].t, sols[1][s1.vol1.dx]; label="bulk=2e9")
126-
# lines!(ax, sols[1].t, sols[1][s1.vol2.dx]; label="bulk=2e9")
127-
# lines!(ax, sols[1].t, sols[1][s1.mass.v]; label="bulk=2e9")
128-
# Legend(fig[1,2], ax)
129-
# fig
87+
# the volume should discharge to 10bar
88+
@test sol[s.vol.port.p][end] 10e5
89+
90+
# ------------------------------------------------------------
91+
# Test DynmaicVolume and minimum_volume feature
92+
function System(; name)
93+
pars = []
94+
95+
# DynamicVolume values
96+
area = 0.01
97+
length = 0.1
98+
99+
systems = @named begin
100+
fluid = IC.HydraulicFluid()
101+
src1 = IC.InputSource(; p_int = 10e5)
102+
src2 = IC.InputSource(; p_int = 10e5)
103+
104+
vol1 = IC.DynamicVolume(; p_int = 10e5, area, direction = +1, dead_volume = 2e-4,
105+
minimum_volume = 2e-4)
106+
vol2 = IC.DynamicVolume(; p_int = 10e5, area, direction = -1, dead_volume = 2e-4,
107+
minimum_volume = 2e-4, x_int = length)
108+
109+
mass = T.Mass(; m = 100)
110+
111+
sin1 = B.Sine(; frequency = 0.5, amplitude = +1e5, offset = 10e5)
112+
sin2 = B.Sine(; frequency = 0.5, amplitude = -1e5, offset = 10e5)
113+
end
114+
115+
eqs = [connect(fluid, src1.port, src2.port)
116+
connect(src1.port, vol1.port)
117+
connect(src2.port, vol2.port)
118+
connect(vol1.flange, mass.flange, vol2.flange)
119+
connect(src1.input, sin1.output)
120+
connect(src2.input, sin2.output)]
121+
122+
ODESystem(eqs, t, [], pars; name, systems)
123+
end
124+
125+
@named min_vol_system = System()
126+
s = complete(min_vol_system)
127+
sys = structural_simplify(min_vol_system)
128+
prob = ODEProblem(sys, [], (0, 5.0))
129+
130+
sol = solve(prob, ImplicitEuler(nlsolve = NEWTON); adaptive = false, dt = 1e-4,
131+
initializealg = NoInit())
132+
133+
# begin
134+
# fig = Figure()
135+
136+
# ax = Axis(fig[1,1], ylabel="position [m]", xlabel="time [s]")
137+
# lines!(ax, sol.t, sol[s.vol1.x]; label="vol1")
138+
# lines!(ax, sol.t, sol[s.vol2.x]; label="vol2")
139+
140+
# ax = Axis(fig[2,1], ylabel="pressure [bar]", xlabel="time [s]")
141+
# lines!(ax, sol.t, sol[s.vol1.port.p]/1e5; label="vol1")
142+
# lines!(ax, sol.t, sol[s.vol2.port.p]/1e5; label="vol2")
143+
# Legend(fig[2,2], ax)
144+
# fig
145+
# end
146+
147+
# volume/mass should stop moving at opposite ends
148+
@test round(sol[s.vol1.x][1]; digits = 2) == 0.0
149+
@test round(sol[s.vol2.x][1]; digits = 2) == 0.1
150+
@test round(sol[s.vol1.x][end]; digits = 2) == 0.1
151+
@test round(sol[s.vol2.x][end]; digits = 2) == 0.0

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ using SafeTestsets
2424
@safetestset "Mechanical Rotation" begin include("Mechanical/rotational.jl") end
2525
@safetestset "Mechanical Translation" begin include("Mechanical/translational.jl") end
2626
@safetestset "Multi-Domain" begin include("multi_domain.jl") end
27+
28+
# Hydraulic
29+
@safetestset "Hydraulic IsothermalCompressible" begin include("Hydraulic/isothermal_compressible.jl") end

0 commit comments

Comments
 (0)