https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PorousFlowWaterNCG.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "PorousFlowWaterNCG.h"
13#include "Conversion.h"
14
16
19{
21 params.addRequiredParam<UserObjectName>("water_fp", "The name of the user object for water");
22 params.addRequiredParam<UserObjectName>(
23 "gas_fp", "The name of the user object for the non-condensable gas");
24 params.addClassDescription("Fluid state class for water and non-condensable gas");
25 return params;
26}
27
30 _water_fp(getUserObject<SinglePhaseFluidProperties>("water_fp")),
31 _water97_fp(getUserObject<Water97FluidProperties>("water_fp")),
32 _ncg_fp(getUserObject<SinglePhaseFluidProperties>("gas_fp")),
33 _Mh2o(_water_fp.molarMass()),
34 _Mncg(_ncg_fp.molarMass()),
35 _water_triple_temperature(_water_fp.triplePointTemperature()),
36 _water_critical_temperature(_water_fp.criticalTemperature()),
37 _ncg_henry(_ncg_fp.henryCoefficients())
38{
39 // Check that the correct FluidProperties UserObjects have been provided
40 if (_water_fp.fluidName() != "water")
41 paramError("water_fp", "A valid water FluidProperties UserObject must be provided in water_fp");
42
43 // Set the number of phases and components, and their indexes
44 _num_phases = 2;
48
49 // Check that _aqueous_phase_number is <= total number of phases
51 paramError("liquid_phase_number",
52 "This value is larger than the possible number of phases ",
54
55 // Check that _aqueous_fluid_component is <= total number of fluid components
57 paramError("liquid_fluid_component",
58 "This value is larger than the possible number of fluid components",
60
62}
63
64std::string
66{
67 return "water-ncg";
68}
69
70void
72 Real temperature,
73 Real Xnacl,
74 Real Z,
75 unsigned int qp,
76 std::vector<FluidStateProperties> & fsp) const
77{
78 // Make AD versions of primary variables then call AD thermophysicalProperties()
79 ADReal p = pressure;
80 Moose::derivInsert(p.derivatives(), _pidx, 1.0);
81 ADReal T = temperature;
82 Moose::derivInsert(T.derivatives(), _Tidx, 1.0);
83 ADReal Zncg = Z;
84 Moose::derivInsert(Zncg.derivatives(), _Zidx, 1.0);
85 // X is not used, but needed for consistency with PorousFlowFluidState interface
86 ADReal X = Xnacl;
87 Moose::derivInsert(X.derivatives(), _Xidx, 1.0);
88
89 thermophysicalProperties(p, T, X, Zncg, qp, fsp);
90}
91
92void
94 const ADReal & temperature,
95 const ADReal & /* Xnacl */,
96 const ADReal & Z,
97 unsigned int qp,
98 std::vector<FluidStateProperties> & fsp) const
99{
102
103 // Check whether the input temperature is within the region of validity
104 checkVariables(temperature.value());
105
106 // Clear all of the FluidStateProperties data
108
109 FluidStatePhaseEnum phase_state;
110 massFractions(pressure, temperature, Z, phase_state, fsp);
111
112 switch (phase_state)
113 {
115 {
116 // Set the gas saturations
117 gas.saturation = 1.0;
118
119 // Calculate gas properties
120 gasProperties(pressure, temperature, fsp);
121
122 break;
123 }
124
126 {
127 // Calculate the liquid properties
128 const ADReal liquid_pressure = pressure - _pc.capillaryPressure(1.0, qp);
129 liquidProperties(liquid_pressure, temperature, fsp);
130
131 break;
132 }
133
135 {
136 // Calculate the gas and liquid properties in the two phase region
137 twoPhaseProperties(pressure, temperature, Z, qp, fsp);
138
139 break;
140 }
141 }
142
143 // Liquid saturations can now be set
144 liquid.saturation = 1.0 - gas.saturation;
145
146 // Save pressures to FluidStateProperties object
147 gas.pressure = pressure;
148 liquid.pressure = pressure - _pc.capillaryPressure(liquid.saturation, qp);
149}
150
151void
153 const ADReal & temperature,
154 const ADReal & Z,
155 FluidStatePhaseEnum & phase_state,
156 std::vector<FluidStateProperties> & fsp) const
157{
160
161 // Equilibrium mass fraction of NCG in liquid and H2O in gas phases
162 ADReal Xncg, Yh2o;
163 equilibriumMassFractions(pressure, temperature, Xncg, Yh2o);
164
165 ADReal Yncg = 1.0 - Yh2o;
166
167 // Determine which phases are present based on the value of Z
168 phaseState(Z.value(), Xncg.value(), Yncg.value(), phase_state);
169
170 // The equilibrium mass fractions calculated above are only correct in the two phase
171 // state. If only liquid or gas phases are present, the mass fractions are given by
172 // the total mass fraction Z.
173 ADReal Xh2o = 0.0;
174
175 switch (phase_state)
176 {
178 {
179 Xncg = Z;
180 Yncg = 0.0;
181 Xh2o = 1.0 - Z;
182 Yh2o = 0.0;
183 break;
184 }
185
187 {
188 Xncg = 0.0;
189 Yncg = Z;
190 Yh2o = 1.0 - Z;
191 break;
192 }
193
195 {
196 // Keep equilibrium mass fractions
197 Xh2o = 1.0 - Xncg;
198 break;
199 }
200 }
201
202 // Save the mass fractions in the FluidStateMassFractions object
204 liquid.mass_fraction[_gas_fluid_component] = Xncg;
207}
208
209void
211 const ADReal & temperature,
212 std::vector<FluidStateProperties> & fsp) const
213{
216
217 const ADReal psat = _water_fp.vaporPressure(temperature);
218
219 const ADReal Yncg = gas.mass_fraction[_gas_fluid_component];
220 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
221
222 // NCG density, viscosity and enthalpy calculated using partial pressure
223 // Yncg * gas_poreressure (Dalton's law)
224 ADReal ncg_density, ncg_viscosity;
225 _ncg_fp.rho_mu_from_p_T(Yncg * pressure, temperature, ncg_density, ncg_viscosity);
226 ADReal ncg_enthalpy = _ncg_fp.h_from_p_T(Yncg * pressure, temperature);
227
228 // Vapor density, viscosity and enthalpy calculated using partial pressure
229 // X1 * psat (Raoult's law)
230 ADReal vapor_density, vapor_viscosity;
231
232 _water_fp.rho_mu_from_p_T((1.0 - Xncg) * psat, temperature, vapor_density, vapor_viscosity);
233 ADReal vapor_enthalpy = _water_fp.h_from_p_T((1.0 - Xncg) * psat, temperature);
234
235 // Density is just the sum of individual component densities
236 gas.density = ncg_density + vapor_density;
237
238 // Viscosity of the gas phase is a weighted sum of the individual viscosities
239 gas.viscosity = Yncg * ncg_viscosity + (1.0 - Yncg) * vapor_viscosity;
240
241 // Enthalpy of the gas phase is a weighted sum of the individual enthalpies
242 gas.enthalpy = Yncg * ncg_enthalpy + (1.0 - Yncg) * vapor_enthalpy;
243
244 // Internal energy of the gas phase (e = h - pv)
245 mooseAssert(gas.density.value() > 0.0, "Gas density must be greater than zero");
246 gas.internal_energy = gas.enthalpy - pressure / gas.density;
247}
248
249void
251 const ADReal & temperature,
252 std::vector<FluidStateProperties> & fsp) const
253{
255
256 // Calculate liquid density and viscosity if in the two phase or single phase
257 // liquid region, assuming they are not affected by the presence of dissolved
258 // NCG. Note: the (small) contribution due to derivative of capillary pressure
259 // wrt pressure (using the chain rule) is not implemented.
260 ADReal liquid_density, liquid_viscosity;
261 _water_fp.rho_mu_from_p_T(pressure, temperature, liquid_density, liquid_viscosity);
262
263 liquid.density = liquid_density;
264 liquid.viscosity = liquid_viscosity;
265
266 // Enthalpy does include a contribution due to the enthalpy of dissolution
267 const ADReal hdis = enthalpyOfDissolution(temperature);
268
269 const ADReal water_enthalpy = _water_fp.h_from_p_T(pressure, temperature);
270 const ADReal ncg_enthalpy = _ncg_fp.h_from_p_T(pressure, temperature);
271
272 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
273 liquid.enthalpy = (1.0 - Xncg) * water_enthalpy + Xncg * (ncg_enthalpy + hdis);
274
275 // Internal energy of the liquid phase (e = h - pv)
276 mooseAssert(liquid.density.value() > 0.0, "Liquid density must be greater than zero");
277 liquid.internal_energy = liquid.enthalpy - pressure / liquid.density;
278}
279
280ADReal
281PorousFlowWaterNCG::liquidDensity(const ADReal & pressure, const ADReal & temperature) const
282{
283 return _water_fp.rho_from_p_T(pressure, temperature);
284}
285
286ADReal
288 const ADReal & temperature,
289 std::vector<FluidStateProperties> & fsp) const
290{
291 auto & liquid = fsp[_aqueous_phase_number];
292 auto & gas = fsp[_gas_phase_number];
293
294 ADReal psat = _water_fp.vaporPressure(temperature);
295
296 const ADReal Yncg = gas.mass_fraction[_gas_fluid_component];
297 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
298
299 ADReal ncg_density = _ncg_fp.rho_from_p_T(Yncg * pressure, temperature);
300 ADReal vapor_density = _water_fp.rho_from_p_T((1.0 - Xncg) * psat, temperature);
301
302 // Density is just the sum of individual component densities
303 return ncg_density + vapor_density;
304}
305
306ADReal
308 const ADReal & temperature,
309 const ADReal & Z,
310 std::vector<FluidStateProperties> & fsp) const
311{
312 auto & gas = fsp[_gas_phase_number];
313 auto & liquid = fsp[_aqueous_phase_number];
314
315 // Approximate liquid density as saturation isn't known yet, by using the gas
316 // pressure rather than the liquid pressure. This does result in a small error
317 // in the calculated saturation, but this is below the error associated with
318 // the correlations. A more accurate saturation could be found iteraviely,
319 // at the cost of increased computational expense
320
321 // The gas and liquid densities
322 const ADReal gas_density = gasDensity(pressure, temperature, fsp);
323 const ADReal liquid_density = liquidDensity(pressure, temperature);
324
325 // Set mass equilibrium constants used in the calculation of vapor mass fraction
326 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
327 const ADReal Yncg = gas.mass_fraction[_gas_fluid_component];
328
329 const ADReal K0 = Yncg / Xncg;
330 const ADReal K1 = (1.0 - Yncg) / (1.0 - Xncg);
331 const ADReal vapor_mass_fraction = vaporMassFraction(Z, K0, K1);
332
333 // The gas saturation in the two phase case
334 const ADReal saturation = vapor_mass_fraction * liquid_density /
335 (gas_density + vapor_mass_fraction * (liquid_density - gas_density));
336
337 return saturation;
338}
339
340void
342 const ADReal & temperature,
343 const ADReal & Z,
344 unsigned int qp,
345 std::vector<FluidStateProperties> & fsp) const
346{
347 auto & gas = fsp[_gas_phase_number];
348
349 // Calculate all of the gas phase properties, as these don't depend on saturation
350 gasProperties(pressure, temperature, fsp);
351
352 // The gas saturation in the two phase case
353 gas.saturation = saturation(pressure, temperature, Z, fsp);
354
355 // The liquid pressure and properties can now be calculated
356 const ADReal liquid_pressure = pressure - _pc.capillaryPressure(1.0 - gas.saturation, qp);
357 liquidProperties(liquid_pressure, temperature, fsp);
358}
359
360void
362 const ADReal & temperature,
363 ADReal & Xncg,
364 ADReal & Yh2o) const
365{
366 // Equilibrium constants for each component (Henry's law for the NCG
367 // component, and Raoult's law for water).
368 const ADReal Kh = _water97_fp.henryConstant(temperature, _ncg_henry);
369 const ADReal psat = _water_fp.vaporPressure(temperature);
370
371 const ADReal Kncg = Kh / pressure;
372 const ADReal Kh2o = psat / pressure;
373
374 // The mole fractions for the NCG component in the two component
375 // case can be expressed in terms of the equilibrium constants only
376 const ADReal xncg = (1.0 - Kh2o) / (Kncg - Kh2o);
377 const ADReal yncg = Kncg * xncg;
378
379 // Convert mole fractions to mass fractions
380 Xncg = moleFractionToMassFraction(xncg);
381 Yh2o = 1.0 - moleFractionToMassFraction(yncg);
382}
383
384ADReal
386{
387 return xmol * _Mncg / (xmol * _Mncg + (1.0 - xmol) * _Mh2o);
388}
389
390void
392{
393 // Check whether the input temperature is within the region of validity of this equation
394 // of state (T_triple <= T <= T_critical)
395 if (temperature < _water_triple_temperature || temperature > _water_critical_temperature)
396 mooseException(name() + ": temperature " + Moose::stringify(temperature) +
397 " is outside range 273.16 K <= T <= 647.096 K");
398}
399
400ADReal
402{
403 // Henry's constant
404 const ADReal Kh = _water97_fp.henryConstant(temperature, _ncg_henry);
405
406 ADReal hdis = -_R * temperature * temperature * Kh.derivatives()[_Tidx] / Kh / _Mncg;
407
408 // Derivative of enthalpy of dissolution wrt temperature requires the second derivative of
409 // Henry's constant wrt temperature. For simplicity, approximate this numerically
410 const Real dT = temperature.value() * 1.0e-8;
411 const ADReal t2 = temperature + dT;
413
414 const Real dhdis_dT =
415 (-_R * t2 * t2 * Kh2.derivatives()[_Tidx] / Kh2 / _Mncg - hdis).value() / dT;
416
417 hdis.derivatives() = temperature.derivatives() * dhdis_dT;
418
419 return hdis;
420}
421
422Real
424 Real pressure, Real temperature, Real /* Xnacl */, Real saturation, unsigned int qp) const
425{
426 // Check whether the input temperature is within the region of validity
427 checkVariables(temperature);
428
429 // As we do not require derivatives, we can simply ignore their initialisation
430 const ADReal p = pressure;
431 const ADReal T = temperature;
432
433 // FluidStateProperties data structure
434 std::vector<FluidStateProperties> fsp(_num_phases, FluidStateProperties(_num_components));
435 auto & liquid = fsp[_aqueous_phase_number];
436 auto & gas = fsp[_gas_phase_number];
437
438 // Calculate equilibrium mass fractions in the two-phase state
439 ADReal Xncg, Yh2o;
440 equilibriumMassFractions(p, T, Xncg, Yh2o);
441
442 // Save the mass fractions in the FluidStateMassFractions object to calculate gas density
443 const ADReal Yncg = 1.0 - Yh2o;
444 liquid.mass_fraction[_aqueous_fluid_component] = 1.0 - Xncg;
445 liquid.mass_fraction[_gas_fluid_component] = Xncg;
446 gas.mass_fraction[_aqueous_fluid_component] = Yh2o;
447 gas.mass_fraction[_gas_fluid_component] = Yncg;
448
449 // Gas density
450 const Real gas_density = gasDensity(p, T, fsp).value();
451
452 // Liquid density
453 const ADReal liquid_pressure = p - _pc.capillaryPressure(1.0 - saturation, qp);
454 const Real liquid_density = liquidDensity(liquid_pressure, T).value();
455
456 // The total mass fraction of ncg (Z) can now be calculated
457 const Real Z = (saturation * gas_density * Yncg.value() +
458 (1.0 - saturation) * liquid_density * Xncg.value()) /
459 (saturation * gas_density + (1.0 - saturation) * liquid_density);
460
461 return Z;
462}
DualNumber< Real, DNDerivativeType, true > ADReal
const Real p
const double T
FluidStatePhaseEnum
Phase state enum.
registerMooseObject("PorousFlowApp", PorousFlowWaterNCG)
const std::string name
Definition Setup.h:21
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void paramError(const std::string &param, Args... args) const
virtual Real capillaryPressure(Real saturation, unsigned qp=0) const
Capillary pressure is calculated as a function of true saturation.
unsigned int _num_components
Number of components.
const PorousFlowCapillaryPressure & _pc
Capillary pressure UserObject.
unsigned int _gas_phase_number
Phase number of the gas phase.
const Real _R
Universal gas constant (J/mol/K)
const unsigned int _aqueous_phase_number
Phase number of the aqueous phase.
FluidStateProperties _empty_fsp
Empty FluidStateProperties object.
void clearFluidStateProperties(std::vector< FluidStateProperties > &fsp) const
Clears the contents of the FluidStateProperties data structure.
unsigned int _num_phases
Number of phases.
Real vaporMassFraction(Real Z0, Real K0, Real K1) const
Solves Rachford-Rice equation to provide vapor mass fraction.
Compositional flash routines for miscible multiphase flow classes with multiple fluid components.
const unsigned int _Xidx
Index of derivative wrt salt mass fraction X.
unsigned int _gas_fluid_component
Fluid component number of the gas phase.
const unsigned int _pidx
Index of derivative wrt pressure.
const unsigned int _Tidx
Index of derivative wrt temperature.
const unsigned int _Zidx
Index of derivative wrt total mass fraction Z.
const unsigned int _aqueous_fluid_component
Fluid component number of the aqueous component.
void phaseState(Real Zi, Real Xi, Real Yi, FluidStatePhaseEnum &phase_state) const
Determines the phase state gven the total mass fraction and equilibrium mass fractions.
Specialized class for water and a non-condensable gas (NCG) Includes dissolution of gas in liquid wat...
const Real _Mncg
Molar mass of non-condensable gas (kg/mol)
void checkVariables(Real temperature) const
Check that the temperature is between the triple and critical values.
static InputParameters validParams()
ADReal gasDensity(const ADReal &pressure, const ADReal &temperature, std::vector< FluidStateProperties > &fsp) const
Density of the gas phase.
ADReal saturation(const ADReal &pressure, const ADReal &temperature, const ADReal &Z, std::vector< FluidStateProperties > &fsp) const
Gas saturation in the two-phase region.
void massFractions(const ADReal &pressure, const ADReal &temperature, const ADReal &Z, FluidStatePhaseEnum &phase_state, std::vector< FluidStateProperties > &fsp) const
Mass fractions of NCG and H2O in both phases, as well as derivatives wrt PorousFlow variables.
ADReal enthalpyOfDissolution(const ADReal &temperature) const
Enthalpy of dissolution of NCG in water calculated using Henry's constant From Himmelblau,...
const Water97FluidProperties & _water97_fp
Fluid properties UserObject for water (used to access Henry's law)
void liquidProperties(const ADReal &pressure, const ADReal &temperature, std::vector< FluidStateProperties > &fsp) const
Liquid properties - density, viscosity and enthalpy Note: The pressure here is the liquid pressure.
const std::vector< Real > _ncg_henry
Henry's coefficients for the NCG.
ADReal liquidDensity(const ADReal &pressure, const ADReal &temperature) const
Density of the liquid phase Note: The pressure here is the gas pressure.
const Real _water_critical_temperature
Critical temperature of water (K)
const SinglePhaseFluidProperties & _ncg_fp
Fluid properties UserObject for the NCG.
void gasProperties(const ADReal &pressure, const ADReal &temperature, std::vector< FluidStateProperties > &fsp) const
Gas properties - density, viscosity and enthalpy.
void thermophysicalProperties(Real pressure, Real temperature, Real Xnacl, Real Z, unsigned int qp, std::vector< FluidStateProperties > &fsp) const override
Determines the complete thermophysical state of the system for a given set of primary variables.
PorousFlowWaterNCG(const InputParameters &parameters)
ADReal moleFractionToMassFraction(const ADReal &xmol) const
Convert mole fraction to mass fraction.
virtual std::string fluidStateName() const override
Name of FluidState.
const SinglePhaseFluidProperties & _water_fp
Fluid properties UserObject for water.
virtual Real totalMassFraction(Real pressure, Real temperature, Real Xnacl, Real saturation, unsigned int qp) const override
Total mass fraction of fluid component summed over all phases in the two-phase state for a specified ...
const Real _Mh2o
Molar mass of water (kg/mol)
void twoPhaseProperties(const ADReal &pressure, const ADReal &temperature, const ADReal &Z, unsigned int qp, std::vector< FluidStateProperties > &fsp) const
Gas and liquid properties in the two-phase region.
void equilibriumMassFractions(const ADReal &pressure, const ADReal &temperature, ADReal &Xncg, ADReal &Yh2o) const
Mass fractions of NCG in liquid phase and H2O in gas phase at thermodynamic equilibrium.
Common class for single phase fluid properties.
virtual Real vaporPressure(Real T) const
Vapor pressure.
e e e e s T T T T T rho v v T e p T T virtual T std::string fluidName() const
Fluid name.
virtual void rho_mu_from_p_T(Real p, Real T, Real &rho, Real &mu) const
Combined methods.
Water (H2O) fluid properties as a function of pressure (Pa) and temperature (K) from IAPWS-IF97: Revi...
Real henryConstant(Real temperature, const std::vector< Real > &coeffs) const
IAPWS formulation of Henry's law constant for dissolution in water From Guidelines on the Henry's con...
std::string stringify(const T &t)
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)
AD data structure to pass calculated thermophysical properties.
std::vector< ADReal > mass_fraction