Skip to content

Commit 3b0e06b

Browse files
authored
Add table generator + improve docs (#27)
* Add table generator + improve docs * Update Project.toml
1 parent 852d464 commit 3b0e06b

14 files changed

Lines changed: 1729 additions & 7 deletions

File tree

Project.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
name = "MultiComponentFlash"
22
uuid = "35e5bd01-9722-4017-9deb-64a5d32478ff"
3+
version = "1.1.19"
34
authors = ["Olav Møyner <olav.moyner@gmail.com>"]
4-
version = "1.1.18"
55

66
[deps]
77
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
88
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
910
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
1011
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1112

1213
[compat]
1314
ForwardDiff = "0.10, 1"
15+
LinearAlgebra = "1"
16+
Printf = "1"
1417
Roots = "1, 2"
1518
StaticArrays = "1"
16-
LinearAlgebra = "1"
1719
julia = "1.6"
1820

1921
[extras]
20-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2122
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
2223
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
24+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2325

2426
[targets]
2527
test = ["Test", "ForwardDiff", "StaticArrays"]

docs/src/examples/advanced.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,11 @@ xlabel!("T [°Celsius]")
139139
```
140140

141141
![Phase diagram](../assets/phase_diagram_simple.png)
142+
143+
### PVT table generation
144+
145+
There is experimental support for generating simulator input blackoil tables (e.g. PVTG/PVDG and PVTO/PVDO).
146+
147+
```@docs
148+
generate_pvt_tables
149+
```

src/MultiComponentFlash.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module MultiComponentFlash
1212
include("flash_types.jl")
1313
include("flow_coupler_types.jl")
1414
# Types that define specific cubic equations of state
15-
export SoaveRedlichKwong, RedlichKwong, PengRobinson, PengRobinsonCorrected, ZudkevitchJoffe
15+
export SoaveRedlichKwong, RedlichKwong, PengRobinson, PengRobinsonCorrected, ZudkevitchJoffe, SoreideWhitson
1616
# The generic cubic form that supports the above
1717
export GenericCubicEOS
1818
# KValue "fake" EOS
@@ -57,4 +57,8 @@ module MultiComponentFlash
5757

5858
include("flow_coupler.jl")
5959
include("utils.jl")
60+
61+
include("PVTExperiments/PVTExperiments.jl")
62+
using .PVTExperiments
63+
export generate_pvt_tables
6064
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module PVTExperiments
2+
# Notes: This module was in part generated with the help of Claude Opus. We
3+
# export a single function, `generate_pvt_tables`, which is the main
4+
# interface for generating all the tables at once. Additional experiments
5+
# are available, but their interfaces are not exported and can be changed
6+
# without breaking versions.
7+
using MultiComponentFlash
8+
using Printf
9+
10+
import MultiComponentFlash: flashed_mixture_2ph, number_of_components
11+
12+
const WATER_DENSITY_SC = 999.0 # kg/m³ at standard conditions
13+
const R_GAS = MultiComponentFlash.IDEAL_GAS_CONSTANT # 8.3144598 J/(mol·K)
14+
15+
include("types.jl")
16+
include("utils.jl")
17+
include("cce.jl")
18+
include("dle.jl")
19+
include("cvd.jl")
20+
include("mss.jl")
21+
include("tables.jl")
22+
include("interface.jl")
23+
24+
export generate_pvt_tables
25+
end

src/PVTExperiments/cce.jl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
cce(eos, z, T; p_range, n_points)
3+
4+
Perform a Constant Composition Expansion (CCE) experiment.
5+
6+
The mixture with composition `z` is held at temperature `T` (K) and the
7+
pressure is decreased from a high value through the saturation pressure.
8+
The volume is measured relative to the volume at the saturation point.
9+
10+
# Arguments
11+
- `eos`: Equation of state
12+
- `z`: Overall mole fractions
13+
- `T`: Temperature (K)
14+
- `p_range`: Tuple (p_max, p_min) in Pa. Default: (50e6, 1e5)
15+
- `n_points`: Number of pressure steps. Default: 40
16+
"""
17+
function cce(eos, z, T;
18+
p_range = (50e6, 1e5),
19+
n_points = 40
20+
)
21+
z = collect(Float64, z)
22+
z ./= sum(z)
23+
nc = number_of_components(eos)
24+
25+
# Find saturation pressure
26+
p_sat, is_bp = find_saturation_pressure(eos, z, T;
27+
p_min = p_range[2], p_max = p_range[1])
28+
29+
# Generate pressure steps
30+
pressures = collect(range(p_range[1], p_range[2], length = n_points))
31+
sort!(pressures, rev = true)
32+
33+
# Compute properties at saturation point for reference volume
34+
props_sat = flash_and_properties(eos, p_sat, T, z)
35+
V_mol_sat = (1.0 - props_sat.V) * props_sat.V_mol_l + props_sat.V * props_sat.V_mol_v
36+
37+
# Storage
38+
Z_factors = zeros(n_points)
39+
V_factors = zeros(n_points)
40+
rel_vol = zeros(n_points)
41+
ρ_oil = zeros(n_points)
42+
ρ_gas = zeros(n_points)
43+
μ_oil = zeros(n_points)
44+
μ_gas = zeros(n_points)
45+
46+
for (i, p) in enumerate(pressures)
47+
props = flash_and_properties(eos, p, T, z)
48+
V_factors[i] = props.V
49+
Z_factors[i] = (1.0 - props.V) * props.Z_L + props.V * props.Z_V
50+
51+
# Total molar volume at this pressure
52+
V_mol = (1.0 - props.V) * props.V_mol_l + props.V * props.V_mol_v
53+
rel_vol[i] = V_mol / V_mol_sat
54+
55+
ρ_oil[i] = props.ρ_l
56+
ρ_gas[i] = props.ρ_v
57+
μ_oil[i] = props.μ_l
58+
μ_gas[i] = props.μ_v
59+
end
60+
61+
return CCEResult(T, z, pressures, Z_factors, V_factors, rel_vol,
62+
ρ_oil, ρ_gas, μ_oil, μ_gas, p_sat, is_bp)
63+
end

src/PVTExperiments/cvd.jl

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""
2+
cvd(eos, z, T; p_range, n_points, p_sc, T_sc)
3+
4+
Perform a Constant Volume Depletion (CVD) experiment.
5+
6+
Starting at the dew point, the pressure is reduced in steps. At each step,
7+
gas is removed to restore the cell to its original volume. The liquid dropout
8+
and produced gas properties are recorded.
9+
10+
This experiment is typical for gas condensate systems.
11+
12+
# Arguments
13+
- `eos`: Equation of state
14+
- `z`: Overall mole fractions
15+
- `T`: Temperature (K)
16+
- `p_range`: (p_max, p_min) for the experiment. Default: (50e6, 1e5)
17+
- `n_points`: Number of pressure steps. Default: 20
18+
- `p_sc`: Standard condition pressure (Pa). Default: 101325.0
19+
- `T_sc`: Standard condition temperature (K). Default: 288.706 (60°F)
20+
"""
21+
function cvd(eos, z, T;
22+
p_range = (50e6, 1e5),
23+
n_points = 20,
24+
p_sc = 101325.0,
25+
T_sc = 288.706
26+
)
27+
z = collect(Float64, z)
28+
z ./= sum(z)
29+
nc = number_of_components(eos)
30+
31+
# Find saturation pressure
32+
p_sat, is_bp = find_saturation_pressure(eos, z, T;
33+
p_min = p_range[2], p_max = p_range[1])
34+
35+
if is_bp
36+
@warn "CVD is designed for dew point (gas condensate) fluids. Found bubble point fluid."
37+
end
38+
39+
# Pressure steps from p_sat down
40+
pressures = collect(range(p_sat, p_range[2], length = n_points + 1))
41+
42+
# Storage
43+
Z_factors = zeros(n_points + 1)
44+
liq_dropout = zeros(n_points + 1)
45+
gas_produced_cum = zeros(n_points + 1)
46+
ρ_gas_arr = zeros(n_points + 1)
47+
μ_gas_arr = zeros(n_points + 1)
48+
Z_gas_arr = zeros(n_points + 1)
49+
gas_comp = Vector{Vector{Float64}}(undef, n_points + 1)
50+
51+
# Initial state at dew point: single phase gas
52+
props_init = flash_and_properties(eos, p_sat, T, z)
53+
V_cell = props_init.V_mol_v # Reference cell volume per mole
54+
55+
n_total = 1.0 # Total moles in cell
56+
z_cell = copy(z) # Current overall composition in cell
57+
cum_gas_produced = 0.0 # Cumulative gas produced (moles)
58+
59+
for (i, p) in enumerate(pressures)
60+
if i == 1
61+
# At dew point - single phase gas
62+
Z_factors[i] = props_init.Z_V
63+
liq_dropout[i] = 0.0
64+
gas_produced_cum[i] = 0.0
65+
ρ_gas_arr[i] = props_init.ρ_v
66+
μ_gas_arr[i] = props_init.μ_v
67+
Z_gas_arr[i] = props_init.Z_V
68+
gas_comp[i] = copy(z)
69+
else
70+
# Flash at lower pressure
71+
props = flash_and_properties(eos, p, T, z_cell)
72+
73+
Z_gas_arr[i] = props.Z_V
74+
gas_comp[i] = copy(props.y)
75+
ρ_gas_arr[i] = props.ρ_v
76+
μ_gas_arr[i] = props.μ_v
77+
78+
# Two-phase Z factor
79+
Z_factors[i] = (1.0 - props.V) * props.Z_L + props.V * props.Z_V
80+
81+
# Volumes
82+
V_liq = n_total * (1.0 - props.V) * props.V_mol_l
83+
V_gas = n_total * props.V * props.V_mol_v
84+
V_total = V_liq + V_gas
85+
86+
# Liquid dropout = liquid volume / cell volume
87+
liq_dropout[i] = V_liq / V_cell
88+
89+
# Gas to remove to restore cell volume
90+
V_excess = V_total - V_cell
91+
if V_excess > 0 && props.V_mol_v > 0
92+
n_gas_remove = V_excess / props.V_mol_v
93+
else
94+
n_gas_remove = 0.0
95+
end
96+
97+
cum_gas_produced += n_gas_remove
98+
gas_produced_cum[i] = cum_gas_produced
99+
100+
# Update cell: remove gas, keep liquid + remaining gas
101+
n_gas_remaining = n_total * props.V - n_gas_remove
102+
n_liq = n_total * (1.0 - props.V)
103+
104+
if n_gas_remaining < 0
105+
n_gas_remaining = 0.0
106+
end
107+
108+
n_total_new = n_liq + n_gas_remaining
109+
110+
# New composition
111+
if n_total_new > 0
112+
z_cell = (n_liq .* props.x .+ n_gas_remaining .* props.y) ./ n_total_new
113+
z_cell ./= sum(z_cell)
114+
end
115+
n_total = n_total_new
116+
end
117+
end
118+
119+
# Normalize cumulative gas produced
120+
gas_produced_cum ./= max(gas_produced_cum[end], 1e-30)
121+
122+
return CVDResult(T, collect(Float64, z ./ sum(z)), pressures,
123+
Z_factors, liq_dropout, gas_produced_cum,
124+
ρ_gas_arr, μ_gas_arr, Z_gas_arr, gas_comp, p_sat)
125+
end

0 commit comments

Comments
 (0)