Skip to content

Commit 5078d9d

Browse files
authored
Merge pull request #175 from SciML/bgc/negative_pressure
Bgc/negative pressure
2 parents a63c15f + 4500270 commit 5078d9d

File tree

7 files changed

+167
-28
lines changed

7 files changed

+167
-28
lines changed

docs/make.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ using ModelingToolkitStandardLibrary.Magnetic
77
using ModelingToolkitStandardLibrary.Magnetic.FluxTubes
88
using ModelingToolkitStandardLibrary.Electrical
99
using ModelingToolkitStandardLibrary.Thermal
10+
using ModelingToolkitStandardLibrary.Hydraulic
11+
using ModelingToolkitStandardLibrary.Hydraulic.IsothermalCompressible
1012

1113
cp("./docs/Manifest.toml", "./docs/src/assets/Manifest.toml", force = true)
1214
cp("./docs/Project.toml", "./docs/src/assets/Project.toml", force = true)
@@ -33,7 +35,9 @@ makedocs(sitename = "ModelingToolkitStandardLibrary.jl",
3335
ModelingToolkitStandardLibrary.Magnetic,
3436
ModelingToolkitStandardLibrary.Magnetic.FluxTubes,
3537
ModelingToolkitStandardLibrary.Electrical,
36-
ModelingToolkitStandardLibrary.Thermal],
38+
ModelingToolkitStandardLibrary.Thermal,
39+
ModelingToolkitStandardLibrary.Hydraulic,
40+
ModelingToolkitStandardLibrary.Hydraulic.IsothermalCompressible],
3741
format = Documenter.HTML(assets = ["assets/favicon.ico"],
3842
canonical = "https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/"),
3943
pages = pages)

docs/pages.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pages = [
1414
"Magnetic Components" => "API/magnetic.md",
1515
"Mechanical Components" => "API/mechanical.md",
1616
"Thermal Components" => "API/thermal.md",
17+
"Hydraulic Components" => "API/hydraulic.md",
1718
"Linear Analysis" => "API/linear_analysis.md",
1819
],
1920
]

docs/src/API/hydraulic.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# ModelingToolkit Standard Library: Hydrualic Components
2+
3+
```@contents
4+
Pages = ["hydraulic.md"]
5+
Depth = 3
6+
```
7+
8+
## Index
9+
10+
```@index
11+
Pages = ["hydraulic.md"]
12+
```
13+
14+
## IsothermalCompressible Components
15+
16+
```@meta
17+
CurrentModule = ModelingToolkitStandardLibrary.Hydraulic.IsothermalCompressible
18+
```
19+
20+
### IsothermalCompressible Utils
21+
22+
```@docs
23+
HydraulicPort
24+
HydraulicFluid
25+
friction_factor
26+
```
27+
28+
### IsothermalCompressible Components
29+
30+
```@docs
31+
Cap
32+
TubeBase
33+
Tube
34+
FixedVolume
35+
DynamicVolume
36+
```
37+
38+
### IsothermalCompressible Sources
39+
40+
```@docs
41+
MassFlow
42+
Pressure
43+
FixedPressure
44+
```

docs/src/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ The following are the constituant libraries of the ModelingToolkit Standard Libr
2828
- [Electrical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/electrical/)
2929
- [Magnetic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/magnetic/)
3030
- [Thermal Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/thermal/)
31+
- [Hydraulic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/hydraulic/)
3132

3233
## Contributing
3334

src/Hydraulic/IsothermalCompressible/components.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ end
4242
"""
4343
TubeBase(add_inertia = true; p_int, area, length_int, head_factor = 1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name)
4444
45-
Variable length internal flow model of the fully developed flow friction, ignoring any compressibility. Includes optional inertia equation when `add_inertia = true` to model wave propogation which includes change in flow and length terms.
45+
Variable length internal flow model of the fully developed incompressible flow friction. Includes optional inertia term when `add_inertia = true` to model wave propagation. Hydraulic ports have equal flow but variable pressure. Density is averaged over the pressures, used to calculated average flow velocity and flow friction.
46+
47+
# States:
48+
- `x`: [m] length of the pipe
49+
- `ddm`: [kg/s^2] Rate of change of mass flow rate in control volume.
4650
4751
# Parameters:
4852
- `p_int`: [Pa] initial pressure
@@ -104,7 +108,7 @@ Variable length internal flow model of the fully developed flow friction, ignori
104108
end
105109

106110
eqs = [0 ~ port_a.dm + port_b.dm
107-
Δp ~ ifelse(x > 0, shear + inertia, 0)]
111+
Δp ~ ifelse(x > 0, shear + inertia, zero(x))]
108112

109113
if add_inertia
110114
push!(eqs, D(dm) ~ ddm)
@@ -116,7 +120,7 @@ end
116120
"""
117121
Tube(N, add_inertia=true; p_int, area, length, head_factor=1, perimeter = 2 * sqrt(area * pi), shape_factor = 64, name)
118122
119-
Constant length internal flow model with volume discretized by `N` which models the fully developed flow friction, compressibility, and inertia effects when `add_inertia = true`
123+
Constant length internal flow model discretized by `N` (`FixedVolume`: `N`, `TubeBase`:`N-1`) which models the fully developed flow friction, compressibility (when `N>1`), and inertia effects when `add_inertia = true`. See `TubeBase` and `FixedVolume` for more information.
120124
121125
# Parameters:
122126
- `p_int`: [Pa] initial pressure
@@ -258,7 +262,10 @@ end
258262
port_b = HydraulicPort(; p_int = p_b_int)
259263
end
260264

