https://mooseframework.inl.gov
Loading...
Searching...
No Matches
IdealRealGasMixtureFluidProperties.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
12#include "BrentsMethod.h"
13#include <numeric>
14
16
21#define define_from_v_e_using_T_v(prop) \
22 Real IdealRealGasMixtureFluidProperties::prop##_from_v_e( \
23 Real v, Real e, const std::vector<Real> & x) const \
24 { \
25 const Real T = T_from_v_e(v, e, x); \
26 return prop##_from_T_v(T, v, x); \
27 } \
28 \
29 void IdealRealGasMixtureFluidProperties::prop##_from_v_e(Real v, \
30 Real e, \
31 const std::vector<Real> & x, \
32 Real & y, \
33 Real & dy_dv, \
34 Real & dy_de, \
35 std::vector<Real> & dy_dx) const \
36 { \
37 Real T, dT_dv_e, dT_de_v; \
38 std::vector<Real> dT_dx_ve(_n_secondary_vapors); \
39 T_from_v_e(v, e, x, T, dT_dv_e, dT_de_v, dT_dx_ve); \
40 \
41 Real dy_dT_v, dy_dv_T; \
42 std::vector<Real> dy_dx_Tv; \
43 prop##_from_T_v(T, v, x, y, dy_dT_v, dy_dv_T, dy_dx_Tv); \
44 \
45 dy_dv = dy_dv_T + dy_dT_v * dT_dv_e; \
46 dy_de = dy_dT_v * dT_de_v; \
47 dy_dx.resize(_n_secondary_vapors); \
48 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
49 dy_dx[i] = dy_dx_Tv[i] + dy_dT_v * dT_dx_ve[i]; \
50 }
51
56#define define_from_p_T_using_T_v(prop) \
57 Real IdealRealGasMixtureFluidProperties::prop##_from_p_T( \
58 Real p, Real T, const std::vector<Real> & x) const \
59 { \
60 const Real v = v_from_p_T(p, T, x); \
61 return prop##_from_T_v(T, v, x); \
62 } \
63 \
64 void IdealRealGasMixtureFluidProperties::prop##_from_p_T(Real p, \
65 Real T, \
66 const std::vector<Real> & x, \
67 Real & y, \
68 Real & dy_dp, \
69 Real & dy_dT, \
70 std::vector<Real> & dy_dx) const \
71 { \
72 Real v, dv_dp_T, dv_dT_p; \
73 std::vector<Real> dv_dx_pT; \
74 v_from_p_T(p, T, x, v, dv_dp_T, dv_dT_p, dv_dx_pT); \
75 \
76 Real dy_dT_v, dy_dv_T; \
77 std::vector<Real> dy_dx_Tv; \
78 prop##_from_T_v(T, v, x, y, dy_dT_v, dy_dv_T, dy_dx_Tv); \
79 \
80 dy_dp = dy_dv_T * dv_dp_T; \
81 dy_dT = dy_dT_v + dy_dv_T * dv_dT_p; \
82 dy_dx.resize(_n_secondary_vapors); \
83 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
84 dy_dx[i] = dy_dx_Tv[i] + dy_dv_T * dv_dx_pT[i]; \
85 }
86
90#define define_mass_specific_prop_from_T_v(prop) \
91 Real IdealRealGasMixtureFluidProperties::prop##_from_T_v( \
92 Real T, Real v, const std::vector<Real> & x) const \
93 { \
94 const Real x_primary = primaryMassFraction(x); \
95 Real y = x_primary * _fp_primary->prop##_from_T_v(T, v / x_primary); \
96 \
97 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
98 y += x[i] * _fp_secondary[i]->prop##_from_T_v(T, v / x[i]); \
99 \
100 return y; \
101 } \
102 \
103 void IdealRealGasMixtureFluidProperties::prop##_from_T_v(Real T, \
104 Real v, \
105 const std::vector<Real> & x, \
106 Real & y, \
107 Real & dy_dT, \
108 Real & dy_dv, \
109 std::vector<Real> & dy_dx) const \
110 { \
111 const Real x_primary = primaryMassFraction(x); \
112 mooseAssert(!MooseUtils::absoluteFuzzyEqual(x_primary, 0.0), "Mass fraction may not be zero"); \
113 \
114 Real y_primary, dy_dT_primary, dy_dv_primary; \
115 _fp_primary->prop##_from_T_v(T, v / x_primary, y_primary, dy_dT_primary, dy_dv_primary); \
116 y = x_primary * y_primary; \
117 dy_dT = x_primary * dy_dT_primary; \
118 dy_dv = dy_dv_primary; \
119 \
120 Real dy_dT_sec; \
121 std::vector<Real> y_sec(_n_secondary_vapors), dy_dv_sec(_n_secondary_vapors); \
122 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
123 { \
124 mooseAssert(!MooseUtils::absoluteFuzzyEqual(x[i], 0.0), "Mass fraction may not be zero"); \
125 _fp_secondary[i]->prop##_from_T_v(T, v / x[i], y_sec[i], dy_dT_sec, dy_dv_sec[i]); \
126 y += x[i] * y_sec[i]; \
127 dy_dT += x[i] * dy_dT_sec; \
128 dy_dv += dy_dv_sec[i]; \
129 } \
130 \
131 dy_dx.resize(_n_secondary_vapors); \
132 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
133 { \
134 dy_dx[i] = y_sec[i] - x[i] * dy_dv_sec[i] * v / (x[i] * x[i]); \
135 /* the secondary mass fractions are independent and x_primary = 1 - sum_j x_j */ \
136 const Real dx_primary_dxi = -1.0; \
137 dy_dx[i] += dx_primary_dxi * y_primary - \
138 x_primary * dy_dv_primary * v / (x_primary * x_primary) * dx_primary_dxi; \
139 } \
140 }
141
145#define define_transport_prop_from_T_v(prop) \
146 Real IdealRealGasMixtureFluidProperties::prop##_from_T_v( \
147 Real T, Real v, const std::vector<Real> & x) const \
148 { \
149 const Real x_primary = primaryMassFraction(x); \
150 Real M_primary = _fp_primary->molarMass(); \
151 \
152 Real sum = x_primary / M_primary; \
153 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
154 sum += x[i] / _fp_secondary[i]->molarMass(); \
155 const Real M_star = 1. / sum; \
156 \
157 const Real vp = v / x_primary; \
158 const Real ep = _fp_primary->e_from_T_v(T, vp); \
159 const Real yp = _fp_primary->prop##_from_v_e(vp, ep); \
160 Real y = x_primary * M_star / M_primary * yp; \
161 \
162 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
163 { \
164 const Real vi = v / x[i]; \
165 const Real ei = _fp_secondary[i]->e_from_T_v(T, vi); \
166 const Real Mi = _fp_secondary[i]->molarMass(); \
167 const Real yi = _fp_secondary[i]->prop##_from_v_e(vi, ei); \
168 y += x[i] * M_star / Mi * yi; \
169 } \
170 \
171 return y; \
172 } \
173 \
174 void IdealRealGasMixtureFluidProperties::prop##_from_T_v(Real T, \
175 Real v, \
176 const std::vector<Real> & x, \
177 Real & y, \
178 Real & dy_dT, \
179 Real & dy_dv, \
180 std::vector<Real> & dy_dx) const \
181 { \
182 const Real x_primary = primaryMassFraction(x); \
183 Real M_primary = _fp_primary->molarMass(); \
184 \
185 Real sum = x_primary / M_primary; \
186 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
187 sum += x[i] / _fp_secondary[i]->molarMass(); \
188 const Real M_star = 1. / sum; \
189 \
190 const Real vp = v / x_primary; \
191 const Real ep = _fp_primary->e_from_T_v(T, vp); \
192 const Real yp = _fp_primary->prop##_from_v_e(vp, ep); \
193 y = x_primary * M_star / M_primary * yp; \
194 \
195 imperfectJacobianMessage("The temperature and specific volume derivatives in the following " \
196 "function are currently neglected:\n\n", \
197 __PRETTY_FUNCTION__); \
198 dy_dT = 0; \
199 dy_dv = 0; \
200 \
201 Real sum_yj = 0; \
202 dy_dx.resize(_n_secondary_vapors); \
203 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
204 { \
205 const Real vi = v / x[i]; \
206 const Real ei = _fp_secondary[i]->e_from_T_v(T, vi); \
207 const Real Mi = _fp_secondary[i]->molarMass(); \
208 const Real yi = _fp_secondary[i]->prop##_from_v_e(vi, ei); \
209 y += x[i] * M_star / Mi * yi; \
210 dy_dx[i] = -M_star / M_primary * yp - \
211 x_primary * (1 / Mi - 1 / M_primary) * M_star * M_star / M_primary * yp + \
212 M_star / Mi * yi; \
213 sum_yj += x[i] * yi / Mi; \
214 } \
215 \
216 for (unsigned int i = 0; i < _n_secondary_vapors; i++) \
217 { \
218 const Real Mi = _fp_secondary[i]->molarMass(); \
219 dy_dx[i] += -M_star * M_star * (1 / Mi - 1 / M_primary) * sum_yj; \
220 } \
221 }
222
223// clang-format off
226
227define_transport_prop_from_T_v(mu)
228define_transport_prop_from_T_v(k)
229
230define_from_p_T_using_T_v(e)
231define_from_p_T_using_T_v(s)
232define_from_p_T_using_T_v(c)
233define_from_p_T_using_T_v(cp)
234define_from_p_T_using_T_v(cv)
235define_from_p_T_using_T_v(mu)
236define_from_p_T_using_T_v(k)
237
238define_from_v_e_using_T_v(p)
239define_from_v_e_using_T_v(c)
240
242// clang-format on
243{
245 params += NaNInterface::validParams();
246
247 params.addClassDescription("Class for fluid properties of an arbitrary vapor mixture");
248
249 params.addRequiredParam<UserObjectName>(
250 "fp_primary", "Name of fluid properties user object for primary vapor component");
251 params.addRequiredParam<std::vector<UserObjectName>>(
252 "fp_secondary", "Name of fluid properties user object(s) for secondary vapor component(s)");
253 params.addParam<Real>("_T_mix_max", 1300., "Maximum temperature of the mixture");
254 params.deprecateParam("_T_mix_max", "T_mix_max", "04/01/2027");
255
256 return params;
257}
258
260 const InputParameters & parameters)
261 : VaporMixtureFluidProperties(parameters),
262 NaNInterface(this),
263 _fp_primary(&getUserObject<SinglePhaseFluidProperties>("fp_primary")),
264 _fp_secondary_names(getParam<std::vector<UserObjectName>>("fp_secondary")),
265 _n_secondary_vapors(_fp_secondary_names.size()),
266 _T_mix_max(getParam<Real>("T_mix_max"))
267{
269 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
270 _fp_secondary[i] = &getUserObjectByName<SinglePhaseFluidProperties>(_fp_secondary_names[i]);
271}
272
278
281{
282 mooseAssert(i < getNumberOfSecondaryVapors(), "Requested secondary index too high.");
283 return *_fp_secondary[i];
284}
285
286Real
287IdealRealGasMixtureFluidProperties::T_from_v_e(Real v, Real e, const std::vector<Real> & x) const
288{
289 Real v_primary = v / primaryMassFraction(x);
290 const Real vc = 1. / _fp_primary->criticalDensity();
291 const Real ec = _fp_primary->criticalInternalEnergy();
292
293 // Initial estimate of a bracketing interval for the temperature
294 Real lower_temperature, upper_temperature;
295 if (v_primary > vc)
296 {
297 Real e_sat_primary = _fp_primary->e_spndl_from_v(v_primary);
298 lower_temperature = _fp_primary->T_from_v_e(v_primary, e_sat_primary);
299 }
300 else
301 lower_temperature = _fp_primary->T_from_v_e(v_primary, ec);
302
303 upper_temperature = _T_mix_max;
304
305 // Use BrentsMethod to find temperature
306 auto energy_diff = [&v, &e, &x, this](Real T) { return this->e_from_T_v(T, v, x) - e; };
307
308 BrentsMethod::bracket(energy_diff, lower_temperature, upper_temperature);
309 return BrentsMethod::root(energy_diff, lower_temperature, upper_temperature);
310}
311
312void
313IdealRealGasMixtureFluidProperties::T_from_v_e(Real v,
314 Real e,
315 const std::vector<Real> & x,
316 Real & T,
317 Real & dT_dv,
318 Real & dT_de,
319 std::vector<Real> & dT_dx) const
320{
321 T = T_from_v_e(v, e, x);
322
323 Real e_unused, de_dT_v, de_dv_T;
324 std::vector<Real> de_dx_Tv;
325 e_from_T_v(T, v, x, e_unused, de_dT_v, de_dv_T, de_dx_Tv);
326
327 dT_dv = -de_dv_T / de_dT_v;
328 dT_de = 1.0 / de_dT_v;
329 dT_dx.resize(_n_secondary_vapors);
330 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
331 dT_dx[i] = -de_dx_Tv[i] / de_dT_v;
332}
333
334Real
335IdealRealGasMixtureFluidProperties::rho_from_p_T(Real p, Real T, const std::vector<Real> & x) const
336{
337 return 1.0 / v_from_p_T(p, T, x);
338}
339
340void
341IdealRealGasMixtureFluidProperties::rho_from_p_T(Real p,
342 Real T,
343 const std::vector<Real> & x,
344 Real & rho,
345 Real & drho_dp,
346 Real & drho_dT,
347 std::vector<Real> & drho_dx) const
348{
349 Real v, dv_dp, dv_dT;
350 std::vector<Real> dv_dx;
351 v_from_p_T(p, T, x, v, dv_dp, dv_dT, dv_dx);
352
353 rho = 1.0 / v;
354 const Real drho_dv = -1.0 / (v * v);
355 drho_dp = drho_dv * dv_dp;
356 drho_dT = drho_dv * dv_dT;
357 drho_dx.resize(_n_secondary_vapors);
358 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
359 drho_dx[i] = drho_dv * dv_dx[i];
360}
361
362Real
363IdealRealGasMixtureFluidProperties::v_from_p_T(Real p, Real T, const std::vector<Real> & x) const
364{
365 const Real x_primary = primaryMassFraction(x);
366 Real M_primary = _fp_primary->molarMass();
367
368 Real sum = x_primary / M_primary;
369 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
370 sum += x[i] / _fp_secondary[i]->molarMass();
371 Real M_star = 1. / sum;
372 Real v_ideal = R_molar * T / (M_star * p);
373
374 // check range of validity for primary (condensable) component
376 const Real vc = 1. / _fp_primary->criticalDensity();
377 Real v_spndl, e_spndl;
378 if (T < Tc)
379 _fp_primary->v_e_spndl_from_T(T, v_spndl, e_spndl);
380 else
381 v_spndl = vc;
382
383 Real lower_spec_volume = v_spndl * x_primary;
384 Real upper_spec_volume = v_ideal; // p*v/(RT) <= 1
385
386 // Initial estimate of a bracketing interval for the temperature
387 Real p_max = p_from_T_v(T, lower_spec_volume, x);
388 if (p > p_max || upper_spec_volume < lower_spec_volume)
389 return getNaN();
390
391 // Use BrentsMethod to find temperature
392 auto pressure_diff = [&T, &p, &x, this](Real v) { return this->p_from_T_v(T, v, x) - p; };
393
394 BrentsMethod::bracket(pressure_diff, lower_spec_volume, upper_spec_volume);
395 return BrentsMethod::root(pressure_diff, lower_spec_volume, upper_spec_volume);
396}
397
398void
399IdealRealGasMixtureFluidProperties::v_from_p_T(Real p,
400 Real T,
401 const std::vector<Real> & x,
402 Real & v,
403 Real & dv_dp,
404 Real & dv_dT,
405 std::vector<Real> & dv_dx) const
406{
407 v = v_from_p_T(p, T, x);
408
409 Real p_unused, dp_dT, dp_dv;
410 std::vector<Real> dp_dx;
411 p_from_T_v(T, v, x, p_unused, dp_dT, dp_dv, dp_dx);
412
413 dv_dp = 1. / dp_dv;
414 dv_dT = -dp_dT / dp_dv;
415 dv_dx.resize(_n_secondary_vapors);
416 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
417 dv_dx[i] = -dp_dx[i] / dp_dv;
418}
419
420Real
421IdealRealGasMixtureFluidProperties::e_from_p_rho(Real p,
422 Real rho,
423 const std::vector<Real> & x) const
424{
425 Real v = 1. / rho;
426 Real T = T_from_p_v(p, v, x);
427
428 return e_from_T_v(T, v, x);
429}
430
431void
432IdealRealGasMixtureFluidProperties::e_from_p_rho(Real p,
433 Real rho,
434 const std::vector<Real> & x,
435 Real & e,
436 Real & de_dp,
437 Real & de_drho,
438 std::vector<Real> & de_dx) const
439{
440 Real v = 1. / rho;
441 Real T = T_from_p_v(p, v, x);
442 Real p_, dp_dT, dp_dv, de_dT, de_dv;
443 std::vector<Real> dp_dx_Tv;
444 std::vector<Real> de_dx_Tv;
445
446 p_from_T_v(T, v, x, p_, dp_dT, dp_dv, dp_dx_Tv);
447 e_from_T_v(T, v, x, e, de_dT, de_dv, de_dx_Tv);
448
449 de_dp = de_dT / dp_dT;
450 de_drho = (-v * v) * (de_dv - de_dT * dp_dv / dp_dT);
451
452 // Derivatives with respect to mass fractions:
453 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
454 {
455 de_dx[i] = de_dx_Tv[i] - de_dT * dp_dx_Tv[i] / dp_dT;
456 }
457}
458
459Real
460IdealRealGasMixtureFluidProperties::T_from_p_v(Real p, Real v, const std::vector<Real> & x) const
461{
462 Real v_primary = v / primaryMassFraction(x);
463 const Real vc = 1. / _fp_primary->criticalDensity();
465
466 // Initial estimate of a bracketing interval for the temperature
467 Real lower_temperature, upper_temperature;
468 if (v_primary > vc)
469 {
470 Real e_sat_primary = _fp_primary->e_spndl_from_v(v_primary);
471 lower_temperature = _fp_primary->T_from_v_e(v_primary, e_sat_primary);
472 }
473 else
474 lower_temperature = _fp_primary->T_from_v_e(v_primary, ec);
475
476 upper_temperature = _T_mix_max;
477
478 // Use BrentsMethod to find temperature
479 auto pressure_diff = [&p, &v, &x, this](Real T) { return this->p_from_T_v(T, v, x) - p; };
480
481 BrentsMethod::bracket(pressure_diff, lower_temperature, upper_temperature);
482 return BrentsMethod::root(pressure_diff, lower_temperature, upper_temperature);
483}
484
485void
486IdealRealGasMixtureFluidProperties::T_from_p_v(Real p,
487 Real v,
488 const std::vector<Real> & x,
489 Real & T,
490 Real & dT_dp,
491 Real & dT_dv,
492 std::vector<Real> & dT_dx) const
493{
494 T = T_from_p_v(p, v, x);
495
496 // pressure and derivatives
497 Real p_unused, dp_dT_v, dp_dv_T;
498 std::vector<Real> dp_dx_Tv;
499 p_from_T_v(T, v, x, p_unused, dp_dT_v, dp_dv_T, dp_dx_Tv);
500
501 // Compute derivatives using the following rules:
502 dT_dp = 1. / dp_dT_v;
503 dT_dv = -dp_dv_T / dp_dT_v;
504
505 // Derivatives with respect to mass fractions:
506 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
507 dT_dx[i] = -dp_dx_Tv[i] / dp_dT_v;
508}
509
510Real
511IdealRealGasMixtureFluidProperties::p_from_T_v(Real T, Real v, const std::vector<Real> & x) const
512{
513 const Real x_primary = primaryMassFraction(x);
514 Real p = _fp_primary->p_from_T_v(T, v / x_primary);
515
516 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
517 p += _fp_secondary[i]->p_from_T_v(T, v / x[i]);
518
519 return p;
520}
521
522void
523IdealRealGasMixtureFluidProperties::p_from_T_v(Real T,
524 Real v,
525 const std::vector<Real> & x,
526 Real & p,
527 Real & dp_dT,
528 Real & dp_dv,
529 std::vector<Real> & dp_dx) const
530{
531 const Real x_primary = primaryMassFraction(x);
532
533 Real p_primary, dp_dT_primary, dp_dv_primary;
534 _fp_primary->p_from_T_v(T, v / x_primary, p_primary, dp_dT_primary, dp_dv_primary);
535 p = p_primary;
536 dp_dT = dp_dT_primary;
537 dp_dv = dp_dv_primary / x_primary;
538
539 // get the partial pressures and their derivatives first
540 Real p_sec, dp_dT_sec;
541 std::vector<Real> dp_dv_sec(_n_secondary_vapors);
542 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
543 {
544 _fp_secondary[i]->p_from_T_v(T, v / x[i], p_sec, dp_dT_sec, dp_dv_sec[i]);
545 p += p_sec;
546 dp_dT += dp_dT_sec;
547 dp_dv += dp_dv_sec[i] / x[i];
548 }
549
550 // get the composition dependent derivatives of the secondary vapors
551 dp_dx.resize(_n_secondary_vapors);
552 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
553 {
554 dp_dx[i] = -dp_dv_sec[i] * v / (x[i] * x[i]);
555 // the secondary mass fractions are independent and x_primary = 1 - sum_j x_j
556 const Real dx_primary_dxi = -1.0;
557 dp_dx[i] += -dp_dv_primary * v / (x_primary * x_primary) * dx_primary_dxi;
558 }
559}
560
561Real
562IdealRealGasMixtureFluidProperties::c_from_T_v(Real T, Real v, const std::vector<Real> & x) const
563{
564 Real p, dp_dT, dp_dv;
565 std::vector<Real> dp_dx;
566 p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
567
568 Real s, ds_dT, ds_dv;
569 std::vector<Real> ds_dx;
570 s_from_T_v(T, v, x, s, ds_dT, ds_dv, ds_dx);
571
572 const Real dp_dv_s = dp_dv - dp_dT * ds_dv / ds_dT;
573
574 if (dp_dv_s >= 0)
575 mooseWarning("c_from_T_v(): dp_dv_s = ", dp_dv_s, ". Should be negative.");
576 return v * std::sqrt(-dp_dv_s);
577}
578
579void
580IdealRealGasMixtureFluidProperties::c_from_T_v(Real T,
581 Real v,
582 const std::vector<Real> & x,
583 Real & c,
584 Real & dc_dT,
585 Real & dc_dv,
586 std::vector<Real> & dc_dx) const
587{
588 c = c_from_T_v(T, v, x);
589
590 // For derived properties, we would need higher order derivatives;
591 // therefore, numerical derivatives are used here.
592 const Real dT = 1.e-6;
593 const Real T_perturbed = T + dT;
594 Real c_perturbed = c_from_T_v(T_perturbed, v, x);
595 dc_dT = (c_perturbed - c) / dT;
596
597 const Real dv = v * 1.e-6;
598 const Real v_perturbed = v + dv;
599 c_perturbed = c_from_T_v(T, v_perturbed, x);
600 dc_dv = (c_perturbed - c) / dv;
601
602 dc_dx.resize(_n_secondary_vapors);
603 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
604 {
605 std::vector<Real> x_perturbed(x);
606 const Real dx_i = 1e-6;
607 for (unsigned int j = 0; j < _n_secondary_vapors; j++)
608 {
609 if (j != i)
610 x_perturbed[j] =
611 x[j] * (1.0 - (x[i] + dx_i)) / (1.0 - x[i]); // recalculate new mass fractions
612 }
613 x_perturbed[i] += dx_i;
614 c_perturbed = c_from_T_v(T, v, x_perturbed);
615 dc_dx[i] = ((c_perturbed - c) / dx_i);
616 }
617}
618
619Real
620IdealRealGasMixtureFluidProperties::cp_from_T_v(Real T, Real v, const std::vector<Real> & x) const
621{
622 const Real x_primary = primaryMassFraction(x);
623
624 Real p, dp_dT, dp_dv;
625 std::vector<Real> dp_dx;
626 p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
627
628 Real h, dh_dT, dh_dv;
629 _fp_primary->h_from_T_v(T, v / x_primary, h, dh_dT, dh_dv);
630 // h_i is evaluated at v_i = v / x_i, so dv_i/dv = 1 / x_i
631 const Real cp_primary = dh_dT - dh_dv / x_primary * dp_dT / dp_dv;
632 Real cp = x_primary * cp_primary;
633
634 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
635 {
636 _fp_secondary[i]->h_from_T_v(T, v / x[i], h, dh_dT, dh_dv);
637 const Real cp_sec = dh_dT - dh_dv / x[i] * dp_dT / dp_dv;
638 cp += x[i] * cp_sec;
639 }
640
641 return cp;
642}
643
644void
645IdealRealGasMixtureFluidProperties::cp_from_T_v(Real T,
646 Real v,
647 const std::vector<Real> & x,
648 Real & cp,
649 Real & dcp_dT,
650 Real & dcp_dv,
651 std::vector<Real> & dcp_dx) const
652{
653 const Real x_primary = primaryMassFraction(x);
654
655 Real p, dp_dT, dp_dv;
656 std::vector<Real> dp_dx;
657 p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
658
659 Real h, dh_dT, dh_dv;
660 _fp_primary->h_from_T_v(T, v / x_primary, h, dh_dT, dh_dv);
661 // h_i is evaluated at v_i = v / x_i, so dv_i/dv = 1 / x_i
662 const Real cp_primary = dh_dT - dh_dv / x_primary * dp_dT / dp_dv;
663 cp = x_primary * cp_primary;
664
665 // Neglect these for now. These require higher-order derivatives, so a finite
666 // difference should probably be used, as for sound speed.
667 imperfectJacobianMessage("The temperature and specific volume derivatives in the following "
668 "function are currently neglected:\n\n",
669 __PRETTY_FUNCTION__);
670 dcp_dT = 0;
671 dcp_dv = 0;
672
673 dcp_dx.resize(_n_secondary_vapors);
674 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
675 {
676 _fp_secondary[i]->h_from_T_v(T, v / x[i], h, dh_dT, dh_dv);
677 const Real cp_sec = dh_dT - dh_dv / x[i] * dp_dT / dp_dv;
678 cp += x[i] * cp_sec;
679 dcp_dx[i] = cp_sec - cp_primary;
680 }
681}
682
683Real
684IdealRealGasMixtureFluidProperties::cv_from_T_v(Real T, Real v, const std::vector<Real> & x) const
685{
686 const Real x_primary = primaryMassFraction(x);
687 Real cv = x_primary * _fp_primary->cv_from_T_v(T, v / x_primary);
688
689 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
690 cv += x[i] * _fp_secondary[i]->cv_from_T_v(T, v / x[i]);
691
692 return cv;
693}
694
695void
696IdealRealGasMixtureFluidProperties::cv_from_T_v(Real T,
697 Real v,
698 const std::vector<Real> & x,
699 Real & cv,
700 Real & dcv_dT,
701 Real & dcv_dv,
702 std::vector<Real> & dcv_dx) const
703{
704 const Real x_primary = primaryMassFraction(x);
705 const Real cv_primary = _fp_primary->cv_from_T_v(T, v / x_primary);
706 cv = x_primary * cv_primary;
707
708 // Neglect these for now. These require higher-order derivatives, so a finite
709 // difference should probably be used, as for sound speed.
710 imperfectJacobianMessage("The temperature and specific volume derivatives in the following "
711 "function are currently neglected:\n\n",
712 __PRETTY_FUNCTION__);
713 dcv_dT = 0;
714 dcv_dv = 0;
715
716 dcv_dx.resize(_n_secondary_vapors);
717 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
718 {
719 const Real cv_sec = _fp_secondary[i]->cv_from_T_v(T, v / x[i]);
720 cv += x[i] * cv_sec;
721 dcv_dx[i] = cv_sec - cv_primary;
722 }
723}
724
725Real
727 Real T,
728 const std::vector<Real> & x) const
729{
730 Real T_c = _fp_primary->criticalTemperature();
731 Real xs;
732 if (T > T_c)
733 // return 1. to indicate that the primary fluid will not condense for
734 // given (p,T)
735 xs = 1.;
736 else
737 {
738 Real pp_sat = _fp_primary->pp_sat_from_p_T(p, T);
739 if (pp_sat < 0.)
740 {
741 // return 1. to indicate that the primary fluid will not condense for
742 // given (p,T)
743 xs = 1.;
744 return xs;
745 }
746 Real v_primary = _fp_primary->v_from_p_T(pp_sat, T);
747 Real pp_sat_secondary = p - pp_sat;
748
749 Real v_secondary;
750 if (_n_secondary_vapors == 1)
751 v_secondary = _fp_secondary[0]->v_from_p_T(pp_sat_secondary, T);
752 else
753 {
754 std::vector<Real> x_sec(_n_secondary_vapors);
755 const Real x_primary = primaryMassFraction(x);
756 Real sum = 0.;
757 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
758 {
759 x_sec[i] = x[i] / (1. - x_primary);
760 sum += x_sec[i] / _fp_secondary[i]->molarMass();
761 }
762 Real M_star = 1. / sum;
763 v_secondary = R_molar * T / (M_star * pp_sat_secondary);
764 int it = 0;
765 double f = 1., df_dvs, pp_sec, p_sec, dp_dT_sec, dp_dv_sec, dp_dv;
766 double tol_p = 1.e-8;
767 while (std::fabs(f / pp_sat_secondary) > tol_p)
768 {
769 pp_sec = 0.;
770 dp_dv = 0.;
771 for (unsigned int i = 0; i < _n_secondary_vapors; i++)
772 {
773 _fp_secondary[i]->p_from_T_v(T, v_secondary / x_sec[i], p_sec, dp_dT_sec, dp_dv_sec);
774 pp_sec += p_sec;
775 dp_dv += dp_dv_sec / x_sec[i];
776 }
777 f = pp_sec - pp_sat_secondary;
778 df_dvs = dp_dv;
779 v_secondary -= f / df_dvs;
780 if (it++ > 15)
781 return getNaN();
782 }
783 }
784
785 xs = v_secondary / (v_primary + v_secondary);
786 }
787
788 return xs;
789}
Real f(Real x)
Test function for Brents method.
const std::vector< double > x
const double mu
const Real p
const double rho
const double T
const double v
registerMooseObject("FluidPropertiesApp", IdealRealGasMixtureFluidProperties)
Class for fluid properties of an arbitrary vapor mixture.
static constexpr const Real R_molar
molar (or universal) gas constant
virtual const SinglePhaseFluidProperties & getSecondaryFluidProperties(unsigned int i=0) const override
Gets a secondary component single-phase fluid properties.
const Real _T_mix_max
maximum temperature of all components
Real xs_prim_from_p_T(Real p, Real T, const std::vector< Real > &x) const
Mass fraction of primary (condensable) component at saturation from pressure and temperature.
const unsigned int _n_secondary_vapors
Number of secondary vapors.
IdealRealGasMixtureFluidProperties(const InputParameters &parameters)
std::vector< const SinglePhaseFluidProperties * > _fp_secondary
Secondary vapor fluid properties.
const SinglePhaseFluidProperties *const _fp_primary
Primary vapor fluid properties.
virtual const SinglePhaseFluidProperties & getPrimaryFluidProperties() const override
Gets the primary component single-phase fluid properties.
static InputParameters validParams()
const std::vector< UserObjectName > _fp_secondary_names
Names of secondary vapor fluid properties.
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void deprecateParam(const std::string &old_name, const std::string &new_name, const std::string &removal_date)
void mooseWarning(Args &&... args) const
Interface class for producing errors, warnings, or just quiet NaNs.
Real getNaN() const
Throws an error or returns a NaN with or without a warning, with a default message.
static InputParameters validParams()
Common class for single phase fluid properties.
virtual Real criticalInternalEnergy() const
Critical specific internal energy.
virtual void v_e_spndl_from_T(Real T, Real &v, Real &e) const
Specific internal energy from temperature and specific volume.
virtual Real e_spndl_from_v(Real v) const
Specific internal energy from temperature and specific volume.
virtual Real criticalDensity() const
Critical density.
virtual Real molarMass() const
Molar mass [kg/mol].
virtual Real criticalTemperature() const
Critical temperature.
Base class for fluid properties of vapor mixtures.
Real primaryMassFraction(const std::vector< Real > &x) const
Computes the mass fraction of the primary vapor given mass fractions of the secondary vapors.
void imperfectJacobianMessage(Args... args) const
Real root(std::function< Real(Real)> const &f, Real x1, Real x2, Real tol=1.0e-12)
Finds the root of a function using Brent's method.
void bracket(std::function< Real(Real)> const &f, Real &x1, Real &x2)
Function to bracket a root of a given function.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real