https://mooseframework.inl.gov
PorousFlowHystereticCapillaryPressure.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
12 
14 
17 {
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");
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  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  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");
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  params.addRangeCheckedParam<Real>(
43  "S_lr",
44  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");
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  params.addRangeCheckedParam<Real>(
57  "Pc_max",
58  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  MooseEnum low_ext_enum("none quadratic exponential", "exponential");
64  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  params.addRangeCheckedParam<Real>(
77  "high_ratio",
78  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  MooseEnum high_ext_enum("none power", "power");
83  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  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  return params;
97 }
98 
100  const InputParameters & parameters)
101  : PorousFlowVariableBase(parameters),
102  _alpha_d(getParam<Real>("alpha_d")),
103  _alpha_w(getParam<Real>("alpha_w")),
104  _n_d(getParam<Real>("n_d")),
105  _n_w(getParam<Real>("n_w")),
106  _s_l_min(getParam<Real>("S_l_min")),
107  _s_lr(getParam<Real>("S_lr")),
108  _s_gr_max(getParam<Real>("S_gr_max")),
109  _pc_max(getParam<Real>("Pc_max")),
110  _high_ratio(getParam<Real>("high_ratio")),
111  _low_ext_type(
112  getParam<MooseEnum>("low_extension_type")
113  .getEnum<PorousFlowVanGenuchten::LowCapillaryPressureExtension::ExtensionStrategy>()),
114  _s_low_d(PorousFlowVanGenuchten::saturationHys(_pc_max, _s_l_min, 0.0, _alpha_d, _n_d)),
115  _dpc_low_d(
116  PorousFlowVanGenuchten::dcapillaryPressureHys(_s_low_d, _s_l_min, 0.0, _alpha_d, _n_d)),
117  _low_ext_d(_low_ext_type, _s_low_d, _pc_max, _dpc_low_d),
118  _s_low_w(PorousFlowVanGenuchten::saturationHys(_pc_max, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
120  _s_low_w, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
121  _low_ext_w(_low_ext_type, _s_low_w, _pc_max, _dpc_low_w),
122  _high_ext_type(
123  getParam<MooseEnum>("high_extension_type")
124  .getEnum<PorousFlowVanGenuchten::HighCapillaryPressureExtension::ExtensionStrategy>()),
125  _s_high(_high_ratio * (1 - _s_gr_max)),
126  _pc_high(
127  PorousFlowVanGenuchten::capillaryPressureHys(_s_high, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
129  _s_high, _s_l_min, _s_gr_max, _alpha_w, _n_w)),
130  _high_ext(_high_ext_type, _s_high, _pc_high, _dpc_high),
131  _hys_order(_nodal_material ? getMaterialProperty<unsigned>("PorousFlow_hysteresis_order_nodal")
132  : getMaterialProperty<unsigned>("PorousFlow_hysteresis_order_qp")),
133  _hys_order_old(_nodal_material
134  ? getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_nodal")
135  : getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_qp")),
136  _hys_sat_tps(
137  _nodal_material
138  ? getMaterialProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
139  "PorousFlow_hysteresis_saturation_tps_nodal")
140  : getMaterialProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
141  "PorousFlow_hysteresis_saturation_tps_qp")),
142  _pc_older(_nodal_material
143  ? getMaterialPropertyOlder<Real>("PorousFlow_hysteretic_capillary_pressure_nodal")
144  : getMaterialPropertyOlder<Real>("PorousFlow_hysteretic_capillary_pressure_qp")),
145  _pc_tps(_nodal_material
146  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
147  "PorousFlow_hysteresis_Pc_tps_nodal")
148  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
149  "PorousFlow_hysteresis_Pc_tps_qp")),
150  _s_d_tps(_nodal_material
151  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
152  "PorousFlow_hysteresis_s_d_tps_nodal")
153  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
154  "PorousFlow_hysteresis_s_d_tps_qp")),
155  _s_gr_tps(_nodal_material
156  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
157  "PorousFlow_hysteresis_s_gr_tps_nodal")
158  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
159  "PorousFlow_hysteresis_s_gr_tps_qp")),
160  _w_low_ext_tps(
161  _nodal_material
162  ? declareProperty<std::array<PorousFlowVanGenuchten::LowCapillaryPressureExtension,
164  "PorousFlow_hysteresis_w_low_ext_tps_nodal")
165  : declareProperty<std::array<PorousFlowVanGenuchten::LowCapillaryPressureExtension,
167  "PorousFlow_hysteresis_w_low_ext_tps_qp")),
168  _w_high_ext_tps(
169  _nodal_material
170  ? declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
172  "PorousFlow_hysteresis_w_high_ext_tps_nodal")
173  : declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
175  "PorousFlow_hysteresis_w_high_ext_tps_qp")),
176  _s_w_tps(_nodal_material
177  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
178  "PorousFlow_hysteresis_s_w_tps_nodal")
179  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
180  "PorousFlow_hysteresis_s_w_tps_qp"))
181 {
182  if (_s_gr_max >= 1 - _s_l_min)
183  paramError("S_gr_max", "Must be less than 1 - S_l_min");
184  if (_s_high < _s_low_w)
185  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  if (_s_lr <= _s_l_min)
189  mooseWarning("S_lr should usually be greater than S_l_min");
190 }
191 
192 void
194 {
196  Real tp_sat;
197  Real tp_pc;
198  if (_hys_order[_qp] >= 1)
199  {
200  tp_sat = _hys_sat_tps[_qp].at(0);
202  tp_sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
203  computeTurningPointInfo(0, tp_sat, tp_pc);
204  }
205  if (_hys_order[_qp] >= 2)
206  {
207  tp_sat = _hys_sat_tps[_qp].at(1);
208  tp_pc = firstOrderWettingPc(tp_sat);
209  computeTurningPointInfo(1, tp_sat, tp_pc);
210  }
211  if (_hys_order[_qp] >= 3)
212  {
213  tp_sat = _hys_sat_tps[_qp].at(2);
214  tp_pc = secondOrderDryingPc(tp_sat);
215  computeTurningPointInfo(2, tp_sat, tp_pc);
216  }
217 }
218 
219 void
221 {
222  // size stuff correctly and prepare the derivative matrices with zeroes
224 
225  if (_hys_order[_qp] != _hys_order_old[_qp] && _hys_order[_qp] > 0)
226  {
227  const unsigned tp_num = _hys_order[_qp] - 1;
228  computeTurningPointInfo(tp_num, _hys_sat_tps[_qp].at(tp_num), _pc_older[_qp]);
229  }
230 }
231 
232 Real
234 {
235  const Real a = 1.0 / _s_gr_max - 1.0 / (1.0 - _s_lr);
236  return (1.0 - slDel) / (1.0 + a * (1.0 - slDel));
237 }
238 
239 void
241  Real tp_sat,
242  Real tp_pc)
243 {
244  _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
249  tp_sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
250  // saturation on the drying curve, at tp_pc
251  _s_d_tps[_qp].at(tp_num) =
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  _s_gr_tps[_qp].at(tp_num) = landSat(tp_sat);
257  // the low extension of the wetting curve defined by _s_gr_tps
258  const Real s_w_low_ext = PorousFlowVanGenuchten::saturationHys(
259  _pc_max, _s_l_min, _s_gr_tps[_qp].at(tp_num), _alpha_w, _n_w);
261  s_w_low_ext, _s_l_min, _s_gr_tps[_qp].at(tp_num), _alpha_w, _n_w);
263  _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 =
267  ? 1.0 - _s_gr_tps[_qp].at(tp_num)
268  : _high_ratio *
269  (1.0 -
270  _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 =
274  _s_l_min,
275  _s_gr_tps[_qp].at(tp_num),
276  _alpha_w,
277  _n_w,
278  _w_low_ext_tps[_qp].at(tp_num));
279  const Real dpc_w_high_ext =
281  _s_l_min,
282  _s_gr_tps[_qp].at(tp_num),
283  _alpha_w,
284  _n_w,
285  _w_low_ext_tps[_qp].at(tp_num));
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  _s_w_tps[_qp].at(tp_num) = PorousFlowVanGenuchten::saturationHys(pc_d_tps,
291  _s_l_min,
292  _s_gr_tps[_qp].at(tp_num),
293  _alpha_w,
294  _n_w,
295  _w_low_ext_tps[_qp].at(tp_num),
296  _w_high_ext_tps[_qp].at(tp_num));
297 }
298 
299 Real
301 {
302  Real pc = 0.0;
303  if (_hys_order[_qp] == 0) // on primary drying curve
305  sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
306  else if (_hys_order[_qp] == 1) // first-order wetting
307  pc = firstOrderWettingPc(sat);
308  else if (_hys_order[_qp] == 2) // second-order drying
309  pc = secondOrderDryingPc(sat);
310  else // third order drying and wetting
311  {
312  const Real tp1 = _hys_sat_tps[_qp].at(1);
313  const Real tp2 = _hys_sat_tps[_qp].at(2);
314  const Real pc1 = firstOrderWettingPc(sat);
315  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  if (sat < tp2)
318  pc = pc2;
319  else if (sat > tp1)
320  pc = pc1;
321  else if (pc1 >= pc2)
322  pc = pc1;
323  else if (pc1 <= 0.0 || pc2 <= 0.0)
324  pc = 0.0;
325  else
326  pc = std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
327  }
328  return pc;
329 }
330 
331 Real
333 {
334  Real dpc = 0.0;
335  if (_hys_order[_qp] == 0) // on primary drying curve
337  sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
338  else if (_hys_order[_qp] == 1) // first-order wetting
339  dpc = dfirstOrderWettingPc(sat);
340  else if (_hys_order[_qp] == 2) // second-order drying
341  dpc = dsecondOrderDryingPc(sat);
342  else // third order drying and wetting
343  {
344  const Real tp1 = _hys_sat_tps[_qp].at(1);
345  const Real tp2 = _hys_sat_tps[_qp].at(2);
346  const Real pc1 = firstOrderWettingPc(sat);
347  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  if (sat < tp2)
350  dpc = dsecondOrderDryingPc(sat);
351  else if (sat > tp1)
352  dpc = dfirstOrderWettingPc(sat);
353  else if (pc1 >= pc2)
354  dpc = dfirstOrderWettingPc(sat);
355  else if (pc1 <= 0.0 || pc2 <= 0.0)
356  dpc = 0.0;
357  else
358  {
359  const Real pc =
360  std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
361  const Real dpc1 = dfirstOrderWettingPc(sat);
362  const Real dpc2 = dsecondOrderDryingPc(sat);
363  dpc = pc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
364  (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1));
365  }
366  }
367  return dpc;
368 }
369 
370 Real
372 {
373  Real d2pc = 0.0;
374  if (_hys_order[_qp] == 0) // on primary drying curve
376  sat, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
377  else if (_hys_order[_qp] == 1) // first-order wetting
378  d2pc = d2firstOrderWettingPc(sat);
379  else if (_hys_order[_qp] == 2) // second-order drying
380  d2pc = d2secondOrderDryingPc(sat);
381  else // third order drying and wetting
382  {
383  const Real tp1 = _hys_sat_tps[_qp].at(1);
384  const Real tp2 = _hys_sat_tps[_qp].at(2);
385  const Real pc1 = firstOrderWettingPc(sat);
386  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  if (sat < tp2)
389  d2pc = d2secondOrderDryingPc(sat);
390  else if (sat > tp1)
391  d2pc = d2firstOrderWettingPc(sat);
392  else if (pc1 >= pc2)
393  d2pc = d2firstOrderWettingPc(sat);
394  else if (pc1 <= 0.0 || pc2 <= 0.0)
395  d2pc = 0.0;
396  else
397  {
398  const Real pc =
399  std::exp(std::log(pc1) + (sat - tp1) * (std::log(pc2) - std::log(pc1)) / (tp2 - tp1));
400  const Real dpc1 = dfirstOrderWettingPc(sat);
401  const Real dpc2 = dsecondOrderDryingPc(sat);
402  const Real dpc = pc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
403  (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1));
404  const Real d2pc1 = d2firstOrderWettingPc(sat);
405  const Real d2pc2 = d2secondOrderDryingPc(sat);
406  d2pc =
407  dpc * (dpc1 / pc1 + (std::log(pc2) - std::log(pc1)) / (tp2 - tp1) +
408  (sat - tp1) * (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1)) +
409  pc *
410  (d2pc1 / pc1 - dpc1 * dpc1 / pc1 / pc1 + (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1) +
411  (dpc2 / pc2 - dpc1 / pc1) / (tp2 - tp1) +
412  (sat - tp1) *
413  (d2pc2 / pc2 - dpc2 * dpc2 / pc2 / pc2 - d2pc1 / pc1 + dpc1 * dpc1 / pc1 / pc1) /
414  (tp2 - tp1));
415  }
416  }
417  return d2pc;
418 }
419 
420 Real
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  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
428  ? 1.0
429  : 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  const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
434  (sat - _hys_sat_tps[_qp].at(0)) /
435  (max_s - _hys_sat_tps[_qp].at(0));
437  _s_l_min,
438  _s_gr_tps[_qp].at(0),
439  _alpha_w,
440  _n_w,
441  _w_low_ext_tps[_qp].at(0),
442  _w_high_ext_tps[_qp].at(0));
443 }
444 
445 Real
447 {
448  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
450  ? 1.0
451  : 1.0 - _s_gr_tps[_qp].at(0);
452  const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
453  (sat - _hys_sat_tps[_qp].at(0)) /
454  (max_s - _hys_sat_tps[_qp].at(0));
455  const Real dsat_to_use = (max_s - _s_w_tps[_qp].at(0)) / (max_s - _hys_sat_tps[_qp].at(0));
457  _s_l_min,
458  _s_gr_tps[_qp].at(0),
459  _alpha_w,
460  _n_w,
461  _w_low_ext_tps[_qp].at(0),
462  _w_high_ext_tps[_qp].at(0)) *
463  dsat_to_use;
464 }
465 
466 Real
468 {
469  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
471  ? 1.0
472  : 1.0 - _s_gr_tps[_qp].at(0);
473  const Real sat_to_use = _s_w_tps[_qp].at(0) + (max_s - _s_w_tps[_qp].at(0)) *
474  (sat - _hys_sat_tps[_qp].at(0)) /
475  (max_s - _hys_sat_tps[_qp].at(0));
476  const Real dsat_to_use = (max_s - _s_w_tps[_qp].at(0)) / (max_s - _hys_sat_tps[_qp].at(0));
478  _s_l_min,
479  _s_gr_tps[_qp].at(0),
480  _alpha_w,
481  _n_w,
482  _w_low_ext_tps[_qp].at(0),
483  _w_high_ext_tps[_qp].at(0)) *
484  dsat_to_use * dsat_to_use;
485 }
486 
487 Real
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  const Real tp0 = _hys_sat_tps[_qp].at(0);
494  const Real tp1 = _hys_sat_tps[_qp].at(1);
495  const Real s1 = _s_d_tps[_qp].at(1);
496  const Real sat_to_use =
497  (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0)
498  : sat; // final case can occur just at transition from 2nd to 0th order
500  sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d);
501 }
502 
503 Real
505 {
506  const Real tp0 = _hys_sat_tps[_qp].at(0);
507  const Real tp1 = _hys_sat_tps[_qp].at(1);
508  const Real s1 = _s_d_tps[_qp].at(1);
509  const Real sat_to_use = (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0) : sat;
510  const Real dsat_to_use = (sat >= tp0) ? (s1 - tp0) / (tp1 - tp0) : 1.0;
512  sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d) *
513  dsat_to_use;
514 }
515 
516 Real
518 {
519  const Real tp0 = _hys_sat_tps[_qp].at(0);
520  const Real tp1 = _hys_sat_tps[_qp].at(1);
521  const Real s1 = _s_d_tps[_qp].at(1);
522  const Real sat_to_use = (sat >= tp0) ? tp0 + (sat - tp0) * (s1 - tp0) / (tp1 - tp0) : sat;
523  const Real dsat_to_use = (sat >= tp0) ? (s1 - tp0) / (tp1 - tp0) : 1.0;
525  sat_to_use, _s_l_min, 0.0, _alpha_d, _n_d, _low_ext_d) *
526  dsat_to_use * dsat_to_use;
527 }
528 
529 Real
531 {
532  // this is inverse of firstOrderWettingPc: see that method for comments
533  const Real sat_to_use = PorousFlowVanGenuchten::saturationHys(pc,
534  _s_l_min,
535  _s_gr_tps[_qp].at(0),
536  _alpha_w,
537  _n_w,
538  _w_low_ext_tps[_qp].at(0),
539  _w_high_ext_tps[_qp].at(0));
540  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
542  ? 1.0
543  : 1.0 - _s_gr_tps[_qp].at(0);
544  if (sat_to_use > max_s) // this occurs when using no high extension and pc = 0
545  return max_s;
546  return (sat_to_use - _s_w_tps[_qp].at(0)) * (max_s - _hys_sat_tps[_qp].at(0)) /
547  (max_s - _s_w_tps[_qp].at(0)) +
548  _hys_sat_tps[_qp].at(0);
549 }
550 
551 Real
553 {
554  const Real dsat_to_use = PorousFlowVanGenuchten::dsaturationHys(pc,
555  _s_l_min,
556  _s_gr_tps[_qp].at(0),
557  _alpha_w,
558  _n_w,
559  _w_low_ext_tps[_qp].at(0),
560  _w_high_ext_tps[_qp].at(0));
561  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
563  ? 1.0
564  : 1.0 - _s_gr_tps[_qp].at(0);
565  if (pc <= 0.0 && _w_high_ext_tps[_qp].at(0).strategy ==
567  return 0.0;
568  return dsat_to_use * (max_s - _hys_sat_tps[_qp].at(0)) / (max_s - _s_w_tps[_qp].at(0));
569 }
570 
571 Real
573 {
574  const Real d2sat_to_use = PorousFlowVanGenuchten::d2saturationHys(pc,
575  _s_l_min,
576  _s_gr_tps[_qp].at(0),
577  _alpha_w,
578  _n_w,
579  _w_low_ext_tps[_qp].at(0),
580  _w_high_ext_tps[_qp].at(0));
581  const Real max_s = (_w_high_ext_tps[_qp].at(0).strategy ==
583  ? 1.0
584  : 1.0 - _s_gr_tps[_qp].at(0);
585  if (pc <= 0.0 && _w_high_ext_tps[_qp].at(0).strategy ==
587  return 0.0;
588  return d2sat_to_use * (max_s - _hys_sat_tps[_qp].at(0)) / (max_s - _s_w_tps[_qp].at(0));
589 }
590 
591 Real
593 {
594  // This is the inverse of secondOrderDryingPc: see that method for comments
595  const Real sat_to_use =
597  const Real tp0 = _hys_sat_tps[_qp].at(0);
598  const Real tp1 = _hys_sat_tps[_qp].at(1);
599  const Real s1 = _s_d_tps[_qp].at(1);
600  return (sat_to_use >= tp0) ? (sat_to_use - tp0) * (tp1 - tp0) / (s1 - tp0) + tp0 : sat_to_use;
601 }
602 
603 Real
605 {
606  const Real sat_to_use =
608  const Real dsat_to_use =
610  const Real tp0 = _hys_sat_tps[_qp].at(0);
611  const Real tp1 = _hys_sat_tps[_qp].at(1);
612  const Real s1 = _s_d_tps[_qp].at(1);
613  return (sat_to_use >= tp0) ? dsat_to_use * (tp1 - tp0) / (s1 - tp0) : dsat_to_use;
614 }
615 
616 Real
618 {
619  const Real sat_to_use =
621  const Real d2sat_to_use =
623  const Real tp0 = _hys_sat_tps[_qp].at(0);
624  const Real tp1 = _hys_sat_tps[_qp].at(1);
625  const Real s1 = _s_d_tps[_qp].at(1);
626  return (sat_to_use >= tp0) ? d2sat_to_use * (tp1 - tp0) / (s1 - tp0) : d2sat_to_use;
627 }
628 
629 Real
631 {
632  Real sat = 0.0;
633  if (_hys_order[_qp] == 0) // on primary drying curve
635  else if (_hys_order[_qp] == 1) // first-order wetting
636  sat = firstOrderWettingSat(pc);
637  else if (_hys_order[_qp] == 2) // second-order drying
638  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  const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
647  const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
648  const Real sat1 = firstOrderWettingSat(pc);
649  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  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  else if (pc > pc_tp2)
657  sat = sat2;
658  else if (pc < pc_tp1)
659  sat = sat1;
660  else
661  sat = sat1 + (std::log(pc) - std::log(pc_tp1)) * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1);
662  }
663  return sat;
664 }
665 
666 Real
668 {
669  Real dsat = 0.0;
670  if (_hys_order[_qp] == 0) // on primary drying curve
672  else if (_hys_order[_qp] == 1) // first-order wetting
673  dsat = dfirstOrderWettingSat(pc);
674  else if (_hys_order[_qp] == 2) // second-order drying
675  dsat = dsecondOrderDryingSat(pc);
676  else // third order drying and wetting
677  {
678  const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
679  const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
680  const Real sat1 = firstOrderWettingSat(pc);
681  const Real sat2 = secondOrderDryingSat(pc);
682  const Real dsat1 = dfirstOrderWettingSat(pc);
683  const Real dsat2 = dsecondOrderDryingSat(pc);
684  if (pc_tp1 <= 0.0 || pc <= 0.0 || pc_tp2 <= 0.0)
685  dsat = dsat2;
686  else if (pc > pc_tp2)
687  dsat = dsat2;
688  else if (pc < pc_tp1)
689  dsat = dsat1;
690  else
691  dsat = dsat1 + 1.0 / pc * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1) +
692  (std::log(pc) - std::log(pc_tp1)) * (dsat2 - dsat1) / std::log(pc_tp2 / pc_tp1);
693  }
694  return dsat;
695 }
696 
697 Real
699 {
700  Real d2sat = 0.0;
701  if (_hys_order[_qp] == 0) // on primary drying curve
703  else if (_hys_order[_qp] == 1) // first-order wetting
704  d2sat = d2firstOrderWettingSat(pc);
705  else if (_hys_order[_qp] == 2) // second-order drying
706  d2sat = d2secondOrderDryingSat(pc);
707  else // third order drying and wetting
708  {
709  const Real pc_tp1 = _pc_tps[_qp].at(1); // pc on first-order wetting at TP_1
710  const Real pc_tp2 = _pc_tps[_qp].at(2); // pc on second-order drying at TP_2
711  const Real sat1 = firstOrderWettingSat(pc);
712  const Real sat2 = secondOrderDryingSat(pc);
713  const Real dsat1 = dfirstOrderWettingSat(pc);
714  const Real dsat2 = dsecondOrderDryingSat(pc);
715  const Real d2sat1 = d2firstOrderWettingSat(pc);
716  const Real d2sat2 = d2secondOrderDryingSat(pc);
717  if (pc_tp1 <= 0.0 || pc <= 0.0 || pc_tp2 <= 0.0)
718  d2sat = d2sat2;
719  else if (pc > pc_tp2)
720  d2sat = d2sat2;
721  else if (pc < pc_tp1)
722  d2sat = d2sat1;
723  else
724  d2sat = d2sat1 - 1.0 / pc / pc * (sat2 - sat1) / std::log(pc_tp2 / pc_tp1) +
725  2.0 / pc * (dsat2 - dsat1) / std::log(pc_tp2 / pc_tp1) +
726  (std::log(pc) - std::log(pc_tp1)) * (d2sat2 - d2sat1) / std::log(pc_tp2 / pc_tp1);
727  }
728  return d2sat;
729 }
Real dcapillaryPressureHys(Real sl, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Derivative of capillaryPressureHys with respect to sl.
virtual void computeQpProperties() override
Real d2capillaryPressureHys(Real sl, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Second derivative of capillaryPressureHys with respect to sl.
const MaterialProperty< Real > & _pc_older
Older value of capillary pressure.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
const Real _s_gr_max
Residual gas saturation: 1 - _s_gr_max is the maximum saturation for which the van Genuchten expressi...
MaterialProperty< std::array< PorousFlowVanGenuchten::LowCapillaryPressureExtension, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _w_low_ext_tps
Nodal or quadpoint values of the low extension of the wetting curve defined by _s_gr_tps.
MaterialProperty< std::array< PorousFlowVanGenuchten::HighCapillaryPressureExtension, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _w_high_ext_tps
Nodal or quadpoint values of the high extension of the wetting curve defined by _s_gr_tps.
const PorousFlowVanGenuchten::LowCapillaryPressureExtension::ExtensionStrategy _low_ext_type
Type of low-saturation extension.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const MaterialProperty< unsigned > & _hys_order
Hysteresis order, as computed by PorousFlowHysteresisOrder.
const Real _high_ratio
The high-saturation extension to the wetting will commence at _high_ratio * (1 - _s_gr_del) ...
const Real _pc_max
Maximum capillary pressure: for Pc above this value, a "lower" extension will be used.
Parameters associated with the extension of the hysteretic wetting capillary pressure function to hig...
void mooseWarning(Args &&... args)
static InputParameters validParams()
const Real _s_lr
Liquid saturation below which the liquid relative permeability is zero.
const PorousFlowVanGenuchten::HighCapillaryPressureExtension::ExtensionStrategy _high_ext_type
Type of high-saturation extension of the wetting curves.
van Genuchten effective saturation, capillary pressure and relative permeability functions.
void computeTurningPointInfo(unsigned tp_num, Real tp_sat, Real tp_pc)
Compute all relevant quantities at the given turning point.
const Real _alpha_d
van Genuchten alpha parameter for the primary drying curve
const Real _s_high
Saturation at the point of high-saturation extension.
const Real _n_d
van Genuchten n parameter for the primary drying curve
PorousFlowHystereticCapillaryPressure(const InputParameters &parameters)
Real capillaryPressureHys(Real sl, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Hysteretic capillary pressure function (Eqn(1) of Doughty2007) with extensions (page5 and Fig1 of Dou...
constexpr unsigned MAX_HYSTERESIS_ORDER
const MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps
Saturation values at the turning points, as computed by PorousFlowHysteresisOrder.
Parameters associated with the extension of the hysteretic capillary pressure function to low saturat...
Base class for thermophysical variable materials, which assemble materials for primary variables such...
registerMooseObject("PorousFlowApp", PorousFlowHystereticCapillaryPressure)
const Real _alpha_w
van Genuchten alpha parameter for the primary wetting curve
Base material designed to calculate and store quantities relevant for hysteretic capillary pressure c...
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _pc_tps
Nodal or quadpoint values of Pc at the turning points.
const Real _n_w
van Genuchten n parameter for the primary wetting curve
virtual void initQpStatefulProperties() override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const PorousFlowVanGenuchten::LowCapillaryPressureExtension _low_ext_d
Parameters involved in the low-saturation extension of the primary drying curve.
const Real _s_low_w
Saturation on the primary wetting curve where low-saturation extension commences. ...
void addClassDescription(const std::string &doc_string)
Real dsaturationHys(Real pc, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Derivative of Hysteretic saturation function with respect to pc.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _s_gr_tps
Computed nodal or quadpoint values of S_gr_Del, ie, the Land expression, at the turning points...
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _s_w_tps
Computed nodal or quadpoint values of liquid saturation on the wetting curve defined by _s_gr_del...
Real d2saturationHys(Real pc, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Second derivative of Hysteretic saturation function with respect to pc.
const Real _s_l_min
Minimum liquid saturation for which the van Genuchten expression is valid (Pc(_s_l_min) = infinity) ...
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _s_d_tps
Computed nodal or quadpoint values of saturation on the drying curve at _pc_tps.
const MaterialProperty< unsigned > & _hys_order_old
Old value of hysteresis order, as computed by PorousFlowHysteresisOrder.
Real saturationHys(Real pc, Real slmin, Real sgrdel, Real alpha, Real n, const LowCapillaryPressureExtension &low_ext=LowCapillaryPressureExtension(), const HighCapillaryPressureExtension &high_ext=HighCapillaryPressureExtension())
Hysteretic saturation function (Eqn(1) of Doughty2007) with extensions (page5 and Fig1 of Doughty2008...