version 3.11-dev
Loading...
Searching...
No Matches
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-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12
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
117 const Scalar couplingPoreRadius = pnmGridGeometry.poreInscribedRadius(pnmDofIdx);
118 const Scalar eps = couplingPoreRadius*relEps_;
119
120 GlobalPosition lowerLeft = pnmPos - GlobalPosition(couplingPoreRadius - eps);
121 lowerLeft[couplingNormalDirectionIndex] = pnmPos[couplingNormalDirectionIndex];
122 GlobalPosition upperRight = pnmPos + GlobalPosition(couplingPoreRadius - eps);
123 upperRight[couplingNormalDirectionIndex] = pnmPos[couplingNormalDirectionIndex];
124
125 auto axes = std::move(std::bitset<FreeFlowMomentumGridGeometry::Grid::dimensionworld>{}.set());
126 axes.set(couplingNormalDirectionIndex, false);
127
128 using PoreIntersectionGeometryType = Dune::AxisAlignedCubeGeometry<Scalar,
129 FreeFlowMomentumGridGeometry::GridView::dimension-1,
130 FreeFlowMomentumGridGeometry::GridView::dimensionworld>;
131
132 PoreIntersectionGeometryType poreIntersectionGeometry(lowerLeft, upperRight, axes);
133 const auto allCoupledFreeFlowElements = intersectingEntities(std::move(poreIntersectionGeometry), ffMomentumGridGeometry.boundingBoxTree());
134
135
136 for (const auto& ffElementInfo : allCoupledFreeFlowElements)
137 {
138 const auto freeFlowElementIndex = ffElementInfo.second();
139 pnmElementToFreeFlowElementsMap_[pnmElementIdx].push_back(freeFlowElementIndex);
140 freeFlowElementToPNMElementMap_[freeFlowElementIndex] = pnmElementIdx;
141
142 pnmToFreeFlowMassStencils_[pnmElementIdx].push_back(freeFlowElementIndex);
143 freeFlowMassToPNMStencils_[freeFlowElementIndex].push_back(pnmDofIdx);
144
145 ffFvGeometry.bindElement(ffMomentumGridGeometry.element(freeFlowElementIndex));
146 const auto coupledFreeFlowMomentumDofIndices = coupledFFMomentumDofs_(ffFvGeometry, ffMassGridGeometry, pnmPos, couplingPoreRadius, couplingNormalDirectionIndex);
147
148 pnmToFreeFlowMomentumStencils_[pnmElementIdx].push_back(coupledFreeFlowMomentumDofIndices.coupledFrontalDof);
149 freeFlowMomentumToPNMStencils_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof].push_back(pnmDofIdx);
150 freeFlowMomentumToPNMStencils_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof].push_back(otherPNMScvDofIdx);
151
152 isCoupledFreeFlowMomentumDof_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof] = true;
153 isCoupledFreeFlowMomentumDofOnInterface_[coupledFreeFlowMomentumDofIndices.coupledFrontalDof] = true;
154
155 // treat the coupled ff dofs not directly on interface
156 for (const auto ffDofIdx : coupledFreeFlowMomentumDofIndices.coupledLateralDofs)
157 {
158 freeFlowMomentumToPNMStencils_[ffDofIdx].push_back(pnmDofIdx);
159 freeFlowMomentumToPNMStencils_[ffDofIdx].push_back(otherPNMScvDofIdx);
160 isCoupledFreeFlowMomentumDof_[ffDofIdx] = true;
161 }
162 }
163 }
164 }
165
166 std::cout << "took " << watch.elapsed() << " seconds." << std::endl;
167 }
168
174 const std::vector<std::size_t>& poreNetworkToFreeFlowMomentumCouplingStencil(const std::size_t eIdxI) const
175 {
177 return pnmToFreeFlowMomentumStencils_.at(eIdxI);
178 else
179 return emptyStencil_;
180 }
181
187 const std::vector<std::size_t>& poreNetworkToFreeFlowMassCouplingStencil(const std::size_t eIdxI) const
188 {
190 return pnmToFreeFlowMassStencils_.at(eIdxI);
191 else
192 return emptyStencil_;
193 }
194
200 const std::vector<std::size_t>& freeFlowMassToPoreNetworkCouplingStencil(const std::size_t eIdxI) const
201 {
202 if (isCoupledFreeFlowElement(eIdxI))
203 return freeFlowMassToPNMStencils_.at(eIdxI);
204 else
205 return emptyStencil_;
206 }
207
213 const std::vector<std::size_t>& freeFlowMomentumToPoreNetworkCouplingStencil(const std::size_t dofIndex) const
214 {
215 if (isCoupledFreeFlowMomentumDof(dofIndex))
216 return freeFlowMomentumToPNMStencils_.at(dofIndex);
217 else
218 return emptyStencil_;
219 }
220
224 bool isCoupledFreeFlowElement(std::size_t eIdx) const
225 {
226 return static_cast<bool>(freeFlowElementToPNMElementMap_.count(eIdx));
227 }
228
232 bool isCoupledFreeFlowMomentumDof(std::size_t dofIdx) const
233 {
234 return isCoupledFreeFlowMomentumDof_[dofIdx];
235 }
236
240 bool isCoupledPoreNetworkElement(std::size_t eIdx) const
241 {
242 return static_cast<bool>(pnmElementToFreeFlowElementsMap_.count(eIdx));
243 }
244
248 bool isCoupledPoreNetworkDof(std::size_t dofIdx) const
249 {
250 return isCoupledPNMDof_[dofIdx];
251 }
252
253 bool isCoupledFreeFlowMomentumScvf(std::size_t scvfIdx) const
254 {
255 return isCoupledFrontalFreeFlowMomentumScvf_.count(scvfIdx);
256 }
257
258 bool isCoupledFreeFlowMomentumLateralScvf(std::size_t scvfIdx) const
259 {
260 return isCoupledLateralFreeFlowMomentumScvf_.count(scvfIdx);
261 }
262
263 bool isCoupledFreeFlowMassScvf(std::size_t scvfIdx) const
264 {
265 return isCoupledFreeFlowMassScvf_.count(scvfIdx);
266 }
267
269 { return pnmElementToFreeFlowElementsMap_;}
270
272 { return freeFlowElementToPNMElementMap_; }
273
274private:
275
276 void clear_()
277 {
278 pnmElementToFreeFlowElementsMap_.clear();
279 freeFlowElementToPNMElementMap_.clear();
280 isCoupledPNMDof_.clear();
281 isCoupledFreeFlowMomentumDof_.clear();
282 isCoupledFreeFlowMomentumDofOnInterface_.clear();
283 pnmToFreeFlowMassStencils_.clear();
284 pnmToFreeFlowMomentumStencils_.clear();
285 freeFlowMassToPNMStencils_.clear();
286 freeFlowMomentumToPNMStencils_.clear();
287 }
288
289 template<class FreeFlowMomentumGridGeometry, class PoreNetworkGridGeometry>
290 void resize_(const FreeFlowMomentumGridGeometry& ffMomentumGridGeometry,
291 const PoreNetworkGridGeometry& pnmGridGeometry)
292 {
293 const auto numPNMDofs = pnmGridGeometry.numDofs();
294 const auto numFreeFlowMomentumDofs = ffMomentumGridGeometry.numDofs();
295 isCoupledPNMDof_.resize(numPNMDofs, false);
296 isCoupledFreeFlowMomentumDof_.resize(numFreeFlowMomentumDofs, false);
297 isCoupledFreeFlowMomentumDofOnInterface_.resize(numFreeFlowMomentumDofs, false);
298 }
299
301 template<class FVElementGeometry, class FreeFlowMassGridGeometry, class GlobalPosition, class Scalar>
302 auto coupledFFMomentumDofs_(const FVElementGeometry& fvGeometry,
303 const FreeFlowMassGridGeometry& ffMassGridGeometry,
304 const GlobalPosition& pnmPos,
305 const Scalar couplingPoreRadius,
306 const int couplingInterfaceDirectionIdx)
307 {
308
309 struct Result
310 {
311 Dune::ReservedVector<std::size_t, FVElementGeometry::maxNumElementScvs> coupledLateralDofs;
312 std::size_t coupledFrontalDof;
313 } result;
314
315
316 using std::abs;
317 for (const auto& scv : scvs(fvGeometry))
318 {
319 const Scalar eps = couplingPoreRadius*relEps_;
320
321 if (scv.dofAxis() == couplingInterfaceDirectionIdx) // the free flow dofs that lie within the coupling interface
322 {
323 if (abs(scv.dofPosition()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
324 {
325 result.coupledFrontalDof = scv.dofIndex();
326
327 // treat scvfs
328 for (const auto& scvf : scvfs(fvGeometry, scv))
329 {
330 // add lateral faces "standing" on coupling interface
331 if (scvf.isLateral() && !scvf.boundary())
332 isCoupledLateralFreeFlowMomentumScvf_[scvf.index()] = true;
333 else if (scvf.isFrontal() && scvf.boundary()) // add face lying on interface
334 {
335 isCoupledFrontalFreeFlowMomentumScvf_[scvf.index()] = true;
336
337 const auto& element = ffMassGridGeometry.element(fvGeometry.elementIndex()); // this local variable is needed to prevent a memory error
338 const auto ffMassFVGeometry = localView(ffMassGridGeometry).bindElement(element);
339 for (const auto& ffMassScvf : scvfs(ffMassFVGeometry))
340 {
341 if (abs(ffMassScvf.center()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
342 isCoupledFreeFlowMassScvf_[ffMassScvf.index()] = true;
343 }
344 }
345 }
346 }
347 }
348 else // the free flow dofs perpendicular to the coupling interface
349 {
350 bool isCoupledDof = false;
351
352 for (int dimIdx = 0; dimIdx < GlobalPosition::dimension; ++dimIdx)
353 {
354 if (dimIdx == couplingInterfaceDirectionIdx || scv.boundary())
355 continue;
356
357 isCoupledDof = abs(scv.dofPosition()[dimIdx] - pnmPos[dimIdx]) < couplingPoreRadius + eps;
358 }
359
360 if (isCoupledDof)
361 {
362 result.coupledLateralDofs.push_back(scv.dofIndex());
363
364 // treat scvfs
365 for (const auto& scvf : scvfs(fvGeometry, scv))
366 {
367 if (scvf.isLateral() && scvf.boundary())
368 {
369 // add lateral scvfs lying on interface
370 if (abs(scvf.ipGlobal()[couplingInterfaceDirectionIdx] - pnmPos[couplingInterfaceDirectionIdx]) < eps)
371 isCoupledLateralFreeFlowMomentumScvf_[scvf.index()] = true;
372 }
373 }
374 }
375 }
376 }
377
378 return result;
379 }
380
381 std::vector<std::size_t> emptyStencil_;
382
383 std::unordered_map<std::size_t, std::vector<std::size_t>> pnmElementToFreeFlowElementsMap_;
384 std::unordered_map<std::size_t, std::size_t> freeFlowElementToPNMElementMap_;
385
386 std::vector<bool> isCoupledPNMDof_;
387 std::vector<bool> isCoupledFreeFlowMomentumDof_;
388 std::vector<bool> isCoupledFreeFlowMomentumDofOnInterface_;
389
390 std::unordered_map<std::size_t, bool> isCoupledLateralFreeFlowMomentumScvf_;
391 std::unordered_map<std::size_t, bool> isCoupledFrontalFreeFlowMomentumScvf_;
392 std::unordered_map<std::size_t, bool> isCoupledFreeFlowMassScvf_;
393
394 MapType pnmToFreeFlowMassStencils_;
395 MapType pnmToFreeFlowMomentumStencils_;
396 MapType freeFlowMassToPNMStencils_;
397 MapType freeFlowMomentumToPNMStencils_;
398
399 static constexpr double relEps_ = 1e-6;
400};
401
402} // end namespace Dumux
403
404#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:258
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:200
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:213
bool isCoupledFreeFlowMomentumScvf(std::size_t scvfIdx) const
Definition boundary/freeflowporenetwork/couplingmapper.hh:253
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:248
bool isCoupledFreeFlowMassScvf(std::size_t scvfIdx) const
Definition boundary/freeflowporenetwork/couplingmapper.hh:263
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:174
const auto & freeFlowElementToPNMElementMap() const
Definition boundary/freeflowporenetwork/couplingmapper.hh:271
const auto & pnmElementToFreeFlowElementsMap() const
Definition boundary/freeflowporenetwork/couplingmapper.hh:268
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:232
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:187
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:224
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:240
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::pair< int, std::size_t > > intersectingEntities(const Dune::FieldVector< ctype, dimworld > &point, const DistributedBoundingBoxTree< EntitySet > &tree, bool isCartesianGrid=false, bool onlyOwned=true)
Compute all intersections between entities and a point on a distributed tree.
Definition distributedintersectingentities.hh:81
@ element
Definition fieldtype.hh:23
Algorithms that finds which geometric entities intersect.
The available discretization methods in Dumux.
Definition adapt.hh:17
std::ranges::range auto scvs(const FVElementGeometry &fvGeometry, const LocalDof &localDof)
Definition localdof.hh:82
Base class for the finite volume geometry vector for face-centered staggered models This builds up th...