GCC Code Coverage Report


Directory: Bembel/src/
File: AnsatzSpace/Glue.hpp
Date: 2024-12-18 07:36:36
Exec Total Coverage
Lines: 205 230 89.1%
Functions: 66 70 94.3%
Branches: 174 267 65.2%

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 272 int number_of_boundary_dofs = 0;
169
3/4
✓ Branch 4 taken 6812 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 6812 times.
✓ Branch 10 taken 144 times.
13804 for (auto dofset : dof_id) {
170 // This sorting is required, since by construction only dofs[0]<dofs[1] is
171 // given, but in the case of corners and H1 discretizations, it might
172 // happen that dofs[0]>dofs[2]. Moreover, we count how many dofs we "glue
173 // away" or skipped on the boundary.
174
1/2
✓ Branch 3 taken 6812 times.
✗ Branch 4 not taken.
13532 std::sort(dofset.dofs.begin(), dofset.dofs.end());
175
1/2
✓ Branch 2 taken 6812 times.
✗ Branch 3 not taken.
13532 dof_is_master[dofset.dofs[0]] = true;
176
2/2
✓ Branch 1 taken 4044 times.
✓ Branch 2 taken 2768 times.
13532 if (dofset.dofs.size() == 1) ++number_of_boundary_dofs;
177
2/2
✓ Branch 1 taken 3040 times.
✓ Branch 2 taken 6812 times.
19516 for (int i = 1; i < dofset.dofs.size(); ++i) {
178
1/2
✓ Branch 2 taken 3040 times.
✗ Branch 3 not taken.
5984 dof_is_slave[dofset.dofs[i]] = true;
179 5984 ++number_of_slaves;
180 }
181 }
182
183 // Now, we sort w.r.t. the masters.
184
1/2
✓ Branch 3 taken 144 times.
✗ Branch 4 not taken.
272 std::sort(dof_id.begin(), dof_id.end(),
185 45710 [](GlueRoutines::dofIdentification a,
186 GlueRoutines::dofIdentification b) {
187 45710 return a.dofs[0] < b.dofs[0];
188 });
189
190 272 const int post_dofs = pre_dofs - number_of_slaves - number_of_boundary_dofs;
191 272 const int glued_dofs = pre_dofs - number_of_slaves;
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
272 assert(post_dofs != 0 && "All degrees of freedom are on the boundary!");
193 // This block is the heart of the algorithms. Skip keeps track of how many
194 // slaves have been skipped already, and master_index keeps track on which
195 // master is about to be glued next. post_index keeps track on how many
196 // dofs are on the boundary of the patches and therefore skipped.
197 272 int skip = 0;
198 272 int master_index = 0;
199 272 int post_index = 0;
200
2/2
✓ Branch 0 taken 43596 times.
✓ Branch 1 taken 144 times.
82524 for (int i = 0; i < glued_dofs; ++i) {
201
6/8
✓ Branch 1 taken 46602 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3006 times.
✓ Branch 5 taken 43596 times.
✓ Branch 6 taken 3006 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3006 times.
✓ Branch 9 taken 43596 times.
88202 while (dof_is_slave[i + skip] && ((i + skip) < pre_dofs)) ++skip;
202 82252 const int pre_index = i + skip;
203 // skip dofs on patch boundary without a slave patch
204
7/8
✓ Branch 1 taken 43596 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6812 times.
✓ Branch 5 taken 36784 times.
✓ Branch 8 taken 4044 times.
✓ Branch 9 taken 2768 times.
✓ Branch 10 taken 4044 times.
✓ Branch 11 taken 39552 times.
82252 if (dof_is_master[pre_index] && dof_id[master_index].dofs.size() == 1) {
205 8076 ++master_index;
206 8076 continue;
207 }
208
1/2
✓ Branch 2 taken 39552 times.
✗ Branch 3 not taken.
74176 trips.push_back(Eigen::Triplet<double>(pre_index, post_index, 1));
209 // The dof cannot be declared slave and master at the same time
210
5/8
✓ Branch 1 taken 39552 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2768 times.
✓ Branch 5 taken 36784 times.
✓ Branch 7 taken 2768 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 2768 times.
74176 assert(!(dof_is_master[pre_index] && dof_is_slave[pre_index]));
211
3/4
✓ Branch 1 taken 39552 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2768 times.
✓ Branch 5 taken 36784 times.
74176 if (dof_is_master[pre_index]) {
212 // The dofs in dof_id don't know they might be moved forward. So the
213 // smallest dof of those to be identified should coincide with the
214 // pre_index.
215
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2768 times.
5456 assert(pre_index == dof_id[master_index].dofs[0]);
216 5456 const int number_of_partners = dof_id[master_index].dofs.size();
217
2/2
✓ Branch 0 taken 3040 times.
✓ Branch 1 taken 2768 times.
11440 for (int j = 1; j < number_of_partners; ++j) {
218
1/2
✓ Branch 3 taken 3040 times.
✗ Branch 4 not taken.
5984 trips.push_back(Eigen::Triplet<double>(dof_id[master_index].dofs[j],
219 post_index,
220 5984 dof_id[master_index].coef));
221 }
222 5456 ++master_index;
223 }
224 74176 ++post_index;
225 }
226
227
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
272 Eigen::SparseMatrix<double> glue_matrix(pre_dofs, post_dofs);
228
1/2
✓ Branch 3 taken 144 times.
✗ Branch 4 not taken.
272 glue_matrix.setFromTriplets(trips.begin(), trips.end());
229
230
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
272 assert(trips.size() == (pre_dofs - number_of_boundary_dofs));
231 544 return glue_matrix;
232 272 }
233 };
234
235 namespace GlueRoutines {
236
237 /**
238 * \brief This routine collects the indices of DOFs with support on an edge.
239 *
240 * The indices are computed depending on the edge case and number of DOFs in the
241 * x and y direction of the tensor product. The shift parameter can be used to
242 * shift all dofs according to the patch index or vector component.
243 *
244 * Edge Case
245 *
246 * 0 = (0,0)->(1,0)
247 *
248 * 1 = (1,0)->(1,1)
249 *
250 * 2 = (1,1)->(0,1)
251 *
252 * 3 = (0,1)->(0,0)
253 *
254 * \param edgeCase Edge case of the patch to collect the DOFs.
255 * \param dimXdir Number of DOFs in the x direction of the tensor product.
256 * \param dimYdir Number of DOFs in the y direction of the tensor product.
257 * \param shift Offset of the DOFs.
258 */
259 4224 std::vector<int> getEdgeDofIndices(int edgeCase, int dimXdir, int dimYdir,
260 int shift) {
261 4224 std::vector<int> out;
262
5/5
✓ Branch 0 taken 669 times.
✓ Branch 1 taken 669 times.
✓ Branch 2 taken 669 times.
✓ Branch 3 taken 669 times.
✓ Branch 4 taken 1548 times.
4224 switch (edgeCase) {
263 669 case (0): {
264
1/2
✓ Branch 1 taken 669 times.
✗ Branch 2 not taken.
669 out.reserve(dimXdir);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 669 times.
669 assert(
266 "This should be a given. If not something went wrong in Glue.hpp" &&
267 dimXdir <= dimYdir);
268
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 669 times.
4408 for (int i = 0; i < dimXdir; ++i) {
269
1/2
✓ Branch 1 taken 3739 times.
✗ Branch 2 not taken.
3739 out.push_back(i + shift);
270 }
271 669 return out;
272 };
273 669 case (1): {
274
1/2
✓ Branch 1 taken 669 times.
✗ Branch 2 not taken.
669 out.reserve(dimYdir);
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 669 times.
669 assert(
276 "This should be a given. If not something went wrong in Glue.hpp" &&
277 dimYdir <= dimXdir);
278
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 669 times.
4408 for (int i = 0; i < dimYdir; ++i) {
279
1/2
✓ Branch 1 taken 3739 times.
✗ Branch 2 not taken.
3739 out.push_back(dimXdir * (i + 1) - 1 + shift);
280 }
281 669 return out;
282 };
283 669 case (2): {
284
1/2
✓ Branch 1 taken 669 times.
✗ Branch 2 not taken.
669 out.reserve(dimXdir);
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 669 times.
669 assert(
286 "This should be a given. If not something went wrong in Glue.hpp" &&
287 dimXdir <= dimYdir);
288
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 669 times.
4408 for (int i = 0; i < dimXdir; ++i) {
289
1/2
✓ Branch 1 taken 3739 times.
✗ Branch 2 not taken.
3739 out.push_back(dimXdir * (dimYdir - 1) + i + shift);
290 }
291 669 return out;
292 };
293 669 case (3): {
294
1/2
✓ Branch 1 taken 669 times.
✗ Branch 2 not taken.
669 out.reserve(dimYdir);
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 669 times.
669 assert(
296 "This should be a given. If not something went wrong in Glue.hpp" &&
297 dimYdir <= dimXdir);
298
2/2
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 669 times.
4408 for (int i = 0; i < dimYdir; ++i) {
299
1/2
✓ Branch 1 taken 3739 times.
✗ Branch 2 not taken.
3739 out.push_back(dimXdir * i + shift);
300 }
301 669 return out;
302 };
303 1548 default: {
304 };
305 // An edge might have a -1 index. This occurs only when no partner could
306 // be found, and the -1 is the placeholder of the missing partner.
307 }
308 1548 return out;
309 }
310
311 // The following 7 functions figure out how edges and the vector components
312 // are oriented w.r.t. each other.
313 6196 inline bool edgeIsForwardParametrized(int edgeCase) {
314
4/4
✓ Branch 0 taken 5071 times.
✓ Branch 1 taken 1125 times.
✓ Branch 2 taken 1129 times.
✓ Branch 3 taken 3942 times.
6196 return edgeCase == 0 || edgeCase == 1;
315 }
316 3028 inline bool edgeIsBackwardsParametrized(int edgeCase) {
317 3028 return !(edgeIsForwardParametrized(edgeCase));
318 }
319 3128 inline bool normalComponentIsInwardDirected(int edgeCase) {
320
4/4
✓ Branch 0 taken 2559 times.
✓ Branch 1 taken 569 times.
✓ Branch 2 taken 571 times.
✓ Branch 3 taken 1988 times.
3128 return edgeCase == 0 || edgeCase == 3;
321 }
322 1534 inline bool normalComponentIsOutwardDirected(int edgeCase) {
323 1534 return !(normalComponentIsInwardDirected(edgeCase));
324 }
325
326 2112 inline bool reverseParametrized(const std::array<int, 4> &edge) {
327
2/2
✓ Branch 2 taken 916 times.
✓ Branch 3 taken 140 times.
3168 return ((edgeIsForwardParametrized(edge[2]) &&
328 1056 edgeIsForwardParametrized(edge[3])) ||
329
2/2
✓ Branch 2 taken 914 times.
✓ Branch 3 taken 142 times.
3028 (edgeIsBackwardsParametrized(edge[2]) &&
330 1056 edgeIsBackwardsParametrized(edge[3])))
331
4/4
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 1056 times.
✓ Branch 2 taken 1056 times.
✓ Branch 3 taken 916 times.
5280 ? true
332 2112 : false;
333 }
334
335 1064 inline int glueCoefficientDivergenceConforming(const std::array<int, 4> &edge) {
336
2/2
✓ Branch 2 taken 466 times.
✓ Branch 3 taken 64 times.
1594 return ((normalComponentIsInwardDirected(edge[2]) &&
337
2/2
✓ Branch 2 taken 534 times.
✓ Branch 3 taken 466 times.
1530 normalComponentIsInwardDirected(edge[3])) ||
338
2/2
✓ Branch 2 taken 454 times.
✓ Branch 3 taken 80 times.
1534 (normalComponentIsOutwardDirected(edge[2]) &&
339 534 normalComponentIsOutwardDirected(edge[3])))
340
2/2
✓ Branch 0 taken 530 times.
✓ Branch 1 taken 534 times.
2128 ? -1
341 1064 : 1;
342 }
343 6384 inline bool edgeToBeGluedInFirstComp(int edgeCase) {
344
4/4
✓ Branch 0 taken 5373 times.
✓ Branch 1 taken 1011 times.
✓ Branch 2 taken 4362 times.
✓ Branch 3 taken 1011 times.
6384 return (edgeCase == 0 || edgeCase == 2) ? false : true;
345 }
346
347 /**
348 * \brief Specification of the struct for the discontinuous case.
349 *
350 * Nothing needs to be glued.
351 */
352 template <typename Derived>
353 struct glue_identificationmaker_<Derived, DifferentialForm::Discontinuous> {
354 11 static std::vector<dofIdentification> makeIdentification(
355 const std::vector<std::array<int, 4>> &edges_,
356 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
357 11 return {};
358 }
359 };
360
361 /**
362 * \brief Specification of the struct for the scalar continuous case.
363 *
364 * The scalar case: Here, we have only one vector component to worry about.
365 * However, we need to take care of edges, since here multiple (more than 2)
366 * dofs will be reduced to one. This is also the reason we we work with the
367 * dofIdentification struct and not already with Eigen::Triplet. The function
368 * builds upon the fact, that px == py == p.
369 */
370 template <typename Derived>
371 struct glue_identificationmaker_<Derived, DifferentialForm::Continuous> {
372 66 static std::vector<dofIdentification> makeIdentification(
373 const std::vector<std::array<int, 4>> &edges_,
374 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
375 // We check if the space can even be continuous globally.
376
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
66 assert(superspace.get_polynomial_degree() >= 1);
377 66 const int one_d_dim = superspace.get_polynomial_degree() + 1 +
378 66 proj.get_knot_repetition() *
379 66 ((1 << superspace.get_refinement_level()) - 1);
380 66 const int dofs_per_patch = one_d_dim * one_d_dim;
381
382 66 std::vector<GlueRoutines::dofIdentification> out;
383
1/2
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
66 out.reserve(edges_.size() * one_d_dim);
384
385
1/2
✓ Branch 3 taken 66 times.
✗ Branch 4 not taken.
66 std::vector<int> already_stored_in(proj.get_dofs_after_projector(), -1);
386 66 int d_count = 0;
387
2/2
✓ Branch 7 taken 1048 times.
✓ Branch 8 taken 66 times.
1114 for (auto edge : edges_) {
388 // The shift is to account for different patches, since the
389 // getEdgeDof-routines only can enumerate w.r.t. patch 0.
390 1048 const int shift_e1 = edge[0] * dofs_per_patch;
391 1048 const int shift_e2 = edge[1] * dofs_per_patch;
392 1048 const bool needReversion = reverseParametrized(edge);
393
394
1/2
✓ Branch 1 taken 1048 times.
✗ Branch 2 not taken.
1048 std::vector<int> dofs_e1 =
395 1048 getEdgeDofIndices(edge[2], one_d_dim, one_d_dim, shift_e1);
396
1/2
✓ Branch 1 taken 1048 times.
✗ Branch 2 not taken.
1048 std::vector<int> dofs_e2 =
397 1048 getEdgeDofIndices(edge[3], one_d_dim, one_d_dim, shift_e2);
398
399 // Check if the edge is hanging. For screens one would need an "else".
400
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) {
401
2/2
✓ Branch 1 taken 1648 times.
✓ Branch 2 taken 280 times.
1928 for (int i = 0; i < one_d_dim; ++i) {
402 1648 GlueRoutines::dofIdentification d;
403
2/2
✓ Branch 0 taken 824 times.
✓ Branch 1 taken 824 times.
1648 const int j = needReversion ? one_d_dim - 1 - i : i;
404 // const int j = i;
405
1/2
✓ Branch 2 taken 1648 times.
✗ Branch 3 not taken.
1648 assert(dofs_e1[i] != dofs_e2[j] &&
406 "If this happens something went horribly wrong.");
407
408 1648 const int small_dof = std::min(dofs_e1[i], dofs_e2[j]);
409 1648 const int large_dof = std::max(dofs_e1[i], dofs_e2[j]);
410
411 // In the continuous case, more than two dofs might be glued together.
412 // Therefore, we must check if we know one of the dofs already. If
413 // yes, we just add the new one to the dof_id, if not, we make a new
414 // identification group.
415
2/2
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 1360 times.
1648 if (already_stored_in[small_dof] > -1) {
416 // It could be that the large_dof is already matched with another
417 // dof, i.e., in the case of a 'circle'. Then there is nothing to
418 // do. If not, we match it with the master of the partner.
419
2/2
✓ Branch 1 taken 272 times.
✓ Branch 2 taken 16 times.
288 if (already_stored_in[large_dof] == -1) {
420
1/2
✓ Branch 3 taken 272 times.
✗ Branch 4 not taken.
272 out[already_stored_in[small_dof]].dofs.push_back(large_dof);
421 272 already_stored_in[large_dof] = already_stored_in[small_dof];
422 } else {
423
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 if (!(already_stored_in[small_dof] ==
424 already_stored_in[large_dof])) {
425 // This case is tricky. Assume that we have to identify four
426 // DOFs with each other, but they have already been assigned in
427 // pairs of two. Then we need to reverse this process. First, we
428 // grab the two storage locations.
429 const int small_store = std::min(already_stored_in[large_dof],
430 already_stored_in[small_dof]);
431 const int large_store = std::max(already_stored_in[large_dof],
432 already_stored_in[small_dof]);
433 for (auto dfs : out[large_store].dofs) {
434 // now we put all of those in the larger location into the
435 // smaller one.
436 already_stored_in[dfs] = small_store;
437 for (auto other_dfs : out[small_store].dofs) {
438 assert(dfs != other_dfs);
439 }
440 out[small_store].dofs.push_back(dfs);
441 }
442 // Now we set the larger storage location to empty.
443 out[large_store].dofs = {};
444 out[large_store].coef = 0;
445 }
446 }
447
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1360 times.
1360 } else if (already_stored_in[large_dof] > -1) {
448 // It could be that the small_dof is already matched with another
449 // dof, i.e., in the case of a 'circle'. Then there is nothing to
450 // do. If not, we match it with the master of the partner.
451 if (already_stored_in[small_dof] == -1) {
452 out[already_stored_in[large_dof]].dofs.push_back(small_dof);
453 already_stored_in[small_dof] = already_stored_in[large_dof];
454 } else {
455 if (!(already_stored_in[small_dof] ==
456 already_stored_in[large_dof])) {
457 // This case is tricky. Assume that we have to identify four
458 // DOFs with each other, but they have already been assigned in
459 // pairs of two. Then we need to reverse this process. First, we
460 // grab the two storage locations.
461 const int small_store = std::min(already_stored_in[large_dof],
462 already_stored_in[small_dof]);
463 const int large_store = std::max(already_stored_in[large_dof],
464 already_stored_in[small_dof]);
465 for (auto dfs : out[large_store].dofs) {
466 // now we put all of those in the larger location into the
467 // smaller one.
468 already_stored_in[dfs] = small_store;
469 for (auto other_dfs : out[small_store].dofs) {
470 assert(dfs != other_dfs);
471 }
472 out[small_store].dofs.push_back(dfs);
473 }
474 // Now we set the larger storage location to empty.
475 out[large_store].dofs = {};
476 out[large_store].coef = 0;
477 }
478 }
479 } else {
480 // With the exception of corners, this will be the default case.
481 // We just add a pair of dofs to the bookkeeping.
482
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 d.dofs.push_back(small_dof);
483
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 d.dofs.push_back(large_dof);
484 1360 already_stored_in[small_dof] = d_count;
485 1360 already_stored_in[large_dof] = d_count;
486 1360 d_count++;
487 1360 d.coef = 1;
488
1/2
✓ Branch 1 taken 1360 times.
✗ Branch 2 not taken.
1360 out.push_back(d);
489 }
490 }
491 }
492 }
493 // Now we need to clean up the empty dofsets, since subsequent routines
494 // assume there to be at least one element.
495
2/2
✓ Branch 4 taken 1360 times.
✓ Branch 5 taken 66 times.
1426 for (auto x = out.begin(); x != out.end(); ++x) {
496
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1360 times.
1360 if ((*x).dofs.size() == 0) {
497 out.erase(x);
498 }
499 }
500 132 return out;
501 66 }
502 };
503
504 /**
505 * \brief Specification of the struct for the vector Div conforming case.
506 *
507 * The Maxwell case: Here, the identification is 1-to-1, but we need to take
508 * care to glue the right vector component. The function builds upon the fact,
509 * that px == py == p.
510 */
511 template <typename Derived>
512 struct glue_identificationmaker_<Derived, DifferentialForm::DivConforming> {
513 67 static std::vector<dofIdentification> makeIdentification(
514 const std::vector<std::array<int, 4>> &edges_,
515 const SuperSpace<Derived> &superspace, const Projector<Derived> &proj) {
516 // since we assume px == py and uniform refinement in both directions, there
517 // will be a small_dim and a large_dim in every vector component.
518 67 const int small_dim = superspace.get_polynomial_degree() +
519 67 proj.get_knot_repetition() *
520 67 ((1 << superspace.get_refinement_level()) - 1);
521 67 const int large_dim = superspace.get_polynomial_degree() + 1 +
522 67 proj.get_knot_repetition() *
523 67 ((1 << superspace.get_refinement_level()) - 1);
524 67 const int dofs_per_patch_per_component = small_dim * large_dim;
525 // sanity check
526
1/2
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 assert(dofs_per_patch_per_component ==
527 (proj.get_dofs_after_projector() /
528 (superspace.get_number_of_patches() * 2)) &&
529 "The assembly of the glue matrix is highly specific to the space. "
530 "Something went wrong; is the discrete space correct?");
531
532 67 std::vector<GlueRoutines::dofIdentification> out;
533
1/2
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 out.reserve(edges_.size() * small_dim);
534
535
2/2
✓ Branch 7 taken 1064 times.
✓ Branch 8 taken 67 times.
1131 for (auto edge : edges_) {
536
3/4
✓ Branch 2 taken 780 times.
✓ Branch 3 taken 284 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 780 times.
1064 assert(edge[0] <= edge[1] || edge[1] == -1);
537 // The DOFs that need to be glued on edges 0 and 2 are in the second
538 // vector component. The remainder of the dof-index-shift is to account
539 // for dofs on other patches.
540 1064 const int shift_e1 = edge[0] * dofs_per_patch_per_component +
541 1064 (edgeToBeGluedInFirstComp(edge[2])
542
2/2
✓ Branch 0 taken 534 times.
✓ Branch 1 taken 530 times.
1594 ? 0
543 : (dofs_per_patch_per_component *
544 530 superspace.get_number_of_patches()));
545 1064 const int shift_e2 = edge[1] * dofs_per_patch_per_component +
546 1064 (edgeToBeGluedInFirstComp(edge[3])
547
2/2
✓ Branch 0 taken 920 times.
✓ Branch 1 taken 144 times.
1208 ? 0
548 : (dofs_per_patch_per_component *
549 144 superspace.get_number_of_patches()));
550
551 1064 const int xdir_dim_1 =
552
2/2
✓ Branch 2 taken 534 times.
✓ Branch 3 taken 530 times.
1064 edgeToBeGluedInFirstComp(edge[2]) ? large_dim : small_dim;
553 1064 const int ydir_dim_1 =
554
2/2
✓ Branch 2 taken 534 times.
✓ Branch 3 taken 530 times.
1064 edgeToBeGluedInFirstComp(edge[2]) ? small_dim : large_dim;
555 1064 const int xdir_dim_2 =
556
2/2
✓ Branch 2 taken 920 times.
✓ Branch 3 taken 144 times.
1064 edgeToBeGluedInFirstComp(edge[3]) ? large_dim : small_dim;
557 1064 const int ydir_dim_2 =
558
2/2
✓ Branch 2 taken 920 times.
✓ Branch 3 taken 144 times.
1064 edgeToBeGluedInFirstComp(edge[3]) ? small_dim : large_dim;
559
1/2
✓ Branch 1 taken 1064 times.
✗ Branch 2 not taken.
1064 std::vector<int> dofs_e1 =
560 1064 getEdgeDofIndices(edge[2], xdir_dim_1, ydir_dim_1, shift_e1);
561
1/2
✓ Branch 1 taken 1064 times.
✗ Branch 2 not taken.
1064 std::vector<int> dofs_e2 =
562 1064 getEdgeDofIndices(edge[3], xdir_dim_2, ydir_dim_2, shift_e2);
563
564 1064 const int size_of_edge_dofs = dofs_e1.size();
565
3/4
✓ Branch 1 taken 780 times.
✓ Branch 2 taken 284 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 780 times.
1064 assert(size_of_edge_dofs == dofs_e2.size() || edge[1] == -1);
566
567 1064 const bool needReversion = reverseParametrized(edge);
568 1064 const int coef = glueCoefficientDivergenceConforming(edge);
569
570 // Check if the edge is hanging.
571
5/6
✓ Branch 1 taken 284 times.
✓ Branch 2 taken 780 times.
✓ Branch 4 taken 284 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 284 times.
✓ Branch 7 taken 780 times.
1064 if (edge[1] > -1 && edge[0] > -1) {
572
2/2
✓ Branch 1 taken 1408 times.
✓ Branch 2 taken 284 times.
1692 for (int i = 0; i < size_of_edge_dofs; ++i) {
573 1408 GlueRoutines::dofIdentification d;
574
2/2
✓ Branch 0 taken 702 times.
✓ Branch 1 taken 706 times.
1408 const int j = needReversion ? size_of_edge_dofs - 1 - i : i;
575
1/2
✓ Branch 2 taken 1408 times.
✗ Branch 3 not taken.
1408 assert(dofs_e1[i] != dofs_e2[j] &&
576 "If this happens something went horribly wrong.");
577
1/2
✓ Branch 4 taken 1408 times.
✗ Branch 5 not taken.
1408 d.dofs.push_back(std::min(dofs_e1[i], dofs_e2[j]));
578
1/2
✓ Branch 4 taken 1408 times.
✗ Branch 5 not taken.
1408 d.dofs.push_back(std::max(dofs_e1[i], dofs_e2[j]));
579 1408 d.coef = coef;
580
1/2
✓ Branch 1 taken 1408 times.
✗ Branch 2 not taken.
1408 out.push_back(d);
581 }
582 } else {
583
2/2
✓ Branch 1 taken 4044 times.
✓ Branch 2 taken 780 times.
4824 for (int i = 0; i < size_of_edge_dofs; ++i) {
584 // Add only the master dof which is skipped in subsequent routines
585 4044 GlueRoutines::dofIdentification d;
586
1/2
✓ Branch 2 taken 4044 times.
✗ Branch 3 not taken.
4044 d.dofs.push_back(dofs_e1[i]);
587
1/2
✓ Branch 1 taken 4044 times.
✗ Branch 2 not taken.
4044 out.push_back(d);
588 }
589 }
590 }
591
1/2
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
67 out.shrink_to_fit();
592 67 return out;
593 }
594 };
595 }; // namespace GlueRoutines
596
597 } // namespace Bembel
598 #endif // BEMBEL_SRC_ANSATZSPACE_GLUE_HPP_
599