1 // MeshOptimizer - Copyright (C) 2013-2019 UCLouvain-ULiege
2 //
3 // Permission is hereby granted, free of charge, to any person
4 // obtaining a copy of this software and associated documentation
5 // files (the "Software"), to deal in the Software without
6 // restriction, including without limitation the rights to use, copy,
7 // modify, merge, publish, distribute, and/or sell copies of the
8 // Software, and to permit persons to whom the Software is furnished
9 // to do so, provided that the above copyright notice(s) and this
10 // permission notice appear in all copies of the Software and that
11 // both the above copyright notice(s) and this permission notice
12 // appear in supporting documentation.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE
18 // COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR
19 // ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
20 // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22 // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 // OF THIS SOFTWARE.
24 //
25 // Contributors: Thomas Toulorge, Jonathan Lambrechts
26 
27 #include "GmshMessage.h"
28 #include "GFace.h"
29 #include "BasisFactory.h"
30 #include "CondNumBasis.h"
31 #include "IntegralBoundaryDist.h"
32 #include "CADDistances.h"
33 #include "qualityMeasures.h"
34 #include "Patch.h"
35 #include "bezierBasis.h"
36 #include "nodalBasis.h"
37 
Patch(const std::map<MElement *,GEntity * > & element2entity,const std::map<MElement *,GEntity * > & bndEl2Ent,const std::set<MElement * > & els,std::set<MVertex * > & toFix,const std::set<MElement * > & bndEls,bool fixBndNodes)38 Patch::Patch(const std::map<MElement *, GEntity *> &element2entity,
39              const std::map<MElement *, GEntity *> &bndEl2Ent,
40              const std::set<MElement *> &els, std::set<MVertex *> &toFix,
41              const std::set<MElement *> &bndEls, bool fixBndNodes)
42   : _typeLengthScale(LS_NONE), _invLengthScaleSq(0.)
43 {
44   _dim = (*els.begin())->getDim();
45 
46   // Initialize elements, vertices, free vertices and element->vertices
47   // connectivity
48   const int nElements = els.size();
49   _nPC = 0;
50   _el.resize(nElements);
51   _el2FV.resize(nElements);
52   _el2V.resize(nElements);
53   _nNodEl.resize(nElements);
54   _indPCEl.resize(nElements);
55   if(!element2entity.empty()) _gEnt.resize(nElements);
56   int iEl = 0;
57   bool nonGeoMove = false;
58   for(auto it = els.begin(); it != els.end(); ++it, ++iEl) {
59     _el[iEl] = *it;
60     if(!element2entity.empty()) {
61       auto itEl2Ent = element2entity.find(*it);
62       _gEnt[iEl] = (itEl2Ent == element2entity.end()) ? 0 : itEl2Ent->second;
63     }
64     _nNodEl[iEl] = _el[iEl]->getNumVertices();
65     for(int iVEl = 0; iVEl < _nNodEl[iEl]; iVEl++) {
66       MVertex *vert = _el[iEl]->getVertex(iVEl);
67       GEntity *ge = vert->onWhat();
68       const int vDim = ge->dim();
69       const bool hasParam = ge->haveParametrization();
70       int iV = addVert(vert);
71       _el2V[iEl].push_back(iV);
72       if((vDim > 0) && (toFix.find(vert) == toFix.end()) &&
73          (!fixBndNodes || vDim == _dim)) { // Free vertex?
74         VertexCoord *coord;
75         if(vDim == 3)
76           coord = new VertexCoordPhys3D();
77         else if(hasParam)
78           coord = new VertexCoordParent(vert);
79         else {
80           if(vDim == 2)
81             coord = new VertexCoordLocalSurf(vert);
82           else
83             coord = new VertexCoordLocalLine(vert);
84           nonGeoMove = true;
85         }
86         int iFV = addFreeVert(vert, iV, vDim, coord, toFix);
87         _el2FV[iEl].push_back(iFV);
88         for(int i = _startPCFV[iFV]; i < _startPCFV[iFV] + vDim; i++)
89           _indPCEl[iEl].push_back(i);
90       }
91       else
92         _el2FV[iEl].push_back(-1);
93     }
94   }
95 
96   if(nonGeoMove)
97     Msg::Warning("Some nodes will be moved along local lines "
98                  "or planes, they may not remain on the exact geometry");
99 
100   // Initialize boundary elements and related connectivity if required
101   if(!bndEls.empty()) {
102     int nBndElts = bndEls.size();
103     _bndEl.resize(nBndElts);
104     _bndEl2Ent.resize(nBndElts);
105     _bndEl2V.resize(nBndElts);
106     _bndEl2FV.resize(nBndElts);
107     int iBndEl = 0;
108     bool unknownVert = false;
109     for(auto it = bndEls.begin(); it != bndEls.end(); ++it, ++iBndEl) {
110       MElement *bndEl = *it;
111       _bndEl[iBndEl] = bndEl;
112       auto itBndEl2Ent = bndEl2Ent.find(bndEl);
113       _bndEl2Ent[iBndEl] =
114         (itBndEl2Ent == bndEl2Ent.end()) ? 0 : itBndEl2Ent->second;
115       int nBndElVerts = bndEl->getNumVertices();
116       _bndEl2V[iBndEl].resize(nBndElVerts);
117       _bndEl2FV[iBndEl].resize(nBndElVerts);
118       for(int iVBndEl = 0; iVBndEl < nBndElVerts; iVBndEl++) {
119         MVertex *vert = bndEl->getVertex(iVBndEl);
120         auto itV = std::find(_vert.begin(), _vert.end(), vert);
121         if(itV == _vert.end())
122           unknownVert = true;
123         else
124           _bndEl2V[iBndEl][iVBndEl] = std::distance(_vert.begin(), itV);
125         auto itFV = std::find(_freeVert.begin(), _freeVert.end(), vert);
126         if(itFV == _freeVert.end())
127           _bndEl2FV[iBndEl][iVBndEl] = -1;
128         else
129           _bndEl2FV[iBndEl][iVBndEl] = std::distance(_freeVert.begin(), itFV);
130       }
131     }
132     if(unknownVert)
133       Msg::Error("Unknown nodes in boundary element at patch initialization");
134   }
135 
136   // Initial coordinates
137   _ixyz.resize(nVert());
138   for(int iV = 0; iV < nVert(); iV++) _ixyz[iV] = _vert[iV]->point();
139   _iuvw.resize(nFV());
140   for(int iFV = 0; iFV < nFV(); iFV++)
141     _iuvw[iFV] = _coordFV[iFV]->getUvw(_freeVert[iFV]);
142 
143   // Set current coordinates
144   _xyz = _ixyz;
145   _uvw = _iuvw;
146 }
147 
addVert(MVertex * vert)148 int Patch::addVert(MVertex *vert)
149 {
150   auto itVert = find(_vert.begin(), _vert.end(), vert);
151   if(itVert == _vert.end()) {
152     _vert.push_back(vert);
153     return _vert.size() - 1;
154   }
155   else
156     return std::distance(_vert.begin(), itVert);
157 }
158 
addFreeVert(MVertex * vert,const int iV,const int nPCV,VertexCoord * param,std::set<MVertex * > & toFix)159 int Patch::addFreeVert(MVertex *vert, const int iV, const int nPCV,
160                        VertexCoord *param, std::set<MVertex *> &toFix)
161 {
162   auto itVert = find(_freeVert.begin(), _freeVert.end(), vert);
163   if(itVert == _freeVert.end()) {
164     const int iStart =
165       (_startPCFV.size() == 0) ? 0 : _startPCFV.back() + _nPCFV.back();
166     const bool forcedV =
167       (vert->onWhat()->dim() < 2) || (toFix.find(vert) != toFix.end());
168     _freeVert.push_back(vert);
169     _coordFV.push_back(param);
170     _fv2V.push_back(iV);
171     _startPCFV.push_back(iStart);
172     _nPCFV.push_back(nPCV);
173     _nPC += nPCV;
174     return _freeVert.size() - 1;
175   }
176   else
177     return std::distance(_freeVert.begin(), itVert);
178 }
179 
getUvw(double * it)180 void Patch::getUvw(double *it)
181 {
182   for(int iFV = 0; iFV < nFV(); iFV++) {
183     SPoint3 &uvwV = _uvw[iFV];
184     *it = uvwV[0];
185     it++;
186     if(_nPCFV[iFV] >= 2) {
187       *it = uvwV[1];
188       it++;
189     }
190     if(_nPCFV[iFV] == 3) {
191       *it = uvwV[2];
192       it++;
193     }
194   }
195 }
196 
updateMesh(const double * it)197 void Patch::updateMesh(const double *it)
198 {
199   for(int iFV = 0; iFV < nFV(); iFV++) {
200     int iV = _fv2V[iFV];
201     SPoint3 &uvwV = _uvw[iFV];
202     uvwV[0] = *it;
203     it++;
204     if(_nPCFV[iFV] >= 2) {
205       uvwV[1] = *it;
206       it++;
207     }
208     if(_nPCFV[iFV] == 3) {
209       uvwV[2] = *it;
210       it++;
211     }
212     _xyz[iV] = _coordFV[iFV]->uvw2Xyz(uvwV);
213   }
214 }
215 
updateGEntityPositions()216 void Patch::updateGEntityPositions()
217 {
218   for(int iV = 0; iV < nVert(); iV++)
219     _vert[iV]->setXYZ(_xyz[iV].x(), _xyz[iV].y(), _xyz[iV].z());
220   for(int iFV = 0; iFV < nFV(); iFV++)
221     _coordFV[iFV]->exportVertexCoord(_uvw[iFV]);
222 }
223 
pcScale(int iFV,std::vector<double> & scale)224 void Patch::pcScale(int iFV, std::vector<double> &scale)
225 {
226   // Calc. derivative of x, y & z w.r.t. parametric coordinates
227   const SPoint3 dX(1., 0., 0.), dY(0., 1., 0.), dZ(0., 0., 1.);
228   SPoint3 gX, gY, gZ;
229   _coordFV[iFV]->gXyz2gUvw(_uvw[iFV], dX, gX);
230   _coordFV[iFV]->gXyz2gUvw(_uvw[iFV], dY, gY);
231   _coordFV[iFV]->gXyz2gUvw(_uvw[iFV], dZ, gZ);
232 
233   // Scale = inverse norm. of vector (dx/du, dy/du, dz/du)
234   scale[0] = 1. / sqrt(gX[0] * gX[0] + gY[0] * gY[0] + gZ[0] * gZ[0]);
235   if(_nPCFV[iFV] >= 2)
236     scale[1] = 1. / sqrt(gX[1] * gX[1] + gY[1] * gY[1] + gZ[1] * gZ[1]);
237   if(_nPCFV[iFV] == 3)
238     scale[2] = 1. / sqrt(gX[2] * gX[2] + gY[2] * gY[2] + gZ[2] * gZ[2]);
239 }
240 
writeMSH(const char * filename)241 void Patch::writeMSH(const char *filename)
242 {
243   FILE *f = fopen(filename, "w");
244 
245   fprintf(f, "$MeshFormat\n");
246   fprintf(f, "2.2 0 8\n");
247   fprintf(f, "$EndMeshFormat\n");
248 
249   fprintf(f, "$Nodes\n");
250   fprintf(f, "%d\n", nVert());
251   for(int i = 0; i < nVert(); i++)
252     fprintf(f, "%d %22.15E %22.15E %22.15E\n", i + 1, _xyz[i].x(), _xyz[i].y(),
253             _xyz[i].z());
254   fprintf(f, "$EndNodes\n");
255 
256   fprintf(f, "$Elements\n");
257   fprintf(f, "%d\n", nEl());
258   for(int iEl = 0; iEl < nEl(); iEl++) {
259     fprintf(f, "%d %d 2 0 0", _el[iEl]->getNum(), _el[iEl]->getTypeForMSH());
260     for(size_t iVEl = 0; iVEl < _el2V[iEl].size(); iVEl++)
261       fprintf(f, " %d", _el2V[iEl][iVEl] + 1);
262     fprintf(f, "\n");
263   }
264   fprintf(f, "$EndElements\n");
265 
266   fclose(f);
267 }
268 
initScaledNodeDispSq(LengthScaling scaling)269 void Patch::initScaledNodeDispSq(LengthScaling scaling)
270 {
271   if((_invLengthScaleSq == 0.) || _typeLengthScale != scaling) {
272     _typeLengthScale = scaling;
273     double maxDSq = 0.;
274     switch(scaling) {
275     case LS_MAXNODEDIST: {
276       for(int iEl = 0; iEl < nEl(); iEl++) {
277         const double d = _el[iEl]->maxDistToStraight(), dd = d * d;
278         if(dd > maxDSq) maxDSq = dd;
279       }
280       break;
281     }
282     case LS_MAXOUTERRADIUS: {
283       for(int iEl = 0; iEl < nEl(); iEl++) {
284         const double d = _el[iEl]->getOuterRadius(), dd = d * d;
285         if(dd > maxDSq) maxDSq = dd;
286       }
287       break;
288     }
289     case LS_MINEDGELENGTH: {
290       for(int iEl = 0; iEl < nEl(); iEl++) {
291         const double d = _el[iEl]->minEdge(), dd = d * d;
292         if(dd > maxDSq) maxDSq = dd;
293       }
294       break;
295     }
296     }
297     _invLengthScaleSq = 1. / maxDSq;
298   }
299 }
300 
scaledNodeDispSq(int iFV)301 double Patch::scaledNodeDispSq(int iFV)
302 {
303   const int &iV = _fv2V[iFV];
304   const SPoint3 d = _xyz[iV] - _ixyz[iV];
305   return (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) * _invLengthScaleSq;
306 }
307 
gradScaledNodeDispSq(int iFV,std::vector<double> & gDSq)308 void Patch::gradScaledNodeDispSq(int iFV, std::vector<double> &gDSq)
309 {
310   const int &iV = _fv2V[iFV];
311   const SPoint3 gXyz = (_xyz[iV] - _ixyz[iV]) * 2. * _invLengthScaleSq;
312   SPoint3 gUvw;
313   gXyz2gUvw(iFV, gXyz, gUvw);
314 
315   gDSq[0] = gUvw[0];
316   if(_nPCFV[iFV] >= 2) gDSq[1] = gUvw[1];
317   if(_nPCFV[iFV] == 3) gDSq[2] = gUvw[2];
318 }
319 
initScaledJac()320 void Patch::initScaledJac()
321 {
322   // Initialize _nBezEl
323   if(_nBezEl.empty()) {
324     _nBezEl.resize(nEl());
325     for(int iEl = 0; iEl < nEl(); iEl++)
326       _nBezEl[iEl] = _el[iEl]->getJacobianFuncSpace()->getNumSamplingPnts();
327   }
328 
329   // Set normals to 2D elements (with magnitude of inverse Jacobian) or initial
330   // Jacobians of 3D elements
331   if((_dim == 2) && _jacNormEl.empty()) {
332     _jacNormEl.resize(nEl());
333     for(int iEl = 0; iEl < nEl(); iEl++)
334       calcNormalEl2D(iEl, NS_INVNORM, _jacNormEl[iEl], false);
335   }
336   else if(_invStraightJac.empty()) {
337     _invStraightJac.resize(nEl(), 1.);
338     double dumJac[3][3];
339     for(int iEl = 0; iEl < nEl(); iEl++)
340       _invStraightJac[iEl] =
341         1. / fabs(_el[iEl]->getPrimaryJacobian(0., 0., 0., dumJac));
342   }
343 }
344 
initMetricMin()345 void Patch::initMetricMin()
346 {
347   // Initialize _nBezEl
348   if(_nBezEl.empty()) {
349     _nBezEl.resize(nEl());
350     for(int iEl = 0; iEl < nEl(); iEl++)
351       _nBezEl[iEl] = _el[iEl]->getJacobianFuncSpace()->getNumSamplingPnts();
352   }
353 }
354 
initScaledCADDistSq(double refCADDist)355 void Patch::initScaledCADDistSq(double refCADDist)
356 {
357   _invRefCADDistSq = 1. / (refCADDist * refCADDist);
358 }
359 
calcNormalEl2D(int iEl,NormalScaling scaling,fullMatrix<double> & elNorm,bool ideal)360 void Patch::calcNormalEl2D(int iEl, NormalScaling scaling,
361                            fullMatrix<double> &elNorm, bool ideal)
362 {
363   const JacobianBasis *jac = _el[iEl]->getJacobianFuncSpace();
364 
365   fullMatrix<double> primNodesXYZ(jac->getNumPrimMapNodes(), 3);
366   SVector3 geoNorm(0., 0., 0.);
367   GEntity *ge = (_gEnt.empty()) ? nullptr : _gEnt[iEl];
368   const bool hasGeoNorm = ge && (ge->dim() == 2) && ge->haveParametrization();
369   for(int i = 0; i < jac->getNumPrimMapNodes(); i++) {
370     const int &iV = _el2V[iEl][i];
371     primNodesXYZ(i, 0) = _xyz[iV].x();
372     primNodesXYZ(i, 1) = _xyz[iV].y();
373     primNodesXYZ(i, 2) = _xyz[iV].z();
374     if(hasGeoNorm && (_vert[iV]->onWhat() == ge)) {
375       double u, v;
376       _vert[iV]->getParameter(0, u);
377       _vert[iV]->getParameter(1, v);
378       geoNorm += ((GFace *)ge)->normal(SPoint2(u, v));
379     }
380   }
381   if(hasGeoNorm && (geoNorm.normSq() == 0.)) {
382     SPoint2 param =
383       ((GFace *)ge)->parFromPoint(_el[iEl]->barycenter(true), false);
384     geoNorm = ((GFace *)ge)->normal(param);
385   }
386 
387   elNorm.resize(1, 3);
388   const double norm = jac->getPrimNormal2D(primNodesXYZ, elNorm, ideal);
389   double factor;
390   switch(scaling) {
391   case NS_UNIT: factor = 1.; break;
392   case NS_INVNORM: factor = 1. / norm; break;
393   case NS_SQRTNORM: factor = sqrt(norm); break;
394   }
395   if(hasGeoNorm) {
396     const double scal = geoNorm(0) * elNorm(0, 0) + geoNorm(1) * elNorm(0, 1) +
397                         geoNorm(2) * elNorm(0, 2);
398     if(scal < 0.) factor = -factor;
399   }
400   elNorm.scale(factor); // Re-scaling normal here is faster than an extra
401                         // scaling operation on the Jacobian
402 }
403 
scaledJacAndGradients(int iEl,std::vector<double> & sJ,std::vector<double> & gSJ)404 void Patch::scaledJacAndGradients(int iEl, std::vector<double> &sJ,
405                                   std::vector<double> &gSJ)
406 {
407   const JacobianBasis *jacBasis = _el[iEl]->getJacobianFuncSpace();
408   const int &numSamplingPnts = _nBezEl[iEl];
409   const int &numMapNodes = _nNodEl[iEl];
410   fullMatrix<double> JDJ(numSamplingPnts, 3 * numMapNodes + 1);
411 
412   // Coordinates of nodes
413   fullMatrix<double> nodesXYZ(numMapNodes, 3), normals(_dim, 3);
414   for(int i = 0; i < numMapNodes; i++) {
415     int &iVi = _el2V[iEl][i];
416     nodesXYZ(i, 0) = _xyz[iVi].x();
417     nodesXYZ(i, 1) = _xyz[iVi].y();
418     nodesXYZ(i, 2) = _xyz[iVi].z();
419   }
420 
421   // Calculate Jacobian and gradients, scale if 3D (already scaled by
422   // regularization normals in 2D)
423   jacBasis->getSignedJacAndGradients(nodesXYZ, _jacNormEl[iEl], JDJ);
424   if(_dim == 3) JDJ.scale(_invStraightJac[iEl]);
425 
426   // Transform Jacobian and gradients from Lagrangian to Bezier basis
427   bezierCoeff BDB(jacBasis->getFuncSpaceData(), JDJ);
428 
429   // Scaled jacobian
430   for(int l = 0; l < numSamplingPnts; l++) sJ[l] = BDB(l, 3 * numMapNodes);
431 
432   // Gradients of the scaled jacobian
433   int iPC = 0;
434   std::vector<SPoint3> gXyzV(numSamplingPnts);
435   std::vector<SPoint3> gUvwV(numSamplingPnts);
436   for(int i = 0; i < numMapNodes; i++) {
437     int &iFVi = _el2FV[iEl][i];
438     if(iFVi >= 0) {
439       for(int l = 0; l < numSamplingPnts; l++)
440         gXyzV[l] =
441           SPoint3(BDB(l, i + 0 * numMapNodes), BDB(l, i + 1 * numMapNodes),
442                   BDB(l, i + 2 * numMapNodes));
443       _coordFV[iFVi]->gXyz2gUvw(_uvw[iFVi], gXyzV, gUvwV);
444       for(int l = 0; l < numSamplingPnts; l++) {
445         gSJ[indGSJ(iEl, l, iPC)] = gUvwV[l][0];
446         if(_nPCFV[iFVi] >= 2) gSJ[indGSJ(iEl, l, iPC + 1)] = gUvwV[l][1];
447         if(_nPCFV[iFVi] == 3) gSJ[indGSJ(iEl, l, iPC + 2)] = gUvwV[l][2];
448       }
449       iPC += _nPCFV[iFVi];
450     }
451   }
452 }
453 
metricMinAndGradients(int iEl,std::vector<double> & lambda,std::vector<double> & gradLambda)454 void Patch::metricMinAndGradients(int iEl, std::vector<double> &lambda,
455                                   std::vector<double> &gradLambda)
456 {
457   const JacobianBasis *jacBasis = _el[iEl]->getJacobianFuncSpace();
458   const int &numSamplingPnts = jacBasis->getNumSamplingPnts();
459   const int &numMapNodes = jacBasis->getNumMapNodes();
460   const int &numPrimMapNodes = jacBasis->getNumPrimMapNodes();
461   fullVector<double> lambdaJ(numSamplingPnts), lambdaB(numSamplingPnts);
462   fullMatrix<double> gradLambdaJ(numSamplingPnts, 2 * numMapNodes);
463   fullMatrix<double> gradLambdaB(numSamplingPnts, 2 * numMapNodes);
464 
465   // Coordinates of nodes
466   fullMatrix<double> nodesXYZ(numMapNodes, 3),
467     nodesXYZStraight(numPrimMapNodes, 3);
468   for(int i = 0; i < numMapNodes; i++) {
469     int &iVi = _el2V[iEl][i];
470     nodesXYZ(i, 0) = _xyz[iVi].x();
471     nodesXYZ(i, 1) = _xyz[iVi].y();
472     nodesXYZ(i, 2) = _xyz[iVi].z();
473     if(i < numPrimMapNodes) {
474       nodesXYZStraight(i, 0) = _ixyz[iVi].x();
475       nodesXYZStraight(i, 1) = _ixyz[iVi].y();
476       nodesXYZStraight(i, 2) = _ixyz[iVi].z();
477     }
478   }
479 
480   jacBasis->getMetricMinAndGradients(nodesXYZ, nodesXYZStraight, lambdaJ,
481                                      gradLambdaJ);
482 
483   // l2b.mult(lambdaJ, lambdaB);
484   // l2b.mult(gradLambdaJ, gradLambdaB);
485   lambdaB = lambdaJ;
486   gradLambdaB = gradLambdaJ;
487 
488   int iPC = 0;
489   std::vector<SPoint3> gXyzV(numSamplingPnts);
490   std::vector<SPoint3> gUvwV(numSamplingPnts);
491   for(int l = 0; l < numSamplingPnts; l++) { lambda[l] = lambdaB(l); }
492   for(int i = 0; i < numMapNodes; i++) {
493     int &iFVi = _el2FV[iEl][i];
494     if(iFVi >= 0) {
495       for(int l = 0; l < numSamplingPnts; l++) {
496         gXyzV[l] =
497           SPoint3(gradLambdaB(l, i + 0 * numMapNodes),
498                   gradLambdaB(l, i + 1 * numMapNodes), /*BDB(l,i+2*nbNod)*/ 0.);
499       }
500       _coordFV[iFVi]->gXyz2gUvw(_uvw[iFVi], gXyzV, gUvwV);
501       for(int l = 0; l < numSamplingPnts; l++) {
502         gradLambda[indGSJ(iEl, l, iPC)] = gUvwV[l][0];
503         if(_nPCFV[iFVi] >= 2) gradLambda[indGSJ(iEl, l, iPC + 1)] = gUvwV[l][1];
504         if(_nPCFV[iFVi] == 3) gradLambda[indGSJ(iEl, l, iPC + 2)] = gUvwV[l][2];
505       }
506       iPC += _nPCFV[iFVi];
507     }
508   }
509 }
510 
bndDistAndGradients(int iEl,double & f,std::vector<double> & gradF,double eps)511 bool Patch::bndDistAndGradients(int iEl, double &f, std::vector<double> &gradF,
512                                 double eps)
513 {
514   MElement *element = _el[iEl];
515   f = 0.;
516   // dommage ;-)
517   if(element->getDim() != 2) return false;
518 
519   int currentId = 0;
520   std::vector<int> vertex2param(element->getNumVertices());
521   for(size_t i = 0; i < element->getNumVertices(); ++i) {
522     if(_el2FV[iEl][i] >= 0) {
523       vertex2param[i] = currentId;
524       currentId += _nPCFV[_el2FV[iEl][i]];
525     }
526     else
527       vertex2param[i] = -1;
528   }
529   gradF.clear();
530   gradF.resize(currentId, 0.);
531 
532   const nodalBasis &elbasis = *element->getFunctionSpace();
533   bool edgeFound = false;
534   for(int iEdge = 0; iEdge < element->getNumEdges(); ++iEdge) {
535     int clId = elbasis.getClosureId(iEdge, 1);
536     const std::vector<int> &closure = elbasis.closures[clId];
537     std::vector<MVertex *> vertices;
538     GEdge *edge = nullptr;
539     for(size_t i = 0; i < closure.size(); ++i) {
540       MVertex *v = element->getVertex(closure[i]);
541       vertices.push_back(v);
542       // only valid in 2D
543       if((int)i >= 2 && v->onWhat() && v->onWhat()->dim() == 1) {
544         edge = v->onWhat()->cast2Edge();
545       }
546     }
547     if(edge) {
548       edgeFound = true;
549       std::vector<double> localgrad;
550       std::vector<SPoint3> nodes(closure.size());
551       std::vector<double> params(closure.size());
552       std::vector<bool> onedge(closure.size());
553       for(size_t i = 0; i < closure.size(); ++i) {
554         nodes[i] = _xyz[_el2V[iEl][closure[i]]];
555         onedge[i] = element->getVertex(closure[i])->onWhat() == edge &&
556                     _el2FV[iEl][closure[i]] >= 0;
557         if(onedge[i]) { params[i] = _uvw[_el2FV[iEl][closure[i]]].x(); }
558         else
559           reparamMeshVertexOnEdge(element->getVertex(closure[i]), edge,
560                                   params[i]);
561       }
562       f += computeBndDistAndGradient(
563         edge, params, vertices,
564         *BasisFactory::getNodalBasis(elbasis.getClosureType(clId)), nodes,
565         onedge, localgrad, eps);
566       for(size_t i = 0; i < closure.size(); ++i)
567         if(onedge[i]) gradF[vertex2param[closure[i]]] += localgrad[i];
568     }
569   }
570   return edgeFound;
571 }
572 
scaledCADDistSqAndGradients(int iBndEl,double & scaledDist,std::vector<double> & gradScaledDist)573 void Patch::scaledCADDistSqAndGradients(int iBndEl, double &scaledDist,
574                                         std::vector<double> &gradScaledDist)
575 {
576   const std::vector<int> &iV = _bndEl2V[iBndEl], &iFV = _bndEl2FV[iBndEl];
577   const int nV = iV.size();
578   const GradientBasis *gb;
579   const int tag = _bndEl[iBndEl]->getTypeForMSH();
580   gb = BasisFactory::getGradientBasis(tag, FuncSpaceData(_bndEl[iBndEl]));
581 
582   // Coordinates of nodes
583   fullMatrix<double> nodesXYZ(nV, 3);
584   for(int i = 0; i < nV; i++) {
585     const int &iVi = iV[i];
586     nodesXYZ(i, 0) = _xyz[iVi].x();
587     nodesXYZ(i, 1) = _xyz[iVi].y();
588     nodesXYZ(i, 2) = _xyz[iVi].z();
589   }
590 
591   // Compute distance and gradients (CAUTION: returns gradients w.r.t. vertices,
592   // not free vertices)
593   if(_dim == 2) { // 2D
594     const GEdge *ge = _bndEl2Ent[iBndEl]->cast2Edge();
595     const Range<double> parBounds = ge->parBounds(0);
596     const double eps = 1.e-6 * (parBounds.high() - parBounds.low());
597     std::vector<SVector3> tanCAD(nV);
598     for(int i = 0; i < nV; i++) {
599       const int &iVi = iV[i], &iFVi = iFV[i];
600       MVertex *&vert = _vert[iVi];
601       double tCAD;
602       if(iFVi >= 0) // If free vertex, ...
603         tCAD =
604           _uvw[iFVi].x(); // ... get stored param. coord. (can be only line).
605       else { // Otherwise, get param. coord. from CAD.
606         if(ge->getBeginVertex() &&
607            ge->getBeginVertex()->mesh_vertices[0] == vert)
608           tCAD = parBounds.low();
609         else if(ge->getEndVertex() &&
610                 ge->getEndVertex()->mesh_vertices[0] == vert)
611           tCAD = parBounds.high();
612         else
613           tCAD = ge->parFromPoint(_xyz[iVi]);
614       }
615       tanCAD[i] = ge->firstDer(tCAD); // Compute tangent at vertex
616       tanCAD[i].normalize(); // Normalize tangent
617     }
618     scaledDist = _invRefCADDistSq * taylorDistanceSq1D(gb, nodesXYZ, tanCAD);
619     for(int i = 0; i < nV; i++) {
620       const int &iFVi = iFV[i];
621       if(iFVi < 0) continue; // Skip if not free vertex
622       const double xS = nodesXYZ(i, 0), yS = nodesXYZ(i, 1),
623                    zS = nodesXYZ(i, 2); // Save coord. of perturbed node for FD
624       const SVector3 tanCADS =
625         tanCAD[i]; // Save tangent to CAD at perturbed node
626       const double tCAD =
627         _uvw[iFVi].x() + eps; // New param. coord. of perturbed node
628       GPoint gp = ge->point(tCAD); // New coord. of perturbed node
629       nodesXYZ(i, 0) = gp.x();
630       nodesXYZ(i, 1) = gp.y();
631       nodesXYZ(i, 2) = gp.z();
632       tanCAD[i] = ge->firstDer(tCAD); // New tangent to CAD at perturbed node
633       tanCAD[i].normalize(); // Normalize new tangent
634       const double sDistDiff =
635         _invRefCADDistSq *
636         taylorDistanceSq1D(gb, nodesXYZ,
637                            tanCAD); // Compute distance with perturbed node
638       gradScaledDist[i] = (sDistDiff - scaledDist) / eps; // Compute gradient
639       nodesXYZ(i, 0) = xS;
640       nodesXYZ(i, 1) = yS;
641       nodesXYZ(i, 2) = zS; // Restore coord. of perturbed node
642       tanCAD[i] = tanCADS; // Restore tan. to CAD at perturbed node
643     }
644   }
645   else { // 3D
646     const GFace *gf = _bndEl2Ent[iBndEl]->cast2Face();
647     const Range<double> parBounds0 = gf->parBounds(0),
648                         parBounds1 = gf->parBounds(1);
649     const double eps0 = 1.e-6 * (parBounds0.high() - parBounds0.low());
650     const double eps1 = 1.e-6 * (parBounds1.high() - parBounds1.low());
651     std::vector<SVector3> normCAD(nV);
652     for(int i = 0; i < nV; i++) {
653       const int &iVi = iV[i], &iFVi = iFV[i];
654       MVertex *&vert = _vert[iVi];
655       SPoint2 pCAD;
656       if(iFVi >= 0) { // If free vertex...
657         if(vert->onWhat() == gf) // If on surface, ...
658           pCAD = SPoint2(_uvw[iFVi].x(),
659                          _uvw[iFVi].y()); // ... get stored param. coord.
660         else { // Otherwise, reparametrize on surface
661           const GEdge *ge = vert->onWhat()->cast2Edge();
662           pCAD = ge->reparamOnFace(gf, _uvw[iFVi].x(), 1);
663         }
664       }
665       else
666         reparamMeshVertexOnFace(
667           vert, gf, pCAD); // If not free vertex, reparametrize on surface
668       normCAD[i] = gf->normal(pCAD); // Compute normal at vertex
669       normCAD[i].normalize(); // Normalize normal
670     }
671     scaledDist = _invRefCADDistSq * taylorDistanceSq2D(gb, nodesXYZ, normCAD);
672     //    std::cout << "DBGTT: bnd el. " << _bndEl[iBndEl]->getNum() << ":
673     //    scaledDist = " << scaledDist << "\n";
674     for(int i = 0; i < nV; i++) {
675       const int &iVi = iV[i], &iFVi = iFV[i];
676       if(iFVi < 0) continue; // Skip if not free vertex
677       const double xS = nodesXYZ(i, 0), yS = nodesXYZ(i, 1),
678                    zS = nodesXYZ(i, 2); // Save coord. of perturbed node for FD
679       const SVector3 normCADS =
680         normCAD[i]; // Save normal to CAD at perturbed node
681       if(_nPCFV[iFVi] == 2) { // Vertex classified on surface, 2D gradient
682         const SPoint2 pCAD0 = SPoint2(
683           _uvw[iFVi].x() + eps0,
684           _uvw[iFVi].y()); // New param. coord. of perturbed node in 1st dir.
685         GPoint gp0 =
686           gf->point(pCAD0); // New coord. of perturbed node in 1st dir.
687         nodesXYZ(i, 0) = gp0.x();
688         nodesXYZ(i, 1) = gp0.y();
689         nodesXYZ(i, 2) = gp0.z();
690         normCAD[i] =
691           gf->normal(pCAD0); // New normal to CAD at perturbed node in 1st dir.
692         normCAD[i].normalize(); // Normalize new normal
693         const double sDistDiff0 =
694           _invRefCADDistSq *
695           taylorDistanceSq2D(
696             gb, nodesXYZ,
697             normCAD); // Compute distance with perturbed node in 1st dir.
698         gradScaledDist[2 * i] =
699           (sDistDiff0 - scaledDist) / eps0; // Compute gradient in 1st dir.
700         const SPoint2 pCAD1 =
701           SPoint2(_uvw[iFVi].x(),
702                   _uvw[iFVi].y() +
703                     eps1); // New param. coord. of perturbed node in 2nd dir.
704         GPoint gp1 =
705           gf->point(pCAD1); // New coord. of perturbed node in 2nd dir.
706         nodesXYZ(i, 0) = gp1.x();
707         nodesXYZ(i, 1) = gp1.y();
708         nodesXYZ(i, 2) = gp1.z();
709         normCAD[i] =
710           gf->normal(pCAD1); // New normal to CAD at perturbed node in 2nd dir.
711         normCAD[i].normalize(); // Normalize new normal
712         double sDistDiff1 =
713           _invRefCADDistSq *
714           taylorDistanceSq2D(
715             gb, nodesXYZ,
716             normCAD); // Compute distance with perturbed node in 2nd dir.
717         gradScaledDist[2 * i + 1] =
718           (sDistDiff1 - scaledDist) / eps1; // Compute gradient in 2nd dir.
719       }
720       else if(_nPCFV[iFVi] == 1) { // Vertex classified on edge, 1D gradient
721         MVertex *&vert = _vert[iVi];
722         const GEdge *ge = vert->onWhat()->cast2Edge();
723         const Range<double> parBounds = ge->parBounds(0);
724         const double eps = 1.e-6 * (parBounds.high() - parBounds.low());
725         const double tCAD =
726           _uvw[iFVi].x() + eps; // New param. coord. of perturbed node
727         GPoint gp = ge->point(tCAD); // New coord. of perturbed node
728         nodesXYZ(i, 0) = gp.x();
729         nodesXYZ(i, 1) = gp.y();
730         nodesXYZ(i, 2) = gp.z();
731         SPoint2 pCAD = gf->parFromPoint(
732           SPoint3(gp.x(), gp.y(), gp.z()),
733           true); // Get param. coord. of perturbed node in face from CAD
734         normCAD[i] = gf->normal(pCAD); // New normal to CAD at perturbed node
735         normCAD[i].normalize(); // Normalize new normal
736         const double sDistDiff =
737           _invRefCADDistSq *
738           taylorDistanceSq2D(gb, nodesXYZ,
739                              normCAD); // Compute distance with perturbed node
740         gradScaledDist[2 * i] =
741           (sDistDiff - scaledDist) / eps; // Compute gradient
742       }
743       else
744         std::cout << "DBGTT: Inconsistent _nPCFV(iFVi), vert. "
745                   << _vert[iVi]->getNum() << "\n";
746       nodesXYZ(i, 0) = xS;
747       nodesXYZ(i, 1) = yS;
748       nodesXYZ(i, 2) = zS; // Restore coord. of perturbed node
749       normCAD[i] = normCADS; // Restore tan. to CAD at perturbed node
750     }
751   }
752 }
753 
initIdealJac()754 void Patch::initIdealJac()
755 {
756   // Initialize _nBezEl
757   if(_nIJacEl.empty()) {
758     _nIJacEl.resize(nEl());
759     for(int iEl = 0; iEl < nEl(); iEl++)
760       _nIJacEl[iEl] = _el[iEl]->getJacobianFuncSpace()->getNumSamplingPnts();
761   }
762 
763   // Set normals to 2D elements (with magnitude of inverse Jacobian) or initial
764   // Jacobians of 3D elements
765   if((_dim == 2) && _IJacNormEl.empty()) {
766     _IJacNormEl.resize(nEl());
767     for(int iEl = 0; iEl < nEl(); iEl++)
768       calcNormalEl2D(iEl, NS_INVNORM, _IJacNormEl[iEl], true);
769   }
770   else if(_invStraightJac.empty()) {
771     _invIJac.resize(nEl(), 1.);
772     for(int iEl = 0; iEl < nEl(); iEl++) {
773       int nEd = _el[iEl]->getNumEdges();
774       double sumEdLength = 0.;
775       for(int iEd = 0; iEd < nEd; iEd++)
776         sumEdLength += _el[iEl]->getEdge(iEd).length();
777       const double invMeanEdLength = double(nEd) / sumEdLength;
778       _invIJac[iEl] = invMeanEdLength * invMeanEdLength * invMeanEdLength;
779     }
780   }
781 }
782 
idealJacAndGradients(int iEl,std::vector<double> & iJ,std::vector<double> & gIJ)783 void Patch::idealJacAndGradients(int iEl, std::vector<double> &iJ,
784                                  std::vector<double> &gIJ)
785 {
786   const JacobianBasis *jacBasis = _el[iEl]->getJacobianFuncSpace();
787   const int &numSamplingPnts = _nIJacEl[iEl];
788   const int &numMapNodes = _nNodEl[iEl];
789   fullMatrix<double> JDJ(numSamplingPnts, 3 * numMapNodes + 1);
790 
791   // Coordinates of nodes
792   fullMatrix<double> nodesXYZ(numMapNodes, 3), normals(_dim, 3);
793   for(int i = 0; i < numMapNodes; i++) {
794     int &iVi = _el2V[iEl][i];
795     nodesXYZ(i, 0) = _xyz[iVi].x();
796     nodesXYZ(i, 1) = _xyz[iVi].y();
797     nodesXYZ(i, 2) = _xyz[iVi].z();
798   }
799 
800   // Calculate Jacobian and gradients, scale if 3D (already scaled by
801   // regularization normals in 2D)
802   jacBasis->getSignedIdealJacAndGradients(nodesXYZ, _IJacNormEl[iEl], JDJ);
803   if(_dim == 3) JDJ.scale(_invIJac[iEl]);
804 
805   // Transform Jacobian and gradients from Lagrangian to Bezier basis
806   bezierCoeff BDB(jacBasis->getFuncSpaceData(), JDJ);
807 
808   // Scaled jacobian
809   for(int l = 0; l < numSamplingPnts; l++) iJ[l] = BDB(l, 3 * numMapNodes);
810 
811   // Gradients of the scaled jacobian
812   int iPC = 0;
813   std::vector<SPoint3> gXyzV(numSamplingPnts);
814   std::vector<SPoint3> gUvwV(numSamplingPnts);
815   for(int i = 0; i < numMapNodes; i++) {
816     int &iFVi = _el2FV[iEl][i];
817     if(iFVi >= 0) {
818       for(int l = 0; l < numSamplingPnts; l++)
819         gXyzV[l] = SPoint3(BDB(l, i), BDB(l, i + numMapNodes),
820                            BDB(l, i + 2 * numMapNodes));
821       _coordFV[iFVi]->gXyz2gUvw(_uvw[iFVi], gXyzV, gUvwV);
822       for(int l = 0; l < numSamplingPnts; l++) {
823         gIJ[indGIJac(iEl, l, iPC)] = gUvwV[l][0];
824         if(_nPCFV[iFVi] >= 2) gIJ[indGIJac(iEl, l, iPC + 1)] = gUvwV[l][1];
825         if(_nPCFV[iFVi] == 3) gIJ[indGIJac(iEl, l, iPC + 2)] = gUvwV[l][2];
826       }
827       iPC += _nPCFV[iFVi];
828     }
829   }
830 }
831 
initInvCondNum()832 void Patch::initInvCondNum()
833 {
834   // Initialize _nBezEl
835   if(_nICNEl.empty()) {
836     _nICNEl.resize(nEl());
837     for(int iEl = 0; iEl < nEl(); iEl++) {
838       const CondNumBasis *cnBasis =
839         BasisFactory::getCondNumBasis(_el[iEl]->getTypeForMSH());
840       _nICNEl[iEl] = cnBasis->getNumCondNumNodes();
841     }
842   }
843 
844   // Set normals to 2D elements
845   if((_dim == 2) && _condNormEl.empty()) {
846     _condNormEl.resize(nEl());
847     for(int iEl = 0; iEl < nEl(); iEl++)
848       calcNormalEl2D(iEl, NS_UNIT, _condNormEl[iEl], true);
849   }
850 }
851 
invCondNumAndGradients(int iEl,std::vector<double> & condNum,std::vector<double> & gCondNum)852 void Patch::invCondNumAndGradients(int iEl, std::vector<double> &condNum,
853                                    std::vector<double> &gCondNum)
854 {
855   const CondNumBasis *cnBasis =
856     BasisFactory::getCondNumBasis(_el[iEl]->getTypeForMSH());
857   const int &numICN = _nICNEl[iEl];
858   const int &numMapNodes = _nNodEl[iEl];
859   fullMatrix<double> IDI(numICN, 3 * numMapNodes + 1);
860 
861   // Coordinates of nodes
862   fullMatrix<double> nodesXYZ(numMapNodes, 3), normals;
863   for(int i = 0; i < numMapNodes; i++) {
864     int &iVi = _el2V[iEl][i];
865     nodesXYZ(i, 0) = _xyz[iVi].x();
866     nodesXYZ(i, 1) = _xyz[iVi].y();
867     nodesXYZ(i, 2) = _xyz[iVi].z();
868   }
869 
870   // Calculate ICN and gradients
871   cnBasis->getSignedInvCondNumAndGradients(nodesXYZ, _condNormEl[iEl], IDI);
872 
873   // Inverse condition number
874   for(int l = 0; l < numICN; l++) condNum[l] = IDI(l, 3 * numMapNodes);
875 
876   // Gradients of the inverse condition number
877   int iPC = 0;
878   std::vector<SPoint3> gXyzV(numICN);
879   std::vector<SPoint3> gUvwV(numICN);
880   for(int i = 0; i < numMapNodes; i++) {
881     int &iFVi = _el2FV[iEl][i];
882     if(iFVi >= 0) {
883       for(int l = 0; l < numICN; l++)
884         gXyzV[l] = SPoint3(IDI(l, i), IDI(l, i + numMapNodes),
885                            IDI(l, i + 2 * numMapNodes));
886       _coordFV[iFVi]->gXyz2gUvw(_uvw[iFVi], gXyzV, gUvwV);
887       for(int l = 0; l < numICN; l++) {
888         gCondNum[indGICN(iEl, l, iPC)] = gUvwV[l][0];
889         if(_nPCFV[iFVi] >= 2) gCondNum[indGICN(iEl, l, iPC + 1)] = gUvwV[l][1];
890         if(_nPCFV[iFVi] == 3) gCondNum[indGICN(iEl, l, iPC + 2)] = gUvwV[l][2];
891       }
892       iPC += _nPCFV[iFVi];
893     }
894   }
895 }
896