LCOV - code coverage report
Current view: top level - src/materials - PorousFlowHystereticCapillaryPressure.C (source / functions) Hit Total Coverage
Test: idaholab/moose porous_flow: #32971 (54bef8) with base c6cf66 Lines: 413 419 98.6 %
Date: 2026-05-29 20:38:56 Functions: 24 24 100.0 %
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 "PorousFlowHystereticCapillaryPressure.h"
      11             : #include "PorousFlowCapillaryPressure.h"
      12             : 
      13             : registerMooseObject("PorousFlowApp", PorousFlowHystereticCapillaryPressure);
      14             : 
      15             : InputParameters
      16        4619 : PorousFlowHystereticCapillaryPressure::validParams()
      17             : {
      18        4619 :   InputParameters params = PorousFlowVariableBase::validParams();
      19        9238 :   params.addRequiredRangeCheckedParam<Real>(
      20             :       "alpha_d",
      21             :       "alpha_d > 0",
      22             :       "van Genuchten alpha parameter for the primary drying curve.  If using standard units, this "
      23             :       "is measured in Pa^-1.  Suggested value is around 1E-5");
      24        9238 :   params.addRequiredRangeCheckedParam<Real>(
      25             :       "alpha_w",
      26             :       "alpha_w > 0",
      27             :       "van Genuchten alpha parameter for the primary wetting curve.  If using standard units, this "
      28             :       "is measured in Pa^-1.  Suggested value is around 1E-5");
      29        9238 :   params.addRequiredRangeCheckedParam<Real>("n_d",
      30             :                                             "n_d > 1",
      31             :                                             "van Genuchten n parameter for the primary drying "
      32             :                                             "curve.  Dimensionless.  Suggested value is around 2");
      33        9238 :   params.addRequiredRangeCheckedParam<Real>("n_w",
      34             :                                             "n_w > 1",
      35             :                                             "van Genuchten n parameter for the primary wetting "
      36             :                                             "curve.  Dimensionless.  Suggested value is around 2");
      37        9238 :   params.addRequiredRangeCheckedParam<Real>(
      38             :       "S_l_min",
      39             :       "S_l_min >= 0 & S_l_min < 1",
      40             :       "Minimum liquid saturation for which the van Genuchten expression is valid.  If no lower "
      41             :       "extension is used then Pc = Pc_max for liquid saturation <= S_l_min");
      42       13857 :   params.addRangeCheckedParam<Real>(
      43             :       "S_lr",
      44        9238 :       0.0,
      45             :       "S_lr >= 0 & S_lr < 1",
      46             :       "Liquid residual saturation where the liquid relative permeability is zero.  This is used in "
      47             :       "the Land expression to find S_gr_del.  Almost definitely you need to set S_lr equal to the "
      48             :       "quantity used for your relative-permeability curves.  Almost definitely you should set S_lr "
      49             :       "> S_l_min");
      50        9238 :   params.addRequiredRangeCheckedParam<Real>(
      51             :       "S_gr_max",
      52             :       "S_gr_max >= 0",
      53             :       "Residual gas saturation.  1 - S_gr_max is the maximum saturation for which the "
      54             :       "van Genuchten expression is valid for the wetting curve.  You must ensure S_gr_max < 1 - "
      55             :       "S_l_min.  Often S_gr_max = -0.3136 * ln(porosity) - 0.1334 is used");
      56       13857 :   params.addRangeCheckedParam<Real>(
      57             :       "Pc_max",
      58        9238 :       std::numeric_limits<Real>::max(),
      59             :       "Pc_max > 0",
      60             :       "Value of capillary pressure at which the lower extension commences.  The default value "
      61             :       "means capillary pressure uses the van Genuchten expression for S > S_l_min and is "
      62             :       "'infinity' for S <= S_l_min.  This will result in poor convergence around S = S_l_min");
      63        9238 :   MooseEnum low_ext_enum("none quadratic exponential", "exponential");
      64        9238 :   params.addParam<MooseEnum>(
      65             :       "low_extension_type",
      66             :       low_ext_enum,
      67             :       "Type of extension to use for small liquid saturation values.  The extensions modify the "
      68             :       "capillary pressure for all saturation values less than S(Pc_max).  That is, if the "
      69             :       "van Genuchten "
      70             :       "expression would produce Pc > Pc_max, then the extension is used instead.  NONE: Simply "
      71             :       "cut-off the capillary-pressure at Pc_max, so that Pc <= Pc_max for all S.  QUADRATIC: Pc is "
      72             :       "a quadratic in S that is continuous and differentiable at S(Pc_max) and has zero derivative "
      73             :       "at S = 0 (hence, its value at S = 0 will be greater than Pc_max).  EXPONENTIAL: Pc is an "
      74             :       "exponential in S that is continuous and differentiable at S(Pc_max) (hence, its value at S "
      75             :       "= 0 will be much greater than Pc_max");
      76       13857 :   params.addRangeCheckedParam<Real>(
      77             :       "high_ratio",
      78        9238 :       0.9,
      79             :       "high_ratio > 0 & high_ratio < 1",
      80             :       "The extension to the wetting curves commences at high_ratio * (1 - S_gr_del), where 1 - "
      81             :       "S_gr_del is the value of the liquid saturation when Pc = 0 (on the wetting curve)");
      82        9238 :   MooseEnum high_ext_enum("none power", "power");
      83        9238 :   params.addParam<MooseEnum>(
      84             :       "high_extension_type",
      85             :       high_ext_enum,
      86             :       "Type of extension to use for the wetting curves when the liquid saturation is around 1.  "
      87             :       "The extensions modify the wetting capillary pressure for all saturation values greater than "
      88             :       "high_ratio * (1 - S_gr_del), where 1 - S_gr_del is the value of liquid saturation when the "
      89             :       "van Genuchten expression gives Pc = 0.  NONE: use the van Genuchten expression and when S > "
      90             :       "1 - S_gr_del, set Pc = 0.  POWER: Pc is proportional to (1 - S)^power, where the "
      91             :       "coefficient of proportionality and the power are chosen so the resulting curve is "
      92             :       "continuous and differentiable");
      93        4619 :   params.addClassDescription(
      94             :       "This Material computes information that is required for the computation of hysteretic "
      95             :       "capillary pressures in single and multi phase situations");
      96        4619 :   return params;
      97        4619 : }
      98             : 
      99        3558 : PorousFlowHystereticCapillaryPressure::PorousFlowHystereticCapillaryPressure(
     100        3558 :     const InputParameters & parameters)
     101             :   : PorousFlowVariableBase(parameters),
     102        3558 :     _alpha_d(getParam<Real>("alpha_d")),
     103        7116 :     _alpha_w(getParam<Real>("alpha_w")),
     104        7116 :     _n_d(getParam<Real>("n_d")),
     105        7116 :     _n_w(getParam<Real>("n_w")),
     106        7116 :     _s_l_min(getParam<Real>("S_l_min")),
     107        7116 :     _s_lr(getParam<Real>("S_lr")),
     108        7116 :     _s_gr_max(getParam<Real>("S_gr_max")),
     109        7116 :     _pc_max(getParam<Real>("Pc_max")),
     110        7116 :     _high_ratio(getParam<Real>("high_ratio")),
     111        3558 :     _low_ext_type(
     112        7116 :         getParam<MooseEnum>("low_extension_type")
     113             :             .getEnum<PorousFlowVanGenuchten::LowCapillaryPressureExtension::ExtensionStrategy>()),
     114        3558 :     _s_low_d(PorousFlowVanGenuchten::saturationHys(_pc_max, _s_l_min, 0.0, _alpha_d, _n_d)),
     115        3558 :     _dpc_low_d(
     116        3558 :         PorousFlowVanGenuchten::dcapillaryPressureHys(_s_low_d, _s_l_min, 0.0, _alpha_d, _n_d)),
     117        3558 :     _low_ext_d(_low_ext_type, _s_low_d, _pc_max, _dpc_low_d),
     118        3558 :     _s_low_w(PorousFlowVanGenuchten::saturationHys(_pc_max, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
     119        7116 :     _dpc_low_w(PorousFlowVanGenuchten::dcapillaryPressureHys(
     120        3558 :         _s_low_w, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
     121        3558 :     _low_ext_w(_low_ext_type, _s_low_w, _pc_max, _dpc_low_w),
     122        3558 :     _high_ext_type(
     123        3558 :         getParam<MooseEnum>("high_extension_type")
     124             :             .getEnum<PorousFlowVanGenuchten::HighCapillaryPressureExtension::ExtensionStrategy>()),
     125        3558 :     _s_high(_high_ratio * (1 - _s_gr_max)),
     126        3558 :     _pc_high(
     127        3558 :         PorousFlowVanGenuchten::capillaryPressureHys(_s_high, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
     128        7116 :     _dpc_high(PorousFlowVanGenuchten::dcapillaryPressureHys(
     129        3558 :         _s_high, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
     130        3558 :     _high_ext(_high_ext_type, _s_high, _pc_high, _dpc_high),
     131        3558 :     _hys_order(_nodal_material ? getMaterialProperty<unsigned>("PorousFlow_hysteresis_order_nodal")
     132       10210 :                                : getMaterialProperty<unsigned>("PorousFlow_hysteresis_order_qp")),
     133        7116 :     _hys_order_old(_nodal_material
     134        3558 :                        ? getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_nodal")
     135       10210 :                        : getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_qp")),
     136        3558 :     _hys_sat_tps(
     137        3558 :         _nodal_material
     138        3558 :             ? getMaterialProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     139             :                   "PorousFlow_hysteresis_saturation_tps_nodal")
     140       10210 :             : getMaterialProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     141             :                   "PorousFlow_hysteresis_saturation_tps_qp")),
     142        7116 :     _pc_older(_nodal_material
     143        3558 :                   ? getMaterialPropertyOlder<Real>("PorousFlow_hysteretic_capillary_pressure_nodal")
     144       10210 :                   : getMaterialPropertyOlder<Real>("PorousFlow_hysteretic_capillary_pressure_qp")),
     145        7116 :     _pc_tps(_nodal_material
     146        3558 :                 ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     147             :                       "PorousFlow_hysteresis_Pc_tps_nodal")
     148        6884 :                 : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     149             :                       "PorousFlow_hysteresis_Pc_tps_qp")),
     150        7116 :     _s_d_tps(_nodal_material
     151        3558 :                  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     152             :                        "PorousFlow_hysteresis_s_d_tps_nodal")
     153        6884 :                  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     154             :                        "PorousFlow_hysteresis_s_d_tps_qp")),
     155        7116 :     _s_gr_tps(_nodal_material
     156        3558 :                   ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     157             :                         "PorousFlow_hysteresis_s_gr_tps_nodal")
     158        6884 :                   : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     159             :                         "PorousFlow_hysteresis_s_gr_tps_qp")),
     160        3558 :     _w_low_ext_tps(
     161        3558 :         _nodal_material
     162        3558 :             ? declareProperty<std::array<PorousFlowVanGenuchten::LowCapillaryPressureExtension,
     163         232 :                                          PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     164             :                   "PorousFlow_hysteresis_w_low_ext_tps_nodal")
     165             :             : declareProperty<std::array<PorousFlowVanGenuchten::LowCapillaryPressureExtension,
     166        6884 :                                          PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     167             :                   "PorousFlow_hysteresis_w_low_ext_tps_qp")),
     168        3558 :     _w_high_ext_tps(
     169        3558 :         _nodal_material
     170        3558 :             ? declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
     171         232 :                                          PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     172             :                   "PorousFlow_hysteresis_w_high_ext_tps_nodal")
     173             :             : declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
     174        6884 :                                          PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     175             :                   "PorousFlow_hysteresis_w_high_ext_tps_qp")),
     176        7116 :     _s_w_tps(_nodal_material
     177        3558 :                  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     178             :                        "PorousFlow_hysteresis_s_w_tps_nodal")
     179        6884 :                  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
     180        3558 :                        "PorousFlow_hysteresis_s_w_tps_qp"))
     181             : {
     182        3558 :   if (_s_gr_max >= 1 - _s_l_min)
     183           2 :     paramError("S_gr_max", "Must be less than 1 - S_l_min");
     184        3556 :   if (_s_high < _s_low_w)
     185           2 :     paramError("high_ratio",
     186             :                "Should be chosen sufficiently close to 1 so that the high extension does not "
     187             :                "interfere with the low extension.  Instead, you may have chosen Pc_max too low");
     188        3554 :   if (_s_lr <= _s_l_min)
     189           2 :     mooseWarning("S_lr should usually be greater than S_l_min");
     190        3552 : }
     191             : 
     192             : void
     193      213024 : PorousFlowHystereticCapillaryPressure::initQpStatefulProperties()
     194             : {
     195      213024 :   PorousFlowVariableBase::initQpStatefulProperties();
     196             :   Real tp_sat;
     197             :   Real tp_pc;
     198      213024 :   if (_hys_order[_qp] >= 1)
     199             :   {
     200      157836 :     tp_sat = _hys_sat_tps[_qp].at(0);
     201      315672 :     tp_pc = PorousFlowVanGenuchten::capillaryPressureHys(
     202      157836 :         tp_sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     203      157836 :     computeTurningPointInfo(0, tp_sat, tp_pc);
     204             :   }
     205      213024 :   if (_hys_order[_qp] >= 2)
     206             :   {
     207       79820 :     tp_sat = _hys_sat_tps[_qp].at(1);
     208       79820 :     tp_pc = firstOrderWettingPc(tp_sat);
     209       79820 :     computeTurningPointInfo(1, tp_sat, tp_pc);
     210             :   }
     211      213024 :   if (_hys_order[_qp] >= 3)
     212             :   {
     213       29900 :     tp_sat = _hys_sat_tps[_qp].at(2);
     214       29900 :     tp_pc = secondOrderDryingPc(tp_sat);
     215       29900 :     computeTurningPointInfo(2, tp_sat, tp_pc);
     216             :   }
     217      213024 : }
     218             : 
     219             : void
     220      573258 : PorousFlowHystereticCapillaryPressure::computeQpProperties()
     221             : {
     222             :   // size stuff correctly and prepare the derivative matrices with zeroes
     223      573258 :   PorousFlowVariableBase::computeQpProperties();
     224             : 
     225      573258 :   if (_hys_order[_qp] != _hys_order_old[_qp] && _hys_order[_qp] > 0)
     226             :   {
     227        9104 :     const unsigned tp_num = _hys_order[_qp] - 1;
     228        9104 :     computeTurningPointInfo(tp_num, _hys_sat_tps[_qp].at(tp_num), _pc_older[_qp]);
     229             :   }
     230      573258 : }
     231             : 
     232             : Real
     233      276660 : PorousFlowHystereticCapillaryPressure::landSat(Real slDel) const
     234             : {
     235      276660 :   const Real a = 1.0 / _s_gr_max - 1.0 / (1.0 - _s_lr);
     236      276660 :   return (1.0 - slDel) / (1.0 + a * (1.0 - slDel));
     237             : }
     238             : 
     239             : void
     240      276660 : PorousFlowHystereticCapillaryPressure::computeTurningPointInfo(unsigned tp_num,
     241             :                                                                Real tp_sat,
     242             :                                                                Real tp_pc)
     243             : {
     244      276660 :   _pc_tps[_qp].at(tp_num) = tp_pc;
     245             : 
     246             :   // Quantities on the drying curve:
     247             :   // pc on the drying curve at the turning point saturation
     248      276660 :   const Real pc_d_tps = PorousFlowVanGenuchten::capillaryPressureHys(
     249      276660 :       tp_sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     250             :   // saturation on the drying curve, at tp_pc
     251      276660 :   _s_d_tps[_qp].at(tp_num) =
     252      276660 :       PorousFlowVanGenuchten::saturationHys(tp_pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     253             : 
     254             :   // Quantities relevant to the wetting curve defined by the Land expression.
     255             :   // s_gr_tps is the Land expression as a function of the turning point saturation
     256      276660 :   _s_gr_tps[_qp].at(tp_num) = landSat(tp_sat);
     257             :   // the low extension of the wetting curve defined by _s_gr_tps
     258      276660 :   const Real s_w_low_ext = PorousFlowVanGenuchten::saturationHys(
     259      276660 :       _pc_max, _s_l_min, _s_gr_tps[_qp].at(tp_num), _alpha_w, _n_w);
     260      276660 :   const Real dpc_w_low_ext = PorousFlowVanGenuchten::dcapillaryPressureHys(
     261      276660 :       s_w_low_ext, _s_l_min, _s_gr_tps[_qp].at(tp_num), _alpha_w, _n_w);
     262      276660 :   _w_low_ext_tps[_qp].at(tp_num) = PorousFlowVanGenuchten::LowCapillaryPressureExtension(
     263      276660 :       _low_ext_type, s_w_low_ext, _pc_max, dpc_w_low_ext);
     264             :   // the high extension of the wetting curve defined by _s_gr_tps
     265             :   const Real s_w_high_ext =
     266      276660 :       (_high_ext_type == PorousFlowVanGenuchten::HighCapillaryPressureExtension::NONE)
     267      276660 :           ? 1.0 - _s_gr_tps[_qp].at(tp_num)
     268      166160 :           : _high_ratio *
     269      166160 :                 (1.0 -
     270      166160 :                  _s_gr_tps[_qp].at(
     271             :                      tp_num)); // if NONE then use the vanGenuchten all the way to 1 - _s_gr_tps
     272             :   const Real pc_w_high_ext =
     273      276660 :       PorousFlowVanGenuchten::capillaryPressureHys(s_w_high_ext,
     274      276660 :                                                    _s_l_min,
     275      276660 :                                                    _s_gr_tps[_qp].at(tp_num),
     276      276660 :                                                    _alpha_w,
     277      276660 :                                                    _n_w,
     278             :                                                    _w_low_ext_tps[_qp].at(tp_num));
     279             :   const Real dpc_w_high_ext =
     280      276660 :       PorousFlowVanGenuchten::dcapillaryPressureHys(s_w_high_ext,
     281      276660 :                                                     _s_l_min,
     282      276660 :                                                     _s_gr_tps[_qp].at(tp_num),
     283      276660 :                                                     _alpha_w,
     284      276660 :                                                     _n_w,
     285      276660 :                                                     _w_low_ext_tps[_qp].at(tp_num));
     286      276660 :   _w_high_ext_tps[_qp].at(tp_num) = PorousFlowVanGenuchten::HighCapillaryPressureExtension(
     287             :       _high_ext_type, s_w_high_ext, pc_w_high_ext, dpc_w_high_ext);
     288             : 
     289             :   // saturation on the wetting curve defined by _s_gr_tps, at pc = pc_d_tps
     290      553320 :   _s_w_tps[_qp].at(tp_num) = PorousFlowVanGenuchten::saturationHys(pc_d_tps,
     291      276660 :                                                                    _s_l_min,
     292      276660 :                                                                    _s_gr_tps[_qp].at(tp_num),
     293      276660 :                                                                    _alpha_w,
     294      276660 :                                                                    _n_w,
     295      276660 :                                                                    _w_low_ext_tps[_qp].at(tp_num),
     296             :                                                                    _w_high_ext_tps[_qp].at(tp_num));
     297      276660 : }
     298             : 
     299             : Real
     300     1322184 : PorousFlowHystereticCapillaryPressure::capillaryPressureQp(Real sat) const
     301             : {
     302             :   Real pc = 0.0;
     303     1322184 :   if (_hys_order[_qp] == 0) // on primary drying curve
     304      313136 :     pc = PorousFlowVanGenuchten::capillaryPressureHys(
     305      313136 :         sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     306     1009048 :   else if (_hys_order[_qp] == 1) // first-order wetting
     307      501584 :     pc = firstOrderWettingPc(sat);
     308      507464 :   else if (_hys_order[_qp] == 2) // second-order drying
     309      317904 :     pc = secondOrderDryingPc(sat);
     310             :   else // third order drying and wetting
     311             :   {
     312      189560 :     const Real tp1 = _hys_sat_tps[_qp].at(1);
     313      189560 :     const Real tp2 = _hys_sat_tps[_qp].at(2);
     314      189560 :     const Real pc1 = firstOrderWettingPc(sat);
     315      189560 :     const Real pc2 = secondOrderDryingPc(sat);
     316             :     // handle cases that occur just at the transition from 3rd to 2nd order, or 3rd to 1st order
     317      189560 :     if (sat < tp2)
     318             :       pc = pc2;
     319      189560 :     else if (sat > tp1)
     320             :       pc = pc1;
     321      189464 :     else if (pc1 >= pc2)
     322             :       pc = pc1;
     323      189464 :     else if (pc1 <= 0.0 || pc2 <= 0.0)
     324             :       pc = 0.0;
     325             :     else
     326      157730 :       pc = std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
     327             :   }
     328     1322184 :   return pc;
     329             : }
     330             : 
     331             : Real
     332      262872 : PorousFlowHystereticCapillaryPressure::dcapillaryPressureQp(Real sat) const
     333             : {
     334             :   Real dpc = 0.0;
     335      262872 :   if (_hys_order[_qp] == 0) // on primary drying curve
     336       85368 :     dpc = PorousFlowVanGenuchten::dcapillaryPressureHys(
     337       85368 :         sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     338      177504 :   else if (_hys_order[_qp] == 1) // first-order wetting
     339       79980 :     dpc = dfirstOrderWettingPc(sat);
     340       97524 :   else if (_hys_order[_qp] == 2) // second-order drying
     341       60624 :     dpc = dsecondOrderDryingPc(sat);
     342             :   else // third order drying and wetting
     343             :   {
     344       36900 :     const Real tp1 = _hys_sat_tps[_qp].at(1);
     345       36900 :     const Real tp2 = _hys_sat_tps[_qp].at(2);
     346       36900 :     const Real pc1 = firstOrderWettingPc(sat);
     347       36900 :     const Real pc2 = secondOrderDryingPc(sat);
     348             :     // handle cases that occur just at the transition from 3rd to 2nd order, or 3rd to 1st order
     349       36900 :     if (sat < tp2)
     350           0 :       dpc = dsecondOrderDryingPc(sat);
     351       36900 :     else if (sat > tp1)
     352           0 :       dpc = dfirstOrderWettingPc(sat);
     353       36900 :     else if (pc1 >= pc2)
     354           0 :       dpc = dfirstOrderWettingPc(sat);
     355       36900 :     else if (pc1 <= 0.0 || pc2 <= 0.0)
     356             :       dpc = 0.0;
     357             :     else
     358             :     {
     359             :       const Real pc =
     360       31611 :           std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
     361       31611 :       const Real dpc1 = dfirstOrderWettingPc(sat);
     362       31611 :       const Real dpc2 = dsecondOrderDryingPc(sat);
     363       31611 :       dpc = pc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
     364       31611 :                   (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1));
     365             :     }
     366             :   }
     367      262872 :   return dpc;
     368             : }
     369             : 
     370             : Real
     371       90846 : PorousFlowHystereticCapillaryPressure::d2capillaryPressureQp(Real sat) const
     372             : {
     373             :   Real d2pc = 0.0;
     374       90846 :   if (_hys_order[_qp] == 0) // on primary drying curve
     375       30384 :     d2pc = PorousFlowVanGenuchten::d2capillaryPressureHys(
     376       30384 :         sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     377       60462 :   else if (_hys_order[_qp] == 1) // first-order wetting
     378       27690 :     d2pc = d2firstOrderWettingPc(sat);
     379       32772 :   else if (_hys_order[_qp] == 2) // second-order drying
     380       20472 :     d2pc = d2secondOrderDryingPc(sat);
     381             :   else // third order drying and wetting
     382             :   {
     383       12300 :     const Real tp1 = _hys_sat_tps[_qp].at(1);
     384       12300 :     const Real tp2 = _hys_sat_tps[_qp].at(2);
     385       12300 :     const Real pc1 = firstOrderWettingPc(sat);
     386       12300 :     const Real pc2 = secondOrderDryingPc(sat);
     387             :     // handle cases that occur just at the transition from 3rd to 2nd order, or 3rd to 1st order
     388       12300 :     if (sat < tp2)
     389           0 :       d2pc = d2secondOrderDryingPc(sat);
     390       12300 :     else if (sat > tp1)
     391           0 :       d2pc = d2firstOrderWettingPc(sat);
     392       12300 :     else if (pc1 >= pc2)
     393           0 :       d2pc = d2firstOrderWettingPc(sat);
     394       12300 :     else if (pc1 <= 0.0 || pc2 <= 0.0)
     395             :       d2pc = 0.0;
     396             :     else
     397             :     {
     398             :       const Real pc =
     399       10537 :           std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
     400       10537 :       const Real dpc1 = dfirstOrderWettingPc(sat);
     401       10537 :       const Real dpc2 = dsecondOrderDryingPc(sat);
     402       10537 :       const Real dpc = pc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
     403       10537 :                              (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1));
     404       10537 :       const Real d2pc1 = d2firstOrderWettingPc(sat);
     405       10537 :       const Real d2pc2 = d2secondOrderDryingPc(sat);
     406       10537 :       d2pc =
     407       10537 :           dpc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
     408             :                  (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1)) +
     409       10537 :           pc *
     410       10537 :               (d2pc1 / pc1 - dpc1 * dpc1 / pc1 / pc1 + (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1) +
     411       10537 :                (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1) +
     412       10537 :                (sat - tp1) *
     413       10537 :                    (d2pc2 / pc2 - dpc2 * dpc2 / pc2 / pc2 - d2pc1 / pc1 + dpc1 * dpc1 / pc1 / pc1) /
     414             :                    (tp2 - tp1));
     415             :     }
     416             :   }
     417       90846 :   return d2pc;
     418             : }
     419             : 
     420             : Real
     421      820164 : PorousFlowHystereticCapillaryPressure::firstOrderWettingPc(Real sat) const
     422             : {
     423             :   // Simplest version is to just use the wetting curve defined by _s_gr_tps[0], but want
     424             :   // continuity at sat = _hys_sat_tps[0] (the turning point), so use the following process. The
     425             :   // wetting curve is defined for S <= max_s, where
     426      820164 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     427             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     428      820164 :                          ? 1.0
     429      335900 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     430             :   // define an interpolation: s_to_use smoothly transitions from _s_w_tps[0] (the value of liquid
     431             :   // saturation on the wetting curve defined by _s_gr_tps[0]) when sat = _hys_sat_tps[0], to max_s
     432             :   // when sat = max_s
     433      820164 :   const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
     434      820164 :                                                     (sat - _hys_sat_tps[_qp].at(0)) /
     435      820164 :                                                     (max_s - _hys_sat_tps[_qp].at(0));
     436      820164 :   return PorousFlowVanGenuchten::capillaryPressureHys(sat_to_use,
     437      820164 :                                                       _s_l_min,
     438      820164 :                                                       _s_gr_tps[_qp].at(0),
     439      820164 :                                                       _alpha_w,
     440      820164 :                                                       _n_w,
     441      820164 :                                                       _w_low_ext_tps[_qp].at(0),
     442      820164 :                                                       _w_high_ext_tps[_qp].at(0));
     443             : }
     444             : 
     445             : Real
     446      122128 : PorousFlowHystereticCapillaryPressure::dfirstOrderWettingPc(Real sat) const
     447             : {
     448      122128 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     449             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     450      122128 :                          ? 1.0
     451       33948 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     452      122128 :   const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
     453      122128 :                                                     (sat - _hys_sat_tps[_qp].at(0)) /
     454      122128 :                                                     (max_s - _hys_sat_tps[_qp].at(0));
     455      122128 :   const Real dsat_to_use = (max_s - _s_w_tps[_qp].at(0)) / (max_s - _hys_sat_tps[_qp].at(0));
     456      122128 :   return PorousFlowVanGenuchten::dcapillaryPressureHys(sat_to_use,
     457      122128 :                                                        _s_l_min,
     458      122128 :                                                        _s_gr_tps[_qp].at(0),
     459      122128 :                                                        _alpha_w,
     460      122128 :                                                        _n_w,
     461      122128 :                                                        _w_low_ext_tps[_qp].at(0),
     462             :                                                        _w_high_ext_tps[_qp].at(0)) *
     463      122128 :          dsat_to_use;
     464             : }
     465             : 
     466             : Real
     467       38227 : PorousFlowHystereticCapillaryPressure::d2firstOrderWettingPc(Real sat) const
     468             : {
     469       38227 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     470             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     471       38227 :                          ? 1.0
     472       10537 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     473       38227 :   const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
     474       38227 :                                                     (sat - _hys_sat_tps[_qp].at(0)) /
     475       38227 :                                                     (max_s - _hys_sat_tps[_qp].at(0));
     476       38227 :   const Real dsat_to_use = (max_s - _s_w_tps[_qp].at(0)) / (max_s - _hys_sat_tps[_qp].at(0));
     477       38227 :   return PorousFlowVanGenuchten::d2capillaryPressureHys(sat_to_use,
     478       38227 :                                                         _s_l_min,
     479       38227 :                                                         _s_gr_tps[_qp].at(0),
     480       38227 :                                                         _alpha_w,
     481       38227 :                                                         _n_w,
     482       38227 :                                                         _w_low_ext_tps[_qp].at(0),
     483       38227 :                                                         _w_high_ext_tps[_qp].at(0)) *
     484       38227 :          dsat_to_use * dsat_to_use;
     485             : }
     486             : 
     487             : Real
     488      586564 : PorousFlowHystereticCapillaryPressure::secondOrderDryingPc(Real sat) const
     489             : {
     490             :   // Simplest version is to just use the primary drying curve, but want
     491             :   // continuity at sat = _hys_sat_tps[0] (the dry-to-wet turning point) and sat = _hys_sat_tps[1]
     492             :   // (the wet-to-dry turning point), so use the following process.
     493      586564 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     494      586564 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     495      586564 :   const Real s1 = _s_d_tps[_qp].at(1);
     496             :   const Real sat_to_use =
     497      586564 :       (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0)
     498             :                    : sat; // final case can occur just at transition from 2nd to 0th order
     499     1173128 :   return PorousFlowVanGenuchten::capillaryPressureHys(
     500      586564 :       sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     501             : }
     502             : 
     503             : Real
     504      102772 : PorousFlowHystereticCapillaryPressure::dsecondOrderDryingPc(Real sat) const
     505             : {
     506      102772 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     507      102772 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     508      102772 :   const Real s1 = _s_d_tps[_qp].at(1);
     509      102772 :   const Real sat_to_use = (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0) : sat;
     510      102772 :   const Real dsat_to_use = (sat >= tp0) ? (s1 - tp0) / (tp1 - tp0) : 1.0;
     511      205544 :   return PorousFlowVanGenuchten::dcapillaryPressureHys(
     512      102772 :              sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d) *
     513      102772 :          dsat_to_use;
     514             : }
     515             : 
     516             : Real
     517       31009 : PorousFlowHystereticCapillaryPressure::d2secondOrderDryingPc(Real sat) const
     518             : {
     519       31009 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     520       31009 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     521       31009 :   const Real s1 = _s_d_tps[_qp].at(1);
     522       31009 :   const Real sat_to_use = (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0) : sat;
     523       31009 :   const Real dsat_to_use = (sat >= tp0) ? (s1 - tp0) / (tp1 - tp0) : 1.0;
     524       62018 :   return PorousFlowVanGenuchten::d2capillaryPressureHys(
     525       31009 :              sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d) *
     526       31009 :          dsat_to_use * dsat_to_use;
     527             : }
     528             : 
     529             : Real
     530      241020 : PorousFlowHystereticCapillaryPressure::firstOrderWettingSat(Real pc) const
     531             : {
     532             :   // this is inverse of firstOrderWettingPc: see that method for comments
     533      241020 :   const Real sat_to_use = PorousFlowVanGenuchten::saturationHys(pc,
     534      241020 :                                                                 _s_l_min,
     535      241020 :                                                                 _s_gr_tps[_qp].at(0),
     536      241020 :                                                                 _alpha_w,
     537      241020 :                                                                 _n_w,
     538      241020 :                                                                 _w_low_ext_tps[_qp].at(0),
     539      241020 :                                                                 _w_high_ext_tps[_qp].at(0));
     540      241020 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     541             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     542      241020 :                          ? 1.0
     543       73800 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     544      241020 :   if (sat_to_use > max_s) // this occurs when using no high extension and pc = 0
     545             :     return max_s;
     546      220069 :   return (sat_to_use - _s_w_tps[_qp].at(0)) * (max_s - _hys_sat_tps[_qp].at(0)) /
     547      220069 :              (max_s - _s_w_tps[_qp].at(0)) +
     548      220069 :          _hys_sat_tps[_qp].at(0);
     549             : }
     550             : 
     551             : Real
     552      166656 : PorousFlowHystereticCapillaryPressure::dfirstOrderWettingSat(Real pc) const
     553             : {
     554      166656 :   const Real dsat_to_use = PorousFlowVanGenuchten::dsaturationHys(pc,
     555      166656 :                                                                   _s_l_min,
     556      166656 :                                                                   _s_gr_tps[_qp].at(0),
     557      166656 :                                                                   _alpha_w,
     558      166656 :                                                                   _n_w,
     559      166656 :                                                                   _w_low_ext_tps[_qp].at(0),
     560      166656 :                                                                   _w_high_ext_tps[_qp].at(0));
     561      166656 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     562             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     563      166656 :                          ? 1.0
     564       41000 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     565      166656 :   if (pc <= 0.0 && _w_high_ext_tps[_qp].at(0).strategy ==
     566             :                        PorousFlowVanGenuchten::HighCapillaryPressureExtension::NONE)
     567             :     return 0.0;
     568      156119 :   return dsat_to_use * (max_s - _hys_sat_tps[_qp].at(0)) / (max_s - _s_w_tps[_qp].at(0));
     569             : }
     570             : 
     571             : Real
     572       56664 : PorousFlowHystereticCapillaryPressure::d2firstOrderWettingSat(Real pc) const
     573             : {
     574       56664 :   const Real d2sat_to_use = PorousFlowVanGenuchten::d2saturationHys(pc,
     575       56664 :                                                                     _s_l_min,
     576       56664 :                                                                     _s_gr_tps[_qp].at(0),
     577       56664 :                                                                     _alpha_w,
     578       56664 :                                                                     _n_w,
     579       56664 :                                                                     _w_low_ext_tps[_qp].at(0),
     580       56664 :                                                                     _w_high_ext_tps[_qp].at(0));
     581       56664 :   const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
     582             :                       PorousFlowVanGenuchten::HighCapillaryPressureExtension::POWER)
     583       56664 :                          ? 1.0
     584       12300 :                          : 1.0 - _s_gr_tps[_qp].at(0);
     585       56664 :   if (pc <= 0.0 && _w_high_ext_tps[_qp].at(0).strategy ==
     586             :                        PorousFlowVanGenuchten::HighCapillaryPressureExtension::NONE)
     587             :     return 0.0;
     588       52277 :   return d2sat_to_use * (max_s - _hys_sat_tps[_qp].at(0)) / (max_s - _s_w_tps[_qp].at(0));
     589             : }
     590             : 
     591             : Real
     592      191572 : PorousFlowHystereticCapillaryPressure::secondOrderDryingSat(Real pc) const
     593             : {
     594             :   // This is the inverse of secondOrderDryingPc: see that method for comments
     595             :   const Real sat_to_use =
     596      191572 :       PorousFlowVanGenuchten::saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     597      191572 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     598      191572 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     599      191572 :   const Real s1 = _s_d_tps[_qp].at(1);
     600      191572 :   return (sat_to_use >= tp0) ? (sat_to_use - tp0) * (tp1 - tp0) / (s1 - tp0) + tp0 : sat_to_use;
     601             : }
     602             : 
     603             : Real
     604      122136 : PorousFlowHystereticCapillaryPressure::dsecondOrderDryingSat(Real pc) const
     605             : {
     606             :   const Real sat_to_use =
     607      122136 :       PorousFlowVanGenuchten::saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     608             :   const Real dsat_to_use =
     609      122136 :       PorousFlowVanGenuchten::dsaturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     610      122136 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     611      122136 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     612      122136 :   const Real s1 = _s_d_tps[_qp].at(1);
     613      122136 :   return (sat_to_use >= tp0) ? dsat_to_use * (tp1 - tp0) / (s1 - tp0) : dsat_to_use;
     614             : }
     615             : 
     616             : Real
     617       37608 : PorousFlowHystereticCapillaryPressure::d2secondOrderDryingSat(Real pc) const
     618             : {
     619             :   const Real sat_to_use =
     620       37608 :       PorousFlowVanGenuchten::saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     621             :   const Real d2sat_to_use =
     622       37608 :       PorousFlowVanGenuchten::d2saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     623       37608 :   const Real tp0 = _hys_sat_tps[_qp].at(0);
     624       37608 :   const Real tp1 = _hys_sat_tps[_qp].at(1);
     625       37608 :   const Real s1 = _s_d_tps[_qp].at(1);
     626       37608 :   return (sat_to_use >= tp0) ? d2sat_to_use * (tp1 - tp0) / (s1 - tp0) : d2sat_to_use;
     627             : }
     628             : 
     629             : Real
     630      411484 : PorousFlowHystereticCapillaryPressure::liquidSaturationQp(Real pc) const
     631             : {
     632             :   Real sat = 0.0;
     633      411484 :   if (_hys_order[_qp] == 0) // on primary drying curve
     634      141016 :     sat = PorousFlowVanGenuchten::saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     635      270468 :   else if (_hys_order[_qp] == 1) // first-order wetting
     636      135080 :     sat = firstOrderWettingSat(pc);
     637      135388 :   else if (_hys_order[_qp] == 2) // second-order drying
     638       85632 :     sat = secondOrderDryingSat(pc);
     639             :   else // third order drying and wetting
     640             :   {
     641             :     // NOTE: this is not the exact inverse of the third-order capillary-pressure formula, but only
     642             :     // the approximate inverse.  In any simulation, only liquidSaturationQp or capillaryPressureQp
     643             :     // are used (not both) so having a slightly different formulation for these two functions is OK
     644             :     // Rationale: when pc is close to pc_tp1 then use the first-order wetting curve; when pc is
     645             :     // close to pc_tp2 then use the second-order drying curve
     646       49756 :     const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
     647       49756 :     const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
     648       49756 :     const Real sat1 = firstOrderWettingSat(pc);
     649       49756 :     const Real sat2 = secondOrderDryingSat(pc);
     650             :     // handle cases that occur just at the transition from 3rd to 2nd order, or 3rd to 1st order
     651       49756 :     if (pc_tp1 <= 0.0 || pc <= 0.0 ||
     652             :         pc_tp2 <= 0.0) // only the first condition is strictly necessary as cannot get pc==0 or
     653             :                        // pc_tp2=0 without pc_tp1=0 in reality.  The other conditions are added in
     654             :                        // case of numerical strangenesses
     655             :       sat = sat2;
     656       33308 :     else if (pc > pc_tp2)
     657             :       sat = sat2;
     658       33308 :     else if (pc < pc_tp1)
     659             :       sat = sat1;
     660             :     else
     661       32948 :       sat = sat1 + (std::log(pc) - std::log(pc_tp1)) * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1);
     662             :   }
     663      411484 :   return sat;
     664             : }
     665             : 
     666             : Real
     667      350850 : PorousFlowHystereticCapillaryPressure::dliquidSaturationQp(Real pc) const
     668             : {
     669             :   Real dsat = 0.0;
     670      350850 :   if (_hys_order[_qp] == 0) // on primary drying curve
     671      132870 :     dsat = PorousFlowVanGenuchten::dsaturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     672      217980 :   else if (_hys_order[_qp] == 1) // first-order wetting
     673      110472 :     dsat = dfirstOrderWettingSat(pc);
     674      107508 :   else if (_hys_order[_qp] == 2) // second-order drying
     675       65952 :     dsat = dsecondOrderDryingSat(pc);
     676             :   else // third order drying and wetting
     677             :   {
     678       41556 :     const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
     679       41556 :     const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
     680       41556 :     const Real sat1 = firstOrderWettingSat(pc);
     681       41556 :     const Real sat2 = secondOrderDryingSat(pc);
     682       41556 :     const Real dsat1 = dfirstOrderWettingSat(pc);
     683       41556 :     const Real dsat2 = dsecondOrderDryingSat(pc);
     684       41556 :     if (pc_tp1 <= 0.0 || pc <= 0.0 || pc_tp2 <= 0.0)
     685             :       dsat = dsat2;
     686       29208 :     else if (pc > pc_tp2)
     687             :       dsat = dsat2;
     688       29208 :     else if (pc < pc_tp1)
     689             :       dsat = dsat1;
     690             :     else
     691       28848 :       dsat = dsat1 + 1.0 / pc * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1) +
     692       28848 :              (std::log(pc) - std::log(pc_tp1)) * (dsat2 - dsat1) / std::log(pc_tp2 / pc_tp1);
     693             :   }
     694      350850 :   return dsat;
     695             : }
     696             : 
     697             : Real
     698      140946 : PorousFlowHystereticCapillaryPressure::d2liquidSaturationQp(Real pc) const
     699             : {
     700             :   Real d2sat = 0.0;
     701      140946 :   if (_hys_order[_qp] == 0) // on primary drying curve
     702       61302 :     d2sat = PorousFlowVanGenuchten::d2saturationHys(pc, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
     703       79644 :   else if (_hys_order[_qp] == 1) // first-order wetting
     704       42036 :     d2sat = d2firstOrderWettingSat(pc);
     705       37608 :   else if (_hys_order[_qp] == 2) // second-order drying
     706       22980 :     d2sat = d2secondOrderDryingSat(pc);
     707             :   else // third order drying and wetting
     708             :   {
     709       14628 :     const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
     710       14628 :     const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
     711       14628 :     const Real sat1 = firstOrderWettingSat(pc);
     712       14628 :     const Real sat2 = secondOrderDryingSat(pc);
     713       14628 :     const Real dsat1 = dfirstOrderWettingSat(pc);
     714       14628 :     const Real dsat2 = dsecondOrderDryingSat(pc);
     715       14628 :     const Real d2sat1 = d2firstOrderWettingSat(pc);
     716       14628 :     const Real d2sat2 = d2secondOrderDryingSat(pc);
     717       14628 :     if (pc_tp1 <= 0.0 || pc <= 0.0 || pc_tp2 <= 0.0)
     718             :       d2sat = d2sat2;
     719       10504 :     else if (pc > pc_tp2)
     720             :       d2sat = d2sat2;
     721       10504 :     else if (pc < pc_tp1)
     722             :       d2sat = d2sat1;
     723             :     else
     724       10324 :       d2sat = d2sat1 - 1.0 / pc / pc * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1) +
     725       10324 :               2.0 / pc * (dsat2 - dsat1) / std::log(pc_tp2 / pc_tp1) +
     726       10324 :               (std::log(pc) - std::log(pc_tp1)) * (d2sat2 - d2sat1) / std::log(pc_tp2 / pc_tp1);
     727             :   }
     728      140946 :   return d2sat;
     729             : }

Generated by: LCOV version 1.14