version 3.11-dev
Loading...
Searching...
No Matches
fluxoveraxisalignedsurface.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_FREELOW_NAVIERSTOKES_FLUX_OVER_AXISALIGNED_SURFACE_HH
13#define DUMUX_FREELOW_NAVIERSTOKES_FLUX_OVER_AXISALIGNED_SURFACE_HH
14
15#include <algorithm>
16#include <type_traits>
17#include <vector>
18
19#include <dune/common/exceptions.hh>
20#include <dune/geometry/axisalignedcubegeometry.hh>
21
29
30namespace Dumux {
31
36template<class GridVariables, class SolutionVector, class LocalResidual>
38{
39 using Scalar = typename GridVariables::Scalar;
41 using FVElementGeometry = typename GridGeometry::LocalView;
42 using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace;
43 using GridView = typename GridGeometry::GridView;
44 using VolumeVariables = typename GridVariables::VolumeVariables;
45 using Element = typename GridView::template Codim<0>::Entity;
46 using NumEqVector = typename LocalResidual::ElementResidualVector::value_type;
47
48 static constexpr auto dim = GridView::dimension;
49 static constexpr auto dimWorld = GridView::dimensionworld;
50
51 static_assert(dim > 1, "Only implemented for dim > 1");
52
53 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
54
55 // in 2D, the surface is represented as a line
56 using SurfaceT = Dune::AxisAlignedCubeGeometry<Scalar, (dim == 2 ? 1 : 2), dimWorld>;
57
58 struct SurfaceData
59 {
60 SurfaceT surface;
61 std::size_t normalDirectionIndex;
62 NumEqVector flux;
63 };
64
65public:
66
67 using Surface = SurfaceT;
68
72 FluxOverAxisAlignedSurface(const GridVariables& gridVariables,
73 const SolutionVector& sol,
74 const LocalResidual& localResidual,
75 bool nonIntersectingSurfaceIsError = false)
76 : gridVariables_(gridVariables)
77 , sol_(sol)
78 , localResidual_(localResidual)
79 , nonIntersectingSurfaceIsError_(nonIntersectingSurfaceIsError)
80 {
81 verbose_ = getParamFromGroup<bool>(problem_().paramGroup(), "FluxOverAxisAlignedSurface.Verbose", false);
82 }
83
90 template<class T>
91 void addAxisAlignedSurface(const std::string& name, T&& surface)
92 {
93 static_assert(std::is_same_v<std::decay_t<T>, Surface>);
94 surfaces_.emplace(std::make_pair(
95 name, std::make_pair(surface, NumEqVector(0.0))
96 ));
97 }
98
106 void addAxisAlignedSurface(const std::string& name,
107 const GlobalPosition& lowerLeft,
108 const GlobalPosition& upperRight)
109 {
110 using std::abs;
111 const GlobalPosition v = upperRight - lowerLeft;
112 const auto it = std::find_if(v.begin(), v.end(), [](const auto& x){ return abs(x) < 1e-20; });
113 if (it == v.end())
114 DUNE_THROW(Dune::InvalidStateException, "Surface is not axis-parallel!");
115
116 const std::size_t normalDirectionIndex = std::distance(v.begin(), it);
117 auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
118 inSurfaceAxes.set(normalDirectionIndex, false);
119 auto surface = Surface(lowerLeft, upperRight, inSurfaceAxes);
120
121 surfaces_.emplace(std::make_pair(
122 name,
123 SurfaceData{
124 std::move(surface), normalDirectionIndex, NumEqVector(0.0)
125 }
126 ));
127 }
128
136 void addAxisAlignedPlane(const std::string& name,
137 const GlobalPosition& center,
138 const std::size_t normalDirectionIndex)
139 {
140 const auto& gridDisc = Dumux::gridDiscretization(gridVariables_);
141 GlobalPosition lowerLeft = gridDisc.bBoxMin();
142 GlobalPosition upperRight = gridDisc.bBoxMax();
143
144 lowerLeft[normalDirectionIndex] = center[normalDirectionIndex];
145 upperRight[normalDirectionIndex] = center[normalDirectionIndex];
146
147 auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
148 inSurfaceAxes.set(normalDirectionIndex, false);
149 auto surface = Surface(lowerLeft, upperRight, inSurfaceAxes);
150
151 surfaces_.emplace(std::make_pair(
152 name,
153 SurfaceData{
154 std::move(surface), normalDirectionIndex, NumEqVector(0.0)
155 }
156 ));
157 }
158
163 {
164 auto fluxType = [this](const auto& element,
165 const auto& fvGeometry,
166 const auto& elemVolVars,
167 const auto& scvf,
168 const auto& elemFluxVarsCache)
169 {
170 return localResidual_.evalFlux(
171 problem_(), element, fvGeometry, elemVolVars, elemFluxVarsCache, scvf
172 );
173 };
174
175 calculateFluxes(fluxType);
176 }
177
189 template<class FluxType>
190 void calculateFluxes(const FluxType& fluxType)
191 {
192 // make sure to reset all the values of the surfaces, in case this method has been called already before
193 for (auto& surface : surfaces_)
194 surface.second.flux = 0.0;
195
196 snapSurfaceToClosestFace_();
197 calculateFluxes_(fluxType);
198 }
199
205 const auto& flux(const std::string& name) const
206 {
207 return surfaces_.at(name).flux;
208 }
209
213 const std::map<std::string, SurfaceData>& surfaces() const
214 { return surfaces_; }
215
219 void printAllFluxes() const
220 {
221 for (const auto& [name, data] : surfaces_)
222 std::cout << "Flux over surface " << name << ": " << data.flux << std::endl;
223 }
224
228 void setNonIntersectingSurfaceIsError(bool isError = true)
229 { nonIntersectingSurfaceIsError_ = isError; }
230
231private:
232
233 template<class FluxType>
234 void calculateFluxes_(const FluxType& fluxType)
235 {
236 auto fvGeometry = localView(problem_().gridGeometry());
237 auto elemVolVars = localView(gridVariables_.curGridVolVars());
238 auto elemFluxVarsCache = localView(gridVariables_.gridFluxVarsCache());
239
240 for (const auto& element : elements(problem_().gridGeometry().gridView()))
241 {
242 fvGeometry.bind(element);
243 elemVolVars.bind(element, fvGeometry, sol_);
244 elemFluxVarsCache.bind(element, fvGeometry, elemVolVars);
245
246 for (const auto& scvf : scvfs(fvGeometry))
247 {
248 // iterate through all surfaces and check if the flux at the given position
249 // should be accounted for in the respective surface
250 for (auto& [name, surfaceData] : surfaces_)
251 {
252 if (considerScvf_(scvf, surfaceData))
253 {
254 const auto result = fluxType(element, fvGeometry, elemVolVars, scvf, elemFluxVarsCache);
255 surfaceData.flux += result;
256
257 if (verbose_)
258 std::cout << "At element " << problem_().gridGeometry().elementMapper().index(element)
259 << ": Flux at face " << scvf.ipGlobal() << ": " << result << " (" << name << ")" << std::endl;
260 }
261 }
262 }
263 }
264 }
265
267 bool considerScvf_(const SubControlVolumeFace& scvf, const SurfaceData& SurfaceData) const
268 {
269 // In order to avoid considering scvfs at the same element intersection (and hence, the corresponding flux) twice,
270 // only use those with a unit outer normal pointing towards positive coordinate direction,
271 // unless the scvf lies on a boundary (then there is no second scvf).
272 if (scvf.boundary() || !std::signbit(scvf.unitOuterNormal()[SurfaceData.normalDirectionIndex]))
273 return intersectsPointGeometry(scvf.ipGlobal(), SurfaceData.surface);
274 else
275 return false;
276 }
277
278 void snapSurfaceToClosestFace_()
279 {
280 using GeometriesEntitySet = Dumux::GeometriesEntitySet<Surface>;
281 const auto gridView = problem_().gridGeometry().gridView();
282
283 for (auto& [name, surfaceData] : surfaces_)
284 {
285 GeometriesEntitySet entitySet({surfaceData.surface});
286 Dumux::BoundingBoxTree<GeometriesEntitySet> geometriesTree(std::make_shared<GeometriesEntitySet>(entitySet));
287 const auto intersectingElements = intersectingEntities(
288 problem_().gridGeometry().boundingBoxTree(), geometriesTree
289 );
290
291 if (intersectingElements.empty())
292 {
293 if (!nonIntersectingSurfaceIsError_)
294 continue;
295
296 std::cout << "surface boundaries: " << std::endl;
297 printSurfaceBoundaries_(surfaceData.surface);
298
299 DUNE_THROW(Dune::InvalidStateException, "surface " << name << " does not intersect with any element");
300 }
301
302 std::vector<std::size_t> sortedResults;
303 sortedResults.reserve(gridView.size(0));
304
305 for (const auto& i : intersectingElements)
306 sortedResults.push_back(i.first());
307
308 std::sort(sortedResults.begin(), sortedResults.end());
309 sortedResults.erase(std::unique(
310 sortedResults.begin(), sortedResults.end()
311 ), sortedResults.end());
312
313 // pick the first intersecting element and make sure the surface snaps to the closest face with the same (or opposite facing) normal vector
314 GlobalPosition normalVector(0.0);
315 normalVector[surfaceData.normalDirectionIndex] = 1.0;
316
317 const auto& firstIntersectingElement = problem_().gridGeometry().element(sortedResults[0]);
318 Scalar distance = std::numeric_limits<Scalar>::max();
319 bool snappingOcurred = false;
320
321 GlobalPosition surfaceLowerLeft = surfaceData.surface.corner(0);
322 GlobalPosition surfaceUpperRight = surfaceData.surface.corner(3);
323
324 bool surfaceAlreadyOnFaces = false;
325 for (const auto& intersection : intersections(gridView, firstIntersectingElement))
326 {
327 if (surfaceAlreadyOnFaces)
328 continue;
329
330 using std::abs;
331 if (abs(1.0 - abs(normalVector * intersection.centerUnitOuterNormal())) < 1e-8)
332 {
333
334 const auto getDistance = [](const auto& p, const auto& geo)
335 {
336 if constexpr (dim == 2)
337 return distancePointSegment(p, geo);
338 else
339 return distancePointPolygon(p, geo);
340 };
341
342 const auto& geo = intersection.geometry();
343 if (const Scalar d = getDistance(geo.center(), surfaceData.surface); d < 1e-8 * diameter(geo))
344 {
345 // no snapping required, face already lies on surface
346 surfaceAlreadyOnFaces = true;
347 snappingOcurred = false;
348 }
349 else if (d < distance)
350 {
351 distance = d;
352 snappingOcurred = true;
353
354 // move the surface boundaries
355 for (int i = 0; i < surfaceData.surface.corners(); ++i)
356 {
357 const auto& faceCenter = geo.center();
358 surfaceLowerLeft[surfaceData.normalDirectionIndex] = faceCenter[surfaceData.normalDirectionIndex];
359 surfaceUpperRight[surfaceData.normalDirectionIndex] = faceCenter[surfaceData.normalDirectionIndex];
360 }
361 }
362 }
363 }
364
365 if (snappingOcurred)
366 {
367 std::cout << "\n\nSurface '" << name << "' was automatically snapped to the closest faces" << std::endl;
368 std::cout << "Old surface boundaries: " << std::endl;
369 printSurfaceBoundaries_(surfaceData.surface);
370
371 // overwrite the old surface with the new boundaries
372 auto inSurfaceAxes = std::move(std::bitset<dimWorld>{}.set());
373 inSurfaceAxes.set(surfaceData.normalDirectionIndex, false);
374 surfaceData.surface = Surface{surfaceLowerLeft, surfaceUpperRight, inSurfaceAxes};
375
376 std::cout << "New surface boundaries: " << std::endl;
377 printSurfaceBoundaries_(surfaceData.surface);
378 std::cout << std::endl;
379 }
380 }
381 }
382
383 void printSurfaceBoundaries_(const Surface& surface) const
384 {
385 for (int i = 0; i < surface.corners(); ++i)
386 std::cout << surface.corner(i) << std::endl;
387 }
388
389 const auto& problem_() const { return gridVariables_.curGridVolVars().problem(); }
390
391 std::map<std::string, SurfaceData> surfaces_;
392 const GridVariables& gridVariables_;
393 const SolutionVector& sol_;
394 const LocalResidual localResidual_; // store a copy of the local residual
395 bool verbose_;
396 bool nonIntersectingSurfaceIsError_;
397};
398
399} // end namespace Dumux
400
401#endif
void addAxisAlignedPlane(const std::string &name, const GlobalPosition &center, const std::size_t normalDirectionIndex)
Add an axis-aligned plane (line in 2D) with a given name, specifying the planes's center and normal.
Definition fluxoveraxisalignedsurface.hh:136
void setNonIntersectingSurfaceIsError(bool isError=true)
Set if non-intersecting surfaces are treated as error.
Definition fluxoveraxisalignedsurface.hh:228
void printAllFluxes() const
Prints all fluxes.
Definition fluxoveraxisalignedsurface.hh:219
void addAxisAlignedSurface(const std::string &name, T &&surface)
Add an axis-aligned surface with a given name.
Definition fluxoveraxisalignedsurface.hh:91
const std::map< std::string, SurfaceData > & surfaces() const
Provides access to all surfaces.
Definition fluxoveraxisalignedsurface.hh:213
const auto & flux(const std::string &name) const
Return the flux over given surface.
Definition fluxoveraxisalignedsurface.hh:205
void calculateAllFluxes()
Calculate the fluxes over all surfaces.
Definition fluxoveraxisalignedsurface.hh:162
SurfaceT Surface
Definition fluxoveraxisalignedsurface.hh:67
void addAxisAlignedSurface(const std::string &name, const GlobalPosition &lowerLeft, const GlobalPosition &upperRight)
Add an axis-aligned surface (segment in 2D) with a given name, specifying the surface's corner points...
Definition fluxoveraxisalignedsurface.hh:106
void calculateFluxes(const FluxType &fluxType)
Calculate the fluxes over all surfaces for a given flux type.
Definition fluxoveraxisalignedsurface.hh:190
FluxOverAxisAlignedSurface(const GridVariables &gridVariables, const SolutionVector &sol, const LocalResidual &localResidual, bool nonIntersectingSurfaceIsError=false)
The constructor.
Definition fluxoveraxisalignedsurface.hh:72
A function to compute a geometry's diameter, i.e. the longest distance between points of a geometry.
Helper functions for distance queries.
An interface for a set of geometric entities.
Type traits for classes providing a grid discretization.
typename NumEqVectorTraits< PrimaryVariables >::type NumEqVector
A vector with the same size as numbers of equations This is the default implementation and has to be ...
Definition numeqvector.hh:34
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition localview.hh:26
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
static Geometry::ctype distancePointPolygon(const typename Geometry::GlobalCoordinate &p, const Geometry &geometry)
Compute the shortest distance from a point to a given polygon geometry.
Definition distance.hh:256
static Point::value_type distancePointSegment(const Point &p, const Point &a, const Point &b)
Compute the distance from a point to the segment connecting the points a and b.
Definition distance.hh:135
Geometry::ctype diameter(const Geometry &geo)
Computes the longest distance between points of a geometry.
Definition diameter.hh:26
Corners::value_type center(const Corners &corners)
The center of a given list of corners.
Definition center.hh:24
std::vector< std::pair< int, std::size_t > > intersectingEntities(const Dune::FieldVector< ctype, dimworld > &point, const DistributedBoundingBoxTree< EntitySet > &tree, bool isCartesianGrid=false, bool onlyOwned=true)
Compute all intersections between entities and a point on a distributed tree.
Definition distributedintersectingentities.hh:81
T getParamFromGroup(Args &&... args)
A free function to get a parameter from the parameter tree singleton with a model group.
Definition parameters.hh:149
decltype(auto) gridDiscretization(const T &t, Args &&... args)
The grid discretization.
Definition griddiscretization.hh:65
typename Detail::GridDiscretizationType< T >::type GridDiscretization_t
The grid discretization type of a class exporting it.
Definition griddiscretization.hh:54
Algorithms that finds which geometric entities intersect.
Detect if a point intersects a geometry.
constexpr Surface surface
Definition couplingmanager1d3d_surface.hh:37
Definition adapt.hh:17
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.