3.5-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
localrulesforplatonicbody.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*****************************************************************************
4 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
26#ifndef DUMUX_PNM_2P_LOCAL_RULES_FOR_PLATONIC_BODY_HH
27#define DUMUX_PNM_2P_LOCAL_RULES_FOR_PLATONIC_BODY_HH
28
29#include <algorithm>
30#include <cassert>
31#include <cmath>
32#include <optional>
33#include <dune/common/math.hh>
39
41
46template<class Scalar>
48{
49 PlatonicBodyParams() = default;
50
51 PlatonicBodyParams& setPoreInscribedRadius(Scalar r) { radius_ = r; return *this;}
52 PlatonicBodyParams& setPoreShape(Pore::Shape s) { shape_ = s; return *this;}
53 PlatonicBodyParams& setSurfaceTension(Scalar st) { surfaceTension_ = st; return *this;}
54
55 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
56 PlatonicBodyParams(const SpatialParams& spatialParams,
57 const Element& element,
58 const SubControlVolume& scv,
59 const ElemSol& elemSol)
60 : shape_(spatialParams.gridGeometry().poreGeometry()[scv.dofIndex()])
61 , radius_(spatialParams.poreInscribedRadius(element, scv, elemSol))
62 , surfaceTension_(spatialParams.surfaceTension(element, scv, elemSol))
63 {}
64
65 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
66 void update(const SpatialParams& spatialParams,
67 const Element& element,
68 const SubControlVolume& scv,
69 const ElemSol& elemSol)
70 {
71 const auto& gridGeometry = spatialParams.gridGeometry();
72 shape_ = gridGeometry.poreGeometry()[scv.dofIndex()];
73 radius_ = spatialParams.poreInscribedRadius(element, scv, elemSol);
74 surfaceTension_ = spatialParams.surfaceTension(element, scv, elemSol);
75 }
76
77 Pore::Shape poreShape() const { return shape_; }
78
79 Scalar poreInscribedRadius() const { return radius_; }
80
81 Scalar surfaceTension() const { return surfaceTension_; }
82
83 bool operator== (const PlatonicBodyParams& p) const
84 {
85 return Dune::FloatCmp::eq(radius_, p.radius_, 1e-6)
86 && Dune::FloatCmp::eq(surfaceTension_, p.surfaceTension_, 1e-6)
87 && shape_ == p.shape_;
88 }
89
90private:
91 Pore::Shape shape_;
92 Scalar radius_;
93 Scalar surfaceTension_;
94};
95
104template<Pore::Shape shape>
106{
107 template<class Scalar>
109
110 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
111 static auto makeParams(const SpatialParams& spatialParams,
112 const Element& element,
113 const SubControlVolume& scv,
114 const ElemSol& elemSol)
115 {
116 using Scalar = std::decay_t<decltype(spatialParams.poreInscribedRadius(element, scv, elemSol))>;
117 return Params<Scalar>(spatialParams, element, scv, elemSol);
118 }
119
126 template<class Scalar>
127 static Scalar pc(Scalar sw, const Params<Scalar>& params)
128 {
129 assert(isPlatonicBody_(params.poreShape()));
130
131 using std::clamp;
132 sw = clamp(sw, 0.0, 1.0); // the equation below is only defined for 0.0 <= sw <= 1.0
133
134 const Scalar poreRadius = params.poreInscribedRadius();
135 const Scalar sigma = params.surfaceTension();
136 // TODO incorporate contact angle!!!
137
138 using std::exp;
139 return 2.0*sigma / (poreRadius*(1.0 - exp(-expFactor_<Scalar>()*sw)));
140 }
141
148 template<class Scalar>
149 static Scalar sw(Scalar pc, const Params<Scalar>& params)
150 {
151 assert(isPlatonicBody_(params.poreShape()));
152
153 using std::max;
154 pc = max(pc, 0.0); // the equation below is undefined for negative pcs
155
156 const Scalar poreRadius = params.poreInscribedRadius();
157 const Scalar sigma = params.surfaceTension();
158
159 using std::log;
160 return -1.0/expFactor_<Scalar>()* log(1.0 - 2.0*sigma/(poreRadius*pc));
161 }
162
170 template<class Scalar>
171 static Scalar dpc_dsw(Scalar sw, const Params<Scalar>& params)
172 {
173 assert(isPlatonicBody_(params.poreShape()));
174
175 using std::clamp;
176 sw = clamp(sw, 0.0, 1.0); // the equation below is only defined for 0.0 <= sw <= 1.0
177
178 const Scalar sigma = params.surfaceTension();
179 const Scalar poreRadius = params.poreInscribedRadius();
180 using std::exp;
181 const Scalar e = exp(expFactor_<Scalar>()*sw);
182 return -(2.0*expFactor_<Scalar>()*sigma*e) / (poreRadius*(1.0-e)*(1.0-e));
183 }
184
192 template<class Scalar>
193 static Scalar dsw_dpc(Scalar pc, const Params<Scalar>& params)
194 {
195 assert(isPlatonicBody_(params.poreShape()));
196
197 using std::max;
198 pc = max(pc, 0.0); // the equation below is undefined for negative pcs
199
200 const Scalar sigma = params.surfaceTension();
201 const Scalar poreRadius = params.poreInscribedRadius();
202 return sigma / (expFactor_<Scalar>()*sigma*pc - 0.5*expFactor_<Scalar>()*poreRadius*pc*pc);
203 }
204
205private:
206
207 template<class Scalar>
208 static constexpr Scalar expFactor_()
209 {
210 if constexpr (shape == Pore::Shape::tetrahedron)
211 return 3.87;
212 else if constexpr (shape == Pore::Shape::cube)
213 return 6.83;
214 else if constexpr (shape == Pore::Shape::octahedron)
215 return 8.71;
216 else if constexpr (shape == Pore::Shape::dodecahedron)
217 return 22.87;
218 else if constexpr (shape == Pore::Shape::icosahedron)
219 return 24.11;
220 else
221 {
222 static_assert(AlwaysFalse<Scalar>::value, "Shape not supported");
223 return 0;
224 }
225 }
226
227 static constexpr bool isPlatonicBody_(Pore::Shape s)
228 {
229 return s == Pore::Shape::tetrahedron ||
230 s == Pore::Shape::cube ||
234 }
235};
236
237template<class Scalar, class BaseLaw>
239{
241 using BaseLawParams = typename BaseLaw::template Params<Scalar>;
242public:
243
248 {
249 linear, spline, powerLaw
250 };
251
252 template<class S>
253 struct Params
254 {
255 // export method type
264 { pcLowSw_ = pcLowSw; }
265
269 S pcLowSw() const
270 { return pcLowSw_; }
271
276 { pcHighSw_ = pcHighSw; }
277
284 S pcHighSw() const
285 { return pcHighSw_; }
286
291 { highSwRegularizationMethod_ = method; }
292
297 { return highSwRegularizationMethod_; }
298
299private:
300 S pcLowSw_ = 0.01;
301 S pcHighSw_ = 0.99;
303 };
304
305
306 template<class MaterialLaw>
307 void init(const MaterialLaw* m, const Params<Scalar>& p, const std::string& paramGroup = "")
308 {
309 initPcParameters_(m, p, paramGroup);
310 }
311
312 OptionalScalar<Scalar> pc(const Scalar sw) const
313 {
314 // make sure that the capilary pressure observes a
315 // derivative != 0 for 'illegal' saturations. This is
316 // required for example by newton solvers (if the
317 // derivative is calculated numerically) in order to get the
318 // saturation moving to the right direction if it
319 // temporarily is in an 'illegal' range.
320 if (sw < pcLowSw_)
321 return pcLowSwPcValue_() + pcDerivativeLowSw_() * (sw - pcLowSw_);
322
323 if (sw <= pcHighSw_)
324 return {}; // standard
325 else if (sw < 1.0) // regularized part below sw = 1.0
326 {
327 using std::pow;
328 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
329 return pcHighSwPcValue_() * pow(((1.0-sw)/(1.0-pcHighSw_)), 1.0/3.0);
330
331 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
332 {
333 const Scalar slope = -pcHighSwPcValue_() / (1.0 - pcHighSw_);
334 return pcHighSwPcValue_() + (sw - pcHighSw_) * slope;
335 }
336
337 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
338 return pcSpline_().eval(sw);
339
340 else
341 DUNE_THROW(Dune::NotImplemented, "Regularization not method not implemented");
342 }
343 else // regularized part above sw = 1.0
344 return pcDerivativeHighSwEnd_()*(sw - 1.0);
345 }
346
350 OptionalScalar<Scalar> sw(const Scalar pc) const
351 {
352 if (pc <= 0.0)
353 {
354 if (pcHighSw_ >= 1.0)
355 return 1.0; // no regularization for high sw
356 else
357 return pc/pcDerivativeHighSwEnd_() + 1.0;
358 }
359
360 // low saturation
361 if (pc > pcLowSwPcValue_())
362 return (pc - pcLowSwPcValue_())/pcDerivativeLowSw_() + pcLowSw_;
363
364 // high saturation
365 else if (pc <= pcHighSwPcValue_())
366 {
367 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
368 {
369 // invert power law
370 using Dune::power;
371 return power(pc/pcHighSwPcValue_(), 3) * (pcHighSw_ - 1.0) + 1.0;
372 }
373
374 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
375 return pc/pcDerivativeHighSwEnd_() + 1.0;
376
377 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
378 // invert spline
379 return pcSpline_().intersectInterval(pcHighSw_, 1.0, 0.0, 0.0, 0.0, pc);
380 }
381
382 // no regularization
383 return {};
384 }
385
390 {
391 if (sw <= pcLowSw_)
392 return pcDerivativeLowSw_();
393
394 else if (sw >= 1.0)
395 return pcDerivativeHighSwEnd_();
396
397 else if (sw > pcHighSw_)
398 {
399 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
400 {
401 using std::pow;
402 return pcHighSwPcValue_()/3.0 * 1.0 /((pcHighSw_-1.0) * pow((sw-1.0)/(pcHighSw_-1.0), 2.0/3.0));
403 }
404
405 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
406 return pcDerivativeHighSwEnd_();
407
408 else
409 return pcSpline_().evalDerivative(sw);
410 }
411
412 else
413 return {}; // no regularization
414 }
415
420 {
421 if (pc <= 0.0)
422 {
423 if (pcHighSw_ >= 1.0)
424 return 0.0;
425 else
426 return 1.0/pcDerivativeHighSwEnd_();
427 }
428
429 // derivative of the inverse of the function is one over derivative of the function
430 else if (pc <= pcHighSwPcValue_())
431 {
432 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
433 {
434 using Dune::power;
435 return (3.0*pcHighSw_ - 3.0) * power(pc, 2) / power(pcHighSwPcValue_(), 3);
436 }
437
438 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
439 return 1.0/pcDerivativeHighSwEnd_();
440
441 else
442 return 1.0/pcSpline_().evalDerivative(pcSpline_().intersectInterval(pcHighSw_, 1.0, 0.0, 0.0, 0.0, pc));
443 }
444
445 else if (pc >= pcLowSwPcValue_())
446 return 1.0/pcDerivativeLowSw_();
447
448 else
449 return {}; // no regularization
450 }
451
452 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
453 void updateParams(const SpatialParams& spatialParams,
454 const Element& element,
455 const SubControlVolume& scv,
456 const ElemSol& elemSol)
457 {}
458
459private:
460
461 template<class MaterialLaw>
462 void initPcParameters_(const MaterialLaw* m, const Params<Scalar>& params, const std::string& paramGroup)
463 {
464 highSwRegularizationMethod_ = params.highSwRegularizationMethod();
465
466 // maybe overwrite method using input
467 static const bool hasMethod = hasParamInGroup(paramGroup, "HighSwRegularizationMethod");
468 if (hasMethod)
469 {
470 // set method only once
471 static const HighSwRegularizationMethod inputmethod = [&]
472 {
473 static const auto input = getParamFromGroup<std::string>(paramGroup, "HighSwRegularizationMethod");
474 if (input == "Linear")
476 else if (input == "Spline")
478 else if (input == "PowerLaw")
480 else
481 DUNE_THROW(Dune::InvalidStateException, input << " is not a valid regularization method");
482 }();
483
484 highSwRegularizationMethod_ = inputmethod;
485 }
486
487 static const bool splineZeroSlope = getParamFromGroup<bool>(paramGroup, "HighSwSplineZeroSlope", true);
488 highSwSplineZeroSlope_ = splineZeroSlope;
489
490 // print name only once
491 [[maybe_unused]] static const bool printName = [&]
492 {
493 std::string name;
494 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
495 name = "Linear";
496 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
497 name = "PowerLaw";
498 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
499 name = "Spline";
500 else
501 DUNE_THROW(Dune::NotImplemented, "Regularization not method not implemented");
502
503 std::cout << "\n*****\nUsing " << name << " as regularization method for high Sw\n*****" << std::endl;
504 return true;
505 }();
506
507 using std::isnan;
508 static const auto pcLowSwInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationLowSw", params.pcLowSw());
509 pcLowSw_ = !isnan(pcLowSwInput) ? pcLowSwInput : params.pcLowSw();
510
511 static const auto pcHighSwInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationHighSw", std::numeric_limits<Scalar>::quiet_NaN());
512 pcHighSw_ = !isnan(pcHighSwInput) ? pcHighSwInput : params.pcHighSw();
513
514 baseLawParamsPtr_ = &m->basicParams();
515
516 static const auto highSwFixedSlopeInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationHighSwFixedSlope", std::numeric_limits<Scalar>::quiet_NaN());
517 highSwFixedSlope_ = highSwFixedSlopeInput;
518
519 // Note: we do not pre-calculate all end-point values (e.g., pcLowSwPcValue_ and pcDerivativeLowSw_)
520 // as done in, e.g., VanGenuchten. This is because this law will generally be instantiated only as
521 // a temporary object since all pore bodies generally have different parameters.
522 // We still cache above-mentioned values, but only if they are actually needed (see below).
523 }
524
525 // the capillary pressure for the lower saturation threshold
526 Scalar pcLowSwPcValue_() const
527 {
528 // calculated value within first function call, used cached value otherwise
529 if (!optionalPcLowSwPcValue_)
530 optionalPcLowSwPcValue_ = BaseLaw::pc(pcLowSw_, *baseLawParamsPtr_);
531 return optionalPcLowSwPcValue_.value();
532 }
533
534 // dpc_dsw for the lower saturation threshold
535 Scalar pcDerivativeLowSw_() const
536 {
537 if (!optionalPcDerivativeLowSw_)
538 optionalPcDerivativeLowSw_ = BaseLaw::dpc_dsw(pcLowSw_, *baseLawParamsPtr_);
539 return optionalPcDerivativeLowSw_.value();
540 }
541
542 // the capillary pressure for the upper saturation threshold
543 Scalar pcHighSwPcValue_() const
544 {
545 if (!optionalPcHighSwPcValue_)
546 optionalPcHighSwPcValue_ = BaseLaw::pc(pcHighSw_, *baseLawParamsPtr_);
547 return optionalPcHighSwPcValue_.value();
548 }
549
550 // dpc_dsw at Sw = 1.0
551 Scalar pcDerivativeHighSwEnd_() const
552 {
553 using std::isnan;
554 if (!isnan(highSwFixedSlope_))
555 return highSwFixedSlope_;
556 else
557 return (0.0 - pcHighSwPcValue_()) / (1.0 - pcHighSw_);
558 }
559
560 const Spline<Scalar>& pcSpline_() const
561 {
562 if (!optionalPcSpline_)
563 {
564 const auto slopes = highSwSplineZeroSlope_ ? std::array{0.0, 0.0}
565 : std::array{BaseLaw::dpc_dsw(pcHighSw_, *baseLawParamsPtr_), pcDerivativeHighSwEnd_()};
566
567 optionalPcSpline_ = Spline<Scalar>(pcHighSw_, 1.0, // x0, x1
568 pcHighSwPcValue_(), 0, // y0, y1
569 slopes[0], slopes[1]); // m0, m1
570 }
571 return optionalPcSpline_.value();
572 }
573
574 Scalar pcLowSw_, pcHighSw_;
575 mutable OptionalScalar<Scalar> optionalPcLowSwPcValue_, optionalPcDerivativeLowSw_;
576 mutable OptionalScalar<Scalar> optionalPcHighSwPcValue_;
577 HighSwRegularizationMethod highSwRegularizationMethod_;
578 const BaseLawParams* baseLawParamsPtr_;
579 mutable std::optional<Spline<Scalar>> optionalPcSpline_;
580 bool highSwSplineZeroSlope_;
581 Scalar highSwFixedSlope_;
582};
583
589template<Pore::Shape shape, typename Scalar = double>
594
600template<Pore::Shape shape, typename Scalar = double>
602
603} // end namespace Dumux::FluidMatrix
604
605#endif
Type traits.
Provides 3rd order polynomial splines.
A wrapper that can either contain a valid Scalar or NaN.
Base classes for standard pore-local pc-Sw curves.
This file contains functions related to calculate pore-body properties.
bool hasParamInGroup(const std::string &paramGroup, const std::string &param)
Check whether a key exists in the parameter tree with a model group prefix.
Definition: parameters.hh:177
Definition: localrulesforplatonicbody.hh:40
Shape
Collection of different pore-body shapes.
Definition: poreproperties.hh:35
constexpr Shape shape(const Scalar shapeFactor) noexcept
Returns the shape for a given shape factor.
Definition: throatproperties.hh:176
const GridGeometry & gridGeometry() const
The finite volume grid geometry.
Definition: common/fvspatialparams.hh:142
T value() const
Definition: optionalscalar.hh:48
Template which always yields a false value.
Definition: typetraits.hh:35
A tag to turn off regularization and it's overhead.
Definition: noregularization.hh:34
The parameter type.
Definition: localrulesforplatonicbody.hh:48
PlatonicBodyParams & setPoreShape(Pore::Shape s)
Definition: localrulesforplatonicbody.hh:52
PlatonicBodyParams & setSurfaceTension(Scalar st)
Definition: localrulesforplatonicbody.hh:53
PlatonicBodyParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:56
Pore::Shape poreShape() const
Definition: localrulesforplatonicbody.hh:77
bool operator==(const PlatonicBodyParams &p) const
Definition: localrulesforplatonicbody.hh:83
Scalar surfaceTension() const
Definition: localrulesforplatonicbody.hh:81
PlatonicBodyParams & setPoreInscribedRadius(Scalar r)
Definition: localrulesforplatonicbody.hh:51
Scalar poreInscribedRadius() const
Definition: localrulesforplatonicbody.hh:79
void update(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:66
Implementation of the simplified pore-local capillary pressure-saturation curve for platonic bodies (...
Definition: localrulesforplatonicbody.hh:106
static Scalar dpc_dsw(Scalar sw, const Params< Scalar > &params)
The partial derivative of the capillary pressure w.r.t. the wetting phase saturation.
Definition: localrulesforplatonicbody.hh:171
static Scalar sw(Scalar pc, const Params< Scalar > &params)
The wetting-phase saturation of a pore body.
Definition: localrulesforplatonicbody.hh:149
static Scalar pc(Scalar sw, const Params< Scalar > &params)
The capillary pressure-saturation curve.
Definition: localrulesforplatonicbody.hh:127
static Scalar dsw_dpc(Scalar pc, const Params< Scalar > &params)
The partial derivative of the wetting phase saturation w.r.t. the capillary pressure.
Definition: localrulesforplatonicbody.hh:193
static auto makeParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:111
OptionalScalar< Scalar > dsw_dpc(const Scalar pc) const
The regularized partial derivative of the saturation to the capillary pressure.
Definition: localrulesforplatonicbody.hh:419
OptionalScalar< Scalar > sw(const Scalar pc) const
The regularized saturation-capillary pressure curve.
Definition: localrulesforplatonicbody.hh:350
void init(const MaterialLaw *m, const Params< Scalar > &p, const std::string &paramGroup="")
Definition: localrulesforplatonicbody.hh:307
HighSwRegularizationMethod
The available options for regularizing the pc-SW curve at high wetting-phase saturations.
Definition: localrulesforplatonicbody.hh:248
OptionalScalar< Scalar > pc(const Scalar sw) const
Definition: localrulesforplatonicbody.hh:312
void updateParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:453
OptionalScalar< Scalar > dpc_dsw(const Scalar sw) const
The regularized partial derivative of the capillary pressure w.r.t. the saturation.
Definition: localrulesforplatonicbody.hh:389
void setpcHighSw(S pcHighSw)
Set the threshold saturation above which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:275
void setpcLowSw(S pcLowSw)
Set the threshold saturation below which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:263
S pcLowSw() const
Threshold saturation below which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:269
S pcHighSw() const
Threshold saturation above which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:284
void setHighSwRegularizationMethod(HighSwRegularizationMethod method)
Set the regularization method for high saturations.
Definition: localrulesforplatonicbody.hh:290
HighSwRegularizationMethod highSwRegularizationMethod() const
Return the regularization method for high saturations.
Definition: localrulesforplatonicbody.hh:296
Base class for all standard pore-local pc-Sw curves.
Definition: singleshapelocalrules.hh:44
The base class for spatial parameters for pore-network models.
Definition: porenetwork/common/spatialparams.hh:45
Scalar poreInscribedRadius(const Element &element, const SubControlVolume &scv, const ElementSolutionVector &elemSol) const
Inscribed radius of the pore body . Can be solution-dependent.
Definition: porenetwork/common/spatialparams.hh:115