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