https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PorousFlowVanGenuchten.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
11#include "PorousFlowCubic.h"
12
14{
15Real
16effectiveSaturation(Real p, Real alpha, Real m)
17{
18 Real n, seff;
19
20 if (p >= 0.0)
21 return 1.0;
22 else
23 {
24 n = 1.0 / (1.0 - m);
25 seff = 1.0 + std::pow(-alpha * p, n);
26 return std::pow(seff, -m);
27 }
28}
29
30Real
31dEffectiveSaturation(Real p, Real alpha, Real m)
32{
33 if (p >= 0.0)
34 return 0.0;
35 else
36 {
37 Real n = 1.0 / (1.0 - m);
38 Real inner = 1.0 + std::pow(-alpha * p, n);
39 Real dinner_dp = -n * alpha * std::pow(-alpha * p, n - 1.0);
40 Real dseff_dp = -m * std::pow(inner, -m - 1) * dinner_dp;
41 return dseff_dp;
42 }
43}
44
45Real
46d2EffectiveSaturation(Real p, Real alpha, Real m)
47{
48 if (p >= 0.0)
49 return 0.0;
50 else
51 {
52 Real n = 1.0 / (1.0 - m);
53 Real inner = 1.0 + std::pow(-alpha * p, n);
54 Real dinner_dp = -n * alpha * std::pow(-alpha * p, n - 1.0);
55 Real d2inner_dp2 = n * (n - 1.0) * alpha * alpha * std::pow(-alpha * p, n - 2.0);
56 Real d2seff_dp2 = m * (m + 1.0) * std::pow(inner, -m - 2.0) * std::pow(dinner_dp, 2.0) -
57 m * std::pow(inner, -m - 1.0) * d2inner_dp2;
58 return d2seff_dp2;
59 }
60}
61
62Real
63capillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
64{
65 if (seff >= 1.0)
66 return 0.0;
67 else if (seff <= 0.0)
68 return pc_max;
69 else
70 {
71 Real a = std::pow(seff, -1.0 / m) - 1.0;
72 return std::min(std::pow(a, 1.0 - m) / alpha, pc_max);
73 }
74}
75
76Real
77dCapillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
78{
79 if (seff <= 0.0 || seff >= 1.0)
80 return 0.0;
81 else
82 {
83 Real a = std::pow(seff, -1.0 / m) - 1.0;
84 // Return 0 if pc > pc_max
85 if (std::pow(a, 1.0 - m) / alpha > pc_max)
86 return 0.0;
87 else
88 return (m - 1.0) * std::pow(a, -m) * std::pow(seff, -1.0 - 1.0 / m) / m / alpha;
89 }
90}
91
92Real
93d2CapillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
94{
95 if (seff <= 0.0 || seff >= 1.0)
96 return 0.0;
97 else
98 {
99 Real a = std::pow(seff, -1.0 / m) - 1.0;
100 // Return 0 if pc > pc_max
101 if (std::pow(a, 1.0 - m) / alpha > pc_max)
102 return 0.0;
103 else
104 {
105 Real d2pc = -std::pow(a, -1.0 - m) * std::pow(seff, -2.0 - 2.0 / m) +
106 ((1.0 + m) / m) * std::pow(a, -m) * std::pow(seff, -1.0 / m - 2.0);
107 d2pc *= (1.0 - m) / m / alpha;
108 return d2pc;
109 }
110 }
111}
112
113Real
114dRelativePermeability(Real seff, Real m)
115{
116 // Guard against division by zero
117 if (seff <= 0.0 || seff >= 1.0)
118 return 0.0;
119
120 const Real a = 1.0 - std::pow(seff, 1.0 / m);
121 const Real da = -1.0 / m * std::pow(seff, 1.0 / m - 1.0);
122 const Real b = 1.0 - std::pow(a, m);
123 const Real db = -m * std::pow(a, m - 1.0) * da;
124
125 return 0.5 * std::pow(seff, -0.5) * Utility::pow<2>(b) + 2.0 * std::sqrt(seff) * b * db;
126}
127
128Real
129d2RelativePermeability(Real seff, Real m)
130{
131 // Guard against division by zero
132 if (seff <= 0.0 || seff >= 1.0)
133 return 0.0;
134
135 const Real a = 1.0 - std::pow(seff, 1.0 / m);
136 const Real da = -1.0 / m * std::pow(seff, 1.0 / m - 1.0);
137 const Real d2a = -(1.0 / m) * (1.0 / m - 1.0) * std::pow(seff, 1.0 / m - 2.0);
138 const Real b = 1.0 - std::pow(a, m);
139 const Real db = -m * std::pow(a, m - 1.0) * da;
140 const Real d2b = -m * (m - 1.0) * std::pow(a, m - 2.0) * da * da - m * std::pow(a, m - 1.0) * d2a;
141
142 return -0.25 * std::pow(seff, -1.5) * Utility::pow<2>(b) + 2.0 * std::pow(seff, -0.5) * b * db +
143 2.0 * std::sqrt(seff) * db * db + 2.0 * std::sqrt(seff) * b * d2b;
144}
145
146Real
147dRelativePermeabilityNW(Real seff, Real m)
148{
149 // Guard against division by zero
150 if (seff <= 0.0 || seff >= 1.0)
151 return 0.0;
152
153 const Real a = std::pow(1.0 - seff, 1.0 / m);
154 const Real da = -1.0 / m * a / (1.0 - seff);
155 const Real b = std::pow(1.0 - a, 2.0 * m);
156 const Real db = -2.0 * m * b / (1.0 - a) * da;
157
158 return 0.5 * std::pow(seff, -0.5) * b + std::sqrt(seff) * db;
159}
160
161Real
162d2RelativePermeabilityNW(Real seff, Real m)
163{
164 // Guard against division by zero
165 if (seff <= 0.0 || seff >= 1.0)
166 return 0.0;
167
168 const Real a = std::pow(1.0 - seff, 1.0 / m);
169 const Real da = -1.0 / m * a / (1.0 - seff);
170 const Real d2a = 1.0 / m * (1.0 / m - 1) * std::pow(1.0 - seff, 1.0 / m - 2.0);
171 const Real b = std::pow(1.0 - a, 2.0 * m);
172 const Real db = -2.0 * m * b / (1.0 - a) * da;
173 const Real d2b =
174 -2.0 * m * (db / (1.0 - a) * da + b * Utility::pow<2>(da / (1.0 - a)) + b / (1.0 - a) * d2a);
175
176 return -0.25 * std::pow(seff, -1.5) * b + std::pow(seff, -0.5) * db + std::sqrt(seff) * d2b;
177}
178
179Real
181 Real slmin,
182 Real sgrdel,
183 Real alpha,
184 Real n,
185 const LowCapillaryPressureExtension & low_ext,
186 const HighCapillaryPressureExtension & high_ext)
187{
188 Real pc = 0.0;
189 if (sl < low_ext.S) // important for initializing low_ext that this is < and not <=
190 {
191 switch (low_ext.strategy)
192 {
194 pc = low_ext.Pc + low_ext.dPc * 0.5 * (sl * sl - low_ext.S * low_ext.S) / low_ext.S;
195 break;
197 pc = low_ext.Pc * std::exp(low_ext.dPc * (sl - low_ext.S) / low_ext.Pc);
198 break;
199 default:
200 pc = low_ext.Pc;
201 }
202 return pc;
203 }
204 if (sl > high_ext.S) // important for initializing high_ext that this is >, not >=
205 {
206 switch (high_ext.strategy)
207 {
209 {
210 if (sl >= 1.0)
211 pc = 0.0;
212 else
213 {
214 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
215 pc = high_ext.Pc * std::pow((1.0 - sl) / (1.0 - high_ext.S), expon);
216 }
217 break;
218 }
219 default:
220 pc = 0.0;
221 }
222 return pc;
223 }
224 const Real seff = (sl - slmin) / (1.0 - sgrdel - slmin);
225 if (seff >= 1.0)
226 pc = 0.0; // no sensible high extension defined
227 else if (seff <= 0.0)
228 pc = low_ext.Pc; // no sensible low extension defined
229 else
230 {
231 const Real a = std::pow(seff, n / (1.0 - n)) - 1.0;
232 pc = (1.0 / alpha) * std::pow(a, 1.0 / n);
233 }
234 return pc;
235}
236
237Real
239 Real slmin,
240 Real sgrdel,
241 Real alpha,
242 Real n,
243 const LowCapillaryPressureExtension & low_ext,
244 const HighCapillaryPressureExtension & high_ext)
245{
246 Real dpc = 0.0;
247 if (sl < low_ext.S) // important for initializing low_ext that this is < and not <=
248 {
249 switch (low_ext.strategy)
250 {
252 dpc = low_ext.dPc * sl / low_ext.S;
253 break;
255 dpc = low_ext.dPc * std::exp(low_ext.dPc * (sl - low_ext.S) / low_ext.Pc);
256 break;
257 default:
258 dpc = 0.0;
259 }
260 return dpc;
261 }
262 if (sl > high_ext.S) // important for initializing high_ext that this is >, not >=
263 {
264 switch (high_ext.strategy)
265 {
267 {
268 if (sl >= 1.0)
269 dpc = 0.0;
270 else
271 {
272 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
273 dpc = high_ext.dPc * std::pow((1.0 - sl) / (1.0 - high_ext.S), expon - 1.0);
274 }
275 break;
276 }
277 default:
278 dpc = 0.0;
279 }
280 return dpc;
281 }
282 const Real seff = (sl - slmin) / (1.0 - sgrdel - slmin);
283 if (seff >= 1.0)
284 dpc = 0.0; // no sensible high extension defined
285 else if (seff <= 0.0)
286 dpc = 0.0; // no sensible low extension defined
287 else
288 {
289 const Real a = std::pow(seff, n / (1.0 - n)) - 1.0;
290 const Real dseff = 1.0 / (1.0 - sgrdel - slmin);
291 const Real dpc_dseff = (1.0 / alpha / (1.0 - n)) * std::pow(a, 1.0 / n - 1.0) *
292 std::pow(seff, n / (1.0 - n) - 1.0);
293 dpc = dpc_dseff * dseff;
294 }
295 return dpc;
296}
297
298Real
300 Real slmin,
301 Real sgrdel,
302 Real alpha,
303 Real n,
304 const LowCapillaryPressureExtension & low_ext,
305 const HighCapillaryPressureExtension & high_ext)
306{
307 Real d2pc = 0.0;
308 if (sl < low_ext.S) // important for initializing low_ext that this is < and not <=
309 {
310 switch (low_ext.strategy)
311 {
313 d2pc = low_ext.dPc / low_ext.S;
314 break;
316 d2pc = std::pow(low_ext.dPc, 2) / low_ext.Pc *
317 std::exp(low_ext.dPc * (sl - low_ext.S) / low_ext.Pc);
318 break;
319 default:
320 d2pc = 0.0;
321 }
322 return d2pc;
323 }
324 if (sl > high_ext.S) // important for initializing high_ext that this is >, not >=
325 {
326 switch (high_ext.strategy)
327 {
329 {
330 if (sl >= 1.0)
331 d2pc = 0.0;
332 else
333 {
334 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
335 d2pc = high_ext.dPc * (1.0 - expon) / (1.0 - high_ext.S) *
336 std::pow((1.0 - sl) / (1.0 - high_ext.S), expon - 2.0);
337 }
338 break;
339 }
340 default:
341 d2pc = 0.0;
342 }
343 return d2pc;
344 }
345 const Real seff = (sl - slmin) / (1.0 - sgrdel - slmin);
346 if (seff >= 1.0)
347 d2pc = 0.0; // no sensible high extension defined
348 else if (seff <= 0.0)
349 d2pc = 0.0; // no sensible low extension defined
350 else
351 {
352 const Real a = std::pow(seff, n / (1.0 - n)) - 1.0;
353 const Real dseff = 1.0 / (1.0 - sgrdel - slmin);
354 const Real d2pc_dseff =
355 (1.0 / alpha / (1.0 - n)) *
356 (std::pow(a, 1.0 / n - 2.0) * std::pow(seff, 2.0 * (n / (1.0 - n) - 1.0)) +
357 (n / (1.0 - n) - 1.0) * std::pow(a, 1.0 / n - 1.0) * std::pow(seff, n / (1.0 - n) - 2.0));
358 d2pc = d2pc_dseff * dseff * dseff;
359 }
360 return d2pc;
361}
362
363Real
365 Real slmin,
366 Real sgrdel,
367 Real alpha,
368 Real n,
369 const LowCapillaryPressureExtension & low_ext,
370 const HighCapillaryPressureExtension & high_ext)
371{
372 if (pc <= 0)
373 return 1.0;
374 Real s = 1.0;
375 if (pc > low_ext.Pc) // important for initialization of the low_ext that this is > and not >=
376 {
377 switch (low_ext.strategy)
378 {
380 {
381 const Real s2 = low_ext.S * low_ext.S + 2.0 * (pc - low_ext.Pc) * low_ext.S / low_ext.dPc;
382 if (s2 <= 0.0) // this occurs when we're trying to find a saturation on the wetting curve
383 // defined by sl = sgrDel at pc = Pcd_Del, if this pc is actually impossible
384 // to achieve on this wetting curve
385 s = 0.0;
386 else
387 s = std::sqrt(s2);
388 break;
389 }
391 {
392 const Real ss = low_ext.S + std::log(pc / low_ext.Pc) * low_ext.Pc / low_ext.dPc;
393 if (ss <= 0.0) // this occurs when we're trying to find a saturation on the
394 // wetting curve defined by sl = sgrDel at pc = Pcd_Del, if this
395 // pc is actually impossible to achieve on this wetting curve
396 s = 0.0;
397 else
398 s = ss;
399 break;
400 }
401 default:
402 s = low_ext.S;
403 }
404 return s;
405 }
406 if (pc < high_ext.Pc) // important for initialization of the high_ext that this is < and not <=
407 {
408 switch (high_ext.strategy)
409 {
411 {
412 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
413 s = 1.0 - std::pow(pc / high_ext.Pc, 1.0 / expon) * (1.0 - high_ext.S);
414 break;
415 }
416 default:
417 s = high_ext.S;
418 }
419 return s;
420 }
421 if (pc == std::numeric_limits<Real>::max())
422 s = 0.0;
423 else
424 {
425 const Real seffpow = 1.0 + std::pow(pc * alpha, n);
426 const Real seff = std::pow(seffpow, (1.0 - n) / n);
427 s = (1.0 - sgrdel - slmin) * seff + slmin;
428 }
429 return s;
430}
431
432Real
434 Real slmin,
435 Real sgrdel,
436 Real alpha,
437 Real n,
438 const LowCapillaryPressureExtension & low_ext,
439 const HighCapillaryPressureExtension & high_ext)
440{
441 if (pc <= 0)
442 return 0.0;
443 Real ds = 0.0;
444 if (pc > low_ext.Pc) // important for initialization of the low_ext that this is > and not >=
445 {
446 switch (low_ext.strategy)
447 {
449 {
450 const Real s2 = low_ext.S * low_ext.S + 2.0 * (pc - low_ext.Pc) * low_ext.S / low_ext.dPc;
451 if (s2 <= 0.0) // this occurs when we're trying to find a saturation on the wetting curve
452 // defined by sl = sgrDel at pc = Pcd_Del, if this pc is actually impossible
453 // to achieve on this wetting curve
454 ds = 0.0;
455 else
456 {
457 const Real ds2 = 2.0 * low_ext.S / low_ext.dPc;
458 ds = 0.5 * ds2 / std::sqrt(s2);
459 }
460 break;
461 }
463 {
464 const Real s = low_ext.S + std::log(pc / low_ext.Pc) * low_ext.Pc / low_ext.dPc;
465 if (s <= 0.0) // this occurs when we're trying to find a saturation on the
466 // wetting curve defined by sl = sgrDel at pc = Pcd_Del, if this
467 // pc is actually impossible to achieve on this wetting curve
468 ds = 0.0;
469 else
470 ds = low_ext.Pc / pc / low_ext.dPc;
471 break;
472 }
473 default:
474 ds = 0.0;
475 }
476 return ds;
477 }
478 if (pc < high_ext.Pc) // important for initialization of the high_ext that this is < and not <=
479 {
480 switch (high_ext.strategy)
481 {
483 {
484 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
485 ds = -(1.0 - high_ext.S) / pc / expon * std::pow(pc / high_ext.Pc, 1.0 / expon);
486 break;
487 }
488 default:
489 ds = 0.0;
490 }
491 return ds;
492 }
493 if (pc == std::numeric_limits<Real>::max())
494 ds = 0.0;
495 else
496 {
497 const Real seffpow = 1.0 + std::pow(pc * alpha, n);
498 const Real dseffpow = n * (seffpow - 1.0) / pc;
499 const Real seff = std::pow(seffpow, (1.0 - n) / n);
500 const Real dseff = (1.0 - n) / n * seff / seffpow * dseffpow;
501 ds = (1.0 - sgrdel - slmin) * dseff;
502 }
503 return ds;
504}
505
506Real
508 Real slmin,
509 Real sgrdel,
510 Real alpha,
511 Real n,
512 const LowCapillaryPressureExtension & low_ext,
513 const HighCapillaryPressureExtension & high_ext)
514{
515 if (pc <= 0)
516 return 0.0;
517 Real d2s = 0.0;
518 if (pc > low_ext.Pc) // important for initialization of the low_ext that this is > and not >=
519 {
520 switch (low_ext.strategy)
521 {
523 {
524 const Real s2 = low_ext.S * low_ext.S + 2.0 * (pc - low_ext.Pc) * low_ext.S / low_ext.dPc;
525 if (s2 <= 0.0) // this occurs when we're trying to find a saturation on the wetting curve
526 // defined by sl = sgrDel at pc = Pcd_Del, if this pc is actually impossible
527 // to achieve on this wetting curve
528 d2s = 0.0;
529 else
530 {
531 const Real ds2 = 2.0 * low_ext.S / low_ext.dPc;
532 d2s = -0.25 * ds2 * ds2 / std::pow(s2, 1.5);
533 }
534 break;
535 }
537 {
538 const Real s = low_ext.S + std::log(pc / low_ext.Pc) * low_ext.Pc / low_ext.dPc;
539 if (s <= 0.0) // this occurs when we're trying to find a saturation on the
540 // wetting curve defined by sl = sgrDel at pc = Pcd_Del, if this
541 // pc is actually impossible to achieve on this wetting curve
542 d2s = 0.0;
543 else
544 d2s = -low_ext.Pc / std::pow(pc, 2.0) / low_ext.dPc;
545 break;
546 }
547 default:
548 d2s = 0.0;
549 }
550 return d2s;
551 }
552 if (pc < high_ext.Pc) // important for initialization of the high_ext that this is < and not <=
553 {
554 switch (high_ext.strategy)
555 {
557 {
558 const Real expon = -high_ext.dPc / high_ext.Pc * (1.0 - high_ext.S);
559 d2s = -(1.0 - high_ext.S) * (1.0 / expon) * (1.0 / expon - 1.0) /
560 std::pow(high_ext.Pc, 2.0) * std::pow(pc / high_ext.Pc, 1.0 / expon - 2.0);
561 break;
562 }
563 default:
564 d2s = 0.0;
565 }
566 return d2s;
567 }
568 if (pc == std::numeric_limits<Real>::max())
569 d2s = 0.0;
570 else
571 {
572 const Real seffpow = 1.0 + std::pow(pc * alpha, n);
573 const Real dseffpow = n * (seffpow - 1.0) / pc;
574 const Real d2seffpow = (n - 1.0) * dseffpow / pc;
575 const Real seff = std::pow(seffpow, (1.0 - n) / n);
576 const Real dseff = (1.0 - n) / n * seff / seffpow * dseffpow;
577 const Real d2seff =
578 (1.0 - n) / n *
579 (dseff * dseffpow - seff * dseffpow * dseffpow / seffpow + seff * d2seffpow) / seffpow;
580 d2s = (1.0 - sgrdel - slmin) * d2seff;
581 }
582 return d2s;
583}
584
585Real
587 Real slr,
588 Real sgrdel,
589 Real sgrmax,
590 Real sldel,
591 Real m,
592 Real upper_liquid_param,
593 Real y0,
594 Real y0p,
595 Real y1,
596 Real y1p)
597{
598 if (sl <= slr) // by the definition of slr, always return 0
599 return 0.0;
600 const Real sl_bar = (sl - slr) / (1.0 - slr); // effective saturation
601 // a and b are useful parameters. Define b along the drying curve initially, and
602 // modify a and b appropriately if the wetting result is required
603 Real a = 0;
604 Real b = 0;
605 if (sgrdel == 0.0 || sl <= sldel) // along the drying curve
606 a = std::pow(1.0 - std::pow(sl_bar, 1.0 / m), m);
607 else // along the wetting curve
608 {
609 // In most cases, use sldel and sgrdel as provided to this function. However, because "there is
610 // no hysteresis along the extension" according to p6 of Doughty2008, if the turning point is
611 // less than slr, then use the expressions for the case when the turning point was slr
612 const Real my_sldel = (sldel < slr) ? slr : sldel;
613 const Real my_sgrdel = (sldel < slr) ? sgrmax : sgrdel;
614 if (sl >= 1.0 - 0.5 * my_sgrdel)
615 {
616 // follow the drying curve. The parameter b has already been defined. It
617 // is important for initialization of the curic that the above condition is >= and not >
618 a = std::pow(1.0 - std::pow(sl_bar, 1.0 / m), m);
619 }
620 else if (sl > upper_liquid_param * (1.0 - my_sgrdel))
621 {
622 // follow the cubic modification of the wetting curve. Immediately exit from this function by
623 // returning the cubic result
625 sl, upper_liquid_param * (1.0 - my_sgrdel), y0, y0p, 1.0 - 0.5 * my_sgrdel, y1, y1p);
626 }
627 else
628 {
629 // standard case of wetting curve outside the cubic-modification and drying-curve regions
630 const Real sl_bar_del = (my_sldel - slr) / (1.0 - slr);
631 const Real s_gt_bar =
632 my_sgrdel * (sl - my_sldel) / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
633 a = (1 - s_gt_bar / (1.0 - sl_bar_del)) *
634 std::pow(1.0 - std::pow(sl_bar + s_gt_bar, 1.0 / m), m);
635 b = s_gt_bar / (1.0 - sl_bar_del) * std::pow(1.0 - std::pow(sl_bar_del, 1.0 / m), m);
636 }
637 }
638 return std::sqrt(sl_bar) * Utility::pow<2>(1.0 - a - b);
639}
640
641Real
643 Real slr,
644 Real sgrdel,
645 Real sgrmax,
646 Real sldel,
647 Real m,
648 Real upper_liquid_param,
649 Real y0,
650 Real y0p,
651 Real y1,
652 Real y1p)
653{
654 if (sl <= slr) // by the definition of slr, always return 0
655 return 0.0;
656 if (sl == 1.0) // derivative is infinite at this point
657 return std::numeric_limits<Real>::max();
658 const Real sl_bar = (sl - slr) / (1.0 - slr); // effective saturation
659 const Real sl_bar_prime = 1.0 / (1.0 - slr);
660 // a and b are useful parameters. Define b along the drying curve initially, and
661 // modify a and b appropriately if the wetting result is required
662 Real a = 0;
663 Real a_prime = 0.0;
664 Real b = 0;
665 Real b_prime = 0.0;
666 if (sgrdel == 0.0 || sl <= sldel) // along the drying curve
667 {
668 const Real c = std::pow(sl_bar, 1.0 / m);
669 const Real dc_dsbar = c / m / sl_bar;
670 a = std::pow(1.0 - c, m);
671 const Real da_dsbar = -m * a / (1.0 - c) * dc_dsbar;
672 a_prime = da_dsbar * sl_bar_prime;
673 }
674 else // along the wetting curve
675 {
676 // In most cases, use sldel and sgrdel as provided to this function. However, because "there is
677 // no hysteresis along the extension" according to p6 of Doughty2008, if the turning point is
678 // less than slr, then use the expressions for the case when the turning point was slr
679 const Real my_sldel = (sldel < slr) ? slr : sldel;
680 const Real my_sgrdel = (sldel < slr) ? sgrmax : sgrdel;
681 if (sl >= 1.0 - 0.5 * my_sgrdel)
682 {
683 // follow the drying curve. The parameter b has already been defined. It
684 // is important for initialization of the curic that the above condition is >= and not >
685 const Real c = std::pow(sl_bar, 1.0 / m);
686 const Real dc_dsbar = c / m / sl_bar;
687 a = std::pow(1.0 - c, m);
688 const Real da_dsbar = -m * a / (1.0 - c) * dc_dsbar;
689 a_prime = da_dsbar * sl_bar_prime;
690 }
691 else if (sl > upper_liquid_param * (1.0 - my_sgrdel))
692 {
693 // follow the cubic modification of the wetting curve. Immediately exit from this function by
694 // returning the cubic result
696 sl, upper_liquid_param * (1.0 - my_sgrdel), y0, y0p, 1.0 - 0.5 * my_sgrdel, y1, y1p);
697 }
698 else
699 {
700 // standard case of wetting curve outside the cubic-modification and drying-curve regions
701 const Real sl_bar_del = (my_sldel - slr) / (1.0 - slr);
702 const Real s_gt_bar =
703 my_sgrdel * (sl - my_sldel) / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
704 const Real s_gt_bar_prime = my_sgrdel / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
705 const Real c = std::pow(sl_bar + s_gt_bar, 1.0 / m);
706 const Real c_prime = c / m / (sl_bar + s_gt_bar) * (sl_bar_prime + s_gt_bar_prime);
707 a = (1 - s_gt_bar / (1.0 - sl_bar_del)) * std::pow(1.0 - c, m);
708 a_prime =
709 -s_gt_bar_prime / (1.0 - sl_bar_del) * std::pow(1.0 - c, m) - m * a / (1.0 - c) * c_prime;
710 b = s_gt_bar / (1.0 - sl_bar_del) * std::pow(1.0 - std::pow(sl_bar_del, 1.0 / m), m);
711 b_prime = s_gt_bar_prime * b / s_gt_bar;
712 }
713 }
714 const Real kr = std::sqrt(sl_bar) * Utility::pow<2>(1.0 - a - b);
715 return 0.5 * kr / sl_bar * sl_bar_prime -
716 std::sqrt(sl_bar) * 2.0 * (1.0 - a - b) * (a_prime + b_prime);
717}
718
719Real
721 Real slr,
722 Real sgrdel,
723 Real sgrmax,
724 Real sldel,
725 Real m,
726 Real gamma,
727 Real k_rg_max,
728 Real y0p)
729{
730 if (sl < slr)
731 {
732 // in the extended region, so immediately return with the relevant value
733 if (k_rg_max == 1.0)
734 return 1.0;
735 return PorousFlowCubic::cubic(sl, 0.0, 1.0, 0.0, slr, k_rg_max, y0p);
736 }
737 if (sl > 1.0 - sgrdel) // saturation is above 1.0 - residual gas saturation
738 return 0.0;
739 const Real sl_bar = (sl - slr) / (1.0 - slr);
740 Real s_gt_bar = 0.0; // initialize this parameter as if on the drying curve
741 if (sgrdel != 0.0 && sl > sldel)
742 {
743 // On the wetting curve
744 // In most cases, use sldel and sgrdel as provided to this function. However, because "there is
745 // no hysteresis along the extension" according to p6 of Doughty2008, if the turning point is
746 // less than slr, then use the expressions for the case when the turning point was slr
747 const Real my_sldel = (sldel < slr) ? slr : sldel;
748 const Real my_sgrdel = (sldel < slr) ? sgrmax : sgrdel;
749 s_gt_bar = my_sgrdel * (sl - my_sldel) / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
750 }
751 Real kr = 0.0;
752 if (sl_bar + s_gt_bar < 1.0) // check for the condition where sl is too big, in which case kr =
753 // 0, irrespective of hysteresis
754 {
755 const Real a = std::pow(1.0 - (sl_bar + s_gt_bar), gamma);
756 const Real c = std::pow(sl_bar + s_gt_bar, 1.0 / m);
757 const Real b = std::pow(1.0 - c, 2.0 * m);
758 kr = k_rg_max * a * b;
759 }
760 return kr;
761}
762
763Real
765 Real slr,
766 Real sgrdel,
767 Real sgrmax,
768 Real sldel,
769 Real m,
770 Real gamma,
771 Real k_rg_max,
772 Real y0p)
773{
774 if (sl < slr)
775 {
776 // in the extended region, so immediately return with the relevant value
777 if (k_rg_max == 1.0)
778 return 0.0;
779 return PorousFlowCubic::dcubic(sl, 0.0, 1.0, 0.0, slr, k_rg_max, y0p);
780 }
781 if (sl > 1.0 - sgrdel) // saturation is above 1.0 - residual gas saturation
782 return 0.0;
783 const Real sl_bar = (sl - slr) / (1.0 - slr);
784 const Real sl_bar_prime = 1.0 / (1.0 - slr);
785 Real s_gt_bar = 0.0; // initialize this parameter as if on the drying curve
786 Real s_gt_bar_prime = 0.0; // again, assume on drying curve
787 if (sgrdel != 0.0 && sl > sldel)
788 {
789 // On the wetting curve
790 // In most cases, use sldel and sgrdel as provided to this function. However, because "there is
791 // no hysteresis along the extension" according to p6 of Doughty2008, if the turning point is
792 // less than slr, then use the expressions for the case when the turning point was slr
793 const Real my_sldel = (sldel < slr) ? slr : sldel;
794 const Real my_sgrdel = (sldel < slr) ? sgrmax : sgrdel;
795 s_gt_bar = my_sgrdel * (sl - my_sldel) / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
796 s_gt_bar_prime = my_sgrdel / (1.0 - slr) / (1.0 - my_sldel - my_sgrdel);
797 }
798 Real kr_prime = 0.0;
799 if (sl_bar + s_gt_bar < 1.0) // check for the condition where sl is too big, in which case kr =
800 // 0, irrespective of hysteresis
801 {
802 const Real a = std::pow(1.0 - (sl_bar + s_gt_bar), gamma);
803 const Real a_prime = -gamma * a / (1.0 - (sl_bar + s_gt_bar)) * (sl_bar_prime + s_gt_bar_prime);
804 const Real c = std::pow(sl_bar + s_gt_bar, 1.0 / m);
805 const Real c_prime =
806 (c == 0 ? 0.0 : c / m / (sl_bar + s_gt_bar) * (sl_bar_prime + s_gt_bar_prime));
807 const Real b = std::pow(1.0 - c, 2.0 * m);
808 const Real b_prime = -2.0 * m * b / (1.0 - c) * c_prime;
809 kr_prime = k_rg_max * (a * b_prime + a_prime * b);
810 }
811 return kr_prime;
812}
813}
const Real p
const GeochemicalDatabaseReader db("database/moose_testdb.json", true, true, false)
Real dcubic(Real x, Real x0, Real y0, Real y0p, Real x1, Real y1, Real y1p)
Derivative of cubic function, f(x), with respect to x.
Real cubic(Real x, Real x0, Real y0, Real y0p, Real x1, Real y1, Real y1p)
Cubic function f(x) that satisfies f(x0) = y0 f'(x0) = y0p f(x1) = y1 f'(x1) = y1p.
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 drelativePermeabilityNWHys(Real sl, Real slr, Real sgrdel, Real sgrmax, Real sldel, Real m, Real gamma, Real k_rg_max, Real y0p)
Derivative of hysteretic relative permeability for gas with respect to the liquid saturation.
Real dRelativePermeability(Real seff, Real m)
Derivative of relative permeability with respect to effective saturation.
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 relativePermeabilityNWHys(Real sl, Real slr, Real sgrdel, Real sgrmax, Real sldel, Real m, Real gamma, Real k_rg_max, Real y0p)
Hysteretic relative permeability for gas.
Real d2EffectiveSaturation(Real p, Real alpha, Real m)
Second derivative of effective saturation wrt porepressure.
Real dEffectiveSaturation(Real p, Real alpha, Real m)
Derivative of effective saturation wrt porepressure.
Real dCapillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
Derivative of capillary pressure wrt effective saturation.
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 drelativePermeabilityHys(Real sl, Real slr, Real sgrdel, Real sgrmax, Real sldel, Real m, Real upper_liquid_param, Real y0, Real y0p, Real y1, Real y1p)
Derivative of Hysteretic relative permeability for liquid, with respect to liquid saturation.
Real capillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
Capillary pressure as a function of effective saturation.
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 d2CapillaryPressure(Real seff, Real alpha, Real m, Real pc_max)
Second derivative of capillary pressure wrt effective saturation.
Real effectiveSaturation(Real p, Real alpha, Real m)
Effective saturation as a function of porepressure.
Real relativePermeabilityHys(Real sl, Real slr, Real sgrdel, Real sgrmax, Real sldel, Real m, Real upper_liquid_param, Real y0, Real y0p, Real y1, Real y1p)
Hysteretic relative permeability for liquid.
Real d2RelativePermeabilityNW(Real seff, Real m)
Second derivative of relative permeability for a non-wetting phase with respect to effective saturati...
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.
Real d2RelativePermeability(Real seff, Real m)
Second derivative of relative permeability with respect to effective saturation.
Real dRelativePermeabilityNW(Real seff, Real m)
Derivative of relative permeability for a non-wetting phase with respect to effective saturation.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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...