GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/test/multidomain/embedded/1d3d/root_soil_benchmark/couplingmanager.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 150 171 87.7%
Functions: 13 20 65.0%
Branches: 150 316 47.5%

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 * \ingroup EmbeddedCoupling
10 * \brief Coupling manager for 1d-3d mixed-dimension method with distributed sources
11 */
12 #ifndef DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_KERNEL_ROOTSOIL_HH
13 #define DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_KERNEL_ROOTSOIL_HH
14
15 #include <random>
16 #include <vector>
17 #include <array>
18 #include <memory>
19 #include <type_traits>
20 #include <unordered_map>
21
22 #include <dune/common/timer.hh>
23 #include <dune/common/fvector.hh>
24 #include <dune/common/exceptions.hh>
25 #include <dune/geometry/quadraturerules.hh>
26 #include <dune/common/std/type_traits.hh>
27
28 #include <dumux/common/properties.hh>
29 #include <dumux/common/math.hh>
30 #include <dumux/common/indextraits.hh>
31 #include <dumux/geometry/distance.hh>
32
33 #include <dumux/multidomain/embedded/pointsourcedata.hh>
34 #include <dumux/multidomain/embedded/integrationpointsource.hh>
35 #include <dumux/multidomain/embedded/couplingmanagerbase.hh>
36 #include <dumux/multidomain/embedded/circlepoints.hh>
37 #include <dumux/multidomain/embedded/extendedsourcestencil.hh>
38 #include <dumux/multidomain/embedded/cylinderintegration.hh>
39
40 #include <dumux/multidomain/embedded/couplingmanager1d3d.hh>
41
42 namespace Dumux {
43
44 /*!
45 * \ingroup EmbeddedCoupling
46 * \brief Coupling manager for 1d-3d mixed-dimension method with distributed sources
47 * \tparam MDTraits the multidomain traits
48 * \tparam CouplingReconstruction the interface reconstruction operator
49 *
50 * Implements the coupling method described in Koch et al (2022), https://doi.org/10.1016/j.jcp.2021.110823
51 */
52 template<class MDTraits, class CouplingReconstruction>
53 class CouplingManagerRootSoilKernel
54 : public EmbeddedCouplingManagerBase<MDTraits, CouplingManagerRootSoilKernel<MDTraits, CouplingReconstruction>>
55 {
56 using ThisType = CouplingManagerRootSoilKernel<MDTraits, CouplingReconstruction>;
57 using ParentType = EmbeddedCouplingManagerBase<MDTraits, ThisType>;
58 using Scalar = typename MDTraits::Scalar;
59 using SolutionVector = typename MDTraits::SolutionVector;
60 using PointSourceData = typename ParentType::PointSourceTraits::PointSourceData;
61
62 static constexpr auto bulkIdx_ = typename MDTraits::template SubDomain<0>::Index();
63 static constexpr auto lowDimIdx_ = typename MDTraits::template SubDomain<1>::Index();
64
65 // the sub domain type aliases
66 template<std::size_t id> using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
67 template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
68 template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
69 template<std::size_t id> using GridView = typename GridGeometry<id>::GridView;
70 template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity;
71 template<std::size_t id> using GridIndex = typename IndexTraits<GridView<id>>::GridIndex;
72
73 using GlobalPosition = typename Element<bulkIdx_>::Geometry::GlobalCoordinate;
74
75 template<std::size_t id>
76 static constexpr bool isBox()
77 { return GridGeometry<id>::discMethod == DiscretizationMethods::box; }
78
79 static_assert(!isBox<bulkIdx_>() && !isBox<lowDimIdx_>(), "The kernel coupling method is only implemented for the tpfa method");
80 static_assert(Dune::Capabilities::isCartesian<typename GridView<bulkIdx_>::Grid>::v, "The kernel coupling method is only implemented for structured grids");
81
82 static constexpr int bulkDim = GridView<bulkIdx_>::dimension;
83 static constexpr int lowDimDim = GridView<lowDimIdx_>::dimension;
84 static constexpr int dimWorld = GridView<bulkIdx_>::dimensionworld;
85
86 // detect if a class (the spatial params) has a kernelWidthFactor() function
87 template <typename T, typename ...Ts>
88 using VariableKernelWidthDetector = decltype(std::declval<T>().kernelWidthFactor(std::declval<Ts>()...));
89
90 template<class T, typename ...Args>
91 static constexpr bool hasKernelWidthFactor()
92 { return Dune::Std::is_detected<VariableKernelWidthDetector, T, Args...>::value; }
93
94 template <typename T, typename ...Ts>
95 using VariableMinKernelRadDetector = decltype(std::declval<T>().minKernelRadius(std::declval<Ts>()...));
96
97 template<class T, typename ...Args>
98 static constexpr bool hasMinKernelRadius()
99 { return Dune::Std::is_detected<VariableMinKernelRadDetector, T, Args...>::value; }
100
101 using Reconstruction = CouplingReconstruction;
102
103 public:
104 using ParentType::ParentType;
105
106 static constexpr auto bulkIdx = typename MDTraits::template SubDomain<0>::Index();
107 static constexpr auto lowDimIdx = typename MDTraits::template SubDomain<1>::Index();
108 static constexpr auto couplingMode = Embedded1d3dCouplingMode::Kernel{};
109
110 1 void init(std::shared_ptr<Problem<bulkIdx>> bulkProblem,
111 std::shared_ptr<Problem<lowDimIdx>> lowDimProblem,
112 std::shared_ptr<Reconstruction> reconstruction,
113 const SolutionVector& curSol)
114 {
115
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);
116 1 reconstruction_ = reconstruction;
117
118 1 computeLowDimVolumeFractions();
119
120 3 const auto refinement = getParamFromGroup<int>(bulkProblem->paramGroup(), "Grid.Refinement", 0);
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (refinement > 0)
122 DUNE_THROW(Dune::NotImplemented, "The current intersection detection may likely fail for refined grids.");
123 1 }
124
125 /*!
126 * \brief extend the jacobian pattern of the diagonal block of domain i
127 * by those entries that are not already in the uncoupled pattern
128 * \note Such additional dependencies can arise from the coupling, e.g. if a coupling source
129 * term depends on a non-local average of a quantity of the same domain
130 */
131 template<std::size_t id, class JacobianPattern>
132 void extendJacobianPattern(Dune::index_constant<id> domainI, JacobianPattern& pattern) const
133 {
134 extendedSourceStencil_.extendJacobianPattern(*this, domainI, pattern);
135 }
136
137 /*!
138 * \brief evaluate additional derivatives of the element residual of a domain with respect
139 * to dofs in the same domain that are not in the regular stencil (per default this is not the case)
140 * \note Such additional dependencies can arise from the coupling, e.g. if a coupling source
141 * term depends on a non-local average of a quantity of the same domain
142 * \note This is the same for box and cc
143 */
144 template<std::size_t i, class LocalAssemblerI, class JacobianMatrixDiagBlock, class GridVariables>
145 void evalAdditionalDomainDerivatives(Dune::index_constant<i> domainI,
146 const LocalAssemblerI& localAssemblerI,
147 const typename LocalAssemblerI::LocalResidual::ElementResidualVector&,
148 JacobianMatrixDiagBlock& A,
149 GridVariables& gridVariables)
150 {
151 5283450 extendedSourceStencil_.evalAdditionalDomainDerivatives(*this, domainI, localAssemblerI, A, gridVariables);
152 }
153
154 /* \brief Compute integration point point sources and associated data
155 *
156 * This method uses grid glue to intersect the given grids. Over each intersection
157 * we later need to integrate a source term. This method places point sources
158 * at each quadrature point and provides the point source with the necessary
159 * information to compute integrals (quadrature weight and integration element)
160 * \param order The order of the quadrature rule for integration of sources over an intersection
161 * \param verbose If the point source computation is verbose
162 */
163 1 void computePointSourceData(std::size_t order = 1, bool verbose = false)
164 {
165 // initialize the maps
166 // do some logging and profiling
167 1 Dune::Timer watch;
168 2 std::cout << "[coupling] Initializing the integration point source data structures..." << std::endl;
169
170 // prepare the internal data structures
171 1 prepareDataStructures_();
172 2 std::cout << "[coupling] Resized data structures." << std::endl;
173
174
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 const auto& bulkGridGeometry = this->problem(bulkIdx).gridGeometry();
175
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 const auto& lowDimGridGeometry = this->problem(lowDimIdx).gridGeometry();
176
177 // generate a bunch of random vectors and values for
178 // Monte-carlo integration on the cylinder defined by line and radius
179
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 static const double characteristicRelativeLength = getParam<double>("MixedDimension.KernelIntegrationCRL", 0.1);
180 1 EmbeddedCoupling::CylinderIntegration<Scalar> cylIntegration(characteristicRelativeLength, 1);
181
182
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 static const bool writeIntegrationPointsToFile = getParam<bool>("MixedDimension.WriteIntegrationPointsToFile", false);
183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (writeIntegrationPointsToFile)
184 {
185 std::ofstream ipPointFile("kernel_points.log", std::ios::trunc);
186 ipPointFile << "x,y,z\n";
187 std::cout << "[coupling] Initialized kernel_points.log." << std::endl;
188 }
189
190
4/4
✓ Branch 0 taken 1329 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1329 times.
✓ Branch 3 taken 1 times.
1332 for (const auto& is : intersections(this->glue()))
191 {
192 // all inside elements are identical...
193 1329 const auto& inside = is.targetEntity(0);
194 // get the intersection geometry for integrating over it
195
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 const auto intersectionGeometry = is.geometry();
196 // get the Gaussian quadrature rule for the local intersection
197 2658 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
198
199 // for each intersection integrate kernel and add:
200 // * 1d: a new point source
201 // * 3d: a new kernel volume source
202
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1329 times.
✓ Branch 3 taken 1329 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1329 times.
✗ Branch 7 not taken.
1329 const auto radius = this->problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
203
3/6
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1329 times.
✗ Branch 8 not taken.
3987 const auto kernelWidthFactor = kernelWidthFactor_(this->problem(lowDimIdx).spatialParams(), lowDimElementIdx);
204
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1329 times.
✓ Branch 3 taken 1329 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1329 times.
✗ Branch 7 not taken.
1329 const auto minKernelRadius = minKernelRadius_(this->problem(lowDimIdx).spatialParams(), lowDimElementIdx);
205 using std::max;
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1329 times.
1329 const auto kernelWidth = max(kernelWidthFactor*radius, minKernelRadius);
207 1329 const auto a = intersectionGeometry.corner(0);
208 1329 const auto b = intersectionGeometry.corner(1);
209
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 cylIntegration.setGeometry(a, b, kernelWidth);
210
211 // we can have multiple 3d elements per 1d intersection, for evaluation taking one of them should be fine
212
4/4
✓ Branch 0 taken 1329 times.
✓ Branch 1 taken 1329 times.
✓ Branch 2 taken 1329 times.
✓ Branch 3 taken 1329 times.
3987 for (int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
213 {
214 // compute source id
215 // for each id we have
216 // (1) a point source for the 1d domain
217 // (2) a bulk source weight for the each element in the 3d domain (the fraction of the total source/sink for the 1d element with the point source)
218 // TODO: i.e. one integration of the kernel should be enough (for each of the weights it's weight/embeddings)
219 // (3) the flux scaling factor for each outside element, i.e. each id
220 1329 const auto id = this->idCounter_++;
221
222 // compute the weights for the bulk volume sources
223
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 const auto& outside = is.domainEntity(outsideIdx);
224 2658 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
225
2/4
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
2658 const auto surfaceFactor = computeBulkSource(intersectionGeometry, radius, kernelWidth, id, lowDimElementIdx, bulkElementIdx, cylIntegration, is.numDomainNeighbors());
226
227 // place a point source at the intersection center
228 1329 const auto center = intersectionGeometry.center();
229
3/6
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1329 times.
✗ Branch 8 not taken.
3987 this->pointSources(lowDimIdx).emplace_back(center, id, surfaceFactor, intersectionGeometry.volume(), lowDimElementIdx);
230
4/8
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1329 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1329 times.
✗ Branch 11 not taken.
5316 this->pointSources(lowDimIdx).back().setEmbeddings(is.numDomainNeighbors());
231
232 // pre compute additional data used for the evaluation of
233 // the actual solution dependent source term
234
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
2658 PointSourceData psData;
235
236 // lowdim interpolation (evaluate at center)
237 if constexpr (isBox<lowDimIdx>())
238 {
239 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
240 const auto lowDimGeometry = this->problem(lowDimIdx).gridGeometry().element(lowDimElementIdx).geometry();
241 ShapeValues shapeValues;
242 this->getShapeValues(lowDimIdx, this->problem(lowDimIdx).gridGeometry(), lowDimGeometry, center, shapeValues);
243 psData.addLowDimInterpolation(shapeValues, this->vertexIndices(lowDimIdx, lowDimElementIdx), lowDimElementIdx);
244 }
245 else
246 {
247
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 psData.addLowDimInterpolation(lowDimElementIdx);
248 }
249
250 // bulk interpolation (evaluate at center)
251 if constexpr (isBox<bulkIdx>())
252 {
253 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
254 const auto bulkGeometry = this->problem(bulkIdx).gridGeometry().element(bulkElementIdx).geometry();
255 ShapeValues shapeValues;
256 this->getShapeValues(bulkIdx, this->problem(bulkIdx).gridGeometry(), bulkGeometry, center, shapeValues);
257 psData.addBulkInterpolation(shapeValues, this->vertexIndices(bulkIdx, bulkElementIdx), bulkElementIdx);
258 }
259 else
260 {
261
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 psData.addBulkInterpolation(bulkElementIdx);
262 }
263
264 // publish point source data in the global vector
265
2/4
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
2658 this->pointSourceData().emplace_back(std::move(psData));
266
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 const auto factor = getParam<Scalar>("Problem.Factor", 1.0);
267
1/2
✓ Branch 2 taken 1329 times.
✗ Branch 3 not taken.
3987 const auto avgMinDist = factor*distancePointGeometry_(0.5*(a+b), outside.geometry());
268 2658 this->averageDistanceToBulkCell().push_back(avgMinDist);
269
270 // export the lowdim coupling stencil
271 // we insert all vertices / elements and make it unique later
272 if constexpr (isBox<bulkIdx>())
273 {
274 const auto& vertices = this->vertexIndices(bulkIdx, bulkElementIdx);
275 this->couplingStencils(lowDimIdx)[lowDimElementIdx].insert(this->couplingStencils(lowDimIdx)[lowDimElementIdx].end(),
276 vertices.begin(), vertices.end());
277 }
278 else
279 {
280
3/6
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1329 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1329 times.
✗ Branch 8 not taken.
2658 this->couplingStencils(lowDimIdx)[lowDimElementIdx].push_back(bulkElementIdx);
281 }
282 }
283 }
284
285 // make the stencils unique
286 1 makeUniqueStencil_();
287
288
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
3 if (!this->pointSources(bulkIdx).empty())
289 DUNE_THROW(Dune::InvalidStateException, "Kernel method shouldn't have point sources in the bulk domain but only volume sources!");
290
291
2/4
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
3 std::cout << "[coupling] Finished preparing manager in " << watch.elapsed() << " seconds." << std::endl;
292 1 }
293
294 //! Compute the low dim volume fraction in the bulk domain cells
295 1 void computeLowDimVolumeFractions()
296 {
297 // resize the storage vector
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 lowDimVolumeInBulkElement_.resize(this->gridView(bulkIdx).size(0));
299 // get references to the grid geometries
300
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 const auto& lowDimGridGeometry = this->problem(lowDimIdx).gridGeometry();
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const auto& bulkGridGeometry = this->problem(bulkIdx).gridGeometry();
302
303 // compute the low dim volume fractions
304
4/4
✓ Branch 0 taken 1329 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1329 times.
✓ Branch 3 taken 1 times.
1332 for (const auto& is : intersections(this->glue()))
305 {
306 // all inside elements are identical...
307 1329 const auto& inside = is.targetEntity(0);
308 1329 const auto intersectionGeometry = is.geometry();
309 2658 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
310
311 // compute the volume the low-dim domain occupies in the bulk domain if it were full-dimensional
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1329 times.
1329 const auto radius = this->problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
313
4/4
✓ Branch 0 taken 1329 times.
✓ Branch 1 taken 1329 times.
✓ Branch 2 taken 1329 times.
✓ Branch 3 taken 1329 times.
3987 for (int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
314 {
315 1329 const auto& outside = is.domainEntity(outsideIdx);
316 2658 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
317 3987 lowDimVolumeInBulkElement_[bulkElementIdx] += intersectionGeometry.volume()*M_PI*radius*radius;
318 }
319 }
320 1 }
321
322 //! return the average distance to the coupled bulk cell center
323 Scalar averageDistance(std::size_t id) const
324 309506562 { return averageDistanceToBulkCell_[id]; }
325
326 //! Return the local radius at an integration point with identifier id
327 154753281 Scalar radius(std::size_t id) const
328 {
329
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 154753281 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 154753281 times.
309506562 const auto& data = this->pointSourceData()[id];
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 154753281 times.
154753281 return this->problem(lowDimIdx).spatialParams().radius(data.lowDimElementIdx());
331 }
332
333 //! Return the local radial permeability at an integration point with identifier id
334 150023814 Scalar Kr(std::size_t id) const
335 {
336
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 150023814 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 150023814 times.
300047628 const auto& data = this->pointSourceData()[id];
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150023814 times.
150023814 return this->problem(lowDimIdx).spatialParams().Kr(data.lowDimElementIdx());
338 }
339
340 //! The volume the lower dimensional domain occupies in the bulk domain element
341 // For one-dimensional low dim domain we assume radial tubes
342 Scalar lowDimVolume(const Element<bulkIdx>& element) const
343 {
344 const auto eIdx = this->problem(bulkIdx).gridGeometry().elementMapper().index(element);
345 return lowDimVolumeInBulkElement_[eIdx];
346 }
347
348 //! The volume fraction the lower dimensional domain occupies in the bulk domain element
349 // For one-dimensional low dim domain we assume radial tubes
350 Scalar lowDimVolumeFraction(const Element<bulkIdx>& element) const
351 {
352 const auto totalVolume = element.geometry().volume();
353 return lowDimVolume(element) / totalVolume;
354 }
355
356 //! return all source ids for a bulk elements
357 const std::vector<std::size_t>& bulkSourceIds(GridIndex<bulkIdx> eIdx, int scvIdx = 0) const
358 64209819 { return bulkSourceIds_[eIdx][scvIdx]; }
359
360 //! return all source ids for a bulk elements
361 const std::vector<Scalar>& bulkSourceWeights(GridIndex<bulkIdx> eIdx, int scvIdx = 0) const
362 64209819 { return bulkSourceWeights_[eIdx][scvIdx]; }
363
364 const Reconstruction& reconstruction() const
365 309506562 { return *reconstruction_; }
366
367 private:
368
369 std::vector<Scalar> averageDistanceToBulkCell_;
370
371 //! Return reference to average distances to bulk cell
372 std::vector<Scalar>& averageDistanceToBulkCell()
373
1/2
✓ Branch 1 taken 1329 times.
✗ Branch 2 not taken.
1329 { return averageDistanceToBulkCell_; }
374
375 //! compute the kernel distributed sources and add stencils
376 template<class Line, class CylIntegration>
377 1329 Scalar computeBulkSource(const Line& line, const Scalar radius, const Scalar kernelWidth,
378 std::size_t id, GridIndex<lowDimIdx> lowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx,
379 const CylIntegration& cylIntegration, int embeddings)
380 {
381 // Monte-carlo integration on the cylinder defined by line and radius
382
6/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1328 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
1329 static const auto bulkParamGroup = this->problem(bulkIdx).paramGroup();
383
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1328 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1329 static const auto min = getParamFromGroup<GlobalPosition>(bulkParamGroup, "Grid.LowerLeft");
384
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1328 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1329 static const auto max = getParamFromGroup<GlobalPosition>(bulkParamGroup, "Grid.UpperRight");
385
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1328 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1329 static const auto cells = getParamFromGroup<std::array<int, bulkDim>>(bulkParamGroup, "Grid.Cells");
386 1329 const auto cylSamples = cylIntegration.size();
387 1329 const auto& a = line.corner(0);
388 1329 const auto& b = line.corner(1);
389
390 // optionally write debugging / visual output of the integration points
391
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1328 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1329 static const auto writeIntegrationPointsToFile = getParam<bool>("MixedDimension.WriteIntegrationPointsToFile", false);
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1329 times.
1329 if (writeIntegrationPointsToFile)
393 {
394 std::ofstream ipPointFile("kernel_points.log", std::ios::app);
395 for (int i = 0; i < cylSamples; ++i)
396 {
397 const auto& point = cylIntegration.integrationPoint(i);
398 if (const bool hasIntersection = intersectsPointBoundingBox(point, min, max); hasIntersection)
399 ipPointFile << point[0] << "," << point[1] << "," << point[2] << '\n';
400 }
401 }
402
403 Scalar integral = 0.0;
404
2/2
✓ Branch 0 taken 410661 times.
✓ Branch 1 taken 1329 times.
411990 for (int i = 0; i < cylSamples; ++i)
405 {
406 410661 const auto& point = cylIntegration.integrationPoint(i);
407 // TODO: below only works for Cartesian grids with ijk numbering (e.g. level 0 YaspGrid (already fails when refined))
408 // more general is the bounding box tree solution which always works, however it's much slower
409 //const auto bulkIndices = intersectingEntities(point, this->problem(bulkIdx).gridGeometry().boundingBoxTree(), true);
410
2/2
✓ Branch 1 taken 410249 times.
✓ Branch 2 taken 412 times.
410661 if (const bool hasIntersection = intersectsPointBoundingBox(point, min, max); hasIntersection)
411 {
412 410249 const auto bulkElementIdx = intersectingEntityCartesianGrid(point, min, max, cells);
413
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 410249 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 410249 times.
410249 assert(bulkElementIdx < this->problem(bulkIdx).gridGeometry().gridView().size(0));
414
2/2
✓ Branch 1 taken 409957 times.
✓ Branch 2 taken 292 times.
410249 const auto localWeight = evalConstKernel_(a, b, point, radius, kernelWidth)*cylIntegration.integrationElement(i)/Scalar(embeddings);
415 410249 integral += localWeight;
416
14/14
✓ Branch 0 taken 409957 times.
✓ Branch 1 taken 292 times.
✓ Branch 2 taken 409957 times.
✓ Branch 3 taken 292 times.
✓ Branch 4 taken 409957 times.
✓ Branch 5 taken 292 times.
✓ Branch 6 taken 409957 times.
✓ Branch 7 taken 292 times.
✓ Branch 8 taken 405451 times.
✓ Branch 9 taken 4506 times.
✓ Branch 10 taken 405451 times.
✓ Branch 11 taken 4506 times.
✓ Branch 12 taken 405451 times.
✓ Branch 13 taken 4506 times.
1640996 if (!bulkSourceIds_[bulkElementIdx][0].empty() && id == bulkSourceIds_[bulkElementIdx][0].back())
417 {
418 1621804 bulkSourceWeights_[bulkElementIdx][0].back() += localWeight;
419 }
420 else
421 {
422 9596 bulkSourceIds_[bulkElementIdx][0].emplace_back(id);
423 14394 bulkSourceWeights_[bulkElementIdx][0].emplace_back(localWeight);
424 4798 addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx);
425 }
426 }
427 }
428
429 // the surface factor (which fraction of the source is inside the domain and needs to be considered)
430 1329 const auto length = (a-b).two_norm()/Scalar(embeddings);
431 1329 return integral/length;
432 }
433
434 1 void prepareDataStructures_()
435 {
436 // clear all internal members like pointsource vectors and stencils
437 // initializes the point source id counter
438 1 this->clear();
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 bulkSourceIds_.clear();
440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 bulkSourceWeights_.clear();
441 2 extendedSourceStencil_.stencil().clear();
442
443 // precompute the vertex indices for efficiency for the box method
444 1 this->precomputeVertexIndices(bulkIdx);
445 1 this->precomputeVertexIndices(lowDimIdx);
446
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 bulkSourceIds_.resize(this->gridView(bulkIdx).size(0));
448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 bulkSourceWeights_.resize(this->gridView(bulkIdx).size(0));
449
450 // intersect the bounding box trees
451 1 this->glueGrids();
452
453 // reserve memory for data
454 4 this->pointSourceData().reserve(this->glue().size());
455 3 this->averageDistanceToBulkCell().reserve(this->glue().size());
456
457 // reserve memory for stencils
458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const auto numBulkElements = this->gridView(bulkIdx).size(0);
459
2/2
✓ Branch 0 taken 7680 times.
✓ Branch 1 taken 1 times.
7681 for (GridIndex<bulkIdx> bulkElementIdx = 0; bulkElementIdx < numBulkElements; ++bulkElementIdx)
460 {
461 15360 this->couplingStencils(bulkIdx)[bulkElementIdx].reserve(10);
462 15360 extendedSourceStencil_.stencil()[bulkElementIdx].reserve(10);
463 23040 bulkSourceIds_[bulkElementIdx][0].reserve(10);
464 23040 bulkSourceWeights_[bulkElementIdx][0].reserve(10);
465 }
466 1 }
467
468 //! Make the stencils unique
469 1 void makeUniqueStencil_()
470 {
471 // make extra stencils unique
472
2/2
✓ Branch 0 taken 7680 times.
✓ Branch 1 taken 1 times.
7684 for (auto&& stencil : extendedSourceStencil_.stencil())
473 {
474 23040 std::sort(stencil.second.begin(), stencil.second.end());
475 38400 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
476
477 // remove the vertices element (box)
478 if constexpr (isBox<bulkIdx>())
479 {
480 const auto& indices = this->vertexIndices(bulkIdx, stencil.first);
481 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
482 [&](auto i){ return std::find(indices.begin(), indices.end(), i) != indices.end(); }),
483 stencil.second.end());
484 }
485 // remove the own element (cell-centered)
486 else
487 {
488 38400 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
489
9/10
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 95 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 83 times.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 112 times.
✓ Branch 6 taken 18 times.
✓ Branch 7 taken 136 times.
✓ Branch 8 taken 210 times.
✗ Branch 9 not taken.
715 [&](auto i){ return i == stencil.first; }),
490 stencil.second.end());
491 }
492 }
493
494 // make stencils unique
495 using namespace Dune::Hybrid;
496 9 forEach(integralRange(Dune::index_constant<2>{}), [&](const auto domainIdx)
497 {
498
4/4
✓ Branch 0 taken 1170 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 7680 times.
✓ Branch 4 taken 1 times.
8858 for (auto&& stencil : this->couplingStencils(domainIdx))
499 {
500 26550 std::sort(stencil.second.begin(), stencil.second.end());
501 44250 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
502 }
503 });
504 1 }
505
506 //! add additional stencil entries for the bulk element
507 4798 void addBulkSourceStencils_(GridIndex<bulkIdx> bulkElementIdx, GridIndex<lowDimIdx> coupledLowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx)
508 {
509 // add the lowdim element to the coupling stencil of this bulk element
510 if constexpr (isBox<lowDimIdx>())
511 {
512 const auto& vertices = this->vertexIndices(lowDimIdx, coupledLowDimElementIdx);
513 this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(),
514 vertices.begin(), vertices.end());
515
516 }
517 else
518 {
519 9596 auto& s = this->couplingStencils(bulkIdx)[bulkElementIdx];
520 4798 s.push_back(coupledLowDimElementIdx);
521 }
522
523 // the extended source stencil, every 3d element with a source is coupled to
524 // the element/dofs where the 3d quantities are measured
525 if constexpr (isBox<bulkIdx>())
526 {
527 const auto& vertices = this->vertexIndices(bulkIdx, coupledBulkElementIdx);
528 extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(),
529 vertices.begin(), vertices.end());
530 }
531 else
532 {
533 9596 auto& s = extendedSourceStencil_.stencil()[bulkElementIdx];
534 4798 s.push_back(coupledBulkElementIdx);
535 }
536 4798 }
537
538 //! a cylindrical kernel around the segment a->b
539 410249 Scalar evalConstKernel_(const GlobalPosition& a,
540 const GlobalPosition& b,
541 const GlobalPosition& point,
542 const Scalar R,
543 const Scalar rho) const noexcept
544 {
545 // projection of point onto line a + t*(b-a)
546 410249 const auto ab = b - a;
547 410249 const auto t = (point - a)*ab/ab.two_norm2();
548
549 // return 0 if we are outside cylinder
550
2/4
✓ Branch 0 taken 410249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 410249 times.
✗ Branch 3 not taken.
410249 if (t < 0.0 || t > 1.0)
551 return 0.0;
552
553 // compute distance
554 410249 auto proj = a; proj.axpy(t, ab);
555 410249 const auto radiusSquared = (proj - point).two_norm2();
556
557
1/2
✓ Branch 0 taken 410249 times.
✗ Branch 1 not taken.
410249 if (radiusSquared > rho*rho)
558 return 0.0;
559
560 410249 return 1.0/(M_PI*rho*rho);
561 }
562
563 /*!
564 * \brief Get the kernel width factor from the spatial params (if possible)
565 */
566 template<class SpatialParams>
567 auto kernelWidthFactor_(const SpatialParams& spatialParams, unsigned int eIdx)
568 -> std::enable_if_t<hasKernelWidthFactor<SpatialParams, unsigned int>(), Scalar>
569 { return spatialParams.kernelWidthFactor(eIdx); }
570
571 /*!
572 * \brief Get the kernel width factor (constant) from the input file (if possible)
573 */
574 template<class SpatialParams>
575 auto kernelWidthFactor_(const SpatialParams& spatialParams, unsigned int eIdx)
576 -> std::enable_if_t<!hasKernelWidthFactor<SpatialParams, unsigned int>(), Scalar>
577 {
578 static const Scalar kernelWidthFactor = getParam<Scalar>("MixedDimension.KernelWidthFactor");
579 return kernelWidthFactor;
580 }
581
582 /*!
583 * \brief Get the minimal kernel radius from the spatial params (if possible)
584 */
585 template<class SpatialParams>
586 auto minKernelRadius_(const SpatialParams& spatialParams, unsigned int eIdx)
587 -> std::enable_if_t<hasMinKernelRadius<SpatialParams, unsigned int>(), Scalar>
588 { return spatialParams.minKernelRadius(eIdx); }
589
590 /*!
591 * \brief Get the kernel width factor (constant) from the input file (if possible)
592 */
593 template<class SpatialParams>
594 auto minKernelRadius_(const SpatialParams& spatialParams, unsigned int eIdx)
595 -> std::enable_if_t<!hasMinKernelRadius<SpatialParams, unsigned int>(), Scalar>
596 {
597 static const Scalar minKernelRadius = getParam<Scalar>("MixedDimension.MinKernelRadius", 0.0);
598 return minKernelRadius;
599 }
600
601 template<class Geometry>
602 1329 typename Geometry::ctype distancePointGeometry_(const typename Geometry::GlobalCoordinate& p,
603 const Geometry& geometry,
604 std::size_t integrationOrder = 2)
605 {
606 1329 typename Geometry::ctype avgDist = 0.0;
607 6645 Dune::FieldVector<double, 2> center = { geometry.center()[0], geometry.center()[1] };
608 3987 Dune::FieldVector<double, 2> p2 = { p[0], p[1] };
609 1329 const auto factor = getParam<double>("Problem.DistancePointGeometryFactor", 1.0);
610 2658 avgDist = (center-p2).two_norm()*factor;
611 1329 return avgDist;
612 }
613
614 //! the extended source stencil object for kernel coupling
615 EmbeddedCoupling::ExtendedSourceStencil<ThisType> extendedSourceStencil_;
616 //! vector for the volume fraction of the lowdim domain in the bulk domain cells
617 std::vector<Scalar> lowDimVolumeInBulkElement_;
618 //! kernel sources to integrate for each bulk element
619 std::vector<std::array<std::vector<std::size_t>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceIds_;
620 //! the integral of the kernel for each point source / integration point, i.e. weight for the source
621 std::vector<std::array<std::vector<Scalar>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceWeights_;
622
623 //! source reconstruction scheme
624 std::shared_ptr<Reconstruction> reconstruction_;
625 };
626
627 } // end namespace Dumux
628
629 #endif
630