$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 #ifndef LIBMESH_PARSED_FUNCTION_H 00019 #define LIBMESH_PARSED_FUNCTION_H 00020 00021 #include "libmesh/libmesh_config.h" 00022 #include "libmesh/function_base.h" 00023 00024 #ifdef LIBMESH_HAVE_FPARSER 00025 00026 // Local includes 00027 #include "libmesh/dense_vector.h" 00028 #include "libmesh/vector_value.h" 00029 #include "libmesh/point.h" 00030 00031 // FParser includes 00032 #include "libmesh/fparser_ad.hh" 00033 00034 // C++ includes 00035 #include <algorithm> // std::find 00036 #include <cmath> 00037 #include <cmath> 00038 #include <cstddef> 00039 #include <limits> 00040 #include <string> 00041 00042 namespace libMesh { 00043 00044 template <typename Output=Number, typename OutputGradient=Gradient> 00045 class ParsedFunction : public FunctionBase<Output> 00046 { 00047 public: 00048 explicit 00049 ParsedFunction (const std::string& expression, const std::vector<std::string>* additional_vars=NULL, 00050 const std::vector<Output>* initial_vals=NULL) : 00051 _expression (expression), 00052 _spacetime (LIBMESH_DIM+1 + (additional_vars ? 00053 additional_vars->size() : 0)), 00054 _valid_derivatives (true), 00055 _additional_vars (additional_vars ? *additional_vars : 00056 std::vector<std::string>()), 00057 _initial_vals (initial_vals ? *initial_vals : 00058 std::vector<Output>()) 00059 // Size the spacetime vector to account for space, time, and any additional 00060 // variables passed 00061 //_spacetime(LIBMESH_DIM+1 + (additional_vars ? additional_vars->size() : 0)), 00062 { 00063 std::string variables = "x"; 00064 #if LIBMESH_DIM > 1 00065 variables += ",y"; 00066 #endif 00067 #if LIBMESH_DIM > 2 00068 variables += ",z"; 00069 #endif 00070 variables += ",t"; 00071 00072 // If additional vars were passed, append them to the string 00073 // that we send to the function parser. Also add them to the 00074 // end of our spacetime vector 00075 if (additional_vars) 00076 { 00077 for (unsigned int i=0; i < additional_vars->size(); ++i) 00078 { 00079 variables += "," + (*additional_vars)[i]; 00080 // Initialize extra variables to the vector passed in or zero 00081 // Note: The initial_vals vector can be shorter than the additional_vars vector 00082 _spacetime[LIBMESH_DIM+1 + i] = (initial_vals && i < initial_vals->size()) ? (*initial_vals)[i] : 0; 00083 } 00084 } 00085 00086 size_t nextstart = 0, end = 0; 00087 00088 while (end != std::string::npos) 00089 { 00090 // If we're past the end of the string, we can't make any more 00091 // subparsers 00092 if (nextstart >= expression.size()) 00093 break; 00094 00095 // If we're at the start of a brace delimited section, then we 00096 // parse just that section: 00097 if (expression[nextstart] == '{') 00098 { 00099 nextstart++; 00100 end = expression.find('}', nextstart); 00101 } 00102 // otherwise we parse the whole thing 00103 else 00104 end = std::string::npos; 00105 00106 // We either want the whole end of the string (end == npos) or 00107 // a substring in the middle. 00108 std::string subexpression = 00109 expression.substr(nextstart, (end == std::string::npos) ? 00110 std::string::npos : end - nextstart); 00111 00112 // fparser can crash on empty expressions 00113 if (subexpression.empty()) 00114 libmesh_error_msg("ERROR: FunctionParser is unable to parse empty expression.\n"); 00115 00116 // Parse (and optimize if possible) the subexpression. 00117 // Add some basic constants, to Real precision. 00118 FunctionParserADBase<Output> fp; 00119 fp.AddConstant("NaN", std::numeric_limits<Real>::quiet_NaN()); 00120 fp.AddConstant("pi", std::acos(Real(-1))); 00121 fp.AddConstant("e", std::exp(Real(1))); 00122 if (fp.Parse(subexpression, variables) != -1) // -1 for success 00123 libmesh_error_msg("ERROR: FunctionParser is unable to parse expression: " << subexpression << '\n' << fp.ErrorMsg()); 00124 00125 // use of derivatives is optional. suppress error output on the console 00126 // use the has_derivatives() method to check if AutoDiff was successful. 00127 fp.silenceAutoDiffErrors(); 00128 00129 // generate derivatives through automatic differentiation 00130 FunctionParserADBase<Output> dx_fp(fp); 00131 if (dx_fp.AutoDiff("x") != -1) // -1 for success 00132 _valid_derivatives = false; 00133 dx_fp.Optimize(); 00134 dx_parsers.push_back(dx_fp); 00135 #if LIBMESH_DIM > 1 00136 FunctionParserADBase<Output> dy_fp(fp); 00137 if (dy_fp.AutoDiff("y") != -1) // -1 for success 00138 _valid_derivatives = false; 00139 dy_fp.Optimize(); 00140 dy_parsers.push_back(dy_fp); 00141 #endif 00142 #if LIBMESH_DIM > 2 00143 FunctionParserADBase<Output> dz_fp(fp); 00144 if (dz_fp.AutoDiff("z") != -1) // -1 for success 00145 _valid_derivatives = false; 00146 dz_fp.Optimize(); 00147 dz_parsers.push_back(dz_fp); 00148 #endif 00149 FunctionParserADBase<Output> dt_fp(fp); 00150 if (dt_fp.AutoDiff("t") != -1) // -1 for success 00151 _valid_derivatives = false; 00152 dt_fp.Optimize(); 00153 dt_parsers.push_back(dt_fp); 00154 00155 // now optimise original function (after derivatives are taken) 00156 fp.Optimize(); 00157 parsers.push_back(fp); 00158 00159 // If at end, use nextstart=maxSize. Else start at next 00160 // character. 00161 nextstart = (end == std::string::npos) ? 00162 std::string::npos : end + 1; 00163 } 00164 00165 this->_initialized = true; 00166 } 00167 00168 virtual Output operator() (const Point& p, 00169 const Real time = 0) 00170 { 00171 set_spacetime(p, time); 00172 return eval(parsers[0], "f", 0); 00173 } 00174 00175 // Query if the automatic derivative generation was successful 00176 virtual bool has_derivatives() { return _valid_derivatives; } 00177 00178 virtual Output dot(const Point& p, 00179 const Real time = 0) 00180 { 00181 set_spacetime(p, time); 00182 return eval(dt_parsers[0], "df/dt", 0); 00183 } 00184 00185 virtual OutputGradient gradient(const Point& p, 00186 const Real time = 0) 00187 { 00188 OutputGradient grad; 00189 set_spacetime(p, time); 00190 00191 grad(0) = eval(dx_parsers[0], "df/dx", 0); 00192 #if LIBMESH_DIM > 1 00193 grad(1) = eval(dy_parsers[0], "df/dy", 0); 00194 #endif 00195 #if LIBMESH_DIM > 2 00196 grad(2) = eval(dz_parsers[0], "df/dz", 0); 00197 #endif 00198 00199 return grad; 00200 } 00201 00202 virtual void operator() (const Point& p, 00203 const Real time, 00204 DenseVector<Output>& output) 00205 { 00206 set_spacetime(p, time); 00207 00208 unsigned int size = output.size(); 00209 00210 libmesh_assert_equal_to (size, parsers.size()); 00211 00212 // The remaining locations in _spacetime are currently fixed at construction 00213 // but could potentially be made dynamic 00214 for (unsigned int i=0; i != size; ++i) 00215 output(i) = eval(parsers[i], "f", i); 00216 } 00217 00222 virtual Output component (unsigned int i, 00223 const Point& p, 00224 Real time) 00225 { 00226 set_spacetime(p, time); 00227 libmesh_assert_less (i, parsers.size()); 00228 00229 // The remaining locations in _spacetime are currently fixed at construction 00230 // but could potentially be made dynamic 00231 libmesh_assert_less(i, parsers.size()); 00232 return eval(parsers[i], "f", i); 00233 } 00234 00238 virtual Output & getVarAddress(const std::string & variable_name) 00239 { 00240 const std::vector<std::string>::iterator it = 00241 std::find(_additional_vars.begin(), _additional_vars.end(), variable_name); 00242 00243 if (it == _additional_vars.end()) 00244 libmesh_error_msg("ERROR: Requested variable not found in parsed function"); 00245 00246 // Iterator Arithmetic (How far from the end of the array is our target address?) 00247 return _spacetime[_spacetime.size() - (_additional_vars.end() - it)]; 00248 } 00249 00250 00251 virtual UniquePtr<FunctionBase<Output> > clone() const { 00252 return UniquePtr<FunctionBase<Output> > 00253 (new ParsedFunction(_expression, &_additional_vars, &_initial_vals)); 00254 } 00255 00256 private: 00257 // Set the _spacetime argument vector 00258 void set_spacetime(const Point& p, 00259 const Real time = 0) 00260 { 00261 _spacetime[0] = p(0); 00262 #if LIBMESH_DIM > 1 00263 _spacetime[1] = p(1); 00264 #endif 00265 #if LIBMESH_DIM > 2 00266 _spacetime[2] = p(2); 00267 #endif 00268 _spacetime[LIBMESH_DIM] = time; 00269 00270 // The remaining locations in _spacetime are currently fixed at construction 00271 // but could potentially be made dynamic 00272 } 00273 00274 // Evaluate the ith FunctionParser and check the result 00275 inline Output eval(FunctionParserADBase<Output> & parser, 00276 const std::string & libmesh_dbg_var(function_name), 00277 unsigned int libmesh_dbg_var(component_idx)) 00278 { 00279 #ifndef NDEBUG 00280 Output result = parser.Eval(&_spacetime[0]); 00281 int error_code = parser.EvalError(); 00282 if (error_code) 00283 { 00284 libMesh::err << "ERROR: FunctionParser is unable to evaluate component " 00285 << component_idx 00286 << " of expression '" 00287 << function_name 00288 << "' with arguments:\n"; 00289 for (unsigned int j=0; j<_spacetime.size(); ++j) 00290 libMesh::err << '\t' << _spacetime[j] << '\n'; 00291 libMesh::err << '\n'; 00292 00293 // Currently no API to report error messages, we'll do it manually 00294 std::string error_message = "Reason: "; 00295 00296 switch (error_code) 00297 { 00298 case 1: 00299 error_message += "Division by zero"; 00300 break; 00301 case 2: 00302 error_message += "Square Root error (negative value)"; 00303 break; 00304 case 3: 00305 error_message += "Log error (negative value)"; 00306 break; 00307 case 4: 00308 error_message += "Trigonometric error (asin or acos of illegal value)"; 00309 break; 00310 case 5: 00311 error_message += "Maximum recursion level reached"; 00312 break; 00313 default: 00314 error_message += "Unknown"; 00315 break; 00316 } 00317 libmesh_error_msg(error_message); 00318 } 00319 00320 return result; 00321 #else 00322 return parser.Eval(&_spacetime[0]); 00323 #endif 00324 } 00325 00326 std::string _expression; 00327 std::vector<FunctionParserADBase<Output> > parsers; 00328 std::vector<Output> _spacetime; 00329 00330 // derivative functions 00331 std::vector<FunctionParserADBase<Output> > dx_parsers; 00332 #if LIBMESH_DIM > 1 00333 std::vector<FunctionParserADBase<Output> > dy_parsers; 00334 #endif 00335 #if LIBMESH_DIM > 2 00336 std::vector<FunctionParserADBase<Output> > dz_parsers; 00337 #endif 00338 std::vector<FunctionParserADBase<Output> > dt_parsers; 00339 bool _valid_derivatives; 00340 00341 // Additional variables/values that can be parsed and handled by the function parser 00342 std::vector<std::string> _additional_vars; 00343 std::vector<Output> _initial_vals; 00344 }; 00345 00346 00347 } // namespace libMesh 00348 00349 00350 #else // LIBMESH_HAVE_FPARSER 00351 00352 00353 namespace libMesh { 00354 00355 00356 template <typename Output=Number> 00357 class ParsedFunction : public FunctionBase<Output> 00358 { 00359 public: 00360 ParsedFunction (std::string /* expression */) : _dummy(0) 00361 { 00362 libmesh_not_implemented(); 00363 } 00364 00365 virtual Output operator() (const Point&, 00366 const Real /* time */ = 0) 00367 { return 0.; } 00368 00369 virtual void operator() (const Point&, 00370 const Real /* time */, 00371 DenseVector<Output>& /* output */) {} 00372 00373 virtual void init() {} 00374 virtual void clear() {} 00375 virtual Output & getVarAddress(const std::string & /*variable_name*/) { return _dummy; } 00376 virtual UniquePtr<FunctionBase<Output> > clone() const { 00377 return UniquePtr<FunctionBase<Output> > 00378 (new ParsedFunction<Output>("")); 00379 } 00380 private: 00381 Output _dummy; 00382 }; 00383 00384 00385 } // namespace libMesh 00386 00387 00388 #endif // LIBMESH_HAVE_FPARSER 00389 00390 #endif // LIBMESH_PARSED_FUNCTION_H