MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
ncnurbs.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 = -1 - 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(-1 - 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 = -1 - 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{-1 - 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{-1 - 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] = -1 - 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[-1 - 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(-1 - 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 = ori < 0 ? -1 - ori : 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 = -1 - 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 = -1 - 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 = ori < 0 ? -1 - ori : 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] ?
1420 parentEdge : -1 - parentEdge,
1421 {childPair.first, childPair.second},
1422 {newParentEdge ? 0 : prevKI, ks}});
1423 }
1424 }
1425
1426 const int childEdge = childPairTopo ? v2e[childPair] : -1 - auxv2e[childPair];
1427
1428 // Check whether this is the final vertex in this parent edge. Note that
1429 // the logic for comparing (pv[0],pv[1]) to the next parents assumes the
1430 // ordering does not change, which is ensured by the assumption that the
1431 // knot-span index is increasing.
1432 bool finalVertex = (i == nv2k-1);
1433 if (i < nv2k-1)
1434 {
1435 int tv_next, ks_next;
1436 std::array<int, 2> pv_next;
1437 v2k.GetVertex2D(i + 1, tv_next, ks_next, pv_next);
1438 if (pv_next[0] != pv[0] || pv_next[1] != pv[1])
1439 {
1440 finalVertex = true;
1441 }
1442 }
1443
1444 edgePairs.emplace_back(tv, ks, childEdge, parentEdge);
1445
1446 if (finalVertex)
1447 {
1448 // Also find the edge with vertices (tv, pv[1]), and define it as a
1449 // slave edge.
1450 const std::pair<int, int> finalChildPair(tv < pv[1] ? tv : pv[1],
1451 tv < pv[1] ? pv[1] : tv);
1452 const bool finalChildPairTopo = v2e.count(finalChildPair) > 0;
1453 if (!finalChildPairTopo)
1454 {
1455 // Check whether finalChildPair is in auxEdges.
1456 if (auxv2e.count(finalChildPair) == 0)
1457 {
1458 // Create a new auxiliary edge
1459 auxv2e[finalChildPair] = auxEdges.size();
1460
1461 // -1 denotes `ne` at endpoint
1462 auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
1463 -1 - parentEdge : parentEdge,
1464 {finalChildPair.first, finalChildPair.second},
1465 {ks, -1}});
1466 }
1467 }
1468
1469 const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
1470 -1 - auxv2e[finalChildPair];
1471 edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
1472 }
1473
1474 prevV = tv;
1475 prevKI = ks;
1476 prevParent = parentEdge;
1477 } // loop over vertices in vertex_to_knotspan
1478}
1479
1480void NCNURBSExtension::ProcessVertexToKnot3D(
1481 const VertexToKnotSpan &v2k,
1482 const std::map<std::pair<int, int>, int> &v2f,
1483 std::vector<std::array<int, 2>> &parentSize,
1484 std::vector<EdgePairInfo> &edgePairs,
1485 std::vector<FacePairInfo> &facePairs,
1486 std::vector<int> &parentFaces,
1487 std::vector<int> &parentVerts)
1488{
1489 auxEdges.clear();
1490 auxFaces.clear();
1491 auxv2e.clear();
1492 auxv2f.clear();
1493
1494 const int nv2k = v2k.Size();
1495
1496 // Note that the logic here assumes that the "vertex_to_knotspan" data in the
1497 // mesh file has vertices in order of ascending (k1,k2), with k2 being the
1498 // fast variable, and with corners skipped.
1499
1500 // Find parentOffset, which stores the indices in v2k at which parent faces
1501 // start.
1502 int prevParent = -1;
1503 std::vector<int> parentOffset;
1504 std::vector<bool> parentV2Kedge;
1505 int n1 = 0;
1506 int n2 = 0;
1507 int n1min = 0;
1508 int n2min = 0;
1509 for (int i = 0; i < nv2k; ++i)
1510 {
1511 int tv;
1512 std::array<int, 2> ks;
1513 std::array<int, 4> pv;
1514 v2k.GetVertex3D(i, tv, ks, pv);
1515
1516 // The face with vertices (pv[0], pv[1], pv[2], pv[3]) is defined as a
1517 // parent face.
1518 const std::pair<int, int> parentPair = QuadrupleToPair(pv);
1519 const int parentFace = v2f.at(parentPair);
1520 const bool newParentFace = (prevParent != parentFace);
1521 if (newParentFace)
1522 {
1523 parentOffset.push_back(i);
1524 parentFaces.push_back(parentFace);
1525
1526 // Find the knotvectors for the first two edges this face.
1527 {
1528 Array<int> edges, ori, verts;
1529 patchTopo->GetFaceEdges(parentFace, edges, ori);
1530 patchTopo->GetFaceVertices(parentFace, verts);
1531
1532 std::array<int,2> kv = {-1, -1};
1533 for (int e=0; e<2; ++e)
1534 {
1535 // Find the edge with vertices pv[e] and pv[e+1].
1536 for (auto edge : edges)
1537 {
1538 Array<int> evert;
1539 patchTopo->GetEdgeVertices(edge, evert);
1540 const bool matching = (evert[0] == pv[e] && evert[1] == pv[e+1]) ||
1541 (evert[1] == pv[e] && evert[0] == pv[e+1]);
1542 if (matching) { kv[e] = KnotInd(edge); }
1543 }
1544
1545 MFEM_ASSERT(kv[e] >= 0, "");
1546 }
1547
1548 parentToKV[parentPair] = std::array<int, 2> {kv[0], kv[1]};
1549 }
1550
1551 if (i > 0)
1552 {
1553 // In the case of only 1 element in the 1-direction, it is assumed
1554 // that the 2-direction has more than 1 element, so there are
1555 // knot-spans (0, ki2) and (1, ki2) for 0 < ki2 < n2. This will
1556 // result in n1 = 0, which should be 1. Also, n2 will be 1 less than
1557 // it should be. Similarly for the situation with directions
1558 // reversed.
1559 const int n1range = n1 - n1min;
1560 const int n2range = n2 - n2min;
1561 parentV2Kedge.push_back(n1range == 0 || n2range == 0);
1562 }
1563
1564 auto getEdgeNE = [&](int d)
1565 {
1566 Array<int> ev(2);
1567 for (int j=0; j<2; ++j) { ev[j] = pv[j + d]; }
1568 ev.Sort();
1569 return KnotVecNE(v2e.at(std::pair<int, int>(ev[0], ev[1])));
1570 };
1571 parentSize.emplace_back(std::array<int, 2> {getEdgeNE(0), getEdgeNE(1)});
1572
1573 n1 = ks[0]; // Finding max of ks[0]
1574 n2 = ks[1]; // Finding max of ks[1]
1575
1576 n1min = n1;
1577 n2min = n2;
1578 }
1579 else
1580 {
1581 n1 = std::max(n1, ks[0]); // Finding max of ks[0]
1582 n2 = std::max(n2, ks[1]); // Finding max of ks[1]
1583
1584 n1min = std::min(n1min, ks[0]);
1585 n2min = std::min(n2min, ks[1]);
1586 }
1587
1588 prevParent = parentFace;
1589 }
1590
1591 {
1592 const int n1range = n1 - n1min;
1593 const int n2range = n2 - n2min;
1594 parentV2Kedge.push_back(n1range == 0 || n2range == 0);
1595 }
1596
1597 const int numParents = parentOffset.size();
1598 parentOffset.push_back(nv2k);
1599
1600 std::set<int> visitedParentEdges;
1601 std::map<int, int> edgePairOS;
1602 bool consistent = true;
1603
1604 for (int parent = 0; parent < numParents; ++parent)
1605 {
1606 const int parentFace = parentFaces[parent];
1607
1608 int parentEdges[4];
1609 bool parentEdgeRev[4];
1610
1611 int tvi;
1612 std::array<int, 2> ks;
1613 std::array<int, 4> pv;
1614 v2k.GetVertex3D(parentOffset[parent], tvi, ks, pv);
1615
1616 // Set all 4 edges of the parent face as master edges.
1617 {
1618 Array<int> ev(2);
1619 for (int i=0; i<4; ++i)
1620 {
1621 for (int j=0; j<2; ++j)
1622 {
1623 ev[j] = pv[(i + j) % 4];
1624 }
1625
1626 const bool reverse = (ev[1] < ev[0]);
1627 parentEdgeRev[i] = reverse;
1628
1629 ev.Sort();
1630
1631 const std::pair<int, int> edge_i(ev[0], ev[1]);
1632
1633 const int parentEdge = v2e.at(edge_i);
1634 masterEdges.insert(parentEdge);
1635 parentEdges[i] = parentEdge;
1636 }
1637 }
1638
1639 n1 = parentSize[parent][0];
1640 n2 = parentSize[parent][1];
1641 Array2D<int> gridVertex(n1 + 1, n2 + 1);
1642 gridVertex = -1;
1643
1644 gridVertex(0,0) = pv[0];
1645 gridVertex(n1,0) = pv[1];
1646 gridVertex(n1,n2) = pv[2];
1647 gridVertex(0,n2) = pv[3];
1648
1649 for (int i=0; i<4; ++i) { parentVerts.push_back(pv[i]); }
1650
1651 int r1min = -1;
1652 int r1max = -1;
1653 int r2min = -1;
1654 int r2max = -1;
1655
1656 for (int i = parentOffset[parent]; i < parentOffset[parent + 1]; ++i)
1657 {
1658 v2k.GetVertex3D(i, tvi, ks, pv);
1659 gridVertex(ks[0], ks[1]) = tvi;
1660 if (i == parentOffset[parent])
1661 {
1662 // Initialize min/max
1663 r1min = ks[0];
1664 r1max = ks[0];
1665
1666 r2min = ks[1];
1667 r2max = ks[1];
1668 }
1669 else
1670 {
1671 r1min = std::min(r1min, ks[0]);
1672 r1max = std::max(r1max, ks[0]);
1673
1674 r2min = std::min(r2min, ks[1]);
1675 r2max = std::max(r2max, ks[1]);
1676 }
1677 } // loop over vertices in v2k
1678
1679 MFEM_ASSERT((r1max - r1min + 1) * (r2max - r2min + 1) >=
1680 parentOffset[parent + 1] - parentOffset[parent], "");
1681
1682 std::array<int,2> kvi;
1683 if (kvf.size() > 0)
1684 {
1685 const std::pair<int, int> parentPair = v2k.GetVertexParentPair(
1686 parentOffset[parent]);
1687 std::array<int, 2> kv = parentToKV.at(parentPair);
1688 for (int i=0; i<2; ++i) { kvi[i] = kv[i]; }
1689 }
1690
1691 // Default refinement factor
1692 const int rf = ref_factors.Size() == 3 ? ref_factors[0] : 1;
1693
1694 int n1orig = kvf_coarse.size() > 0 ? kvf_coarse[kvi[0]].Size() : n1 / rf;
1695 int n2orig = kvf_coarse.size() > 0 ? kvf_coarse[kvi[1]].Size() : n2 / rf;
1696
1697 if (kvf.size() > 0 && kvf_coarse.size() == 0)
1698 {
1699 n1orig = kvf[kvi[0]].Size();
1700 n2orig = kvf[kvi[1]].Size();
1701 }
1702
1703 if (kvf.size() > 0)
1704 {
1705 MFEM_ASSERT(kvf[kvi[0]].Sum() == n1 && kvf[kvi[1]].Sum() == n2, "");
1706 }
1707
1708 std::vector<Array<int>> cgrid(2);
1709 std::array<int,2> n_orig = {n1orig, n2orig};
1710 for (int dir=0; dir<2; ++dir)
1711 {
1712 cgrid[dir].SetSize(n_orig[dir] + 1);
1713 cgrid[dir][0] = 0;
1714 for (int ii = 0; ii < n_orig[dir]; ++ii)
1715 {
1716 const int iir = parentEdgeRev[dir] ? n_orig[dir] - 1 - ii : ii;
1717
1718 int d = 1; // refinement factor
1719
1720 if (kvf_coarse.size() > 0)
1721 {
1722 d = kvf_coarse[kvi[dir]][iir];
1723 }
1724 else if (kvf.size() > 0)
1725 {
1726 d = kvf[kvi[dir]][iir];
1727 }
1728
1729 cgrid[dir][ii + 1] = cgrid[dir][ii] + d;
1730 }
1731 }
1732
1733 MFEM_ASSERT(cgrid[0][n_orig[0]] == n1 && cgrid[1][n_orig[1]] == n2, "");
1734
1735 bool allset = true;
1736 bool hasSlaveFaces = false;
1737 bool hasAuxFace = false;
1738
1739 for (int ii=0; ii<=n_orig[0]; ++ii)
1740 {
1741 const int i = cgrid[0][ii];
1742 for (int jj=0; jj<=n_orig[1]; ++jj)
1743 {
1744 const int j = cgrid[1][jj];
1745 if (gridVertex(i,j) < 0)
1746 {
1747 allset = false;
1748 }
1749 else if (0 < i && i < n1 && 0 < j && j < n2)
1750 {
1751 hasSlaveFaces = true;
1752 }
1753 }
1754 }
1755
1756 auto SetFacePairOnGridRange = [&](int i0, int i1, int j0, int j1)
1757 {
1758 std::array<int, 4> cv{gridVertex(i0, j0), gridVertex(i1, j0),
1759 gridVertex(i1, j1), gridVertex(i0, j1)};
1760 const std::pair<int, int> childPair = QuadrupleToPair(cv);
1761
1762 // min(cv) may be negative, if gridVertex is not set everywhere.
1763 if (childPair.first < 0) { return; }
1764
1765 const int d0 = i1 - i0;
1766 const int d1 = j1 - j0;
1767
1768 const bool childPairTopo = v2f.count(childPair) > 0;
1769 if (childPairTopo)
1770 {
1771 const int childFace = v2f.at(childPair);
1772 const int ori = GetFaceOrientation(patchTopo, childFace, cv);
1773 // ori gives the orientation and index of cv matching the first
1774 // vertex of childFace.
1775 facePairs.emplace_back(
1776 FacePairInfo{cv[0], parentFace, SlaveFaceInfo{childFace,
1777 ori, {i0, j0}, {d0, d1}}});
1778 }
1779 else
1780 {
1781 // Check whether the parent face is on the boundary.
1782 const Mesh::FaceInformation faceInfo = patchTopo->GetFaceInformation(
1783 parentFace);
1784 const bool bdryParentFace = faceInfo.IsBoundary();
1785
1786 if (!allset && !bdryParentFace)
1787 {
1788 hasAuxFace = true;
1789 // Check whether childPair is in auxFaces.
1790 if (auxv2f.count(childPair) == 0)
1791 {
1792 // Create a new auxiliary face
1793 auxv2f[childPair] = auxFaces.size();
1794 AuxiliaryFace auxFace;
1795 for (int k=0; k<4; ++k) { auxFace.v[k] = cv[k]; }
1796
1797 auxFace.parent = parentFace;
1798 // Orientation is defined as 0 for a new auxiliary face.
1799 auxFace.ori = 0;
1800 auxFace.ksi0[0] = i0;
1801 auxFace.ksi0[1] = j0;
1802 auxFace.ksi1[0] = i1;
1803 auxFace.ksi1[1] = j1;
1804
1805 auxFaces.push_back(auxFace);
1806 facePairs.emplace_back(
1807 FacePairInfo{cv[0], parentFace,
1808 SlaveFaceInfo{-1 - auxv2f[childPair],
1809 0, {i0, j0}, {d0, d1}}});
1810 }
1811 }
1812 }
1813 };
1814
1815 // Loop over child faces and set facePairs, as well as auxiliary faces.
1816 for (int ii=0; ii<n_orig[0]; ++ii)
1817 {
1818 const int i = cgrid[0][ii];
1819 const int i1 = cgrid[0][ii + 1];
1820 for (int jj=0; jj<n_orig[1]; ++jj)
1821 {
1822 const int j = cgrid[1][jj];
1823 const int j1 = cgrid[1][jj + 1];
1824 SetFacePairOnGridRange(i, i1, j, j1);
1825 }
1826 }
1827
1828 // Loop over child boundary edges and set edgePairs.
1829 for (int dir=1; dir<=2; ++dir)
1830 {
1831 const int ne = dir == 1 ? n1 : n2;
1832 for (int s=0; s<2; ++s) // Loop over 2 sides for this direction.
1833 {
1834 const int parentEdge = parentEdges[dir == 1 ? 2*s : (2*s) + 1];
1835 const bool reverse_p = parentEdgeRev[dir == 1 ? 2*s : (2*s) + 1];
1836 // Sides with s=1 are reversed in defining parentEdgeRev.
1837 const bool reverse = s == 0 ? reverse_p : !reverse_p;
1838 const bool parentVisited = visitedParentEdges.count(parentEdge) > 0;
1839
1840 if (!parentVisited)
1841 {
1842 edgePairOS[parentEdge] = edgePairs.size();
1843 edgePairs.resize(edgePairs.size() + ne);
1844 }
1845
1846 int tvprev = -1;
1847 int kiprev = -1;
1848 bool lagTV = false;
1849 int firstEdge = -1;
1850 int os_e = 0;
1851
1852 // Loop edges in direction `dir`
1853 for (int e_orig = 0; e_orig < n_orig[dir-1]; ++e_orig)
1854 {
1855 const int e_i = os_e;
1856 const int e_orig_rev = reverse ? n_orig[dir-1] - 1 - e_orig : e_orig;
1857 int de = rf;
1858 if (kvf_coarse.size() > 0)
1859 {
1860 de = kvf_coarse[kvi[dir - 1]][e_orig_rev];
1861 }
1862 else if (kvf.size() > 0)
1863 {
1864 de = kvf[kvi[dir - 1]][e_orig_rev];
1865 }
1866
1867 os_e += de;
1868
1869 // For both directions, side s=0 has increasing indices and s=1
1870 // has decreasing indices.
1871
1872 const int i0 = e_i;
1873 const int i1 = e_i + de;
1874
1875 // Edge index with respect to the master edge.
1876 const int e_idx = reverse ? ne - e_i - de : e_i;
1877
1878 Array<int> cv(2);
1879 if (dir == 1)
1880 {
1881 cv[0] = gridVertex(i0,s*n2);
1882 cv[1] = gridVertex(i1,s*n2);
1883 }
1884 else
1885 {
1886 cv[0] = gridVertex((1-s)*n1, i0);
1887 cv[1] = gridVertex((1-s)*n1, i1);
1888 }
1889
1890 const int cv0 = cv[0];
1891 int tv_int = -1; // Top-vertex interior to the master edge
1892 int ki = -1; // Knot-span index of tv_int, w.r.t. the master edge
1893
1894 if (lagTV)
1895 {
1896 tv_int = tvprev;
1897 ki = kiprev;
1898 }
1899
1900 if (tvprev == -1)
1901 {
1902 kiprev = (i0 == 0 || i0 == ne) ? i1 : i0;
1903 // Top-vertex interior to the master edge
1904 tvprev = (i0 == 0 || i0 == ne) ? cv[1] : cv[0];
1905 }
1906 else if (e_i < ne - 1) // Don't set to the endpoint
1907 {
1908 kiprev = (tvprev == cv[0]) ? i1 : i0;
1909 // Next interior vertex along the master edge
1910 tvprev = (tvprev == cv[0]) ? cv[1] : cv[0];
1911 }
1912
1913 if (!lagTV)
1914 {
1915 tv_int = tvprev;
1916 ki = kiprev;
1917 }
1918
1919 cv.Sort();
1920 if (cv[0] < 0) // may occur if gridVertex is not set everywhere.
1921 {
1922 continue;
1923 }
1924 else if (firstEdge == -1)
1925 {
1926 firstEdge = e_i;
1927 if (e_i > 0)
1928 {
1929 tv_int = cv0;
1930 ki = i0;
1931 tvprev = cv.Sum() - cv0; // cv1
1932 kiprev = i1;
1933 lagTV = true;
1934 }
1935 }
1936
1937 const int tv = (e_idx == ne - de) ? -1 : tv_int;
1938 const int tvki = (e_idx == ne - de) ? -1 : (reverse ? ne - ki : ki);
1939
1940 const std::pair<int, int> edge_i(cv[0], cv[1]);
1941 const int childEdge = v2e.at(edge_i);
1942
1943 if (tv == -1) { lagTV = true; }
1944
1945 if (!parentVisited)
1946 {
1947 // edgePairs is ordered starting from the vertex of lower index.
1948 edgePairs[edgePairOS[parentEdge] + e_idx].Set(tv, tvki,
1949 childEdge,
1950 parentEdge);
1951 }
1952 else
1953 {
1954 // Consistency check
1955 const int os = edgePairOS[parentEdge];
1956 if (edgePairs[os + e_idx].child != childEdge ||
1957 edgePairs[os + e_idx].parent != parentEdge)
1958 {
1959 consistent = false;
1960 }
1961 }
1962 }
1963
1964 visitedParentEdges.insert(parentEdge);
1965 }
1966 } // dir
1967
1968 // Set auxiliary and patch-slave faces outside the set gridVertex. Here,
1969 // patch-slave refers to a slave face that is a face of a neighboring
1970 // patch and may contain multiple mesh faces. In general, there can be at
1971 // most 8 = 3^2 - 1 such faces.
1972
1973 std::array<int, 4> gv1 = {0, r1min, r1max, n1};
1974 std::array<int, 4> gv2 = {0, r2min, r2max, n2};
1975
1976 if (hasSlaveFaces && !allset)
1977 {
1978 for (int i=0; i<3; ++i)
1979 for (int j=0; j<3; ++j)
1980 {
1981 // Skip the middle, which is covered by gridVertex.
1982 if (i == 1 && j == 1) { continue; }
1983
1984 // Skip degenerate faces
1985 if (gv1[i] == gv1[i+1] || gv2[j] == gv2[j+1]) { continue; }
1986
1987 // Define auxiliary face (gv1[i], gv1[i+1]) x (gv2[j], gv2[j+1])
1988 SetFacePairOnGridRange(gv1[i], gv1[i+1], gv2[j], gv2[j+1]);
1989 }
1990 }
1991
1992 // Set auxiliary edges outside the gridVertex, on the boundary of the
1993 // parent face. Note that auxiliary edges cannot simply be found as edges
1994 // of auxiliary faces, because the faces above are defined only on parent
1995 // faces listed in V2K data. Other auxiliary faces will be defined in
1996 // FindAdditionalFacesSA by using auxiliary edges found below.
1997
1998 // Auxiliary edges in first and second directions
1999 for (int d=0; d<2; ++d)
2000 {
2001 for (int i=0; i<3; ++i)
2002 {
2003 if (i == 1) // Skip the middle, which is covered by gridVertex.
2004 {
2005 continue;
2006 }
2007
2008 for (int s=0; s<2; ++s) // Loop over 2 sides in this direction
2009 {
2010 // Set gv1 (d == 0) or gv2 (d == 1) for this edge.
2011 // Set range of set knot-span indices for this edge.
2012 int rmin = -1;
2013 int rmax = -1;
2014 const int n_d = d == 0 ? n1 : n2;
2015 for (int j=1; j<n_d; ++j)
2016 {
2017 const int tv = d == 0 ? gridVertex(j,s*n2) :
2018 gridVertex((1-s)*n1,j);
2019 if (tv >= 0)
2020 {
2021 if (rmin == -1)
2022 {
2023 // Initialize range
2024 rmin = j;
2025 rmax = j;
2026 }
2027 else
2028 {
2029 rmin = std::min(rmin, j);
2030 rmax = std::max(rmax, j);
2031 }
2032 }
2033 }
2034
2035 if (rmax == -1)
2036 {
2037 // No vertices set in gridVertex on the interior of this edge.
2038 continue;
2039 }
2040
2041 if (d == 0)
2042 {
2043 gv1[1] = rmin;
2044 gv1[2] = rmax;
2045 }
2046 else
2047 {
2048 gv2[1] = rmin;
2049 gv2[2] = rmax;
2050 }
2051
2052 const int pid = d == 0 ? 2*s : (2*s) + 1; // Parent index
2053 const bool reverse_p = parentEdgeRev[pid];
2054 // Sides with s=1 are reversed in defining parentEdgeRev.
2055 const bool reverse = s == 0 ? reverse_p : !reverse_p;
2056
2057 // Define an auxiliary edge (gv1[i], gv1[i+1])
2058 Array<int> cv(2);
2059 std::array<int, 2> ki;
2060
2061 if (d == 0)
2062 {
2063 cv[0] = gridVertex(gv1[i],s*n2);
2064 cv[1] = gridVertex(gv1[i+1],s*n2);
2065
2066 ki[0] = gv1[i];
2067 ki[1] = gv1[i+1];
2068 }
2069 else
2070 {
2071 cv[0] = gridVertex((1-s)*n1,gv2[i]);
2072 cv[1] = gridVertex((1-s)*n1,gv2[i+1]);
2073
2074 ki[0] = gv2[i];
2075 ki[1] = gv2[i+1];
2076 }
2077
2078 if (cv[0] == cv[1])
2079 {
2080 continue;
2081 }
2082
2083 // Top-vertex interior to the master edge.
2084 const int tv = i == 0 ? cv[1] : cv[0];
2085 const int tvki_f = i == 0 ? ki[1] : ki[0]; // face index
2086 const int tvki = reverse ? n_d - tvki_f : tvki_f; // edge index
2087
2088 cv.Sort();
2089 MFEM_ASSERT(cv[0] >= 0, "");
2090
2091 const std::pair<int, int> childPair(cv[0], cv[1]);
2092 const bool childPairTopo = v2e.count(childPair) > 0;
2093 if (!childPairTopo)
2094 {
2095 const int pv0 = d == 0 ? gridVertex(0,s*n2) :
2096 gridVertex((1-s)*n1,0);
2097 const int pv1 = d == 0 ? gridVertex(n1,s*n2) :
2098 gridVertex((1-s)*n1,n2);
2099 const std::pair<int, int> parentPair(pv0 < pv1 ? pv0 : pv1,
2100 pv0 < pv1 ? pv1 : pv0);
2101 const int parentEdge = v2e.at(parentPair);
2102 MFEM_ASSERT(parentEdges[pid] == parentEdge, "");
2103
2104 // Check whether childPair is in auxEdges.
2105 if (auxv2e.count(childPair) == 0)
2106 {
2107 const int knotIndex0 = (d == 0) ? gv1[i] : gv2[i];
2108 const int knotIndex1 = (d == 0) ? gv1[i+1] : gv2[i+1];
2109
2110 // Create a new auxiliary edge
2111 auxv2e[childPair] = auxEdges.size();
2112 auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
2113 parentEdge :
2114 -1 - parentEdge,
2115 {childPair.first, childPair.second},
2116 {knotIndex0, knotIndex1}});
2117 }
2118
2119 const bool start = (i == 0 && !reverse) || (i != 0 && reverse);
2120 int end_idx = kvf_coarse.size() > 0 ?
2121 (start ? 0 : kvf_coarse[kvi[d]].Size() - 1) : 0;
2122 int de = kvf_coarse.size() > 0 ? kvf_coarse[kvi[d]][end_idx] : rf;
2123 if (kvf.size() > 0 && kvf_coarse.size() == 0)
2124 {
2125 end_idx = start ? 0 : kvf[kvi[d]].Size() - 1;
2126 de = kvf[kvi[d]][end_idx];
2127 }
2128
2129 const int e_idx_i = i == 0 ? 0 : n_d - de;
2130 const int e_idx = reverse ? n_d - de - e_idx_i : e_idx_i;
2131
2132 const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
2133 (e_idx == n_d - de) ? -1 : tvki,
2134 -1 - auxv2e[childPair], parentEdge);
2135
2136 const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
2137 if (unset)
2138 {
2139 edgePairs[edgePairOS[parentEdge] + e_idx] = ep_e;
2140 }
2141 else
2142 {
2143 // Verify matching
2144 MFEM_ASSERT(edgePairs[edgePairOS[parentEdge] + e_idx] == ep_e, "");
2145 }
2146 }
2147 else // childPairTopo == true, so this edge is a slave edge.
2148 {
2149 const int childEdge = v2e.at(childPair);
2150
2151 const int pv0 = d == 0 ? gridVertex(0,s*n2) : gridVertex((1-s)*n1,0);
2152 const int pv1 = d == 0 ? gridVertex(n1,s*n2) : gridVertex((1-s)*n1,n2);
2153 const std::pair<int, int> parentPair(pv0 < pv1 ? pv0 : pv1,
2154 pv0 < pv1 ? pv1 : pv0);
2155 const int parentEdge = v2e.at(parentPair);
2156 MFEM_ASSERT(parentEdges[pid] == parentEdge, "");
2157
2158 const bool start = (i == 0 && !reverse) || (i != 0 && reverse);
2159 int end_idx = kvf_coarse.size() > 0 ?
2160 (start ? 0 : kvf_coarse[kvi[d]].Size() - 1) : 0;
2161 int de = kvf_coarse.size() > 0 ? kvf_coarse[kvi[d]][end_idx] : rf;
2162 if (kvf.size() > 0 && kvf_coarse.size() == 0)
2163 {
2164 end_idx = start ? 0 : kvf[kvi[d]].Size() - 1;
2165 de = kvf[kvi[d]][end_idx];
2166 }
2167
2168 const int e_idx_i = i == 0 ? 0 : n_d - de;
2169 const int e_idx = reverse ? n_d - de - e_idx_i : e_idx_i;
2170
2171 const int tv_e = (e_idx == n_d - de) ? -1 : tv;
2172 const int tv_ki = (e_idx == n_d - de) ? -1 : tvki;
2173
2174 const EdgePairInfo ep_e(tv_e, tv_ki, childEdge, parentEdge);
2175
2176#ifdef MFEM_DEBUG
2177 const bool unset =
2178 !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
2179 const bool matching =
2180 edgePairs[edgePairOS[parentEdge] + e_idx] == ep_e;
2181 MFEM_ASSERT(unset || matching, "");
2182#endif
2183
2184 edgePairs[edgePairOS[parentEdge] + e_idx] = ep_e;
2185 }
2186 }
2187 }
2188 }
2189
2190 if (hasSlaveFaces || hasAuxFace) { masterFaces.insert(parentFace); }
2191 } // loop over parents
2192
2193 MFEM_VERIFY(consistent, "");
2194}
2195
2196void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
2197{
2198 auxface2patch.SetSize(auxFaces.size(), 2);
2199
2200 if (auxFaces.size() == 0) { return; }
2201
2202 auxface2patch = -1;
2203
2204 const int dim = Dimension();
2205
2206 bool consistent = true;
2207
2208 for (int p=0; p<num_structured_patches; ++p)
2209 {
2210 Array<int> faces, orient;
2211 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2212 else { patchTopo->GetElementFaces(p, faces, orient); }
2213
2214 for (auto face : faces)
2215 {
2216 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2217 masterFaceToId.count(face) > 0;
2218 if (isMaster) // If a master face
2219 {
2220 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2221 masterFaceToId.at(face);
2222 const std::vector<int> &slaves = dim == 2 ? masterEdgeInfo[mid].slaves :
2223 masterFaceInfo[mid].slaves;
2224 for (auto s : slaves)
2225 {
2226 if (s < 0)
2227 {
2228 // Auxiliary face.
2229 const int aux = -1 - s;
2230 if (auxface2patch(aux, 0) >= 0)
2231 {
2232 if (auxface2patch(aux, 1) != -1) { consistent = false; }
2233 auxface2patch(aux, 1) = p;
2234 }
2235 else
2236 {
2237 auxface2patch(aux, 0) = p;
2238 }
2239 }
2240 }
2241 }
2242 }
2243 }
2244
2245 MFEM_VERIFY(consistent, "");
2246}
2247
2248void NCNURBSExtension::GetSlaveFaceToPatchTable(Array2D<int> &sface2patch)
2249{
2250 const int dim = Dimension();
2251 const int numUnique = dim == 2 ? slaveEdgesUnique.Size() :
2252 slaveFacesUnique.Size();
2253 sface2patch.SetSize(numUnique, 2);
2254
2255 if (numUnique == 0) { return; }
2256
2257 sface2patch = -1;
2258
2259 bool consistent = true;
2260
2261 for (int p=0; p<num_structured_patches; ++p)
2262 {
2263 Array<int> faces, orient;
2264 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2265 else { patchTopo->GetElementFaces(p, faces, orient); }
2266
2267 for (auto face : faces)
2268 {
2269 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2270 masterFaceToId.count(face) > 0;
2271 if (isMaster) // If a master face
2272 {
2273 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2274 masterFaceToId.at(face);
2275 const std::vector<int> &slaves = dim == 2 ? masterEdgeInfo[mid].slaves :
2276 masterFaceInfo[mid].slaves;
2277 for (auto id : slaves)
2278 {
2279 if (id >= 0)
2280 {
2281 const int s = dim == 2 ? slaveEdges[id] : slaveFaces[id].index;
2282 const int u = dim == 2 ? slaveEdgesToUnique[s] :
2283 slaveFacesToUnique[s];
2284 if (sface2patch(u, 0) >= 0)
2285 {
2286 if (sface2patch(u, 1) != -1) { consistent = false; }
2287 sface2patch(u, 1) = p;
2288 }
2289 else
2290 {
2291 sface2patch(u, 0) = p;
2292 }
2293 }
2294 }
2295 }
2296 }
2297 }
2298
2299 MFEM_VERIFY(consistent, "");
2300}
2301
2302void RemapKnotIndex(bool rev, const Array<int> &rf, int &k)
2303{
2304 const int ne = rf.Size();
2305 const int k0 = k;
2306 k = 0;
2307 for (int p=0; p<k0; ++p)
2308 {
2309 const int rp = rev ? ne - 1 - p : p;
2310 k += rf[rp];
2311 }
2312}
2313
2314void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
2315{
2316 for (auto auxEdge : auxEdges)
2317 {
2318 const int p = auxEdge.parent;
2319 const int parent = p < 0 ? -1 - p : p;
2320 const int kv = KnotInd(parent);
2321 for (int i=0; i<2; ++i)
2322 {
2323 RemapKnotIndex(false, kvf[kv], auxEdge.ksi[i]);
2324 }
2325 }
2326
2327 for (auto auxFace : auxFaces)
2328 {
2329 Array<int> pv;
2330 std::array<int, 4> quad;
2331 patchTopo->GetFaceVertices(auxFace.parent, pv);
2332 MFEM_ASSERT(pv.Size() == 4, "");
2333 for (int i=0; i<4; ++i) { quad[i] = pv[i]; }
2334 // The face with vertices (pv0, pv1, pv2, pv3) is defined as a parent face.
2335 const std::pair<int, int> parentPair = QuadrupleToPair(quad);
2336 const std::array<int, 2> kv = parentToKV.at(parentPair);
2337
2338 RemapKnotIndex(false, kvf[kv[0]], auxFace.ksi0[0]);
2339 RemapKnotIndex(false, kvf[kv[0]], auxFace.ksi1[0]);
2340
2341 RemapKnotIndex(false, kvf[kv[1]], auxFace.ksi0[1]);
2342 RemapKnotIndex(false, kvf[kv[1]], auxFace.ksi1[1]);
2343 }
2344}
2345
2347
2348void NCNURBSExtension::LoadFactorsForKV(const std::string &filename)
2349{
2350 if (kvf_coarse.size() == 0) { kvf_coarse = kvf; }
2351 if (kvf.size() == 0) { kvf.resize(NumOfKnotVectors); }
2352
2353 for (int kv=0; kv<NumOfKnotVectors; ++kv)
2354 {
2355 kvf[kv].SetSize(knotVectors[kv]->GetNE());
2356 kvf[kv] = unsetFactor;
2357 }
2358
2359 if (filename.empty()) { return; }
2360
2361 ifstream f(filename);
2362 int nkv;
2363 f >> nkv;
2364
2365 for (int i=0; i<nkv; ++i)
2366 {
2367 int kv, nf, rf;
2368 f >> kv >> nf;
2369 MFEM_ASSERT(nf == 1, ""); // TODO: support input of multiple factors.
2370
2371 kvf[kv] = unsetFactor;
2372 for (int j=0; j<nf; ++j) { f >> rf; }
2373
2374 for (int j=0; j<kvf[kv].Size(); ++j) { kvf[kv][j] = rf; }
2375 }
2376
2377 f.close();
2378}
2379
2380int NCNURBSExtension::AuxiliaryEdgeNE(int aux_edge)
2381{
2382 const int signedParentEdge = auxEdges[aux_edge].parent;
2383 const int ki0 = auxEdges[aux_edge].ksi[0];
2384 const int ki1raw = auxEdges[aux_edge].ksi[1];
2385 int ki1 = ki1raw;
2386 if (ki1raw == -1)
2387 {
2388 const bool rev = signedParentEdge < 0;
2389 const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
2390 ki1 = KnotVec(parentEdge)->GetNE();
2391 }
2392
2393 return ki1 - ki0;
2394}
2395
2396// parentVerts are ordered with ascending knot-span index in parent edge, with
2397// knots from lower edge endpoint vertex to higher.
2398void NCNURBSExtension::SlaveEdgeToParent(int se, int parent,
2399 const Array<int> &os,
2400 const std::vector<int> &parentVerts,
2401 Array<int> &edges)
2402{
2403 Array<int> sev(2);
2404 if (se < 0) // Auxiliary edge
2405 {
2406 for (int i=0; i<2; ++i) { sev[i] = auxEdges[-1 - se].v[i]; }
2407 }
2408 else
2409 {
2410 patchTopo->GetEdgeVertices(se, sev);
2411 }
2412
2413 // Number of slave and auxiliary edges, not mesh edges
2414 const int nedge = parentVerts.size() + 1;
2415 MFEM_ASSERT((int) parentVerts.size() + 2 == os.Size(), "");
2416
2417 Array<int> parentEndpoints;
2418 patchTopo->GetEdgeVertices(parent, parentEndpoints);
2419
2420 bool found = false;
2421 for (int i=0; i<nedge; ++i)
2422 {
2423 const bool first = (i == 0);
2424 const bool last = (i == nedge - 1);
2425 const int v0 = first ? parentEndpoints[0] : parentVerts[i - 1];
2426 const int v1 = last ? parentEndpoints[1] : parentVerts[i];
2427 if (sev[0] == v0 && sev[1] == v1)
2428 {
2429 found = true;
2430 MFEM_ASSERT(edges.Size() == os[i + 1] - os[i], "");
2431 for (int j=0; j<edges.Size(); ++j) { edges[j] = os[i] + j; }
2432 }
2433 else if (sev[0] == v1 && sev[1] == v0)
2434 {
2435 found = true;
2436 MFEM_ASSERT(edges.Size() == os[i + 1] - os[i], "");
2437 for (int j=0; j<edges.Size(); ++j) { edges[j] = os[i + 1] - 1 - j; }
2438 }
2439 }
2440
2441 MFEM_VERIFY(found, "");
2442}
2443
2444void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
2445{
2446 const int np = masterEdgeInfo[mid].slaves.size();
2447 MFEM_VERIFY(np > 0, "");
2448 os.SetSize(np + 1);
2449 os[0] = 0;
2450
2451 for (int i=0; i<np; ++i)
2452 {
2453 const int p = masterEdgeInfo[mid].slaves[i];
2454 const int s = slaveEdges[p];
2455 int nes = 0;
2456 if (s >= 0)
2457 {
2458 nes = knotVectors[KnotInd(s)]->GetNE();
2459 }
2460 else
2461 {
2462 nes = AuxiliaryEdgeNE(-1 - s);
2463 }
2464
2465 os[i+1] = os[i] + nes;
2466 }
2467}
2468
2470{
2471 Array<int> frf(rf.Sum());
2472 int os = 0;
2473 for (auto f : rf)
2474 {
2475 for (int i=0; i<f; ++i)
2476 {
2477 frf[os + i] = f;
2478 }
2479
2480 os += f;
2481 }
2482
2483 return frf;
2484}
2485
2487{
2488 constexpr char dirEdges3D[3][4] = {{0, 2, 4, 6}, {1, 3, 5, 7}, {8, 9, 10, 11}};
2489 constexpr char dirEdges2D[2][2] = {{0, 2}, {1, 3}};
2490
2491 Array<int> edges, oedges;
2492 patchTopo->GetElementEdges(p, edges, oedges);
2493
2494 const int dim = Dimension();
2495 const int nedge = dim == 3 ? 4 : 2;
2496
2497 int dirSet = 0;
2498
2499 bool partialChange = false;
2500 bool consistent = true;
2501
2502 auto SetFactorsDirection = [&](int j, int os_final, int af, Array<int> &rf)
2503 {
2504 if (af == unsetFactor) { return; }
2505 if (rf.Size() == 0)
2506 {
2507 rf.SetSize(os_final);
2508 rf = unsetFactor;
2509 }
2510 if (rf[j] != unsetFactor && af != rf[j]) { consistent = false; }
2511 rf[j] = af;
2512 };
2513
2514 auto SetFactorsEdge = [&](int j, int rf, Array<int> &pf)
2515 {
2516 if (rf == unsetFactor) { return; }
2517 if (pf[j] != unsetFactor && pf[j] != rf) { consistent = false; }
2518 pf[j] = rf;
2519 };
2520
2521 auto LoopEdgesForDirection = [&](int d, Array<int> &rf, bool first)
2522 {
2523 for (int i=0; i<nedge; ++i)
2524 {
2525 const int edgeIndex = dim == 3 ? dirEdges3D[d][i] : dirEdges2D[d][i];
2526 const int edge = edges[edgeIndex];
2527 const bool isMaster = IsMasterEdge(edge);
2528 const int kv = KnotInd(edge);
2529 const bool rev = KnotSign(edge) < 0;
2530
2531 if (first)
2532 {
2533 const int nfe = knotVectors[kv]->GetNE();
2534 const bool fullSize = kvf.size() > 0 && kvf[kv].Size() == nfe;
2535 const Array<int> rf_i = fullSize ? kvf[kv] :
2537 if (rf_i.Size() > 0 && rf.Size() == 0) { rf = rf_i; }
2538 }
2539 else
2540 {
2541 if (kvf[kv] != rf) { partialChange = true; }
2542 kvf[kv] = rf;
2543 }
2544
2545 if (isMaster)
2546 {
2547 // Check whether slave edges have factors set.
2548 const int mid = masterEdgeToId.at(edge);
2549 const int numPieces = masterEdgeInfo[mid].slaves.size();
2550 Array<int> os;
2551 GetMasterEdgePieceOffsets(mid, os);
2552
2553 for (int piece=0; piece<numPieces; ++piece)
2554 {
2555 const int e = masterEdgeInfo[mid].slaves[piece];
2556 const int s = slaveEdges[e];
2557 Array<int> parentEdges;
2558 Array<int> *pf; // Refinement factors for this piece
2559
2560 if (s >= 0) // Slave edge
2561 {
2562 const int kvs = KnotInd(s);
2563 parentEdges.SetSize(kvf[kvs].Size());
2564 pf = &kvf[kvs];
2565 }
2566 else // Aux edge
2567 {
2568 const int aux_edge = -1 - s;
2569 if (auxef[aux_edge].Size() == 0)
2570 {
2571 auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
2572 auxef[aux_edge] = unsetFactor;
2573 }
2574 parentEdges.SetSize(auxef[aux_edge].Size());
2575 pf = &auxef[aux_edge];
2576 }
2577
2578 if (first && parentEdges.Size() == 0) { continue; }
2579 SlaveEdgeToParent(s, edge, os, masterEdgeInfo[mid].vertices, parentEdges);
2580 MFEM_ASSERT(parentEdges.Size() == os[piece + 1] - os[piece], "");
2581
2582 for (int j = os[piece]; j < os[piece + 1]; ++j)
2583 {
2584 const int jj = parentEdges[j - os[piece]];
2585 const int jr = rev ? rf.Size() - 1 - jj : jj;
2586 if (first)
2587 {
2588 SetFactorsDirection(jr, os[numPieces],
2589 (*pf)[j - os[piece]], rf);
2590 }
2591 else
2592 {
2593 SetFactorsEdge(j - os[piece], rf[jr], *pf);
2594 }
2595 }
2596 }
2597 }
2598 }
2599 };
2600
2601 for (int d=0; d<dim; ++d)
2602 {
2603 // Find the array of factors for direction d
2604 Array<int> rf;
2605 LoopEdgesForDirection(d, rf, true);
2606 if (rf.Size() == 0) { continue; } // This direction is unset
2607
2608 // Set the same factor for all knotvectors in direction d.
2609 LoopEdgesForDirection(d, rf, false);
2610 if (rf.Min() > unsetFactor) { dirSet += static_cast<int>(pow(2, d)); }
2611 }
2612
2613 MFEM_VERIFY(consistent, "");
2614 return partialChange ? -1 - dirSet : dirSet;
2615}
2616
2618{
2619 const int dim = Dimension();
2620 if (dim == 1 || num_structured_patches < 1)
2621 {
2622 for (size_t i=0; i<kvf.size(); ++i)
2623 {
2624 kvf[i] = rf_default;
2625 }
2626 return;
2627 }
2628
2629 // Note that a slave edge can be a patchTopo edge (nonnegative index) or an
2630 // AuxiliaryEdge (negative index). A slave edge can be contained in multiple
2631 // overlapping master edges.
2632
2633 // First, set slaveEdgesUnique.
2634 {
2635 slaveEdgesUnique.SetSize(0);
2636 slaveEdgesUnique.Reserve(slaveEdges.size());
2637 for (auto s : slaveEdges)
2638 {
2639 if (slaveEdgesToUnique.count(s) == 0)
2640 {
2641 slaveEdgesToUnique[s] = slaveEdgesUnique.Size();
2642 slaveEdgesUnique.Append(s);
2643 }
2644 }
2645 }
2646
2647 // Set slaveFacesUnique.
2648 {
2649 slaveFacesUnique.SetSize(0);
2650 slaveFacesUnique.Reserve(slaveFaces.size());
2651 for (auto s : slaveFaces)
2652 {
2653 if (slaveFacesToUnique.count(s.index) == 0)
2654 {
2655 slaveFacesToUnique[s.index] = slaveFacesUnique.Size();
2656 slaveFacesUnique.Append(s.index);
2657 }
2658 }
2659 }
2660
2661 // Initialize a set of patches to visit, using face-neighbors of the first
2662 // patch.
2663 const Table *face2elem = patchTopo->GetFaceToElementTable();
2664
2665 Array2D<int> auxface2patch, sface2patch;
2666 GetAuxFaceToPatchTable(auxface2patch);
2667 GetSlaveFaceToPatchTable(sface2patch);
2668
2669 Array<int> faces, orient;
2670
2671 auto faceNeighbors = [&](int p, std::set<int> &nghb)
2672 {
2673 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2674 else { patchTopo->GetElementFaces(p, faces, orient); }
2675
2676 for (auto face : faces)
2677 {
2678 Array<int> row;
2679 face2elem->GetRow(face, row);
2680
2681 const bool isSlave = dim == 2 ? slaveEdgesToUnique.count(
2682 face) > 0 : slaveFacesToUnique.count(face) > 0;
2683 if (isSlave)
2684 {
2685 const int u = dim == 2 ? slaveEdgesToUnique[face] :
2686 slaveFacesToUnique[face];
2687 for (int i=0; i<2; ++i)
2688 {
2689 const int elem = sface2patch(u, i);
2690 if (elem >= 0 && elem != p) { nghb.insert(elem); }
2691 }
2692 }
2693
2694 for (auto elem : row) { nghb.insert(elem); }
2695 }
2696 };
2697
2698 auto masterFaceNeighbors = [&](int p, std::set<int> &nghb)
2699 {
2700 if (dim == 2) { patchTopo->GetElementEdges(p, faces, orient); }
2701 else { patchTopo->GetElementFaces(p, faces, orient); }
2702
2703 for (auto face : faces)
2704 {
2705 const bool isMaster = dim == 2 ? masterEdgeToId.count(face) > 0 :
2706 masterFaceToId.count(face) > 0;
2707 if (isMaster) // If a master face
2708 {
2709 const int mid = dim == 2 ? masterEdgeToId.at(face) :
2710 masterFaceToId.at(face);
2711 const std::vector<int> &slaves =
2712 dim == 2 ? masterEdgeInfo[mid].slaves : masterFaceInfo[mid].slaves;
2713 for (auto s : slaves)
2714 {
2715 if (s < 0)
2716 {
2717 // Auxiliary face.
2718 const int aux = -1 - s;
2719 for (int i=0; i<2; ++i)
2720 {
2721 const int patch = auxface2patch(aux, i);
2722 if (patch >= 0) { nghb.insert(patch); }
2723 }
2724 }
2725 else
2726 {
2727 // Slave face in patchTopo.
2728 Array<int> row;
2729 face2elem->GetRow(s, row);
2730 for (auto elem : row) { nghb.insert(elem); }
2731 }
2732 }
2733 }
2734 }
2735 };
2736
2737 const int npatchall = patches.Size();
2738 Array<int> patchState(npatchall);
2739 patchState = 0;
2740
2741 auxef.resize(auxEdges.size());
2742
2743 std::set<int> nextPatches, unchanged;
2744 const int dirAllSet = dim == 3 ? 7 : 3;
2745 int lastChanged = 0;
2746 int iter = 0;
2747 bool done = false;
2748 while (iter < 100 && !done)
2749 {
2750 // Start each iteration at the patch last changed
2751 nextPatches.clear();
2752 nextPatches.insert(lastChanged);
2753
2754 std::set<int> visited; // Visit each patch only once per iteration
2755 iter++;
2756
2757 while (nextPatches.size() > 0)
2758 {
2759 const int p = *nextPatches.begin();
2760 nextPatches.erase(p);
2761
2762 visited.insert(p);
2763
2764 const int dirSetSigned = SetPatchFactors(p);
2765 const bool partialChange = dirSetSigned < 0;
2766 const int dirSet = partialChange ? -1 - dirSetSigned : dirSetSigned;
2767 const bool changed = (patchState[p] != dirSet) || partialChange;
2768 patchState[p] = dirSet;
2769
2770 // Find neighbors of patch p
2771 std::set<int> neighbors;
2772
2773 // First, find neighbors sharing a conforming face, via face2elem.
2774 faceNeighbors(p, neighbors);
2775
2776 // Second, find neighbors sharing a slave/auxiliary face in patchTopo.
2777 masterFaceNeighbors(p, neighbors);
2778
2779 // Add neighbors not done to nextPatches. Note that a patch can be
2780 // added to nextPatches on multiple iterations, to propagate factors in
2781 // different directions, on multiple sweeps.
2782
2783 for (auto n : neighbors)
2784 {
2785 if (n < npatchall && n != p && patchState[n] != dirAllSet &&
2786 visited.count(n) == 0)
2787 {
2788 nextPatches.insert(n);
2789 }
2790 }
2791
2792 if (changed)
2793 {
2794 unchanged.erase(p);
2795 lastChanged = p;
2796 }
2797 else
2798 {
2799 unchanged.insert(p);
2800 }
2801
2802 if (unchanged.size() == (size_t) npatchall)
2803 {
2804 // Make another pass through all patches to check for changes
2805 for (int i=0; i<npatchall; ++i)
2806 {
2807 const int dirSetSigned_i = SetPatchFactors(i);
2808 const bool partialChange_i = dirSetSigned_i < 0;
2809 const int dirSet_i = partialChange_i ? -1 - dirSetSigned_i :
2810 dirSetSigned_i;
2811 const bool changed_i = (patchState[i] != dirSet_i) ||
2812 partialChange_i;
2813 patchState[p] = dirSet_i;
2814 if (changed_i)
2815 {
2816 unchanged.erase(i);
2817 lastChanged = i;
2818 }
2819 }
2820 }
2821
2822 if (unchanged.size() == (size_t) npatchall)
2823 {
2824 done = true;
2825 break;
2826 }
2827 }
2828 }
2829
2830 // For any unset entries of kvf, set to default refinement factor rf_default.
2831 for (size_t i=0; i<kvf.size(); ++i)
2832 {
2833 if (kvf[i].Size() == 0)
2834 {
2835 kvf[i].SetSize(knotVectors[i]->GetNE());
2836 kvf[i] = rf_default;
2837 }
2838 else
2839 {
2840 for (int j=0; j<kvf[i].Size(); ++j)
2841 {
2842 if (kvf[i][j] == unsetFactor) { kvf[i][j] = rf_default; }
2843 }
2844 }
2845
2846 if (knotVectors[i]->spacing)
2847 {
2849 (knotVectors[i]->spacing.get());
2850 if (pws)
2851 {
2852 Array<int> pwn = pws->RelativePieceSizes();
2853 const bool rev = pws->GetReverse();
2854 const int np = pwn.Size();
2855 const int f = kvf[i].Size() / pwn.Sum();
2856 MFEM_ASSERT(kvf[i].Size() == f * pwn.Sum(), "");
2857
2858 Array<int> os(np + 1);
2859 os[0] = 0;
2860 for (int j=1; j<np+1; ++j)
2861 {
2862 const int jp = rev ? np - j : j - 1;
2863 os[j] = os[j-1] + (f * pwn[jp]);
2864 }
2865
2866 Array<int> pwf(np);
2867 for (int j=0; j<np; ++j)
2868 {
2869 pwf[j] = kvf[i][os[j]];
2870 for (int r=os[j]+1; r<os[j+1]; ++r)
2871 {
2872 MFEM_ASSERT(kvf[i][r] == pwf[j], "");
2873 }
2874 }
2875
2876 pws->ScalePartition(pwf, true);
2877 }
2878 }
2879 }
2880}
2881
2883{
2884 Array<int> rf(f.Sum());
2885
2886 int os = 0;
2887 for (int i=0; i<f.Size(); ++i)
2888 {
2889 const int f_i = f[i];
2890 for (int j=0; j<f_i; ++j) { rf[os + j] = f_i; }
2891
2892 os += f_i;
2893 }
2894
2895 MFEM_ASSERT(os == rf.Size(), "");
2896
2897 f = rf;
2898}
2899
2901 const std::string &kvf_filename,
2902 bool coarsened)
2903{
2904 if (ref_factors.Size() > 0)
2905 {
2906 MFEM_VERIFY(ref_factors.Size() == Dimension(), "");
2907 for (int i=0; i<ref_factors.Size(); ++i) { ref_factors[i] *= rf; }
2908 }
2909 else
2910 {
2912 ref_factors = rf;
2913 }
2914
2915 LoadFactorsForKV(kvf_filename);
2917
2918 Refine(coarsened);
2919}
2920
2921void NCNURBSExtension::ReadCoarsePatchCP(std::istream &input)
2922{
2923 input >> num_structured_patches;
2924
2925 const int maxOrder = mOrders.Max();
2926
2927 // For degree maxOrder, there are 2*(maxOrder + 1) knots for a single
2928 // element, and the number of control points in each dimension is
2929 // 2*(maxOrder + 1) - maxOrder - 1
2930 const int ncp1D = maxOrder + 1;
2931 const int ncp = static_cast<int>(pow(ncp1D, Dimension()));
2932
2934 for (int p=0; p<num_structured_patches; ++p)
2935 for (int i=0; i<ncp; ++i)
2936 for (int j=0; j<Dimension(); ++j) { input >> patchCP(p, i, j); }
2937}
2938
2939void NCNURBSExtension::PrintCoarsePatches(std::ostream &os)
2940{
2941 const int maxOrder = mOrders.Max();
2942 const int patchCP_size1 = patchCP.GetSize1();
2943 MFEM_VERIFY(patchCP_size1 == num_structured_patches || patchCP_size1 == 0,
2944 "");
2945
2946 if (patchCP_size1 == 0) { return; }
2947
2948 // For degree maxOrder, there are 2*(maxOrder + 1) knots for a single element,
2949 // and the number of control points in each dimension is
2950 // 2*(maxOrder + 1) - maxOrder - 1
2951 const int ncp1D = maxOrder + 1;
2952 const int ncp = static_cast<int>(pow(ncp1D, Dimension()));
2953
2954 os << "\npatch_cp\n" << num_structured_patches << "\n";
2955 for (int p=0; p<num_structured_patches; ++p)
2956 {
2957 for (int i=0; i<ncp; ++i)
2958 {
2959 os << patchCP(p, i, 0);
2960 for (int j=1; j<Dimension(); ++j)
2961 {
2962 os << ' ' << patchCP(p, i, j);
2963 }
2964 os << '\n';
2965 }
2966 }
2967}
2968
2970{
2971 MFEM_ASSERT(f.Size() == c.Sum(), "");
2972 bool consistent = true;
2973 int os = 0;
2974 for (int j=0; j<c.Size(); ++j)
2975 {
2976 const int cf = c[j];
2977 const int ff = f[os];
2978 for (int i=0; i<cf; ++i)
2979 {
2980 if (f[os + i] != ff) { consistent = false; }
2981 }
2982
2983 c[j] *= ff;
2984
2985 os += cf;
2986 }
2987
2988 MFEM_VERIFY(consistent, "");
2989}
2990
2991void NCNURBSExtension::UpdateCoarseKVF()
2992{
2993 if (kvf_coarse.size() == 0) { return; }
2994 for (int k=0; k<NumOfKnotVectors; ++k)
2995 {
2997 }
2998}
2999
3000int GetFaceOrientation(const Mesh *mesh, const int face,
3001 const std::array<int, 4> &verts)
3002{
3003 Array<int> fverts;
3004 mesh->GetFaceVertices(face, fverts);
3005 MFEM_ASSERT(fverts.Size() == 4, "");
3006
3007 // Verify that verts and fvert have the same entries as sets, by deep-copying
3008 // and sorting.
3009 {
3010 Array<int> s1(4);
3011 Array<int> s2(fverts);
3012
3013 for (int i=0; i<4; ++i) { s1[i] = verts[i]; }
3014
3015 s1.Sort(); s2.Sort();
3016 MFEM_ASSERT(s1 == s2, "");
3017 }
3018
3019 // Find the shift of the first vertex.
3020 int s = -1;
3021 for (int i=0; i<4; ++i)
3022 {
3023 if (verts[i] == fverts[0]) { s = i; }
3024 }
3025
3026 // Check whether ordering is reversed.
3027 const bool rev = verts[(s + 1) % 4] != fverts[1];
3028 if (rev) { s = -1 - s; } // Reversed order is encoded by the sign.
3029 return s;
3030}
3031
3032// The 2D array `a` is of size n1*n2, with index j + n2*i corresponding to (i,j)
3033// with the fast index j, for 0 <= i < n1 and 0 <= j < n2. We assume that j is
3034// the fast index in (i,j). The orientation is encoded by ori, defining a shift
3035// and relative direction, such that a quad face F1, on which the ordering of
3036// `a` is based, has vertex with index `shift` matching vertex 0 of the new quad
3037// face F2, on which the new ordering of `a` should be based. For more details,
3038// see GetFaceOrientation.
3039bool Reorder2D(int ori, std::array<int, 2> &s0)
3040{
3041 const int shift = ori < 0 ? -1 - ori : ori;
3042
3043 // Shift is an F1 index in the counter-clockwise ordering of 4 quad vertices.
3044 // Now find the (i,j) indices of this index, with i,j in {0,1}.
3045 const int s0i = (shift == 0 || shift == 3) ? 0 : 1;
3046 const int s0j = (shift < 2) ? 0 : 1;
3047
3048 s0[0] = s0i;
3049 s0[1] = s0j;
3050
3051 // Determine whether the dimensions of F1 and F2 are reversed. Do this by
3052 // finding the (i,j) indices of s1, which is the next vertex on F1.
3053 const int shift1 = ori < 0 ? shift - 1: shift + 1;
3054 const int s1 = (shift1 + 4) % 4;
3055 const int s1i = (s1 == 0 || s1 == 3) ? 0 : 1;
3056 const bool dimReverse = s0i == s1i;
3057
3058 return dimReverse;
3059}
3060
3061void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
3062 int &n)
3063{
3064 const bool rev = (signedShift < 0);
3065 const int shift = rev ? -1 - signedShift : signedShift;
3066 MFEM_ASSERT(0 <= shift && shift < 4, "");
3067
3068 // We consider 8 cases for the possible values of rev and shift.
3069 if (rev)
3070 {
3071 if (shift == 0)
3072 {
3073 // New: 3 2 Old: 1 2
3074 // 0 1 0 3
3075 n = sm;
3076 m = sn;
3077 }
3078 else if (shift == 1)
3079 {
3080 // New: 3 2 Old: 2 3
3081 // 0 1 1 0
3082 m = sm;
3083 n = sn;
3084 }
3085 else if (shift == 2)
3086 {
3087 // New: 3 2 Old: 3 0
3088 // 0 1 2 1
3089 n = sm;
3090 m = sn;
3091 }
3092 else // shift == 3
3093 {
3094 // New: 3 2 Old: 0 1
3095 // 0 1 3 2
3096 m = sm;
3097 n = sn;
3098 }
3099 }
3100 else
3101 {
3102 if (shift == 0)
3103 {
3104 // New: 3 2 Old: 3 2
3105 // 0 1 0 1
3106 m = sm;
3107 n = sn;
3108 }
3109 else if (shift == 1)
3110 {
3111 // New: 3 2 Old: 0 3
3112 // 0 1 1 2
3113 n = sm;
3114 m = sn;
3115 }
3116 else if (shift == 2)
3117 {
3118 // New: 3 2 Old: 1 0
3119 // 0 1 2 3
3120 m = sm;
3121 n = sn;
3122 }
3123 else // shift == 3
3124 {
3125 // New: 3 2 Old: 2 1
3126 // 0 1 3 0
3127 n = sm;
3128 m = sn;
3129 }
3130 }
3131}
3132
3133void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
3134 int& sm, int& sn, int& si, int& sj)
3135{
3136 const bool rev = (signedShift < 0);
3137 const int shift = rev ? -1 - signedShift : signedShift;
3138 MFEM_ASSERT(0 <= shift && shift < 4, "");
3139
3140 // (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
3141 // to new vertex 0 in counter-clockwise quad ordering.
3142
3143 // We consider 8 cases for the possible values of rev and shift.
3144 if (rev)
3145 {
3146 if (shift == 0)
3147 {
3148 // New: 3 2 Old: 1 2
3149 // 0 1 0 3
3150 sm = n;
3151 sn = m;
3152
3153 si = j;
3154 sj = i;
3155 }
3156 else if (shift == 1)
3157 {
3158 // New: 3 2 Old: 2 3
3159 // 0 1 1 0
3160 sm = m;
3161 sn = n;
3162
3163 si = m - 1 - i;
3164 sj = j;
3165 }
3166 else if (shift == 2)
3167 {
3168 // New: 3 2 Old: 3 0
3169 // 0 1 2 1
3170 sm = n;
3171 sn = m;
3172
3173 si = n - 1 - j;
3174 sj = m - 1 - i;
3175 }
3176 else // shift == 3
3177 {
3178 // New: 3 2 Old: 0 1
3179 // 0 1 3 2
3180 sm = m;
3181 sn = n;
3182
3183 si = i;
3184 sj = n - 1 - j;
3185 }
3186 }
3187 else
3188 {
3189 if (shift == 0)
3190 {
3191 // New: 3 2 Old: 3 2
3192 // 0 1 0 1
3193 sm = m;
3194 sn = n;
3195
3196 si = i;
3197 sj = j;
3198 }
3199 else if (shift == 1)
3200 {
3201 // New: 3 2 Old: 0 3
3202 // 0 1 1 2
3203 sm = n;
3204 sn = m;
3205
3206 si = j;
3207 sj = m - 1 - i;
3208 }
3209 else if (shift == 2)
3210 {
3211 // New: 3 2 Old: 1 0
3212 // 0 1 2 3
3213 sm = m;
3214 sn = n;
3215
3216 si = m - 1 - i;
3217 sj = n - 1 - j;
3218 }
3219 else // shift == 3
3220 {
3221 // New: 3 2 Old: 2 1
3222 // 0 1 3 0
3223 sm = n;
3224 sn = m;
3225
3226 si = n - 1 - j;
3227 sj = i;
3228 }
3229 }
3230}
3231
3232// Given a quadruple in q, return the pair (q_i, q_j), where q_i is the minimum
3233// entry of q, and q_j is the entry two indices away from q_i. When q contains
3234// indices of the vertices of a quadrilateral, the returned pair represents the
3235// unique diagonal touching the vertex of minimum index, which is a more concise
3236// way of representing the quadrilateral, facilitating the search of faces.
3237std::pair<int, int> QuadrupleToPair(const std::array<int, 4> &q)
3238{
3239 const auto qmin = std::min_element(q.begin(), q.end());
3240 const int idmin = std::distance(q.begin(), qmin);
3241 return std::pair<int, int>(q[idmin], q[(idmin + 2) % 4]);
3242}
3243
3244void VertexToKnotSpan::SetSize(int dimension, int numVertices)
3245{
3246 dim = dimension;
3247 MFEM_ASSERT((dim == 2 || dim == 3) && numVertices > 0, "Invalid size");
3248 data.SetSize(numVertices, dim == 3 ? 7 : 4);
3249}
3250
3251void VertexToKnotSpan::SetVertex2D(int index, int v, int ks,
3252 const std::array<int, 2> &pv)
3253{
3254 data(index,0) = v;
3255 data(index,1) = ks;
3256 data(index,2) = pv[0];
3257 data(index,3) = pv[1];
3258}
3259
3261 const std::array<int, 2> &ks,
3262 const std::array<int, 4> &pv)
3263{
3264 data(index,0) = v;
3265 data(index,1) = ks[0];
3266 data(index,2) = ks[1];
3267 data(index,3) = pv[0];
3268 data(index,4) = pv[1];
3269 data(index,5) = pv[2];
3270 data(index,6) = pv[3];
3271}
3272
3274{
3275 data(index,1) = ks;
3276}
3277
3278void VertexToKnotSpan::SetKnotSpans3D(int index, const std::array<int, 2> &ks)
3279{
3280 data(index,1) = ks[0];
3281 data(index,2) = ks[1];
3282}
3283
3284void VertexToKnotSpan::GetVertex2D(int index, int &v, int &ks,
3285 std::array<int, 2> &pv) const
3286{
3287 v = data(index,0);
3288 ks = data(index,1);
3289 pv[0] = data(index,2);
3290 pv[1] = data(index,3);
3291}
3292
3293void VertexToKnotSpan::GetVertex3D(int index, int &v, std::array<int, 2> &ks,
3294 std::array<int, 4> &pv) const
3295{
3296 v = data(index,0);
3297 ks[0] = data(index,1);
3298 ks[1] = data(index,2);
3299 pv[0] = data(index,3);
3300 pv[1] = data(index,4);
3301 pv[2] = data(index,5);
3302 pv[3] = data(index,6);
3303}
3304
3305void VertexToKnotSpan::Print(std::ostream &os) const
3306{
3307 const int nv = data.NumRows();
3308 const int m = data.NumCols();
3309 os << nv << "\n";
3310 for (int i = 0; i < nv; i++)
3311 {
3312 os << data(i,0);
3313 for (int j = 1; j < m; j++)
3314 {
3315 os << " " << data(i,j);
3316 }
3317 os << "\n";
3318 }
3319}
3320
3321std::pair<int, int> VertexToKnotSpan::GetVertexParentPair(int index) const
3322{
3323 if (dim == 3)
3324 {
3325 std::array<int, 4> pv;
3326 for (int i=0; i<4; ++i) { pv[i] = data(index, 3 + i); }
3327 // The face with vertices (pv[0], pv[1], pv[2], pv[3]) is defined as a
3328 // parent face.
3329 return QuadrupleToPair(pv);
3330 }
3331
3332 int c0 = data(index, 2);
3333 int c1 = data(index, 3);
3334 if (c0 > c1) { std::swap(c0, c1); }
3335 return std::pair<int, int>(c0, c1);
3336}
3337
3339{
3340 MFEM_VERIFY(!nonconformingPT,
3341 "NURBS NC-patch meshes cannot use this method of refinement");
3342
3343 if (ref_factors.Size())
3344 {
3345 MFEM_VERIFY(ref_factors.Size() == rf.Size(), "");
3346 for (int i=0; i<rf.Size(); ++i) { ref_factors[i] *= rf[i]; }
3347 }
3348 else
3349 {
3350 ref_factors = rf;
3351 }
3352
3353 Refine(false, &rf);
3354}
3355
3356void NCNURBSExtension::Refine(bool coarsened, const Array<int> *rf)
3357{
3358 const int maxOrder = mOrders.Max();
3359 const int dim = Dimension();
3360
3361 for (int p = 0; p < patches.Size(); p++)
3362 {
3363 if (nonconformingPT)
3364 {
3365 std::vector<Array<int>> prf(dim);
3367 Array<int> edges, orient;
3368 patchTopo->GetElementEdges(p, edges, orient);
3369
3370 if (dim == 3)
3371 {
3372 constexpr char e3[3] = {0, 3, 8};
3373 for (int i=0; i<3; ++i)
3374 {
3375 prf[i] = kvf[KnotInd(edges[e3[i]])];
3376 pkv[i] = knotVectors[KnotInd(edges[e3[i]])];
3377 }
3378 }
3379 else
3380 {
3381 MFEM_VERIFY(dim == 2, "");
3382 for (int i=0; i<2; ++i)
3383 {
3384 prf[i] = kvf[KnotInd(edges[i])];
3385 pkv[i] = knotVectors[KnotInd(edges[i])];
3386 }
3387 }
3388
3390 {
3391 for (int i=0; i<dim; ++i)
3392 {
3393 // Collapse prf[i] to a single factor
3394 MFEM_VERIFY(prf[i].IsConstant(), "");
3395 prf[i].SetSize(1);
3396 }
3397 }
3398
3399 patches[p]->UpdateSpacingPartitions(pkv);
3400 patches[p]->UniformRefinement(prf, coarsened, maxOrder);
3401 }
3402 else
3403 {
3404 patches[p]->UniformRefinement(*rf);
3405 }
3406 }
3407
3408 if (nonconformingPT)
3409 {
3411 UpdateAuxiliaryKnotSpans(ref_factors);
3412 UpdateCoarseKVF();
3413 }
3414}
3415
3416void NCNURBSExtension::SetDofToPatch()
3417{
3419 dof2patch = -1;
3420
3421 const int dim = Dimension();
3422 if (dim == 1) { return; }
3423
3424 Array<int> edges, faces, orient;
3425 const int np = patchTopo->GetNE();
3426
3427 for (int p = 0; p < np; p++)
3428 {
3429 patchTopo->GetElementEdges(p, edges, orient);
3430 for (auto e : edges)
3431 {
3432 if (masterEdges.count(e) > 0)
3433 {
3434 Array<int> mdof;
3435 GetMasterEdgeDofs(true, e, mdof);
3436 for (auto dof : mdof) { dof2patch[dof] = p; }
3437 }
3438 }
3439
3440 if (dim == 3)
3441 {
3442 patchTopo->GetElementFaces(p, faces, orient);
3443
3444 for (auto f : faces)
3445 {
3446 if (masterFaces.count(f) > 0)
3447 {
3448 Array2D<int> mdof;
3449 GetMasterFaceDofs(true, f, mdof);
3450 for (int j=0; j<mdof.NumCols(); ++j)
3451 for (int k=0; k<mdof.NumRows(); ++k)
3452 {
3453 dof2patch[mdof(k,j)] = p;
3454 }
3455 }
3456 }
3457 }
3458 }
3459}
3460
3461// This function assumes a uniform number of control points per element in kv.
3463{
3464 const int ne = kv->GetNE();
3465
3466 // Total number of CP on edge, excluding vertex CP.
3467 const int totalEdgeCP = kv->GetNCP() - 2 - ne + 1;
3468 const int perEdgeCP = totalEdgeCP / ne;
3469
3470 MFEM_VERIFY(perEdgeCP * ne == totalEdgeCP, "");
3471
3472 return perEdgeCP;
3473}
3474
3476{
3477 const int nv = patchTopo->GetNV();
3478 const int ne = patchTopo->GetNEdges();
3479 const int nf = patchTopo->GetNFaces();
3480 const int np = patchTopo->GetNE();
3481 int meshCounter, spaceCounter, dim = Dimension();
3482
3483 std::set<int> reversedParents;
3484 if (patchTopo->ncmesh)
3485 {
3486 // Note that master or slave entities exist only for a mesh with
3487 // vertex_parents, not for the vertex_to_knotspan case. Currently, a mesh
3488 // is not allowed to have both cases, see the MFEM_VERIFY below.
3489
3490 const NCMesh::NCList& nce = patchTopo->ncmesh->GetNCList(1);
3491 const NCMesh::NCList& ncf = patchTopo->ncmesh->GetNCList(2);
3492
3493 masterEdges.clear();
3494 masterFaces.clear();
3495 slaveEdges.clear();
3496 slaveFaces.clear();
3497 masterEdgeToId.clear();
3498 masterFaceToId.clear();
3499
3500 MFEM_VERIFY(nce.masters.Size() > 0 ||
3502 MFEM_VERIFY(!(nce.masters.Size() > 0 &&
3503 patchTopo->ncmesh->GetVertexToKnotSpan().Size() > 0), "");
3504
3505 std::vector<EdgePairInfo> edgePairs;
3506 std::vector<FacePairInfo> facePairs;
3507 std::vector<int> parentFaces, parentVerts;
3508 std::vector<std::array<int, 2>> parentSize;
3509
3510 const bool is3D = dim == 3;
3511
3512 std::map<std::pair<int, int>, int> v2f;
3513
3515 {
3516 // Intersections of master edges may not be edges in patchTopo->ncmesh,
3517 // so we represent them in auxEdges, to account for their vertices and
3518 // DOFs.
3519 {
3520 int vert_index[2];
3522 for (auto edgeID : EL.conforming)
3523 {
3524 patchTopo->ncmesh->GetEdgeVertices(edgeID, vert_index);
3525 v2e[std::pair<int, int> (vert_index[0], vert_index[1])] = edgeID.index;
3526 }
3527 }
3528
3529 if (is3D)
3530 {
3531 Array<int> vert;
3532 for (int i=0; i<patchTopo->GetNumFaces(); ++i)
3533 {
3534 patchTopo->GetFaceVertices(i, vert);
3535 const int vmin = vert.Min();
3536 const int idmin = vert.Find(vmin);
3537 v2f[std::pair<int, int> (vert[idmin], vert[(idmin + 2) % 4])] = i;
3538 }
3539 }
3540
3542
3543 if (is3D)
3544 ProcessVertexToKnot3D(v2k, v2f, parentSize, edgePairs,
3545 facePairs, parentFaces, parentVerts);
3546 else
3547 {
3548 ProcessVertexToKnot2D(v2k, reversedParents, edgePairs);
3549 }
3550 } // if using vertex_to_knotspan
3551
3552 const int numMasters = is3D ? ncf.masters.Size() : nce.masters.Size();
3553
3554 if (is3D)
3555 {
3556 for (auto masterFace : ncf.masters)
3557 {
3558 masterFaces.insert(masterFace.index);
3559 }
3560 }
3561
3562 for (auto masterEdge : nce.masters)
3563 {
3564 masterEdges.insert(masterEdge.index);
3565 }
3566
3567 masterEdgeIndex.SetSize(masterEdges.size());
3568 int cnt = 0;
3569 for (auto medge : masterEdges)
3570 {
3571 masterEdgeIndex[cnt] = medge;
3572 masterEdgeToId[medge] = cnt;
3573 cnt++;
3574 }
3575 MFEM_VERIFY(cnt == masterEdgeIndex.Size(), "");
3576
3577 Array<int> masterFaceIndex(parentFaces.size());
3578
3579 // Note that masterFaces is a subset of parentFaces.
3580 MFEM_VERIFY(masterFaces.size() <= parentFaces.size(), "");
3581
3582 cnt = 0;
3583 for (auto mface : parentFaces)
3584 {
3585 masterFaceIndex[cnt] = mface;
3586 masterFaceToId[mface] = cnt;
3587 cnt++;
3588 }
3589
3590 MFEM_VERIFY(cnt == masterFaceIndex.Size(), "");
3591
3592 masterEdgeInfo.clear();
3593 masterEdgeInfo.resize(masterEdgeIndex.Size());
3594
3595 masterFaceInfo.clear();
3596 masterFaceInfo.resize(masterFaceIndex.Size());
3597
3599 {
3600 // Note that this is used in 2D and 3D.
3601 const int npairs = edgePairs.size();
3602
3603 for (int i=0; i<npairs; ++i)
3604 {
3605 if (!edgePairs[i].isSet) { continue; }
3606
3607 const int v = edgePairs[i].v;
3608 const int s = edgePairs[i].child;
3609 const int m = edgePairs[i].parent;
3610 const int ksi = edgePairs[i].ksi;
3611
3612 slaveEdges.push_back(s);
3613
3614 const int mid = masterEdgeToId[m];
3615 const int si = slaveEdges.size() - 1;
3616 masterEdgeInfo[mid].slaves.push_back(si);
3617 if (v >= 0)
3618 {
3619 masterEdgeInfo[mid].vertices.push_back(v);
3620 masterEdgeInfo[mid].ks.push_back(ksi);
3621 }
3622 }
3623
3624 ProcessFacePairs(0, 0, parentSize, parentVerts, facePairs);
3625 }
3626
3627 for (int i=0; i<nce.slaves.Size(); ++i)
3628 {
3629 const NCMesh::Slave& slaveEdge = nce.slaves[i];
3630 int vert_index[2];
3631 patchTopo->ncmesh->GetEdgeVertices(slaveEdge, vert_index);
3632 slaveEdges.push_back(slaveEdge.index);
3633
3634 const int mid = masterEdgeToId[slaveEdge.master];
3635 masterEdgeInfo[mid].slaves.push_back(i);
3636 }
3637
3638 if (!is3D)
3639 {
3640 for (int m=0; m<numMasters; ++m)
3641 {
3642 // Order the slaves of each master edge, from the first to second
3643 // vertex of the master edge.
3644 const int numSlaves = masterEdgeInfo[m].slaves.size();
3645 MFEM_ASSERT(numSlaves > 0, "");
3646 int mvert[2];
3647 int svert[2];
3648 patchTopo->ncmesh->GetEdgeVertices(nce.masters[m], mvert);
3649
3650 std::vector<int> orderedSlaves(numSlaves);
3651 std::set<int> used;
3652
3653 int vi = mvert[0];
3654 for (int s=0; s<numSlaves; ++s)
3655 {
3656 // Find the slave edge containing vertex vi.
3657 // This has quadratic complexity, but numSlaves is small.
3658 orderedSlaves[s] = -1;
3659 for (int t=0; t<numSlaves; ++t)
3660 {
3661 const int sid = masterEdgeInfo[m].slaves[t];
3662 if (used.count(sid) > 0) { continue; }
3663 patchTopo->ncmesh->GetEdgeVertices(nce.slaves[sid], svert);
3664 if (svert[0] == vi || svert[1] == vi)
3665 {
3666 orderedSlaves[s] = sid;
3667 used.insert(sid);
3668 break;
3669 }
3670 }
3671
3672 MFEM_ASSERT(orderedSlaves[s] >= 0, "");
3673
3674 // Update vi to the next vertex
3675 vi = (svert[0] == vi) ? svert[1] : svert[0];
3676
3677 if (s < numSlaves - 1)
3678 {
3679 masterEdgeInfo[m].vertices.push_back(vi);
3680 masterEdgeInfo[m].ks.push_back(-1); // Used only in 3D.
3681 }
3682 }
3683
3684 masterEdgeInfo[m].slaves = orderedSlaves;
3685 } // m
3686 }
3687
3688 if (is3D)
3689 {
3690 // Remove edges from masterEdges if they do not have any slave edges.
3691 std::vector<int> falseMasterEdges;
3692 for (auto me : masterEdges)
3693 {
3694 const int mid = masterEdgeToId.at(me);
3695 if (masterEdgeInfo[mid].slaves.size() <= 1)
3696 {
3697 falseMasterEdges.push_back(me);
3698 }
3699 }
3700
3701 for (auto me : falseMasterEdges) { masterEdges.erase(me); }
3702
3703 // Find slave and auxiliary faces not yet defined.
3704 const int nfp0 = facePairs.size();
3705 std::set<int> addParentFaces;
3706 FindAdditionalFacesSA(v2f, addParentFaces, facePairs);
3707
3708 cnt = parentFaces.size();
3709 const int npf0 = cnt;
3710
3711 for (auto pf : addParentFaces)
3712 {
3713 if (masterFaces.count(pf) == 0)
3714 {
3715 masterFaces.insert(pf);
3716 masterFaceIndex.Append(pf);
3717
3718 masterFaceToId[pf] = cnt;
3719 cnt++;
3720
3721 {
3722 Array<int> edges, ori, verts;
3723 patchTopo->GetFaceEdges(pf, edges, ori);
3724 patchTopo->GetFaceVertices(pf, verts);
3725 MFEM_ASSERT(edges.Size() == 4 && verts.Size() == 4, "");
3726
3727 parentSize.emplace_back(std::array<int, 2>
3728 {
3729 KnotVec(edges[0])->GetNE(),
3730 KnotVec(edges[1])->GetNE()
3731 });
3732
3733 masterFaceInfo.push_back(
3734 MasterFaceInfo(KnotVec(edges[0])->GetNE(),
3735 KnotVec(edges[1])->GetNE()));
3736
3737 for (int i=0; i<4; ++i) { parentVerts.push_back(verts[i]); }
3738 }
3739 }
3740 }
3741
3742 MFEM_VERIFY(cnt == masterFaceIndex.Size(), "");
3743
3744 ProcessFacePairs(nfp0, npf0, parentSize, parentVerts, facePairs);
3745 }
3746 }
3747
3748 for (auto rp : reversedParents)
3749 {
3750 masterEdgeInfo[masterEdgeToId[rp]].Reverse();
3751 }
3752
3753 Array<int> edges, orient;
3754
3759
3764
3765 // Get vertex offsets
3766 for (meshCounter = 0; meshCounter < nv; meshCounter++)
3767 {
3768 v_meshOffsets[meshCounter] = meshCounter;
3769 v_spaceOffsets[meshCounter] = meshCounter;
3770 }
3771 spaceCounter = meshCounter;
3772
3773 // Get edge offsets
3774 for (int e = 0; e < ne; e++)
3775 {
3776 e_meshOffsets[e] = meshCounter;
3777 e_spaceOffsets[e] = spaceCounter;
3778
3779 if (masterEdges.count(e) == 0) // If not a master edge
3780 {
3781 meshCounter += KnotVec(e)->GetNE() - 1;
3782 spaceCounter += KnotVec(e)->GetNCP() - 2;
3783 }
3784 }
3785
3786 const int nauxe = auxEdges.size();
3787 aux_e_meshOffsets.SetSize(nauxe + 1);
3788 aux_e_spaceOffsets.SetSize(nauxe + 1);
3789 for (int e = 0; e < nauxe; e++)
3790 {
3791 aux_e_meshOffsets[e] = meshCounter;
3792 aux_e_spaceOffsets[e] = spaceCounter;
3793
3794 // Find the number of elements and CP in this auxiliary edge, which is
3795 // defined only on part of the master edge knotvector.
3796 const int signedParentEdge = auxEdges[e].parent;
3797 const int ki0 = auxEdges[e].ksi[0];
3798 const int ki1raw = auxEdges[e].ksi[1];
3799 const bool rev = signedParentEdge < 0;
3800 const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
3801 const int masterNE = KnotVec(parentEdge)->GetNE();
3802 const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
3803 const int perEdgeCP = GetNCPperEdge(KnotVec(e));
3804 const int auxne = ki1 - ki0;
3805 MFEM_ASSERT(auxne > 0, "");
3806 meshCounter += auxne - 1;
3807 spaceCounter += (auxne * perEdgeCP) + auxne - 1;
3808 }
3809
3810 aux_e_meshOffsets[nauxe] = meshCounter;
3811 aux_e_spaceOffsets[nauxe] = spaceCounter;
3812
3813 // Get face offsets
3814 for (int f = 0; f < nf; f++)
3815 {
3816 f_meshOffsets[f] = meshCounter;
3817 f_spaceOffsets[f] = spaceCounter;
3818
3819 if (masterFaces.count(f) == 0) // If not a master face
3820 {
3821 patchTopo->GetFaceEdges(f, edges, orient);
3822
3823 meshCounter +=
3824 (KnotVec(edges[0])->GetNE() - 1) *
3825 (KnotVec(edges[1])->GetNE() - 1);
3826 spaceCounter +=
3827 (KnotVec(edges[0])->GetNCP() - 2) *
3828 (KnotVec(edges[1])->GetNCP() - 2);
3829 }
3830 }
3831
3832 const int nauxf = auxFaces.size();
3833 aux_f_meshOffsets.SetSize(nauxf + 1);
3834 aux_f_spaceOffsets.SetSize(nauxf + 1);
3835 for (int f = 0; f < nauxf; f++)
3836 {
3837 aux_f_meshOffsets[f] = meshCounter;
3838 aux_f_spaceOffsets[f] = spaceCounter;
3839
3840 const int parentFace = auxFaces[f].parent;
3841 patchTopo->GetFaceEdges(parentFace, edges, orient);
3842
3843 // Number of control points per edge, in first and second directions.
3844 const int perEdgeCP0 = GetNCPperEdge(KnotVec(edges[0]));
3845 const int perEdgeCP1 = GetNCPperEdge(KnotVec(edges[1]));
3846
3847 const int auxne0 = auxFaces[f].ksi1[0] - auxFaces[f].ksi0[0];
3848 const int auxne1 = auxFaces[f].ksi1[1] - auxFaces[f].ksi0[1];
3849 meshCounter += (auxne0 - 1) * (auxne1 - 1);
3850 spaceCounter += ((auxne0 * perEdgeCP0) + auxne0 - 1) *
3851 ((auxne1 * perEdgeCP1) + auxne1 - 1);
3852 }
3853
3854 aux_f_meshOffsets[nauxf] = meshCounter;
3855 aux_f_spaceOffsets[nauxf] = spaceCounter;
3856
3857 // Get patch offsets
3858 GetPatchOffsets(meshCounter, spaceCounter);
3859
3860 NumOfVertices = meshCounter;
3861 NumOfDofs = spaceCounter;
3862
3863 SetDofToPatch();
3864}
3865
3866} // namespace mfem
Dynamic 2D array using row-major layout.
Definition array.hpp:430
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.hpp:534
int NumCols() const
Definition array.hpp:448
int NumRows() const
Definition array.hpp:447
void SetSize(int m, int n)
Set the 2D array size to m x n.
Definition array.hpp:445
int GetSize1() const
Get the 3D array size in the first dimension.
Definition array.hpp:557
void SetSize(int n1, int n2, int n3)
Set the 3D array size to n1 x n2 x n3.
Definition array.hpp:553
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:53
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:312
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:184
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
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:166
int Find(const T &el) const
Return the first index where 'el' is found; return -1 if not found.
Definition array.hpp:971
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:912
T Sum() const
Return the sum of all the array entries using the '+'' operator for class 'T'.
Definition array.cpp:129
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:86
int GetNE() const
Return the number of elements, defined by distinct knots.
Definition nurbs.hpp:83
Mesh data type.
Definition mesh.hpp:65
void GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:7642
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:6658
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1383
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6846
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1386
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1293
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:7835
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1374
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7672
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1627
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:313
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:7588
Table * GetFaceToElementTable() const
Definition mesh.cpp:7801
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:3475
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:3338
void PropagateFactorsForKV(int rf_default)
Ensure consistent refinement factors on all knotvectors.
Definition ncnurbs.cpp:2617
void RefineWithKVFactors(int rf, const std::string &kvf_filename, bool coarsened) override
Definition ncnurbs.cpp:2900
void LoadFactorsForKV(const std::string &filename)
Load refinement factors for a list of knotvectors from file.
Definition ncnurbs.cpp:2348
int SetPatchFactors(int p)
Set consistent refinement factors on patch p.
Definition ncnurbs.cpp:2486
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:474
std::vector< Array< int > > kvf_coarse
Definition nurbs.hpp:580
Mesh * patchTopo
Patch topology mesh.
Definition nurbs.hpp:513
Array< int > ref_factors
Knotvector refinement factors.
Definition nurbs.hpp:582
Array< int > p_meshOffsets
Definition nurbs.hpp:541
Array< int > mOrders
Orders of all KnotVectors.
Definition nurbs.hpp:495
int num_structured_patches
Whether patchTopo is a nonconforming mesh.
Definition nurbs.hpp:576
Array3D< double > patchCP
Number of structured patches.
Definition nurbs.hpp:578
static constexpr int unsetFactor
Refinement factors in each dimension.
Definition nurbs.hpp:584
int KnotInd(int edge) const
Return the unsigned index of the KnotVector for edge edge.
Definition nurbs.hpp:1290
KnotVector * KnotVec(int edge)
DOF to owning patch map in SetSolutionVector()
Definition nurbs.hpp:1301
Array< int > v_meshOffsets
Global mesh offsets, meshOffsets == meshVertexOffsets.
Definition nurbs.hpp:538
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:5378
Array< int > f_spaceOffsets
Definition nurbs.hpp:546
int NumOfVertices
Global entity counts.
Definition nurbs.hpp:501
Array< NURBSPatch * > patches
Array of all patches in the mesh.
Definition nurbs.hpp:567
virtual bool IsMasterEdge(int edge) const
Return true if edge is a master NC-patch edge.
Definition nurbs.hpp:721
void GetPatchOffsets(int &meshCounter, int &spaceCounter)
Helper function for GenerateOffsets().
Definition nurbs.cpp:3748
void Load(std::istream &input, bool spacing)
Load data from file (used by constructor).
Definition nurbs.cpp:2277
int NumOfKnotVectors
Number of unique (not comprehensive) KnotVectors.
Definition nurbs.hpp:498
Array< int > edge_to_ukv
Map from patchTopo edge indices to unique KnotVector indices.
Definition nurbs.hpp:519
std::vector< Array< int > > kvf
Control points for coarse structured patches.
Definition nurbs.hpp:580
int KnotVecNE(int edge) const
Return the number of knotvector elements for edge edge.
Definition nurbs.hpp:1327
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:5383
virtual bool IsMasterFace(int face) const
Return true if face is a master NC-patch face.
Definition nurbs.hpp:724
Array< int > e_meshOffsets
Definition nurbs.hpp:539
int KnotSign(int edge) const
Return the sign (orientation) of the KnotVector for edge edge.
Definition nurbs.hpp:1296
Array< int > dof2patch
Unset refinement factor value.
Definition nurbs.hpp:586
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:821
Array< int > f_meshOffsets
Definition nurbs.hpp:540
Array< KnotVector * > knotVectors
Set of unique KnotVectors.
Definition nurbs.hpp:522
Array< int > p_spaceOffsets
Definition nurbs.hpp:547
Array< int > v_spaceOffsets
Global space offsets, spaceOffsets == dofOffsets.
Definition nurbs.hpp:544
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:846
Array< int > e_spaceOffsets
Definition nurbs.hpp:545
Piecewise spacing function, with spacing functions defining spacing within arbitarily many fixed subi...
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 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:3293
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:3251
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:3260
void SetSize(int dimension, int numVertices)
Set the spatial dimension and number of vertices.
Definition ncnurbs.cpp:3244
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:3284
void Print(std::ostream &os) const
Print all the data.
Definition ncnurbs.cpp:3305
void SetKnotSpans3D(int index, const std::array< int, 2 > &ks)
Set the knot-span indices for a vertex in 3D.
Definition ncnurbs.cpp:3278
std::pair< int, int > GetVertexParentPair(int index) const
Return the vertex pair representing the parent edge (2D) or face (3D).
Definition ncnurbs.cpp:3321
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:3273
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:3133
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
void UpdateFactors(Array< int > &f)
Definition ncnurbs.cpp:2882
int GetNCPperEdge(const KnotVector *kv)
Definition ncnurbs.cpp:3462
bool ConsistentlySetEntry(int v, int &e)
Definition ncnurbs.cpp:843
void ApplyFineToCoarse(const Array< int > &f, Array< int > &c)
Definition ncnurbs.cpp:2969
void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m, int &n)
Definition ncnurbs.cpp:3061
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:3237
bool Reorder2D(int ori, std::array< int, 2 > &s0)
Definition ncnurbs.cpp:3039
void RemapKnotIndex(bool rev, const Array< int > &rf, int &k)
Definition ncnurbs.cpp:2302
int GetFaceOrientation(const Mesh *mesh, const int face, const std::array< int, 4 > &verts)
Definition ncnurbs.cpp:3000
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:2469
real_t p(const Vector &x, real_t t)
bool IsBoundary() const
Return true if the face is a boundary face.
Definition mesh.hpp:2122
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