1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 //
6 // Contributor(s):
7 // Jonathan Lambrechts
8 //
9
10 #include <cstdlib>
11 #include <limits>
12 #include <list>
13 #include <cmath>
14 #include <fstream>
15 #include <string>
16 #include <string.h>
17 #include <sstream>
18 #include <algorithm>
19 #include "GmshConfig.h"
20 #include "Context.h"
21 #include "Field.h"
22 #include "GModel.h"
23 #include "GModelIO_GEO.h"
24 #include "GmshMessage.h"
25 #include "Numeric.h"
26 #include "mathEvaluator.h"
27 #include "BackgroundMeshTools.h"
28 #include "STensor3.h"
29 #include "ExtrudeParams.h"
30 #include "automaticMeshSizeField.h"
31 #include "fullMatrix.h"
32 #include "SPoint3KDTree.h"
33 #include "MVertex.h"
34
35 #if defined(HAVE_POST)
36 #include "PView.h"
37 #include "PViewData.h"
38 #endif
39
40 #if defined(WIN32) && !defined(__CYGWIN__)
41 #include <windows.h>
42 #include <io.h>
43 #else
44 #include <unistd.h>
45 #endif
46
47 #if defined(HAVE_ANN)
48 #include "ANN/ANN.h"
49 #endif
50
~Field()51 Field::~Field()
52 {
53 for(auto it = options.begin(); it != options.end(); ++it) delete it->second;
54 for(auto it = callbacks.begin(); it != callbacks.end(); ++it)
55 delete it->second;
56 }
57
getOption(const std::string & optionName)58 FieldOption *Field::getOption(const std::string &optionName)
59 {
60 auto it = options.find(optionName);
61 if(it == options.end()) {
62 Msg::Error("Field option '%s' does not exist", optionName.c_str());
63 return nullptr;
64 }
65 return it->second;
66 }
67
reset()68 void FieldManager::reset()
69 {
70 for(auto it = begin(); it != end(); it++) { delete it->second; }
71 clear();
72 }
73
get(int id)74 Field *FieldManager::get(int id)
75 {
76 auto it = find(id);
77 if(it == end()) return nullptr;
78 return it->second;
79 }
80
newField(int id,const std::string & type_name)81 Field *FieldManager::newField(int id, const std::string &type_name)
82 {
83 if(find(id) != end()) {
84 Msg::Error("Field id %i is already defined", id);
85 return nullptr;
86 }
87 if(mapTypeName.find(type_name) == mapTypeName.end()) {
88 Msg::Error("Unknown field type \"%s\"", type_name.c_str());
89 return nullptr;
90 }
91 Field *f = (*mapTypeName[type_name])();
92 if(!f) return nullptr;
93 f->id = id;
94 (*this)[id] = f;
95 return f;
96 }
97
newId()98 int FieldManager::newId()
99 {
100 int i = 0;
101 auto it = begin();
102 while(1) {
103 i++;
104 while(it != end() && it->first < i) it++;
105 if(it == end() || it->first != i) break;
106 }
107 return std::max(i, 1);
108 }
109
maxId()110 int FieldManager::maxId()
111 {
112 if(!empty())
113 return rbegin()->first;
114 else
115 return 0;
116 }
117
deleteField(int id)118 void FieldManager::deleteField(int id)
119 {
120 auto it = find(id);
121 if(it == end()) {
122 Msg::Error("Cannot delete field id %i, it does not exist", id);
123 return;
124 }
125 delete it->second;
126 erase(it);
127 }
128
129 // StructuredField
130 class StructuredField : public Field {
131 private:
132 double _o[3], _d[3];
133 int _n[3];
134 double *_data;
135 bool _errorStatus;
136 bool _textFormat, _outsideValueSet;
137 double _outsideValue;
138 std::string _fileName;
139
140 public:
StructuredField()141 StructuredField()
142 {
143 _data = nullptr;
144 _errorStatus = false;
145 _textFormat = false;
146 _outsideValueSet = false;
147 _outsideValue = MAX_LC;
148
149 options["FileName"] =
150 new FieldOptionPath(_fileName, "Name of the input file", &updateNeeded);
151 options["TextFormat"] = new FieldOptionBool(
152 _textFormat,
153 "True for ASCII input files, false for binary files (4 bite "
154 "signed integers for n, double precision floating points for v, D and O)",
155 &updateNeeded);
156 options["SetOutsideValue"] = new FieldOptionBool(
157 _outsideValueSet, "True to use the \"OutsideValue\" option. If False, "
158 "the last values of the grid are used.");
159 options["OutsideValue"] = new FieldOptionDouble(
160 _outsideValue, "Value of the field outside the grid (only used if the "
161 "\"SetOutsideValue\" option is true).");
162 }
getDescription()163 std::string getDescription()
164 {
165 return "Linearly interpolate between data provided on a 3D rectangular "
166 "structured grid.\n\n"
167 "The format of the input file is:\n\n"
168 " Ox Oy Oz \n"
169 " Dx Dy Dz \n"
170 " nx ny nz \n"
171 " v(0,0,0) v(0,0,1) v(0,0,2) ... \n"
172 " v(0,1,0) v(0,1,1) v(0,1,2) ... \n"
173 " v(0,2,0) v(0,2,1) v(0,2,2) ... \n"
174 " ... ... ... \n"
175 " v(1,0,0) ... ... \n\n"
176 "where O are the coordinates of the first node, D are the distances "
177 "between nodes in each direction, n are the numbers of nodes in "
178 "each direction, and v are the values on each node.";
179 }
getName()180 const char *getName() { return "Structured"; }
~StructuredField()181 virtual ~StructuredField()
182 {
183 if(_data) delete[] _data;
184 }
185 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)186 double operator()(double x, double y, double z, GEntity *ge = nullptr)
187 {
188 if(updateNeeded) {
189 _errorStatus = false;
190 try {
191 std::ifstream input;
192 if(_textFormat)
193 input.open(_fileName.c_str());
194 else
195 input.open(_fileName.c_str(), std::ios::binary);
196 if(!input.is_open()) {
197 Msg::Error("Could not open file '%s'", _fileName.c_str());
198 return MAX_LC;
199 }
200 input.exceptions(std::ifstream::eofbit | std::ifstream::failbit |
201 std::ifstream::badbit);
202 if(!_textFormat) {
203 input.read((char *)_o, 3 * sizeof(double));
204 input.read((char *)_d, 3 * sizeof(double));
205 input.read((char *)_n, 3 * sizeof(int));
206 int nt = _n[0] * _n[1] * _n[2];
207 if(_data) delete[] _data;
208 _data = new double[nt];
209 input.read((char *)_data, nt * sizeof(double));
210 }
211 else {
212 input >> _o[0] >> _o[1] >> _o[2] >> _d[0] >> _d[1] >> _d[2] >>
213 _n[0] >> _n[1] >> _n[2];
214 int nt = _n[0] * _n[1] * _n[2];
215 if(_data) delete[] _data;
216 _data = new double[nt];
217 for(int i = 0; i < nt; i++) input >> _data[i];
218 }
219 input.close();
220 } catch(...) {
221 _errorStatus = true;
222 Msg::Error("Field %i: error reading file '%s'", this->id,
223 _fileName.c_str());
224 }
225 updateNeeded = false;
226 }
227 if(_errorStatus) return MAX_LC;
228 // tri-linear
229 int id[2][3];
230 double xi[3];
231 double xyz[3] = {x, y, z};
232 for(int i = 0; i < 3; i++) {
233 id[0][i] = (int)floor((xyz[i] - _o[i]) / _d[i]);
234 id[1][i] = id[0][i] + 1;
235 if(_outsideValueSet && (id[0][i] < 0 || id[1][i] >= _n[i]) && _n[i] > 1)
236 return _outsideValue;
237 id[0][i] = std::max(std::min(id[0][i], _n[i] - 1), 0);
238 id[1][i] = std::max(std::min(id[1][i], _n[i] - 1), 0);
239 xi[i] = (xyz[i] - (_o[i] + id[0][i] * _d[i])) / _d[i];
240 xi[i] = std::max(std::min(xi[i], 1.), 0.);
241 }
242 double v = 0;
243 for(int i = 0; i < 2; i++)
244 for(int j = 0; j < 2; j++)
245 for(int k = 0; k < 2; k++) {
246 v += _data[id[i][0] * _n[1] * _n[2] + id[j][1] * _n[2] + id[k][2]] *
247 (i * xi[0] + (1 - i) * (1 - xi[0])) *
248 (j * xi[1] + (1 - j) * (1 - xi[1])) *
249 (k * xi[2] + (1 - k) * (1 - xi[2]));
250 }
251 return v;
252 }
253 };
254
255 class LonLatField : public Field {
256 private:
257 int _inField, _fromStereo;
258 double _stereoRadius;
259
260 public:
getDescription()261 std::string getDescription()
262 {
263 return "Evaluate Field[InField] in geographic coordinates (longitude, "
264 "latitude):\n\n"
265 " F = Field[InField](atan(y / x), asin(z / sqrt(x^2 + y^2 + z^2))";
266 }
LonLatField()267 LonLatField()
268 {
269 _inField = 1;
270 _fromStereo = 0;
271 _stereoRadius = 6371e3;
272
273 options["InField"] =
274 new FieldOptionInt(_inField, "Tag of the field to evaluate");
275 options["FromStereo"] = new FieldOptionInt(
276 _fromStereo, "If = 1, the mesh is in stereographic coordinates: "
277 "xi = 2Rx/(R+z), eta = 2Ry/(R+z)");
278 options["RadiusStereo"] = new FieldOptionDouble(
279 _stereoRadius, "Radius of the sphere of the stereograpic coordinates");
280
281 // deprecated names
282 options["IField"] =
283 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
284 }
getName()285 const char *getName() { return "LonLat"; }
286 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)287 double operator()(double x, double y, double z, GEntity *ge = nullptr)
288 {
289 if(_inField == id) return MAX_LC;
290 Field *field = GModel::current()->getFields()->get(_inField);
291 if(!field) {
292 Msg::Warning("Unknown Field %i", _inField);
293 return MAX_LC;
294 }
295 if(_fromStereo == 1) {
296 double xi = x;
297 double eta = y;
298 double r2 = _stereoRadius * _stereoRadius;
299 x = 4 * r2 * xi / (4 * r2 + xi * xi + eta * eta);
300 y = 4 * r2 * eta / (4 * r2 + xi * xi + eta * eta);
301 z = _stereoRadius * (4 * r2 - eta * eta - xi * xi) /
302 (4 * r2 + xi * xi + eta * eta);
303 }
304 return (*field)(atan2(y, x), asin(z / _stereoRadius), 0);
305 }
306 };
307
308 class BoxField : public Field {
309 private:
310 double _vIn, _vOut, _xMin, _xMax, _yMin, _yMax, _zMin, _zMax, _thick;
311
312 public:
getDescription()313 std::string getDescription()
314 {
315 return "Return VIn inside the box, and VOut outside. The box is defined "
316 "by\n\n"
317 " Xmin <= x <= XMax &&\n"
318 " YMin <= y <= YMax &&\n"
319 " ZMin <= z <= ZMax\n\n"
320 "If Thickness is > 0, the mesh size is interpolated between VIn and "
321 "VOut in a layer around the box of the prescribed thickness.";
322 }
BoxField()323 BoxField()
324 {
325 _vIn = _vOut = MAX_LC;
326 _xMin = _xMax = _yMin = _yMax = _zMin = _zMax = _thick = 0.;
327
328 options["VIn"] = new FieldOptionDouble(_vIn, "Value inside the box");
329 options["VOut"] = new FieldOptionDouble(_vOut, "Value outside the box");
330 options["XMin"] =
331 new FieldOptionDouble(_xMin, "Minimum X coordinate of the box");
332 options["XMax"] =
333 new FieldOptionDouble(_xMax, "Maximum X coordinate of the box");
334 options["YMin"] =
335 new FieldOptionDouble(_yMin, "Minimum Y coordinate of the box");
336 options["YMax"] =
337 new FieldOptionDouble(_yMax, "Maximum Y coordinate of the box");
338 options["ZMin"] =
339 new FieldOptionDouble(_zMin, "Minimum Z coordinate of the box");
340 options["ZMax"] =
341 new FieldOptionDouble(_zMax, "Maximum Z coordinate of the box");
342 options["Thickness"] = new FieldOptionDouble(
343 _thick, "Thickness of a transition layer outside the box");
344 }
getName()345 const char *getName() { return "Box"; }
346 using Field::operator();
computeDistance(double xp,double yp,double zp)347 double computeDistance(double xp, double yp, double zp)
348 {
349 // orthogonal basis with origin (_xMin,_yMin,_zMin)
350 double x0[3] = {_xMin, _yMin, _zMin};
351 double x1[3] = {_xMax, _yMin, _zMin};
352 double y1[3] = {_xMin, _yMax, _zMin};
353 double z1[3] = {_xMin, _yMin, _zMax};
354 double nx[3] = {x1[0] - x0[0], x1[1] - x0[1], x1[2] - x0[2]};
355 double ny[3] = {y1[0] - x0[0], y1[1] - x0[1], y1[2] - x0[2]};
356 double nz[3] = {z1[0] - x0[0], z1[1] - x0[1], z1[2] - x0[2]};
357 double pvect[3] = {xp - x0[0], yp - x0[1], zp - x0[2]};
358 double projX = scalProd(nx, pvect);
359 double tempX = scalProd(nx, nx);
360 if(tempX) projX /= tempX;
361 double projY = scalProd(ny, pvect);
362 double tempY = scalProd(ny, ny);
363 if(tempY) projY /= tempY;
364 double projZ = scalProd(nz, pvect);
365 double tempZ = scalProd(nz, nz);
366 if(tempZ) projZ /= tempZ;
367 if(projX < 0.0) projX = 0.0;
368 if(projX > 1.0) projX = 1.0;
369 if(projY < 0.0) projY = 0.0;
370 if(projY > 1.0) projY = 1.0;
371 if(projZ < 0.0) projZ = 0.0;
372 if(projZ > 1.0) projZ = 1.0;
373 double psbox[3] = {x0[0] + projX * nx[0] + projY * ny[0] + projZ * nz[0],
374 x0[1] + projX * nx[1] + projY * ny[1] + projZ * nz[1],
375 x0[2] + projX * nx[2] + projY * ny[2] + projZ * nz[2]};
376 double dist =
377 sqrt(std::pow((psbox[0] - xp), 2) + std::pow((psbox[1] - yp), 2) +
378 std::pow((psbox[2] - zp), 2));
379 return dist;
380 }
operator ()(double x,double y,double z,GEntity * ge=nullptr)381 double operator()(double x, double y, double z, GEntity *ge = nullptr)
382 {
383 // inside
384 if(x >= _xMin && x <= _xMax && y >= _yMin && y <= _yMax && z >= _zMin &&
385 z <= _zMax) {
386 return _vIn;
387 }
388 // transition layer
389 if(_thick > 0) {
390 double dist = computeDistance(x, y, z);
391 if(dist <= _thick) return _vIn + (dist / _thick) * (_vOut - _vIn);
392 }
393 return _vOut;
394 }
395 };
396
397 class CylinderField : public Field {
398 private:
399 double _vIn, _vOut;
400 double _xc, _yc, _zc;
401 double _xa, _ya, _za;
402 double _r;
403
404 public:
getDescription()405 std::string getDescription()
406 {
407 return "Return VIn inside a frustrated cylinder, and VOut outside. "
408 "The cylinder is defined by\n\n"
409 " ||dX||^2 < R^2 &&\n"
410 " (X-X0).A < ||A||^2\n"
411 " dX = (X - X0) - ((X - X0).A)/(||A||^2) . A";
412 }
CylinderField()413 CylinderField()
414 {
415 _vIn = _vOut = MAX_LC;
416 _xc = _yc = _zc = _xa = _ya = _r = 0.;
417 _za = 1.;
418
419 options["VIn"] = new FieldOptionDouble(_vIn, "Value inside the cylinder");
420 options["VOut"] =
421 new FieldOptionDouble(_vOut, "Value outside the cylinder");
422 options["XCenter"] =
423 new FieldOptionDouble(_xc, "X coordinate of the cylinder center");
424 options["YCenter"] =
425 new FieldOptionDouble(_yc, "Y coordinate of the cylinder center");
426 options["ZCenter"] =
427 new FieldOptionDouble(_zc, "Z coordinate of the cylinder center");
428 options["XAxis"] =
429 new FieldOptionDouble(_xa, "X component of the cylinder axis");
430 options["YAxis"] =
431 new FieldOptionDouble(_ya, "Y component of the cylinder axis");
432 options["ZAxis"] =
433 new FieldOptionDouble(_za, "Z component of the cylinder axis");
434 options["Radius"] = new FieldOptionDouble(_r, "Radius");
435 }
getName()436 const char *getName() { return "Cylinder"; }
437 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)438 double operator()(double x, double y, double z, GEntity *ge = nullptr)
439 {
440 double dx = x - _xc;
441 double dy = y - _yc;
442 double dz = z - _zc;
443
444 double adx =
445 (_xa * dx + _ya * dy + _za * dz) / (_xa * _xa + _ya * _ya + _za * _za);
446
447 dx -= adx * _xa;
448 dy -= adx * _ya;
449 dz -= adx * _za;
450
451 return ((dx * dx + dy * dy + dz * dz < _r * _r) && fabs(adx) < 1) ? _vIn :
452 _vOut;
453 }
454 };
455
456 class BallField : public Field {
457 private:
458 double _vIn, _vOut;
459 double _xc, _yc, _zc;
460 double _r, _thick;
461
462 public:
getDescription()463 std::string getDescription()
464 {
465 return "Return VIn inside a spherical ball, and VOut outside. "
466 "The ball is defined by\n\n"
467 " ||dX||^2 < R^2 &&\n"
468 " dX = (X - XC)^2 + (Y-YC)^2 + (Z-ZC)^2\n\n"
469 "If Thickness is > 0, the mesh size is interpolated between VIn and "
470 "VOut in a layer around the ball of the prescribed thickness.";
471 }
BallField()472 BallField()
473 {
474 _vIn = _vOut = MAX_LC;
475 _xc = _yc = _zc = _r = _thick = 0.;
476
477 options["VIn"] = new FieldOptionDouble(_vIn, "Value inside the ball");
478 options["VOut"] = new FieldOptionDouble(_vOut, "Value outside the ball");
479 options["XCenter"] =
480 new FieldOptionDouble(_xc, "X coordinate of the ball center");
481 options["YCenter"] =
482 new FieldOptionDouble(_yc, "Y coordinate of the ball center");
483 options["ZCenter"] =
484 new FieldOptionDouble(_zc, "Z coordinate of the ball center");
485 options["Radius"] = new FieldOptionDouble(_r, "Radius");
486 options["Thickness"] = new FieldOptionDouble(
487 _thick, "Thickness of a transition layer outside the ball");
488 }
getName()489 const char *getName() { return "Ball"; }
490 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)491 double operator()(double x, double y, double z, GEntity *ge = nullptr)
492 {
493 double dx = x - _xc;
494 double dy = y - _yc;
495 double dz = z - _zc;
496 double d = sqrt(dx * dx + dy * dy + dz * dz);
497 if(d < _r) return _vIn;
498 // transition layer
499 if(_thick > 0) {
500 double dist = d - _r;
501 if(dist <= _thick) return _vIn + (dist / _thick) * (_vOut - _vIn);
502 }
503 return _vOut;
504 }
505 };
506
507 class FrustumField : public Field {
508 private:
509 double _x1, _y1, _z1;
510 double _x2, _y2, _z2;
511 double _r1i, _r1o, _r2i, _r2o;
512 double _v1i, _v1o, _v2i, _v2o;
513
514 public:
getDescription()515 std::string getDescription()
516 {
517 return "Interpolate mesh sizes on a extended cylinder frustrum defined "
518 "by inner (R1i and R2i) and outer (R1o and R2o) radii and two "
519 "endpoints P1 and P2."
520 "The field value F for a point P is given by :\n\n"
521 " u = P1P . P1P2 / ||P1P2|| \n"
522 " r = || P1P - u*P1P2 || \n"
523 " Ri = (1 - u) * R1i + u * R2i \n"
524 " Ro = (1 - u) * R1o + u * R2o \n"
525 " v = (r - Ri) / (Ro - Ri) \n"
526 " F = (1 - v) * ((1 - u) * v1i + u * v2i) + "
527 " v * ((1 - u) * v1o + u * v2o)\n"
528 "with (u, v) in [0, 1] x [0, 1].";
529 }
FrustumField()530 FrustumField()
531 {
532 _x1 = _y1 = _z1 = 0.;
533 _x2 = _y2 = _z2 = 0.;
534 _z1 = 1.;
535 _r1i = _r2i = 0.;
536 _r1o = _r2o = 1.;
537 _v1i = _v2i = 0.1;
538 _v1o = _v2o = 1.;
539
540 options["X1"] = new FieldOptionDouble(_x1, "X coordinate of endpoint 1");
541 options["Y1"] = new FieldOptionDouble(_y1, "Y coordinate of endpoint 1");
542 options["Z1"] = new FieldOptionDouble(_z1, "Z coordinate of endpoint 1");
543 options["X2"] = new FieldOptionDouble(_x2, "X coordinate of endpoint 2");
544 options["Y2"] = new FieldOptionDouble(_y2, "Y coordinate of endpoint 2");
545 options["Z2"] = new FieldOptionDouble(_z2, "Z coordinate of endpoint 2");
546 options["InnerR1"] =
547 new FieldOptionDouble(_r1i, "Inner radius of Frustum at endpoint 1");
548 options["OuterR1"] =
549 new FieldOptionDouble(_r1o, "Outer radius of Frustum at endpoint 1");
550 options["InnerR2"] =
551 new FieldOptionDouble(_r2i, "Inner radius of Frustum at endpoint 2");
552 options["OuterR2"] =
553 new FieldOptionDouble(_r2o, "Outer radius of Frustum at endpoint 2");
554 options["InnerV1"] =
555 new FieldOptionDouble(_v1i, "Mesh size at point 1, inner radius");
556 options["OuterV1"] =
557 new FieldOptionDouble(_v1o, "Mesh size at point 1, outer radius");
558 options["InnerV2"] =
559 new FieldOptionDouble(_v2i, "Mesh size at point 2, inner radius");
560 options["OuterV2"] =
561 new FieldOptionDouble(_v2o, "Mesh size at point 2, outer radius");
562
563 // deprecated names
564 options["R1_inner"] =
565 new FieldOptionDouble(_r1i, "[Deprecated]", nullptr, true);
566 options["R1_outer"] =
567 new FieldOptionDouble(_r1o, "[Deprecated]", nullptr, true);
568 options["R2_inner"] =
569 new FieldOptionDouble(_r2i, "[Deprecated]", nullptr, true);
570 options["R2_outer"] =
571 new FieldOptionDouble(_r2o, "[Deprecated]", nullptr, true);
572 options["V1_inner"] =
573 new FieldOptionDouble(_v1i, "[Deprecated]", nullptr, true);
574 options["V1_outer"] =
575 new FieldOptionDouble(_v1o, "[Deprecated]", nullptr, true);
576 options["V2_inner"] =
577 new FieldOptionDouble(_v2i, "[Deprecated]", nullptr, true);
578 options["V2_outer"] =
579 new FieldOptionDouble(_v2o, "[Deprecated]", nullptr, true);
580 }
getName()581 const char *getName() { return "Frustum"; }
582 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)583 double operator()(double x, double y, double z, GEntity *ge = nullptr)
584 {
585 double dx = x - _x1;
586 double dy = y - _y1;
587 double dz = z - _z1;
588 double x12 = _x2 - _x1;
589 double y12 = _y2 - _y1;
590 double z12 = _z2 - _z1;
591 double l12 = sqrt(x12 * x12 + y12 * y12 + z12 * z12);
592
593 double l = (dx * x12 + dy * y12 + dz * z12) / l12;
594 double r = sqrt(dx * dx + dy * dy + dz * dz - l * l);
595
596 double u = l / l12; // u varies between 0 (P1) and 1 (P2)
597 double ri = (1 - u) * _r1i + u * _r2i;
598 double ro = (1 - u) * _r1o + u * _r2o;
599 double v = (r - ri) / (ro - ri); // v varies between 0 (inner) and 1 (outer)
600
601 double lc = MAX_LC;
602 if(u >= 0 && u <= 1 && v >= 0 && v <= 1) {
603 lc =
604 (1 - v) * ((1 - u) * _v1i + u * _v2i) + v * ((1 - u) * _v1o + u * _v2o);
605 }
606 return lc;
607 }
608 };
609
610 class ThresholdField : public Field {
611 protected:
612 int _inField;
613 double _dMin, _dMax, _lcMin, _lcMax;
614 bool _sigmoid, _stopAtDistMax;
615
616 public:
getName()617 virtual const char *getName() { return "Threshold"; }
getDescription()618 virtual std::string getDescription()
619 {
620 return "Return F = SizeMin if Field[InField] <= DistMin, "
621 "F = SizeMax if Field[InField] >= DistMax, and "
622 "the interpolation between SizeMin and SizeMax if DistMin < "
623 "Field[InField] < DistMax.";
624 }
ThresholdField()625 ThresholdField()
626 {
627 _inField = 0;
628 _dMin = 1;
629 _dMax = 10;
630 _lcMin = 0.1;
631 _lcMax = 1;
632 _sigmoid = false;
633 _stopAtDistMax = false;
634
635 options["InField"] =
636 new FieldOptionInt(_inField, "Tag of the field to evaluate");
637 options["DistMin"] = new FieldOptionDouble(
638 _dMin, "Distance from entity up to which the mesh size will be SizeMin");
639 options["DistMax"] = new FieldOptionDouble(
640 _dMax, "Distance from entity after which the mesh size will be SizeMax");
641 options["SizeMin"] =
642 new FieldOptionDouble(_lcMin, "Mesh size inside DistMin");
643 options["SizeMax"] =
644 new FieldOptionDouble(_lcMax, "Mesh size outside DistMax");
645 options["Sigmoid"] = new FieldOptionBool(
646 _sigmoid,
647 "True to interpolate between SizeMin and LcMax using a sigmoid, "
648 "false to interpolate linearly");
649 options["StopAtDistMax"] = new FieldOptionBool(
650 _stopAtDistMax, "True to not impose mesh size outside DistMax (i.e., "
651 "F = a very big value if Field[InField] > DistMax)");
652
653 // deprecated names
654 options["IField"] =
655 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
656 options["LcMin"] =
657 new FieldOptionDouble(_lcMin, "[Deprecated]", nullptr, true);
658 options["LcMax"] =
659 new FieldOptionDouble(_lcMax, "[Deprecated]", nullptr, true);
660 }
661 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)662 double operator()(double x, double y, double z, GEntity *ge = nullptr)
663 {
664 if(_inField == id) return MAX_LC;
665 Field *field = GModel::current()->getFields()->get(_inField);
666 if(!field) {
667 Msg::Warning("Unknown Field %i", _inField);
668 return MAX_LC;
669 }
670 double d = (*field)(x, y, z);
671 if(_stopAtDistMax && d >= _dMax) return MAX_LC;
672 double r = (d - _dMin) / (_dMax - _dMin);
673 r = std::max(std::min(r, 1.), 0.);
674 double lc;
675 if(_sigmoid) {
676 double s = exp(12. * r - 6.) / (1. + exp(12. * r - 6.));
677 lc = _lcMin * (1. - s) + _lcMax * s;
678 }
679 else { // linear
680 lc = _lcMin * (1 - r) + _lcMax * r;
681 }
682 return lc;
683 }
684 };
685
686 class GradientField : public Field {
687 private:
688 int _inField, _kind;
689 double _delta;
690
691 public:
getName()692 const char *getName() { return "Gradient"; }
getDescription()693 std::string getDescription()
694 {
695 return "Compute the finite difference gradient of Field[InField]:\n\n"
696 " F = (Field[InField](X + Delta/2) - "
697 " Field[InField](X - Delta/2)) / Delta";
698 }
GradientField()699 GradientField()
700 {
701 _inField = 1;
702 _kind = 3;
703 _delta = CTX::instance()->lc / 1e4;
704
705 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
706 options["Kind"] = new FieldOptionInt(
707 _kind,
708 "Component of the gradient to evaluate: 0 for X, 1 for Y, 2 for Z, "
709 "3 for the norm");
710 options["Delta"] = new FieldOptionDouble(_delta, "Finite difference step");
711
712 // deprecated names
713 options["IField"] =
714 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
715 }
716 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)717 double operator()(double x, double y, double z, GEntity *ge = nullptr)
718 {
719 if(_inField == id) return MAX_LC;
720 Field *field = GModel::current()->getFields()->get(_inField);
721 if(!field) {
722 Msg::Warning("Unknown Field %i", _inField);
723 return MAX_LC;
724 }
725 double gx, gy, gz;
726 switch(_kind) {
727 case 0: /* x */
728 return ((*field)(x + _delta / 2, y, z) - (*field)(x - _delta / 2, y, z)) /
729 _delta;
730 case 1: /* y */
731 return ((*field)(x, y + _delta / 2, z) - (*field)(x, y - _delta / 2, z)) /
732 _delta;
733 case 2: /* z */
734 return ((*field)(x, y, z + _delta / 2) - (*field)(x, y, z - _delta / 2)) /
735 _delta;
736 case 3: /* norm */
737 gx = ((*field)(x + _delta / 2, y, z) - (*field)(x - _delta / 2, y, z)) /
738 _delta;
739 gy = ((*field)(x, y + _delta / 2, z) - (*field)(x, y - _delta / 2, z)) /
740 _delta;
741 gz = ((*field)(x, y, z + _delta / 2) - (*field)(x, y, z - _delta / 2)) /
742 _delta;
743 return sqrt(gx * gx + gy * gy + gz * gz);
744 default:
745 Msg::Error("Field %i: unknown kind (%i) of gradient", this->id, _kind);
746 return MAX_LC;
747 }
748 }
749 };
750
751 class CurvatureField : public Field {
752 private:
753 int _inField;
754 double _delta;
755
756 public:
getName()757 const char *getName() { return "Curvature"; }
getDescription()758 std::string getDescription()
759 {
760 return "Compute the curvature of Field[InField]:\n\n"
761 " F = div(norm(grad(Field[InField])))";
762 }
CurvatureField()763 CurvatureField()
764 {
765 _inField = 1;
766 _delta = CTX::instance()->lc / 1e4;
767
768 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
769 options["Delta"] =
770 new FieldOptionDouble(_delta, "Step of the finite differences");
771
772 // deprecated names
773 options["IField"] =
774 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
775 }
grad_norm(Field & f,double x,double y,double z,double * g)776 void grad_norm(Field &f, double x, double y, double z, double *g)
777 {
778 g[0] = f(x + _delta / 2, y, z) - f(x - _delta / 2, y, z);
779 g[1] = f(x, y + _delta / 2, z) - f(x, y - _delta / 2, z);
780 g[2] = f(x, y, z + _delta / 2) - f(x, y, z - _delta / 2);
781 double n = sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);
782 g[0] /= n;
783 g[1] /= n;
784 g[2] /= n;
785 }
786 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)787 double operator()(double x, double y, double z, GEntity *ge = nullptr)
788 {
789 if(_inField == id) return MAX_LC;
790 Field *field = GModel::current()->getFields()->get(_inField);
791 if(!field) {
792 Msg::Warning("Unknown Field %i", _inField);
793 return MAX_LC;
794 }
795 double grad[6][3];
796 grad_norm(*field, x + _delta / 2, y, z, grad[0]);
797 grad_norm(*field, x - _delta / 2, y, z, grad[1]);
798 grad_norm(*field, x, y + _delta / 2, z, grad[2]);
799 grad_norm(*field, x, y - _delta / 2, z, grad[3]);
800 grad_norm(*field, x, y, z + _delta / 2, grad[4]);
801 grad_norm(*field, x, y, z - _delta / 2, grad[5]);
802 return (grad[0][0] - grad[1][0] + grad[2][1] - grad[3][1] + grad[4][2] -
803 grad[5][2]) /
804 _delta;
805 }
806 };
807
808 class MaxEigenHessianField : public Field {
809 private:
810 int _inField;
811 double _delta;
812
813 public:
getName()814 const char *getName() { return "MaxEigenHessian"; }
getDescription()815 std::string getDescription()
816 {
817 return "Compute the maximum eigenvalue of the Hessian matrix of "
818 "Field[InField], with the gradients evaluated by finite "
819 "differences:\n\n"
820 " F = max(eig(grad(grad(Field[InField]))))";
821 }
MaxEigenHessianField()822 MaxEigenHessianField()
823 {
824 _inField = 1;
825 _delta = CTX::instance()->lc / 1e4;
826
827 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
828 options["Delta"] =
829 new FieldOptionDouble(_delta, "Step used for the finite differences");
830
831 // deprecated names
832 options["IField"] =
833 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
834 }
835 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)836 double operator()(double x, double y, double z, GEntity *ge = nullptr)
837 {
838 if(_inField == id) return MAX_LC;
839 Field *field = GModel::current()->getFields()->get(_inField);
840 if(!field) {
841 Msg::Warning("Unknown Field %i", _inField);
842 return MAX_LC;
843 }
844 double mat[3][3], eig[3];
845 mat[1][0] = mat[0][1] = (*field)(x + _delta / 2, y + _delta / 2, z) +
846 (*field)(x - _delta / 2, y - _delta / 2, z) -
847 (*field)(x - _delta / 2, y + _delta / 2, z) -
848 (*field)(x + _delta / 2, y - _delta / 2, z);
849 mat[2][0] = mat[0][2] = (*field)(x + _delta / 2, y, z + _delta / 2) +
850 (*field)(x - _delta / 2, y, z - _delta / 2) -
851 (*field)(x - _delta / 2, y, z + _delta / 2) -
852 (*field)(x + _delta / 2, y, z - _delta / 2);
853 mat[2][1] = mat[1][2] = (*field)(x, y + _delta / 2, z + _delta / 2) +
854 (*field)(x, y - _delta / 2, z - _delta / 2) -
855 (*field)(x, y - _delta / 2, z + _delta / 2) -
856 (*field)(x, y + _delta / 2, z - _delta / 2);
857 double f = (*field)(x, y, z);
858 mat[0][0] = (*field)(x + _delta, y, z) + (*field)(x - _delta, y, z) - 2 * f;
859 mat[1][1] = (*field)(x, y + _delta, z) + (*field)(x, y - _delta, z) - 2 * f;
860 mat[2][2] = (*field)(x, y, z + _delta) + (*field)(x, y, z - _delta) - 2 * f;
861 eigenvalue(mat, eig);
862 return eig[0] / (_delta * _delta);
863 }
864 };
865
866 class LaplacianField : public Field {
867 private:
868 int _inField;
869 double _delta;
870
871 public:
getName()872 const char *getName() { return "Laplacian"; }
getDescription()873 std::string getDescription()
874 {
875 return "Compute finite difference the Laplacian of Field[InField]:\n\n"
876 " F = G(x+d,y,z) + G(x-d,y,z) +\n"
877 " G(x,y+d,z) + G(x,y-d,z) +\n"
878 " G(x,y,z+d) + G(x,y,z-d) - 6 * G(x,y,z),\n\n"
879 "where G = Field[InField] and d = Delta.";
880 }
LaplacianField()881 LaplacianField()
882 {
883 _inField = 1;
884 _delta = CTX::instance()->lc / 1e4;
885
886 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
887 options["Delta"] = new FieldOptionDouble(_delta, "Finite difference step");
888
889 // deprecated names
890 options["IField"] =
891 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
892 }
893 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)894 double operator()(double x, double y, double z, GEntity *ge = nullptr)
895 {
896 if(_inField == id) return MAX_LC;
897 Field *field = GModel::current()->getFields()->get(_inField);
898 if(!field) {
899 Msg::Warning("Unknown Field %i", _inField);
900 return MAX_LC;
901 }
902 return ((*field)(x + _delta, y, z) + (*field)(x - _delta, y, z) +
903 (*field)(x, y + _delta, z) + (*field)(x, y - _delta, z) +
904 (*field)(x, y, z + _delta) + (*field)(x, y, z - _delta) -
905 6 * (*field)(x, y, z)) /
906 (_delta * _delta);
907 }
908 };
909
910 class MeanField : public Field {
911 private:
912 int _inField;
913 double _delta;
914
915 public:
getName()916 const char *getName() { return "Mean"; }
getDescription()917 std::string getDescription()
918 {
919 return "Return the mean value\n\n"
920 " F = (G(x + delta, y, z) + G(x - delta, y, z) +\n"
921 " G(x, y + delta, z) + G(x, y - delta, z) +\n"
922 " G(x, y, z + delta) + G(x, y, z - delta) +\n"
923 " G(x, y, z)) / 7,\n\n"
924 "where G = Field[InField].";
925 }
MeanField()926 MeanField()
927 {
928 _inField = 1;
929 _delta = CTX::instance()->lc / 1e4;
930
931 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
932 options["Delta"] =
933 new FieldOptionDouble(_delta, "Distance used to compute the mean value");
934
935 // deprecated names
936 options["IField"] =
937 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
938 }
939 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)940 double operator()(double x, double y, double z, GEntity *ge = nullptr)
941 {
942 if(_inField == id) return MAX_LC;
943 Field *field = GModel::current()->getFields()->get(_inField);
944 if(!field) {
945 Msg::Warning("Unknown Field %i", _inField);
946 return MAX_LC;
947 }
948 return ((*field)(x + _delta, y, z) + (*field)(x - _delta, y, z) +
949 (*field)(x, y + _delta, z) + (*field)(x, y - _delta, z) +
950 (*field)(x, y, z + _delta) + (*field)(x, y, z - _delta) +
951 (*field)(x, y, z)) /
952 7;
953 }
954 };
955
956 class MathEvalExpression {
957 private:
958 mathEvaluator *_f;
959 std::set<int> _fields;
960
961 public:
MathEvalExpression()962 MathEvalExpression() : _f(nullptr) {}
~MathEvalExpression()963 ~MathEvalExpression()
964 {
965 if(_f) delete _f;
966 }
set_function(const std::string & f)967 bool set_function(const std::string &f)
968 {
969 // get id numbers of fields appearing in the function
970 _fields.clear();
971 std::size_t i = 0;
972 while(i < f.size()) {
973 std::size_t j = 0;
974 if(f[i] == 'F') {
975 std::string id("");
976 while(i + 1 + j < f.size() && f[i + 1 + j] >= '0' &&
977 f[i + 1 + j] <= '9') {
978 id += f[i + 1 + j];
979 j++;
980 }
981 _fields.insert(atoi(id.c_str()));
982 }
983 i += j + 1;
984 }
985 std::vector<std::string> expressions(1), variables(3 + _fields.size());
986 expressions[0] = f;
987 variables[0] = "x";
988 variables[1] = "y";
989 variables[2] = "z";
990 i = 3;
991 for(auto it = _fields.begin(); it != _fields.end(); it++) {
992 std::ostringstream sstream;
993 sstream << "F" << *it;
994 variables[i++] = sstream.str();
995 }
996 if(_f) delete _f;
997 _f = new mathEvaluator(expressions, variables);
998 if(expressions.empty()) {
999 delete _f;
1000 _f = nullptr;
1001 return false;
1002 }
1003 return true;
1004 }
evaluate(double x,double y,double z)1005 double evaluate(double x, double y, double z)
1006 {
1007 if(!_f) return MAX_LC;
1008 std::vector<double> values(3 + _fields.size()), res(1);
1009 values[0] = x;
1010 values[1] = y;
1011 values[2] = z;
1012 int i = 3;
1013 for(auto it = _fields.begin(); it != _fields.end(); it++) {
1014 Field *field = GModel::current()->getFields()->get(*it);
1015 if(field) {
1016 values[i++] = (*field)(x, y, z);
1017 }
1018 else {
1019 Msg::Warning("Unknown Field %i", *it);
1020 values[i++] = MAX_LC;
1021 }
1022 }
1023 if(_f->eval(values, res))
1024 return res[0];
1025 else
1026 return MAX_LC;
1027 }
1028 };
1029
1030 class MathEvalExpressionAniso {
1031 private:
1032 mathEvaluator *_f[6];
1033 std::set<int> _fields[6];
1034
1035 public:
MathEvalExpressionAniso()1036 MathEvalExpressionAniso()
1037 {
1038 for(int i = 0; i < 6; i++) _f[i] = nullptr;
1039 }
~MathEvalExpressionAniso()1040 ~MathEvalExpressionAniso()
1041 {
1042 for(int i = 0; i < 6; i++)
1043 if(_f[i]) delete _f[i];
1044 }
set_function(int iFunction,const std::string & f)1045 bool set_function(int iFunction, const std::string &f)
1046 {
1047 // get id numbers of fields appearing in the function
1048 _fields[iFunction].clear();
1049 std::size_t i = 0;
1050 while(i < f.size()) {
1051 std::size_t j = 0;
1052 if(f[i] == 'F') {
1053 std::string id("");
1054 while(i + 1 + j < f.size() && f[i + 1 + j] >= '0' &&
1055 f[i + 1 + j] <= '9') {
1056 id += f[i + 1 + j];
1057 j++;
1058 }
1059 _fields[iFunction].insert(atoi(id.c_str()));
1060 }
1061 i += j + 1;
1062 }
1063 std::vector<std::string> expressions(1),
1064 variables(3 + _fields[iFunction].size());
1065 expressions[0] = f;
1066 variables[0] = "x";
1067 variables[1] = "y";
1068 variables[2] = "z";
1069 i = 3;
1070 for(auto it = _fields[iFunction].begin(); it != _fields[iFunction].end();
1071 it++) {
1072 std::ostringstream sstream;
1073 sstream << "F" << *it;
1074 variables[i++] = sstream.str();
1075 }
1076 if(_f[iFunction]) delete _f[iFunction];
1077 _f[iFunction] = new mathEvaluator(expressions, variables);
1078 if(expressions.empty()) {
1079 delete _f[iFunction];
1080 _f[iFunction] = nullptr;
1081 return false;
1082 }
1083 return true;
1084 }
evaluate(double x,double y,double z,SMetric3 & metr)1085 void evaluate(double x, double y, double z, SMetric3 &metr)
1086 {
1087 const int index[6][2] = {{0, 0}, {1, 1}, {2, 2}, {0, 1}, {0, 2}, {1, 2}};
1088 for(int iFunction = 0; iFunction < 6; iFunction++) {
1089 if(!_f[iFunction])
1090 metr(index[iFunction][0], index[iFunction][1]) = MAX_LC;
1091 else {
1092 std::vector<double> values(3 + _fields[iFunction].size()), res(1);
1093 values[0] = x;
1094 values[1] = y;
1095 values[2] = z;
1096 int i = 3;
1097 for(auto it = _fields[iFunction].begin();
1098 it != _fields[iFunction].end(); it++) {
1099 Field *field = GModel::current()->getFields()->get(*it);
1100 if(field) {
1101 values[i++] = (*field)(x, y, z);
1102 }
1103 else {
1104 Msg::Warning("Unknown Field %i", *it);
1105 values[i++] = MAX_LC;
1106 }
1107 }
1108 if(_f[iFunction]->eval(values, res))
1109 metr(index[iFunction][0], index[iFunction][1]) = res[0];
1110 else
1111 metr(index[iFunction][0], index[iFunction][1]) = MAX_LC;
1112 }
1113 }
1114 }
1115 };
1116
1117 class MathEvalField : public Field {
1118 private:
1119 MathEvalExpression _expr;
1120 std::string _f;
1121
1122 public:
MathEvalField()1123 MathEvalField()
1124 {
1125 _f = "F2 + Sin(z)";
1126 options["F"] = new FieldOptionString(
1127 _f, "Mathematical function to evaluate.", &updateNeeded);
1128 }
1129 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1130 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1131 {
1132 double ret = 0;
1133 #pragma omp critical
1134 {
1135 if(updateNeeded) {
1136 if(!_expr.set_function(_f))
1137 Msg::Error("Field %i: invalid matheval expression \"%s\"", this->id,
1138 _f.c_str());
1139 updateNeeded = false;
1140 }
1141 ret = _expr.evaluate(x, y, z);
1142 }
1143 return ret;
1144 }
getName()1145 const char *getName() { return "MathEval"; }
getDescription()1146 std::string getDescription()
1147 {
1148 return "Evaluate a mathematical expression. The expression can contain "
1149 "x, y, z for spatial coordinates, F0, F1, ... for field values, and "
1150 "mathematical functions.";
1151 }
1152 };
1153
1154 class MathEvalFieldAniso : public Field {
1155 private:
1156 MathEvalExpressionAniso _expr;
1157 std::string _f[6];
1158
1159 public:
isotropic() const1160 virtual bool isotropic() const { return false; }
MathEvalFieldAniso()1161 MathEvalFieldAniso()
1162 {
1163 _f[0] = "F2 + Sin(z)";
1164 _f[1] = "F2 + Sin(z)";
1165 _f[2] = "F2 + Sin(z)";
1166 _f[3] = "F2 + Sin(z)";
1167 _f[4] = "F2 + Sin(z)";
1168 _f[5] = "F2 + Sin(z)";
1169
1170 options["M11"] = new FieldOptionString(
1171 _f[0], "Element 11 of the metric tensor", &updateNeeded);
1172 options["M22"] = new FieldOptionString(
1173 _f[1], "Element 22 of the metric tensor", &updateNeeded);
1174 options["M33"] = new FieldOptionString(
1175 _f[2], "Element 33 of the metric tensor", &updateNeeded);
1176 options["M12"] = new FieldOptionString(
1177 _f[3], "Element 12 of the metric tensor", &updateNeeded);
1178 options["M13"] = new FieldOptionString(
1179 _f[4], "Element 13 of the metric tensor", &updateNeeded);
1180 options["M23"] = new FieldOptionString(
1181 _f[5], "Element 23 of the metric tensor", &updateNeeded);
1182
1183 // deprecated names
1184 options["m11"] =
1185 new FieldOptionString(_f[0], "[Deprecated]", &updateNeeded, true);
1186 options["m22"] =
1187 new FieldOptionString(_f[1], "[Deprecated]", &updateNeeded, true);
1188 options["m33"] =
1189 new FieldOptionString(_f[2], "[Deprecated]", &updateNeeded, true);
1190 options["m12"] =
1191 new FieldOptionString(_f[3], "[Deprecated]", &updateNeeded, true);
1192 options["m13"] =
1193 new FieldOptionString(_f[4], "[Deprecated]", &updateNeeded, true);
1194 options["m23"] =
1195 new FieldOptionString(_f[5], "[Deprecated]", &updateNeeded, true);
1196 }
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge=nullptr)1197 void operator()(double x, double y, double z, SMetric3 &metr,
1198 GEntity *ge = nullptr)
1199 {
1200 #pragma omp critical
1201 {
1202 if(updateNeeded) {
1203 for(int i = 0; i < 6; i++) {
1204 if(!_expr.set_function(i, _f[i]))
1205 Msg::Error("Field %i: invalid matheval expression \"%s\"", this->id,
1206 _f[i].c_str());
1207 }
1208 updateNeeded = false;
1209 }
1210 _expr.evaluate(x, y, z, metr);
1211 }
1212 }
operator ()(double x,double y,double z,GEntity * ge=nullptr)1213 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1214 {
1215 SMetric3 metr;
1216 #pragma omp critical
1217 {
1218 if(updateNeeded) {
1219 for(int i = 0; i < 6; i++) {
1220 if(!_expr.set_function(i, _f[i]))
1221 Msg::Error("Field %i: invalid matheval expression \"%s\"", this->id,
1222 _f[i].c_str());
1223 }
1224 updateNeeded = false;
1225 }
1226 _expr.evaluate(x, y, z, metr);
1227 }
1228 return metr(0, 0);
1229 }
getName()1230 const char *getName() { return "MathEvalAniso"; }
getDescription()1231 std::string getDescription()
1232 {
1233 return "Evaluate a metric expression. The expressions can contain "
1234 "x, y, z for spatial coordinates, F0, F1, ... for field values, and "
1235 "mathematical functions.";
1236 }
1237 };
1238
1239 #if defined(WIN32) && !defined(__CYGWIN__)
1240 // windows implementation from
1241 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
1242 class Popen2 {
1243 private:
1244 HANDLE _hIn, _hOut;
1245
1246 public:
Popen2()1247 Popen2()
1248 {
1249 _hIn = nullptr;
1250 _hOut = nullptr;
1251 }
stop()1252 void stop()
1253 {
1254 if(_hIn) {
1255 CloseHandle(_hIn);
1256 CloseHandle(_hOut);
1257 _hIn = _hOut = nullptr;
1258 }
1259 }
started() const1260 bool started() const { return _hIn; }
start(const char * command)1261 bool start(const char *command)
1262 {
1263 stop();
1264 HANDLE hChildStd_OUT_Wr, hChildStd_IN_Rd;
1265 PROCESS_INFORMATION piProcInfo;
1266 SECURITY_ATTRIBUTES saAttr;
1267 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1268 saAttr.bInheritHandle = TRUE;
1269 saAttr.lpSecurityDescriptor = nullptr;
1270 if(!CreatePipe(&_hIn, &hChildStd_OUT_Wr, &saAttr, 0))
1271 Msg::Error("StdoutRd CreatePipe");
1272 if(!CreatePipe(&hChildStd_IN_Rd, &_hOut, &saAttr, 0))
1273 Msg::Error("Stdin CreatePipe");
1274 if(!CreatePipe(&_hIn, &hChildStd_OUT_Wr, &saAttr, 0))
1275 Msg::Error("StdoutRd CreatePipe");
1276 if(!SetHandleInformation(_hIn, HANDLE_FLAG_INHERIT, 0))
1277 Msg::Error("Stdout SetHandleInformation");
1278 STARTUPINFO siStartInfo;
1279 BOOL bSuccess = FALSE;
1280 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1281 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
1282 siStartInfo.cb = sizeof(STARTUPINFO);
1283 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1284 siStartInfo.hStdOutput = hChildStd_OUT_Wr;
1285 siStartInfo.hStdInput = hChildStd_IN_Rd;
1286 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1287 bSuccess = CreateProcess(nullptr, (char *)command, nullptr, nullptr, TRUE,
1288 0, nullptr, nullptr, &siStartInfo, &piProcInfo);
1289 if(!bSuccess) {
1290 Msg::Error("Child process creation failed %i", GetLastError());
1291 _hIn = _hOut = nullptr;
1292 return false;
1293 }
1294 CloseHandle(piProcInfo.hProcess);
1295 CloseHandle(piProcInfo.hThread);
1296 return true;
1297 }
read(void * data,size_t size)1298 bool read(void *data, size_t size)
1299 {
1300 if(!_hIn) return false;
1301 DWORD nSuccess = 0;
1302 ReadFile(_hIn, data, size, &nSuccess, nullptr);
1303 return nSuccess == size;
1304 }
write(void * data,size_t size)1305 bool write(void *data, size_t size)
1306 {
1307 if(!_hOut) return false;
1308 DWORD nSuccess = 0;
1309 WriteFile(_hOut, data, size, &nSuccess, nullptr);
1310 return nSuccess == size;
1311 }
~Popen2()1312 ~Popen2() { stop(); }
1313 };
1314
1315 #else // unix
1316
1317 class Popen2 {
1318 private:
1319 int _fdIn, _fdOut;
1320
1321 public:
Popen2()1322 Popen2() { _fdIn = _fdOut = -1; }
stop()1323 void stop()
1324 {
1325 if(_fdIn != -1) {
1326 ::close(_fdIn);
1327 ::close(_fdOut);
1328 _fdIn = _fdOut = -1;
1329 }
1330 }
started() const1331 bool started() const { return _fdIn; }
start(const char * command)1332 bool start(const char *command)
1333 {
1334 stop();
1335 int p_stdin[2], p_stdout[2];
1336 if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0) return false;
1337 int pid = fork();
1338 if(pid < 0)
1339 return false;
1340 else if(pid == 0) {
1341 close(p_stdin[1]);
1342 dup2(p_stdin[0], 0);
1343 close(p_stdout[0]);
1344 dup2(p_stdout[1], 1);
1345 execl("/bin/sh", "sh", "-c", command, nullptr);
1346 perror("execl");
1347 exit(0);
1348 }
1349 _fdOut = p_stdin[1];
1350 _fdIn = p_stdout[0];
1351 return true;
1352 }
read(void * data,size_t size)1353 bool read(void *data, size_t size)
1354 {
1355 return ::read(_fdIn, data, size) == (ssize_t)size;
1356 }
write(void * data,size_t size)1357 bool write(void *data, size_t size)
1358 {
1359 return ::write(_fdOut, data, size) == (ssize_t)size;
1360 }
~Popen2()1361 ~Popen2() { stop(); }
1362 };
1363 #endif
1364
1365 class ExternalProcessField : public Field {
1366 private:
1367 std::string _cmdLine;
1368 Popen2 _pipes;
closePipes()1369 void closePipes()
1370 {
1371 if(_pipes.started()) {
1372 double xyz[3] = {std::numeric_limits<double>::quiet_NaN(),
1373 std::numeric_limits<double>::quiet_NaN(),
1374 std::numeric_limits<double>::quiet_NaN()};
1375 _pipes.write((void *)xyz, 3 * sizeof(double));
1376 _pipes.stop();
1377 }
1378 }
1379
1380 public:
ExternalProcessField()1381 ExternalProcessField()
1382 {
1383 _cmdLine = "";
1384
1385 options["CommandLine"] =
1386 new FieldOptionString(_cmdLine, "Command line to launch", &updateNeeded);
1387 }
~ExternalProcessField()1388 ~ExternalProcessField() { closePipes(); }
1389 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1390 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1391 {
1392 double xyz[3] = {x, y, z};
1393 double f;
1394 if(updateNeeded) {
1395 closePipes();
1396 _pipes.start(_cmdLine.c_str());
1397 updateNeeded = false;
1398 }
1399 if(!_pipes.write((void *)xyz, 3 * sizeof(double)) ||
1400 !_pipes.read((void *)&f, sizeof(double))) {
1401 f = 1e22; // std::numeric_limits<double>::max();
1402 }
1403 return f;
1404 }
getName()1405 const char *getName() { return "ExternalProcess"; }
getDescription()1406 std::string getDescription()
1407 {
1408 return "**This Field is experimental**\n"
1409 "Call an external process that received coordinates triple (x,y,z) "
1410 "as binary double precision numbers on stdin and is supposed to "
1411 "write the "
1412 "field value on stdout as a binary double precision number.\n"
1413 "NaN,NaN,NaN is sent as coordinate to indicate the end of the "
1414 "process.\n"
1415 "\n"
1416 "Example of client (python2):\n"
1417 "import os\n"
1418 "import struct\n"
1419 "import math\n"
1420 "import sys\n"
1421 "if sys.platform == \"win32\" :\n"
1422 " import msvcrt\n"
1423 " msvcrt.setmode(0, os.O_BINARY)\n"
1424 " msvcrt.setmode(1, os.O_BINARY)\n"
1425 "while(True):\n"
1426 " xyz = struct.unpack(\"ddd\", os.read(0,24))\n"
1427 " if math.isnan(xyz[0]):\n"
1428 " break\n"
1429 " f = 0.001 + xyz[1]*0.009\n"
1430 " os.write(1,struct.pack(\"d\",f))\n"
1431 "\n"
1432 "Example of client (python3):\n"
1433 "import struct\n"
1434 "import sys\n"
1435 "import math\n"
1436 "while(True):\n"
1437 " xyz = struct.unpack(\"ddd\", sys.stdin.buffer.read(24))\n"
1438 " if math.isnan(xyz[0]):\n"
1439 " break\n"
1440 " f = 0.001 + xyz[1]*0.009\n"
1441 " sys.stdout.buffer.write(struct.pack(\"d\",f))\n"
1442 " sys.stdout.flush()\n"
1443 "\n"
1444 "Example of client (c, unix):\n"
1445 "#include <unistd.h>\n"
1446 "int main(int argc, char **argv) {\n"
1447 " double xyz[3];\n"
1448 " while(read(STDIN_FILENO, &xyz, 3*sizeof(double)) == "
1449 "3*sizeof(double)) {\n"
1450 " if (xyz[0] != xyz[0]) break; //nan\n"
1451 " double f = 0.001 + 0.009 * xyz[1];\n"
1452 " write(STDOUT_FILENO, &f, sizeof(double));\n"
1453 " }\n"
1454 " return 0;\n"
1455 "}\n"
1456 "\n"
1457 "Example of client (c, windows):\n"
1458 "#include <stdio.h>\n"
1459 "#include <io.h>\n"
1460 "#include <fcntl.h>\n"
1461 "int main(int argc, char **argv) {\n"
1462 " double xyz[3];\n"
1463 " setmode(fileno(stdin),O_BINARY);\n"
1464 " setmode(fileno(stdout),O_BINARY);\n"
1465 " while(read(fileno(stdin), &xyz, 3*sizeof(double)) == "
1466 "3*sizeof(double)) {\n"
1467 " if (xyz[0] != xyz[0])\n"
1468 " break;\n"
1469 " double f = f = 0.01 + 0.09 * xyz[1];\n"
1470 " write(fileno(stdout), &f, sizeof(double));\n"
1471 " }\n"
1472 "}\n";
1473 }
1474 };
1475
1476 class ParametricField : public Field {
1477 private:
1478 MathEvalExpression _expr[3];
1479 std::string _f[3];
1480 int _inField;
1481
1482 public:
ParametricField()1483 ParametricField()
1484 {
1485 _inField = 1;
1486
1487 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
1488 options["FX"] = new FieldOptionString(
1489 _f[0], "X component of parametric function", &updateNeeded);
1490 options["FY"] = new FieldOptionString(
1491 _f[1], "Y component of parametric function", &updateNeeded);
1492 options["FZ"] = new FieldOptionString(
1493 _f[2], "Z component of parametric function", &updateNeeded);
1494
1495 // deprecated names
1496 options["IField"] =
1497 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
1498 }
getDescription()1499 std::string getDescription()
1500 {
1501 return "Evaluate Field[InField] in parametric coordinates:\n\n"
1502 " F = Field[InField](FX,FY,FZ)\n\n"
1503 "See the MathEval Field help to get a description of valid FX, FY "
1504 "and FZ expressions.";
1505 }
1506 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1507 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1508 {
1509 if(updateNeeded) {
1510 for(int i = 0; i < 3; i++) {
1511 if(!_expr[i].set_function(_f[i]))
1512 Msg::Error("Field %i: invalid matheval expression \"%s\"", this->id,
1513 _f[i].c_str());
1514 }
1515 updateNeeded = false;
1516 }
1517 if(_inField == id) return MAX_LC;
1518 Field *field = GModel::current()->getFields()->get(_inField);
1519 if(!field) {
1520 Msg::Warning("Unknown Field %i", _inField);
1521 return MAX_LC;
1522 }
1523 return (*field)(_expr[0].evaluate(x, y, z), _expr[1].evaluate(x, y, z),
1524 _expr[2].evaluate(x, y, z));
1525 }
getName()1526 const char *getName() { return "Param"; }
1527 };
1528
1529 #if defined(HAVE_POST)
1530
1531 class PostViewField : public Field {
1532 private:
1533 int _viewIndex, _viewTag;
1534 bool _cropNegativeValues, _useClosest;
1535
1536 public:
PostViewField()1537 PostViewField()
1538 {
1539 _viewIndex = 0;
1540 _viewTag = -1;
1541 _cropNegativeValues = true;
1542 _useClosest = true;
1543
1544 options["ViewIndex"] =
1545 new FieldOptionInt(_viewIndex, "Post-processing view index");
1546 options["ViewTag"] =
1547 new FieldOptionInt(_viewTag, "Post-processing view tag");
1548 options["CropNegativeValues"] = new FieldOptionBool(
1549 _cropNegativeValues, "return MAX_LC instead of a negative value (this "
1550 "option is needed for backward compatibility with "
1551 "the BackgroundMesh option");
1552 options["UseClosest"] =
1553 new FieldOptionBool(_useClosest, "Use value at closest node if "
1554 "no exact match is found");
1555
1556 // deprecated names
1557 options["IView"] =
1558 new FieldOptionInt(_viewIndex, "[Deprecated]", nullptr, true);
1559 }
~PostViewField()1560 ~PostViewField()
1561 {
1562 }
getView() const1563 PView *getView() const
1564 {
1565 PView *v = nullptr;
1566 if(_viewTag >= 0) {
1567 v = PView::getViewByTag(_viewTag);
1568 if(!v) {
1569 Msg::Error("View with tag %d does not exist", _viewTag);
1570 }
1571 }
1572 if(!v) {
1573 if(_viewIndex < 0 || _viewIndex >= (int)PView::list.size()) {
1574 Msg::Error("View with index %d does not exist", _viewIndex);
1575 return nullptr;
1576 }
1577 v = PView::list[_viewIndex];
1578 }
1579 if(v->getData()->hasModel(GModel::current())) {
1580 Msg::Error(
1581 "Cannot use view based on current mesh for background mesh: you might"
1582 " want to use a list-based view (.pos file) instead");
1583 return nullptr;
1584 }
1585 return v;
1586 }
isotropic() const1587 virtual bool isotropic() const
1588 {
1589 PView *v = getView();
1590 if(v && v->getData()->getNumTensors()) return false;
1591 return true;
1592 }
numComponents() const1593 virtual int numComponents() const
1594 {
1595 PView *v = getView();
1596 if(v && v->getData()->getNumTensors()) return 9;
1597 if(v && v->getData()->getNumVectors()) return 3;
1598 return 1;
1599 }
operator ()(double x,double y,double z,GEntity * ge=nullptr)1600 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1601 {
1602 double l = MAX_LC;
1603 PView *v = getView();
1604 if(!v) return l;
1605 double dist = _useClosest ? -1. : 0.;
1606 if(numComponents() == 3) {
1607 double values[3];
1608 if(!v->getData()->searchVectorClosest(x, y, z, dist, values, 0)) {
1609 Msg::Warning("No vector element found containing point "
1610 "(%g,%g,%g) in PostView field (for norm)", x, y, z);
1611 }
1612 else {
1613 l = sqrt(values[0] * values[0] + values[1] * values[1] +
1614 values[2] * values[2]);
1615 }
1616 }
1617 else if(numComponents() == 1) {
1618 if(!v->getData()->searchScalarClosest(x, y, z, dist, &l, 0)) {
1619 Msg::Warning("No scalar element found containing point "
1620 "(%g,%g,%g) in PostView field", x, y, z);
1621 }
1622 }
1623 else {
1624 Msg::Warning("No vector or scalar value found in PostView field");
1625 }
1626
1627 if(l <= 0 && _cropNegativeValues) return MAX_LC;
1628 return l;
1629 }
operator ()(double x,double y,double z,SVector3 & v,GEntity * ge=0)1630 void operator()(double x, double y, double z, SVector3 &v, GEntity *ge = 0)
1631 {
1632 PView *vie = getView();
1633 if(!vie) {
1634 v.data()[0] = MAX_LC;
1635 v.data()[1] = MAX_LC;
1636 v.data()[2] = MAX_LC;
1637 return;
1638 }
1639 double dist = _useClosest ? -1. : 0.;
1640 if(numComponents() == 3) {
1641 double values[3];
1642 if(!vie->getData()->searchVectorClosest(x, y, z, dist, values, 0)) {
1643 Msg::Warning("No vector element found containing point "
1644 "(%g,%g,%g) in PostView field", x, y, z);
1645 }
1646 else {
1647 v = SVector3(values[0], values[1], values[2]);
1648 }
1649 }
1650 else {
1651 Msg::Warning("No vector value found in PostView field");
1652 }
1653 }
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge=nullptr)1654 void operator()(double x, double y, double z, SMetric3 &metr,
1655 GEntity *ge = nullptr)
1656 {
1657 PView *v = getView();
1658 if(!v) return;
1659 double dist = _useClosest ? -1. : 0.;
1660 double l[9] = {0., 0., 0., 0., 0., 0., 0., 0., 0.};
1661 if(!v->getData()->searchTensorClosest(x, y, z, dist, l)) {
1662 Msg::Warning("No tensor element found containing point "
1663 "(%g,%g,%g) in PostView field", x, y, z);
1664 }
1665 else {
1666 metr(0, 0) = l[0];
1667 metr(0, 1) = l[1];
1668 metr(0, 2) = l[2];
1669 metr(1, 0) = l[3];
1670 metr(1, 1) = l[4];
1671 metr(1, 2) = l[5];
1672 metr(2, 0) = l[6];
1673 metr(2, 1) = l[7];
1674 metr(2, 2) = l[8];
1675 }
1676 }
getName()1677 const char *getName() { return "PostView"; }
getDescription()1678 std::string getDescription()
1679 {
1680 return "Evaluate the post processing view with index ViewIndex, or "
1681 "with tag ViewTag if ViewTag is positive.";
1682 }
1683 };
1684
1685 #endif
1686
1687 class MinAnisoField : public Field {
1688 private:
1689 std::list<int> _fieldIds;
1690
1691 public:
MinAnisoField()1692 MinAnisoField()
1693 {
1694 options["FieldsList"] =
1695 new FieldOptionList(_fieldIds, "Field indices", &updateNeeded);
1696 }
isotropic() const1697 virtual bool isotropic() const { return false; }
getDescription()1698 std::string getDescription()
1699 {
1700 return "Take the intersection of a list of possibly anisotropic fields.";
1701 }
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge=nullptr)1702 virtual void operator()(double x, double y, double z, SMetric3 &metr,
1703 GEntity *ge = nullptr)
1704 {
1705 SMetric3 v(1. / MAX_LC);
1706 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1707 Field *f = (GModel::current()->getFields()->get(*it));
1708 if(!f) Msg::Warning("Unknown Field %i", *it);
1709 SMetric3 ff;
1710 if(f && *it != id) {
1711 if(f->isotropic()) {
1712 double l = (*f)(x, y, z, ge);
1713 ff = SMetric3(1. / (l * l));
1714 }
1715 else {
1716 (*f)(x, y, z, ff, ge);
1717 }
1718 v = intersection_conserve_mostaniso(v, ff);
1719 }
1720 }
1721 metr = v;
1722 }
operator ()(double x,double y,double z,GEntity * ge=nullptr)1723 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1724 {
1725 SMetric3 metr(1. / MAX_LC);
1726 double v = MAX_LC;
1727 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1728 Field *f = (GModel::current()->getFields()->get(*it));
1729 if(!f) Msg::Warning("Unknown Field %i", *it);
1730 SMetric3 m;
1731 if(f && *it != id) {
1732 if(!f->isotropic()) { (*f)(x, y, z, m, ge); }
1733 else {
1734 double L = (*f)(x, y, z, ge);
1735 for(int i = 0; i < 3; i++) m(i, i) = 1. / (L * L);
1736 }
1737 }
1738 metr = intersection(metr, m);
1739 }
1740 fullMatrix<double> V(3, 3);
1741 fullVector<double> S(3);
1742 metr.eig(V, S, 1);
1743 double val = sqrt(1. / S(2)); // S(2) is largest eigenvalue
1744 return std::min(v, val);
1745 }
getName()1746 const char *getName() { return "MinAniso"; }
1747 };
1748
1749 class IntersectAnisoField : public Field {
1750 private:
1751 std::list<int> _fieldIds;
1752
1753 public:
IntersectAnisoField()1754 IntersectAnisoField()
1755 {
1756 options["FieldsList"] =
1757 new FieldOptionList(_fieldIds, "Field indices", &updateNeeded);
1758 }
isotropic() const1759 virtual bool isotropic() const { return false; }
getDescription()1760 std::string getDescription()
1761 {
1762 return "Take the intersection of 2 anisotropic fields according to "
1763 "Alauzet.";
1764 }
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge=nullptr)1765 virtual void operator()(double x, double y, double z, SMetric3 &metr,
1766 GEntity *ge = nullptr)
1767 {
1768 // check if _fieldIds contains 2 elements other error message
1769 SMetric3 v;
1770 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1771 Field *f = (GModel::current()->getFields()->get(*it));
1772 if(!f) Msg::Warning("Unknown Field %i", *it);
1773 SMetric3 ff;
1774 if(f && *it != id) {
1775 if(f->isotropic()) {
1776 double l = (*f)(x, y, z, ge);
1777 ff = SMetric3(1. / (l * l));
1778 }
1779 else {
1780 (*f)(x, y, z, ff, ge);
1781 }
1782 if(it == _fieldIds.begin())
1783 v = ff;
1784 else
1785 v = intersection_alauzet(v, ff);
1786 }
1787 }
1788 metr = v;
1789 }
operator ()(double x,double y,double z,GEntity * ge=nullptr)1790 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1791 {
1792 // check if _fieldIds contains 2 elements other error message
1793 SMetric3 metr;
1794 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1795 Field *f = (GModel::current()->getFields()->get(*it));
1796 if(!f) Msg::Warning("Unknown Field %i", *it);
1797 SMetric3 m;
1798 if(f && *it != id) {
1799 if(!f->isotropic()) { (*f)(x, y, z, m, ge); }
1800 else {
1801 double L = (*f)(x, y, z, ge);
1802 for(int i = 0; i < 3; i++) m(i, i) = 1. / (L * L);
1803 }
1804 }
1805 if(it == _fieldIds.begin())
1806 metr = m;
1807 else
1808 metr = intersection_alauzet(metr, m);
1809 }
1810 fullMatrix<double> V(3, 3);
1811 fullVector<double> S(3);
1812 metr.eig(V, S, 1);
1813 return sqrt(1. / S(2)); // S(2) is largest eigenvalue
1814 }
getName()1815 const char *getName() { return "IntersectAniso"; }
1816 };
1817
1818 class MinField : public Field {
1819 private:
1820 std::list<int> _fieldIds;
1821 std::vector<Field*> _fields;
1822
1823 public:
MinField()1824 MinField()
1825 {
1826 options["FieldsList"] =
1827 new FieldOptionList(_fieldIds, "Field indices", &updateNeeded);
1828 }
getDescription()1829 std::string getDescription()
1830 {
1831 return "Take the minimum value of a list of fields.";
1832 }
1833 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1834 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1835 {
1836 #pragma omp critical
1837 {
1838 if(updateNeeded) {
1839 _fields.clear();
1840 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1841 Field *f = (GModel::current()->getFields()->get(*it));
1842 if(!f) Msg::Warning("Unknown Field %i", *it);
1843 if(f && *it != id) _fields.push_back(f);
1844 }
1845 updateNeeded = false;
1846 }
1847 }
1848
1849 double v = MAX_LC;
1850 for(auto f : _fields) {
1851 if(f->isotropic())
1852 v = std::min(v, (*f)(x, y, z, ge));
1853 else {
1854 SMetric3 ff;
1855 (*f)(x, y, z, ff, ge);
1856 fullMatrix<double> V(3, 3);
1857 fullVector<double> S(3);
1858 ff.eig(V, S, 1);
1859 v = std::min(v, sqrt(1. / S(2))); // S(2) is largest eigenvalue
1860 }
1861 }
1862 return v;
1863 }
getName()1864 const char *getName() { return "Min"; }
1865 };
1866
1867 class MaxField : public Field {
1868 private:
1869 std::list<int> _fieldIds;
1870 std::vector<Field*> _fields;
1871
1872 public:
MaxField()1873 MaxField()
1874 {
1875 options["FieldsList"] =
1876 new FieldOptionList(_fieldIds, "Field indices", &updateNeeded);
1877 }
getDescription()1878 std::string getDescription()
1879 {
1880 return "Take the maximum value of a list of fields.";
1881 }
1882 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1883 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1884 {
1885 #pragma omp critical
1886 {
1887 if(updateNeeded) {
1888 _fields.clear();
1889 for(auto it = _fieldIds.begin(); it != _fieldIds.end(); it++) {
1890 Field *f = (GModel::current()->getFields()->get(*it));
1891 if(!f) Msg::Warning("Unknown Field %i", *it);
1892 if(f && *it != id) _fields.push_back(f);
1893 }
1894 updateNeeded = false;
1895 }
1896 }
1897
1898 double v = -MAX_LC;
1899 for(auto f : _fields) {
1900 if(f->isotropic())
1901 v = std::max(v, (*f)(x, y, z, ge));
1902 else {
1903 SMetric3 ff;
1904 (*f)(x, y, z, ff, ge);
1905 fullMatrix<double> V(3, 3);
1906 fullVector<double> S(3);
1907 ff.eig(V, S, 1);
1908 v = std::max(v, sqrt(1. / S(0))); // S(0) is smallest eigenvalue
1909 }
1910 }
1911 return v;
1912 }
getName()1913 const char *getName() { return "Max"; }
1914 };
1915
1916 class RestrictField : public Field {
1917 private:
1918 int _inField;
1919 bool _boundary;
1920 std::list<int> _pointTags, _curveTags, _surfaceTags, _volumeTags;
1921
1922 public:
RestrictField()1923 RestrictField()
1924 {
1925 _inField = 1;
1926 _boundary = true;
1927
1928 options["InField"] = new FieldOptionInt(_inField, "Input field tag");
1929 options["PointsList"] = new FieldOptionList(_pointTags, "Point tags");
1930 options["CurvesList"] = new FieldOptionList(_curveTags, "Curve tags");
1931 options["SurfacesList"] = new FieldOptionList(_surfaceTags, "Surface tags");
1932 options["VolumesList"] = new FieldOptionList(_volumeTags, "Volume tags");
1933 options["IncludeBoundary"] =
1934 new FieldOptionBool(_boundary, "Include the boundary of the entities");
1935
1936 // deprecated names
1937 options["IField"] =
1938 new FieldOptionInt(_inField, "[Deprecated]", nullptr, true);
1939 options["VerticesList"] =
1940 new FieldOptionList(_pointTags, "[Deprecated]", nullptr, true);
1941 options["EdgesList"] =
1942 new FieldOptionList(_curveTags, "[Deprecated]", nullptr, true);
1943 options["FacesList"] =
1944 new FieldOptionList(_surfaceTags, "[Deprecated]", nullptr, true);
1945 options["RegionsList"] =
1946 new FieldOptionList(_volumeTags, "[Deprecated]", nullptr, true);
1947 }
getDescription()1948 std::string getDescription()
1949 {
1950 return "Restrict the application of a field to a given list of geometrical "
1951 "points, curves, surfaces or volumes (as well as their boundaries "
1952 "if IncludeBoundary is set).";
1953 }
1954 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)1955 double operator()(double x, double y, double z, GEntity *ge = nullptr)
1956 {
1957 if(_inField == id) return MAX_LC;
1958 Field *f = GModel::current()->getFields()->get(_inField);
1959 if(!f) {
1960 Msg::Warning("Unknown Field %i", _inField);
1961 return MAX_LC;
1962 }
1963 if(!ge) return (*f)(x, y, z);
1964 if((ge->dim() == 0 && std::find(_pointTags.begin(), _pointTags.end(),
1965 ge->tag()) != _pointTags.end()) ||
1966 (ge->dim() == 1 && std::find(_curveTags.begin(), _curveTags.end(),
1967 ge->tag()) != _curveTags.end()) ||
1968 (ge->dim() == 2 && std::find(_surfaceTags.begin(), _surfaceTags.end(),
1969 ge->tag()) != _surfaceTags.end()) ||
1970 (ge->dim() == 3 && std::find(_volumeTags.begin(), _volumeTags.end(),
1971 ge->tag()) != _volumeTags.end()))
1972 return (*f)(x, y, z);
1973 if(_boundary) {
1974 if(ge->dim() <= 2) {
1975 std::list<GRegion *> volumes = ge->regions();
1976 for(auto v : volumes) {
1977 if(std::find(_volumeTags.begin(), _volumeTags.end(), v->tag()) !=
1978 _volumeTags.end()) return (*f)(x, y, z);
1979 }
1980 }
1981 if(ge->dim() <= 1) {
1982 std::vector<GFace *> surfaces = ge->faces();
1983 for(auto s : surfaces) {
1984 if(std::find(_surfaceTags.begin(), _surfaceTags.end(), s->tag()) !=
1985 _surfaceTags.end()) return (*f)(x, y, z);
1986 }
1987 }
1988 if(ge->dim() == 0) {
1989 std::vector<GEdge *> curves = ge->edges();
1990 for(auto c : curves) {
1991 if(std::find(_curveTags.begin(), _curveTags.end(), c->tag()) !=
1992 _curveTags.end()) return (*f)(x, y, z);
1993 }
1994 }
1995 }
1996 return MAX_LC;
1997 }
getName()1998 const char *getName() { return "Restrict"; }
1999 };
2000
2001 class ConstantField : public Field {
2002 private:
2003 double _vIn, _vOut;
2004 bool _boundary;
2005 std::list<int> _pointTags, _curveTags, _surfaceTags, _volumeTags;
2006
2007 public:
ConstantField()2008 ConstantField()
2009 {
2010 _vIn = _vOut = MAX_LC;
2011 _boundary = true;
2012
2013 options["VIn"] = new FieldOptionDouble(_vIn, "Value inside the entities");
2014 options["VOut"] = new FieldOptionDouble(_vOut, "Value outside the entities");
2015 options["PointsList"] = new FieldOptionList(_pointTags, "Point tags");
2016 options["CurvesList"] = new FieldOptionList(_curveTags, "Curve tags");
2017 options["SurfacesList"] = new FieldOptionList(_surfaceTags, "Surface tags");
2018 options["VolumesList"] = new FieldOptionList(_volumeTags, "Volume tags");
2019 options["IncludeBoundary"] =
2020 new FieldOptionBool(_boundary, "Include the boundary of the entities");
2021 }
getDescription()2022 std::string getDescription()
2023 {
2024 return "Return VIn when inside the entities (and on their boundary if "
2025 "IncludeBoundary is set), and VOut outside.";
2026 }
2027 using Field::operator();
operator ()(double x,double y,double z,GEntity * ge=nullptr)2028 double operator()(double x, double y, double z, GEntity *ge = nullptr)
2029 {
2030 if(!ge) return MAX_LC;
2031 if((ge->dim() == 0 && std::find(_pointTags.begin(), _pointTags.end(),
2032 ge->tag()) != _pointTags.end()) ||
2033 (ge->dim() == 1 && std::find(_curveTags.begin(), _curveTags.end(),
2034 ge->tag()) != _curveTags.end()) ||
2035 (ge->dim() == 2 && std::find(_surfaceTags.begin(), _surfaceTags.end(),
2036 ge->tag()) != _surfaceTags.end()) ||
2037 (ge->dim() == 3 && std::find(_volumeTags.begin(), _volumeTags.end(),
2038 ge->tag()) != _volumeTags.end()))
2039 return _vIn;
2040 if(_boundary) {
2041 if(ge->dim() <= 2) {
2042 std::list<GRegion *> volumes = ge->regions();
2043 for(auto v : volumes) {
2044 if(std::find(_volumeTags.begin(), _volumeTags.end(), v->tag()) !=
2045 _volumeTags.end()) return _vIn;
2046 }
2047 }
2048 if(ge->dim() <= 1) {
2049 std::vector<GFace *> surfaces = ge->faces();
2050 for(auto s : surfaces) {
2051 if(std::find(_surfaceTags.begin(), _surfaceTags.end(), s->tag()) !=
2052 _surfaceTags.end()) return _vIn;
2053 }
2054 }
2055 if(ge->dim() == 0) {
2056 std::vector<GEdge *> curves = ge->edges();
2057 for(auto c : curves) {
2058 if(std::find(_curveTags.begin(), _curveTags.end(), c->tag()) !=
2059 _curveTags.end()) return _vIn;
2060 }
2061 }
2062 }
2063 return _vOut;
2064 }
getName()2065 const char *getName() { return "Constant"; }
2066 };
2067
2068 struct AttractorInfo {
AttractorInfoAttractorInfo2069 AttractorInfo(int a = 0, int b = 0, double c = 0, double d = 0)
2070 : ent(a), dim(b), u(c), v(d)
2071 {
2072 }
2073 int ent, dim;
2074 double u, v;
2075 };
2076
2077 #if defined(HAVE_ANN)
2078
2079 class AttractorAnisoCurveField : public Field {
2080 private:
2081 ANNkd_tree *_kdTree;
2082 ANNpointArray _zeroNodes;
2083 ANNidxArray _index;
2084 ANNdistArray _dist;
2085 std::list<int> _curveTags;
2086 double _dMin, _dMax, _lMinTangent, _lMaxTangent, _lMinNormal, _lMaxNormal;
2087 int _sampling;
2088 std::vector<SVector3> _tg;
2089
2090 public:
AttractorAnisoCurveField()2091 AttractorAnisoCurveField() : _kdTree(nullptr), _zeroNodes(nullptr)
2092 {
2093 _index = new ANNidx[1];
2094 _dist = new ANNdist[1];
2095 _sampling = 20;
2096 updateNeeded = true;
2097 _dMin = 0.1;
2098 _dMax = 0.5;
2099 _lMinNormal = 0.05;
2100 _lMinTangent = 0.5;
2101 _lMaxNormal = 0.5;
2102 _lMaxTangent = 0.5;
2103
2104 options["CurvesList"] = new FieldOptionList(
2105 _curveTags, "Tags of curves in the geometric model", &updateNeeded);
2106 options["Sampling"] = new FieldOptionInt(
2107 _sampling, "Number of sampling points on each curve",
2108 &updateNeeded);
2109 options["DistMin"] = new FieldOptionDouble(
2110 _dMin, "Minimum distance, below this distance from the curves, "
2111 "prescribe the minimum mesh sizes");
2112 options["DistMax"] = new FieldOptionDouble(
2113 _dMax, "Maxmium distance, above this distance from the curves, prescribe "
2114 "the maximum mesh sizes");
2115 options["SizeMinTangent"] = new FieldOptionDouble(
2116 _lMinTangent, "Minimum mesh size in the direction tangeant to the "
2117 "closest curve");
2118 options["SizeMaxTangent"] = new FieldOptionDouble(
2119 _lMaxTangent, "Maximum mesh size in the direction tangeant to the "
2120 "closest curve");
2121 options["SizeMinNormal"] = new FieldOptionDouble(
2122 _lMinNormal, "Minimum mesh size in the direction normal to the "
2123 "closest curve");
2124 options["SizeMaxNormal"] = new FieldOptionDouble(
2125 _lMaxNormal, "Maximum mesh size in the direction normal to the "
2126 "closest curve");
2127
2128 // deprecated names
2129 options["EdgesList"] =
2130 new FieldOptionList(_curveTags, "[Deprecated]", &updateNeeded, true);
2131 options["NNodesByEdge"] =
2132 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2133 options["dMin"] =
2134 new FieldOptionDouble(_dMin, "[Deprecated]", nullptr, true);
2135 options["dMax"] =
2136 new FieldOptionDouble(_dMax, "[Deprecated]", nullptr, true);
2137 options["lMinTangent"] =
2138 new FieldOptionDouble(_lMinTangent, "[Deprecated]", nullptr, true);
2139 options["lMaxTangent"] =
2140 new FieldOptionDouble(_lMaxTangent, "[Deprecated]", nullptr, true);
2141 options["lMinNormal"] =
2142 new FieldOptionDouble(_lMinNormal, "[Deprecated]", nullptr, true);
2143 options["lMaxNormal"] =
2144 new FieldOptionDouble(_lMaxNormal, "[Deprecated]", nullptr, true);
2145 options["NumPointsPerCurve"] =
2146 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2147
2148 // make sure all internal GEO CAD data has been synced with GModel
2149 GModel::current()->getGEOInternals()->synchronize(GModel::current());
2150 }
isotropic() const2151 virtual bool isotropic() const { return false; }
~AttractorAnisoCurveField()2152 ~AttractorAnisoCurveField()
2153 {
2154 if(_kdTree) delete _kdTree;
2155 if(_zeroNodes) annDeallocPts(_zeroNodes);
2156 delete[] _index;
2157 delete[] _dist;
2158 }
getName()2159 const char *getName() { return "AttractorAnisoCurve"; }
getDescription()2160 std::string getDescription()
2161 {
2162 return "Compute the distance to the given curves and specify the mesh size "
2163 "independently in the direction normal and parallel to the nearest "
2164 "curve. For efficiency each curve is replaced by a set of Sampling "
2165 "points, to which the distance is actually computed.";
2166 }
update()2167 void update()
2168 {
2169 if(_zeroNodes) {
2170 annDeallocPts(_zeroNodes);
2171 delete _kdTree;
2172 }
2173 int totpoints = _sampling * _curveTags.size();
2174 if(totpoints) { _zeroNodes = annAllocPts(totpoints, 3); }
2175 _tg.resize(totpoints);
2176 int k = 0;
2177 for(auto it = _curveTags.begin(); it != _curveTags.end(); ++it) {
2178 GEdge *e = GModel::current()->getEdgeByTag(*it);
2179 if(e) {
2180 for(int i = 0; i < _sampling; i++) {
2181 double u = (double)i / (_sampling - 1);
2182 Range<double> b = e->parBounds(0);
2183 double t = b.low() + u * (b.high() - b.low());
2184 GPoint gp = e->point(t);
2185 SVector3 d = e->firstDer(t);
2186 _zeroNodes[k][0] = gp.x();
2187 _zeroNodes[k][1] = gp.y();
2188 _zeroNodes[k][2] = gp.z();
2189 _tg[k] = d;
2190 _tg[k].normalize();
2191 k++;
2192 }
2193 }
2194 else {
2195 Msg::Warning("Unknown curve %d", *it);
2196 }
2197 }
2198 _kdTree = new ANNkd_tree(_zeroNodes, totpoints, 3);
2199 updateNeeded = false;
2200 }
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge=nullptr)2201 void operator()(double x, double y, double z, SMetric3 &metr,
2202 GEntity *ge = nullptr)
2203 {
2204 if(updateNeeded) update();
2205 double xyz[3] = {x, y, z};
2206 #pragma omp critical // avoid crash (still incorrect) - use Distance instead
2207 _kdTree->annkSearch(xyz, 1, _index, _dist);
2208 double d = sqrt(_dist[0]);
2209 double lTg = d < _dMin ? _lMinTangent :
2210 d > _dMax ? _lMaxTangent :
2211 _lMinTangent + (_lMaxTangent - _lMinTangent) *
2212 (d - _dMin) / (_dMax - _dMin);
2213 double lN = d < _dMin ? _lMinNormal :
2214 d > _dMax ? _lMaxNormal :
2215 _lMinNormal + (_lMaxNormal - _lMinNormal) *
2216 (d - _dMin) / (_dMax - _dMin);
2217 SVector3 t = _tg[_index[0]];
2218 SVector3 n0 = crossprod(t, fabs(t(0)) > fabs(t(1)) ? SVector3(0, 1, 0) :
2219 SVector3(1, 0, 0));
2220 SVector3 n1 = crossprod(t, n0);
2221 metr = SMetric3(1 / lTg / lTg, 1 / lN / lN, 1 / lN / lN, t, n0, n1);
2222 }
operator ()(double X,double Y,double Z,GEntity * ge=nullptr)2223 virtual double operator()(double X, double Y, double Z, GEntity *ge = nullptr)
2224 {
2225 if(updateNeeded) update();
2226 double xyz[3] = {X, Y, Z};
2227 #pragma omp critical // avoid crash (still incorrect) - use Distance instead
2228 _kdTree->annkSearch(xyz, 1, _index, _dist);
2229 double d = sqrt(_dist[0]);
2230 return std::max(d, 0.05);
2231 }
2232 };
2233
2234 class AttractorField : public Field {
2235 private:
2236 ANNkd_tree *_kdTree;
2237 ANNpointArray _zeroNodes;
2238 std::list<int> _pointTags, _curveTags, _surfaceTags;
2239 std::vector<AttractorInfo> _infos;
2240 int _xFieldId, _yFieldId, _zFieldId;
2241 Field *_xField, *_yField, *_zField;
2242 int _sampling;
2243 ANNidxArray _index;
2244 ANNdistArray _dist;
2245
2246 public:
AttractorField(int dim,int tag,int nbe)2247 AttractorField(int dim, int tag, int nbe)
2248 : _kdTree(nullptr), _zeroNodes(nullptr), _sampling(nbe)
2249 {
2250 _deprecated = true;
2251 _index = new ANNidx[1];
2252 _dist = new ANNdist[1];
2253 if(dim == 0)
2254 _pointTags.push_back(tag);
2255 else if(dim == 1)
2256 _curveTags.push_back(tag);
2257 else if(dim == 2)
2258 _surfaceTags.push_back(tag);
2259 _xField = _yField = _zField = nullptr;
2260 _xFieldId = _yFieldId = _zFieldId = -1;
2261 updateNeeded = true;
2262 }
AttractorField()2263 AttractorField() : _kdTree(nullptr), _zeroNodes(nullptr)
2264 {
2265 _deprecated = true;
2266 _index = new ANNidx[1];
2267 _dist = new ANNdist[1];
2268 _sampling = 20;
2269 _xFieldId = _yFieldId = _zFieldId = -1;
2270
2271 options["PointsList"] = new FieldOptionList(
2272 _pointTags, "Tags of points in the geometric model", &updateNeeded);
2273 options["CurvesList"] = new FieldOptionList(
2274 _curveTags, "Tags of curves in the geometric model", &updateNeeded);
2275 options["SurfacesList"] = new FieldOptionList(
2276 _surfaceTags, "Tags of surfaces in the geometric model", &updateNeeded);
2277 options["Sampling"] = new FieldOptionInt(
2278 _sampling, "Linear (i.e. per dimension) number of sampling points to "
2279 "discretize each curve and surface", &updateNeeded);
2280 options["FieldX"] = new FieldOptionInt(
2281 _xFieldId, "Tag of the field to use as x coordinate", &updateNeeded);
2282 options["FieldY"] = new FieldOptionInt(
2283 _yFieldId, "Tag of the field to use as y coordinate", &updateNeeded);
2284 options["FieldZ"] = new FieldOptionInt(
2285 _zFieldId, "Tag of the field to use as z coordinate", &updateNeeded);
2286
2287 // deprecated names
2288 options["NodesList"] =
2289 new FieldOptionList(_pointTags, "[Deprecated]", &updateNeeded, true);
2290 options["EdgesList"] =
2291 new FieldOptionList(_curveTags, "[Deprecated]", &updateNeeded, true);
2292 options["FacesList"] =
2293 new FieldOptionList(_surfaceTags, "[Deprecated]", &updateNeeded, true);
2294 options["NNodesByEdge"] =
2295 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2296 options["NumPointsPerCurve"] =
2297 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2298 }
~AttractorField()2299 ~AttractorField()
2300 {
2301 delete[] _index;
2302 delete[] _dist;
2303 if(_kdTree) delete _kdTree;
2304 if(_zeroNodes) annDeallocPts(_zeroNodes);
2305 }
getName()2306 const char *getName() { return "Attractor"; }
getDescription()2307 std::string getDescription()
2308 {
2309 return "Compute the distance to the given points, curves or surfaces. "
2310 "For efficiency, curves and surfaces are replaced by a set "
2311 "of points (sampled according to Sampling), to which the distance "
2312 "is actually computed. "
2313 "The Attractor field is deprecated: use the Distance field instead.";
2314 }
getCoord(double x,double y,double z,double & cx,double & cy,double & cz,GEntity * ge=nullptr)2315 void getCoord(double x, double y, double z, double &cx, double &cy,
2316 double &cz, GEntity *ge = nullptr)
2317 {
2318 cx = _xField ? (*_xField)(x, y, z, ge) : x;
2319 cy = _yField ? (*_yField)(x, y, z, ge) : y;
2320 cz = _zField ? (*_zField)(x, y, z, ge) : z;
2321 }
getAttractorInfo() const2322 std::pair<AttractorInfo, SPoint3> getAttractorInfo() const
2323 {
2324 return std::make_pair(_infos[_index[0]], SPoint3(_zeroNodes[_index[0]][0],
2325 _zeroNodes[_index[0]][1],
2326 _zeroNodes[_index[0]][2]));
2327 }
update()2328 void update()
2329 {
2330 if(updateNeeded) {
2331 _xField = _xFieldId >= 0 ?
2332 (GModel::current()->getFields()->get(_xFieldId)) :
2333 nullptr;
2334 _yField = _yFieldId >= 0 ?
2335 (GModel::current()->getFields()->get(_yFieldId)) :
2336 nullptr;
2337 _zField = _zFieldId >= 0 ?
2338 (GModel::current()->getFields()->get(_zFieldId)) :
2339 nullptr;
2340 if(_zeroNodes) {
2341 annDeallocPts(_zeroNodes);
2342 delete _kdTree;
2343 }
2344 std::vector<SPoint3> points;
2345 std::vector<SPoint2> uvpoints;
2346 double x, y, z;
2347 std::vector<double> px, py, pz;
2348
2349 for(auto it = _pointTags.begin(); it != _pointTags.end(); ++it) {
2350 GVertex *gv = GModel::current()->getVertexByTag(*it);
2351 if(gv) {
2352 getCoord(gv->x(), gv->y(), gv->z(), x, y, z, gv);
2353 px.push_back(x);
2354 py.push_back(y);
2355 pz.push_back(z);
2356 _infos.push_back(AttractorInfo(*it, 0, 0, 0));
2357 }
2358 else {
2359 Msg::Warning("Unknown point %d", *it);
2360 }
2361 }
2362
2363 for(auto it = _curveTags.begin(); it != _curveTags.end(); ++it) {
2364 GEdge *e = GModel::current()->getEdgeByTag(*it);
2365 if(e) {
2366 if(e->mesh_vertices.size()) {
2367 for(std::size_t i = 0; i < e->mesh_vertices.size(); i++) {
2368 double u;
2369 e->mesh_vertices[i]->getParameter(0, u);
2370 GPoint gp = e->point(u);
2371 getCoord(gp.x(), gp.y(), gp.z(), x, y, z, e);
2372 px.push_back(x);
2373 py.push_back(y);
2374 pz.push_back(z);
2375 _infos.push_back(AttractorInfo(*it, 1, u, 0));
2376 }
2377 }
2378 int NNN = _sampling - e->mesh_vertices.size();
2379 for(int i = 1; i < NNN - 1; i++) {
2380 double u = (double)i / (NNN - 1);
2381 Range<double> b = e->parBounds(0);
2382 double t = b.low() + u * (b.high() - b.low());
2383 GPoint gp = e->point(t);
2384 getCoord(gp.x(), gp.y(), gp.z(), x, y, z, e);
2385 px.push_back(x);
2386 py.push_back(y);
2387 pz.push_back(z);
2388 _infos.push_back(AttractorInfo(*it, 1, t, 0));
2389 }
2390 }
2391 else {
2392 Msg::Warning("Unknown curve %d", *it);
2393 }
2394 }
2395
2396 for(auto it = _surfaceTags.begin(); it != _surfaceTags.end(); ++it) {
2397 GFace *f = GModel::current()->getFaceByTag(*it);
2398 if(f) {
2399 double maxDist = f->bounds().diag() / _sampling;
2400 f->fillPointCloud(maxDist, &points, &uvpoints);
2401 for(std::size_t i = 0; i < points.size(); i++) {
2402 getCoord(points[i].x(), points[i].y(), points[i].z(), x, y, z, f);
2403 px.push_back(x);
2404 py.push_back(y);
2405 pz.push_back(z);
2406 }
2407 for(std::size_t i = 0; i < uvpoints.size(); i++)
2408 _infos.push_back
2409 (AttractorInfo(*it, 2, uvpoints[i].x(), uvpoints[i].y()));
2410 }
2411 else {
2412 Msg::Warning("Unknown surface %d", *it);
2413 }
2414 }
2415
2416 int totpoints = px.size();
2417 if(!totpoints) { // for backward compatibility
2418 totpoints = 1;
2419 px.push_back(0.);
2420 py.push_back(0.);
2421 pz.push_back(0.);
2422 }
2423
2424 _zeroNodes = annAllocPts(totpoints, 3);
2425 for(int i = 0; i < totpoints; i++) {
2426 _zeroNodes[i][0] = px[i];
2427 _zeroNodes[i][1] = py[i];
2428 _zeroNodes[i][2] = pz[i];
2429 }
2430 _kdTree = new ANNkd_tree(_zeroNodes, totpoints, 3);
2431 updateNeeded = false;
2432 }
2433 }
2434
2435 using Field::operator();
operator ()(double X,double Y,double Z,GEntity * ge=nullptr)2436 virtual double operator()(double X, double Y, double Z, GEntity *ge = nullptr)
2437 {
2438 #pragma omp critical
2439 {
2440 update();
2441 }
2442 double xyz[3];
2443 getCoord(X, Y, Z, xyz[0], xyz[1], xyz[2], ge);
2444 #pragma omp critical // avoid crash (still incorrect) - use Distance instead
2445 _kdTree->annkSearch(xyz, 1, _index, _dist);
2446 double d = _dist[0];
2447 return sqrt(d);
2448 }
2449 };
2450
2451 #endif // ANN
2452
2453 class OctreeField : public Field {
2454 private:
2455 // octree field
2456 class Cell {
2457 private:
2458 void *_data;
2459 bool _isleaf;
2460
2461 public:
evaluate(double x,double y,double z) const2462 double evaluate(double x, double y, double z) const
2463 {
2464 if(_isleaf) return *(double *)_data;
2465 Cell *sub = (Cell *)_data;
2466 int i = x > 0.5 ? 1 : 0;
2467 int j = y > 0.5 ? 1 : 0;
2468 int k = z > 0.5 ? 1 : 0;
2469 return sub[i * 4 + j * 2 + k].evaluate(2 * x - i, 2 * y - j, 2 * z - k);
2470 }
Cell()2471 Cell(){};
init(double x0,double y0,double z0,double l,Field & field,int level)2472 void init(double x0, double y0, double z0, double l, Field &field,
2473 int level)
2474 {
2475 #if 0
2476 double v[8] =
2477 {field(x0, y0, z0), field(x0, y0, z0 + l), field(x0, y0 + l, z0),
2478 field(x0, y0 + l, z0 + l), field(x0 + l, y0, z0), field(x0 + l, y0, z0 + l),
2479 field(x0 + l, y0 + l, z0), field(x0 + l, y0 + l, z0 + l)};
2480 double dmax = 0;
2481 double vmin = v[0];
2482 double vc = field(x0+l/2,y0+l/2,z0+l/2);
2483 for (int i = 0; i < 8; ++i){
2484 dmax = std::max(dmax, std::abs(vc-v[i]));
2485 vmin = std::min(vmin, v[i]);
2486 }
2487 #else
2488 double dmax = 0;
2489 double vc = field(x0 + l / 2, y0 + l / 2, z0 + l / 2);
2490 double vmin = vc;
2491 bool split = level > 0;
2492 if(level > -4) {
2493 #define NSAMPLE 2
2494 double dl = l / NSAMPLE;
2495 for(int i = 0; i <= NSAMPLE; ++i) {
2496 for(int j = 0; j <= NSAMPLE; ++j) {
2497 for(int k = 0; k <= NSAMPLE; ++k) {
2498 double w = field(x0 + i * dl, y0 + j * dl, z0 + k * dl);
2499 dmax = std::max(dmax, std::abs(vc - w));
2500 vmin = std::min(vmin, w);
2501 split |= (dmax / vmin > 0.2 && vmin < l);
2502 if(split) break;
2503 }
2504 }
2505 }
2506 #endif
2507 }
2508 if(split) {
2509 _isleaf = false;
2510 Cell *sub = new Cell[8];
2511 double l2 = l / 2;
2512 sub[0].init(x0, y0, z0, l2, field, level - 1);
2513 sub[1].init(x0, y0, z0 + l2, l2, field, level - 1);
2514 sub[2].init(x0, y0 + l2, z0, l2, field, level - 1);
2515 sub[3].init(x0, y0 + l2, z0 + l2, l2, field, level - 1);
2516 sub[4].init(x0 + l2, y0, z0, l2, field, level - 1);
2517 sub[5].init(x0 + l2, y0, z0 + l2, l2, field, level - 1);
2518 sub[6].init(x0 + l2, y0 + l2, z0, l2, field, level - 1);
2519 sub[7].init(x0 + l2, y0 + l2, z0 + l2, l2, field, level - 1);
2520 _data = (void *)sub;
2521 }
2522 else {
2523 _isleaf = true;
2524 _data = (void *)new double;
2525 *(double *)_data = vc;
2526 }
2527 }
~Cell()2528 ~Cell()
2529 {
2530 if(_isleaf) { delete(double *)_data; }
2531 else {
2532 Cell *sub = (Cell *)_data;
2533 delete[] sub;
2534 }
2535 }
print(double x0,double y0,double z0,double l,FILE * f)2536 void print(double x0, double y0, double z0, double l, FILE *f)
2537 {
2538 if(_isleaf) {
2539 fprintf(f, "SP(%g, %g, %g) {%g};\n", x0 + l / 2, y0 + l / 2, z0 + l / 2,
2540 *(double *)_data);
2541 }
2542 else {
2543 Cell *sub = (Cell *)_data;
2544 double l2 = l / 2;
2545 sub[0].print(x0, y0, z0, l2, f);
2546 sub[1].print(x0, y0, z0 + l2, l2, f);
2547 sub[2].print(x0, y0 + l2, z0, l2, f);
2548 sub[3].print(x0, y0 + l2, z0 + l2, l2, f);
2549 sub[4].print(x0 + l2, y0, z0, l2, f);
2550 sub[5].print(x0 + l2, y0, z0 + l2, l2, f);
2551 sub[6].print(x0 + l2, y0 + l2, z0, l2, f);
2552 sub[7].print(x0 + l2, y0 + l2, z0 + l2, l2, f);
2553 }
2554 }
2555 };
2556 Cell *_root;
2557 int _inFieldId;
2558 Field *_inField;
2559 SBoundingBox3d bounds;
2560 double _l0;
2561
2562 public:
OctreeField()2563 OctreeField()
2564 {
2565 _root = nullptr;
2566 _inFieldId = 1;
2567
2568 options["InField"] = new FieldOptionInt
2569 (_inFieldId, "Id of the field to represent on the octree", &updateNeeded);
2570 }
~OctreeField()2571 ~OctreeField()
2572 {
2573 if(_root) delete _root;
2574 }
getName()2575 const char *getName() { return "Octree"; }
getDescription()2576 std::string getDescription()
2577 {
2578 return "Pre compute another field on an octree to speed-up evalution.";
2579 }
update()2580 void update()
2581 {
2582 if(updateNeeded) {
2583 updateNeeded = false;
2584 if(_root) {
2585 delete _root;
2586 _root = nullptr;
2587 }
2588 }
2589 if(!_root) {
2590 _inField = _inFieldId >= 0 ?
2591 (GModel::current()->getFields()->get(_inFieldId)) :
2592 nullptr;
2593 if(!_inField) return;
2594 GModel::current()->getFields()->get(_inFieldId)->update();
2595 bounds = GModel::current()->bounds();
2596 _root = new Cell;
2597 SVector3 d = bounds.max() - bounds.min();
2598 _l0 = std::max(std::max(d.x(), d.y()), d.z());
2599 _root->init(bounds.min().x(), bounds.min().y(), bounds.min().z(), _l0,
2600 *_inField, 4);
2601 }
2602 }
operator ()(double X,double Y,double Z,GEntity * ge=nullptr)2603 virtual double operator()(double X, double Y, double Z, GEntity *ge = nullptr)
2604 {
2605 SPoint3 xmin = bounds.min();
2606 SVector3 d = bounds.max() - xmin;
2607 return _root->evaluate((X - xmin.x()) / _l0, (Y - xmin.y()) / _l0,
2608 (Z - xmin.z()) / _l0);
2609 }
2610 };
2611
2612 struct PointCloud {
2613 std::vector<SPoint3> pts;
2614 };
2615
2616 template <typename Derived> struct PointCloudAdaptor {
2617 const Derived &obj;
PointCloudAdaptorPointCloudAdaptor2618 PointCloudAdaptor(const Derived &obj_) : obj(obj_) {}
derivedPointCloudAdaptor2619 inline const Derived &derived() const { return obj; }
kdtree_get_point_countPointCloudAdaptor2620 inline size_t kdtree_get_point_count() const { return derived().pts.size(); }
kdtree_distancePointCloudAdaptor2621 inline double kdtree_distance(const double *p1, const size_t idx_p2,
2622 size_t /*size*/) const
2623 {
2624 const double d0 = p1[0] - derived().pts[idx_p2].x();
2625 const double d1 = p1[1] - derived().pts[idx_p2].y();
2626 const double d2 = p1[2] - derived().pts[idx_p2].z();
2627 return d0 * d0 + d1 * d1 + d2 * d2;
2628 }
kdtree_get_ptPointCloudAdaptor2629 inline double kdtree_get_pt(const size_t idx, int dim) const
2630 {
2631 if(dim == 0)
2632 return derived().pts[idx].x();
2633 else if(dim == 1)
2634 return derived().pts[idx].y();
2635 else
2636 return derived().pts[idx].z();
2637 }
kdtree_get_bboxPointCloudAdaptor2638 template <class BBOX> bool kdtree_get_bbox(BBOX & /*bb*/) const
2639 {
2640 return false;
2641 }
2642 };
2643
2644 class DistanceField : public Field {
2645 std::list<int> _pointTags, _curveTags, _surfaceTags;
2646 std::vector<AttractorInfo> _infos;
2647 int _sampling;
2648 int _xFieldId, _yFieldId, _zFieldId; // unused
2649 SPoint3Cloud _pc;
2650 SPoint3CloudAdaptor<SPoint3Cloud> _pc2kdtree;
2651 SPoint3KDTree *_kdtree;
2652 std::size_t _outIndex;
2653 double _outDistSqr;
2654
2655 public:
DistanceField()2656 DistanceField() : _pc2kdtree(_pc), _kdtree(nullptr), _outIndex(0),
2657 _outDistSqr(0.)
2658 {
2659 _sampling = 20;
2660
2661 options["PointsList"] = new FieldOptionList(
2662 _pointTags, "Tags of points in the geometric model", &updateNeeded);
2663 options["CurvesList"] = new FieldOptionList(
2664 _curveTags, "Tags of curves in the geometric model", &updateNeeded);
2665 options["SurfacesList"] = new FieldOptionList(
2666 _surfaceTags, "Tags of surfaces in the geometric model "
2667 "(only OpenCASCADE and discrete surfaces are currently supported)",
2668 &updateNeeded);
2669 options["Sampling"] = new FieldOptionInt(
2670 _sampling, "Linear (i.e. per dimension) number of sampling points to "
2671 "discretize each curve and surface", &updateNeeded);
2672
2673 // deprecated names
2674 options["NodesList"] =
2675 new FieldOptionList(_pointTags, "[Deprecated]", &updateNeeded, true);
2676 options["EdgesList"] =
2677 new FieldOptionList(_curveTags, "[Deprecated]", &updateNeeded, true);
2678 options["NNodesByEdge"] =
2679 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2680 options["FacesList"] =
2681 new FieldOptionList(_surfaceTags, "[Deprecated]", &updateNeeded, true);
2682 options["FieldX"] =
2683 new FieldOptionInt(_xFieldId, "[Deprecated]", &updateNeeded, true);
2684 options["FieldY"] =
2685 new FieldOptionInt(_yFieldId, "[Deprecated]", &updateNeeded, true);
2686 options["FieldZ"] =
2687 new FieldOptionInt(_zFieldId, "[Deprecated]", &updateNeeded, true);
2688 options["NumPointsPerCurve"] =
2689 new FieldOptionInt(_sampling, "[Deprecated]", &updateNeeded, true);
2690 }
DistanceField(int dim,int tag,int nbe)2691 DistanceField(int dim, int tag, int nbe)
2692 : _sampling(nbe), _pc2kdtree(_pc), _kdtree(nullptr), _outIndex(0),
2693 _outDistSqr(0)
2694 {
2695 if(dim == 0)
2696 _pointTags.push_back(tag);
2697 else if(dim == 2)
2698 _curveTags.push_back(tag);
2699 else if(dim == 3)
2700 _surfaceTags.push_back(tag);
2701 _xFieldId = _yFieldId = _zFieldId = -1; // not used
2702 updateNeeded = true;
2703 }
~DistanceField()2704 ~DistanceField()
2705 {
2706 if(_kdtree) delete _kdtree;
2707 }
getName()2708 const char *getName() { return "Distance"; }
getDescription()2709 std::string getDescription()
2710 {
2711 return "Compute the distance to the given points, curves or surfaces. "
2712 "For efficiency, curves and surfaces are replaced by a set "
2713 "of points (sampled according to Sampling), to which the distance "
2714 "is actually computed.";
2715 }
getAttractorInfo() const2716 std::pair<AttractorInfo, SPoint3> getAttractorInfo() const
2717 {
2718 if(_outIndex < _infos.size() && _outIndex < _pc.pts.size())
2719 return std::make_pair(_infos[_outIndex], _pc.pts[_outIndex]);
2720 return std::make_pair(AttractorInfo(), SPoint3());
2721 }
update()2722 void update()
2723 {
2724 if(updateNeeded) {
2725 _infos.clear();
2726 std::vector<SPoint3> &points = _pc.pts;
2727 points.clear();
2728
2729 for(auto it = _pointTags.begin(); it != _pointTags.end(); ++it) {
2730 GVertex *gv = GModel::current()->getVertexByTag(*it);
2731 if(gv) {
2732 points.push_back(SPoint3(gv->x(), gv->y(), gv->z()));
2733 _infos.push_back(AttractorInfo(*it, 0, 0, 0));
2734 }
2735 else {
2736 Msg::Warning("Unknown point %d", *it);
2737 }
2738 }
2739
2740 for(auto it = _curveTags.begin(); it != _curveTags.end(); ++it) {
2741 GEdge *e = GModel::current()->getEdgeByTag(*it);
2742 if(e) {
2743 if(e->mesh_vertices.size()) {
2744 for(std::size_t i = 0; i < e->mesh_vertices.size(); i++) {
2745 points.push_back(SPoint3(e->mesh_vertices[i]->x(),
2746 e->mesh_vertices[i]->y(),
2747 e->mesh_vertices[i]->z()));
2748 double t = 0.;
2749 e->mesh_vertices[i]->getParameter(0, t);
2750 _infos.push_back(AttractorInfo(*it, 1, t, 0));
2751 }
2752 }
2753 int NNN = _sampling - e->mesh_vertices.size();
2754 for(int i = 1; i < NNN - 1; i++) {
2755 double u = (double)i / (NNN - 1);
2756 Range<double> b = e->parBounds(0);
2757 double t = b.low() + u * (b.high() - b.low());
2758 GPoint gp = e->point(t);
2759 points.push_back(SPoint3(gp.x(), gp.y(), gp.z()));
2760 _infos.push_back(AttractorInfo(*it, 1, t, 0));
2761 }
2762 }
2763 else {
2764 Msg::Warning("Unknown curve %d", *it);
2765 }
2766 }
2767
2768 for(auto it = _surfaceTags.begin(); it != _surfaceTags.end(); ++it) {
2769 GFace *f = GModel::current()->getFaceByTag(*it);
2770 if(f) {
2771 double maxDist = f->bounds().diag() / _sampling;
2772 std::vector<SPoint2> uvpoints;
2773 f->fillPointCloud(maxDist, &points, &uvpoints);
2774 for(std::size_t i = 0; i < uvpoints.size(); i++)
2775 _infos.push_back
2776 (AttractorInfo(*it, 2, uvpoints[i].x(), uvpoints[i].y()));
2777 }
2778 else {
2779 Msg::Warning("Unknown surface %d", *it);
2780 }
2781 }
2782
2783 // construct a kd-tree index:
2784 _kdtree = new SPoint3KDTree(3, _pc2kdtree,
2785 nanoflann::KDTreeSingleIndexAdaptorParams(10));
2786 _kdtree->buildIndex();
2787 updateNeeded = false;
2788 }
2789 }
2790 using Field::operator();
operator ()(double X,double Y,double Z,GEntity * ge=nullptr)2791 virtual double operator()(double X, double Y, double Z, GEntity *ge = nullptr)
2792 {
2793 if(!_kdtree) return MAX_LC;
2794 double query_pt[3] = {X, Y, Z};
2795 const size_t num_results = 1;
2796 nanoflann::KNNResultSet<double> resultSet(num_results);
2797 resultSet.init(&_outIndex, &_outDistSqr);
2798 _kdtree->findNeighbors(resultSet, &query_pt[0], nanoflann::SearchParams(10));
2799 return sqrt(_outDistSqr);
2800 }
2801 };
2802
getName()2803 const char *BoundaryLayerField::getName() { return "BoundaryLayer"; }
2804
getDescription()2805 std::string BoundaryLayerField::getDescription()
2806 {
2807 return "Insert a 2D boundary layer mesh next to some curves in the model.";
2808 }
2809
BoundaryLayerField()2810 BoundaryLayerField::BoundaryLayerField()
2811 {
2812 betaLaw = 0;
2813 nb_divisions = 10;
2814 beta = 1.01;
2815 hWallN = .1;
2816 hFar = 1;
2817 ratio = 1.1;
2818 thickness = 1.e-2;
2819 tgtAnisoRatio = 1.e10;
2820 iRecombine = 0;
2821 iIntersect = 0;
2822
2823 options["CurvesList"] = new FieldOptionList(
2824 _curveTags,
2825 "Tags of curves in the geometric model for which a boundary "
2826 "layer is needed",
2827 &updateNeeded);
2828 options["FanPointsList"] =
2829 new FieldOptionList(_fanPointTags,
2830 "Tags of points in the geometric model for which a fan "
2831 "is created",
2832 &updateNeeded);
2833 options["FanPointsSizesList"] = new FieldOptionList(
2834 _fanSizes,
2835 "Number of elements in the fan for each fan node. "
2836 "If not present default value Mesh.BoundaryLayerFanElements",
2837 &updateNeeded);
2838 options["PointsList"] = new FieldOptionList(
2839 _pointTags,
2840 "Tags of points in the geometric model for which a boundary "
2841 "layer ends",
2842 &updateNeeded);
2843 options["Size"] =
2844 new FieldOptionDouble(hWallN, "Mesh size normal to the curve");
2845 options["SizesList"] = new FieldOptionListDouble(
2846 _hWallNNodes, "Mesh size normal to the curve, per point (overwrites "
2847 "Size when defined)");
2848 options["Ratio"] =
2849 new FieldOptionDouble(ratio, "Size ratio between two successive layers");
2850 options["SizeFar"] =
2851 new FieldOptionDouble(hFar, "Mesh size far from the curves");
2852 options["Thickness"] =
2853 new FieldOptionDouble(thickness, "Maximal thickness of the boundary layer");
2854 options["Quads"] = new FieldOptionInt(
2855 iRecombine, "Generate recombined elements in the boundary layer");
2856 options["IntersectMetrics"] =
2857 new FieldOptionInt(iIntersect, "Intersect metrics of all surfaces");
2858 options["AnisoMax"] = new FieldOptionDouble(
2859 tgtAnisoRatio, "Threshold angle for creating a mesh fan in the boundary "
2860 "layer");
2861 options["BetaLaw"] = new FieldOptionInt(
2862 betaLaw, "Use Beta Law instead of geometric progression ");
2863 options["Beta"] =
2864 new FieldOptionDouble(beta, "Beta coefficient of the Beta Law");
2865 options["NbLayers"] =
2866 new FieldOptionInt(nb_divisions, "Number of Layers in theBeta Law");
2867 options["ExcludedSurfacesList"] =
2868 new FieldOptionList(_excludedSurfaceTags,
2869 "Tags of surfaces in the geometric model where the "
2870 "boundary layer should not be constructed",
2871 &updateNeeded);
2872
2873 // deprecated names
2874 options["EdgesList"] =
2875 new FieldOptionList(_curveTags, "[Deprecated]", &updateNeeded, true);
2876 options["FanNodesList"] =
2877 new FieldOptionList(_fanPointTags, "[Deprecated]", &updateNeeded, true);
2878 options["NodesList"] =
2879 new FieldOptionList(_pointTags, "[Deprecated]", &updateNeeded, true);
2880 options["hwall_n"] =
2881 new FieldOptionDouble(hWallN, "[Deprecated]", nullptr, true);
2882 options["hwall_n_nodes"] =
2883 new FieldOptionListDouble(_hWallNNodes, "[Deprecated]", nullptr, true);
2884 options["ratio"] =
2885 new FieldOptionDouble(ratio, "[Deprecated]", nullptr, true);
2886 options["hfar"] =
2887 new FieldOptionDouble(hFar, "[Deprecated]", nullptr, true);
2888 options["thickness"] =
2889 new FieldOptionDouble(thickness, "[Deprecated]", nullptr, true);
2890 options["ExcludedFaceList"] =
2891 new FieldOptionList(_excludedSurfaceTags, "[Deprecated]", &updateNeeded, true);
2892 }
2893
removeAttractors()2894 void BoundaryLayerField::removeAttractors()
2895 {
2896 for(auto it = _attFields.begin(); it != _attFields.end(); ++it) delete *it;
2897 _attFields.clear();
2898 updateNeeded = true;
2899 }
2900
setupFor1d(int iE)2901 void BoundaryLayerField::setupFor1d(int iE)
2902 {
2903 if(_curveTagsSaved.empty()) {
2904 _curveTagsSaved = _curveTags;
2905 _pointTagsSaved = _pointTags;
2906 }
2907
2908 _pointTags.clear();
2909 _curveTags.clear();
2910
2911 bool found = std::find(_curveTagsSaved.begin(), _curveTagsSaved.end(), iE) !=
2912 _curveTagsSaved.end();
2913
2914 if(!found) {
2915 GEdge *ge = GModel::current()->getEdgeByTag(iE);
2916 if(ge) {
2917 GVertex *gv0 = ge->getBeginVertex();
2918 if(gv0) {
2919 found = std::find(_pointTagsSaved.begin(), _pointTagsSaved.end(),
2920 gv0->tag()) != _pointTagsSaved.end();
2921 if(found) _pointTags.push_back(gv0->tag());
2922 }
2923 GVertex *gv1 = ge->getEndVertex();
2924 if(gv1) {
2925 found = std::find(_pointTagsSaved.begin(), _pointTagsSaved.end(),
2926 gv1->tag()) != _pointTagsSaved.end();
2927 if(found) _pointTags.push_back(gv1->tag());
2928 }
2929 }
2930 else {
2931 Msg::Warning("Unknown curve %d", iE);
2932 }
2933 }
2934 removeAttractors();
2935 }
2936
setupFor2d(int iF)2937 bool BoundaryLayerField::setupFor2d(int iF)
2938 {
2939 if(std::find(_excludedSurfaceTags.begin(), _excludedSurfaceTags.end(), iF) !=
2940 _excludedSurfaceTags.end())
2941 return false;
2942
2943 // remove GFaces from the attractors (only used in 2D) for edges and vertices
2944 if(_curveTagsSaved.empty()) {
2945 _curveTagsSaved = _curveTags;
2946 _pointTagsSaved = _pointTags;
2947 }
2948
2949 _pointTags.clear();
2950 _curveTags.clear();
2951
2952 // FIXME :
2953 // NOT REALLY A NICE WAY TO DO IT (VERY AD HOC)
2954 // THIS COULD BE PART OF THE INPUT
2955 // OR (better) CHANGE THE PHILOSOPHY
2956
2957 GFace *gf = GModel::current()->getFaceByTag(iF);
2958 if(!gf) {
2959 Msg::Warning("Unknown surface %d", iF);
2960 return false;
2961 }
2962 std::vector<GEdge *> ed = gf->edges();
2963 std::vector<GEdge *> const &embedded_edges = gf->embeddedEdges();
2964 ed.insert(ed.begin(), embedded_edges.begin(), embedded_edges.end());
2965
2966 for(auto it = ed.begin(); it != ed.end(); ++it) {
2967 bool isIn = false;
2968 int iE = (*it)->tag();
2969 bool found = std::find(_curveTagsSaved.begin(), _curveTagsSaved.end(),
2970 iE) != _curveTagsSaved.end();
2971 // this edge is a BL Edge
2972 if(found) {
2973 std::vector<GFace *> fc = (*it)->faces();
2974 int numf = 0;
2975 for(auto it = fc.begin(); it != fc.end(); it++) {
2976 if((*it)->meshAttributes.extrude &&
2977 (*it)->meshAttributes.extrude->geo.Mode == EXTRUDED_ENTITY) {
2978 // ok
2979 }
2980 else if(std::find(_excludedSurfaceTags.begin(),
2981 _excludedSurfaceTags.end(),
2982 (*it)->tag()) != _excludedSurfaceTags.end()) {
2983 // ok
2984 }
2985 else {
2986 numf++;
2987 }
2988 }
2989 // one only face, or other faces are extruded --> 2D --> BL
2990 if(numf <= 1)
2991 isIn = true;
2992 else {
2993 Msg::Error("Only 2D Boundary Layers are supported (curve %d is adjacet "
2994 "to %d surfaces)",
2995 iE, fc.size());
2996 }
2997 }
2998 if(isIn) {
2999 _curveTags.push_back(iE);
3000 if((*it)->getBeginVertex())
3001 _pointTags.push_back((*it)->getBeginVertex()->tag());
3002 if((*it)->getEndVertex())
3003 _pointTags.push_back((*it)->getEndVertex()->tag());
3004 }
3005 }
3006
3007 removeAttractors();
3008 return true;
3009 }
3010
operator ()(double x,double y,double z,GEntity * ge)3011 double BoundaryLayerField::operator()(double x, double y, double z, GEntity *ge)
3012 {
3013 if(updateNeeded) {
3014 for(auto it = _pointTags.begin(); it != _pointTags.end(); ++it) {
3015 _attFields.push_back(new DistanceField(0, *it, 100000));
3016 }
3017 for(auto it = _curveTags.begin(); it != _curveTags.end(); ++it) {
3018 _attFields.push_back(new DistanceField(1, *it, 300000));
3019 }
3020 updateNeeded = false;
3021 }
3022
3023 double dist = 1.e22;
3024 if(_attFields.empty()) return dist;
3025 for(auto it = _attFields.begin(); it != _attFields.end(); ++it) {
3026 double cdist = (*(*it))(x, y, z);
3027 if(cdist < dist) { dist = cdist; }
3028 }
3029
3030 if(dist > thickness * ratio) return 1.e22;
3031 currentDistance = dist;
3032 // const double dist = (*field) (x, y, z);
3033 // currentDistance = dist;
3034 double lc = dist * (ratio - 1) + hWallN;
3035
3036 // double lc = hWallN;
3037 return std::min(hFar, lc);
3038 }
3039
3040 // assume that the closest point is one of the model vertices
computeFor1dMesh(double x,double y,double z,SMetric3 & metr)3041 void BoundaryLayerField::computeFor1dMesh(double x, double y, double z,
3042 SMetric3 &metr)
3043 {
3044 double xpk = 0., ypk = 0., zpk = 0.;
3045 double distk = 1.e22;
3046 for(auto it = _pointTags.begin(); it != _pointTags.end(); ++it) {
3047 GVertex *v = GModel::current()->getVertexByTag(*it);
3048 if(v) {
3049 double xp = v->x();
3050 double yp = v->y();
3051 double zp = v->z();
3052 const double dist =
3053 sqrt((x - xp) * (x - xp) + (y - yp) * (y - yp) + (z - zp) * (z - zp));
3054 if(dist < distk) {
3055 distk = dist;
3056 xpk = xp;
3057 ypk = yp;
3058 zpk = zp;
3059 }
3060 }
3061 else {
3062 Msg::Warning("Unknown point %d", *it);
3063 }
3064 }
3065
3066 const double ll1 = (distk * (ratio - 1) + hWallN) / (1. + 0.5 * (ratio - 1));
3067 double lc_n = std::min(ll1, hFar);
3068
3069 if(distk > thickness) lc_n = hFar;
3070 lc_n = std::max(lc_n, CTX::instance()->mesh.lcMin);
3071 lc_n = std::min(lc_n, CTX::instance()->mesh.lcMax);
3072 SVector3 t1 = SVector3(x - xpk, y - ypk, z - zpk);
3073 t1.normalize();
3074 metr = buildMetricTangentToCurve(t1, lc_n, lc_n);
3075 }
3076
operator ()(DistanceField * cc,double dist,double x,double y,double z,SMetric3 & metr,GEntity * ge)3077 void BoundaryLayerField::operator()(DistanceField *cc, double dist, double x,
3078 double y, double z, SMetric3 &metr,
3079 GEntity *ge)
3080 {
3081 // dist = hwall -> lc = hwall * ratio
3082 // dist = hwall (1+ratio) -> lc = hwall ratio ^ 2
3083 // dist = hwall (1+ratio+ratio^2) -> lc = hwall ratio ^ 3
3084 // dist = hwall (1+ratio+ratio^2+...+ratio^{m-1}) = (ratio^{m} - 1)/(ratio-1)
3085 // -> lc = hwall ratio ^ m
3086 // -> find m
3087 // dist/hwall = (ratio^{m} - 1)/(ratio-1)
3088 // (dist/hwall)*(ratio-1) + 1 = ratio^{m}
3089 // lc = dist*(ratio-1) + hwall
3090
3091 const double ll1 = dist * (ratio - 1) + hWallN;
3092 double lc_n = std::min(ll1, hFar);
3093 double lc_t = std::min(lc_n * CTX::instance()->mesh.anisoMax, hFar);
3094
3095 lc_n = std::max(lc_n, CTX::instance()->mesh.lcMin);
3096 lc_n = std::min(lc_n, CTX::instance()->mesh.lcMax);
3097 lc_t = std::max(lc_t, CTX::instance()->mesh.lcMin);
3098 lc_t = std::min(lc_t, CTX::instance()->mesh.lcMax);
3099
3100 std::pair<AttractorInfo, SPoint3> pp = cc->getAttractorInfo();
3101 double beta = CTX::instance()->mesh.smoothRatio;
3102 if(pp.first.dim == 0) {
3103 GVertex *v = GModel::current()->getVertexByTag(pp.first.ent);
3104 if(!v) return;
3105 SVector3 t1;
3106 if(dist < thickness) { t1 = SVector3(1, 0, 0); }
3107 else {
3108 t1 = SVector3(v->x() - x, v->y() - y, v->z() - z);
3109 }
3110 metr = buildMetricTangentToCurve(t1, lc_n, lc_n);
3111 return;
3112 }
3113 else if(pp.first.dim == 1) {
3114 GEdge *e = GModel::current()->getEdgeByTag(pp.first.ent);
3115 if(!e) {
3116 Msg::Warning("Unknown curve %d", pp.first.ent);
3117 return;
3118 }
3119 if(dist < thickness) {
3120 SVector3 t1 = e->firstDer(pp.first.u);
3121 double crv = e->curvature(pp.first.u);
3122 const double b = lc_t;
3123 const double h = lc_n;
3124 double oneOverD2 =
3125 .5 / (b * b) *
3126 (1. +
3127 sqrt(1. + (4. * crv * crv * b * b * b * b / (h * h * beta * beta))));
3128 metr = buildMetricTangentToCurve(t1, sqrt(1. / oneOverD2), lc_n);
3129 return;
3130 }
3131 else {
3132 GPoint p = e->point(pp.first.u);
3133 SVector3 t2 = SVector3(p.x() - x, p.y() - y, p.z() - z);
3134 metr = buildMetricTangentToCurve(t2, lc_t, lc_n);
3135 return;
3136 }
3137 }
3138 else {
3139 GFace *gf = GModel::current()->getFaceByTag(pp.first.ent);
3140 if(!gf) {
3141 Msg::Warning("Unknown surface %d", pp.first.ent);
3142 return;
3143 }
3144 if(dist < thickness) {
3145 double cmin, cmax;
3146 SVector3 dirMax, dirMin;
3147 cmax = gf->curvatures(SPoint2(pp.first.u, pp.first.v), dirMax, dirMin,
3148 cmax, cmin);
3149 const double b = lc_t;
3150 const double h = lc_n;
3151 double oneOverD2_min =
3152 .5 / (b * b) *
3153 (1. +
3154 sqrt(1. + (4. * cmin * cmin * b * b * b * b / (h * h * beta * beta))));
3155 double oneOverD2_max =
3156 .5 / (b * b) *
3157 (1. +
3158 sqrt(1. + (4. * cmax * cmax * b * b * b * b / (h * h * beta * beta))));
3159 double dmin = sqrt(1. / oneOverD2_min);
3160 double dmax = sqrt(1. / oneOverD2_max);
3161 dmin = std::min(dmin, dmax * tgtAnisoRatio);
3162 metr = buildMetricTangentToSurface(dirMin, dirMax, dmin, dmax, lc_n);
3163 return;
3164 }
3165 else {
3166 GPoint p = gf->point(SPoint2(pp.first.u, pp.first.v));
3167 SVector3 t2 = SVector3(p.x() - x, p.y() - y, p.z() - z);
3168 metr = buildMetricTangentToCurve(t2, lc_n, lc_t);
3169 return;
3170 }
3171 }
3172 }
3173
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge)3174 void BoundaryLayerField::operator()(double x, double y, double z,
3175 SMetric3 &metr, GEntity *ge)
3176 {
3177 if(updateNeeded) {
3178 for(auto it = _pointTags.begin(); it != _pointTags.end(); ++it) {
3179 _attFields.push_back(new DistanceField(0, *it, 100000));
3180 }
3181 for(auto it = _curveTags.begin(); it != _curveTags.end(); ++it) {
3182 _attFields.push_back(new DistanceField(1, *it, 10000));
3183 }
3184 updateNeeded = false;
3185 }
3186
3187 currentDistance = 1.e22;
3188 currentClosest = nullptr;
3189 std::vector<SMetric3> hop;
3190 SMetric3 v(1. / (CTX::instance()->mesh.lcMax * CTX::instance()->mesh.lcMax));
3191 hop.push_back(v);
3192 for(auto it = _attFields.begin(); it != _attFields.end(); ++it) {
3193 double cdist = (*(*it))(x, y, z);
3194 SPoint3 CLOSEST = (*it)->getAttractorInfo().second;
3195 SMetric3 localMetric;
3196 if(iIntersect) {
3197 (*this)(*it, cdist, x, y, z, localMetric, ge);
3198 hop.push_back(localMetric);
3199 }
3200 if(cdist < currentDistance) {
3201 if(!iIntersect) (*this)(*it, cdist, x, y, z, localMetric, ge);
3202 currentDistance = cdist;
3203 currentClosest = *it;
3204 v = localMetric;
3205 _closestPoint = CLOSEST;
3206 }
3207 }
3208 if(iIntersect)
3209 for(std::size_t i = 0; i < hop.size(); i++)
3210 v = intersection_conserveM1(v, hop[i]);
3211 metr = v;
3212 }
3213
FieldManager()3214 FieldManager::FieldManager()
3215 {
3216 mapTypeName["Structured"] = new FieldFactoryT<StructuredField>();
3217 mapTypeName["Threshold"] = new FieldFactoryT<ThresholdField>();
3218 mapTypeName["BoundaryLayer"] = new FieldFactoryT<BoundaryLayerField>();
3219 mapTypeName["Box"] = new FieldFactoryT<BoxField>();
3220 mapTypeName["Cylinder"] = new FieldFactoryT<CylinderField>();
3221 mapTypeName["Ball"] = new FieldFactoryT<BallField>();
3222 mapTypeName["Frustum"] = new FieldFactoryT<FrustumField>();
3223 mapTypeName["LonLat"] = new FieldFactoryT<LonLatField>();
3224 #if defined(HAVE_POST)
3225 mapTypeName["PostView"] = new FieldFactoryT<PostViewField>();
3226 #endif
3227 mapTypeName["Gradient"] = new FieldFactoryT<GradientField>();
3228 mapTypeName["Octree"] = new FieldFactoryT<OctreeField>();
3229 mapTypeName["Distance"] = new FieldFactoryT<DistanceField>();
3230 mapTypeName["Restrict"] = new FieldFactoryT<RestrictField>();
3231 mapTypeName["Constant"] = new FieldFactoryT<ConstantField>();
3232 mapTypeName["Min"] = new FieldFactoryT<MinField>();
3233 mapTypeName["MinAniso"] = new FieldFactoryT<MinAnisoField>();
3234 mapTypeName["IntersectAniso"] = new FieldFactoryT<IntersectAnisoField>();
3235 mapTypeName["Max"] = new FieldFactoryT<MaxField>();
3236 mapTypeName["Laplacian"] = new FieldFactoryT<LaplacianField>();
3237 mapTypeName["Mean"] = new FieldFactoryT<MeanField>();
3238 mapTypeName["Curvature"] = new FieldFactoryT<CurvatureField>();
3239 mapTypeName["Param"] = new FieldFactoryT<ParametricField>();
3240 mapTypeName["ExternalProcess"] = new FieldFactoryT<ExternalProcessField>();
3241 mapTypeName["MathEval"] = new FieldFactoryT<MathEvalField>();
3242 mapTypeName["MathEvalAniso"] = new FieldFactoryT<MathEvalFieldAniso>();
3243 #if defined(HAVE_ANN)
3244 mapTypeName["Attractor"] = new FieldFactoryT<AttractorField>();
3245 mapTypeName["AttractorAnisoCurve"] =
3246 new FieldFactoryT<AttractorAnisoCurveField>();
3247 #endif
3248 mapTypeName["MaxEigenHessian"] = new FieldFactoryT<MaxEigenHessianField>();
3249 mapTypeName["AutomaticMeshSizeField"] =
3250 new FieldFactoryT<automaticMeshSizeField>();
3251 _backgroundField = -1;
3252 }
3253
initialize()3254 void FieldManager::initialize()
3255 {
3256 auto it = begin();
3257 for(; it != end(); ++it) it->second->update();
3258 }
3259
~FieldManager()3260 FieldManager::~FieldManager()
3261 {
3262 for(auto it = mapTypeName.begin(); it != mapTypeName.end(); it++)
3263 delete it->second;
3264 for(auto it = begin(); it != end(); it++) delete it->second;
3265 }
3266
setBackgroundField(Field * BGF)3267 void FieldManager::setBackgroundField(Field *BGF)
3268 {
3269 int id = newId();
3270 (*this)[id] = BGF;
3271 _backgroundField = id;
3272 }
3273
putOnNewView(int viewTag)3274 void Field::putOnNewView(int viewTag)
3275 {
3276 #if defined(HAVE_POST)
3277 if(GModel::current()->getMeshStatus() < 1) {
3278 Msg::Error("No mesh available to create the view: please mesh your model!");
3279 return;
3280 }
3281 std::map<int, std::vector<double> > d;
3282 std::vector<GEntity *> entities;
3283 GModel::current()->getEntities(entities);
3284 for(std::size_t i = 0; i < entities.size(); i++) {
3285 for(std::size_t j = 0; j < entities[i]->mesh_vertices.size(); j++) {
3286 MVertex *v = entities[i]->mesh_vertices[j];
3287 d[v->getNum()].push_back((*this)(v->x(), v->y(), v->z(), entities[i]));
3288 }
3289 }
3290 std::ostringstream oss;
3291 oss << "Field " << id;
3292 PView *view =
3293 new PView(oss.str(), "NodeData", GModel::current(), d, 0, -1, viewTag);
3294 view->setChanged(true);
3295 #endif
3296 }
3297
3298 #if defined(HAVE_POST)
putOnView(PView * view,int comp)3299 void Field::putOnView(PView *view, int comp)
3300 {
3301 PViewData *data = view->getData();
3302 for(int ent = 0; ent < data->getNumEntities(0); ent++) {
3303 for(int ele = 0; ele < data->getNumElements(0, ent); ele++) {
3304 if(data->skipElement(0, ent, ele)) continue;
3305 for(int nod = 0; nod < data->getNumNodes(0, ent, ele); nod++) {
3306 double x, y, z;
3307 data->getNode(0, ent, ele, nod, x, y, z);
3308 double val = (*this)(x, y, z);
3309 for(int comp = 0; comp < data->getNumComponents(0, ent, ele); comp++)
3310 data->setValue(0, ent, ele, nod, comp, val);
3311 }
3312 }
3313 }
3314 std::ostringstream oss;
3315 oss << "Field " << id;
3316 data->setName(oss.str());
3317 data->finalize();
3318 view->setChanged(true);
3319 data->destroyAdaptiveData();
3320 }
3321 #endif
3322
setBackgroundMesh(int iView)3323 void FieldManager::setBackgroundMesh(int iView)
3324 {
3325 int id = newId();
3326 Field *f = newField(id, "PostView");
3327 f->options["ViewIndex"]->numericalValue(iView);
3328 (*this)[id] = f;
3329 _backgroundField = id;
3330 }
3331
GenericField()3332 GenericField::GenericField(){};
3333
~GenericField()3334 GenericField::~GenericField(){};
3335
operator ()(double x,double y,double z,GEntity * ge)3336 double GenericField::operator()(double x, double y, double z, GEntity *ge)
3337 {
3338 std::vector<double> sizes(cbs_with_data.size() +
3339 cbs_extended_with_data.size());
3340 auto it = sizes.begin();
3341
3342 // Go over all callback functions
3343 for(auto itcbs = cbs_with_data.begin(); itcbs != cbs_with_data.end();
3344 itcbs++, it++) {
3345 bool ok = (itcbs->first)(x, y, z, itcbs->second, (*it));
3346 if(!ok) { Msg::Warning("GenericField error from callback"); }
3347 }
3348
3349 // Go over all extended callback functions
3350 for(auto itcbs = cbs_extended_with_data.begin();
3351 itcbs != cbs_extended_with_data.end(); itcbs++, it++) {
3352 bool ok = (itcbs->first)(x, y, z, ge, itcbs->second, (*it));
3353 if(!ok) { Msg::Warning("GenericField error from callback"); }
3354 }
3355
3356 // Take minimum value
3357 return (*std::min_element(sizes.begin(), sizes.end()));
3358 }
3359
setCallbackWithData(ptrfunction fct,void * data)3360 void GenericField::setCallbackWithData(ptrfunction fct, void *data)
3361 {
3362 cbs_with_data.push_back(std::make_pair(fct, data));
3363 }
3364
setCallbackWithData(ptrfunctionextended fct,void * data)3365 void GenericField::setCallbackWithData(ptrfunctionextended fct, void *data)
3366 {
3367 cbs_extended_with_data.push_back(std::make_pair(fct, data));
3368 }
3369