G+Smo  25.01.0
Geometry + Simulation Modules
 
Loading...
Searching...
No Matches
gsAlmostC1.hpp
Go to the documentation of this file.
1
17
21
22namespace 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
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
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, 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 GISMO_UNUSED(valence);
823 // for C0 vertices, the 1,0 and 0,1 DoFs on the interface need to be eliminated
824 // However, we need a minimum of 3 DoFs around the vertex and we keep the 0,0s anyways
825 // Using _removeLowestIndices(indices,3), only the 0,1 or 1,0 index with the highest is selected for removal
826 // The 1,0 or 0,1s on the boundary are also kept
827
828 // The 0,0s are kept
829 // std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
830 // From the 1,0s we take the highest and eliminate it
831 std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
832
833 _removeLowestIndices(indices1,1);
834 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
835 m_mapModified.eliminateDof(it->second,it->first);
836
837 std::vector<patchCorner> pcorners;
838 m_topology.getCornerList(pcorner,pcorners);
839 for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
840 {
841 // mark the vertex as passed
842 m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
843 }
844 }
845
846 // Reimplemented
847 template<short_t d,class T>
848 void gsAlmostC1<d,T>::_computeMapperIrregularBoundaryVertexSmooth_v(patchCorner pcorner, index_t valence)
849 {
850 GISMO_UNUSED(valence);
851 std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
852 std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
853 std::vector<patchCorner> pcorners;
854 m_topology.getCornerList(pcorner,pcorners);
855 for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
856 {
857 // mark the vertex as passed
858 m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
859 }
860
861 // Eliminate the 1,0 and 0,1s
862 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
863 m_mapModified.eliminateDof(it->second,it->first);
864
865 _removeLowestIndices(indices0,3);
866 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices0.begin(); it!=indices0.end(); it++)
867 m_mapModified.eliminateDof(it->second,it->first);
868 }
869
870 // Extra compared to bass class
871 template<short_t d,class T>
872 void gsAlmostC1<d,T>::_computeMapperInteriorVertex_v4(patchCorner pcorner, index_t valence)
873 {
874 this->_computeMapperRegularBoundaryVertexSmooth_v2(pcorner,valence);
875 }
876
877 // Reimplemented
878 template<short_t d,class T>
879 void gsAlmostC1<d,T>::_computeMapperInteriorVertex_v(patchCorner pcorner, index_t valence)
880 {
881 GISMO_UNUSED(valence);
882 std::vector<std::pair<index_t,index_t>> indices0 = _getAllInterfaceIndices(pcorner,0,m_bases);
883 std::vector<std::pair<index_t,index_t>> indices1 = _getAllInterfaceIndices(pcorner,1,m_bases);
884 std::vector<patchCorner> pcorners;
885 m_topology.getCornerList(pcorner,pcorners);
886 for (std::vector<patchCorner>::iterator it=pcorners.begin(); it!=pcorners.end(); it++)
887 {
888 // mark the vertex as passed
889 m_vertCheck[ _vertIndex(it->patch, it->corner()) ] = true;
890 }
891
892 // Eliminate the left-over 0,0s
893 _removeLowestIndices(indices0,3);
894 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices0.begin(); it!=indices0.end(); it++)
895 m_mapModified.eliminateDof(it->second,it->first);
896 // ... and the 1,0 and 0,1s
897 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices1.begin(); it!=indices1.end(); it++)
898 m_mapModified.eliminateDof(it->second,it->first);
899 }
900
901 template<short_t d,class T>
903 {
904 this->_handleIrregularBoundaryVertexNonSmooth(pcorner,valence);
905 }
906
907 template<short_t d,class T>
909 {
910 GISMO_UNUSED(valence);
911 std::vector<patchSide> psides;
912 std::vector<patchCorner> corners;
913 std::vector<index_t> indices;
914 sparseEntry_t entries;
915
916 boundaryInterface iface;
917 patchSide otherSide;
918
919 index_t colIdx, rowIdx;
920 T weight;
921
922 pcorner.getContainingSides(d,psides);
923
924 gsBasis<T> * basis;
925
926 // 2. make container for the interfaces
927 std::vector<index_t> rowIndices, colIndices, patchIndices;
928
929 // pcorner is the current corner
930 m_topology.getCornerList(pcorner,corners);
931
932 index_t b11_p1 = _indexFromVert(1,pcorner,psides[0],1); // point 1,1 (does not matter which reference side is taken)
933 rowIdx = m_mapModified.index(b11_p1,pcorner.patch);
934 colIdx = m_mapOriginal.index(b11_p1,pcorner.patch);
935 // Influence of 1,1 to itself
936 weight = 1.;
937 entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
938
939 for (std::vector<patchSide>::iterator side = psides.begin(); side != psides.end(); ++side)
940 {
941 if (!m_topology.isInterface(*side))
942 continue;
943
944 GISMO_ENSURE(m_topology.getInterface(*side,iface),"Side must be an interface!");
945
946 m_topology.getNeighbour(*side,otherSide);
947 patchCorner otherCorner = iface.mapCorner(pcorner);
948
949 index_t b10_p1 = _indexFromVert(1,pcorner,*side,0); // index from vertex pcorners[c] along side psides[0] with offset 0.
950 index_t b10_p2 = _indexFromVert(1,otherCorner,otherSide,0); // point 0,1
951
952 weight = 0.5;
953 colIdx = m_mapOriginal.index(b10_p1,side->patch);
954 entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
955 colIdx = m_mapOriginal.index(b10_p2,otherSide.patch);
956 entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
957 }
958
959 // 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
960 pcorner.getContainingSides(d,psides);
961
962 // colIndices stores the 0,0 corners (including the 0,0s of the boundary sides)
963 for (typename std::vector<patchCorner>::iterator it = corners.begin(); it!=corners.end(); it++)
964 {
965 basis = &m_bases.basis(it->patch);
966 colIndices.push_back(basis->functionAtCorner(*it));
967 patchIndices.push_back(it->patch);
968 }
969
970 basis = &m_bases.basis(pcorner.patch);
971 // Check if one of the adjacent interfaces is a boundary; if so, add weight 1.0 to itself and add it to the rowIndices
972 index_t idx;
973
974 for (index_t k = 0; k!=2; k++)
975 if (!m_topology.getInterface(psides[k],iface)) // check if the side is NOT an interface
976 {
977 idx = _indexFromVert(1,pcorner,psides[k],0);
978 rowIdx = m_mapModified.index(idx,pcorner.patch); //1,0 corner (on the boundary)
979 colIdx = m_mapOriginal.index(idx,pcorner.patch); //1,0 corner (on the boundary)
980 weight = 1.0;
981 entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
982 rowIndices.push_back(rowIdx);
983 }
984
985 GISMO_ASSERT(rowIndices.size()<2,"Size issue, the boundary vertex is adjacent to two boundaries??" << rowIndices.size());
986
987 if (rowIndices.size()==1)
988 {
989 rowIdx = rowIndices[0];
990 for (size_t k=0; k!=colIndices.size(); k++)
991 {
992 colIdx = m_mapOriginal.index(colIndices.at(k),patchIndices.at(k));
993 weight = 1. / 2.;
994 entries.push_back(std::make_tuple(rowIdx,colIdx,weight));
995 }
996 }
997
998 #pragma omp critical (handle_boundary_vertex_ff)
999 {
1000 _pushAndCheck(entries);
1001
1002 // 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)
1003 std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,0,m_bases);
1004 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1005 if (m_mapModified.is_free(it->second,it->first))
1006 {
1007 rowIdx = m_mapModified.index(it->second,it->first);
1008 m_basisCheck[rowIdx] = true;
1009 }
1010
1011 m_basisCheck[rowIdx] = true;
1012 m_vertCheck[ _vertIndex(pcorner.patch, pcorner.corner()) ] = true;
1013 }
1014 }
1015
1016 template<short_t d,class T>
1017 void gsAlmostC1<d,T>::_handleIrregularBoundaryVertexNonSmooth(patchCorner pcorner, index_t valence)
1018 {
1019 Base::_handleIrregularBoundaryVertexNonSmooth(pcorner,valence);
1020 #pragma omp critical (handle_boundary_vertex_ff)
1021 {
1022 index_t rowIdx;
1023 // 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)
1024 std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,1,m_bases);
1025 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1026 if (m_mapModified.is_free(it->second,it->first))
1027 {
1028 rowIdx = m_mapModified.index(it->second,it->first);
1029 m_basisCheck[rowIdx] = true;
1030 }
1031 }
1032 }
1033
1034 template<short_t d,class T>
1036 {
1037 Base::_handleInteriorVertex(pcorner,valence);
1038 #pragma omp critical (handle_interior_vertex)
1039 {
1040 index_t rowIdx;
1041 // 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)
1042 std::vector<std::pair<index_t,index_t>> indices = this->_getInterfaceIndices(pcorner,0,m_bases);
1043 for (std::vector<std::pair<index_t,index_t>>::iterator it=indices.begin(); it!=indices.end(); it++)
1044 if (m_mapModified.is_free(it->second,it->first))
1045 {
1046 rowIdx = m_mapModified.index(it->second,it->first);
1047 m_basisCheck[rowIdx] = true;
1048 }
1049 }
1050 }
1051
1052
1053} // namespace gismo
Struct which represents a certain side of a box.
Definition gsBoundary.h:85
Constructs the D-Patch, from which the transformation matrix can be called.
Definition gsAlmostC1.h:34
void _makeTHB()
Prepares the THB basis if needed.
Definition gsAlmostC1.hpp:504
virtual void defaultOptions()
Sets the default options.
Definition gsDPatchBase.hpp:39
gsMatrix< T > freeCoefficients()
Computes the C1 coefficients for pre-multiplication to make the multipatch.
Definition gsAlmostC1.hpp:261
void _initTHB()
Initializes the matrix, the basis and the mappers.
Definition gsAlmostC1.hpp:406
void _handleRegularBoundaryVertexNonSmooth(patchCorner pcorner, index_t valence)
Handles an interface in the global matrix.
Definition gsAlmostC1.hpp:902
void _countDoFs()
Initializes the matrix, the basis and the mappers.
Definition gsAlmostC1.hpp:685
void _initBasis()
Initializes the basis.
Definition gsAlmostC1.hpp:400
gsMatrix< T > _preCoefficients()
Computes the C1 coefficients for pre-multiplication to make the multipatch.
Definition gsAlmostC1.hpp:282
void _computeEVs()
Computes D-Patch smoothing.
Definition gsAlmostC1.hpp:592
gsAlmostC1()
Empty constructor.
Definition gsAlmostC1.h:51
void _handleInteriorVertex(patchCorner pcorner, index_t valence)
Handles a vertex in the global matrix.
Definition gsAlmostC1.hpp:1035
void setCoefficients(const gsMatrix< T > &coefs, gsMultiPatch< T > &mp) const
Set the coefficients of mp to coefs.
Definition gsAlmostC1.hpp:374
Creates a mapped object or data pointer to a matrix without copying data.
Definition gsAsMatrix.h:32
A basis represents a family of scalar basis functions defined over a common parameter domain.
Definition gsBasis.h:79
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
virtual gsMatrix< index_t > boundaryOffset(boxSide const &s, index_t offset) const
Definition gsBasis.hpp:339
bool getCornerList(const patchCorner &start, std::vector< patchCorner > &cornerList) const
Definition gsBoxTopology.cpp:187
Constructs the D-Patch, from which the transformation matrix can be called.
Definition gsDPatchBase.h:37
Maintains a mapping from patch-local dofs to global dof indices and allows the elimination of individ...
Definition gsDofMapper.h:69
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
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
Class representing a (scalar) hierarchical tensor basis of functions .
Definition gsHTensorBasis.h:75
Class for representing a knot vector.
Definition gsKnotVector.h:80
size_t uSize() const
Number of unique knots (i.e., without repetitions).
Definition gsKnotVector.h:245
size_t numElements() const
Number of knot intervals inside domain.
Definition gsKnotVector.h:268
A matrix with arbitrary coefficient type and fixed or dynamic size.
Definition gsMatrix.h:41
Holds a set of patch-wise bases and their topology information.
Definition gsMultiBasis.h:37
Container class for a set of geometry patches and their topology, that is, the interface connections ...
Definition gsMultiPatch.h:100
size_t nPatches() const
Number of patches.
Definition gsMultiPatch.h:274
Iterator over the non-zero entries of a sparse matrix.
Definition gsSparseMatrix.h:74
Sparse matrix class, based on gsEigen::SparseMatrix.
Definition gsSparseMatrix.h:139
A truncated hierarchical B-Spline function, in d dimensions.
Definition gsTHBSpline.h:38
A tensor product of d B-spline functions, with arbitrary target dimension.
Definition gsTensorBSpline.h:45
A vector with arbitrary coefficient type and fixed or dynamic size.
Definition gsVector.h:37
T at(index_t i) const
Returns the i-th element of the vector.
Definition gsVector.h:177
Provides generic assembler routines.
#define short_t
Definition gsConfig.h:35
#define index_t
Definition gsConfig.h:32
#define GISMO_ERROR(message)
Definition gsDebug.h:118
#define gsWarn
Definition gsDebug.h:50
#define GISMO_UNUSED(x)
Definition gsDebug.h:112
#define GISMO_ENSURE(cond, message)
Definition gsDebug.h:102
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
Generic expressions evaluator.
Generic expressions helper.
Provides definition of HTensorBasis abstract interface.
Provides declaration of THBSplineBasis class.
Provides declaration of functions writing Paraview files.
The G+Smo namespace, containing all definitions for the library.
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 <j> in the set ; by def...
void freeAll(It begin, It end)
Frees all pointers in the range [begin end)
Definition gsMemory.h:312
Struct which represents an interface between two patches.
Definition gsBoundary.h:650
Struct which represents a certain corner of a hyper-cube.
Definition gsBoundary.h:292
index_t m_index
Index of the corner.
Definition gsBoundary.h:298
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
Struct which represents a certain corner of a patch.
Definition gsBoundary.h:393
void getContainingSides(short_t dim, std::vector< patchSide > &sides) const
returns a vector of patchSides that contain this corner
Definition gsBoundary.h:416
Struct which represents a certain side of a patch.
Definition gsBoundary.h:232
index_t patch
The index of the patch.
Definition gsBoundary.h:234