54 using GridView =
typename Grid::LeafGridView;
55 using Element =
typename Grid::template Codim<0>::Entity;
56 using GlobalPosition =
typename Element::Geometry::GlobalCoordinate;
57 using Vertex =
typename Grid::template Codim<Grid::dimension>::Entity;
59 static constexpr auto dim = Grid::dimension;
60 static constexpr auto dimWorld = Grid::dimensionworld;
61 using BoundaryList = std::array<int, 2*dimWorld>;
67 , paramGroup_(paramGroup)
68 , priorityList_(getPriorityList_())
70 computeBoundingBox_();
71 boundaryFaceIndex_ = getBoundaryFacemarkerInput_();
83 for (
auto boundaryIdx : priorityList_)
85 if (onBoundary_(pos, boundaryIdx))
86 return boundaryFaceIndex_[boundaryIdx];
98 if (poreLabels[0] == poreLabels[1])
100 if (poreLabels[0] == -1)
101 return poreLabels[1];
102 if (poreLabels[1] == -1)
103 return poreLabels[0];
106 for(
const auto i : priorityList_)
108 if (poreLabels[0] == boundaryFaceIndex_[i])
109 return poreLabels[0];
110 if (poreLabels[1] == boundaryFaceIndex_[i])
111 return poreLabels[1];
114 DUNE_THROW(Dune::InvalidStateException,
"Something went wrong with the throat labels");
120 template <
class SetParameter,
class GetParameter>
122 const GetParameter& getParameter,
123 const std::size_t numSubregions)
125 using cytpe =
typename GlobalPosition::value_type;
126 using InternalBoundingBox = Dune::AxisAlignedCubeGeometry<cytpe, dimWorld, dimWorld>;
127 std::vector<InternalBoundingBox> internalBoundingBoxes;
130 if (numSubregions > 0)
133 for (
int i = 0; i < numSubregions; ++i)
137 internalBoundingBoxes.emplace_back(std::move(lowerLeft), std::move(upperRight));
143 const std::vector<Scalar> maxPoreRadius = getMaxPoreRadii_(numSubregions, getParameter);
144 std::vector<bool> poreRadiusLimited(gridView_.size(dim),
false);
147 auto defaultPoreRadius = poreRadiusGenerator_(-1);
150 std::vector<
decltype(defaultPoreRadius)> subregionPoreRadius;
151 for (
int i = 0; i < numSubregions; ++i)
152 subregionPoreRadius.emplace_back(poreRadiusGenerator_(i));
155 const auto poreVolume = poreVolumeGenerator_(getParameter);
158 for (
const auto& vertex : vertices(gridView_))
160 const auto& pos = vertex.geometry().center();
161 const auto vIdxGlobal = gridView_.indexSet().index(vertex);
163 setParameter(vertex,
"PoreLabel", poreLabel);
167 auto setRadiusAndLogIfCapped = [&](
const Scalar value)
169 if (value > maxPoreRadius[vIdxGlobal])
171 poreRadiusLimited[vIdxGlobal] =
true;
172 setParameter(vertex,
"PoreInscribedRadius", maxPoreRadius[vIdxGlobal]);
175 setParameter(vertex,
"PoreInscribedRadius", value);
178 if (numSubregions == 0)
179 setRadiusAndLogIfCapped(defaultPoreRadius(vertex, poreLabel));
183 setParameter(vertex,
"PoreRegionId", -1);
184 setRadiusAndLogIfCapped(defaultPoreRadius(vertex, poreLabel));
186 for (
int id = 0;
id < numSubregions; ++id)
188 const auto& subregion = internalBoundingBoxes[id];
191 setParameter(vertex,
"PoreRegionId",
id);
192 setRadiusAndLogIfCapped(subregionPoreRadius[
id](vertex, poreLabel));
197 setParameter(vertex,
"PoreVolume", poreVolume(vertex, poreLabel));
201 auto defaultThroatInscribedRadius = throatInscribedRadiusGenerator_(-1, getParameter);
202 auto defaultThroatLength = throatLengthGenerator_(-1, getParameter);
205 std::vector<
decltype(defaultThroatInscribedRadius)> subregionThroatInscribedRadius;
206 std::vector<
decltype(defaultThroatLength)> subregionThroatLength;
207 for (
int i = 0; i < numSubregions; ++i)
209 subregionThroatInscribedRadius.emplace_back(throatInscribedRadiusGenerator_(i, getParameter));
210 subregionThroatLength.emplace_back(throatLengthGenerator_(i, getParameter));
214 for (
const auto& element : elements(gridView_))
216 if (numSubregions == 0)
218 setParameter(element,
"ThroatInscribedRadius", defaultThroatInscribedRadius(element));
219 setParameter(element,
"ThroatLength", defaultThroatLength(element));
224 setParameter(element,
"ThroatRegionId", -1);
225 setParameter(element,
"ThroatInscribedRadius", defaultThroatInscribedRadius(element));
226 setParameter(element,
"ThroatLength", defaultThroatLength(element));
228 for (
int id = 0;
id < numSubregions; ++id)
230 const auto& subregion = internalBoundingBoxes[id];
233 setParameter(element,
"ThroatRegionId",
id);
234 setParameter(element,
"ThroatInscribedRadius", subregionThroatInscribedRadius[
id](element));
235 setParameter(element,
"ThroatLength", subregionThroatLength[
id](element));
241 const auto vertex0 = element.template subEntity<dim>(0);
242 const auto vertex1 = element.template subEntity<dim>(1);
243 const std::array poreLabels{
static_cast<int>(getParameter(vertex0,
"PoreLabel")),
244 static_cast<int>(getParameter(vertex1,
"PoreLabel"))};
245 setParameter(element,
"ThroatLabel",
throatLabel(poreLabels));
248 const auto numPoreRadiusLimited = std::count(poreRadiusLimited.begin(), poreRadiusLimited.end(),
true);
249 if (numPoreRadiusLimited > 0)
250 std::cout <<
"*******\nWarning! " << numPoreRadiusLimited <<
" out of " << poreRadiusLimited.size()
251 <<
" pore body radii have been capped automatically in order to prevent intersecting pores\n*******" << std::endl;
262 BoundaryList getPriorityList_()
const
264 const auto list = [&]()
266 BoundaryList priorityList;
267 std::iota(priorityList.begin(), priorityList.end(), 0);
276 catch(Dune::RangeError& e) {
277 DUNE_THROW(
Dumux::ParameterException,
"You must specify priorities for all directions (" << dimWorld <<
") \n" << e.what());
280 if (!isUnique_(priorityList))
284 if (std::any_of(priorityList.begin(), priorityList.end(), [](
const int i ){ return (i < 0 || i >= 2*dimWorld); }))
292 void computeBoundingBox_()
295 for (
const auto& vertex : vertices(gridView_))
297 for (
int i = 0; i < dimWorld; i++)
301 bBoxMin_[i] = min(bBoxMin_[i],
vertex.geometry().corner(0)[i]);
302 bBoxMax_[i] = max(bBoxMax_[i],
vertex.geometry().corner(0)[i]);
310 BoundaryList getBoundaryFacemarkerInput_()
const
312 BoundaryList boundaryFaceMarker;
313 std::fill(boundaryFaceMarker.begin(), boundaryFaceMarker.end(), 0);
314 boundaryFaceMarker[0] = 1;
315 boundaryFaceMarker[1] = 1;
319 std::cout <<
"\n\n ****** \nWarning: Grid.BoundaryFaceMarker is deprecated and will be removed after 3.5. Use Grid.BoundaryPoreLabels instead. \n\n ******" << std::endl;
323 catch (Dune::RangeError& e) {
324 DUNE_THROW(Dumux::ParameterException,
"You must specify all boundaries faces: xmin xmax ymin ymax (zmin zmax). \n" << e.what());
326 if (std::none_of(boundaryFaceMarker.begin(), boundaryFaceMarker.end(), [](
const int i ){ return i == 1; }))
327 DUNE_THROW(Dumux::ParameterException,
"At least one face must have index 1");
328 if (std::any_of(boundaryFaceMarker.begin(), boundaryFaceMarker.end(), [](
const int i ){ return (i < 0 || i > 2*dimWorld); }))
329 DUNE_THROW(Dumux::ParameterException,
"Face indices must range from 0 to " << 2*dimWorld );
334 for (
const auto& entry : input)
336 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"
337 "Example (2D, defaults are used for the remaining boundaries): xMin:2 yMax:3\n";
338 if (entry.find(
':') == std::string::npos)
339 DUNE_THROW(Dumux::ParameterException, errorMessage);
341 static const std::map<std::string, int> labels = {{
"xMin", 0}, {
"xMax", 1}, {
"yMin", 2}, {
"yMax", 3}, {
"zMin", 4}, {
"zMax", 5}};
342 const auto splitEntry =
split(entry,
":");
343 const std::string location = std::string(splitEntry[0].begin(), splitEntry[0].end());
346 value = std::stoi(std::string(splitEntry[1].begin(), splitEntry[1].end()));
349 DUNE_THROW(Dumux::ParameterException, errorMessage);
352 if (splitEntry.size() != 2)
353 DUNE_THROW(Dumux::ParameterException, errorMessage);
354 if (!labels.count(location))
355 DUNE_THROW(Dumux::ParameterException, errorMessage);
357 boundaryFaceMarker[labels.at(location)] = value;
360 return boundaryFaceMarker;
364 template <
class GetParameter>
365 std::vector<Scalar> getMaxPoreRadii_(std::size_t numSubregions,
const GetParameter& getParameter)
const
367 const auto numVertices = gridView_.size(dim);
368 std::vector<Scalar> maxPoreRadius(numVertices, std::numeric_limits<Scalar>::max());
371 return maxPoreRadius;
375 std::vector<Scalar> subregionInputThroatLengths;
377 if (numSubregions > 0)
379 for (
int i = 0; i < numSubregions; ++i)
382 const std::string paramGroup = paramGroup_ +
".SubRegion" + std::to_string(i);
384 subregionInputThroatLengths.push_back(subregionInputThroatLength);
388 for (
const auto& element : elements(gridView_))
391 if (numSubregions > 0)
394 const auto subregionId = getParameter(element,
"ThroatRegionId");
395 if (subregionId >= 0)
398 if (subregionInputThroatLengths[subregionId] > 0.0)
401 else if (inputThroatLength > 0.0)
404 else if (inputThroatLength > 0.0)
409 const Scalar delta =
element.geometry().volume();
411 const Scalar maxRadius = (delta - minThroatLength)/2.0;
412 for (
int vIdxLocal = 0; vIdxLocal < 2 ; ++vIdxLocal)
414 const int vIdxGlobal = gridView_.indexSet().subIndex(element, vIdxLocal, dim);
415 maxPoreRadius[vIdxGlobal] = std::min(maxPoreRadius[vIdxGlobal], maxRadius);
419 return maxPoreRadius;
423 std::function<Scalar(
const Vertex&,
const int)> poreRadiusGenerator_(
const int subregionId)
const
426 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
429 std::mt19937 generator;
437 const auto generateFunction = [&](
auto& poreRadiusDist)
439 return [=](
const auto&
vertex,
const int poreLabel)
mutable
441 const auto radius = poreRadiusDist(generator);
444 if (poreLabelsToSetFixedRadius.empty() && poreLabelsToApplyFactorForRadius.empty())
448 else if (!poreLabelsToSetFixedRadius.empty() || !poreRadiusForLabel.empty())
450 if (poreLabelsToSetFixedRadius.size() != poreRadiusForLabel.size())
451 DUNE_THROW(Dumux::ParameterException,
"PoreLabelsToSetFixedRadius must be of same size as FixedPoreRadiusForLabel");
453 if (
const auto it = std::find(poreLabelsToSetFixedRadius.begin(), poreLabelsToSetFixedRadius.end(), poreLabel); it != poreLabelsToSetFixedRadius.end())
454 return poreRadiusForLabel[std::distance(poreLabelsToSetFixedRadius.begin(), it)];
458 else if (!poreLabelsToApplyFactorForRadius.empty() || !poreRadiusFactorForLabel.empty())
460 if (poreLabelsToApplyFactorForRadius.size() != poreRadiusFactorForLabel.size())
461 DUNE_THROW(Dumux::ParameterException,
"PoreLabelsToApplyFactorForRadius must be of same size as PoreRadiusFactorForLabel");
463 if (
const auto it = std::find(poreLabelsToApplyFactorForRadius.begin(), poreLabelsToApplyFactorForRadius.end(), poreLabel); it != poreLabelsToApplyFactorForRadius.end())
464 return poreRadiusFactorForLabel[std::distance(poreLabelsToApplyFactorForRadius.begin(), it)] * radius;
474 if (fixedPoreRadius <= 0.0)
478 generator.seed(seed);
481 if (type ==
"lognormal")
484 const auto [meanPoreRadius, stddevPoreRadius] = getDistributionInputParams_(
"Lognormal", prefix,
485 "MeanPoreInscribedRadius",
486 "StandardDeviationPoreInscribedRadius");
487 const Scalar variance = stddevPoreRadius*stddevPoreRadius;
491 const Scalar mu = log(meanPoreRadius/sqrt(1.0 + variance/(meanPoreRadius*meanPoreRadius)));
492 const Scalar sigma = sqrt(log(1.0 + variance/(meanPoreRadius*meanPoreRadius)));
494 Dumux::SimpleLogNormalDistribution<> poreRadiusDist(mu, sigma);
495 return generateFunction(poreRadiusDist);
497 else if (type ==
"uniform")
500 const auto [minPoreRadius, maxPoreRadius] = getDistributionInputParams_(
"Uniform", prefix,
501 "MinPoreInscribedRadius",
502 "MaxPoreInscribedRadius");
503 Dumux::SimpleUniformDistribution<> poreRadiusDist(minPoreRadius, maxPoreRadius);
504 return generateFunction(poreRadiusDist);
507 DUNE_THROW(Dune::InvalidStateException,
"Unknown parameter type " << type);
513 auto poreRadiusDist = [fixedPoreRadius](
auto& gen){
return fixedPoreRadius; };
514 return generateFunction(poreRadiusDist);
519 std::array<Scalar, 2> getDistributionInputParams_(
const std::string& distributionName,
520 const std::string& prefix,
521 const std::string& paramName0,
522 const std::string& paramName1)
const
529 catch (
const Dumux::ParameterException& e)
531 std::cout <<
"\n" << distributionName <<
" pore-size distribution needs input parameters "
532 << prefix + paramName0 <<
" and " << prefix + paramName1 <<
".\n"
533 <<
"Alternatively, use " << prefix <<
"PoreInscribedRadius to set a fixed inscribed pore radius." << std::endl;
534 DUNE_THROW(Dumux::ParameterException, e.what());
538 template <
class GetParameter>
539 auto poreVolumeGenerator_(
const GetParameter& getParameter)
const
543 if (!isUnique_(capPoresOnBoundaries))
544 DUNE_THROW(Dune::InvalidStateException,
"CapPoresOnBoundaries must not contain duplicates");
547 return [=] (
const auto&
vertex,
const auto vIdx)
549 const Scalar r = getParameter(vertex,
"PoreInscribedRadius");
555 const Scalar h = fixedHeight > 0.0 ? fixedHeight : getParameter(vertex,
"PoreHeight");
562 if (capPoresOnBoundaries.empty())
566 std::size_t numCaps = 0;
568 const auto& pos =
vertex.geometry().center();
569 for (
auto boundaryIdx : capPoresOnBoundaries)
571 if (onBoundary_(pos, boundaryIdx))
578 if (numCaps > dimWorld)
579 DUNE_THROW(Dune::InvalidStateException,
"Pore " << vIdx <<
" at " << pos <<
" capped " << numCaps <<
" times. Capping should not happen more than " << dimWorld <<
" times");
587 template <
class GetParameter>
588 auto throatInscribedRadiusGenerator_(
const int subregionId,
const GetParameter& getParameter)
const
591 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
594 const Scalar inputThroatInscribedRadius =
getParamFromGroup<Scalar>(paramGroup_, prefix +
"ThroatInscribedRadius", -1.0);
599 return [=](
const Element&
element)
601 const Scalar delta =
element.geometry().volume();
602 const std::array<Vertex, 2> vertices = {
element.template subEntity<dim>(0),
element.template subEntity<dim>(1)};
605 if (inputThroatInscribedRadius > 0.0)
606 return inputThroatInscribedRadius;
609 const Scalar poreRadius0 = getParameter(vertices[0],
"PoreInscribedRadius");
610 const Scalar poreRadius1 = getParameter(vertices[1],
"PoreInscribedRadius");
617 template <
class GetParameter>
618 auto throatLengthGenerator_(
int subregionId,
const GetParameter& getParameter)
const
621 const std::string prefix = subregionId < 0 ?
"Grid." :
"Grid.Subregion" + std::to_string(subregionId) +
".";
625 bool subtractRadiiFromThroatLength =
true;
626 if (
hasParamInGroup(paramGroup_, prefix +
"SubstractRadiiFromThroatLength"))
628 std::cout <<
"\n\n ****** \n Warning: SubstractRadiiFromThroatLength is deprecated and will be removed after 3.5. Use SubtractPoreInscribedRadiiFromThroatLength instead \n\n ******" << std::endl;
629 subtractRadiiFromThroatLength =
getParamFromGroup<bool>(paramGroup_, prefix +
"SubstractRadiiFromThroatLength");
632 subtractRadiiFromThroatLength =
getParamFromGroup<bool>(paramGroup_, prefix +
"SubtractPoreInscribedRadiiFromThroatLength",
true);
634 return [=](
const Element&
element)
636 if (inputThroatLength > 0.0)
637 return inputThroatLength;
639 const Scalar delta =
element.geometry().volume();
640 if (subtractRadiiFromThroatLength)
642 const std::array<Vertex, 2> vertices = {
element.template subEntity<dim>(0),
element.template subEntity<dim>(1)};
643 const Scalar result = delta - getParameter(vertices[0],
"PoreInscribedRadius") - getParameter(vertices[1],
"PoreInscribedRadius");
645 DUNE_THROW(Dune::GridError,
"Pore radii are so large they intersect! Something went wrong at element " << gridView_.indexSet().index(element));
654 bool onBoundary_(
const GlobalPosition& pos, std::size_t boundaryIdx)
const
656 constexpr auto eps = 1e-8;
658 static constexpr std::array boundaryIdxToCoordinateIdx{0, 0, 1, 1, 2, 2};
659 const auto coordinateIdx = boundaryIdxToCoordinateIdx[boundaryIdx];
660 auto isMaxBoundary = [] (
int n) {
return (n % 2 != 0); };
662 if (isMaxBoundary(boundaryIdx))
663 return pos[coordinateIdx] > bBoxMax_[coordinateIdx] - eps;
665 return pos[coordinateIdx] < bBoxMin_[coordinateIdx] + eps;
671 bool isUnique_(T t)
const
673 std::sort(t.begin(), t.end());
674 return (std::unique(t.begin(), t.end()) == t.end());
677 const GridView gridView_;
679 const std::string paramGroup_;
680 BoundaryList priorityList_;
681 BoundaryList boundaryFaceIndex_;
684 GlobalPosition bBoxMin_ = GlobalPosition(std::numeric_limits<double>::max());
685 GlobalPosition bBoxMax_ = GlobalPosition(std::numeric_limits<double>::min());