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