https://mooseframework.inl.gov
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"
12 #include "Water97FluidProperties.h"
13 #include "Conversion.h"
14 
15 registerMooseObject("PorousFlowApp", PorousFlowWaterNCG);
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;
45  _num_components = 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 ",
53  _num_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 
64 std::string
66 {
67  return "water-ncg";
68 }
69 
70 void
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 
92 void
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
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
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 
151 void
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;
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 
209 void
211  const ADReal & temperature,
212  std::vector<FluidStateProperties> & fsp) const
213 {
216 
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 
249 void
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
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 
280 ADReal
282 {
283  return _water_fp.rho_from_p_T(pressure, temperature);
284 }
285 
286 ADReal
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 
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 
306 ADReal
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_fluid_component];
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 
340 void
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
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 
360 void
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).
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 
384 ADReal
386 {
387  return xmol * _Mncg / (xmol * _Mncg + (1.0 - xmol) * _Mh2o);
388 }
389 
390 void
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 
400 ADReal
402 {
403  // Henry's constant
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;
412  const ADReal Kh2 = _water97_fp.henryConstant(t2, _ncg_henry);
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 
422 Real
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
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 }
const unsigned int _aqueous_fluid_component
Fluid component number of the aqueous component.
const unsigned int _aqueous_phase_number
Phase number of the aqueous phase.
unsigned int _num_phases
Number of phases.
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...
virtual std::string fluidStateName() const override
Name of FluidState.
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.
const std::vector< Real > _ncg_henry
Henry&#39;s coefficients for the NCG.
unsigned int _gas_fluid_component
Fluid component number of the gas phase.
const unsigned int _Tidx
Index of derivative wrt temperature.
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...
ADReal moleFractionToMassFraction(const ADReal &xmol) const
Convert mole fraction to mass fraction.
unsigned int _gas_phase_number
Phase number of the gas phase.
void clearFluidStateProperties(std::vector< FluidStateProperties > &fsp) const
Clears the contents of the FluidStateProperties data structure.
unsigned int _num_components
Number of components.
Compositional flash routines for miscible multiphase flow classes with multiple fluid components...
static const std::string temperature
Definition: NS.h:59
DualNumber< Real, DNDerivativeType, true > ADReal
virtual const std::string & name() const
void addRequiredParam(const std::string &name, const std::string &doc_string)
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.
const unsigned int _pidx
Index of derivative wrt pressure.
virtual Real capillaryPressure(Real saturation, unsigned qp=0) const
Capillary pressure is calculated as a function of true saturation.
ADReal saturation(const ADReal &pressure, const ADReal &temperature, const ADReal &Z, std::vector< FluidStateProperties > &fsp) const
Gas saturation in the two-phase region.
const SinglePhaseFluidProperties & _water_fp
Fluid properties UserObject for water.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
PorousFlowWaterNCG(const InputParameters &parameters)
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...
const Real _R
Universal gas constant (J/mol/K)
const Water97FluidProperties & _water97_fp
Fluid properties UserObject for water (used to access Henry&#39;s law)
AD data structure to pass calculated thermophysical properties.
Common class for single phase fluid properties.
FluidStatePhaseEnum
Phase state enum.
void paramError(const std::string &param, Args... args) const
std::string stringify(const T &t)
ADReal enthalpyOfDissolution(const ADReal &temperature) const
Enthalpy of dissolution of NCG in water calculated using Henry&#39;s constant From Himmelblau, Partial molal heats and entropies of solution for gases dissolved in water from the freezing to the near critical point, J.
ADReal gasDensity(const ADReal &pressure, const ADReal &temperature, std::vector< FluidStateProperties > &fsp) const
Density of the gas phase.
const Real _Mh2o
Molar mass of water (kg/mol)
Specialized class for water and a non-condensable gas (NCG) Includes dissolution of gas in liquid wat...
const Real _water_critical_temperature
Critical temperature of water (K)
virtual void rho_mu_from_p_T(Real p, Real T, Real &rho, Real &mu) const
Combined methods.
static const std::string Z
Definition: NS.h:169
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const PorousFlowCapillaryPressure & _pc
Capillary pressure UserObject.
static InputParameters validParams()
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.
void checkVariables(Real temperature) const
Check that the temperature is between the triple and critical values.
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 ...
Real vaporMassFraction(Real Z0, Real K0, Real K1) const
Solves Rachford-Rice equation to provide vapor mass fraction.
Water (H2O) fluid properties as a function of pressure (Pa) and temperature (K) from IAPWS-IF97: Revi...
static const std::string pressure
Definition: NS.h:56
const unsigned int _Zidx
Index of derivative wrt total mass fraction Z.
Real henryConstant(Real temperature, const std::vector< Real > &coeffs) const
IAPWS formulation of Henry&#39;s law constant for dissolution in water From Guidelines on the Henry&#39;s con...
ADReal liquidDensity(const ADReal &pressure, const ADReal &temperature) const
Density of the liquid phase Note: The pressure here is the gas pressure.
const Real _Mncg
Molar mass of non-condensable gas (kg/mol)
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.
void addClassDescription(const std::string &doc_string)
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N >> &derivs, libMesh::dof_id_type index, Real value)
const unsigned int _Xidx
Index of derivative wrt salt mass fraction X.
FluidStateProperties _empty_fsp
Empty FluidStateProperties object.
virtual Real vaporPressure(Real T) const
Vapor pressure.
void gasProperties(const ADReal &pressure, const ADReal &temperature, std::vector< FluidStateProperties > &fsp) const
Gas properties - density, viscosity and enthalpy.
std::vector< ADReal > mass_fraction
registerMooseObject("PorousFlowApp", PorousFlowWaterNCG)
const SinglePhaseFluidProperties & _ncg_fp
Fluid properties UserObject for the NCG.