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)
 
  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);
 
  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;
 
  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)
 
  313                    DUNE_THROW(Dumux::ParameterException, errorMessage);
 
  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()));
 
  323                    DUNE_THROW(Dumux::ParameterException, errorMessage);
 
  326                if (splitEntry.size() != 2)
 
  327                    DUNE_THROW(Dumux::ParameterException, errorMessage);
 
  328                if (!labels.count(location))
 
  329                    DUNE_THROW(Dumux::ParameterException, errorMessage);
 
  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());
 
  345            return maxPoreRadius;
 
  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);
 
  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();
 
  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;
 
  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())
 
  425                        DUNE_THROW(Dumux::ParameterException, 
"PoreLabelsToSetFixedRadius must be of same size as FixedPoreRadiusForLabel");
 
  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;
 
  448        if (fixedPoreRadius <= 0.0)
 
  452            generator.seed(seed);
 
  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)));
 
  468                Dumux::SimpleLogNormalDistribution<> poreRadiusDist(mu, sigma);
 
  469                return generateFunction(poreRadiusDist);
 
  471            else if (type == 
"uniform")
 
  474                const auto [minPoreRadius, maxPoreRadius] = getDistributionInputParams_(
"Uniform", prefix,
 
  475                                                                                        "MinPoreInscribedRadius",
 
  476                                                                                        "MaxPoreInscribedRadius");
 
  477                Dumux::SimpleUniformDistribution<> poreRadiusDist(minPoreRadius, maxPoreRadius);
 
  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 
  503        catch (
const Dumux::ParameterException& e)
 
  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;
 
  508            DUNE_THROW(Dumux::ParameterException, e.what());
 
  512    template <
class GetParameter>
 
  513    auto poreVolumeGenerator_(
const GetParameter& getParameter)
 const 
  517        if (!isUnique_(capPoresOnBoundaries))
 
  518            DUNE_THROW(Dune::InvalidStateException, 
"CapPoresOnBoundaries must not contain duplicates");
 
  521        return [=,
this] (
const auto& 
vertex, 
const auto vIdx)
 
  523            const Scalar r = getParameter(vertex, 
"PoreInscribedRadius");
 
  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);
 
  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) + 
".";
 
  598        const bool subtractRadiiFromThroatLength = 
getParamFromGroup<bool>(paramGroup_, prefix + 
"SubtractPoreInscribedRadiiFromThroatLength", 
true);
 
  600        return [=,
this](
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());