261-
vars = @variables begin area(t) = area_int end
265+
vars = @variables begin
266+
area(t) = area_int
267+
y(t) = area_int
268+
end
262269

263270
# let
264271
ρ = (full_density(port_a) + full_density(port_b)) / 2
@@ -275,7 +282,8 @@ end
275282
Cd = ifelse(Δp > 0, Cd, Cd_reverse)
276283

277284
eqs = [0 ~ port_a.dm + port_b.dm
278-
dm ~ regRoot(2 * Δp * ρ / Cd) * x]
285+
dm ~ regRoot(2 * Δp * ρ / Cd) * x
286+
y ~ x]
279287

280288
ODESystem(eqs, t, vars, pars; name, systems)
281289
end
@@ -459,6 +467,7 @@ dm ────► │ │ area
459467
# Valve
460468
Cd = 1e2,
461469
Cd_reverse = Cd,
470+
minimum_area = 0,
462471
name)
463472
@assert(N>0,
464473
"the Tube component must be defined with more than 1 segment (i.e. N>1), found N=$N")
@@ -482,15 +491,16 @@ dm ────► │ │ area
482491

483492
Cd = Cd
484493
Cd_reverse = Cd_reverse
494+
minimum_area = minimum_area
485495
end
486496

487497
vars = @variables x(t)=x_int vol(t)=x_int * area
488498

489499
ports = @named begin
490500
port = HydraulicPort(; p_int)
491501
flange = MechanicalPort()
492-
damper = ValveBase(true; p_a_int = p_int, p_b_int = p_int, area_int = 1, Cd,
493-
Cd_reverse)
502+
damper = ValveBase(; p_a_int = p_int, p_b_int = p_int, area_int = 1, Cd,
503+
Cd_reverse, minimum_area)
494504
end
495505

496506
pipe_bases = []
@@ -519,7 +529,7 @@ dm ────► │ │ area
519529
Δx,
520530
ifelse(x₀ - Δx * (i - 1) > 0,
521531
x₀ - Δx * (i - 1),
522-
0))
532+
zero(Δx)))
523533

