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_;