libMesh
parsed_fem_function.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 
19 
20 #ifndef LIBMESH_PARSED_FEM_FUNCTION_H
21 #define LIBMESH_PARSED_FEM_FUNCTION_H
22 
23 #include "libmesh/libmesh_config.h"
24 
25 // Local Includes
26 #include "libmesh/fem_function_base.h"
27 #include "libmesh/int_range.h"
28 #include "libmesh/point.h"
29 #include "libmesh/system.h"
30 
31 #ifdef LIBMESH_HAVE_FPARSER
32 // FParser includes
33 #include "libmesh/fparser.hh"
34 #endif
35 
36 // C++ includes
37 #include <cctype>
38 #include <iomanip>
39 #include <sstream>
40 #include <string>
41 #include <vector>
42 #include <memory>
43 
44 namespace libMesh
45 {
46 
56 template <typename Output=Number>
57 class ParsedFEMFunction : public FEMFunctionBase<Output>
58 {
59 public:
60 
64  explicit
65  ParsedFEMFunction (const System & sys,
66  std::string expression,
67  const std::vector<std::string> * additional_vars=nullptr,
68  const std::vector<Output> * initial_vals=nullptr);
69 
81  ParsedFEMFunction (ParsedFEMFunction &&) = default;
82  virtual ~ParsedFEMFunction () = default;
83 
87  void reparse (std::string expression);
88 
89  virtual void init_context (const FEMContext & c) override;
90 
91  virtual std::unique_ptr<FEMFunctionBase<Output>> clone () const override;
92 
93  virtual Output operator() (const FEMContext & c,
94  const Point & p,
95  const Real time = 0.) override;
96 
97  void operator() (const FEMContext & c,
98  const Point & p,
99  const Real time,
100  DenseVector<Output> & output) override;
101 
102  virtual Output component(const FEMContext & c,
103  unsigned int i,
104  const Point & p,
105  Real time=0.) override;
106 
107  const std::string & expression() { return _expression; }
108 
117  Output get_inline_value(std::string_view inline_var_name) const;
118 
129  void set_inline_value(std::string_view inline_var_name,
130  Output newval);
131 
132 protected:
133  // Helper function for reparsing minor changes to expression
134  void partial_reparse (std::string expression);
135 
136  // Helper function for parsing out variable names
137  std::size_t find_name (std::string_view varname,
138  std::string_view expr) const;
139 
140  // Helper function for evaluating function arguments
141  void eval_args(const FEMContext & c,
142  const Point & p,
143  const Real time);
144 
145  // Evaluate the ith FunctionParser and check the result
146 #ifdef LIBMESH_HAVE_FPARSER
147  inline Output eval(FunctionParserBase<Output> & parser,
148  std::string_view libmesh_dbg_var(function_name),
149  unsigned int libmesh_dbg_var(component_idx)) const;
150 #else // LIBMESH_HAVE_FPARSER
151  inline Output eval(char & libmesh_dbg_var(parser),
152  std::string_view libmesh_dbg_var(function_name),
153  unsigned int libmesh_dbg_var(component_idx)) const;
154 #endif
155 
156 private:
157  const System & _sys;
158  std::string _expression;
159  std::vector<std::string> _subexpressions;
160  unsigned int _n_vars,
165 #ifdef LIBMESH_HAVE_FPARSER
166  std::vector<std::unique_ptr<FunctionParserBase<Output>>> parsers;
167 #else
168  std::vector<char*> parsers;
169 #endif
170  std::vector<Output> _spacetime;
171 
172  // Flags for which variables need to be computed
173 
174  // _need_var[v] is true iff value(v) is needed
175  std::vector<bool> _need_var;
176 
177  // _need_var_grad[v*LIBMESH_DIM+i] is true iff grad(v,i) is needed
178  std::vector<bool> _need_var_grad;
179 
180 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
181  // _need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+i*LIBMESH_DIM+j] is true
182  // iff grad(v,i,j) is needed
183  std::vector<bool> _need_var_hess;
184 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
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>
196 inline
198  std::string expression,
199  const std::vector<std::string> * additional_vars,
200  const std::vector<Output> * initial_vals) :
201  _sys(sys),
202  _expression (), // overridden by parse()
203  _n_vars(sys.n_vars()),
204  _n_requested_vars(0),
205  _n_requested_grad_components(0),
206  _n_requested_hess_components(0),
207  _requested_normals(false),
208  _need_var(_n_vars, false),
209  _need_var_grad(_n_vars*LIBMESH_DIM, false),
210 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
211  _need_var_hess(_n_vars*LIBMESH_DIM*LIBMESH_DIM, false),
212 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
213  _additional_vars (additional_vars ? *additional_vars : std::vector<std::string>()),
214  _initial_vals (initial_vals ? *initial_vals : std::vector<Output>())
215 {
216  this->reparse(std::move(expression));
217 }
218 
219 
220 template <typename Output>
221 inline
223  FEMFunctionBase<Output>(other),
224  _sys(other._sys),
225  _expression(other._expression),
226  _subexpressions(other._subexpressions),
227  _n_vars(other._n_vars),
228  _n_requested_vars(other._n_requested_vars),
229  _n_requested_grad_components(other._n_requested_grad_components),
230  _n_requested_hess_components(other._n_requested_hess_components),
231  _requested_normals(other._requested_normals),
232  _spacetime(other._spacetime),
233  _need_var(other._need_var),
234  _need_var_grad(other._need_var_grad),
235 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
236  _need_var_hess(other._need_var_hess),
237 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
238  variables(other.variables),
239  _additional_vars(other._additional_vars),
240  _initial_vals(other._initial_vals)
241 {
242  this->reparse(_expression);
243 }
244 
245 
246 template <typename Output>
247 inline
250 {
251  // We can only be assigned another ParsedFEMFunction defined on the same System
252  libmesh_assert(&_sys == &other._sys);
253 
254  // Use copy-and-swap idiom
255  ParsedFEMFunction<Output> tmp(other);
256  std::swap(tmp, *this);
257  return *this;
258 }
259 
260 
261 template <typename Output>
262 inline
263 void
264 ParsedFEMFunction<Output>::reparse (std::string expression)
265 {
266  variables = "x";
267 #if LIBMESH_DIM > 1
268  variables += ",y";
269 #endif
270 #if LIBMESH_DIM > 2
271  variables += ",z";
272 #endif
273  variables += ",t";
274 
275  for (unsigned int v=0; v != _n_vars; ++v)
276  {
277  const std::string & varname = _sys.variable_name(v);
278  std::size_t varname_i = find_name(varname, expression);
279 
280  // If we didn't find our variable name then let's go to the
281  // next.
282  if (varname_i == std::string::npos)
283  continue;
284 
285  _need_var[v] = true;
286  variables += ',';
287  variables += varname;
288  _n_requested_vars++;
289  }
290 
291  for (unsigned int v=0; v != _n_vars; ++v)
292  {
293  const std::string & varname = _sys.variable_name(v);
294 
295  for (unsigned int d=0; d != LIBMESH_DIM; ++d)
296  {
297  std::string gradname = std::string("grad_") +
298  "xyz"[d] + '_' + varname;
299  std::size_t gradname_i = find_name(gradname, expression);
300 
301  // If we didn't find that gradient component of our
302  // variable name then let's go to the next.
303  if (gradname_i == std::string::npos)
304  continue;
305 
306  _need_var_grad[v*LIBMESH_DIM+d] = true;
307  variables += ',';
308  variables += gradname;
309  _n_requested_grad_components++;
310  }
311  }
312 
313 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
314  for (unsigned int v=0; v != _n_vars; ++v)
315  {
316  const std::string & varname = _sys.variable_name(v);
317 
318  for (unsigned int d1=0; d1 != LIBMESH_DIM; ++d1)
319  for (unsigned int d2=0; d2 != LIBMESH_DIM; ++d2)
320  {
321  std::string hessname = std::string("hess_") +
322  "xyz"[d1] + "xyz"[d2] + '_' + varname;
323  std::size_t hessname_i = find_name(hessname, expression);
324 
325  // If we didn't find that hessian component of our
326  // variable name then let's go to the next.
327  if (hessname_i == std::string::npos)
328  continue;
329 
330  _need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+d1*LIBMESH_DIM+d2]
331  = true;
332  variables += ',';
333  variables += hessname;
334  _n_requested_hess_components++;
335  }
336  }
337 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
338 
339  {
340  std::size_t nx_i = find_name("n_x", expression);
341  std::size_t ny_i = find_name("n_y", expression);
342  std::size_t nz_i = find_name("n_z", expression);
343 
344  // If we found any requests for normal components then we'll
345  // compute normals
346  if (nx_i != std::string::npos ||
347  ny_i != std::string::npos ||
348  nz_i != std::string::npos)
349  {
350  _requested_normals = true;
351  variables += ',';
352  variables += "n_x";
353  if (LIBMESH_DIM > 1)
354  {
355  variables += ',';
356  variables += "n_y";
357  }
358  if (LIBMESH_DIM > 2)
359  {
360  variables += ',';
361  variables += "n_z";
362  }
363  }
364  }
365 
366  _spacetime.resize
367  (LIBMESH_DIM + 1 + _n_requested_vars +
368  _n_requested_grad_components + _n_requested_hess_components +
369  (_requested_normals ? LIBMESH_DIM : 0) +
370  _additional_vars.size());
371 
372  // If additional vars were passed, append them to the string
373  // that we send to the function parser. Also add them to the
374  // end of our spacetime vector
375  unsigned int offset = LIBMESH_DIM + 1 + _n_requested_vars +
376  _n_requested_grad_components + _n_requested_hess_components;
377 
378  for (auto i : index_range(_additional_vars))
379  {
380  variables += "," + _additional_vars[i];
381  // Initialize extra variables to the vector passed in or zero
382  // Note: The initial_vals vector can be shorter than the additional_vars vector
383  _spacetime[offset + i] =
384  (i < _initial_vals.size()) ? _initial_vals[i] : 0;
385  }
386 
387  this->partial_reparse(std::move(expression));
388 }
389 
390 template <typename Output>
391 inline
392 void
394 {
395  for (unsigned int v=0; v != _n_vars; ++v)
396  {
397  FEBase * elem_fe;
398  c.get_element_fe(v, elem_fe);
399  if (_n_requested_vars)
400  elem_fe->get_phi();
401  if (_n_requested_grad_components)
402  elem_fe->get_dphi();
403 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
404  if (_n_requested_hess_components)
405  elem_fe->get_d2phi();
406 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
407  }
408 
409  if (_requested_normals)
410  {
411  FEBase * side_fe;
412  c.get_side_fe(0, side_fe);
413 
414  side_fe->get_normals();
415 
416  // FIXME: this is a hack to support normals at quadrature
417  // points; we don't support normals elsewhere.
418  side_fe->get_xyz();
419  }
420 }
421 
422 template <typename Output>
423 inline
424 std::unique_ptr<FEMFunctionBase<Output>>
426 {
427  return std::make_unique<ParsedFEMFunction>
428  (_sys, _expression, &_additional_vars, &_initial_vals);
429 }
430 
431 template <typename Output>
432 inline
433 Output
435  const Point & p,
436  const Real time)
437 {
438  eval_args(c, p, time);
439 
440  return eval(*parsers[0], "f", 0);
441 }
442 
443 
444 
445 template <typename Output>
446 inline
447 void
449  const Point & p,
450  const Real time,
451  DenseVector<Output> & output)
452 {
453  eval_args(c, p, time);
454 
455  unsigned int size = output.size();
456 
457  libmesh_assert_equal_to (size, parsers.size());
458 
459  for (unsigned int i=0; i != size; ++i)
460  output(i) = eval(*parsers[i], "f", i);
461 }
462 
463 
464 template <typename Output>
465 inline
466 Output
468  unsigned int i,
469  const Point & p,
470  Real time)
471 {
472  eval_args(c, p, time);
473 
474  libmesh_assert_less (i, parsers.size());
475  return eval(*parsers[i], "f", i);
476 }
477 
478 template <typename Output>
479 inline
480 Output
481 ParsedFEMFunction<Output>::get_inline_value(std::string_view inline_var_name) const
482 {
483  libmesh_assert_greater (_subexpressions.size(), 0);
484 
485 #ifndef NDEBUG
486  bool found_var_name = false;
487 #endif
488  Output old_var_value(0.);
489 
490  for (const std::string & subexpression : _subexpressions)
491  {
492  const std::size_t varname_i =
493  find_name(inline_var_name, subexpression);
494  if (varname_i == std::string::npos)
495  continue;
496 
497  const std::size_t assignment_i =
498  subexpression.find(":", varname_i+1);
499 
500  libmesh_assert_not_equal_to(assignment_i, std::string::npos);
501 
502  libmesh_assert_equal_to(subexpression[assignment_i+1], '=');
503  for (std::size_t i = varname_i+1; i != assignment_i; ++i)
504  libmesh_assert_equal_to(subexpression[i], ' ');
505 
506  std::size_t end_assignment_i =
507  subexpression.find(";", assignment_i+1);
508 
509  libmesh_assert_not_equal_to(end_assignment_i, std::string::npos);
510 
511  std::string new_subexpression =
512  subexpression.substr(0, end_assignment_i+1).
513  append(inline_var_name);
514 
515 #ifdef LIBMESH_HAVE_FPARSER
516  // Parse and evaluate the new subexpression.
517  // Add the same constants as we used originally.
518  auto fp = std::make_unique<FunctionParserBase<Output>>();
519  fp->AddConstant("NaN", std::numeric_limits<Real>::quiet_NaN());
520  fp->AddConstant("pi", std::acos(Real(-1)));
521  fp->AddConstant("e", std::exp(Real(1)));
522  libmesh_error_msg_if
523  (fp->Parse(new_subexpression, variables) != -1, // -1 for success
524  "ERROR: FunctionParser is unable to parse modified expression: "
525  << new_subexpression << '\n' << fp->ErrorMsg());
526 
527  Output new_var_value = this->eval(*fp, new_subexpression, 0);
528 #ifdef NDEBUG
529  return new_var_value;
530 #else
531  if (found_var_name)
532  {
533  libmesh_assert_equal_to(old_var_value, new_var_value);
534  }
535  else
536  {
537  old_var_value = new_var_value;
538  found_var_name = true;
539  }
540 #endif
541 
542 #else
543  libmesh_error_msg("ERROR: This functionality requires fparser!");
544 #endif
545  }
546 
547  libmesh_assert(found_var_name);
548  return old_var_value;
549 }
550 
551 template <typename Output>
552 inline
553 void
554 ParsedFEMFunction<Output>::set_inline_value (std::string_view inline_var_name,
555  Output newval)
556 {
557  libmesh_assert_greater (_subexpressions.size(), 0);
558 
559 #ifndef NDEBUG
560  bool found_var_name = false;
561 #endif
562  for (auto s : index_range(_subexpressions))
563  {
564  const std::string & subexpression = _subexpressions[s];
565  const std::size_t varname_i =
566  find_name(inline_var_name, subexpression);
567  if (varname_i == std::string::npos)
568  continue;
569 
570 #ifndef NDEBUG
571  found_var_name = true;
572 #endif
573 
574  const std::size_t assignment_i =
575  subexpression.find(":", varname_i+1);
576 
577  libmesh_assert_not_equal_to(assignment_i, std::string::npos);
578 
579  libmesh_assert_equal_to(subexpression[assignment_i+1], '=');
580  for (std::size_t i = varname_i+1; i != assignment_i; ++i)
581  libmesh_assert_equal_to(subexpression[i], ' ');
582 
583  std::size_t end_assignment_i =
584  subexpression.find(";", assignment_i+1);
585 
586  libmesh_assert_not_equal_to(end_assignment_i, std::string::npos);
587 
588  std::ostringstream new_subexpression;
589  new_subexpression << subexpression.substr(0, assignment_i+2)
590  << std::setprecision(std::numeric_limits<Output>::digits10+2)
591 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
592  << '(' << newval.real() << '+'
593  << newval.imag() << 'i' << ')'
594 #else
595  << newval
596 #endif
597  << subexpression.substr(end_assignment_i,
598  std::string::npos);
599  _subexpressions[s] = new_subexpression.str();
600  }
601 
602  libmesh_assert(found_var_name);
603 
604  std::string new_expression;
605 
606  for (const auto & subexpression : _subexpressions)
607  {
608  new_expression += '{';
609  new_expression += subexpression;
610  new_expression += '}';
611  }
612 
613  this->partial_reparse(new_expression);
614 }
615 
616 
617 template <typename Output>
618 inline
619 void
621 {
622  _expression = std::move(expression);
623  _subexpressions.clear();
624  parsers.clear();
625 
626  size_t nextstart = 0, end = 0;
627 
628  while (end != std::string::npos)
629  {
630  // If we're past the end of the string, we can't make any more
631  // subparsers
632  if (nextstart >= _expression.size())
633  break;
634 
635  // If we're at the start of a brace delimited section, then we
636  // parse just that section:
637  if (_expression[nextstart] == '{')
638  {
639  nextstart++;
640  end = _expression.find('}', nextstart);
641  }
642  // otherwise we parse the whole thing
643  else
644  end = std::string::npos;
645 
646  // We either want the whole end of the string (end == npos) or
647  // a substring in the middle.
648  _subexpressions.push_back
649  (_expression.substr(nextstart, (end == std::string::npos) ?
650  std::string::npos : end - nextstart));
651 
652  // fparser can crash on empty expressions
653  libmesh_error_msg_if(_subexpressions.back().empty(),
654  "ERROR: FunctionParser is unable to parse empty expression.\n");
655 
656 
657 #ifdef LIBMESH_HAVE_FPARSER
658  // Parse (and optimize if possible) the subexpression.
659  // Add some basic constants, to Real precision.
660  auto fp = std::make_unique<FunctionParserBase<Output>>();
661  fp->AddConstant("NaN", std::numeric_limits<Real>::quiet_NaN());
662  fp->AddConstant("pi", std::acos(Real(-1)));
663  fp->AddConstant("e", std::exp(Real(1)));
664  libmesh_error_msg_if
665  (fp->Parse(_subexpressions.back(), variables) != -1, // -1 for success
666  "ERROR: FunctionParser is unable to parse expression: "
667  << _subexpressions.back() << '\n' << fp->ErrorMsg());
668  fp->Optimize();
669  parsers.push_back(std::move(fp));
670 #else
671  libmesh_error_msg("ERROR: This functionality requires fparser!");
672 #endif
673 
674  // If at end, use nextstart=maxSize. Else start at next
675  // character.
676  nextstart = (end == std::string::npos) ?
677  std::string::npos : end + 1;
678  }
679 }
680 
681 template <typename Output>
682 inline
683 std::size_t
684 ParsedFEMFunction<Output>::find_name (std::string_view varname,
685  std::string_view expr) const
686 {
687  const std::size_t namesize = varname.size();
688  std::size_t varname_i = expr.find(varname);
689 
690  while ((varname_i != std::string::npos) &&
691  (((varname_i > 0) &&
692  (std::isalnum(expr[varname_i-1]) ||
693  (expr[varname_i-1] == '_'))) ||
694  ((varname_i+namesize < expr.size()) &&
695  (std::isalnum(expr[varname_i+namesize]) ||
696  (expr[varname_i+namesize] == '_')))))
697  {
698  varname_i = expr.find(varname, varname_i+1);
699  }
700 
701  return varname_i;
702 }
703 
704 
705 
706 // Helper function for evaluating function arguments
707 template <typename Output>
708 inline
709 void
711  const Point & p,
712  const Real time)
713 {
714  _spacetime[0] = p(0);
715 #if LIBMESH_DIM > 1
716  _spacetime[1] = p(1);
717 #endif
718 #if LIBMESH_DIM > 2
719  _spacetime[2] = p(2);
720 #endif
721  _spacetime[LIBMESH_DIM] = time;
722 
723  unsigned int request_index = 0;
724  for (unsigned int v=0; v != _n_vars; ++v)
725  {
726  if (!_need_var[v])
727  continue;
728 
729  c.point_value(v, p, _spacetime[LIBMESH_DIM+1+request_index]);
730  request_index++;
731  }
732 
733  if (_n_requested_grad_components)
734  for (unsigned int v=0; v != _n_vars; ++v)
735  {
736  if (!_need_var_grad[v*LIBMESH_DIM]
737 #if LIBMESH_DIM > 1
738  && !_need_var_grad[v*LIBMESH_DIM+1]
739 #if LIBMESH_DIM > 2
740  && !_need_var_grad[v*LIBMESH_DIM+2]
741 #endif
742 #endif
743  )
744  continue;
745 
746  Gradient g;
747  c.point_gradient(v, p, g);
748 
749  for (unsigned int d=0; d != LIBMESH_DIM; ++d)
750  {
751  if (!_need_var_grad[v*LIBMESH_DIM+d])
752  continue;
753 
754  _spacetime[LIBMESH_DIM+1+request_index] = g(d);
755  request_index++;
756  }
757  }
758 
759 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
760  if (_n_requested_hess_components)
761  for (unsigned int v=0; v != _n_vars; ++v)
762  {
763  if (!_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM]
764 #if LIBMESH_DIM > 1
765  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+1]
766  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+2]
767  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+3]
768 #if LIBMESH_DIM > 2
769  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+4]
770  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+5]
771  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+6]
772  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+7]
773  && !_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+8]
774 #endif
775 #endif
776  )
777  continue;
778 
779  Tensor h;
780  c.point_hessian(v, p, h);
781 
782  for (unsigned int d1=0; d1 != LIBMESH_DIM; ++d1)
783  for (unsigned int d2=0; d2 != LIBMESH_DIM; ++d2)
784  {
785  if (!_need_var_hess[v*LIBMESH_DIM*LIBMESH_DIM+d1*LIBMESH_DIM+d2])
786  continue;
787 
788  _spacetime[LIBMESH_DIM+1+request_index] = h(d1,d2);
789  request_index++;
790  }
791  }
792 #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
793 
794  if (_requested_normals)
795  {
796  FEBase * side_fe;
797  c.get_side_fe(0, side_fe);
798 
799  const std::vector<Point> & normals = side_fe->get_normals();
800 
801  const std::vector<Point> & xyz = side_fe->get_xyz();
802 
803  libmesh_assert_equal_to(normals.size(), xyz.size());
804 
805  // We currently only support normals at quadrature points!
806 #ifndef NDEBUG
807  bool at_quadrature_point = false;
808 #endif
809  for (auto qp : index_range(normals))
810  {
811  if (p == xyz[qp])
812  {
813  const Point & n = normals[qp];
814  for (unsigned int d=0; d != LIBMESH_DIM; ++d)
815  {
816  _spacetime[LIBMESH_DIM+1+request_index] = n(d);
817  request_index++;
818  }
819 #ifndef NDEBUG
820  at_quadrature_point = true;
821 #endif
822  break;
823  }
824  }
825 
826  libmesh_assert(at_quadrature_point);
827  }
828 
829  // The remaining locations in _spacetime are currently set at construction
830  // but could potentially be made dynamic
831 }
832 
833 
834 // Evaluate the ith FunctionParser and check the result
835 #ifdef LIBMESH_HAVE_FPARSER
836 template <typename Output>
837 inline
838 Output
839 ParsedFEMFunction<Output>::eval (FunctionParserBase<Output> & parser,
840  std::string_view libmesh_dbg_var(function_name),
841  unsigned int libmesh_dbg_var(component_idx)) const
842 {
843 #ifndef NDEBUG
844  Output result = parser.Eval(_spacetime.data());
845  int error_code = parser.EvalError();
846  if (error_code)
847  {
848  libMesh::err << "ERROR: FunctionParser is unable to evaluate component "
849  << component_idx
850  << " for '"
851  << function_name;
852 
853  for (auto i : index_range(parsers))
854  if (parsers[i].get() == &parser)
855  libMesh::err << "' of expression '"
856  << _subexpressions[i];
857 
858  libMesh::err << "' with arguments:\n";
859  for (const auto & st : _spacetime)
860  libMesh::err << '\t' << st << '\n';
861  libMesh::err << '\n';
862 
863  // Currently no API to report error messages, we'll do it manually
864  std::string error_message = "Reason: ";
865 
866  switch (error_code)
867  {
868  case 1:
869  error_message += "Division by zero";
870  break;
871  case 2:
872  error_message += "Square Root error (negative value)";
873  break;
874  case 3:
875  error_message += "Log error (negative value)";
876  break;
877  case 4:
878  error_message += "Trigonometric error (asin or acos of illegal value)";
879  break;
880  case 5:
881  error_message += "Maximum recursion level reached";
882  break;
883  default:
884  error_message += "Unknown";
885  break;
886  }
887  libmesh_error_msg(error_message);
888  }
889 
890  return result;
891 #else
892  return parser.Eval(_spacetime.data());
893 #endif
894 }
895 #else // LIBMESH_HAVE_FPARSER
896 template <typename Output>
897 inline
898 Output
899 ParsedFEMFunction<Output>::eval (char & /*parser*/,
900  std::string_view /*function_name*/,
901  unsigned int /*component_idx*/) const
902 {
903  libmesh_error_msg("ERROR: This functionality requires fparser!");
904  return Output(0);
905 }
906 #endif
907 
908 
909 } // namespace libMesh
910 
911 #endif // LIBMESH_PARSED_FEM_FUNCTION_H
OStreamProxy err
std::vector< bool > _need_var_grad
virtual void init_context(const FEMContext &c) override
Prepares a context object for use.
std::vector< Output > _initial_vals
void get_side_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge/face (2D/3D) finite element object for variable var for the largest dimension in th...
Definition: fem_context.h:317
std::vector< std::string > _additional_vars
std::size_t find_name(std::string_view varname, std::string_view expr) const
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
std::vector< std::string > _subexpressions
void partial_reparse(std::string expression)
std::vector< Output > _spacetime
The libMesh namespace provides an interface to certain functionality in the library.
virtual std::unique_ptr< FEMFunctionBase< Output > > clone() const override
Number point_value(unsigned int var, const Point &p) const
Definition: fem_context.C:869
virtual Output operator()(const FEMContext &c, const Point &p, const Real time=0.) override
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition: fe_base.h:230
Output get_inline_value(std::string_view inline_var_name) const
unsigned int n_vars
ParsedFEMFunction provides support for FParser-based parsed functions in FEMSystem.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
void reparse(std::string expression)
Re-parse with new expression.
virtual ~ParsedFEMFunction()=default
std::vector< std::unique_ptr< FunctionParserBase< Output > > > parsers
Tensor point_hessian(unsigned int var, const Point &p) const
Definition: fem_context.C:967
libmesh_assert(ctx)
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
virtual_for_inffe const std::vector< Point > & get_xyz() const
Definition: fe_abstract.h:272
const std::vector< std::vector< OutputTensor > > & get_d2phi() const
Definition: fe_base.h:319
ParsedFEMFunction(const System &sys, std::string expression, const std::vector< std::string > *additional_vars=nullptr, const std::vector< Output > *initial_vals=nullptr)
Constructor.
ParsedFEMFunction & operator=(const ParsedFEMFunction &)
Constructors.
std::vector< bool > _need_var_hess
void set_inline_value(std::string_view inline_var_name, Output newval)
Changes the value of an inline variable.
void eval_args(const FEMContext &c, const Point &p, const Real time)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual_for_inffe const std::vector< Point > & get_normals() const
Definition: fe_abstract.h:451
virtual unsigned int size() const override final
Definition: dense_vector.h:104
virtual Output component(const FEMContext &c, unsigned int i, const Point &p, Real time=0.) override
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh...
Definition: fem_context.h:277
std::vector< char * > parsers
Gradient point_gradient(unsigned int var, const Point &p) const
Definition: fem_context.C:915
FEMFunctionBase is a base class from which users can derive in order to define "function-like" object...
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:111
This class forms the foundation from which generic finite elements may be derived.
const std::string & expression()
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
Output eval(FunctionParserBase< Output > &parser, std::string_view libmesh_dbg_var(function_name), unsigned int libmesh_dbg_var(component_idx)) const
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition: fe_base.h:207