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