// Gmsh - Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle // // See the LICENSE.txt file for license information. Please report all // bugs and problems to <gmsh@geuz.org>. // // Contributor(s): // Jonathan Lambrechts // #include <list> #include <math.h> #include <fstream> #include <string> #include <string.h> #include <sstream> #ifdef HAVE_MATH_EVAL #include "matheval.h" #endif #ifdef HAVE_ANN #include "ANN/ANN.h" #endif #include "Context.h" #include "Field.h" #include "GeoInterpolation.h" #include "GModel.h" #include "GmshMessage.h" #if !defined(HAVE_NO_POST) #include "OctreePost.h" #include "PViewDataList.h" #include "MVertex.h" #endif extern Context_T CTX; class FieldOptionDouble : public FieldOption { public: double &val; FieldOptionType get_type(){ return FIELD_OPTION_DOUBLE; } FieldOptionDouble(double &_val, std::string _help, bool *_status=0) : FieldOption(_help, _status), val(_val){} double numerical_value() const { return val; } void numerical_value(double v){ modified(); val = v; } void get_text_representation(std::string &v_str) { std::ostringstream sstream; sstream.precision(16); sstream << val; v_str = sstream.str(); } }; class FieldOptionInt : public FieldOption { public: int &val; FieldOptionType get_type(){ return FIELD_OPTION_INT; } FieldOptionInt(int &_val, std::string _help, bool *_status=0) : FieldOption(_help, _status), val(_val){} double numerical_value() const { return val; } void numerical_value(double v){ modified(); val = (int)v; } void get_text_representation(std::string & v_str) { std::ostringstream sstream; sstream << val; v_str = sstream.str(); } }; class FieldOptionList : public FieldOption { public: std::list<int> &val; FieldOptionType get_type(){ return FIELD_OPTION_LIST; } FieldOptionList(std::list<int> &_val, std::string _help, bool *_status=0) : FieldOption(_help, _status), val(_val) {} std::list<int> &list(){ modified(); return val; } const std::list<int>& list() const { return val; } void get_text_representation(std::string & v_str) { std::ostringstream sstream; sstream << "{"; for(std::list<int>::iterator it = val.begin(); it != val.end(); it++) { if(it != val.begin()) sstream << ", "; sstream << *it; } sstream << "}"; v_str = sstream.str(); } }; class FieldOptionString : public FieldOption { public: std::string & val; virtual FieldOptionType get_type(){ return FIELD_OPTION_STRING; } FieldOptionString(std::string &_val, std::string _help, bool *_status=0) : FieldOption(_help, _status), val(_val) {} std::string &string() { modified(); return val; } const std::string &string() const { return val; } void get_text_representation(std::string &v_str) { std::ostringstream sstream; sstream << "\"" << val << "\""; v_str = sstream.str(); } }; class FieldOptionPath : public FieldOptionString { public: virtual FieldOptionType get_type(){ return FIELD_OPTION_PATH; } FieldOptionPath(std::string &_val, std::string _help, bool *_status=0) : FieldOptionString(_val, _help, _status) {} }; class FieldOptionBool : public FieldOption { public: bool & val; FieldOptionType get_type(){ return FIELD_OPTION_BOOL; } FieldOptionBool(bool & _val, std::string _help, bool *_status=0) : FieldOption(_help, _status), val(_val) {} double numerical_value() const { return val; } void numerical_value(double v){ modified(); val = v; } void get_text_representation(std::string & v_str) { std::ostringstream sstream; sstream << val; v_str = sstream.str(); } }; void FieldManager::reset() { for(std::map<int, Field *>::iterator it = begin(); it != end(); it++) { delete it->second; } clear(); } Field *FieldManager::get(int id) { iterator it = find(id); if(it == end()) return NULL; return it->second; } Field *FieldManager::new_field(int id, std::string type_name) { if(find(id) != end()) { Msg::Error("Field id %i is already defined.", id); return 0; } if(map_type_name.find(type_name) == map_type_name.end()) { Msg::Error("Unknown field type \"%s\".", type_name.c_str()); return 0; } Field *f = (*map_type_name[type_name]) (); if(!f) return 0; f->id = id; (*this)[id] = f; return f; } int FieldManager::new_id() { int i = 0; iterator it = begin(); while(1) { i++; while(it != end() && it->first < i) it++; if(it == end() || it->first != i) break; } return std::max(i, 1); } int FieldManager::max_id() { if(!empty()) return rbegin()->first; else return 0; } void FieldManager::delete_field(int id) { iterator it = find(id); if(it == end()) { Msg::Error("Cannot delete field id %i, it does not exist.", id); return; } delete it->second; erase(it); } // StructuredField class StructuredField : public Field { double o[3], d[3]; int n[3]; double *data; bool error_status; bool text_format; std::string file_name; public: StructuredField() { options["FileName"] = new FieldOptionPath(file_name, "Name of the input file", &update_needed); text_format = false; options["TextFormat"] = new FieldOptionBool(text_format, "True for ASCII input " "files, false for binary files\n" "(4 bite signed integers for n, " "double precision floating points " "for v, D and O)", &update_needed); data = 0; } std::string get_description() { return "Linearly interpolate between data provided on a 3D rectangular structured grid. " "The format of the input file is : \n" "Ox Oy Oz \n" "Dx Dy Dz \n" "nx ny nz \n" "v(0,0,0) v(0,0,1) v(0,0,2) ... \n" "v(0,1,0) v(0,1,1) v(0,1,2) ... \n" "v(0,2,0) v(0,2,1) v(0,2,2) ... \n" "... ... ... \n" "v(1,0,0) ... ... \n" "where O are the coordinates of the first node, " "D are the distances between nodes in each direction, " "n are the numbers of nodes in each directions, " "and v are the values on each nodes."; } const char *get_name() { return "Structured"; } virtual ~StructuredField() { if(data) delete[]data; } double operator() (double x, double y, double z, GEntity *ge=0) { if(update_needed) { error_status = false; try { std::ifstream input; if(text_format) input.open(file_name.c_str()); else input.open(file_name.c_str(),std::ios::binary); if(!input.is_open()) throw(1); input. exceptions(std::ifstream::eofbit | std::ifstream::failbit | std:: ifstream::badbit); if(!text_format) { input.read((char *)o, 3 * sizeof(double)); input.read((char *)d, 3 * sizeof(double)); input.read((char *)n, 3 * sizeof(int)); int nt = n[0] * n[1] * n[2]; if(data) delete[]data; data = new double[nt]; input.read((char *)data, nt * sizeof(double)); } else { input >> o[0] >> o[1] >> o[2] >> d[0] >> d[1] >> d[2] >> n[0] >> n[1] >> n[2]; int nt = n[0] * n[1] * n[2]; if(data) delete[]data; data = new double[nt]; for(int i = 0; i < nt; i++) input >> data[i]; } input.close(); } catch(...) { error_status = true; Msg::Error("Field %i : error reading file %s", this->id, file_name.c_str()); } update_needed = false; } if(error_status) return MAX_LC; //tri-linear int id[2][3]; double xi[3]; double xyz[3] = { x, y, z }; for(int i = 0; i < 3; i++) { id[0][i] = (int)floor((xyz[i] - o[i]) / d[i]); id[1][i] = id[0][i] + 1; id[0][i] = std::max(std::min(id[0][i], n[i] - 1), 0); id[1][i] = std::max(std::min(id[1][i], n[i] - 1), 0); xi[i] = (xyz[i] - (o[i] + id[0][i] * d[i])) / d[i]; xi[i] = std::max(std::min(xi[i], 1.), 0.); } double v = 0; for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) for(int k = 0; k < 2; k++) { v += data[id[i][0] * n[1] * n[2] + id[j][1] * n[2] + id[k][2]] * (i * xi[0] + (1 - i) * (1 - xi[0])) * (j * xi[1] + (1 - j) * (1 - xi[1])) * (k * xi[2] + (1 - k) * (1 - xi[2])); } return v; } }; class UTMField : public Field { int field_id, zone; double a, b, n, n2, n3, n4, n5, e, e2, e1, e12, e13, e14, J1, J2, J3, J4, Ap, Bp, Cp, Dp, Ep, e4, e6, ep, ep2, ep4, k0, mu_fact; public: std::string get_description() { return "Evaluate Field[IField] in Universal Transverse Mercator coordinates. " "The formulas for the coordinates transformation are taken from " "http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.HTM\n"; } UTMField() { field_id = 1; zone = 0; options["IField"] = new FieldOptionInt(field_id, "Index of the field to evaluate"); options["Zone"] = new FieldOptionInt(zone, "Zone of the UTM projection"); a = 6378137; /* Equatorial Radius */ b = 6356752.3142; /* Rayon Polar Radius */ /* see http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.HTM */ n = (a - b) / (a + b); n2 = n * n; n3 = n * n * n; n4 = n * n * n * n; n5 = n * n * n * n * n; e = sqrt(1 - b * b / a / a); e2 = e * e; e1 = (1 - sqrt(1 - e2)) / (1 + sqrt(1 - e2)); e12 = e1 * e1; e13 = e1 * e1 * e1; e14 = e1 * e1 * e1 * e1; J1 = (3 * e1 / 2 - 27 * e13 / 32); J2 = (21 * e12 / 16 - 55 * e14 / 32); J3 = 151 * e13 / 96; J4 = 1097 * e14 / 512; Ap = a * (1 - n + (5. / 4.) * (n2 - n3) + (81. / 64.) * (n4 - n5)); Bp = -3 * a * n / 2 * (1 - n + (7. / 8.) * (n2 - n3) + (55. / 64.) * (n4 - n5)); Cp = 14 * a * n2 / 16 * (1 - n + (3. / 4) * (n2 - n3)); Dp = -35 * a * n3 / 48 * (1 - n + 11. / 16. * (n2 - n3)); Ep = +315 * a * n4 / 51 * (1 - n); e4 = e2 * e2; e6 = e2 * e2 * e2; ep = e * a / b; ep2 = ep * ep; ep4 = ep2 * ep2; k0 = 0.9996; mu_fact = 1 / (k0 * a * (1 - e2 / 4 - 3 * e4 / 64 - 5 * e6 / 256)); } const char *get_name() { return "UTM"; } double operator() (double x, double y, double z, GEntity *ge=0) { double r = sqrt(x * x + y * y + z * z); double lon = atan2(y, x); double lat = asin(z / r); double meridionalarc = Ap * lat + Bp * sin(2 * lat) + Cp * sin(4 * lat) + Dp * sin(6 * lat) + Ep; double slat = sin(lat); double clat = cos(lat); double slat2 = slat * slat; double clat2 = clat * clat; double clat3 = clat2 * clat; double clat4 = clat3 * clat; double tlat2 = slat2 / clat2; double nu = a / sqrt(1 - e * e * slat2); double p = lon - ((zone - 0.5) / 30 - 1) * M_PI; double p2 = p * p; double p3 = p * p2; double p4 = p2 * p2; double utm_x = k0 * nu * clat * p + (k0 * nu * clat3 / 6) * (1 - tlat2 + ep2 * clat2) * p3 + 5e5; double utm_y = meridionalarc * k0 + k0 * nu * slat * clat / 2 * p2 + k0 * nu * slat * clat3 / 24 * (5 - tlat2 + 9 * ep2 * clat2 + 4 * ep4 * clat4) * p4; Field *field = GModel::current()->getFields()->get(field_id); if(!field) return MAX_LC; return (*field)(utm_x, utm_y, 0); } }; class LonLatField : public Field { int field_id; public: std::string get_description() { return "Evaluate Field[IField] in geographic coordinates (longitude,latitude). \n" "F = Field[IField](arctan(y/x),arcsin(z/sqrt(x^2+y^2+z^2))"; } LonLatField() { field_id = 1; options["IField"] = new FieldOptionInt(field_id, "Index of the field to evaluate."); } const char *get_name() { return "LonLat"; } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(field_id); if(!field) return MAX_LC; return (*field)(atan2(y, x), asin(z / sqrt(x * x + y * y + z * z)), 0); } }; class BoxField : public Field { double v_in, v_out, x_min, x_max, y_min, y_max, z_min, z_max; public: std::string get_description() { return "The value of this field is VIn inside the box, VOut outside the box. \n" "The box is given by Xmin<=x<=XMax && YMin<=y<=YMax && ZMin<=z<=ZMax"; } BoxField() { v_in = v_out = x_min = x_max = y_min = y_max = z_min = z_max = 0; options["VIn"] = new FieldOptionDouble(v_in, "Value inside the box"); options["VOut"] = new FieldOptionDouble(v_out, "Value outside the box"); options["XMin"] = new FieldOptionDouble(x_min, "Minimum X coordinate of the box"); options["XMax"] = new FieldOptionDouble(x_max, "Maximum X coordinate of the box"); options["YMin"] = new FieldOptionDouble(y_min, "Minimum Y coordinate of the box"); options["YMax"] = new FieldOptionDouble(y_max, "Maximum Y coordinate of the box"); options["ZMin"] = new FieldOptionDouble(z_min, "Minimum Z coordinate of the box"); options["ZMax"] = new FieldOptionDouble(z_max, "Maximum Z coordinate of the box"); } const char *get_name() { return "Box"; } double operator() (double x, double y, double z, GEntity *ge=0) { return (x <= x_max && x >= x_min && y <= y_max && y >= y_min && z <= z_max && z >= z_min) ? v_in : v_out; } }; class ThresholdField : public Field { int iField; double dmin, dmax, lcmin, lcmax; bool sigmoid, stopAtDistMax; public: const char *get_name() { return "Threshold"; } std::string get_description() { return "F = LCMin if Field[IField] <= DistMin\n" "F = LCMax if Field[IField] >= DistMax\n" "F = Interpolation between LcMin and LcMax if DistMin<Field[IField]<DistMax"; } ThresholdField() { iField = 0; dmin = 1; dmax = 10; lcmin = 0.1; lcmax = 1; sigmoid = false; stopAtDistMax = false; options["IField"] = new FieldOptionInt(iField, "Index of the field to evaluate"); options["DistMin"] = new FieldOptionDouble(dmin, "Distance from entity up to which " "element size will be LcMin"); options["DistMax"] = new FieldOptionDouble(dmax, "Distance from entity after which" "element size will be LcMax"); options["LcMin"] = new FieldOptionDouble(lcmin, "Element size inside DistMin"); options["LcMax"] = new FieldOptionDouble(lcmax, "Element size outside DistMax"); options["Sigmoid"] = new FieldOptionBool(sigmoid, "True to interpolate between LcMin " "and LcMax using a sigmoid, false to " "interpolate linearly"); options["StopAtDistMax"] = new FieldOptionBool(stopAtDistMax, "True to not impose " "element size outside DistMax (i.e. " "F = a very big value if " "Field[IField]>DistMax)"); } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; double r = ((*field) (x, y, z) - dmin) / (dmax - dmin); r = std::max(std::min(r, 1.), 0.); double lc; if(stopAtDistMax && r >= 1.){ lc = MAX_LC; } else if(sigmoid){ double s = exp(12. * r - 6.) / (1. + exp(12. * r - 6.)); lc = lcmin * (1. - s) + lcmax * s; } else{ // linear lc = lcmin * (1 - r) + lcmax * r; } return lc; } }; class GradientField : public Field { int iField, kind; double delta; public: const char *get_name() { return "Gradient"; } std::string get_description() { return "Compute the finite difference gradient of Field[IField].\n " "F = (Field[IField](X + Delta/2) - Field[IField](X - Delta/2))/Delta"; } GradientField() : iField(0), kind(3), delta(CTX.lc / 1e4) { iField = 1; kind = 0; delta = 0.; options["IField"] = new FieldOptionInt(iField, "Field index"); options["Kind"] = new FieldOptionInt(kind, "Component of the gradient to evaluate :" " 0 for X, 1 for Y, 2 for Z, 3 for the norm"); options["Delta"] = new FieldOptionDouble(delta, "Finite difference step"); } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; double gx, gy, gz; switch (kind) { case 0: /* x */ return ((*field) (x + delta / 2, y, z) - (*field) (x - delta / 2, y, z)) / delta; case 1: /* y */ return ((*field) (x, y + delta / 2, z) - (*field) (x, y - delta / 2, z)) / delta; case 2: /* z */ return ((*field) (x, y, z + delta / 2) - (*field) (x, y, z - delta / 2)) / delta; case 3: /* norm */ gx = ((*field) (x + delta / 2, y, z) - (*field) (x - delta / 2, y, z)) / delta; gy = ((*field) (x, y + delta / 2, z) - (*field) (x, y - delta / 2, z)) / delta; gz = ((*field) (x, y, z + delta / 2) - (*field) (x, y, z - delta / 2)) / delta; return sqrt(gx * gx + gy * gy + gz * gz); default: Msg::Error("Field %i : Unknown kind (%i) of gradient.", this->id, kind); return MAX_LC; } } }; class CurvatureField : public Field { int iField; double delta; public: const char *get_name() { return "Curvature"; } std::string get_description() { return "Compute the curvature of Field[IField]. \n" "F = divergence( || grad( Field[IField] ) || )"; } CurvatureField() : iField(0), delta(CTX.lc / 1e4) { iField = 1; delta = 0.; options["IField"] = new FieldOptionInt(iField, "Field index"); options["Delta"] = new FieldOptionDouble(delta, "Step of the finite differences"); } void grad_norm(Field &f,double x,double y,double z, double *g) { g[0] = f(x + delta / 2, y, z) - f(x - delta / 2, y, z); g[1] = f(x, y + delta / 2, z) - f(x, y - delta / 2, z); g[2] = f(x, y, z + delta / 2) - f(x, y, z - delta / 2); double n=sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]); g[0] /= n; g[1] /= n; g[2] /= n; } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; double grad[6][3]; grad_norm(*field, x + delta / 2, y, z, grad[0]); grad_norm(*field, x - delta / 2, y, z, grad[1]); grad_norm(*field, x, y + delta / 2, z, grad[2]); grad_norm(*field, x, y - delta / 2, z, grad[3]); grad_norm(*field, x, y, z + delta / 2, grad[4]); grad_norm(*field, x, y, z - delta / 2, grad[5]); return (grad[0][0] - grad[1][0] + grad[2][1] - grad[3][1] + grad[4][2] - grad[5][2]) / delta; } }; #if defined(HAVE_GSL) #include <gsl/gsl_math.h> #include <gsl/gsl_eigen.h> class MaxEigenHessianField : public Field { int iField; double delta; gsl_eigen_symm_workspace *gslwork; gsl_matrix *gslmat; gsl_vector *eigenvalues; public: const char *get_name() { return "MaxEigenHessian"; } std::string get_description() { return "Compute the maximum eigen value of the Hessian matrix of Field[IField]. " "Gradients are evaluated by finite differences, " "eigenvalues are computed using the GSL library." "F = max ( eigenvalues ( grad ( grad ( Field[IField] ) ) ) ) "; } MaxEigenHessianField() : iField(0), delta(CTX.lc / 1e4) { iField = 1; delta = 0.; options["IField"] = new FieldOptionInt(iField, "Field index"); options["Delta"] = new FieldOptionDouble(delta, "Step used for the finite differences"); gslwork = gsl_eigen_symm_alloc(3); eigenvalues = gsl_vector_alloc(3); gslmat = gsl_matrix_alloc(3, 3); } ~MaxEigenHessianField() { gsl_eigen_symm_free(gslwork); gsl_vector_free(eigenvalues); gsl_matrix_free(gslmat); } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; gsl_matrix_set(gslmat,1,0, (*field) (x + delta/2 , y+delta/2, z) + (*field) (x - delta/2 , y-delta/2, z) - (*field) (x - delta/2 , y+delta/2, z) - (*field) (x + delta/2 , y-delta/2, z)); gsl_matrix_set(gslmat,2,0, (*field) (x + delta/2 , y, z+delta/2) + (*field) (x - delta/2 , y, z-delta/2) - (*field) (x - delta/2 , y, z+delta/2) - (*field) (x + delta/2 , y, z-delta/2)); gsl_matrix_set(gslmat,2,1, (*field) (x, y + delta/2 , z+delta/2) + (*field) (x, y - delta/2 , z-delta/2) - (*field) (x, y - delta/2 , z+delta/2) - (*field) (x, y + delta/2 , z-delta/2)); double f=(*field)(x,y,z); gsl_matrix_set(gslmat,0,0, (*field) (x + delta , y, z)+ (*field) (x - delta , y, z) -2*f); gsl_matrix_set(gslmat,1,1, (*field) (x, y + delta, z)+ (*field) (x , y - delta, z) -2* f); gsl_matrix_set(gslmat,2,2, (*field) (x, y ,z + delta)+ (*field) (x , y, z - delta)-2*f); gsl_eigen_symm(gslmat,eigenvalues,gslwork); return std::max(fabs(gsl_vector_get(eigenvalues, 0)), std::max(fabs(gsl_vector_get(eigenvalues, 0)), fabs(gsl_vector_get(eigenvalues, 1))) ) / (delta * delta); } }; #endif class LaplacianField : public Field { int iField; double delta; public: const char *get_name() { return "Laplacian"; } std::string get_description() { return "Compute finite difference the Laplacian of Field[IField].\n" "F = divergence(gradient(Field[IField])) \n" "F = G(x+d,y,z)+G(x-d,y,z)+G(x,y+d,z)+G(x,y-d,z)+ " "+G(x,y,z+d)+G(x,y,z-d)-6*G(x,y,z) " "where G=Field[IField] and d=delta\n"; } LaplacianField() : iField(0), delta(CTX.lc / 1e4) { iField = 1; delta = 0.1; options["IField"] = new FieldOptionInt(iField, "Field index"); options["Delta"] = new FieldOptionDouble(delta, "Finite difference step"); } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; return ((*field) (x + delta , y, z)+ (*field) (x - delta , y, z) +(*field) (x, y + delta , z)+ (*field) (x, y - delta , z) +(*field) (x, y, z + delta )+ (*field) (x, y, z - delta ) -6* (*field) (x , y, z)) / (delta*delta); } }; class MeanField : public Field { int iField; double delta; public: const char *get_name() { return "Mean"; } std::string get_description() { return "Very simple smoother.\n" "F = (G(x+delta,y,z)+G(x-delta,y,z) " "+G(x,y+delta,z)+G(x,y-delta,z) " "+G(x,y,z+delta)+G(x,y,z-delta) " "+G(x,y,z))/7 " "where G=Field[IField]"; } MeanField() : iField(0), delta(CTX.lc / 1e4) { options["IField"] = new FieldOptionInt(iField, "Field index"); options["Delta"] = new FieldOptionDouble(delta, "Distance used to compute the mean value"); } double operator() (double x, double y, double z, GEntity *ge=0) { Field *field = GModel::current()->getFields()->get(iField); if(!field) return MAX_LC; return ((*field) (x + delta , y, z) + (*field) (x - delta, y, z) + (*field) (x, y + delta, z) + (*field) (x, y - delta, z) + (*field) (x, y, z + delta) + (*field) (x, y, z - delta) + (*field) (x, y, z)) / 7; } }; #if defined(HAVE_MATH_EVAL) class MathEvalExpression { bool error_status; std::list<Field*> *list; int nvalues; char **names; double *values; void *eval; int *evaluators_id; std::string function; char *c_str_function; public: double evaluate(double x, double y, double z) { if(error_status) return MAX_LC; for(int i = 0; i < nvalues; i++){ switch (evaluators_id[i]) { case -1: values[i] = x; break; case -2: values[i] = y; break; case -3: values[i] = z; break; default: { Field *f = GModel::current()->getFields()->get(evaluators_id[i]); values[i] = f ? (*f) (x, y, z) : MAX_LC; } } } return evaluator_evaluate(eval, nvalues, names, values); } MathEvalExpression() { eval = 0; values = 0; c_str_function = 0; evaluators_id = 0; } bool set_function(const std::string & f) { free_members(); error_status = false; c_str_function = strdup(f.c_str()); eval = evaluator_create(c_str_function); if(!eval) { error_status = true; return false; } evaluator_get_variables(eval, &names, &nvalues); values = new double[nvalues]; evaluators_id = new int[nvalues]; for(int i = 0; i < nvalues; i++) { int id; if(!strcmp("x", names[i])) evaluators_id[i] = -1; else if(!strcmp("y", names[i])) evaluators_id[i] = -2; else if(!strcmp("z", names[i])) evaluators_id[i] = -3; else if(sscanf(names[i], "F%i", &id) == 1) evaluators_id[i] = id; else { Msg::Error("Unknown matheval argument \"%s\"\n", names[i]); error_status = true; return false; } } return true; } void free_members() { if(c_str_function) free(c_str_function); if(eval) evaluator_destroy(eval); if(values) delete[]values; if(evaluators_id) delete evaluators_id; } ~MathEvalExpression() { free_members(); } }; class MathEvalField : public Field { MathEvalExpression expr; std::string f; public: MathEvalField() { options["F"] = new FieldOptionString(f, "Mathematical function to evaluate.", &update_needed); f = "F2 + Sin(z)"; } double operator() (double x, double y, double z, GEntity *ge=0) { if(update_needed) { if(!expr.set_function(f)) Msg::Error("Field %i : Invalid matheval expression \"%s\"", this->id, f.c_str()); update_needed = false; } return expr.evaluate(x, y, z); } const char *get_name() { return "MathEval"; } std::string get_description() { return "Evaluate a mathematical expression. " "The expression can contains x, y, z for spatial coordinates, " "F0, F1, ... for field values, and mathematical functions. " "This evaluator is based on a modified version of the GNU libmatheval library. \n" "Example : F2 + Sin(z)"; } }; class ParametricField : public Field { MathEvalExpression expr[3]; std::string f[3]; int ifield; public: ParametricField() { ifield = 1; options["IField"] = new FieldOptionInt(ifield, "Field index"); options["FX"] = new FieldOptionString(f[0], "X component of parametric function", &update_needed); options["FY"] = new FieldOptionString(f[1], "Y component of parametric function", &update_needed); options["FZ"] = new FieldOptionString(f[2], "Z component of parametric function", &update_needed); } std::string get_description() { return "Evaluate Field IField in parametric coordinate. " "See MathEval Field help to get a description of valid FX, FY and FZ expressions.\n" "F = Field[IField](FX,FY,FZ) "; } double operator() (double x, double y, double z, GEntity *ge=0) { if(update_needed) { for(int i = 0; i < 3; i++) { if(!expr[i].set_function(f[i])) Msg::Error("Field %i : Invalid matheval expression \"%s\"", this->id, f[i].c_str()); } update_needed = false; } Field *field = GModel::current()->getFields()->get(ifield); if(!field) return MAX_LC; return (*field)(expr[0].evaluate(x, y, z), expr[1].evaluate(x, y, z), expr[2].evaluate(x, y, z)); } const char *get_name() { return "Param"; } }; #endif #if !defined(HAVE_NO_POST) class PostViewField : public Field { OctreePost *octree; public: int view_index; bool crop_negative_values; double operator() (double x, double y, double z, GEntity *ge=0) { // FIXME: should test unique view num instead, but that would be slower if(view_index < 0 || view_index >= (int)PView::list.size()) return MAX_LC; if(update_needed){ if(octree) delete octree; octree = new OctreePost(PView::list[view_index]); update_needed = false; } double l = 0.; if(!octree->searchScalar(x, y, z, &l, 0)) { // try really hard to find an element around the point /* double fact[4] = {1.e-6, 1.e-5, 1.e-4, 1.e-3}; for(int i = 0; i < 4; i++){ double eps = CTX.lc * fact[i]; printf("approx search witg eps=%g\n", eps); if(octree->searchScalar(x + eps, y, z, &l, 0)) break; if(octree->searchScalar(x - eps, y, z, &l, 0)) break; if(octree->searchScalar(x, y + eps, z, &l, 0)) break; if(octree->searchScalar(x, y - eps, z, &l, 0)) break; if(octree->searchScalar(x, y, z + eps, &l, 0)) break; if(octree->searchScalar(x, y, z - eps, &l, 0)) break; if(octree->searchScalar(x + eps, y - eps, z - eps, &l, 0)) break; if(octree->searchScalar(x + eps, y + eps, z - eps, &l, 0)) break; if(octree->searchScalar(x - eps, y - eps, z - eps, &l, 0)) break; if(octree->searchScalar(x - eps, y + eps, z - eps, &l, 0)) break; if(octree->searchScalar(x + eps, y - eps, z + eps, &l, 0)) break; if(octree->searchScalar(x + eps, y + eps, z + eps, &l, 0)) break; if(octree->searchScalar(x - eps, y - eps, z + eps, &l, 0)) break; if(octree->searchScalar(x - eps, y + eps, z + eps, &l, 0)) break; } */ } if(l <= 0 && crop_negative_values) return MAX_LC; return l; } const char *get_name() { return "PostView"; } std::string get_description() { return "Evaluate the post processing view IView."; } PostViewField() { octree = 0; view_index = 0; options["IView"] = new FieldOptionInt(view_index, "Post-processing view index", &update_needed); crop_negative_values = true; options["CropNegativeValues"] = new FieldOptionBool(crop_negative_values, "return LC_MAX instead of a " "negative value (this option " "is needed for backward " "compatibility with the " "BackgroundMesh option", &update_needed); } ~PostViewField() { if(octree) delete octree; } }; #endif class MinField : public Field { std::list<int> idlist; public: MinField() { options["FieldsList"] = new FieldOptionList(idlist, "Field indices", &update_needed); } std::string get_description() { return "Take the minimum value of a list of fields. "; } double operator() (double x, double y, double z, GEntity *ge=0) { double v = MAX_LC; for(std::list<int>::iterator it = idlist.begin(); it != idlist.end(); it++) { Field *f = (GModel::current()->getFields()->get(*it)); if(f) v = std::min(v, (*f) (x, y, z, ge)); } return v; } const char *get_name() { return "Min"; } }; class MaxField : public Field { std::list<int> idlist; public: MaxField() { options["FieldsList"] = new FieldOptionList(idlist, "Field indices", &update_needed); } std::string get_description() { return "Take the maximum value of a list of fields."; } double operator() (double x, double y, double z, GEntity *ge=0) { double v = -MAX_LC; for(std::list<int>::iterator it = idlist.begin(); it != idlist.end(); it++) { Field *f = (GModel::current()->getFields()->get(*it)); if(f) v = std::max(v, (*f) (x, y, z, ge)); } return v; } const char *get_name() { return "Max"; } }; class RestrictField : public Field { int ifield; std::list<int> edges, faces, regions; public: RestrictField() { ifield = 1; options["IField"] = new FieldOptionInt(ifield, "Field index"); options["EdgesList"] = new FieldOptionList(edges, "Curve indices"); options["FacesList"] = new FieldOptionList(faces, "Surface indices"); options["RegionsList"] = new FieldOptionList(regions, "Volume indices"); } std::string get_description() { return "Restrict the application of a field to a given list of geometrical " "curves, surfaces or volumes. "; } double operator() (double x, double y, double z, GEntity *ge=0) { Field *f = (GModel::current()->getFields()->get(ifield)); if(!f) return MAX_LC; if(!ge) return (*f) (x, y, z); if((ge->dim() == 0) || (ge->dim() == 1 && std::find(edges.begin(), edges.end(), ge->tag()) != edges.end()) || (ge->dim() == 2 && std::find(faces.begin(), faces.end(), ge->tag()) != faces.end()) || (ge->dim() == 3 && std::find(regions.begin(), regions.end(), ge->tag()) != regions.end())) return (*f) (x, y, z); return MAX_LC; } const char *get_name() { return "Restrict"; } }; #ifdef HAVE_ANN class AttractorField : public Field { ANNkd_tree *kdtree; ANNpointArray zeronodes; ANNidxArray index; ANNdistArray dist; std::list<int> nodes_id, edges_id, faces_id; int n_nodes_by_edge; public: AttractorField() : kdtree(0), zeronodes(0) { index = new ANNidx[1]; dist = new ANNdist[1]; n_nodes_by_edge = 20; options["NodesList"] = new FieldOptionList(nodes_id, "Indices of " "nodes in the geomtric model", &update_needed); options["EdgesList"] = new FieldOptionList(edges_id, "Indices of " "curves in the geometric model", &update_needed); options["NNodesByEdge"] = new FieldOptionInt(n_nodes_by_edge, "Number of nodes " "used to discetized each curve", &update_needed); options["FacesList"] = new FieldOptionList(faces_id, "Indices of " "surfaces in the geometric model " "(Warning: might give strange " "results for complex surfaces)", &update_needed); } ~AttractorField() { if(kdtree) delete kdtree; if(zeronodes) annDeallocPts(zeronodes); delete[]index; delete[]dist; } const char *get_name() { return "Attractor"; } std::string get_description() { return "Compute the distance from the nearest node in a list. " "It can also be used to compute distance from curves, in this case each " "curve is replaced by NNodesByEdge equidistant nodes and the distance " "from those nodes is computed. \n" "The ANN library is used to find the nearest node: " "http://www.cs.umd.edu/~mount/ANN/ "; } virtual double operator() (double X, double Y, double Z, GEntity *ge=0) { if(update_needed) { if(zeronodes) { annDeallocPts(zeronodes); delete kdtree; } int totpoints = nodes_id.size() + n_nodes_by_edge * edges_id.size() + n_nodes_by_edge * n_nodes_by_edge * faces_id.size(); if(totpoints) zeronodes = annAllocPts(totpoints, 4); int k = 0; for(std::list<int>::iterator it = nodes_id.begin(); it != nodes_id.end(); ++it) { Vertex *v = FindPoint(*it); if(v) { zeronodes[k][0] = v->Pos.X; zeronodes[k][1] = v->Pos.Y; zeronodes[k++][2] = v->Pos.Z; } else { GVertex *gv = GModel::current()->getVertexByTag(*it); if(gv) { zeronodes[k][0] = gv->x(); zeronodes[k][1] = gv->y(); zeronodes[k++][2] = gv->z(); } } } for(std::list<int>::iterator it = edges_id.begin(); it != edges_id.end(); ++it) { Curve *c = FindCurve(*it); if(c) { for(int i = 0; i < n_nodes_by_edge; i++) { double u = (double)i / (n_nodes_by_edge - 1); Vertex V = InterpolateCurve(c, u, 0); zeronodes[k][0] = V.Pos.X; zeronodes[k][1] = V.Pos.Y; zeronodes[k++][2] = V.Pos.Z; } } else { GEdge *e = GModel::current()->getEdgeByTag(*it); if(e) { for(int i = 0; i < n_nodes_by_edge; i++) { double u = (double)i / (n_nodes_by_edge - 1); Range<double> b = e->parBounds(0); double t = b.low() + u * (b.high() - b.low()); GPoint gp = e->point(t); zeronodes[k][0] = gp.x(); zeronodes[k][1] = gp.y(); zeronodes[k++][2] = gp.z(); } } } } // This can lead to weird results as we generate attractors over // the whole parametric plane (we should really use a mesh, // e.g. a refined STL.) for(std::list<int>::iterator it = faces_id.begin(); it != faces_id.end(); ++it) { Surface *s = FindSurface(*it); if(s) { for(int i = 0; i < n_nodes_by_edge; i++) { for(int j = 0; j < n_nodes_by_edge; j++) { double u = (double)i / (n_nodes_by_edge - 1); double v = (double)j / (n_nodes_by_edge - 1); Vertex V = InterpolateSurface(s, u, v, 0, 0); zeronodes[k][0] = V.Pos.X; zeronodes[k][1] = V.Pos.Y; zeronodes[k++][2] = V.Pos.Z; } } } else { GFace *f = GModel::current()->getFaceByTag(*it); if(f) { for(int i = 0; i < n_nodes_by_edge; i++) { for(int j = 0; j < n_nodes_by_edge; j++) { double u = (double)i / (n_nodes_by_edge - 1); double v = (double)j / (n_nodes_by_edge - 1); Range<double> b1 = ge->parBounds(0); Range<double> b2 = ge->parBounds(1); double t1 = b1.low() + u * (b1.high() - b1.low()); double t2 = b2.low() + v * (b2.high() - b2.low()); GPoint gp = f->point(t1, t2); zeronodes[k][0] = gp.x(); zeronodes[k][1] = gp.y(); zeronodes[k++][2] = gp.z(); } } } } } kdtree = new ANNkd_tree(zeronodes, totpoints, 3); update_needed = false; } double xyz[3] = { X, Y, Z }; kdtree->annkSearch(xyz, 1, index, dist); return sqrt(dist[0]); } }; #endif template<class F> class FieldFactoryT : public FieldFactory { public: Field * operator()() { return new F; } }; template<class F> Field *field_factory() { return new F(); }; FieldManager::FieldManager() { map_type_name["Structured"] = new FieldFactoryT<StructuredField>(); map_type_name["Threshold"] = new FieldFactoryT<ThresholdField>(); map_type_name["Box"] = new FieldFactoryT<BoxField>(); map_type_name["LonLat"] = new FieldFactoryT<LonLatField>(); #if !defined(HAVE_NO_POST) map_type_name["PostView"] = new FieldFactoryT<PostViewField>(); #endif map_type_name["Gradient"] = new FieldFactoryT<GradientField>(); map_type_name["Restrict"] = new FieldFactoryT<RestrictField>(); map_type_name["Min"] = new FieldFactoryT<MinField>(); map_type_name["Max"] = new FieldFactoryT<MaxField>(); map_type_name["UTM"] = new FieldFactoryT<UTMField>(); map_type_name["Laplacian"] = new FieldFactoryT<LaplacianField>(); map_type_name["Mean"] = new FieldFactoryT<MeanField>(); map_type_name["Curvature"] = new FieldFactoryT<CurvatureField>(); #if defined(HAVE_MATH_EVAL) map_type_name["Param"] = new FieldFactoryT<ParametricField>(); map_type_name["MathEval"] = new FieldFactoryT<MathEvalField>(); #endif #if defined(HAVE_ANN) map_type_name["Attractor"] = new FieldFactoryT<AttractorField>(); #endif #if defined(HAVE_GSL) map_type_name["MaxEigenHessian"] = new FieldFactoryT<MaxEigenHessianField>(); #endif background_field = -1; } static void evaluate(Field * field, List_T * list1, int nbElm1, int nbNod, int nbComp, int comp) { if(!nbElm1) return; int nb = List_Nbr(list1) / nbElm1; for(int i = 0; i < List_Nbr(list1); i += nb) { double *x = (double *)List_Pointer_Fast(list1, i); double *y = (double *)List_Pointer_Fast(list1, i + nbNod); double *z = (double *)List_Pointer_Fast(list1, i + 2 * nbNod); for(int j = 0; j < nbNod; j++) { // store data from the main view into v double *val1 = (double *)List_Pointer_Fast(list1, i + 3 * nbNod + nbNod * nbComp * 0 + nbComp * j); val1[comp] = (*field) (x[j], y[j], z[j]); } } } Field::Field() { } #if !defined(HAVE_NO_POST) void Field::put_on_new_view() { std::map<int, std::vector<double> > d; std::vector<GEntity*> entities; GModel::current()->getEntities(entities); for(unsigned int i = 0; i < entities.size(); i++){ for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){ MVertex *v = entities[i]->mesh_vertices[j]; d[v->getNum()].push_back((*this)(v->x(), v->y(), v->z(), entities[i])); } } std::ostringstream oss; oss << "Field " << id; PView *view= new PView(oss.str().c_str(), "NodeData", GModel::current(), d); view->setChanged(true); } void Field::put_on_view(PView * view, int comp) { PViewDataList *data = dynamic_cast<PViewDataList*>(view->getData()); if(!data) return; evaluate(this, data->SP, data->NbSP, 1, 1, 0); evaluate(this, data->SL, data->NbSL, 2, 1, 0); evaluate(this, data->ST, data->NbST, 3, 1, 0); evaluate(this, data->SQ, data->NbSQ, 4, 1, 0); evaluate(this, data->SS, data->NbSS, 4, 1, 0); evaluate(this, data->SH, data->NbSH, 8, 1, 0); evaluate(this, data->SI, data->NbSI, 6, 1, 0); evaluate(this, data->SY, data->NbSY, 5, 1, 0); for(int cc = 0; cc < 3; cc++) { if(comp < 0 || comp == cc) { evaluate(this, data->VP, data->NbVP, 1, 3, cc); evaluate(this, data->VL, data->NbVL, 2, 3, cc); evaluate(this, data->VT, data->NbVT, 3, 3, cc); evaluate(this, data->VQ, data->NbVQ, 4, 3, cc); evaluate(this, data->VS, data->NbVS, 4, 3, cc); evaluate(this, data->VH, data->NbVH, 8, 3, cc); evaluate(this, data->VI, data->NbVI, 6, 3, cc); evaluate(this, data->VY, data->NbVY, 5, 3, cc); } } for(int cc = 0; cc < 9; cc++) { if(comp < 0 || comp == cc) { evaluate(this, data->TP, data->NbTP, 1, 9, cc); evaluate(this, data->TL, data->NbTL, 2, 9, cc); evaluate(this, data->TT, data->NbTT, 3, 9, cc); evaluate(this, data->TQ, data->NbTQ, 4, 9, cc); evaluate(this, data->TS, data->NbTS, 4, 9, cc); evaluate(this, data->TH, data->NbTH, 8, 9, cc); evaluate(this, data->TI, data->NbTI, 6, 9, cc); evaluate(this, data->TY, data->NbTY, 5, 9, cc); } } data->finalize(); view->setChanged(true); } #endif void FieldManager::set_background_mesh(int iView) { int id = new_id(); Field *f = new_field(id, "PostView"); f->options["IView"]->numerical_value(iView); (*this)[id] = f; background_field = id; }