MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
nurbs.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 "nurbs.hpp"
13
14#include "point.hpp"
15#include "segment.hpp"
16#include "quadrilateral.hpp"
17#include "hexahedron.hpp"
18#include "../fem/gridfunc.hpp"
19#include "../general/text.hpp"
20
21#include <fstream>
22#include <algorithm>
23#if defined(_MSC_VER) && (_MSC_VER < 1800)
24#include <float.h>
25#define copysign _copysign
26#endif
27
28namespace mfem
29{
30
31using namespace std;
32
33const int KnotVector::MaxOrder = 10;
34
36{
37 input >> Order >> NumOfControlPoints;
38
39 knot.Load(input, NumOfControlPoints + Order + 1);
41 coarse = false;
42}
43
44KnotVector::KnotVector(int order, int NCP)
45{
46 Order = order;
49 NumOfElements = 0;
50 coarse = false;
51
52 knot = -1.;
53}
54
55KnotVector::KnotVector(int order, const Vector& intervals,
56 const Array<int>& continuity)
57{
58 // NOTE: This may need to be generalized to support periodicity
59 // in the future.
60 MFEM_ASSERT(continuity.Size() == (intervals.Size() + 1),
61 "Incompatible sizes of continuity and intervals.");
62 Order = order;
63 const int num_knots = Order * continuity.Size() - continuity.Sum();
64 // Some continuities may still be invalid; this assert only avoids
65 // passing a negative num_knots to Vector::SetSize().
66 MFEM_ASSERT(num_knots >= 0, "Invalid continuity vector for order.");
67 NumOfControlPoints = num_knots - Order - 1;
68 knot.SetSize(num_knots);
69 real_t accum = 0.0;
70 int iknot = 0;
71 for (int i = 0; i < continuity.Size(); ++i)
72 {
73 const int multiplicity = Order - continuity[i];
74 MFEM_ASSERT(multiplicity >= 1 && multiplicity <= Order+1,
75 "Invalid knot multiplicity for order.");
76 for (int j = 0; j < multiplicity; ++j)
77 {
78 knot[iknot] = accum;
79 ++iknot;
80 }
81 if (i < intervals.Size()) { accum += intervals[i]; }
82 }
83 // Assert that there are enough knots to provide a complete basis over all
84 // the elements in the knot vector.
85 MFEM_ASSERT(knot.Size() >= (2*(Order+1)),
86 "Insufficient number of knots to define NURBS.");
87 // Calculate the number of elements provided by the knot vector
88 NumOfElements = 0;
89 for (int i = 0; i < GetNKS(); ++i)
90 {
91 if (isElement(i))
92 {
94 }
95 }
96 coarse = false;
97}
98
100{
101 Order = kv.Order;
104 knot = kv.knot;
105 coarse = kv.coarse;
106 if (kv.spacing) { spacing = kv.spacing->Clone(); }
107
108 return *this;
109}
110
112{
113 if (t < 0)
114 {
115 mfem_error("KnotVector::DegreeElevate :\n"
116 " Parent KnotVector order higher than child");
117 }
118
119 const int nOrder = Order + t;
120 KnotVector *newkv = new KnotVector(nOrder, GetNCP() + t);
121
122 for (int i = 0; i <= nOrder; i++)
123 {
124 (*newkv)[i] = knot(0);
125 }
126 for (int i = nOrder + 1; i < newkv->GetNCP(); i++)
127 {
128 (*newkv)[i] = knot(i - t);
129 }
130 for (int i = 0; i <= nOrder; i++)
131 {
132 (*newkv)[newkv->GetNCP() + i] = knot(knot.Size()-1);
133 }
134
135 newkv->GetElements();
136
137 return newkv;
138}
139
140void KnotVector::UniformRefinement(Vector &new_knots, int rf) const
141{
142 MFEM_VERIFY(rf > 1, "Refinement factor must be at least 2.");
143
144 const real_t h = 1.0 / ((real_t) rf);
145
146 new_knots.SetSize(NumOfElements * (rf - 1));
147 int j = 0;
148 for (int i = 0; i < knot.Size()-1; i++)
149 {
150 if (knot(i) != knot(i+1))
151 {
152 for (int m = 1; m < rf; ++m)
153 {
154 new_knots(j) = ((1.0 - (m * h)) * knot(i)) + (m * h * knot(i+1));
155 j++;
156 }
157 }
158 }
159}
160
162{
163 if (spacing)
164 {
165 if (spacing->Nested())
166 {
167 return 1;
168 }
169 else
170 {
171 return spacing->Size(); // Coarsen only if non-nested
172 }
173 }
174 else
175 {
176 return 1;
177 }
178}
179
181{
182 Vector fine;
183 if (cf < 2) { return fine; }
184
185 const int cne = NumOfElements / cf; // Coarse number of elements
186 MFEM_VERIFY(cne > 0 && cne * cf == NumOfElements, "Invalid coarsening factor");
187
188 fine.SetSize(cne * (cf - 1));
189
190 int fcnt = 0;
191 int i = Order;
192 real_t kprev = knot(Order);
193 int ifine0 = 0;
194 for (int c=0; c<cne; ++c) // Loop over coarse elements
195 {
196 int cnt = 0;
197 while (cnt < cf)
198 {
199 i++;
200 if (knot(i) != kprev)
201 {
202 kprev = knot(i);
203 cnt++;
204 if (cnt < cf)
205 {
206 if (fcnt == 0) { ifine0 = i; }
207 fine[fcnt] = knot(i);
208 fcnt++;
209 }
210 }
211 }
212 }
213
214 MFEM_VERIFY(fcnt == fine.Size(), "");
215
216 // Find the multiplicity of each fine knot
217 Array<int> mlt(fine.Size());
218 mlt = 1;
219
220 for (int j=ifine0+1, ifine=0; j<knot.Size(); ++j)
221 {
222 if (knot(j) == fine(ifine))
223 {
224 mlt[ifine]++;
225 }
226 else
227 {
228 ifine++;
229 if (ifine == fine.Size()) { break; }
230 }
231 }
232
233 Vector mfine(mlt.Sum());
234
235 MFEM_VERIFY(mlt.Sum() == fine.Size() * mlt[0], "");
236
237 for (i=0; i<fine.Size(); ++i)
238 {
239 for (int j=0; j<mlt[0]; ++j)
240 {
241 mfine[(fine.Size() * j) + i] = fine[i];
242 }
243 }
244
245 return mfine;
246}
247
248void KnotVector::Refinement(Vector &new_knots, int rf) const
249{
250 MFEM_VERIFY(rf > 1, "Refinement factor must be at least 2.");
251
252 if (spacing)
253 {
254 spacing->ScaleParameters(1.0 / ((real_t) rf));
255 spacing->SetSize(rf * NumOfElements);
256
257 Vector s;
258 spacing->EvalAll(s);
259
260 new_knots.SetSize(s.Size() - NumOfElements);
261
262 const real_t k0 = knot(0);
263 const real_t k1 = knot(knot.Size() - 1);
264
265 Array<int> span0(NumOfElements + 1);
266 span0[0] = 0;
267
268 int j = 1;
269 for (int i = 0; i < knot.Size() - 1; i++)
270 {
271 if (knot(i) != knot(i+1))
272 {
273 span0[j] = i+1;
274 j++;
275 }
276 }
277
278 MFEM_VERIFY(j == NumOfElements + 1, "Incorrect number of knot spans");
279
280 real_t s0 = 0.0;
281
282 int os = 0;
283 int os1 = 0;
284 for (int i=0; i<NumOfElements; ++i)
285 {
286 // Note that existing coarse knots are not modified here according to
287 // the spacing formula, because modifying them will not produce a
288 // correctly spaced mesh without also updating control points. Here, we
289 // only define new knots according to the spacing formula. Non-nested
290 // refinement should be done by using a single element per patch and
291 // a sufficiently large refinement factor to produce the desired mesh
292 // with only one refinement.
293
294 s0 += s[os];
295
296 for (j = 0; j < rf - 1; ++j)
297 {
298 // Define a new knot between the coarse knots
299 new_knots(os1 + j) = ((1.0 - s0) * k0) + (s0 * k1);
300 s0 += s[os + j + 1];
301 }
302
303 os += rf;
304 os1 += rf - 1;
305 }
306 }
307 else
308 {
309 UniformRefinement(new_knots, rf);
310 }
311}
312
314{
315 NumOfElements = 0;
316 for (int i = Order; i < NumOfControlPoints; i++)
317 {
318 if (knot(i) != knot(i+1))
319 {
321 }
322 }
323}
324
326{
327 real_t apb = knot(0) + knot(knot.Size()-1);
328
329 int ns = (NumOfControlPoints - Order)/2;
330 for (int i = 1; i <= ns; i++)
331 {
332 real_t tmp = apb - knot(Order + i);
333 knot(Order + i) = apb - knot(NumOfControlPoints - i);
334 knot(NumOfControlPoints - i) = tmp;
335 }
336
337 if (spacing) { spacing->Flip(); }
338}
339
340void KnotVector::Print(std::ostream &os) const
341{
342 os << Order << ' ' << NumOfControlPoints << ' ';
343 knot.Print(os, knot.Size());
344}
345
346void KnotVector::PrintFunctions(std::ostream &os, int samples) const
347{
348 MFEM_VERIFY(GetNE(), "Elements not counted. Use GetElements().");
349
350 Vector shape(Order+1);
351
352 real_t x, dx = 1.0/real_t (samples - 1);
353
354 /* @a cnt is a counter including elements between repeated knots if
355 present. This is required for usage of CalcShape. */
356 int cnt = 0;
357
358 for (int e = 0; e < GetNE(); e++, cnt++)
359 {
360 // Avoid printing shapes between repeated knots
361 if (!isElement(cnt)) { e--; continue; }
362
363 for (int j = 0; j <samples; j++)
364 {
365 x = j*dx;
366 os << x + e;
367
368 CalcShape(shape, cnt, x);
369 for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
370
371 CalcDShape(shape, cnt, x);
372 for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
373
374 CalcD2Shape(shape, cnt, x);
375 for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
376 os << endl;
377 }
378 }
379}
380
381// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
382// Algorithm A2.2 p. 70
383void KnotVector::CalcShape(Vector &shape, int i, real_t xi) const
384{
386
387 int p = Order;
388 int ip = (i >= 0) ? (i + p) : (-1 - i + p);
389 real_t u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip), saved, tmp;
390 real_t left[MaxOrder+1], right[MaxOrder+1];
391
392 shape(0) = 1.;
393 for (int j = 1; j <= p; ++j)
394 {
395 left[j] = u - knot(ip+1-j);
396 right[j] = knot(ip+j) - u;
397 saved = 0.;
398 for (int r = 0; r < j; ++r)
399 {
400 tmp = shape(r)/(right[r+1] + left[j-r]);
401 shape(r) = saved + right[r+1]*tmp;
402 saved = left[j-r]*tmp;
403 }
404 shape(j) = saved;
405 }
406}
407
408// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
409// Algorithm A2.3 p. 72
410void KnotVector::CalcDShape(Vector &grad, int i, real_t xi) const
411{
412 int p = Order, rk, pk;
413 int ip = (i >= 0) ? (i + p) : (-1 - i + p);
414 real_t u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip), temp, saved, d;
415 real_t ndu[MaxOrder+1][MaxOrder+1], left[MaxOrder+1], right[MaxOrder+1];
416
417#ifdef MFEM_DEBUG
418 if (p > MaxOrder)
419 {
420 mfem_error("KnotVector::CalcDShape : Order > MaxOrder!");
421 }
422#endif
423
424 ndu[0][0] = 1.0;
425 for (int j = 1; j <= p; j++)
426 {
427 left[j] = u - knot(ip-j+1);
428 right[j] = knot(ip+j) - u;
429 saved = 0.0;
430 for (int r = 0; r < j; r++)
431 {
432 ndu[j][r] = right[r+1] + left[j-r];
433 temp = ndu[r][j-1]/ndu[j][r];
434 ndu[r][j] = saved + right[r+1]*temp;
435 saved = left[j-r]*temp;
436 }
437 ndu[j][j] = saved;
438 }
439
440 for (int r = 0; r <= p; ++r)
441 {
442 d = 0.0;
443 rk = r-1;
444 pk = p-1;
445 if (r >= 1)
446 {
447 d = ndu[rk][pk]/ndu[p][rk];
448 }
449 if (r <= pk)
450 {
451 d -= ndu[r][pk]/ndu[p][r];
452 }
453 grad(r) = d;
454 }
455
456 if (i >= 0)
457 {
458 grad *= p*(knot(ip+1) - knot(ip));
459 }
460 else
461 {
462 grad *= p*(knot(ip) - knot(ip+1));
463 }
464}
465
466// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
467// Algorithm A2.3 p. 72
468void KnotVector::CalcDnShape(Vector &gradn, int n, int i, real_t xi) const
469{
470 int p = Order, rk, pk, j1, j2,r,j,k;
471 int ip = (i >= 0) ? (i + p) : (-1 - i + p);
472 real_t u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip);
473 real_t temp, saved, d;
474 real_t a[2][MaxOrder+1],ndu[MaxOrder+1][MaxOrder+1], left[MaxOrder+1],
475 right[MaxOrder+1];
476
477#ifdef MFEM_DEBUG
478 if (p > MaxOrder)
479 {
480 mfem_error("KnotVector::CalcDnShape : Order > MaxOrder!");
481 }
482#endif
483
484 ndu[0][0] = 1.0;
485 for (j = 1; j <= p; j++)
486 {
487 left[j] = u - knot(ip-j+1);
488 right[j] = knot(ip+j)- u;
489
490 saved = 0.0;
491 for (r = 0; r < j; r++)
492 {
493 ndu[j][r] = right[r+1] + left[j-r];
494 temp = ndu[r][j-1]/ndu[j][r];
495 ndu[r][j] = saved + right[r+1]*temp;
496 saved = left[j-r]*temp;
497 }
498 ndu[j][j] = saved;
499 }
500
501 for (r = 0; r <= p; r++)
502 {
503 int s1 = 0;
504 int s2 = 1;
505 a[0][0] = 1.0;
506 for (k = 1; k <= n; k++)
507 {
508 d = 0.0;
509 rk = r-k;
510 pk = p-k;
511 if (r >= k)
512 {
513 a[s2][0] = a[s1][0]/ndu[pk+1][rk];
514 d = a[s2][0]*ndu[rk][pk];
515 }
516
517 if (rk >= -1)
518 {
519 j1 = 1;
520 }
521 else
522 {
523 j1 = -rk;
524 }
525
526 if (r-1<= pk)
527 {
528 j2 = k-1;
529 }
530 else
531 {
532 j2 = p-r;
533 }
534
535 for (j = j1; j <= j2; j++)
536 {
537 a[s2][j] = (a[s1][j] - a[s1][j-1])/ndu[pk+1][rk+j];
538 d += a[s2][j]*ndu[rk+j][pk];
539 }
540
541 if (r <= pk)
542 {
543 a[s2][k] = - a[s1][k-1]/ndu[pk+1][r];
544 d += a[s2][j]*ndu[rk+j][pk];
545 }
546 gradn[r] = d;
547 j = s1;
548 s1 = s2;
549 s2 = j;
550 }
551 }
552
553 if (i >= 0)
554 {
555 u = (knot(ip+1) - knot(ip));
556 }
557 else
558 {
559 u = (knot(ip) - knot(ip+1));
560 }
561
562 temp = p*u;
563 for (k = 1; k <= n-1; k++) { temp *= (p-k)*u; }
564
565 for (j = 0; j <= p; j++) { gradn[j] *= temp; }
566
567}
568
569void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
570{
571 Vector shape(Order+1);
572 Vector maxima(GetNCP());
573 real_t arg1, arg2, arg, max1, max2, max;
574
575 xi.SetSize(GetNCP());
576 u.SetSize(GetNCP());
577 ks.SetSize(GetNCP());
578 for (int j = 0; j < GetNCP(); j++)
579 {
580 maxima[j] = 0;
581 for (int d = 0; d < Order+1; d++)
582 {
583 int i = j - d;
584 if (isElement(i))
585 {
586 arg1 = std::numeric_limits<real_t>::epsilon() / 2_r;
587 CalcShape(shape, i, arg1);
588 max1 = shape[d];
589
590 arg2 = 1_r - arg1;
591 CalcShape(shape, i, arg2);
592 max2 = shape[d];
593
594 arg = (arg1 + arg2)/2;
595 CalcShape(shape, i, arg);
596 max = shape[d];
597
598 while ( ( max > max1 ) || (max > max2) )
599 {
600 if (max1 < max2)
601 {
602 max1 = max;
603 arg1 = arg;
604 }
605 else
606 {
607 max2 = max;
608 arg2 = arg;
609 }
610
611 arg = (arg1 + arg2)/2;
612 CalcShape(shape, i, arg);
613 max = shape[d];
614 }
615
616 if (max > maxima[j])
617 {
618 maxima[j] = max;
619 ks[j] = i;
620 xi[j] = arg;
621 u[j] = getKnotLocation(arg, i+Order);
622 }
623 }
624 }
625 }
626}
627
628// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
629// Algorithm A9.1 p. 369
630void KnotVector::FindInterpolant(Array<Vector*> &x, bool reuse_inverse)
631{
632 int order = GetOrder();
633 int ncp = GetNCP();
634
635 // Find interpolation points
636 Vector xi_args, u_args;
637 Array<int> i_args;
638 FindMaxima(i_args, xi_args, u_args);
639
640 // Assemble collocation matrix
641#ifdef MFEM_USE_LAPACK
642 // If using LAPACK, we use banded matrix storage (order + 1 nonzeros per row).
643 // Find banded structure of matrix.
644 int KL = 0; // Number of subdiagonals
645 int KU = 0; // Number of superdiagonals
646 for (int i = 0; i < ncp; i++)
647 {
648 for (int p = 0; p < order+1; p++)
649 {
650 const int col = i_args[i] + p;
651 if (col < i)
652 {
653 KL = std::max(KL, i - col);
654 }
655 else if (i < col)
656 {
657 KU = std::max(KU, col - i);
658 }
659 }
660 }
661
662 const int LDAB = (2*KL) + KU + 1;
663 const int N = ncp;
664
665 fact_AB.SetSize(LDAB, N);
666#else
667 // Without LAPACK, we store and invert a DenseMatrix (inefficient).
668 if (!reuse_inverse)
669 {
670 A_coll_inv.SetSize(ncp, ncp);
671 A_coll_inv = 0.0;
672 }
673#endif
674
675 Vector shape(order+1);
676
677 if (!reuse_inverse) // Set collocation matrix entries
678 {
679 for (int i = 0; i < ncp; i++)
680 {
681 CalcShape(shape, i_args[i], xi_args[i]);
682 for (int p = 0; p < order+1; p++)
683 {
684 const int j = i_args[i] + p;
685#ifdef MFEM_USE_LAPACK
686 fact_AB(KL+KU+i-j,j) = shape[p];
687#else
688 A_coll_inv(i,j) = shape[p];
689#endif
690 }
691 }
692 }
693
694 // Solve the system
695#ifdef MFEM_USE_LAPACK
696 const int NRHS = x.Size();
697 DenseMatrix B(N, NRHS);
698 for (int j=0; j<NRHS; ++j)
699 {
700 for (int i=0; i<N; ++i) { B(i, j) = (*x[j])[i]; }
701 }
702
703 if (reuse_inverse)
704 {
705 BandedFactorizedSolve(KL, KU, fact_AB, B, false, fact_ipiv);
706 }
707 else
708 {
709 BandedSolve(KL, KU, fact_AB, B, fact_ipiv);
710 }
711
712 for (int j=0; j<NRHS; ++j)
713 {
714 for (int i=0; i<N; ++i) { (*x[j])[i] = B(i, j); }
715 }
716#else
717 if (!reuse_inverse) { A_coll_inv.Invert(); }
718 Vector tmp;
719 for (int i = 0; i < x.Size(); i++)
720 {
721 tmp = *x[i];
722 A_coll_inv.Mult(tmp, *x[i]);
723 }
724#endif
725}
726
727int KnotVector::findKnotSpan(real_t u) const
728{
729 int low, mid, high;
730
731 if (u == knot(NumOfControlPoints+Order))
732 {
733 mid = NumOfControlPoints;
734 }
735 else
736 {
737 low = Order;
738 high = NumOfControlPoints + 1;
739 mid = (low + high)/2;
740 while ( (u < knot(mid-1)) || (u > knot(mid)) )
741 {
742 if (u < knot(mid-1))
743 {
744 high = mid;
745 }
746 else
747 {
748 low = mid;
749 }
750 mid = (low + high)/2;
751 }
752 }
753 return mid;
754}
755
756void KnotVector::Difference(const KnotVector &kv, Vector &diff) const
757{
758 if (Order != kv.GetOrder())
759 {
760 mfem_error("KnotVector::Difference :\n"
761 " Can not compare knot vectors with different orders!");
762 }
763
764 int s = kv.Size() - Size();
765 if (s < 0)
766 {
767 kv.Difference(*this, diff);
768 return;
769 }
770
771 diff.SetSize(s);
772
773 if (s == 0) { return; }
774
775 s = 0;
776 int i = 0;
777 for (int j = 0; j < kv.Size(); j++)
778 {
779 if (abs(knot(i) - kv[j]) < 2 * std::numeric_limits<real_t>::epsilon())
780 {
781 i++;
782 }
783 else
784 {
785 diff(s) = kv[j];
786 s++;
787 }
788 }
789}
790
791KnotVector* KnotVector::FullyCoarsen()
792{
793 KnotVector *kvc = new KnotVector(Order, Order + 1);
794 MFEM_VERIFY(kvc->Size() == 2 * (Order + 1), "");
795 for (int i=0; i<Order+1; ++i)
796 {
797 (*kvc)[i] = 0.0;
798 (*kvc)[i + Order + 1] = 1.0;
799 }
800
801 kvc->GetElements();
802 if (spacing)
803 {
804 kvc->spacing = spacing->Clone();
805 kvc->spacing->FullyCoarsen();
806 }
807
808 return kvc;
809}
810
811void NURBSPatch::init(int dim)
812{
813 MFEM_ASSERT(dim > 1, "NURBS patch dimension (including weight) must be "
814 "greater than 1.");
815 Dim = dim;
816 sd = nd = -1;
817
818 if (kv.Size() == 1)
819 {
820 ni = kv[0]->GetNCP();
821 MFEM_ASSERT(ni > 0, "Invalid knot vector dimension.");
822 nj = -1;
823 nk = -1;
824
825 data = new real_t[ni*Dim];
826
827#ifdef MFEM_DEBUG
828 for (int i = 0; i < ni*Dim; i++)
829 {
830 data[i] = -999.99;
831 }
832#endif
833 }
834 else if (kv.Size() == 2)
835 {
836 ni = kv[0]->GetNCP();
837 nj = kv[1]->GetNCP();
838 MFEM_ASSERT(ni > 0 && nj > 0, "Invalid knot vector dimensions.");
839 nk = -1;
840
841 data = new real_t[ni*nj*Dim];
842
843#ifdef MFEM_DEBUG
844 for (int i = 0; i < ni*nj*Dim; i++)
845 {
846 data[i] = -999.99;
847 }
848#endif
849 }
850 else if (kv.Size() == 3)
851 {
852 ni = kv[0]->GetNCP();
853 nj = kv[1]->GetNCP();
854 nk = kv[2]->GetNCP();
855 MFEM_ASSERT(ni > 0 && nj > 0 && nk > 0,
856 "Invalid knot vector dimensions.");
857
858 data = new real_t[ni*nj*nk*Dim];
859
860#ifdef MFEM_DEBUG
861 for (int i = 0; i < ni*nj*nk*Dim; i++)
862 {
863 data[i] = -999.99;
864 }
865#endif
866 }
867 else
868 {
869 mfem_error("NURBSPatch::init : Wrong dimension of knotvectors!");
870 }
871}
872
873NURBSPatch::NURBSPatch(const NURBSPatch &orig)
874 : ni(orig.ni), nj(orig.nj), nk(orig.nk), Dim(orig.Dim),
875 data(NULL), kv(orig.kv.Size()), nd(orig.nd), ls(orig.ls), sd(orig.sd)
876{
877 // Allocate and copy data:
878 const int data_size = Dim*ni*nj*((kv.Size() == 2) ? 1 : nk);
879 data = new real_t[data_size];
880 std::memcpy(data, orig.data, data_size*sizeof(real_t));
881
882 // Copy the knot vectors:
883 for (int i = 0; i < kv.Size(); i++)
884 {
885 kv[i] = new KnotVector(*orig.kv[i]);
886 }
887}
888
889NURBSPatch::NURBSPatch(std::istream &input)
890{
891 int pdim, dim, size = 1;
892 string ident;
893
894 input >> ws >> ident >> pdim; // knotvectors
895 kv.SetSize(pdim);
896 for (int i = 0; i < pdim; i++)
897 {
898 kv[i] = new KnotVector(input);
899 size *= kv[i]->GetNCP();
900 }
901
902 input >> ws >> ident >> dim; // dimension
903 init(dim + 1);
904
905 input >> ws >> ident; // controlpoints (homogeneous coordinates)
906 if (ident == "controlpoints" || ident == "controlpoints_homogeneous")
907 {
908 for (int j = 0, i = 0; i < size; i++)
909 for (int d = 0; d <= dim; d++, j++)
910 {
911 input >> data[j];
912 }
913 }
914 else // "controlpoints_cartesian" (Cartesian coordinates with weight)
915 {
916 for (int j = 0, i = 0; i < size; i++)
917 {
918 for (int d = 0; d <= dim; d++)
919 {
920 input >> data[j+d];
921 }
922 for (int d = 0; d < dim; d++)
923 {
924 data[j+d] *= data[j+dim];
925 }
926 j += (dim+1);
927 }
928 }
929}
930
931NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1, int dim)
932{
933 kv.SetSize(2);
934 kv[0] = new KnotVector(*kv0);
935 kv[1] = new KnotVector(*kv1);
936 init(dim);
937}
938
939NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1,
940 const KnotVector *kv2, int dim)
941{
942 kv.SetSize(3);
943 kv[0] = new KnotVector(*kv0);
944 kv[1] = new KnotVector(*kv1);
945 kv[2] = new KnotVector(*kv2);
946 init(dim);
947}
948
949NURBSPatch::NURBSPatch(Array<const KnotVector *> &kvs, int dim)
950{
951 kv.SetSize(kvs.Size());
952 for (int i = 0; i < kv.Size(); i++)
953 {
954 kv[i] = new KnotVector(*kvs[i]);
955 }
956 init(dim);
957}
958
959NURBSPatch::NURBSPatch(NURBSPatch *parent, int dir, int Order, int NCP)
960{
961 kv.SetSize(parent->kv.Size());
962 for (int i = 0; i < kv.Size(); i++)
963 if (i != dir)
964 {
965 kv[i] = new KnotVector(*parent->kv[i]);
966 }
967 else
968 {
969 kv[i] = new KnotVector(Order, NCP);
970 }
971 init(parent->Dim);
972}
973
974void NURBSPatch::swap(NURBSPatch *np)
975{
976 if (data != NULL)
977 {
978 delete [] data;
979 }
980
981 for (int i = 0; i < kv.Size(); i++)
982 {
983 if (kv[i]) { delete kv[i]; }
984 }
985
986 data = np->data;
987 np->kv.Copy(kv);
988
989 ni = np->ni;
990 nj = np->nj;
991 nk = np->nk;
992 Dim = np->Dim;
993
994 np->data = NULL;
995 np->kv.SetSize(0);
996
997 delete np;
998}
999
1000NURBSPatch::~NURBSPatch()
1001{
1002 if (data != NULL)
1003 {
1004 delete [] data;
1005 }
1006
1007 for (int i = 0; i < kv.Size(); i++)
1008 {
1009 if (kv[i]) { delete kv[i]; }
1010 }
1011}
1012
1013void NURBSPatch::Print(std::ostream &os) const
1014{
1015 int size = 1;
1016
1017 os << "knotvectors\n" << kv.Size() << '\n';
1018 for (int i = 0; i < kv.Size(); i++)
1019 {
1020 kv[i]->Print(os);
1021 size *= kv[i]->GetNCP();
1022 }
1023
1024 os << "\ndimension\n" << Dim - 1
1025 << "\n\ncontrolpoints\n";
1026 for (int j = 0, i = 0; i < size; i++)
1027 {
1028 os << data[j++];
1029 for (int d = 1; d < Dim; d++)
1030 {
1031 os << ' ' << data[j++];
1032 }
1033 os << '\n';
1034 }
1035}
1036
1037int NURBSPatch::SetLoopDirection(int dir)
1038{
1039 if (nj == -1) // 1D case
1040 {
1041 if (dir == 0)
1042 {
1043 sd = Dim;
1044 nd = ni;
1045 ls = Dim;
1046 return ls;
1047 }
1048 else
1049 {
1050 mfem::err << "NURBSPatch::SetLoopDirection :\n"
1051 " Direction error in 1D patch, dir = " << dir << '\n';
1052 mfem_error();
1053 }
1054 }
1055 else if (nk == -1) // 2D case
1056 {
1057 if (dir == 0)
1058 {
1059 sd = Dim;
1060 nd = ni;
1061 ls = nj*Dim;
1062 return ls;
1063 }
1064 else if (dir == 1)
1065 {
1066 sd = ni*Dim;
1067 nd = nj;
1068 ls = ni*Dim;
1069 return ls;
1070 }
1071 else
1072 {
1073 mfem::err << "NURBSPatch::SetLoopDirection :\n"
1074 " Direction error in 2D patch, dir = " << dir << '\n';
1075 mfem_error();
1076 }
1077 }
1078 else // 3D case
1079 {
1080 if (dir == 0)
1081 {
1082 sd = Dim;
1083 nd = ni;
1084 ls = nj*nk*Dim;
1085 return ls;
1086 }
1087 else if (dir == 1)
1088 {
1089 sd = ni*Dim;
1090 nd = nj;
1091 ls = ni*nk*Dim;
1092 return ls;
1093 }
1094 else if (dir == 2)
1095 {
1096 sd = ni*nj*Dim;
1097 nd = nk;
1098 ls = ni*nj*Dim;
1099 return ls;
1100 }
1101 else
1102 {
1103 mfem::err << "NURBSPatch::SetLoopDirection :\n"
1104 " Direction error in 3D patch, dir = " << dir << '\n';
1105 mfem_error();
1106 }
1107 }
1108
1109 return -1;
1110}
1111
1112void NURBSPatch::UniformRefinement(Array<int> const& rf, int multiplicity)
1113{
1114 Vector new_knots;
1115 for (int dir = 0; dir < kv.Size(); dir++)
1116 {
1117 if (rf[dir] != 1)
1118 {
1119 kv[dir]->Refinement(new_knots, rf[dir]);
1120 for (int i=0; i<multiplicity; ++i)
1121 {
1122 KnotInsert(dir, new_knots);
1123 }
1124 }
1125 }
1126}
1127
1128void NURBSPatch::UniformRefinement(const std::vector<Array<int>> &rf,
1129 bool coarsened, int multiplicity)
1130{
1131 Vector new_knots;
1132 for (int dir = 0; dir < kv.Size(); dir++)
1133 {
1134 if (coarsened)
1135 {
1136 const int f = rf[dir].Sum();
1137 if (f == 1) { continue; }
1138 kv[dir]->Refinement(new_knots, f);
1139 }
1140 else
1141 {
1142 MFEM_VERIFY(rf[dir].IsConstant(), "");
1143 if (rf[dir][0] == 1) { continue; }
1144 kv[dir]->Refinement(new_knots, rf[dir][0]);
1145 }
1146
1147 for (int i=0; i<multiplicity; ++i)
1148 {
1149 KnotInsert(dir, new_knots);
1150 }
1151 }
1152}
1153
1154void NURBSPatch::UniformRefinement(int rf, int multiplicity)
1155{
1156 Array<int> rf_array(kv.Size());
1157 rf_array = rf;
1158 UniformRefinement(rf_array, multiplicity);
1159}
1160
1161void NURBSPatch::UpdateSpacingPartitions(const Array<KnotVector*> &pkv)
1162{
1163 MFEM_VERIFY(pkv.Size() == kv.Size(), "");
1164
1165 for (int dir = 0; dir < kv.Size(); dir++)
1166 {
1167 if (kv[dir]->spacing && pkv[dir]->spacing)
1168 {
1169 PiecewiseSpacingFunction *pws = dynamic_cast<PiecewiseSpacingFunction*>
1170 (kv[dir]->spacing.get());
1171 const PiecewiseSpacingFunction *upws =
1172 dynamic_cast<const PiecewiseSpacingFunction*>(pkv[dir]->spacing.get());
1173
1174 MFEM_VERIFY((pws == nullptr) == (upws == nullptr), "");
1175
1176 if (pws)
1177 {
1178 Array<int> s0 = pws->RelativePieceSizes();
1179 Array<int> s1 = upws->RelativePieceSizes();
1180 MFEM_ASSERT(s0.Size() == s1.Size(), "");
1181
1182 Array<int> rf(s0.Size());
1183 for (int i=0; i<s0.Size(); ++i)
1184 {
1185 const int f = s1[i] / s0[i];
1186 MFEM_ASSERT(f * s0[i] == s1[i], "Inconsistent spacings");
1187 rf[i] = f;
1188 }
1189
1190 pws->ScalePartition(rf, false);
1191 }
1192 }
1193 }
1194}
1195
1196void NURBSPatch::Coarsen(Array<int> const& cf, real_t tol)
1197{
1198 for (int dir = 0; dir < kv.Size(); dir++)
1199 {
1200 if (!kv[dir]->coarse)
1201 {
1202 const int ne_fine = kv[dir]->GetNE();
1203 KnotRemove(dir, kv[dir]->GetFineKnots(cf[dir]), tol);
1204 kv[dir]->coarse = true;
1205 kv[dir]->GetElements();
1206
1207 const int ne_coarse = kv[dir]->GetNE();
1208 MFEM_VERIFY(ne_fine == cf[dir] * ne_coarse, "");
1209 if (kv[dir]->spacing)
1210 {
1211 kv[dir]->spacing->SetSize(ne_coarse);
1212 kv[dir]->spacing->ScaleParameters((real_t) cf[dir]);
1213 }
1214 }
1215 }
1216}
1217
1218void NURBSPatch::Coarsen(int cf, real_t tol)
1219{
1220 Array<int> cf_array(kv.Size());
1221 cf_array = cf;
1222 Coarsen(cf_array, tol);
1223}
1224
1225void NURBSPatch::GetCoarseningFactors(Array<int> & f) const
1226{
1227 f.SetSize(kv.Size());
1228 for (int dir = 0; dir < kv.Size(); dir++)
1229 {
1230 f[dir] = kv[dir]->GetCoarseningFactor();
1231 }
1232}
1233
1234void NURBSPatch::KnotInsert(Array<KnotVector *> &newkv)
1235{
1236 MFEM_ASSERT(newkv.Size() == kv.Size(), "Invalid input to KnotInsert");
1237 for (int dir = 0; dir < kv.Size(); dir++)
1238 {
1239 KnotInsert(dir, *newkv[dir]);
1240 }
1241}
1242
1243void NURBSPatch::KnotInsert(int dir, const KnotVector &newkv)
1244{
1245 if (dir >= kv.Size() || dir < 0)
1246 {
1247 mfem_error("NURBSPatch::KnotInsert : Incorrect direction!");
1248 }
1249
1250 int t = newkv.GetOrder() - kv[dir]->GetOrder();
1251
1252 if (t > 0)
1253 {
1254 DegreeElevate(dir, t);
1255 }
1256 else if (t < 0)
1257 {
1258 mfem_error("NURBSPatch::KnotInsert : Incorrect order!");
1259 }
1260
1261 Vector diff;
1262 GetKV(dir)->Difference(newkv, diff);
1263 if (diff.Size() > 0)
1264 {
1265 KnotInsert(dir, diff);
1266 }
1267}
1268
1269void NURBSPatch::KnotInsert(Array<Vector *> &newkv)
1270{
1271 MFEM_ASSERT(newkv.Size() == kv.Size(), "Invalid input to KnotInsert");
1272 for (int dir = 0; dir < kv.Size(); dir++)
1273 {
1274 KnotInsert(dir, *newkv[dir]);
1275 }
1276}
1277
1278void NURBSPatch::KnotRemove(Array<Vector *> &rmkv, real_t tol)
1279{
1280 for (int dir = 0; dir < kv.Size(); dir++)
1281 {
1282 KnotRemove(dir, *rmkv[dir], tol);
1283 }
1284}
1285
1286void NURBSPatch::KnotRemove(int dir, const Vector &knot, real_t tol)
1287{
1288 // TODO: implement an efficient version of this!
1289 for (auto k : knot)
1290 {
1291 KnotRemove(dir, k, 1, tol);
1292 }
1293}
1294
1295// Algorithm A5.5 from "The NURBS Book", 2nd ed, Piegl and Tiller, chapter 5.
1296void NURBSPatch::KnotInsert(int dir, const Vector &knot)
1297{
1298 if (knot.Size() == 0 ) { return; }
1299
1300 if (dir >= kv.Size() || dir < 0)
1301 {
1302 mfem_error("NURBSPatch::KnotInsert : Invalid direction!");
1303 }
1304
1305 NURBSPatch &oldp = *this;
1306 KnotVector &oldkv = *kv[dir];
1307
1308 NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder(),
1309 oldkv.GetNCP() + knot.Size());
1310 NURBSPatch &newp = *newpatch;
1311 KnotVector &newkv = *newp.GetKV(dir);
1312
1313 newkv.spacing = oldkv.spacing;
1314
1315 int size = oldp.SetLoopDirection(dir);
1316 if (size != newp.SetLoopDirection(dir))
1317 {
1318 mfem_error("NURBSPatch::KnotInsert : Size mismatch!");
1319 }
1320
1321 int rr = knot.Size() - 1;
1322 int a = oldkv.findKnotSpan(knot(0)) - 1;
1323 int b = oldkv.findKnotSpan(knot(rr)) - 1;
1324 int pl = oldkv.GetOrder();
1325 int ml = oldkv.GetNCP();
1326
1327 for (int j = 0; j <= a; j++)
1328 {
1329 newkv[j] = oldkv[j];
1330 }
1331 for (int j = b+pl; j <= ml+pl; j++)
1332 {
1333 newkv[j+rr+1] = oldkv[j];
1334 }
1335 for (int k = 0; k <= (a-pl); k++)
1336 {
1337 for (int ll = 0; ll < size; ll++)
1338 {
1339 newp.slice(k,ll) = oldp.slice(k,ll);
1340 }
1341 }
1342 for (int k = (b-1); k < ml; k++)
1343 {
1344 for (int ll = 0; ll < size; ll++)
1345 {
1346 newp.slice(k+rr+1,ll) = oldp.slice(k,ll);
1347 }
1348 }
1349
1350 int i = b+pl-1;
1351 int k = b+pl+rr;
1352
1353 for (int j = rr; j >= 0; j--)
1354 {
1355 while ( (knot(j) <= oldkv[i]) && (i > a) )
1356 {
1357 newkv[k] = oldkv[i];
1358 for (int ll = 0; ll < size; ll++)
1359 {
1360 newp.slice(k-pl-1,ll) = oldp.slice(i-pl-1,ll);
1361 }
1362
1363 k--;
1364 i--;
1365 }
1366
1367 for (int ll = 0; ll < size; ll++)
1368 {
1369 newp.slice(k-pl-1,ll) = newp.slice(k-pl,ll);
1370 }
1371
1372 for (int l = 1; l <= pl; l++)
1373 {
1374 int ind = k-pl+l;
1375 real_t alfa = newkv[k+l] - knot(j);
1376 if (fabs(alfa) == 0.0)
1377 {
1378 for (int ll = 0; ll < size; ll++)
1379 {
1380 newp.slice(ind-1,ll) = newp.slice(ind,ll);
1381 }
1382 }
1383 else
1384 {
1385 alfa = alfa/(newkv[k+l] - oldkv[i-pl+l]);
1386 for (int ll = 0; ll < size; ll++)
1387 {
1388 newp.slice(ind-1,ll) = alfa*newp.slice(ind-1,ll) +
1389 (1.0-alfa)*newp.slice(ind,ll);
1390 }
1391 }
1392 }
1393
1394 newkv[k] = knot(j);
1395 k--;
1396 }
1397
1398 newkv.GetElements();
1399
1400 swap(newpatch);
1401}
1402
1403// Algorithm A5.8 from "The NURBS Book", 2nd ed, Piegl and Tiller, chapter 5.
1404int NURBSPatch::KnotRemove(int dir, real_t knot, int ntimes, real_t tol)
1405{
1406 if (dir >= kv.Size() || dir < 0)
1407 {
1408 mfem_error("NURBSPatch::KnotRemove : Invalid direction!");
1409 }
1410
1411 NURBSPatch &oldp = *this;
1412 KnotVector &oldkv = *kv[dir];
1413
1414 // Find the index of the last occurrence of the knot.
1415 int id = -1;
1416 int multiplicity = 0;
1417 for (int i=0; i<oldkv.Size(); ++i)
1418 {
1419 if (oldkv[i] == knot)
1420 {
1421 id = i;
1422 multiplicity++;
1423 }
1424 }
1425
1426 MFEM_VERIFY(0 < id && id < oldkv.Size() - 1 && ntimes <= multiplicity,
1427 "Only interior knots of sufficient multiplicity may be removed.");
1428
1429 const int p = oldkv.GetOrder();
1430
1431 NURBSPatch tmpp(this, dir, p, oldkv.GetNCP());
1432
1433 const int size = oldp.SetLoopDirection(dir);
1434 if (size != tmpp.SetLoopDirection(dir))
1435 {
1436 mfem_error("NURBSPatch::KnotRemove : Size mismatch!");
1437 }
1438
1439 // Copy old data
1440 for (int k = 0; k < oldp.nd; ++k)
1441 {
1442 for (int ll = 0; ll < size; ll++)
1443 {
1444 tmpp.slice(k,ll) = oldp.slice(k,ll);
1445 }
1446 }
1447
1448 const int r = id;
1449 const int s = multiplicity;
1450
1451 int last = r - s;
1452 int first = r - p;
1453
1454 int i = first;
1455 int j = last;
1456
1457 Array2D<real_t> temp(last + ntimes + 1, size);
1458
1459 for (int t=0; t<ntimes; ++t)
1460 {
1461 int off = first - 1; // Difference in index between temp and P.
1462
1463 for (int ll = 0; ll < size; ll++)
1464 {
1465 temp(0, ll) = oldp.slice(off, ll);
1466 temp(last + 1 - off, ll) = oldp.slice(last + 1, ll);
1467 }
1468
1469 int ii = 1;
1470 int jj = last - off;
1471
1472 while (j - i > t)
1473 {
1474 // Compute new control points for one removal step
1475 const real_t a_i = (knot - oldkv[i]) / (oldkv[i+p+1+t] - oldkv[i]);
1476 const real_t a_j = (knot - oldkv[j-t]) / (oldkv[j+p+1] - oldkv[j-t]);
1477
1478 for (int ll = 0; ll < size; ll++)
1479 {
1480 temp(ii,ll) = (1.0 / a_i) * oldp.slice(i,ll) -
1481 ((1.0/a_i) - 1.0) * temp(ii - 1, ll);
1482
1483 temp(jj,ll) = (1.0 / (1.0 - a_j)) * (oldp.slice(j,ll) -
1484 (a_j * temp(jj + 1, ll)));
1485 }
1486
1487 i++; ii++;
1488 j--; jj--;
1489 }
1490
1491 // Check whether knot is removable
1492 Vector diff(size);
1493 if (j - i < t)
1494 {
1495 for (int ll = 0; ll < size; ll++)
1496 {
1497 diff[ll] = temp(ii-1, ll) - temp(jj+1, ll);
1498 }
1499 }
1500 else
1501 {
1502 const real_t a_i = (knot - oldkv[i]) / (oldkv[i+p+1+t] - oldkv[i]);
1503 for (int ll = 0; ll < size; ll++)
1504 diff[ll] = oldp.slice(i,ll) - (a_i * temp(ii+t+1, ll))
1505 - ((1.0 - a_i) * temp(ii-1, ll));
1506 }
1507
1508 const real_t dist = diff.Norml2();
1509 if (dist >= tol)
1510 {
1511 // Removal failed. Return the number of successful removals.
1512 mfem::out << "Knot removal failed after " << t
1513 << " successful removals" << endl;
1514 return t;
1515 }
1516
1517 // Note that the new weights may not be positive.
1518
1519 // Save new control points
1520 i = first;
1521 j = last;
1522
1523 while (j - i > t)
1524 {
1525 for (int ll = 0; ll < size; ll++)
1526 {
1527 tmpp.slice(i,ll) = temp(i - off,ll);
1528 tmpp.slice(j,ll) = temp(j - off,ll);
1529 }
1530 i++;
1531 j--;
1532 }
1533
1534 first--;
1535 last++;
1536 } // End of loop (t) over ntimes.
1537
1538 const int fout = ((2*r) - s - p) / 2; // First control point out
1539 j = fout;
1540 i = j;
1541
1542 for (int k=1; k<ntimes; ++k)
1543 {
1544 if (k % 2 == 1)
1545 {
1546 i++;
1547 }
1548 else
1549 {
1550 j--;
1551 }
1552 }
1553
1554 NURBSPatch *newpatch = new NURBSPatch(this, dir, p,
1555 oldkv.GetNCP() - ntimes);
1556 NURBSPatch &newp = *newpatch;
1557 if (size != newp.SetLoopDirection(dir))
1558 {
1559 mfem_error("NURBSPatch::KnotRemove : Size mismatch!");
1560 }
1561
1562 for (int k = 0; k < fout; ++k)
1563 {
1564 for (int ll = 0; ll < size; ll++)
1565 {
1566 newp.slice(k,ll) = oldp.slice(k,ll); // Copy old data
1567 }
1568 }
1569
1570 for (int k = i+1; k < oldp.nd; ++k)
1571 {
1572 for (int ll = 0; ll < size; ll++)
1573 {
1574 newp.slice(j,ll) = tmpp.slice(k,ll); // Shift
1575 }
1576
1577 j++;
1578 }
1579
1580 KnotVector &newkv = *newp.GetKV(dir);
1581 MFEM_VERIFY(newkv.Size() == oldkv.Size() - ntimes, "");
1582
1583 newkv.spacing = oldkv.spacing;
1584 newkv.coarse = oldkv.coarse;
1585
1586 for (int k = 0; k < r - ntimes + 1; k++)
1587 {
1588 newkv[k] = oldkv[k];
1589 }
1590 for (int k = r + 1; k < oldkv.Size(); k++)
1591 {
1592 newkv[k - ntimes] = oldkv[k];
1593 }
1594
1595 newkv.GetElements();
1596
1597 swap(newpatch);
1598
1599 return ntimes;
1600}
1601
1602void NURBSPatch::DegreeElevate(int t)
1603{
1604 for (int dir = 0; dir < kv.Size(); dir++)
1605 {
1606 DegreeElevate(dir, t);
1607 }
1608}
1609
1610// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
1611void NURBSPatch::DegreeElevate(int dir, int t)
1612{
1613 if (dir >= kv.Size() || dir < 0)
1614 {
1615 mfem_error("NURBSPatch::DegreeElevate : Incorrect direction!");
1616 }
1617
1618 MFEM_ASSERT(t >= 0, "DegreeElevate cannot decrease the degree.");
1619
1620 int i, j, k, kj, mpi, mul, mh, kind, cind, first, last;
1621 int r, a, b, oldr, save, s, tr, lbz, rbz, l;
1622 real_t inv, ua, ub, numer, alf, den, bet, gam;
1623
1624 NURBSPatch &oldp = *this;
1625 KnotVector &oldkv = *kv[dir];
1626 oldkv.GetElements();
1627
1628 auto *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
1629 oldkv.GetNCP() + oldkv.GetNE()*t);
1630 NURBSPatch &newp = *newpatch;
1631 KnotVector &newkv = *newp.GetKV(dir);
1632
1633 if (oldkv.spacing) { newkv.spacing = oldkv.spacing; }
1634
1635 int size = oldp.SetLoopDirection(dir);
1636 if (size != newp.SetLoopDirection(dir))
1637 {
1638 mfem_error("NURBSPatch::DegreeElevate : Size mismatch!");
1639 }
1640
1641 int p = oldkv.GetOrder();
1642 int n = oldkv.GetNCP()-1;
1643
1644 DenseMatrix bezalfs (p+t+1, p+1);
1645 DenseMatrix bpts (p+1, size);
1646 DenseMatrix ebpts (p+t+1, size);
1647 DenseMatrix nextbpts(p-1, size);
1648 Vector alphas (p-1);
1649
1650 int m = n + p + 1;
1651 int ph = p + t;
1652 int ph2 = ph/2;
1653
1654 {
1655 Array2D<int> binom(ph+1, ph+1);
1656 for (i = 0; i <= ph; i++)
1657 {
1658 binom(i,0) = binom(i,i) = 1;
1659 for (j = 1; j < i; j++)
1660 {
1661 binom(i,j) = binom(i-1,j) + binom(i-1,j-1);
1662 }
1663 }
1664
1665 bezalfs(0,0) = 1.0;
1666 bezalfs(ph,p) = 1.0;
1667
1668 for (i = 1; i <= ph2; i++)
1669 {
1670 inv = 1.0/binom(ph,i);
1671 mpi = min(p,i);
1672 for (j = max(0,i-t); j <= mpi; j++)
1673 {
1674 bezalfs(i,j) = inv*binom(p,j)*binom(t,i-j);
1675 }
1676 }
1677 }
1678
1679 for (i = ph2+1; i < ph; i++)
1680 {
1681 mpi = min(p,i);
1682 for (j = max(0,i-t); j <= mpi; j++)
1683 {
1684 bezalfs(i,j) = bezalfs(ph-i,p-j);
1685 }
1686 }
1687
1688 mh = ph;
1689 kind = ph + 1;
1690 r = -1;
1691 a = p;
1692 b = p + 1;
1693 cind = 1;
1694 ua = oldkv[0];
1695 for (l = 0; l < size; l++)
1696 {
1697 newp.slice(0,l) = oldp.slice(0,l);
1698 }
1699 for (i = 0; i <= ph; i++)
1700 {
1701 newkv[i] = ua;
1702 }
1703
1704 for (i = 0; i <= p; i++)
1705 {
1706 for (l = 0; l < size; l++)
1707 {
1708 bpts(i,l) = oldp.slice(i,l);
1709 }
1710 }
1711
1712 while (b < m)
1713 {
1714 i = b;
1715 while (b < m && oldkv[b] == oldkv[b+1]) { b++; }
1716
1717 mul = b-i+1;
1718
1719 mh = mh + mul + t;
1720 ub = oldkv[b];
1721 oldr = r;
1722 r = p-mul;
1723 if (oldr > 0) { lbz = (oldr+2)/2; }
1724 else { lbz = 1; }
1725
1726 if (r > 0) { rbz = ph-(r+1)/2; }
1727 else { rbz = ph; }
1728
1729 if (r > 0)
1730 {
1731 numer = ub - ua;
1732 for (k = p ; k > mul; k--)
1733 {
1734 alphas[k-mul-1] = numer/(oldkv[a+k]-ua);
1735 }
1736
1737 for (j = 1; j <= r; j++)
1738 {
1739 save = r-j;
1740 s = mul+j;
1741 for (k = p; k >= s; k--)
1742 {
1743 for (l = 0; l < size; l++)
1744 bpts(k,l) = (alphas[k-s]*bpts(k,l) +
1745 (1.0-alphas[k-s])*bpts(k-1,l));
1746 }
1747 for (l = 0; l < size; l++)
1748 {
1749 nextbpts(save,l) = bpts(p,l);
1750 }
1751 }
1752 }
1753
1754 for (i = lbz; i <= ph; i++)
1755 {
1756 for (l = 0; l < size; l++)
1757 {
1758 ebpts(i,l) = 0.0;
1759 }
1760 mpi = min(p,i);
1761 for (j = max(0,i-t); j <= mpi; j++)
1762 {
1763 for (l = 0; l < size; l++)
1764 {
1765 ebpts(i,l) += bezalfs(i,j)*bpts(j,l);
1766 }
1767 }
1768 }
1769
1770 if (oldr > 1)
1771 {
1772 first = kind-2;
1773 last = kind;
1774 den = ub-ua;
1775 bet = (ub-newkv[kind-1])/den;
1776
1777 for (tr = 1; tr < oldr; tr++)
1778 {
1779 i = first;
1780 j = last;
1781 kj = j-kind+1;
1782 while (j-i > tr)
1783 {
1784 if (i < cind)
1785 {
1786 alf = (ub-newkv[i])/(ua-newkv[i]);
1787 for (l = 0; l < size; l++)
1788 {
1789 newp.slice(i,l) = alf*newp.slice(i,l)-(1.0-alf)*newp.slice(i-1,l);
1790 }
1791 }
1792 if (j >= lbz)
1793 {
1794 if ((j-tr) <= (kind-ph+oldr))
1795 {
1796 gam = (ub-newkv[j-tr])/den;
1797 for (l = 0; l < size; l++)
1798 {
1799 ebpts(kj,l) = gam*ebpts(kj,l) + (1.0-gam)*ebpts(kj+1,l);
1800 }
1801 }
1802 else
1803 {
1804 for (l = 0; l < size; l++)
1805 {
1806 ebpts(kj,l) = bet*ebpts(kj,l) + (1.0-bet)*ebpts(kj+1,l);
1807 }
1808 }
1809 }
1810 i = i+1;
1811 j = j-1;
1812 kj = kj-1;
1813 }
1814 first--;
1815 last++;
1816 }
1817 }
1818
1819 if (a != p)
1820 {
1821 for (i = 0; i < (ph-oldr); i++)
1822 {
1823 newkv[kind] = ua;
1824 kind = kind+1;
1825 }
1826 }
1827 for (j = lbz; j <= rbz; j++)
1828 {
1829 for (l = 0; l < size; l++)
1830 {
1831 newp.slice(cind,l) = ebpts(j,l);
1832 }
1833 cind = cind +1;
1834 }
1835
1836 if (b < m)
1837 {
1838 for (j = 0; j <r; j++)
1839 for (l = 0; l < size; l++)
1840 {
1841 bpts(j,l) = nextbpts(j,l);
1842 }
1843
1844 for (j = r; j <= p; j++)
1845 for (l = 0; l < size; l++)
1846 {
1847 bpts(j,l) = oldp.slice(b-p+j,l);
1848 }
1849
1850 a = b;
1851 b = b+1;
1852 ua = ub;
1853 }
1854 else
1855 {
1856 for (i = 0; i <= ph; i++)
1857 {
1858 newkv[kind+i] = ub;
1859 }
1860 }
1861 }
1862 newkv.GetElements();
1863
1864 swap(newpatch);
1865}
1866
1867void NURBSPatch::FlipDirection(int dir)
1868{
1869 int size = SetLoopDirection(dir);
1870
1871 for (int id = 0; id < nd/2; id++)
1872 for (int i = 0; i < size; i++)
1873 {
1874 Swap<real_t>((*this).slice(id,i), (*this).slice(nd-1-id,i));
1875 }
1876 kv[dir]->Flip();
1877}
1878
1879void NURBSPatch::SwapDirections(int dir1, int dir2)
1880{
1881 if (abs(dir1-dir2) == 2)
1882 {
1883 mfem_error("NURBSPatch::SwapDirections :"
1884 " directions 0 and 2 are not supported!");
1885 }
1886
1887 Array<const KnotVector *> nkv(kv);
1888
1889 Swap<const KnotVector *>(nkv[dir1], nkv[dir2]);
1890 NURBSPatch *newpatch = new NURBSPatch(nkv, Dim);
1891
1892 int size = SetLoopDirection(dir1);
1893 newpatch->SetLoopDirection(dir2);
1894
1895 for (int id = 0; id < nd; id++)
1896 for (int i = 0; i < size; i++)
1897 {
1898 (*newpatch).slice(id,i) = (*this).slice(id,i);
1899 }
1900
1901 swap(newpatch);
1902}
1903
1904void NURBSPatch::Rotate(real_t angle, real_t n[])
1905{
1906 if (Dim == 3)
1907 {
1908 Rotate2D(angle);
1909 }
1910 else
1911 {
1912 if (n == NULL)
1913 {
1914 mfem_error("NURBSPatch::Rotate : Specify an angle for a 3D rotation.");
1915 }
1916
1917 Rotate3D(n, angle);
1918 }
1919}
1920
1921void NURBSPatch::Get2DRotationMatrix(real_t angle, DenseMatrix &T)
1922{
1923 real_t s = sin(angle);
1924 real_t c = cos(angle);
1925
1926 T.SetSize(2);
1927 T(0,0) = c;
1928 T(0,1) = -s;
1929 T(1,0) = s;
1930 T(1,1) = c;
1931}
1932
1933void NURBSPatch::Rotate2D(real_t angle)
1934{
1935 if (Dim != 3)
1936 {
1937 mfem_error("NURBSPatch::Rotate2D : not a NURBSPatch in 2D!");
1938 }
1939
1940 DenseMatrix T(2);
1941 Vector x(2), y(NULL, 2);
1942
1943 Get2DRotationMatrix(angle, T);
1944
1945 int size = 1;
1946 for (int i = 0; i < kv.Size(); i++)
1947 {
1948 size *= kv[i]->GetNCP();
1949 }
1950
1951 for (int i = 0; i < size; i++)
1952 {
1953 y.SetData(data + i*Dim);
1954 x = y;
1955 T.Mult(x, y);
1956 }
1957}
1958
1959void NURBSPatch::Get3DRotationMatrix(real_t n[], real_t angle, real_t r,
1960 DenseMatrix &T)
1961{
1962 real_t c, s, c1;
1963 const real_t l2 = n[0]*n[0] + n[1]*n[1] + n[2]*n[2];
1964 const real_t l = sqrt(l2);
1965
1966 MFEM_ASSERT(l2 > 0.0, "3D rotation axis is undefined");
1967
1968 if (fabs(angle) == (real_t)(M_PI_2))
1969 {
1970 s = r*copysign(1., angle);
1971 c = 0.;
1972 c1 = -1.;
1973 }
1974 else if (fabs(angle) == (real_t)(M_PI))
1975 {
1976 s = 0.;
1977 c = -r;
1978 c1 = c - 1.;
1979 }
1980 else
1981 {
1982 s = r*sin(angle);
1983 c = r*cos(angle);
1984 c1 = c - 1.;
1985 }
1986
1987 T.SetSize(3);
1988
1989 T(0,0) = (n[0]*n[0] + (n[1]*n[1] + n[2]*n[2])*c)/l2;
1990 T(0,1) = -(n[0]*n[1]*c1)/l2 - (n[2]*s)/l;
1991 T(0,2) = -(n[0]*n[2]*c1)/l2 + (n[1]*s)/l;
1992 T(1,0) = -(n[0]*n[1]*c1)/l2 + (n[2]*s)/l;
1993 T(1,1) = (n[1]*n[1] + (n[0]*n[0] + n[2]*n[2])*c)/l2;
1994 T(1,2) = -(n[1]*n[2]*c1)/l2 - (n[0]*s)/l;
1995 T(2,0) = -(n[0]*n[2]*c1)/l2 - (n[1]*s)/l;
1996 T(2,1) = -(n[1]*n[2]*c1)/l2 + (n[0]*s)/l;
1997 T(2,2) = (n[2]*n[2] + (n[0]*n[0] + n[1]*n[1])*c)/l2;
1998}
1999
2000void NURBSPatch::Rotate3D(real_t n[], real_t angle)
2001{
2002 if (Dim != 4)
2003 {
2004 mfem_error("NURBSPatch::Rotate3D : not a NURBSPatch in 3D!");
2005 }
2006
2007 DenseMatrix T(3);
2008 Vector x(3), y(NULL, 3);
2009
2010 Get3DRotationMatrix(n, angle, 1., T);
2011
2012 int size = 1;
2013 for (int i = 0; i < kv.Size(); i++)
2014 {
2015 size *= kv[i]->GetNCP();
2016 }
2017
2018 for (int i = 0; i < size; i++)
2019 {
2020 y.SetData(data + i*Dim);
2021 x = y;
2022 T.Mult(x, y);
2023 }
2024}
2025
2026int NURBSPatch::MakeUniformDegree(int degree)
2027{
2028 int maxd = degree;
2029
2030 if (maxd == -1)
2031 {
2032 for (int dir = 0; dir < kv.Size(); dir++)
2033 {
2034 maxd = std::max(maxd, kv[dir]->GetOrder());
2035 }
2036 }
2037
2038 for (int dir = 0; dir < kv.Size(); dir++)
2039 {
2040 if (maxd > kv[dir]->GetOrder())
2041 {
2042 DegreeElevate(dir, maxd - kv[dir]->GetOrder());
2043 }
2044 }
2045
2046 return maxd;
2047}
2048
2049NURBSPatch *Interpolate(NURBSPatch &p1, NURBSPatch &p2)
2050{
2051 if (p1.kv.Size() != p2.kv.Size() || p1.Dim != p2.Dim)
2052 {
2053 mfem_error("Interpolate(NURBSPatch &, NURBSPatch &)");
2054 }
2055
2056 int size = 1, dim = p1.Dim;
2057 Array<const KnotVector *> kv(p1.kv.Size() + 1);
2058
2059 for (int i = 0; i < p1.kv.Size(); i++)
2060 {
2061 if (p1.kv[i]->GetOrder() < p2.kv[i]->GetOrder())
2062 {
2063 p1.KnotInsert(i, *p2.kv[i]);
2064 p2.KnotInsert(i, *p1.kv[i]);
2065 }
2066 else
2067 {
2068 p2.KnotInsert(i, *p1.kv[i]);
2069 p1.KnotInsert(i, *p2.kv[i]);
2070 }
2071 kv[i] = p1.kv[i];
2072 size *= kv[i]->GetNCP();
2073 }
2074
2075 KnotVector &nkv = *(new KnotVector(1, 2));
2076 nkv[0] = nkv[1] = 0.0;
2077 nkv[2] = nkv[3] = 1.0;
2078 nkv.GetElements();
2079 kv.Last() = &nkv;
2080
2081 NURBSPatch *patch = new NURBSPatch(kv, dim);
2082 delete kv.Last();
2083
2084 for (int i = 0; i < size; i++)
2085 {
2086 for (int d = 0; d < dim; d++)
2087 {
2088 patch->data[i*dim+d] = p1.data[i*dim+d];
2089 patch->data[(i+size)*dim+d] = p2.data[i*dim+d];
2090 }
2091 }
2092
2093 return patch;
2094}
2095
2096NURBSPatch *Revolve3D(NURBSPatch &patch, real_t n[], real_t ang, int times)
2097{
2098 if (patch.Dim != 4)
2099 {
2100 mfem_error("Revolve3D(NURBSPatch &, real_t [], real_t)");
2101 }
2102
2103 int size = 1, ns;
2104 Array<const KnotVector *> nkv(patch.kv.Size() + 1);
2105
2106 for (int i = 0; i < patch.kv.Size(); i++)
2107 {
2108 nkv[i] = patch.kv[i];
2109 size *= nkv[i]->GetNCP();
2110 }
2111 ns = 2*times + 1;
2112 KnotVector &lkv = *(new KnotVector(2, ns));
2113 nkv.Last() = &lkv;
2114 lkv[0] = lkv[1] = lkv[2] = 0.0;
2115 for (int i = 1; i < times; i++)
2116 {
2117 lkv[2*i+1] = lkv[2*i+2] = i;
2118 }
2119 lkv[ns] = lkv[ns+1] = lkv[ns+2] = times;
2120 lkv.GetElements();
2121 NURBSPatch *newpatch = new NURBSPatch(nkv, 4);
2122 delete nkv.Last();
2123
2124 DenseMatrix T(3), T2(3);
2125 Vector u(NULL, 3), v(NULL, 3);
2126
2127 NURBSPatch::Get3DRotationMatrix(n, ang, 1., T);
2128 real_t c = cos(ang/2);
2129 NURBSPatch::Get3DRotationMatrix(n, ang/2, 1./c, T2);
2130 T2 *= c;
2131
2132 real_t *op = patch.data, *np;
2133 for (int i = 0; i < size; i++)
2134 {
2135 np = newpatch->data + 4*i;
2136 for (int j = 0; j < 4; j++)
2137 {
2138 np[j] = op[j];
2139 }
2140 for (int j = 0; j < times; j++)
2141 {
2142 u.SetData(np);
2143 v.SetData(np += 4*size);
2144 T2.Mult(u, v);
2145 v[3] = c*u[3];
2146 v.SetData(np += 4*size);
2147 T.Mult(u, v);
2148 v[3] = u[3];
2149 }
2150 op += 4;
2151 }
2152
2153 return newpatch;
2154}
2155
2156void NURBSPatch::SetKnotVectorsCoarse(bool c)
2157{
2158 for (int i=0; i<kv.Size(); ++i) { kv[i]->coarse = c; }
2159}
2160
2161void NURBSPatch::FullyCoarsen(const Array2D<double> & cp, int ncp1D)
2162{
2163 // Remove interior knots
2164 Array<const KnotVector *> kvc(kv.Size());
2165 for (int dir = 0; dir < kv.Size(); dir++)
2166 {
2167 kvc[dir] = kv[dir]->FullyCoarsen();
2168 }
2169
2170 // Copy CP
2171 NURBSPatch *newpatch = new NURBSPatch(kvc, Dim);
2172 NURBSPatch &newp = *newpatch;
2173
2174 if (Dim == 4) // 3D
2175 {
2176 for (int i=0; i<ncp1D; ++i)
2177 for (int j=0; j<ncp1D; ++j)
2178 for (int k=0; k<ncp1D; ++k)
2179 {
2180 const int dof = i + (ncp1D * (j + (ncp1D * k)));
2181 for (int l = 0; l < Dim - 1; ++l)
2182 {
2183 newp(i,j,k,l) = cp(dof, l);
2184 newp(i,j,k,Dim-1) = 1.0; // Assuming unit weights
2185 }
2186 }
2187 }
2188 else if (Dim == 3) // 2D
2189 {
2190 for (int i=0; i<ncp1D; ++i)
2191 for (int j=0; j<ncp1D; ++j)
2192 {
2193 const int dof = i + (ncp1D * j);
2194 for (int l=0; l<Dim - 1; ++l)
2195 {
2196 newp(i,j,l) = cp(dof, l);
2197 newp(i,j,Dim-1) = 1.0; // Assuming unit weights
2198 }
2199 }
2200 }
2201 else
2202 {
2203 MFEM_ABORT("Dimension not supported in FullyCoarsen");
2204 }
2205
2206 swap(newpatch);
2207}
2208
2209NURBSExtension::NURBSExtension(const NURBSExtension &orig)
2210 : mOrder(orig.mOrder), mOrders(orig.mOrders),
2211 NumOfKnotVectors(orig.NumOfKnotVectors),
2212 NumOfVertices(orig.NumOfVertices),
2213 NumOfElements(orig.NumOfElements),
2214 NumOfBdrElements(orig.NumOfBdrElements),
2215 NumOfDofs(orig.NumOfDofs),
2216 NumOfActiveVertices(orig.NumOfActiveVertices),
2217 NumOfActiveElems(orig.NumOfActiveElems),
2218 NumOfActiveBdrElems(orig.NumOfActiveBdrElems),
2219 NumOfActiveDofs(orig.NumOfActiveDofs),
2220 activeVert(orig.activeVert),
2221 activeElem(orig.activeElem),
2222 activeBdrElem(orig.activeBdrElem),
2223 activeDof(orig.activeDof),
2224 patchTopo(new Mesh(*orig.patchTopo)),
2225 own_topo(true),
2226 edge_to_ukv(orig.edge_to_ukv),
2227 knotVectors(orig.knotVectors.Size()), // knotVectors are copied in the body
2228 knotVectorsCompr(orig.knotVectorsCompr.Size()),
2229 weights(orig.weights),
2230 d_to_d(orig.d_to_d),
2231 master(orig.master),
2232 slave(orig.slave),
2233 v_meshOffsets(orig.v_meshOffsets),
2234 e_meshOffsets(orig.e_meshOffsets),
2235 f_meshOffsets(orig.f_meshOffsets),
2236 p_meshOffsets(orig.p_meshOffsets),
2237 v_spaceOffsets(orig.v_spaceOffsets),
2238 e_spaceOffsets(orig.e_spaceOffsets),
2239 f_spaceOffsets(orig.f_spaceOffsets),
2240 p_spaceOffsets(orig.p_spaceOffsets),
2241 el_dof(orig.el_dof ? new Table(*orig.el_dof) : NULL),
2242 bel_dof(orig.bel_dof ? new Table(*orig.bel_dof) : NULL),
2243 el_to_patch(orig.el_to_patch),
2244 bel_to_patch(orig.bel_to_patch),
2245 el_to_IJK(orig.el_to_IJK),
2246 bel_to_IJK(orig.bel_to_IJK),
2247 patches(orig.patches.Size()), // patches are copied in the body
2248 num_structured_patches(orig.num_structured_patches),
2249 patchCP(orig.patchCP),
2250 kvf(orig.kvf),
2251 kvf_coarse(orig.kvf_coarse),
2252 dof2patch(orig.dof2patch)
2253{
2254 // Copy the knot vectors:
2255 for (int i = 0; i < knotVectors.Size(); i++)
2256 {
2257 knotVectors[i] = new KnotVector(*orig.knotVectors[i]);
2258 }
2259 CreateComprehensiveKV();
2260
2261 // Copy the patches:
2262 for (int p = 0; p < patches.Size(); p++)
2263 {
2264 patches[p] = new NURBSPatch(*orig.patches[p]);
2265 }
2266}
2267
2268NURBSExtension::NURBSExtension(std::istream &input, bool spacing)
2269{
2270 // Read topology
2271 patchTopo = new Mesh;
2272 patchTopo->LoadPatchTopo(input, edge_to_ukv);
2273
2274 Load(input, spacing);
2275}
2276
2277void NURBSExtension::Load(std::istream &input, bool spacing)
2278{
2279 own_topo = true;
2280
2281 CheckPatches();
2282 // CheckBdrPatches();
2283
2284 skip_comment_lines(input, '#');
2285
2286 // Read knotvectors or patches
2287 string ident;
2288 input >> ws >> ident; // 'knotvectors' or 'patches'
2289 if (ident == "knotvectors")
2290 {
2291 input >> NumOfKnotVectors;
2292 knotVectors.SetSize(NumOfKnotVectors);
2293 for (int i = 0; i < NumOfKnotVectors; i++)
2294 {
2295 knotVectors[i] = new KnotVector(input);
2296 }
2297
2298 if (spacing) // Read spacing formulas for knotvectors
2299 {
2300 input >> ws >> ident; // 'spacing' or 'refinements'
2301
2302 if (ident == "refinements")
2303 {
2304 ref_factors.SetSize(Dimension());
2305 for (int i=0; i<Dimension(); ++i)
2306 {
2307 input >> ref_factors[i];
2308 }
2309
2310 input >> ws >> ident; // 'spacing'
2311 }
2312
2313 if (ident == "knotvector_refinements")
2314 {
2315 kvf.resize(NumOfKnotVectors);
2316 for (int i=0; i<NumOfKnotVectors; ++i)
2317 {
2318 int nf;
2319 input >> nf;
2320 kvf[i].SetSize(nf);
2321 for (int j=0; j<nf; ++j)
2322 {
2323 input >> kvf[i][j];
2324 }
2325 }
2326
2327 input >> ws >> ident; // 'spacing'
2328 }
2329
2330 MFEM_VERIFY(ident == "spacing",
2331 "Spacing formula section missing from NURBS mesh file");
2332
2333 int numSpacing = 0;
2334 input >> numSpacing;
2335 for (int j = 0; j < numSpacing; j++)
2336 {
2337 int ki, spacingType, numIntParam, numRealParam;
2338 input >> ki >> spacingType >> numIntParam >> numRealParam;
2339
2340 MFEM_VERIFY(0 <= ki && ki < NumOfKnotVectors,
2341 "Invalid knotvector index");
2342 MFEM_VERIFY(numIntParam >= 0 && numRealParam >= 0,
2343 "Invalid number of parameters in KnotVector");
2344
2345 Array<int> ipar(numIntParam);
2346 Vector dpar(numRealParam);
2347
2348 for (int i=0; i<numIntParam; ++i)
2349 {
2350 input >> ipar[i];
2351 }
2352
2353 for (int i=0; i<numRealParam; ++i)
2354 {
2355 input >> dpar[i];
2356 }
2357
2358 const SpacingType s = (SpacingType) spacingType;
2359 knotVectors[ki]->spacing = GetSpacingFunction(s, ipar, dpar);
2360 }
2361 }
2362 }
2363 else if (ident == "patches")
2364 {
2365 patches.SetSize(GetNP());
2366 for (int p = 0; p < patches.Size(); p++)
2367 {
2368 skip_comment_lines(input, '#');
2369 patches[p] = new NURBSPatch(input);
2370 }
2371
2372 NumOfKnotVectors = 0;
2373 for (int i = 0; i < patchTopo->GetNEdges(); i++)
2374 if (NumOfKnotVectors < KnotInd(i))
2375 {
2376 NumOfKnotVectors = KnotInd(i);
2377 }
2378 NumOfKnotVectors++;
2379 knotVectors.SetSize(NumOfKnotVectors);
2380 knotVectors = NULL;
2381
2382 Array<int> edges, oedge;
2383 for (int p = 0; p < patches.Size(); p++)
2384 {
2385 if (Dimension() == 1)
2386 {
2387 if (knotVectors[KnotInd(p)] == NULL)
2388 {
2389 knotVectors[KnotInd(p)] =
2390 new KnotVector(*patches[p]->GetKV(0));
2391 }
2392 }
2393 else if (Dimension() == 2)
2394 {
2395 patchTopo->GetElementEdges(p, edges, oedge);
2396 if (knotVectors[KnotInd(edges[0])] == NULL)
2397 {
2398 knotVectors[KnotInd(edges[0])] =
2399 new KnotVector(*patches[p]->GetKV(0));
2400 }
2401 if (knotVectors[KnotInd(edges[1])] == NULL)
2402 {
2403 knotVectors[KnotInd(edges[1])] =
2404 new KnotVector(*patches[p]->GetKV(1));
2405 }
2406 }
2407 else if (Dimension() == 3)
2408 {
2409 patchTopo->GetElementEdges(p, edges, oedge);
2410 if (knotVectors[KnotInd(edges[0])] == NULL)
2411 {
2412 knotVectors[KnotInd(edges[0])] =
2413 new KnotVector(*patches[p]->GetKV(0));
2414 }
2415 if (knotVectors[KnotInd(edges[3])] == NULL)
2416 {
2417 knotVectors[KnotInd(edges[3])] =
2418 new KnotVector(*patches[p]->GetKV(1));
2419 }
2420 if (knotVectors[KnotInd(edges[8])] == NULL)
2421 {
2422 knotVectors[KnotInd(edges[8])] =
2423 new KnotVector(*patches[p]->GetKV(2));
2424 }
2425 }
2426 }
2427 }
2428 else
2429 {
2430 MFEM_ABORT("invalid section: " << ident);
2431 }
2432
2433 CreateComprehensiveKV();
2434
2435 SetOrdersFromKnotVectors();
2436
2437 GenerateOffsets();
2438 CountElements();
2439 CountBdrElements();
2440 // NumOfVertices, NumOfElements, NumOfBdrElements, NumOfDofs
2441
2442 skip_comment_lines(input, '#');
2443
2444 // Check for a list of mesh elements
2445 if (patches.Size() == 0)
2446 {
2447 input >> ws >> ident;
2448 }
2449 if (patches.Size() == 0 && ident == "mesh_elements")
2450 {
2451 input >> NumOfActiveElems;
2452 activeElem.SetSize(GetGNE());
2453 activeElem = false;
2454 int glob_elem;
2455 for (int i = 0; i < NumOfActiveElems; i++)
2456 {
2457 input >> glob_elem;
2458 activeElem[glob_elem] = true;
2459 }
2460
2461 skip_comment_lines(input, '#');
2462 input >> ws >> ident;
2463 }
2464 else
2465 {
2466 NumOfActiveElems = NumOfElements;
2467 activeElem.SetSize(NumOfElements);
2468 activeElem = true;
2469 }
2470
2471 GenerateActiveVertices();
2472 InitDofMap();
2473 GenerateElementDofTable();
2474 GenerateActiveBdrElems();
2475 GenerateBdrElementDofTable();
2476
2477 // periodic
2478 if (ident == "periodic")
2479 {
2480 master.Load(input);
2481 slave.Load(input);
2482
2483 skip_comment_lines(input, '#');
2484 input >> ws >> ident;
2485 }
2486
2487 if (patches.Size() == 0)
2488 {
2489 // weights
2490 if (ident == "weights")
2491 {
2492 weights.Load(input, GetNDof());
2493 }
2494 else // e.g. ident = "unitweights" or "autoweights"
2495 {
2496 weights.SetSize(GetNDof());
2497 weights = 1.0;
2498 }
2499 }
2500
2501 // periodic
2502 ConnectBoundaries();
2503}
2504
2505NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder)
2506{
2507 patchTopo = parent->patchTopo;
2508 own_topo = false;
2509
2510 parent->edge_to_ukv.Copy(edge_to_ukv);
2511
2512 NumOfKnotVectors = parent->GetNKV();
2513 knotVectors.SetSize(NumOfKnotVectors);
2514 knotVectorsCompr.SetSize(parent->GetNP()*parent->Dimension());
2515 const Array<int> &pOrders = parent->GetOrders();
2516 for (int i = 0; i < NumOfKnotVectors; i++)
2517 {
2518 if (newOrder > pOrders[i])
2519 {
2520 knotVectors[i] =
2521 parent->GetKnotVector(i)->DegreeElevate(newOrder - pOrders[i]);
2522 }
2523 else
2524 {
2525 knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
2526 }
2527 }
2528 CreateComprehensiveKV();
2529
2530 // copy some data from parent
2531 NumOfElements = parent->NumOfElements;
2532 NumOfBdrElements = parent->NumOfBdrElements;
2533
2534 SetOrdersFromKnotVectors();
2535
2536 GenerateOffsets(); // dof offsets will be different from parent
2537
2538 NumOfActiveVertices = parent->NumOfActiveVertices;
2539 NumOfActiveElems = parent->NumOfActiveElems;
2540 NumOfActiveBdrElems = parent->NumOfActiveBdrElems;
2541 parent->activeVert.Copy(activeVert);
2542 InitDofMap();
2543 parent->activeElem.Copy(activeElem);
2544 parent->activeBdrElem.Copy(activeBdrElem);
2545
2546 GenerateElementDofTable();
2547 GenerateBdrElementDofTable();
2548
2549 weights.SetSize(GetNDof());
2550 weights = 1.0;
2551
2552 // periodic
2553 parent->master.Copy(master);
2554 parent->slave.Copy(slave);
2555 ConnectBoundaries();
2556}
2557
2558NURBSExtension::NURBSExtension(NURBSExtension *parent,
2559 const Array<int> &newOrders, Mode mode)
2560 : mode(mode)
2561{
2562 newOrders.Copy(mOrders);
2563 SetOrderFromOrders();
2564
2565 patchTopo = parent->patchTopo;
2566 own_topo = false;
2567
2568 parent->edge_to_ukv.Copy(edge_to_ukv);
2569
2570 NumOfKnotVectors = parent->GetNKV();
2571 MFEM_VERIFY(mOrders.Size() == NumOfKnotVectors, "invalid newOrders array");
2572 knotVectors.SetSize(NumOfKnotVectors);
2573 const Array<int> &pOrders = parent->GetOrders();
2574
2575 for (int i = 0; i < NumOfKnotVectors; i++)
2576 {
2577 if (mOrders[i] > pOrders[i])
2578 {
2579 knotVectors[i] =
2580 parent->GetKnotVector(i)->DegreeElevate(mOrders[i] - pOrders[i]);
2581 }
2582 else
2583 {
2584 knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
2585 }
2586 }
2587 CreateComprehensiveKV();
2588
2589 // copy some data from parent
2590 NumOfElements = parent->NumOfElements;
2591 NumOfBdrElements = parent->NumOfBdrElements;
2592
2593 GenerateOffsets(); // dof offsets will be different from parent
2594
2595 NumOfActiveVertices = parent->NumOfActiveVertices;
2596 NumOfActiveElems = parent->NumOfActiveElems;
2597 NumOfActiveBdrElems = parent->NumOfActiveBdrElems;
2598 parent->activeVert.Copy(activeVert);
2599 InitDofMap();
2600 parent->activeElem.Copy(activeElem);
2601 parent->activeBdrElem.Copy(activeBdrElem);
2602
2603 GenerateElementDofTable();
2604 GenerateBdrElementDofTable();
2605
2606 weights.SetSize(GetNDof());
2607 weights = 1.0;
2608
2609 parent->master.Copy(master);
2610 parent->slave.Copy(slave);
2611 ConnectBoundaries();
2612}
2613
2614NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces)
2615{
2616 NURBSExtension *parent = mesh_array[0]->NURBSext;
2617
2618 if (!parent->own_topo)
2619 {
2620 mfem_error("NURBSExtension::NURBSExtension :\n"
2621 " parent does not own the patch topology!");
2622 }
2623 patchTopo = parent->patchTopo;
2624 own_topo = true;
2625 parent->own_topo = false;
2626
2627 parent->edge_to_ukv.Copy(edge_to_ukv);
2628
2629 parent->GetOrders().Copy(mOrders);
2630 mOrder = parent->GetOrder();
2631
2632 NumOfKnotVectors = parent->GetNKV();
2633 knotVectors.SetSize(NumOfKnotVectors);
2634 for (int i = 0; i < NumOfKnotVectors; i++)
2635 {
2636 knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
2637 }
2638 CreateComprehensiveKV();
2639
2640 GenerateOffsets();
2641 CountElements();
2642 CountBdrElements();
2643
2644 // assuming the meshes define a partitioning of all the elements
2645 NumOfActiveElems = NumOfElements;
2646 activeElem.SetSize(NumOfElements);
2647 activeElem = true;
2648
2649 GenerateActiveVertices();
2650 InitDofMap();
2651 GenerateElementDofTable();
2652 GenerateActiveBdrElems();
2653 GenerateBdrElementDofTable();
2654
2655 weights.SetSize(GetNDof());
2656 MergeWeights(mesh_array, num_pieces);
2657}
2658
2659NURBSExtension::NURBSExtension(const Mesh *patch_topology,
2660 const Array<const NURBSPatch*> &patches_)
2661{
2662 // Basic topology checks
2663 MFEM_VERIFY(patches_.Size() > 0, "Must have at least one patch");
2664 MFEM_VERIFY(patches_.Size() == patch_topology->GetNE(),
2665 "Number of patches must equal number of elements in patch_topology");
2666
2667 // Copy patch_topology mesh and NURBSPatch(es)
2668 patchTopo = new Mesh( *patch_topology );
2669 patches.SetSize(patches_.Size());
2670 for (int p = 0; p < patches.Size(); p++)
2671 {
2672 patches[p] = new NURBSPatch(*patches_[p]);
2673 }
2674
2675 Array<int> ukv_to_rpkv;
2676 patchTopo->GetEdgeToUniqueKnotvector(edge_to_ukv, ukv_to_rpkv);
2677 own_topo = true;
2678
2679 CheckPatches(); // This is checking the edge_to_ukv mapping
2680
2681 // Set number of unique (not comprehensive) knot vectors
2682 NumOfKnotVectors = ukv_to_rpkv.Size();
2683 knotVectors.SetSize(NumOfKnotVectors);
2684 knotVectors = NULL;
2685
2686 // Assign the unique knot vectors from patches
2687 for (int i = 0; i < NumOfKnotVectors; i++)
2688 {
2689 // pkv = p*dim + d for an arbitrarily chosen patch p,
2690 // in its reference direction d
2691 const int pkv = ukv_to_rpkv[i];
2692 const int p = pkv / Dimension();
2693 const int d = pkv % Dimension();
2694 knotVectors[i] = new KnotVector(*patches[p]->GetKV(d));
2695 }
2696
2697 CreateComprehensiveKV();
2698 SetOrdersFromKnotVectors();
2699
2700 GenerateOffsets();
2701 CountElements();
2702 CountBdrElements();
2703
2704 NumOfActiveElems = NumOfElements;
2705 activeElem.SetSize(NumOfElements);
2706 activeElem = true;
2707
2708 GenerateActiveVertices();
2709 InitDofMap();
2710 GenerateElementDofTable();
2711 GenerateActiveBdrElems();
2712 GenerateBdrElementDofTable();
2713
2714 ConnectBoundaries();
2715}
2716
2717NURBSExtension::~NURBSExtension()
2718{
2719 if (bel_dof) { delete bel_dof; }
2720 if (el_dof) { delete el_dof; }
2721
2722 for (int i = 0; i < knotVectors.Size(); i++)
2723 {
2724 delete knotVectors[i];
2725 }
2726
2727 for (int i = 0; i < knotVectorsCompr.Size(); i++)
2728 {
2729 delete knotVectorsCompr[i];
2730 }
2731
2732 for (int i = 0; i < patches.Size(); i++)
2733 {
2734 delete patches[i];
2735 }
2736
2737 if (own_topo)
2738 {
2739 delete patchTopo;
2740 }
2741}
2742
2743void NURBSExtension::Print(std::ostream &os, const std::string &comments) const
2744{
2745 Array<int> kvSpacing;
2746 if (patches.Size() == 0)
2747 {
2748 for (int i = 0; i < NumOfKnotVectors; i++)
2749 {
2750 if (knotVectors[i]->spacing) { kvSpacing.Append(i); }
2751 }
2752 }
2753
2754 bool writeSpacing = false;
2755 bool writeRefinements = false;
2756 if (patchTopo->ncmesh)
2757 {
2758 // Writing MFEM NURBS NC-patch mesh v1.0
2759 patchTopo->ncmesh->Print(os, comments, true);
2760 patchTopo->PrintTopoEdges(os, edge_to_ukv, true);
2761 writeSpacing = true;
2762 writeRefinements = true;
2763 }
2764 else
2765 {
2766 const int version = kvSpacing.Size() > 0 ? 11 : 10; // v1.0 or v1.1
2767 if (version == 11) { writeSpacing = true; }
2768 patchTopo->PrintTopo(os, edge_to_ukv, version, comments);
2769 }
2770
2771 if (patches.Size() == 0)
2772 {
2773 os << "\nknotvectors\n" << NumOfKnotVectors << '\n';
2774 for (int i = 0; i < NumOfKnotVectors; i++)
2775 {
2776 knotVectors[i]->Print(os);
2777 }
2778
2779 if (writeRefinements && ref_factors.Size() > 0)
2780 {
2781 os << "\nrefinements\n";
2782 for (int i=0; i<ref_factors.Size(); ++i)
2783 {
2784 os << ref_factors[i];
2785 if (i == ref_factors.Size() - 1) { os << '\n'; }
2786 else { os << ' '; }
2787 }
2788 }
2789
2790 if (kvf.size() > 0)
2791 {
2792 MFEM_VERIFY(kvf.size() == (size_t) NumOfKnotVectors, "");
2793 os << "\nknotvector_refinements\n";
2794 for (size_t i=0; i<kvf.size(); ++i)
2795 {
2796 if (kvf_coarse.size() > 0)
2797 {
2798 os << kvf_coarse[i].Size();
2799 for (int j=0; j<kvf_coarse[i].Size(); ++j)
2800 {
2801 os << ' ' << kvf_coarse[i][j];
2802 }
2803 }
2804 else
2805 {
2806 os << kvf[i].Size();
2807 for (int j=0; j<kvf[i].Size(); ++j)
2808 {
2809 os << ' ' << kvf[i][j];
2810 }
2811 }
2812 os << '\n';
2813 }
2814 }
2815
2816 if (writeSpacing)
2817 {
2818 os << "\nspacing\n" << kvSpacing.Size() << '\n';
2819 for (auto kv : kvSpacing)
2820 {
2821 os << kv << " ";
2822 knotVectors[kv]->spacing->Print(os);
2823 }
2824 }
2825
2826 if (NumOfActiveElems < NumOfElements)
2827 {
2828 os << "\nmesh_elements\n" << NumOfActiveElems << '\n';
2829 for (int i = 0; i < NumOfElements; i++)
2830 if (activeElem[i])
2831 {
2832 os << i << '\n';
2833 }
2834 }
2835
2836 os << "\nweights\n";
2837 weights.Print(os, 1);
2838 }
2839 else
2840 {
2841 os << "\npatches\n";
2842 for (int p = 0; p < patches.Size(); p++)
2843 {
2844 os << "\n# patch " << p << "\n\n";
2845 patches[p]->Print(os);
2846 }
2847 }
2848}
2849
2850void NURBSExtension::PrintCharacteristics(std::ostream &os) const
2851{
2852 os <<
2853 "NURBS Mesh entity sizes:\n"
2854 "Dimension = " << Dimension() << "\n"
2855 "Unique Orders = ";
2856 Array<int> unique_orders(mOrders);
2857 unique_orders.Sort();
2858 unique_orders.Unique();
2859 unique_orders.Print(os, unique_orders.Size());
2860 os <<
2861 "NumOfKnotVectors = " << GetNKV() << "\n"
2862 "NumOfPatches = " << GetNP() << "\n"
2863 "NumOfBdrPatches = " << GetNBP() << "\n"
2864 "NumOfVertices = " << GetGNV() << "\n"
2865 "NumOfElements = " << GetGNE() << "\n"
2866 "NumOfBdrElements = " << GetGNBE() << "\n"
2867 "NumOfDofs = " << GetNTotalDof() << "\n"
2868 "NumOfActiveVertices = " << GetNV() << "\n"
2869 "NumOfActiveElems = " << GetNE() << "\n"
2870 "NumOfActiveBdrElems = " << GetNBE() << "\n"
2871 "NumOfActiveDofs = " << GetNDof() << '\n';
2872 for (int i = 0; i < NumOfKnotVectors; i++)
2873 {
2874 os << ' ' << i + 1 << ") ";
2875 knotVectors[i]->Print(os);
2876 }
2877 os << endl;
2878}
2879
2880void NURBSExtension::PrintFunctions(const char *basename, int samples) const
2881{
2882 std::ofstream os;
2883 for (int i = 0; i < NumOfKnotVectors; i++)
2884 {
2885 std::ostringstream filename;
2886 filename << basename << "_" << i << ".dat";
2887 os.open(filename.str().c_str());
2888 knotVectors[i]->PrintFunctions(os,samples);
2889 os.close();
2890 }
2891}
2892
2893void NURBSExtension::InitDofMap()
2894{
2895 master.SetSize(0);
2896 slave.SetSize(0);
2897 d_to_d.SetSize(0);
2898}
2899
2900void NURBSExtension::ConnectBoundaries(Array<int> &bnds0, Array<int> &bnds1)
2901{
2902 bnds0.Copy(master);
2903 bnds1.Copy(slave);
2904 ConnectBoundaries();
2905}
2906
2907void NURBSExtension::ConnectBoundaries()
2908{
2909 if (master.Size() != slave.Size())
2910 {
2911 mfem_error("NURBSExtension::ConnectBoundaries() boundary lists not of equal size");
2912 }
2913 if (master.Size() == 0 ) { return; }
2914
2915 // Initialize d_to_d
2916 d_to_d.SetSize(NumOfDofs);
2917 for (int i = 0; i < NumOfDofs; i++) { d_to_d[i] = i; }
2918
2919 // Connect
2920 for (int i = 0; i < master.Size(); i++)
2921 {
2922 int bnd0 = -1, bnd1 = -1;
2923 for (int b = 0; b < GetNBP(); b++)
2924 {
2925 if (master[i] == patchTopo->GetBdrAttribute(b)) { bnd0 = b; }
2926 if (slave[i]== patchTopo->GetBdrAttribute(b)) { bnd1 = b; }
2927 }
2928 MFEM_VERIFY(bnd0 != -1,"Bdr 0 not found");
2929 MFEM_VERIFY(bnd1 != -1,"Bdr 1 not found");
2930
2931 if (Dimension() == 1)
2932 {
2933 ConnectBoundaries1D(bnd0, bnd1);
2934 }
2935 else if (Dimension() == 2)
2936 {
2937 ConnectBoundaries2D(bnd0, bnd1);
2938 }
2939 else
2940 {
2941 ConnectBoundaries3D(bnd0, bnd1);
2942 }
2943 }
2944
2945 // Clean d_to_d
2946 Array<int> tmp(d_to_d.Size()+1);
2947 tmp = 0;
2948
2949 for (int i = 0; i < d_to_d.Size(); i++)
2950 {
2951 tmp[d_to_d[i]] = 1;
2952 }
2953
2954 int cnt = 0;
2955 for (int i = 0; i < tmp.Size(); i++)
2956 {
2957 if (tmp[i] == 1) { tmp[i] = cnt++; }
2958 }
2959 NumOfDofs = cnt;
2960
2961 for (int i = 0; i < d_to_d.Size(); i++)
2962 {
2963 d_to_d[i] = tmp[d_to_d[i]];
2964 }
2965
2966 // Finalize
2967 if (el_dof) { delete el_dof; }
2968 if (bel_dof) { delete bel_dof; }
2969 GenerateElementDofTable();
2970 GenerateBdrElementDofTable();
2971}
2972
2973void NURBSExtension::ConnectBoundaries1D(int bnd0, int bnd1)
2974{
2975 NURBSPatchMap p2g0(this);
2976 NURBSPatchMap p2g1(this);
2977
2978 int okv0[1],okv1[1];
2979 const KnotVector *kv0[1],*kv1[1];
2980
2981 p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0);
2982 p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1);
2983
2984 d_to_d[p2g0(0)] = d_to_d[p2g1(0)];
2985}
2986
2987void NURBSExtension::ConnectBoundaries2D(int bnd0, int bnd1)
2988{
2989 NURBSPatchMap p2g0(this);
2990 NURBSPatchMap p2g1(this);
2991
2992 int okv0[1],okv1[1];
2993 const KnotVector *kv0[1],*kv1[1];
2994
2995 p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0);
2996 p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1);
2997
2998 int nx = p2g0.nx();
2999 int nks0 = kv0[0]->GetNKS();
3000
3001#ifdef MFEM_DEBUG
3002 bool compatible = true;
3003 if (p2g0.nx() != p2g1.nx()) { compatible = false; }
3004 if (kv0[0]->GetNKS() != kv1[0]->GetNKS()) { compatible = false; }
3005 if (kv0[0]->GetOrder() != kv1[0]->GetOrder()) { compatible = false; }
3006
3007 if (!compatible)
3008 {
3009 mfem::out<<p2g0.nx()<<" "<<p2g1.nx()<<endl;
3010 mfem::out<<kv0[0]->GetNKS()<<" "<<kv1[0]->GetNKS()<<endl;
3011 mfem::out<<kv0[0]->GetOrder()<<" "<<kv1[0]->GetOrder()<<endl;
3012 mfem_error("NURBS boundaries not compatible");
3013 }
3014#endif
3015
3016 for (int i = 0; i < nks0; i++)
3017 {
3018 if (kv0[0]->isElement(i))
3019 {
3020 if (!kv1[0]->isElement(i)) { mfem_error("isElement does not match"); }
3021 for (int ii = 0; ii <= kv0[0]->GetOrder(); ii++)
3022 {
3023 int ii0 = (okv0[0] >= 0) ? (i+ii) : (nx-i-ii);
3024 int ii1 = (okv1[0] >= 0) ? (i+ii) : (nx-i-ii);
3025
3026 d_to_d[p2g0(ii0)] = d_to_d[p2g1(ii1)];
3027 }
3028
3029 }
3030 }
3031}
3032
3033void NURBSExtension::ConnectBoundaries3D(int bnd0, int bnd1)
3034{
3035 NURBSPatchMap p2g0(this);
3036 NURBSPatchMap p2g1(this);
3037
3038 int okv0[2],okv1[2];
3039 const KnotVector *kv0[2],*kv1[2];
3040
3041 p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0);
3042 p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1);
3043
3044 int nx = p2g0.nx();
3045 int ny = p2g0.ny();
3046
3047 int nks0 = kv0[0]->GetNKS();
3048 int nks1 = kv0[1]->GetNKS();
3049
3050#ifdef MFEM_DEBUG
3051 bool compatible = true;
3052 if (p2g0.nx() != p2g1.nx()) { compatible = false; }
3053 if (p2g0.ny() != p2g1.ny()) { compatible = false; }
3054
3055 if (kv0[0]->GetNKS() != kv1[0]->GetNKS()) { compatible = false; }
3056 if (kv0[1]->GetNKS() != kv1[1]->GetNKS()) { compatible = false; }
3057
3058 if (kv0[0]->GetOrder() != kv1[0]->GetOrder()) { compatible = false; }
3059 if (kv0[1]->GetOrder() != kv1[1]->GetOrder()) { compatible = false; }
3060
3061 if (!compatible)
3062 {
3063 mfem::out<<p2g0.nx()<<" "<<p2g1.nx()<<endl;
3064 mfem::out<<p2g0.ny()<<" "<<p2g1.ny()<<endl;
3065
3066 mfem::out<<kv0[0]->GetNKS()<<" "<<kv1[0]->GetNKS()<<endl;
3067 mfem::out<<kv0[1]->GetNKS()<<" "<<kv1[1]->GetNKS()<<endl;
3068
3069 mfem::out<<kv0[0]->GetOrder()<<" "<<kv1[0]->GetOrder()<<endl;
3070 mfem::out<<kv0[1]->GetOrder()<<" "<<kv1[1]->GetOrder()<<endl;
3071 mfem_error("NURBS boundaries not compatible");
3072 }
3073#endif
3074
3075 for (int j = 0; j < nks1; j++)
3076 {
3077 if (kv0[1]->isElement(j))
3078 {
3079 if (!kv1[1]->isElement(j)) { mfem_error("isElement does not match #1"); }
3080 for (int i = 0; i < nks0; i++)
3081 {
3082 if (kv0[0]->isElement(i))
3083 {
3084 if (!kv1[0]->isElement(i)) { mfem_error("isElement does not match #0"); }
3085 for (int jj = 0; jj <= kv0[1]->GetOrder(); jj++)
3086 {
3087 int jj0 = (okv0[1] >= 0) ? (j+jj) : (ny-j-jj);
3088 int jj1 = (okv1[1] >= 0) ? (j+jj) : (ny-j-jj);
3089
3090 for (int ii = 0; ii <= kv0[0]->GetOrder(); ii++)
3091 {
3092 int ii0 = (okv0[0] >= 0) ? (i+ii) : (nx-i-ii);
3093 int ii1 = (okv1[0] >= 0) ? (i+ii) : (nx-i-ii);
3094
3095 d_to_d[p2g0(ii0,jj0)] = d_to_d[p2g1(ii1,jj1)];
3096 }
3097 }
3098 }
3099 }
3100 }
3101 }
3102}
3103
3104void NURBSExtension::GenerateActiveVertices()
3105{
3106 int vert[8], nv, g_el, nx, ny, nz, dim = Dimension();
3107
3108 NURBSPatchMap p2g(this);
3109 const KnotVector *kv[3];
3110
3111 g_el = 0;
3112 activeVert.SetSize(GetGNV());
3113 activeVert = -1;
3114 for (int p = 0; p < GetNP(); p++)
3115 {
3116 p2g.SetPatchVertexMap(p, kv);
3117
3118 nx = p2g.nx();
3119 ny = (dim >= 2) ? p2g.ny() : 1;
3120 nz = (dim == 3) ? p2g.nz() : 1;
3121
3122 for (int k = 0; k < nz; k++)
3123 {
3124 for (int j = 0; j < ny; j++)
3125 {
3126 for (int i = 0; i < nx; i++)
3127 {
3128 if (activeElem[g_el])
3129 {
3130 if (dim == 1)
3131 {
3132 vert[0] = p2g(i );
3133 vert[1] = p2g(i+1);
3134 nv = 2;
3135 }
3136 else if (dim == 2)
3137 {
3138 vert[0] = p2g(i, j );
3139 vert[1] = p2g(i+1,j );
3140 vert[2] = p2g(i+1,j+1);
3141 vert[3] = p2g(i, j+1);
3142 nv = 4;
3143 }
3144 else
3145 {
3146 vert[0] = p2g(i, j, k);
3147 vert[1] = p2g(i+1,j, k);
3148 vert[2] = p2g(i+1,j+1,k);
3149 vert[3] = p2g(i, j+1,k);
3150
3151 vert[4] = p2g(i, j, k+1);
3152 vert[5] = p2g(i+1,j, k+1);
3153 vert[6] = p2g(i+1,j+1,k+1);
3154 vert[7] = p2g(i, j+1,k+1);
3155 nv = 8;
3156 }
3157
3158 for (int v = 0; v < nv; v++)
3159 {
3160 activeVert[vert[v]] = 1;
3161 }
3162 }
3163 g_el++;
3164 }
3165 }
3166 }
3167 }
3168
3169 NumOfActiveVertices = 0;
3170 for (int i = 0; i < GetGNV(); i++)
3171 if (activeVert[i] == 1)
3172 {
3173 activeVert[i] = NumOfActiveVertices++;
3174 }
3175}
3176
3177void NURBSExtension::GenerateActiveBdrElems()
3178{
3179 int dim = Dimension();
3180 Array<KnotVector *> kv(dim);
3181
3182 activeBdrElem.SetSize(GetGNBE());
3183 if (GetGNE() == GetNE())
3184 {
3185 activeBdrElem = true;
3186 NumOfActiveBdrElems = GetGNBE();
3187 return;
3188 }
3189 activeBdrElem = false;
3190 NumOfActiveBdrElems = 0;
3191 // the mesh will generate the actual boundary including boundary
3192 // elements that are not on boundary patches. we use this for
3193 // visualization of processor boundaries
3194
3195 // TODO: generate actual boundary?
3196}
3197
3198
3199void NURBSExtension::MergeWeights(Mesh *mesh_array[], int num_pieces)
3200{
3201 Array<int> lelem_elem;
3202
3203 for (int i = 0; i < num_pieces; i++)
3204 {
3205 NURBSExtension *lext = mesh_array[i]->NURBSext;
3206
3207 lext->GetElementLocalToGlobal(lelem_elem);
3208
3209 for (int lel = 0; lel < lext->GetNE(); lel++)
3210 {
3211 int gel = lelem_elem[lel];
3212
3213 int nd = el_dof->RowSize(gel);
3214 int *gdofs = el_dof->GetRow(gel);
3215 int *ldofs = lext->el_dof->GetRow(lel);
3216 for (int j = 0; j < nd; j++)
3217 {
3218 weights(gdofs[j]) = lext->weights(ldofs[j]);
3219 }
3220 }
3221 }
3222}
3223
3224void NURBSExtension::MergeGridFunctions(
3225 GridFunction *gf_array[], int num_pieces, GridFunction &merged)
3226{
3227 FiniteElementSpace *gfes = merged.FESpace();
3228 Array<int> lelem_elem, dofs;
3229 Vector lvec;
3230
3231 for (int i = 0; i < num_pieces; i++)
3232 {
3233 FiniteElementSpace *lfes = gf_array[i]->FESpace();
3234 NURBSExtension *lext = lfes->GetMesh()->NURBSext;
3235
3236 lext->GetElementLocalToGlobal(lelem_elem);
3237
3238 for (int lel = 0; lel < lext->GetNE(); lel++)
3239 {
3240 lfes->GetElementVDofs(lel, dofs);
3241 gf_array[i]->GetSubVector(dofs, lvec);
3242
3243 gfes->GetElementVDofs(lelem_elem[lel], dofs);
3244 merged.SetSubVector(dofs, lvec);
3245 }
3246 }
3247}
3248
3249void NURBSExtension::CheckPatches()
3250{
3251 if (Dimension() == 1 ) { return; }
3252
3253 Array<int> edges, oedge;
3254
3255 for (int p = 0; p < GetNP(); p++)
3256 {
3257 patchTopo->GetElementEdges(p, edges, oedge);
3258
3259 for (int i = 0; i < edges.Size(); i++)
3260 {
3261 edges[i] = edge_to_ukv[edges[i]];
3262 if (oedge[i] < 0)
3263 {
3264 edges[i] = -1 - edges[i];
3265 }
3266 }
3267
3268 if ((Dimension() == 2 &&
3269 (edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) ||
3270
3271 (Dimension() == 3 &&
3272 (edges[0] != edges[2] || edges[0] != edges[4] ||
3273 edges[0] != edges[6] || edges[1] != edges[3] ||
3274 edges[1] != edges[5] || edges[1] != edges[7] ||
3275 edges[8] != edges[9] || edges[8] != edges[10] ||
3276 edges[8] != edges[11])))
3277 {
3278 mfem::err << "NURBSExtension::CheckPatch (patch = " << p
3279 << ")\n Inconsistent edge-to-knotvector mapping!";
3280 mfem_error();
3281 }
3282 }
3283}
3284
3285void NURBSExtension::CheckBdrPatches()
3286{
3287 Array<int> edges;
3288 Array<int> oedge;
3289
3290 for (int p = 0; p < GetNBP(); p++)
3291 {
3292 patchTopo->GetBdrElementEdges(p, edges, oedge);
3293
3294 for (int i = 0; i < edges.Size(); i++)
3295 {
3296 edges[i] = edge_to_ukv[edges[i]];
3297 if (oedge[i] < 0)
3298 {
3299 edges[i] = -1 - edges[i];
3300 }
3301 }
3302
3303 if ((Dimension() == 2 && (edges[0] < 0)) ||
3304 (Dimension() == 3 && (edges[0] < 0 || edges[1] < 0)))
3305 {
3306 mfem::err << "NURBSExtension::CheckBdrPatch (boundary patch = "
3307 << p << ") : Bad orientation!\n";
3308 mfem_error();
3309 }
3310 }
3311}
3312
3313void NURBSExtension::CheckKVDirection(int p, Array <int> &kvdir)
3314{
3315 // patchTopo->GetElementEdges is not yet implemented for 1D
3316 MFEM_VERIFY(Dimension()>1, "1D not yet implemented.");
3317
3318 kvdir.SetSize(Dimension());
3319 kvdir = 0;
3320
3321 Array<int> patchvert, edges, orient, edgevert;
3322
3323 patchTopo->GetElementVertices(p, patchvert);
3324
3325 patchTopo->GetElementEdges(p, edges, orient);
3326
3327 // Compare the vertices of the patches with the vertices of the knotvectors of knot2dge
3328 // Based on the match the orientation will be a 1 or a -1
3329 // -1: direction is flipped
3330 // 1: direction is not flipped
3331
3332 for (int i = 0; i < edges.Size(); i++)
3333 {
3334 // First side
3335 patchTopo->GetEdgeVertices(edges[i], edgevert);
3336 const int ks = KnotSign(edges[i]);
3337 if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[1])
3338 {
3339 kvdir[0] = ks;
3340 }
3341
3342 if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[0])
3343 {
3344 kvdir[0] = -ks;
3345 }
3346
3347 // Second side
3348 if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[3])
3349 {
3350 kvdir[1] = ks;
3351 }
3352
3353 if (edgevert[0] == patchvert[3] && edgevert[1] == patchvert[0])
3354 {
3355 kvdir[1] = -ks;
3356 }
3357 }
3358
3359 if (Dimension() == 3)
3360 {
3361 // Third side
3362 for (int i = 0; i < edges.Size(); i++)
3363 {
3364 patchTopo->GetEdgeVertices(edges[i], edgevert);
3365 const int ks = KnotSign(edges[i]);
3366
3367 if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[4])
3368 {
3369 kvdir[2] = ks;
3370 }
3371
3372 if (edgevert[0] == patchvert[4] && edgevert[1] == patchvert[0])
3373 {
3374 kvdir[2] = -ks;
3375 }
3376 }
3377 }
3378
3379 MFEM_VERIFY(kvdir.Find(0) == -1, "Could not find direction of knotvector.");
3380}
3381
3382void NURBSExtension::CreateComprehensiveKV()
3383{
3384 Array<int> edges, orient, kvdir;
3385 Array<int> e(Dimension());
3386
3387 // 1D: comprehensive and unique KV are the same
3388 if (Dimension() == 1)
3389 {
3390 knotVectorsCompr.SetSize(GetNKV());
3391 for (int i = 0; i < GetNKV(); i++)
3392 {
3393 knotVectorsCompr[i] = new KnotVector(*(KnotVec(i)));
3394 }
3395 return;
3396 }
3397 else if (Dimension() == 2)
3398 {
3399 knotVectorsCompr.SetSize(GetNP()*Dimension());
3400 e[0] = 0;
3401 e[1] = 1;
3402 }
3403 else if (Dimension() == 3)
3404 {
3405 knotVectorsCompr.SetSize(GetNP()*Dimension());
3406 e[0] = 0;
3407 e[1] = 3;
3408 e[2] = 8;
3409 }
3410
3411 for (int p = 0; p < GetNP(); p++)
3412 {
3413 CheckKVDirection(p, kvdir);
3414
3415 patchTopo->GetElementEdges(p, edges, orient);
3416
3417 for (int d = 0; d < Dimension(); d++)
3418 {
3419 // Indices in unique and comprehensive sets of the KnotVector
3420 int iun = edges[e[d]];
3421 int icomp = Dimension()*p+d;
3422 knotVectorsCompr[icomp] = new KnotVector(*(KnotVec(iun)));
3423 if (kvdir[d] == -1) { knotVectorsCompr[icomp]->Flip(); }
3424 }
3425 }
3426
3427 MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
3428}
3429
3430void NURBSExtension::UpdateUniqueKV()
3431{
3432 Array<int> e(Dimension());
3433
3434 // 1D: comprehensive and unique KV are the same
3435 if (Dimension() == 1)
3436 {
3437 for (int i = 0; i < GetNKV(); i++)
3438 {
3439 *(KnotVec(i)) = *(knotVectorsCompr[i]);
3440 }
3441 return;
3442 }
3443 else if (Dimension() == 2)
3444 {
3445 e[0] = 0;
3446 e[1] = 1;
3447 }
3448 else if (Dimension() == 3)
3449 {
3450 e[0] = 0;
3451 e[1] = 3;
3452 e[2] = 8;
3453 }
3454
3455 for (int p = 0; p < GetNP(); p++)
3456 {
3457 Array<int> edges, orient, kvdir;
3458
3459 patchTopo->GetElementEdges(p, edges, orient);
3460 CheckKVDirection(p, kvdir);
3461
3462 for (int d = 0; d < Dimension(); d++)
3463 {
3464 bool flip = false;
3465 if (kvdir[d] == -1) { flip = true; }
3466
3467 // Indices in unique and comprehensive sets of the KnotVector
3468 int iun = edges[e[d]];
3469 int icomp = Dimension()*p+d;
3470
3471 // Check if difference in order
3472 int o1 = KnotVec(iun)->GetOrder();
3473 int o2 = knotVectorsCompr[icomp]->GetOrder();
3474 int diffo = abs(o1 - o2);
3475
3476 int ne1 = KnotVec(iun)->GetNE();
3477 int ne2 = knotVectorsCompr[icomp]->GetNE();
3478
3479 if (diffo || ne1 != ne2)
3480 {
3481 // Update reduced set of knotvectors
3482 *(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
3483
3484 // Give correct direction to unique knotvector.
3485 if (flip) { KnotVec(iun)->Flip(); }
3486 }
3487
3488 // Check if difference between knots
3489 Vector diffknot;
3490
3491 if (flip) { knotVectorsCompr[icomp]->Flip(); }
3492
3493 KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diffknot);
3494
3495 if (flip) { knotVectorsCompr[icomp]->Flip(); }
3496
3497 if (diffknot.Size() > 0)
3498 {
3499 // Update reduced set of knotvectors
3500 *(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
3501
3502 // Give correct direction to unique knotvector.
3503 if (flip) {KnotVec(iun)->Flip();}
3504 }
3505 }
3506 }
3507
3508 MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
3509}
3510
3511bool NURBSExtension::ConsistentKVSets()
3512{
3513 // patchTopo->GetElementEdges is not yet implemented for 1D
3514 MFEM_VERIFY(Dimension() > 1, "1D not yet implemented.");
3515
3516 Array<int> edges, orient, kvdir;
3517 Vector diff;
3518
3519 Array<int> e(Dimension());
3520
3521 e[0] = 0;
3522
3523 if (Dimension() == 2)
3524 {
3525 e[1] = 1;
3526 }
3527 else if (Dimension() == 3)
3528 {
3529 e[1] = 3;
3530 e[2] = 8;
3531 }
3532
3533 for (int p = 0; p < GetNP(); p++)
3534 {
3535 patchTopo->GetElementEdges(p, edges, orient);
3536
3537 CheckKVDirection(p, kvdir);
3538
3539 for (int d = 0; d < Dimension(); d++)
3540 {
3541 bool flip = false;
3542 if (kvdir[d] == -1) {flip = true;}
3543
3544 // Indices in unique and comprehensive sets of the KnotVector
3545 int iun = edges[e[d]];
3546 int icomp = Dimension()*p+d;
3547
3548 // Check if KnotVectors are of equal order
3549 int o1 = KnotVec(iun)->GetOrder();
3550 int o2 = knotVectorsCompr[icomp]->GetOrder();
3551 int diffo = abs(o1 - o2);
3552
3553 if (diffo)
3554 {
3555 mfem::out << "\norder of knotVectorsCompr " << d << " of patch " << p;
3556 mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
3557 return false;
3558 }
3559
3560 // Check if Knotvectors have the same knots
3561 if (flip) {knotVectorsCompr[icomp]->Flip();}
3562
3563 KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diff);
3564
3565 if (flip) {knotVectorsCompr[icomp]->Flip();}
3566
3567 if (diff.Size() > 0)
3568 {
3569 mfem::out << "\nknotVectorsCompr " << d << " of patch " << p;
3570 mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
3571 return false;
3572 }
3573 }
3574 }
3575 return true;
3576}
3577
3578void NURBSExtension::GetPatchKnotVectors(int p, Array<KnotVector *> &kv)
3579{
3580 Array<int> edges, orient;
3581
3582 kv.SetSize(Dimension());
3583
3584 if (Dimension() == 1)
3585 {
3586 kv[0] = knotVectorsCompr[Dimension()*p];
3587 }
3588 else if (Dimension() == 2)
3589 {
3590 kv[0] = knotVectorsCompr[Dimension()*p];
3591 kv[1] = knotVectorsCompr[Dimension()*p + 1];
3592 }
3593 else
3594 {
3595 kv[0] = knotVectorsCompr[Dimension()*p];
3596 kv[1] = knotVectorsCompr[Dimension()*p + 1];
3597 kv[2] = knotVectorsCompr[Dimension()*p + 2];
3598 }
3599}
3600
3601void NURBSExtension::GetPatchKnotVectors(int p, Array<const KnotVector *> &kv)
3602const
3603{
3604 kv.SetSize(Dimension());
3605
3606 if (Dimension() == 1)
3607 {
3608 kv[0] = knotVectorsCompr[Dimension()*p];
3609 }
3610 else if (Dimension() == 2)
3611 {
3612 kv[0] = knotVectorsCompr[Dimension()*p];
3613 kv[1] = knotVectorsCompr[Dimension()*p + 1];
3614 }
3615 else
3616 {
3617 kv[0] = knotVectorsCompr[Dimension()*p];
3618 kv[1] = knotVectorsCompr[Dimension()*p + 1];
3619 kv[2] = knotVectorsCompr[Dimension()*p + 2];
3620 }
3621}
3622
3623void NURBSExtension::GetBdrPatchKnotVectors(int bp, Array<KnotVector *> &kv)
3624{
3625 Array<int> edges;
3626 Array<int> orient;
3627
3628 kv.SetSize(Dimension() - 1);
3629
3630 if (Dimension() == 2)
3631 {
3632 patchTopo->GetBdrElementEdges(bp, edges, orient);
3633 kv[0] = KnotVec(edges[0]);
3634 }
3635 else if (Dimension() == 3)
3636 {
3637 patchTopo->GetBdrElementEdges(bp, edges, orient);
3638 kv[0] = KnotVec(edges[0]);
3639 kv[1] = KnotVec(edges[1]);
3640 }
3641}
3642
3643void NURBSExtension::GetBdrPatchKnotVectors(
3644 int bp, Array<const KnotVector *> &kv) const
3645{
3646 Array<int> edges;
3647 Array<int> orient;
3648
3649 kv.SetSize(Dimension() - 1);
3650
3651 if (Dimension() == 2)
3652 {
3653 patchTopo->GetBdrElementEdges(bp, edges, orient);
3654 kv[0] = KnotVec(edges[0]);
3655 }
3656 else if (Dimension() == 3)
3657 {
3658 patchTopo->GetBdrElementEdges(bp, edges, orient);
3659 kv[0] = KnotVec(edges[0]);
3660 kv[1] = KnotVec(edges[1]);
3661 }
3662}
3663
3664void NURBSExtension::SetOrderFromOrders()
3665{
3666 MFEM_VERIFY(mOrders.Size() > 0, "");
3667 mOrder = mOrders[0];
3668 for (int i = 1; i < mOrders.Size(); i++)
3669 {
3670 if (mOrders[i] != mOrder)
3671 {
3672 mOrder = NURBSFECollection::VariableOrder;
3673 return;
3674 }
3675 }
3676}
3677
3678void NURBSExtension::SetOrdersFromKnotVectors()
3679{
3680 mOrders.SetSize(NumOfKnotVectors);
3681 for (int i = 0; i < NumOfKnotVectors; i++)
3682 {
3683 mOrders[i] = knotVectors[i]->GetOrder();
3684 }
3685 SetOrderFromOrders();
3686}
3687
3688void NURBSExtension::GenerateOffsets()
3689{
3690 const int nv = patchTopo->GetNV();
3691 const int ne = patchTopo->GetNEdges();
3692 const int nf = patchTopo->GetNFaces();
3693 const int np = patchTopo->GetNE();
3694 int meshCounter, spaceCounter;
3695
3696 Array<int> edges, orient;
3697
3698 v_meshOffsets.SetSize(nv);
3699 e_meshOffsets.SetSize(ne);
3700 f_meshOffsets.SetSize(nf);
3701 p_meshOffsets.SetSize(np);
3702
3703 v_spaceOffsets.SetSize(nv);
3704 e_spaceOffsets.SetSize(ne);
3705 f_spaceOffsets.SetSize(nf);
3706 p_spaceOffsets.SetSize(np);
3707
3708 // Get vertex offsets
3709 for (meshCounter = 0; meshCounter < nv; meshCounter++)
3710 {
3711 v_meshOffsets[meshCounter] = meshCounter;
3712 v_spaceOffsets[meshCounter] = meshCounter;
3713 }
3714 spaceCounter = meshCounter;
3715
3716 // Get edge offsets
3717 for (int e = 0; e < ne; e++)
3718 {
3719 e_meshOffsets[e] = meshCounter;
3720 e_spaceOffsets[e] = spaceCounter;
3721 meshCounter += KnotVec(e)->GetNE() - 1;
3722 spaceCounter += KnotVec(e)->GetNCP() - 2;
3723 }
3724
3725 // Get face offsets
3726 for (int f = 0; f < nf; f++)
3727 {
3728 f_meshOffsets[f] = meshCounter;
3729 f_spaceOffsets[f] = spaceCounter;
3730
3731 patchTopo->GetFaceEdges(f, edges, orient);
3732
3733 meshCounter +=
3734 (KnotVec(edges[0])->GetNE() - 1) *
3735 (KnotVec(edges[1])->GetNE() - 1);
3736 spaceCounter +=
3737 (KnotVec(edges[0])->GetNCP() - 2) *
3738 (KnotVec(edges[1])->GetNCP() - 2);
3739 }
3740
3741 // Get patch offsets
3742 GetPatchOffsets(meshCounter, spaceCounter);
3743
3744 NumOfVertices = meshCounter;
3745 NumOfDofs = spaceCounter;
3746}
3747
3748void NURBSExtension::GetPatchOffsets(int &meshCounter, int &spaceCounter)
3749{
3750 const int np = patchTopo->GetNE();
3751 const int dim = Dimension();
3752 Array<int> edges, orient;
3753 for (int p = 0; p < np; p++)
3754 {
3755 p_meshOffsets[p] = meshCounter;
3756 p_spaceOffsets[p] = spaceCounter;
3757
3758 if (dim == 1)
3759 {
3760 meshCounter += KnotVec(0)->GetNE() - 1;
3761 spaceCounter += KnotVec(0)->GetNCP() - 2;
3762 }
3763 else if (dim == 2)
3764 {
3765 patchTopo->GetElementEdges(p, edges, orient);
3766 meshCounter +=
3767 (KnotVec(edges[0])->GetNE() - 1) *
3768 (KnotVec(edges[1])->GetNE() - 1);
3769 spaceCounter +=
3770 (KnotVec(edges[0])->GetNCP() - 2) *
3771 (KnotVec(edges[1])->GetNCP() - 2);
3772 }
3773 else
3774 {
3775 patchTopo->GetElementEdges(p, edges, orient);
3776 meshCounter +=
3777 (KnotVec(edges[0])->GetNE() - 1) *
3778 (KnotVec(edges[3])->GetNE() - 1) *
3779 (KnotVec(edges[8])->GetNE() - 1);
3780 spaceCounter +=
3781 (KnotVec(edges[0])->GetNCP() - 2) *
3782 (KnotVec(edges[3])->GetNCP() - 2) *
3783 (KnotVec(edges[8])->GetNCP() - 2);
3784 }
3785 }
3786}
3787
3788void NURBSExtension::CountElements()
3789{
3790 int dim = Dimension();
3791 Array<const KnotVector *> kv(dim);
3792
3793 NumOfElements = 0;
3794 for (int p = 0; p < GetNP(); p++)
3795 {
3796 GetPatchKnotVectors(p, kv);
3797
3798 int ne = kv[0]->GetNE();
3799 for (int d = 1; d < dim; d++)
3800 {
3801 ne *= kv[d]->GetNE();
3802 }
3803
3804 NumOfElements += ne;
3805 }
3806}
3807
3808void NURBSExtension::CountBdrElements()
3809{
3810 int dim = Dimension() - 1;
3811 Array<KnotVector *> kv(dim);
3812
3813 NumOfBdrElements = 0;
3814 for (int p = 0; p < GetNBP(); p++)
3815 {
3816 GetBdrPatchKnotVectors(p, kv);
3817
3818 int ne = 1;
3819 for (int d = 0; d < dim; d++)
3820 {
3821 ne *= kv[d]->GetNE();
3822 }
3823
3824 NumOfBdrElements += ne;
3825 }
3826}
3827
3828void NURBSExtension::GetElementTopo(Array<Element *> &elements) const
3829{
3830 elements.SetSize(GetNE());
3831
3832 if (Dimension() == 1)
3833 {
3834 Get1DElementTopo(elements);
3835 }
3836 else if (Dimension() == 2)
3837 {
3838 Get2DElementTopo(elements);
3839 }
3840 else
3841 {
3842 Get3DElementTopo(elements);
3843 }
3844}
3845
3846void NURBSExtension::Get1DElementTopo(Array<Element *> &elements) const
3847{
3848 int el = 0;
3849 int eg = 0;
3850 int ind[2];
3851 NURBSPatchMap p2g(this);
3852 const KnotVector *kv[1];
3853
3854 for (int p = 0; p < GetNP(); p++)
3855 {
3856 p2g.SetPatchVertexMap(p, kv);
3857 int nx = p2g.nx();
3858
3859 int patch_attr = patchTopo->GetAttribute(p);
3860
3861 for (int i = 0; i < nx; i++)
3862 {
3863 if (activeElem[eg])
3864 {
3865 ind[0] = activeVert[p2g(i)];
3866 ind[1] = activeVert[p2g(i+1)];
3867
3868 elements[el] = new Segment(ind, patch_attr);
3869 el++;
3870 }
3871 eg++;
3872 }
3873 }
3874}
3875
3876void NURBSExtension::Get2DElementTopo(Array<Element *> &elements) const
3877{
3878 int el = 0;
3879 int eg = 0;
3880 int ind[4];
3881 NURBSPatchMap p2g(this);
3882 const KnotVector *kv[2];
3883
3884 for (int p = 0; p < GetNP(); p++)
3885 {
3886 p2g.SetPatchVertexMap(p, kv);
3887 int nx = p2g.nx();
3888 int ny = p2g.ny();
3889
3890 int patch_attr = patchTopo->GetAttribute(p);
3891
3892 for (int j = 0; j < ny; j++)
3893 {
3894 for (int i = 0; i < nx; i++)
3895 {
3896 if (activeElem[eg])
3897 {
3898 ind[0] = activeVert[p2g(i, j )];
3899 ind[1] = activeVert[p2g(i+1,j )];
3900 ind[2] = activeVert[p2g(i+1,j+1)];
3901 ind[3] = activeVert[p2g(i, j+1)];
3902
3903 elements[el] = new Quadrilateral(ind, patch_attr);
3904 el++;
3905 }
3906 eg++;
3907 }
3908 }
3909 }
3910}
3911
3912void NURBSExtension::Get3DElementTopo(Array<Element *> &elements) const
3913{
3914 int el = 0;
3915 int eg = 0;
3916 int ind[8];
3917 NURBSPatchMap p2g(this);
3918 const KnotVector *kv[3];
3919
3920 for (int p = 0; p < GetNP(); p++)
3921 {
3922 p2g.SetPatchVertexMap(p, kv);
3923 int nx = p2g.nx();
3924 int ny = p2g.ny();
3925 int nz = p2g.nz();
3926
3927 int patch_attr = patchTopo->GetAttribute(p);
3928
3929 for (int k = 0; k < nz; k++)
3930 {
3931 for (int j = 0; j < ny; j++)
3932 {
3933 for (int i = 0; i < nx; i++)
3934 {
3935 if (activeElem[eg])
3936 {
3937 ind[0] = activeVert[p2g(i, j, k)];
3938 ind[1] = activeVert[p2g(i+1,j, k)];
3939 ind[2] = activeVert[p2g(i+1,j+1,k)];
3940 ind[3] = activeVert[p2g(i, j+1,k)];
3941
3942 ind[4] = activeVert[p2g(i, j, k+1)];
3943 ind[5] = activeVert[p2g(i+1,j, k+1)];
3944 ind[6] = activeVert[p2g(i+1,j+1,k+1)];
3945 ind[7] = activeVert[p2g(i, j+1,k+1)];
3946
3947 elements[el] = new Hexahedron(ind, patch_attr);
3948 el++;
3949 }
3950 eg++;
3951 }
3952 }
3953 }
3954 }
3955}
3956
3957void NURBSExtension::GetBdrElementTopo(Array<Element *> &boundary) const
3958{
3959 boundary.SetSize(GetNBE());
3960
3961 if (Dimension() == 1)
3962 {
3963 Get1DBdrElementTopo(boundary);
3964 }
3965 else if (Dimension() == 2)
3966 {
3967 Get2DBdrElementTopo(boundary);
3968 }
3969 else
3970 {
3971 Get3DBdrElementTopo(boundary);
3972 }
3973}
3974
3975void NURBSExtension::Get1DBdrElementTopo(Array<Element *> &boundary) const
3976{
3977 int g_be, l_be;
3978 int ind[2], okv[1];
3979 NURBSPatchMap p2g(this);
3980 const KnotVector *kv[1];
3981
3982 g_be = l_be = 0;
3983 for (int b = 0; b < GetNBP(); b++)
3984 {
3985 p2g.SetBdrPatchVertexMap(b, kv, okv);
3986 int bdr_patch_attr = patchTopo->GetBdrAttribute(b);
3987
3988 if (activeBdrElem[g_be])
3989 {
3990 ind[0] = activeVert[p2g[0]];
3991 boundary[l_be] = new Point(ind, bdr_patch_attr);
3992 l_be++;
3993 }
3994 g_be++;
3995 }
3996}
3997
3998void NURBSExtension::Get2DBdrElementTopo(Array<Element *> &boundary) const
3999{
4000 int g_be, l_be;
4001 int ind[2], okv[1];
4002 NURBSPatchMap p2g(this);
4003 const KnotVector *kv[1];
4004
4005 g_be = l_be = 0;
4006 for (int b = 0; b < GetNBP(); b++)
4007 {
4008 p2g.SetBdrPatchVertexMap(b, kv, okv);
4009 int nx = p2g.nx();
4010
4011 int bdr_patch_attr = patchTopo->GetBdrAttribute(b);
4012
4013 for (int i = 0; i < nx; i++)
4014 {
4015 if (activeBdrElem[g_be])
4016 {
4017 int i_ = (okv[0] >= 0) ? i : (nx - 1 - i);
4018 ind[0] = activeVert[p2g[i_ ]];
4019 ind[1] = activeVert[p2g[i_+1]];
4020
4021 boundary[l_be] = new Segment(ind, bdr_patch_attr);
4022 l_be++;
4023 }
4024 g_be++;
4025 }
4026 }
4027}
4028
4029void NURBSExtension::Get3DBdrElementTopo(Array<Element *> &boundary) const
4030{
4031 int g_be, l_be;
4032 int ind[4], okv[2];
4033 NURBSPatchMap p2g(this);
4034 const KnotVector *kv[2];
4035
4036 g_be = l_be = 0;
4037 for (int b = 0; b < GetNBP(); b++)
4038 {
4039 p2g.SetBdrPatchVertexMap(b, kv, okv);
4040 int nx = p2g.nx();
4041 int ny = p2g.ny();
4042
4043 int bdr_patch_attr = patchTopo->GetBdrAttribute(b);
4044
4045 for (int j = 0; j < ny; j++)
4046 {
4047 int j_ = (okv[1] >= 0) ? j : (ny - 1 - j);
4048 for (int i = 0; i < nx; i++)
4049 {
4050 if (activeBdrElem[g_be])
4051 {
4052 int i_ = (okv[0] >= 0) ? i : (nx - 1 - i);
4053 ind[0] = activeVert[p2g(i_, j_ )];
4054 ind[1] = activeVert[p2g(i_+1,j_ )];
4055 ind[2] = activeVert[p2g(i_+1,j_+1)];
4056 ind[3] = activeVert[p2g(i_, j_+1)];
4057
4058 boundary[l_be] = new Quadrilateral(ind, bdr_patch_attr);
4059 l_be++;
4060 }
4061 g_be++;
4062 }
4063 }
4064 }
4065}
4066
4067void NURBSExtension::GenerateElementDofTable()
4068{
4069 activeDof.SetSize(GetNTotalDof());
4070 activeDof = 0;
4071
4072 if (Dimension() == 1)
4073 {
4074 Generate1DElementDofTable();
4075 }
4076 else if (Dimension() == 2)
4077 {
4078 Generate2DElementDofTable();
4079 }
4080 else
4081 {
4082 Generate3DElementDofTable();
4083 }
4084
4085 SetPatchToElements();
4086
4087 NumOfActiveDofs = 0;
4088 for (int d = 0; d < GetNTotalDof(); d++)
4089 if (activeDof[d])
4090 {
4091 NumOfActiveDofs++;
4092 activeDof[d] = NumOfActiveDofs;
4093 }
4094
4095 int *dof = el_dof->GetJ();
4096 int ndof = el_dof->Size_of_connections();
4097 for (int i = 0; i < ndof; i++)
4098 {
4099 dof[i] = activeDof[dof[i]] - 1;
4100 }
4101}
4102
4103void NURBSExtension::Generate1DElementDofTable()
4104{
4105 int el = 0;
4106 int eg = 0;
4107 const KnotVector *kv[2];
4108 NURBSPatchMap p2g(this);
4109
4110 Array<Connection> el_dof_list;
4111 el_to_patch.SetSize(NumOfActiveElems);
4112 el_to_IJK.SetSize(NumOfActiveElems, 2);
4113
4114 for (int p = 0; p < GetNP(); p++)
4115 {
4116 p2g.SetPatchDofMap(p, kv);
4117
4118 // Load dofs
4119 const int ord0 = kv[0]->GetOrder();
4120 for (int i = 0; i < kv[0]->GetNKS(); i++)
4121 {
4122 if (kv[0]->isElement(i))
4123 {
4124 if (activeElem[eg])
4125 {
4126 Connection conn(el,0);
4127 for (int ii = 0; ii <= ord0; ii++)
4128 {
4129 conn.to = DofMap(p2g(i+ii));
4130 activeDof[conn.to] = 1;
4131 el_dof_list.Append(conn);
4132 }
4133 el_to_patch[el] = p;
4134 el_to_IJK(el,0) = i;
4135
4136 el++;
4137 }
4138 eg++;
4139 }
4140 }
4141 }
4142 // We must NOT sort el_dof_list in this case.
4143 el_dof = new Table(NumOfActiveElems, el_dof_list);
4144}
4145
4146void NURBSExtension::Generate2DElementDofTable()
4147{
4148 int el = 0;
4149 int eg = 0;
4150 const KnotVector *kv[2];
4151 NURBSPatchMap p2g(this);
4152
4153 Array<Connection> el_dof_list;
4154 el_to_patch.SetSize(NumOfActiveElems);
4155 el_to_IJK.SetSize(NumOfActiveElems, 2);
4156
4157 for (int p = 0; p < GetNP(); p++)
4158 {
4159 p2g.SetPatchDofMap(p, kv);
4160
4161 // Load dofs
4162 const int ord0 = kv[0]->GetOrder();
4163 const int ord1 = kv[1]->GetOrder();
4164 for (int j = 0; j < kv[1]->GetNKS(); j++)
4165 {
4166 if (kv[1]->isElement(j))
4167 {
4168 for (int i = 0; i < kv[0]->GetNKS(); i++)
4169 {
4170 if (kv[0]->isElement(i))
4171 {
4172 if (activeElem[eg])
4173 {
4174 Connection conn(el,0);
4175 for (int jj = 0; jj <= ord1; jj++)
4176 {
4177 for (int ii = 0; ii <= ord0; ii++)
4178 {
4179 conn.to = DofMap(p2g(i+ii,j+jj));
4180 activeDof[conn.to] = 1;
4181 el_dof_list.Append(conn);
4182 }
4183 }
4184 el_to_patch[el] = p;
4185 el_to_IJK(el,0) = i;
4186 el_to_IJK(el,1) = j;
4187
4188 el++;
4189 }
4190 eg++;
4191 }
4192 }
4193 }
4194 }
4195 }
4196 // We must NOT sort el_dof_list in this case.
4197 el_dof = new Table(NumOfActiveElems, el_dof_list);
4198}
4199
4200void NURBSExtension::Generate3DElementDofTable()
4201{
4202 int el = 0;
4203 int eg = 0;
4204 const KnotVector *kv[3];
4205 NURBSPatchMap p2g(this);
4206
4207 Array<Connection> el_dof_list;
4208 el_to_patch.SetSize(NumOfActiveElems);
4209 el_to_IJK.SetSize(NumOfActiveElems, 3);
4210
4211 for (int p = 0; p < GetNP(); p++)
4212 {
4213 p2g.SetPatchDofMap(p, kv);
4214
4215 // Load dofs
4216 const int ord0 = kv[0]->GetOrder();
4217 const int ord1 = kv[1]->GetOrder();
4218 const int ord2 = kv[2]->GetOrder();
4219 for (int k = 0; k < kv[2]->GetNKS(); k++)
4220 {
4221 if (kv[2]->isElement(k))
4222 {
4223 for (int j = 0; j < kv[1]->GetNKS(); j++)
4224 {
4225 if (kv[1]->isElement(j))
4226 {
4227 for (int i = 0; i < kv[0]->GetNKS(); i++)
4228 {
4229 if (kv[0]->isElement(i))
4230 {
4231 if (activeElem[eg])
4232 {
4233 Connection conn(el,0);
4234 for (int kk = 0; kk <= ord2; kk++)
4235 {
4236 for (int jj = 0; jj <= ord1; jj++)
4237 {
4238 for (int ii = 0; ii <= ord0; ii++)
4239 {
4240 conn.to = DofMap(p2g(i+ii, j+jj, k+kk));
4241 activeDof[conn.to] = 1;
4242 el_dof_list.Append(conn);
4243 }
4244 }
4245 }
4246
4247 el_to_patch[el] = p;
4248 el_to_IJK(el,0) = i;
4249 el_to_IJK(el,1) = j;
4250 el_to_IJK(el,2) = k;
4251
4252 el++;
4253 }
4254 eg++;
4255 }
4256 }
4257 }
4258 }
4259 }
4260 }
4261 }
4262 // We must NOT sort el_dof_list in this case.
4263 el_dof = new Table(NumOfActiveElems, el_dof_list);
4264}
4265
4266void NURBSExtension::GetPatchDofs(const int patch, Array<int> &dofs)
4267{
4268 const KnotVector *kv[3];
4269 NURBSPatchMap p2g(this);
4270
4271 p2g.SetPatchDofMap(patch, kv);
4272
4273 if (Dimension() == 1)
4274 {
4275 const int nx = kv[0]->GetNCP();
4276 dofs.SetSize(nx);
4277
4278 for (int i=0; i<nx; ++i)
4279 {
4280 dofs[i] = DofMap(p2g(i));
4281 }
4282 }
4283 else if (Dimension() == 2)
4284 {
4285 const int nx = kv[0]->GetNCP();
4286 const int ny = kv[1]->GetNCP();
4287 dofs.SetSize(nx * ny);
4288
4289 for (int j=0; j<ny; ++j)
4290 for (int i=0; i<nx; ++i)
4291 {
4292 dofs[i + (nx * j)] = DofMap(p2g(i, j));
4293 }
4294 }
4295 else if (Dimension() == 3)
4296 {
4297 const int nx = kv[0]->GetNCP();
4298 const int ny = kv[1]->GetNCP();
4299 const int nz = kv[2]->GetNCP();
4300 dofs.SetSize(nx * ny * nz);
4301
4302 for (int k=0; k<nz; ++k)
4303 for (int j=0; j<ny; ++j)
4304 for (int i=0; i<nx; ++i)
4305 {
4306 dofs[i + (nx * (j + (k * ny)))] = DofMap(p2g(i, j, k));
4307 }
4308 }
4309 else
4310 {
4311 MFEM_ABORT("Only 1D/2D/3D supported currently in NURBSExtension::GetPatchDofs");
4312 }
4313}
4314
4315void NURBSExtension::GenerateBdrElementDofTable()
4316{
4317 if (Dimension() == 1)
4318 {
4319 Generate1DBdrElementDofTable();
4320 }
4321 else if (Dimension() == 2)
4322 {
4323 Generate2DBdrElementDofTable();
4324 }
4325 else
4326 {
4327 Generate3DBdrElementDofTable();
4328 }
4329
4330 SetPatchToBdrElements();
4331
4332 int *dof = bel_dof->GetJ();
4333 int ndof = bel_dof->Size_of_connections();
4334 for (int i = 0; i < ndof; i++)
4335 {
4336 int idx = dof[i];
4337 if (idx < 0)
4338 {
4339 dof[i] = -1 - (activeDof[-1-idx] - 1);
4340 dof[i] = -activeDof[-1-idx];
4341 }
4342 else
4343 {
4344 dof[i] = activeDof[idx] - 1;
4345 }
4346 }
4347}
4348
4349void NURBSExtension::Generate1DBdrElementDofTable()
4350{
4351 int gbe = 0;
4352 int lbe = 0, okv[1];
4353 const KnotVector *kv[1];
4354 NURBSPatchMap p2g(this);
4355
4356 Array<Connection> bel_dof_list;
4357 bel_to_patch.SetSize(NumOfActiveBdrElems);
4358 bel_to_IJK.SetSize(NumOfActiveBdrElems, 1);
4359
4360 for (int b = 0; b < GetNBP(); b++)
4361 {
4362 p2g.SetBdrPatchDofMap(b, kv, okv);
4363 // Load dofs
4364 if (activeBdrElem[gbe])
4365 {
4366 Connection conn(lbe,0);
4367 conn.to = DofMap(p2g[0]);
4368 bel_dof_list.Append(conn);
4369 bel_to_patch[lbe] = b;
4370 bel_to_IJK(lbe,0) = 0;
4371 lbe++;
4372 }
4373 gbe++;
4374 }
4375 // We must NOT sort bel_dof_list in this case.
4376 bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list);
4377}
4378
4379void NURBSExtension::Generate2DBdrElementDofTable()
4380{
4381 int gbe = 0;
4382 int lbe = 0, okv[1];
4383 const KnotVector *kv[1];
4384 NURBSPatchMap p2g(this);
4385
4386 Array<Connection> bel_dof_list;
4387 bel_to_patch.SetSize(NumOfActiveBdrElems);
4388 bel_to_IJK.SetSize(NumOfActiveBdrElems, 1);
4389
4390 for (int b = 0; b < GetNBP(); b++)
4391 {
4392 p2g.SetBdrPatchDofMap(b, kv, okv);
4393 const int nx = p2g.nx(); // NCP-1
4394 // Load dofs
4395 const int nks0 = kv[0]->GetNKS();
4396 const int ord0 = kv[0]->GetOrder();
4397
4398 bool add_dofs = true;
4399 int s = 1;
4400
4401 if (mode == Mode::H_DIV)
4402 {
4403 int fn = patchTopo->GetBdrElementFaceIndex(b);
4404 if (ord0 == mOrders.Max()) { add_dofs = false; }
4405 if (fn == 0) { s = -1; }
4406 if (fn == 2) { s = -1; }
4407 }
4408 else if (mode == Mode::H_CURL)
4409 {
4410 if (ord0 == mOrders.Max()) { add_dofs = false; }
4411 }
4412
4413 for (int i = 0; i < nks0; i++)
4414 {
4415 if (kv[0]->isElement(i))
4416 {
4417 if (activeBdrElem[gbe])
4418 {
4419 Connection conn(lbe,0);
4420 if (add_dofs)
4421 {
4422 for (int ii = 0; ii <= ord0; ii++)
4423 {
4424 conn.to = DofMap(p2g[(okv[0] >= 0) ? (i+ii) : (nx-i-ii)]);
4425 if (s == -1) { conn.to = -1 -conn.to; }
4426 bel_dof_list.Append(conn);
4427 }
4428 }
4429 bel_to_patch[lbe] = b;
4430 bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
4431 lbe++;
4432 }
4433 gbe++;
4434 }
4435 }
4436 }
4437 // We must NOT sort bel_dof_list in this case.
4438 bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list);
4439}
4440
4441
4442void NURBSExtension::Generate3DBdrElementDofTable()
4443{
4444 int gbe = 0;
4445 int lbe = 0, okv[2];
4446 const KnotVector *kv[2];
4447 NURBSPatchMap p2g(this);
4448
4449 Array<Connection> bel_dof_list;
4450 bel_to_patch.SetSize(NumOfActiveBdrElems);
4451 bel_to_IJK.SetSize(NumOfActiveBdrElems, 2);
4452
4453 for (int b = 0; b < GetNBP(); b++)
4454 {
4455 p2g.SetBdrPatchDofMap(b, kv, okv);
4456 const int nx = p2g.nx(); // NCP0-1
4457 const int ny = p2g.ny(); // NCP1-1
4458
4459 // Load dofs
4460 const int nks0 = kv[0]->GetNKS();
4461 const int ord0 = kv[0]->GetOrder();
4462 const int nks1 = kv[1]->GetNKS();
4463 const int ord1 = kv[1]->GetOrder();
4464
4465 // Check if dofs are actually defined on boundary
4466 bool add_dofs = true;
4467 int s = 1;
4468
4469 if (mode == Mode::H_DIV)
4470 {
4471 int fn = patchTopo->GetBdrElementFaceIndex(b);
4472 if (ord0 != ord1) { add_dofs = false; }
4473 if (fn == 4) { s = -1; }
4474 if (fn == 1) { s = -1; }
4475 if (fn == 0) { s = -1; }
4476 }
4477 else if (mode == Mode::H_CURL)
4478 {
4479 if (ord0 == ord1) { add_dofs = false; }
4480 }
4481
4482
4483 for (int j = 0; j < nks1; j++)
4484 {
4485 if (kv[1]->isElement(j))
4486 {
4487 for (int i = 0; i < nks0; i++)
4488 {
4489 if (kv[0]->isElement(i))
4490 {
4491 if (activeBdrElem[gbe])
4492 {
4493 Connection conn(lbe,0);
4494 if (add_dofs)
4495 {
4496 for (int jj = 0; jj <= ord1; jj++)
4497 {
4498 const int jj_ = (okv[1] >= 0) ? (j+jj) : (ny-j-jj);
4499 for (int ii = 0; ii <= ord0; ii++)
4500 {
4501 const int ii_ = (okv[0] >= 0) ? (i+ii) : (nx-i-ii);
4502 conn.to = DofMap(p2g(ii_, jj_));
4503 if (s == -1) { conn.to = -1 -conn.to; }
4504 bel_dof_list.Append(conn);
4505 }
4506 }
4507 }
4508 bel_to_patch[lbe] = b;
4509 bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
4510 bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j);
4511 lbe++;
4512 }
4513 gbe++;
4514 }
4515 }
4516 }
4517 }
4518 }
4519 // We must NOT sort bel_dof_list in this case.
4520 bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list);
4521}
4522
4523void NURBSExtension::GetVertexLocalToGlobal(Array<int> &lvert_vert)
4524{
4525 lvert_vert.SetSize(GetNV());
4526 for (int gv = 0; gv < GetGNV(); gv++)
4527 if (activeVert[gv] >= 0)
4528 {
4529 lvert_vert[activeVert[gv]] = gv;
4530 }
4531}
4532
4533void NURBSExtension::GetElementLocalToGlobal(Array<int> &lelem_elem)
4534{
4535 lelem_elem.SetSize(GetNE());
4536 for (int le = 0, ge = 0; ge < GetGNE(); ge++)
4537 if (activeElem[ge])
4538 {
4539 lelem_elem[le++] = ge;
4540 }
4541}
4542
4543void NURBSExtension::LoadFE(int i, const FiniteElement *FE) const
4544{
4545 const NURBSFiniteElement *NURBSFE =
4546 dynamic_cast<const NURBSFiniteElement *>(FE);
4547
4548 if (NURBSFE->GetElement() != i)
4549 {
4550 Array<int> dofs;
4551 NURBSFE->SetIJK(el_to_IJK.GetRow(i));
4552 if (el_to_patch[i] != NURBSFE->GetPatch())
4553 {
4554 GetPatchKnotVectors(el_to_patch[i], NURBSFE->KnotVectors());
4555 NURBSFE->SetPatch(el_to_patch[i]);
4556 NURBSFE->SetOrder();
4557 }
4558 el_dof->GetRow(i, dofs);
4559 weights.GetSubVector(dofs, NURBSFE->Weights());
4560 NURBSFE->SetElement(i);
4561 }
4562}
4563
4564void NURBSExtension::LoadBE(int i, const FiniteElement *BE) const
4565{
4566 if (Dimension() == 1) { return; }
4567
4568 const NURBSFiniteElement *NURBSFE =
4569 dynamic_cast<const NURBSFiniteElement *>(BE);
4570
4571 if (NURBSFE->GetElement() != i)
4572 {
4573 Array<int> dofs;
4574 NURBSFE->SetIJK(bel_to_IJK.GetRow(i));
4575 if (bel_to_patch[i] != NURBSFE->GetPatch())
4576 {
4577 GetBdrPatchKnotVectors(bel_to_patch[i], NURBSFE->KnotVectors());
4578 NURBSFE->SetPatch(bel_to_patch[i]);
4579 NURBSFE->SetOrder();
4580 }
4581 bel_dof->GetRow(i, dofs);
4582 weights.GetSubVector(dofs, NURBSFE->Weights());
4583 NURBSFE->SetElement(i);
4584 }
4585}
4586
4587void NURBSExtension::ConvertToPatches(const Vector &Nodes)
4588{
4589 delete el_dof;
4590 delete bel_dof;
4591
4592 if (patches.Size() == 0)
4593 {
4594 GetPatchNets(Nodes, Dimension());
4595 }
4596}
4597
4598void NURBSExtension::SetCoordsFromPatches(Vector &Nodes)
4599{
4600 if (patches.Size() == 0) { return; }
4601
4602 SetSolutionVector(Nodes, Dimension());
4603 patches.SetSize(0);
4604}
4605
4606void NURBSExtension::SetKnotsFromPatches()
4607{
4608 if (patches.Size() == 0)
4609 {
4610 mfem_error("NURBSExtension::SetKnotsFromPatches :"
4611 " No patches available!");
4612 }
4613
4614 Array<KnotVector *> kv;
4615
4616 for (int p = 0; p < patches.Size(); p++)
4617 {
4618 GetPatchKnotVectors(p, kv);
4619
4620 for (int i = 0; i < kv.Size(); i++)
4621 {
4622 *kv[i] = *patches[p]->GetKV(i);
4623 }
4624 }
4625
4626 UpdateUniqueKV();
4627 SetOrdersFromKnotVectors();
4628
4629 GenerateOffsets();
4630 CountElements();
4631 CountBdrElements();
4632
4633 // all elements must be active
4634 NumOfActiveElems = NumOfElements;
4635 activeElem.SetSize(NumOfElements);
4636 activeElem = true;
4637
4638 GenerateActiveVertices();
4639 InitDofMap();
4640 GenerateElementDofTable();
4641 GenerateActiveBdrElems();
4642 GenerateBdrElementDofTable();
4643
4644 ConnectBoundaries();
4645}
4646
4647void NURBSExtension::LoadSolution(std::istream &input, GridFunction &sol) const
4648{
4649 const FiniteElementSpace *fes = sol.FESpace();
4650 MFEM_VERIFY(fes->GetNURBSext() == this, "");
4651
4652 sol.SetSize(fes->GetVSize());
4653
4654 Array<const KnotVector *> kv(Dimension());
4655 NURBSPatchMap p2g(this);
4656 const int vdim = fes->GetVDim();
4657
4658 for (int p = 0; p < GetNP(); p++)
4659 {
4660 skip_comment_lines(input, '#');
4661
4662 p2g.SetPatchDofMap(p, kv);
4663 const int nx = kv[0]->GetNCP();
4664 const int ny = kv[1]->GetNCP();
4665 const int nz = (kv.Size() == 2) ? 1 : kv[2]->GetNCP();
4666 for (int k = 0; k < nz; k++)
4667 {
4668 for (int j = 0; j < ny; j++)
4669 {
4670 for (int i = 0; i < nx; i++)
4671 {
4672 const int ll = (kv.Size() == 2) ? p2g(i,j) : p2g(i,j,k);
4673 const int l = DofMap(ll);
4674 for (int vd = 0; vd < vdim; vd++)
4675 {
4676 input >> sol(fes->DofToVDof(l,vd));
4677 }
4678 }
4679 }
4680 }
4681 }
4682}
4683
4684void NURBSExtension::PrintSolution(const GridFunction &sol, std::ostream &os)
4685const
4686{
4687 const FiniteElementSpace *fes = sol.FESpace();
4688 MFEM_VERIFY(fes->GetNURBSext() == this, "");
4689
4690 Array<const KnotVector *> kv(Dimension());
4691 NURBSPatchMap p2g(this);
4692 const int vdim = fes->GetVDim();
4693
4694 for (int p = 0; p < GetNP(); p++)
4695 {
4696 os << "\n# patch " << p << "\n\n";
4697
4698 p2g.SetPatchDofMap(p, kv);
4699 const int nx = kv[0]->GetNCP();
4700 const int ny = kv[1]->GetNCP();
4701 const int nz = (kv.Size() == 2) ? 1 : kv[2]->GetNCP();
4702 for (int k = 0; k < nz; k++)
4703 {
4704 for (int j = 0; j < ny; j++)
4705 {
4706 for (int i = 0; i < nx; i++)
4707 {
4708 const int ll = (kv.Size() == 2) ? p2g(i,j) : p2g(i,j,k);
4709 const int l = DofMap(ll);
4710 os << sol(fes->DofToVDof(l,0));
4711 for (int vd = 1; vd < vdim; vd++)
4712 {
4713 os << ' ' << sol(fes->DofToVDof(l,vd));
4714 }
4715 os << '\n';
4716 }
4717 }
4718 }
4719 }
4720}
4721
4722void NURBSExtension::DegreeElevate(int rel_degree, int degree)
4723{
4724 for (int p = 0; p < patches.Size(); p++)
4725 {
4726 for (int dir = 0; dir < patches[p]->GetNKV(); dir++)
4727 {
4728 int oldd = patches[p]->GetKV(dir)->GetOrder();
4729 int newd = std::min(oldd + rel_degree, degree);
4730 if (newd > oldd)
4731 {
4732 patches[p]->DegreeElevate(dir, newd - oldd);
4733 }
4734 }
4735 }
4736}
4737
4738NURBSExtension* NURBSExtension::GetDivExtension(int component)
4739{
4740 // Smarter routine
4741 if (GetNP() > 1)
4742 {
4743 mfem_error("NURBSExtension::GetDivExtension currently "
4744 "only works for single patch NURBS meshes ");
4745 }
4746
4747 Array<int> newOrders = GetOrders();
4748 newOrders[component] += 1;
4749
4750 return new NURBSExtension(this, newOrders, Mode::H_DIV);
4751}
4752
4753NURBSExtension* NURBSExtension::GetCurlExtension(int component)
4754{
4755 // Smarter routine
4756 if (GetNP() > 1)
4757 {
4758 mfem_error("NURBSExtension::GetCurlExtension currently "
4759 "only works for single patch NURBS meshes ");
4760 }
4761
4762 Array<int> newOrders = GetOrders();
4763 for (int c = 0; c < newOrders.Size(); c++) { newOrders[c]++; }
4764 newOrders[component] -= 1;
4765
4766 return new NURBSExtension(this, newOrders, Mode::H_CURL);
4767}
4768
4769void NURBSExtension::UniformRefinement(const Array<int> &rf)
4770{
4771 for (int p = 0; p < patches.Size(); p++)
4772 {
4773 patches[p]->UniformRefinement(rf);
4774 }
4775}
4776
4777void NURBSExtension::UniformRefinement(int rf)
4778{
4779 Array<int> rf_array(Dimension());
4780 rf_array = rf;
4781 UniformRefinement(rf_array);
4782}
4783
4784void NURBSExtension::Coarsen(const Array<int> &cf, real_t tol)
4785{
4786 // First, mark all knot vectors on all patches as not coarse. This prevents
4787 // coarsening the same knot vector twice.
4788 for (int p = 0; p < patches.Size(); p++)
4789 {
4790 patches[p]->SetKnotVectorsCoarse(false);
4791 }
4792
4793 for (int p = 0; p < patches.Size(); p++)
4794 {
4795 patches[p]->Coarsen(cf, tol);
4796 }
4797
4798 if (ref_factors.Size() > 0)
4799 {
4800 MFEM_VERIFY(cf.Size() == ref_factors.Size(), "");
4801 for (int i=0; i<cf.Size(); ++i) { ref_factors[i] /= cf[i]; }
4802 }
4803}
4804
4805void NURBSExtension::FullyCoarsen()
4806{
4807 // First, mark all knot vectors on all patches as not coarse. This prevents
4808 // coarsening the same knot vector twice.
4809 for (int p = 0; p < patches.Size(); p++)
4810 {
4811 patches[p]->SetKnotVectorsCoarse(false);
4812 }
4813
4814 const int maxOrder = mOrders.Max();
4815
4816 // For degree maxOrder, there are 2*(maxOrder + 1) knots for a single element,
4817 // and the number of control points in each dimension is
4818 // 2*(maxOrder + 1) - maxOrder - 1
4819 const int ncp1D = maxOrder + 1;
4820 const int ncp = static_cast<int>(pow(ncp1D, Dimension()));
4821
4822 for (int p = 0; p < patches.Size(); p++)
4823 {
4824 if (p < num_structured_patches)
4825 {
4826 // Use data from patchCP
4827 Array2D<double> pcp(ncp, Dimension());
4828 for (int i=0; i<ncp; ++i)
4829 {
4830 for (int j=0; j<Dimension(); ++j) { pcp(i, j) = patchCP(p, i, j); }
4831 }
4832
4833 patches[p]->FullyCoarsen(pcp, ncp1D);
4834 }
4835 }
4836}
4837
4838void NURBSExtension::Coarsen(int cf, real_t tol)
4839{
4840 Array<int> cf_array(Dimension());
4841 cf_array = cf;
4842 Coarsen(cf_array, tol);
4843}
4844
4845void NURBSExtension::GetCoarseningFactors(Array<int> & f) const
4846{
4847 f.SetSize(0);
4848 for (auto patch : patches)
4849 {
4850 Array<int> pf;
4851 patch->GetCoarseningFactors(pf);
4852 if (f.Size() == 0)
4853 {
4854 f = pf; // Initialize
4855 }
4856 else
4857 {
4858 MFEM_VERIFY(f.Size() == pf.Size(), "");
4859 for (int i=0; i<f.Size(); ++i)
4860 {
4861 if (nonconformingPT)
4862 {
4863 if ((f[i] == 1 && pf[i] != 1) || (pf[i] < f[i] && pf[i] != 1))
4864 {
4865 f[i] = pf[i];
4866 }
4867 }
4868 else
4869 {
4870 MFEM_VERIFY(f[i] == pf[i] || f[i] == 1 || pf[i] == 1,
4871 "Inconsistent patch coarsening factors");
4872 if (f[i] == 1 && pf[i] != 1)
4873 {
4874 f[i] = pf[i];
4875 }
4876 }
4877 }
4878 }
4879 }
4880}
4881
4882void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
4883{
4884 Array<int> edges;
4885 Array<int> orient;
4886 Array<int> kvdir;
4887
4888 Array<KnotVector *> pkv(Dimension());
4889
4890 for (int p = 0; p < patches.Size(); p++)
4891 {
4892 if (Dimension()==1)
4893 {
4894 pkv[0] = kv[KnotInd(p)];
4895 }
4896 else if (Dimension()==2)
4897 {
4898 patchTopo->GetElementEdges(p, edges, orient);
4899 pkv[0] = kv[KnotInd(edges[0])];
4900 pkv[1] = kv[KnotInd(edges[1])];
4901 }
4902 else if (Dimension()==3)
4903 {
4904 patchTopo->GetElementEdges(p, edges, orient);
4905 pkv[0] = kv[KnotInd(edges[0])];
4906 pkv[1] = kv[KnotInd(edges[3])];
4907 pkv[2] = kv[KnotInd(edges[8])];
4908 }
4909
4910 // Check whether inserted knots should be flipped before inserting.
4911 // Knotvectors are stored in a different array pkvc such that the original
4912 // knots which are inserted are not changed.
4913 // We need those knots for multiple patches so they have to remain original
4914 CheckKVDirection(p, kvdir);
4915
4916 Array<KnotVector *> pkvc(Dimension());
4917 for (int d = 0; d < Dimension(); d++)
4918 {
4919 pkvc[d] = new KnotVector(*(pkv[d]));
4920
4921 if (kvdir[d] == -1)
4922 {
4923 pkvc[d]->Flip();
4924 }
4925 }
4926
4927 patches[p]->KnotInsert(pkvc);
4928 for (int d = 0; d < Dimension(); d++) { delete pkvc[d]; }
4929 }
4930}
4931
4932void NURBSExtension::KnotInsert(Array<Vector *> &kv)
4933{
4934 Array<int> edges;
4935 Array<int> orient;
4936 Array<int> kvdir;
4937
4938 Array<Vector *> pkv(Dimension());
4939
4940 for (int p = 0; p < patches.Size(); p++)
4941 {
4942 if (Dimension()==1)
4943 {
4944 pkv[0] = kv[KnotInd(p)];
4945 }
4946 else if (Dimension()==2)
4947 {
4948 patchTopo->GetElementEdges(p, edges, orient);
4949 pkv[0] = kv[KnotInd(edges[0])];
4950 pkv[1] = kv[KnotInd(edges[1])];
4951 }
4952 else if (Dimension()==3)
4953 {
4954 patchTopo->GetElementEdges(p, edges, orient);
4955 pkv[0] = kv[KnotInd(edges[0])];
4956 pkv[1] = kv[KnotInd(edges[3])];
4957 pkv[2] = kv[KnotInd(edges[8])];
4958 }
4959
4960 // Check whether inserted knots should be flipped before inserting.
4961 // Knotvectors are stored in a different array pkvc such that the original
4962 // knots which are inserted are not changed.
4963 CheckKVDirection(p, kvdir);
4964
4965 Array<Vector *> pkvc(Dimension());
4966 for (int d = 0; d < Dimension(); d++)
4967 {
4968 pkvc[d] = new Vector(*(pkv[d]));
4969
4970 if (kvdir[d] == -1)
4971 {
4972 // Find flip point, for knotvectors that do not have the domain [0:1]
4973 KnotVector *kva = knotVectorsCompr[Dimension()*p+d];
4974 real_t apb = (*kva)[0] + (*kva)[kva->Size()-1];
4975
4976 // Flip vector
4977 int size = pkvc[d]->Size();
4978 int ns = static_cast<int>(ceil(size/2.0));
4979 for (int j = 0; j < ns; j++)
4980 {
4981 real_t tmp = apb - pkvc[d]->Elem(j);
4982 pkvc[d]->Elem(j) = apb - pkvc[d]->Elem(size-1-j);
4983 pkvc[d]->Elem(size-1-j) = tmp;
4984 }
4985 }
4986 }
4987
4988 patches[p]->KnotInsert(pkvc);
4989
4990 for (int i = 0; i < Dimension(); i++) { delete pkvc[i]; }
4991 }
4992}
4993
4994void NURBSExtension::KnotRemove(Array<Vector *> &kv, real_t tol)
4995{
4996 Array<int> edges;
4997 Array<int> orient;
4998 Array<int> kvdir;
4999
5000 Array<Vector *> pkv(Dimension());
5001
5002 for (int p = 0; p < patches.Size(); p++)
5003 {
5004 if (Dimension()==1)
5005 {
5006 pkv[0] = kv[KnotInd(p)];
5007 }
5008 else if (Dimension()==2)
5009 {
5010 patchTopo->GetElementEdges(p, edges, orient);
5011 pkv[0] = kv[KnotInd(edges[0])];
5012 pkv[1] = kv[KnotInd(edges[1])];
5013 }
5014 else if (Dimension()==3)
5015 {
5016 patchTopo->GetElementEdges(p, edges, orient);
5017 pkv[0] = kv[KnotInd(edges[0])];
5018 pkv[1] = kv[KnotInd(edges[3])];
5019 pkv[2] = kv[KnotInd(edges[8])];
5020 }
5021
5022 // Check whether knots should be flipped before removing.
5023 CheckKVDirection(p, kvdir);
5024
5025 Array<Vector *> pkvc(Dimension());
5026 for (int d = 0; d < Dimension(); d++)
5027 {
5028 pkvc[d] = new Vector(*(pkv[d]));
5029
5030 if (kvdir[d] == -1)
5031 {
5032 // Find flip point, for knotvectors that do not have the domain [0:1]
5033 KnotVector *kva = knotVectorsCompr[Dimension()*p+d];
5034 real_t apb = (*kva)[0] + (*kva)[kva->Size()-1];
5035
5036 // Flip vector
5037 int size = pkvc[d]->Size();
5038 int ns = static_cast<int>(ceil(size/2.0));
5039 for (int j = 0; j < ns; j++)
5040 {
5041 real_t tmp = apb - pkvc[d]->Elem(j);
5042 pkvc[d]->Elem(j) = apb - pkvc[d]->Elem(size-1-j);
5043 pkvc[d]->Elem(size-1-j) = tmp;
5044 }
5045 }
5046 }
5047
5048 patches[p]->KnotRemove(pkvc, tol);
5049
5050 for (int i = 0; i < Dimension(); i++) { delete pkvc[i]; }
5051 }
5052}
5053
5054void NURBSExtension::GetPatchNets(const Vector &coords, int vdim)
5055{
5056 if (Dimension() == 1)
5057 {
5058 Get1DPatchNets(coords, vdim);
5059 }
5060 else if (Dimension() == 2)
5061 {
5062 Get2DPatchNets(coords, vdim);
5063 }
5064 else
5065 {
5066 Get3DPatchNets(coords, vdim);
5067 }
5068}
5069
5070void NURBSExtension::Get1DPatchNets(const Vector &coords, int vdim)
5071{
5072 Array<const KnotVector *> kv(1);
5073 NURBSPatchMap p2g(this);
5074
5075 patches.SetSize(GetNP());
5076 for (int p = 0; p < GetNP(); p++)
5077 {
5078 p2g.SetPatchDofMap(p, kv);
5079 patches[p] = new NURBSPatch(kv, vdim+1);
5080 NURBSPatch &Patch = *patches[p];
5081
5082 for (int i = 0; i < kv[0]->GetNCP(); i++)
5083 {
5084 const int l = DofMap(p2g(i));
5085 for (int d = 0; d < vdim; d++)
5086 {
5087 Patch(i,d) = coords(l*vdim + d)*weights(l);
5088 }
5089 Patch(i,vdim) = weights(l);
5090 }
5091 }
5092}
5093
5094void NURBSExtension::Get2DPatchNets(const Vector &coords, int vdim)
5095{
5096 Array<const KnotVector *> kv(2);
5097 NURBSPatchMap p2g(this);
5098
5099 patches.SetSize(GetNP());
5100 for (int p = 0; p < GetNP(); p++)
5101 {
5102 p2g.SetPatchDofMap(p, kv);
5103 patches[p] = new NURBSPatch(kv, vdim+1);
5104 NURBSPatch &Patch = *patches[p];
5105
5106 for (int j = 0; j < kv[1]->GetNCP(); j++)
5107 {
5108 for (int i = 0; i < kv[0]->GetNCP(); i++)
5109 {
5110 const int l = DofMap(p2g(i,j));
5111 for (int d = 0; d < vdim; d++)
5112 {
5113 Patch(i,j,d) = coords(l*vdim + d)*weights(l);
5114 }
5115 Patch(i,j,vdim) = weights(l);
5116 }
5117 }
5118 }
5119}
5120
5121void NURBSExtension::Get3DPatchNets(const Vector &coords, int vdim)
5122{
5123 Array<const KnotVector *> kv(3);
5124 NURBSPatchMap p2g(this);
5125
5126 patches.SetSize(GetNP());
5127 for (int p = 0; p < GetNP(); p++)
5128 {
5129 p2g.SetPatchDofMap(p, kv);
5130 patches[p] = new NURBSPatch(kv, vdim+1);
5131 NURBSPatch &Patch = *patches[p];
5132
5133 for (int k = 0; k < kv[2]->GetNCP(); k++)
5134 {
5135 for (int j = 0; j < kv[1]->GetNCP(); j++)
5136 {
5137 for (int i = 0; i < kv[0]->GetNCP(); i++)
5138 {
5139 const int l = DofMap(p2g(i,j,k));
5140 for (int d = 0; d < vdim; d++)
5141 {
5142 Patch(i,j,k,d) = coords(l*vdim + d)*weights(l);
5143 }
5144 Patch(i,j,k,vdim) = weights(l);
5145 }
5146 }
5147 }
5148 }
5149}
5150
5151void NURBSExtension::SetSolutionVector(Vector &coords, int vdim)
5152{
5153 if (Dimension() == 1)
5154 {
5155 Set1DSolutionVector(coords, vdim);
5156 }
5157 else if (Dimension() == 2)
5158 {
5159 Set2DSolutionVector(coords, vdim);
5160 }
5161 else
5162 {
5163 Set3DSolutionVector(coords, vdim);
5164 }
5165}
5166
5167void NURBSExtension::Set1DSolutionVector(Vector &coords, int vdim)
5168{
5169 Array<const KnotVector *> kv(1);
5170 NURBSPatchMap p2g(this);
5171
5172 weights.SetSize(GetNDof());
5173 for (int p = 0; p < GetNP(); p++)
5174 {
5175 p2g.SetPatchDofMap(p, kv);
5176 NURBSPatch &patch = *patches[p];
5177 MFEM_ASSERT(vdim+1 == patch.GetNC(), "");
5178
5179 for (int i = 0; i < kv[0]->GetNCP(); i++)
5180 {
5181 const int l = p2g(i);
5182 for (int d = 0; d < vdim; d++)
5183 {
5184 coords(l*vdim + d) = patch(i,d)/patch(i,vdim);
5185 }
5186 weights(l) = patch(i,vdim);
5187 }
5188
5189 delete patches[p];
5190 }
5191}
5192
5193void NURBSExtension::Set2DSolutionVector(Vector &coords, int vdim)
5194{
5195 Array<const KnotVector *> kv(2);
5196 NURBSPatchMap p2g(this);
5197
5198 const bool d2p = dof2patch.Size() > 0;
5199
5200 weights.SetSize(GetNDof());
5201 for (int p = 0; p < GetNP(); p++)
5202 {
5203 p2g.SetPatchDofMap(p, kv);
5204 NURBSPatch &patch = *patches[p];
5205 MFEM_ASSERT(vdim+1 == patch.GetNC(), "");
5206
5207 for (int j = 0; j < kv[1]->GetNCP(); j++)
5208 {
5209 for (int i = 0; i < kv[0]->GetNCP(); i++)
5210 {
5211 const int l = p2g(i,j);
5212 if (d2p && dof2patch[l] >= 0 && dof2patch[l] != p) { continue; }
5213
5214 for (int d = 0; d < vdim; d++)
5215 {
5216 coords(l*vdim + d) = patch(i,j,d)/patch(i,j,vdim);
5217 }
5218 weights(l) = patch(i,j,vdim);
5219 }
5220 }
5221 delete patches[p];
5222 }
5223}
5224
5225void NURBSExtension::Set3DSolutionVector(Vector &coords, int vdim)
5226{
5227 Array<const KnotVector *> kv(3);
5228 NURBSPatchMap p2g(this);
5229
5230 const bool d2p = dof2patch.Size() > 0;
5231
5232 weights.SetSize(GetNDof());
5233 for (int p = 0; p < GetNP(); p++)
5234 {
5235 p2g.SetPatchDofMap(p, kv);
5236 NURBSPatch &patch = *patches[p];
5237 MFEM_ASSERT(vdim+1 == patch.GetNC(), "");
5238
5239 for (int k = 0; k < kv[2]->GetNCP(); k++)
5240 {
5241 for (int j = 0; j < kv[1]->GetNCP(); j++)
5242 {
5243 for (int i = 0; i < kv[0]->GetNCP(); i++)
5244 {
5245 const int l = p2g(i,j,k);
5246 if (d2p && dof2patch[l] >= 0 && dof2patch[l] != p) { continue; }
5247
5248 for (int d = 0; d < vdim; d++)
5249 {
5250 coords(l*vdim + d) = patch(i,j,k,d)/patch(i,j,k,vdim);
5251 }
5252 weights(l) = patch(i,j,k,vdim);
5253 }
5254 }
5255 }
5256 delete patches[p];
5257 }
5258}
5259
5260void NURBSExtension::GetElementIJK(int elem, Array<int> & ijk)
5261{
5262 MFEM_VERIFY(ijk.Size() == el_to_IJK.NumCols(), "");
5263 el_to_IJK.GetRow(elem, ijk);
5264}
5265
5266void NURBSExtension::GetPatches(Array<NURBSPatch*> &patches_copy)
5267{
5268 const int NP = patches.Size();
5269 patches_copy.SetSize(NP);
5270 for (int p = 0; p < NP; p++)
5271 {
5272 patches_copy[p] = new NURBSPatch(*GetPatch(p));
5273 }
5274}
5275
5276void NURBSExtension::SetPatchToElements()
5277{
5278 const int np = GetNP();
5279 patch_to_el.resize(np);
5280
5281 for (int e=0; e<el_to_patch.Size(); ++e)
5282 {
5283 patch_to_el[el_to_patch[e]].Append(e);
5284 }
5285}
5286
5287void NURBSExtension::SetPatchToBdrElements()
5288{
5289 const int nbp = GetNBP();
5290 patch_to_bel.resize(nbp);
5291
5292 for (int e=0; e<bel_to_patch.Size(); ++e)
5293 {
5294 patch_to_bel[bel_to_patch[e]].Append(e);
5295 }
5296}
5297
5298const Array<int>& NURBSExtension::GetPatchElements(int patch)
5299{
5300 MFEM_ASSERT(patch_to_el.size() > 0, "patch_to_el not set");
5301
5302 return patch_to_el[patch];
5303}
5304
5305const Array<int>& NURBSExtension::GetPatchBdrElements(int patch)
5306{
5307 MFEM_ASSERT(patch_to_bel.size() > 0, "patch_to_el not set");
5308
5309 return patch_to_bel[patch];
5310}
5311
5312void NURBSExtension::GetVertexDofs(int vertex, Array<int> &dofs) const
5313{
5314 MFEM_ASSERT(vertex < v_spaceOffsets.Size(), "");
5315
5316 const int os = v_spaceOffsets[vertex];
5317 const int os1 = vertex + 1 == v_spaceOffsets.Size() ? e_spaceOffsets[0] :
5318 v_spaceOffsets[vertex + 1];
5319
5320 dofs.SetSize(0);
5321 dofs.Reserve(os1 - os);
5322
5323 for (int i=os; i<os1; ++i) { dofs.Append(i); }
5324}
5325
5326void NURBSExtension::GetEdgeDofs(int edge, Array<int> &dofs) const
5327{
5328 MFEM_ASSERT(edge < e_spaceOffsets.Size(), "");
5329
5330 const int os = e_spaceOffsets[edge];
5331 const int os_upper = f_spaceOffsets.Size() > 0 ? f_spaceOffsets[0] :
5332 p_spaceOffsets[0];
5333 const int os1 = edge + 1 == e_spaceOffsets.Size() ? os_upper :
5334 v_spaceOffsets[edge + 1];
5335
5336 dofs.SetSize(0);
5337 // Reserve 2 for the two vertices and os1 - os for the interior edge DOFs.
5338 dofs.Reserve(2 + os1 - os);
5339
5340 // First get the DOFs for the vertices of the edge.
5341
5342 Array<int> vert;
5343 patchTopo->GetEdgeVertices(edge, vert);
5344
5345 for (auto v : vert)
5346 {
5347 Array<int> vdofs;
5348 GetVertexDofs(v, vdofs);
5349 dofs.Append(vdofs);
5350 }
5351
5352 // Now get the interior edge DOFs.
5353 for (int i=os; i<os1; ++i) { dofs.Append(i); }
5354}
5355
5356void NURBSExtension::ReadCoarsePatchCP(std::istream &input)
5357{
5358 MFEM_ABORT("ReadCoarsePatchCP is supported only in NCNURBSExtension");
5359}
5360
5361void NURBSExtension::PrintCoarsePatches(std::ostream &os)
5362{
5363 const int patchCP_size1 = patchCP.GetSize1();
5364 MFEM_VERIFY(patchCP_size1 == num_structured_patches || patchCP_size1 == 0,
5365 "");
5366
5367 if (patchCP_size1 == 0) { return; }
5368
5369 MFEM_ABORT("PrintCoarsePatches is supported only in NCNURBSExtension");
5370}
5371
5372int NURBSExtension::VertexPairToEdge(const std::pair<int, int> &vertices) const
5373{
5374 MFEM_ABORT("VertexPairToEdge is supported only in NCNURBSExtension");
5375 return -1;
5376}
5377
5378void NURBSExtension::GetMasterEdgeDofs(bool dof, int me, Array<int> &dofs) const
5379{
5380 MFEM_ABORT("GetMasterEdgeDofs is supported only in NCNURBSExtension");
5381}
5382
5383void NURBSExtension::GetMasterFaceDofs(bool dof, int mf,
5384 Array2D<int> &dofs) const
5385{
5386 MFEM_ABORT("GetMasterFaceDofs is supported only in NCNURBSExtension");
5387}
5388
5389void NURBSExtension::RefineWithKVFactors(int rf,
5390 const std::string &kvf_filename,
5391 bool coarsened)
5392{
5393 MFEM_ABORT("RefineWithKVFactors is supported only in NCNURBSExtension");
5394}
5395
5396NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1, int dim_,
5397 const real_t* control_points)
5398{
5399 kv.SetSize(2);
5400 kv[0] = new KnotVector(*kv0);
5401 kv[1] = new KnotVector(*kv1);
5402 init(dim_);
5403 memcpy(data, control_points, sizeof (real_t) * ni * nj * dim_);
5404}
5405
5406NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1,
5407 const KnotVector *kv2, int dim_,
5408 const real_t* control_points)
5409{
5410 kv.SetSize(3);
5411 kv[0] = new KnotVector(*kv0);
5412 kv[1] = new KnotVector(*kv1);
5413 kv[2] = new KnotVector(*kv2);
5414 init(dim_);
5415 memcpy(data, control_points, sizeof (real_t) * ni * nj * nk * dim_);
5416}
5417
5418NURBSPatch::NURBSPatch(Array<const KnotVector *> &kv_, int dim_,
5419 const real_t* control_points)
5420{
5421 kv.SetSize(kv_.Size());
5422 int n = dim_;
5423 for (int i = 0; i < kv.Size(); i++)
5424 {
5425 kv[i] = new KnotVector(*kv_[i]);
5426 n *= kv[i]->GetNCP();
5427 }
5428 init(dim_);
5429 memcpy(data, control_points, sizeof(real_t)*n);
5430}
5431
5432#ifdef MFEM_USE_MPI
5433ParNURBSExtension::ParNURBSExtension(const ParNURBSExtension &orig)
5434 : NURBSExtension(orig),
5435 partitioning(orig.partitioning),
5436 gtopo(orig.gtopo),
5437 ldof_group(orig.ldof_group)
5438{
5439}
5440
5441ParNURBSExtension::ParNURBSExtension(MPI_Comm comm, NURBSExtension *parent,
5442 const int *partitioning_,
5443 const Array<bool> &active_bel)
5444 : gtopo(comm)
5445{
5446 if (parent->NumOfActiveElems < parent->NumOfElements)
5447 {
5448 // SetActive (BuildGroups?) and the way the weights are copied
5449 // do not support this case
5450 mfem_error("ParNURBSExtension::ParNURBSExtension :\n"
5451 " all elements in the parent must be active!");
5452 }
5453
5454 patchTopo = parent->patchTopo;
5455 // steal ownership of patchTopo from the 'parent' NURBS extension
5456 if (!parent->own_topo)
5457 {
5458 mfem_error("ParNURBSExtension::ParNURBSExtension :\n"
5459 " parent does not own the patch topology!");
5460 }
5461 own_topo = true;
5462 parent->own_topo = false;
5463
5464 parent->edge_to_ukv.Copy(edge_to_ukv);
5465
5466 parent->GetOrders().Copy(mOrders);
5467 mOrder = parent->GetOrder();
5468
5469 NumOfKnotVectors = parent->GetNKV();
5470 knotVectors.SetSize(NumOfKnotVectors);
5471 for (int i = 0; i < NumOfKnotVectors; i++)
5472 {
5473 knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
5474 }
5475 CreateComprehensiveKV();
5476
5477 GenerateOffsets();
5478 CountElements();
5479 CountBdrElements();
5480
5481 // copy 'partitioning_' to 'partitioning'
5482 partitioning.SetSize(GetGNE());
5483 for (int i = 0; i < GetGNE(); i++)
5484 {
5485 partitioning[i] = partitioning_[i];
5486 }
5487 SetActive(partitioning, active_bel);
5488
5489 GenerateActiveVertices();
5490 GenerateElementDofTable();
5491 // GenerateActiveBdrElems(); // done by SetActive for now
5492 GenerateBdrElementDofTable();
5493
5494 Table *serial_elem_dof = parent->GetElementDofTable();
5495 BuildGroups(partitioning, *serial_elem_dof);
5496
5497 weights.SetSize(GetNDof());
5498 // copy weights from parent
5499 for (int gel = 0, lel = 0; gel < GetGNE(); gel++)
5500 {
5501 if (activeElem[gel])
5502 {
5503 int ndofs = el_dof->RowSize(lel);
5504 int *ldofs = el_dof->GetRow(lel);
5505 int *gdofs = serial_elem_dof->GetRow(gel);
5506 for (int i = 0; i < ndofs; i++)
5507 {
5508 weights(ldofs[i]) = parent->weights(gdofs[i]);
5509 }
5510 lel++;
5511 }
5512 }
5513}
5514
5515ParNURBSExtension::ParNURBSExtension(NURBSExtension *parent,
5516 const ParNURBSExtension *par_parent)
5517 : gtopo(par_parent->gtopo.GetComm())
5518{
5519 // steal all data from parent
5520 mOrder = parent->mOrder;
5521 Swap(mOrders, parent->mOrders);
5522
5523 patchTopo = parent->patchTopo;
5524 own_topo = parent->own_topo;
5525 parent->own_topo = false;
5526
5527 Swap(edge_to_ukv, parent->edge_to_ukv);
5528
5529 NumOfKnotVectors = parent->NumOfKnotVectors;
5530 Swap(knotVectors, parent->knotVectors);
5531 Swap(knotVectorsCompr, parent->knotVectorsCompr);
5532
5533 NumOfVertices = parent->NumOfVertices;
5534 NumOfElements = parent->NumOfElements;
5535 NumOfBdrElements = parent->NumOfBdrElements;
5536 NumOfDofs = parent->NumOfDofs;
5537
5538 Swap(v_meshOffsets, parent->v_meshOffsets);
5539 Swap(e_meshOffsets, parent->e_meshOffsets);
5540 Swap(f_meshOffsets, parent->f_meshOffsets);
5541 Swap(p_meshOffsets, parent->p_meshOffsets);
5542
5543 Swap(v_spaceOffsets, parent->v_spaceOffsets);
5544 Swap(e_spaceOffsets, parent->e_spaceOffsets);
5545 Swap(f_spaceOffsets, parent->f_spaceOffsets);
5546 Swap(p_spaceOffsets, parent->p_spaceOffsets);
5547
5548 Swap(d_to_d, parent->d_to_d);
5549 Swap(master, parent->master);
5550 Swap(slave, parent->slave);
5551
5552 NumOfActiveVertices = parent->NumOfActiveVertices;
5553 NumOfActiveElems = parent->NumOfActiveElems;
5554 NumOfActiveBdrElems = parent->NumOfActiveBdrElems;
5555 NumOfActiveDofs = parent->NumOfActiveDofs;
5556
5557 Swap(activeVert, parent->activeVert);
5558 Swap(activeElem, parent->activeElem);
5559 Swap(activeBdrElem, parent->activeBdrElem);
5560 Swap(activeDof, parent->activeDof);
5561
5562 el_dof = parent->el_dof;
5563 bel_dof = parent->bel_dof;
5564 parent->el_dof = parent->bel_dof = NULL;
5565
5566 Swap(el_to_patch, parent->el_to_patch);
5567 Swap(bel_to_patch, parent->bel_to_patch);
5568 Swap(el_to_IJK, parent->el_to_IJK);
5569 Swap(bel_to_IJK, parent->bel_to_IJK);
5570
5571 Swap(weights, parent->weights);
5572 MFEM_VERIFY(!parent->HavePatches(), "");
5573
5574 delete parent;
5575
5576 MFEM_VERIFY(par_parent->partitioning,
5577 "parent ParNURBSExtension has no partitioning!");
5578
5579 // Support for the case when 'parent' is not a local NURBSExtension, i.e.
5580 // NumOfActiveElems is not the same as in 'par_parent'. In that case, we
5581 // assume 'parent' is a global NURBSExtension, i.e. all elements are active.
5582 bool extract_weights = false;
5583 if (NumOfActiveElems != par_parent->NumOfActiveElems)
5584 {
5585 MFEM_ASSERT(NumOfActiveElems == NumOfElements, "internal error");
5586
5587 SetActive(par_parent->partitioning, par_parent->activeBdrElem);
5588 GenerateActiveVertices();
5589 delete el_dof;
5590 el_to_patch.DeleteAll();
5591 el_to_IJK.DeleteAll();
5592 GenerateElementDofTable();
5593 // GenerateActiveBdrElems(); // done by SetActive for now
5594 delete bel_dof;
5595 bel_to_patch.DeleteAll();
5596 bel_to_IJK.DeleteAll();
5597 GenerateBdrElementDofTable();
5598 extract_weights = true;
5599 }
5600
5601 Table *glob_elem_dof = GetGlobalElementDofTable();
5602 BuildGroups(par_parent->partitioning, *glob_elem_dof);
5603 if (extract_weights)
5604 {
5605 Vector glob_weights;
5606 Swap(weights, glob_weights);
5607 weights.SetSize(GetNDof());
5608 // Copy the local 'weights' from the 'glob_weights'.
5609 // Assumption: the local element ids follow the global ordering.
5610 for (int gel = 0, lel = 0; gel < GetGNE(); gel++)
5611 {
5612 if (activeElem[gel])
5613 {
5614 int ndofs = el_dof->RowSize(lel);
5615 int *ldofs = el_dof->GetRow(lel);
5616 int *gdofs = glob_elem_dof->GetRow(gel);
5617 for (int i = 0; i < ndofs; i++)
5618 {
5619 weights(ldofs[i]) = glob_weights(gdofs[i]);
5620 }
5621 lel++;
5622 }
5623 }
5624 }
5625 delete glob_elem_dof;
5626}
5627
5628Table *ParNURBSExtension::GetGlobalElementDofTable()
5629{
5630 if (Dimension() == 1)
5631 {
5632 return Get1DGlobalElementDofTable();
5633 }
5634 else if (Dimension() == 2)
5635 {
5636 return Get2DGlobalElementDofTable();
5637 }
5638 else
5639 {
5640 return Get3DGlobalElementDofTable();
5641 }
5642}
5643
5644Table *ParNURBSExtension::Get1DGlobalElementDofTable()
5645{
5646 int el = 0;
5647 const KnotVector *kv[1];
5648 NURBSPatchMap p2g(this);
5649 Array<Connection> gel_dof_list;
5650
5651 for (int p = 0; p < GetNP(); p++)
5652 {
5653 p2g.SetPatchDofMap(p, kv);
5654
5655 // Load dofs
5656 const int ord0 = kv[0]->GetOrder();
5657
5658 for (int i = 0; i < kv[0]->GetNKS(); i++)
5659 {
5660 if (kv[0]->isElement(i))
5661 {
5662 Connection conn(el,0);
5663 for (int ii = 0; ii <= ord0; ii++)
5664 {
5665 conn.to = DofMap(p2g(i+ii));
5666 gel_dof_list.Append(conn);
5667 }
5668 el++;
5669 }
5670 }
5671 }
5672 // We must NOT sort gel_dof_list in this case.
5673 return (new Table(GetGNE(), gel_dof_list));
5674}
5675
5676Table *ParNURBSExtension::Get2DGlobalElementDofTable()
5677{
5678 int el = 0;
5679 const KnotVector *kv[2];
5680 NURBSPatchMap p2g(this);
5681 Array<Connection> gel_dof_list;
5682
5683 for (int p = 0; p < GetNP(); p++)
5684 {
5685 p2g.SetPatchDofMap(p, kv);
5686
5687 // Load dofs
5688 const int ord0 = kv[0]->GetOrder();
5689 const int ord1 = kv[1]->GetOrder();
5690 for (int j = 0; j < kv[1]->GetNKS(); j++)
5691 {
5692 if (kv[1]->isElement(j))
5693 {
5694 for (int i = 0; i < kv[0]->GetNKS(); i++)
5695 {
5696 if (kv[0]->isElement(i))
5697 {
5698 Connection conn(el,0);
5699 for (int jj = 0; jj <= ord1; jj++)
5700 {
5701 for (int ii = 0; ii <= ord0; ii++)
5702 {
5703 conn.to = DofMap(p2g(i+ii,j+jj));
5704 gel_dof_list.Append(conn);
5705 }
5706 }
5707 el++;
5708 }
5709 }
5710 }
5711 }
5712 }
5713 // We must NOT sort gel_dof_list in this case.
5714 return (new Table(GetGNE(), gel_dof_list));
5715}
5716
5717Table *ParNURBSExtension::Get3DGlobalElementDofTable()
5718{
5719 int el = 0;
5720 const KnotVector *kv[3];
5721 NURBSPatchMap p2g(this);
5722 Array<Connection> gel_dof_list;
5723
5724 for (int p = 0; p < GetNP(); p++)
5725 {
5726 p2g.SetPatchDofMap(p, kv);
5727
5728 // Load dofs
5729 const int ord0 = kv[0]->GetOrder();
5730 const int ord1 = kv[1]->GetOrder();
5731 const int ord2 = kv[2]->GetOrder();
5732 for (int k = 0; k < kv[2]->GetNKS(); k++)
5733 {
5734 if (kv[2]->isElement(k))
5735 {
5736 for (int j = 0; j < kv[1]->GetNKS(); j++)
5737 {
5738 if (kv[1]->isElement(j))
5739 {
5740 for (int i = 0; i < kv[0]->GetNKS(); i++)
5741 {
5742 if (kv[0]->isElement(i))
5743 {
5744 Connection conn(el,0);
5745 for (int kk = 0; kk <= ord2; kk++)
5746 {
5747 for (int jj = 0; jj <= ord1; jj++)
5748 {
5749 for (int ii = 0; ii <= ord0; ii++)
5750 {
5751 conn.to = DofMap(p2g(i+ii,j+jj,k+kk));
5752 gel_dof_list.Append(conn);
5753 }
5754 }
5755 }
5756 el++;
5757 }
5758 }
5759 }
5760 }
5761 }
5762 }
5763 }
5764 // We must NOT sort gel_dof_list in this case.
5765 return (new Table(GetGNE(), gel_dof_list));
5766}
5767
5768void ParNURBSExtension::SetActive(const int *partition,
5769 const Array<bool> &active_bel)
5770{
5771 activeElem.SetSize(GetGNE());
5772 activeElem = false;
5773 NumOfActiveElems = 0;
5774 const int MyRank = gtopo.MyRank();
5775 for (int i = 0; i < GetGNE(); i++)
5776 if (partition[i] == MyRank)
5777 {
5778 activeElem[i] = true;
5779 NumOfActiveElems++;
5780 }
5781
5782 active_bel.Copy(activeBdrElem);
5783 NumOfActiveBdrElems = 0;
5784 for (int i = 0; i < GetGNBE(); i++)
5785 if (activeBdrElem[i])
5786 {
5787 NumOfActiveBdrElems++;
5788 }
5789}
5790
5791void ParNURBSExtension::BuildGroups(const int *partition,
5792 const Table &elem_dof)
5793{
5794 Table dof_proc;
5795
5796 ListOfIntegerSets groups;
5797 IntegerSet group;
5798
5799 Transpose(elem_dof, dof_proc); // dof_proc is dof_elem
5800
5801 // convert elements to processors
5802 for (int i = 0; i < dof_proc.Size_of_connections(); i++)
5803 {
5804 dof_proc.GetJ()[i] = partition[dof_proc.GetJ()[i]];
5805 }
5806
5807 // the first group is the local one
5808 int MyRank = gtopo.MyRank();
5809 group.Recreate(1, &MyRank);
5810 groups.Insert(group);
5811
5812 int dof = 0;
5813 ldof_group.SetSize(GetNDof());
5814 for (int d = 0; d < GetNTotalDof(); d++)
5815 if (activeDof[d])
5816 {
5817 group.Recreate(dof_proc.RowSize(d), dof_proc.GetRow(d));
5818 ldof_group[dof] = groups.Insert(group);
5819
5820 dof++;
5821 }
5822
5823 gtopo.Create(groups, 1822);
5824}
5825#endif // MFEM_USE_MPI
5826
5827
5828void NURBSPatchMap::GetPatchKnotVectors(int p, const KnotVector *kv[])
5829{
5830 Ext->patchTopo->GetElementVertices(p, verts);
5831
5832 if (Ext->Dimension() == 1)
5833 {
5834 kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
5835 }
5836 else if (Ext->Dimension() == 2)
5837 {
5838 Ext->patchTopo->GetElementEdges(p, edges, oedge);
5839
5840 kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
5841 kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
5842 }
5843 else if (Ext->Dimension() == 3)
5844 {
5845 Ext->patchTopo->GetElementEdges(p, edges, oedge);
5846 Ext->patchTopo->GetElementFaces(p, faces, oface);
5847
5848 kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
5849 kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
5850 kv[2] = Ext->knotVectorsCompr[Ext->Dimension()*p + 2];
5851 }
5852 opatch = 0;
5853}
5854
5855void NURBSPatchMap::GetBdrPatchKnotVectors(int p, const KnotVector *kv[],
5856 int *okv)
5857{
5858 Ext->patchTopo->GetBdrElementVertices(p, verts);
5859
5860 if (Ext->Dimension() == 2)
5861 {
5862 Ext->patchTopo->GetBdrElementEdges(p, edges, oedge);
5863 kv[0] = Ext->KnotVec(edges[0], oedge[0], &okv[0]);
5864 opatch = oedge[0];
5865 }
5866 else if (Ext->Dimension() == 3)
5867 {
5868 faces.SetSize(1);
5869 Ext->patchTopo->GetBdrElementEdges(p, edges, oedge);
5870 Ext->patchTopo->GetBdrElementFace(p, &faces[0], &opatch);
5871
5872 kv[0] = Ext->KnotVec(edges[0], oedge[0], &okv[0]);
5873 kv[1] = Ext->KnotVec(edges[1], oedge[1], &okv[1]);
5874 }
5875}
5876
5877void NURBSPatchMap::SetPatchVertexMap(int p, const KnotVector *kv[])
5878{
5879 GetPatchKnotVectors(p, kv);
5880
5881 I = kv[0]->GetNE() - 1;
5882
5883 for (int i = 0; i < verts.Size(); i++)
5884 {
5885 verts[i] = Ext->v_meshOffsets[verts[i]];
5886 }
5887
5888 if (Ext->Dimension() >= 2)
5889 {
5890 J = kv[1]->GetNE() - 1;
5891 SetMasterEdges(false, kv);
5892 for (int i = 0; i < edges.Size(); i++)
5893 {
5894 edges[i] = Ext->e_meshOffsets[edges[i]];
5895 }
5896 }
5897 if (Ext->Dimension() == 3)
5898 {
5899 K = kv[2]->GetNE() - 1;
5900 SetMasterFaces(false);
5901 for (int i = 0; i < faces.Size(); i++)
5902 {
5903 faces[i] = Ext->f_meshOffsets[faces[i]];
5904 }
5905 }
5906
5907 pOffset = Ext->p_meshOffsets[p];
5908}
5909
5910void NURBSPatchMap::SetPatchDofMap(int p, const KnotVector *kv[])
5911{
5912 GetPatchKnotVectors(p, kv);
5913
5914 I = kv[0]->GetNCP() - 2;
5915
5916 for (int i = 0; i < verts.Size(); i++)
5917 {
5918 verts[i] = Ext->v_spaceOffsets[verts[i]];
5919 }
5920 if (Ext->Dimension() >= 2)
5921 {
5922 J = kv[1]->GetNCP() - 2;
5923 SetMasterEdges(true);
5924
5925 if (Ext->NonconformingPatches() && Ext->patchTopo->ncmesh
5926 && Ext->patchTopo->ncmesh->GetVertexToKnotSpan().Size() > 0)
5927 {
5928 for (int i = 0; i < edges.Size(); i++)
5929 {
5930 // Find the patchTopo->ncmesh edge corresponding to edges[i].
5931 Array<int> vert;
5932 Ext->patchTopo->GetEdgeVertices(edges[i], vert);
5933 const std::pair<int, int> vpair(vert[0], vert[1]);
5934 const int ncedge = Ext->VertexPairToEdge(vpair);
5935 edges[i] = Ext->e_spaceOffsets[ncedge];
5936 }
5937 }
5938 else
5939 {
5940 for (int i = 0; i < edges.Size(); i++)
5941 {
5942 edges[i] = Ext->e_spaceOffsets[edges[i]];
5943 }
5944 }
5945 }
5946 if (Ext->Dimension() == 3)
5947 {
5948 K = kv[2]->GetNCP() - 2;
5949 SetMasterFaces(true);
5950 for (int i = 0; i < faces.Size(); i++)
5951 {
5952 faces[i] = Ext->f_spaceOffsets[faces[i]];
5953 }
5954 }
5955
5956 pOffset = Ext->p_spaceOffsets[p];
5957}
5958
5959void NURBSPatchMap::SetBdrPatchVertexMap(int p, const KnotVector *kv[],
5960 int *okv)
5961{
5962 GetBdrPatchKnotVectors(p, kv, okv);
5963
5964 for (int i = 0; i < verts.Size(); i++)
5965 {
5966 verts[i] = Ext->v_meshOffsets[verts[i]];
5967 }
5968
5969 if (Ext->Dimension() == 1)
5970 {
5971 I = 0;
5972 }
5973 else if (Ext->Dimension() == 2)
5974 {
5975 I = kv[0]->GetNE() - 1;
5976 pOffset = Ext->e_meshOffsets[edges[0]];
5977 SetMasterEdges(false);
5978 }
5979 else if (Ext->Dimension() == 3)
5980 {
5981 I = kv[0]->GetNE() - 1;
5982 J = kv[1]->GetNE() - 1;
5983
5984 SetMasterEdges(false);
5985 SetMasterFaces(false);
5986 for (int i = 0; i < edges.Size(); i++)
5987 {
5988 edges[i] = Ext->e_meshOffsets[edges[i]];
5989 }
5990
5991 pOffset = Ext->f_meshOffsets[faces[0]];
5992 }
5993}
5994
5995void NURBSPatchMap::SetBdrPatchDofMap(int p, const KnotVector *kv[], int *okv)
5996{
5997 GetBdrPatchKnotVectors(p, kv, okv);
5998
5999 for (int i = 0; i < verts.Size(); i++)
6000 {
6001 verts[i] = Ext->v_spaceOffsets[verts[i]];
6002 }
6003
6004 if (Ext->Dimension() == 1)
6005 {
6006 I = 0;
6007 }
6008 else if (Ext->Dimension() == 2)
6009 {
6010 I = kv[0]->GetNCP() - 2;
6011 pOffset = Ext->e_spaceOffsets[edges[0]];
6012
6013 SetMasterEdges(true);
6014 }
6015 else if (Ext->Dimension() == 3)
6016 {
6017 I = kv[0]->GetNCP() - 2;
6018 J = kv[1]->GetNCP() - 2;
6019
6020 SetMasterEdges(true);
6021 for (int i = 0; i < edges.Size(); i++)
6022 {
6023 edges[i] = Ext->e_spaceOffsets[edges[i]];
6024 }
6025
6026 pOffset = Ext->f_spaceOffsets[faces[0]];
6027 }
6028}
6029
6030}
int Size() const
Return the logical size of the array.
Definition array.hpp:166
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
std::shared_ptr< SpacingFunction > spacing
Function to define the distribution of knots for any number of knot spans.
Definition nurbs.hpp:207
void PrintFunctions(std::ostream &os, int samples=11) const
Prints the non-zero shape functions and their first and second derivatives associated with the KnotVe...
Definition nurbs.cpp:346
int NumOfElements
Number of elements, defined by distinct knots.
Definition nurbs.hpp:52
void CalcDnShape(Vector &gradn, int n, int i, real_t xi) const
Calculate n-th derivatives (order n) of the nonvanishing shape function values in grad for the elemen...
Definition nurbs.cpp:468
int Order
Order of the B-spline basis functions.
Definition nurbs.hpp:46
KnotVector & operator=(const KnotVector &kv)
Definition nurbs.cpp:99
bool isElement(int i) const
Return whether the knot index Order plus i is the beginning of an element.
Definition nurbs.hpp:99
void CalcShape(Vector &shape, int i, real_t xi) const
Calculate the nonvanishing shape function values in shape for the element corresponding to knot index...
Definition nurbs.cpp:383
void UniformRefinement(Vector &new_knots, int rf) const
Uniformly refine by factor rf, by inserting knots in each span.
Definition nurbs.cpp:140
Vector knot
Stores the values of all knots.
Definition nurbs.hpp:43
KnotVector()=default
Create an empty KnotVector.
int GetNKS() const
Return the number of control points minus the order. This is not the number of knot spans,...
Definition nurbs.hpp:104
KnotVector * FullyCoarsen()
Coarsen to a single element.
Definition nurbs.cpp:791
void GetElements()
Count the number of elements.
Definition nurbs.cpp:313
KnotVector * DegreeElevate(int t) const
Return a new KnotVector with elevated degree by repeating the endpoints of the KnotVector.
Definition nurbs.cpp:111
static const int MaxOrder
Definition nurbs.hpp:40
void CalcD2Shape(Vector &grad2, int i, real_t xi) const
Calculate second-order shape function derivatives, using CalcDnShape.
Definition nurbs.hpp:133
int GetNCP() const
Return the number of control points.
Definition nurbs.hpp:86
int NumOfControlPoints
Number of control points.
Definition nurbs.hpp:49
void CalcDShape(Vector &grad, int i, real_t xi) const
Calculate derivatives of the nonvanishing shape function values in grad for the element corresponding...
Definition nurbs.cpp:410
int Size() const
Return the number of knots, including multiplicities.
Definition nurbs.hpp:92
bool coarse
Flag to indicate whether the KnotVector has been coarsened, which means it is ready for non-nested re...
Definition nurbs.hpp:211
void Flip()
Reverse the knots.
Definition nurbs.cpp:325
void Difference(const KnotVector &kv, Vector &diff) const
Definition nurbs.cpp:756
int GetCoarseningFactor() const
Definition nurbs.cpp:161
Vector GetFineKnots(const int cf) const
Definition nurbs.cpp:180
int GetNE() const
Return the number of elements, defined by distinct knots.
Definition nurbs.hpp:83
void Refinement(Vector &new_knots, int rf) const
Refine with refinement factor rf.
Definition nurbs.cpp:248
void Print(std::ostream &os) const
Print the order, number of control points, and knots.
Definition nurbs.cpp:340
Mesh data type.
Definition mesh.hpp:65
NCNURBSExtension extends NURBSExtension to support NC-patch NURBS meshes.
Definition ncnurbs.hpp:23
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
Definition nurbs.hpp:474
A NURBS patch can be 1D, 2D, or 3D, and is defined as a tensor product of KnotVectors.
Definition nurbs.hpp:226
Parallel version of NURBSExtension.
Definition nurbs.hpp:1036
Vector data type.
Definition vector.hpp:82
void Print(std::ostream &out=mfem::out, int width=8) const
Prints vector to stream out.
Definition vector.cpp:870
void Load(std::istream **in, int np, int *dim)
Reads a vector from multiple files.
Definition vector.cpp:127
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
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 a
Definition lissajous.cpp:41
string direction
void mfem_error(const char *msg)
Definition error.cpp:154
NURBSPatch * Interpolate(NURBSPatch &p1, NURBSPatch &p2)
Definition nurbs.cpp:2049
NURBSPatch * Revolve3D(NURBSPatch &patch, real_t n[], real_t ang, int times)
Definition nurbs.cpp:2096
float real_t
Definition config.hpp:46