MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
ncnurbs.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "ncnurbs.hpp"
13
14namespace mfem
15{
16
17using namespace std;
18
19// Helper functions for NC-NURBS
20
21void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
22 int& sm, int& sn, int& si, int& sj);
23
24void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
25 int &n);
26
27int GetFaceOrientation(const Mesh *mesh, const int face,
28 const std::array<int, 4> &verts);
29
30bool Reorder2D(int ori, std::array<int, 2> &s0);
31
32std::pair<int, int> QuadrupleToPair(const std::array<int, 4> &q);
33
34NCNURBSExtension::NCNURBSExtension(std::istream &input, bool spacing)
35{
36 // Read topology
37 patchTopo = new Mesh;
39 nonconformingPT = true;
40
41 Load(input, spacing);
42}
43
45 : NURBSExtension(orig),
46 aux_e_meshOffsets(orig.aux_e_meshOffsets),
47 aux_f_meshOffsets(orig.aux_f_meshOffsets),
48 aux_e_spaceOffsets(orig.aux_e_spaceOffsets),
49 aux_f_spaceOffsets(orig.aux_f_spaceOffsets),
50 auxEdges(orig.auxEdges),
51 auxFaces(orig.auxFaces),
52 auxef(orig.auxef)
53{ }
54
55void NCNURBSExtension::GetMasterEdgeEntities(
56 int edge, Array<int> &edgeV, Array<int> &edgeE, Array<int> &edgeVki)
57{
58 const int mid = masterEdgeToId.at(edge);
59 const std::size_t nes = masterEdgeInfo[mid].slaves.size();
60 MFEM_ASSERT(masterEdgeInfo[mid].vertices.size() + 1 == nes, "");
61
62 // Vertices in masterEdgeVerts[mid] are ordered starting
63 // from the master edge endpoint with lower vertex index.
64
65 Array<int> everts;
66 patchTopo->GetEdgeVertices(edge, everts);
67
68 edgeV.Append(everts[0]);
69 edgeVki.Append(0);
70
71 MFEM_ASSERT(masterEdgeInfo[mid].vertices.size() ==
72 masterEdgeInfo[mid].ks.size(), "");
73 for (std::size_t i=0; i<masterEdgeInfo[mid].vertices.size(); ++i)
74 {
75 edgeV.Append(masterEdgeInfo[mid].vertices[i]);
76 edgeVki.Append(masterEdgeInfo[mid].ks[i]);
77 }
78
79 const int nelem = KnotVec(edge)->GetNE();
80
81 edgeV.Append(everts[1]);
82 edgeVki.Append(nelem);
83
84 for (std::size_t i=0; i<nes; ++i)
85 {
86 const int edge_i = slaveEdges[masterEdgeInfo[mid].slaves[i]];
87 edgeE.Append(edge_i);
88
89 Array<int> sverts(2);
90 if (edge_i >= 0) // If a slave edge
91 {
92 patchTopo->GetEdgeVertices(edge_i, sverts);
93 }
94 else
95 {
96 const int auxEdge = FlipIndexSign(edge_i);
97 GetAuxEdgeVertices(auxEdge, sverts);
98 }
99
100 MFEM_ASSERT((sverts[0] == edgeV[i] &&
101 sverts[1] == edgeV[i+1]) ||
102 (sverts[1] == edgeV[i] &&
103 sverts[0] == edgeV[i+1]), "");
104 }
105}
106
107void NCNURBSExtension::FindAdditionalFacesSA(
108 std::map<std::pair<int, int>, int> &v2f,
109 std::set<int> &addParentFaces,
110 std::vector<FacePairInfo> &facePairs)
111{
112 for (int f=0; f<patchTopo->GetNFaces(); ++f)
113 {
114 if (masterFaces.find(f) != masterFaces.end())
115 {
116 continue; // Already a master face
117 }
118
119 Array<int> edges, ori, verts;
120 patchTopo->GetFaceEdges(f, edges, ori);
121 patchTopo->GetFaceVertices(f, verts);
122
123 MFEM_ASSERT(edges.Size() == 4 && verts.Size() == 4, "");
124
125 const int fn1 = KnotVec(edges[0])->GetNE();
126 const int fn2 = KnotVec(edges[1])->GetNE();
127
128 // Loop over the 2 pairs of opposite sides
129 for (int p=0; p<2; ++p) // Pair p
130 {
131 std::array<int, 2> oppEdges;
132 const int sideEdge0 = edges[1 - p];
133 bool bothMaster = true;
134 for (int s=0; s<2; ++s)
135 {
136 oppEdges[s] = edges[p + 2*s];
137
138 bool isTrueMasterEdge = false;
139 if (masterEdges.count(oppEdges[s]) > 0)
140 {
141 const int mid = masterEdgeToId.at(oppEdges[s]);
142 if (masterEdgeInfo[mid].slaves.size() != 0) { isTrueMasterEdge = true; }
143 }
144
145 if (!isTrueMasterEdge) { bothMaster = false; }
146 }
147
148 if (!bothMaster) { continue; }
149
150 // Possibly define auxiliary and/or slave faces on this face
151
152 // Check for auxiliary and slave edges
153 std::vector<Array<int>> sideAuxEdges(2);
154 std::vector<Array<int>> sideSlaveEdges(2);
155 for (int s=0; s<2; ++s)
156 {
157 const int mid = masterEdgeToId.at(oppEdges[s]);
158 for (auto edge : masterEdgeInfo[mid].slaves)
159 {
160 if (edge < 0)
161 {
162 sideAuxEdges[s].Append(FlipIndexSign(edge));
163 }
164 else
165 {
166 sideSlaveEdges[s].Append(edge);
167 }
168 }
169 }
170
171 const bool hasAux = sideAuxEdges[0].Size() > 0;
172 const bool hasSlave = sideSlaveEdges[0].Size() > 0;
173
174 // Find patchTopo vertices in the interior of each side
175 if (hasAux || hasSlave)
176 {
177 std::vector<Array<int>> edgeV(2);
178 std::vector<Array<int>> edgeE(2);
179 std::vector<Array<int>> edgeVki(2);
180
181 for (int s=0; s<2; ++s)
182 {
183 GetMasterEdgeEntities(oppEdges[s], edgeV[s], edgeE[s], edgeVki[s]);
184 }
185
186 // Check whether the number and types of edges on opposite
187 // sides match. If not, skip this pair of sides.
188 if (edgeE[0].Size() != edgeE[1].Size()) { continue; }
189
190 const int nes = edgeE[0].Size();
191
192 {
193 bool matching = true;
194 for (int i=0; i<nes; ++i)
195 {
196 if ((edgeE[0][i] >= 0) != (edgeE[1][i] >= 0))
197 {
198 matching = false;
199 break;
200 }
201 }
202
203 if (!matching) { continue; }
204 }
205
206 // Check whether edgeV[s] are in the same order or reversed, for
207 // s=0,1.
208 bool rev = true;
209 {
210 Array<int> sideVerts0;
211 patchTopo->GetEdgeVertices(sideEdge0, sideVerts0);
212 sideVerts0.Sort();
213
214 std::array<bool, 2> found{false, false};
215 Array<int> ep(2);
216 for (int e=0; e<2; ++e) // Loop over ends
217 {
218 for (int s=0; s<2; ++s) // Loop over sides
219 {
220 ep[s] = edgeV[s][e * (edgeV[s].Size() - 1)];
221
222 for (int i=0; i<2; ++i)
223 {
224 if (ep[s] == sideVerts0[i])
225 {
226 found[i] = true;
227 }
228 }
229 }
230
231 ep.Sort();
232 if (ep == sideVerts0)
233 {
234 rev = false;
235 }
236 }
237
238 MFEM_ASSERT(found[0] && found[1], "");
239 }
240
241 // Find auxiliary or slave subfaces of face f.
242 // Note that there may be no master faces in patchTopo->ncmesh.
243
244 for (int i=0; i<2; ++i)
245 {
246 MFEM_ASSERT(edgeV[i].Size() == nes + 1, "");
247 }
248
249 for (int e=0; e<nes; ++e) // Loop over edges
250 {
251 std::array<int, 4> fverts{edgeV[0][e], edgeV[0][e + 1],
252 edgeV[1][rev ? nes - e - 1 : e + 1],
253 edgeV[1][rev ? nes - e : e]};
254
255 // Get indices with respect to the edge.
256 const int eki = edgeVki[0][e];
257 const int eki1 = edgeVki[0][e + 1];
258
259 const int e1ki = edgeVki[1][rev ? nes - e : e];
260 const int e1ki1 = edgeVki[1][rev ? nes - e - 1 : e + 1];
261
262 // ori_f is the signed shift such that fverts[abs1(ori_f)] is
263 // closest to vertex 0, verts[0], of parent face f, and the
264 // relative direction of the ordering is encoded in the sign.
265 int ori_f = 0;
266
267 // Set the 2D knot-span indices of the 4 vertices in fverts,
268 // ordered with respect to the face f.
269 Array2D<int> fki(4,2);
270 {
271 // Use eki to get the 2D face knot-span index.
272
273 // Number of elements on the edge.
274 const int eNE = edgeVki[0][edgeVki[0].Size() - 1];
275
276 if (p == 0)
277 {
278 MFEM_ASSERT(edgeV[0][0] == verts[0] ||
279 edgeV[0][edgeV[0].Size() - 1] == verts[0],
280 "");
281 MFEM_ASSERT(edgeV[1][0] == verts[3] ||
282 edgeV[1][edgeV[0].Size() - 1] == verts[3],
283 "");
284 MFEM_ASSERT(eNE == fn1, "");
285 MFEM_ASSERT(edgeVki[1][edgeVki[1].Size() - 1] == fn1,
286 "");
287
288 const bool rev0 = edgeV[0][0] != verts[0];
289 fki(0,0) = rev0 ? eNE - eki : eki;
290 fki(0,1) = 0;
291
292 fki(1,0) = rev0 ? eNE - eki1 : eki1;
293 fki(1,1) = 0;
294
295 if (rev0) { ori_f = -2; }
296
297 // Other side
298 const bool rev1 = edgeV[1][0] != verts[3];
299
300 fki(2,0) = rev1 ? eNE - e1ki1 : e1ki1;
301 fki(2,1) = fn2;
302
303 fki(3,0) = rev1 ? eNE - e1ki : e1ki;
304 fki(3,1) = fn2;
305
306 MFEM_ASSERT(fki(0,0) == fki(3,0) &&
307 fki(1,0) == fki(2,0), "");
308 }
309 else
310 {
311 MFEM_ASSERT(edgeV[0][0] == verts[1] ||
312 edgeV[0][edgeV[0].Size() - 1] == verts[1],
313 "");
314 MFEM_ASSERT(edgeV[1][0] == verts[0] ||
315 edgeV[1][edgeV[0].Size() - 1] == verts[0],
316 "");
317 MFEM_ASSERT(eNE == fn2, "");
318 MFEM_ASSERT(edgeVki[1][edgeVki[1].Size() - 1] == fn2,
319 "");
320
321 const bool rev0 = edgeV[0][0] != verts[1];
322 fki(0,0) = fn1;
323 fki(0,1) = rev0 ? eNE - eki : eki;
324
325 fki(1,0) = fn1;
326 fki(1,1) = rev0 ? eNE - eki1 : eki1;
327
328 if (rev0)
329 {
330 ori_f = -3;
331 }
332 else
333 {
334 ori_f = 3;
335 }
336
337 // Other side
338 const bool rev1 = edgeV[1][0] != verts[0];
339
340 fki(2,0) = 0;
341 fki(2,1) = rev1 ? fn2 - e1ki1 : e1ki1;
342
343 fki(3,0) = 0;
344 fki(3,1) = rev1 ? fn2 - e1ki : e1ki;
345
346 MFEM_ASSERT(fki(0,1) == fki(3,1) &&
347 fki(1,1) == fki(2,1), "");
348 }
349 }
350
351 // Returns the vertex with minimum knot-span indices.
352 auto VertexMinKI = [&fki]()
353 {
354 int id = -1;
355 {
356 std::array<int, 2> kiMin;
357 for (int j=0; j<2; ++j)
358 {
359 kiMin[j] = fki(0,j);
360 for (int i=1; i<4; ++i)
361 {
362 if (fki(i,j) < kiMin[j]) { kiMin[j] = fki(i,j); }
363 }
364 }
365
366 for (int i=0; i<4; ++i)
367 {
368 if (fki(i,0) == kiMin[0] && fki(i,1) == kiMin[1])
369 {
370 MFEM_ASSERT(id == -1, "");
371 id = i;
372 }
373 }
374 }
375
376 MFEM_ASSERT(id >= 0, "");
377 return id;
378 };
379
380 const std::pair<int, int> vpair = QuadrupleToPair(fverts);
381 if (edgeE[0][e] >= 0)
382 {
383 const bool vPairTopo = v2f.count(vpair) > 0;
384 if (!vPairTopo) { continue; }
385
386 const int sface = v2f.at(vpair);
387 addParentFaces.insert(f);
388
389 // Set facePairs
390
391 // Find the vertex with minimum knot-span indices.
392 const int vMinID = VertexMinKI();
393
394 std::array<int, 4> fvertsMasterOrdering;
395 for (int i=0; i<4; ++i)
396 {
397 if (ori_f >= 0)
398 {
399 fvertsMasterOrdering[i] = fverts[(vMinID + i) % 4];
400 }
401 else
402 {
403 fvertsMasterOrdering[i] = fverts[(vMinID + 4 - i) % 4];
404 }
405 }
406
407 const int ori_sface = GetFaceOrientation(patchTopo, sface,
408 fvertsMasterOrdering);
409
410 facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
411 SlaveFaceInfo{sface, ori_sface,
412 {fki(vMinID,0), fki(vMinID,1)},
413 {
414 fki((vMinID + 2) % 4,0) - fki(vMinID,0),
415 fki((vMinID + 2) % 4,1) - fki(vMinID,1)
416 }}});
417 }
418 else // Auxiliary face
419 {
420 const int afid = auxv2f.count(vpair) > 0 ?
421 auxv2f.at(vpair) : -1;
422
423 addParentFaces.insert(f);
424
425 const int vMinID = VertexMinKI();
426
427 if (afid >= 0)
428 {
429 // Find orientation of ordered vertices for this face,
430 // in fvertsOrdered, w.r.t. the auxFaces ordering.
431 std::array<int, 4> fvertsOrdered, afverts;
432
433 int ori_f2 = -1;
434 for (int i=0; i<4; ++i)
435 {
436 afverts[i] = auxFaces[afid].v[i];
437 if (ori_f >= 0)
438 {
439 fvertsOrdered[i] = fverts[(vMinID + i) % 4];
440 }
441 else
442 {
443 fvertsOrdered[i] = fverts[(vMinID + 4 - i) % 4];
444 }
445
446 if (fvertsOrdered[i] == afverts[0]) { ori_f2 = i; }
447 }
448
449 MFEM_ASSERT(ori_f2 >= 0, "");
450
451 if (fvertsOrdered[(ori_f2 + 1) % 4] != afverts[1])
452 {
453 for (int j=0; j<4; ++j)
454 {
455 MFEM_ASSERT(fvertsOrdered[(ori_f2 + 4 - j) % 4]
456 == afverts[j], "");
457 }
458
459 ori_f2 = FlipIndexSign(ori_f2);
460 }
461 else
462 {
463 for (int j=0; j<4; ++j)
464 {
465 MFEM_ASSERT(fvertsOrdered[(ori_f2 + j) % 4]
466 == afverts[j], "");
467 }
468 }
469
470 facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
471 SlaveFaceInfo{FlipIndexSign(afid), ori_f2,
472 {fki(vMinID,0), fki(vMinID,1)},
473 {
474 fki((vMinID + 2) % 4,0) - fki(vMinID,0),
475 fki((vMinID + 2) % 4,1) - fki(vMinID,1)
476 }}});
477 }
478 else
479 {
480 // Create a new auxiliary face.
481 const int auxFaceId = auxFaces.size();
482 // Find the knot-span indices of the vertices in fverts,
483 // with respect to the parent face.
484 AuxiliaryFace auxFace;
485 for (int i=0; i<4; ++i)
486 {
487 if (ori_f >= 0)
488 {
489 auxFace.v[i] = fverts[(vMinID + i) % 4];
490 }
491 else
492 {
493 auxFace.v[i] = fverts[(vMinID + 4 - i) % 4];
494 }
495 }
496
497 // Orientation is defined as 0 for a new auxiliary face.
498 ori_f = 0;
499
500 auxFace.parent = f;
501 auxFace.ori = ori_f;
502 for (int i=0; i<2; ++i)
503 {
504 auxFace.ksi0[i] = fki(vMinID,i);
505 auxFace.ksi1[i] = fki((vMinID + 2) % 4,i);
506 }
507
508 auxv2f[vpair] = auxFaces.size();
509 auxFaces.push_back(auxFace);
510
511 facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
512 SlaveFaceInfo{FlipIndexSign(auxFaceId), ori_f,
513 {fki(vMinID,0), fki(vMinID,1)},
514 {
515 fki((vMinID + 2) % 4,0) - fki(vMinID,0),
516 fki((vMinID + 2) % 4,1) - fki(vMinID,1)
517 }}});
518 }
519 }
520 }
521 }
522 } // Pair (p) loop
523 } // f
524}
525
526void NCNURBSExtension::ProcessFacePairs(int start, int midStart,
527 const std::vector<std::array<int, 2>> &parentSize,
528 std::vector<int> &parentVerts,
529 const std::vector<FacePairInfo> &facePairs)
530{
531 const int nfpairs = facePairs.size();
532 const bool is3D = Dimension() == 3;
533 MFEM_VERIFY(nfpairs > 0 || !is3D, "");
534 int midPrev = -1;
535 int orientation = 0;
536 for (int q=start; q<nfpairs; ++q)
537 {
538 // We assume that j is the fast index in (i,j).
539 // Note that facePairs is set by ProcessVertexToKnot3D.
540 const int i = facePairs[q].info.ksi[0];
541 const int j = facePairs[q].info.ksi[1];
542 const int nfe1 = facePairs[q].info.ne[0]; // Number of elements, direction 1
543 const int nfe2 = facePairs[q].info.ne[1]; // Number of elements, direction 2
544 const int v0 = facePairs[q].v0; // Bottom-left corner vertex of child face
545 const int childFace = facePairs[q].info.index;
546 const int parentFace = facePairs[q].parent;
547 const int cpori =
548 facePairs[q].info.ori; // Orientation for childFace w.r.t. parentFace
549 const int mid = masterFaceToId.at(parentFace);
550
551 // Ignore data about master faces already processed.
552 if (mid < midStart) { continue; }
553
554 MFEM_ASSERT(0 <= i && i < parentSize[mid][0] && 0 <= j &&
555 j < parentSize[mid][1], "");
556 if (mid != midPrev) // Next parent face
557 {
558 std::array<int, 4> pv;
559 for (int k=0; k<4; ++k) { pv[k] = parentVerts[(4*mid) + k]; }
560 const int ori = GetFaceOrientation(patchTopo, parentFace, pv);
561 // Ori is the signed shift such that pv[abs1(ori)] is vertex 0 of
562 // parentFace, and the relative direction of the ordering is encoded in
563 // the sign.
564 if (q > start && midPrev >= 0)
565 {
566 // For the previous parentFace, use previous orientation to reorder
567 // masterFaceSlaves, masterFaceSlaveCorners, masterFaceSizes.
568 std::array<int, 2> s0;
569 masterFaceInfo[midPrev].rev = Reorder2D(orientation, s0);
570 masterFaceInfo[midPrev].s0 = s0;
571 }
572
573 orientation = ori;
574 midPrev = mid;
575 } // next parent face
576
577 slaveFaces.emplace_back(SlaveFaceInfo{childFace, cpori, {i, j},
578 {nfe1, nfe2}});
579
580 const int si = slaveFaces.size() - 1;
581 masterFaceInfo[mid].slaves.push_back(si);
582 masterFaceInfo[mid].slaveCorners.push_back(v0);
583 masterFaceInfo[mid].ne[0] = parentSize[mid][0];
584 masterFaceInfo[mid].ne[1] = parentSize[mid][1];
585 } // Loop (q) over facePairs
586
587 if (midPrev >= 0)
588 {
589 std::array<int, 2> s0;
590 masterFaceInfo[midPrev].rev = Reorder2D(orientation, s0);
591 masterFaceInfo[midPrev].s0 = s0;
592 }
593}
594
595void NCNURBSExtension::GetAuxEdgeVertices(int auxEdge, Array<int> &verts) const
596{
597 verts.SetSize(2);
598 for (int i=0; i<2; ++i) { verts[i] = auxEdges[auxEdge].v[i]; }
599}
600
601void NCNURBSExtension::GetAuxFaceVertices(int auxFace, Array<int> &verts) const
602{
603 verts.SetSize(4);
604 for (int i=0; i<4; ++i) { verts[i] = auxFaces[auxFace].v[i]; }
605}
606
607void NCNURBSExtension::GetAuxFaceEdges(int auxFace, Array<int> &edges) const
608{
609 edges.SetSize(4);
610 Array<int> verts(2);
611 for (int i=0; i<4; ++i)
612 {
613 for (int j=0; j<2; ++j) { verts[j] = auxFaces[auxFace].v[(i + j) % 4]; }
614
615 verts.Sort();
616 const std::pair<int, int> edge_v(verts[0], verts[1]);
617 // Note that v2e is a map only for conforming patchTopo->ncmesh edges.
618 // Auxiliary edges are in auxv2e, not in v2e.
619 if (v2e.count(edge_v) > 0)
620 {
621 edges[i] = v2e.at(edge_v); // patchTopo edge
622 }
623 else // Auxiliary edge
624 {
625 edges[i] = FlipIndexSign(auxv2e.at(edge_v));
626 }
627 }
628}
629
630// Negative indices are for array `b`. Nonnegative indices are for array `a`,
631// except for index `a.Size()`, corresponding to `b[0]`.
632int OffsetHelper(int i, int j, const Array<int> &a, const Array<int> &b)
633{
634 if (i < 0)
635 {
636 return b[FlipIndexSign(i) + j];
637 }
638 else if (i + j < a.Size())
639 {
640 return a[i + j];
641 }
642 else
643 {
644 return b[0];
645 }
646}
647
648int NCNURBSExtension::GetEdgeOffset(bool dof, int edge, int increment) const
649{
650 return OffsetHelper(edge, increment, dof ? e_spaceOffsets : e_meshOffsets,
651 dof ? aux_e_spaceOffsets : aux_e_meshOffsets);
652}
653
654int NCNURBSExtension::GetFaceOffset(bool dof, int face, int increment) const
655{
656 return OffsetHelper(face, increment, dof ? f_spaceOffsets : f_meshOffsets,
657 dof ? aux_f_spaceOffsets : aux_f_meshOffsets);
658}
659
661 Array<int> &dofs) const
662{
663 MFEM_ASSERT(masterEdges.count(me) > 0, "Not a master edge");
664 const int mid = masterEdgeToId.at(me);
665
666 MFEM_ASSERT(masterEdgeInfo[mid].vertices.size() ==
667 masterEdgeInfo[mid].slaves.size() - 1, "");
668
669 const Array<int>& v_offsets = dof ? v_spaceOffsets : v_meshOffsets;
670 const std::size_t nes = masterEdgeInfo[mid].slaves.size();
671 for (std::size_t s=0; s<nes; ++s)
672 {
673 const int slaveId = slaveEdges[masterEdgeInfo[mid].slaves[s]];
674
675 Array<int> svert;
676 if (slaveId >= 0)
677 {
678 patchTopo->GetEdgeVertices(slaveId, svert);
679 }
680 else // Auxiliary edge
681 {
682 GetAuxEdgeVertices(FlipIndexSign(slaveId), svert);
683 }
684
685 bool reverse = false;
686 if (nes > 1)
687 {
688 const int mev = masterEdgeInfo[mid].vertices[std::max((int) s - 1,0)];
689 MFEM_ASSERT(mev == svert[0] || mev == svert[1], "");
690 if (s == 0)
691 {
692 // In this case, mev is the second vertex of the edge.
693 if (svert[0] == mev) { reverse = true; }
694 }
695 else
696 {
697 // In this case, mev is the first vertex of the edge.
698 if (svert[1] == mev) { reverse = true; }
699 }
700 }
701
702 const int eos = GetEdgeOffset(dof, slaveId, 0);
703 const int eos1 = GetEdgeOffset(dof, slaveId, 1);
704 const int nvs = eos1 - eos;
705 MFEM_ASSERT(nvs >= 0, "");
706
707 // Add all slave edge vertices/DOFs
708
709 Array<int> sdofs(nvs);
710 for (int j=0; j<nvs; ++j) { sdofs[j] = reverse ? eos1 - 1 - j : eos + j; }
711
712 dofs.Append(sdofs);
713
714 if (s < masterEdgeInfo[mid].slaves.size() - 1)
715 {
716 // Add interior vertex DOF
717 dofs.Append(v_offsets[masterEdgeInfo[mid].vertices[s]]);
718 }
719 }
720}
721
722// Set masterDofs.
723void NURBSPatchMap::SetMasterEdges(bool dof, const KnotVector *kv[])
724{
725 edgeMaster.SetSize(edges.Size());
726 edgeMasterOffset.SetSize(edges.Size());
727 masterDofs.SetSize(0);
728
729 int mos = 0;
730 for (int i=0; i<edges.Size(); ++i)
731 {
732 edgeMaster[i] = Ext->IsMasterEdge(edges[i]);
733 edgeMasterOffset[i] = mos;
734
735 if (edgeMaster[i])
736 {
737 Array<int> mdof;
738 Ext->GetMasterEdgeDofs(dof, edges[i], mdof);
739 masterDofs.Append(mdof);
740 mos += mdof.Size();
741 }
742 }
743}
744
745void NCNURBSExtension::GetFaceOrdering(int sf, int n1, int n2, int v0,
746 int e1, int e2, Array<int> &perm) const
747{
748 perm.SetSize(n1 * n2);
749
750 // The ordering of entities in the face is based on the vertices.
751
752 Array<int> faceEdges, ori, evert, e2vert, vert;
753 patchTopo->GetFaceEdges(sf, faceEdges, ori);
754 patchTopo->GetFaceVertices(sf, vert);
755 patchTopo->GetEdgeVertices(faceEdges[e1], evert);
756 MFEM_ASSERT(evert[0] == v0 || evert[1] == v0, "");
757
758 bool d[2];
759 d[0] = (evert[0] == v0);
760
761 const int v10 = d[0] ? evert[1] : evert[0];
762
763 // The face has {fn1,fn2} interior entities, with ordering based on `vert`.
764 // Now we find these sizes by first finding the edge with vertices [v0, v10].
765 int e0 = -1;
766 for (int i=0; i<4; ++i)
767 {
768 patchTopo->GetEdgeVertices(faceEdges[i], evert);
769 if ((evert[0] == v0 && evert[1] == v10) ||
770 (evert[1] == v0 && evert[0] == v10)) { e0 = i; }
771 }
772
773 MFEM_ASSERT(e0 >= 0, "");
774
775 const bool tr = e0 % 2 == 1; // True means (fn1,fn2) == (n2,n1)
776
777 patchTopo->GetEdgeVertices(faceEdges[e2], evert);
778 MFEM_ASSERT(evert[0] == v10 || evert[1] == v10, "");
779 d[1] = (evert[0] == v10);
780
781 const int v11 = d[1] ? evert[1] : evert[0];
782
783 int v01 = -1;
784 for (int i=0; i<4; ++i)
785 {
786 if (vert[i] != v0 && vert[i] != v10 && vert[i] != v11) { v01 = vert[i]; }
787 }
788
789 MFEM_ASSERT(v01 >= 0 && v01 == vert.Sum() - v0 - v10 - v11, "");
790
791 // Translate indices [v0, v10, v11, v01] to pairs of indices in {0,1}.
792 constexpr char ipair[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
793 int f00[2];
794
795 int allv[4] = {v0, v10, v11, v01};
796 int locv[4];
797 for (int i=0; i<4; ++i)
798 {
799 locv[i] = -1;
800 for (int j=0; j<4; ++j)
801 {
802 if (vert[j] == allv[i])
803 {
804 locv[i] = j;
805 }
806 }
807
808 MFEM_ASSERT(locv[i] >= 0, "");
809 }
810
811 for (int i=0; i<2; ++i) { f00[i] = ipair[locv[0]][i]; }
812
813 const int i0 = f00[0];
814 const int j0 = f00[1];
815
816 for (int i=0; i<n1; ++i)
817 for (int j=0; j<n2; ++j)
818 {
819 // Entity perm[i] of the face should be entity i in the master face
820 // ordering. The master face ordering varies faster in the direction
821 // from v0 to v10, and slower in the direction from v10 to v11, or
822 // equivalently, from v0 to v01.
823 if (tr)
824 {
825 const int fi = i0 == 0 ? j : n2 - 1 - j;
826 const int fj = j0 == 0 ? i : n1 - 1 - i;
827 const int p = fi + (fj * n2); // Index in the slave face ordering
828 const int m = i + (j * n1); // Index in the master face ordering
829 perm[m] = p;
830 }
831 else
832 {
833 const int fi = i0 == 0 ? i : n1 - 1 - i;
834 const int fj = j0 == 0 ? j : n2 - 1 - j;
835 const int p = fi + (fj * n1); // Index in the slave face ordering
836 const int m = i + (j * n1); // Index in the master face ordering
837 perm[m] = p;
838 }
839 }
840}
841
842// Set an integer, with a check that it is uninitialized (-1) or unchanged.
843bool ConsistentlySetEntry(int v, int &e)
844{
845 const bool consistent = e == -1 || e == v;
846 e = v;
847 return consistent;
848}
849
850// Reorder a 2D array to start at a corner given by (i0,j0) in {0,1}^2.
851void ReorderArray2D(int i0, int j0, const Array2D<int> &a,
853{
854 const int m = a.NumRows();
855 const int n = a.NumCols();
856
857 b.SetSize(m, n);
858
859 const int s0 = i0 == 0 ? 1 : -1;
860 const int s1 = j0 == 0 ? 1 : -1;
861 for (int i=0; i<m; ++i)
862 {
863 const int ia = (i0 * (m - 1)) + (s0 * i);
864 for (int j=0; j<n; ++j)
865 {
866 const int ja = (j0 * (n - 1)) + (s1 * j);
867 b(i, j) = a(ia, ja);
868 }
869 }
870}
871
872// Set a quadrilateral vertex index permutation for a given orientation.
873void GetVertexOrdering(int ori, std::array<int, 4> &perm)
874{
875 const int oriAbs = UnsignIndex(ori);
876
877 for (int i=0; i<4; ++i)
878 {
879 if (ori < 0)
880 {
881 perm[i] = (oriAbs - i + 4) % 4;
882 }
883 else
884 {
885 perm[i] = (ori + i) % 4;
886 }
887 }
888}
889
890// Append master face DOFs to masterDofs.
891void NURBSPatchMap::SetMasterFaces(bool dof)
892{
893 faceMaster.SetSize(faces.Size());
894 faceMasterOffset.SetSize(faces.Size());
895
896 // The loop over master edges is already done by SetMasterEdges, and now we
897 // append face DOFs to masterDofs.
898
899 int mos = masterDofs.Size();
900 for (int i=0; i<faces.Size(); ++i)
901 {
902 faceMaster[i] = Ext->IsMasterFace(faces[i]);
903 faceMasterOffset[i] = mos;
904
905 if (!faceMaster[i]) { continue; }
906
907 Array2D<int> mdof;
908 Ext->GetMasterFaceDofs(dof, faces[i], mdof);
909 if (mdof.NumRows() == 0)
910 {
911 faceMaster[i] = false;
912 continue;
913 }
914
915 for (int j=0; j<mdof.NumCols(); ++j)
916 for (int k=0; k<mdof.NumRows(); ++k)
917 {
918 masterDofs.Append(mdof(k,j));
919 }
920
921 mos += mdof.NumRows() * mdof.NumCols();
922 } // loop (i) over faces
923}
924
926 Array2D<int> &dofs) const
927{
928 const int mid = masterFaceToId.at(mf);
929 const bool rev = masterFaceInfo[mid].rev;
930 const int s0i = masterFaceInfo[mid].s0[0];
931 const int s0j = masterFaceInfo[mid].s0[1];
932 const int n1orig = masterFaceInfo[mid].ne[0];
933 const int n2orig = masterFaceInfo[mid].ne[1];
934
935 // Skip master faces with no slave faces (only having slave edges).
936 if (n1orig == 0 && n2orig == 0) { return; }
937
938 const int n1 = rev ? n2orig : n1orig;
939 const int n2 = rev ? n1orig : n2orig;
940
941 MFEM_ASSERT((n1 > 1 || n2 > 1) &&
942 n1 * n2 >= (int) masterFaceInfo[mid].slaves.size(),
943 "Inconsistent number of faces");
944
945 int fcnt = 0;
946 for (auto slaveId : masterFaceInfo[mid].slaves)
947 {
948 fcnt += slaveFaces[slaveId].ne[0] * slaveFaces[slaveId].ne[1];
949 }
950
951 MFEM_VERIFY(fcnt == n1 * n2, "");
952 MFEM_VERIFY((int) masterFaceInfo[mid].slaveCorners.size() <= n1 * n2, "");
953
954 // Set an array of vertices or DOFs for the interior of this master face.
955 // Set master face entity dimensions.
956 int mnf1, mnf2;
957 Array<int> medges;
958 {
959 Array<int> mori;
960 patchTopo->GetFaceEdges(mf, medges, mori);
961 }
962
963 MFEM_ASSERT(medges.Size() == 4, "");
964
965 if (dof)
966 {
967 mnf1 = KnotVec(medges[0])->GetNCP() - 2;
968 mnf2 = KnotVec(medges[1])->GetNCP() - 2;
969 }
970 else
971 {
972 mnf1 = KnotVec(medges[0])->GetNE() - 1;
973 mnf2 = KnotVec(medges[1])->GetNE() - 1;
974 }
975
976 // Set dimensions for a single mesh edge.
977 const int sne1 = (mnf1 - n1 + 1) / n1;
978 const int sne2 = (mnf2 - n2 + 1) / n2;
979
980 MFEM_ASSERT(sne1 * n1 == mnf1 - n1 + 1, "");
981 MFEM_ASSERT(sne2 * n2 == mnf2 - n2 + 1, "");
982
983 const Array<int> &v_offsets = dof ? v_spaceOffsets : v_meshOffsets;
984
985 Array2D<int> mdof(mnf1, mnf2);
986 mdof = -1;
987
988 bool consistent = true;
989
990 for (std::size_t s=0; s<masterFaceInfo[mid].slaves.size(); ++s)
991 {
992 const int sId = masterFaceInfo[mid].slaves[s];
993 const int slaveId = slaveFaces[sId].index;
994 const int v0 = masterFaceInfo[mid].slaveCorners[s];
995 const int ori = slaveFaces[sId].ori;
996 // ori gives the orientation and index of cv matching the first vertex of
997 // childFace, where cv is the array of slave face vertices in CCW order
998 // with respect to the master face.
999
1000 const int sI = slaveFaces[sId].ksi[0];
1001 const int sJ = slaveFaces[sId].ksi[1];
1002 const int ne1 = slaveFaces[sId].ne[0];
1003 const int ne2 = slaveFaces[sId].ne[1];
1004
1005 const int fos = GetFaceOffset(dof, slaveId, 0);
1006 const int fos1 = GetFaceOffset(dof, slaveId, 1);
1007 const int nvs = fos1 - fos;
1008
1009 // These offsets are for the interior entities of the face. To get the
1010 // lower edge, subtract 1.
1011 const int os1 = (sI * sne1) + sI;
1012 const int os2 = (sJ * sne2) + sJ;
1013
1014 std::array<int, 4> orderedVertices;
1015 std::array<bool, 4> edgeBdry;
1016 std::set<int> vbdry;
1017 int nf1 = 0, nf2 = 0;
1018 auto SetEdgeEntries = [&](int eidx, int edge, const Array<int> &evert,
1019 int &vstart)
1020 {
1021 const bool reverse = (vstart == evert[1]);
1022 const int vend = evert.Sum() - vstart;
1023 vstart = vend;
1024
1025 // Skip edges on the boundary of the master face.
1026 if (edgeBdry[eidx])
1027 {
1028 for (auto v : evert) { vbdry.insert(v); }
1029 return;
1030 }
1031 const bool horizontal = (eidx % 2 == 0);
1032 const int nf_e = horizontal ? nf1 : nf2;
1033
1034 // Edge entities
1035 const int eos = GetEdgeOffset(dof, edge, 0);
1036#ifdef MFEM_DEBUG
1037 const int eos1 = GetEdgeOffset(dof, edge, 1);
1038#endif
1039 const bool edgeIsMaster = masterEdges.count(edge) > 0;
1040
1041 Array<int> edofs;
1042 if (edgeIsMaster)
1043 {
1044 // This edge is a slave edge and a master edge. Instead of
1045 // getting DOFs from e_offsets, take them from the slave edges
1046 // of this edge.
1047 GetMasterEdgeDofs(dof, edge, edofs);
1048 }
1049 else
1050 {
1051 MFEM_ASSERT(eos1 - eos == nf_e, "");
1052
1053 edofs.SetSize(nf_e);
1054 for (int j=0; j<nf_e; ++j)
1055 {
1056 edofs[j] = eos + j;
1057 }
1058 }
1059
1060 MFEM_ASSERT(edofs.Size() == nf_e, "");
1061
1062 for (int j=0; j<nf_e; ++j)
1063 {
1064 int m1, m2;
1065 if (eidx == 0)
1066 {
1067 m1 = os1 + j;
1068 m2 = os2 - 1;
1069 }
1070 else if (eidx == 1)
1071 {
1072 m1 = os1 + nf1;
1073 m2 = os2 + j;
1074 }
1075 else if (eidx == 2)
1076 {
1077 m1 = os1 + nf1 - 1 - j;
1078 m2 = os2 + nf2;
1079 }
1080 else
1081 {
1082 m1 = os1 - 1;
1083 m2 = os2 + nf2 - 1 - j;
1084 }
1085
1086 if (!ConsistentlySetEntry(reverse ? edofs[nf_e - 1 - j]
1087 : edofs[j], mdof(m1, m2)))
1088 {
1089 consistent = false;
1090 }
1091 }
1092 };
1093
1094 if (slaveId < 0)
1095 {
1096 // Auxiliary face
1097 const int auxFace = FlipIndexSign(slaveId);
1098
1099 // Set slave face entity dimensions.
1100 if (dof)
1101 {
1102 nf1 = (sne1 * ne1) + ne1 - 1;
1103 nf2 = (sne2 * ne2) + ne2 - 1;
1104 }
1105 else
1106 {
1107 nf1 = auxFaces[auxFace].ksi1[0] - auxFaces[auxFace].ksi0[0] - 1;
1108 nf2 = auxFaces[auxFace].ksi1[1] - auxFaces[auxFace].ksi0[1] - 1;
1109 }
1110
1111 MFEM_VERIFY(sne1 * ne1 == nf1 - ne1 + 1 &&
1112 sne2 * ne2 == nf2 - ne2 + 1 &&
1113 nvs == nf1 * nf2, "");
1114
1115 // If ori >= 0, then vertex ori of the aux face is closest to vertex 0
1116 // of the parent face. If ori < 0, then the orientations of the aux face
1117 // and parent face are reversed.
1118
1119 // NOTE: When an aux face is first defined, it is constructed with
1120 // orientation 0 w.r.t. its parent face. However, it can be part of
1121 // another parent face. In this case, the original aux face index is
1122 // used, but it is paired with a different parent face, with
1123 // possibly different orientation w.r.t. that parent face. In
1124 // NURBSPatchMap::SetMasterFaces, the DOFs are set on the parent
1125 // face in its knot-span indices, with an aux face of possibly nonzero
1126 // orientation. When the orientation is nonzero, the aux face DOFs
1127 // must be reordered for the parent face, based on orientation.
1128
1129 int onf1, onf2;
1130 GetInverseShiftedDimensions2D(ori, nf1, nf2, onf1, onf2);
1131 for (int k=0; k<onf2; ++k)
1132 for (int j=0; j<onf1; ++j)
1133 {
1134 int sm, sn, sj, sk;
1135 GetShiftedGridPoints2D(onf1, onf2, j, k, ori, sm, sn, sj, sk);
1136 const int q = j + (k * onf1);
1137 if (!ConsistentlySetEntry(fos + q, mdof(os1 + sj, os2 + sk)))
1138 {
1139 consistent = false;
1140 }
1141 }
1142
1143 // Set entries on edges of this face, if interior to the master face.
1144
1145 // Horizontal edges
1146 edgeBdry[0] = sJ == 0;
1147 edgeBdry[2] = sJ + ne2 == n2;
1148
1149 // Vertical edges
1150 edgeBdry[1] = sI + ne1 == n1;
1151 edgeBdry[3] = sI == 0;
1152
1153 Array<int> faceEdges;
1154 GetAuxFaceEdges(auxFace, faceEdges);
1155
1156 std::array<int, 4> perm;
1157 GetVertexOrdering(ori, perm);
1158
1159 int vstart = v0;
1160 for (int eidx=0; eidx<4; ++eidx)
1161 {
1162 orderedVertices[eidx] = vstart;
1163 MFEM_ASSERT(orderedVertices[eidx] ==
1164 auxFaces[auxFace].v[perm[eidx]], "");
1165 const int eperm = ori < 0 ? (perm[eidx] - 1 + 4) % 4 : perm[eidx];
1166 const int edge = faceEdges[eperm];
1167 Array<int> evert;
1168 if (edge >= 0)
1169 {
1170 patchTopo->GetEdgeVertices(edge, evert);
1171 }
1172 else
1173 {
1174 const int auxEdge = FlipIndexSign(edge);
1175 GetAuxEdgeVertices(auxEdge, evert);
1176 }
1177 MFEM_ASSERT(evert[0] == vstart || evert[1] == vstart, "");
1178 SetEdgeEntries(eidx, edge, evert, vstart);
1179 } // eidx
1180 }
1181 else // slaveId >= 0
1182 {
1183 // Determine which slave face edges are in the first and second
1184 // dimensions of the master face, by using ori.
1185 int e1 = -1, e2 = -1;
1186 {
1187 const int aori = UnsignIndex(ori);
1188 if (aori % 2 == 0)
1189 {
1190 e1 = 0;
1191 e2 = 1;
1192 }
1193 else
1194 {
1195 e1 = 1;
1196 e2 = 0;
1197 }
1198
1199 if (ori < 0)
1200 {
1201 // Swap e1, e2
1202 const int sw = e1;
1203 e1 = e2;
1204 e2 = sw;
1205 }
1206 }
1207
1208 // Now, e1 is one of the two horizontal edges in the master face
1209 // directions. If it does not touch v0, then take the other horizontal
1210 // edge. Do the same for e2.
1211
1212 Array<int> sedges;
1213 {
1214 Array<int> sori;
1215 patchTopo->GetFaceEdges(slaveId, sedges, sori);
1216 }
1217
1218 int v1 = -1;
1219 {
1220 Array<int> evert;
1221 patchTopo->GetEdgeVertices(sedges[e1], evert);
1222 if (evert.Find(v0) == -1)
1223 {
1224 e1 += 2;
1225 patchTopo->GetEdgeVertices(sedges[e1], evert);
1226 }
1227
1228 const int idv0 = evert.Find(v0);
1229 MFEM_ASSERT(idv0 >= 0, "");
1230 v1 = evert[1 - idv0];
1231
1232 patchTopo->GetEdgeVertices(sedges[e2], evert);
1233 if (evert.Find(v1) == -1)
1234 {
1235 e2 += 2;
1236 patchTopo->GetEdgeVertices(sedges[e2], evert);
1237 }
1238
1239 MFEM_ASSERT(evert.Find(v1) >= 0, "");
1240 }
1241
1242 // Set slave face entity dimensions.
1243 if (dof)
1244 {
1245 nf1 = KnotVec(sedges[e1])->GetNCP() - 2;
1246 nf2 = KnotVec(sedges[e2])->GetNCP() - 2;
1247 }
1248 else
1249 {
1250 nf1 = KnotVec(sedges[e1])->GetNE() - 1;
1251 nf2 = KnotVec(sedges[e2])->GetNE() - 1;
1252 }
1253
1254 MFEM_ASSERT(sne1 * ne1 == nf1 - ne1 + 1, "");
1255 MFEM_ASSERT(sne2 * ne2 == nf2 - ne2 + 1, "");
1256
1257 MFEM_ASSERT(nvs == nf1 * nf2, "");
1258
1259 // Find the DOFs of the slave face ordered for the master face. We know
1260 // that e1 and e2 are the local indices of the slave face edges on the
1261 // bottom and right side, with respect to the master face directions.
1262 Array<int> perm;
1263 GetFaceOrdering(slaveId, nf1, nf2, v0, e1, e2, perm);
1264
1265 for (int k=0; k<nf2; ++k)
1266 for (int j=0; j<nf1; ++j)
1267 {
1268 const int q = j + (k * nf1);
1269 if (!ConsistentlySetEntry(fos + perm[q],
1270 mdof(os1 + j, os2 + k)))
1271 {
1272 consistent = false;
1273 }
1274 }
1275
1276 // Set entries on edges of this face, if interior to the master face.
1277 std::array<int, 4> edgeOrder{e1, e2, (e1 + 2) % 4, (e2 + 2) % 4};
1278
1279 // Horizontal edges
1280 edgeBdry[0] = sJ == 0;
1281 edgeBdry[2] = sJ + ne2 == n2;
1282
1283 // Vertical edges
1284 edgeBdry[1] = sI + ne1 == n1;
1285 edgeBdry[3] = sI == 0;
1286
1287 int vstart = v0;
1288 for (int eidx=0; eidx<4; ++eidx)
1289 {
1290 orderedVertices[eidx] = vstart;
1291 const int edge = sedges[edgeOrder[eidx]];
1292 Array<int> evert;
1293 patchTopo->GetEdgeVertices(edge, evert);
1294 SetEdgeEntries(eidx, edge, evert, vstart);
1295 } // eidx
1296 }
1297
1298 // Set entries at vertices of this face, if interior to the master face.
1299 for (int vidx=0; vidx<4; ++vidx)
1300 {
1301 const int v = orderedVertices[vidx];
1302 if (vbdry.count(v) == 0) // If not on the master face boundary.
1303 {
1304 int m1, m2;
1305 if (vidx == 0)
1306 {
1307 m1 = os1 - 1;
1308 m2 = os2 - 1;
1309 }
1310 else if (vidx == 1)
1311 {
1312 m1 = os1 + nf1;
1313 m2 = os2 - 1;
1314 }
1315 else if (vidx == 2)
1316 {
1317 m1 = os1 + nf1;
1318 m2 = os2 + nf2;
1319 }
1320 else
1321 {
1322 m1 = os1 - 1;
1323 m2 = os2 + nf2;
1324 }
1325
1326 if (!ConsistentlySetEntry(v_offsets[v], mdof(m1, m2)))
1327 {
1328 consistent = false;
1329 }
1330 }
1331 } // vidx
1332 } // Loop (s) over slave faces.
1333
1334 // Let `ori` be the signed shift such that pv[abs1(ori)] is vertex 0 of
1335 // parentFace, and the relative direction of the ordering is encoded in the
1336 // sign. Here, pv means parent vertices, as ordered in the mesh file. Then
1337 // Reorder2D takes `ori` and computes (s0i, s0j) as the corresponding integer
1338 // coordinates in {0,1}x{0,1}. Thus reference vertex (s0i, s0j) of pv (parent
1339 // vertices in mesh file) is vertex (0, 0) of parentFace. Currently, mdof is
1340 // in the ordering of pv, and the entries are now reordered, according to
1341 // parentFace vertex ordering, for appending to masterDofs. That means the
1342 // first entry appended to masterDofs should be the entry of mdof
1343 // corresponding to (s0i, s0j).
1344
1345 ReorderArray2D(s0i, s0j, mdof, dofs);
1346 const bool all_set = dofs.NumRows() * dofs.NumCols() == 0 || dofs.Min() >= 0;
1347 MFEM_VERIFY(all_set && consistent, "");
1348}
1349
1350int NURBSPatchMap::GetMasterEdgeDof(const int e, const int i) const
1351{
1352 const int os = edgeMasterOffset[e];
1353 return masterDofs[os + i];
1354}
1355
1356int NURBSPatchMap::GetMasterFaceDof(const int f, const int i) const
1357{
1358 const int os = faceMasterOffset[f];
1359 return masterDofs[os + i];
1360}
1361
1362void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
1363 std::set<int> &reversedParents,
1364 std::vector<EdgePairInfo> &edgePairs)
1365{
1366 auxEdges.clear();
1367 auxv2e.clear();
1368
1369 const int nv2k = v2k.Size();
1370
1371 int prevParent = -1;
1372 int prevV = -1;
1373 int prevKI = -1;
1374 for (int i=0; i<nv2k; ++i)
1375 {
1376 int tv, ks;
1377 std::array<int, 2> pv;
1378 v2k.GetVertex2D(i, tv, ks, pv);
1379
1380 // Given that the parent Mesh is not yet constructed, and all we have at
1381 // this point is patchTopo->ncmesh, we should only define master/slave
1382 // edges by indices in patchTopo->ncmesh, as done in the case of nonempty
1383 // nce.masters. Now find the edge in patchTopo->ncmesh with vertices
1384 // (pv[0], pv[1]), and define it as a master edge.
1385
1386 const std::pair<int, int> parentPair(pv[0] < pv[1] ? pv[0] : pv[1],
1387 pv[0] < pv[1] ? pv[1] : pv[0]);
1388
1389 MFEM_ASSERT(v2e.count(parentPair) > 0, "Vertex pair not found");
1390 const int parentEdge = v2e[parentPair];
1391 masterEdges.insert(parentEdge);
1392
1393 const int kv = KnotInd(parentEdge);
1394 parentToKV[parentPair] = std::array<int, 2> {kv, -1};
1395
1396 const bool rev = pv[1] < pv[0];
1397 if (rev) { reversedParents.insert(parentEdge); }
1398
1399 // Note that the logic here assumes that the "vertex_to_knotspan" data in
1400 // the mesh file has vertices in order of ascending knotIndex.
1401
1402 const bool newParentEdge = (prevParent != parentEdge);
1403 const int v0 = newParentEdge ? pv[0] : prevV;
1404
1405 if (ks == 1) { MFEM_ASSERT(newParentEdge, ""); }
1406
1407 // Find the edge in patchTopo->ncmesh with vertices (v0, tv), and define
1408 // it as a slave edge.
1409
1410 const std::pair<int, int> childPair(v0 < tv ? v0 : tv, v0 < tv ? tv : v0);
1411 const bool childPairTopo = v2e.count(childPair) > 0;
1412 if (!childPairTopo)
1413 {
1414 // Check whether childPair is in auxEdges.
1415 if (auxv2e.count(childPair) == 0)
1416 {
1417 // Create a new auxiliary edge
1418 auxv2e[childPair] = auxEdges.size();
1419 auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ? parentEdge :
1420 FlipIndexSign(parentEdge),
1421 {childPair.first, childPair.second},
1422 {newParentEdge ? 0 : prevKI, ks}});
1423 }
1424 }
1425
1426 const int childEdge = childPairTopo ? v2e[childPair] :
1427 FlipIndexSign(auxv2e[childPair]);
1428
1429 // Check whether this is the final vertex in this parent edge. Note that
1430 // the logic for comparing (pv[0],pv[1]) to the next parents assumes the
1431 // ordering does not change, which is ensured by the assumption that the
1432 // knot-span index is increasing.
1433 bool finalVertex = (i == nv2k-1);
1434 if (i < nv2k-1)
1435 {
1436 int tv_next, ks_next;
1437 std::array<int, 2> pv_next;
1438 v2k.GetVertex2D(i + 1, tv_next, ks_next, pv_next);
1439 if (pv_next[0] != pv[0] || pv_next[1] != pv[1])
1440 {
1441 finalVertex = true;
1442 }
1443 }
1444
1445 edgePairs.emplace_back(tv, ks, childEdge, parentEdge);
1446
1447 if (finalVertex)
1448 {
1449 // Also find the edge with vertices (tv, pv[1]), and define it as a
1450 // slave edge.
1451 const std::pair<int, int> finalChildPair(tv < pv[1] ? tv : pv[1],
1452 tv < pv[1] ? pv[1] : tv);
1453 const bool finalChildPairTopo = v2e.count(finalChildPair) > 0;
1454 if (!finalChildPairTopo)
1455 {
1456 // Check whether finalChildPair is in auxEdges.
1457 if (auxv2e.count(finalChildPair) == 0)
1458 {
1459 // Create a new auxiliary edge
1460 auxv2e[finalChildPair] = auxEdges.size();
1461
1462 // -1 denotes `ne` at endpoint
1463 auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
1464 FlipIndexSign(parentEdge) :
1465 parentEdge,
1466 {finalChildPair.first, finalChildPair.second},
1467 {ks, -1}});
1468 }
1469 }
1470
1471 const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
1472 FlipIndexSign(auxv2e[finalChildPair]);
1473 edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
1474 }
1475
1476 prevV = tv;
1477 prevKI = ks;
1478 prevParent = parentEdge;
1479 } // loop over vertices in vertex_to_knotspan
1480}
1481
1482void NCNURBSExtension::ProcessVertexToKnot3D(
1483 const VertexToKnotSpan &v2k,
1484 const std::map<std::pair<int, int>, int> &v2f,
1485 std::vector<std::array<int, 2>> &parentSize,
1486 std::vector<EdgePairInfo> &edgePairs,
1487 std::vector<FacePairInfo> &facePairs,
1488 std::vector<int> &parentFaces,
1489 std::vector<int> &parentVerts)
1490{
1491 auxEdges.clear();
1492 auxFaces.clear();
1493 auxv2e.clear();
1494 auxv2f.clear();
1495
1496 const int nv2k = v2k.Size();
1497
1498 // Note that the logic here assumes that the "vertex_to_knotspan" data in the
1499 // mesh file has vertices in order of ascending (k1,k2), with k2 being the
1500 // fast variable, and with corners skipped.
1501
1502 // Find parentOffset, which stores the indices in v2k at which parent faces
1503 // start.
1504 int prevParent = -1;
1505 std::vector<int> parentOffset;
1506 std::vector<bool> parentV2Kedge;
1507 int n1 = 0;
1508 int n2 = 0;
1509 int n1min = 0;
1510 int n2min = 0;
1511 for (int i = 0; i < nv2k; ++i)
1512 {
1513 int tv;
1514 std::array<int, 2> ks;
1515 std::array<int, 4> pv;
1516 v2k.GetVertex3D(i, tv, ks, pv);
1517
1518 // The face with vertices (pv[0], pv[1], pv[2], pv[3]) is defined as a
1519 // parent face.
1520 const std::pair<int, int> parentPair = QuadrupleToPair(pv);
1521 const int parentFace = v2f.at(parentPair);
1522 const bool newParentFace = (prevParent != parentFace);
1523 if (newParentFace)
1524 {
1525 parentOffset.push_back(i);
1526 parentFaces.push_back(parentFace);
1527
1528 // Find the knotvectors for the first two edges this face.
1529 {
1530 Array<int> edges, ori, verts;
1531 patchTopo->GetFaceEdges(parentFace, edges, ori);
1532 patchTopo->GetFaceVertices(parentFace, verts);
1533
1534 std::array<int,2> kv = {-1, -1};
1535 for (int e=0; e<2; ++e)
1536 {
1537 // Find the edge with vertices pv[e] and pv[e+1].
1538 for (auto edge : edges)
1539 {
1540 Array<int> evert;
1541 patchTopo->GetEdgeVertices(edge, evert);
1542 const bool matching = (evert[0] == pv[e] && evert[1] == pv[e+1]) ||
1543 (evert[1] == pv[e] && evert[0] == pv[e+1]);
1544 if (matching) { kv[e] = KnotInd(edge); }
1545 }
1546
1547 MFEM_ASSERT(kv[e] >= 0, "");
1548 }
1549
1550 parentToKV[parentPair] = std::array<int, 2> {kv[0], kv[1]};
1551 }
1552
1553 if (i > 0)
1554 {
1555 // In the case of only 1 element in the 1-direction, it is assumed
1556 // that the 2-direction has more than 1 element, so there are
1557 // knot-spans (0, ki2) and (1, ki2) for 0 < ki2 < n2. This will
1558 // result in n1 = 0, which should be 1. Also, n2 will be 1 less than
1559 // it should be. Similarly for the situation with directions
1560 // reversed.
1561 const int n1range = n1 - n1min;
1562 const int n2range = n2 - n2min;
1563 parentV2Kedge.push_back(n1range == 0 || n2range == 0);
1564 }
1565
1566 auto getEdgeNE = [&](int d)
1567 {
1568 Array<int> ev(2);
1569 for (int j=0; j<2; ++j) { ev[j] = pv[j + d]; }
1570 ev.Sort();
1571 return KnotVecNE(v2e.at(std::pair<int, int>(ev[0], ev[1])));
1572 };
1573 parentSize.emplace_back(std::array<int, 2> {getEdgeNE(0), getEdgeNE(1)});
1574
1575 n1 = ks[0]; // Finding max of ks[0]
1576 n2 = ks[1]; // Finding max of ks[1]
1577
1578 n1min = n1;
1579 n2min = n2;
1580 }
1581 else
1582 {
1583 n1 = std::max(n1, ks[0]); // Finding max of ks[0]
1584 n2 = std::max(n2, ks[1]); // Finding max of ks[1]
1585
1586 n1min = std::min(n1min, ks[0]);
1587 n2min = std::min(n2min, ks[1]);
1588 }
1589
1590 prevParent = parentFace;
1591 }
1592
1593 {
1594 const int n1range = n1 - n1min;
1595 const int n2range = n2 - n2min;
1596 parentV2Kedge.push_back(n1range == 0 || n2range == 0);
1597 }
1598
1599 const int numParents = parentOffset.size();
1600 parentOffset.push_back(nv2k);
1601
1602 std::set<int> visitedParentEdges;
1603 std::map<int, int> edgePairOS;
1604 bool consistent = true;
1605
1606 for (int parent = 0; parent < numParents; ++parent)
1607 {
1608 const int parentFace = parentFaces[parent];
1609
1610 int parentEdges[4];
1611 bool parentEdgeRev[4];
1612
1613 int tvi;
1614 std::array<int, 2> ks;
1615 std::array<int, 4> pv;
1616 v2k.GetVertex3D(parentOffset[parent], tvi, ks, pv);
1617
1618 // Set all 4 edges of the parent face as master edges.
1619 {
1620 Array<int> ev(2);
1621 for (int i=0; i<4; ++i)
1622 {
1623 for (int j=0; j<2; ++j)
1624 {
1625 ev[j] = pv[(i + j) % 4];
1626 }
1627
1628 const bool reverse = (ev[1] < ev[0]);
1629 parentEdgeRev[i] = reverse;
1630
1631 ev.Sort();
1632
1633 const std::pair<int, int> edge_i(ev[0], ev[1]);
1634
1635 const int parentEdge = v2e.at(edge_i);
1636 masterEdges.insert(parentEdge);
1637 parentEdges[i] = parentEdge;
1638 }
1639 }
1640
1641 n1 = parentSize[parent][0];
1642 n2 = parentSize[parent][1];
1643 Array2D<int> gridVertex(n1 + 1, n2 + 1);
1644 gridVertex = -1;
1645
1646 gridVertex(0,0) = pv[0];
1647 gridVertex(n1,0) = pv[1];
1648 gridVertex(n1,n2) = pv[2];
1649 gridVertex(0,n2) = pv[3];
1650
1651 for (int i=0; i<4; ++i) { parentVerts.push_back(pv[i]); }
1652
1653 int r1min = -1;
1654 int r1max = -1;
1655 int r2min = -1;
1656 int r2max = -1;
1657
1658 for (int i = parentOffset[parent]; i < parentOffset[parent + 1]; ++i)
1659 {
1660 v2k.GetVertex3D(i, tvi, ks, pv);
1661 gridVertex(ks[0], ks[1]) = tvi;
1662 if (i == parentOffset[parent])
1663 {
1664 // Initialize min/max
1665 r1min = ks[0];
1666 r1max = ks[0];
1667
1668 r2min = ks[1];
1669 r2max = ks[1];
1670 }
1671 else
1672 {
1673 r1min = std::min(r1min, ks[0]);
1674 r1max = std::max(r1max, ks[0]);
1675
1676 r2min = std::min(r2min, ks[1]);
1677 r2max = std::max(r2max, ks[1]);
1678 }
1679 } // loop over vertices in v2k
1680
1681 MFEM_ASSERT((r1max - r1min + 1) * (r2max - r2min + 1) >=
1682 parentOffset[parent + 1] - parentOffset[parent], "");
1683
1684 std::array<int,2> kvi;
1685 if (kvf.size() > 0)
1686 {
1687 const std::pair<int, int> parentPair = v2k.GetVertexParentPair(
1688 parentOffset[parent]);
1689 std::array<int, 2> kv = parentToKV.at(parentPair);
1690 for (int i=0; i<2; ++i) { kvi[i] = kv[i]; }
1691 }
1692
1693 // Default refinement factor
1694 const int rf = ref_factors.Size() == 3 ? ref_factors[0] : 1;
1695
1696 int n1orig = kvf_coarse.size() > 0 ? kvf_coarse[kvi[0]].Size() : n1 / rf;
1697 int n2orig = kvf_coarse.size() > 0 ? kvf_coarse[kvi[1]].Size() : n2 / rf;
1698
1699 if (kvf.size() > 0 && kvf_coarse.size() == 0)
1700 {
1701 n1orig = kvf[kvi[0]].Size();
1702 n2orig = kvf[kvi[1]].Size();
1703 }
1704
1705 if (kvf.size() > 0)
1706 {
1707 MFEM_ASSERT(kvf[kvi[0]].Sum() == n1 && kvf[kvi[1]].Sum() == n2, "");
1708 }
1709
1710 std::vector<Array<int>> cgrid(2);
1711 std::array<int,2> n_orig = {n1orig, n2orig};
1712 for (int dir=0; dir<2; ++dir)
1713 {
1714 cgrid[dir].SetSize(n_orig[dir] + 1);
1715 cgrid[dir][0] = 0;
1716 for (int ii = 0; ii < n_orig[dir]; ++ii)
1717 {
1718 const int iir = parentEdgeRev[dir] ? n_orig[dir] - 1 - ii : ii;
1719
1720 int d = 1; // refinement factor
1721
1722 if (kvf_coarse.size() > 0)
1723 {
1724 d = kvf_coarse[kvi[dir]][iir];
1725 }
1726 else if (kvf.size() > 0)
1727 {
1728 d = kvf[kvi[dir]][iir];
1729 }
1730
1731 cgrid[dir][ii + 1] = cgrid[dir][ii] + d;
1732 }
1733 }
1734
1735 MFEM_ASSERT(cgrid[0][n_orig[0]] == n1 && cgrid[1][n_orig[1]] == n2, "");
1736
1737 bool allset = true;
1738 bool hasSlaveFaces = false;
1739 bool hasAuxFace = false;
1740
1741 for (int ii=0; ii<=n_orig[0]; ++ii)
1742 {
1743 const int i = cgrid[0][ii];
1744 for (int jj=0; jj<=n_orig[1]; ++jj)
1745 {
1746 const int j = cgrid[1][jj];
1747 if (gridVertex(i,j) < 0)
1748 {
1749 allset = false;
1750 }
1751 else if (0 < i && i < n1 && 0 < j && j < n2)
1752 {
1753 hasSlaveFaces = true;
1754 }
1755 }
1756 }
1757
1758 auto SetFacePairOnGridRange = [&](int i0, int i1, int j0, int j1)
1759 {
1760 std::array<int, 4> cv{gridVertex(i0, j0), gridVertex(i1, j0),
1761 gridVertex(i1, j1), gridVertex(i0, j1)};
1762 const std::pair<int, int> childPair = QuadrupleToPair(cv);
1763
1764 // min(cv) may be negative, if gridVertex is not set everywhere.
1765 if (childPair.first < 0) { return; }
1766
1767 const int d0 = i1 - i0;
1768 const int d1 = j1 - j0;
1769
1770 const bool childPairTopo = v2f.count(childPair) > 0;
1771 if (childPairTopo)
1772 {
1773 const int childFace = v2f.at(childPair);
1774 const int ori = GetFaceOrientation(patchTopo, childFace, cv);
1775 // ori gives the orientation and index of cv matching the first
1776 // vertex of childFace.
1777 facePairs.emplace_back(
1778 FacePairInfo{cv[0], parentFace, SlaveFaceInfo{childFace,
1779 ori, {i0, j0}, {d0, d1}}});
1780 }
1781 else
1782 {
1783 // Check whether the parent face is on the boundary.
1784 const Mesh::FaceInformation faceInfo = patchTopo->GetFaceInformation(
1785 parentFace);
1786 const bool bdryParentFace = faceInfo.IsBoundary();
1787
1788 if (!allset && !bdryParentFace)
1789 {
1790 hasAuxFace = true;
1791 // Check whether childPair is in auxFaces.
1792 if (auxv2f.count(childPair) == 0)
1793 {
1794 // Create a new auxiliary face
1795 auxv2f[childPair] = auxFaces.size();
1796 AuxiliaryFace auxFace;
1797 for (int k=0; k<4; ++k) { auxFace.v[k] = cv[k]; }
1798
1799 auxFace.parent = parentFace;
1800 // Orientation is defined as 0 for a new auxiliary face.
1801 auxFace.ori = 0;
1802 auxFace.ksi0[0] = i0;
1803 auxFace.ksi0[1] = j0;
1804 auxFace.ksi1[0] = i1;
1805 auxFace.ksi1[1] = j1;
1806
1807 auxFaces.push_back(auxFace);
1808 facePairs.emplace_back(
1809 FacePairInfo{cv[0], parentFace,
1810 SlaveFaceInfo{FlipIndexSign(auxv2f[childPair]),
1811 0, {i0, j0}, {d0, d1}}});
1812 }
1813 }
1814 }
1815 };
1816
1817 // Loop over child faces and set facePairs, as well as auxiliary faces.
1818 for (int ii=0; ii<n_orig[0]; ++ii)
1819 {
1820 const int i = cgrid[0][ii];
1821 const int i1 = cgrid[0][ii + 1];
1822 for (int jj=0; jj<n_orig[1]; ++jj)
1823 {
1824 const int j = cgrid[1][jj];
1825 const int j1 = cgrid[1][jj + 1];
1826 SetFacePairOnGridRange(i, i1, j, j1);
1827 }
1828 }
1829
1830 // Loop over child boundary edges and set edgePairs.
1831 for (int dir=1; dir<=2; ++dir)
1832 {
1833 const int ne = dir == 1 ? n1 : n2;
1834 for (int s=0; s<2; ++s) // Loop over 2 sides for this direction.
1835 {
1836 const int parentEdge = parentEdges[dir == 1 ? 2*s : (2*s) + 1];
1837 const bool reverse_p = parentEdgeRev[dir == 1 ? 2*s : (2*s) + 1];
1838 // Sides with s=1 are reversed in defining parentEdgeRev.
1839 const bool reverse = s == 0 ? reverse_p : !reverse_p;
1840 const bool parentVisited = visitedParentEdges.count(parentEdge) > 0;
1841
1842 if (!parentVisited)
1843 {
1844 edgePairOS[parentEdge] = edgePairs.size();
1845 edgePairs.resize(edgePairs.size() + ne);
1846 }
1847
1848 int tvprev = -1;
1849 int kiprev = -1;
1850 bool lagTV = false;
1851 int firstEdge = -1;
1852 int os_e = 0;
1853
1854 // Loop edges in direction `dir`
1855 for (int e_orig = 0; e_orig < n_orig[dir-1]; ++e_orig)
1856 {
1857 const int e_i = os_e;
1858 const int e_orig_rev = reverse ? n_orig[dir-1] - 1 - e_orig : e_orig;
1859 int de = rf;
1860 if (kvf_coarse.size() > 0)
1861 {
1862 de = kvf_coarse[kvi[dir - 1]][e_orig_rev];
1863 }
1864 else if (kvf.size() > 0)
1865 {
1866 de = kvf[kvi[dir - 1]][e_orig_rev];
1867 }
1868
1869 os_e += de;
1870
1871 // For both directions, side s=0 has increasing indices and s=1
1872 // has decreasing indices.
1873
1874 const int i0 = e_i;
1875 const int i1 = e_i + de;
1876
1877 // Edge index with respect to the master edge.
1878 const int e_idx = reverse ? ne - e_i - de : e_i;
1879
1880 Array<int> cv(2);
1881 if (dir == 1)
1882 {
1883 cv[0] = gridVertex(i0,s*n2);
1884 cv[1] = gridVertex(i1,s*n2);
1885 }
1886 else
1887 {
1888 cv[0] = gridVertex((1-s)*n1, i0);
1889 cv[1] = gridVertex((1-s)*n1, i1);
1890 }
1891
1892 const int cv0 = cv[0];
1893 int tv_int = -1; // Top-vertex interior to the master edge
1894 int ki = -1; // Knot-span index of tv_int, w.r.t. the master edge
1895
1896 if (lagTV)
1897 {
1898 tv_int = tvprev;
1899 ki = kiprev;
1900 }
1901
1902 if (tvprev == -1)
1903 {
1904 kiprev = (i0 == 0 || i0 == ne) ? i1 : i0;
1905 // Top-vertex interior to the master edge
1906 tvprev = (i0 == 0 || i0 == ne) ? cv[1] : cv[0];
1907 }
1908 else if (e_i < ne - 1) // Don't set to the endpoint
1909 {
1910 kiprev = (tvprev == cv[0]) ? i1 : i0;
1911 // Next interior vertex along the master edge
1912 tvprev = (tvprev == cv[0]) ? cv[1] : cv[0];
1913 }
1914
1915 if (!lagTV)
1916 {
1917 tv_int = tvprev;
1918 ki = kiprev;
1919 }
1920
1921 cv.Sort();
1922 if (cv[0] < 0) // may occur if gridVertex is not set everywhere.
1923 {
1924 continue;
1925 }
1926 else if (firstEdge == -1)
1927 {
1928 firstEdge = e_i;
1929 if (e_i > 0)
1930 {
1931 tv_int = cv0;
1932 ki = i0;
1933 tvprev = cv.Sum() - cv0; // cv1
1934 kiprev = i1;
1935 lagTV = true;
1936 }
1937 }
1938
1939 const int tv = (e_idx == ne - de) ? -1 : tv_int;
1940 const int tvki = (e_idx == ne - de) ? -1 : (reverse ? ne - ki : ki);
1941
1942 const std::pair<int, int> edge_i(cv[0], cv[1]);
1943 const int childEdge = v2e.at(edge_i);
1944
1945 if (tv == -1) { lagTV = true; }
1946
1947 if (!parentVisited)
1948 {
1949 // edgePairs is ordered starting from the vertex of lower index.
1950 edgePairs[edgePairOS[parentEdge] + e_idx].Set(tv, tvki,
1951 childEdge,
1952 parentEdge);
1953 }
1954 else
1955 {
1956 // Consistency check
1957 const int os = edgePairOS[parentEdge];
1958 if (edgePairs[os + e_idx].child != childEdge ||
1959 edgePairs[os + e_idx].parent != parentEdge)
1960 {
1961 consistent = false;
1962 }
1963 }
1964 }
1965
1966 visitedParentEdges.insert(parentEdge);
1967 }
1968 } // dir
1969
1970 // Set auxiliary and patch-slave faces outside the set gridVertex. Here,
1971 // patch-slave refers to a slave face that is a face of a neighboring
1972 // patch and may contain multiple mesh faces. In general, there can be at
1973 // most 8 = 3^2 - 1 such faces.
1974
1975 std::array<int, 4> gv1 = {0, r1min, r1max, n1};
1976 std::array<int, 4> gv2 = {0, r2min, r2max, n2};
1977
1978 if (hasSlaveFaces && !allset)
1979 {
1980 for (int i=0; i<3; ++i)
1981 for (int j=0; j<3; ++j)
1982 {
1983 // Skip the middle, which is covered by gridVertex.
1984 if (i == 1 && j == 1) { continue; }
1985
1986 // Skip degenerate faces
1987 if (gv1[i] == gv1[i+1] || gv2[j] == gv2[j+1]) { continue; }
1988
1989 // Define auxiliary face (gv1[i], gv1[i+1]) x (gv2[j], gv2[j+1])
1990 SetFacePairOnGridRange(gv1[i], gv1[i+1], gv2[j], gv2[j+1]);
1991 }
1992 }
1993
1994 // Set auxiliary edges outside the gridVertex, on the boundary of the
1995 // parent face. Note that auxiliary edges cannot simply be found as edges
1996 // of auxiliary faces, because the faces above are defined only on parent
1997 // faces listed in V2K data. Other auxiliary faces will be defined in
1998 // FindAdditionalFacesSA by using auxiliary edges found below.
1999
2000 // Auxiliary edges in first and second directions
2001 for (int d=0; d<2; ++d)
2002 {
2003 for (int i=0; i<3; ++i)
2004 {
2005 if (i == 1) // Skip the middle, which is covered by gridVertex.
2006 {
2007 continue;
2008 }
2009
2010 for (int s=0; s<2; ++s) // Loop over 2 sides in this direction
2011 {
2012 // Set gv1 (d == 0) or gv2 (d == 1) for this edge.
2013 // Set range of set knot-span indices for this edge.
2014 int rmin = -1;
2015 int rmax = -1;
2016 const int n_d = d == 0 ? n1 : n2;
2017 for (int j=1; j<n_d; ++j)
2018 {
2019 const int tv = d == 0 ? gridVertex(j,s*n2) :
2020 gridVertex((1-s)*n1,j);
2021 if (tv >= 0)
2022 {
2023 if (rmin == -1)
2024 {
2025 // Initialize range
2026 rmin = j;
2027 rmax = j;
2028 }
2029 else
2030 {
2031 rmin = std::min(rmin, j);
2032 rmax = std::max(rmax, j);
2033 }
2034 }
2035 }
2036
2037 if (rmax == -1)
2038 {
2039 // No vertices set in gridVertex on the interior of this edge.
2040 continue;
2041 }
2042
2043 if (d == 0)
2044 {
2045 gv1[1] = rmin;
2046 gv1[2] = rmax;
2047 }
2048 else
2049 {
2050 gv2[1] = rmin;
2051 gv2[2] = rmax;
2052 }
2053
2054 const int pid = d == 0 ? 2*s : (2*s) + 1; // Parent index
2055 const bool reverse_p = parentEdgeRev[pid];
2056 // Sides with s=1 are reversed in defining parentEdgeRev.
2057 const bool reverse = s == 0 ? reverse_p : !reverse_p;
2058
2059 // Define an auxiliary edge (gv1[i], gv1[i+1])
2060 Array<int> cv(2);
2061 std::array<int, 2> ki;
2062
2063 if (d == 0)
2064 {
2065 cv[0] = gridVertex(gv1[i],s*n2);
2066 cv[1] = gridVertex(gv1[i+1],s*n2);
2067
2068 ki[0] = gv1[i];
2069 ki[1] = gv1[i+1];
2070 }
2071 else
2072 {
2073 cv[0] = gridVertex((1-s)*n1,gv2[i]);
2074 cv[1] = gridVertex((1-s)*n1,gv2[i+1]);
2075
2076 ki[0] = gv2[i];
2077 ki[1] = gv2[i+1];
2078 }
2079
2080 if (cv[0] == cv[1])
2081 {
2082 continue;
2083 }
2084
2085 // Top-vertex interior to the master edge.
2086 const int tv = i == 0 ? cv[1] : cv[0];
2087 const int tvki_f = i == 0 ? ki[1] : ki[0]; // face index
2088 const int tvki = reverse ? n_d - tvki_f : tvki_f; // edge index
2089
2090 cv.Sort();
2091 MFEM_ASSERT(cv[0] >= 0, "");
2092
2093 const std::pair<int, int> childPair(cv[0], cv[1]);
2094 const bool childPairTopo = v2e.count(childPair) > 0;
2095 if (!childPairTopo)
2096 {
2097 const int pv0 = d == 0 ? gridVertex(0,s*n2) :
2098 gridVertex((1-s)*n1,0);
2099 const int pv1 = d == 0 ? gridVertex(n1,s*n2) :
2100 gridVertex((1-s)*n1,n2);
2101 const std::pair<int, int> parentPair(pv0 < pv1 ? pv0 : pv1,
2102 pv0 < pv1 ? pv1 : pv0);
2103 const int parentEdge = v2e.at(parentPair);
2104 MFEM_ASSERT(parentEdges[pid] == parentEdge, "");
2105
2106 // Check whether childPair is in auxEdges.
2107 if (auxv2e.count(childPair) == 0)
2108 {
2109 const int knotIndex0 = (d == 0) ? gv1[i] : gv2[i];
2110 const int knotIndex1 = (d == 0) ? gv1[i+1] : gv2[i+1];
2111
2112 // Create a new auxiliary edge
2113 auxv2e[childPair] = auxEdges.size();
2114 auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
2115 parentEdge :
2116 FlipIndexSign(parentEdge),
2117 {childPair.first, childPair.second},
2118 {knotIndex0, knotIndex1}});
2119 }
2120
2121 const bool start = (i == 0 && !reverse) || (i != 0 && reverse);
2122 int end_idx = kvf_coarse.size() > 0 ?
2123 (start ? 0 : kvf_coarse[kvi[d]].Size() - 1) : 0;
2124 int de = kvf_coarse.size() > 0 ? kvf_coarse[kvi[d]][end_idx] : rf;
2125 if (kvf.size() > 0 && kvf_coarse.size() == 0)
2126 {
2127 end_idx = start ? 0 : kvf[kvi[d]].Size() - 1;
2128 de = kvf[kvi[d]][end_idx];
2129 }
2130
2131 const int e_idx_i = i == 0 ? 0 : n_d - de;
2132 const int e_idx = reverse ? n_d - de - e_idx_i : e_idx_i;
2133
2134 const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
2135 (e_idx == n_d - de) ? -1 : tvki,
2136 FlipIndexSign(auxv2e[childPair]),
2137 parentEdge);
2138
2139 const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
2140 if (unset)
2141 {
2142 edgePairs[edgePairOS[parentEdge] + e_idx] = ep_e;
2143 }
2144 else
2145 {
2146 // Verify matching
2147 MFEM_ASSERT(edgePairs[edgePairOS[parentEdge] + e_idx] == ep_e, "");
2148 }
2149 }
2150 else // childPairTopo == true, so this edge is a slave edge.
2151 {
2152 const int childEdge = v2e.at(childPair);
2153
2154 const int pv0 = d == 0 ? gridVertex(0,s*n2) : gridVertex((1-s)*n1,0);
2155 const int pv1 = d == 0 ? gridVertex(n1,s*n2) : gridVertex((1-s)*n1,n2);
2156 const std::pair<int, int> parentPair(pv0 < pv1 ? pv0 : pv1,
2157 pv0 < pv1 ? pv1 : pv0);
2158 const int parentEdge = v2e.at(parentPair);
2159 MFEM_ASSERT(parentEdges[pid] == parentEdge, "");
2160
2161 const bool start = (i == 0 && !reverse) || (i != 0 && reverse);
2162 int end_idx = kvf_coarse.size() > 0 ?
2163 (start ? 0 : kvf_coarse[kvi[d]].Size() - 1) : 0;
2164 int de = kvf_coarse.size() > 0 ? kvf_coarse[kvi[d]][end_idx] : rf;
2165 if (kvf.size() > 0 && kvf_coarse.size() == 0)
2166 {
2167 end_idx = start ? 0 : kvf[kvi[d]].Size() - 1;
2168 de = kvf[kvi[d]][end_idx];
2169 }
2170
2171 const int e_idx_i = i == 0 ? 0 : n_d - de;
2172 const int e_idx = reverse ? n_d - de - e_idx_i : e_idx_i;
2173
2174 const int tv_e = (e_idx == n_d - de) ? -1 : tv;
2175 const int tv_ki = (e_idx == n_d - de) ? -1 : tvki;
2176
2177 const EdgePairInfo ep_e(tv_e, tv_ki, childEdge, parentEdge);
2178
2179#ifdef MFEM_DEBUG
2180 const bool unset =
2181 !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
2182 const bool matching =
2183 edgePairs[edgePairOS[parentEdge] + e_idx] == ep_e;
2184 MFEM_ASSERT(unset || matching, "");
2185#endif
2186
2187 edgePairs[edgePairOS[parentEdge] + e_idx] = ep_e;
2188 }
2189 }
2190 }
2191 }
2192
2193 if (hasSlaveFaces || hasAuxFace) { masterFaces.insert(parentFace); }
2194 } // loop over parents
2195
2196 MFEM_VERIFY(consistent, "");
2197}
2198
2199void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
2200{
2201 auxface2patch.SetSize(auxFaces.size(), 2);
2202
2203 if (auxFaces.size() == 0) { return; }
2204
2205 auxface2patch = -1;
2206
2207 const int dim = Dimension();
2208
2209 bool consistent = true;
2210
2211 for (int p=0; p<num_structured_patches; ++p)
2212 {
2213 Array<int> faces, orient;
2214 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2215 else { patchTopo->GetElementFaces(p, faces, orient); }
2216
2217 for (auto face : faces)
2218 {
2219 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2220 masterFaceToId.count(face) > 0;
2221 if (isMaster) // If a master face
2222 {
2223 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2224 masterFaceToId.at(face);
2225 const std::vector<int> &slaves = dim == 2 ? masterEdgeInfo[mid].slaves :
2226 masterFaceInfo[mid].slaves;
2227 for (auto s : slaves)
2228 {
2229 if (s < 0)
2230 {
2231 // Auxiliary face.
2232 const int aux = FlipIndexSign(s);
2233 if (auxface2patch(aux, 0) >= 0)
2234 {
2235 if (auxface2patch(aux, 1) != -1) { consistent = false; }
2236 auxface2patch(aux, 1) = p;
2237 }
2238 else
2239 {
2240 auxface2patch(aux, 0) = p;
2241 }
2242 }
2243 }
2244 }
2245 }
2246 }
2247
2248 MFEM_VERIFY(consistent, "");
2249}
2250
2251void NCNURBSExtension::GetSlaveFaceToPatchTable(Array2D<int> &sface2patch)
2252{
2253 const int dim = Dimension();
2254 const int numUnique = dim == 2 ? slaveEdgesUnique.Size() :
2255 slaveFacesUnique.Size();
2256 sface2patch.SetSize(numUnique, 2);
2257
2258 if (numUnique == 0) { return; }
2259
2260 sface2patch = -1;
2261
2262 bool consistent = true;
2263
2264 for (int p=0; p<num_structured_patches; ++p)
2265 {
2266 Array<int> faces, orient;
2267 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2268 else { patchTopo->GetElementFaces(p, faces, orient); }
2269
2270 for (auto face : faces)
2271 {
2272 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2273 masterFaceToId.count(face) > 0;
2274 if (isMaster) // If a master face
2275 {
2276 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2277 masterFaceToId.at(face);
2278 const std::vector<int> &slaves = dim == 2 ? masterEdgeInfo[mid].slaves :
2279 masterFaceInfo[mid].slaves;
2280 for (auto id : slaves)
2281 {
2282 if (id >= 0)
2283 {
2284 const int s = dim == 2 ? slaveEdges[id] : slaveFaces[id].index;
2285 const int u = dim == 2 ? slaveEdgesToUnique[s] :
2286 slaveFacesToUnique[s];
2287 if (sface2patch(u, 0) >= 0)
2288 {
2289 if (sface2patch(u, 1) != -1) { consistent = false; }
2290 sface2patch(u, 1) = p;
2291 }
2292 else
2293 {
2294 sface2patch(u, 0) = p;
2295 }
2296 }
2297 }
2298 }
2299 }
2300 }
2301
2302 MFEM_VERIFY(consistent, "");
2303}
2304
2305void RemapKnotIndex(bool rev, const Array<int> &rf, int &k)
2306{
2307 const int ne = rf.Size();
2308 const int k0 = k;
2309 k = 0;
2310 for (int p=0; p<k0; ++p)
2311 {
2312 const int rp = rev ? ne - 1 - p : p;
2313 k += rf[rp];
2314 }
2315}
2316
2317void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
2318{
2319 for (auto auxEdge : auxEdges)
2320 {
2321 const int p = auxEdge.parent;
2322 const int parent = UnsignIndex(p);
2323 const int kv = KnotInd(parent);
2324 for (int i=0; i<2; ++i)
2325 {
2326 RemapKnotIndex(false, kvf[kv], auxEdge.ksi[i]);
2327 }
2328 }
2329
2330 for (auto auxFace : auxFaces)
2331 {
2332 Array<int> pv;
2333 std::array<int, 4> quad;
2334 patchTopo->GetFaceVertices(auxFace.parent, pv);
2335 MFEM_ASSERT(pv.Size() == 4, "");
2336 for (int i=0; i<4; ++i) { quad[i] = pv[i]; }
2337 // The face with vertices (pv0, pv1, pv2, pv3) is defined as a parent face.
2338 const std::pair<int, int> parentPair = QuadrupleToPair(quad);
2339 const std::array<int, 2> kv = parentToKV.at(parentPair);
2340
2341 RemapKnotIndex(false, kvf[kv[0]], auxFace.ksi0[0]);
2342 RemapKnotIndex(false, kvf[kv[0]], auxFace.ksi1[0]);
2343
2344 RemapKnotIndex(false, kvf[kv[1]], auxFace.ksi0[1]);
2345 RemapKnotIndex(false, kvf[kv[1]], auxFace.ksi1[1]);
2346 }
2347}
2348
2350
2351void NCNURBSExtension::LoadFactorsForKV(const std::string &filename)
2352{
2353 if (kvf_coarse.size() == 0) { kvf_coarse = kvf; }
2354 if (kvf.size() == 0) { kvf.resize(NumOfKnotVectors); }
2355
2356 for (int kv=0; kv<NumOfKnotVectors; ++kv)
2357 {
2358 kvf[kv].SetSize(knotVectors[kv]->GetNE());
2359 kvf[kv] = unsetFactor;
2360 }
2361
2362 if (filename.empty()) { return; }
2363
2364 ifstream f(filename);
2365 int nkv;
2366 f >> nkv;
2367
2368 for (int i=0; i<nkv; ++i)
2369 {
2370 int kv, nf, rf;
2371 f >> kv >> nf;
2372 MFEM_ASSERT(nf == 1, ""); // TODO: support input of multiple factors.
2373
2374 kvf[kv] = unsetFactor;
2375 for (int j=0; j<nf; ++j) { f >> rf; }
2376
2377 for (int j=0; j<kvf[kv].Size(); ++j) { kvf[kv][j] = rf; }
2378 }
2379
2380 f.close();
2381}
2382
2383int NCNURBSExtension::AuxiliaryEdgeNE(int aux_edge)
2384{
2385 const int signedParentEdge = auxEdges[aux_edge].parent;
2386 const int ki0 = auxEdges[aux_edge].ksi[0];
2387 const int ki1raw = auxEdges[aux_edge].ksi[1];
2388 const int ki1 = ki1raw == -1 ? KnotVec(UnsignIndex(signedParentEdge))->GetNE()
2389 : ki1raw;
2390 return ki1 - ki0;
2391}
2392
2393// parentVerts are ordered with ascending knot-span index in parent edge, with
2394// knots from lower edge endpoint vertex to higher.
2395void NCNURBSExtension::SlaveEdgeToParent(int se, int parent,
2396 const Array<int> &os,
2397 const std::vector<int> &parentVerts,
2398 Array<int> &edges)
2399{
2400 Array<int> sev(2);
2401 if (se < 0) // Auxiliary edge
2402 {
2403 for (int i=0; i<2; ++i) { sev[i] = auxEdges[FlipIndexSign(se)].v[i]; }
2404 }
2405 else
2406 {
2407 patchTopo->GetEdgeVertices(se, sev);
2408 }
2409
2410 // Number of slave and auxiliary edges, not mesh edges
2411 const int nedge = parentVerts.size() + 1;
2412 MFEM_ASSERT((int) parentVerts.size() + 2 == os.Size(), "");
2413
2414 Array<int> parentEndpoints;
2415 patchTopo->GetEdgeVertices(parent, parentEndpoints);
2416
2417 bool found = false;
2418 for (int i=0; i<nedge; ++i)
2419 {
2420 const bool first = (i == 0);
2421 const bool last = (i == nedge - 1);
2422 const int v0 = first ? parentEndpoints[0] : parentVerts[i - 1];
2423 const int v1 = last ? parentEndpoints[1] : parentVerts[i];
2424 if (sev[0] == v0 && sev[1] == v1)
2425 {
2426 found = true;
2427 MFEM_ASSERT(edges.Size() == os[i + 1] - os[i], "");
2428 for (int j=0; j<edges.Size(); ++j) { edges[j] = os[i] + j; }
2429 }
2430 else if (sev[0] == v1 && sev[1] == v0)
2431 {
2432 found = true;
2433 MFEM_ASSERT(edges.Size() == os[i + 1] - os[i], "");
2434 for (int j=0; j<edges.Size(); ++j) { edges[j] = os[i + 1] - 1 - j; }
2435 }
2436 }
2437
2438 MFEM_VERIFY(found, "");
2439}
2440
2441void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
2442{
2443 const int np = masterEdgeInfo[mid].slaves.size();
2444 MFEM_VERIFY(np > 0, "");
2445 os.SetSize(np + 1);
2446 os[0] = 0;
2447
2448 for (int i=0; i<np; ++i)
2449 {
2450 const int p = masterEdgeInfo[mid].slaves[i];
2451 const int s = slaveEdges[p];
2452 int nes = 0;
2453 if (s >= 0)
2454 {
2455 nes = knotVectors[KnotInd(s)]->GetNE();
2456 }
2457 else
2458 {
2459 nes = AuxiliaryEdgeNE(FlipIndexSign(s));
2460 }
2461
2462 os[i+1] = os[i] + nes;
2463 }
2464}
2465
2467{
2468 Array<int> frf(rf.Sum());
2469 int os = 0;
2470 for (auto f : rf)
2471 {
2472 for (int i=0; i<f; ++i)
2473 {
2474 frf[os + i] = f;
2475 }
2476
2477 os += f;
2478 }
2479
2480 return frf;
2481}
2482
2484{
2485 constexpr char dirEdges3D[3][4] = {{0, 2, 4, 6}, {1, 3, 5, 7}, {8, 9, 10, 11}};
2486 constexpr char dirEdges2D[2][2] = {{0, 2}, {1, 3}};
2487
2488 Array<int> edges, oedges;
2489 patchTopo->GetElementEdges(p, edges, oedges);
2490
2491 const int dim = Dimension();
2492 const int nedge = dim == 3 ? 4 : 2;
2493
2494 int dirSet = 0;
2495
2496 bool partialChange = false;
2497 bool consistent = true;
2498
2499 auto SetFactorsDirection = [&](int j, int os_final, int af, Array<int> &rf)
2500 {
2501 if (af == unsetFactor) { return; }
2502 if (rf.Size() == 0)
2503 {
2504 rf.SetSize(os_final);
2505 rf = unsetFactor;
2506 }
2507 if (rf[j] != unsetFactor && af != rf[j]) { consistent = false; }
2508 rf[j] = af;
2509 };
2510
2511 auto SetFactorsEdge = [&](int j, int rf, Array<int> &pf)
2512 {
2513 if (rf == unsetFactor) { return; }
2514 if (pf[j] != unsetFactor && pf[j] != rf) { consistent = false; }
2515 pf[j] = rf;
2516 };
2517
2518 auto LoopEdgesForDirection = [&](int d, Array<int> &rf, bool first)
2519 {
2520 for (int i=0; i<nedge; ++i)
2521 {
2522 const int edgeIndex = dim == 3 ? dirEdges3D[d][i] : dirEdges2D[d][i];
2523 const int edge = edges[edgeIndex];
2524 const bool isMaster = IsMasterEdge(edge);
2525 const int kv = KnotInd(edge);
2526 const bool rev = KnotSign(edge) < 0;
2527
2528 if (first)
2529 {
2530 const int nfe = knotVectors[kv]->GetNE();
2531 const bool fullSize = kvf.size() > 0 && kvf[kv].Size() == nfe;
2532 const Array<int> rf_i = fullSize ? kvf[kv] :
2534 if (rf_i.Size() > 0 && rf.Size() == 0) { rf = rf_i; }
2535 }
2536 else
2537 {
2538 if (kvf[kv] != rf) { partialChange = true; }
2539 kvf[kv] = rf;
2540 }
2541
2542 if (isMaster)
2543 {
2544 // Check whether slave edges have factors set.
2545 const int mid = masterEdgeToId.at(edge);
2546 const int numPieces = masterEdgeInfo[mid].slaves.size();
2547 Array<int> os;
2548 GetMasterEdgePieceOffsets(mid, os);
2549
2550 for (int piece=0; piece<numPieces; ++piece)
2551 {
2552 const int e = masterEdgeInfo[mid].slaves[piece];
2553 const int s = slaveEdges[e];
2554 Array<int> parentEdges;
2555 Array<int> *pf; // Refinement factors for this piece
2556
2557 if (s >= 0) // Slave edge
2558 {
2559 const int kvs = KnotInd(s);
2560 parentEdges.SetSize(kvf[kvs].Size());
2561 pf = &kvf[kvs];
2562 }
2563 else // Aux edge
2564 {
2565 const int aux_edge = FlipIndexSign(s);
2566 if (auxef[aux_edge].Size() == 0)
2567 {
2568 auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
2569 auxef[aux_edge] = unsetFactor;
2570 }
2571 parentEdges.SetSize(auxef[aux_edge].Size());
2572 pf = &auxef[aux_edge];
2573 }
2574
2575 if (first && parentEdges.Size() == 0) { continue; }
2576 SlaveEdgeToParent(s, edge, os, masterEdgeInfo[mid].vertices, parentEdges);
2577 MFEM_ASSERT(parentEdges.Size() == os[piece + 1] - os[piece], "");
2578
2579 for (int j = os[piece]; j < os[piece + 1]; ++j)
2580 {
2581 const int jj = parentEdges[j - os[piece]];
2582 const int jr = rev ? rf.Size() - 1 - jj : jj;
2583 if (first)
2584 {
2585 SetFactorsDirection(jr, os[numPieces],
2586 (*pf)[j - os[piece]], rf);
2587 }
2588 else
2589 {
2590 SetFactorsEdge(j - os[piece], rf[jr], *pf);
2591 }
2592 }
2593 }
2594 }
2595 }
2596 };
2597
2598 for (int d=0; d<dim; ++d)
2599 {
2600 // Find the array of factors for direction d
2601 Array<int> rf;
2602 LoopEdgesForDirection(d, rf, true);
2603 if (rf.Size() == 0) { continue; } // This direction is unset
2604
2605 // Set the same factor for all knotvectors in direction d.
2606 LoopEdgesForDirection(d, rf, false);
2607 if (rf.Min() > unsetFactor) { dirSet += static_cast<int>(pow(2, d)); }
2608 }
2609
2610 MFEM_VERIFY(consistent, "");
2611 return partialChange ? FlipIndexSign(dirSet) : dirSet;
2612}
2613
2615{
2616 const int dim = Dimension();
2617 if (dim == 1 || num_structured_patches < 1)
2618 {
2619 for (size_t i=0; i<kvf.size(); ++i)
2620 {
2621 kvf[i] = rf_default;
2622 }
2623 return;
2624 }
2625
2626 // Note that a slave edge can be a patchTopo edge (nonnegative index) or an
2627 // AuxiliaryEdge (negative index). A slave edge can be contained in multiple
2628 // overlapping master edges.
2629
2630 // First, set slaveEdgesUnique.
2631 {
2632 slaveEdgesUnique.SetSize(0);
2633 slaveEdgesUnique.Reserve(slaveEdges.size());
2634 for (auto s : slaveEdges)
2635 {
2636 if (slaveEdgesToUnique.count(s) == 0)
2637 {
2638 slaveEdgesToUnique[s] = slaveEdgesUnique.Size();
2639 slaveEdgesUnique.Append(s);
2640 }
2641 }
2642 }
2643
2644 // Set slaveFacesUnique.
2645 {
2646 slaveFacesUnique.SetSize(0);
2647 slaveFacesUnique.Reserve(slaveFaces.size());
2648 for (auto s : slaveFaces)
2649 {
2650 if (slaveFacesToUnique.count(s.index) == 0)
2651 {
2652 slaveFacesToUnique[s.index] = slaveFacesUnique.Size();
2653 slaveFacesUnique.Append(s.index);
2654 }
2655 }
2656 }
2657
2658 // Initialize a set of patches to visit, using face-neighbors of the first
2659 // patch.
2660 const Table *face2elem = patchTopo->GetFaceToElementTable();
2661
2662 Array2D<int> auxface2patch, sface2patch;
2663 GetAuxFaceToPatchTable(auxface2patch);
2664 GetSlaveFaceToPatchTable(sface2patch);
2665
2666 Array<int> faces, orient;
2667
2668 auto faceNeighbors = [&](int p, std::set<int> &nghb)
2669 {
2670 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2671 else { patchTopo->GetElementFaces(p, faces, orient); }
2672
2673 for (auto face : faces)
2674 {
2675 Array<int> row;
2676 face2elem->GetRow(face, row);
2677
2678 const bool isSlave = dim == 2 ? slaveEdgesToUnique.count(
2679 face) > 0 : slaveFacesToUnique.count(face) > 0;
2680 if (isSlave)
2681 {
2682 const int u = dim == 2 ? slaveEdgesToUnique[face] :
2683 slaveFacesToUnique[face];
2684 for (int i=0; i<2; ++i)
2685 {
2686 const int elem = sface2patch(u, i);
2687 if (elem >= 0 && elem != p) { nghb.insert(elem); }
2688 }
2689 }
2690
2691 for (auto elem : row) { nghb.insert(elem); }
2692 }
2693 };
2694
2695 auto masterFaceNeighbors = [&](int p, std::set<int> &nghb)
2696 {
2697 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2698 else { patchTopo->GetElementFaces(p, faces, orient); }
2699
2700 for (auto face : faces)
2701 {
2702 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2703 masterFaceToId.count(face) > 0;
2704 if (isMaster) // If a master face
2705 {
2706 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2707 masterFaceToId.at(face);
2708 const std::vector<int> &slaves =
2709 dim == 2 ? masterEdgeInfo[mid].slaves : masterFaceInfo[mid].slaves;
2710 for (auto s : slaves)
2711 {
2712 if (s < 0)
2713 {
2714 // Auxiliary face.
2715 const int aux = FlipIndexSign(s);
2716 for (int i=0; i<2; ++i)
2717 {
2718 const int patch = auxface2patch(aux, i);
2719 if (patch >= 0) { nghb.insert(patch); }
2720 }
2721 }
2722 else
2723 {
2724 // Slave face in patchTopo.
2725 Array<int> row;
2726 face2elem->GetRow(s, row);
2727 for (auto elem : row) { nghb.insert(elem); }
2728 }
2729 }
2730 }
2731 }
2732 };
2733
2734 const int npatchall = patches.Size();
2735 Array<int> patchState(npatchall);
2736 patchState = 0;
2737
2738 auxef.resize(auxEdges.size());
2739
2740 std::set<int> nextPatches, unchanged;
2741 const int dirAllSet = dim == 3 ? 7 : 3;
2742 int lastChanged = 0;
2743 int iter = 0;
2744 bool done = false;
2745 while (iter < 100 && !done)
2746 {
2747 // Start each iteration at the patch last changed
2748 nextPatches.clear();
2749 nextPatches.insert(lastChanged);
2750
2751 std::set<int> visited; // Visit each patch only once per iteration
2752 iter++;
2753
2754 while (nextPatches.size() > 0)
2755 {
2756 const int p = *nextPatches.begin();
2757 nextPatches.erase(p);
2758
2759 visited.insert(p);
2760
2761 const int dirSetSigned = SetPatchFactors(p);
2762 const bool partialChange = dirSetSigned < 0;
2763 const int dirSet = UnsignIndex(dirSetSigned);
2764 const bool changed = (patchState[p] != dirSet) || partialChange;
2765 patchState[p] = dirSet;
2766
2767 // Find neighbors of patch p
2768 std::set<int> neighbors;
2769
2770 // First, find neighbors sharing a conforming face, via face2elem.
2771 faceNeighbors(p, neighbors);
2772
2773 // Second, find neighbors sharing a slave/auxiliary face in patchTopo.
2774 masterFaceNeighbors(p, neighbors);
2775
2776 // Add neighbors not done to nextPatches. Note that a patch can be
2777 // added to nextPatches on multiple iterations, to propagate factors in
2778 // different directions, on multiple sweeps.
2779
2780 for (auto n : neighbors)
2781 {
2782 if (n < npatchall && n != p && patchState[n] != dirAllSet &&
2783 visited.count(n) == 0)
2784 {
2785 nextPatches.insert(n);
2786 }
2787 }
2788
2789 if (changed)
2790 {
2791 unchanged.erase(p);
2792 lastChanged = p;
2793 }
2794 else
2795 {
2796 unchanged.insert(p);
2797 }
2798
2799 if (unchanged.size() == (size_t) npatchall)
2800 {
2801 // Make another pass through all patches to check for changes
2802 for (int i=0; i<npatchall; ++i)
2803 {
2804 const int dirSetSigned_i = SetPatchFactors(i);
2805 const bool partialChange_i = dirSetSigned_i < 0;
2806 const int dirSet_i = partialChange_i ?
2807 FlipIndexSign(dirSetSigned_i) : dirSetSigned_i;
2808 const bool changed_i = (patchState[i] != dirSet_i) ||
2809 partialChange_i;
2810 patchState[p] = dirSet_i;
2811 if (changed_i)
2812 {
2813 unchanged.erase(i);
2814 lastChanged = i;
2815 }
2816 }
2817 }
2818
2819 if (unchanged.size() == (size_t) npatchall)
2820 {
2821 done = true;
2822 break;
2823 }
2824 }
2825 }
2826
2827 delete face2elem;
2828
2829 // For any unset entries of kvf, set to default refinement factor rf_default.
2830 for (size_t i=0; i<kvf.size(); ++i)
2831 {
2832 if (kvf[i].Size() == 0)
2833 {
2834 kvf[i].SetSize(knotVectors[i]->GetNE());
2835 kvf[i] = rf_default;
2836 }
2837 else
2838 {
2839 for (int j=0; j<kvf[i].Size(); ++j)
2840 {
2841 if (kvf[i][j] == unsetFactor) { kvf[i][j] = rf_default; }
2842 }
2843 }
2844
2845 if (knotVectors[i]->spacing)
2846 {
2848 (knotVectors[i]->spacing.get());
2849 if (pws)
2850 {
2851 Array<int> pwn = pws->RelativePieceSizes();
2852 const bool rev = pws->GetReverse();
2853 const int np = pwn.Size();
2854 const int f = kvf[i].Size() / pwn.Sum();
2855 MFEM_ASSERT(kvf[i].Size() == f * pwn.Sum(), "");
2856
2857 Array<int> os(np + 1);
2858 os[0] = 0;
2859 for (int j=1; j<np+1; ++j)
2860 {
2861 const int jp = rev ? np - j : j - 1;
2862 os[j] = os[j-1] + (f * pwn[jp]);
2863 }
2864
2865 Array<int> pwf(np);
2866 for (int j=0; j<np; ++j)
2867 {
2868 pwf[j] = kvf[i][os[j]];
2869 for (int r=os[j]+1; r<os[j+1]; ++r)
2870 {
2871 MFEM_ASSERT(kvf[i][r] == pwf[j], "");
2872 }
2873 }
2874
2875 pws->ScalePartition(pwf, true);
2876 }
2877 }
2878 }
2879}
2880
2882{
2883 Array<int> rf(f.Sum());
2884
2885 int os = 0;
2886 for (int i=0; i<f.Size(); ++i)
2887 {
2888 const int f_i = f[i];
2889 for (int j=0; j<f_i; ++j) { rf[os + j] = f_i; }
2890
2891 os += f_i;
2892 }
2893
2894 MFEM_ASSERT(os == rf.Size(), "");
2895
2896 f = rf;
2897}
2898
2900 const std::string &kvf_filename,
2901 bool coarsened)
2902{
2903 if (ref_factors.Size() > 0)
2904 {
2905 MFEM_VERIFY(ref_factors.Size() == Dimension(), "");
2906 for (int i=0; i<ref_factors.Size(); ++i) { ref_factors[i] *= rf; }
2907 }
2908 else
2909 {
2911 ref_factors = rf;
2912 }
2913
2914 LoadFactorsForKV(kvf_filename);
2916
2917 Refine(coarsened);
2918}
2919
2920void NCNURBSExtension::ReadCoarsePatchCP(std::istream &input)
2921{
2922 input >> num_structured_patches;
2923
2924 const int maxOrder = mOrders.Max();
2925
2926 // For degree maxOrder, there are 2*(maxOrder + 1) knots for a single
2927 // element, and the number of control points in each dimension is
2928 // 2*(maxOrder + 1) - maxOrder - 1
2929 const int ncp1D = maxOrder + 1;
2930 const int ncp = static_cast<int>(pow(ncp1D, Dimension()));
2931
2933 for (int p=0; p<num_structured_patches; ++p)
2934 for (int i=0; i<ncp; ++i)
2935 for (int j=0; j<Dimension(); ++j) { input >> patchCP(p, i, j); }
2936}
2937
2938void NCNURBSExtension::PrintCoarsePatches(std::ostream &os)
2939{
2940 const int maxOrder = mOrders.Max();
2941 const int patchCP_size1 = patchCP.GetSize1();
2942 MFEM_VERIFY(patchCP_size1 == num_structured_patches || patchCP_size1 == 0,
2943 "");
2944
2945 if (patchCP_size1 == 0) { return; }
2946
2947 // For degree maxOrder, there are 2*(maxOrder + 1) knots for a single element,
2948 // and the number of control points in each dimension is
2949 // 2*(maxOrder + 1) - maxOrder - 1
2950 const int ncp1D = maxOrder + 1;
2951 const int ncp = static_cast<int>(pow(ncp1D, Dimension()));
2952
2953 os << "\npatch_cp\n" << num_structured_patches << "\n";
2954 for (int p=0; p<num_structured_patches; ++p)
2955 {
2956 for (int i=0; i<ncp; ++i)
2957 {
2958 os << patchCP(p, i, 0);
2959 for (int j=1; j<Dimension(); ++j)
2960 {
2961 os << ' ' << patchCP(p, i, j);
2962 }
2963 os << '\n';
2964 }
2965 }
2966}
2967
2969{
2970 MFEM_ASSERT(f.Size() == c.Sum(), "");
2971 bool consistent = true;
2972 int os = 0;
2973 for (int j=0; j<c.Size(); ++j)
2974 {
2975 const int cf = c[j];
2976 const int ff = f[os];
2977 for (int i=0; i<cf; ++i)
2978 {
2979 if (f[os + i] != ff) { consistent = false; }
2980 }
2981
2982 c[j] *= ff;
2983
2984 os += cf;
2985 }
2986
2987 MFEM_VERIFY(consistent, "");
2988}
2989
2990void NCNURBSExtension::UpdateCoarseKVF()
2991{
2992 if (kvf_coarse.size() == 0) { return; }
2993 for (int k=0; k<NumOfKnotVectors; ++k)
2994 {
2996 }
2997}
2998
2999int GetFaceOrientation(const Mesh *mesh, const int face,
3000 const std::array<int, 4> &verts)
3001{
3002 Array<int> fverts;
3003 mesh->GetFaceVertices(face, fverts);
3004 MFEM_ASSERT(fverts.Size() == 4, "");
3005
3006 // Verify that verts and fvert have the same entries as sets, by deep-copying
3007 // and sorting.
3008 {
3009 Array<int> s1(4);
3010 Array<int> s2(fverts);
3011
3012 for (int i=0; i<4; ++i) { s1[i] = verts[i]; }
3013
3014 s1.Sort(); s2.Sort();
3015 MFEM_ASSERT(s1 == s2, "");
3016 }
3017
3018 // Find the shift of the first vertex.
3019 int s = -1;
3020 for (int i=0; i<4; ++i)
3021 {
3022 if (verts[i] == fverts[0]) { s = i; }
3023 }
3024
3025 // Check whether ordering is reversed.
3026 const bool rev = verts[(s + 1) % 4] != fverts[1];
3027 if (rev) { s = FlipIndexSign(s); } // Reversed order is encoded by the sign.
3028 return s;
3029}
3030
3031// The 2D array `a` is of size n1*n2, with index j + n2*i corresponding to (i,j)
3032// with the fast index j, for 0 <= i < n1 and 0 <= j < n2. We assume that j is
3033// the fast index in (i,j). The orientation is encoded by ori, defining a shift
3034// and relative direction, such that a quad face F1, on which the ordering of
3035// `a` is based, has vertex with index `shift` matching vertex 0 of the new quad
3036// face F2, on which the new ordering of `a` should be based. For more details,
3037// see GetFaceOrientation.
3038bool Reorder2D(int ori, std::array<int, 2> &s0)
3039{
3040 const int shift = UnsignIndex(ori);
3041
3042 // Shift is an F1 index in the counter-clockwise ordering of 4 quad vertices.
3043 // Now find the (i,j) indices of this index, with i,j in {0,1}.
3044 const int s0i = (shift == 0 || shift == 3) ? 0 : 1;
3045 const int s0j = (shift < 2) ? 0 : 1;
3046
3047 s0[0] = s0i;
3048 s0[1] = s0j;
3049
3050 // Determine whether the dimensions of F1 and F2 are reversed. Do this by
3051 // finding the (i,j) indices of s1, which is the next vertex on F1.
3052 const int shift1 = ori < 0 ? shift - 1: shift + 1;
3053 const int s1 = (shift1 + 4) % 4;
3054 const int s1i = (s1 == 0 || s1 == 3) ? 0 : 1;
3055 const bool dimReverse = s0i == s1i;
3056
3057 return dimReverse;
3058}
3059
3060void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
3061 int &n)
3062{
3063 const bool rev = (signedShift < 0);
3064 const int shift = UnsignIndex(signedShift);
3065 MFEM_ASSERT(0 <= shift && shift < 4, "");
3066
3067 // We consider 8 cases for the possible values of rev and shift.
3068 if (rev)
3069 {
3070 if (shift == 0)
3071 {
3072 // New: 3 2 Old: 1 2
3073 // 0 1 0 3
3074 n = sm;
3075 m = sn;
3076 }
3077 else if (shift == 1)
3078 {
3079 // New: 3 2 Old: 2 3
3080 // 0 1 1 0
3081 m = sm;
3082 n = sn;
3083 }
3084 else if (shift == 2)
3085 {
3086 // New: 3 2 Old: 3 0
3087 // 0 1 2 1
3088 n = sm;
3089 m = sn;
3090 }
3091 else // shift == 3
3092 {
3093 // New: 3 2 Old: 0 1
3094 // 0 1 3 2
3095 m = sm;
3096 n = sn;
3097 }
3098 }
3099 else
3100 {
3101 if (shift == 0)
3102 {
3103 // New: 3 2 Old: 3 2
3104 // 0 1 0 1
3105 m = sm;
3106 n = sn;
3107 }
3108 else if (shift == 1)
3109 {
3110 // New: 3 2 Old: 0 3
3111 // 0 1 1 2
3112 n = sm;
3113 m = sn;
3114 }
3115 else if (shift == 2)
3116 {
3117 // New: 3 2 Old: 1 0
3118 // 0 1 2 3
3119 m = sm;
3120 n = sn;
3121 }
3122 else // shift == 3
3123 {
3124 // New: 3 2 Old: 2 1
3125 // 0 1 3 0
3126 n = sm;
3127 m = sn;
3128 }
3129 }
3130}
3131
3132void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
3133 int& sm, int& sn, int& si, int& sj)
3134{
3135 const bool rev = (signedShift < 0);
3136 const int shift = UnsignIndex(signedShift);
3137 MFEM_ASSERT(0 <= shift && shift < 4, "");
3138
3139 // (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
3140 // to new vertex 0 in counter-clockwise quad ordering.
3141
3142 // We consider 8 cases for the possible values of rev and shift.
3143 if (rev)
3144 {
3145 if (shift == 0)
3146 {
3147 // New: 3 2 Old: 1 2
3148 // 0 1 0 3
3149 sm = n;
3150 sn = m;
3151
3152 si = j;
3153 sj = i;
3154 }
3155 else if (shift == 1)
3156 {
3157 // New: 3 2 Old: 2 3
3158 // 0 1 1 0
3159 sm = m;
3160 sn = n;
3161
3162 si = m - 1 - i;
3163 sj = j;
3164 }
3165 else if (shift == 2)
3166 {
3167 // New: 3 2 Old: 3 0
3168 // 0 1 2 1
3169 sm = n;
3170 sn = m;
3171
3172 si = n - 1 - j;
3173 sj = m - 1 - i;
3174 }
3175 else // shift == 3
3176 {
3177 // New: 3 2 Old: 0 1
3178 // 0 1 3 2
3179 sm = m;
3180 sn = n;
3181
3182 si = i;
3183 sj = n - 1 - j;
3184 }
3185 }
3186 else
3187 {
3188 if (shift == 0)
3189 {
3190 // New: 3 2 Old: 3 2
3191 // 0 1 0 1
3192 sm = m;
3193 sn = n;
3194
3195 si = i;
3196 sj = j;
3197 }
3198 else if (shift == 1)
3199 {
3200 // New: 3 2 Old: 0 3
3201 // 0 1 1 2
3202 sm = n;
3203 sn = m;
3204
3205 si = j;
3206 sj = m - 1 - i;
3207 }
3208 else if (shift == 2)
3209 {
3210 // New: 3 2 Old: 1 0
3211 // 0 1 2 3
3212 sm = m;
3213 sn = n;
3214
3215 si = m - 1 - i;
3216 sj = n - 1 - j;
3217 }
3218 else // shift == 3
3219 {
3220 // New: 3 2 Old: 2 1
3221 // 0 1 3 0
3222 sm = n;
3223 sn = m;
3224
3225 si = n - 1 - j;
3226 sj = i;
3227 }
3228 }
3229}
3230
3231// Given a quadruple in q, return the pair (q_i, q_j), where q_i is the minimum
3232// entry of q, and q_j is the entry two indices away from q_i. When q contains
3233// indices of the vertices of a quadrilateral, the returned pair represents the
3234// unique diagonal touching the vertex of minimum index, which is a more concise
3235// way of representing the quadrilateral, facilitating the search of faces.
3236std::pair<int, int> QuadrupleToPair(const std::array<int, 4> &q)
3237{
3238 const auto qmin = std::min_element(q.begin(), q.end());
3239 const int idmin = std::distance(q.begin(), qmin);
3240 return std::pair<int, int>(q[idmin], q[(idmin + 2) % 4]);
3241}
3242
3243void VertexToKnotSpan::SetSize(int dimension, int numVertices)
3244{
3245 dim = dimension;
3246 MFEM_ASSERT((dim == 2 || dim == 3) && numVertices > 0, "Invalid size");
3247 data.SetSize(numVertices, dim == 3 ? 7 : 4);
3248}
3249
3250void VertexToKnotSpan::SetVertex2D(int index, int v, int ks,
3251 const std::array<int, 2> &pv)
3252{
3253 data(index,0) = v;
3254 data(index,1) = ks;
3255 data(index,2) = pv[0];
3256 data(index,3) = pv[1];
3257}
3258
3260 const std::array<int, 2> &ks,
3261 const std::array<int, 4> &pv)
3262{
3263 data(index,0) = v;
3264 data(index,1) = ks[0];
3265 data(index,2) = ks[1];
3266 data(index,3) = pv[0];
3267 data(index,4) = pv[1];
3268 data(index,5) = pv[2];
3269 data(index,6) = pv[3];
3270}
3271
3273{
3274 data(index,1) = ks;
3275}
3276
3277void VertexToKnotSpan::SetKnotSpans3D(int index, const std::array<int, 2> &ks)
3278{
3279 data(index,1) = ks[0];
3280 data(index,2) = ks[1];
3281}
3282
3283void VertexToKnotSpan::GetVertex2D(int index, int &v, int &ks,
3284 std::array<int, 2> &pv) const
3285{
3286 v = data(index,0);
3287 ks = data(index,1);
3288 pv[0] = data(index,2);
3289 pv[1] = data(index,3);
3290}
3291
3292void VertexToKnotSpan::GetVertex3D(int index, int &v, std::array<int, 2> &ks,
3293 std::array<int, 4> &pv) const
3294{
3295 v = data(index,0);
3296 ks[0] = data(index,1);
3297 ks[1] = data(index,2);
3298 pv[0] = data(index,3);
3299 pv[1] = data(index,4);
3300 pv[2] = data(index,5);
3301 pv[3] = data(index,6);
3302}
3303
3304void VertexToKnotSpan::Print(std::ostream &os) const
3305{
3306 const int nv = data.NumRows();
3307 const int m = data.NumCols();
3308 os << nv << "\n";
3309 for (int i = 0; i < nv; i++)
3310 {
3311 os << data(i,0);
3312 for (int j = 1; j < m; j++)
3313 {
3314 os << " " << data(i,j);
3315 }
3316 os << "\n";
3317 }
3318}
3319
3320std::pair<int, int> VertexToKnotSpan::GetVertexParentPair(int index) const
3321{
3322 if (dim == 3)
3323 {
3324 std::array<int, 4> pv;
3325 for (int i=0; i<4; ++i) { pv[i] = data(index, 3 + i); }
3326 // The face with vertices (pv[0], pv[1], pv[2], pv[3]) is defined as a
3327 // parent face.
3328 return QuadrupleToPair(pv);
3329 }
3330
3331 int c0 = data(index, 2);
3332 int c1 = data(index, 3);
3333 if (c0 > c1) { std::swap(c0, c1); }
3334 return std::pair<int, int>(c0, c1);
3335}
3336
3338{
3339 MFEM_VERIFY(!nonconformingPT,
3340 "NURBS NC-patch meshes cannot use this method of refinement");
3341
3342 if (ref_factors.Size())
3343 {
3344 MFEM_VERIFY(ref_factors.Size() == rf.Size(), "");
3345 for (int i=0; i<rf.Size(); ++i) { ref_factors[i] *= rf[i]; }
3346 }
3347 else
3348 {
3349 ref_factors = rf;
3350 }
3351
3352 Refine(false, &rf);
3353}
3354
3355void NCNURBSExtension::Refine(bool coarsened, const Array<int> *rf)
3356{
3357 const int maxOrder = mOrders.Max();
3358 const int dim = Dimension();
3359
3360 for (int p = 0; p < patches.Size(); p++)
3361 {
3362 if (nonconformingPT)
3363 {
3364 std::vector<Array<int>> prf(dim);
3366 Array<int> edges, orient;
3367 patchTopo->GetElementEdges(p, edges, orient);
3368
3369 if (dim == 3)
3370 {
3371 constexpr char e3[3] = {0, 3, 8};
3372 for (int i=0; i<3; ++i)
3373 {
3374 prf[i] = kvf[KnotInd(edges[e3[i]])];
3375 pkv[i] = knotVectors[KnotInd(edges[e3[i]])];
3376 }
3377 }
3378 else
3379 {
3380 MFEM_VERIFY(dim == 2, "");
3381 for (int i=0; i<2; ++i)
3382 {
3383 prf[i] = kvf[KnotInd(edges[i])];
3384 pkv[i] = knotVectors[KnotInd(edges[i])];
3385 }
3386 }
3387
3389 {
3390 for (int i=0; i<dim; ++i)
3391 {
3392 // Collapse prf[i] to a single factor
3393 MFEM_VERIFY(prf[i].IsConstant(), "");
3394 prf[i].SetSize(1);
3395 }
3396 }
3397
3398 patches[p]->UpdateSpacingPartitions(pkv);
3399 patches[p]->UniformRefinement(prf, coarsened, maxOrder);
3400 }
3401 else
3402 {
3403 patches[p]->UniformRefinement(*rf);
3404 }
3405 }
3406
3407 if (nonconformingPT)
3408 {
3410 UpdateAuxiliaryKnotSpans(ref_factors);
3411 UpdateCoarseKVF();
3412 }
3413}
3414
3415void NCNURBSExtension::SetDofToPatch()
3416{
3418 dof2patch = -1;
3419
3420 const int dim = Dimension();
3421 if (dim == 1) { return; }
3422
3423 Array<int> edges, faces, orient;
3424 const int np = patchTopo->GetNE();
3425
3426 for (int p = 0; p < np; p++)
3427 {
3428 patchTopo->GetElementEdges(p, edges, orient);
3429 for (auto e : edges)
3430 {
3431 if (masterEdges.count(e) > 0)
3432 {
3433 Array<int> mdof;
3434 GetMasterEdgeDofs(true, e, mdof);
3435 for (auto dof : mdof) { dof2patch[dof] = p; }
3436 }
3437 }
3438
3439 if (dim == 3)
3440 {
3441 patchTopo->GetElementFaces(p, faces, orient);
3442
3443 for (auto f : faces)
3444 {
3445 if (masterFaces.count(f) > 0)
3446 {
3447 Array2D<int> mdof;
3448 GetMasterFaceDofs(true, f, mdof);
3449 for (int j=0; j<mdof.NumCols(); ++j)
3450 for (int k=0; k<mdof.NumRows(); ++k)
3451 {
3452 dof2patch[mdof(k,j)] = p;
3453 }
3454 }
3455 }
3456 }
3457 }
3458}
3459
3460// This function assumes a uniform number of control points per element in kv.
3462{
3463 const int ne = kv->GetNE();
3464
3465 // Total number of CP on edge, excluding vertex CP.
3466 const int totalEdgeCP = kv->GetNCP() - 2 - ne + 1;
3467 const int perEdgeCP = totalEdgeCP / ne;
3468
3469 MFEM_VERIFY(perEdgeCP * ne == totalEdgeCP, "");
3470
3471 return perEdgeCP;
3472}
3473
3475{
3476 const int nv = patchTopo->GetNV();
3477 const int ne = patchTopo->GetNEdges();
3478 const int nf = patchTopo->GetNFaces();
3479 const int np = patchTopo->GetNE();
3480 int meshCounter, spaceCounter, dim = Dimension();
3481
3482 std::set<int> reversedParents;
3483 if (patchTopo->ncmesh)
3484 {
3485 // Note that master or slave entities exist only for a mesh with
3486 // vertex_parents, not for the vertex_to_knotspan case. Currently, a mesh
3487 // is not allowed to have both cases, see the MFEM_VERIFY below.
3488
3489 const NCMesh::NCList& nce = patchTopo->ncmesh->GetNCList(1);
3490 const NCMesh::NCList& ncf = patchTopo->ncmesh->GetNCList(2);
3491
3492 masterEdges.clear();
3493 masterFaces.clear();
3494 slaveEdges.clear();
3495 slaveFaces.clear();
3496 masterEdgeToId.clear();
3497 masterFaceToId.clear();
3498
3499 MFEM_VERIFY(nce.masters.Size() > 0 ||
3501 MFEM_VERIFY(!(nce.masters.Size() > 0 &&
3502 patchTopo->ncmesh->GetVertexToKnotSpan().Size() > 0), "");
3503
3504 std::vector<EdgePairInfo> edgePairs;
3505 std::vector<FacePairInfo> facePairs;
3506 std::vector<int> parentFaces, parentVerts;
3507 std::vector<std::array<int, 2>> parentSize;
3508
3509 const bool is3D = dim == 3;
3510
3511 std::map<std::pair<int, int>, int> v2f;
3512
3514 {
3515 // Intersections of master edges may not be edges in patchTopo->ncmesh,
3516 // so we represent them in auxEdges, to account for their vertices and
3517 // DOFs.
3518 {
3519 int vert_index[2];
3521 for (auto edgeID : EL.conforming)
3522 {
3523 patchTopo->ncmesh->GetEdgeVertices(edgeID, vert_index);
3524 v2e[std::pair<int, int> (vert_index[0], vert_index[1])] = edgeID.index;
3525 }
3526 }
3527
3528 if (is3D)
3529 {
3530 Array<int> vert;
3531 for (int i=0; i<patchTopo->GetNumFaces(); ++i)
3532 {
3533 patchTopo->GetFaceVertices(i, vert);
3534 const int vmin = vert.Min();
3535 const int idmin = vert.Find(vmin);
3536 v2f[std::pair<int, int> (vert[idmin], vert[(idmin + 2) % 4])] = i;
3537 }
3538 }
3539
3541
3542 if (is3D)
3543 ProcessVertexToKnot3D(v2k, v2f, parentSize, edgePairs,
3544 facePairs, parentFaces, parentVerts);
3545 else
3546 {
3547 ProcessVertexToKnot2D(v2k, reversedParents, edgePairs);
3548 }
3549 } // if using vertex_to_knotspan
3550
3551 const int numMasters = is3D ? ncf.masters.Size() : nce.masters.Size();
3552
3553 if (is3D)
3554 {
3555 for (auto masterFace : ncf.masters)
3556 {
3557 masterFaces.insert(masterFace.index);
3558 }
3559 }
3560
3561 for (auto masterEdge : nce.masters)
3562 {
3563 masterEdges.insert(masterEdge.index);
3564 }
3565
3566 masterEdgeIndex.SetSize(masterEdges.size());
3567 int cnt = 0;
3568 for (auto medge : masterEdges)
3569 {
3570 masterEdgeIndex[cnt] = medge;
3571 masterEdgeToId[medge] = cnt;
3572 cnt++;
3573 }
3574 MFEM_VERIFY(cnt == masterEdgeIndex.Size(), "");
3575
3576 Array<int> masterFaceIndex(parentFaces.size());
3577
3578 // Note that masterFaces is a subset of parentFaces.
3579 MFEM_VERIFY(masterFaces.size() <= parentFaces.size(), "");
3580
3581 cnt = 0;
3582 for (auto mface : parentFaces)
3583 {
3584 masterFaceIndex[cnt] = mface;
3585 masterFaceToId[mface] = cnt;
3586 cnt++;
3587 }
3588
3589 MFEM_VERIFY(cnt == masterFaceIndex.Size(), "");
3590
3591 masterEdgeInfo.clear();
3592 masterEdgeInfo.resize(masterEdgeIndex.Size());
3593
3594 masterFaceInfo.clear();
3595 masterFaceInfo.resize(masterFaceIndex.Size());
3596
3598 {
3599 // Note that this is used in 2D and 3D.
3600 const int npairs = edgePairs.size();
3601
3602 for (int i=0; i<npairs; ++i)
3603 {
3604 if (!edgePairs[i].isSet) { continue; }
3605
3606 const int v = edgePairs[i].v;
3607 const int s = edgePairs[i].child;
3608 const int m = edgePairs[i].parent;
3609 const int ksi = edgePairs[i].ksi;
3610
3611 slaveEdges.push_back(s);
3612
3613 const int mid = masterEdgeToId[m];
3614 const int si = slaveEdges.size() - 1;
3615 masterEdgeInfo[mid].slaves.push_back(si);
3616 if (v >= 0)
3617 {
3618 masterEdgeInfo[mid].vertices.push_back(v);
3619 masterEdgeInfo[mid].ks.push_back(ksi);
3620 }
3621 }
3622
3623 ProcessFacePairs(0, 0, parentSize, parentVerts, facePairs);
3624 }
3625
3626 for (int i=0; i<nce.slaves.Size(); ++i)
3627 {
3628 const NCMesh::Slave& slaveEdge = nce.slaves[i];
3629 int vert_index[2];
3630 patchTopo->ncmesh->GetEdgeVertices(slaveEdge, vert_index);
3631 slaveEdges.push_back(slaveEdge.index);
3632
3633 const int mid = masterEdgeToId[slaveEdge.master];
3634 masterEdgeInfo[mid].slaves.push_back(i);
3635 }
3636
3637 if (!is3D)
3638 {
3639 for (int m=0; m<numMasters; ++m)
3640 {
3641 // Order the slaves of each master edge, from the first to second
3642 // vertex of the master edge.
3643 const int numSlaves = masterEdgeInfo[m].slaves.size();
3644 MFEM_ASSERT(numSlaves > 0, "");
3645 int mvert[2];
3646 int svert[2];
3647 patchTopo->ncmesh->GetEdgeVertices(nce.masters[m], mvert);
3648
3649 std::vector<int> orderedSlaves(numSlaves);
3650 std::set<int> used;
3651
3652 int vi = mvert[0];
3653 for (int s=0; s<numSlaves; ++s)
3654 {
3655 // Find the slave edge containing vertex vi.
3656 // This has quadratic complexity, but numSlaves is small.
3657 orderedSlaves[s] = -1;
3658 for (int t=0; t<numSlaves; ++t)
3659 {
3660 const int sid = masterEdgeInfo[m].slaves[t];
3661 if (used.count(sid) > 0) { continue; }
3662 patchTopo->ncmesh->GetEdgeVertices(nce.slaves[sid], svert);
3663 if (svert[0] == vi || svert[1] == vi)
3664 {
3665 orderedSlaves[s] = sid;
3666 used.insert(sid);
3667 break;
3668 }
3669 }
3670
3671 MFEM_ASSERT(orderedSlaves[s] >= 0, "");
3672
3673 // Update vi to the next vertex
3674 vi = (svert[0] == vi) ? svert[1] : svert[0];
3675
3676 if (s < numSlaves - 1)
3677 {
3678 masterEdgeInfo[m].vertices.push_back(vi);
3679 masterEdgeInfo[m].ks.push_back(-1); // Used only in 3D.
3680 }
3681 }
3682
3683 masterEdgeInfo[m].slaves = orderedSlaves;
3684 } // m
3685 }
3686
3687 if (is3D)
3688 {
3689 // Remove edges from masterEdges if they do not have any slave edges.
3690 std::vector<int> falseMasterEdges;
3691 for (auto me : masterEdges)
3692 {
3693 const int mid = masterEdgeToId.at(me);
3694 if (masterEdgeInfo[mid].slaves.size() <= 1)
3695 {
3696 falseMasterEdges.push_back(me);
3697 }
3698 }
3699
3700 for (auto me : falseMasterEdges) { masterEdges.erase(me); }
3701
3702 // Find slave and auxiliary faces not yet defined.
3703 const int nfp0 = facePairs.size();
3704 std::set<int> addParentFaces;
3705 FindAdditionalFacesSA(v2f, addParentFaces, facePairs);
3706
3707 cnt = parentFaces.size();
3708 const int npf0 = cnt;
3709
3710 for (auto pf : addParentFaces)
3711 {
3712 if (masterFaces.count(pf) == 0)
3713 {
3714 masterFaces.insert(pf);
3715 masterFaceIndex.Append(pf);
3716
3717 masterFaceToId[pf] = cnt;
3718 cnt++;
3719
3720 {
3721 Array<int> edges, ori, verts;
3722 patchTopo->GetFaceEdges(pf, edges, ori);
3723 patchTopo->GetFaceVertices(pf, verts);
3724 MFEM_ASSERT(edges.Size() == 4 && verts.Size() == 4, "");
3725
3726 parentSize.emplace_back(std::array<int, 2>
3727 {
3728 KnotVec(edges[0])->GetNE(),
3729 KnotVec(edges[1])->GetNE()
3730 });
3731
3732 masterFaceInfo.push_back(
3733 MasterFaceInfo(KnotVec(edges[0])->GetNE(),
3734 KnotVec(edges[1])->GetNE()));
3735
3736 for (int i=0; i<4; ++i) { parentVerts.push_back(verts[i]); }
3737 }
3738 }
3739 }
3740
3741 MFEM_VERIFY(cnt == masterFaceIndex.Size(), "");
3742
3743 ProcessFacePairs(nfp0, npf0, parentSize, parentVerts, facePairs);
3744 }
3745 }
3746
3747 for (auto rp : reversedParents)
3748 {
3749 masterEdgeInfo[masterEdgeToId[rp]].Reverse();
3750 }
3751
3752 Array<int> edges, orient;
3753
3758
3763
3764 // Get vertex offsets
3765 for (meshCounter = 0; meshCounter < nv; meshCounter++)
3766 {
3767 v_meshOffsets[meshCounter] = meshCounter;
3768 v_spaceOffsets[meshCounter] = meshCounter;
3769 }
3770 spaceCounter = meshCounter;
3771
3772 // Get edge offsets
3773 for (int e = 0; e < ne; e++)
3774 {
3775 e_meshOffsets[e] = meshCounter;
3776 e_spaceOffsets[e] = spaceCounter;
3777
3778 if (masterEdges.count(e) == 0) // If not a master edge
3779 {
3780 meshCounter += KnotVec(e)->GetNE() - 1;
3781 spaceCounter += KnotVec(e)->GetNCP() - 2;
3782 }
3783 }
3784
3785 const int nauxe = auxEdges.size();
3786 aux_e_meshOffsets.SetSize(nauxe + 1);
3787 aux_e_spaceOffsets.SetSize(nauxe + 1);
3788 for (int e = 0; e < nauxe; e++)
3789 {
3790 aux_e_meshOffsets[e] = meshCounter;
3791 aux_e_spaceOffsets[e] = spaceCounter;
3792
3793 // Find the number of elements and CP in this auxiliary edge, which is
3794 // defined only on part of the master edge knotvector.
3795 const int signedParentEdge = auxEdges[e].parent;
3796 const int ki0 = auxEdges[e].ksi[0];
3797 const int ki1raw = auxEdges[e].ksi[1];
3798 const int parentEdge = UnsignIndex(signedParentEdge);
3799 const int masterNE = KnotVec(parentEdge)->GetNE();
3800 const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
3801 const int perEdgeCP = GetNCPperEdge(KnotVec(e));
3802 const int auxne = ki1 - ki0;
3803 MFEM_ASSERT(auxne > 0, "");
3804 meshCounter += auxne - 1;
3805 spaceCounter += (auxne * perEdgeCP) + auxne - 1;
3806 }
3807
3808 aux_e_meshOffsets[nauxe] = meshCounter;
3809 aux_e_spaceOffsets[nauxe] = spaceCounter;
3810
3811 // Get face offsets
3812 for (int f = 0; f < nf; f++)
3813 {
3814 f_meshOffsets[f] = meshCounter;
3815 f_spaceOffsets[f] = spaceCounter;
3816
3817 if (masterFaces.count(f) == 0) // If not a master face
3818 {
3819 patchTopo->GetFaceEdges(f, edges, orient);
3820
3821 meshCounter +=
3822 (KnotVec(edges[0])->GetNE() - 1) *
3823 (KnotVec(edges[1])->GetNE() - 1);
3824 spaceCounter +=
3825 (KnotVec(edges[0])->GetNCP() - 2) *
3826 (KnotVec(edges[1])->GetNCP() - 2);
3827 }
3828 }
3829
3830 const int nauxf = auxFaces.size();
3831 aux_f_meshOffsets.SetSize(nauxf + 1);
3832 aux_f_spaceOffsets.SetSize(nauxf + 1);
3833 for (int f = 0; f < nauxf; f++)
3834 {
3835 aux_f_meshOffsets[f] = meshCounter;
3836 aux_f_spaceOffsets[f] = spaceCounter;
3837
3838 const int parentFace = auxFaces[f].parent;
3839 patchTopo->GetFaceEdges(parentFace, edges, orient);
3840
3841 // Number of control points per edge, in first and second directions.
3842 const int perEdgeCP0 = GetNCPperEdge(KnotVec(edges[0]));
3843 const int perEdgeCP1 = GetNCPperEdge(KnotVec(edges[1]));
3844
3845 const int auxne0 = auxFaces[f].ksi1[0] - auxFaces[f].ksi0[0];
3846 const int auxne1 = auxFaces[f].ksi1[1] - auxFaces[f].ksi0[1];
3847 meshCounter += (auxne0 - 1) * (auxne1 - 1);
3848 spaceCounter += ((auxne0 * perEdgeCP0) + auxne0 - 1) *
3849 ((auxne1 * perEdgeCP1) + auxne1 - 1);
3850 }
3851
3852 aux_f_meshOffsets[nauxf] = meshCounter;
3853 aux_f_spaceOffsets[nauxf] = spaceCounter;
3854
3855 // Get patch offsets
3856 GetPatchOffsets(meshCounter, spaceCounter);
3857
3858 NumOfVertices = meshCounter;
3859 NumOfDofs = spaceCounter;
3860
3861 SetDofToPatch();
3862}
3863
3864} // namespace mfem
Dynamic 2D array using row-major layout.
Definition array.hpp:459
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.hpp:563
int NumCols() const
Definition array.hpp:477
int NumRows() const
Definition array.hpp:476
void SetSize(int m, int n)
Set the 2D array size to m x n.
Definition array.hpp:474
int GetSize1() const
Get the 3D array size in the first dimension.
Definition array.hpp:586
void SetSize(int n1, int n2, int n3)
Set the 3D array size to n1 x n2 x n3.
Definition array.hpp:582
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
int size
Size of the array.
Definition array.hpp:57
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:341
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:210
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.cpp:86
int Size() const
Return the logical size of the array.
Definition array.hpp:192
int Find(const T &el) const
Return the first index where 'el' is found; return -1 if not found.
Definition array.hpp:1000
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
T Sum() const
Return the sum of all the array entries using the '+'' operator for class 'T'.
Definition array.cpp:145
A vector of knots in one dimension, with B-spline basis functions of a prescribed order.
Definition nurbs.hpp:38
int GetNCP() const
Return the number of control points.
Definition nurbs.hpp:111
int GetNE() const
Return the number of elements, defined by distinct knots.
Definition nurbs.hpp:108
Mesh data type.
Definition mesh.hpp:67
void GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:8109
void LoadNonconformingPatchTopo(std::istream &input, Array< int > &edge_to_ukv)
Read NURBS patch/macro-element mesh (MFEM NURBS NC-patch mesh format)
Definition mesh.cpp:7070
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1396
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1399
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1368
void GetElementFaces(int i, Array< int > &faces, Array< int > &ori) const
Return the indices and the orientations of all faces of element i.
Definition mesh.cpp:8318
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1387
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:8139
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1640
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:318
void GetElementEdges(int i, Array< int > &edges, Array< int > &cor) const
Return the indices and the orientations of all edges of element i.
Definition mesh.cpp:8044
Table * GetFaceToElementTable() const
Definition mesh.cpp:8284
const VertexToKnotSpan & GetVertexToKnotSpan() const
Definition ncmesh.hpp:401
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
Definition ncmesh.cpp:5639
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
Definition ncmesh.hpp:391
void RefineVertexToKnotSpan(const std::vector< Array< int > > &kvf, const Array< KnotVector * > &kvext, std::map< std::pair< int, int >, std::array< int, 2 > > &parentToKV)
Remap knot-span indices vertex_to_knotspan after refinement.
Definition ncmesh.cpp:5110
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:376
NCNURBSExtension extends NURBSExtension to support NC-patch NURBS meshes.
Definition ncnurbs.hpp:23
void GenerateOffsets() override
Set the mesh and space offsets, and also count the global NumOfVertices and the global NumOfDofs.
Definition ncnurbs.cpp:3474
NCNURBSExtension(const NCNURBSExtension &orig)
Copy constructor: deep copy.
Definition ncnurbs.cpp:44
void GetMasterFaceDofs(bool dof, int mf, Array2D< int > &dofs) const override
Get the DOFs (dof = true) or vertices (dof = false) for master face mf.
Definition ncnurbs.cpp:925
bool IsMasterEdge(int edge) const override
Return true if edge is a master NC-patch edge.
Definition ncnurbs.hpp:38
void UniformRefinement(const Array< int > &rf) override
Definition ncnurbs.cpp:3337
void PropagateFactorsForKV(int rf_default)
Ensure consistent refinement factors on all knotvectors.
Definition ncnurbs.cpp:2614
void RefineWithKVFactors(int rf, const std::string &kvf_filename, bool coarsened) override
Definition ncnurbs.cpp:2899
void LoadFactorsForKV(const std::string &filename)
Load refinement factors for a list of knotvectors from file.
Definition ncnurbs.cpp:2351
int SetPatchFactors(int p)
Set consistent refinement factors on patch p.
Definition ncnurbs.cpp:2483
void GetMasterEdgeDofs(bool dof, int me, Array< int > &dofs) const override
Get the DOFs (dof = true) or vertices (dof = false) for master edge me.
Definition ncnurbs.cpp:660
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
Definition nurbs.hpp:575
std::vector< Array< int > > kvf_coarse
Definition nurbs.hpp:681
Mesh * patchTopo
Patch topology mesh.
Definition nurbs.hpp:614
Array< int > ref_factors
Knotvector refinement factors.
Definition nurbs.hpp:683
Array< int > p_meshOffsets
Definition nurbs.hpp:642
Array< int > mOrders
Orders of all KnotVectors.
Definition nurbs.hpp:596
int num_structured_patches
Whether patchTopo is a nonconforming mesh.
Definition nurbs.hpp:677
Array3D< double > patchCP
Number of structured patches.
Definition nurbs.hpp:679
static constexpr int unsetFactor
Refinement factors in each dimension.
Definition nurbs.hpp:685
int KnotInd(int edge) const
Return the unsigned index of the KnotVector for edge edge.
Definition nurbs.hpp:1402
KnotVector * KnotVec(int edge)
DOF to owning patch map in SetSolutionVector()
Definition nurbs.hpp:1412
Array< int > v_meshOffsets
Global mesh offsets, meshOffsets == meshVertexOffsets.
Definition nurbs.hpp:639
virtual void GetMasterEdgeDofs(bool dof, int me, Array< int > &dofs) const
Get the DOFs (dof = true) or vertices (dof = false) for master edge me.
Definition nurbs.cpp:5790
Array< int > f_spaceOffsets
Definition nurbs.hpp:647
int NumOfVertices
Global entity counts.
Definition nurbs.hpp:602
Array< NURBSPatch * > patches
Array of all patches in the mesh.
Definition nurbs.hpp:668
virtual bool IsMasterEdge(int edge) const
Return true if edge is a master NC-patch edge.
Definition nurbs.hpp:823
void GetPatchOffsets(int &meshCounter, int &spaceCounter)
Helper function for GenerateOffsets().
Definition nurbs.cpp:4187
void Load(std::istream &input, bool spacing)
Load data from file (used by constructor).
Definition nurbs.cpp:2748
int NumOfKnotVectors
Number of unique (not comprehensive) KnotVectors.
Definition nurbs.hpp:599
Array< int > edge_to_ukv
Map from patchTopo edge indices to unique KnotVector indices.
Definition nurbs.hpp:620
std::vector< Array< int > > kvf
Control points for coarse structured patches.
Definition nurbs.hpp:681
int KnotVecNE(int edge) const
Return the number of knotvector elements for edge edge.
Definition nurbs.hpp:1438
virtual void GetMasterFaceDofs(bool dof, int mf, Array2D< int > &dofs) const
Get the DOFs (dof = true) or vertices (dof = false) for master face mf.
Definition nurbs.cpp:5795
virtual bool IsMasterFace(int face) const
Return true if face is a master NC-patch face.
Definition nurbs.hpp:826
Array< int > e_meshOffsets
Definition nurbs.hpp:640
int KnotSign(int edge) const
Return the sign (orientation) of the KnotVector for edge edge.
Definition nurbs.hpp:1407
Array< int > dof2patch
Unset refinement factor value.
Definition nurbs.hpp:687
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:926
Array< int > f_meshOffsets
Definition nurbs.hpp:641
Array< KnotVector * > knotVectors
Set of unique KnotVectors.
Definition nurbs.hpp:623
Array< int > p_spaceOffsets
Definition nurbs.hpp:648
Array< int > v_spaceOffsets
Global space offsets, spaceOffsets == dofOffsets.
Definition nurbs.hpp:645
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:958
Array< int > e_spaceOffsets
Definition nurbs.hpp:646
Piecewise spacing function, with spacing functions defining spacing within arbitrarily many fixed sub...
Definition spacing.hpp:628
Array< int > RelativePieceSizes() const
Definition spacing.hpp:716
void ScalePartition(Array< int > const &f, bool reorient)
Definition spacing.hpp:718
bool GetReverse() const
Definition spacing.hpp:50
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
void SetSize(int dim, int connections_per_row)
Set the size and the number of connections for the table.
Definition table.cpp:172
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
For a NURBS mesh with nonconforming patch topology, this struct provides a map from hanging vertices ...
Definition ncmesh.hpp:122
void GetVertex3D(int index, int &v, std::array< int, 2 > &ks, std::array< int, 4 > &pv) const
Get the data for a vertex in 3D.
Definition ncnurbs.cpp:3292
void SetVertex2D(int index, int v, int ks, const std::array< int, 2 > &pv)
Set the data for a vertex in 2D.
Definition ncnurbs.cpp:3250
void SetVertex3D(int index, int v, const std::array< int, 2 > &ks, const std::array< int, 4 > &pv)
Set the data for a vertex in 3D.
Definition ncnurbs.cpp:3259
void SetSize(int dimension, int numVertices)
Set the spatial dimension and number of vertices.
Definition ncnurbs.cpp:3243
void GetVertex2D(int index, int &v, int &ks, std::array< int, 2 > &pv) const
Get the data for a vertex in 2D.
Definition ncnurbs.cpp:3283
void Print(std::ostream &os) const
Print all the data.
Definition ncnurbs.cpp:3304
void SetKnotSpans3D(int index, const std::array< int, 2 > &ks)
Set the knot-span indices for a vertex in 3D.
Definition ncnurbs.cpp:3277
std::pair< int, int > GetVertexParentPair(int index) const
Return the vertex pair representing the parent edge (2D) or face (3D).
Definition ncnurbs.cpp:3320
int Size() const
Return the number of vertices.
Definition ncmesh.hpp:157
void SetKnotSpan2D(int index, int ks)
Set the knot-span index for a vertex in 2D.
Definition ncnurbs.cpp:3272
int dim
Definition ex24.cpp:53
constexpr int dimension
This example only works in 3D. Kernels for 2D are not implemented.
Definition hooke.cpp:45
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
MFEM_HOST_DEVICE T tr(const tensor< T, n, n > &A)
Returns the trace of a square matrix.
Definition tensor.hpp:1317
MFEM_HOST_DEVICE dual< value_type, gradient_type > pow(dual< value_type, gradient_type > a, dual< value_type, gradient_type > b)
implementation of a (dual) raised to the b (dual) power
Definition dual.hpp:374
int OffsetHelper(int i, int j, const Array< int > &a, const Array< int > &b)
Definition ncnurbs.cpp:632
void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift, int &sm, int &sn, int &si, int &sj)
Definition ncnurbs.cpp:3132
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
MFEM_HOST_DEVICE int FlipIndexSign(int i)
Signed indices i -> -1 - i are used as a convention to encode orientation.
Definition globals.hpp:117
void UpdateFactors(Array< int > &f)
Definition ncnurbs.cpp:2881
int GetNCPperEdge(const KnotVector *kv)
Definition ncnurbs.cpp:3461
bool ConsistentlySetEntry(int v, int &e)
Definition ncnurbs.cpp:843
MFEM_HOST_DEVICE int UnsignIndex(int i)
Definition globals.hpp:118
void ApplyFineToCoarse(const Array< int > &f, Array< int > &c)
Definition ncnurbs.cpp:2968
void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m, int &n)
Definition ncnurbs.cpp:3060
void GetVertexOrdering(int ori, std::array< int, 4 > &perm)
Definition ncnurbs.cpp:873
std::pair< int, int > QuadrupleToPair(const std::array< int, 4 > &q)
Definition ncnurbs.cpp:3236
bool Reorder2D(int ori, std::array< int, 2 > &s0)
Definition ncnurbs.cpp:3038
void RemapKnotIndex(bool rev, const Array< int > &rf, int &k)
Definition ncnurbs.cpp:2305
int GetFaceOrientation(const Mesh *mesh, const int face, const std::array< int, 4 > &verts)
Definition ncnurbs.cpp:2999
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
void ReorderArray2D(int i0, int j0, const Array2D< int > &a, Array2D< int > &b)
Definition ncnurbs.cpp:851
Array< int > CoarseToFineFactors(const Array< int > &rf)
Definition ncnurbs.cpp:2466
STL namespace.
real_t p(const Vector &x, real_t t)
bool IsBoundary() const
Return true if the face is a boundary face.
Definition mesh.hpp:2145
int index
Mesh number.
Definition ncmesh.hpp:261
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:301
Array< MeshId > conforming
All MeshIds corresponding to conformal faces.
Definition ncmesh.hpp:302
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:304
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:303
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:287
int master
master number (in Mesh numbering)
Definition ncmesh.hpp:288