libMesh
parsed_function.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 #ifndef LIBMESH_PARSED_FUNCTION_H
19 #define LIBMESH_PARSED_FUNCTION_H
20 
21 #include "libmesh/libmesh_config.h"
22 #include "libmesh/function_base.h"
23 
24 #ifdef LIBMESH_HAVE_FPARSER
25 
26 // Local includes
27 #include "libmesh/dense_vector.h"
28 #include "libmesh/int_range.h"
29 #include "libmesh/vector_value.h"
30 #include "libmesh/point.h"
31 
32 // FParser includes
33 #include "libmesh/fparser_ad.hh"
34 
35 // C++ includes
36 #include <algorithm> // std::find
37 #include <cmath>
38 #include <cmath>
39 #include <cstddef>
40 #include <iomanip>
41 #include <limits>
42 #include <memory>
43 #include <sstream>
44 #include <string>
45 #include <vector>
46 
47 namespace libMesh
48 {
49 
59 template <typename Output=Number, typename OutputGradient=Gradient>
60 class ParsedFunction : public FunctionBase<Output>
61 {
62 public:
63  explicit
64  ParsedFunction (std::string expression,
65  const std::vector<std::string> * additional_vars=nullptr,
66  const std::vector<Output> * initial_vals=nullptr);
67 
75 
76  ParsedFunction (ParsedFunction &&) = default;
78  virtual ~ParsedFunction () = default;
79 
83  void reparse (std::string expression);
84 
85  virtual Output operator() (const Point & p,
86  const Real time = 0) override;
87 
91  virtual bool has_derivatives() { return _valid_derivatives; }
92 
93  virtual Output dot(const Point & p,
94  const Real time = 0);
95 
96  virtual OutputGradient gradient(const Point & p,
97  const Real time = 0);
98 
99  virtual void operator() (const Point & p,
100  const Real time,
101  DenseVector<Output> & output) override;
102 
103  virtual Output component (unsigned int i,
104  const Point & p,
105  Real time) override;
106 
107  const std::string & expression() { return _expression; }
108 
112  virtual Output & getVarAddress(std::string_view variable_name);
113 
114  virtual std::unique_ptr<FunctionBase<Output>> clone() const override;
115 
124  Output get_inline_value(std::string_view inline_var_name) const;
125 
136  void set_inline_value(std::string_view inline_var_name,
137  Output newval);
138 
139 protected:
143  void partial_reparse (std::string expression);
144 
148  std::size_t find_name (std::string_view varname,
149  std::string_view expr) const;
150 
154  bool expression_is_time_dependent( std::string_view expression ) const;
155 
156 private:
160  void set_spacetime(const Point & p,
161  const Real time = 0);
162 
166  inline Output eval(FunctionParserADBase<Output> & parser,
167  std::string_view libmesh_dbg_var(function_name),
168  unsigned int libmesh_dbg_var(component_idx)) const;
169 
170  std::string _expression;
171  std::vector<std::string> _subexpressions;
172  std::vector<std::unique_ptr<FunctionParserADBase<Output>>> parsers;
173  std::vector<Output> _spacetime;
174 
175  // derivative functions
176  std::vector<std::unique_ptr<FunctionParserADBase<Output>>> dx_parsers;
177 #if LIBMESH_DIM > 1
178  std::vector<std::unique_ptr<FunctionParserADBase<Output>>> dy_parsers;
179 #endif
180 #if LIBMESH_DIM > 2
181  std::vector<std::unique_ptr<FunctionParserADBase<Output>>> dz_parsers;
182 #endif
183  std::vector<std::unique_ptr<FunctionParserADBase<Output>>> dt_parsers;
185 
186  // Variables/values that can be parsed and handled by the function parser
187  std::string variables;
188  std::vector<std::string> _additional_vars;
189  std::vector<Output> _initial_vals;
190 };
191 
192 
193 /*----------------------- Inline functions ----------------------------------*/
194 
195 template <typename Output, typename OutputGradient>
196 inline
198  const std::vector<std::string> * additional_vars,
199  const std::vector<Output> * initial_vals) :
200  _expression (), // overridden by parse()
201  // Size the spacetime vector to account for space, time, and any additional
202  // variables passed
203  _spacetime (LIBMESH_DIM+1 + (additional_vars ? additional_vars->size() : 0)),
204  _valid_derivatives (true),
205  _additional_vars (additional_vars ? *additional_vars : std::vector<std::string>()),
206  _initial_vals (initial_vals ? *initial_vals : std::vector<Output>())
207 {
208  // time-dependence established in reparse function
209  this->reparse(std::move(expression));
210  this->_initialized = true;
211 }
212 
213 
214 template <typename Output, typename OutputGradient>
215 inline
217  FunctionBase<Output>(other),
218  _expression(other._expression),
219  _subexpressions(other._subexpressions),
220  _spacetime(other._spacetime),
221  _valid_derivatives(other._valid_derivatives),
222  variables(other.variables),
223  _additional_vars(other._additional_vars),
224  _initial_vals(other._initial_vals)
225 {
226  // parsers can be generated from scratch by reparsing expression
227  this->reparse(this->_expression);
228  this->_initialized = true;
229 }
230 
231 
232 
233 template <typename Output, typename OutputGradient>
234 inline
237 {
238  // Use copy-and-swap idiom
240  std::swap(tmp, *this);
241  return *this;
242 }
243 
244 
245 template <typename Output, typename OutputGradient>
246 inline
247 void
249 {
250  variables = "x";
251 #if LIBMESH_DIM > 1
252  variables += ",y";
253 #endif
254 #if LIBMESH_DIM > 2
255  variables += ",z";
256 #endif
257  variables += ",t";
258 
259  // If additional vars were passed, append them to the string
260  // that we send to the function parser. Also add them to the
261  // end of our spacetime vector
262  for (auto i : index_range(_additional_vars))
263  {
264  variables += "," + _additional_vars[i];
265  // Initialize extra variables to the vector passed in or zero
266  // Note: The initial_vals vector can be shorter than the additional_vars vector
267  _spacetime[LIBMESH_DIM+1 + i] =
268  (i < _initial_vals.size()) ? _initial_vals[i] : 0;
269  }
270 
271  this->_is_time_dependent = this->expression_is_time_dependent(expression);
272 
273  this->partial_reparse(std::move(expression));
274 }
275 
276 template <typename Output, typename OutputGradient>
277 inline
278 Output
280 {
281  set_spacetime(p, time);
282  return eval(*parsers[0], "f", 0);
283 }
284 
285 template <typename Output, typename OutputGradient>
286 inline
287 Output
289 {
290  set_spacetime(p, time);
291  return eval(*dt_parsers[0], "df/dt", 0);
292 }
293 
294 template <typename Output, typename OutputGradient>
295 inline
296 OutputGradient
298 {
299  OutputGradient grad;
300  set_spacetime(p, time);
301 
302  grad(0) = eval(*dx_parsers[0], "df/dx", 0);
303 #if LIBMESH_DIM > 1
304  grad(1) = eval(*dy_parsers[0], "df/dy", 0);
305 #endif
306 #if LIBMESH_DIM > 2
307  grad(2) = eval(*dz_parsers[0], "df/dz", 0);
308 #endif
309 
310  return grad;
311 }
312 
313 template <typename Output, typename OutputGradient>
314 inline
315 void
317  (const Point & p,
318  const Real time,
319  DenseVector<Output> & output)
320 {
321  set_spacetime(p, time);
322 
323  unsigned int size = output.size();
324 
325  libmesh_assert_equal_to (size, parsers.size());
326 
327  // The remaining locations in _spacetime are currently fixed at construction
328  // but could potentially be made dynamic
329  for (unsigned int i=0; i != size; ++i)
330  output(i) = eval(*parsers[i], "f", i);
331 }
332 
337 template <typename Output, typename OutputGradient>
338 inline
339 Output
341  const Point & p,
342  Real time)
343 {
344  set_spacetime(p, time);
345  libmesh_assert_less (i, parsers.size());
346 
347  // The remaining locations in _spacetime are currently fixed at construction
348  // but could potentially be made dynamic
349  libmesh_assert_less(i, parsers.size());
350  return eval(*parsers[i], "f", i);
351 }
352 
356 template <typename Output, typename OutputGradient>
357 inline
358 Output &
360 {
361  const std::vector<std::string>::iterator it =
362  std::find(_additional_vars.begin(), _additional_vars.end(), variable_name);
363 
364  libmesh_error_msg_if(it == _additional_vars.end(),
365  "ERROR: Requested variable not found in parsed function");
366 
367  // Iterator Arithmetic (How far from the end of the array is our target address?)
368  return _spacetime[_spacetime.size() - (_additional_vars.end() - it)];
369 }
370 
371 
372 template <typename Output, typename OutputGradient>
373 inline
374 std::unique_ptr<FunctionBase<Output>>
376 {
377  return std::make_unique<ParsedFunction>(_expression,
378  &_additional_vars,
379  &_initial_vals);
380 }
381 
382 template <typename Output, typename OutputGradient>
383 inline
384 Output
385 ParsedFunction<Output,OutputGradient>::get_inline_value (std::string_view inline_var_name) const
386 {
387  libmesh_assert_greater (_subexpressions.size(), 0);
388 
389 #ifndef NDEBUG
390  bool found_var_name = false;
391 #endif
392  Output old_var_value(0.);
393 
394  for (const auto & subexpression : _subexpressions)
395  {
396  const std::size_t varname_i =
397  find_name(inline_var_name, subexpression);
398  if (varname_i == std::string::npos)
399  continue;
400 
401  const std::size_t assignment_i =
402  subexpression.find(":", varname_i+1);
403 
404  libmesh_assert_not_equal_to(assignment_i, std::string::npos);
405 
406  libmesh_assert_equal_to(subexpression[assignment_i+1], '=');
407  for (std::size_t i = varname_i+1; i != assignment_i; ++i)
408  libmesh_assert_equal_to(subexpression[i], ' ');
409 
410  std::size_t end_assignment_i =
411  subexpression.find(";", assignment_i+1);
412 
413  libmesh_assert_not_equal_to(end_assignment_i, std::string::npos);
414 
415  std::string new_subexpression =
416  subexpression.substr(0, end_assignment_i+1).append(inline_var_name);
417 
418 #ifdef LIBMESH_HAVE_FPARSER
419  // Parse and evaluate the new subexpression.
420  // Add the same constants as we used originally.
421  FunctionParserADBase<Output> fp;
422  fp.AddConstant("NaN", std::numeric_limits<Real>::quiet_NaN());
423  fp.AddConstant("pi", std::acos(Real(-1)));
424  fp.AddConstant("e", std::exp(Real(1)));
425  libmesh_error_msg_if
426  (fp.Parse(new_subexpression, variables) != -1, // -1 for success
427  "ERROR: FunctionParser is unable to parse modified expression: "
428  << new_subexpression << '\n' << fp.ErrorMsg());
429 
430  Output new_var_value = this->eval(fp, new_subexpression, 0);
431 #ifdef NDEBUG
432  return new_var_value;
433 #else
434  if (found_var_name)
435  {
436  libmesh_assert_equal_to(old_var_value, new_var_value);
437  }
438  else
439  {
440  old_var_value = new_var_value;
441  found_var_name = true;
442  }
443 #endif
444 
445 #else
446  libmesh_error_msg("ERROR: This functionality requires fparser!");
447 #endif
448  }
449 
450  libmesh_assert(found_var_name);
451  return old_var_value;
452 }
453 
454 
455 template <typename Output, typename OutputGradient>
456 inline
457 void
459  Output newval)
460 {
461  libmesh_assert_greater (_subexpressions.size(), 0);
462 
463 #ifndef NDEBUG
464  bool found_var_name = false;
465 #endif
466  for (auto & subexpression : _subexpressions)
467  {
468  const std::size_t varname_i =
469  find_name(inline_var_name, subexpression);
470  if (varname_i == std::string::npos)
471  continue;
472 
473 #ifndef NDEBUG
474  found_var_name = true;
475 #endif
476  const std::size_t assignment_i =
477  subexpression.find(":", varname_i+1);
478 
479  libmesh_assert_not_equal_to(assignment_i, std::string::npos);
480 
481  libmesh_assert_equal_to(subexpression[assignment_i+1], '=');
482  for (std::size_t i = varname_i+1; i != assignment_i; ++i)
483  libmesh_assert_equal_to(subexpression[i], ' ');
484 
485  std::size_t end_assignment_i =
486  subexpression.find(";", assignment_i+1);
487 
488  libmesh_assert_not_equal_to(end_assignment_i, std::string::npos);
489 
490  std::ostringstream new_subexpression;
491  new_subexpression << subexpression.substr(0, assignment_i+2)
492  << std::setprecision(std::numeric_limits<Output>::digits10+2)
493 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
494  << '(' << newval.real() << '+'
495  << newval.imag() << 'i' << ')'
496 #else
497  << newval
498 #endif
499  << subexpression.substr(end_assignment_i,
500  std::string::npos);
501  subexpression = new_subexpression.str();
502  }
503 
504  libmesh_assert(found_var_name);
505 
506  std::string new_expression;
507 
508  for (const auto & subexpression : _subexpressions)
509  {
510  new_expression += '{';
511  new_expression += subexpression;
512  new_expression += '}';
513  }
514 
515  this->partial_reparse(new_expression);
516 }
517 
518 
519 template <typename Output, typename OutputGradient>
520 inline
521 void
523 {
524  _expression = std::move(expression);
525  _subexpressions.clear();
526  parsers.clear();
527 
528  size_t nextstart = 0, end = 0;
529 
530  while (end != std::string::npos)
531  {
532  // If we're past the end of the string, we can't make any more
533  // subparsers
534  if (nextstart >= _expression.size())
535  break;
536 
537  // If we're at the start of a brace delimited section, then we
538  // parse just that section:
539  if (_expression[nextstart] == '{')
540  {
541  nextstart++;
542  end = _expression.find('}', nextstart);
543  }
544  // otherwise we parse the whole thing
545  else
546  end = std::string::npos;
547 
548  // We either want the whole end of the string (end == npos) or
549  // a substring in the middle.
550  _subexpressions.push_back
551  (_expression.substr(nextstart, (end == std::string::npos) ?
552  std::string::npos : end - nextstart));
553 
554  // fparser can crash on empty expressions
555  libmesh_error_msg_if(_subexpressions.back().empty(),
556  "ERROR: FunctionParser is unable to parse empty expression.\n");
557 
558  // Parse (and optimize if possible) the subexpression.
559  // Add some basic constants, to Real precision.
560  auto fp = std::make_unique<FunctionParserADBase<Output>>();
561  fp->AddConstant("NaN", std::numeric_limits<Real>::quiet_NaN());
562  fp->AddConstant("pi", std::acos(Real(-1)));
563  fp->AddConstant("e", std::exp(Real(1)));
564  libmesh_error_msg_if
565  (fp->Parse(_subexpressions.back(), variables) != -1, // -1 for success
566  "ERROR: FunctionParser is unable to parse expression: "
567  << _subexpressions.back() << '\n' << fp->ErrorMsg());
568 
569  // use of derivatives is optional. suppress error output on the console
570  // use the has_derivatives() method to check if AutoDiff was successful.
571  // also enable immediate optimization
572  fp->SetADFlags(FunctionParserADBase<Output>::ADSilenceErrors |
573  FunctionParserADBase<Output>::ADAutoOptimize);
574 
575  // optimize original function
576  fp->Optimize();
577 
578  // generate derivatives through automatic differentiation
579  auto dx_fp = std::make_unique<FunctionParserADBase<Output>>(*fp);
580  if (dx_fp->AutoDiff("x") != -1) // -1 for success
581  _valid_derivatives = false;
582  dx_parsers.push_back(std::move(dx_fp));
583 #if LIBMESH_DIM > 1
584  auto dy_fp = std::make_unique<FunctionParserADBase<Output>>(*fp);
585  if (dy_fp->AutoDiff("y") != -1) // -1 for success
586  _valid_derivatives = false;
587  dy_parsers.push_back(std::move(dy_fp));
588 #endif
589 #if LIBMESH_DIM > 2
590  auto dz_fp = std::make_unique<FunctionParserADBase<Output>>(*fp);
591  if (dz_fp->AutoDiff("z") != -1) // -1 for success
592  _valid_derivatives = false;
593  dz_parsers.push_back(std::move(dz_fp));
594 #endif
595  auto dt_fp = std::make_unique<FunctionParserADBase<Output>>(*fp);
596  if (dt_fp->AutoDiff("t") != -1) // -1 for success
597  _valid_derivatives = false;
598  dt_parsers.push_back(std::move(dt_fp));
599 
600  // If at end, use nextstart=maxSize. Else start at next
601  // character.
602  nextstart = (end == std::string::npos) ?
603  std::string::npos : end + 1;
604 
605  // Store fp for later use
606  parsers.push_back(std::move(fp));
607  }
608 }
609 
610 
611 template <typename Output, typename OutputGradient>
612 inline
613 std::size_t
615  std::string_view expr) const
616 {
617  const std::size_t namesize = varname.size();
618  std::size_t varname_i = expr.find(varname);
619 
620  while ((varname_i != std::string::npos) &&
621  (((varname_i > 0) &&
622  (std::isalnum(expr[varname_i-1]) ||
623  (expr[varname_i-1] == '_'))) ||
624  ((varname_i+namesize < expr.size()) &&
625  (std::isalnum(expr[varname_i+namesize]) ||
626  (expr[varname_i+namesize] == '_')))))
627  {
628  varname_i = expr.find(varname, varname_i+1);
629  }
630 
631  return varname_i;
632 }
633 template <typename Output, typename OutputGradient>
634 inline
635 bool
637 {
638  bool is_time_dependent = false;
639 
640  // By definition, time is "t" for FunctionBase-based objects, so we just need to
641  // see if this expression has the variable "t" in it.
642  if (this->find_name(std::string("t"), expression) != std::string::npos)
643  is_time_dependent = true;
644 
645  return is_time_dependent;
646 }
647 
648 // Set the _spacetime argument vector
649 template <typename Output, typename OutputGradient>
650 inline
651 void
653  const Real time)
654 {
655  _spacetime[0] = p(0);
656 #if LIBMESH_DIM > 1
657  _spacetime[1] = p(1);
658 #endif
659 #if LIBMESH_DIM > 2
660  _spacetime[2] = p(2);
661 #endif
662  _spacetime[LIBMESH_DIM] = time;
663 
664  // The remaining locations in _spacetime are currently fixed at construction
665  // but could potentially be made dynamic
666 }
667 
668 // Evaluate the ith FunctionParser and check the result
669 template <typename Output, typename OutputGradient>
670 inline
671 Output
672 ParsedFunction<Output,OutputGradient>::eval (FunctionParserADBase<Output> & parser,
673  std::string_view function_name,
674  unsigned int component_idx) const
675 {
676  Output result = parser.Eval(_spacetime.data());
677  int error_code = parser.EvalError();
678  if (error_code)
679  {
680  libMesh::err << "ERROR: FunctionParser is unable to evaluate component "
681  << component_idx
682  << " for '"
683  << function_name;
684 
685  for (auto i : index_range(parsers))
686  if (parsers[i].get() == &parser)
687  libMesh::err << "' of expression '"
688  << _subexpressions[i];
689 
690  libMesh::err << "' with arguments:\n";
691  for (const auto & item : _spacetime)
692  libMesh::err << '\t' << item << '\n';
693  libMesh::err << '\n';
694 
695  // Currently no API to report error messages, we'll do it manually
696  std::string error_message = "Reason: ";
697 
698  switch (error_code)
699  {
700  case 1:
701  error_message += "Division by zero";
702  break;
703  case 2:
704  error_message += "Square Root error (negative value)";
705  break;
706  case 3:
707  error_message += "Log error (negative value)";
708  break;
709  case 4:
710  error_message += "Trigonometric error (asin or acos of illegal value)";
711  break;
712  case 5:
713  error_message += "Maximum recursion level reached";
714  break;
715  default:
716  error_message += "Unknown";
717  break;
718  }
719  libmesh_error_msg(error_message);
720  }
721 
722  return result;
723 }
724 
725 } // namespace libMesh
726 
727 
728 #else // !LIBMESH_HAVE_FPARSER
729 
730 
731 namespace libMesh {
732 
733 
734 template <typename Output=Number>
735 class ParsedFunction : public FunctionBase<Output>
736 {
737 public:
738  ParsedFunction (std::string /* expression */,
739  const std::vector<std::string> * = nullptr,
740  const std::vector<Output> * = nullptr) : _dummy(0)
741  {
742  libmesh_not_implemented();
743  }
744 
749  ParsedFunction (ParsedFunction &&) = delete;
750  ParsedFunction (const ParsedFunction &) = delete;
751  ParsedFunction & operator= (const ParsedFunction &) = delete;
753  virtual ~ParsedFunction () = default;
754 
755  virtual Output operator() (const Point &,
756  const Real /* time */ = 0)
757  { return 0.; }
758 
759  virtual void operator() (const Point &,
760  const Real /* time */,
761  DenseVector<Output> & /* output */) {}
762 
763  virtual void init() {}
764  virtual void clear() {}
765  virtual Output & getVarAddress(std::string_view /*variable_name*/) { return _dummy; }
766  virtual std::unique_ptr<FunctionBase<Output>> clone() const
767  {
768  return std::make_unique<ParsedFunction<Output>>("");
769  }
770 private:
771  Output _dummy;
772 };
773 
774 
775 
776 } // namespace libMesh
777 
778 
779 #endif // LIBMESH_HAVE_FPARSER
780 
781 #endif // LIBMESH_PARSED_FUNCTION_H
virtual Output dot(const Point &p, const Real time=0)
OStreamProxy err
void set_inline_value(std::string_view inline_var_name, Output newval)
Changes the value of an inline variable.
virtual std::unique_ptr< FunctionBase< Output > > clone() const override
Output get_inline_value(std::string_view inline_var_name) const
virtual void clear()
Clears the function.
void partial_reparse(std::string expression)
Re-parse with minor changes to expression.
Output eval(FunctionParserADBase< Output > &parser, std::string_view libmesh_dbg_var(function_name), unsigned int libmesh_dbg_var(component_idx)) const
Evaluate the ith FunctionParser and check the result.
A Function generated (via FParser) by parsing a mathematical expression.
ParsedFunction & operator=(const ParsedFunction &)
std::vector< Output > _spacetime
std::vector< std::unique_ptr< FunctionParserADBase< Output > > > parsers
std::vector< std::string > _additional_vars
void set_spacetime(const Point &p, const Real time=0)
Set the _spacetime argument vector.
std::vector< std::string > _subexpressions
The libMesh namespace provides an interface to certain functionality in the library.
std::vector< std::unique_ptr< FunctionParserADBase< Output > > > dy_parsers
std::vector< std::unique_ptr< FunctionParserADBase< Output > > > dt_parsers
bool expression_is_time_dependent(std::string_view expression) const
std::size_t find_name(std::string_view varname, std::string_view expr) const
Helper function for parsing out variable names.
virtual Output & getVarAddress(std::string_view variable_name)
virtual Output component(unsigned int i, const Point &p, Real time) override
libmesh_assert(ctx)
ParsedFunction(std::string expression, const std::vector< std::string > *additional_vars=nullptr, const std::vector< Output > *initial_vals=nullptr)
virtual ~ParsedFunction()=default
std::vector< std::unique_ptr< FunctionParserADBase< Output > > > dx_parsers
virtual std::unique_ptr< FunctionBase< Output > > clone() const
virtual bool has_derivatives()
Query if the automatic derivative generation was successful.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual Output & getVarAddress(std::string_view)
ParsedFunction(std::string, const std::vector< std::string > *=nullptr, const std::vector< Output > *=nullptr)
std::vector< std::unique_ptr< FunctionParserADBase< Output > > > dz_parsers
void reparse(std::string expression)
Re-parse with new expression.
Base class for functors that can be evaluated at a point and (optionally) time.
std::vector< Output > _initial_vals
virtual Output operator()(const Point &p, const Real time=0) override
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117
const std::string & expression()
virtual void init()
The actual initialization process.
virtual OutputGradient gradient(const Point &p, const Real time=0)