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