524534
comp = VolumeBase(; name = Symbol("v$i"), p_int = ParentScope(p_int), x_int = 0,
525535
area = ParentScope(area),
@@ -529,9 +539,11 @@ dm ────► │ │ area
529539
end
530540

531541
ratio = (x - x_min) / (x_damp - x_min)
532-
damper_area = ifelse(x >= x_damp, 1,
533-
ifelse((x < x_damp) &
534-
(x > x_min), ratio, 0))
542+
damper_area = ifelse(x >= x_damp,
543+
one(x),
544+
ifelse((x < x_damp) & (x > x_min),
545+
ratio,
546+
zero(x)))
535547

536548
eqs = [vol ~ x * area
537549
D(x) ~ flange.v * direction

src/Hydraulic/IsothermalCompressible/utils.jl

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Connector port for hydraulic components.
2020
β
2121
μ
2222
n
23+
let_gas
2324
ρ_gas
2425
p_gas
2526
end
@@ -35,25 +36,27 @@ end
3536
"""
3637
HydraulicFluid(; density=997, bulk_modulus=2.09e9, viscosity=0.0010016, name)
3738
38-
Fluid parameter setter for isothermal compressible fluid domain. Defaults given for water at 20°C and 0Pa gage (1atm absolute) reference pressure. Density is modeled using the Tait equation of state. For pressures below the reference pressure, density is linearly interpolated to the gas state, this helps prevent pressures from going below the reference pressure.
39+
Fluid parameter setter for isothermal compressible fluid domain. Defaults given for water at 20°C and 0Pa gage (1atm absolute) reference pressure. Density is modeled using the Tait equation of state. For pressures below the reference pressure, density is linearly interpolated to the gas state (when `let_gas` is set to 1), this helps prevent pressures from going below the reference pressure.
3940
4041
# Parameters:
4142
4243
- `ρ`: [kg/m^3] fluid density at 0Pa reference gage pressure (set by `density` argument)
4344
- `Β`: [Pa] fluid bulk modulus describing the compressibility (set by `bulk_modulus` argument)
4445
- `μ`: [Pa*s] or [kg/m-s] fluid dynamic viscosity (set by `viscosity` argument)
4546
- `n`: density exponent
47+
- `let_gas`: set to 1 to allow fluid to transition from liquid to gas (for density calculation only)
4648
- `ρ_gas`: [kg/m^3] density of fluid in gas state at reference gage pressure `p_gas` (set by `gas_density` argument)
4749
- `p_gas`: [Pa] reference pressure (set by `gas_pressure` argument)
4850
"""
4951
@connector function HydraulicFluid(; density = 997, bulk_modulus = 2.09e9,
5052
viscosity = 0.0010016, gas_density = 0.0073955,
51-
gas_pressure = -1000, n = 1, name)
53+
gas_pressure = -1000, n = 1, let_gas = 1, name)
5254
pars = @parameters begin
5355
ρ = density
5456
β = bulk_modulus
5557
μ = viscosity
5658
n = n
59+
let_gas = let_gas
5760
ρ_gas = gas_density
5861
p_gas = gas_pressure
5962
end
@@ -96,7 +99,7 @@ function friction_factor(dm, area, d_h, density, viscosity, shape_factor)
9699
Re = density * u * d_h / viscosity
97100
f_laminar = shape_factor * regPow(Re, -1, 1e-6)
98101

99-
Re = maximum([Re, 1])
102+
Re = max(Re, one(Re))
100103
f_turbulent = (shape_factor / 64) * (0.79 * log(Re) - 1.64)^(-2)
101104

102105
f = transition(2000, 3000, f_laminar, f_turbulent, Re)
@@ -120,15 +123,26 @@ function transition(x1, x2, y1, y2, x)
120123
end
121124

122125
density_ref(port) = port.ρ
126+
density_exp(port) = port.n
123127
gas_density_ref(port) = port.ρ_gas
124128
gas_pressure_ref(port) = port.p_gas
125129
bulk_modulus(port) = port.β
126130
viscosity(port) = port.μ
127-
liquid_density(port, p) = density_ref(port) * (1 + p / bulk_modulus(port))
131+
132+
function liquid_density(port, p)
133+
density_ref(port) *
134+
regPow(1 + density_exp(port) * p / bulk_modulus(port), 1 / density_exp(port))
135+
end #Tait-Murnaghan equation of state
128136
liquid_density(port) = liquid_density(port, port.p)
129137
function gas_density(port, p)
130-
density_ref(port) -
131-
p * (density_ref(port) - gas_density_ref(port)) / gas_pressure_ref(port)
138+
slope = (density_ref(port) - gas_density_ref(port)) / (0 - gas_pressure_ref(port))
139+
b = density_ref(port)
140+
141+
return b + p * slope
142+
end
143+
function full_density(port, p)
144+
ifelse(port.let_gas == 1,
145+
ifelse(p > 0, liquid_density(port, p), gas_density(port, p)),
146+
liquid_density(port, p))
132147
end
133-
full_density(port, p) = liquid_density(port, p) #ifelse( p > 0, liquid_density(port, p), gas_density(port, p) )
134148
full_density(port) = full_density(port, port.p)

test/Hydraulic/isothermal_compressible.jl

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using ModelingToolkitStandardLibrary.Blocks: Parameter
88
@parameters t
99
D = Differential(t)
1010

11-
NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 10, relax = 9 // 10)
11+
NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 100, relax = 9 // 10)
1212

1313
@testset "Fluid Domain and Tube" begin
1414
function System(N; bulk_modulus, name)
@@ -20,7 +20,7 @@ NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 10, relax = 9
2020
smooth = true)
2121
src = IC.Pressure(; p_int = 0)
2222
vol = IC.FixedVolume(; p_int = 0, vol = 10.0)
23-
res = IC.Tube(N; p_int = 0, area = 0.01, length = 500.0)
23+
res = IC.Tube(N; p_int = 0, area = 0.01, length = 50.0)
2424
end
2525

