12#ifndef DUMUX_IO_PARAMETERS_FOR_GENERATED_GRID
13#define DUMUX_IO_PARAMETERS_FOR_GENERATED_GRID
22#include <dune/common/exceptions.hh>
23#include <dune/grid/common/exceptions.hh>
24#include <dune/geometry/axisalignedcubegeometry.hh>
39template <
class Gr
id,
class Scalar>
42 using GridView =
typename Grid::LeafGridView;
43 using Element =
typename Grid::template Codim<0>::Entity;
44 using GlobalPosition =
typename Element::Geometry::GlobalCoordinate;
45 using Vertex =
typename Grid::template Codim<Grid::dimension>::Entity;
47 static constexpr auto dim = Grid::dimension;
48 static constexpr auto dimWorld = Grid::dimensionworld;
49 using BoundaryList = std::array<int, 2*dimWorld>;
55 , paramGroup_(paramGroup)
56 , priorityList_(getPriorityList_())
58 computeBoundingBox_();
59 boundaryFaceIndex_ = getBoundaryFacemarkerInput_();
71 for (
auto boundaryIdx : priorityList_)
73 if (onBoundary_(pos, boundaryIdx))
74 return boundaryFaceIndex_[boundaryIdx];
86 if (poreLabels[0] == poreLabels[1])
88 if (poreLabels[0] == -1)
90 if (poreLabels[1] == -1)
94 for(
const auto i : priorityList_)
96 if (poreLabels[0] == boundaryFaceIndex_[i])
98 if (poreLabels[1] == boundaryFaceIndex_[i])
102 DUNE_THROW(Dune::InvalidStateException,
"Something went wrong with the throat labels");
108 template <
class SetParameter,
class GetParameter>
110 const GetParameter& getParameter,
111 const std::size_t numSubregions)
113 using cytpe =
typename GlobalPosition::value_type;
114 using InternalBoundingBox = Dune::AxisAlignedCubeGeometry<cytpe, dimWorld, dimWorld>;
115 std::vector<InternalBoundingBox> internalBoundingBoxes;
118 if (numSubregions > 0)
121 for (
int i = 0; i < numSubregions; ++i)
123 auto lowerLeft = getParamFromGroup<GlobalPosition>(paramGroup_,
"Grid.Subregion" + std::to_string(i) +
".LowerLeft");
124 auto upperRight = getParamFromGroup<GlobalPosition>(paramGroup_,
"Grid.Subregion" + std::to_string(i) +
".UpperRight");
125 internalBoundingBoxes.emplace_back(std::move(lowerLeft), std::move(upperRight));
131 const std::vector<Scalar> maxPoreRadius = getMaxPoreRadii_(numSubregions, getParameter);
132 std::vector<bool> poreRadiusLimited(gridView_.size(dim),
false);
135 auto defaultPoreRadius = poreRadiusGenerator_(-1);
138 std::vector<
decltype(defaultPoreRadius)> subregionPoreRadius;
139 for (
int i = 0; i < numSubregions; ++i)
140 subregionPoreRadius.emplace_back(poreRadiusGenerator_(i));
143 const auto poreVolume = poreVolumeGenerator_(getParameter);
146 for (
const auto& vertex : vertices(gridView_))
148 const auto& pos = vertex.geometry().center();
149 const auto vIdxGlobal = gridView_.indexSet().index(vertex);
151 setParameter(vertex,
"PoreLabel", poreLabel);
155 auto setRadiusAndLogIfCapped = [&](
const Scalar value)
157 if (value > maxPoreRadius[vIdxGlobal])
159 poreRadiusLimited[vIdxGlobal] =
true;
160 setParameter(vertex,
"PoreInscribedRadius", maxPoreRadius[vIdxGlobal]);
163 setParameter(vertex,
"PoreInscribedRadius", value);
166 if (numSubregions == 0)
167 setRadiusAndLogIfCapped(defaultPoreRadius(vertex, poreLabel));
171 setParameter(vertex,
"PoreRegionId", -1);
172 setRadiusAndLogIfCapped(defaultPoreRadius(vertex, poreLabel));
174 for (
int id = 0;
id < numSubregions; ++id)
176 const auto& subregion = internalBoundingBoxes[id];
179 setParameter(vertex,
"PoreRegionId",
id);
180 setRadiusAndLogIfCapped(subregionPoreRadius[
id](vertex, poreLabel));
185 setParameter(vertex,
"PoreVolume", poreVolume(vertex, poreLabel));
189 auto defaultThroatInscribedRadius = throatInscribedRadiusGenerator_(-1, getParameter);
190 auto defaultThroatLength = throatLengthGenerator_(-1, getParameter);
193 std::vector<
decltype(defaultThroatInscribedRadius)> subregionThroatInscribedRadius;
194 std::vector<
decltype(defaultThroatLength)> subregionThroatLength;
195 for (
int i = 0; i < numSubregions; ++i)
197 subregionThroatInscribedRadius.emplace_back(throatInscribedRadiusGenerator_(i, getParameter));
198 subregionThroatLength.emplace_back(throatLengthGenerator_(i, getParameter));
202 for (
const auto& element : elements(gridView_))
204 if (numSubregions == 0)
206 setParameter(element,
"ThroatInscribedRadius", defaultThroatInscribedRadius(element));
207 setParameter(element,
"ThroatLength", defaultThroatLength(element));
212 setParameter(element,
"ThroatRegionId", -1);
213 setParameter(element,
"ThroatInscribedRadius", defaultThroatInscribedRadius(element));
214 setParameter(element,
"ThroatLength", defaultThroatLength(element));
216 for (
int id = 0;
id < numSubregions; ++id)
218 const auto& subregion = internalBoundingBoxes[id];
221 setParameter(element,
"ThroatRegionId",
id);
222 setParameter(element,
"ThroatInscribedRadius", subregionThroatInscribedRadius[
id](element));
223 setParameter(element,
"ThroatLength", subregionThroatLength[
id](element));
229 const auto vertex0 = element.template subEntity<dim>(0);
230 const auto vertex1 = element.template subEntity<dim>(1);
231 const std::array poreLabels{
static_cast<int>(getParameter(vertex0,
"PoreLabel")),
232 static_cast<int>(getParameter(vertex1,
"PoreLabel"))};
233 setParameter(element,
"ThroatLabel",
throatLabel(poreLabels));
236 const auto numPoreRadiusLimited = std::count(poreRadiusLimited.begin(), poreRadiusLimited.end(),
true);
237 if (numPoreRadiusLimited > 0)
238 std::cout <<
"*******\nWarning! " << numPoreRadiusLimited <<
" out of " << poreRadiusLimited.size()
239 <<
" pore body radii have been capped automatically in order to prevent intersecting pores\n*******" << std::endl;
250 BoundaryList getPriorityList_()
const
252 const auto list = [&]()
254 BoundaryList priorityList;
255 std::iota(priorityList.begin(), priorityList.end(), 0);
261 priorityList = getParamFromGroup<BoundaryList>(paramGroup_,
"Grid.PriorityList");
264 catch(Dune::RangeError& e) {
265 DUNE_THROW(
Dumux::ParameterException,
"You must specify priorities for all directions (" << dimWorld <<
") \n" << e.what());
268 if (!isUnique_(priorityList))
272 if (std::any_of(priorityList.begin(), priorityList.end(), [](
const int i ){ return (i < 0 || i >= 2*dimWorld); }))
280 void computeBoundingBox_()
283 for (
const auto& vertex : vertices(gridView_))
285 for (
int i = 0; i < dimWorld; i++)
289 bBoxMin_[i] = min(bBoxMin_[i],
vertex.geometry().corner(0)[i]);
290 bBoxMax_[i] = max(bBoxMax_[i],
vertex.geometry().corner(0)[i]);
298 BoundaryList getBoundaryFacemarkerInput_()
const
300 BoundaryList boundaryFaceMarker;
301 std::fill(boundaryFaceMarker.begin(), boundaryFaceMarker.end(), 0);
302 boundaryFaceMarker[0] = 1;
303 boundaryFaceMarker[1] = 1;
307 const auto input = getParamFromGroup<std::vector<std::string>>(paramGroup_,
"Grid.BoundaryPoreLabels");
308 for (
const auto& entry : input)
310 const std::string errorMessage =
"You must specify BoundaryPoreLabels in the format pos:num, where pos can be xMin, xMax, yMin, yMax, zMin, zMax and num is the corresponding label.\n"
311 "Example (2D, defaults are used for the remaining boundaries): xMin:2 yMax:3\n";
312 if (entry.find(
':') == std::string::npos)
315 static const std::map<std::string, int> labels = {{
"xMin", 0}, {
"xMax", 1}, {
"yMin", 2}, {
"yMax", 3}, {
"zMin", 4}, {
"zMax", 5}};
316 const auto splitEntry =
split(entry,
":");
317 const std::string location = std::string(splitEntry[0].begin(), splitEntry[0].end());
320 value = std::stoi(std::string(splitEntry[1].begin(), splitEntry[1].end()));
326 if (splitEntry.size() != 2)
328 if (!labels.count(location))
331 boundaryFaceMarker[labels.at(location)] = value;
334 return boundaryFaceMarker;
338 template <
class GetParameter>
339 std::vector<Scalar> getMaxPoreRadii_(std::size_t numSubregions,
const GetParameter& getParameter)
const
341 const auto numVertices = gridView_.size(dim);
342 std::vector<Scalar> maxPoreRadius(numVertices, std::numeric_limits<Scalar>::max());
344 if (!getParamFromGroup<bool>(paramGroup_,
"Grid.CapPoreRadii",
true))
345 return maxPoreRadius;
348 const Scalar inputThroatLength = getParamFromGroup<Scalar>(paramGroup_,
"Grid.ThroatLength", -1.0);
349 std::vector<Scalar> subregionInputThroatLengths;
351 if (numSubregions > 0)
353 for (
int i = 0; i < numSubregions; ++i)
356 const std::string paramGroup = paramGroup_ +
".SubRegion" + std::to_string(i);
357 const Scalar subregionInputThroatLength = getParamFromGroup<Scalar>(paramGroup,
"Grid.ThroatLength", -1.0);
358 subregionInputThroatLengths.push_back(subregionInputThroatLength);
362 for (
const auto& element : elements(gridView_))
365 if (numSubregions > 0)
368 const auto subregionId = getParameter(element,
"ThroatRegionId");
369 if (subregionId >= 0)
372 if (subregionInputThroatLengths[subregionId] > 0.0)
375 else if (inputThroatLength > 0.0)
378 else if (inputThroatLength > 0.0)
383 const Scalar delta =
element.geometry().volume();
384 static const Scalar minThroatLength = getParamFromGroup<Scalar>(paramGroup_,
"Grid.MinThroatLength", 1e-6);
385 const Scalar maxRadius = (delta - minThroatLength)/2.0;
386 for (
int vIdxLocal = 0; vIdxLocal < 2 ; ++vIdxLocal)
388 const int vIdxGlobal = gridView_.indexSet().subIndex(element, vIdxLocal, dim);
389 maxPoreRadius[vIdxGlobal] = std::min(maxPoreRadius[vIdxGlobal], maxRadius);
393 return maxPoreRadius;
397 std::function<Scalar(
const Vertex&,
const int)> poreRadiusGenerator_(
const int subregionId)
const
400 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
403 std::mt19937 generator;
406 const auto poreLabelsToSetFixedRadius = getParamFromGroup<std::vector<int>>(paramGroup_, prefix +
"PoreLabelsToSetFixedRadius", std::vector<int>{});
407 const auto poreLabelsToApplyFactorForRadius = getParamFromGroup<std::vector<int>>(paramGroup_, prefix +
"PoreLabelsToApplyFactorForRadius", std::vector<int>{});
408 const auto poreRadiusForLabel = getParamFromGroup<std::vector<Scalar>>(paramGroup_, prefix +
"FixedPoreRadiusForLabel", std::vector<Scalar>{});
409 const auto poreRadiusFactorForLabel = getParamFromGroup<std::vector<Scalar>>(paramGroup_, prefix +
"PoreRadiusFactorForLabel", std::vector<Scalar>{});
411 const auto generateFunction = [&](
auto& poreRadiusDist)
413 return [=](
const auto&
vertex,
const int poreLabel)
mutable
415 const auto radius = poreRadiusDist(generator);
418 if (poreLabelsToSetFixedRadius.empty() && poreLabelsToApplyFactorForRadius.empty())
422 else if (!poreLabelsToSetFixedRadius.empty() || !poreRadiusForLabel.empty())
424 if (poreLabelsToSetFixedRadius.size() != poreRadiusForLabel.size())
427 if (
const auto it = std::find(poreLabelsToSetFixedRadius.begin(), poreLabelsToSetFixedRadius.end(), poreLabel); it != poreLabelsToSetFixedRadius.end())
428 return poreRadiusForLabel[
std::distance(poreLabelsToSetFixedRadius.begin(), it)];
432 else if (!poreLabelsToApplyFactorForRadius.empty() || !poreRadiusFactorForLabel.empty())
434 if (poreLabelsToApplyFactorForRadius.size() != poreRadiusFactorForLabel.size())
435 DUNE_THROW(
Dumux::ParameterException,
"PoreLabelsToApplyFactorForRadius must be of same size as PoreRadiusFactorForLabel");
437 if (
const auto it = std::find(poreLabelsToApplyFactorForRadius.begin(), poreLabelsToApplyFactorForRadius.end(), poreLabel); it != poreLabelsToApplyFactorForRadius.end())
438 return poreRadiusFactorForLabel[
std::distance(poreLabelsToApplyFactorForRadius.begin(), it)] * radius;
446 const Scalar fixedPoreRadius = getParamFromGroup<Scalar>(paramGroup_, prefix +
"PoreInscribedRadius", -1.0);
448 if (fixedPoreRadius <= 0.0)
451 const auto seed = getParamFromGroup<unsigned int>(paramGroup_, prefix +
"ParameterRandomNumberSeed", std::random_device{}());
452 generator.seed(seed);
454 const auto type = getParamFromGroup<std::string>(paramGroup_, prefix +
"ParameterType",
"lognormal");
455 if (type ==
"lognormal")
458 const auto [meanPoreRadius, stddevPoreRadius] = getDistributionInputParams_(
"Lognormal", prefix,
459 "MeanPoreInscribedRadius",
460 "StandardDeviationPoreInscribedRadius");
461 const Scalar variance = stddevPoreRadius*stddevPoreRadius;
465 const Scalar mu = log(meanPoreRadius/sqrt(1.0 + variance/(meanPoreRadius*meanPoreRadius)));
466 const Scalar sigma = sqrt(log(1.0 + variance/(meanPoreRadius*meanPoreRadius)));
469 return generateFunction(poreRadiusDist);
471 else if (type ==
"uniform")
474 const auto [minPoreRadius, maxPoreRadius] = getDistributionInputParams_(
"Uniform", prefix,
475 "MinPoreInscribedRadius",
476 "MaxPoreInscribedRadius");
478 return generateFunction(poreRadiusDist);
481 DUNE_THROW(Dune::InvalidStateException,
"Unknown parameter type " << type);
487 auto poreRadiusDist = [fixedPoreRadius](
auto& gen){
return fixedPoreRadius; };
488 return generateFunction(poreRadiusDist);
493 std::array<Scalar, 2> getDistributionInputParams_(
const std::string& distributionName,
494 const std::string& prefix,
495 const std::string& paramName0,
496 const std::string& paramName1)
const
500 return std::array{getParamFromGroup<Scalar>(paramGroup_, prefix + paramName0),
501 getParamFromGroup<Scalar>(paramGroup_, prefix + paramName1)};
505 std::cout <<
"\n" << distributionName <<
" pore-size distribution needs input parameters "
506 << prefix + paramName0 <<
" and " << prefix + paramName1 <<
".\n"
507 <<
"Alternatively, use " << prefix <<
"PoreInscribedRadius to set a fixed inscribed pore radius." << std::endl;
512 template <
class GetParameter>
513 auto poreVolumeGenerator_(
const GetParameter& getParameter)
const
515 const auto geometry =
Pore::shapeFromString(getParamFromGroup<std::string>(paramGroup_,
"Grid.PoreGeometry"));
516 const auto capPoresOnBoundaries = getParamFromGroup<std::vector<int>>(paramGroup_,
"Grid.CapPoresOnBoundaries", std::vector<int>{});
517 if (!isUnique_(capPoresOnBoundaries))
518 DUNE_THROW(Dune::InvalidStateException,
"CapPoresOnBoundaries must not contain duplicates");
521 return [=] (
const auto&
vertex,
const auto vIdx)
523 const Scalar r = getParameter(vertex,
"PoreInscribedRadius");
528 static const Scalar fixedHeight = getParamFromGroup<Scalar>(paramGroup_,
"Grid.PoreHeight", -1.0);
529 const Scalar h = fixedHeight > 0.0 ? fixedHeight : getParameter(vertex,
"PoreHeight");
536 if (capPoresOnBoundaries.empty())
540 std::size_t numCaps = 0;
542 const auto& pos =
vertex.geometry().center();
543 for (
auto boundaryIdx : capPoresOnBoundaries)
545 if (onBoundary_(pos, boundaryIdx))
552 if (numCaps > dimWorld)
553 DUNE_THROW(Dune::InvalidStateException,
"Pore " << vIdx <<
" at " << pos <<
" capped " << numCaps <<
" times. Capping should not happen more than " << dimWorld <<
" times");
561 template <
class GetParameter>
562 auto throatInscribedRadiusGenerator_(
const int subregionId,
const GetParameter& getParameter)
const
565 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
568 const Scalar inputThroatInscribedRadius = getParamFromGroup<Scalar>(paramGroup_, prefix +
"ThroatInscribedRadius", -1.0);
571 const Scalar throatN = getParamFromGroup<Scalar>(paramGroup_, prefix +
"ThroatInscribedRadiusN", 0.1);
573 return [=](
const Element&
element)
575 const Scalar delta =
element.geometry().volume();
576 const std::array<Vertex, 2> vertices = {
element.template subEntity<dim>(0),
element.template subEntity<dim>(1)};
579 if (inputThroatInscribedRadius > 0.0)
580 return inputThroatInscribedRadius;
583 const Scalar poreRadius0 = getParameter(vertices[0],
"PoreInscribedRadius");
584 const Scalar poreRadius1 = getParameter(vertices[1],
"PoreInscribedRadius");
591 template <
class GetParameter>
592 auto throatLengthGenerator_(
int subregionId,
const GetParameter& getParameter)
const
595 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
596 const Scalar inputThroatLength = getParamFromGroup<Scalar>(paramGroup_, prefix +
"ThroatLength", -1.0);
598 const bool subtractRadiiFromThroatLength = getParamFromGroup<bool>(paramGroup_, prefix +
"SubtractPoreInscribedRadiiFromThroatLength",
true);
600 return [=](
const Element&
element)
602 if (inputThroatLength > 0.0)
603 return inputThroatLength;
605 const Scalar delta =
element.geometry().volume();
606 if (subtractRadiiFromThroatLength)
608 const std::array<Vertex, 2> vertices = {
element.template subEntity<dim>(0),
element.template subEntity<dim>(1)};
609 const Scalar result = delta - getParameter(vertices[0],
"PoreInscribedRadius") - getParameter(vertices[1],
"PoreInscribedRadius");
611 DUNE_THROW(Dune::GridError,
"Pore radii are so large they intersect! Something went wrong at element " << gridView_.indexSet().index(element));
620 bool onBoundary_(
const GlobalPosition& pos, std::size_t boundaryIdx)
const
622 constexpr auto eps = 1e-8;
624 static constexpr std::array boundaryIdxToCoordinateIdx{0, 0, 1, 1, 2, 2};
625 const auto coordinateIdx = boundaryIdxToCoordinateIdx[boundaryIdx];
626 auto isMaxBoundary = [] (
int n) {
return (n % 2 != 0); };
628 if (isMaxBoundary(boundaryIdx))
629 return pos[coordinateIdx] > bBoxMax_[coordinateIdx] - eps;
631 return pos[coordinateIdx] < bBoxMin_[coordinateIdx] + eps;
637 bool isUnique_(T t)
const
639 std::sort(t.begin(), t.end());
640 return (std::unique(t.begin(), t.end()) == t.end());
643 const GridView gridView_;
645 const std::string paramGroup_;
646 BoundaryList priorityList_;
647 BoundaryList boundaryFaceIndex_;
650 GlobalPosition bBoxMin_ = GlobalPosition(std::numeric_limits<double>::max());
651 GlobalPosition bBoxMax_ = GlobalPosition(std::numeric_limits<double>::min());
Exception thrown if a run-time parameter is not specified correctly.
Definition: exceptions.hh:48
Helper class to assign parameters to a generated grid.
Definition: parametersforgeneratedgrid.hh:41
int throatLabel(const std::array< int, 2 > &poreLabels) const
Computes and returns the label of a given throat.
Definition: parametersforgeneratedgrid.hh:84
ParametersForGeneratedGrid(const GridView &gridView, const std::string ¶mGroup)
Definition: parametersforgeneratedgrid.hh:53
int boundaryFaceMarkerAtPos(const GlobalPosition &pos) const
Returns the boundary face marker index at given position.
Definition: parametersforgeneratedgrid.hh:67
void assignParameters(const SetParameter &setParameter, const GetParameter &getParameter, const std::size_t numSubregions)
Assign parameters for generically created grids.
Definition: parametersforgeneratedgrid.hh:109
A simple log-normal distribution.
Definition: random.hh:183
bool intersectsPointGeometry(const Dune::FieldVector< ctype, dimworld > &point, const Geometry &g)
Find out whether a point is inside a three-dimensional geometry.
Definition: intersectspointgeometry.hh:28
static ctype distance(const Dune::FieldVector< ctype, dimWorld > &a, const Dune::FieldVector< ctype, dimWorld > &b)
Compute the shortest distance between two points.
Definition: distance.hh:282
auto volume(const Geometry &geo, unsigned int integrationOrder=4)
The volume of a given geometry.
Definition: volume.hh:159
bool hasParamInGroup(const std::string ¶mGroup, const std::string ¶m)
Check whether a key exists in the parameter tree with a model group prefix.
Definition: parameters.hh:165
Detect if a point intersects a geometry.
Shape shapeFromString(const std::string &s)
Get the shape from a string description of the shape.
Definition: poreproperties.hh:44
Scalar volume(Shape shape, Scalar inscribedRadius)
Returns the volume of a given geometry based on the inscribed radius.
Definition: poreproperties.hh:61
Scalar averagedRadius(const Scalar poreRadiusOne, const Scalar poreRadiusTwo, const Scalar centerTocenterDist, const Scalar n=0.1)
Returns the radius of a pore throat.
Definition: throatproperties.hh:68
Definition: discretization/porenetwork/fvelementgeometry.hh:24
std::vector< std::string_view > split(std::string_view str, std::string_view delim, bool removeEmpty=false)
Definition: stringutilities.hh:68
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
This file contains functions related to calculate pore-body properties.
Some tools for random number generation.
Helpers for working with strings.
This file contains functions related to calculate pore-throat properties.