OGS
PhaseTransition.cpp
Go to the documentation of this file.
1
10#include "PhaseTransition.h"
11
13
14namespace
15{
17 std::map<int, std::shared_ptr<MaterialPropertyLib::Medium>> const& media,
18 std::string const& phase_name)
19{
20 // Always the first (begin) medium that holds fluid phases.
21 auto const& medium = media.begin()->second;
22 return medium->phase(phase_name).numberOfComponents();
23}
24
26 std::map<int, std::shared_ptr<MaterialPropertyLib::Medium>> const& media,
27 std::string const& phase_name,
29{
30 // It is always the first (begin) medium that holds fluid phases.
31 auto const& medium = media.begin()->second;
32 auto const& phase = medium->phase(phase_name);
33
34 // find the component for which the property 'property_type' is defined
35 for (std::size_t c = 0; c < phase.numberOfComponents(); c++)
36 {
37 if (phase.component(c).hasProperty(property_type))
38 {
39 return c;
40 }
41 }
42
43 // A lot of checks can (and should) be done to make sure that the right
44 // components with the right properties are used. For example, the names
45 // of the components can be compared to check that the name of the
46 // evaporable component does not also correspond to the name of the
47 // solute.
48
50 "PhaseTransitionModel: findComponentIndex() could not find the "
51 "specified property type '{:s}' in phase '{:s}'.",
53 phase_name);
54}
55} // namespace
56
57namespace ProcessLib::TH2M
58{
59namespace ConstitutiveRelations
60{
62 std::map<int, std::shared_ptr<MaterialPropertyLib::Medium>> const& media)
63 : PhaseTransitionModel(media),
64 n_components_gas_{numberOfComponents(media, "Gas")},
65 // Identifies and initialises the indices of the various components via
66 // their properties
67 gas_phase_vapour_component_index_{findComponentIndex(
68 media, "Gas", MaterialPropertyLib::PropertyType::vapour_pressure)},
69 // Dry air component is complement of vapour component index
70 gas_phase_dry_air_component_index_{1 - gas_phase_vapour_component_index_},
71 // The solute is the component of the liquid phase which has been assigned
72 // the property `henry_coefficient'.
73 liquid_phase_solute_component_index_{findComponentIndex(
74 media, "AqueousLiquid",
75 MaterialPropertyLib::PropertyType::henry_coefficient)},
76 // The solvent is again the bitwise complement of the solute component
77 liquid_phase_solvent_component_index_{
78 1 - liquid_phase_solute_component_index_}
79{
80 DBUG("Create PhaseTransition constitutive model.");
81
82 if (n_components_gas_ != 2)
83 {
85 "The current implementation of PhaseTransitionModelEvaporation "
86 "requires the specification of exactly two components in the "
87 "gas "
88 "phase.");
89 }
90
91 // It is always the first (begin) medium that holds fluid phases.
92 auto const medium = media.begin()->second;
93 auto const& gas_phase = medium->phase("Gas");
94 auto const& liquid_phase = medium->phase("AqueousLiquid");
95
96 // check for minimum requirement definitions in media object
97 std::array const required_vapour_component_properties = {
102
103 std::array const required_dry_air_component_properties = {
106
107 std::array const required_solute_component_properties = {
111
112 std::array const required_solvent_component_properties = {
114
115 std::array const required_gas_properties = {MaterialPropertyLib::density};
116
117 std::array const required_liquid_properties = {
119
120 checkRequiredProperties(
121 gas_phase.component(gas_phase_vapour_component_index_),
122 required_vapour_component_properties);
123 checkRequiredProperties(
124 gas_phase.component(gas_phase_dry_air_component_index_),
125 required_dry_air_component_properties);
126 checkRequiredProperties(
127 liquid_phase.component(liquid_phase_solute_component_index_),
128 required_solute_component_properties);
129 checkRequiredProperties(
130 liquid_phase.component(liquid_phase_solvent_component_index_),
131 required_solvent_component_properties);
132 checkRequiredProperties(gas_phase, required_gas_properties);
133 checkRequiredProperties(liquid_phase, required_liquid_properties);
134}
135
137 MediaData const& media_data,
138 GasPressureData const& p_GR,
139 CapillaryPressureData const& p_cap,
140 TemperatureData const& T_data,
141 PureLiquidDensityData const& rho_W_LR,
142 FluidEnthalpyData& fluid_enthalpy_data,
143 MassMoleFractionsData& mass_mole_fractions_data,
144 FluidDensityData& fluid_density_data,
145 VapourPartialPressureData& vapour_pressure_data,
146 ConstituentDensityData& constituent_density_data,
147 PhaseTransitionData& cv) const
148{
150
151 // primary variables
152 auto const pGR = p_GR();
153 auto const pCap = p_cap();
154 auto const T = T_data.T;
155 variables.gas_phase_pressure = pGR;
156 variables.capillary_pressure = pCap;
157 variables.temperature = T;
158
159 auto const& liquid_phase = media_data.liquid;
160 auto const& gas_phase = media_data.gas;
161
163
164 // identify vapour and air components for convenient access
165 auto const& vapour_component =
167 auto const& dry_air_component =
168 gas_phase.component(gas_phase_dry_air_component_index_);
169
170 // specific latent heat (of evaporation)
171 const auto dh_evap =
172 vapour_component
174 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
175
176 // molar mass of evaporating component
177 auto const M_W =
178 vapour_component.property(MaterialPropertyLib::PropertyType::molar_mass)
179 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
180
181 // provide evaporation enthalpy and molar mass of the evaporating
182 // component in the variable array
183 variables.enthalpy_of_evaporation = dh_evap;
184 variables.molar_mass = M_W;
185
186 // vapour pressure over flat interface
187 const auto p_vap_flat =
188 vapour_component
190 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
191
192 const auto dp_vap_flat_dT =
193 vapour_component
195 .template dValue<double>(variables,
197 x_t.x, x_t.t, x_t.dt);
198
199 // molar mass of dry air component
200 auto const M_C =
201 dry_air_component
203 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
204
205 // Water pressure is passed to the VariableArray in order to calculate
206 // the water density.
207 auto const pLR = pGR - pCap;
208 variables.liquid_phase_pressure = pLR;
209
210 // Kelvin-Laplace correction for menisci
211 const double K =
212 pCap > 0. ? std::exp(-pCap * M_W / rho_W_LR() / R / T) : 1.;
213 const double dK_dT =
214 pCap > 0. ? pCap * M_W / rho_W_LR() / R / T / T * K : 0;
215 const double dK_dpCap =
216 pCap > 0. ? -M_W / rho_W_LR() / R / T * K
217 : 0.; // rho_W_LR is treated as a constant here. However, the
218 // resulting errors are very small and can be ignored.
219
220 // vapour pressure inside porespace (== water partial pressure in gas
221 // phase)
222 vapour_pressure_data.pWGR = p_vap_flat * K;
223 auto const dpWGR_dT = dp_vap_flat_dT * K + p_vap_flat * dK_dT;
224 auto const dpWGR_dpCap = p_vap_flat * dK_dpCap;
225
226 // gas phase molar fractions
227 auto const xnWG_min =
228 1.e-12; // Magic number; prevents the mass fraction of a component
229 // from ever becoming zero (which would cause the partial
230 // density to disappear and thus one of the Laplace terms
231 // of the mass balance on the diagonal of the local element
232 // matrix to be zero). The value is simply made up, seems
233 // reasonable.
234 double const xnWG =
235 std::clamp(vapour_pressure_data.pWGR / pGR, xnWG_min, 1. - xnWG_min);
236 mass_mole_fractions_data.xnCG = 1. - xnWG;
237
238 // gas phase molar fraction derivatives
239 auto const dxnWG_dpGR = -vapour_pressure_data.pWGR / pGR / pGR;
240 auto const dxnWG_dpCap = dpWGR_dpCap / pGR;
241 auto const dxnWG_dT = dpWGR_dT / pGR;
242
243 // molar mass of the gas phase as a mixture of 'air' and vapour
244 auto const MG = mass_mole_fractions_data.xnCG * M_C + xnWG * M_W;
245 variables.molar_mass = MG;
246
247 // gas phase mass fractions
248 double const xmWG = xnWG * M_W / MG;
249 mass_mole_fractions_data.xmCG = 1. - xmWG;
250
251 auto const dxn_dxm_conversion = M_W * M_C / MG / MG;
252 // gas phase mass fraction derivatives
253 cv.dxmWG_dpGR = dxnWG_dpGR * dxn_dxm_conversion;
254 cv.dxmWG_dpCap = dxnWG_dpCap * dxn_dxm_conversion;
255 cv.dxmWG_dT = dxnWG_dT * dxn_dxm_conversion;
256
257 // density of overall gas phase
258 fluid_density_data.rho_GR =
260 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
261
262 // derivatives of average molar mass of the gas phase
263 auto const dMG = M_W - M_C;
264 auto const dMG_dpGR = dxnWG_dpGR * dMG;
265
266 variables.molar_mass_derivative = dMG_dpGR;
267
268 // Derivatives of the density of the (composite gas phase) and the
269 // partial densities of its components. The density of the mixture can
270 // be obtained via the property 'IdealGasLawBinaryMixture', for this
271 // purpose the derivatives of the mean molar mass are passed in each
272 // case via the variable_array.
273 cv.drho_GR_dp_GR =
275 .template dValue<double>(
277 x_t.x, x_t.t, x_t.dt);
278
279 auto const dMG_dpCap = dxnWG_dpCap * dMG;
280 variables.molar_mass_derivative = dMG_dpCap;
281 cv.drho_GR_dp_cap =
283 .template dValue<double>(
285 x_t.x, x_t.t, x_t.dt);
286
287 auto const dMG_dT = dxnWG_dT * dMG;
288 variables.molar_mass_derivative = dMG_dT;
289 cv.drho_GR_dT =
291 .template dValue<double>(variables,
293 x_t.x, x_t.t, x_t.dt);
294
295 // The derivatives of the partial densities of the gas phase are
296 // hard-coded (they should remain so, as they are a fundamental part of
297 // this evaporation model). By outsourcing the derivatives of the phase
298 // density to the MPL, a constant phase density can also be assumed, the
299 // derivatives of the partial densities are then unaffected and the
300 // model is still consistent.
301 constituent_density_data.rho_C_GR =
302 mass_mole_fractions_data.xmCG * fluid_density_data.rho_GR;
303 constituent_density_data.rho_W_GR = xmWG * fluid_density_data.rho_GR;
304
305 // 'Air'-component partial density derivatives
306 cv.drho_C_GR_dp_GR = mass_mole_fractions_data.xmCG * cv.drho_GR_dp_GR -
307 cv.dxmWG_dpGR * fluid_density_data.rho_GR;
308 cv.drho_C_GR_dp_cap = mass_mole_fractions_data.xmCG * cv.drho_GR_dp_cap -
309 cv.dxmWG_dpCap * fluid_density_data.rho_GR;
310 cv.drho_C_GR_dT = mass_mole_fractions_data.xmCG * cv.drho_GR_dT -
311 cv.dxmWG_dT * fluid_density_data.rho_GR;
312
313 // Vapour-component partial density derivatives
314 cv.drho_W_GR_dp_GR =
315 xmWG * cv.drho_GR_dp_GR + cv.dxmWG_dpGR * fluid_density_data.rho_GR;
317 xmWG * cv.drho_GR_dp_cap + cv.dxmWG_dpCap * fluid_density_data.rho_GR;
318 cv.drho_W_GR_dT =
319 xmWG * cv.drho_GR_dT + cv.dxmWG_dT * fluid_density_data.rho_GR;
320
321 // specific heat capacities of dry air and vapour
322 auto const cpCG =
323 dry_air_component
325 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
326 auto const cpWG =
327 vapour_component
329 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
330
331 // specific enthalpy of dry air and vapour components
332 cv.hCG = cpCG * T;
333 cv.hWG = cpWG * T + dh_evap;
334
335 // specific enthalpy of gas phase
336 fluid_enthalpy_data.h_G =
337 mass_mole_fractions_data.xmCG * cv.hCG + xmWG * cv.hWG;
338 cv.dh_G_dT = 0;
339
340 // specific inner energies of gas phase
341 cv.uG = fluid_enthalpy_data.h_G - pGR / fluid_density_data.rho_GR;
342 cv.du_G_dT = 0;
343 cv.du_G_dp_GR = 0;
344
345 // diffusion
346 auto const tortuosity =
347 media_data.medium
349 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
350
351 auto const D_W_G_m =
352 vapour_component.property(MaterialPropertyLib::PropertyType::diffusion)
353 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
355 tortuosity * D_W_G_m; // Note here that D_W_G = D_C_G.
356
357 variables.molar_fraction = mass_mole_fractions_data.xnCG;
358
359 // Dissolution part -- Liquid phase properties
360 // -------------------------------------------
361
362 // Reference to the gas component dissolved in the liquid phase
363 auto const& solute_component =
364 liquid_phase.component(liquid_phase_solute_component_index_);
365
366 // The amount of dissolved gas is described by Henry's law. If no
367 // dissolution is intended, the user must define the Henry coefficient
368 // as 'constant 0'.
369 //
370 // Henry-Coefficient and derivatives
371 auto const H =
372 solute_component
374 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
375
376 auto const dH_dT =
377 solute_component
379 .template dValue<double>(variables,
381 x_t.x, x_t.t, x_t.dt);
382 auto const dH_dpGR =
383 solute_component
385 .template dValue<double>(
387 x_t.x, x_t.t, x_t.dt);
388
389 // Concentration of the dissolved gas as amount of substance of the
390 // mixture component C related to the total volume of the liquid phase.
391 auto const cCL = H * mass_mole_fractions_data.xnCG * pGR;
392 // Fortunately for the developer, the signs of the derivatives of the
393 // composition of binary mixtures are often opposed.
394 auto const dxnCG_dpGR = -dxnWG_dpGR;
395 auto const dxnCG_dT = -dxnWG_dT;
396
397 auto const dcCL_dpGR =
398 (dH_dpGR * mass_mole_fractions_data.xnCG + H * dxnCG_dpGR) * pGR +
399 H * mass_mole_fractions_data.xnCG;
400 auto const dcCL_dT =
401 pGR * (dH_dT * mass_mole_fractions_data.xnCG + H * dxnCG_dT);
402
403 variables.concentration = cCL;
404 // Liquid density including dissolved gas components. Attention! This
405 // only works if the concentration of the C-component is taken into
406 // account in the selected equation of state, e.g. via a (multi)-linear
407 // equation of state. Should a constant density be assumed, no
408 // dissolution will take place, since the composition of the water phase
409 // is determined via the mass fractions (as the ratio of the densities
410 // of solvent and solution). NB! This problem did not occur with the gas
411 // phase because the composition there was determined via the molar
412 // fractions (ratio of the partial pressures).
413 fluid_density_data.rho_LR =
414 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
415 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
416 variables.density = fluid_density_data.rho_LR;
417
418 // Gas component partial density in liquid phase
419 constituent_density_data.rho_C_LR = fluid_density_data.rho_LR - rho_W_LR();
420
421 // liquid phase composition (mass fraction)
422 mass_mole_fractions_data.xmWL =
423 std::clamp(rho_W_LR() / fluid_density_data.rho_LR, 0., 1.);
424 auto const xmCL = 1. - mass_mole_fractions_data.xmWL;
425
426 // Attention! Usually a multi-linear equation of state is used to
427 // determine the density of the solution. This requires independent
428 // variables, but in this case the concentration is not independent, but
429 // is determined via the equilibrium state. The exact derivation of the
430 // density according to e.g. the pressure pLR is thus:
431 //
432 // drho_d_pGR = rho_ref * (beta_pLR + betaC * dC_dpLR)
433 //
434 // instead of
435 //
436 // d_rho_d_pGR =
437 // liquid_phase.property(MaterialPropertyLib::PropertyType::density)
438 // .template dValue<double>(
439 // variables,
440 // MaterialPropertyLib::Variable::liquid_phase_pressure,
441 // x_t.x, x_t.t, x_t.dt);
442
443 auto const rho_ref_betaP =
444 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
445 .template dValue<double>(
447 x_t.x, x_t.t, x_t.dt);
448
449 auto const rho_ref_betaT =
450 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
451 .template dValue<double>(variables,
453 x_t.x, x_t.t, x_t.dt);
454
455 auto const rho_ref_betaC =
456 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
457 .template dValue<double>(
459 x_t.t, x_t.dt);
460
461 // liquid phase density derivatives
462 auto const drhoLR_dpGR = rho_ref_betaP + rho_ref_betaC * dcCL_dpGR;
463 auto const drhoLR_dpCap = -rho_ref_betaP;
464 cv.drho_LR_dT = rho_ref_betaT + rho_ref_betaC * dcCL_dT;
465
466 // solvent partial density derivatives
467 auto const drhoWLR_dpGR = rho_ref_betaP;
468 auto const drhoWLR_dpCap = -rho_ref_betaP;
469 auto const drhoWLR_dT = rho_ref_betaT;
470
471 // liquid phase mass fraction derivatives
472 cv.dxmWL_dpGR =
473 1. / fluid_density_data.rho_LR *
474 (drhoWLR_dpGR - mass_mole_fractions_data.xmWL * drhoLR_dpGR);
475 cv.dxmWL_dpCap =
476 1. / fluid_density_data.rho_LR *
477 (drhoWLR_dpCap - mass_mole_fractions_data.xmWL * drhoLR_dpCap);
478 cv.dxmWL_dT = 1. / fluid_density_data.rho_LR *
479 (drhoWLR_dT - mass_mole_fractions_data.xmWL * cv.drho_LR_dT);
480
481 // liquid phase molar fractions and derivatives
482 mass_mole_fractions_data.xnWL =
483 mass_mole_fractions_data.xmWL * M_C /
484 (mass_mole_fractions_data.xmWL * M_C + xmCL * M_W);
485
486 // Reference to the pure liquid component
487 auto const& solvent_component =
488 liquid_phase.component(liquid_phase_solvent_component_index_);
489
490 // specific heat capacities of liquid phase components
491 auto const cpCL =
492 solute_component
494 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
495 auto const cpWL =
496 solvent_component
498 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
499
500 // specific heat of solution
501 const auto dh_sol =
502 solute_component
504 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
505
506 // specific enthalpy of liquid phase and its components
507 double const hCL = cpCL * T + dh_sol;
508 double const hWL = cpWL * T;
509 fluid_enthalpy_data.h_L = xmCL * hCL + mass_mole_fractions_data.xmWL * hWL;
510 cv.dh_L_dT = 0;
511
512 // specific inner energies of liquid phase
513 cv.uL = fluid_enthalpy_data.h_L;
514 cv.du_L_dT = 0;
515
516 // diffusion
517 auto const D_C_L_m =
518 solute_component.property(MaterialPropertyLib::PropertyType::diffusion)
519 .template value<double>(variables, x_t.x, x_t.t, x_t.dt);
521 tortuosity * D_C_L_m; // Note here that D_C_L = D_W_L.
522
523 // Some default initializations.
524 cv.drho_LR_dp_LR = 0;
525 cv.drho_W_LR_dp_GR = 0.;
526 cv.drho_W_LR_dT = 0.;
527 cv.drho_W_LR_dp_LR = 0.;
528}
529
530} // namespace ConstitutiveRelations
531} // namespace ProcessLib::TH2M
#define OGS_FATAL(...)
Definition Error.h:26
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:30
Property const & property(PropertyType const &) const
A get-function for retrieving a certain property.
Definition Component.cpp:33
Property const & property(PropertyType const &p) const
Definition Medium.cpp:54
Component const & component(std::size_t const &index) const
Definition Phase.cpp:33
static const std::array< std::string, PropertyType::number_of_properties > property_enum_to_string
int findComponentIndex(std::map< int, std::shared_ptr< MaterialPropertyLib::Medium > > const &media, std::string const &phase_name, MaterialPropertyLib::PropertyType property_type)
int numberOfComponents(std::map< int, std::shared_ptr< MaterialPropertyLib::Medium > > const &media, std::string const &phase_name)
ParameterLib::SpatialPosition x
Definition Base.h:74
MaterialPropertyLib::Medium const & medium
Definition Base.h:45
MaterialPropertyLib::Phase const & liquid
Definition Base.h:47
MaterialPropertyLib::Phase const & gas
Definition Base.h:48
void eval(SpaceTimeData const &x_t, MediaData const &media_data, GasPressureData const &p_GR, CapillaryPressureData const &p_cap, TemperatureData const &T_data, PureLiquidDensityData const &rho_W_LR, FluidEnthalpyData &fluid_enthalpy_data, MassMoleFractionsData &mass_mole_fractions_data, FluidDensityData &fluid_density_data, VapourPartialPressureData &vapour_pressure_data, ConstituentDensityData &constituent_density_data, PhaseTransitionData &cv) const override
PhaseTransition(std::map< int, std::shared_ptr< MaterialPropertyLib::Medium > > const &media)