38    using Scalar = 
typename GridVariables::Scalar;
 
   39    using GridGeometry = 
typename GridVariables::GridGeometry;
 
   40    using FVElementGeometry = 
typename GridGeometry::LocalView;
 
   41    using SubControlVolumeFace = 
typename FVElementGeometry::SubControlVolumeFace;
 
   42    using GridView = 
typename GridGeometry::GridView;
 
   43    using VolumeVariables = 
typename GridVariables::VolumeVariables;
 
   44    using Element = 
typename GridView::template Codim<0>::Entity;
 
   45    using NumEqVector = 
typename LocalResidual::ElementResidualVector::value_type;
 
   47    static constexpr auto dim = GridView::dimension;
 
   48    static constexpr auto dimWorld = GridView::dimensionworld;
 
   50    static_assert(dim > 1, 
"Only implemented for dim > 1");
 
   52    using GlobalPosition = 
typename Element::Geometry::GlobalCoordinate;
 
   55    using SurfaceT = Dune::AxisAlignedCubeGeometry<Scalar, (dim == 2 ? 1 : 2), dimWorld>;
 
   60        std::size_t normalDirectionIndex;
 
   72                               const SolutionVector& sol,
 
   73                               const LocalResidual& localResidual,
 
   74                               bool nonIntersectingSurfaceIsError = 
