GCC Code Coverage Report


Directory: Bembel/src/
File: Bembel/src/AnsatzSpace/Glue.hpp
Date: 2024-09-30 07:01:38
Exec Total Coverage
Lines: 193 218 88.5%
Functions: 65 70 92.9%
Branches: 160 249 64.3%

Line Branch Exec Source
1 // This file is part of Bembel, the higher order C++ boundary element library.
2 //
3 // Copyright (C) 2022 see <http://www.bembel.eu>
4 //
5 // It was written as part of a cooperation of J. Doelz, H. Harbrecht, S. Kurz,
6 // M. Multerer, S. Schoeps, and F. Wolf at Technische Universitaet Darmstadt,
7 // Universitaet Basel, and Universita della Svizzera italiana, Lugano. This
8 // source code is subject to the GNU General Public License version 3 and
9 // provided WITHOUT ANY WARRANTY, see <http://www.bembel.eu> for further
10 // information.
11 #ifndef BEMBEL_SRC_ANSATZSPACE_GLUE_HPP_
12 #define BEMBEL_SRC_ANSATZSPACE_GLUE_HPP_
13
14 namespace Bembel {
15 /*
16
17 Every edge has an index between 0 and 3, where
18 0 = (0,0)->(1,0)
19 1 = (1,0)->(1,1)
20 2 = (1,1)->(0,1)
21 3 = (0,1)->(0,0)
22 */
23
24 /*
25 Example of Dof enumeration on one patch:
26 dimXdir = 4;
27 dimYdir = 3;
28 Edge 2 (1,1)
29
30 y |8 9 10 11|
31 A Edge 3 |4 5 6 7 | Edge 1
32 | |0 1 2 3 |
33
34 Edge 0
35 (0,0) -> x
36 */
37
38 namespace GlueRoutines {
39 /**
40 * \brief Helper struct.
41 */
42 struct dofIdentification {
43 std::vector<int> dofs;
44 int coef;
45 };
46 /**
47 * \brief Helper struct for assembling the Glue for the different cases.
48 */
49 template <typename Derived, unsigned int DF>
50 struct glue_identificationmaker_ {
51 static std::vector<dofIdentification> makeIdentification(
52 const std::vector<std::array<int, 4>> &edges_,
53 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
54 assert(false && "Needs to be specialized");
55 return {};
56 }
57 };
58
59 } // namespace GlueRoutines
60
61 /**
62 * \ingroup AnsatzSpace
63 * \brief This class takes care of identifying DOFs on different edges, which
64 *must be identified with one another.
65 *
66 * The Glue class has routines to assemble continuous B-splines across patch
67 *boundaries depending on the template parameter
68 *LinearOperatorTraits<Derived>::Form. If this template parameter is equal to
69 *DifferentialForm::Continuous, the B-splines are globally glued continuously.
70 *Otherwise, it can also be DifferentialForm::DivConforming, in which case the
71 *normal component is glued continuously. Finally, there is the option of not
72 *gluing the B-splines at all if the parameter is
73 *DifferentialForm::Discontinuous.
74 **/
75 template <typename Derived>
76 class Glue {
77 std::vector<std::array<int, 4>> edges_;
78 int dofs_after_glue;
79 Eigen::SparseMatrix<double> glue_mat_;
80
81 public:
82 /**
83 * \brief Constructor for the Glue class.
84 *
85 * This constructor initializes a Glue object with the provided SuperSpace and
86 * Projector objects.
87 *
88 * \param superspace The SuperSpace to handle the basis functions.
89 * \param proj The Projector which provides information on the dofs before
90 * gluing.
91 */
92
1/2
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
272 Glue(const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
93
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
272 init_Glue(superspace, proj);
94 272 }
95 /**
96 * \brief Initializes the AnsatzSpace object.
97 *
98 * This function initializes the member variables of the Glue object.
99 *
100 * \param superspace The SuperSpace to handle the basis functions.
101 * \param proj The Projector which provides information on the dofs before
102 * gluing.
103 */
104 272 void init_Glue(const SuperSpace<Derived> &superspace,
105 const Projector<Derived> &proj) {
106 272 edges_ = superspace.get_mesh().get_element_tree().patchTopologyInfo();
107
1/2
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
272 glue_mat_ = assembleGlueMatrix(superspace, proj);
108 272 return;
109 }
110 /**
111 * \brief Returns Glue matrix to assemble continuous B-splines.
112 *
113 * This function returns the Glue matrix which transforms smooth B-splines to
114 * continuous shape functions across patch boundaries. If continuity is not
115 * needed this matrix is the identity. Other wise this matrix is a tall thin
116 * matrix combining degrees of freedom.
117 *
118 * \return Eigen::SparseMatrix<double> Glue matrix
119 */
120 272 Eigen::SparseMatrix<double> get_glue_matrix() const { return glue_mat_; }
121 /**
122 * \brief Generates a list of degrees of freedom (DOFs) identifications.
123 *
124 * This function creates a list of degrees of freedom (DOFs) identifications
125 * which need to be glued together. The return type is a std::vector of
126 * GlueRoutines::dofIdentification which collects the DOFs to be glued.
127 * Furthermore, the coef in this struct denotes the orientation.
128 *
129 * \param superspace The SuperSpace reference to handle basis functions.
130 * \param proj The Projector contains the number of DOFs.
131 * \return A vector of GlueRoutines::dofIdentification containing the DOF
132 * identifications.
133 */
134 272 inline std::vector<GlueRoutines::dofIdentification> makeDofIdentificationList(
135 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
136 return GlueRoutines::glue_identificationmaker_<
137 Derived,
138 272 LinearOperatorTraits<Derived>::Form>::makeIdentification(edges_,
139 superspace,
140 272 proj);
141 }
142 /**
143 * \brief Assembles the Glue matrix according to the DOF identification list.
144 *
145 * This function first sorts the DOFs in each identification list. Than the
146 * identification lists get sorted according to the first entries. After that
147 * the DOFs get stored in the Glue matrix.
148 *
149 * \param superspace The SuperSpace reference to handle basis functions.
150 * \param proj The Projector contains the number of DOFs.
151 * \return The Glue matrix.
152 */
153 272 Eigen::SparseMatrix<double> assembleGlueMatrix(
154 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
155 272 const int pre_dofs = proj.get_dofs_after_projector();
156 // The dofs that need to be identified with each other are divided into
157 // master and slaves, where the master is the firs w.r.t. the tp-ordering
158 272 std::vector<Eigen::Triplet<double>> trips;
159
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
272 std::vector<GlueRoutines::dofIdentification> dof_id =
160 makeDofIdentificationList(superspace, proj);
161
162 // We keep track of certain information
163
1/2
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
272 std::vector<bool> dof_is_slave(pre_dofs, false);
164
1/2
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
272 std::vector<bool> dof_is_master(pre_dofs, false);
165
166 // Here, we fill the two vectors above
167 272 int number_of_slaves = 0;
168
3/4
✓ Branch 4 taken 2764 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 2764 times.
✓ Branch 10 taken 144 times.
5724 for (auto dofset : dof_id) {
169 // This sorting is required, since by construction only dofs[0]<dofs[1] is
170 // given, but in the case of corners and H1 discretizations, it might
171 // happen that dofs[0]>dofs[2]. Moreover, we count how many dofs we "glue
172 // away".
173
1/2
✓ Branch 3 taken 2764 times.
✗ Branch 4 not taken.
5452 std::sort(dofset.dofs.begin(), dofset.dofs.end());
174
1/2
✓ Branch 2 taken 2764 times.
✗ Branch 3 not taken.
5452 dof_is_master[dofset.dofs[0]] = true;
175
2/2
✓ Branch 1 taken 3036 times.
✓ Branch 2 taken 2764 times.
11432 for (int i = 1; i < dofset.dofs.size(); ++i) {
176
1/2
✓ Branch 2 taken 3036 times.
✗ Branch 3 not taken.
5980 dof_is_slave[dofset.dofs[i]] = true;
177 5980 ++number_of_slaves;
178 }
179 }
180
181 // Now, we sort w.r.t. the masters.
182
1/2
✓ Branch 3 taken 144 times.
✗ Branch 4 not taken.
272 std::sort(dof_id.begin(), dof_id.end(),
183 14300 [](GlueRoutines::dofIdentification a,
184 GlueRoutines::dofIdentification b) {
185 14300 return a.dofs[0] < b.dofs[0];
186 });
187
188 272 const int post_dofs = pre_dofs - number_of_slaves;
189
190 // This block is the heart of the algorithms. Skip keeps track of how many
191 // slaves have been skipped already, and master_index keeps track on which
192 // master is about to be glued next.
193 272 int skip = 0;
194 272 int master_index = 0;
195
2/2
✓ Branch 0 taken 43584 times.
✓ Branch 1 taken 144 times.
82512 for (int i = 0; i < post_dofs; ++i) {
196 82240 const int post_index = i;
197
6/8
✓ Branch 1 taken 46586 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3002 times.
✓ Branch 5 taken 43584 times.
✓ Branch 6 taken 3002 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3002 times.
✓ Branch 9 taken 43584 times.
88186 while (dof_is_slave[i + skip] && ((i + skip) < pre_dofs)) ++skip;
198 82240 const int pre_index = i + skip;
199
1/2
✓ Branch 2 taken 43584 times.
✗ Branch 3 not taken.
82240 trips.push_back(Eigen::Triplet<double>(pre_index, post_index, 1));
200 // The dof cannot be declared slave and master at the same time
201
5/8
✓ Branch 1 taken 43584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2764 times.
✓ Branch 5 taken 40820 times.
✓ Branch 7 taken 2764 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 2764 times.
82240 assert(!(dof_is_master[pre_index] && dof_is_slave[pre_index]));
202
3/4
✓ Branch 1 taken 43584 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2764 times.
✓ Branch 5 taken 40820 times.
82240 if (dof_is_master[pre_index]) {
203 // The dofs in dof_id don't know they might be moved forward. So the
204 // smallest dof of those to be identified should coincide with the
205 // pre_index.
206
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2764 times.
5452 assert(pre_index == dof_id[master_index].dofs[0]);
207 5452 const int number_of_partners = dof_id[master_index].dofs.size();
208
2/2
✓ Branch 0 taken 3036 times.
✓ Branch 1 taken 2764 times.
11432 for (int j = 1; j < number_of_partners; ++j) {
209
1/2
✓ Branch 3 taken 3036 times.
✗ Branch 4 not taken.
5980 trips.push_back(Eigen::Triplet<double>(dof_id[master_index].dofs[j],
210 post_index,
211 5980 dof_id[master_index].coef));
212 }
213 5452 ++master_index;
214 }
215 }
216
217
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
272 Eigen::SparseMatrix<double> glue_matrix(pre_dofs, post_dofs);
218
1/2
✓ Branch 3 taken 144 times.
✗ Branch 4 not taken.
272 glue_matrix.setFromTriplets(trips.begin(), trips.end());
219
220
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
272 assert(trips.size() == pre_dofs);
221 544 return glue_matrix;
222 272 }
223 };
224
225 namespace GlueRoutines {
226
227 /**
228 * \brief This routine collects the indices of DOFs with support on an edge.
229 *
230 * The indices are computed depending on the edge case and number of DOFs in the
231 * x and y direction of the tensor product. The shift parameter can be used to
232 * shift all dofs according to the patch index or vector component.
233 *
234 * Edge Case
235 *
236 * 0 = (0,0)->(1,0)
237 *
238 * 1 = (1,0)->(1,1)
239 *
240 * 2 = (1,1)->(0,1)
241 *
242 * 3 = (0,1)->(0,0)
243 *
244 * \param edgeCase Edge case of the patch to collect the DOFs.
245 * \param dimXdir Number of DOFs in the x direction of the tensor product.
246 * \param dimYdir Number of DOFs in the y direction of the tensor product.
247 * \param shift Offset of the DOFs.
248 */
249 4200 std::vector<int> getEdgeDofIndices(int edgeCase, int dimXdir, int dimYdir,
250 int shift) {
251 4200 std::vector<int> out;
252
5/5
✓ Branch 0 taken 665 times.
✓ Branch 1 taken 665 times.
✓ Branch 2 taken 665 times.
✓ Branch 3 taken 665 times.
✓ Branch 4 taken 1540 times.
4200 switch (edgeCase) {
253 665 case (0): {
254
1/2
✓ Branch 1 taken 665 times.
✗ Branch 2 not taken.
665 out.reserve(dimXdir);
255
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 665 times.
665 assert(
256 "This should be a given. If not something went wrong in Glue.hpp" &&
257 dimXdir <= dimYdir);
258
2/2
✓ Branch 0 taken 3735 times.
✓ Branch 1 taken 665 times.
4400 for (int i = 0; i < dimXdir; ++i) {
259
1/2
✓ Branch 1 taken 3735 times.
✗ Branch 2 not taken.
3735 out.push_back(i + shift);
260 }
261 665 return out;
262 };
263 665 case (1): {
264
1/2
✓ Branch 1 taken 665 times.
✗ Branch 2 not taken.
665 out.reserve(dimYdir);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 665 times.
665 assert(
266 "This should be a given. If not something went wrong in Glue.hpp" &&
267 dimYdir <= dimXdir);
268
2/2
✓ Branch 0 taken 3735 times.
✓ Branch 1 taken 665 times.
4400 for (int i = 0; i < dimYdir; ++i) {
269
1/2
✓ Branch 1 taken 3735 times.
✗ Branch 2 not taken.
3735 out.push_back(dimXdir * (i + 1) - 1 + shift);
270 }
271 665 return out;
272 };
273 665 case (2): {
274
1/2
✓ Branch 1 taken 665 times.
✗ Branch 2 not taken.
665 out.reserve(dimXdir);
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 665 times.
665 assert(
276 "This should be a given. If not something went wrong in Glue.hpp" &&
277 dimXdir <= dimYdir);
278
2/2
✓ Branch 0 taken 3735 times.
✓ Branch 1 taken 665 times.
4400 for (int i = 0; i < dimXdir; ++i) {
279
1/2
✓ Branch 1 taken 3735 times.
✗ Branch 2 not taken.
3735 out.push_back(dimXdir * (dimYdir - 1) + i + shift);
280 }
281 665 return out;
282 };
283 665 case (3): {
284
1/2
✓ Branch 1 taken 665 times.
✗ Branch 2 not taken.
665 out.reserve(dimYdir);
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 665 times.
665 assert(
286 "This should be a given. If not something went wrong in Glue.hpp" &&
287 dimYdir <= dimXdir);
288
2/2
✓ Branch 0 taken 3735 times.
✓ Branch 1 taken 665 times.
4400 for (int i = 0; i < dimYdir; ++i) {
289
1/2
✓ Branch 1 taken 3735 times.
✗ Branch 2 not taken.
3735 out.push_back(dimXdir * i + shift);
290 }
291 665 return out;
292 };
293 1540 default: {
294 };
295 // An edge might have a -1 index. This occurs only when no partner could
296 // be found, and the -1 is the placeholder of the missing partner.
297 }
298 1540 return out;
299 }
300
301 // The following 7 functions figure out how edges and the vector components
302 // are oriented w.r.t. each other.
303 6160 inline bool edgeIsForwardParametrized(int edgeCase) {
304
4/4
✓ Branch 0 taken 5042 times.
✓ Branch 1 taken 1118 times.
✓ Branch 2 taken 1122 times.
✓ Branch 3 taken 3920 times.
6160 return edgeCase == 0 || edgeCase == 1;
305 }
306 3010 inline bool edgeIsBackwardsParametrized(int edgeCase) {
307 3010 return !(edgeIsForwardParametrized(edgeCase));
308 }
309 3092 inline bool normalComponentIsInwardDirected(int edgeCase) {
310
4/4
✓ Branch 0 taken 2530 times.
✓ Branch 1 taken 562 times.
✓ Branch 2 taken 564 times.
✓ Branch 3 taken 1966 times.
3092 return edgeCase == 0 || edgeCase == 3;
311 }
312 1516 inline bool normalComponentIsOutwardDirected(int edgeCase) {
313 1516 return !(normalComponentIsInwardDirected(edgeCase));
314 }
315
316 2100 inline bool reverseParametrized(const std::array<int, 4> &edge) {
317
2/2
✓ Branch 2 taken 910 times.
✓ Branch 3 taken 140 times.
3150 return ((edgeIsForwardParametrized(edge[2]) &&
318 1050 edgeIsForwardParametrized(edge[3])) ||
319
2/2
✓ Branch 2 taken 910 times.
✓ Branch 3 taken 140 times.
3010 (edgeIsBackwardsParametrized(edge[2]) &&
320 1050 edgeIsBackwardsParametrized(edge[3])))
321
4/4
✓ Branch 0 taken 1050 times.
✓ Branch 1 taken 1050 times.
✓ Branch 2 taken 1050 times.
✓ Branch 3 taken 910 times.
5250 ? true
322 2100 : false;
323 }
324
325 1052 inline int glueCoefficientDivergenceConforming(const std::array<int, 4> &edge) {
326
2/2
✓ Branch 2 taken 460 times.
✓ Branch 3 taken 64 times.
1576 return ((normalComponentIsInwardDirected(edge[2]) &&
327
2/2
✓ Branch 2 taken 528 times.
✓ Branch 3 taken 460 times.
1512 normalComponentIsInwardDirected(edge[3])) ||
328
2/2
✓ Branch 2 taken 450 times.
✓ Branch 3 taken 78 times.
1516 (normalComponentIsOutwardDirected(edge[2]) &&
329 528 normalComponentIsOutwardDirected(edge[3])))
330
2/2
✓ Branch 0 taken 524 times.
✓ Branch 1 taken 528 times.
2104 ? -1
331 1052 : 1;
332 }
333 6312 inline bool edgeToBeGluedInFirstComp(int edgeCase) {
334
4/4
✓ Branch 0 taken 5313 times.
✓ Branch 1 taken 999 times.
✓ Branch 2 taken 4314 times.
✓ Branch 3 taken 999 times.
6312 return (edgeCase == 0 || edgeCase == 2) ? false : true;
335 }
336
337 /**
338 * \brief Specification of the struct for the discontinuous case.
339 *
340 * Nothing needs to be glued.
341 */
342 template <typename Derived>
343 struct glue_identificationmaker_<Derived, DifferentialForm::Discontinuous> {
344 11 static std::vector<dofIdentification> makeIdentification(
345 const std::vector<std::array<int, 4>> &edges_,
346 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
347 11 return {};
348 }
349 };
350
351 /**
352 * \brief Specification of the struct for the scalar continuous case.
353 *
354 * The scalar case: Here, we have only one vector component to worry about.
355 * However, we need to take care of edges, since here multiple (more than 2)
356 * dofs will be reduced to one. This is also the reason we we work with the
357 * dofIdentification struct and not already with Eigen::Triplet. The function
358 * builds upon the fact, that px == py == p.
359 */
360 template <typename Derived>
361 struct glue_identificationmaker_<Derived, DifferentialForm::Continuous> {
362 66 static std::vector<dofIdentification> makeIdentification(
363 const std::vector<std::array<int, 4>> &edges_,
364 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
365 // We check if the space can even be continuous globally.
366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
66 assert(superspace.get_polynomial_degree() >= 1);
367 66 const int one_d_dim = superspace.get_polynomial_degree() + 1 +
368 66 proj.get_knot_repetition() *
369 66 ((1 << superspace.get_refinement_level()) - 1);
370 66 const int dofs_per_patch = one_d_dim * one_d_dim;
371
372 66 std::vector<GlueRoutines::dofIdentification> out;
373
1/2
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
66 out.reserve(edges_.size() * one_d_dim);
374
375
1/2
✓ Branch 3 taken 66 times.
✗ Branch 4 not taken.
66 std::vector<int> already_stored_in(proj.get_dofs_after_projector(), -1);
376 66 int d_count = 0;
377
2/2
✓ Branch 7 taken 1048 times.
✓ Branch 8 taken 66 times.
1114 for (auto edge : edges_) {
378 // The shift is to account for different patches, since the
379 // getEdgeDof-routines only can enumerate w.r.t. patch 0.
380 1048 const int shift_e1 = edge[0] * dofs_per_patch;
381 1048 const int shift_e2 = edge[1] * dofs_per_patch;
382 1048 const bool needReversion = reverseParametrized(edge);
383
384
1/2
✓ Branch 1 taken 1048 times.
✗ Branch 2 not taken.
1048 std::vector<int> dofs_e1 =
385 1048 getEdgeDofIndices(edge[2], one_d_dim, one_d_dim, shift_e1);
386
1/2
✓ Branch 1 taken 1048 times.
✗ Branch 2 not taken.
1048 std::vector<int> dofs_e2 =
387 1048 getEdgeDofIndices(edge[3], one_d_dim, one_d_dim, shift_e2);
388
389 // Check if the edge is hanging. For screens one would need an "else".
390
5/6
✓ Branch 1 taken 280 times.
✓ Branch 2 taken 768 times.
✓ Branch 4 taken 280 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 280 times.
✓ Branch 7 taken 768 times.
1048 if (edge[1] > -1 && edge[0] > -1) {
391
2/2
✓ Branch 1 taken 1648 times.
✓ Branch 2 taken 280 times.
1928 for (int i = 0; i < one_d_dim; ++i) {
392 1648 GlueRoutines::dofIdentification d;
393
2/2
✓ Branch 0 taken 824 times.
✓ Branch 1 taken 824 times.
1648 const int j = needReversion ? one_d_dim - 1 - i : i;
394 // const int j = i;
395
1/2
✓ Branch 2 taken 1648 times.
✗ Branch 3 not taken.
1648 assert(dofs_e1[i] != dofs_e2[j] &&
396 "If this happens something went horribly wrong.");
397
398 1648 const int small_dof = std::min(dofs_e1[i], dofs_e2[j]);
399 1648 const int large_dof = std::max(dofs_e1[i], dofs_e2[j]);
400
401 // In the continuous case, more than two dofs might be glued together.
402 // Therefore, we must check if we know one of the dofs already. If
403 // yes, we just add the new one to the dof_id, if not, we make a new
404 // identification group.
405
2/2
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 1360 times.
1648 if (already_stored_in[small_dof] > -1) {
406 // It could be that the large_dof is already matched with another
407 // dof, i.e., in the case of a 'circle'. Then there is nothing to
408 // do. If not, we match it with the master of the partner.
409
2/2
✓ Branch 1 taken 272 times.
✓ Branch 2 taken 16 times.
288 if (already_stored_in[large_dof] == -1) {
410
1/2
✓ Branch 3 taken 272 times.
✗ Branch 4 not taken.
272 out[already_stored_in[small_dof]].dofs.push_back(large_dof);
411 272 already_stored_in[large_dof] = already_stored_in[small_dof];
412 } else {
413
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if (!(already_stored_in[small_dof] ==
414 already_stored_in[large_dof])) {
415 // This case is tricky. Assume that we have to identify four
416 // DOFs with each other, but they have already been assigned in
417 // pairs of two. Then we need to reverse this process. First, we
418 // grab the two storage locations.
419 const int small_store = std::min(already_stored_in[large_dof],
420 already_stored_in[small_dof]);
421 const int large_store = std::max(already_stored_in[large_dof],
422 already_stored_in[small_dof]);
423 for (auto dfs : out[large_store].dofs) {
424 // now we put all of those in the larger location into the
425 // smaller one.
426 already_stored_in[dfs] = small_store;
427 for (auto other_dfs : out[small_store].dofs) {
428 assert(dfs != other_dfs);
429 }
430 out[small_store].dofs.push_back(dfs);
431 }
432 // Now we set the larger storage location to empty.
433 out[large_store].dofs = {};
434 out[large_store].coef = 0;
435 }
436 }
437
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1360 times.
1360 } else if (already_stored_in[large_dof] > -1) {
438 // It could be that the small_dof is already matched with another
439 // dof, i.e., in the case of a 'circle'. Then there is nothing to
440 // do. If not, we match it with the master of the partner.
441 if (already_stored_in[small_dof] == -1) {
442 out[already_stored_in[large_dof]].dofs.push_back(small_dof);
443 already_stored_in[small_dof] = already_stored_in[large_dof];
444 } else {
445 if (!(already_stored_in[small_dof] ==
446 already_stored_in[large_dof])) {
447 // This case is tricky. Assume that we have to identify four
448 // DOFs with each other, but they have already been assigned in
449 // pairs of two. Then we need to reverse this process. First, we
450 // grab the two storage locations.
451 const int small_store = std::min(already_stored_in[large_dof],
452 already_stored_in[small_dof]);
453 const int large_store = std::max(already_stored_in[large_dof],
454 already_stored_in[small_dof]);
455 for (auto dfs : out[large_store].dofs) {
456 // now we put all of those in the larger location into the
457 // smaller one.
458 already_stored_in[dfs] = small_store;
459 for (auto other_dfs : out[small_store].dofs) {
460 assert(dfs != other_dfs);
461 }
462 out[small_store].dofs.push_back(dfs);
463 }
464 // Now we set the larger storage location to empty.
465 out[large_store].dofs = {};
466 out[large_store].coef = 0;
467 }
468 }
469 } else {
470 // With the exception of corners, this will be the default case.
471 // We just add a pair of dofs to the bookkeeping.
472
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 d.dofs.push_back(small_dof);
473
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 d.dofs.push_back(large_dof);
474 1360 already_stored_in[small_dof] = d_count;
475 1360 already_stored_in[large_dof] = d_count;
476 1360 d_count++;
477 1360 d.coef = 1;
478
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 out.push_back(d);
479 }
480 }
481 }
482 }
483 // Now we need to clean up the empty dofsets, since subsequent routines
484 // assume there to be at least one element.
485
2/2
✓ Branch 4 taken 1360 times.
✓ Branch 5 taken 66 times.
1426 for (auto x = out.begin(); x != out.end(); ++x) {
486
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1360 times.
1360 if ((*x).dofs.size() == 0) {
487 out.erase(x);
488 }
489 }
490 132 return out;
491 66 }
492 };
493
494 /**
495 * \brief Specification of the struct for the vector Div conforming case.
496 *
497 * The Maxwell case: Here, the identification is 1-to-1, but we need to take
498 * care to glue the right vector component. The function builds upon the fact,
499 * that px == py == p.
500 */
501 template <typename Derived>
502 struct glue_identificationmaker_<Derived, DifferentialForm::DivConforming> {
503 67 static std::vector<dofIdentification> makeIdentification(
504 const std::vector<std::array<int, 4>> &edges_,
505 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
506 // since we assume px == py and uniform refinement in both directions, there
507 // will be a small_dim and a large_dim in every vector component.
508 67 const int small_dim = superspace.get_polynomial_degree() +
509 67 proj.get_knot_repetition() *
510 67 ((1 << superspace.get_refinement_level()) - 1);
511 67 const int large_dim = superspace.get_polynomial_degree() + 1 +
512 67 proj.get_knot_repetition() *
513 67 ((1 << superspace.get_refinement_level()) - 1);
514 67 const int dofs_per_patch_per_component = small_dim * large_dim;
515 // sanity check
516
1/2
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 assert(dofs_per_patch_per_component ==
517 (proj.get_dofs_after_projector() /
518 (superspace.get_number_of_patches() * 2)) &&
519 "The assembly of the glue matrix is highly specific to the space. "
520 "Something went wrong; is the discrete space correct?");
521
522 67 std::vector<GlueRoutines::dofIdentification> out;
523
1/2
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 out.reserve(edges_.size() * small_dim);
524
525
2/2
✓ Branch 7 taken 1052 times.
✓ Branch 8 taken 67 times.
1119 for (auto edge : edges_) {
526
3/4
✓ Branch 2 taken 772 times.
✓ Branch 3 taken 280 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 772 times.
1052 assert(edge[0] <= edge[1] || edge[1] == -1);
527 // The DOFs that need to be glued on edges 0 and 2 are in the second
528 // vector component. The remainder of the dof-index-shift is to account
529 // for dofs on other patches.
530 1052 const int shift_e1 = edge[0] * dofs_per_patch_per_component +
531 1052 (edgeToBeGluedInFirstComp(edge[2])
532
2/2
✓ Branch 0 taken 528 times.
✓ Branch 1 taken 524 times.
1576 ? 0
533 : (dofs_per_patch_per_component *
534 524 superspace.get_number_of_patches()));
535 1052 const int shift_e2 = edge[1] * dofs_per_patch_per_component +
536 1052 (edgeToBeGluedInFirstComp(edge[3])
537
2/2
✓ Branch 0 taken 910 times.
✓ Branch 1 taken 142 times.
1194 ? 0
538 : (dofs_per_patch_per_component *
539 142 superspace.get_number_of_patches()));
540
541 1052 const int xdir_dim_1 =
542
2/2
✓ Branch 2 taken 528 times.
✓ Branch 3 taken 524 times.
1052 edgeToBeGluedInFirstComp(edge[2]) ? large_dim : small_dim;
543 1052 const int ydir_dim_1 =
544
2/2
✓ Branch 2 taken 528 times.
✓ Branch 3 taken 524 times.
1052 edgeToBeGluedInFirstComp(edge[2]) ? small_dim : large_dim;
545 1052 const int xdir_dim_2 =
546
2/2
✓ Branch 2 taken 910 times.
✓ Branch 3 taken 142 times.
1052 edgeToBeGluedInFirstComp(edge[3]) ? large_dim : small_dim;
547 1052 const int ydir_dim_2 =
548
2/2
✓ Branch 2 taken 910 times.
✓ Branch 3 taken 142 times.
1052 edgeToBeGluedInFirstComp(edge[3]) ? small_dim : large_dim;
549
1/2
✓ Branch 1 taken 1052 times.
✗ Branch 2 not taken.
1052 std::vector<int> dofs_e1 =
550 1052 getEdgeDofIndices(edge[2], xdir_dim_1, ydir_dim_1, shift_e1);
551
1/2
✓ Branch 1 taken 1052 times.
✗ Branch 2 not taken.
1052 std::vector<int> dofs_e2 =
552 1052 getEdgeDofIndices(edge[3], xdir_dim_2, ydir_dim_2, shift_e2);
553
554 1052 const int size_of_edge_dofs = dofs_e1.size();
555
3/4
✓ Branch 1 taken 772 times.
✓ Branch 2 taken 280 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 772 times.
1052 assert(size_of_edge_dofs == dofs_e2.size() || edge[1] == -1);
556
557 1052 const bool needReversion = reverseParametrized(edge);
558 1052 const int coef = glueCoefficientDivergenceConforming(edge);
559
560 // Check if the edge is hanging. For screens one would need an "else".
561
5/6
✓ Branch 1 taken 280 times.
✓ Branch 2 taken 772 times.
✓ Branch 4 taken 280 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 280 times.
✓ Branch 7 taken 772 times.
1052 if (edge[1] > -1 && edge[0] > -1) {
562
2/2
✓ Branch 1 taken 1404 times.
✓ Branch 2 taken 280 times.
1684 for (int i = 0; i < size_of_edge_dofs; ++i) {
563 1404 GlueRoutines::dofIdentification d;
564
2/2
✓ Branch 0 taken 702 times.
✓ Branch 1 taken 702 times.
1404 const int j = needReversion ? size_of_edge_dofs - 1 - i : i;
565
1/2
✓ Branch 2 taken 1404 times.
✗ Branch 3 not taken.
1404 assert(dofs_e1[i] != dofs_e2[j] &&
566 "If this happens something went horribly wrong.");
567
1/2
✓ Branch 4 taken 1404 times.
✗ Branch 5 not taken.
1404 d.dofs.push_back(std::min(dofs_e1[i], dofs_e2[j]));
568
1/2
✓ Branch 4 taken 1404 times.
✗ Branch 5 not taken.
1404 d.dofs.push_back(std::max(dofs_e1[i], dofs_e2[j]));
569 1404 d.coef = coef;
570
1/2
✓ Branch 1 taken 1404 times.
✗ Branch 2 not taken.
1404 out.push_back(d);
571 }
572 }
573 }
574
1/2
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
67 out.shrink_to_fit();
575 67 return out;
576 }
577 };
578 }; // namespace GlueRoutines
579
580 } // namespace Bembel
581 #endif // BEMBEL_SRC_ANSATZSPACE_GLUE_HPP_
582