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 DarcyDarcyCoupling | ||
10 | * \copydoc Dumux::DarcyDarcyBoundaryCouplingMapper | ||
11 | */ | ||
12 | |||
13 | #ifndef DUMUX_MULTIDOMAIN_DARCYDARCY_BOUNDARY_COUPLINGMAPPER_HH | ||
14 | #define DUMUX_MULTIDOMAIN_DARCYDARCY_BOUNDARY_COUPLINGMAPPER_HH | ||
15 | |||
16 | #include <iostream> | ||
17 | #include <unordered_map> | ||
18 | #include <tuple> | ||
19 | #include <vector> | ||
20 | |||
21 | #include <dune/common/timer.hh> | ||
22 | #include <dune/common/exceptions.hh> | ||
23 | #include <dune/common/indices.hh> | ||
24 | |||
25 | #include <dumux/geometry/intersectingentities.hh> | ||
26 | #include <dumux/discretization/method.hh> | ||
27 | |||
28 | namespace Dumux { | ||
29 | |||
30 | /*! | ||
31 | * \ingroup DarcyDarcyCoupling | ||
32 | * \brief the default mapper for conforming equal dimension boundary coupling between two domains (box or cc) | ||
33 | * \todo how to extend to arbitrary number of domains? | ||
34 | */ | ||
35 | template<class MDTraits> | ||
36 | class DarcyDarcyBoundaryCouplingMapper | ||
37 | { | ||
38 | using Scalar = typename MDTraits::Scalar; | ||
39 | |||
40 | template<std::size_t i> using GridGeometry = typename MDTraits::template SubDomain<i>::GridGeometry; | ||
41 | template<std::size_t i> using SubControlVolumeFace = typename GridGeometry<i>::SubControlVolumeFace; | ||
42 | template<std::size_t i> using GridView = typename GridGeometry<i>::GridView; | ||
43 | template<std::size_t i> using Element = typename GridView<i>::template Codim<0>::Entity; | ||
44 | |||
45 | template<std::size_t i> | ||
46 | static constexpr auto domainIdx() | ||
47 | { return typename MDTraits::template SubDomain<i>::Index{}; } | ||
48 | |||
49 | template<std::size_t i> | ||
50 | static constexpr bool isCCTpfa() | ||
51 | { return GridGeometry<i>::discMethod == DiscretizationMethods::cctpfa; } | ||
52 | |||
53 | struct ScvfInfo | ||
54 | { | ||
55 | std::size_t eIdxOutside; | ||
56 | std::size_t flipScvfIdx; | ||
57 | }; | ||
58 | |||
59 | using FlipScvfMapType = std::unordered_map<std::size_t, ScvfInfo>; | ||
60 | using MapType = std::unordered_map<std::size_t, std::vector<std::size_t>>; | ||
61 | |||
62 | static constexpr std::size_t numSD = MDTraits::numSubDomains; | ||
63 | |||
64 | public: | ||
65 | /*! | ||
66 | * \brief Main update routine | ||
67 | */ | ||
68 | template<class CouplingManager> | ||
69 | 17 | void update(const CouplingManager& couplingManager) | |
70 | { | ||
71 | // TODO: Box and multiple domains | ||
72 | static_assert(numSD == 2, "More than two subdomains not implemented!"); | ||
73 | static_assert(isCCTpfa<0>() && isCCTpfa<1>(), "Only cctpfa implemented!"); | ||
74 | |||
75 | 17 | Dune::Timer watch; | |
76 | 34 | std::cout << "Initializing the coupling map..." << std::endl; | |
77 | |||
78 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 17 times.
|
51 | for (std::size_t domIdx = 0; domIdx < numSD; ++domIdx) |
79 | { | ||
80 | 68 | stencils_[domIdx].clear(); | |
81 | 68 | scvfInfo_[domIdx].clear(); | |
82 | } | ||
83 | |||
84 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | const auto& problem0 = couplingManager.problem(domainIdx<0>()); |
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | const auto& problem1 = couplingManager.problem(domainIdx<1>()); |
86 | 17 | const auto& gg0 = problem0.gridGeometry(); | |
87 | 17 | const auto& gg1 = problem1.gridGeometry(); | |
88 | |||
89 | 48 | isCoupledScvf_[0].resize(gg0.numScvf(), false); | |
90 | 34 | isCoupledScvf_[1].resize(gg1.numScvf(), false); | |
91 | |||
92 |
2/4✓ Branch 2 taken 136795 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 50 times.
✗ Branch 6 not taken.
|
273590 | for (const auto& element0 : elements(gg0.gridView())) |
93 | { | ||
94 |
1/2✓ Branch 1 taken 1578 times.
✗ Branch 2 not taken.
|
138356 | auto fvGeometry0 = localView(gg0); |
95 |
1/2✓ Branch 1 taken 1578 times.
✗ Branch 2 not taken.
|
136778 | fvGeometry0.bindElement(element0); |
96 | |||
97 |
6/6✓ Branch 0 taken 547112 times.
✓ Branch 1 taken 136778 times.
✓ Branch 2 taken 547112 times.
✓ Branch 3 taken 136778 times.
✓ Branch 4 taken 4640 times.
✓ Branch 5 taken 536160 times.
|
820668 | for (const auto& scvf0 : scvfs(fvGeometry0)) |
98 | { | ||
99 | // skip all non-boundaries | ||
100 |
2/2✓ Branch 0 taken 4990 times.
✓ Branch 1 taken 542122 times.
|
547112 | if (!scvf0.boundary()) |
101 | 545822 | continue; | |
102 | |||
103 | // get elements intersecting with the scvf center | ||
104 | // for robustness add epsilon in unit outer normal direction | ||
105 | 19960 | const auto eps = (scvf0.ipGlobal() - element0.geometry().corner(0)).two_norm()*1e-8; | |
106 | 9980 | auto globalPos = scvf0.ipGlobal(); globalPos.axpy(eps, scvf0.unitOuterNormal()); | |
107 |
2/7✓ Branch 1 taken 350 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 350 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
11270 | const auto indices = intersectingEntities(globalPos, gg1.boundingBoxTree()); |
108 | |||
109 | // skip if no intersection was found | ||
110 |
4/4✓ Branch 0 taken 3700 times.
✓ Branch 1 taken 1290 times.
✓ Branch 2 taken 3700 times.
✓ Branch 3 taken 1290 times.
|
9980 | if (indices.empty()) |
111 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3700 times.
|
3700 | continue; |
112 | |||
113 | // sanity check | ||
114 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1290 times.
|
2580 | if (indices.size() > 1) |
115 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Are you sure your grids is conforming at the boundary?"); | |
116 | |||
117 | // add the pair to the multimap | ||
118 |
2/4✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 120 times.
✗ Branch 5 not taken.
|
2580 | const auto eIdx0 = gg0.elementMapper().index(element0); |
119 | 1290 | const auto eIdx1 = indices[0]; | |
120 |
3/8✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1290 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
2580 | stencils_[0][eIdx0].push_back(eIdx1); |
121 | |||
122 | // mark the scvf and find and mark the flip scvf | ||
123 |
3/6✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1290 times.
✗ Branch 8 not taken.
|
3870 | isCoupledScvf_[0][scvf0.index()] = true; |
124 |
1/2✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
|
1290 | const auto& element1 = gg1.element(eIdx1); |
125 |
2/4✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
|
2580 | auto fvGeometry1 = localView(gg1); |
126 |
1/2✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
|
1290 | fvGeometry1.bindElement(element1); |
127 | |||
128 | using std::abs; | ||
129 |
4/4✓ Branch 0 taken 5160 times.
✓ Branch 1 taken 1290 times.
✓ Branch 2 taken 5160 times.
✓ Branch 3 taken 1290 times.
|
7740 | for (const auto& scvf1 : scvfs(fvGeometry1)) |
130 |
2/2✓ Branch 0 taken 1376 times.
✓ Branch 1 taken 3784 times.
|
5160 | if (scvf1.boundary()) |
131 |
4/4✓ Branch 0 taken 1290 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 1290 times.
✓ Branch 3 taken 86 times.
|
2752 | if (abs(scvf1.unitOuterNormal()*scvf0.unitOuterNormal() + 1) < eps) |
132 | { | ||
133 |
3/6✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1290 times.
✗ Branch 8 not taken.
|
3870 | isCoupledScvf_[1][scvf1.index()] = true; |
134 |
2/4✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
|
2580 | scvfInfo_[0][scvf0.index()] = ScvfInfo{eIdx1, scvf1.index()}; |
135 |
2/4✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
|
2580 | scvfInfo_[1][scvf1.index()] = ScvfInfo{eIdx0, scvf0.index()}; |
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
140 | // create the inverse map for efficient access | ||
141 |
2/2✓ Branch 0 taken 1262 times.
✓ Branch 1 taken 17 times.
|
1330 | for (const auto& entry : stencils_[0]) |
142 |
4/4✓ Branch 0 taken 1290 times.
✓ Branch 1 taken 1262 times.
✓ Branch 2 taken 1290 times.
✓ Branch 3 taken 1262 times.
|
5076 | for (const auto idx : entry.second) |
143 |
3/6✓ Branch 1 taken 1290 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1290 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1290 times.
✗ Branch 8 not taken.
|
2580 | stencils_[1][idx].push_back(entry.first); |
144 | |||
145 | 51 | std::cout << "took " << watch.elapsed() << " seconds." << std::endl; | |
146 | 17 | } | |
147 | |||
148 | /*! | ||
149 | * \brief returns an iterable container of all indices of degrees of freedom of domain j | ||
150 | * that couple with / influence the element residual of the given element of domain i | ||
151 | * | ||
152 | * \param domainI the domain index of domain i | ||
153 | * \param eIdxI the index of the coupled element of domain í | ||
154 | * \param domainJ the domain index of domain j | ||
155 | * | ||
156 | * \note The element residual definition depends on the discretization scheme of domain i | ||
157 | * box: a container of the residuals of all sub control volumes | ||
158 | * cc : the residual of the (sub) control volume | ||
159 | * fem: the residual of the element | ||
160 | * \note This function has to be implemented by all coupling managers for all combinations of i and j | ||
161 | */ | ||
162 | template<std::size_t i, std::size_t j> | ||
163 | ✗ | const std::vector<std::size_t>& couplingStencil(Dune::index_constant<i> domainI, | |
164 | const std::size_t eIdxI, | ||
165 | Dune::index_constant<j> domainJ) const | ||
166 | { | ||
167 | static_assert(i != j, "A domain cannot be coupled to itself!"); | ||
168 | ✗ | if (isCoupledElement(domainI, eIdxI)) | |
169 | ✗ | return stencils_[i].at(eIdxI); | |
170 | else | ||
171 | ✗ | return emptyStencil_; | |
172 | } | ||
173 | |||
174 | /*! | ||
175 | * \brief Return if an element residual with index eIdx of domain i is coupled to domain j | ||
176 | */ | ||
177 | template<std::size_t i> | ||
178 | ✗ | bool isCoupledElement(Dune::index_constant<i>, std::size_t eIdx) const | |
179 |
4/10✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 5440 times.
✓ Branch 7 taken 368040 times.
✓ Branch 10 taken 3160 times.
✓ Branch 11 taken 368040 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
1489360 | { return static_cast<bool>(stencils_[i].count(eIdx)); } |
180 | |||
181 | /*! | ||
182 | * \brief If the boundary entity is on a coupling boundary | ||
183 | * \param domainI the domain index for which to compute the flux | ||
184 | * \param scvf the sub control volume face | ||
185 | */ | ||
186 | template<std::size_t i> | ||
187 | ✗ | bool isCoupled(Dune::index_constant<i> domainI, | |
188 | const SubControlVolumeFace<i>& scvf) const | ||
189 | { | ||
190 |
0/15✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
388652 | return isCoupledScvf_[i].at(scvf.index()); |
191 | } | ||
192 | |||
193 | /*! | ||
194 | * \brief Return the scvf index of the flipped scvf in the other domain | ||
195 | * \param domainI the domain index for which to compute the flux | ||
196 | * \param scvf the sub control volume face | ||
197 | */ | ||
198 | template<std::size_t i> | ||
199 | ✗ | std::size_t flipScvfIndex(Dune::index_constant<i> domainI, | |
200 | const SubControlVolumeFace<i>& scvf) const | ||
201 | { | ||
202 |
8/14✓ Branch 1 taken 54392 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 54392 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5440 times.
✓ Branch 8 taken 51624 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3160 times.
✓ Branch 11 taken 51624 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 3160 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3160 times.
✗ Branch 17 not taken.
|
218352 | return scvfInfo_[i].at(scvf.index()).flipScvfIdx; |
203 | } | ||
204 | |||
205 | /*! | ||
206 | * \brief Return the outside element index (the element index of the other domain) | ||
207 | * \param domainI the domain index for which to compute the flux | ||
208 | * \param scvf the sub control volume face | ||
209 | */ | ||
210 | template<std::size_t i> | ||
211 | ✗ | std::size_t outsideElementIndex(Dune::index_constant<i> domainI, | |
212 | const SubControlVolumeFace<i>& scvf) const | ||
213 | { | ||
214 |
2/4✗ Branch 2 not taken.
✓ Branch 3 taken 5440 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3160 times.
|
218352 | return scvfInfo_[i].at(scvf.index()).eIdxOutside; |
215 | } | ||
216 | |||
217 | private: | ||
218 | std::array<MapType, numSD> stencils_; | ||
219 | std::vector<std::size_t> emptyStencil_; | ||
220 | std::array<std::vector<bool>, numSD> isCoupledScvf_; | ||
221 | std::array<FlipScvfMapType, numSD> scvfInfo_; | ||
222 | }; | ||
223 | |||
224 | } // end namespace Dumux | ||
225 | |||
226 | #endif | ||
227 |