GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/multidomain/embedded/couplingmanager1d3d_projection.hh
Date: 2024-05-04 19:09:25
Exec Total Coverage
Lines: 167 198 84.3%
Functions: 12 16 75.0%
Branches: 217 446 48.7%

Line Branch Exec Source
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-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \author Timo Koch
10 * \ingroup EmbeddedCoupling
11 * \brief Coupling manager for low-dimensional domains embedded in the bulk
12 * domain with spatially resolved interface.
13 */
14
15 #ifndef DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_PROJECTION_HH
16 #define DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_PROJECTION_HH
17
18 #include <vector>
19 #include <algorithm>
20 #include <string>
21
22 #include <dune/common/timer.hh>
23 #include <dune/common/exceptions.hh>
24 #include <dune/common/fvector.hh>
25 #include <dune/geometry/quadraturerules.hh>
26
27 #include <dumux/common/tag.hh>
28 #include <dumux/common/properties.hh>
29 #include <dumux/common/math.hh>
30 #include <dumux/common/indextraits.hh>
31
32 #include <dumux/geometry/refinementquadraturerule.hh>
33 #include <dumux/geometry/triangulation.hh>
34 #include <dumux/geometry/grahamconvexhull.hh>
35
36 #include <dumux/multidomain/embedded/couplingmanagerbase.hh>
37 #include <dumux/multidomain/embedded/localrefinementquadrature.hh>
38
39 namespace Dumux {
40
41 // some implementation details of the projection coupling manager
42 namespace Detail {
43
44 /*!
45 * \ingroup EmbeddedCoupling
46 * \brief Segment representation of a 1d network grid
47 *
48 * We use separate segment representation which is fast to iterate over and which
49 * can be used to find the unique mapping from points on coupled bulk surface facets
50 * to the network centerline (i.e. a 1D dof on the centerline).
51 */
52 template <class GridGeometry>
53 2 class SegmentNetwork
54 {
55 using GridView = typename GridGeometry::GridView;
56 using GlobalPosition = typename GridView::template Codim<0>::Geometry::GlobalCoordinate;
57 using GridIndex = typename IndexTraits<GridView>::GridIndex;
58
59 132 struct Segment
60 {
61 GlobalPosition a, b;
62 double r;
63 GridIndex eIdx;
64 };
65
66 public:
67 template <class RadiusFunction>
68 1 SegmentNetwork(const GridGeometry& gg, const RadiusFunction& radiusFunction)
69 1 {
70 1 const auto& gridView = gg.gridView();
71
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 segments_.resize(gridView.size(0));
72
5/6
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
89 for (const auto &element : elements(gridView))
73 {
74
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 const auto geometry = element.geometry();
75 88 const auto eIdx = gg.elementMapper().index(element);
76 88 const auto radius = radiusFunction(eIdx);
77
78 132 segments_[eIdx] = Segment{ geometry.corner(0), geometry.corner(1), radius, eIdx };
79 }
80
81
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
3 std::cout << "-- Extracted " << segments_.size() << " segments.\n";
82 1 }
83
84 //! the segment radius
85 auto radius(std::size_t eIdx) const
86 1644 { return segments_[eIdx].r; }
87
88 //! the segment with index eIdx
89 const auto& segment(std::size_t eIdx) const
90 { return segments_[eIdx]; }
91
92 //! the segments
93 const auto& segments() const
94 { return segments_; }
95
96 //! find the closest segment (the segment from which the signed distance to its surface is the closest to zero)
97 //! \note minDist is a positive distance
98 134892 auto findClosestSegmentSurface(const GlobalPosition& globalPos) const
99 {
100 134892 GridIndex eIdx = 0;
101 134892 double minSignedDist = std::numeric_limits<double>::max();
102
103
4/4
✓ Branch 0 taken 5935248 times.
✓ Branch 1 taken 134892 times.
✓ Branch 2 taken 5935248 times.
✓ Branch 3 taken 134892 times.
6339924 for (const auto &segment : segments_)
104 {
105 5935248 const auto signedDist = computeSignedDistance_(globalPos, segment);
106
2/2
✓ Branch 0 taken 2081694 times.
✓ Branch 1 taken 3853554 times.
5935248 if (signedDist < minSignedDist)
107 {
108 2081694 minSignedDist = signedDist;
109 2081694 eIdx = segment.eIdx;
110 }
111 }
112
113 using std::abs;
114 404676 return std::make_tuple(eIdx, abs(minSignedDist));
115 }
116
117 //! same as overload with taking a position but only look at the segments specified by the index vector
118 template <class IndexRange>
119 auto findClosestSegmentSurface(const GlobalPosition& globalPos,
120 const IndexRange& segIndices) const
121 {
122 GridIndex eIdx = 0;
123 double minSignedDist = std::numeric_limits<double>::max();
124
125 for (const auto index : segIndices)
126 {
127 const auto &segment = segments_[index];
128 const auto signedDist = computeSignedDistance_(globalPos, segment);
129 if (signedDist < minSignedDist)
130 {
131 minSignedDist = signedDist;
132 eIdx = segment.eIdx;
133 }
134 }
135
136 using std::abs;
137 return std::make_tuple(eIdx, abs(minSignedDist));
138 }
139
140 // Compute the projection point of p onto the segment connecting a->b
141 GlobalPosition projectionPointOnSegment(const GlobalPosition& p, std::size_t idx) const
142 {
143 6148 const auto& segment = segments_[idx];
144 3074 return projectionPointOnSegment_(p, segment.a, segment.b);
145 }
146
147 private:
148 // Compute the projection of p onto the segment
149 // returns the closest end point if the orthogonal projection onto the line implied by the segment
150 // is outside the segment
151 5938322 GlobalPosition projectionPointOnSegment_(const GlobalPosition& p, const GlobalPosition& a, const GlobalPosition& b) const
152 {
153 5938322 const auto v = b - a;
154 5938322 const auto w = p - a;
155
156 5938322 const auto proj1 = v*w;
157
2/2
✓ Branch 0 taken 2641205 times.
✓ Branch 1 taken 3297117 times.
5938322 if (proj1 <= 0.0)
158 3297117 return a;
159
160 2641205 const auto proj2 = v.two_norm2();
161
2/2
✓ Branch 0 taken 2368097 times.
✓ Branch 1 taken 273108 times.
2641205 if (proj2 <= proj1)
162 2368097 return b;
163
164 273108 const auto t = proj1 / proj2;
165 273108 auto x = a;
166 273108 x.axpy(t, v);
167 273108 return x;
168 }
169
170 // Compute the signed (!) distance to a cylinder segment surface and the projection point onto the centerline
171 5935248 auto computeSignedDistance_(const GlobalPosition& p, const Segment& segment) const
172 {
173 5935248 const auto projPoint = projectionPointOnSegment_(p, segment.a, segment.b);
174 5935248 return (p-projPoint).two_norm()-segment.r;
175 }
176
177 std::vector<Segment> segments_;
178 };
179
180 /*!
181 * \ingroup EmbeddedCoupling
182 * \brief Get the closest segment for a given surface point
183 * \note The point does not necessarily be exactly on the virtual surface
184 * implied by the networks radius function
185 */
186 template<class Network>
187 class NetworkIndicatorFunction
188 {
189 public:
190 1 NetworkIndicatorFunction(const Network& n)
191 1 : network_(n)
192 {}
193
194 template<class Pos>
195 auto operator()(const Pos& pos) const
196 {
197 44204 const auto& [closestSegmentIdx, _] = network_.findClosestSegmentSurface(pos);
198 44204 return closestSegmentIdx;
199 }
200
201 template <class Pos, class HintContainer>
202 auto operator()(const Pos& pos, const HintContainer& hints) const
203 {
204 const auto& [closestSegmentIdx, _] = network_.findClosestSegmentSurface(pos, hints);
205 return closestSegmentIdx;
206 }
207
208 private:
209 const Network& network_;
210 };
211
212 /*!
213 * \ingroup EmbeddedCoupling
214 * \brief Simple legacy VTK writer for outputting debug data on the coupling interface
215 */
216 class DebugIntersectionVTKOutput
217 {
218 public:
219 void push(const std::array<Dune::FieldVector<double, 3>, 3>& corners, double data)
220 {
221 vtkCells_.emplace_back(std::array<std::size_t, 3>{
222 { vtkPoints_.size(), vtkPoints_.size()+1, vtkPoints_.size()+2 }
223 });
224 for (const auto& c : corners)
225 vtkPoints_.push_back(c);
226 vtkCellData_.push_back(data);
227 }
228
229 void write(const std::string& name)
230 {
231 std::ofstream intersectionFile(name);
232 intersectionFile << "# vtk DataFile Version 2.0\n";
233 intersectionFile << "Really cool intersection data\n";
234 intersectionFile << "ASCII\n";
235 intersectionFile << "DATASET UNSTRUCTURED_GRID\n";
236 intersectionFile << "POINTS " << vtkPoints_.size() << " float\n";
237 for (const auto& p : vtkPoints_)
238 intersectionFile << p[0] << " " << p[1] << " " << p[2] << "\n";
239 intersectionFile << "\nCELLS " << vtkCells_.size() << " " << vtkCells_.size()*4 << "\n";
240 for (const auto& c : vtkCells_)
241 intersectionFile << "3 " << c[0] << " " << c[1] << " " << c[2] << "\n";
242 intersectionFile << "\nCELL_TYPES " << vtkCells_.size() << "\n";
243 for (int i = 0; i < vtkCells_.size(); ++i)
244 intersectionFile << "5\n";
245 intersectionFile << "\nCELL_DATA " << vtkCells_.size() << "\nSCALARS index float\nLOOKUP_TABLE default\n";
246 for (const auto& c : vtkCellData_)
247 intersectionFile << c << "\n";
248 }
249
250 private:
251 std::vector<Dune::FieldVector<double, 3>> vtkPoints_;
252 std::vector<std::array<std::size_t, 3>> vtkCells_;
253 std::vector<double> vtkCellData_;
254 };
255
256 } // end namespace Detail
257
258 namespace Embedded1d3dCouplingMode {
259 struct Projection : public Utility::Tag<Projection> {
260 static std::string name() { return "projection"; }
261 };
262
263 inline constexpr Projection projection{};
264 } // end namespace Embedded1d3dCouplingMode
265
266 // forward declaration
267 template<class MDTraits, class CouplingMode>
268 class Embedded1d3dCouplingManager;
269
270 /*!
271 * \ingroup EmbeddedCoupling
272 * \brief Manages the coupling between bulk elements and lower dimensional elements
273 *
274 * The method is described in detail in the article
275 * "Projection-based resolved interface mixed-dimension method for embedded tubular network systems"
276 * by Timo Koch (2021) available at https://arxiv.org/abs/2106.06358
277 *
278 * The network is represented as a line segment network. The bulk domain explicitly resolves the wall
279 * of the tube network (e.g. blood vessel wall, outer root wall), so this generally requires an unstructured grid.
280 * The coupling term coupling the PDEs on the network and in the bulk domain is integrated over the
281 * coupling surface and each integration point couples to quantities evaluated at the closest point on the graph.
282 * There is a unique mapping from every point on the virtual tube surface to the tube centerline, therefore
283 * this coupling is determined by mapping to the closest point on the virtual surface and then evaluating
284 * the 1D quantity on the mapped centerline point. (The inverse mapping may be non-unique.)
285 *
286 * The original paper also includes an algorithm for computing exact intersection for the surface quadrature
287 * in case of simple straight vessels. However, as a comparison in the paper shows that the suggested
288 * approximate quadrature is exact enough (configured by parameter MixedDimension.Projection.SimplexIntegrationRefine
289 * specifying the number of (adaptive local) virtual refinements of a surface facet for integration), only
290 * the approximate general purpose algorithm is included in this implementation. Only one quadrature point
291 * will be added for each surface element and coupled 1D dof including all contribution evaluated by
292 * local refinement. That means the virtual refinement doesn't increase the number of integration points and
293 * additionally is also optimized by using local adaptive refinement only in the places where the index mapping
294 * to the 1D degree of freedom is still multivalent.
295 *
296 * Coupling sources are implement in terms of point sources at quadrature points to make it easy to reuse code
297 * written for other 1D-3D coupling manager modes.
298 *
299 * This algorithm can be configured by several parameters
300 * (although this is usually not necessary as there are sensible defaults):
301 *
302 * - MixedDimension.Projection.SimplexIntegrationRefine
303 * number of virtual refinement steps to determine coupled surface area
304 * - MixedDimension.Projection.EnableIntersectionOutput
305 * set to true to enable debug VTK output for intersections
306 * - MixedDimension.Projection.EstimateNumberOfPointSources
307 * provide an estimate for the expected number of coupling points for memory allocation
308 * - MixedDimension.Projection.CoupledRadiusFactor
309 * threshold distance in which to search for coupled elements (specified as multiple of radius, default 0.1)
310 * - MixedDimension.Projection.CoupledAngleFactor
311 * angle threshold in which to search for coupled elements (angle in radians from surface normal vector, default 0.3)
312 * - MixedDimension.Projection.ConsiderFacesWithinBoundingBoxCoupled
313 * determines if all 3D boundary facets within the mesh bounding box should be considered as coupling faces
314 * - MixedDimension.Projection.CoupledBoundingBoxShrinkingFactor
315 * if ConsiderFacesWithinBoundingBoxCoupled=true shrink the bounding box in all directions by this factor
316 *
317 */
318 template<class MDTraits>
319 class Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Projection>
320 : public EmbeddedCouplingManagerBase<MDTraits, Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Projection>>
321 {
322 using ThisType = Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Projection>;
323 using ParentType = EmbeddedCouplingManagerBase<MDTraits, ThisType>;
324
325 using Scalar = typename MDTraits::Scalar;
326 using SolutionVector = typename MDTraits::SolutionVector;
327 using PointSourceData = typename ParentType::PointSourceTraits::PointSourceData;
328
329 static constexpr auto bulkIdx = typename MDTraits::template SubDomain<0>::Index();
330 static constexpr auto lowDimIdx = typename MDTraits::template SubDomain<1>::Index();
331
332 // the sub domain type aliases
333 template<std::size_t id> using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
334 template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
335 template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
336 template<std::size_t id> using GridView = typename GridGeometry<id>::GridView;
337 template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity;
338 template<std::size_t id> using GridIndex = typename IndexTraits<GridView<id>>::GridIndex;
339
340 static constexpr int lowDimDim = GridView<lowDimIdx>::dimension;
341 static constexpr int bulkDim = GridView<bulkIdx>::dimension;
342 static constexpr int dimWorld = GridView<bulkIdx>::dimensionworld;
343
344 template<std::size_t id>
345 static constexpr bool isBox()
346 { return GridGeometry<id>::discMethod == DiscretizationMethods::box; }
347
348 using GlobalPosition = typename Element<bulkIdx>::Geometry::GlobalCoordinate;
349
350 using SegmentNetwork = Detail::SegmentNetwork<GridGeometry<lowDimIdx>>;
351
352 public:
353 static constexpr Embedded1d3dCouplingMode::Projection couplingMode{};
354
355
5/10
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
1 using ParentType::ParentType;
356
357 1 void init(std::shared_ptr<Problem<bulkIdx>> bulkProblem,
358 std::shared_ptr<Problem<lowDimIdx>> lowDimProblem,
359 const SolutionVector& curSol)
360 {
361
3/10
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
2 ParentType::init(bulkProblem, lowDimProblem, curSol);
362 1 }
363
364 /* \brief Compute integration point point sources and associated data
365 * This method computes a source term on the explicitly given surface of the network grid
366 * by integrating over the elements of the surface, projecting the integration points onto
367 * the centerlines to evaluate 1d quantities and set point sources (minimum distance projection)
368 *
369 * \param order Unused in this algorithm! (passed from default 1D3D coupling manager)
370 * \param verbose If the source computation is verbose
371 */
372 1 void computePointSourceData(std::size_t order = 1, bool verbose = false)
373 {
374 // initialize the maps
375 // do some logging and profiling
376 1 Dune::Timer watch;
377 2 std::cout << "Initializing the coupling manager (projection)" << std::endl;
378
379 // debug VTK output
380 1 const bool enableIntersectionOutput = getParam<bool>("MixedDimension.Projection.EnableIntersectionOutput", false);
381
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 std::unique_ptr<Detail::DebugIntersectionVTKOutput> vtkCoupledFaces =
382 enableIntersectionOutput ? std::make_unique<Detail::DebugIntersectionVTKOutput>() : nullptr;
383
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
2 std::unique_ptr<Detail::DebugIntersectionVTKOutput> vtkIntersections =
384 enableIntersectionOutput ? std::make_unique<Detail::DebugIntersectionVTKOutput>() : nullptr;
385
386 // temporarily represent the network domain as a list of segments
387 // a more efficient data structure
388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const auto& lowDimProblem = this->problem(lowDimIdx);
389 1 const auto& lowDimFvGridGeometry = lowDimProblem.gridGeometry();
390 1 const auto& lowDimGridView = lowDimFvGridGeometry.gridView();
391
1/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 localAreaFactor_.resize(lowDimGridView.size(0), 0.0);
392 1 const auto radiusFunc = [&](const GridIndex<lowDimIdx> eIdx) {
393 132 return lowDimProblem.spatialParams().radius(eIdx);
394 };
395
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
2 const auto network = SegmentNetwork{
396 lowDimFvGridGeometry, radiusFunc
397 };
398
399 // precompute the vertex indices for efficiency (box method)
400
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 this->precomputeVertexIndices(bulkIdx);
401 1 this->precomputeVertexIndices(lowDimIdx);
402
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const auto& bulkProblem = this->problem(bulkIdx);
404
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const auto& bulkFvGridGeometry = bulkProblem.gridGeometry();
405
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 const auto& bulkGridView = bulkFvGridGeometry.gridView();
406
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 bulkElementMarker_.assign(bulkGridView.size(0), 0);
407
2/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 bulkVertexMarker_.assign(bulkGridView.size(bulkDim), 0);
408
409 // construct a bounding box that may be used to determine coupled faces
410 // the shrinking factor shrinks this bounding box
411
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 static const auto bBoxShrinking
412
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 = getParam<Scalar>("MixedDimension.Projection.CoupledBoundingBoxShrinkingFactor", 1e-2);
413 2 const GlobalPosition threshold(
414 4 bBoxShrinking*( bulkFvGridGeometry.bBoxMax()-bulkFvGridGeometry.bBoxMin() ).two_norm()
415 );
416 2 const auto bBoxMinSmall = bulkFvGridGeometry.bBoxMin() + threshold;
417 2 const auto bBoxMaxSmall = bulkFvGridGeometry.bBoxMax() - threshold;
418 2281 auto insideBBox = [bBoxMin=bBoxMinSmall, bBoxMax=bBoxMaxSmall](const GlobalPosition& point) -> bool
419 {
420 static constexpr Scalar eps_ = 1.0e-7;
421
4/4
✓ Branch 0 taken 2147 times.
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 2147 times.
✓ Branch 3 taken 133 times.
4560 const Scalar eps0 = eps_*(bBoxMax[0] - bBoxMin[0]);
422
4/4
✓ Branch 0 taken 2147 times.
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 2147 times.
✓ Branch 3 taken 133 times.
4560 const Scalar eps1 = eps_*(bBoxMax[1] - bBoxMin[1]);
423
4/4
✓ Branch 0 taken 2147 times.
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 2147 times.
✓ Branch 3 taken 133 times.
4560 const Scalar eps2 = eps_*(bBoxMax[2] - bBoxMin[2]);
424
10/10
✓ Branch 0 taken 2147 times.
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 2147 times.
✓ Branch 3 taken 133 times.
✓ Branch 4 taken 2035 times.
✓ Branch 5 taken 112 times.
✓ Branch 6 taken 2035 times.
✓ Branch 7 taken 112 times.
✓ Branch 8 taken 2035 times.
✓ Branch 9 taken 112 times.
4560 return (bBoxMin[0] - eps0 <= point[0] && point[0] <= bBoxMax[0] + eps0 &&
425
12/12
✓ Branch 0 taken 1914 times.
✓ Branch 1 taken 121 times.
✓ Branch 2 taken 1914 times.
✓ Branch 3 taken 121 times.
✓ Branch 4 taken 1914 times.
✓ Branch 5 taken 121 times.
✓ Branch 6 taken 1776 times.
✓ Branch 7 taken 138 times.
✓ Branch 8 taken 1776 times.
✓ Branch 9 taken 138 times.
✓ Branch 10 taken 1776 times.
✓ Branch 11 taken 138 times.
6105 bBoxMin[1] - eps1 <= point[1] && point[1] <= bBoxMax[1] + eps1 &&
426
14/14
✓ Branch 0 taken 2147 times.
✓ Branch 1 taken 133 times.
✓ Branch 2 taken 1744 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 1744 times.
✓ Branch 5 taken 32 times.
✓ Branch 6 taken 1744 times.
✓ Branch 7 taken 32 times.
✓ Branch 8 taken 286 times.
✓ Branch 9 taken 1458 times.
✓ Branch 10 taken 286 times.
✓ Branch 11 taken 1458 times.
✓ Branch 12 taken 286 times.
✓ Branch 13 taken 1458 times.
7608 bBoxMin[2] - eps2 <= point[2] && point[2] <= bBoxMax[2] + eps2);
427 };
428
429 // setup simplex "quadrature" rule
430
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 static const auto quadSimplexRefineMaxLevel
431
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 = getParam<std::size_t>("MixedDimension.Projection.SimplexIntegrationRefine", 4);
432 2 const auto indicator = Detail::NetworkIndicatorFunction { network };
433
434 // estimate number of point sources for memory allocation
435
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 static const auto estimateNumberOfPointSources
436
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
3 = getParam<std::size_t>("MixedDimension.Projection.EstimateNumberOfPointSources", bulkFvGridGeometry.gridView().size(0));
437
438
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 this->pointSources(bulkIdx).reserve(estimateNumberOfPointSources);
439
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 this->pointSources(lowDimIdx).reserve(estimateNumberOfPointSources);
440
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 this->pointSourceData().reserve(estimateNumberOfPointSources);
441
442 // we go over the bulk surface intersection, check if we are coupled,
443 // and if this is the case, we add integration point sources using
444 // the local simplex refinement quadrature procedure (see paper Koch 2021)
445
8/12
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8224 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 8224 times.
✓ Branch 12 taken 1 times.
✓ Branch 14 taken 8224 times.
✗ Branch 15 not taken.
16449 for (const auto& element : elements(bulkGridView))
446 {
447
2/4
✓ Branch 1 taken 8224 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8224 times.
✗ Branch 5 not taken.
16448 const auto bulkElementIdx = bulkFvGridGeometry.elementMapper().index(element);
448
2/4
✓ Branch 1 taken 8224 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8224 times.
✗ Branch 5 not taken.
16448 const auto bulkGeometry = element.geometry();
449
1/2
✓ Branch 1 taken 8224 times.
✗ Branch 2 not taken.
8224 const auto refElement = Dune::referenceElement(element);
450
3/8
✓ Branch 1 taken 8224 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 41120 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 11 taken 32896 times.
✗ Branch 12 not taken.
90464 for (const auto& intersection : intersections(bulkGridView, element))
451 {
452 // skip inner intersection
453
2/2
✓ Branch 1 taken 2280 times.
✓ Branch 2 taken 30616 times.
32896 if (!intersection.boundary())
454 31384 continue;
455
456 // check if we are close enough to the coupling interface
457
7/8
✓ Branch 1 taken 2280 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1512 times.
✓ Branch 4 taken 768 times.
✓ Branch 5 taken 1512 times.
✓ Branch 6 taken 768 times.
✓ Branch 7 taken 1512 times.
✓ Branch 8 taken 768 times.
2280 const auto [isCoupled, closestSegmentIdx, minDist] = isCoupled_(network, intersection, insideBBox);
458
2/2
✓ Branch 0 taken 1512 times.
✓ Branch 1 taken 768 times.
2280 if (!isCoupled)
459 continue;
460
461 // we found an intersection on the coupling interface: mark as coupled element
462
1/2
✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
1512 bulkElementMarker_[bulkElementIdx] = 1;
463
464
1/2
✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
3024 const auto interfaceGeometry = intersection.geometry();
465
4/4
✓ Branch 0 taken 4536 times.
✓ Branch 1 taken 1512 times.
✓ Branch 2 taken 4536 times.
✓ Branch 3 taken 1512 times.
12096 for (int i = 0; i < interfaceGeometry.corners(); ++i)
466 {
467
1/2
✓ Branch 1 taken 4536 times.
✗ Branch 2 not taken.
4536 const auto localVIdx = refElement.subEntity(intersection.indexInInside(), 1, i, bulkDim);
468
2/4
✓ Branch 1 taken 4536 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4536 times.
✗ Branch 5 not taken.
9072 const auto vIdx = bulkFvGridGeometry.vertexMapper().subIndex(element, localVIdx, bulkDim);
469 9072 bulkVertexMarker_[vIdx] = 1;
470 }
471
472 // debug graphical intersection output
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1512 times.
1512 if (enableIntersectionOutput)
474 vtkCoupledFaces->push({
475 { interfaceGeometry.corner(0), interfaceGeometry.corner(1), interfaceGeometry.corner(2)}
476 }, closestSegmentIdx);
477
478
2/6
✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1512 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4536 const auto quadSimplex = LocalRefinementSimplexQuadrature{ interfaceGeometry, quadSimplexRefineMaxLevel, indicator };
479
480 // iterate over all quadrature points (children simplices)
481
4/4
✓ Branch 0 taken 2252 times.
✓ Branch 1 taken 1512 times.
✓ Branch 2 taken 2252 times.
✓ Branch 3 taken 1512 times.
6788 for (const auto& qp : quadSimplex)
482 {
483 4504 const auto surfacePoint = interfaceGeometry.global(qp.position());
484 2252 const auto closestSegmentIdx = qp.indicator();
485 2252 const auto projPoint = network.projectionPointOnSegment(surfacePoint, closestSegmentIdx);
486
487
1/2
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
2252 addPointSourceAtIP_(bulkGeometry, interfaceGeometry, qp, bulkElementIdx, closestSegmentIdx, surfacePoint, projPoint);
488
1/2
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
2252 addStencilEntries_(bulkElementIdx, closestSegmentIdx);
489 }
490 }
491 }
492
493 // make stencils unique
494 using namespace Dune::Hybrid;
495 9 forEach(integralRange(Dune::index_constant<2>{}), [&](const auto domainIdx)
496 {
497
4/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1456 times.
✓ Branch 4 taken 1 times.
1508 for (auto&& stencil : this->couplingStencils(domainIdx))
498 {
499 4500 std::sort(stencil.second.begin(), stencil.second.end());
500 3000 stencil.second.erase(
501 4500 std::unique(stencil.second.begin(), stencil.second.end()),
502 stencil.second.end()
503 );
504 }
505 });
506
507 // debug VTK output
508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (enableIntersectionOutput)
509 vtkCoupledFaces->write("coupledfaces.vtk");
510
511 // compare theoretical cylinder surface to actual discrete surface
512 // of the coupled bulk interface facets
513
5/6
✓ Branch 1 taken 44 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
89 for (const auto& element : elements(lowDimGridView))
514 {
515
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 const auto length = element.geometry().volume();
516 88 const auto eIdx = lowDimFvGridGeometry.elementMapper().index(element);
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 const auto radius = this->problem(lowDimIdx).spatialParams().radius(eIdx);
518 44 const auto cylinderSurface = 2.0*M_PI*radius*length;
519 44 localAreaFactor_[eIdx] /= cylinderSurface;
520 44 localAreaFactor_[eIdx] -= 1.0;
521 }
522
523
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 this->pointSources(bulkIdx).shrink_to_fit();
524
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 this->pointSources(lowDimIdx).shrink_to_fit();
525
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 this->pointSourceData().shrink_to_fit();
526
527
4/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
5 std::cout << "-- Coupling at " << this->pointSourceData().size() << " integration points." << std::endl;
528
3/6
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
3 std::cout << "-- Finished initialization after " << watch.elapsed() << " seconds." << std::endl;
529 1 }
530
531 /*!
532 * \brief Methods to be accessed by the subproblems
533 */
534 // \{
535
536 //! Return a reference to the bulk problem
537 Scalar radius(std::size_t id) const
538 {
539 const auto& data = this->pointSourceData()[id];
540 return this->problem(lowDimIdx).spatialParams().radius(data.lowDimElementIdx());
541 }
542
543 // \}
544
545 //! Marker that is non-zero for bulk elements that are coupled
546 const std::vector<int>& bulkElementMarker() const
547
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 { return bulkElementMarker_; }
548
549 //! Marker that is non-zero for bulk vertices that belong to coupled surface facets
550 const std::vector<int>& bulkVertexMarker() const
551 { return bulkVertexMarker_; }
552
553 //! For each 1D element, the ratio of associated discrete bulk surface area
554 //! to the theoretical bulk surface area resulting from assuming a cylinder-shaped segment
555 //! (excluding cylinder caps).
556 const std::vector<Scalar>& localAreaFactor() const
557 { return localAreaFactor_; }
558
559 private:
560 std::vector<int> bulkElementMarker_, bulkVertexMarker_;
561 std::vector<double> localAreaFactor_;
562
563 // add a point source containing all data needed to evaluate
564 // the coupling source term from both domains
565 template<class BulkGeometry, class InterfaceGeometry, class QP>
566 2252 void addPointSourceAtIP_(const BulkGeometry& bulkGeometry,
567 const InterfaceGeometry& ifGeometry,
568 const QP& qp,
569 const GridIndex<bulkIdx> bulkElementIdx,
570 const GridIndex<lowDimIdx> lowDimElementIdx,
571 const GlobalPosition& surfacePoint,
572 const GlobalPosition& projPoint)
573 {
574 // add a new point source id (for the associated data)
575 2252 const auto id = this->idCounter_++;
576
577 // add a bulk point source
578 2252 const auto qpweight = qp.weight();
579 4504 const auto integrationElement = ifGeometry.integrationElement(qp.position());
580 4504 this->pointSources(bulkIdx).emplace_back(surfacePoint, id, qpweight, integrationElement, bulkElementIdx);
581
582 // find the corresponding projection point and 1d element
583 4504 this->pointSources(lowDimIdx).emplace_back(projPoint, id, qpweight, integrationElement, lowDimElementIdx);
584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2252 times.
2252 localAreaFactor_[lowDimElementIdx] += qpweight*integrationElement;
585
586
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2252 times.
2252 const auto& bulkFvGridGeometry = this->problem(bulkIdx).gridGeometry();
587
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2252 times.
✓ Branch 3 taken 2252 times.
✗ Branch 4 not taken.
2252 const auto& lowDimFvGridGeometry = this->problem(lowDimIdx).gridGeometry();
588
589 // pre compute additional data used for the evaluation of
590 // the actual solution dependent source term
591
1/2
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
4504 PointSourceData psData;
592
593 if constexpr (isBox<lowDimIdx>())
594 {
595 std::vector<Dune::FieldVector<Scalar, 1> > shapeValues;
596 const auto lowDimGeometry = this->problem(lowDimIdx).gridGeometry().element(lowDimElementIdx).geometry();
597 this->getShapeValues(lowDimIdx, lowDimFvGridGeometry, lowDimGeometry, projPoint, shapeValues);
598 psData.addLowDimInterpolation(shapeValues, this->vertexIndices(lowDimIdx, lowDimElementIdx), lowDimElementIdx);
599 }
600 else
601 {
602
1/2
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
2252 psData.addLowDimInterpolation(lowDimElementIdx);
603 }
604
605 if constexpr (isBox<bulkIdx>())
606 {
607
1/4
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2252 std::vector<Dune::FieldVector<Scalar, 1> > shapeValues;
608
1/2
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
2252 this->getShapeValues(bulkIdx, bulkFvGridGeometry, bulkGeometry, surfacePoint, shapeValues);
609
3/6
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2252 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2252 times.
✗ Branch 7 not taken.
6756 psData.addBulkInterpolation(shapeValues, this->vertexIndices(bulkIdx, bulkElementIdx), bulkElementIdx);
610 }
611 else
612 {
613 psData.addBulkInterpolation(bulkElementIdx);
614 }
615
616 // publish point source data in the global vector
617
2/4
✓ Branch 1 taken 2252 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2252 times.
✗ Branch 5 not taken.
4504 this->pointSourceData().emplace_back(std::move(psData));
618 2252 }
619
620 2252 void addStencilEntries_(const GridIndex<bulkIdx> bulkElementIdx, const GridIndex<lowDimIdx> lowDimElementIdx)
621 {
622 // export the bulk coupling stencil
623 if constexpr (isBox<lowDimIdx>())
624 {
625 const auto& vertices = this->vertexIndices(lowDimIdx, lowDimElementIdx);
626 this->couplingStencils(bulkIdx)[bulkElementIdx].insert(
627 this->couplingStencils(bulkIdx)[bulkElementIdx].end(),
628 vertices.begin(), vertices.end()
629 );
630
631 }
632 else
633 {
634 4504 this->couplingStencils(bulkIdx)[bulkElementIdx].push_back(lowDimElementIdx);
635 }
636
637 // export the lowdim coupling stencil
638 if constexpr (isBox<bulkIdx>())
639 {
640 2252 const auto& vertices = this->vertexIndices(bulkIdx, bulkElementIdx);
641 4504 this->couplingStencils(lowDimIdx)[lowDimElementIdx].insert(
642 4504 this->couplingStencils(lowDimIdx)[lowDimElementIdx].end(),
643 vertices.begin(), vertices.end()
644 );
645 }
646 else
647 {
648 this->couplingStencils(lowDimIdx)[lowDimElementIdx].push_back(bulkElementIdx);
649 }
650 2252 }
651
652 template<class BoundaryIntersection, class BBoxCheck>
653 2280 auto isCoupled_(const SegmentNetwork& network, const BoundaryIntersection& intersection, const BBoxCheck& insideBBox) const
654 {
655
1/2
✓ Branch 2 taken 2280 times.
✗ Branch 3 not taken.
2280 const auto center = intersection.geometry().center();
656
4/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2279 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2279 times.
2280 const auto [eIdx, minDist] = network.findClosestSegmentSurface(center);
657
658 // all elements in the bounding box are coupled if this is enabled
659 // this is a simplified check that can be enabled for box-shaped domains
660
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2279 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
2280 static const bool considerBBox = getParam<bool>("MixedDimension.Projection.ConsiderFacesWithinBoundingBoxCoupled", false);
661
3/4
✓ Branch 0 taken 2280 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1458 times.
✓ Branch 3 taken 822 times.
2280 if (considerBBox && insideBBox(center))
662 1458 return std::make_tuple(true, eIdx, minDist);
663
664 // general coupling face detection algorithm:
665 // if the distance is under a certain threshold we are coupled (and the angle is not very big)
666
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 821 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
822 static const auto rFactor = getParam<Scalar>("MixedDimension.Projection.CoupledRadiusFactor", 0.1);
667
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 821 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
822 static const auto spFactor = getParam<Scalar>("MixedDimension.Projection.CoupledAngleFactor", 0.3);
668 822 const auto projPoint = network.projectionPointOnSegment(center, eIdx);
669 822 const auto distVec = projPoint - center;
670
5/6
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 768 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 768 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 54 times.
1698 const bool isCoupled = minDist < rFactor * network.radius(eIdx) && distVec * intersection.centerUnitOuterNormal() > distVec.two_norm() * spFactor;
671 822 return std::make_tuple(isCoupled, eIdx, minDist);
672 }
673 };
674
675 //! we support multithreaded assembly
676 template<class MDTraits>
677 struct CouplingManagerSupportsMultithreadedAssembly<Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Projection>>
678 : public std::true_type {};
679
680 } // end namespace Dumux
681
682 #endif
683