https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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");
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)),
119 _dpc_low_w(PorousFlowVanGenuchten::dcapillaryPressureHys(
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)),
128 _dpc_high(PorousFlowVanGenuchten::dcapillaryPressureHys(
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,
163 PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
164 "PorousFlow_hysteresis_w_low_ext_tps_nodal")
165 : declareProperty<std::array<PorousFlowVanGenuchten::LowCapillaryPressureExtension,
166 PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
167 "PorousFlow_hysteresis_w_low_ext_tps_qp")),
168 _w_high_ext_tps(
169 _nodal_material
170 ? declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
171 PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
172 "PorousFlow_hysteresis_w_high_ext_tps_nodal")
173 : declareProperty<std::array<PorousFlowVanGenuchten::HighCapillaryPressureExtension,
174 PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
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
192void
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
219void
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
232Real
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
239void
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);
260 const Real dpc_w_low_ext = PorousFlowVanGenuchten::dcapillaryPressureHys(
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
299Real
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
331Real
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
370Real
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
420Real
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
445Real
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
466Real
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
487Real
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
503Real
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
516Real
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
529Real
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
551Real
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
571Real
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
591Real
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
603Real
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
616Real
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
629Real
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
666Real
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
697Real
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}
void mooseWarning(Args &&... args)
registerMooseObject("PorousFlowApp", PorousFlowHystereticCapillaryPressure)
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
Base material designed to calculate and store quantities relevant for hysteretic capillary pressure c...
const PorousFlowVanGenuchten::LowCapillaryPressureExtension _low_ext_d
Parameters involved in the low-saturation extension of the primary drying curve.
const Real _high_ratio
The high-saturation extension to the wetting will commence at _high_ratio * (1 - _s_gr_del)
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
Hysteresis order, as computed by PorousFlowHysteresisOrder.
const PorousFlowVanGenuchten::HighCapillaryPressureExtension::ExtensionStrategy _high_ext_type
Type of high-saturation extension of the wetting curves.
const Real _s_l_min
Minimum liquid saturation for which the van Genuchten expression is valid (Pc(_s_l_min) = infinity)
const Real _s_low_w
Saturation on the primary wetting curve where low-saturation extension commences.
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _pc_tps
Nodal or quadpoint values of Pc at the turning points.
const MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps
Saturation values at the turning points, as computed by PorousFlowHysteresisOrder.
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::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 Real _n_d
van Genuchten n parameter for the primary drying curve
void computeTurningPointInfo(unsigned tp_num, Real tp_sat, Real tp_pc)
Compute all relevant quantities at the given turning point.
const Real _s_lr
Liquid saturation below which the liquid relative permeability is zero.
const Real _alpha_w
van Genuchten alpha parameter for the primary wetting curve
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.
const Real _pc_max
Maximum capillary pressure: for Pc above this value, a "lower" extension will be used.
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 MaterialProperty< Real > & _pc_older
Older value of capillary pressure.
PorousFlowHystereticCapillaryPressure(const InputParameters &parameters)
const Real _n_w
van Genuchten n parameter for the primary wetting curve
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,...
const MaterialProperty< unsigned > & _hys_order_old
Old value of hysteresis order, as computed by PorousFlowHysteresisOrder.
const PorousFlowVanGenuchten::LowCapillaryPressureExtension::ExtensionStrategy _low_ext_type
Type of low-saturation extension.
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.
Base class for thermophysical variable materials, which assemble materials for primary variables such...
virtual void computeQpProperties() override
static InputParameters validParams()
virtual void initQpStatefulProperties() override
van Genuchten effective saturation, capillary pressure and relative permeability functions.
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.
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.
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...
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...
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.
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.
Parameters associated with the extension of the hysteretic wetting capillary pressure function to hig...
Parameters associated with the extension of the hysteretic capillary pressure function to low saturat...