3.3.0
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
couplingmanager1d3d_surface.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 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
25#ifndef DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_SURFACE_HH
26#define DUMUX_MULTIDOMAIN_EMBEDDED_COUPLINGMANAGER_1D3D_SURFACE_HH
27
28#include <vector>
29
30#include <dune/common/timer.hh>
31#include <dune/geometry/quadraturerules.hh>
32
33#include <dumux/common/tag.hh>
36
40
41namespace Dumux {
42
43namespace Embedded1d3dCouplingMode {
44struct Surface : public Utility::Tag<Surface> {
45 static std::string name() { return "surface"; }
46
47 [[deprecated("Comparison with enum is deprecated. Removed after 3.3. Use tags.")]]
49 [[deprecated("Comparison with enum is deprecated. Removed after 3.3. Use tags.")]]
51 [[deprecated("Comparison with enum is deprecated. Removed after 3.3. Use tags.")]]
53 [[deprecated("Comparison with enum is deprecated. Removed after 3.3. Use tags.")]]
55};
56
57inline constexpr Surface surface{};
58} // end namespace Embedded1d3dCouplingMode
59
60// forward declaration
61template<class MDTraits, class CouplingMode>
62class Embedded1d3dCouplingManager;
63
71template<class MDTraits>
72class Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Surface>
73: public EmbeddedCouplingManagerBase<MDTraits, Embedded1d3dCouplingManager<MDTraits, Embedded1d3dCouplingMode::Surface>,
74 CircleAveragePointSourceTraits<MDTraits>>
75{
78 using Scalar = typename MDTraits::Scalar;
79 using SolutionVector = typename MDTraits::SolutionVector;
80 using PointSourceData = typename ParentType::PointSourceTraits::PointSourceData;
81
82 static constexpr auto bulkIdx = typename MDTraits::template SubDomain<0>::Index();
83 static constexpr auto lowDimIdx = typename MDTraits::template SubDomain<1>::Index();
84
85 // the sub domain type aliases
86 template<std::size_t id> using SubDomainTypeTag = typename MDTraits::template SubDomain<id>::TypeTag;
87 template<std::size_t id> using Problem = GetPropType<SubDomainTypeTag<id>, Properties::Problem>;
88 template<std::size_t id> using GridGeometry = GetPropType<SubDomainTypeTag<id>, Properties::GridGeometry>;
89 template<std::size_t id> using GridView = typename GridGeometry<id>::GridView;
90 template<std::size_t id> using Element = typename GridView<id>::template Codim<0>::Entity;
91 template<std::size_t id> using GridIndex = typename IndexTraits<GridView<id>>::GridIndex;
92
93 using GlobalPosition = typename Element<bulkIdx>::Geometry::GlobalCoordinate;
94
95 template<std::size_t id>
96 static constexpr bool isBox()
97 { return GridGeometry<id>::discMethod == DiscretizationMethod::box; }
98
99 enum {
100 bulkDim = GridView<bulkIdx>::dimension,
101 lowDimDim = GridView<lowDimIdx>::dimension,
102 dimWorld = GridView<bulkIdx>::dimensionworld
103 };
104public:
105 static constexpr Embedded1d3dCouplingMode::Surface couplingMode{};
106
107 using ParentType::ParentType;
108
109 void init(std::shared_ptr<Problem<bulkIdx>> bulkProblem,
110 std::shared_ptr<Problem<lowDimIdx>> lowDimProblem,
111 const SolutionVector& curSol)
112 {
113 ParentType::init(bulkProblem, lowDimProblem, curSol);
114 computeLowDimVolumeFractions();
115 }
116
123 template<std::size_t id, class JacobianPattern>
124 void extendJacobianPattern(Dune::index_constant<id> domainI, JacobianPattern& pattern) const
125 {
126 extendedSourceStencil_.extendJacobianPattern(*this, domainI, pattern);
127 }
128
136 template<std::size_t i, class LocalAssemblerI, class JacobianMatrixDiagBlock, class GridVariables>
137 void evalAdditionalDomainDerivatives(Dune::index_constant<i> domainI,
138 const LocalAssemblerI& localAssemblerI,
139 const typename LocalAssemblerI::LocalResidual::ElementResidualVector&,
140 JacobianMatrixDiagBlock& A,
141 GridVariables& gridVariables)
142 {
143 extendedSourceStencil_.evalAdditionalDomainDerivatives(*this, domainI, localAssemblerI, this->curSol(), A, gridVariables);
144 }
145
146 /* \brief Compute integration point point sources and associated data
147 *
148 * This method uses grid glue to intersect the given grids. Over each intersection
149 * we later need to integrate a source term. This method places point sources
150 * at each quadrature point and provides the point source with the necessary
151 * information to compute integrals (quadrature weight and integration element)
152 * \param order The order of the quadrature rule for integration of sources over an intersection
153 * \param verbose If the point source computation is verbose
154 */
155 void computePointSourceData(std::size_t order = 1, bool verbose = false)
156 {
157 // if we use the circle average as the 3D values or a point evaluation
158 static const bool useCircleAverage = getParam<bool>("MixedDimension.UseCircleAverage", true);
159
160 // Initialize the bulk bounding box tree
161 const auto& bulkGridGeometry = this->problem(bulkIdx).gridGeometry();
162 const auto& lowDimGridGeometry = this->problem(lowDimIdx).gridGeometry();
163 const auto& bulkTree = bulkGridGeometry.boundingBoxTree();
164
165 // initialize the maps
166 // do some logging and profiling
167 Dune::Timer watch;
168 std::cout << "Initializing the point sources..." << std::endl;
169
170 // clear all internal members like pointsource vectors and stencils
171 // initializes the point source id counter
172 this->clear();
173 extendedSourceStencil_.stencil().clear();
174
175 // precompute the vertex indices for efficiency
176 this->precomputeVertexIndices(bulkIdx);
177 this->precomputeVertexIndices(lowDimIdx);
178
179 // intersect the bounding box trees
180 this->glueGrids();
181
182 // iterate over all intersection and add point sources
183 const auto& lowDimProblem = this->problem(lowDimIdx);
184 for (const auto& is : intersections(this->glue()))
185 {
186 // all inside elements are identical...
187 const auto& lowDimElement = is.targetEntity(0);
188 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(lowDimElement);
189
190 // get the intersection geometry
191 const auto intersectionGeometry = is.geometry();
192 // get the Gaussian quadrature rule for the local intersection
193 const auto& quad = Dune::QuadratureRules<Scalar, lowDimDim>::rule(intersectionGeometry.type(), order);
194
195 // apply the Gaussian quadrature rule and define point sources at each quadrature point
196 // note that the approximation is not optimal if
197 // (a) the one-dimensional elements are too large,
198 // (b) whenever a one-dimensional element is split between two or more elements,
199 // (c) when gradients of important quantities in the three-dimensional domain are large.
200
201 // iterate over all quadrature points
202 for (auto&& qp : quad)
203 {
204 // global position of the quadrature point
205 const auto globalPos = intersectionGeometry.global(qp.position());
206
207 const auto bulkElementIndices = intersectingEntities(globalPos, bulkTree);
208
209 // do not add a point source if the qp is outside of the 3d grid
210 // this is equivalent to having a source of zero for that qp
211 if (bulkElementIndices.empty())
212 continue;
213
215 // get points on the cylinder surface at the integration point
217
218 static const auto numIp = getParam<int>("MixedDimension.NumCircleSegments");
219 const auto radius = lowDimProblem.spatialParams().radius(lowDimElementIdx);
220 const auto normal = intersectionGeometry.corner(1)-intersectionGeometry.corner(0);
221 const auto integrationElement = intersectionGeometry.integrationElement(qp.position())*2*M_PI*radius/Scalar(numIp);
222 const auto qpweight = qp.weight()/(2*M_PI*radius);
223 const auto circleAvgWeight = 2*M_PI*radius/numIp;
224
225 const auto circlePoints = EmbeddedCoupling::circlePoints(globalPos, normal, radius, numIp);
226 std::vector<std::vector<std::size_t>> circleBulkElementIndices(circlePoints.size());
227 std::vector<Scalar> circleIpWeight; circleIpWeight.reserve(circlePoints.size());
228 std::vector<GridIndex<bulkIdx>> circleStencil; circleStencil.reserve(circlePoints.size());
229 // for box
230 std::vector<const std::vector<GridIndex<bulkIdx>>*> circleCornerIndices;
231 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
232 std::vector<ShapeValues> circleShapeValues;
233
234 // go over all points of the average operator
235 for (int k = 0; k < circlePoints.size(); ++k)
236 {
237 circleBulkElementIndices[k] = intersectingEntities(circlePoints[k], bulkTree);
238 if (circleBulkElementIndices[k].empty())
239 continue;
240
241 const auto localCircleAvgWeight = circleAvgWeight / circleBulkElementIndices[k].size();
242 for (const auto bulkElementIdx : circleBulkElementIndices[k])
243 {
244 circleStencil.push_back(bulkElementIdx);
245 circleIpWeight.push_back(localCircleAvgWeight);
246
247 // precompute interpolation data for box scheme for each cut bulk element
248 if constexpr (isBox<bulkIdx>())
249 {
250 const auto bulkElement = bulkGridGeometry.element(bulkElementIdx);
251 circleCornerIndices.push_back(&(this->vertexIndices(bulkIdx, bulkElementIdx)));
252
253 // evaluate shape functions at the integration point
254 const auto bulkGeometry = bulkElement.geometry();
255 ShapeValues shapeValues;
256 this->getShapeValues(bulkIdx, bulkGridGeometry, bulkGeometry, circlePoints[k], shapeValues);
257 circleShapeValues.emplace_back(std::move(shapeValues));
258 }
259 }
260 }
261
262 // export low dim circle stencil
263 if constexpr (isBox<bulkIdx>())
264 {
265 // we insert all vertices and make it unique later
266 for (const auto& vertices : circleCornerIndices)
267 {
268 this->couplingStencils(lowDimIdx)[lowDimElementIdx].insert(this->couplingStencils(lowDimIdx)[lowDimElementIdx].end(),
269 vertices->begin(), vertices->end());
270
271 }
272 }
273 else
274 {
275 this->couplingStencils(lowDimIdx)[lowDimElementIdx].insert(this->couplingStencils(lowDimIdx)[lowDimElementIdx].end(),
276 circleStencil.begin(), circleStencil.end());
277 }
278
279 for (int k = 0; k < circlePoints.size(); ++k)
280 {
281 const auto& circlePos = circlePoints[k];
282
283 // find bulk elements intersection with the circle elements
284 if (circleBulkElementIndices[k].empty())
285 continue;
286
287 // loop over the bulk elements at the integration points (usually one except when it is on a face or edge or vertex)
288 // and add a point source at every point on the circle
289 for (const auto bulkElementIdx : circleBulkElementIndices[k])
290 {
291 const auto id = this->idCounter_++;
292
293 this->pointSources(bulkIdx).emplace_back(circlePos, id, qpweight, integrationElement, bulkElementIdx);
294 this->pointSources(bulkIdx).back().setEmbeddings(circleBulkElementIndices[k].size());
295 this->pointSources(lowDimIdx).emplace_back(globalPos, id, qpweight, integrationElement, lowDimElementIdx);
296 this->pointSources(lowDimIdx).back().setEmbeddings(circleBulkElementIndices[k].size());
297
298 // pre compute additional data used for the evaluation of
299 // the actual solution dependent source term
300 PointSourceData psData;
301
302 if constexpr (isBox<lowDimIdx>())
303 {
304 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
305 ShapeValues shapeValues;
306 this->getShapeValues(lowDimIdx, lowDimGridGeometry, intersectionGeometry, globalPos, shapeValues);
307 psData.addLowDimInterpolation(shapeValues, this->vertexIndices(lowDimIdx, lowDimElementIdx), lowDimElementIdx);
308 }
309 else
310 {
311 psData.addLowDimInterpolation(lowDimElementIdx);
312 }
313
314 // add data needed to compute integral over the circle
315 if constexpr (isBox<bulkIdx>())
316 {
317 if (useCircleAverage)
318 psData.addCircleInterpolation(circleCornerIndices, circleShapeValues, circleIpWeight, circleStencil);
319
320 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
321 const auto bulkGeometry = bulkGridGeometry.element(bulkElementIdx).geometry();
322 ShapeValues shapeValues;
323 this->getShapeValues(bulkIdx, bulkGridGeometry, bulkGeometry, circlePos, shapeValues);
324 psData.addBulkInterpolation(shapeValues, this->vertexIndices(bulkIdx, bulkElementIdx), bulkElementIdx);
325 }
326 else
327 {
328 if (useCircleAverage)
329 psData.addCircleInterpolation(circleIpWeight, circleStencil);
330
331 psData.addBulkInterpolation(bulkElementIdx);
332 }
333
334 // publish point source data in the global vector
335 this->pointSourceData().emplace_back(std::move(psData));
336
337 // export the bulk coupling stencil
338 // we insert all vertices / elements and make it unique later
339 if constexpr (isBox<lowDimIdx>())
340 {
341 const auto& vertices = this->vertexIndices(lowDimIdx, lowDimElementIdx);
342 this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(),
343 vertices.begin(), vertices.end());
344
345 }
346 else
347 {
348 this->couplingStencils(bulkIdx)[bulkElementIdx].push_back(lowDimElementIdx);
349 }
350
351 // export bulk circle stencil (only needed for circle average)
352 if (useCircleAverage)
353 {
354 if constexpr (isBox<bulkIdx>())
355 {
356 // we insert all vertices and make it unique later
357 for (const auto& vertices : circleCornerIndices)
358 {
359 extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(),
360 vertices->begin(), vertices->end());
361
362 }
363 }
364 else
365 {
366 extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(),
367 circleStencil.begin(), circleStencil.end());
368 }
369 }
370 }
371 }
372 }
373 }
374
375 // make the circle stencil unique (for source derivatives)
376 for (auto&& stencil : extendedSourceStencil_.stencil())
377 {
378 std::sort(stencil.second.begin(), stencil.second.end());
379 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
380
381 // remove the vertices element (box)
382 if constexpr (isBox<bulkIdx>())
383 {
384 const auto& indices = this->vertexIndices(bulkIdx, stencil.first);
385 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
386 [&](auto i){ return std::find(indices.begin(), indices.end(), i) != indices.end(); }),
387 stencil.second.end());
388 }
389 // remove the own element (cell-centered)
390 else
391 {
392 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
393 [&](auto i){ return i == stencil.first; }),
394 stencil.second.end());
395 }
396 }
397
398 // make stencils unique
399 using namespace Dune::Hybrid;
400 forEach(integralRange(Dune::index_constant<2>{}), [&](const auto domainIdx)
401 {
402 for (auto&& stencil : this->couplingStencils(domainIdx))
403 {
404 std::sort(stencil.second.begin(), stencil.second.end());
405 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
406 }
407 });
408
409 std::cout << "took " << watch.elapsed() << " seconds." << std::endl;
410 }
411
414 {
415 // resize the storage vector
416 lowDimVolumeInBulkElement_.resize(this->gridView(bulkIdx).size(0));
417 // get references to the grid geometries
418 const auto& lowDimGridGeometry = this->problem(lowDimIdx).gridGeometry();
419 const auto& bulkGridGeometry = this->problem(bulkIdx).gridGeometry();
420
421 // compute the low dim volume fractions
422 for (const auto& is : intersections(this->glue()))
423 {
424 // all inside elements are identical...
425 const auto& inside = is.targetEntity(0);
426 const auto intersectionGeometry = is.geometry();
427 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
428
429 // compute the volume the low-dim domain occupies in the bulk domain if it were full-dimensional
430 const auto radius = this->problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
431 for (int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
432 {
433 const auto& outside = is.domainEntity(outsideIdx);
434 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
435 lowDimVolumeInBulkElement_[bulkElementIdx] += intersectionGeometry.volume()*M_PI*radius*radius;
436 }
437 }
438 }
439
443 // \{
444
446 Scalar radius(std::size_t id) const
447 {
448 const auto& data = this->pointSourceData()[id];
449 return this->problem(lowDimIdx).spatialParams().radius(data.lowDimElementIdx());
450 }
451
453 // For one-dimensional low dim domain we assume radial tubes
454 Scalar lowDimVolume(const Element<bulkIdx>& element) const
455 {
456 const auto eIdx = this->problem(bulkIdx).gridGeometry().elementMapper().index(element);
457 return lowDimVolumeInBulkElement_[eIdx];
458 }
459
461 // For one-dimensional low dim domain we assume radial tubes
462 Scalar lowDimVolumeFraction(const Element<bulkIdx>& element) const
463 {
464 const auto totalVolume = element.geometry().volume();
465 return lowDimVolume(element) / totalVolume;
466 }
467
468 // \}
469
470private:
473
475 std::vector<Scalar> lowDimVolumeInBulkElement_;
476};
477
478} // end namespace Dumux
479
480#endif
Defines the index types used for grid and local indices.
Helper class to create (named and comparable) tagged types.
Point source traits for average-based coupling modes.
Helper function to compute points on a circle.
Coupling manager for low-dimensional domains embedded in the bulk domain. Point sources on each integ...
Vector normal(const Vector &v)
Create a vector normal to the given one (v is expected to be non-zero)
Definition: geometry/normal.hh:36
std::vector< std::size_t > intersectingEntities(const Dune::FieldVector< ctype, dimworld > &point, const BoundingBoxTree< EntitySet > &tree, bool isCartesianGrid=false)
Compute all intersections between entities and a point.
Definition: geometry/intersectingentities.hh:100
EmbeddedCouplingMode
The coupling mode.
Definition: couplingmanager1d3d.hh:44
void circlePoints(std::vector< GlobalPosition > &points, const std::vector< Scalar > &sincos, const GlobalPosition &center, const GlobalPosition &normal, const Scalar radius)
Definition: circlepoints.hh:50
Definition: adapt.hh:29
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:149
constexpr Surface surface
Definition: couplingmanager1d3d_surface.hh:57
Struture to define the index types used for grid and local indices.
Definition: indextraits.hh:38
Property to specify the type of a problem which has to be solved.
Definition: common/properties.hh:57
Definition: common/properties.hh:101
Helper class to create (named and comparable) tagged types Tags any given type. The tagged type is eq...
Definition: tag.hh:42
Manages the coupling between bulk elements and lower dimensional elements Point sources on each integ...
Definition: couplingmanager1d3d.hh:36
Definition: couplingmanager1d3d_surface.hh:44
friend constexpr bool operator==(Surface, EmbeddedCouplingMode m)
Definition: couplingmanager1d3d_surface.hh:48
friend constexpr bool operator!=(EmbeddedCouplingMode m, Surface)
Definition: couplingmanager1d3d_surface.hh:54
static std::string name()
Definition: couplingmanager1d3d_surface.hh:45
friend constexpr bool operator!=(Surface, EmbeddedCouplingMode m)
Definition: couplingmanager1d3d_surface.hh:52
friend constexpr bool operator==(EmbeddedCouplingMode m, Surface)
Definition: couplingmanager1d3d_surface.hh:50
Manages the coupling between bulk elements and lower dimensional elements Point sources on each integ...
Definition: couplingmanager1d3d_surface.hh:75
void computePointSourceData(std::size_t order=1, bool verbose=false)
Definition: couplingmanager1d3d_surface.hh:155
void evalAdditionalDomainDerivatives(Dune::index_constant< i > domainI, const LocalAssemblerI &localAssemblerI, const typename LocalAssemblerI::LocalResidual::ElementResidualVector &, JacobianMatrixDiagBlock &A, GridVariables &gridVariables)
evaluate additional derivatives of the element residual of a domain with respect to dofs in the same ...
Definition: couplingmanager1d3d_surface.hh:137
Scalar lowDimVolume(const Element< bulkIdx > &element) const
The volume the lower dimensional domain occupies in the bulk domain element.
Definition: couplingmanager1d3d_surface.hh:454
void extendJacobianPattern(Dune::index_constant< id > domainI, JacobianPattern &pattern) const
extend the jacobian pattern of the diagonal block of domain i by those entries that are not already i...
Definition: couplingmanager1d3d_surface.hh:124
void computeLowDimVolumeFractions()
Compute the low dim volume fraction in the bulk domain cells.
Definition: couplingmanager1d3d_surface.hh:413
Scalar radius(std::size_t id) const
Methods to be accessed by the subproblems.
Definition: couplingmanager1d3d_surface.hh:446
Scalar lowDimVolumeFraction(const Element< bulkIdx > &element) const
The volume fraction the lower dimensional domain occupies in the bulk domain element.
Definition: couplingmanager1d3d_surface.hh:462
void init(std::shared_ptr< Problem< bulkIdx > > bulkProblem, std::shared_ptr< Problem< lowDimIdx > > lowDimProblem, const SolutionVector &curSol)
Definition: couplingmanager1d3d_surface.hh:109
Manages the coupling between bulk elements and lower dimensional elements Point sources on each integ...
Definition: couplingmanagerbase.hh:78
A class managing an extended source stencil.
Definition: extendedsourcestencil.hh:46
Declares all properties used in Dumux.