$extrastylesheet
parsed_function.h
Go to the documentation of this file.
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