|
| 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