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 and viscosity 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
227 // Vapor density and viscosity calculated using partial pressure
228 // X1 * psat (Raoult's law)
229 ADReal vapor_density, vapor_viscosity;
230
231 _water_fp.rho_mu_from_p_T((1.0 - Xncg) * psat, temperature, vapor_density, vapor_viscosity);
232
233 // Density is just the sum of individual component densities
234 gas.density = ncg_density + vapor_density;
235
236 // Viscosity of the gas phase is a weighted sum of the individual viscosities
237 gas.viscosity = Yncg * ncg_viscosity + (1.0 - Yncg) * vapor_viscosity;
238
239 mooseAssert(gas.density.value() > 0.0, "Gas density must be greater than zero");
240
242 {
243 // Component enthalpies calculated using their partial pressures
244 const ADReal ncg_enthalpy = _ncg_fp.h_from_p_T(Yncg * pressure, temperature);
245 const ADReal vapor_enthalpy = _water_fp.h_from_p_T((1.0 - Xncg) * psat, temperature);
246
247 // Enthalpy of the gas phase is a weighted sum of the individual enthalpies
248 gas.enthalpy = Yncg * ncg_enthalpy + (1.0 - Yncg) * vapor_enthalpy;
249 }
250
251 // Internal energy of the gas phase (e = h - pv)
253 gas.internal_energy = gas.enthalpy - pressure / gas.density;
254}
255
256void
258 const ADReal & temperature,
259 std::vector<FluidStateProperties> & fsp) const
260{
262
263 // Calculate liquid density and viscosity if in the two phase or single phase
264 // liquid region, assuming they are not affected by the presence of dissolved
265 // NCG. Note: the (small) contribution due to derivative of capillary pressure
266 // wrt pressure (using the chain rule) is not implemented.
267 ADReal liquid_density, liquid_viscosity;
268 _water_fp.rho_mu_from_p_T(pressure, temperature, liquid_density, liquid_viscosity);
269
270 liquid.density = liquid_density;
271 liquid.viscosity = liquid_viscosity;
272
273 mooseAssert(liquid.density.value() > 0.0, "Liquid density must be greater than zero");
274
276 {
277 // Enthalpy does include a contribution due to the enthalpy of dissolution
278 const ADReal hdis = enthalpyOfDissolution(temperature);
279
280 const ADReal water_enthalpy = _water_fp.h_from_p_T(pressure, temperature);
281 const ADReal ncg_enthalpy = _ncg_fp.h_from_p_T(pressure, temperature);
282
283 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
284 liquid.enthalpy = (1.0 - Xncg) * water_enthalpy + Xncg * (ncg_enthalpy + hdis);
285 }
286
287 // Internal energy of the liquid phase (e = h - pv)
289 liquid.internal_energy = liquid.enthalpy - pressure / liquid.density;
290}
291
292ADReal
293PorousFlowWaterNCG::liquidDensity(const ADReal & pressure, const ADReal & temperature) const
294{
295 return _water_fp.rho_from_p_T(pressure, temperature);
296}
297
298ADReal
300 const ADReal & temperature,
301 std::vector<FluidStateProperties> & fsp) const
302{
303 auto & liquid = fsp[_aqueous_phase_number];
304 auto & gas = fsp[_gas_phase_number];
305
306 ADReal psat = _water_fp.vaporPressure(temperature);
307
308 const ADReal Yncg = gas.mass_fraction[_gas_fluid_component];
309 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
310
311 ADReal ncg_density = _ncg_fp.rho_from_p_T(Yncg * pressure, temperature);
312 ADReal vapor_density = _water_fp.rho_from_p_T((1.0 - Xncg) * psat, temperature);
313
314 // Density is just the sum of individual component densities
315 return ncg_density + vapor_density;
316}
317
318ADReal
320 const ADReal & temperature,
321 const ADReal & Z,
322 std::vector<FluidStateProperties> & fsp) const
323{
324 auto & gas = fsp[_gas_phase_number];
325 auto & liquid = fsp[_aqueous_phase_number];
326
327 // Approximate liquid density as saturation isn't known yet, by using the gas
328 // pressure rather than the liquid pressure. This does result in a small error
329 // in the calculated saturation, but this is below the error associated with
330 // the correlations. A more accurate saturation could be found iteraviely,
331 // at the cost of increased computational expense
332
333 // The gas and liquid densities
334 const ADReal gas_density = gasDensity(pressure, temperature, fsp);
335 const ADReal liquid_density = liquidDensity(pressure, temperature);
336
337 // Set mass equilibrium constants used in the calculation of vapor mass fraction
338 const ADReal Xncg = liquid.mass_fraction[_gas_fluid_component];
339 const ADReal Yncg = gas.mass_fraction[_gas_fluid_component];
340
341 const ADReal K0 = Yncg / Xncg;
342 const ADReal K1 = (1.0 - Yncg) / (1.0 - Xncg);
343 const ADReal vapor_mass_fraction = vaporMassFraction(Z, K0, K1);
344
345 // The gas saturation in the two phase case
346 const ADReal saturation = vapor_mass_fraction * liquid_density /
347 (gas_density + vapor_mass_fraction * (liquid_density - gas_density));
348
349 return saturation;
350}
351
352void
354 const ADReal & temperature,
355 const ADReal & Z,
356 unsigned int qp,
357 std::vector<FluidStateProperties> & fsp) const
358{
359 auto & gas = fsp[_gas_phase_number];
360
361 // Calculate all of the gas phase properties, as these don't depend on saturation
362 gasProperties(pressure, temperature, fsp);
363
364 // The gas saturation in the two phase case
365 gas.saturation = saturation(pressure, temperature, Z, fsp);
366
367 // The liquid pressure and properties can now be calculated
368 const ADReal liquid_pressure = pressure - _pc.capillaryPressure(1.0 - gas.saturation, qp);
369 liquidProperties(liquid_pressure, temperature, fsp);
370}
371
372void
374 const ADReal & temperature,
375 ADReal & Xncg,
376 ADReal & Yh2o) const
377{
378 // Equilibrium constants for each component (Henry's law for the NCG
379 // component, and Raoult's law for water).
380 const ADReal Kh = _water97_fp.henryConstant(temperature, _ncg_henry);
381 const ADReal psat = _water_fp.vaporPressure(temperature);
382
383 const ADReal Kncg = Kh / pressure;
384 const ADReal Kh2o = psat / pressure;
385
386 // The mole fractions for the NCG component in the two component
387 // case can be expressed in terms of the equilibrium constants only
388 const ADReal xncg = (1.0 - Kh2o) / (Kncg - Kh2o);
389 const ADReal yncg = Kncg * xncg;
390
391 // Convert mole fractions to mass fractions
392 Xncg = moleFractionToMassFraction(xncg);
393 Yh2o = 1.0 - moleFractionToMassFraction(yncg);
394}
395
396ADReal
398{
399 return xmol * _Mncg / (xmol * _Mncg + (1.0 - xmol) * _Mh2o);
400}
401
402void
404{
405 // Check whether the input temperature is within the region of validity of this equation
406 // of state (T_triple <= T <= T_critical)
407 if (temperature < _water_triple_temperature || temperature > _water_critical_temperature)
408 mooseException(name() + ": temperature " + Moose::stringify(temperature) +
409 " is outside range 273.16 K <= T <= 647.096 K");
410}
411
412ADReal
414{
415 // Henry's constant
416 const ADReal Kh = _water97_fp.henryConstant(temperature, _ncg_henry);
417
418 ADReal hdis = -_R * temperature * temperature * Kh.derivatives()[_Tidx] / Kh / _Mncg;
419
420 // Derivative of enthalpy of dissolution wrt temperature requires the second derivative of
421 // Henry's constant wrt temperature. For simplicity, approximate this numerically
422 const Real dT = temperature.value() * 1.0e-8;
423 const ADReal t2 = temperature + dT;
425
426 const Real dhdis_dT =
427 (-_R * t2 * t2 * Kh2.derivatives()[_Tidx] / Kh2 / _Mncg - hdis).value() / dT;
428
429 hdis.derivatives() = temperature.derivatives() * dhdis_dT;
430
431 return hdis;
432}
433
434Real
436 Real pressure, Real temperature, Real /* Xnacl */, Real saturation, unsigned int qp) const
437{
438 // Check whether the input temperature is within the region of validity
439 checkVariables(temperature);
440
441 // As we do not require derivatives, we can simply ignore their initialisation
442 const ADReal p = pressure;
443 const ADReal T = temperature;
444
445 // FluidStateProperties data structure
446 std::vector<FluidStateProperties> fsp(_num_phases, FluidStateProperties(_num_components));
447 auto & liquid = fsp[_aqueous_phase_number];
448 auto & gas = fsp[_gas_phase_number];
449
450 // Calculate equilibrium mass fractions in the two-phase state
451 ADReal Xncg, Yh2o;
452 equilibriumMassFractions(p, T, Xncg, Yh2o);
453
454 // Save the mass fractions in the FluidStateMassFractions object to calculate gas density
455 const ADReal Yncg = 1.0 - Yh2o;
456 liquid.mass_fraction[_aqueous_fluid_component] = 1.0 - Xncg;
457 liquid.mass_fraction[_gas_fluid_component] = Xncg;
458 gas.mass_fraction[_aqueous_fluid_component] = Yh2o;
459 gas.mass_fraction[_gas_fluid_component] = Yncg;
460
461 // Gas density
462 const Real gas_density = gasDensity(p, T, fsp).value();
463
464 // Liquid density
465 const ADReal liquid_pressure = p - _pc.capillaryPressure(1.0 - saturation, qp);
466 const Real liquid_density = liquidDensity(liquid_pressure, T).value();
467
468 // The total mass fraction of ncg (Z) can now be calculated
469 const Real Z = (saturation * gas_density * Yncg.value() +
470 (1.0 - saturation) * liquid_density * Xncg.value()) /
471 (saturation * gas_density + (1.0 - saturation) * liquid_density);
472
473 return Z;
474}
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 bool _compute_enthalpy
Whether to compute the enthalpy of each phase.
const bool _compute_internal_energy
Whether to compute the internal energy of each 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