2626
eqs = [connect(stp.output, src.p)
@@ -36,8 +36,8 @@ NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 10, relax = 9
3636
@named sys5_1 = System(5; bulk_modulus = 1e9)
3737

3838
syss = structural_simplify.([sys1_2, sys1_1, sys5_1])
39-
probs = [ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.2))
40-
for sys in syss]
39+
probs = [ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.05))
40+
for sys in syss] #
4141
sols = [solve(prob, ImplicitEuler(nlsolve = NEWTON); initializealg = NoInit(),
4242
dt = 1e-4, adaptive = false)
4343
for prob in probs]
@@ -51,6 +51,15 @@ NEWTON = NLNewton(check_div = false, always_new = true, max_iter = 10, relax = 9
5151

5252
# N=5 pipe is compressible, will pressurize more slowly
5353
@test sols[2][s1_1.vol.port.p][end] > sols[3][s5_1.vol.port.p][end]
54+
55+
# fig = Figure()
56+
# ax = Axis(fig[1,1])
57+
# # hlines!(ax, 10e5)
58+
# lines!(ax, sols[1][s1_2.vol.port.p])
59+
# lines!(ax, sols[2][s1_1.vol.port.p])
60+
# lines!(ax, sols[3][s5_1.vol.port.p])
61+
# fig
62+
5463
end
5564

5665
@testset "Valve" begin
@@ -131,11 +140,16 @@ end
131140
@named system = System(N; damping_volume)
132141
s = complete(system)
133142
sys = structural_simplify(system)
134-
prob = ODEProblem(sys, [], (0, 5),
143+
prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 5),
135144
[s.vol1.Cd_reverse => 0.1, s.vol2.Cd_reverse => 0.1];
136145
jac = true)
137-
@time sol = solve(prob, ImplicitEuler(nlsolve = NEWTON); dt = 1e-4,
138-
adaptive = false, initializealg = NoInit())
146+
147+
@time sol = solve(prob,
148+
ImplicitEuler(nlsolve = NLNewton(check_div = false,
149+
always_new = true,
150+
max_iter = 10,
151+
relax = 9 // 10));
152+
dt = 0.0001, adaptive = false, initializealg = NoInit())
139153

140154
# begin
141155
# fig = Figure()
@@ -154,7 +168,7 @@ end
154168
# lines!(ax, sol.t, sol[s.vol1.damper.area]; label="area 1")
155169
# lines!(ax, sol.t, sol[s.vol2.damper.area]; label="area 2")
156170

157-
# fig
171+
# display(fig)
158172
# end
159173

160174
i1 = round(Int, 1 / 1e-4)
@@ -264,7 +278,8 @@ end
264278
sys = structural_simplify(system)
265279
defs = ModelingToolkit.defaults(sys)
266280
s = complete(system)
267-
prob = ODEProblem(sys, [], (0, 0.1); tofloat = false, jac = true)
281+
prob = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.1);
282+
tofloat = false, jac = true)
268283

269284
# check the fluid domain
270285
@test Symbol(defs[s.src.port.ρ]) == Symbol(s.fluid.ρ)
@@ -291,4 +306,52 @@ end
291306
@test sol[s.piston.x][end]0.06 atol=0.01
292307
end
293308

309+
@testset "Prevent Negative Pressure" begin
310+
@component function System(; name)
311+
pars = @parameters let_gas = 1
312+
313+
systems = @named begin
314+
fluid = IC.HydraulicFluid(; let_gas)
315+
vol = IC.DynamicVolume(5; p_int = 100e5, area = 0.001, x_int = 0.05,
316+
x_max = 0.1, x_damp = 0.02, x_min = 0.01, direction = +1)
317+
mass = T.Mass(; m = 100, g = -9.807, s_0 = 0.05)
318+
cap = IC.Cap(; p_int = 100e5)
319+
end
320+
321+
eqs = [connect(fluid, cap.port, vol.port)
322+
connect(vol.flange, mass.flange)]
323+
324+
ODESystem(eqs, t, [], pars; name, systems)
325+
end
326+
327+
@named system = System()
328+
s = complete(system)
329+
sys = structural_simplify(system)
330+
prob1 = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.05))
331+
prob2 = ODEProblem(sys, ModelingToolkit.missing_variable_defaults(sys), (0, 0.05),
332+
[s.let_gas => 0])
333+
334+
@time sol1 = solve(prob1, ImplicitEuler(nlsolve = NEWTON); adaptive = false, dt = 1e-4)
335+
@time sol2 = solve(prob2, Rodas4())
336+
337+
# case 1: no negative pressure will only have gravity pulling mass back down
338+
# case 2: with negative pressure, added force pulling mass back down
339+
# - case 1 should push the mass higher
340+
@test maximum(sol1[s.mass.s]) > maximum(sol2[s.mass.s])
341+
342+
# case 1 should prevent negative pressure less than -1000
343+
@test minimum(sol1[s.vol.port.p]) > -1000
344+
@test minimum(sol2[s.vol.port.p]) < -1000
345+
346+
# fig = Figure()
347+
# ax = Axis(fig[1,1])
348+
# lines!(ax, sol1.t, sol1[s.vol.port.p])
349+
# lines!(ax, sol2.t, sol2[s.vol.port.p])
350+
351+
# ax = Axis(fig[1,2])
352+
# lines!(ax, sol1.t, sol1[s.mass.s])
353+
# lines!(ax, sol2.t, sol2[s.mass.s])
354+
# fig
355+
end
356+
294357
#TODO: Test Valve Inversion

0 commit comments

Comments
 (0)