version 3.10-dev
boundary/freeflowporenetwork/couplingmapper.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
13#ifndef DUMUX_MULTIDOMAIN_FREEFLOW_POROUSMEDIUM_COUPLINGMAPPER_HH
14#define DUMUX_MULTIDOMAIN_FREEFLOW_POROUSMEDIUM_COUPLINGMAPPER_HH
15
16#include <algorithm>
17#include <iostream>
18#include <map>
19#include <unordered_map>
20#include <tuple>
21#include <vector>
22
23#include <dune/common/timer.hh>
24#include <dune/common/exceptions.hh>
25#include <dune/common/indices.hh>
26#include <dune/common/reservedvector.hh>
27#include <dune/geometry/axisalignedcubegeometry.hh>
28
33
34namespace Dumux {
35
40// template<class MDTraits, class CouplingManager>
42{
43 using MapType = std::unordered_map<std::size_t, std::vector<std::size_t>>;
44
45public:
49 template<class FreeFlowMomentumGridGeometry, class FreeFlowMassGridGeometry, class PoreNetworkGridGeometry>
50 void update(const FreeFlowMomentumGridGeometry& ffMomentumGridGeometry,
51 const FreeFlowMassGridGeometry& ffMassGridGeometry,
52 const PoreNetworkGridGeometry& pnmGridGeometry)
53 {
54 clear_();
55 resize_(ffMomentumGridGeometry, pnmGridGeometry);
56 Dune::Timer watch;
57 std::cout << "Initializing the coupling map..." << std::endl;
58
59 auto ffFvGeometry = localView(ffMomentumGridGeometry);
60 auto pnmFvGeometry = localView(pnmGridGeometry);
61
62 using GlobalPosition = typename FreeFlowMomentumGridGeometry::GridView::template Codim<0>::Entity::Geometry::GlobalCoordinate;
63
64 for (const auto& pnmElement : elements(pnmGridGeometry.gridView()))
65 {
66 const auto pnmElementIdx = pnmGridGeometry.elementMapper().index(pnmElement);
67 pnmFvGeometry.bindElement(pnmElement);
68 for (const auto& pnmScv : scvs(pnmFvGeometry))
69 {
70 // skip the dof if it is not on the boundary
71 if (!pnmGridGeometry.dofOnBoundary(pnmScv.dofIndex()))
72 continue;
73
74 // get the intersection bulk element
75 const auto pnmPos = pnmScv.dofPosition();
76 const auto pnmDofIdx = pnmScv.dofIndex();
77 const auto& otherPNMScv = pnmFvGeometry.scv(1 - pnmScv.indexInElement());
78 const auto otherPNMScvDofIdx = otherPNMScv.dofIndex();
79
80 // check for intersections, skip if no intersection was found
81 const auto directlyCoupledFreeFlowElements = intersectingEntities(pnmPos, ffMomentumGridGeometry.boundingBoxTree());
82 if (directlyCoupledFreeFlowElements.empty())
83 continue;
84 else
85 isCoupledPNMDof_[pnmDofIdx] = true;
86
87 // determine the normal direction of the local coupling interface heuristically:
88 // find all element intersections touching the pore and take the normal which
89 // occurs most frequently
90 const std::size_t couplingNormalDirectionIndex = [&]
91 {
92 using Key = std::pair<std::size_t, bool>;
93 std::map<Key, std::size_t> result;
94 for (const auto eIdx : directlyCoupledFreeFlowElements)
95 {
96 for (const auto& intersection : intersections(ffMomentumGridGeometry.gridView(), ffMomentumGridGeometry.element(eIdx)))
97 {
98 if (intersectsPointGeometry(pnmPos, intersection.geometry()))
99 {
100 const auto& normal = intersection.centerUnitOuterNormal();
101 const auto normalAxis = Dumux::normalAxis(normal);
102 const Key key = std::make_pair(normalAxis, std::signbit(normal[normalAxis]));
103 ++result[key];
104 }
105 }
106 }
107
108 // TODO how to properly handle this corner (literally) case
109 if (directlyCoupledFreeFlowElements.size() == 1 && result.size() > 1)
110 DUNE_THROW(Dune::InvalidStateException, "Pore may not intersect with faces of different orientation when coupled to only one element");
111
112 return std::max_element(result.begin(), result.end(), [](const auto& x, const auto& y) { return x.second < y.second;})->first.first;
113 }();
114
115 using Scalar = typename FreeFlowMomentumGridGeometry::GridView::ctype;
116 const Scalar couplingPoreRadius = pnmGridGeometry.poreInscribedRadius(pnmDofIdx);
117 GlobalPosition lowerLeft = pnmPos - GlobalPosition(couplingPoreRadius);
118 lowerLeft[couplingNormalDirectionIndex] = pnmPos[couplingNormalDirectionIndex];
119 GlobalPosition upperRight = pnmPos + GlobalPosition(couplingPoreRadius);
120 upperRight[couplingNormalDirectionIndex] = pnmPos[couplingNormalDirectionIndex];
121
122 auto axes = std::move(std::bitset<FreeFlowMomentumGridGeometry::Grid::dimensionworld>{}.set());
123 axes.set(couplingNormalDirectionIndex, false);
124
125 using PoreIntersectionGeometryType = Dune::AxisAlignedCubeGeometry<Scalar,
126 FreeFlowMomentumGridGeometry::GridView::dimension-1,
127 FreeFlowMomentumGridGeometry::GridView::dimensionworld>;
128
129 PoreIntersectionGeometryType poreIntersectionGeometry(lowerLeft, upperRight, axes);
130 const auto allCoupledFreeFlowElements = intersectingEntities(std::move(poreIntersectionGeometry), ffMomentumGridGeometry.boundingBoxTree());
131
132
133 for (const auto& ffElementInfo : allCoupledFreeFlowElements)
134 {
135 const auto freeFlowElementIndex = ffElementInfo.second();
136 pnmElementToFreeFlowElementsMap_[pnmElementIdx].push_back(freeFlowElementIndex);
137 freeFlowElementToPNMElementMap_[freeFlowElementIndex] = pnmElementIdx;
138
139 pnmToFreeFlowMassStencils_[pnmElementIdx].push_back(freeFlowElementIndex);
140 freeFlowMassToPNMStencils_[freeFlowElementIndex].push_back(pnmDofIdx);
141
142 ffFvGeometry.bindElement(ffMomentumGridGeometry.element(freeFlowElementIndex));
143 const auto coupledFreeFlowMomentumDofIndices = coupledFFMomentumDofs_(ffFvGeometry, ffMassGridGeometry, pnmPos, couplingPoreRadius, couplingNormalDirectionIndex);
144
145 pnmToFreeFlowMomentumStencils_[pnmElementIdx].push_back(coupledFreeFlowMomentumDofIndices.coupledFrontalDof);
146 freeFlowMomentumToPNMStencils_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof].push_back(pnmDofIdx);
147 freeFlowMomentumToPNMStencils_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof].push_back(otherPNMScvDofIdx);
148
149 isCoupledFreeFlowMomentumDof_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof] = true;
150 isCoupledFreeFlowMomentumDofOnInterface_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof] = true;
151
152 // treat the coupled ff dofs not directly on interface
153 for (const auto ffDofIdx : coupledFreeFlowMomentumDofIndices.coupledLateralDofs)
154 {
155 freeFlowMomentumToPNMStencils_[ffDofIdx].push_back(pnmDofIdx);
156 freeFlowMomentumToPNMStencils_[ffDofIdx].push_back(otherPNMScvDofIdx);
157 isCoupledFreeFlowMomentumDof_[ffDofIdx] = true;
158 }
159 }
160 }
161 }
162
163 std::cout << "took " << watch.elapsed() << " seconds." << std::endl;
164 }
165
171 const std::vector<std::size_t>& poreNetworkToFreeFlowMomentumCouplingStencil(const std::size_t eIdxI) const
172 {
174 return pnmToFreeFlowMomentumStencils_.at(eIdxI);
175 else
176 return emptyStencil_;
177 }
178
184 const std::vector<std::size_t>& poreNetworkToFreeFlowMassCouplingStencil(const std::size_t eIdxI) const
185 {
187 return pnmToFreeFlowMassStencils_.at(eIdxI);
188 else
189 return emptyStencil_;
190 }
191
197 const std::vector<std::size_t>& freeFlowMassToPoreNetworkCouplingStencil(const std::size_t eIdxI) const
198 {
199 if (isCoupledFreeFlowElement(eIdxI))
200 return freeFlowMassToPNMStencils_.at(eIdxI);
201 else
202 return emptyStencil_;
203 }
204
210 const std::vector<std::size_t>& freeFlowMomentumToPoreNetworkCouplingStencil(const std::size_t dofIndex) const
211 {
212 if (isCoupledFreeFlowMomentumDof(dofIndex))
213 return freeFlowMomentumToPNMStencils_.at(dofIndex);
214 else
215 return emptyStencil_;
216 }
217
221 bool isCoupledFreeFlowElement(std::size_t eIdx) const
222 {
223 return static_cast<bool>(freeFlowElementToPNMElementMap_.count(eIdx));
224 }
225
229 bool isCoupledFreeFlowMomentumDof(std::size_t dofIdx) const
230 {
231 return isCoupledFreeFlowMomentumDof_[dofIdx];
232 }
233
237 bool isCoupledPoreNetworkElement(std::size_t eIdx) const
238 {
239 return static_cast<bool>(pnmElementToFreeFlowElementsMap_.count(eIdx));
240 }
241
245 bool isCoupledPoreNetworkDof(std::size_t dofIdx) const
246 {
247 return isCoupledPNMDof_[dofIdx];
248 }
249
250 bool isCoupledFreeFlowMomentumScvf(std::size_t scvfIdx) const
251 {
252 return isCoupledFrontalFreeFlowMomentumScvf_.count(scvfIdx);
253 }
254
255 bool isCoupledFreeFlowMomentumLateralScvf(std::size_t scvfIdx) const
256 {
257 return isCoupledLateralFreeFlowMomentumScvf_.count(scvfIdx);
258 }
259
260 bool isCoupledFreeFlowMassScvf(std::size_t scvfIdx) const
261 {
262 return isCoupledFreeFlowMassScvf_.count(scvfIdx);
263 }
264
266 { return pnmElementToFreeFlowElementsMap_;}
267
269 { return freeFlowElementToPNMElementMap_; }
270
271private:
272
273 void clear_()
274 {
275 pnmElementToFreeFlowElementsMap_.clear();
276 freeFlowElementToPNMElementMap_.clear();
277 isCoupledPNMDof_.clear();
278 isCoupledFreeFlowMomentumDof_.clear();
279 isCoupledFreeFlowMomentumDofOnInterface_.clear();
280 pnmToFreeFlowMassStencils_.clear();
281 pnmToFreeFlowMomentumStencils_.clear();
282 freeFlowMassToPNMStencils_.clear();
283 freeFlowMomentumToPNMStencils_.clear();
284 }
285
286 template<class FreeFlowMomentumGridGeometry, class PoreNetworkGridGeometry>
287 void resize_(const FreeFlowMomentumGridGeometry& ffMomentumGridGeometry,
288 const PoreNetworkGridGeometry& pnmGridGeometry)
289 {
290 const auto numPNMDofs = pnmGridGeometry.numDofs();
291 const auto numFreeFlowMomentumDofs = ffMomentumGridGeometry.numDofs();
292 isCoupledPNMDof_.resize(numPNMDofs, false);
293 isCoupledFreeFlowMomentumDof_.resize(numFreeFlowMomentumDofs, false);
294 isCoupledFreeFlowMomentumDofOnInterface_.resize(numFreeFlowMomentumDofs, false);
295 }
296
298 template<class FVElementGeometry, class FreeFlowMassGridGeometry, class GlobalPosition, class Scalar>
299 auto coupledFFMomentumDofs_(const FVElementGeometry& fvGeometry,
300 const FreeFlowMassGridGeometry& ffMassGridGeometry,
301 const GlobalPosition& pnmPos,
302 const Scalar couplingPoreRadius,
303 const int couplingInterfaceDirectionIdx)
304 {
305
306 struct Result
307 {
308 Dune::ReservedVector<std::size_t, FVElementGeometry::maxNumElementScvs> coupledLateralDofs;
309 std::size_t coupledFrontalDof;
310 } result;
311
312
313 using std::abs;
314 for (const auto& scv : scvs(fvGeometry))
315 {
316 const Scalar eps = diameter(fvGeometry.geometry(scv))*1e-6; // TODO
317 assert(eps < couplingPoreRadius);
318
319 if (scv.dofAxis() == couplingInterfaceDirectionIdx) // the free flow dofs that lie within the coupling interface
320 {
321 if (abs(scv.dofPosition()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
322 {
323 result.coupledFrontalDof = scv.dofIndex();
324
325 // treat scvfs
326 for (const auto& scvf : scvfs(fvGeometry, scv))
327 {
328 // add lateral faces "standing" on coupling interface
329 if (scvf.isLateral() && !scvf.boundary())
330 isCoupledLateralFreeFlowMomentumScvf_[scvf.index()] = true;
331 else if (scvf.isFrontal() && scvf.boundary()) // add face lying on interface
332 {
333 isCoupledFrontalFreeFlowMomentumScvf_[scvf.index()] = true;
334
335 const auto& element = ffMassGridGeometry.element(fvGeometry.elementIndex()); // this local variable is needed to prevent a memory error
336 const auto ffMassFVGeometry = localView(ffMassGridGeometry).bindElement(element);
337 for (const auto& ffMassScvf : scvfs(ffMassFVGeometry))
338 {
339 if (abs(ffMassScvf.center()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
340 isCoupledFreeFlowMassScvf_[ffMassScvf.index()] = true;
341 }
342 }
343 }
344 }
345 }
346 else // the free flow dofs perpendicular to the coupling interface
347 {
348 bool isCoupledDof = false;
349
350 for (int dimIdx = 0; dimIdx < GlobalPosition::dimension; ++dimIdx)
351 {
352 if (dimIdx == couplingInterfaceDirectionIdx || scv.boundary())
353 continue;
354
355 isCoupledDof = abs(scv.dofPosition()[dimIdx] - pnmPos[dimIdx]) < couplingPoreRadius + eps;
356 }
357
358 if (isCoupledDof)
359 {
360 result.coupledLateralDofs.push_back(scv.dofIndex());
361
362 // treat scvfs
363 for (const auto& scvf : scvfs(fvGeometry, scv))
364 {
365 if (scvf.isLateral() && scvf.boundary())
366 {
367 // add lateral scvfs lying on interface
368 if (abs(scvf.ipGlobal()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
369 isCoupledLateralFreeFlowMomentumScvf_[scvf.index()] = true;
370 }
371 }
372 }
373 }
374 }
375
376 return result;
377 }
378
379 std::vector<std::size_t> emptyStencil_;
380
381 std::unordered_map<std::size_t, std::vector<std::size_t>> pnmElementToFreeFlowElementsMap_;
382 std::unordered_map<std::size_t, std::size_t> freeFlowElementToPNMElementMap_;
383
384 std::vector<bool> isCoupledPNMDof_;
385 std::vector<bool> isCoupledFreeFlowMomentumDof_;
386 std::vector<bool> isCoupledFreeFlowMomentumDofOnInterface_;
387
388 std::unordered_map<std::size_t, bool> isCoupledLateralFreeFlowMomentumScvf_;
389 std::unordered_map<std::size_t, bool> isCoupledFrontalFreeFlowMomentumScvf_;
390 std::unordered_map<std::size_t, bool> isCoupledFreeFlowMassScvf_;
391
392 MapType pnmToFreeFlowMassStencils_;
393 MapType pnmToFreeFlowMomentumStencils_;
394 MapType freeFlowMassToPNMStencils_;
395 MapType freeFlowMomentumToPNMStencils_;
396};
397
398} // end namespace Dumux
399
400#endif
Coupling mapper for staggered free-flow and pore-network models.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:42
bool isCoupledFreeFlowMomentumLateralScvf(std::size_t scvfIdx) const
Definition: boundary/freeflowporenetwork/couplingmapper.hh:255
const std::vector< std::size_t > & freeFlowMassToPoreNetworkCouplingStencil(const std::size_t eIdxI) const
returns an iterable container of all indices of degrees of freedom of domain j that couple with / inf...
Definition: boundary/freeflowporenetwork/couplingmapper.hh:197
void update(const FreeFlowMomentumGridGeometry &ffMomentumGridGeometry, const FreeFlowMassGridGeometry &ffMassGridGeometry, const PoreNetworkGridGeometry &pnmGridGeometry)
Main update routine.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:50
const std::vector< std::size_t > & freeFlowMomentumToPoreNetworkCouplingStencil(const std::size_t dofIndex) const
returns an iterable container of all indices of degrees of freedom of domain j that couple with / inf...
Definition: boundary/freeflowporenetwork/couplingmapper.hh:210
bool isCoupledFreeFlowMomentumScvf(std::size_t scvfIdx) const
Definition: boundary/freeflowporenetwork/couplingmapper.hh:250
bool isCoupledPoreNetworkDof(std::size_t dofIdx) const
Return if an element residual with index eIdx of domain i is coupled to domain j.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:245
bool isCoupledFreeFlowMassScvf(std::size_t scvfIdx) const
Definition: boundary/freeflowporenetwork/couplingmapper.hh:260
const std::vector< std::size_t > & poreNetworkToFreeFlowMomentumCouplingStencil(const std::size_t eIdxI) const
returns an iterable container of all indices of degrees of freedom of domain j that couple with / inf...
Definition: boundary/freeflowporenetwork/couplingmapper.hh:171
const auto & freeFlowElementToPNMElementMap() const
Definition: boundary/freeflowporenetwork/couplingmapper.hh:268
const auto & pnmElementToFreeFlowElementsMap() const
Definition: boundary/freeflowporenetwork/couplingmapper.hh:265
bool isCoupledFreeFlowMomentumDof(std::size_t dofIdx) const
Return if an element residual with index eIdx of domain i is coupled to domain j.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:229
const std::vector< std::size_t > & poreNetworkToFreeFlowMassCouplingStencil(const std::size_t eIdxI) const
returns an iterable container of all indices of degrees of freedom of domain j that couple with / inf...
Definition: boundary/freeflowporenetwork/couplingmapper.hh:184
bool isCoupledFreeFlowElement(std::size_t eIdx) const
Return if an element residual with index eIdx of domain i is coupled to domain j.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:221
bool isCoupledPoreNetworkElement(std::size_t eIdx) const
Return if an element residual with index eIdx of domain i is coupled to domain j.
Definition: boundary/freeflowporenetwork/couplingmapper.hh:237
A function to compute a geometry's diameter, i.e. the longest distance between points of a geometry.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition: localview.hh:26
static std::size_t normalAxis(const Vector &v)
Returns the normal axis index of a unit vector (0 = x, 1 = y, 2 = z)
Definition: normalaxis.hh:26
bool intersectsPointGeometry(const Dune::FieldVector< ctype, dimworld > &point, const Geometry &g)
Find out whether a point is inside a three-dimensional geometry.
Definition: intersectspointgeometry.hh:28
Vector normal(const Vector &v)
Create a vector normal to the given one (v is expected to be non-zero)
Definition: normal.hh:26
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: intersectingentities.hh:102
Geometry::ctype diameter(const Geometry &geo)
Computes the longest distance between points of a geometry.
Definition: diameter.hh:26
Algorithms that finds which geometric entities intersect.
The available discretization methods in Dumux.
Definition: adapt.hh:17