// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mesh_headers.hpp" #include "../fem/fem.hpp" #include "../general/text.hpp" #include #include #if defined(_MSC_VER) && (_MSC_VER < 1800) #include #define copysign _copysign #endif namespace mfem { using namespace std; const int KnotVector::MaxOrder = 10; KnotVector::KnotVector(std::istream &input) { input >> Order >> NumOfControlPoints; knot.Load(input, NumOfControlPoints + Order + 1); GetElements(); } KnotVector::KnotVector(int Order_, int NCP) { Order = Order_; NumOfControlPoints = NCP; knot.SetSize(NumOfControlPoints + Order + 1); knot = -1.; } KnotVector &KnotVector::operator=(const KnotVector &kv) { Order = kv.Order; NumOfControlPoints = kv.NumOfControlPoints; NumOfElements = kv.NumOfElements; knot = kv.knot; // alternatively, re-compute NumOfElements // GetElements(); return *this; } KnotVector *KnotVector::DegreeElevate(int t) const { if (t < 0) { mfem_error("KnotVector::DegreeElevate :\n" " Parent KnotVector order higher than child"); } const int nOrder = Order + t; KnotVector *newkv = new KnotVector(nOrder, GetNCP() + t); for (int i = 0; i <= nOrder; i++) { (*newkv)[i] = knot(0); } for (int i = nOrder + 1; i < newkv->GetNCP(); i++) { (*newkv)[i] = knot(i - t); } for (int i = 0; i <= nOrder; i++) { (*newkv)[newkv->GetNCP() + i] = knot(knot.Size()-1); } newkv->GetElements(); return newkv; } void KnotVector::UniformRefinement(Vector &newknots) const { newknots.SetSize(NumOfElements); int j = 0; for (int i = 0; i < knot.Size()-1; i++) { if (knot(i) != knot(i+1)) { newknots(j) = 0.5*(knot(i) + knot(i+1)); j++; } } } void KnotVector::GetElements() { NumOfElements = 0; for (int i = Order; i < NumOfControlPoints; i++) { if (knot(i) != knot(i+1)) { NumOfElements++; } } } void KnotVector::Flip() { double apb = knot(0) + knot(knot.Size()-1); int ns = (NumOfControlPoints - Order)/2; for (int i = 1; i <= ns; i++) { double tmp = apb - knot(Order + i); knot(Order + i) = apb - knot(NumOfControlPoints - i); knot(NumOfControlPoints - i) = tmp; } } void KnotVector::Print(std::ostream &os) const { os << Order << ' ' << NumOfControlPoints << ' '; knot.Print(os, knot.Size()); } void KnotVector::PrintFunctions(std::ostream &os, int samples) const { Vector shape(Order+1); double x, dx = 1.0/double (samples - 1); for (int i = 0; i MaxOrder!"); int p = Order; int ip = (i >= 0) ? (i + p) : (-1 - i + p); double u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip), saved, tmp; double left[MaxOrder+1], right[MaxOrder+1]; shape(0) = 1.; for (int j = 1; j <= p; ++j) { left[j] = u - knot(ip+1-j); right[j] = knot(ip+j) - u; saved = 0.; for (int r = 0; r < j; ++r) { tmp = shape(r)/(right[r+1] + left[j-r]); shape(r) = saved + right[r+1]*tmp; saved = left[j-r]*tmp; } shape(j) = saved; } } // Routine from "The NURBS book" - 2nd ed - Piegl and Tiller // Algorithm A2.3 p. 72 void KnotVector::CalcDShape(Vector &grad, int i, double xi) const { int p = Order, rk, pk; int ip = (i >= 0) ? (i + p) : (-1 - i + p); double u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip), temp, saved, d; double ndu[MaxOrder+1][MaxOrder+1], left[MaxOrder+1], right[MaxOrder+1]; #ifdef MFEM_DEBUG if (p > MaxOrder) { mfem_error("KnotVector::CalcDShape : Order > MaxOrder!"); } #endif ndu[0][0] = 1.0; for (int j = 1; j <= p; j++) { left[j] = u - knot(ip-j+1); right[j] = knot(ip+j) - u; saved = 0.0; for (int r = 0; r < j; r++) { ndu[j][r] = right[r+1] + left[j-r]; temp = ndu[r][j-1]/ndu[j][r]; ndu[r][j] = saved + right[r+1]*temp; saved = left[j-r]*temp; } ndu[j][j] = saved; } for (int r = 0; r <= p; ++r) { d = 0.0; rk = r-1; pk = p-1; if (r >= 1) { d = ndu[rk][pk]/ndu[p][rk]; } if (r <= pk) { d -= ndu[r][pk]/ndu[p][r]; } grad(r) = d; } if (i >= 0) { grad *= p*(knot(ip+1) - knot(ip)); } else { grad *= p*(knot(ip) - knot(ip+1)); } } // Routine from "The NURBS book" - 2nd ed - Piegl and Tiller void KnotVector::CalcDnShape(Vector &gradn, int n, int i, double xi) const { int p = Order, rk, pk, j1, j2,r,j,k; int ip = (i >= 0) ? (i + p) : (-1 - i + p); double u = getKnotLocation((i >= 0) ? xi : 1. - xi, ip); double temp, saved, d; double a[2][MaxOrder+1],ndu[MaxOrder+1][MaxOrder+1], left[MaxOrder+1], right[MaxOrder+1]; #ifdef MFEM_DEBUG if (p > MaxOrder) { mfem_error("KnotVector::CalcDnShape : Order > MaxOrder!"); } #endif ndu[0][0] = 1.0; for (j = 1; j <= p; j++) { left[j] = u - knot(ip-j+1); right[j] = knot(ip+j)- u; saved = 0.0; for (r = 0; r < j; r++) { ndu[j][r] = right[r+1] + left[j-r]; temp = ndu[r][j-1]/ndu[j][r]; ndu[r][j] = saved + right[r+1]*temp; saved = left[j-r]*temp; } ndu[j][j] = saved; } for (r = 0; r <= p; r++) { int s1 = 0; int s2 = 1; a[0][0] = 1.0; for (k = 1; k <= n; k++) { d = 0.0; rk = r-k; pk = p-k; if (r >= k) { a[s2][0] = a[s1][0]/ndu[pk+1][rk]; d = a[s2][0]*ndu[rk][pk]; } if (rk >= -1) { j1 = 1; } else { j1 = -rk; } if (r-1<= pk) { j2 = k-1; } else { j2 = p-r; } for (j = j1; j <= j2; j++) { a[s2][j] = (a[s1][j] - a[s1][j-1])/ndu[pk+1][rk+j]; d += a[s2][j]*ndu[rk+j][pk]; } if (r <= pk) { a[s2][k] = - a[s1][k-1]/ndu[pk+1][r]; d += a[s2][j]*ndu[rk+j][pk]; } gradn[r] = d; j = s1; s1 = s2; s2 = j; } } if (i >= 0) { u = (knot(ip+1) - knot(ip)); } else { u = (knot(ip) - knot(ip+1)); } temp = p*u; for (k = 1; k <= n-1; k++) { temp *= (p-k)*u; } for (j = 0; j <= p; j++) { gradn[j] *= temp; } } void KnotVector::FindMaxima(Array &ks, Vector &xi, Vector &u) { Vector shape(Order+1); Vector maxima(GetNCP()); double arg1, arg2, arg, max1, max2, max; xi.SetSize(GetNCP()); u.SetSize(GetNCP()); ks.SetSize(GetNCP()); for (int j = 0; j max1 ) || (max > max2) ) { if (max1 < max2) { max1 = max; arg1 = arg; } else { max2 = max; arg2 = arg; } arg = (arg1 + arg2)/2; CalcShape ( shape, i, arg); max = shape[d]; } if (max > maxima[j]) { maxima[j] = max; ks[j] = i; xi[j] = arg; u[j] = getKnotLocation(arg, i+Order); } } } } } // Routine from "The NURBS book" - 2nd ed - Piegl and Tiller // Algorithm A9.1 p. 369 void KnotVector::FindInterpolant(Array &x) { int order = GetOrder(); int ncp = GetNCP(); // Find interpolation points Vector xi_args, u_args; Array i_args; FindMaxima(i_args,xi_args, u_args); // Assemble collocation matrix Vector shape(order+1); DenseMatrix A(ncp,ncp); A = 0.0; for (int i = 0; i < ncp; i++) { CalcShape ( shape, i_args[i], xi_args[i]); for (int p = 0; p < order+1; p++) { A(i,i_args[i] + p) = shape[p]; } } // Solve problems A.Invert(); Vector tmp; for (int i= 0; i < x.Size(); i++) { tmp = *x[i]; A.Mult(tmp,*x[i]); } } int KnotVector::findKnotSpan(double u) const { int low, mid, high; if (u == knot(NumOfControlPoints+Order)) { mid = NumOfControlPoints; } else { low = Order; high = NumOfControlPoints + 1; mid = (low + high)/2; while ( (u < knot(mid-1)) || (u > knot(mid)) ) { if (u < knot(mid-1)) { high = mid; } else { low = mid; } mid = (low + high)/2; } } return mid; } void KnotVector::Difference(const KnotVector &kv, Vector &diff) const { if (Order != kv.GetOrder()) { mfem_error("KnotVector::Difference :\n" " Can not compare knot vectors with different orders!"); } int s = kv.Size() - Size(); if (s < 0) { kv.Difference(*this, diff); return; } diff.SetSize(s); s = 0; int i = 0; for (int j = 0; j < kv.Size(); j++) { if (abs(knot(i) - kv[j]) < 2 * std::numeric_limits::epsilon()) { i++; } else { diff(s) = kv[j]; s++; } } } void NURBSPatch::init(int dim_) { Dim = dim_; sd = nd = -1; if (kv.Size() == 1) { ni = kv[0]->GetNCP(); nj = -1; nk = -1; data = new double[ni*Dim]; #ifdef MFEM_DEBUG for (int i = 0; i < ni*Dim; i++) { data[i] = -999.99; } #endif } else if (kv.Size() == 2) { ni = kv[0]->GetNCP(); nj = kv[1]->GetNCP(); nk = -1; data = new double[ni*nj*Dim]; #ifdef MFEM_DEBUG for (int i = 0; i < ni*nj*Dim; i++) { data[i] = -999.99; } #endif } else if (kv.Size() == 3) { ni = kv[0]->GetNCP(); nj = kv[1]->GetNCP(); nk = kv[2]->GetNCP(); data = new double[ni*nj*nk*Dim]; #ifdef MFEM_DEBUG for (int i = 0; i < ni*nj*nk*Dim; i++) { data[i] = -999.99; } #endif } else { mfem_error("NURBSPatch::init : Wrong dimension of knotvectors!"); } } NURBSPatch::NURBSPatch(const NURBSPatch &orig) : ni(orig.ni), nj(orig.nj), nk(orig.nk), Dim(orig.Dim), data(NULL), kv(orig.kv.Size()), nd(orig.nd), ls(orig.ls), sd(orig.sd) { // Allocate and copy data: const int data_size = Dim*ni*nj*((kv.Size() == 2) ? 1 : nk); data = new double[data_size]; std::memcpy(data, orig.data, data_size*sizeof(double)); // Copy the knot vectors: for (int i = 0; i < kv.Size(); i++) { kv[i] = new KnotVector(*orig.kv[i]); } } NURBSPatch::NURBSPatch(std::istream &input) { int pdim, dim, size = 1; string ident; input >> ws >> ident >> pdim; // knotvectors kv.SetSize(pdim); for (int i = 0; i < pdim; i++) { kv[i] = new KnotVector(input); size *= kv[i]->GetNCP(); } input >> ws >> ident >> dim; // dimension init(dim + 1); input >> ws >> ident; // controlpoints (homogeneous coordinates) if (ident == "controlpoints" || ident == "controlpoints_homogeneous") { for (int j = 0, i = 0; i < size; i++) for (int d = 0; d <= dim; d++, j++) { input >> data[j]; } } else // "controlpoints_cartesian" (Cartesian coordinates with weight) { for (int j = 0, i = 0; i < size; i++) { for (int d = 0; d <= dim; d++) { input >> data[j+d]; } for (int d = 0; d < dim; d++) { data[j+d] *= data[j+dim]; } j += (dim+1); } } } NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1, int dim_) { kv.SetSize(2); kv[0] = new KnotVector(*kv0); kv[1] = new KnotVector(*kv1); init(dim_); } NURBSPatch::NURBSPatch(const KnotVector *kv0, const KnotVector *kv1, const KnotVector *kv2, int dim_) { kv.SetSize(3); kv[0] = new KnotVector(*kv0); kv[1] = new KnotVector(*kv1); kv[2] = new KnotVector(*kv2); init(dim_); } NURBSPatch::NURBSPatch(Array &kv_, int dim_) { kv.SetSize(kv_.Size()); for (int i = 0; i < kv.Size(); i++) { kv[i] = new KnotVector(*kv_[i]); } init(dim_); } NURBSPatch::NURBSPatch(NURBSPatch *parent, int dir, int Order, int NCP) { kv.SetSize(parent->kv.Size()); for (int i = 0; i < kv.Size(); i++) if (i != dir) { kv[i] = new KnotVector(*parent->kv[i]); } else { kv[i] = new KnotVector(Order, NCP); } init(parent->Dim); } void NURBSPatch::swap(NURBSPatch *np) { if (data != NULL) { delete [] data; } for (int i = 0; i < kv.Size(); i++) { if (kv[i]) { delete kv[i]; } } data = np->data; np->kv.Copy(kv); ni = np->ni; nj = np->nj; nk = np->nk; Dim = np->Dim; np->data = NULL; np->kv.SetSize(0); delete np; } NURBSPatch::~NURBSPatch() { if (data != NULL) { delete [] data; } for (int i = 0; i < kv.Size(); i++) { if (kv[i]) { delete kv[i]; } } } void NURBSPatch::Print(std::ostream &os) const { int size = 1; os << "knotvectors\n" << kv.Size() << '\n'; for (int i = 0; i < kv.Size(); i++) { kv[i]->Print(os); size *= kv[i]->GetNCP(); } os << "\ndimension\n" << Dim - 1 << "\n\ncontrolpoints\n"; for (int j = 0, i = 0; i < size; i++) { os << data[j++]; for (int d = 1; d < Dim; d++) { os << ' ' << data[j++]; } os << '\n'; } } int NURBSPatch::SetLoopDirection(int dir) { if (nj == -1) { if (dir == 0) { sd = Dim; nd = ni; ls = Dim; return ls; } else { mfem::err << "NURBSPatch::SetLoopDirection :\n" " Direction error in 1D patch, dir = " << dir << '\n'; mfem_error(); } } else if (nk == -1) { if (dir == 0) { sd = Dim; nd = ni; ls = nj*Dim; return ls; } else if (dir == 1) { sd = ni*Dim; nd = nj; ls = ni*Dim; return ls; } else { mfem::err << "NURBSPatch::SetLoopDirection :\n" " Direction error in 2D patch, dir = " << dir << '\n'; mfem_error(); } } else { if (dir == 0) { sd = Dim; nd = ni; ls = nj*nk*Dim; return ls; } else if (dir == 1) { sd = ni*Dim; nd = nj; ls = ni*nk*Dim; return ls; } else if (dir == 2) { sd = ni*nj*Dim; nd = nk; ls = ni*nj*Dim; return ls; } else { mfem::err << "NURBSPatch::SetLoopDirection :\n" " Direction error in 3D patch, dir = " << dir << '\n'; mfem_error(); } } return -1; } void NURBSPatch::UniformRefinement() { Vector newknots; for (int dir = 0; dir < kv.Size(); dir++) { kv[dir]->UniformRefinement(newknots); KnotInsert(dir, newknots); } } void NURBSPatch::KnotInsert(Array &newkv) { for (int dir = 0; dir < kv.Size(); dir++) { KnotInsert(dir, *newkv[dir]); } } void NURBSPatch::KnotInsert(int dir, const KnotVector &newkv) { if (dir >= kv.Size() || dir < 0) { mfem_error("NURBSPatch::KnotInsert : Incorrect direction!"); } int t = newkv.GetOrder() - kv[dir]->GetOrder(); if (t > 0) { DegreeElevate(dir, t); } else if (t < 0) { mfem_error("NURBSPatch::KnotInsert : Incorrect order!"); } Vector diff; GetKV(dir)->Difference(newkv, diff); if (diff.Size() > 0) { KnotInsert(dir, diff); } } void NURBSPatch::KnotInsert(Array &newkv) { for (int dir = 0; dir < kv.Size(); dir++) { KnotInsert(dir, *newkv[dir]); } } // Routine from "The NURBS book" - 2nd ed - Piegl and Tiller void NURBSPatch::KnotInsert(int dir, const Vector &knot) { if (knot.Size() == 0 ) { return; } if (dir >= kv.Size() || dir < 0) { mfem_error("NURBSPatch::KnotInsert : Incorrect direction!"); } NURBSPatch &oldp = *this; KnotVector &oldkv = *kv[dir]; NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder(), oldkv.GetNCP() + knot.Size()); NURBSPatch &newp = *newpatch; KnotVector &newkv = *newp.GetKV(dir); int size = oldp.SetLoopDirection(dir); if (size != newp.SetLoopDirection(dir)) { mfem_error("NURBSPatch::KnotInsert : Size mismatch!"); } int rr = knot.Size() - 1; int a = oldkv.findKnotSpan(knot(0)) - 1; int b = oldkv.findKnotSpan(knot(rr)) - 1; int pl = oldkv.GetOrder(); int ml = oldkv.GetNCP(); for (int j = 0; j <= a; j++) { newkv[j] = oldkv[j]; } for (int j = b+pl; j <= ml+pl; j++) { newkv[j+rr+1] = oldkv[j]; } for (int k = 0; k <= (a-pl); k++) { for (int ll = 0; ll < size; ll++) { newp.slice(k,ll) = oldp.slice(k,ll); } } for (int k = (b-1); k < ml; k++) { for (int ll = 0; ll < size; ll++) { newp.slice(k+rr+1,ll) = oldp.slice(k,ll); } } int i = b+pl-1; int k = b+pl+rr; for (int j = rr; j >= 0; j--) { while ( (knot(j) <= oldkv[i]) && (i > a) ) { newkv[k] = oldkv[i]; for (int ll = 0; ll < size; ll++) { newp.slice(k-pl-1,ll) = oldp.slice(i-pl-1,ll); } k--; i--; } for (int ll = 0; ll < size; ll++) { newp.slice(k-pl-1,ll) = newp.slice(k-pl,ll); } for (int l = 1; l <= pl; l++) { int ind = k-pl+l; double alfa = newkv[k+l] - knot(j); if (fabs(alfa) == 0.0) { for (int ll = 0; ll < size; ll++) { newp.slice(ind-1,ll) = newp.slice(ind,ll); } } else { alfa = alfa/(newkv[k+l] - oldkv[i-pl+l]); for (int ll = 0; ll < size; ll++) { newp.slice(ind-1,ll) = alfa*newp.slice(ind-1,ll) + (1.0-alfa)*newp.slice(ind, ll); } } } newkv[k] = knot(j); k--; } newkv.GetElements(); swap(newpatch); } void NURBSPatch::DegreeElevate(int t) { for (int dir = 0; dir < kv.Size(); dir++) { DegreeElevate(dir, t); } } // Routine from "The NURBS book" - 2nd ed - Piegl and Tiller void NURBSPatch::DegreeElevate(int dir, int t) { if (dir >= kv.Size() || dir < 0) { mfem_error("NURBSPatch::DegreeElevate : Incorrect direction!"); } int i, j, k, kj, mpi, mul, mh, kind, cind, first, last; int r, a, b, oldr, save, s, tr, lbz, rbz, l; double inv, ua, ub, numer, alf, den, bet, gam; NURBSPatch &oldp = *this; KnotVector &oldkv = *kv[dir]; oldkv.GetElements(); NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t, oldkv.GetNCP() + oldkv.GetNE()*t); NURBSPatch &newp = *newpatch; KnotVector &newkv = *newp.GetKV(dir); int size = oldp.SetLoopDirection(dir); if (size != newp.SetLoopDirection(dir)) { mfem_error("NURBSPatch::DegreeElevate : Size mismatch!"); } int p = oldkv.GetOrder(); int n = oldkv.GetNCP()-1; DenseMatrix bezalfs (p+t+1, p+1); DenseMatrix bpts (p+1, size); DenseMatrix ebpts (p+t+1, size); DenseMatrix nextbpts(p-1, size); Vector alphas (p-1); int m = n + p + 1; int ph = p + t; int ph2 = ph/2; { Array2D binom(ph+1, ph+1); for (i = 0; i <= ph; i++) { binom(i,0) = binom(i,i) = 1; for (j = 1; j < i; j++) { binom(i,j) = binom(i-1,j) + binom(i-1,j-1); } } bezalfs(0,0) = 1.0; bezalfs(ph,p) = 1.0; for (i = 1; i <= ph2; i++) { inv = 1.0/binom(ph,i); mpi = min(p,i); for (j = max(0,i-t); j <= mpi; j++) { bezalfs(i,j) = inv*binom(p,j)*binom(t,i-j); } } } for (i = ph2+1; i < ph; i++) { mpi = min(p,i); for (j = max(0,i-t); j <= mpi; j++) { bezalfs(i,j) = bezalfs(ph-i,p-j); } } mh = ph; kind = ph + 1; r = -1; a = p; b = p + 1; cind = 1; ua = oldkv[0]; for (l = 0; l < size; l++) { newp.slice(0,l) = oldp.slice(0,l); } for (i = 0; i <= ph; i++) { newkv[i] = ua; } for (i = 0; i <= p; i++) { for (l = 0; l < size; l++) { bpts(i,l) = oldp.slice(i,l); } } while (b < m) { i = b; while (b < m && oldkv[b] == oldkv[b+1]) { b++; } mul = b-i+1; mh = mh + mul + t; ub = oldkv[b]; oldr = r; r = p-mul; if (oldr > 0) { lbz = (oldr+2)/2; } else { lbz = 1; } if (r > 0) { rbz = ph-(r+1)/2; } else { rbz = ph; } if (r > 0) { numer = ub - ua; for (k = p ; k > mul; k--) { alphas[k-mul-1] = numer/(oldkv[a+k]-ua); } for (j = 1; j <= r; j++) { save = r-j; s = mul+j; for (k = p; k >= s; k--) { for (l = 0; l < size; l++) bpts(k,l) = (alphas[k-s]*bpts(k,l) + (1.0-alphas[k-s])*bpts(k-1,l)); } for (l = 0; l < size; l++) { nextbpts(save,l) = bpts(p,l); } } } for (i = lbz; i <= ph; i++) { for (l = 0; l < size; l++) { ebpts(i,l) = 0.0; } mpi = min(p,i); for (j = max(0,i-t); j <= mpi; j++) { for (l = 0; l < size; l++) { ebpts(i,l) += bezalfs(i,j)*bpts(j,l); } } } if (oldr > 1) { first = kind-2; last = kind; den = ub-ua; bet = (ub-newkv[kind-1])/den; for (tr = 1; tr < oldr; tr++) { i = first; j = last; kj = j-kind+1; while (j-i > tr) { if (i < cind) { alf = (ub-newkv[i])/(ua-newkv[i]); for (l = 0; l < size; l++) { newp.slice(i,l) = alf*newp.slice(i,l)-(1.0-alf)*newp.slice(i-1,l); } } if (j >= lbz) { if ((j-tr) <= (kind-ph+oldr)) { gam = (ub-newkv[j-tr])/den; for (l = 0; l < size; l++) { ebpts(kj,l) = gam*ebpts(kj,l) + (1.0-gam)*ebpts(kj+1,l); } } else { for (l = 0; l < size; l++) { ebpts(kj,l) = bet*ebpts(kj,l) + (1.0-bet)*ebpts(kj+1,l); } } } i = i+1; j = j-1; kj = kj-1; } first--; last++; } } if (a != p) { for (i = 0; i < (ph-oldr); i++) { newkv[kind] = ua; kind = kind+1; } } for (j = lbz; j <= rbz; j++) { for (l = 0; l < size; l++) { newp.slice(cind,l) = ebpts(j,l); } cind = cind +1; } if (b < m) { for (j = 0; j ((*this).slice(id,i), (*this).slice(nd-1-id,i)); } kv[dir]->Flip(); } void NURBSPatch::SwapDirections(int dir1, int dir2) { if (abs(dir1-dir2) == 2) { mfem_error("NURBSPatch::SwapDirections :" " directions 0 and 2 are not supported!"); } Array nkv(kv); Swap(nkv[dir1], nkv[dir2]); NURBSPatch *newpatch = new NURBSPatch(nkv, Dim); int size = SetLoopDirection(dir1); newpatch->SetLoopDirection(dir2); for (int id = 0; id < nd; id++) for (int i = 0; i < size; i++) { (*newpatch).slice(id,i) = (*this).slice(id,i); } swap(newpatch); } void NURBSPatch::Rotate(double angle, double n[]) { if (Dim == 3) { Rotate2D(angle); } else { if (n == NULL) { mfem_error("NURBSPatch::Rotate : Specify an angle for a 3D rotation."); } Rotate3D(n, angle); } } void NURBSPatch::Get2DRotationMatrix(double angle, DenseMatrix &T) { double s = sin(angle); double c = cos(angle); T.SetSize(2); T(0,0) = c; T(0,1) = -s; T(1,0) = s; T(1,1) = c; } void NURBSPatch::Rotate2D(double angle) { if (Dim != 3) { mfem_error("NURBSPatch::Rotate2D : not a NURBSPatch in 2D!"); } DenseMatrix T(2); Vector x(2), y(NULL, 2); Get2DRotationMatrix(angle, T); int size = 1; for (int i = 0; i < kv.Size(); i++) { size *= kv[i]->GetNCP(); } for (int i = 0; i < size; i++) { y.SetData(data + i*Dim); x = y; T.Mult(x, y); } } void NURBSPatch::Get3DRotationMatrix(double n[], double angle, double r, DenseMatrix &T) { double c, s, c1; double l2 = n[0]*n[0] + n[1]*n[1] + n[2]*n[2]; double l = sqrt(l2); if (fabs(angle) == M_PI_2) { s = r*copysign(1., angle); c = 0.; c1 = -1.; } else if (fabs(angle) == M_PI) { s = 0.; c = -r; c1 = c - 1.; } else { s = r*sin(angle); c = r*cos(angle); c1 = c - 1.; } T.SetSize(3); T(0,0) = (n[0]*n[0] + (n[1]*n[1] + n[2]*n[2])*c)/l2; T(0,1) = -(n[0]*n[1]*c1)/l2 - (n[2]*s)/l; T(0,2) = -(n[0]*n[2]*c1)/l2 + (n[1]*s)/l; T(1,0) = -(n[0]*n[1]*c1)/l2 + (n[2]*s)/l; T(1,1) = (n[1]*n[1] + (n[0]*n[0] + n[2]*n[2])*c)/l2; T(1,2) = -(n[1]*n[2]*c1)/l2 - (n[0]*s)/l; T(2,0) = -(n[0]*n[2]*c1)/l2 - (n[1]*s)/l; T(2,1) = -(n[1]*n[2]*c1)/l2 + (n[0]*s)/l; T(2,2) = (n[2]*n[2] + (n[0]*n[0] + n[1]*n[1])*c)/l2; } void NURBSPatch::Rotate3D(double n[], double angle) { if (Dim != 4) { mfem_error("NURBSPatch::Rotate3D : not a NURBSPatch in 3D!"); } DenseMatrix T(3); Vector x(3), y(NULL, 3); Get3DRotationMatrix(n, angle, 1., T); int size = 1; for (int i = 0; i < kv.Size(); i++) { size *= kv[i]->GetNCP(); } for (int i = 0; i < size; i++) { y.SetData(data + i*Dim); x = y; T.Mult(x, y); } } int NURBSPatch::MakeUniformDegree(int degree) { int maxd = degree; if (maxd == -1) { for (int dir = 0; dir < kv.Size(); dir++) { maxd = std::max(maxd, kv[dir]->GetOrder()); } } for (int dir = 0; dir < kv.Size(); dir++) { if (maxd > kv[dir]->GetOrder()) { DegreeElevate(dir, maxd - kv[dir]->GetOrder()); } } return maxd; } NURBSPatch *Interpolate(NURBSPatch &p1, NURBSPatch &p2) { if (p1.kv.Size() != p2.kv.Size() || p1.Dim != p2.Dim) { mfem_error("Interpolate(NURBSPatch &, NURBSPatch &)"); } int size = 1, dim = p1.Dim; Array kv(p1.kv.Size() + 1); for (int i = 0; i < p1.kv.Size(); i++) { if (p1.kv[i]->GetOrder() < p2.kv[i]->GetOrder()) { p1.KnotInsert(i, *p2.kv[i]); p2.KnotInsert(i, *p1.kv[i]); } else { p2.KnotInsert(i, *p1.kv[i]); p1.KnotInsert(i, *p2.kv[i]); } kv[i] = p1.kv[i]; size *= kv[i]->GetNCP(); } KnotVector &nkv = *(new KnotVector(1, 2)); nkv[0] = nkv[1] = 0.0; nkv[2] = nkv[3] = 1.0; nkv.GetElements(); kv.Last() = &nkv; NURBSPatch *patch = new NURBSPatch(kv, dim); delete kv.Last(); for (int i = 0; i < size; i++) { for (int d = 0; d < dim; d++) { patch->data[i*dim+d] = p1.data[i*dim+d]; patch->data[(i+size)*dim+d] = p2.data[i*dim+d]; } } return patch; } NURBSPatch *Revolve3D(NURBSPatch &patch, double n[], double ang, int times) { if (patch.Dim != 4) { mfem_error("Revolve3D(NURBSPatch &, double [], double)"); } int size = 1, ns; Array nkv(patch.kv.Size() + 1); for (int i = 0; i < patch.kv.Size(); i++) { nkv[i] = patch.kv[i]; size *= nkv[i]->GetNCP(); } ns = 2*times + 1; KnotVector &lkv = *(new KnotVector(2, ns)); nkv.Last() = &lkv; lkv[0] = lkv[1] = lkv[2] = 0.0; for (int i = 1; i < times; i++) { lkv[2*i+1] = lkv[2*i+2] = i; } lkv[ns] = lkv[ns+1] = lkv[ns+2] = times; lkv.GetElements(); NURBSPatch *newpatch = new NURBSPatch(nkv, 4); delete nkv.Last(); DenseMatrix T(3), T2(3); Vector u(NULL, 3), v(NULL, 3); NURBSPatch::Get3DRotationMatrix(n, ang, 1., T); double c = cos(ang/2); NURBSPatch::Get3DRotationMatrix(n, ang/2, 1./c, T2); T2 *= c; double *op = patch.data, *np; for (int i = 0; i < size; i++) { np = newpatch->data + 4*i; for (int j = 0; j < 4; j++) { np[j] = op[j]; } for (int j = 0; j < times; j++) { u.SetData(np); v.SetData(np += 4*size); T2.Mult(u, v); v[3] = c*u[3]; v.SetData(np += 4*size); T.Mult(u, v); v[3] = u[3]; } op += 4; } return newpatch; } NURBSExtension::NURBSExtension(const NURBSExtension &orig) : mOrder(orig.mOrder), mOrders(orig.mOrders), NumOfKnotVectors(orig.NumOfKnotVectors), NumOfVertices(orig.NumOfVertices), NumOfElements(orig.NumOfElements), NumOfBdrElements(orig.NumOfBdrElements), NumOfDofs(orig.NumOfDofs), NumOfActiveVertices(orig.NumOfActiveVertices), NumOfActiveElems(orig.NumOfActiveElems), NumOfActiveBdrElems(orig.NumOfActiveBdrElems), NumOfActiveDofs(orig.NumOfActiveDofs), activeVert(orig.activeVert), activeElem(orig.activeElem), activeBdrElem(orig.activeBdrElem), activeDof(orig.activeDof), patchTopo(new Mesh(*orig.patchTopo)), own_topo(true), edge_to_knot(orig.edge_to_knot), knotVectors(orig.knotVectors.Size()), // knotVectors are copied in the body knotVectorsCompr(orig.knotVectorsCompr.Size()), weights(orig.weights), d_to_d(orig.d_to_d), master(orig.master), slave(orig.slave), v_meshOffsets(orig.v_meshOffsets), e_meshOffsets(orig.e_meshOffsets), f_meshOffsets(orig.f_meshOffsets), p_meshOffsets(orig.p_meshOffsets), v_spaceOffsets(orig.v_spaceOffsets), e_spaceOffsets(orig.e_spaceOffsets), f_spaceOffsets(orig.f_spaceOffsets), p_spaceOffsets(orig.p_spaceOffsets), el_dof(orig.el_dof ? new Table(*orig.el_dof) : NULL), bel_dof(orig.bel_dof ? new Table(*orig.bel_dof) : NULL), el_to_patch(orig.el_to_patch), bel_to_patch(orig.bel_to_patch), el_to_IJK(orig.el_to_IJK), bel_to_IJK(orig.bel_to_IJK), patches(orig.patches.Size()) // patches are copied in the body { // Copy the knot vectors: for (int i = 0; i < knotVectors.Size(); i++) { knotVectors[i] = new KnotVector(*orig.knotVectors[i]); } CreateComprehensiveKV(); // Copy the patches: for (int p = 0; p < patches.Size(); p++) { patches[p] = new NURBSPatch(*orig.patches[p]); } } NURBSExtension::NURBSExtension(std::istream &input) { // Read topology patchTopo = new Mesh; patchTopo->LoadPatchTopo(input, edge_to_knot); own_topo = 1; CheckPatches(); // CheckBdrPatches(); skip_comment_lines(input, '#'); // Read knotvectors or patches string ident; input >> ws >> ident; // 'knotvectors' or 'patches' if (ident == "knotvectors") { input >> NumOfKnotVectors; knotVectors.SetSize(NumOfKnotVectors); for (int i = 0; i < NumOfKnotVectors; i++) { knotVectors[i] = new KnotVector(input); } } else if (ident == "patches") { patches.SetSize(GetNP()); for (int p = 0; p < patches.Size(); p++) { skip_comment_lines(input, '#'); patches[p] = new NURBSPatch(input); } NumOfKnotVectors = 0; for (int i = 0; i < patchTopo->GetNEdges(); i++) if (NumOfKnotVectors < KnotInd(i)) { NumOfKnotVectors = KnotInd(i); } NumOfKnotVectors++; knotVectors.SetSize(NumOfKnotVectors); knotVectors = NULL; Array edges, oedge; for (int p = 0; p < patches.Size(); p++) { if (Dimension() == 1) { if (knotVectors[KnotInd(p)] == NULL) { knotVectors[KnotInd(p)] = new KnotVector(*patches[p]->GetKV(0)); } } if (Dimension() == 2) { patchTopo->GetElementEdges(p, edges, oedge); if (knotVectors[KnotInd(edges[0])] == NULL) { knotVectors[KnotInd(edges[0])] = new KnotVector(*patches[p]->GetKV(0)); } if (knotVectors[KnotInd(edges[1])] == NULL) { knotVectors[KnotInd(edges[1])] = new KnotVector(*patches[p]->GetKV(1)); } } else if (Dimension() == 3) { patchTopo->GetElementEdges(p, edges, oedge); if (knotVectors[KnotInd(edges[0])] == NULL) { knotVectors[KnotInd(edges[0])] = new KnotVector(*patches[p]->GetKV(0)); } if (knotVectors[KnotInd(edges[3])] == NULL) { knotVectors[KnotInd(edges[3])] = new KnotVector(*patches[p]->GetKV(1)); } if (knotVectors[KnotInd(edges[8])] == NULL) { knotVectors[KnotInd(edges[8])] = new KnotVector(*patches[p]->GetKV(2)); } } } } else { MFEM_ABORT("invalid section: " << ident); } CreateComprehensiveKV(); SetOrdersFromKnotVectors(); GenerateOffsets(); CountElements(); CountBdrElements(); // NumOfVertices, NumOfElements, NumOfBdrElements, NumOfDofs skip_comment_lines(input, '#'); // Check for a list of mesh elements if (patches.Size() == 0) { input >> ws >> ident; } if (patches.Size() == 0 && ident == "mesh_elements") { input >> NumOfActiveElems; activeElem.SetSize(GetGNE()); activeElem = false; int glob_elem; for (int i = 0; i < NumOfActiveElems; i++) { input >> glob_elem; activeElem[glob_elem] = true; } skip_comment_lines(input, '#'); input >> ws >> ident; } else { NumOfActiveElems = NumOfElements; activeElem.SetSize(NumOfElements); activeElem = true; } GenerateActiveVertices(); InitDofMap(); GenerateElementDofTable(); GenerateActiveBdrElems(); GenerateBdrElementDofTable(); // periodic if (ident == "periodic") { master.Load(input); slave.Load(input); skip_comment_lines(input, '#'); input >> ws >> ident; } if (patches.Size() == 0) { // weights if (ident == "weights") { weights.Load(input, GetNDof()); } else // e.g. ident = "unitweights" or "autoweights" { weights.SetSize(GetNDof()); weights = 1.0; } } // periodic ConnectBoundaries(); } NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder) { patchTopo = parent->patchTopo; own_topo = 0; parent->edge_to_knot.Copy(edge_to_knot); NumOfKnotVectors = parent->GetNKV(); knotVectors.SetSize(NumOfKnotVectors); knotVectorsCompr.SetSize(parent->GetNP()*parent->Dimension()); const Array &pOrders = parent->GetOrders(); for (int i = 0; i < NumOfKnotVectors; i++) { if (newOrder > pOrders[i]) { knotVectors[i] = parent->GetKnotVector(i)->DegreeElevate(newOrder - pOrders[i]); } else { knotVectors[i] = new KnotVector(*parent->GetKnotVector(i)); } } CreateComprehensiveKV(); // copy some data from parent NumOfElements = parent->NumOfElements; NumOfBdrElements = parent->NumOfBdrElements; SetOrdersFromKnotVectors(); GenerateOffsets(); // dof offsets will be different from parent NumOfActiveVertices = parent->NumOfActiveVertices; NumOfActiveElems = parent->NumOfActiveElems; NumOfActiveBdrElems = parent->NumOfActiveBdrElems; parent->activeVert.Copy(activeVert); InitDofMap(); parent->activeElem.Copy(activeElem); parent->activeBdrElem.Copy(activeBdrElem); GenerateElementDofTable(); GenerateBdrElementDofTable(); weights.SetSize(GetNDof()); weights = 1.0; // periodic parent->master.Copy(master); parent->slave.Copy(slave); ConnectBoundaries(); } NURBSExtension::NURBSExtension(NURBSExtension *parent, const Array &newOrders) { newOrders.Copy(mOrders); SetOrderFromOrders(); patchTopo = parent->patchTopo; own_topo = 0; parent->edge_to_knot.Copy(edge_to_knot); NumOfKnotVectors = parent->GetNKV(); MFEM_VERIFY(mOrders.Size() == NumOfKnotVectors, "invalid newOrders array"); knotVectors.SetSize(NumOfKnotVectors); const Array &pOrders = parent->GetOrders(); for (int i = 0; i < NumOfKnotVectors; i++) { if (mOrders[i] > pOrders[i]) { knotVectors[i] = parent->GetKnotVector(i)->DegreeElevate(mOrders[i] - pOrders[i]); } else { knotVectors[i] = new KnotVector(*parent->GetKnotVector(i)); } } CreateComprehensiveKV(); // copy some data from parent NumOfElements = parent->NumOfElements; NumOfBdrElements = parent->NumOfBdrElements; GenerateOffsets(); // dof offsets will be different from parent NumOfActiveVertices = parent->NumOfActiveVertices; NumOfActiveElems = parent->NumOfActiveElems; NumOfActiveBdrElems = parent->NumOfActiveBdrElems; parent->activeVert.Copy(activeVert); InitDofMap(); parent->activeElem.Copy(activeElem); parent->activeBdrElem.Copy(activeBdrElem); GenerateElementDofTable(); GenerateBdrElementDofTable(); weights.SetSize(GetNDof()); weights = 1.0; parent->master.Copy(master); parent->slave.Copy(slave); ConnectBoundaries(); } NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces) { NURBSExtension *parent = mesh_array[0]->NURBSext; if (!parent->own_topo) { mfem_error("NURBSExtension::NURBSExtension :\n" " parent does not own the patch topology!"); } patchTopo = parent->patchTopo; own_topo = 1; parent->own_topo = 0; parent->edge_to_knot.Copy(edge_to_knot); parent->GetOrders().Copy(mOrders); mOrder = parent->GetOrder(); NumOfKnotVectors = parent->GetNKV(); knotVectors.SetSize(NumOfKnotVectors); for (int i = 0; i < NumOfKnotVectors; i++) { knotVectors[i] = new KnotVector(*parent->GetKnotVector(i)); } CreateComprehensiveKV(); GenerateOffsets(); CountElements(); CountBdrElements(); // assuming the meshes define a partitioning of all the elements NumOfActiveElems = NumOfElements; activeElem.SetSize(NumOfElements); activeElem = true; GenerateActiveVertices(); InitDofMap(); GenerateElementDofTable(); GenerateActiveBdrElems(); GenerateBdrElementDofTable(); weights.SetSize(GetNDof()); MergeWeights(mesh_array, num_pieces); } NURBSExtension::~NURBSExtension() { if (patches.Size() == 0) { delete bel_dof; delete el_dof; } for (int i = 0; i < knotVectors.Size(); i++) { delete knotVectors[i]; } for (int i = 0; i < knotVectorsCompr.Size(); i++) { delete knotVectorsCompr[i]; } for (int i = 0; i < patches.Size(); i++) { delete patches[i]; } if (own_topo) { delete patchTopo; } } void NURBSExtension::Print(std::ostream &os) const { patchTopo->PrintTopo(os, edge_to_knot); if (patches.Size() == 0) { os << "\nknotvectors\n" << NumOfKnotVectors << '\n'; for (int i = 0; i < NumOfKnotVectors; i++) { knotVectors[i]->Print(os); } if (NumOfActiveElems < NumOfElements) { os << "\nmesh_elements\n" << NumOfActiveElems << '\n'; for (int i = 0; i < NumOfElements; i++) if (activeElem[i]) { os << i << '\n'; } } os << "\nweights\n"; weights.Print(os, 1); } else { os << "\npatches\n"; for (int p = 0; p < patches.Size(); p++) { os << "\n# patch " << p << "\n\n"; patches[p]->Print(os); } } } void NURBSExtension::PrintCharacteristics(std::ostream &os) const { os << "NURBS Mesh entity sizes:\n" "Dimension = " << Dimension() << "\n" "Unique Orders = "; Array unique_orders(mOrders); unique_orders.Sort(); unique_orders.Unique(); unique_orders.Print(os, unique_orders.Size()); os << "NumOfKnotVectors = " << GetNKV() << "\n" "NumOfPatches = " << GetNP() << "\n" "NumOfBdrPatches = " << GetNBP() << "\n" "NumOfVertices = " << GetGNV() << "\n" "NumOfElements = " << GetGNE() << "\n" "NumOfBdrElements = " << GetGNBE() << "\n" "NumOfDofs = " << GetNTotalDof() << "\n" "NumOfActiveVertices = " << GetNV() << "\n" "NumOfActiveElems = " << GetNE() << "\n" "NumOfActiveBdrElems = " << GetNBE() << "\n" "NumOfActiveDofs = " << GetNDof() << '\n'; for (int i = 0; i < NumOfKnotVectors; i++) { os << ' ' << i + 1 << ") "; knotVectors[i]->Print(os); } os << endl; } void NURBSExtension::PrintFunctions(const char *basename, int samples) const { std::ofstream os; for (int i = 0; i < NumOfKnotVectors; i++) { std::ostringstream filename; filename << basename<<"_"<PrintFunctions(os,samples); os.close(); } } void NURBSExtension::InitDofMap() { master.SetSize(0); slave.SetSize(0); d_to_d.SetSize(0); } void NURBSExtension::ConnectBoundaries(Array &bnds0, Array &bnds1) { bnds0.Copy(master); bnds1.Copy(slave); ConnectBoundaries(); } void NURBSExtension::ConnectBoundaries() { if (master.Size() != slave.Size()) { mfem_error("NURBSExtension::ConnectBoundaries() boundary lists not of equal size"); } if (master.Size() == 0 ) { return; } // Initialize d_to_d d_to_d.SetSize(NumOfDofs); for (int i = 0; i < NumOfDofs; i++) { d_to_d[i] = i; } // Connect for (int i = 0; i < master.Size(); i++) { int bnd0 = -1, bnd1 = -1; for (int b = 0; b < GetNBP(); b++) { if (master[i] == patchTopo->GetBdrAttribute(b)) { bnd0 = b; } if (slave[i]== patchTopo->GetBdrAttribute(b)) { bnd1 = b; } } MFEM_VERIFY(bnd0 != -1,"Bdr 0 not found"); MFEM_VERIFY(bnd1 != -1,"Bdr 1 not found"); if (Dimension() == 1) { ConnectBoundaries1D(bnd0, bnd1); } else if (Dimension() == 2) { ConnectBoundaries2D(bnd0, bnd1); } else { ConnectBoundaries3D(bnd0, bnd1); } } // Clean d_to_d Array tmp(d_to_d.Size()+1); tmp = 0; for (int i = 0; i < d_to_d.Size(); i++) { tmp[d_to_d[i]] = 1; } int cnt = 0; for (int i = 0; i < tmp.Size(); i++) { if (tmp[i] == 1) { tmp[i] = cnt++; } } NumOfDofs = cnt; for (int i = 0; i < d_to_d.Size(); i++) { d_to_d[i] = tmp[d_to_d[i]]; } // Finalize if (el_dof) { delete el_dof; } if (bel_dof) { delete bel_dof; } GenerateElementDofTable(); GenerateBdrElementDofTable(); } void NURBSExtension::ConnectBoundaries1D(int bnd0, int bnd1) { NURBSPatchMap p2g0(this); NURBSPatchMap p2g1(this); int okv0[1],okv1[1]; const KnotVector *kv0[1],*kv1[1]; p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0); p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1); d_to_d[p2g0(0)] = d_to_d[p2g1(0)]; } void NURBSExtension::ConnectBoundaries2D(int bnd0, int bnd1) { NURBSPatchMap p2g0(this); NURBSPatchMap p2g1(this); int okv0[1],okv1[1]; const KnotVector *kv0[1],*kv1[1]; p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0); p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1); int nx = p2g0.nx(); int nks0 = kv0[0]->GetNKS(); #ifdef MFEM_DEBUG bool compatible = true; if (p2g0.nx() != p2g1.nx()) { compatible = false; } if (kv0[0]->GetNKS() != kv1[0]->GetNKS()) { compatible = false; } if (kv0[0]->GetOrder() != kv1[0]->GetOrder()) { compatible = false; } if (!compatible) { mfem::out<GetNKS()<<" "<GetNKS()<GetOrder()<<" "<GetOrder()<isElement(i)) { if (!kv1[0]->isElement(i)) { mfem_error("isElement does not match"); } for (int ii = 0; ii <= kv0[0]->GetOrder(); ii++) { int ii0 = (okv0[0] >= 0) ? (i+ii) : (nx-i-ii); int ii1 = (okv1[0] >= 0) ? (i+ii) : (nx-i-ii); d_to_d[p2g0(ii0)] = d_to_d[p2g1(ii1)]; } } } } void NURBSExtension::ConnectBoundaries3D(int bnd0, int bnd1) { NURBSPatchMap p2g0(this); NURBSPatchMap p2g1(this); int okv0[2],okv1[2]; const KnotVector *kv0[2],*kv1[2]; p2g0.SetBdrPatchDofMap(bnd0, kv0, okv0); p2g1.SetBdrPatchDofMap(bnd1, kv1, okv1); int nx = p2g0.nx(); int ny = p2g0.ny(); int nks0 = kv0[0]->GetNKS(); int nks1 = kv0[1]->GetNKS(); #ifdef MFEM_DEBUG bool compatible = true; if (p2g0.nx() != p2g1.nx()) { compatible = false; } if (p2g0.ny() != p2g1.ny()) { compatible = false; } if (kv0[0]->GetNKS() != kv1[0]->GetNKS()) { compatible = false; } if (kv0[1]->GetNKS() != kv1[1]->GetNKS()) { compatible = false; } if (kv0[0]->GetOrder() != kv1[0]->GetOrder()) { compatible = false; } if (kv0[1]->GetOrder() != kv1[1]->GetOrder()) { compatible = false; } if (!compatible) { mfem::out<GetNKS()<<" "<GetNKS()<GetNKS()<<" "<GetNKS()<GetOrder()<<" "<GetOrder()<GetOrder()<<" "<GetOrder()<isElement(j)) { if (!kv1[1]->isElement(j)) { mfem_error("isElement does not match #1"); } for (int i = 0; i < nks0; i++) { if (kv0[0]->isElement(i)) { if (!kv1[0]->isElement(i)) { mfem_error("isElement does not match #0"); } for (int jj = 0; jj <= kv0[1]->GetOrder(); jj++) { int jj0 = (okv0[1] >= 0) ? (j+jj) : (ny-j-jj); int jj1 = (okv1[1] >= 0) ? (j+jj) : (ny-j-jj); for (int ii = 0; ii <= kv0[0]->GetOrder(); ii++) { int ii0 = (okv0[0] >= 0) ? (i+ii) : (nx-i-ii); int ii1 = (okv1[0] >= 0) ? (i+ii) : (nx-i-ii); d_to_d[p2g0(ii0,jj0)] = d_to_d[p2g1(ii1,jj1)]; } } } } } } } void NURBSExtension::GenerateActiveVertices() { int vert[8], nv, g_el, nx, ny, nz, dim = Dimension(); NURBSPatchMap p2g(this); const KnotVector *kv[3]; g_el = 0; activeVert.SetSize(GetGNV()); activeVert = -1; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchVertexMap(p, kv); nx = p2g.nx(); ny = (dim >= 2) ? p2g.ny() : 1; nz = (dim == 3) ? p2g.nz() : 1; for (int k = 0; k < nz; k++) { for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { if (activeElem[g_el]) { if (dim == 1) { vert[0] = p2g(i ); vert[1] = p2g(i+1); nv = 2; } else if (dim == 2) { vert[0] = p2g(i, j ); vert[1] = p2g(i+1,j ); vert[2] = p2g(i+1,j+1); vert[3] = p2g(i, j+1); nv = 4; } else { vert[0] = p2g(i, j, k); vert[1] = p2g(i+1,j, k); vert[2] = p2g(i+1,j+1,k); vert[3] = p2g(i, j+1,k); vert[4] = p2g(i, j, k+1); vert[5] = p2g(i+1,j, k+1); vert[6] = p2g(i+1,j+1,k+1); vert[7] = p2g(i, j+1,k+1); nv = 8; } for (int v = 0; v < nv; v++) { activeVert[vert[v]] = 1; } } g_el++; } } } } NumOfActiveVertices = 0; for (int i = 0; i < GetGNV(); i++) if (activeVert[i] == 1) { activeVert[i] = NumOfActiveVertices++; } } void NURBSExtension::GenerateActiveBdrElems() { int dim = Dimension(); Array kv(dim); activeBdrElem.SetSize(GetGNBE()); if (GetGNE() == GetNE()) { activeBdrElem = true; NumOfActiveBdrElems = GetGNBE(); return; } activeBdrElem = false; NumOfActiveBdrElems = 0; // the mesh will generate the actual boundary including boundary // elements that are not on boundary patches. we use this for // visualization of processor boundaries // TODO: generate actual boundary? } void NURBSExtension::MergeWeights(Mesh *mesh_array[], int num_pieces) { Array lelem_elem; for (int i = 0; i < num_pieces; i++) { NURBSExtension *lext = mesh_array[i]->NURBSext; lext->GetElementLocalToGlobal(lelem_elem); for (int lel = 0; lel < lext->GetNE(); lel++) { int gel = lelem_elem[lel]; int nd = el_dof->RowSize(gel); int *gdofs = el_dof->GetRow(gel); int *ldofs = lext->el_dof->GetRow(lel); for (int j = 0; j < nd; j++) { weights(gdofs[j]) = lext->weights(ldofs[j]); } } } } void NURBSExtension::MergeGridFunctions( GridFunction *gf_array[], int num_pieces, GridFunction &merged) { FiniteElementSpace *gfes = merged.FESpace(); Array lelem_elem, dofs; Vector lvec; for (int i = 0; i < num_pieces; i++) { FiniteElementSpace *lfes = gf_array[i]->FESpace(); NURBSExtension *lext = lfes->GetMesh()->NURBSext; lext->GetElementLocalToGlobal(lelem_elem); for (int lel = 0; lel < lext->GetNE(); lel++) { lfes->GetElementVDofs(lel, dofs); gf_array[i]->GetSubVector(dofs, lvec); gfes->GetElementVDofs(lelem_elem[lel], dofs); merged.SetSubVector(dofs, lvec); } } } void NURBSExtension::CheckPatches() { if (Dimension() == 1 ) { return; } Array edges; Array oedge; for (int p = 0; p < GetNP(); p++) { patchTopo->GetElementEdges(p, edges, oedge); for (int i = 0; i < edges.Size(); i++) { edges[i] = edge_to_knot[edges[i]]; if (oedge[i] < 0) { edges[i] = -1 - edges[i]; } } if ((Dimension() == 2 && (edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) || (Dimension() == 3 && (edges[0] != edges[2] || edges[0] != edges[4] || edges[0] != edges[6] || edges[1] != edges[3] || edges[1] != edges[5] || edges[1] != edges[7] || edges[8] != edges[9] || edges[8] != edges[10] || edges[8] != edges[11]))) { mfem::err << "NURBSExtension::CheckPatch (patch = " << p << ")\n Inconsistent edge-to-knot mapping!\n"; mfem_error(); } } } void NURBSExtension::CheckBdrPatches() { Array edges; Array oedge; for (int p = 0; p < GetNBP(); p++) { patchTopo->GetBdrElementEdges(p, edges, oedge); for (int i = 0; i < edges.Size(); i++) { edges[i] = edge_to_knot[edges[i]]; if (oedge[i] < 0) { edges[i] = -1 - edges[i]; } } if ((Dimension() == 2 && (edges[0] < 0)) || (Dimension() == 3 && (edges[0] < 0 || edges[1] < 0))) { mfem::err << "NURBSExtension::CheckBdrPatch (boundary patch = " << p << ") : Bad orientation!\n"; mfem_error(); } } } void NURBSExtension::CheckKVDirection(int p, Array &kvdir) { // patchTopo->GetElementEdges is not yet implemented for 1D MFEM_VERIFY(Dimension()>1, "1D not yet implemented."); kvdir.SetSize(Dimension()); kvdir = 0; Array patchvert, edges, orient, edgevert; patchTopo->GetElementVertices(p, patchvert); patchTopo->GetElementEdges(p, edges, orient); // Compare the vertices of the patches with the vertices of the knotvectors of knot2dge // Based on the match the orientation will be a 1 or a -1 // -1: direction is flipped // 1: direction is not flipped for (int i = 0; i < edges.Size(); i++) { // First side patchTopo->GetEdgeVertices(edges[i], edgevert); if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[1]) { kvdir[0] = 1; } if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[0]) { kvdir[0] = -1; } // Second side if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[2]) { kvdir[1] = 1; } if (edgevert[0] == patchvert[2] && edgevert[1] == patchvert[1]) { kvdir[1] = -1; } } if (Dimension() == 3) { // Third side for (int i = 0; i < edges.Size(); i++) { patchTopo->GetEdgeVertices(edges[i], edgevert); if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[4]) { kvdir[2] = 1; } if (edgevert[0] == patchvert[4] && edgevert[1] == patchvert[0]) { kvdir[2] = -1; } } } MFEM_VERIFY(kvdir.Find(0) == -1, "Could not find direction of knotvector."); } void NURBSExtension::CreateComprehensiveKV() { Array edges, orient, kvdir; Array e(Dimension()); // 1D: comprehensive and unique KV are the same if (Dimension() == 1) { knotVectorsCompr.SetSize(GetNKV()); for (int i = 0; i < GetNKV(); i++) { knotVectorsCompr[i] = new KnotVector(*(KnotVec(i))); } return; } else if (Dimension() == 2) { knotVectorsCompr.SetSize(GetNP()*Dimension()); e[0] = 0; e[1] = 1; } else if (Dimension() == 3) { knotVectorsCompr.SetSize(GetNP()*Dimension()); e[0] = 0; e[1] = 3; e[2] = 8; } for (int p = 0; p < GetNP(); p++) { CheckKVDirection(p, kvdir); patchTopo->GetElementEdges(p, edges, orient); for (int d = 0; d < Dimension(); d++) { // Indices in unique and comprehensive sets of the KnotVector int iun = edges[e[d]]; int icomp = Dimension()*p+d; knotVectorsCompr[icomp] = new KnotVector(*(KnotVec(iun))); if (kvdir[d] == -1) {knotVectorsCompr[icomp]->Flip();} } } MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors"); } void NURBSExtension::UpdateUniqueKV() { Array e(Dimension()); // 1D: comprehensive and unique KV are the same if (Dimension() == 1) { for (int i = 0; i < GetNKV(); i++) { *(KnotVec(i)) = *(knotVectorsCompr[i]); } return; } else if (Dimension() == 2) { e[0] = 0; e[1] = 1; } else if (Dimension() == 3) { e[0] = 0; e[1] = 3; e[2] = 8; } for (int p = 0; p < GetNP(); p++) { Array edges, orient, kvdir; patchTopo->GetElementEdges(p, edges, orient); CheckKVDirection(p, kvdir); for ( int d = 0; d < Dimension(); d++) { bool flip = false; if (kvdir[d] == -1) {flip = true;} // Indices in unique and comprehensive sets of the KnotVector int iun = edges[e[d]]; int icomp = Dimension()*p+d; // Check if difference in order int o1 = KnotVec(iun)->GetOrder(); int o2 = knotVectorsCompr[icomp]->GetOrder(); int diffo = abs(o1 - o2); if (diffo) { // Update reduced set of knotvectors *(KnotVec(iun)) = *(knotVectorsCompr[icomp]); // Give correct direction to unique knotvector. if (flip) { KnotVec(iun)->Flip(); } } // Check if difference between knots Vector diffknot; if (flip) { knotVectorsCompr[icomp]->Flip(); } KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diffknot); if (flip) { knotVectorsCompr[icomp]->Flip(); } if (diffknot.Size() > 0) { // Update reduced set of knotvectors *(KnotVec(iun)) = *(knotVectorsCompr[icomp]); // Give correct direction to unique knotvector. if (flip) {KnotVec(iun)->Flip();} } } } MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors"); } bool NURBSExtension::ConsistentKVSets() { // patchTopo->GetElementEdges is not yet implemented for 1D MFEM_VERIFY(Dimension()>1, "1D not yet implemented."); Array edges, orient, kvdir; Vector diff; Arraye(Dimension()); e[0] = 0; if (Dimension() == 2) { e[1] = 1; } else if (Dimension() == 3) { e[1] = 3; e[2] = 8; } for (int p = 0; p < GetNP(); p++) { patchTopo->GetElementEdges(p, edges, orient); CheckKVDirection(p, kvdir); for (int d = 0; d < Dimension(); d++) { bool flip = false; if (kvdir[d] == -1) {flip = true;} // Indices in unique and comprehensive sets of the KnotVector int iun = edges[e[d]]; int icomp = Dimension()*p+d; // Check if KnotVectors are of equal order int o1 = KnotVec(iun)->GetOrder(); int o2 = knotVectorsCompr[icomp]->GetOrder(); int diffo = abs(o1 - o2); if (diffo) { mfem::out << "\norder of knotVectorsCompr " << d << " of patch " << p; mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n"; return false; } // Check if Knotvectors have the same knots if (flip) {knotVectorsCompr[icomp]->Flip();} KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diff); if (flip) {knotVectorsCompr[icomp]->Flip();} if (diff.Size() > 0) { mfem::out << "\nknotVectorsCompr " << d << " of patch " << p; mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n"; return false; } } } return true; } void NURBSExtension::GetPatchKnotVectors(int p, Array &kv) { Array edges, orient; kv.SetSize(Dimension()); if (Dimension() == 1) { kv[0] = knotVectorsCompr[Dimension()*p]; } else if (Dimension() == 2) { kv[0] = knotVectorsCompr[Dimension()*p]; kv[1] = knotVectorsCompr[Dimension()*p + 1]; } else { kv[0] = knotVectorsCompr[Dimension()*p]; kv[1] = knotVectorsCompr[Dimension()*p + 1]; kv[2] = knotVectorsCompr[Dimension()*p + 2]; } } void NURBSExtension::GetPatchKnotVectors(int p, Array &kv) const { Array edges, orient; kv.SetSize(Dimension()); if (Dimension() == 1) { kv[0] = knotVectorsCompr[Dimension()*p]; } else if (Dimension() == 2) { kv[0] = knotVectorsCompr[Dimension()*p]; kv[1] = knotVectorsCompr[Dimension()*p + 1]; } else { kv[0] = knotVectorsCompr[Dimension()*p]; kv[1] = knotVectorsCompr[Dimension()*p + 1]; kv[2] = knotVectorsCompr[Dimension()*p + 2]; } } void NURBSExtension::GetBdrPatchKnotVectors(int p, Array &kv) { Array edges; Array orient; kv.SetSize(Dimension() - 1); if (Dimension() == 2) { patchTopo->GetBdrElementEdges(p, edges, orient); kv[0] = KnotVec(edges[0]); } else if (Dimension() == 3) { patchTopo->GetBdrElementEdges(p, edges, orient); kv[0] = KnotVec(edges[0]); kv[1] = KnotVec(edges[1]); } } void NURBSExtension::GetBdrPatchKnotVectors( int p, Array &kv) const { Array edges; Array orient; kv.SetSize(Dimension() - 1); if (Dimension() == 2) { patchTopo->GetBdrElementEdges(p, edges, orient); kv[0] = KnotVec(edges[0]); } else if (Dimension() == 3) { patchTopo->GetBdrElementEdges(p, edges, orient); kv[0] = KnotVec(edges[0]); kv[1] = KnotVec(edges[1]); } } void NURBSExtension::SetOrderFromOrders() { MFEM_VERIFY(mOrders.Size() > 0, ""); mOrder = mOrders[0]; for (int i = 1; i < mOrders.Size(); i++) { if (mOrders[i] != mOrder) { mOrder = NURBSFECollection::VariableOrder; return; } } } void NURBSExtension::SetOrdersFromKnotVectors() { mOrders.SetSize(NumOfKnotVectors); for (int i = 0; i < NumOfKnotVectors; i++) { mOrders[i] = knotVectors[i]->GetOrder(); } SetOrderFromOrders(); } void NURBSExtension::GenerateOffsets() { int nv = patchTopo->GetNV(); int ne = patchTopo->GetNEdges(); int nf = patchTopo->GetNFaces(); int np = patchTopo->GetNE(); int meshCounter, spaceCounter, dim = Dimension(); Array edges; Array orient; v_meshOffsets.SetSize(nv); e_meshOffsets.SetSize(ne); f_meshOffsets.SetSize(nf); p_meshOffsets.SetSize(np); v_spaceOffsets.SetSize(nv); e_spaceOffsets.SetSize(ne); f_spaceOffsets.SetSize(nf); p_spaceOffsets.SetSize(np); // Get vertex offsets for (meshCounter = 0; meshCounter < nv; meshCounter++) { v_meshOffsets[meshCounter] = meshCounter; v_spaceOffsets[meshCounter] = meshCounter; } spaceCounter = meshCounter; // Get edge offsets for (int e = 0; e < ne; e++) { e_meshOffsets[e] = meshCounter; e_spaceOffsets[e] = spaceCounter; meshCounter += KnotVec(e)->GetNE() - 1; spaceCounter += KnotVec(e)->GetNCP() - 2; } // Get face offsets for (int f = 0; f < nf; f++) { f_meshOffsets[f] = meshCounter; f_spaceOffsets[f] = spaceCounter; patchTopo->GetFaceEdges(f, edges, orient); meshCounter += (KnotVec(edges[0])->GetNE() - 1) * (KnotVec(edges[1])->GetNE() - 1); spaceCounter += (KnotVec(edges[0])->GetNCP() - 2) * (KnotVec(edges[1])->GetNCP() - 2); } // Get patch offsets for (int p = 0; p < np; p++) { p_meshOffsets[p] = meshCounter; p_spaceOffsets[p] = spaceCounter; if (dim == 1) { meshCounter += KnotVec(0)->GetNE() - 1; spaceCounter += KnotVec(0)->GetNCP() - 2; } else if (dim == 2) { patchTopo->GetElementEdges(p, edges, orient); meshCounter += (KnotVec(edges[0])->GetNE() - 1) * (KnotVec(edges[1])->GetNE() - 1); spaceCounter += (KnotVec(edges[0])->GetNCP() - 2) * (KnotVec(edges[1])->GetNCP() - 2); } else { patchTopo->GetElementEdges(p, edges, orient); meshCounter += (KnotVec(edges[0])->GetNE() - 1) * (KnotVec(edges[3])->GetNE() - 1) * (KnotVec(edges[8])->GetNE() - 1); spaceCounter += (KnotVec(edges[0])->GetNCP() - 2) * (KnotVec(edges[3])->GetNCP() - 2) * (KnotVec(edges[8])->GetNCP() - 2); } } NumOfVertices = meshCounter; NumOfDofs = spaceCounter; } void NURBSExtension::CountElements() { int dim = Dimension(); Array kv(dim); NumOfElements = 0; for (int p = 0; p < GetNP(); p++) { GetPatchKnotVectors(p, kv); int ne = kv[0]->GetNE(); for (int d = 1; d < dim; d++) { ne *= kv[d]->GetNE(); } NumOfElements += ne; } } void NURBSExtension::CountBdrElements() { int dim = Dimension() - 1; Array kv(dim); NumOfBdrElements = 0; for (int p = 0; p < GetNBP(); p++) { GetBdrPatchKnotVectors(p, kv); int ne = 1; for (int d = 0; d < dim; d++) { ne *= kv[d]->GetNE(); } NumOfBdrElements += ne; } } void NURBSExtension::GetElementTopo(Array &elements) const { elements.SetSize(GetNE()); if (Dimension() == 1) { Get1DElementTopo(elements); } else if (Dimension() == 2) { Get2DElementTopo(elements); } else { Get3DElementTopo(elements); } } void NURBSExtension::Get1DElementTopo(Array &elements) const { int el = 0; int eg = 0; int ind[2]; NURBSPatchMap p2g(this); const KnotVector *kv[1]; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchVertexMap(p, kv); int nx = p2g.nx(); int patch_attr = patchTopo->GetAttribute(p); for (int i = 0; i < nx; i++) { if (activeElem[eg]) { ind[0] = activeVert[p2g(i)]; ind[1] = activeVert[p2g(i+1)]; elements[el] = new Segment(ind, patch_attr); el++; } eg++; } } } void NURBSExtension::Get2DElementTopo(Array &elements) const { int el = 0; int eg = 0; int ind[4]; NURBSPatchMap p2g(this); const KnotVector *kv[2]; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchVertexMap(p, kv); int nx = p2g.nx(); int ny = p2g.ny(); int patch_attr = patchTopo->GetAttribute(p); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { if (activeElem[eg]) { ind[0] = activeVert[p2g(i, j )]; ind[1] = activeVert[p2g(i+1,j )]; ind[2] = activeVert[p2g(i+1,j+1)]; ind[3] = activeVert[p2g(i, j+1)]; elements[el] = new Quadrilateral(ind, patch_attr); el++; } eg++; } } } } void NURBSExtension::Get3DElementTopo(Array &elements) const { int el = 0; int eg = 0; int ind[8]; NURBSPatchMap p2g(this); const KnotVector *kv[3]; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchVertexMap(p, kv); int nx = p2g.nx(); int ny = p2g.ny(); int nz = p2g.nz(); int patch_attr = patchTopo->GetAttribute(p); for (int k = 0; k < nz; k++) { for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { if (activeElem[eg]) { ind[0] = activeVert[p2g(i, j, k)]; ind[1] = activeVert[p2g(i+1,j, k)]; ind[2] = activeVert[p2g(i+1,j+1,k)]; ind[3] = activeVert[p2g(i, j+1,k)]; ind[4] = activeVert[p2g(i, j, k+1)]; ind[5] = activeVert[p2g(i+1,j, k+1)]; ind[6] = activeVert[p2g(i+1,j+1,k+1)]; ind[7] = activeVert[p2g(i, j+1,k+1)]; elements[el] = new Hexahedron(ind, patch_attr); el++; } eg++; } } } } } void NURBSExtension::GetBdrElementTopo(Array &boundary) const { boundary.SetSize(GetNBE()); if (Dimension() == 1) { Get1DBdrElementTopo(boundary); } else if (Dimension() == 2) { Get2DBdrElementTopo(boundary); } else { Get3DBdrElementTopo(boundary); } } void NURBSExtension::Get1DBdrElementTopo(Array &boundary) const { int g_be, l_be; int ind[2], okv[1]; NURBSPatchMap p2g(this); const KnotVector *kv[1]; g_be = l_be = 0; for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchVertexMap(b, kv, okv); int bdr_patch_attr = patchTopo->GetBdrAttribute(b); if (activeBdrElem[g_be]) { ind[0] = activeVert[p2g[0]]; boundary[l_be] = new Point(ind, bdr_patch_attr); l_be++; } g_be++; } } void NURBSExtension::Get2DBdrElementTopo(Array &boundary) const { int g_be, l_be; int ind[2], okv[1]; NURBSPatchMap p2g(this); const KnotVector *kv[1]; g_be = l_be = 0; for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchVertexMap(b, kv, okv); int nx = p2g.nx(); int bdr_patch_attr = patchTopo->GetBdrAttribute(b); for (int i = 0; i < nx; i++) { if (activeBdrElem[g_be]) { int i_ = (okv[0] >= 0) ? i : (nx - 1 - i); ind[0] = activeVert[p2g[i_ ]]; ind[1] = activeVert[p2g[i_+1]]; boundary[l_be] = new Segment(ind, bdr_patch_attr); l_be++; } g_be++; } } } void NURBSExtension::Get3DBdrElementTopo(Array &boundary) const { int g_be, l_be; int ind[4], okv[2]; NURBSPatchMap p2g(this); const KnotVector *kv[2]; g_be = l_be = 0; for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchVertexMap(b, kv, okv); int nx = p2g.nx(); int ny = p2g.ny(); int bdr_patch_attr = patchTopo->GetBdrAttribute(b); for (int j = 0; j < ny; j++) { int j_ = (okv[1] >= 0) ? j : (ny - 1 - j); for (int i = 0; i < nx; i++) { if (activeBdrElem[g_be]) { int i_ = (okv[0] >= 0) ? i : (nx - 1 - i); ind[0] = activeVert[p2g(i_, j_ )]; ind[1] = activeVert[p2g(i_+1,j_ )]; ind[2] = activeVert[p2g(i_+1,j_+1)]; ind[3] = activeVert[p2g(i_, j_+1)]; boundary[l_be] = new Quadrilateral(ind, bdr_patch_attr); l_be++; } g_be++; } } } } void NURBSExtension::GenerateElementDofTable() { activeDof.SetSize(GetNTotalDof()); activeDof = 0; if (Dimension() == 1) { Generate1DElementDofTable(); } else if (Dimension() == 2) { Generate2DElementDofTable(); } else { Generate3DElementDofTable(); } SetPatchToElements(); NumOfActiveDofs = 0; for (int d = 0; d < GetNTotalDof(); d++) if (activeDof[d]) { NumOfActiveDofs++; activeDof[d] = NumOfActiveDofs; } int *dof = el_dof->GetJ(); int ndof = el_dof->Size_of_connections(); for (int i = 0; i < ndof; i++) { dof[i] = activeDof[dof[i]] - 1; } } void NURBSExtension::Generate1DElementDofTable() { int el = 0; int eg = 0; const KnotVector *kv[2]; NURBSPatchMap p2g(this); Array el_dof_list; el_to_patch.SetSize(NumOfActiveElems); el_to_IJK.SetSize(NumOfActiveElems, 2); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { if (activeElem[eg]) { Connection conn(el,0); for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii)); activeDof[conn.to] = 1; el_dof_list.Append(conn); } el_to_patch[el] = p; el_to_IJK(el,0) = i; el++; } eg++; } } } // We must NOT sort el_dof_list in this case. el_dof = new Table(NumOfActiveElems, el_dof_list); } void NURBSExtension::Generate2DElementDofTable() { int el = 0; int eg = 0; const KnotVector *kv[2]; NURBSPatchMap p2g(this); Array el_dof_list; el_to_patch.SetSize(NumOfActiveElems); el_to_IJK.SetSize(NumOfActiveElems, 2); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); const int ord1 = kv[1]->GetOrder(); for (int j = 0; j < kv[1]->GetNKS(); j++) { if (kv[1]->isElement(j)) { for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { if (activeElem[eg]) { Connection conn(el,0); for (int jj = 0; jj <= ord1; jj++) { for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii,j+jj)); activeDof[conn.to] = 1; el_dof_list.Append(conn); } } el_to_patch[el] = p; el_to_IJK(el,0) = i; el_to_IJK(el,1) = j; el++; } eg++; } } } } } // We must NOT sort el_dof_list in this case. el_dof = new Table(NumOfActiveElems, el_dof_list); } void NURBSExtension::Generate3DElementDofTable() { int el = 0; int eg = 0; const KnotVector *kv[3]; NURBSPatchMap p2g(this); Array el_dof_list; el_to_patch.SetSize(NumOfActiveElems); el_to_IJK.SetSize(NumOfActiveElems, 3); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); const int ord1 = kv[1]->GetOrder(); const int ord2 = kv[2]->GetOrder(); for (int k = 0; k < kv[2]->GetNKS(); k++) { if (kv[2]->isElement(k)) { for (int j = 0; j < kv[1]->GetNKS(); j++) { if (kv[1]->isElement(j)) { for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { if (activeElem[eg]) { Connection conn(el,0); for (int kk = 0; kk <= ord2; kk++) { for (int jj = 0; jj <= ord1; jj++) { for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii, j+jj, k+kk)); activeDof[conn.to] = 1; el_dof_list.Append(conn); } } } el_to_patch[el] = p; el_to_IJK(el,0) = i; el_to_IJK(el,1) = j; el_to_IJK(el,2) = k; el++; } eg++; } } } } } } } // We must NOT sort el_dof_list in this case. el_dof = new Table(NumOfActiveElems, el_dof_list); } void NURBSExtension::GetPatchDofs(const int patch, Array &dofs) { const KnotVector *kv[3]; NURBSPatchMap p2g(this); p2g.SetPatchDofMap(patch, kv); if (Dimension() == 1) { const int nx = kv[0]->GetNCP(); dofs.SetSize(nx); for (int i=0; iGetNCP(); const int ny = kv[1]->GetNCP(); dofs.SetSize(nx * ny); for (int j=0; jGetNCP(); const int ny = kv[1]->GetNCP(); const int nz = kv[2]->GetNCP(); dofs.SetSize(nx * ny * nz); for (int k=0; kGetJ(); int ndof = bel_dof->Size_of_connections(); for (int i = 0; i < ndof; i++) { dof[i] = activeDof[dof[i]] - 1; } } void NURBSExtension::Generate1DBdrElementDofTable() { int gbe = 0; int lbe = 0, okv[1]; const KnotVector *kv[1]; NURBSPatchMap p2g(this); Array bel_dof_list; bel_to_patch.SetSize(NumOfActiveBdrElems); bel_to_IJK.SetSize(NumOfActiveBdrElems, 1); for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchDofMap(b, kv, okv); // Load dofs if (activeBdrElem[gbe]) { Connection conn(lbe,0); conn.to = DofMap(p2g[0]); bel_dof_list.Append(conn); bel_to_patch[lbe] = b; bel_to_IJK(lbe,0) = 0; lbe++; } gbe++; } // We must NOT sort bel_dof_list in this case. bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list); } void NURBSExtension::Generate2DBdrElementDofTable() { int gbe = 0; int lbe = 0, okv[1]; const KnotVector *kv[1]; NURBSPatchMap p2g(this); Array bel_dof_list; bel_to_patch.SetSize(NumOfActiveBdrElems); bel_to_IJK.SetSize(NumOfActiveBdrElems, 1); for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchDofMap(b, kv, okv); const int nx = p2g.nx(); // NCP-1 // Load dofs const int nks0 = kv[0]->GetNKS(); const int ord0 = kv[0]->GetOrder(); for (int i = 0; i < nks0; i++) { if (kv[0]->isElement(i)) { if (activeBdrElem[gbe]) { Connection conn(lbe,0); for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g[(okv[0] >= 0) ? (i+ii) : (nx-i-ii)]); bel_dof_list.Append(conn); } bel_to_patch[lbe] = b; bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i); lbe++; } gbe++; } } } // We must NOT sort bel_dof_list in this case. bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list); } void NURBSExtension::Generate3DBdrElementDofTable() { int gbe = 0; int lbe = 0, okv[2]; const KnotVector *kv[2]; NURBSPatchMap p2g(this); Array bel_dof_list; bel_to_patch.SetSize(NumOfActiveBdrElems); bel_to_IJK.SetSize(NumOfActiveBdrElems, 2); for (int b = 0; b < GetNBP(); b++) { p2g.SetBdrPatchDofMap(b, kv, okv); const int nx = p2g.nx(); // NCP0-1 const int ny = p2g.ny(); // NCP1-1 // Load dofs const int nks0 = kv[0]->GetNKS(); const int ord0 = kv[0]->GetOrder(); const int nks1 = kv[1]->GetNKS(); const int ord1 = kv[1]->GetOrder(); for (int j = 0; j < nks1; j++) { if (kv[1]->isElement(j)) { for (int i = 0; i < nks0; i++) { if (kv[0]->isElement(i)) { if (activeBdrElem[gbe]) { Connection conn(lbe,0); for (int jj = 0; jj <= ord1; jj++) { const int jj_ = (okv[1] >= 0) ? (j+jj) : (ny-j-jj); for (int ii = 0; ii <= ord0; ii++) { const int ii_ = (okv[0] >= 0) ? (i+ii) : (nx-i-ii); conn.to = DofMap(p2g(ii_, jj_)); bel_dof_list.Append(conn); } } bel_to_patch[lbe] = b; bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i); bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j); lbe++; } gbe++; } } } } } // We must NOT sort bel_dof_list in this case. bel_dof = new Table(NumOfActiveBdrElems, bel_dof_list); } void NURBSExtension::GetVertexLocalToGlobal(Array &lvert_vert) { lvert_vert.SetSize(GetNV()); for (int gv = 0; gv < GetGNV(); gv++) if (activeVert[gv] >= 0) { lvert_vert[activeVert[gv]] = gv; } } void NURBSExtension::GetElementLocalToGlobal(Array &lelem_elem) { lelem_elem.SetSize(GetNE()); for (int le = 0, ge = 0; ge < GetGNE(); ge++) if (activeElem[ge]) { lelem_elem[le++] = ge; } } void NURBSExtension::LoadFE(int i, const FiniteElement *FE) const { const NURBSFiniteElement *NURBSFE = dynamic_cast(FE); if (NURBSFE->GetElement() != i) { Array dofs; NURBSFE->SetIJK(el_to_IJK.GetRow(i)); if (el_to_patch[i] != NURBSFE->GetPatch()) { GetPatchKnotVectors(el_to_patch[i], NURBSFE->KnotVectors()); NURBSFE->SetPatch(el_to_patch[i]); NURBSFE->SetOrder(); } el_dof->GetRow(i, dofs); weights.GetSubVector(dofs, NURBSFE->Weights()); NURBSFE->SetElement(i); } } void NURBSExtension::LoadBE(int i, const FiniteElement *BE) const { if (Dimension() == 1) { return; } const NURBSFiniteElement *NURBSFE = dynamic_cast(BE); if (NURBSFE->GetElement() != i) { Array dofs; NURBSFE->SetIJK(bel_to_IJK.GetRow(i)); if (bel_to_patch[i] != NURBSFE->GetPatch()) { GetBdrPatchKnotVectors(bel_to_patch[i], NURBSFE->KnotVectors()); NURBSFE->SetPatch(bel_to_patch[i]); NURBSFE->SetOrder(); } bel_dof->GetRow(i, dofs); weights.GetSubVector(dofs, NURBSFE->Weights()); NURBSFE->SetElement(i); } } void NURBSExtension::ConvertToPatches(const Vector &Nodes) { delete el_dof; delete bel_dof; if (patches.Size() == 0) { GetPatchNets(Nodes, Dimension()); } } void NURBSExtension::SetCoordsFromPatches(Vector &Nodes) { if (patches.Size() == 0) { return; } SetSolutionVector(Nodes, Dimension()); patches.SetSize(0); } void NURBSExtension::SetKnotsFromPatches() { if (patches.Size() == 0) { mfem_error("NURBSExtension::SetKnotsFromPatches :" " No patches available!"); } Array kv; for (int p = 0; p < patches.Size(); p++) { GetPatchKnotVectors(p, kv); for (int i = 0; i < kv.Size(); i++) { *kv[i] = *patches[p]->GetKV(i); } } UpdateUniqueKV(); SetOrdersFromKnotVectors(); GenerateOffsets(); CountElements(); CountBdrElements(); // all elements must be active NumOfActiveElems = NumOfElements; activeElem.SetSize(NumOfElements); activeElem = true; GenerateActiveVertices(); InitDofMap(); GenerateElementDofTable(); GenerateActiveBdrElems(); GenerateBdrElementDofTable(); ConnectBoundaries(); } void NURBSExtension::LoadSolution(std::istream &input, GridFunction &sol) const { const FiniteElementSpace *fes = sol.FESpace(); MFEM_VERIFY(fes->GetNURBSext() == this, ""); sol.SetSize(fes->GetVSize()); Array kv(Dimension()); NURBSPatchMap p2g(this); const int vdim = fes->GetVDim(); for (int p = 0; p < GetNP(); p++) { skip_comment_lines(input, '#'); p2g.SetPatchDofMap(p, kv); const int nx = kv[0]->GetNCP(); const int ny = kv[1]->GetNCP(); const int nz = (kv.Size() == 2) ? 1 : kv[2]->GetNCP(); for (int k = 0; k < nz; k++) { for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { const int ll = (kv.Size() == 2) ? p2g(i,j) : p2g(i,j,k); const int l = DofMap(ll); for (int vd = 0; vd < vdim; vd++) { input >> sol(fes->DofToVDof(l,vd)); } } } } } } void NURBSExtension::PrintSolution(const GridFunction &sol, std::ostream &os) const { const FiniteElementSpace *fes = sol.FESpace(); MFEM_VERIFY(fes->GetNURBSext() == this, ""); Array kv(Dimension()); NURBSPatchMap p2g(this); const int vdim = fes->GetVDim(); for (int p = 0; p < GetNP(); p++) { os << "\n# patch " << p << "\n\n"; p2g.SetPatchDofMap(p, kv); const int nx = kv[0]->GetNCP(); const int ny = kv[1]->GetNCP(); const int nz = (kv.Size() == 2) ? 1 : kv[2]->GetNCP(); for (int k = 0; k < nz; k++) { for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { const int ll = (kv.Size() == 2) ? p2g(i,j) : p2g(i,j,k); const int l = DofMap(ll); os << sol(fes->DofToVDof(l,0)); for (int vd = 1; vd < vdim; vd++) { os << ' ' << sol(fes->DofToVDof(l,vd)); } os << '\n'; } } } } } void NURBSExtension::DegreeElevate(int rel_degree, int degree) { for (int p = 0; p < patches.Size(); p++) { for (int dir = 0; dir < patches[p]->GetNKV(); dir++) { int oldd = patches[p]->GetKV(dir)->GetOrder(); int newd = std::min(oldd + rel_degree, degree); if (newd > oldd) { patches[p]->DegreeElevate(dir, newd - oldd); } } } } void NURBSExtension::UniformRefinement() { for (int p = 0; p < patches.Size(); p++) { patches[p]->UniformRefinement(); } } void NURBSExtension::KnotInsert(Array &kv) { Array edges; Array orient; Array kvdir; Array pkv(Dimension()); for (int p = 0; p < patches.Size(); p++) { if (Dimension()==1) { pkv[0] = kv[KnotInd(p)]; } else if (Dimension()==2) { patchTopo->GetElementEdges(p, edges, orient); pkv[0] = kv[KnotInd(edges[0])]; pkv[1] = kv[KnotInd(edges[1])]; } else if (Dimension()==3) { patchTopo->GetElementEdges(p, edges, orient); pkv[0] = kv[KnotInd(edges[0])]; pkv[1] = kv[KnotInd(edges[3])]; pkv[2] = kv[KnotInd(edges[8])]; } // Check whether inserted knots should be flipped before inserting. // Knotvectors are stored in a different array pkvc such that the original // knots which are inserted are not changed. // We need those knots for multiple patches so they have to remain original CheckKVDirection(p, kvdir); Array pkvc(Dimension()); for (int d = 0; d < Dimension(); d++) { pkvc[d] = new KnotVector(*(pkv[d])); if (kvdir[d] == -1) { pkvc[d]->Flip(); } } patches[p]->KnotInsert(pkvc); for (int d = 0; d < Dimension(); d++) { delete pkvc[d]; } } } void NURBSExtension::KnotInsert(Array &kv) { Array edges; Array orient; Array kvdir; Array pkv(Dimension()); for (int p = 0; p < patches.Size(); p++) { if (Dimension()==1) { pkv[0] = kv[KnotInd(p)]; } else if (Dimension()==2) { patchTopo->GetElementEdges(p, edges, orient); pkv[0] = kv[KnotInd(edges[0])]; pkv[1] = kv[KnotInd(edges[1])]; } else if (Dimension()==3) { patchTopo->GetElementEdges(p, edges, orient); pkv[0] = kv[KnotInd(edges[0])]; pkv[1] = kv[KnotInd(edges[3])]; pkv[2] = kv[KnotInd(edges[8])]; } // Check whether inserted knots should be flipped before inserting. // Knotvectors are stored in a different array pkvc such that the original // knots which are inserted are not changed. CheckKVDirection(p, kvdir); Array pkvc(Dimension()); for (int d = 0; d < Dimension(); d++) { pkvc[d] = new Vector(*(pkv[d])); if (kvdir[d] == -1) { // Find flip point, for knotvectors that do not have the domain [0:1] KnotVector *kva = knotVectorsCompr[Dimension()*p+d]; double apb = (*kva)[0] + (*kva)[kva->Size()-1]; // Flip vector int size = pkvc[d]->Size(); int ns = ceil(size/2.0); for (int j = 0; j < ns; j++) { double tmp = apb - pkvc[d]->Elem(j); pkvc[d]->Elem(j) = apb - pkvc[d]->Elem(size-1-j); pkvc[d]->Elem(size-1-j) = tmp; } } } patches[p]->KnotInsert(pkvc); for (int i = 0; i < Dimension(); i++) { delete pkvc[i]; } } } void NURBSExtension::GetPatchNets(const Vector &coords, int vdim) { if (Dimension() == 1) { Get1DPatchNets(coords, vdim); } else if (Dimension() == 2) { Get2DPatchNets(coords, vdim); } else { Get3DPatchNets(coords, vdim); } } void NURBSExtension::Get1DPatchNets(const Vector &coords, int vdim) { Array kv(1); NURBSPatchMap p2g(this); patches.SetSize(GetNP()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); patches[p] = new NURBSPatch(kv, vdim+1); NURBSPatch &Patch = *patches[p]; for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = DofMap(p2g(i)); for (int d = 0; d < vdim; d++) { Patch(i,d) = coords(l*vdim + d)*weights(l); } Patch(i,vdim) = weights(l); } } } void NURBSExtension::Get2DPatchNets(const Vector &coords, int vdim) { Array kv(2); NURBSPatchMap p2g(this); patches.SetSize(GetNP()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); patches[p] = new NURBSPatch(kv, vdim+1); NURBSPatch &Patch = *patches[p]; for (int j = 0; j < kv[1]->GetNCP(); j++) { for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = DofMap(p2g(i,j)); for (int d = 0; d < vdim; d++) { Patch(i,j,d) = coords(l*vdim + d)*weights(l); } Patch(i,j,vdim) = weights(l); } } } } void NURBSExtension::Get3DPatchNets(const Vector &coords, int vdim) { Array kv(3); NURBSPatchMap p2g(this); patches.SetSize(GetNP()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); patches[p] = new NURBSPatch(kv, vdim+1); NURBSPatch &Patch = *patches[p]; for (int k = 0; k < kv[2]->GetNCP(); k++) { for (int j = 0; j < kv[1]->GetNCP(); j++) { for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = DofMap(p2g(i,j,k)); for (int d = 0; d < vdim; d++) { Patch(i,j,k,d) = coords(l*vdim + d)*weights(l); } Patch(i,j,k,vdim) = weights(l); } } } } } void NURBSExtension::SetSolutionVector(Vector &coords, int vdim) { if (Dimension() == 1) { Set1DSolutionVector(coords, vdim); } else if (Dimension() == 2) { Set2DSolutionVector(coords, vdim); } else { Set3DSolutionVector(coords, vdim); } } void NURBSExtension::Set1DSolutionVector(Vector &coords, int vdim) { Array kv(1); NURBSPatchMap p2g(this); weights.SetSize(GetNDof()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); NURBSPatch &patch = *patches[p]; MFEM_ASSERT(vdim+1 == patch.GetNC(), ""); for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = p2g(i); for (int d = 0; d < vdim; d++) { coords(l*vdim + d) = patch(i,d)/patch(i,vdim); } weights(l) = patch(i,vdim); } delete patches[p]; } } void NURBSExtension::Set2DSolutionVector(Vector &coords, int vdim) { Array kv(2); NURBSPatchMap p2g(this); weights.SetSize(GetNDof()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); NURBSPatch &patch = *patches[p]; MFEM_ASSERT(vdim+1 == patch.GetNC(), ""); for (int j = 0; j < kv[1]->GetNCP(); j++) { for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = p2g(i,j); for (int d = 0; d < vdim; d++) { coords(l*vdim + d) = patch(i,j,d)/patch(i,j,vdim); } weights(l) = patch(i,j,vdim); } } delete patches[p]; } } void NURBSExtension::Set3DSolutionVector(Vector &coords, int vdim) { Array kv(3); NURBSPatchMap p2g(this); weights.SetSize(GetNDof()); for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); NURBSPatch &patch = *patches[p]; MFEM_ASSERT(vdim+1 == patch.GetNC(), ""); for (int k = 0; k < kv[2]->GetNCP(); k++) { for (int j = 0; j < kv[1]->GetNCP(); j++) { for (int i = 0; i < kv[0]->GetNCP(); i++) { const int l = p2g(i,j,k); for (int d = 0; d < vdim; d++) { coords(l*vdim + d) = patch(i,j,k,d)/patch(i,j,k,vdim); } weights(l) = patch(i,j,k,vdim); } } } delete patches[p]; } } void NURBSExtension::GetElementIJK(int elem, Array & ijk) { MFEM_VERIFY(ijk.Size() == el_to_IJK.NumCols(), ""); el_to_IJK.GetRow(elem, ijk); } void NURBSExtension::SetPatchToElements() { const int np = GetNP(); patch_to_el.resize(np); for (int e=0; e& NURBSExtension::GetPatchElements(int patch) { MFEM_ASSERT(patch_to_el.size() > 0, "patch_to_el not set"); return patch_to_el[patch]; } const Array& NURBSExtension::GetPatchBdrElements(int patch) { MFEM_ASSERT(patch_to_bel.size() > 0, "patch_to_el not set"); return patch_to_bel[patch]; } #ifdef MFEM_USE_MPI ParNURBSExtension::ParNURBSExtension(const ParNURBSExtension &orig) : NURBSExtension(orig), partitioning(orig.partitioning ? new int[orig.GetGNE()] : NULL), gtopo(orig.gtopo), ldof_group(orig.ldof_group) { // Copy the partitioning, if not NULL if (partitioning) { std::memcpy(partitioning, orig.partitioning, orig.GetGNE()*sizeof(int)); } } ParNURBSExtension::ParNURBSExtension(MPI_Comm comm, NURBSExtension *parent, int *part, const Array &active_bel) : gtopo(comm) { if (parent->NumOfActiveElems < parent->NumOfElements) { // SetActive (BuildGroups?) and the way the weights are copied // do not support this case mfem_error("ParNURBSExtension::ParNURBSExtension :\n" " all elements in the parent must be active!"); } patchTopo = parent->patchTopo; // steal ownership of patchTopo from the 'parent' NURBS extension if (!parent->own_topo) { mfem_error("ParNURBSExtension::ParNURBSExtension :\n" " parent does not own the patch topology!"); } own_topo = 1; parent->own_topo = 0; parent->edge_to_knot.Copy(edge_to_knot); parent->GetOrders().Copy(mOrders); mOrder = parent->GetOrder(); NumOfKnotVectors = parent->GetNKV(); knotVectors.SetSize(NumOfKnotVectors); for (int i = 0; i < NumOfKnotVectors; i++) { knotVectors[i] = new KnotVector(*parent->GetKnotVector(i)); } CreateComprehensiveKV(); GenerateOffsets(); CountElements(); CountBdrElements(); // copy 'part' to 'partitioning' partitioning = new int[GetGNE()]; for (int i = 0; i < GetGNE(); i++) { partitioning[i] = part[i]; } SetActive(partitioning, active_bel); GenerateActiveVertices(); GenerateElementDofTable(); // GenerateActiveBdrElems(); // done by SetActive for now GenerateBdrElementDofTable(); Table *serial_elem_dof = parent->GetElementDofTable(); BuildGroups(partitioning, *serial_elem_dof); weights.SetSize(GetNDof()); // copy weights from parent for (int gel = 0, lel = 0; gel < GetGNE(); gel++) { if (activeElem[gel]) { int ndofs = el_dof->RowSize(lel); int *ldofs = el_dof->GetRow(lel); int *gdofs = serial_elem_dof->GetRow(gel); for (int i = 0; i < ndofs; i++) { weights(ldofs[i]) = parent->weights(gdofs[i]); } lel++; } } } ParNURBSExtension::ParNURBSExtension(NURBSExtension *parent, const ParNURBSExtension *par_parent) : gtopo(par_parent->gtopo.GetComm()) { // steal all data from parent mOrder = parent->mOrder; Swap(mOrders, parent->mOrders); patchTopo = parent->patchTopo; own_topo = parent->own_topo; parent->own_topo = 0; Swap(edge_to_knot, parent->edge_to_knot); NumOfKnotVectors = parent->NumOfKnotVectors; Swap(knotVectors, parent->knotVectors); Swap(knotVectorsCompr, parent->knotVectorsCompr); NumOfVertices = parent->NumOfVertices; NumOfElements = parent->NumOfElements; NumOfBdrElements = parent->NumOfBdrElements; NumOfDofs = parent->NumOfDofs; Swap(v_meshOffsets, parent->v_meshOffsets); Swap(e_meshOffsets, parent->e_meshOffsets); Swap(f_meshOffsets, parent->f_meshOffsets); Swap(p_meshOffsets, parent->p_meshOffsets); Swap(v_spaceOffsets, parent->v_spaceOffsets); Swap(e_spaceOffsets, parent->e_spaceOffsets); Swap(f_spaceOffsets, parent->f_spaceOffsets); Swap(p_spaceOffsets, parent->p_spaceOffsets); Swap(d_to_d, parent->d_to_d); Swap(master, parent->master); Swap(slave, parent->slave); NumOfActiveVertices = parent->NumOfActiveVertices; NumOfActiveElems = parent->NumOfActiveElems; NumOfActiveBdrElems = parent->NumOfActiveBdrElems; NumOfActiveDofs = parent->NumOfActiveDofs; Swap(activeVert, parent->activeVert); Swap(activeElem, parent->activeElem); Swap(activeBdrElem, parent->activeBdrElem); Swap(activeDof, parent->activeDof); el_dof = parent->el_dof; bel_dof = parent->bel_dof; parent->el_dof = parent->bel_dof = NULL; Swap(el_to_patch, parent->el_to_patch); Swap(bel_to_patch, parent->bel_to_patch); Swap(el_to_IJK, parent->el_to_IJK); Swap(bel_to_IJK, parent->bel_to_IJK); Swap(weights, parent->weights); MFEM_VERIFY(!parent->HavePatches(), ""); delete parent; partitioning = NULL; MFEM_VERIFY(par_parent->partitioning, "parent ParNURBSExtension has no partitioning!"); // Support for the case when 'parent' is not a local NURBSExtension, i.e. // NumOfActiveElems is not the same as in 'par_parent'. In that case, we // assume 'parent' is a global NURBSExtension, i.e. all elements are active. bool extract_weights = false; if (NumOfActiveElems != par_parent->NumOfActiveElems) { MFEM_ASSERT(NumOfActiveElems == NumOfElements, "internal error"); SetActive(par_parent->partitioning, par_parent->activeBdrElem); GenerateActiveVertices(); delete el_dof; el_to_patch.DeleteAll(); el_to_IJK.DeleteAll(); GenerateElementDofTable(); // GenerateActiveBdrElems(); // done by SetActive for now delete bel_dof; bel_to_patch.DeleteAll(); bel_to_IJK.DeleteAll(); GenerateBdrElementDofTable(); extract_weights = true; } Table *glob_elem_dof = GetGlobalElementDofTable(); BuildGroups(par_parent->partitioning, *glob_elem_dof); if (extract_weights) { Vector glob_weights; Swap(weights, glob_weights); weights.SetSize(GetNDof()); // Copy the local 'weights' from the 'glob_weights'. // Assumption: the local element ids follow the global ordering. for (int gel = 0, lel = 0; gel < GetGNE(); gel++) { if (activeElem[gel]) { int ndofs = el_dof->RowSize(lel); int *ldofs = el_dof->GetRow(lel); int *gdofs = glob_elem_dof->GetRow(gel); for (int i = 0; i < ndofs; i++) { weights(ldofs[i]) = glob_weights(gdofs[i]); } lel++; } } } delete glob_elem_dof; } Table *ParNURBSExtension::GetGlobalElementDofTable() { if (Dimension() == 1) { return Get1DGlobalElementDofTable(); } else if (Dimension() == 2) { return Get2DGlobalElementDofTable(); } else { return Get3DGlobalElementDofTable(); } } Table *ParNURBSExtension::Get1DGlobalElementDofTable() { int el = 0; const KnotVector *kv[1]; NURBSPatchMap p2g(this); Array gel_dof_list; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { Connection conn(el,0); for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii)); gel_dof_list.Append(conn); } el++; } } } // We must NOT sort gel_dof_list in this case. return (new Table(GetGNE(), gel_dof_list)); } Table *ParNURBSExtension::Get2DGlobalElementDofTable() { int el = 0; const KnotVector *kv[2]; NURBSPatchMap p2g(this); Array gel_dof_list; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); const int ord1 = kv[1]->GetOrder(); for (int j = 0; j < kv[1]->GetNKS(); j++) { if (kv[1]->isElement(j)) { for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { Connection conn(el,0); for (int jj = 0; jj <= ord1; jj++) { for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii,j+jj)); gel_dof_list.Append(conn); } } el++; } } } } } // We must NOT sort gel_dof_list in this case. return (new Table(GetGNE(), gel_dof_list)); } Table *ParNURBSExtension::Get3DGlobalElementDofTable() { int el = 0; const KnotVector *kv[3]; NURBSPatchMap p2g(this); Array gel_dof_list; for (int p = 0; p < GetNP(); p++) { p2g.SetPatchDofMap(p, kv); // Load dofs const int ord0 = kv[0]->GetOrder(); const int ord1 = kv[1]->GetOrder(); const int ord2 = kv[2]->GetOrder(); for (int k = 0; k < kv[2]->GetNKS(); k++) { if (kv[2]->isElement(k)) { for (int j = 0; j < kv[1]->GetNKS(); j++) { if (kv[1]->isElement(j)) { for (int i = 0; i < kv[0]->GetNKS(); i++) { if (kv[0]->isElement(i)) { Connection conn(el,0); for (int kk = 0; kk <= ord2; kk++) { for (int jj = 0; jj <= ord1; jj++) { for (int ii = 0; ii <= ord0; ii++) { conn.to = DofMap(p2g(i+ii,j+jj,k+kk)); gel_dof_list.Append(conn); } } } el++; } } } } } } } // We must NOT sort gel_dof_list in this case. return (new Table(GetGNE(), gel_dof_list)); } void ParNURBSExtension::SetActive(const int *partitioning_, const Array &active_bel) { activeElem.SetSize(GetGNE()); activeElem = false; NumOfActiveElems = 0; const int MyRank = gtopo.MyRank(); for (int i = 0; i < GetGNE(); i++) if (partitioning_[i] == MyRank) { activeElem[i] = true; NumOfActiveElems++; } active_bel.Copy(activeBdrElem); NumOfActiveBdrElems = 0; for (int i = 0; i < GetGNBE(); i++) if (activeBdrElem[i]) { NumOfActiveBdrElems++; } } void ParNURBSExtension::BuildGroups(const int *partitioning_, const Table &elem_dof) { Table dof_proc; ListOfIntegerSets groups; IntegerSet group; Transpose(elem_dof, dof_proc); // dof_proc is dof_elem // convert elements to processors for (int i = 0; i < dof_proc.Size_of_connections(); i++) { dof_proc.GetJ()[i] = partitioning_[dof_proc.GetJ()[i]]; } // the first group is the local one int MyRank = gtopo.MyRank(); group.Recreate(1, &MyRank); groups.Insert(group); int dof = 0; ldof_group.SetSize(GetNDof()); for (int d = 0; d < GetNTotalDof(); d++) if (activeDof[d]) { group.Recreate(dof_proc.RowSize(d), dof_proc.GetRow(d)); ldof_group[dof] = groups.Insert(group); dof++; } gtopo.Create(groups, 1822); } #endif // MFEM_USE_MPI void NURBSPatchMap::GetPatchKnotVectors(int p, const KnotVector *kv[]) { Ext->patchTopo->GetElementVertices(p, verts); if (Ext->Dimension() == 1) { kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p]; } else if (Ext->Dimension() == 2) { Ext->patchTopo->GetElementEdges(p, edges, oedge); kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p]; kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1]; } else if (Ext->Dimension() == 3) { Ext->patchTopo->GetElementEdges(p, edges, oedge); Ext->patchTopo->GetElementFaces(p, faces, oface); kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p]; kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1]; kv[2] = Ext->knotVectorsCompr[Ext->Dimension()*p + 2]; } opatch = 0; } void NURBSPatchMap::GetBdrPatchKnotVectors(int p, const KnotVector *kv[], int *okv) { Ext->patchTopo->GetBdrElementVertices(p, verts); if (Ext->Dimension() == 2) { Ext->patchTopo->GetBdrElementEdges(p, edges, oedge); kv[0] = Ext->KnotVec(edges[0], oedge[0], &okv[0]); opatch = oedge[0]; } else if (Ext->Dimension() == 3) { faces.SetSize(1); Ext->patchTopo->GetBdrElementEdges(p, edges, oedge); Ext->patchTopo->GetBdrElementFace(p, &faces[0], &opatch); kv[0] = Ext->KnotVec(edges[0], oedge[0], &okv[0]); kv[1] = Ext->KnotVec(edges[1], oedge[1], &okv[1]); } } void NURBSPatchMap::SetPatchVertexMap(int p, const KnotVector *kv[]) { GetPatchKnotVectors(p, kv); I = kv[0]->GetNE() - 1; for (int i = 0; i < verts.Size(); i++) { verts[i] = Ext->v_meshOffsets[verts[i]]; } if (Ext->Dimension() >= 2) { J = kv[1]->GetNE() - 1; for (int i = 0; i < edges.Size(); i++) { edges[i] = Ext->e_meshOffsets[edges[i]]; } } if (Ext->Dimension() == 3) { K = kv[2]->GetNE() - 1; for (int i = 0; i < faces.Size(); i++) { faces[i] = Ext->f_meshOffsets[faces[i]]; } } pOffset = Ext->p_meshOffsets[p]; } void NURBSPatchMap::SetPatchDofMap(int p, const KnotVector *kv[]) { GetPatchKnotVectors(p, kv); I = kv[0]->GetNCP() - 2; for (int i = 0; i < verts.Size(); i++) { verts[i] = Ext->v_spaceOffsets[verts[i]]; } if (Ext->Dimension() >= 2) { J = kv[1]->GetNCP() - 2; for (int i = 0; i < edges.Size(); i++) { edges[i] = Ext->e_spaceOffsets[edges[i]]; } } if (Ext->Dimension() == 3) { K = kv[2]->GetNCP() - 2; for (int i = 0; i < faces.Size(); i++) { faces[i] = Ext->f_spaceOffsets[faces[i]]; } } pOffset = Ext->p_spaceOffsets[p]; } void NURBSPatchMap::SetBdrPatchVertexMap(int p, const KnotVector *kv[], int *okv) { GetBdrPatchKnotVectors(p, kv, okv); for (int i = 0; i < verts.Size(); i++) { verts[i] = Ext->v_meshOffsets[verts[i]]; } if (Ext->Dimension() == 1) { I = 0; } else if (Ext->Dimension() == 2) { I = kv[0]->GetNE() - 1; pOffset = Ext->e_meshOffsets[edges[0]]; } else if (Ext->Dimension() == 3) { I = kv[0]->GetNE() - 1; J = kv[1]->GetNE() - 1; for (int i = 0; i < edges.Size(); i++) { edges[i] = Ext->e_meshOffsets[edges[i]]; } pOffset = Ext->f_meshOffsets[faces[0]]; } } void NURBSPatchMap::SetBdrPatchDofMap(int p, const KnotVector *kv[], int *okv) { GetBdrPatchKnotVectors(p, kv, okv); for (int i = 0; i < verts.Size(); i++) { verts[i] = Ext->v_spaceOffsets[verts[i]]; } if (Ext->Dimension() == 1) { I = 0; } else if (Ext->Dimension() == 2) { I = kv[0]->GetNCP() - 2; pOffset = Ext->e_spaceOffsets[edges[0]]; } else if (Ext->Dimension() == 3) { I = kv[0]->GetNCP() - 2; J = kv[1]->GetNCP() - 2; for (int i = 0; i < edges.Size(); i++) { edges[i] = Ext->e_spaceOffsets[edges[i]]; } pOffset = Ext->f_spaceOffsets[faces[0]]; } } }