false)
 
   75    : gridVariables_(gridVariables)
 
   77    , localResidual_(localResidual)
 
   78    , nonIntersectingSurfaceIsError_(nonIntersectingSurfaceIsError)
 
 
   92        static_assert(std::is_same_v<std::decay_t<T>, 
Surface>);
 
   93        surfaces_.emplace(std::make_pair(
 
   94            name, std::make_pair(surface, NumEqVector(0.0))
 
 
  106                                const GlobalPosition& lowerLeft,
 
  107                                const GlobalPosition& upperRight)
 
  110        const GlobalPosition v = upperRight - lowerLeft;
 
  111        const auto it = std::find_if(v.begin(), v.end(), [](
const auto& x){ return abs(x) < 1e-20; });
 
  113            DUNE_THROW(Dune::InvalidStateException, 
"Surface is not axis-parallel!");
 
  115        const std::size_t normalDirectionIndex = std::distance(v.begin(), it);
 
  116        auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
 
  117        inSurfaceAxes.set(normalDirectionIndex, 
false);
 
  118        auto surface = 
Surface(lowerLeft, upperRight, inSurfaceAxes);
 
  120        surfaces_.emplace(std::make_pair(
 
  123                std::move(surface), normalDirectionIndex, NumEqVector(0.0)
 
 
  136                              const GlobalPosition& 
center,
 
  137                              const std::size_t normalDirectionIndex)
 
  139        GlobalPosition lowerLeft = gridVariables_.gridGeometry().bBoxMin();
 
  140        GlobalPosition upperRight = gridVariables_.gridGeometry().bBoxMax();
 
  142        lowerLeft[normalDirectionIndex] = 
center[normalDirectionIndex];
 
  143        upperRight[normalDirectionIndex] = 
center[normalDirectionIndex];
 
  145        auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
 
  146        inSurfaceAxes.set(normalDirectionIndex, 
false);
 
  147        auto surface = 
Surface(lowerLeft, upperRight, inSurfaceAxes);
 
  149        surfaces_.emplace(std::make_pair(
 
  152                std::move(surface), normalDirectionIndex, NumEqVector(0.0)
 
 
  162        auto fluxType = [
this](
const auto& element,
 
  163                               const auto& fvGeometry,
 
  164                               const auto& elemVolVars,
 
  166                               const auto& elemFluxVarsCache)
 
  168            return localResidual_.evalFlux(
 
  169                problem_(), element, fvGeometry, elemVolVars, elemFluxVarsCache, scvf
 
 
  187    template<
class FluxType>
 
  191        for (
auto& surface : surfaces_)
 
  192            surface.second.flux = 0.0;
 
  194        snapSurfaceToClosestFace_();
 
  195        calculateFluxes_(fluxType);
 
 
  203    const auto& 
flux(
const std::string& name)
 const 
  205        return surfaces_.at(name).flux;
 
 
  211    const std::map<std::string, SurfaceData>& 
surfaces()
 const 
  212    { 
return surfaces_; }
 
 
  219        for (
const auto& [name, data] : surfaces_)
 
  220            std::cout << 
"Flux over surface " << name << 
": " << data.flux << std::endl;
 
 
  227    { nonIntersectingSurfaceIsError_ = isError; }
 
 
  231    template<
class FluxType>
 
  232    void calculateFluxes_(
const FluxType& fluxType)
 
  234        auto fvGeometry = 
localView(problem_().gridGeometry());
 
  235        auto elemVolVars = 
localView(gridVariables_.curGridVolVars());
 
  236        auto elemFluxVarsCache = 
localView(gridVariables_.gridFluxVarsCache());
 
  238        for (
const auto& element : elements(problem_().gridGeometry().gridView()))
 
  240            fvGeometry.bindElement(element);
 
  241            elemVolVars.bindElement(element, fvGeometry, sol_);
 
  242            elemFluxVarsCache.bindElement(element, fvGeometry, elemVolVars);
 
  244            for (
const auto& scvf : scvfs(fvGeometry))
 
  248                for (
auto& [name, surfaceData] : surfaces_)
 
  250                    if (considerScvf_(scvf, surfaceData))
 
  252                        const auto result = fluxType(element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
 
  253                        surfaceData.flux += result;
 
  256                            std::cout << 
"At element " << problem_().gridGeometry().elementMapper().index(element)
 
  257                                      << 
": Flux at face " << scvf.ipGlobal() << 
": " << result << 
" (" << name << 
")" << std::endl;
 
  265    bool considerScvf_(
const SubControlVolumeFace& scvf, 
const SurfaceData& SurfaceData)
 const 
  270        if (scvf.boundary() || !std::signbit(scvf.unitOuterNormal()[SurfaceData.normalDirectionIndex]))
 
  276    void snapSurfaceToClosestFace_()
 
  278        using GeometriesEntitySet = Dumux::GeometriesEntitySet<Surface>;
 
  279        const auto gridView = problem_().gridGeometry().gridView();
 
  281        for (
auto& [name, surfaceData] : surfaces_)
 
  283            GeometriesEntitySet entitySet({surfaceData.surface});
 
  284            Dumux::BoundingBoxTree<GeometriesEntitySet> geometriesTree(std::make_shared<GeometriesEntitySet>(entitySet));
 
  286                problem_().gridGeometry().boundingBoxTree(), geometriesTree
 
  289            if (intersectingElements.empty())
 
  291                if (!nonIntersectingSurfaceIsError_)
 
  294                std::cout << 
"surface boundaries: " << std::endl;
 
  295                printSurfaceBoundaries_(surfaceData.surface);
 
  297                DUNE_THROW(Dune::InvalidStateException, 
"surface " << name << 
" does not intersect with any element");
 
  300            std::vector<std::size_t> sortedResults;
 
  301            sortedResults.reserve(gridView.size(0));
 
  303            for (
const auto& i : intersectingElements)
 
  304                sortedResults.push_back(i.first());
 
  306            std::sort(sortedResults.begin(), sortedResults.end());
 
  307            sortedResults.erase(std::unique(
 
  308                sortedResults.begin(), sortedResults.end()
 
  309            ), sortedResults.end());
 
  312            GlobalPosition normalVector(0.0);
 
  313            normalVector[surfaceData.normalDirectionIndex] = 1.0;
 
  315            const auto& firstIntersectingElement = problem_().gridGeometry().element(sortedResults[0]);
 
  316            Scalar 
distance = std::numeric_limits<Scalar>::max();
 
  317            bool snappingOcurred = 
false;
 
  319            GlobalPosition surfaceLowerLeft = surfaceData.surface.corner(0);
 
  320            GlobalPosition surfaceUpperRight = surfaceData.surface.corner(3);
 
  322            bool surfaceAlreadyOnFaces = 
false;
 
  323            for (
const auto& intersection : intersections(gridView, firstIntersectingElement))
 
  325                if (surfaceAlreadyOnFaces)
 
  329                if (abs(1.0 - abs(normalVector * intersection.centerUnitOuterNormal())) < 1e-8)
 
  332                    const auto getDistance = [](
const auto& p, 
const auto& geo)
 
  334                        if constexpr (dim == 2)
 
  340                    const auto& geo = intersection.geometry();
 
  341                    if (
const Scalar d = getDistance(geo.center(), surfaceData.surface); d < 1e-8 * 
diameter(geo))
 
  344                        surfaceAlreadyOnFaces = 
true;
 
  345                        snappingOcurred = 
false;
 
  350                        snappingOcurred = 
true;
 
  353                        for (
int i = 0; i < surfaceData.surface.corners(); ++i)
 
  355                            const auto& faceCenter = geo.center();
 
  356                            surfaceLowerLeft[surfaceData.normalDirectionIndex] = faceCenter[surfaceData.normalDirectionIndex];
 
  357                            surfaceUpperRight[surfaceData.normalDirectionIndex] = faceCenter[surfaceData.normalDirectionIndex];
 
  365                std::cout << 
"\n\nSurface '" << name <<  
"' was automatically snapped to the closest faces" << std::endl;
 
  366                std::cout << 
"Old surface boundaries: " << std::endl;
 
  367                printSurfaceBoundaries_(surfaceData.surface);
 
  370                auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
 
  371                inSurfaceAxes.set(surfaceData.normalDirectionIndex, 
false);
 
  372                surfaceData.surface = 
Surface{surfaceLowerLeft, surfaceUpperRight, inSurfaceAxes};
 
  374                std::cout << 
"New surface boundaries: " << std::endl;
 
  375                printSurfaceBoundaries_(surfaceData.surface);
 
  376                std::cout << std::endl;
 
  381    void printSurfaceBoundaries_(
const Surface& surface)
 const 
  383        for (
int i = 0; i < 
surface.corners(); ++i)
 
  384            std::cout << 
surface.corner(i) << std::endl;
 
  387    const auto& problem_()
 const { 
return gridVariables_.curGridVolVars().problem(); }
 
  389    std::map<std::string, SurfaceData> surfaces_;
 
  390    const GridVariables& gridVariables_;
 
  391    const SolutionVector& sol_;
 
  392    const LocalResidual localResidual_; 
 
  394    bool nonIntersectingSurfaceIsError_;