1- from unittest .mock import MagicMock
2-
31import pytest
42
53from ecalc_neqsim_wrapper .thermo import STANDARD_PRESSURE_BARA , STANDARD_TEMPERATURE_KELVIN
64from libecalc .process .fluid_stream .fluid_model import EoSModel , FluidComposition , FluidModel
75from libecalc .process .fluid_stream .fluid_stream import FluidStream
8- from libecalc .process .process_units . liquid_remover import LiquidRemover
6+ from libecalc .process .process_pipeline . process_error import NoGasPhaseError
97
108
119def test_liquid_remover_removes_liquid (fluid_service , liquid_remover_factory ):
@@ -38,23 +36,11 @@ def test_liquid_remover_removes_liquid(fluid_service, liquid_remover_factory):
3836
3937 assert inlet_stream .vapor_fraction_molar < 1.0
4038 assert outlet_stream .vapor_fraction_molar == 1.0
41-
42- expected_gas_mass_fraction = (
43- inlet_stream .vapor_fraction_molar * outlet_stream .fluid .molar_mass / inlet_stream .fluid .molar_mass
44- )
45- expected_mass_rate = inlet_stream .mass_rate_kg_per_h * expected_gas_mass_fraction
4639 assert outlet_stream .mass_rate_kg_per_h < inlet_stream .mass_rate_kg_per_h
47- assert outlet_stream .mass_rate_kg_per_h == expected_mass_rate
4840
4941
5042def test_liquid_remover_passthrough_when_no_liquid (fluid_service , liquid_remover_factory ):
51- composition = FluidComposition (
52- nitrogen = 3 ,
53- CO2 = 1 ,
54- methane = 80 ,
55- ethane = 10 ,
56- propane = 6 ,
57- )
43+ composition = FluidComposition (nitrogen = 3 , CO2 = 1 , methane = 80 , ethane = 10 , propane = 6 )
5844 fluid_model = FluidModel (eos_model = EoSModel .SRK , composition = composition )
5945 fluid = fluid_service .create_fluid (
6046 fluid_model = fluid_model ,
@@ -73,13 +59,117 @@ def test_liquid_remover_passthrough_when_no_liquid(fluid_service, liquid_remover
7359 assert outlet_stream .mass_rate_kg_per_h == inlet_stream .mass_rate_kg_per_h
7460
7561
76- def test_liquid_remover_raises_on_non_positive_inlet_molar_mass ():
62+ def test_liquid_remover_passthrough_supercritical_co2 (fluid_service , liquid_remover_factory ):
63+ """Pure CO2 above critical point: NeqSim reports vapor_fraction=0,
64+ but the EoS critical point check detects supercritical and prevents mass loss."""
65+ composition = FluidComposition (CO2 = 1.0 )
66+ fluid_model = FluidModel (eos_model = EoSModel .SRK , composition = composition )
67+
68+ fluid = fluid_service .create_fluid (
69+ fluid_model = fluid_model ,
70+ pressure_bara = 350.0 ,
71+ temperature_kelvin = 308.15 ,
72+ )
73+ inlet_stream = FluidStream .from_standard_rate (
74+ standard_rate_m3_per_day = 100000 ,
75+ fluid_model = fluid .fluid_model ,
76+ fluid_properties = fluid .properties ,
77+ )
78+
79+ # NeqSim mislabels supercritical CO2 as liquid
80+ assert inlet_stream .vapor_fraction_molar <= 0.0001
81+
82+ remover = liquid_remover_factory ()
83+ outlet_stream = remover .propagate_stream (inlet_stream )
84+
85+ # Mass fully conserved
86+ assert outlet_stream .mass_rate_kg_per_h == inlet_stream .mass_rate_kg_per_h
87+
88+
89+ def test_liquid_remover_passthrough_subcritical_co2_vapor (fluid_service , liquid_remover_factory ):
90+ """CO2 below critical pressure: should be all vapor, passes through."""
91+ composition = FluidComposition (CO2 = 1.0 )
92+ fluid_model = FluidModel (eos_model = EoSModel .SRK , composition = composition )
93+
94+ fluid = fluid_service .create_fluid (
95+ fluid_model = fluid_model ,
96+ pressure_bara = 50.0 ,
97+ temperature_kelvin = 293.15 ,
98+ )
99+ inlet_stream = FluidStream .from_standard_rate (
100+ standard_rate_m3_per_day = 100000 ,
101+ fluid_model = fluid .fluid_model ,
102+ fluid_properties = fluid .properties ,
103+ )
104+
105+ remover = liquid_remover_factory ()
106+ outlet_stream = remover .propagate_stream (inlet_stream )
107+
108+ # Pure CO2 at these conditions is single-phase, mass conserved
109+ assert outlet_stream .mass_rate_kg_per_h == inlet_stream .mass_rate_kg_per_h
110+
111+
112+ def test_liquid_remover_raises_for_genuine_liquid (fluid_service , liquid_remover_factory ):
113+ """Water at ambient conditions is genuinely liquid (not supercritical).
114+ The LiquidRemover raises NoGasPhaseError — no gas to extract."""
115+ composition = FluidComposition (water = 1.0 )
116+ fluid_model = FluidModel (eos_model = EoSModel .SRK , composition = composition )
117+
118+ fluid = fluid_service .create_fluid (
119+ fluid_model = fluid_model ,
120+ pressure_bara = 10.0 ,
121+ temperature_kelvin = 293.15 ,
122+ )
123+ inlet_stream = FluidStream .from_standard_rate (
124+ standard_rate_m3_per_day = 100000 ,
125+ fluid_model = fluid .fluid_model ,
126+ fluid_properties = fluid .properties ,
127+ )
128+
129+ assert inlet_stream .vapor_fraction_molar <= 0.0001
130+
131+ remover = liquid_remover_factory ()
132+ with pytest .raises (NoGasPhaseError ):
133+ remover .propagate_stream (inlet_stream )
134+
135+
136+ def test_liquid_remover_passthrough_supercritical_co2_mixture (fluid_service , liquid_remover_factory ):
137+ """CO2-dominant mixture above its EoS-computed critical point.
138+ Exercises the critical point calculation across multiple components."""
139+ composition = FluidComposition (CO2 = 95.0 , methane = 5.0 )
140+ fluid_model = FluidModel (eos_model = EoSModel .SRK , composition = composition )
141+
142+ # EoS critical point ≈ 297 K, 72 bar
143+ # At 310 K, 100 bar → above both → supercritical
144+ fluid = fluid_service .create_fluid (
145+ fluid_model = fluid_model ,
146+ pressure_bara = 100.0 ,
147+ temperature_kelvin = 310.0 ,
148+ )
149+ inlet_stream = FluidStream .from_standard_rate (
150+ standard_rate_m3_per_day = 100000 ,
151+ fluid_model = fluid .fluid_model ,
152+ fluid_properties = fluid .properties ,
153+ )
154+
155+ remover = liquid_remover_factory ()
156+ outlet_stream = remover .propagate_stream (inlet_stream )
157+
158+ assert outlet_stream .mass_rate_kg_per_h == inlet_stream .mass_rate_kg_per_h
159+
160+
161+ def test_liquid_remover_raises_on_non_positive_inlet_molar_mass (fluid_service , liquid_remover_factory ):
162+ """The assertion guarding against degenerate streams with zero molar mass is still active."""
163+ from unittest .mock import MagicMock
164+
165+ from libecalc .process .process_units .liquid_remover import LiquidRemover
166+
77167 inlet_stream = MagicMock ()
78168 inlet_stream .vapor_fraction_molar = 0.5
79169 inlet_stream .fluid .molar_mass = 0.0
80170
81- fluid_service = MagicMock ()
82- remover = LiquidRemover (fluid_service = fluid_service )
171+ mock_fluid_service = MagicMock ()
172+ remover = LiquidRemover (fluid_service = mock_fluid_service )
83173
84174 with pytest .raises (AssertionError , match = "non-positive molar mass" ):
85175 remover .propagate_stream (inlet_stream )
0 commit comments