version 3.9
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// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
14#ifndef DUMUX_PNM_2P_LOCAL_RULES_FOR_PLATONIC_BODY_HH
15#define DUMUX_PNM_2P_LOCAL_RULES_FOR_PLATONIC_BODY_HH
16
17#include <algorithm>
18#include <cassert>
19#include <cmath>
20#include <optional>
21#include <dune/common/math.hh>
27
29
34template<class Scalar>
36{
37 PlatonicBodyParams() = default;
38
39 PlatonicBodyParams& setPoreInscribedRadius(Scalar r) { radius_ = r; return *this;}
40 PlatonicBodyParams& setPoreShape(Pore::Shape s) { shape_ = s; return *this;}
41 PlatonicBodyParams& setSurfaceTension(Scalar st) { surfaceTension_ = st; return *this;}
42
43 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
44 PlatonicBodyParams(const SpatialParams& spatialParams,
45 const Element& element,
46 const SubControlVolume& scv,
47 const ElemSol& elemSol)
48 : shape_(spatialParams.gridGeometry().poreGeometry()[scv.dofIndex()])
49 , radius_(spatialParams.poreInscribedRadius(element, scv, elemSol))
50 , surfaceTension_(spatialParams.surfaceTension(element, scv, elemSol))
51 {}
52
53 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
54 void update(const SpatialParams& spatialParams,
55 const Element& element,
56 const SubControlVolume& scv,
57 const ElemSol& elemSol)
58 {
59 const auto& gridGeometry = spatialParams.gridGeometry();
60 shape_ = gridGeometry.poreGeometry()[scv.dofIndex()];
61 radius_ = spatialParams.poreInscribedRadius(element, scv, elemSol);
62 surfaceTension_ = spatialParams.surfaceTension(element, scv, elemSol);
63 }
64
65 Pore::Shape poreShape() const { return shape_; }
66
67 Scalar poreInscribedRadius() const { return radius_; }
68
69 Scalar surfaceTension() const { return surfaceTension_; }
70
71 bool operator== (const PlatonicBodyParams& p) const
72 {
73 return Dune::FloatCmp::eq(radius_, p.radius_, 1e-6)
74 && Dune::FloatCmp::eq(surfaceTension_, p.surfaceTension_, 1e-6)
75 && shape_ == p.shape_;
76 }
77
78private:
79 Pore::Shape shape_;
80 Scalar radius_;
81 Scalar surfaceTension_;
82};
83
92template<Pore::Shape shape>
94{
95 template<class Scalar>
97
98 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
99 static auto makeParams(const SpatialParams& spatialParams,
100 const Element& element,
101 const SubControlVolume& scv,
102 const ElemSol& elemSol)
103 {
104 using Scalar = std::decay_t<decltype(spatialParams.poreInscribedRadius(element, scv, elemSol))>;
105 return Params<Scalar>(spatialParams, element, scv, elemSol);
106 }
107
114 template<class Scalar>
115 static Scalar pc(Scalar sw, const Params<Scalar>& params)
116 {
117 assert(isPlatonicBody_(params.poreShape()));
118
119 using std::clamp;
120 sw = clamp(sw, 0.0, 1.0); // the equation below is only defined for 0.0 <= sw <= 1.0
121
122 const Scalar poreRadius = params.poreInscribedRadius();
123 const Scalar sigma = params.surfaceTension();
124 // TODO incorporate contact angle!!!
125
126 using std::exp;
127 return 2.0*sigma / (poreRadius*(1.0 - exp(-expFactor_<Scalar>()*sw)));
128 }
129
136 template<class Scalar>
137 static Scalar sw(Scalar pc, const Params<Scalar>& params)
138 {
139 assert(isPlatonicBody_(params.poreShape()));
140
141 using std::max;
142 pc = max(pc, 0.0); // the equation below is undefined for negative pcs
143
144 const Scalar poreRadius = params.poreInscribedRadius();
145 const Scalar sigma = params.surfaceTension();
146
147 using std::log;
148 return -1.0/expFactor_<Scalar>()* log(1.0 - 2.0*sigma/(poreRadius*pc));
149 }
150
158 template<class Scalar>
159 static Scalar dpc_dsw(Scalar sw, const Params<Scalar>& params)
160 {
161 assert(isPlatonicBody_(params.poreShape()));
162
163 using std::clamp;
164 sw = clamp(sw, 0.0, 1.0); // the equation below is only defined for 0.0 <= sw <= 1.0
165
166 const Scalar sigma = params.surfaceTension();
167 const Scalar poreRadius = params.poreInscribedRadius();
168 using std::exp;
169 const Scalar e = exp(expFactor_<Scalar>()*sw);
170 return -(2.0*expFactor_<Scalar>()*sigma*e) / (poreRadius*(1.0-e)*(1.0-e));
171 }
172
180 template<class Scalar>
181 static Scalar dsw_dpc(Scalar pc, const Params<Scalar>& params)
182 {
183 assert(isPlatonicBody_(params.poreShape()));
184
185 using std::max;
186 pc = max(pc, 0.0); // the equation below is undefined for negative pcs
187
188 const Scalar sigma = params.surfaceTension();
189 const Scalar poreRadius = params.poreInscribedRadius();
190 return sigma / (expFactor_<Scalar>()*sigma*pc - 0.5*expFactor_<Scalar>()*poreRadius*pc*pc);
191 }
192
193private:
194
195 template<class Scalar>
196 static constexpr Scalar expFactor_()
197 {
198 if constexpr (shape == Pore::Shape::tetrahedron)
199 return 3.87;
200 else if constexpr (shape == Pore::Shape::cube)
201 return 6.83;
202 else if constexpr (shape == Pore::Shape::octahedron)
203 return 8.71;
204 else if constexpr (shape == Pore::Shape::dodecahedron)
205 return 22.87;
206 else if constexpr (shape == Pore::Shape::icosahedron)
207 return 24.11;
208 else
209 {
210 static_assert(AlwaysFalse<Scalar>::value, "Shape not supported");
211 return 0;
212 }
213 }
214
215 static constexpr bool isPlatonicBody_(Pore::Shape s)
216 {
217 return s == Pore::Shape::tetrahedron ||
218 s == Pore::Shape::cube ||
222 }
223};
224
230template<class Scalar, class BaseLaw>
232{
234 using BaseLawParams = typename BaseLaw::template Params<Scalar>;
235public:
236
241 {
242 linear, spline, powerLaw
243 };
244
245 template<class S>
246 struct Params
247 {
248 // export method type
257 { pcLowSw_ = pcLowSw; }
258
262 S pcLowSw() const
263 { return pcLowSw_; }
264
269 { pcHighSw_ = pcHighSw; }
270
277 S pcHighSw() const
278 { return pcHighSw_; }
279
284 { highSwRegularizationMethod_ = method; }
285
290 { return highSwRegularizationMethod_; }
291
292private:
293 S pcLowSw_ = 0.01;
294 S pcHighSw_ = 0.99;
296 };
297
298
299 template<class MaterialLaw>
300 void init(const MaterialLaw* m, const Params<Scalar>& p, const std::string& paramGroup = "")
301 {
302 initPcParameters_(m, p, paramGroup);
303 }
304
305 OptionalScalar<Scalar> pc(const Scalar sw) const
306 {
307 // make sure that the capilary pressure observes a
308 // derivative != 0 for 'illegal' saturations. This is
309 // required for example by newton solvers (if the
310 // derivative is calculated numerically) in order to get the
311 // saturation moving to the right direction if it
312 // temporarily is in an 'illegal' range.
313 if (sw < pcLowSw_)
314 return pcLowSwPcValue_() + pcDerivativeLowSw_() * (sw - pcLowSw_);
315
316 if (sw <= pcHighSw_)
317 return {}; // standard
318 else if (sw < 1.0) // regularized part below sw = 1.0
319 {
320 using std::pow;
321 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
322 return pcHighSwPcValue_() * pow(((1.0-sw)/(1.0-pcHighSw_)), 1.0/3.0);
323
324 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
325 {
326 const Scalar slope = -pcHighSwPcValue_() / (1.0 - pcHighSw_);
327 return pcHighSwPcValue_() + (sw - pcHighSw_) * slope;
328 }
329
330 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
331 return pcSpline_().eval(sw);
332
333 else
334 DUNE_THROW(Dune::NotImplemented, "Regularization not method not implemented");
335 }
336 else // regularized part above sw = 1.0
337 return pcDerivativeHighSwEnd_()*(sw - 1.0);
338 }
339
343 OptionalScalar<Scalar> sw(const Scalar pc) const
344 {
345 if (pc <= 0.0)
346 {
347 if (pcHighSw_ >= 1.0)
348 return 1.0; // no regularization for high sw
349 else
350 return pc/pcDerivativeHighSwEnd_() + 1.0;
351 }
352
353 // low saturation
354 if (pc > pcLowSwPcValue_())
355 return (pc - pcLowSwPcValue_())/pcDerivativeLowSw_() + pcLowSw_;
356
357 // high saturation
358 else if (pc <= pcHighSwPcValue_())
359 {
360 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
361 {
362 // invert power law
363 using Dune::power;
364 return power(pc/pcHighSwPcValue_(), 3) * (pcHighSw_ - 1.0) + 1.0;
365 }
366
367 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
368 return pc/pcDerivativeHighSwEnd_() + 1.0;
369
370 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
371 // invert spline
372 return pcSpline_().intersectInterval(pcHighSw_, 1.0, 0.0, 0.0, 0.0, pc);
373 }
374
375 // no regularization
376 return {};
377 }
378
383 {
384 if (sw <= pcLowSw_)
385 return pcDerivativeLowSw_();
386
387 else if (sw >= 1.0)
388 return pcDerivativeHighSwEnd_();
389
390 else if (sw > pcHighSw_)
391 {
392 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
393 {
394 using std::pow;
395 return pcHighSwPcValue_()/3.0 * 1.0 /((pcHighSw_-1.0) * pow((sw-1.0)/(pcHighSw_-1.0), 2.0/3.0));
396 }
397
398 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
399 return pcDerivativeHighSwEnd_();
400
401 else
402 return pcSpline_().evalDerivative(sw);
403 }
404
405 else
406 return {}; // no regularization
407 }
408
413 {
414 if (pc <= 0.0)
415 {
416 if (pcHighSw_ >= 1.0)
417 return 0.0;
418 else
419 return 1.0/pcDerivativeHighSwEnd_();
420 }
421
422 // derivative of the inverse of the function is one over derivative of the function
423 else if (pc <= pcHighSwPcValue_())
424 {
425 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
426 {
427 using Dune::power;
428 return (3.0*pcHighSw_ - 3.0) * power(pc, 2) / power(pcHighSwPcValue_(), 3);
429 }
430
431 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
432 return 1.0/pcDerivativeHighSwEnd_();
433
434 else
435 return 1.0/pcSpline_().evalDerivative(pcSpline_().intersectInterval(pcHighSw_, 1.0, 0.0, 0.0, 0.0, pc));
436 }
437
438 else if (pc >= pcLowSwPcValue_())
439 return 1.0/pcDerivativeLowSw_();
440
441 else
442 return {}; // no regularization
443 }
444
445 template<class SpatialParams, class Element, class SubControlVolume, class ElemSol>
446 void updateParams(const SpatialParams& spatialParams,
447 const Element& element,
448 const SubControlVolume& scv,
449 const ElemSol& elemSol)
450 {}
451
452private:
453
454 template<class MaterialLaw>
455 void initPcParameters_(const MaterialLaw* m, const Params<Scalar>& params, const std::string& paramGroup)
456 {
457 highSwRegularizationMethod_ = params.highSwRegularizationMethod();
458
459 // maybe overwrite method using input
460 static const bool hasMethod = hasParamInGroup(paramGroup, "HighSwRegularizationMethod");
461 if (hasMethod)
462 {
463 // set method only once
464 static const HighSwRegularizationMethod inputmethod = [&]
465 {
466 static const auto input = getParamFromGroup<std::string>(paramGroup, "HighSwRegularizationMethod");
467 if (input == "Linear")
469 else if (input == "Spline")
471 else if (input == "PowerLaw")
473 else
474 DUNE_THROW(Dune::InvalidStateException, input << " is not a valid regularization method");
475 }();
476
477 highSwRegularizationMethod_ = inputmethod;
478 }
479
480 static const bool splineZeroSlope = getParamFromGroup<bool>(paramGroup, "HighSwSplineZeroSlope", true);
481 highSwSplineZeroSlope_ = splineZeroSlope;
482
483 // print name only once
484 [[maybe_unused]] static const bool printName = [&]
485 {
486 std::string name;
487 if (highSwRegularizationMethod_ == HighSwRegularizationMethod::linear)
488 name = "Linear";
489 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::powerLaw)
490 name = "PowerLaw";
491 else if (highSwRegularizationMethod_ == HighSwRegularizationMethod::spline)
492 name = "Spline";
493 else
494 DUNE_THROW(Dune::NotImplemented, "Regularization not method not implemented");
495
496 std::cout << "\n*****\nUsing " << name << " as regularization method for high Sw\n*****" << std::endl;
497 return true;
498 }();
499
500 using std::isnan;
501 static const auto pcLowSwInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationLowSw", params.pcLowSw());
502 pcLowSw_ = !isnan(pcLowSwInput) ? pcLowSwInput : params.pcLowSw();
503
504 static const auto pcHighSwInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationHighSw", std::numeric_limits<Scalar>::quiet_NaN());
505 pcHighSw_ = !isnan(pcHighSwInput) ? pcHighSwInput : params.pcHighSw();
506
507 baseLawParamsPtr_ = &m->basicParams();
508
509 static const auto highSwFixedSlopeInput = getParamFromGroup<Scalar>(paramGroup, "RegularizationHighSwFixedSlope", std::numeric_limits<Scalar>::quiet_NaN());
510 highSwFixedSlope_ = highSwFixedSlopeInput;
511
512 // Note: we do not pre-calculate all end-point values (e.g., pcLowSwPcValue_ and pcDerivativeLowSw_)
513 // as done in, e.g., VanGenuchten. This is because this law will generally be instantiated only as
514 // a temporary object since all pore bodies generally have different parameters.
515 // We still cache above-mentioned values, but only if they are actually needed (see below).
516 }
517
518 // the capillary pressure for the lower saturation threshold
519 Scalar pcLowSwPcValue_() const
520 {
521 // calculated value within first function call, used cached value otherwise
522 if (!optionalPcLowSwPcValue_)
523 optionalPcLowSwPcValue_ = BaseLaw::pc(pcLowSw_, *baseLawParamsPtr_);
524 return optionalPcLowSwPcValue_.value();
525 }
526
527 // dpc_dsw for the lower saturation threshold
528 Scalar pcDerivativeLowSw_() const
529 {
530 if (!optionalPcDerivativeLowSw_)
531 optionalPcDerivativeLowSw_ = BaseLaw::dpc_dsw(pcLowSw_, *baseLawParamsPtr_);
532 return optionalPcDerivativeLowSw_.value();
533 }
534
535 // the capillary pressure for the upper saturation threshold
536 Scalar pcHighSwPcValue_() const
537 {
538 if (!optionalPcHighSwPcValue_)
539 optionalPcHighSwPcValue_ = BaseLaw::pc(pcHighSw_, *baseLawParamsPtr_);
540 return optionalPcHighSwPcValue_.value();
541 }
542
543 // dpc_dsw at Sw = 1.0
544 Scalar pcDerivativeHighSwEnd_() const
545 {
546 using std::isnan;
547 if (!isnan(highSwFixedSlope_))
548 return highSwFixedSlope_;
549 else
550 return (0.0 - pcHighSwPcValue_()) / (1.0 - pcHighSw_);
551 }
552
553 const Spline<Scalar>& pcSpline_() const
554 {
555 if (!optionalPcSpline_)
556 {
557 const auto slopes = highSwSplineZeroSlope_ ? std::array{0.0, 0.0}
558 : std::array{BaseLaw::dpc_dsw(pcHighSw_, *baseLawParamsPtr_), pcDerivativeHighSwEnd_()};
559
560 optionalPcSpline_ = Spline<Scalar>(pcHighSw_, 1.0, // x0, x1
561 pcHighSwPcValue_(), 0, // y0, y1
562 slopes[0], slopes[1]); // m0, m1
563 }
564 return optionalPcSpline_.value();
565 }
566
567 Scalar pcLowSw_, pcHighSw_;
568 mutable OptionalScalar<Scalar> optionalPcLowSwPcValue_, optionalPcDerivativeLowSw_;
569 mutable OptionalScalar<Scalar> optionalPcHighSwPcValue_;
570 HighSwRegularizationMethod highSwRegularizationMethod_;
571 const BaseLawParams* baseLawParamsPtr_;
572 mutable std::optional<Spline<Scalar>> optionalPcSpline_;
573 bool highSwSplineZeroSlope_;
574 Scalar highSwFixedSlope_;
575};
576
581template<Pore::Shape shape, typename Scalar = double>
586
591template<Pore::Shape shape, typename Scalar = double>
593
594} // end namespace Dumux::FluidMatrix
595
596#endif
const GridGeometry & gridGeometry() const
The finite volume grid geometry.
Definition: common/fvspatialparams.hh:130
Base class for all standard pore-local pc-Sw curves.
Definition: singleshapelocalrules.hh:32
Two-phase rules for regularizing the pc-SW for platonic bodies.
Definition: localrulesforplatonicbody.hh:232
OptionalScalar< Scalar > dsw_dpc(const Scalar pc) const
The regularized partial derivative of the saturation to the capillary pressure.
Definition: localrulesforplatonicbody.hh:412
OptionalScalar< Scalar > sw(const Scalar pc) const
The regularized saturation-capillary pressure curve.
Definition: localrulesforplatonicbody.hh:343
void init(const MaterialLaw *m, const Params< Scalar > &p, const std::string &paramGroup="")
Definition: localrulesforplatonicbody.hh:300
HighSwRegularizationMethod
The available options for regularizing the pc-SW curve at high wetting-phase saturations.
Definition: localrulesforplatonicbody.hh:241
OptionalScalar< Scalar > pc(const Scalar sw) const
Definition: localrulesforplatonicbody.hh:305
void updateParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:446
OptionalScalar< Scalar > dpc_dsw(const Scalar sw) const
The regularized partial derivative of the capillary pressure w.r.t. the saturation.
Definition: localrulesforplatonicbody.hh:382
The base class for spatial parameters for pore-network models.
Definition: porenetwork/common/spatialparams.hh:33
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:103
Type traits.
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:165
Definition: localrulesforplatonicbody.hh:28
Shape
Collection of different pore-body shapes.
Definition: poreproperties.hh:23
constexpr Shape shape(const Scalar shapeFactor) noexcept
Returns the shape for a given shape factor.
Definition: throatproperties.hh:165
A wrapper that can either contain a valid Scalar or NaN.
This file contains functions related to calculate pore-body properties.
Base classes for standard pore-local pc-Sw curves.
Provides 3rd order polynomial splines.
Template which always yields a false value.
Definition: common/typetraits/typetraits.hh:24
A tag to turn off regularization and it's overhead.
Definition: noregularization.hh:22
T value() const
Definition: optionalscalar.hh:36
The parameter type.
Definition: localrulesforplatonicbody.hh:36
PlatonicBodyParams & setPoreShape(Pore::Shape s)
Definition: localrulesforplatonicbody.hh:40
PlatonicBodyParams & setSurfaceTension(Scalar st)
Definition: localrulesforplatonicbody.hh:41
PlatonicBodyParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:44
Pore::Shape poreShape() const
Definition: localrulesforplatonicbody.hh:65
bool operator==(const PlatonicBodyParams &p) const
Definition: localrulesforplatonicbody.hh:71
Scalar surfaceTension() const
Definition: localrulesforplatonicbody.hh:69
PlatonicBodyParams & setPoreInscribedRadius(Scalar r)
Definition: localrulesforplatonicbody.hh:39
Scalar poreInscribedRadius() const
Definition: localrulesforplatonicbody.hh:67
void update(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:54
Implementation of the simplified pore-local capillary pressure-saturation curve for platonic bodies (...
Definition: localrulesforplatonicbody.hh:94
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:159
static Scalar sw(Scalar pc, const Params< Scalar > &params)
The wetting-phase saturation of a pore body.
Definition: localrulesforplatonicbody.hh:137
static Scalar pc(Scalar sw, const Params< Scalar > &params)
The capillary pressure-saturation curve.
Definition: localrulesforplatonicbody.hh:115
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:181
static auto makeParams(const SpatialParams &spatialParams, const Element &element, const SubControlVolume &scv, const ElemSol &elemSol)
Definition: localrulesforplatonicbody.hh:99
void setpcHighSw(S pcHighSw)
Set the threshold saturation above which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:268
void setpcLowSw(S pcLowSw)
Set the threshold saturation below which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:256
S pcLowSw() const
Threshold saturation below which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:262
S pcHighSw() const
Threshold saturation above which the capillary pressure is regularized.
Definition: localrulesforplatonicbody.hh:277
void setHighSwRegularizationMethod(HighSwRegularizationMethod method)
Set the regularization method for high saturations.
Definition: localrulesforplatonicbody.hh:283
HighSwRegularizationMethod highSwRegularizationMethod() const
Return the regularization method for high saturations.
Definition: localrulesforplatonicbody.hh:289