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