G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsAlmostC1.hpp
Go to the documentation of this file.
1 
14 #include <gsIO/gsWriteParaview.h>
16 #include <gsHSplines/gsTHBSpline.h>
17 
21 
22 namespace gismo
23 {
24  // Constructors
25  template<short_t d,class T>
27  :
28  Base(patches)
29  {
30  this->defaultOptions();
31 
32  for (size_t p=0; p!=m_Bbases.nBases(); p++)
33  for (short_t dim=0; dim!=d; dim++)
34  GISMO_ENSURE(m_Bbases.basis(p).degree(dim)==2,"Degree of the basis ( dimension "<<dim<<" ) of patch "<<p<<" is "<<m_Bbases.basis(p).degree(dim)<<", but should be 2!");
35  }
36 
37  template<short_t d,class T>
39  {
40  freeAll(m_bases);
41  }
42 
43  // template<short_t d,class T>
44  // void gsAlmostC1<d,T>::compute()
45  // {
46  // // m_RefPatches = m_patches;
47  // _initialize();
48  // _computeMapper();
49  // _computeSmoothMatrix();
50  // GISMO_ASSERT(this->_checkMatrix(m_matrix),"Mapper does not have column sum equal to 1");
51  // _makeTHB();
52  // _computeEVs();
53  // GISMO_ASSERT(this->_checkMatrix(m_matrix),"Mapper does not have column sum equal to 1");
54  // }
55 
56 
57  /*=====================================================================================
58  Special functions
59  =====================================================================================*/
60  template<short_t d,class T>
61  gsMatrix<T> gsAlmostC1<d,T>::_getNormals(const std::vector<patchCorner> & corners) const
62  {
63  gsMatrix<T> normals(3,corners.size());
64 
65  gsVector<bool> pars;
66  gsMatrix<T> mat;
67 
68  gsExprEvaluator<T> ev;
69  typename gsExprEvaluator<T>::geometryMap Gm = ev.getMap(m_RefPatches);
70  index_t k = 0;
71  for (typename std::vector<patchCorner>::const_iterator it = corners.begin(); it!=corners.end(); it++, k++)
72  {
73  it->corner().parameters_into(m_RefPatches.parDim(),pars); // get the parametric coordinates of the corner
74  gsMatrix<T> supp = m_RefPatches.basis(it->patch).support();
75  gsVector<T> vec(supp.rows());
76  for (index_t r = 0; r!=supp.rows(); r++)
77  vec(r) = supp(r,pars(r));
78 
79  normals.col(k) = ev.eval(sn(Gm).normalized(),vec,it->patch);
80  }
81  return normals;
82  }
83 
84 
85  template<short_t d,class T>
86  std::tuple<gsMatrix<T>,gsMatrix<T>,gsMatrix<index_t>> gsAlmostC1<d,T>::_makeTriangle(const patchCorner & corner) const
87  {
88  GISMO_ASSERT(m_RefPatches.nPatches()!=0,"Are the patches refined?");
89 
90  index_t tdim = m_RefPatches.targetDim();
91 
92  std::vector<patchCorner> corners;
93  m_RefPatches.getCornerList(corner,corners);
94 
95  gsVector<bool> pars;
96  gsMatrix<T> mat;
97  // 1. Get the coordinates of the vertex and set its z coordinate to 0
98  gsMatrix<T> um(3,1), midpoint;
99  um.setZero();
100  corner.corner().parameters_into(m_RefPatches.parDim(),pars); // get the parametric coordinates of the corner
101  gsMatrix<T> supp = m_RefPatches.basis(corner.patch).support();
102  gsVector<T> vec(supp.rows());
103  for (index_t r = 0; r!=supp.rows(); r++)
104  vec(r) = supp(r,pars(r));
105 
106  um.block(0,0,tdim,1) = m_RefPatches.patch(corner.patch).eval(vec);
107  midpoint = um; // store the original midpoint
108 
109  // 2. Get the 0,0;0,1; 1,0; 1,1 coordinates
110  gsMatrix<T> u(3,corners.size()*4);
111  u.setZero();
112  gsMatrix<index_t> uind(1,corners.size()*4);
113  uind.setZero();
114 
115  std::vector<patchSide> csides;
116  index_t idx;
117  for (size_t c = 0; c!=corners.size(); c++)
118  {
119  corners[c].getContainingSides(d,csides);
120  index_t k=0;
121  for (index_t i=0; i!=2; i++)
122  for (index_t j=0; j!=2; j++,k++)
123  {
124  idx = _indexFromVert(i,corners[c],csides[0],j);
125  uind(0,4*c+k) = m_mapOriginal.index(idx,corners[c].patch);
126  u.block(0,4*c+k,m_RefPatches.targetDim(),1) = m_RefPatches.patch(corners[c].patch).coefs().row(idx).transpose();
127  }
128  }
129 
130  // 3. Translate all points to a coordinate system with origin um
131  gsMatrix<T> up = u;
132  for (index_t k=0; k!=up.cols(); k++)
133  up.col(k) -= um;
134 
135  // 4. Rotate the points parallel the xy-plane and set their z-coordinates to 0
136  gsMatrix<T,3,3> Rn, Rx;
137  Rn.setIdentity();
138  if (m_RefPatches.targetDim()==2)
139  {
140  // do nothing
141  }
142  else if(m_RefPatches.targetDim()==3)
143  {
144  // Get the average normal at the corner
145  gsVector<T> avgnormal = _getNormals(corners).rowwise().mean();
146  // If the norm is zero, the normals are likely to be opposite
147  if (avgnormal.norm()==0) avgnormal = _getNormals(corners).col(0);
148  // Find the rotation matrix that maps the average normal to the z axis
149  gsVector<T,3> ez;
150  ez<<0,0,1;
151  Rn = _getRotationMatrix(avgnormal.normalized(),ez);
152 
153  for (index_t k=0; k!=up.cols(); k++)
154  up.col(k).applyOnTheLeft(Rn);
155 
156  up.row(2).setZero(); // all points
157  um.row(2).setZero();// midpoint
158  }
159  else
160  GISMO_ERROR("Target dimension of the multipatch should be 2 or 3, but is "<<m_RefPatches.targetDim());
161 
162  // 5. Find the maximum distance from the midpoint to all points
163  T distance, maxDistance = 0;
164  gsMatrix<T> umax;
165  for (index_t k = 0; k!=up.cols(); k++)
166  {
167  distance = (up.col(k)).norm();
168  if (distance > maxDistance)
169  {
170  maxDistance = distance;
171  umax = up.col(k);
172  }
173  }
174 
175  gsVector<T,3> ex;
176  ex<<1,0,0;
177 
178  // 6. Rotate all points such that the maximum point is aligned with the x-axis
179  Rx = _getRotationMatrix(umax.normalized(),ex);
180  for (index_t k=0; k!=up.cols(); k++)
181  up.col(k).applyOnTheLeft(Rx);
182 
183  // 7. Obtain the coordinates of the triangle that encloses the circle with radius maxDistance in the xy plane
184  T r = maxDistance;
185  T a = 1. / ( 1./6. * std::sqrt(3) ) * r;
186  T rr = 1. / 3. * std::sqrt(3) * a;
187 
188  gsMatrix<T> Cp(2,3);
189  Cp.col(0)<<rr,0;
190  Cp.col(1)<<-r, 0.5*a;
191  Cp.col(2)<<-r,-0.5*a;
192 
193  // 8. Get the barycentric coordinates of the points
194  gsMatrix<T> ub = up;
195  up.row(2).setOnes(); // project the parametric points to z=1
196  gsMatrix<T> A(3,3);
197  A.block(0,0,2,3) = Cp;
198  A.row(2).setOnes();
199 
200  for (index_t k = 0; k!=ub.cols(); k++)
201  {
202  ub.col(k) = A.colPivHouseholderQr().solve(up.col(k));
203  GISMO_ASSERT((Cp * ub.col(k)-up.col(k).head(2)).norm()<1e-12,"Something went wrong with the computation of the barycentric coordinates. (Cp * ub.col(k)-up.col(k).head(2)).norm() = "<<(Cp * ub.col(k)-up.col(k).head(2)).norm()<<"; Cp * ub.col(k) = "<<Cp * ub.col(k)<<"; up.col(k).head(2) = "<<up.col(k).head(2));
204  }
205 
206  // 9. Move the corners of the triangle back to physical coordinates
207  gsMatrix<T> Cg(3,3);
208  Cg.setZero();
209  Cg.block(0,0,2,3) = Cp;
210 
211  for (index_t k = 0; k!=Cg.cols(); k++)
212  {
213  Cg.col(k).applyOnTheLeft((Rx).transpose());
214  Cg.col(k).applyOnTheLeft((Rn).transpose());
215  Cg.col(k) += midpoint;
216  }
217 
218  if (m_RefPatches.targetDim()==2)
219  Cg.conservativeResize(2,gsEigen::NoChange);
220 
221  return std::make_tuple(Cg,ub,uind);
222  }
223 
224  template<short_t d,class T>
225  gsMatrix<T,3,3> gsAlmostC1<d,T>::_getRotationMatrix(const gsVector<T,3> & a, const gsVector<T,3> & b) const
226  {
227  GISMO_ASSERT(std::abs(a.norm()-1)<1e-14,"A must be a unit vector, a.norm() = "<<a.norm());
228  GISMO_ASSERT(std::abs(b.norm()-1)<1e-14,"B must be a unit vector, b.norm() = "<<b.norm());
229 
230  gsVector<T,3> v = a.cross(b);
231  v.normalize();
232  T theta = std::acos( a.dot(b) / ( a.norm() * b.norm() ) );
233 
234  T s = std::sin(theta);
235  T c = std::cos(theta);
236  gsMatrix<T,3,3> R,vx,tmp, I;
237  R.setZero();
238  vx.setZero();
239 
240  vx.row(0)<<0,-v.at(2),v.at(1);
241  vx.row(1)<<v.at(2),0,-v.at(0);
242  vx.row(2)<<-v.at(1),v.at(0),0;
243 
244  I.setIdentity();
245  R += I*c;
246  R += vx * s;
247  tmp = (v*v.transpose()) * (1-c);
248  R += tmp;
249 
250  GISMO_ASSERT((R * a - b).norm() < 1e-12,"Rotation matrix is wrong, R*a = "<<R*a<<"; b = "<<b);
251  return R;
252  }
253 
254 
255  /*=====================================================================================
256  Coefficients
257  =====================================================================================*/
258  // ADD THE COEFFICIENTS OF THE TRIANGLES AS EXTRA COEFFICIENTS
259 
260  template<short_t d,class T>
262  {
263  GISMO_ASSERT(m_mapModified.isFinalized(),"Mapper is not finalized, run XXXX first");
264 
265  GISMO_ASSERT((size_t)m_mapModified.freeSize()==m_size,"Size does not match predicted size, m_mapModified.freeSize()="<<m_mapModified.freeSize()<<"; m_size="<<m_size);
266  gsMatrix<T> coefs(m_mapModified.freeSize(),m_patches.geoDim());
267 
268  index_t size;
269  for (size_t p=0; p!=m_bases.nBases(); p++) // patches
270  {
271  size = m_mapModified.patchSize(p);
272  for (index_t k=0; k!=size; k++)
273  {
274  if (m_mapModified.is_free(k,p))
275  coefs.row(m_mapModified.index(k,p,0)) = m_patches.patch(p).coefs().row(k);
276  }
277  }
278  return coefs;
279  }
280 
281  template<short_t d,class T>
283  {
284  GISMO_ASSERT(m_mapModified.isFinalized(),"Mapper is not finalized, run XXXX first");
285 
286  gsMatrix<T> coefs = this->freeCoefficients();
287 
288  // Correct the EVs
289  std::fill(m_vertCheck.begin(), m_vertCheck.end(), false);
290  index_t cidx;
291  std::vector<patchCorner> pcorners;
292  patchCorner pcorner;
293  for (size_t p=0; p!=m_bases.nBases(); p++)
294  {
295  for (index_t c=1; c<5; c++)
296  {
297  cidx = _vertIndex(p,c);
298  if (m_vertCheck.at(cidx))
299  continue;
300 
301  bool C0 = m_C0s[cidx];
302  pcorner = patchCorner(p,c);
303  m_topology.getCornerList(pcorner,pcorners);
304  std::pair<index_t,bool> vdata = _vertexData(pcorner); // corner c
305  if (vdata.first > 2 && !(vdata.first==4 && vdata.second)) // valence must be 3 or larger, but it must not be an interior vertex with v=4
306  {
307  // get the triangle
308  gsMatrix<T> Cg;
309  std::tie(Cg,std::ignore,std::ignore) = _makeTriangle(pcorner);
310 
311  // The barycentric coordinates will be attached to the matrix rows corresponding to the 0,0 corners (and the three lowest patch index corners whenever valence > 3)
312  // We use _getLowestIndices such that the corners are assigned to increasing patch corners
313  std::vector<std::pair<index_t,index_t>> indices = _getAllInterfaceIndices(pcorner,0,m_Bbases);
314  _getLowestIndices(indices,3);
315 
316  std::vector<index_t> rowIndices;
317  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
318  {
319  GISMO_ASSERT(m_mapModified.is_free(it->second,it->first),"This DoF must be free! patch = "<<it->first<<"; index = "<<it->first);
320  rowIndices.push_back(m_mapModified.index(it->second,it->first));
321  }
322 
323  index_t rowIdx;
324  for (index_t j=0; j!=Cg.cols(); j++)
325  {
326  rowIdx = rowIndices[j];
327  coefs.row(rowIdx) = Cg.col(j).transpose();
328  }
329 
330  for (size_t k = 0; k!=pcorners.size(); k++)
331  m_vertCheck[ _vertIndex(pcorners[k].patch, pcorners[k].corner()) ] = true;
332  }
333  else if (vdata.first == 2 && C0) // valence must be 3 or larger, but it must not be an interior vertex with v=4
334  {
335  // get the triangle
336  gsMatrix<T> Cg;
337  std::tie(Cg,std::ignore,std::ignore) = _makeTriangle(pcorner);
338 
339  // The barycentric coordinates will be attached to the matrix rows corresponding to the 0,0 corners (and the three lowest patch index corners whenever valence > 3)
340  // We use _getLowestIndices such that the corners are assigned to increasing patch corners
341  std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_Bbases);
342  std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_Bbases);
343  _getLowestIndices(indices1,1);
344  indices0.push_back(indices1[0]);
345 
346  std::vector<index_t> rowIndices;
347  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices0.begin(); it!=indices0.end(); it++)
348  {
349  GISMO_ASSERT(m_mapModified.is_free(it->second,it->first),"This DoF must be free! patch = "<<it->first<<"; index = "<<it->first);
350  rowIndices.push_back(m_mapModified.index(it->second,it->first));
351  }
352 
353  index_t rowIdx;
354  for (index_t j=0; j!=Cg.cols(); j++)
355  {
356  rowIdx = rowIndices[j];
357  coefs.row(rowIdx) = Cg.col(j).transpose();
358  }
359 
360  for (size_t k = 0; k!=pcorners.size(); k++)
361  m_vertCheck[ _vertIndex(pcorners[k].patch, pcorners[k].corner()) ] = true;
362  }
363  else
364  {
365  m_vertCheck[ _vertIndex(pcorner.patch, pcorner.corner()) ] = true;
366  continue;
367  }
368  }
369  }
370  return coefs;
371  }
372 
373  template<short_t d,class T>
375  {
376  std::vector<index_t> sizes(mp.nPatches());
377  index_t totalsize = 0;
378  for (size_t p=0; p!=mp.nPatches(); p++) // patches
379  {
380  sizes.at(p) = mp.patch(p).coefs().rows();
381  totalsize += sizes.at(p);
382  }
383 
384  GISMO_ENSURE(totalsize==coefs.rows(),"Sizes do not agree");
385 
386  gsMultiBasis<T> basis(mp);
387  gsDofMapper tmpMap(basis);
388  tmpMap.finalize();
389 
390  for (size_t p=0; p!=mp.nPatches(); p++) // patches
391  for (index_t k=0; k!=sizes.at(p); k++)
392  mp.patch(p).coefs().row(k) = coefs.row(tmpMap.index(k,p));
393  }
394 
395  /*=====================================================================================
396  Construction functions
397  =====================================================================================*/
398 
399  template<short_t d,class T>
401  {
402  m_bases = gsMultiBasis<T>(m_patches);
403  }
404 
405  template<short_t d,class T>
407  {
408  m_RefPatches = m_patches;
409  // Cast all patches of the mp object to THB splines
410  gsTHBSpline<d,T> thb;
411  gsTensorBSpline<d,T> * geo;
412  for (size_t k=0; k!=m_RefPatches.nPatches(); ++k)
413  {
414  if ( (geo = dynamic_cast< gsTensorBSpline<d,T> * > (&m_RefPatches.patch(k))) )
415  {
416  thb = gsTHBSpline<d,T>(*geo);
417  m_RefPatches.patch(k) = thb;
418  }
419  else if (dynamic_cast< gsTHBSpline<d,T> * > (&m_RefPatches.patch(k)))
420  { }
421  else
422  gsWarn<<"No THB basis was constructed";
423  }
424  }
425 
426 
427  template <short_t d, class T>
428  void gsAlmostC1<d,T>::_refBoxes(std::vector<std::vector<index_t>> & patchBoxes)
429  {
430  patchBoxes.clear();
431  patchBoxes.resize(m_RefPatches.nPatches());
432 
433  // prepare the geometry
434  gsMatrix<index_t> box(d,2);
435  std::vector<index_t> boxes;
436  gsVector<bool> pars;
437  index_t nelements;
438  patchCorner corner;
439  std::vector<patchCorner> cornerList;
440  std::vector<std::vector<patchCorner> > cornerLists = _getSpecialCornerLists(m_RefPatches);
441 
442  index_t N = cornerLists.size();
443 
444  // Make a mask of corners per patch to track which ones have been handled
445  gsMatrix<bool> mask(m_RefPatches.nPatches(),math::pow(2,d));
446  mask.setConstant(false);
447 
448  for (index_t v =0; v<N; v++)
449  {// Loop over EVs
450  for (size_t c = 0; c<cornerLists[v].size(); c++)
451  {// Loop over corners per EV
452  corner = cornerLists[v].at(c);
453  gsHTensorBasis<d,T> * basis = dynamic_cast<gsHTensorBasis<d,T>*>(&m_RefPatches.basis(corner.patch));
454 
455  if (mask(corner.patch,corner.corner()-1))
456  continue;
457 
458  corner.parameters_into(d,pars);
459  box.setZero();
460  for (short_t dim = 0; dim!=d; dim++)
461  {
462  const gsKnotVector<T> & KV = basis->tensorLevel(0).knots(dim);
463  nelements = 1;
464  box.row(dim).setConstant(pars(dim)*(KV.uSize()-1));
465  box(dim,!pars(dim)) += ( 1 - 2*pars(dim) ) * nelements; // subtracts from box(d,0) if pars(d)==1, adds to box(d,1) if pars(d)==0
466 
467  // If all elements in this direction are refined, we need to add the other corner of this side to the list of corners to be refined
468  if ((index_t)KV.numElements()==nelements)
469  {
470  // Get the patch side in the direction of the knot vector KV
471  GISMO_ASSERT(d==2,"This does not work for d!=2!");
472  boxCorner otherCorner;
473  if ((corner.m_index==1 && dim==0) || (corner.m_index==4 && dim==1)) // the other corner is south east
474  otherCorner = 2;
475  else if ((corner.m_index==1 && dim==1) || (corner.m_index==4 && dim==0)) // the other corner is north west
476  otherCorner = 3;
477  else if ((corner.m_index==2 && dim==0) || (corner.m_index==3 && dim==1)) // the other corner is south west
478  otherCorner = 1;
479  else if ((corner.m_index==2 && dim==1) || (corner.m_index==3 && dim==0)) // the other corner is north east
480  otherCorner = 4;
481  else
482  GISMO_ERROR("Combination unknown...");
483 
484  patchCorner otherPCorner(corner.patch,otherCorner.m_index);
485 
486  cornerList.clear();
487  m_topology.getCornerList(otherPCorner,cornerList);
488  cornerLists.push_back(cornerList);
489  N++;
490  }
491  }
492  boxes.clear();
493  // Assign boxes. This is the box on the current level, level 0.
494  boxes.push_back(0);
495  boxes.insert(boxes.end(), box.data(), box.data()+box.size());
496  patchBoxes.at(corner.patch).insert(patchBoxes.at(corner.patch).end(), boxes.begin(), boxes.end());
497 
498  mask(corner.patch,corner.corner()-1) = true;
499  }// Loop over corners per EV
500  }// Loop over EVs
501  }
502 
503  template<short_t d,class T>
505  {
506  // prepare the geometry
507  std::vector<std::vector<patchCorner> > cornerLists = _getSpecialCornerLists(m_RefPatches);
508 
509  if (cornerLists.size()!=0)
510  {
512  gsMatrix<T> coefs = this->freeCoefficients(); // gets coefficients of the modified size
513  coefs = m_matrix.transpose() * coefs; // maps to local size
514 
515  this->setCoefficients(coefs,m_RefPatches);
516 
518  std::vector< std::vector<index_t> > elVec;
519  this->_refBoxes(elVec);
520 
521  gsSparseMatrix<T> tmp;
522  index_t rows = 0, cols = 0;
523  std::vector<gsEigen::Triplet<T,index_t>> tripletList;
524  for (size_t p=0; p!=m_RefPatches.nPatches(); p++)
525  {
526  // Transform using gsAsMatrix
527  gsAsMatrix<index_t> boxMat(elVec[p],2*d+1,elVec[p].size()/(2*d+1));
528  boxMat.row(0).array() += 1;
529  boxMat.block(1,0,boxMat.rows()-1,boxMat.cols()).array() *= 2;
530 
531  gsHTensorBasis<2,T> *basis = dynamic_cast<gsHTensorBasis<2,T>*>(&m_RefPatches.basis(p));
532  std::vector< gsSortedVector< index_t > > xmat = basis->getXmatrix();
533 
534  m_RefPatches.patch(p).refineElements(elVec[p]);
535 
536  basis->transfer(xmat,tmp);
537  for (index_t i = 0; i<tmp.outerSize(); ++i)
538  for (typename gsSparseMatrix<T>::iterator it(tmp,i); it; ++it)
539  tripletList.push_back(gsEigen::Triplet<T,index_t>(it.row()+rows,it.col()+cols,it.value()));
540 
541  rows += tmp.rows();
542  cols += tmp.cols();
543  }
544 
545  m_tMatrix.resize(rows,cols);
546  m_tMatrix.setFromTriplets(tripletList.begin(), tripletList.end());
547 
548  m_tMatrix.makeCompressed();
549  m_bases = gsMultiBasis<T>(m_RefPatches);
550  }
551 
552  // redefine the mappers
553  m_mapOriginal = gsDofMapper(m_bases);
554  m_mapOriginal.finalize();
555 
556  // gsWriteParaview<T>(m_RefPatches,"mp_ref",100,true);
557  }
558 
559  template<short_t d, class T>
560  std::vector<std::vector<patchCorner> > gsAlmostC1<d,T>::_getSpecialCornerLists(const gsMultiPatch<T> & patches)
561  {
562  std::vector<std::vector<patchCorner> > cornerLists;
563  // get the corners that need refinement
564  std::vector<patchCorner> cornerList;
565  patchCorner pcorner;
566  index_t cidx;
567  for(size_t p = 0;p<patches.nPatches();++p)
568  {
569  for(index_t c=1;c<5;++c)
570  {
571  pcorner=patchCorner(p,c);
572  cidx = _vertIndex(p,c);
573  bool C0 = m_C0s[cidx];
574  bool isCycle = patches.getCornerList(pcorner,cornerList);
575  bool alreadyReached = false;
576  for(size_t k = 0;k<cornerList.size();++k)
577  if((size_t)cornerList[k].patch<p)
578  alreadyReached = true;
579 
580  // add if
581  // interior vertex with valence!=4
582  // or
583  // boundary vertex with valence > 2 (unless C0, then valence > 1)
584  if(((isCycle&&cornerList.size()!=4)||((!isCycle)&&cornerList.size()>2-(size_t)C0))&&!alreadyReached)
585  cornerLists.push_back(cornerList);
586  }
587  }
588  return cornerLists;
589  }
590 
591  template<short_t d,class T>
593  {
594  /*
595  Our goal is to create three vectors c11, c12, c21 which all contain the
596  c11, c12 and c21 coefficients of the patches around the EV in the right order
597  (counter)-clockwise.
598  */
599 
600  std::vector<std::vector<patchCorner> > cornerLists = _getSpecialCornerLists(m_RefPatches);
601 
602 
603  std::fill(m_vertCheck.begin(), m_vertCheck.end(), false);
604  if (cornerLists.size()!=0)
605  {
606  m_matrix = m_matrix * m_tMatrix.transpose();
607 
608  std::vector<patchCorner> pcorners;
609  patchCorner pcorner;
610  gsMatrix<T> Cg; // coefficients
611  gsMatrix<T> ub; // baricentric coordinates
612  gsMatrix<index_t> uind; // column indices of baricentric coordinates
613  index_t cidx;
614 
615  for (std::vector<std::vector<patchCorner> >::iterator it=cornerLists.begin(); it!=cornerLists.end(); it++)
616  {
617 
618  std::vector<patchCorner> pcorners = *it;
619  pcorner = it->at(0);
620  cidx = _vertIndex(pcorner.patch,pcorner.corner());
621  if (m_vertCheck.at(cidx))
622  continue;
623 
624  std::pair<index_t,bool> vdata = _vertexData(pcorner); // corner c
625 
626  // get the triangle
627  gsMatrix<T> Cg;
628  std::tie(Cg,ub,uind) = _makeTriangle(pcorner);
629 
630  // The barycentric coordinates will be attached to the matrix rows corresponding to the 0,0 corners (and the three lowest patch index corners whenever valence > 3)
631  // We use _getLowestIndices such that the corners are assigned to increasing patch corners
632  // We need the index on the old basis (the unrefined basis), because we plug it into the mapModified (which maps the local DoFs to the global ones)
633  std::vector<std::pair<index_t,index_t>> indices, tmp;
634  if (vdata.first==2)
635  {
636  // These are two indices
637  indices = _getAllInterfaceIndices(pcorner,0,m_Bbases);
638  tmp = _getAllInterfaceIndices(pcorner,1,m_Bbases);
639  _getLowestIndices(tmp,1);
640  indices.push_back(tmp[0]);
641  }
642  else
643  {
644  indices = _getAllInterfaceIndices(pcorner,0,m_Bbases);
645  _getLowestIndices(indices,3);
646  }
647 
648 
649  std::vector<index_t> rowIndices;
650  rowIndices.reserve(3);
651  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
652  {
653  // We need the index on the old basis (the unrefined basis), because we plug it into the mapModified (which maps the local DoFs to the global ones)
654  GISMO_ASSERT(m_mapModified.is_free(it->second,it->first),"This DoF must be free! patch = "<<it->first<<"; index = "<<it->first);
655  rowIndices.push_back(m_mapModified.index(it->second,it->first));
656  }
657 
658  index_t rowIdx,colIdx;
659  // set the colums related to the barycentric columns equal to zero
660  for (index_t j=0; j!=ub.cols(); j++)
661  {
662  colIdx = uind(0,j);
663  m_matrix.prune(
664  [&colIdx](index_t i, index_t j, T)
665  { return j!=colIdx; }
666  );
667  }
668 
669  for (index_t i=0; i!=ub.rows(); i++)
670  for (index_t j=0; j!=ub.cols(); j++)
671  {
672  rowIdx = rowIndices[i];
673  colIdx = uind(0,j);
674  m_matrix(rowIdx,colIdx) = ub(i,j);
675  }
676 
677  for (size_t k = 0; k!=pcorners.size(); k++)
678  m_vertCheck[ _vertIndex(pcorners[k].patch, pcorners[k].corner()) ] = true;
679  }
680  m_matrix.makeCompressed();
681  }
682  }
683 
684  template<short_t d,class T>
685  void gsAlmostC1<d,T>::_countDoFs() // also initialize the mappers!
686  {
687  size_t tmp;
688  m_size = tmp = 0;
689 
690  // number of interior basis functions
691  for (size_t p=0; p!=m_bases.nBases(); p++)
692  {
693  tmp += m_bases.basis(p).size();
694  for (index_t k=0; k!=2; k++)
695  {
696  tmp -= m_bases.basis(p).boundaryOffset(boxSide(1),k).size();
697  tmp -= m_bases.basis(p).boundaryOffset(boxSide(2),k).size();
698  tmp -= m_bases.basis(p).boundaryOffset(boxSide(3),k).size()-4;
699  tmp -= m_bases.basis(p).boundaryOffset(boxSide(4),k).size()-4;
700  }
701  }
702  // gsDebug<<"Number of interior DoFs: "<<tmp<<"\n";
703  m_size += tmp;
704 
705  // interfaces
706  gsBasis<T> * basis1;
707  gsBasis<T> * basis2;
708  gsVector<index_t> indices1,indices2;
709  tmp = 0;
710  for(gsBoxTopology::const_iiterator iit = m_topology.iBegin(); iit!= m_topology.iEnd(); iit++)
711  {
712  basis1 = &m_bases.basis(iit->first().patch);
713  basis2 = &m_bases.basis(iit->second().patch);
714  tmp += basis1->boundary(iit->first().side()).size() - 4;
715  tmp += basis2->boundary(iit->second().side()).size() - 4;
716  }
717  // gsDebug<<"Number of interface DoFs: "<<tmp<<"\n";
718  m_size += tmp;
719  // boundaries
720  tmp = 0;
721  for(gsBoxTopology::const_biterator bit = m_topology.bBegin(); bit!= m_topology.bEnd(); bit++)
722  {
723  basis1 = &m_bases.basis(bit->patch);
724  tmp += (basis1->boundaryOffset(bit->side(),0).size() - 4);
725  tmp += (basis1->boundaryOffset(bit->side(),1).size() - 4);
726  }
727  // gsDebug<<"Number of boundary DoFs: "<<tmp<<"\n";
728  m_size += tmp;
729 
730  // add DoFs for the vertices (denoted by v) if
731  // - part of a boundary vertex with valence 1
732  // - valence >2 (interior or boundary vertex) [add 3 in total]
733 
734  // vertices (denoted by v)
735  tmp = 0;
736  std::vector<bool> passed(m_bases.nBases()*4);
737  std::fill(passed.begin(), passed.end(), false);
738 
739  std::vector<patchCorner> corners;
740  for (size_t p=0; p!=m_bases.nBases(); p++)
741  for (index_t c=1; c<5; c++)
742  {
743  index_t idx = _vertIndex(p,c);
744  if (!passed.at(idx))
745  {
746  m_topology.getCornerList(patchCorner(p,c),corners);
747  for (size_t k=0; k!=corners.size(); k++)
748  passed.at(_vertIndex(corners[k].patch,corners[k])) = true;
749 
750  std::pair<index_t,bool> vdata = _vertexData(patchCorner(p,c)); // corner c
751  bool C0 = m_C0s[idx];
752  // 1,1; 0,0; 0,1; 1,0 DoFs
753  if ((!vdata.second) && vdata.first==1) // valence = 1, must be a boundary vertex
754  tmp += 4;
755 
756  // both 1,1 DoFs + 2 for the boundary 1,0 or 0,1 DoFs
757  else if ((!vdata.second) && vdata.first==2 && !C0) // valence = 1, must be a boundary vertex
758  tmp += 4;
759 
760  // all 1,1 DoFs + 3 for the triangle + 2 for the boundary 1,0 or 0,1 DoFs
761  else if ((!vdata.second) && vdata.first>2 && !C0)
762  tmp += vdata.first+3+2;
763 
764  // all 1,1 DoFs + 0,0 DoFs + 2 for the boundary 1,0 or 0,1 DoFs + 1 for the triangle
765  else if ((!vdata.second) && vdata.first==2 && C0)
766  tmp += 2*vdata.first+2+1;
767 
768  // all 1,1 DoFs + 0,0 DoFs + 2 for the boundary 1,0 or 0,1 DoFs
769  else if ((!vdata.second) && vdata.first>2 && C0)
770  tmp += 2*vdata.first+2;
771 
772  // all 1,1 DoFs
773  else if (( vdata.second) && vdata.first==4) // valence = 1, must be a boundary vertex
774  tmp += 4;
775 
776  // all 1,1 DoFs + 3 for the triangle
777  else
778  tmp += vdata.first+3;
779  }
780  }
781 
782  // gsDebug<<"Number of vertex DoFs: "<<tmp<<"\n";
783  m_size += tmp;
784  }
785 
786  template<short_t d,class T>
788  {
789  index_t cidx = _vertIndex(pcorner.patch,pcorner.corner());
790  if (m_vertCheck.at(cidx))
791  return;
792 
793  bool C0 = m_C0s[cidx];
794  bool interior;
795  index_t valence;
796 
797  std::tie(valence,interior) = _vertexData(pcorner); // corner c
798  if (!interior && valence==1) //valence = 1
799  _computeMapperRegularCorner_v1(pcorner,valence);
800  else if (!interior && valence==2 && C0)
801  _computeMapperRegularBoundaryVertexNonSmooth_v2(pcorner,valence);
802  else if (!interior && valence==2 && !C0)
803  _computeMapperRegularBoundaryVertexSmooth_v2(pcorner,valence);
804  else if (!interior && valence >2 && C0)
805  _computeMapperIrregularBoundaryVertexNonSmooth_v(pcorner,valence);
806  else if (!interior && valence >2 && !C0)
807  _computeMapperIrregularBoundaryVertexSmooth_v(pcorner,valence);
808  else if (interior && valence==4)
809  _computeMapperInteriorVertex_v4(pcorner,valence);
810  else if (interior && (valence==3 || valence> 4) )
811  _computeMapperInteriorVertex_v(pcorner,valence);
812  else
813  GISMO_ERROR("Something went terribly wrong, interior="<<interior<<"; valence="<<valence);
814 
815  // label vertex as processed
816  m_vertCheck[ cidx ] = true;
817  }
818 
819  template<short_t d,class T>
820  void gsAlmostC1<d,T>::_computeMapperRegularBoundaryVertexNonSmooth_v2(patchCorner pcorner, index_t valence)
821  {
822  // for C0 vertices, the 1,0 and 0,1 DoFs on the interface need to be eliminated
823  // However, we need a minimum of 3 DoFs around the vertex and we keep the 0,0s anyways
824  // Using _removeLowestIndices(indices,3), only the 0,1 or 1,0 index with the highest is selected for removal
825  // The 1,0 or 0,1s on the boundary are also kept
826 
827  // The 0,0s are kept
828  // std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
829  // From the 1,0s we take the highest and eliminate it
830  std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
831 
832  _removeLowestIndices(indices1,1);
833  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
834  m_mapModified.eliminateDof(it->second,it->first);
835 
836  std::vector<patchCorner> pcorners;
837  m_topology.getCornerList(pcorner,pcorners);
838  for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
839  {
840  // mark the vertex as passed
841  m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
842  }
843  }
844 
845  // Reimplemented
846  template<short_t d,class T>
847  void gsAlmostC1<d,T>::_computeMapperIrregularBoundaryVertexSmooth_v(patchCorner pcorner, index_t valence)
848  {
849  std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
850  std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
851  std::vector<patchCorner> pcorners;
852  m_topology.getCornerList(pcorner,pcorners);
853  for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
854  {
855  // mark the vertex as passed
856  m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
857  }
858 
859  // Eliminate the 1,0 and 0,1s
860  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
861  m_mapModified.eliminateDof(it->second,it->first);
862 
863  _removeLowestIndices(indices0,3);
864  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices0.begin(); it!=indices0.end(); it++)
865  m_mapModified.eliminateDof(it->second,it->first);
866  }
867 
868  // Extra compared to bass class
869  template<short_t d,class T>
870  void gsAlmostC1<d,T>::_computeMapperInteriorVertex_v4(patchCorner pcorner, index_t valence)
871  {
872  this->_computeMapperRegularBoundaryVertexSmooth_v2(pcorner,valence);
873  }
874 
875  // Reimplemented
876  template<short_t d,class T>
877  void gsAlmostC1<d,T>::_computeMapperInteriorVertex_v(patchCorner pcorner, index_t valence)
878  {
879  std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
880  std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
881  std::vector<patchCorner> pcorners;
882  m_topology.getCornerList(pcorner,pcorners);
883  for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
884  {
885  // mark the vertex as passed
886  m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
887  }
888 
889  // Eliminate the left-over 0,0s
890  _removeLowestIndices(indices0,3);
891  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices0.begin(); it!=indices0.end(); it++)
892  m_mapModified.eliminateDof(it->second,it->first);
893  // ... and the 1,0 and 0,1s
894  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
895  m_mapModified.eliminateDof(it->second,it->first);
896  }
897 
898  template<short_t d,class T>
900  {
901  this->_handleIrregularBoundaryVertexNonSmooth(pcorner,valence);
902  }
903 
904  template<short_t d,class T>
906  {
907  std::vector<patchSide> psides;
908  std::vector<patchCorner> corners;
909  std::vector<index_t> indices;
910  sparseEntry_t entries;
911 
912  boundaryInterface iface;
913  patchSide otherSide;
914 
915  index_t colIdx, rowIdx;
916  T weight;
917 
918  pcorner.getContainingSides(d,psides);
919 
920  gsBasis<T> * basis;
921 
922  // 2. make container for the interfaces
923  std::vector<index_t> rowIndices, colIndices, patchIndices;
924 
925  // pcorner is the current corner
926  m_topology.getCornerList(pcorner,corners);
927 
928  index_t b11_p1 = _indexFromVert(1,pcorner,psides[0],1); // point 1,1 (does not matter which reference side is taken)
929  rowIdx = m_mapModified.index(b11_p1,pcorner.patch);
930  colIdx = m_mapOriginal.index(b11_p1,pcorner.patch);
931  // Influence of 1,1 to itself
932  weight = 1.;
933  entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
934 
935  for (std::vector<patchSide>::iterator side = psides.begin(); side != psides.end(); ++side)
936  {
937  if (!m_topology.isInterface(*side))
938  continue;
939 
940  GISMO_ENSURE(m_topology.getInterface(*side,iface),"Side must be an interface!");
941 
942  m_topology.getNeighbour(*side,otherSide);
943  patchCorner otherCorner = iface.mapCorner(pcorner);
944 
945  index_t b10_p1 = _indexFromVert(1,pcorner,*side,0); // index from vertex pcorners[c] along side psides[0] with offset 0.
946  index_t b10_p2 = _indexFromVert(1,otherCorner,otherSide,0); // point 0,1
947 
948  weight = 0.5;
949  colIdx = m_mapOriginal.index(b10_p1,side->patch);
950  entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
951  colIdx = m_mapOriginal.index(b10_p2,otherSide.patch);
952  entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
953  }
954 
955  // The 1,1 corners of each patch will be given 0.5 weight in the interface handling, but in addition they will receive a 1/(v+2) weight from the 0,0 DoFs on each patch
956  pcorner.getContainingSides(d,psides);
957 
958  // colIndices stores the 0,0 corners (including the 0,0s of the boundary sides)
959  for (typename std::vector<patchCorner>::iterator it = corners.begin(); it!=corners.end(); it++)
960  {
961  basis = &m_bases.basis(it->patch);
962  colIndices.push_back(basis->functionAtCorner(*it));
963  patchIndices.push_back(it->patch);
964  }
965 
966  basis = &m_bases.basis(pcorner.patch);
967  // Check if one of the adjacent interfaces is a boundary; if so, add weight 1.0 to itself and add it to the rowIndices
968  index_t idx;
969 
970  for (index_t k = 0; k!=2; k++)
971  if (!m_topology.getInterface(psides[k],iface)) // check if the side is NOT an interface
972  {
973  idx = _indexFromVert(1,pcorner,psides[k],0);
974  rowIdx = m_mapModified.index(idx,pcorner.patch); //1,0 corner (on the boundary)
975  colIdx = m_mapOriginal.index(idx,pcorner.patch); //1,0 corner (on the boundary)
976  weight = 1.0;
977  entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
978  rowIndices.push_back(rowIdx);
979  }
980 
981  GISMO_ASSERT(rowIndices.size()<2,"Size issue, the boundary vertex is adjacent to two boundaries??" << rowIndices.size());
982 
983  if (rowIndices.size()==1)
984  {
985  rowIdx = rowIndices[0];
986  for (size_t k=0; k!=colIndices.size(); k++)
987  {
988  colIdx = m_mapOriginal.index(colIndices.at(k),patchIndices.at(k));
989  weight = 1. / 2.;
990  entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
991  }
992  }
993 
994  #pragma omp critical (handle_boundary_vertex_ff)
995  {
996  _pushAndCheck(entries);
997 
998  // Furthermore, if the corner is one of the three DoFs that is preserved, we mark the 0,0;0,1;1,0 DoF as handled (should be a zero-row)
999  std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,0,m_bases);
1000  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1001  if (m_mapModified.is_free(it->second,it->first))
1002  {
1003  rowIdx = m_mapModified.index(it->second,it->first);
1004  m_basisCheck[rowIdx] = true;
1005  }
1006 
1007  m_basisCheck[rowIdx] = true;
1008  m_vertCheck[ _vertIndex(pcorner.patch, pcorner.corner()) ] = true;
1009  }
1010  }
1011 
1012  template<short_t d,class T>
1013  void gsAlmostC1<d,T>::_handleIrregularBoundaryVertexNonSmooth(patchCorner pcorner, index_t valence)
1014  {
1015  Base::_handleIrregularBoundaryVertexNonSmooth(pcorner,valence);
1016  #pragma omp critical (handle_boundary_vertex_ff)
1017  {
1018  index_t rowIdx;
1019  // Furthermore, if the corner is one of the three DoFs that is preserved, we mark the 0,0;0,1;1,0 DoF as handled (should be a zero-row)
1020  std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,1,m_bases);
1021  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1022  if (m_mapModified.is_free(it->second,it->first))
1023  {
1024  rowIdx = m_mapModified.index(it->second,it->first);
1025  m_basisCheck[rowIdx] = true;
1026  }
1027  }
1028  }
1029 
1030  template<short_t d,class T>
1032  {
1033  Base::_handleInteriorVertex(pcorner,valence);
1034  #pragma omp critical (handle_interior_vertex)
1035  {
1036  index_t rowIdx;
1037  // Furthermore, if the corner is one of the three DoFs that is preserved, we mark the 0,0;0,1;1,0 DoF as handled (should be a zero-row)
1038  std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,0,m_bases);
1039  for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1040  if (m_mapModified.is_free(it->second,it->first))
1041  {
1042  rowIdx = m_mapModified.index(it->second,it->first);
1043  m_basisCheck[rowIdx] = true;
1044  }
1045  }
1046  }
1047 
1048 
1049 } // namespace gismo
Provides definition of HTensorBasis abstract interface.
void parameters_into(index_t dim, gsVector< bool > &param) const
returns a vector of parameters describing the position of the corner
Definition: gsBoundary.h:322
Constructs the D-Patch, from which the transformation matrix can be called.
Definition: gsAlmostC1.h:33
A truncated hierarchical B-Spline function, in d dimensions.
Definition: gsTHBSpline.h:37
Provides generic assembler routines.
Struct which represents a certain side of a patch.
Definition: gsBoundary.h:231
T distance(gsMatrix< T > const &A, gsMatrix< T > const &B, index_t i=0, index_t j=0, bool cols=false)
compute a distance between the point number in the set and the point number &lt;j&gt; in the set ; by def...
Creates a mapped object or data pointer to a matrix without copying data.
Definition: gsLinearAlgebra.h:126
#define short_t
Definition: gsConfig.h:35
size_t numElements() const
Number of knot intervals inside domain.
Definition: gsKnotVector.h:268
A tensor product of d B-spline functions, with arbitrary target dimension.
Definition: gsTensorBSpline.h:44
void _countDoFs()
Initializes the matrix, the basis and the mappers.
Definition: gsAlmostC1.hpp:685
Provides declaration of THBSplineBasis class.
bool getCornerList(const patchCorner &start, std::vector< patchCorner > &cornerList) const
Definition: gsBoxTopology.cpp:187
#define index_t
Definition: gsConfig.h:32
Generic expressions helper.
#define GISMO_ENSURE(cond, message)
Definition: gsDebug.h:102
void getContainingSides(short_t dim, std::vector< patchSide > &sides) const
returns a vector of patchSides that contain this corner
Definition: gsBoundary.h:416
Provides declaration of functions writing Paraview files.
Struct which represents a certain corner of a patch.
Definition: gsBoundary.h:392
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
Maintains a mapping from patch-local dofs to global dof indices and allows the elimination of individ...
Definition: gsDofMapper.h:68
Generic expressions evaluator.
void _handleInteriorVertex(patchCorner pcorner, index_t valence)
Handles a vertex in the global matrix.
Definition: gsAlmostC1.hpp:1031
void finalize()
Must be called after all boundaries and interfaces have been marked to set up the dof numbering...
Definition: gsDofMapper.cpp:240
index_t index(index_t i, index_t k=0, index_t c=0) const
Returns the global dof index associated to local dof i of patch k.
Definition: gsDofMapper.h:325
Class representing a (scalar) hierarchical tensor basis of functions .
Definition: gsHTensorBasis.h:74
#define gsWarn
Definition: gsDebug.h:50
Holds a set of patch-wise bases and their topology information.
Definition: gsMultiBasis.h:36
gsGeometry< T > & patch(size_t i) const
Return the i-th patch.
Definition: gsMultiPatch.h:226
const gsBasis< T > & basis(const index_t k) const
Helper which casts and returns the k-th piece of this function set as a gsBasis.
Definition: gsFunctionSet.hpp:33
size_t nPatches() const
Number of patches.
Definition: gsMultiPatch.h:208
void setCoefficients(const gsMatrix< T > &coefs, gsMultiPatch< T > &mp) const
Set the coefficients of mp to coefs.
Definition: gsAlmostC1.hpp:374
void _initTHB()
Initializes the matrix, the basis and the mappers.
Definition: gsAlmostC1.hpp:406
gsMatrix< T > freeCoefficients()
Computes the C1 coefficients for pre-multiplication to make the multipatch.
Definition: gsAlmostC1.hpp:261
Constructs the D-Patch, from which the transformation matrix can be called.
Definition: gsDPatchBase.h:36
void freeAll(It begin, It end)
Frees all pointers in the range [begin end)
Definition: gsMemory.h:312
Container class for a set of geometry patches and their topology, that is, the interface connections ...
Definition: gsMultiPatch.h:33
Struct which represents a certain side of a box.
Definition: gsBoundary.h:84
gsAlmostC1()
Empty constructor.
Definition: gsAlmostC1.h:51
virtual gsMatrix< index_t > boundaryOffset(boxSide const &s, index_t offset) const
Definition: gsBasis.hpp:316
void _computeEVs()
Computes D-Patch smoothing.
Definition: gsAlmostC1.hpp:592
tensorBasis & tensorLevel(index_t i) const
Returns the tensor basis member of level i.
Definition: gsHTensorBasis.h:668
#define GISMO_ERROR(message)
Definition: gsDebug.h:118
Class for representing a knot vector.
Definition: gsKnotVector.h:79
gsMatrix< T > _preCoefficients()
Computes the C1 coefficients for pre-multiplication to make the multipatch.
Definition: gsAlmostC1.hpp:282
EIGEN_STRONG_INLINE abs_expr< E > abs(const E &u)
Absolute value.
Definition: gsExpressions.h:4486
virtual void defaultOptions()
Sets the default options.
Definition: gsDPatchBase.hpp:39
Struct which represents an interface between two patches.
Definition: gsBoundary.h:649
void transfer(const std::vector< gsSortedVector< index_t > > &old, gsSparseMatrix< T > &result)
Returns transfer matrix between the hirarchical spline given by the characteristic matrix &quot;old&quot; and t...
Definition: gsHTensorBasis.hpp:1735
void _initBasis()
Initializes the basis.
Definition: gsAlmostC1.hpp:400
size_t uSize() const
Number of unique knots (i.e., without repetitions).
Definition: gsKnotVector.h:245
void _makeTHB()
Prepares the THB basis if needed.
Definition: gsAlmostC1.hpp:504
index_t patch
The index of the patch.
Definition: gsBoundary.h:234
void _handleRegularBoundaryVertexNonSmooth(patchCorner pcorner, index_t valence)
Handles an interface in the global matrix.
Definition: gsAlmostC1.hpp:899
index_t m_index
Index of the corner.
Definition: gsBoundary.h:298
Struct which represents a certain corner of a hyper-cube.
Definition: gsBoundary.h:291
gsMatrix< index_t > boundary(boxSide const &s) const
Returns the indices of the basis functions that are nonzero at the domain boundary as single-column-m...
Definition: gsBasis.h:520
A basis represents a family of scalar basis functions defined over a common parameter domain...
Definition: gsBasis.h:78