LCOV - code coverage report
Current view: top level - src/fluidproperties - IdealRealGasMixtureFluidProperties.C (source / functions) Hit Total Coverage
Test: idaholab/moose fluid_properties: #31405 (292dce) with base fef103 Lines: 224 265 84.5 %
Date: 2025-09-04 07:53:14 Functions: 49 52 94.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "IdealRealGasMixtureFluidProperties.h"
      11             : #include "SinglePhaseFluidProperties.h"
      12             : #include "BrentsMethod.h"
      13             : #include <numeric>
      14             : 
      15             : registerMooseObject("FluidPropertiesApp", IdealRealGasMixtureFluidProperties);
      16             : 
      17             : /**
      18             :  * Defines the value and derivative methods from (v,e) for a property y
      19             :  * using T(v,e) and y(T,v).
      20             :  */
      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             : 
      52             : /**
      53             :  * Defines the value and derivative methods from (p,T) for a property y
      54             :  * using v(p,T) and y(T,v).
      55             :  */
      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             : 
      87             : /**
      88             :  * Defines the value and derivative methods from (T,v) for a mass-specific property y
      89             :  */
      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             :       for (unsigned int j = 0; j < _n_secondary_vapors; j++)                                       \
     136             :       {                                                                                            \
     137             :         if (j == i)                                                                                \
     138             :           continue;                                                                                \
     139             :         if (_n_secondary_vapors > 1)                                                               \
     140             :           imperfectJacobianMessage(                                                                \
     141             :               "The mass fraction derivatives in the following "                                    \
     142             :               "function have not been tested for mixtures of 3 or more components:\n\n",           \
     143             :               __PRETTY_FUNCTION__);                                                                \
     144             :         const Real dxj_dxi = 0;                                                                    \
     145             :         dy_dx[i] += dxj_dxi * y_sec[j] - x[j] * dy_dv_sec[j] * v / (x[j] * x[j]) * dxj_dxi;        \
     146             :       }                                                                                            \
     147             :       const Real dx_primary_dxi = -x_primary / (1. - x[i]);                                        \
     148             :       dy_dx[i] += dx_primary_dxi * y_primary -                                                     \
     149             :                   x_primary * dy_dv_primary * v / (x_primary * x_primary) * dx_primary_dxi;        \
     150             :     }                                                                                              \
     151             :   }
     152             : 
     153             : /**
     154             :  * Defines the value and derivative methods from (T,v) for a transport property y
     155             :  */
     156             : #define define_transport_prop_from_T_v(prop)                                                       \
     157             :   Real IdealRealGasMixtureFluidProperties::prop##_from_T_v(                                        \
     158             :       Real T, Real v, const std::vector<Real> & x) const                                           \
     159             :   {                                                                                                \
     160             :     const Real x_primary = primaryMassFraction(x);                                                 \
     161             :     Real M_primary = _fp_primary->molarMass();                                                     \
     162             :                                                                                                    \
     163             :     Real sum = x_primary / M_primary;                                                              \
     164             :     for (unsigned int i = 0; i < _n_secondary_vapors; i++)                                         \
     165             :       sum += x[i] / _fp_secondary[i]->molarMass();                                                 \
     166             :     const Real M_star = 1. / sum;                                                                  \
     167             :                                                                                                    \
     168             :     const Real vp = v / x_primary;                                                                 \
     169             :     const Real ep = _fp_primary->e_from_T_v(T, vp);                                                \
     170             :     const Real yp = _fp_primary->prop##_from_v_e(vp, ep);                                          \
     171             :     Real y = x_primary * M_star / M_primary * yp;                                                  \
     172             :                                                                                                    \
     173             :     for (unsigned int i = 0; i < _n_secondary_vapors; i++)                                         \
     174             :     {                                                                                              \
     175             :       const Real vi = v / x[i];                                                                    \
     176             :       const Real ei = _fp_secondary[i]->e_from_T_v(T, vp);                                         \
     177             :       const Real Mi = _fp_secondary[i]->molarMass();                                               \
     178             :       const Real yi = _fp_secondary[i]->prop##_from_v_e(vi, ei);                                   \
     179             :       y += x[i] * M_star / Mi * yi;                                                                \
     180             :     }                                                                                              \
     181             :                                                                                                    \
     182             :     return y;                                                                                      \
     183             :   }                                                                                                \
     184             :                                                                                                    \
     185             :   void IdealRealGasMixtureFluidProperties::prop##_from_T_v(Real T,                                 \
     186             :                                                            Real v,                                 \
     187             :                                                            const std::vector<Real> & x,            \
     188             :                                                            Real & y,                               \
     189             :                                                            Real & dy_dT,                           \
     190             :                                                            Real & dy_dv,                           \
     191             :                                                            std::vector<Real> & dy_dx) const        \
     192             :   {                                                                                                \
     193             :     const Real x_primary = primaryMassFraction(x);                                                 \
     194             :     Real M_primary = _fp_primary->molarMass();                                                     \
     195             :                                                                                                    \
     196             :     Real sum = x_primary / M_primary;                                                              \
     197             :     for (unsigned int i = 0; i < _n_secondary_vapors; i++)                                         \
     198             :       sum += x[i] / _fp_secondary[i]->molarMass();                                                 \
     199             :     const Real M_star = 1. / sum;                                                                  \
     200             :                                                                                                    \
     201             :     const Real vp = v / x_primary;                                                                 \
     202             :     const Real ep = _fp_primary->e_from_T_v(T, vp);                                                \
     203             :     const Real yp = _fp_primary->prop##_from_v_e(vp, ep);                                          \
     204             :     y = x_primary * M_star / M_primary * yp;                                                       \
     205             :                                                                                                    \
     206             :     imperfectJacobianMessage("The temperature and specific volume derivatives in the following "   \
     207             :                              "function are currently neglected:\n\n",                              \
     208             :                              __PRETTY_FUNCTION__);                                                 \
     209             :     dy_dT = 0;                                                                                     \
     210             :     dy_dv = 0;                                                                                     \
     211             :                                                                                                    \
     212             :     Real sum_yj = 0;                                                                               \
     213             :     dy_dx.resize(_n_secondary_vapors);                                                             \
     214             :     for (unsigned int i = 0; i < _n_secondary_vapors; i++)                                         \
     215             :     {                                                                                              \
     216             :       const Real vi = v / x[i];                                                                    \
     217             :       const Real ei = _fp_secondary[i]->e_from_T_v(T, vi);                                         \
     218             :       const Real Mi = _fp_secondary[i]->molarMass();                                               \
     219             :       const Real yi = _fp_secondary[i]->prop##_from_v_e(vi, ei);                                   \
     220             :       y += x[i] * M_star / Mi * yi;                                                                \
     221             :       dy_dx[i] = -M_star / M_primary * yp -                                                        \
     222             :                  x_primary * (1 / Mi - 1 / M_primary) * M_star * M_star / M_primary * yp +         \
     223             :                  M_star / Mi * yi;                                                                 \
     224             :       sum_yj += x[i] * yi / Mi;                                                                    \
     225             :     }                                                                                              \
     226             :                                                                                                    \
     227             :     for (unsigned int i = 0; i < _n_secondary_vapors; i++)                                         \
     228             :     {                                                                                              \
     229             :       const Real Mi = _fp_secondary[i]->molarMass();                                               \
     230             :       dy_dx[i] += -M_star * M_star * (1 / Mi - 1 / M_primary) * sum_yj;                            \
     231             :     }                                                                                              \
     232             :   }
     233             : 
     234             : // clang-format off
     235         784 : define_mass_specific_prop_from_T_v(e)
     236         391 : define_mass_specific_prop_from_T_v(s)
     237             : 
     238         154 : define_transport_prop_from_T_v(mu)
     239         154 : define_transport_prop_from_T_v(k)
     240             : 
     241          28 : define_from_p_T_using_T_v(e)
     242          43 : define_from_p_T_using_T_v(s)
     243          44 : define_from_p_T_using_T_v(c)
     244          44 : define_from_p_T_using_T_v(cp)
     245          44 : define_from_p_T_using_T_v(cv)
     246          43 : define_from_p_T_using_T_v(mu)
     247          43 : define_from_p_T_using_T_v(k)
     248             : 
     249          27 : define_from_v_e_using_T_v(p)
     250          11 : define_from_v_e_using_T_v(c)
     251             : 
     252         119 : InputParameters IdealRealGasMixtureFluidProperties::validParams()
     253             : // clang-format on
     254             : {
     255         119 :   InputParameters params = VaporMixtureFluidProperties::validParams();
     256         119 :   params += NaNInterface::validParams();
     257             : 
     258         119 :   params.addClassDescription("Class for fluid properties of an arbitrary vapor mixture");
     259             : 
     260         238 :   params.addRequiredParam<UserObjectName>(
     261             :       "fp_primary", "Name of fluid properties user object for primary vapor component");
     262         238 :   params.addRequiredParam<std::vector<UserObjectName>>(
     263             :       "fp_secondary", "Name of fluid properties user object(s) for secondary vapor component(s)");
     264         238 :   params.addParam<Real>("_T_mix_max", 1300., "Maximum temperature of the mixture");
     265             : 
     266         119 :   return params;
     267           0 : }
     268             : 
     269          67 : IdealRealGasMixtureFluidProperties::IdealRealGasMixtureFluidProperties(
     270          67 :     const InputParameters & parameters)
     271             :   : VaporMixtureFluidProperties(parameters),
     272             :     NaNInterface(this),
     273          67 :     _fp_primary(&getUserObject<SinglePhaseFluidProperties>("fp_primary")),
     274         201 :     _fp_secondary_names(getParam<std::vector<UserObjectName>>("fp_secondary")),
     275          67 :     _n_secondary_vapors(_fp_secondary_names.size()),
     276         201 :     _T_mix_max(getParam<Real>("_T_mix_max"))
     277             : {
     278          67 :   _fp_secondary.resize(_n_secondary_vapors);
     279         156 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     280          89 :     _fp_secondary[i] = &getUserObjectByName<SinglePhaseFluidProperties>(_fp_secondary_names[i]);
     281          67 : }
     282             : 
     283             : const SinglePhaseFluidProperties &
     284           0 : IdealRealGasMixtureFluidProperties::getPrimaryFluidProperties() const
     285             : {
     286           0 :   return *_fp_primary;
     287             : }
     288             : 
     289             : const SinglePhaseFluidProperties &
     290           0 : IdealRealGasMixtureFluidProperties::getSecondaryFluidProperties(unsigned int i) const
     291             : {
     292             :   mooseAssert(i < getNumberOfSecondaryVapors(), "Requested secondary index too high.");
     293           0 :   return *_fp_secondary[i];
     294             : }
     295             : 
     296             : Real
     297          59 : IdealRealGasMixtureFluidProperties::T_from_v_e(Real v, Real e, const std::vector<Real> & x) const
     298             : {
     299          59 :   Real v_primary = v / primaryMassFraction(x);
     300          59 :   static const Real vc = 1. / _fp_primary->criticalDensity();
     301          59 :   static const Real ec = _fp_primary->criticalInternalEnergy();
     302             : 
     303             :   // Initial estimate of a bracketing interval for the temperature
     304             :   Real lower_temperature, upper_temperature;
     305          59 :   if (v_primary > vc)
     306             :   {
     307          59 :     Real e_sat_primary = _fp_primary->e_spndl_from_v(v_primary);
     308          59 :     lower_temperature = _fp_primary->T_from_v_e(v_primary, e_sat_primary);
     309             :   }
     310             :   else
     311           0 :     lower_temperature = _fp_primary->T_from_v_e(v_primary, ec);
     312             : 
     313          59 :   upper_temperature = _T_mix_max;
     314             : 
     315             :   // Use BrentsMethod to find temperature
     316         336 :   auto energy_diff = [&v, &e, &x, this](Real T) { return this->e_from_T_v(T, v, x) - e; };
     317             : 
     318          59 :   BrentsMethod::bracket(energy_diff, lower_temperature, upper_temperature);
     319         118 :   return BrentsMethod::root(energy_diff, lower_temperature, upper_temperature);
     320             : }
     321             : 
     322             : void
     323           3 : IdealRealGasMixtureFluidProperties::T_from_v_e(Real v,
     324             :                                                Real e,
     325             :                                                const std::vector<Real> & x,
     326             :                                                Real & T,
     327             :                                                Real & dT_dv,
     328             :                                                Real & dT_de,
     329             :                                                std::vector<Real> & dT_dx) const
     330             : {
     331           3 :   T = T_from_v_e(v, e, x);
     332             : 
     333             :   Real e_unused, de_dT_v, de_dv_T;
     334             :   std::vector<Real> de_dx_Tv;
     335           3 :   e_from_T_v(T, v, x, e_unused, de_dT_v, de_dv_T, de_dx_Tv);
     336             : 
     337           3 :   dT_dv = -de_dv_T / de_dT_v;
     338           3 :   dT_de = 1.0 / de_dT_v;
     339           3 :   dT_dx.resize(_n_secondary_vapors);
     340           6 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     341           3 :     dT_dx[i] = -de_dx_Tv[i] / de_dT_v;
     342           3 : }
     343             : 
     344             : Real
     345          27 : IdealRealGasMixtureFluidProperties::rho_from_p_T(Real p, Real T, const std::vector<Real> & x) const
     346             : {
     347          27 :   return 1.0 / v_from_p_T(p, T, x);
     348             : }
     349             : 
     350             : void
     351           1 : IdealRealGasMixtureFluidProperties::rho_from_p_T(Real p,
     352             :                                                  Real T,
     353             :                                                  const std::vector<Real> & x,
     354             :                                                  Real & rho,
     355             :                                                  Real & drho_dp,
     356             :                                                  Real & drho_dT,
     357             :                                                  std::vector<Real> & drho_dx) const
     358             : {
     359             :   Real v, dv_dp, dv_dT;
     360             :   std::vector<Real> dv_dx;
     361           1 :   v_from_p_T(p, T, x, v, dv_dp, dv_dT, dv_dx);
     362             : 
     363           1 :   rho = 1.0 / v;
     364           1 :   const Real drho_dv = -1.0 / (v * v);
     365           1 :   drho_dp = drho_dv * dv_dp;
     366           1 :   drho_dT = drho_dv * dv_dT;
     367           1 :   drho_dx.resize(_n_secondary_vapors);
     368           2 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     369           1 :     drho_dx[i] = drho_dv * dv_dx[i];
     370           1 : }
     371             : 
     372             : Real
     373         329 : IdealRealGasMixtureFluidProperties::v_from_p_T(Real p, Real T, const std::vector<Real> & x) const
     374             : {
     375         329 :   const Real x_primary = primaryMassFraction(x);
     376         329 :   Real M_primary = _fp_primary->molarMass();
     377             : 
     378         329 :   Real sum = x_primary / M_primary;
     379         676 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     380         347 :     sum += x[i] / _fp_secondary[i]->molarMass();
     381         329 :   Real M_star = 1. / sum;
     382         329 :   Real v_ideal = R_molar * T / (M_star * p);
     383             : 
     384             :   // check range of validity for primary (condensable) component
     385         329 :   static const Real Tc = _fp_primary->criticalTemperature();
     386         329 :   static const Real vc = 1. / _fp_primary->criticalDensity();
     387             :   Real v_spndl, e_spndl;
     388         329 :   if (T < Tc)
     389          87 :     _fp_primary->v_e_spndl_from_T(T, v_spndl, e_spndl);
     390             :   else
     391         242 :     v_spndl = vc;
     392             : 
     393         329 :   Real lower_spec_volume = v_spndl * x_primary;
     394         329 :   Real upper_spec_volume = v_ideal; // p*v/(RT) <= 1
     395             : 
     396             :   // Initial estimate of a bracketing interval for the temperature
     397         329 :   Real p_max = p_from_T_v(T, lower_spec_volume, x);
     398         329 :   if (p > p_max || upper_spec_volume < lower_spec_volume)
     399           0 :     return getNaN();
     400             : 
     401             :   // Use BrentsMethod to find temperature
     402        4479 :   auto pressure_diff = [&T, &p, &x, this](Real v) { return this->p_from_T_v(T, v, x) - p; };
     403             : 
     404         329 :   BrentsMethod::bracket(pressure_diff, lower_spec_volume, upper_spec_volume);
     405         658 :   return BrentsMethod::root(pressure_diff, lower_spec_volume, upper_spec_volume);
     406             : }
     407             : 
     408             : void
     409           9 : IdealRealGasMixtureFluidProperties::v_from_p_T(Real p,
     410             :                                                Real T,
     411             :                                                const std::vector<Real> & x,
     412             :                                                Real & v,
     413             :                                                Real & dv_dp,
     414             :                                                Real & dv_dT,
     415             :                                                std::vector<Real> & dv_dx) const
     416             : {
     417           9 :   v = v_from_p_T(p, T, x);
     418             : 
     419             :   Real p_unused, dp_dT, dp_dv;
     420             :   std::vector<Real> dp_dx;
     421           9 :   p_from_T_v(T, v, x, p_unused, dp_dT, dp_dv, dp_dx);
     422             : 
     423           9 :   dv_dp = 1. / dp_dv;
     424           9 :   dv_dT = -dp_dT / dp_dv;
     425           9 :   dv_dx.resize(_n_secondary_vapors);
     426          18 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     427           9 :     dv_dx[i] = -dp_dx[i] / dp_dv;
     428           9 : }
     429             : 
     430             : Real
     431           8 : IdealRealGasMixtureFluidProperties::e_from_p_rho(Real p,
     432             :                                                  Real rho,
     433             :                                                  const std::vector<Real> & x) const
     434             : {
     435           8 :   Real v = 1. / rho;
     436           8 :   Real T = T_from_p_v(p, v, x);
     437             : 
     438           8 :   return e_from_T_v(T, v, x);
     439             : }
     440             : 
     441             : void
     442           1 : IdealRealGasMixtureFluidProperties::e_from_p_rho(Real p,
     443             :                                                  Real rho,
     444             :                                                  const std::vector<Real> & x,
     445             :                                                  Real & e,
     446             :                                                  Real & de_dp,
     447             :                                                  Real & de_drho,
     448             :                                                  std::vector<Real> & de_dx) const
     449             : {
     450           1 :   Real v = 1. / rho;
     451           1 :   Real T = T_from_p_v(p, v, x);
     452             :   Real p_, dp_dT, dp_dv, de_dT, de_dv;
     453             :   std::vector<Real> dp_dx_Tv;
     454             :   std::vector<Real> de_dx_Tv;
     455             : 
     456           1 :   p_from_T_v(T, v, x, p_, dp_dT, dp_dv, dp_dx_Tv);
     457           1 :   e_from_T_v(T, v, x, e, de_dT, de_dv, de_dx_Tv);
     458             : 
     459           1 :   de_dp = de_dT / dp_dT;
     460           1 :   de_drho = (-v * v) * (de_dv - de_dT * dp_dv / dp_dT);
     461             : 
     462             :   // Derivatives with respect to mass fractions:
     463           2 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     464             :   {
     465           1 :     de_dx[i] = de_dx_Tv[i] - de_dT * dp_dx_Tv[i] / dp_dT;
     466             :   }
     467           1 : }
     468             : 
     469             : Real
     470          18 : IdealRealGasMixtureFluidProperties::T_from_p_v(Real p, Real v, const std::vector<Real> & x) const
     471             : {
     472          18 :   Real v_primary = v / primaryMassFraction(x);
     473          18 :   static const Real vc = 1. / _fp_primary->criticalDensity();
     474          18 :   static const Real ec = _fp_primary->criticalInternalEnergy();
     475             : 
     476             :   // Initial estimate of a bracketing interval for the temperature
     477             :   Real lower_temperature, upper_temperature;
     478          18 :   if (v_primary > vc)
     479             :   {
     480          18 :     Real e_sat_primary = _fp_primary->e_spndl_from_v(v_primary);
     481          18 :     lower_temperature = _fp_primary->T_from_v_e(v_primary, e_sat_primary);
     482             :   }
     483             :   else
     484           0 :     lower_temperature = _fp_primary->T_from_v_e(v_primary, ec);
     485             : 
     486          18 :   upper_temperature = _T_mix_max;
     487             : 
     488             :   // Use BrentsMethod to find temperature
     489          99 :   auto pressure_diff = [&p, &v, &x, this](Real T) { return this->p_from_T_v(T, v, x) - p; };
     490             : 
     491          18 :   BrentsMethod::bracket(pressure_diff, lower_temperature, upper_temperature);
     492          36 :   return BrentsMethod::root(pressure_diff, lower_temperature, upper_temperature);
     493             : }
     494             : 
     495             : void
     496           1 : IdealRealGasMixtureFluidProperties::T_from_p_v(Real p,
     497             :                                                Real v,
     498             :                                                const std::vector<Real> & x,
     499             :                                                Real & T,
     500             :                                                Real & dT_dp,
     501             :                                                Real & dT_dv,
     502             :                                                std::vector<Real> & dT_dx) const
     503             : {
     504           1 :   T = T_from_p_v(p, v, x);
     505             : 
     506             :   // pressure and derivatives
     507             :   Real p_unused, dp_dT_v, dp_dv_T;
     508             :   std::vector<Real> dp_dx_Tv;
     509           1 :   p_from_T_v(T, v, x, p_unused, dp_dT_v, dp_dv_T, dp_dx_Tv);
     510             : 
     511             :   // Compute derivatives using the following rules:
     512           1 :   dT_dp = 1. / dp_dT_v;
     513           1 :   dT_dv = -dp_dv_T / dp_dT_v;
     514             : 
     515             :   // Derivatives with respect to mass fractions:
     516           2 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     517           1 :     dT_dx[i] = -dp_dx_Tv[i] / dp_dT_v;
     518           1 : }
     519             : 
     520             : Real
     521        4939 : IdealRealGasMixtureFluidProperties::p_from_T_v(Real T, Real v, const std::vector<Real> & x) const
     522             : {
     523        4939 :   const Real x_primary = primaryMassFraction(x);
     524        4939 :   Real p = _fp_primary->p_from_T_v(T, v / x_primary);
     525             : 
     526       10058 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     527        5119 :     p += _fp_secondary[i]->p_from_T_v(T, v / x[i]);
     528             : 
     529        4939 :   return p;
     530             : }
     531             : 
     532             : void
     533         121 : IdealRealGasMixtureFluidProperties::p_from_T_v(Real T,
     534             :                                                Real v,
     535             :                                                const std::vector<Real> & x,
     536             :                                                Real & p,
     537             :                                                Real & dp_dT,
     538             :                                                Real & dp_dv,
     539             :                                                std::vector<Real> & dp_dx) const
     540             : {
     541         121 :   const Real x_primary = primaryMassFraction(x);
     542             : 
     543             :   Real p_primary, dp_dT_primary, dp_dv_primary;
     544         121 :   _fp_primary->p_from_T_v(T, v / x_primary, p_primary, dp_dT_primary, dp_dv_primary);
     545         121 :   p = p_primary;
     546         121 :   dp_dT = dp_dT_primary;
     547         121 :   dp_dv = dp_dv_primary / x_primary;
     548             : 
     549             :   // get the partial pressures and their derivatives first
     550             :   Real p_sec, dp_dT_sec, dxj_dxi;
     551         121 :   std::vector<Real> dp_dv_sec(_n_secondary_vapors);
     552         242 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     553             :   {
     554         121 :     _fp_secondary[i]->p_from_T_v(T, v / x[i], p_sec, dp_dT_sec, dp_dv_sec[i]);
     555         121 :     p += p_sec;
     556         121 :     dp_dT += dp_dT_sec;
     557         121 :     dp_dv += dp_dv_sec[i] / x[i];
     558             :   }
     559             : 
     560             :   // get the composition dependent derivatives of the secondary vapors
     561         121 :   dp_dx.resize(_n_secondary_vapors);
     562         242 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     563             :   {
     564         121 :     dp_dx[i] = -dp_dv_sec[i] * v / (x[i] * x[i]);
     565         242 :     for (unsigned int j = 0; j < _n_secondary_vapors; j++)
     566             :     {
     567         121 :       if (j == i)
     568         121 :         continue;
     569             :       // Note: this was previously as follows, but we were unable to understand
     570             :       // why and this is not currently tested (requires 3 or more components in
     571             :       // the mixture):
     572             :       // dxj_dxi = -x[j] / (1. - x[i]);
     573           0 :       if (_n_secondary_vapors > 1)
     574           0 :         imperfectJacobianMessage(
     575             :             "The mass fraction derivatives in the following "
     576             :             "function have not been tested for mixtures of 3 or more components:\n\n",
     577             :             __PRETTY_FUNCTION__);
     578             :       dxj_dxi = 0;
     579           0 :       dp_dx[i] += -dp_dv_sec[j] * v / (x[j] * x[j]) * dxj_dxi;
     580             :     }
     581         121 :     dp_dx[i] += -dp_dv_primary * v / (x_primary * x_primary) * (-x_primary / (1. - x[i]));
     582             :   }
     583         121 : }
     584             : 
     585             : Real
     586          57 : IdealRealGasMixtureFluidProperties::c_from_T_v(Real T, Real v, const std::vector<Real> & x) const
     587             : {
     588             :   Real p, dp_dT, dp_dv;
     589             :   std::vector<Real> dp_dx;
     590          57 :   p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
     591             : 
     592             :   Real s, ds_dT, ds_dv;
     593             :   std::vector<Real> ds_dx;
     594          57 :   s_from_T_v(T, v, x, s, ds_dT, ds_dv, ds_dx);
     595             : 
     596          57 :   const Real dp_dv_s = dp_dv - dp_dT * ds_dv / ds_dT;
     597             : 
     598          57 :   if (dp_dv_s >= 0)
     599           0 :     mooseWarning("c_from_T_v(): dp_dv_s = ", dp_dv_s, ". Should be negative.");
     600          57 :   return v * std::sqrt(-dp_dv_s);
     601          57 : }
     602             : 
     603             : void
     604           2 : IdealRealGasMixtureFluidProperties::c_from_T_v(Real T,
     605             :                                                Real v,
     606             :                                                const std::vector<Real> & x,
     607             :                                                Real & c,
     608             :                                                Real & dc_dT,
     609             :                                                Real & dc_dv,
     610             :                                                std::vector<Real> & dc_dx) const
     611             : {
     612           2 :   c = c_from_T_v(T, v, x);
     613             : 
     614             :   // For derived properties, we would need higher order derivatives;
     615             :   // therefore, numerical derivatives are used here.
     616             :   const Real dT = 1.e-6;
     617           2 :   const Real T_perturbed = T + dT;
     618           2 :   Real c_perturbed = c_from_T_v(T_perturbed, v, x);
     619           2 :   dc_dT = (c_perturbed - c) / dT;
     620             : 
     621           2 :   const Real dv = v * 1.e-6;
     622           2 :   const Real v_perturbed = v + dv;
     623           2 :   c_perturbed = c_from_T_v(T, v_perturbed, x);
     624           2 :   dc_dv = (c_perturbed - c) / dv;
     625             : 
     626           2 :   dc_dx.resize(_n_secondary_vapors);
     627           4 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     628             :   {
     629           2 :     std::vector<Real> x_perturbed(x);
     630             :     const Real dx_i = 1e-6;
     631           4 :     for (unsigned int j = 0; j < _n_secondary_vapors; j++)
     632             :     {
     633           2 :       if (j != i)
     634           0 :         x_perturbed[j] =
     635           0 :             x[j] * (1.0 - (x[i] + dx_i)) / (1.0 - x[i]); // recalculate new mass fractions
     636             :     }
     637           2 :     x_perturbed[i] += dx_i;
     638           2 :     c_perturbed = c_from_T_v(T, v, x_perturbed);
     639           2 :     dc_dx[i] = ((c_perturbed - c) / dx_i);
     640           2 :   }
     641           2 : }
     642             : 
     643             : Real
     644          49 : IdealRealGasMixtureFluidProperties::cp_from_T_v(Real T, Real v, const std::vector<Real> & x) const
     645             : {
     646          49 :   const Real x_primary = primaryMassFraction(x);
     647             : 
     648             :   Real p, dp_dT, dp_dv;
     649             :   std::vector<Real> dp_dx;
     650          49 :   p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
     651             : 
     652             :   Real h, dh_dT, dh_dv;
     653          49 :   _fp_primary->h_from_T_v(T, v / x_primary, h, dh_dT, dh_dv);
     654          49 :   const Real cp_primary = dh_dT - dh_dv * dp_dT / dp_dv;
     655          49 :   Real cp = x_primary * cp_primary;
     656             : 
     657          98 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     658             :   {
     659          49 :     _fp_secondary[i]->h_from_T_v(T, v / x[i], h, dh_dT, dh_dv);
     660          49 :     const Real cp_sec = dh_dT - dh_dv * dp_dT / dp_dv;
     661          49 :     cp += x[i] * cp_sec;
     662             :   }
     663             : 
     664          49 :   return cp;
     665          49 : }
     666             : 
     667             : void
     668           2 : IdealRealGasMixtureFluidProperties::cp_from_T_v(Real T,
     669             :                                                 Real v,
     670             :                                                 const std::vector<Real> & x,
     671             :                                                 Real & cp,
     672             :                                                 Real & dcp_dT,
     673             :                                                 Real & dcp_dv,
     674             :                                                 std::vector<Real> & dcp_dx) const
     675             : {
     676           2 :   const Real x_primary = primaryMassFraction(x);
     677             : 
     678             :   Real p, dp_dT, dp_dv;
     679             :   std::vector<Real> dp_dx;
     680           2 :   p_from_T_v(T, v, x, p, dp_dT, dp_dv, dp_dx);
     681             : 
     682             :   Real h, dh_dT, dh_dv;
     683           2 :   _fp_primary->h_from_T_v(T, v / x_primary, h, dh_dT, dh_dv);
     684           2 :   const Real cp_primary = dh_dT - dh_dv * dp_dT / dp_dv;
     685           2 :   cp = x_primary * cp_primary;
     686             : 
     687             :   // Neglect these for now. These require higher-order derivatives, so a finite
     688             :   // difference should probably be used, as for sound speed.
     689           2 :   imperfectJacobianMessage("The temperature and specific volume derivatives in the following "
     690             :                            "function are currently neglected:\n\n",
     691             :                            __PRETTY_FUNCTION__);
     692           2 :   dcp_dT = 0;
     693           2 :   dcp_dv = 0;
     694             : 
     695           2 :   dcp_dx.resize(_n_secondary_vapors);
     696           4 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     697             :   {
     698           2 :     _fp_secondary[i]->h_from_T_v(T, v / x[i], h, dh_dT, dh_dv);
     699           2 :     const Real cp_sec = dh_dT - dh_dv * dp_dT / dp_dv;
     700           2 :     cp += x[i] * cp_sec;
     701           2 :     dcp_dx[i] = cp_sec - cp_primary;
     702             :   }
     703           2 : }
     704             : 
     705             : Real
     706          49 : IdealRealGasMixtureFluidProperties::cv_from_T_v(Real T, Real v, const std::vector<Real> & x) const
     707             : {
     708          49 :   const Real x_primary = primaryMassFraction(x);
     709          49 :   Real cv = x_primary * _fp_primary->cv_from_T_v(T, v / x_primary);
     710             : 
     711          98 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     712          49 :     cv += x[i] * _fp_secondary[i]->cv_from_T_v(T, v / x[i]);
     713             : 
     714          49 :   return cv;
     715             : }
     716             : 
     717             : void
     718           2 : IdealRealGasMixtureFluidProperties::cv_from_T_v(Real T,
     719             :                                                 Real v,
     720             :                                                 const std::vector<Real> & x,
     721             :                                                 Real & cv,
     722             :                                                 Real & dcv_dT,
     723             :                                                 Real & dcv_dv,
     724             :                                                 std::vector<Real> & dcv_dx) const
     725             : {
     726           2 :   const Real x_primary = primaryMassFraction(x);
     727           2 :   const Real cv_primary = _fp_primary->cv_from_T_v(T, v / x_primary);
     728           2 :   cv = x_primary * cv_primary;
     729             : 
     730             :   // Neglect these for now. These require higher-order derivatives, so a finite
     731             :   // difference should probably be used, as for sound speed.
     732           2 :   imperfectJacobianMessage("The temperature and specific volume derivatives in the following "
     733             :                            "function are currently neglected:\n\n",
     734             :                            __PRETTY_FUNCTION__);
     735           2 :   dcv_dT = 0;
     736           2 :   dcv_dv = 0;
     737             : 
     738           2 :   dcv_dx.resize(_n_secondary_vapors);
     739           4 :   for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     740             :   {
     741           2 :     const Real cv_sec = _fp_secondary[i]->cv_from_T_v(T, v / x[i]);
     742           2 :     cv += x[i] * cv_sec;
     743           2 :     dcv_dx[i] = cv_sec - cv_primary;
     744             :   }
     745           2 : }
     746             : 
     747             : Real
     748           0 : IdealRealGasMixtureFluidProperties::xs_prim_from_p_T(Real p,
     749             :                                                      Real T,
     750             :                                                      const std::vector<Real> & x) const
     751             : {
     752           0 :   Real T_c = _fp_primary->criticalTemperature();
     753             :   Real xs;
     754           0 :   if (T > T_c)
     755             :     // return 1. to indicate that the primary fluid will not condense for
     756             :     // given (p,T)
     757             :     xs = 1.;
     758             :   else
     759             :   {
     760           0 :     Real pp_sat = _fp_primary->pp_sat_from_p_T(p, T);
     761           0 :     if (pp_sat < 0.)
     762             :     {
     763             :       // return 1. to indicate that the primary fluid will not condense for
     764             :       // given (p,T)
     765             :       xs = 1.;
     766             :       return xs;
     767             :     }
     768           0 :     Real v_primary = _fp_primary->v_from_p_T(pp_sat, T);
     769           0 :     Real pp_sat_secondary = p - pp_sat;
     770             : 
     771             :     Real v_secondary;
     772           0 :     if (_n_secondary_vapors == 1)
     773           0 :       v_secondary = _fp_secondary[0]->v_from_p_T(pp_sat_secondary, T);
     774             :     else
     775             :     {
     776           0 :       std::vector<Real> x_sec(_n_secondary_vapors);
     777           0 :       const Real x_primary = primaryMassFraction(x);
     778             :       Real sum = 0.;
     779           0 :       for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     780             :       {
     781           0 :         x_sec[i] = x[i] / (1. - x_primary);
     782           0 :         sum += x_sec[i] / _fp_secondary[i]->molarMass();
     783             :       }
     784           0 :       Real M_star = 1. / sum;
     785           0 :       v_secondary = R_molar * T / (M_star * pp_sat_secondary);
     786             :       int it = 0;
     787             :       double f = 1., df_dvs, pp_sec, p_sec, dp_dT_sec, dp_dv_sec, dp_dv;
     788             :       double tol_p = 1.e-8;
     789           0 :       while (std::fabs(f / pp_sat_secondary) > tol_p)
     790             :       {
     791             :         pp_sec = 0.;
     792             :         dp_dv = 0.;
     793           0 :         for (unsigned int i = 0; i < _n_secondary_vapors; i++)
     794             :         {
     795           0 :           _fp_secondary[i]->p_from_T_v(T, v_secondary / x_sec[i], p_sec, dp_dT_sec, dp_dv_sec);
     796           0 :           pp_sec += p_sec;
     797           0 :           dp_dv += dp_dv_sec / x_sec[i];
     798             :         }
     799           0 :         f = pp_sec - pp_sat_secondary;
     800             :         df_dvs = dp_dv;
     801           0 :         v_secondary -= f / df_dvs;
     802           0 :         if (it++ > 15)
     803           0 :           return getNaN();
     804             :       }
     805           0 :     }
     806             : 
     807           0 :     xs = v_secondary / (v_primary + v_secondary);
     808             :   }
     809             : 
     810             :   return xs;
     811             : }

Generated by: LCOV version 1.14