Flow123d  JS_before_hm-1601-gc6ac32d
field_formula.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file field_formula.cc
15  * @brief
16  */
17 
18 
19 
20 #include "fields/field_formula.hh"
21 #include "fields/field_instances.hh" // for instantiation macros
22 #include "fields/surface_depth.hh"
23 #include "fparser.hh"
24 #include "input/input_type.hh"
25 #include "include/arena_alloc.hh" // bparser
26 #include <boost/algorithm/string/replace.hpp>
27 
28 
29 /// Implementation.
30 
31 namespace it = Input::Type;
32 
33 FLOW123D_FORCE_LINK_IN_CHILD(field_formula)
34 
35 
36 template <int spacedim, class Value>
37 const Input::Type::Record & FieldFormula<spacedim, Value>::get_input_type()
38 {
39 
40  return it::Record("FieldFormula", FieldAlgorithmBase<spacedim,Value>::template_name()+" Field given by runtime interpreted formula.")
43  .declare_key("value", STI::get_input_type() , it::Default::obligatory(),
44  "String, array of strings, or matrix of strings with formulas for individual "
45  "entries of scalar, vector, or tensor value respectively.\n"
46  "For vector values, you can use just one string to enter homogeneous vector.\n"
47  "For square (($N\\times N$))-matrix values, you can use:\n\n"
48  " - array of strings of size (($N$)) to enter diagonal matrix\n"
49  " - array of strings of size (($\\frac12N(N+1)$)) to enter symmetric matrix (upper triangle, row by row)\n"
50  " - just one string to enter (spatially variable) multiple of the unit matrix.\n"
51  "Formula can contain variables ```x,y,z,t,d``` and usual operators and functions." )
52  //.declare_key("unit", FieldAlgorithmBase<spacedim, Value>::get_input_type_unit_si(), it::Default::optional(),
53  // "Definition of unit.")
54  .declare_key("surface_direction", it::String(), it::Default("\"0 0 1\""),
55  "The vector used to project evaluation point onto the surface.")
56  .declare_key("surface_region", it::String(), it::Default::optional(),
57  "The name of region set considered as the surface. You have to set surface region if you "
58  "want to use formula variable ```d```.")
59  .allow_auto_conversion("value")
60  .close();
61 }
62 
63 
64 
65 template <int spacedim, class Value>
67  Input::register_class< FieldFormula<spacedim, Value>, unsigned int >("FieldFormula") +
69 
70 
71 
72 template <int spacedim, class Value>
74 : FieldAlgorithmBase<spacedim, Value>(n_comp),
75  formula_matrix_(this->value_.n_rows(), this->value_.n_cols()),
76  first_time_set_(true), arena_alloc_(nullptr)
77 {
78  this->is_constant_in_space_ = false;
79  parser_matrix_.resize(this->value_.n_rows());
80  for(unsigned int row=0; row < this->value_.n_rows(); row++) {
81  parser_matrix_[row].resize(this->value_.n_cols());
82  }
83  b_parser_.reserve(this->value_.n_rows()*this->value_.n_cols());
84  for(unsigned int i=0; i < this->value_.n_rows()*this->value_.n_cols(); i++) {
85  b_parser_.emplace_back( 1.1 * CacheMapElementNumber::get() );
86  }
87 }
88 
89 
90 
91 template <int spacedim, class Value>
93  this->init_unit_conversion_coefficient(rec, init_data);
94 
95  // read formulas form input
96  STI::init_from_input( formula_matrix_, rec.val<typename STI::AccessType>("value") );
97  in_rec_ = rec;
98 }
99 
100 
101 template <int spacedim, class Value>
103 
104  /* OLD FPARSER CODE */
105  bool any_parser_changed = false;
106  std::string value_input_address = in_rec_.address_string();
107  has_depth_var_ = false;
108  this->is_constant_in_space_ = true; // set flag to true, then if found 'x', 'y', 'z' or 'd' reset to false
109 
110 
111  std::string vars = string("x,y,z").substr(0, 2*spacedim-1);
112  std::vector<bool> time_dependent(this->value_.n_rows() * this->value_.n_cols(), false);
113  // update parsers
114  for(unsigned int row=0; row < this->value_.n_rows(); row++)
115  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
116  // get all variable names from the formula
117  std::vector<std::string> var_list;
118 
119  FunctionParser tmp_parser;
120  tmp_parser.AddConstant("Pi", 3.14159265358979323846);
121  tmp_parser.AddConstant("E", 2.71828182845904523536);
122 
123 
124 #pragma GCC diagnostic push
125 #pragma GCC diagnostic ignored "-Wunused-variable"
126  {
127  int err=tmp_parser.ParseAndDeduceVariables(formula_matrix_.at(row,col), var_list);
128  ASSERT(err != FunctionParser::FP_NO_ERROR)(tmp_parser.ErrorMsg()).error("ParseAndDeduceVariables error\n");
129  }
130 #pragma GCC diagnostic pop
131 
132  for(std::string &var_name : var_list ) {
133  if (var_name == std::string("t") ) time_dependent[row*this->value_.n_rows()+col]=true;
134  else if (var_name == std::string("d") ) {
135  this->is_constant_in_space_ = false;
136  if (surface_depth_)
137  has_depth_var_=true;
138  else
139  WarningOut().fmt("Unset surface region. Variable '{}' in the FieldFormula[{}][{}] == '{}' will be set to zero\n at the input address:\n {} \n",
140  var_name, row, col, formula_matrix_.at(row,col), value_input_address );
141  }
142  else if (var_name == "x" || var_name == "y" || var_name == "z") {
143  this->is_constant_in_space_ = false;
144  continue;
145  }
146  else
147  WarningOut().fmt("Unknown variable '{}' in the FieldFormula[{}][{}] == '{}'\n at the input address:\n {} \n",
148  var_name, row, col, formula_matrix_.at(row,col), value_input_address );
149  }
150 
151  // Seems that we can not just add 't' constant to tmp_parser, since it was already Parsed.
152  parser_matrix_[row][col].AddConstant("Pi", 3.14159265358979323846);
153  parser_matrix_[row][col].AddConstant("E", 2.71828182845904523536);
154  if (time_dependent[row*this->value_.n_rows()+col]) {
155  parser_matrix_[row][col].AddConstant("t", time.end());
156  }
157  }
158 
159  if (has_depth_var_)
160  vars += string(",d");
161  vars += string(",cross_section,const_scalar,scalar_field,unknown_scalar,integer_scalar"); // Temporary solution only for testing field dependency in BParser
162 
163  // update parsers
164  for(unsigned int row=0; row < this->value_.n_rows(); row++)
165  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
166  // TODO:
167  // - possibly add user defined constants and units here ...
168  // - optimization; possibly parse only if time_dependent || formula_matrix[][] has changed ...
169  //parser_matrix_[row][col] = tmp_parser;
170  if (time_dependent[row*this->value_.n_rows()+col] || first_time_set_ ) {
171  parser_matrix_[row][col].Parse(formula_matrix_.at(row,col), vars);
172 
173  if ( parser_matrix_[row][col].GetParseErrorType() != FunctionParser::FP_NO_ERROR ) {
174  THROW( ExcFParserError() << EI_FParserMsg(parser_matrix_[row][col].ErrorMsg()) << EI_Row(row)
175  << EI_Col(col) << EI_Formula(formula_matrix_.at(row,col)) );
176  }
177 
178  parser_matrix_[row][col].Optimize();
179  any_parser_changed = true;
180  }
181 
182 
183  }
184 
185  first_time_set_ = false;
186  this->time_=time;
187  return any_parser_changed;
188 }
189 
190 
191 template <int spacedim, class Value>
192 void FieldFormula<spacedim, Value>::set_mesh(const Mesh *mesh, FMT_UNUSED bool boundary_domain) {
193  // create SurfaceDepth object if surface region is set
194  std::string surface_region;
195  if ( in_rec_.opt_val("surface_region", surface_region) ) {
196  surface_depth_ = std::make_shared<SurfaceDepth>(mesh, surface_region, in_rec_.val<std::string>("surface_direction"));
197  }
198 }
199 
200 
201 /**
202  * Returns one value in one given point. ResultType can be used to avoid some costly calculation if the result is trivial.
203  */
204 template <int spacedim, class Value>
205 typename Value::return_type const & FieldFormula<spacedim, Value>::value(const Point &p, FMT_UNUSED const ElementAccessor<spacedim> &elm)
206 {
207 
208  auto p_depth = this->eval_depth_var(p);
209  for(unsigned int row=0; row < this->value_.n_rows(); row++)
210  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
211  this->value_(row,col) = this->unit_conversion_coefficient_ * parser_matrix_[row][col].Eval(p_depth.memptr());
212  }
213  return this->r_value_;
214 }
215 
216 
217 /**
218  * Returns std::vector of scalar values in several points at once.
219  */
220 template <int spacedim, class Value>
223 {
224  ASSERT_EQ( point_list.size(), value_list.size() );
225  ASSERT_DBG( point_list.n_rows() == spacedim && point_list.n_cols() == 1).error("Invalid point size.\n");
226  for(unsigned int i=0; i< point_list.size(); i++) {
227  Value envelope(value_list[i]);
228  ASSERT_EQ( envelope.n_rows(), this->value_.n_rows() )(i)(envelope.n_rows())(this->value_.n_rows())
229  .error("value_list['i'] has wrong number of rows\n");
230  auto p_depth = this->eval_depth_var(point_list.vec<spacedim>(i));
231 
232  for(unsigned int row=0; row < this->value_.n_rows(); row++)
233  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
234  envelope(row,col) = this->unit_conversion_coefficient_ * parser_matrix_[row][col].Eval(p_depth.memptr());
235  }
236  }
237 }
238 
239 
240 template <int spacedim, class Value>
242  ElementCacheMap &cache_map, unsigned int region_patch_idx)
243 {
244  unsigned int reg_chunk_begin = cache_map.region_chunk_begin(region_patch_idx);
245  unsigned int reg_chunk_end = cache_map.region_chunk_end(region_patch_idx);
246 
247  for (unsigned int i=reg_chunk_begin; i<reg_chunk_end; ++i) {
248  res_[i] = 0.0;
249  }
250  for (auto it : eval_field_data_) {
251  // Copy data from dependent fields to arena. Temporary solution.
252  // TODO hold field data caches in arena, remove this step
253  auto value_cache = it.first->value_cache();
254  for (unsigned int i=reg_chunk_begin; i<reg_chunk_end; ++i) {
255  if (it.first->name() == "X") {
256  x_[i] = value_cache->template vec<3>(i)(0);
257  y_[i] = value_cache->template vec<3>(i)(1);
258  z_[i] = value_cache->template vec<3>(i)(2);
259  } else
260  it.second[i] = value_cache->data_[i];
261  }
262  }
263 
264  // Get vector of subsets as subarray
265  uint subsets_begin = reg_chunk_begin / ElementCacheMap::simd_size_double;
266  uint subsets_end = reg_chunk_end / ElementCacheMap::simd_size_double;
267  std::vector<uint> subset_vec;
268  subset_vec.assign(subsets_ + subsets_begin, subsets_ + subsets_end);
269 
270  for(unsigned int row=0; row < this->value_.n_rows(); row++)
271  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
272  b_parser_[row*this->value_.n_cols()+col].set_subset(subset_vec);
273  b_parser_[row*this->value_.n_cols()+col].run();
274  for (unsigned int i=reg_chunk_begin; i<reg_chunk_end; ++i) {
275  auto cache_val = data_cache.template mat<Value::NRows_, Value::NCols_>(i);
276  cache_val(row, col) = res_[i];
277  data_cache.set(i) = cache_val;
278  }
279  }
280 }
281 
282 
283 template <int spacedim, class Value>
285 {
287  // add value of depth
288  arma::vec p_depth(spacedim+1);
289  p_depth.subvec(0,spacedim-1) = p;
290  try {
291  p_depth(spacedim) = surface_depth_->compute_distance(p);
292  } catch (SurfaceDepth::ExcTooLargeSnapDistance &e) {
293  e << SurfaceDepth::EI_FieldTime(this->time_.end());
294  e << in_rec_.ei_address();
295  throw;
296  }
297  return p_depth;
298  } else {
299  return p;
300  }
301 }
302 
304  uint r = 1;
305  for (auto i : shape) r *= i;
306  return r;
307 }
308 
309 
310 template <int spacedim, class Value>
312  required_fields_.clear(); // returned value
313 
314  std::vector<std::string> variables;
315  for(unsigned int row=0; row < this->value_.n_rows(); row++)
316  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
317  // set expression and data to BParser
318  unsigned int i_p = row*this->value_.n_cols()+col;
319  //b_parser_[i_p].parse(formula_matrix_.at(row,col));
320  std::string expr = formula_matrix_.at(row,col); // Need replace some operations to make them compatible with BParser.
321  // It will be solved by conversion script after remove fparser, but
322  // we mix using of BParser and fparser and need this solution now.
323  boost::replace_all(expr, "^", "**"); // power function
324  boost::replace_all(expr, "max(", "maximum("); // max function
325  boost::replace_all(expr, "min(", "minimum("); // min function
326  boost::replace_all(expr, "Pi", "pi"); // Math.pi
327  boost::replace_all(expr, "E", "e"); // Math.e
328  { // ternary operator
329  std::string pref("if(");
330  auto res = std::mismatch(pref.begin(), pref.end(), expr.begin());
331  if ( (res.first == pref.end()) && (expr.back() == ')') ) {
332  std::string subexpr = expr.substr(3, expr.size()-4);
333  std::string delimiter = ",";
334  std::string cond = subexpr.substr(0, subexpr.find(delimiter));
335  subexpr.erase(0, cond.size()+1);
336  std::string if_case = subexpr.substr(0, subexpr.find(delimiter));
337  std::string else_case = subexpr.substr(if_case.size()+1);
338  expr = "(" + if_case + " if " + cond + " else " + else_case +")";
339  }
340  }
341  try {
342  b_parser_[i_p].parse( expr );
343  } catch (std::exception const& e) {
344  if (typeid(e) == typeid(bparser::Exception)) THROW( ExcParserError() << EI_BParserMsg(e.what()) );
345  else throw;
346  }
347  variables.insert(variables.end(), b_parser_[i_p].variables().begin(), b_parser_[i_p].variables().end());
348  }
349 
350  std::sort( variables.begin(), variables.end() );
351  variables.erase( std::unique( variables.begin(), variables.end() ), variables.end() );
352  has_time_=false;
353  sum_shape_sizes_=0; // scecifies size of arena
354  for (auto var : variables) {
355  if (var == "x" || var == "y" || var == "z") {
356  required_fields_.push_back( field_set.field("X") );
357  sum_shape_sizes_ += spacedim;
358  }
359  else if (var == "t") has_time_ = true;
360  else {
361  auto field_ptr = field_set.field(var);
362  if (field_ptr != nullptr) required_fields_.push_back( field_ptr );
363  else THROW( ExcUnknownField() << EI_Field(var) );
364  // TODO: Test the exception, report input line of the formula.
365  if (field_ptr->value_cache() == nullptr) THROW( ExcNotDoubleField() << EI_Field(var) );
366  // TODO: Test the exception, report input line of the formula.
367 
368  sum_shape_sizes_ += n_shape( field_ptr->shape_ );
369  if (var == "d") {
370  field_set.set_surface_depth(this->surface_depth_);
371  }
372  }
373  }
374 
375  return required_fields_;
376 }
377 
378 
379 template <int spacedim, class Value>
381 {
382  if (arena_alloc_!=nullptr) {
383  delete arena_alloc_;
384  }
385  eval_field_data_.clear();
386  uint vec_size = 1.1 * CacheMapElementNumber::get();
387  while (vec_size%ElementCacheMap::simd_size_double > 0) vec_size++; // alignment of block size
388  // number of subset alignment to block size
390  uint n_vectors = sum_shape_sizes_ + 1; // needs add space of result vector
391  arena_alloc_ = new bparser::ArenaAlloc(ElementCacheMap::simd_size_double, n_vectors * vec_size * sizeof(double) + n_subsets * sizeof(uint));
392  res_ = arena_alloc_->create_array<double>(vec_size);
393  for (auto field : required_fields_) {
394  std::string field_name = field->name();
395  eval_field_data_[field] = arena_alloc_->create_array<double>(n_shape( field->shape_ ) * vec_size);
396  if (field_name == "X") {
397  x_ = eval_field_data_[field] + 0;
398  y_ = eval_field_data_[field] + vec_size;
399  z_ = eval_field_data_[field] + 2*vec_size;
400  }
401  }
402  subsets_ = arena_alloc_->create_array<uint>(n_subsets);
403 
404  for(unsigned int row=0; row < this->value_.n_rows(); row++)
405  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
406  // set expression and data to BParser
407  unsigned int i_p = row*this->value_.n_cols()+col;
408  if (has_time_) {
409  b_parser_[i_p].set_constant("t", {}, {this->time_.end()});
410  }
411  for (auto field : required_fields_) {
412  std::string field_name = field->name();
413  if (field_name == "X") {
414  b_parser_[i_p].set_variable("x", {}, x_);
415  b_parser_[i_p].set_variable("y", {}, y_);
416  b_parser_[i_p].set_variable("z", {}, z_);
417  } else
418  b_parser_[i_p].set_variable(field_name, {}, eval_field_data_[field]);
419  }
420  b_parser_[i_p].set_variable("_result_", {}, res_);
421  b_parser_[i_p].compile();
422  }
423  for (uint i=0; i<n_subsets; ++i)
424  subsets_[i] = i;
425 }
426 
427 
428 template <int spacedim, class Value>
430 
431 
432 // Instantiations of FieldFormula
unsigned int region_chunk_begin(unsigned int region_patch_idx) const
Return begin position of region chunk in FieldValueCache.
double * y_
Coordinates y, part of previous array.
bool has_time_
Flag indicates if time variable &#39;t&#39; is used in formula - parameter of BParser.
double * z_
Coordinates z, part of previous array.
TimeStep time_
Actual time level; initial value is -infinity.
void init_unit_conversion_coefficient(const Input::Record &rec, const struct FieldAlgoBaseInitData &init_data)
Init value of unit_conversion_coefficient_ from input.
Container for various descendants of FieldCommonBase.
Definition: field_set.hh:159
unsigned int size() const
Definition: armor.hh:728
Accessor to input data conforming to declared Array.
Definition: accessors.hh:566
ArmaVec< double, N > vec
Definition: armor.hh:885
virtual void value_list(const Armor::array &point_list, const ElementAccessor< spacedim > &elm, std::vector< typename Value::return_type > &value_list)
unsigned int uint
bparser::ArenaAlloc * arena_alloc_
Arena object providing data arrays.
static void init_from_input(StringTensor &tensor, AccessType input)
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
static unsigned int get()
Return number of stored elements.
Abstract linear system class.
Definition: balance.hh:40
virtual ~FieldFormula()
FieldFormula(unsigned int n_comp=0)
static Default obligatory()
The factory function to make an empty default value which is obligatory.
Definition: type_record.hh:110
#define INSTANCE_ALL(field)
Directing class of FieldValueCache.
Definition: mesh.h:77
std::shared_ptr< SurfaceDepth > surface_depth_
Surface depth object calculate distance from surface.
Helper struct stores data for initizalize descentants of FieldAlgorithmBase.
uint n_cols() const
Definition: armor.hh:720
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:347
bool first_time_set_
Flag indicates first call of set_time method, when FunctionParsers in parser_matrix_ must be initiali...
ArmaVec< Type, nr > vec(uint mat_index) const
Definition: armor.hh:821
StringTensor formula_matrix_
Value::return_type r_value_
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:304
bool set_time(const TimeStep &time) override
virtual void init_from_input(const Input::Record &rec, const struct FieldAlgoBaseInitData &init_data)
double unit_conversion_coefficient_
Coeficient of conversion of user-defined unit.
std::vector< const FieldCommon * > required_fields_
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:196
EI_Address ei_address() const
Definition: accessors.cc:178
static Default optional()
The factory function to make an empty default value which is optional.
Definition: type_record.hh:124
bool opt_val(const string &key, Ret &value) const
double * res_
Result vector of BParser.
FieldCommon * field(const std::string &field_name) const
Definition: field_set.cc:148
virtual Record & allow_auto_conversion(const string &from_key)
Allows shorter input of the Record providing only value of the from_key given as the parameter...
Definition: type_record.cc:133
bool has_depth_var_
Flag indicates if depth variable &#39;d&#39; is used in formula - obsolete parameter of FParser.
#define FMT_UNUSED
Definition: posix.h:75
void set_mesh(const Mesh *mesh, bool boundary_domain) override
uint n_shape(std::vector< uint > shape)
Accessor to the data with type Type::Record.
Definition: accessors.hh:291
uint * subsets_
Subsets indices in range 0 ... n-1.
const Ret val(const string &key) const
std::vector< const FieldCommon * > set_dependency(FieldSet &field_set) override
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:503
unsigned int region_chunk_end(unsigned int region_patch_idx) const
Return end position of region chunk in FieldValueCache.
ArrayMatSet set(uint index)
Definition: armor.hh:838
uint n_rows() const
Definition: armor.hh:715
double end() const
Space< spacedim >::Point Point
Record & copy_keys(const Record &other)
Copy keys from other record.
Definition: type_record.cc:216
std::string & at(unsigned int row)
bool is_constant_in_space_
Flag detects that field is only dependent on time.
Value value_
Last value, prevents passing large values (vectors) by value.
Input::Record in_rec_
Accessor to Input::Record.
virtual Value::return_type const & value(const Point &p, const ElementAccessor< spacedim > &elm)
void set_surface_depth(std::shared_ptr< SurfaceDepth > surface_depth)
Set surface depth object to "d" field.
Definition: field_set.hh:362
#define ASSERT_DBG(expr)
void cache_reinit(const ElementCacheMap &cache_map) override
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:270
static const unsigned int simd_size_double
std::vector< std::vector< FunctionParser > > parser_matrix_
Record type proxy class.
Definition: type_record.hh:182
double * x_
Coordinates x, part of previous array.
arma::vec eval_depth_var(const Point &p)
Class for declaration of the input data that are in string format.
Definition: type_base.hh:582
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
Representation of one time step..
std::vector< bparser::Parser > b_parser_
#define FLOW123D_FORCE_LINK_IN_CHILD(x)
Definition: global_defs.h:180
#define ASSERT_EQ(a, b)
Definition of comparative assert macro (EQual)
Definition: asserts.hh:328
uint sum_shape_sizes_
Helper variable for construct of arena, holds sum of sizes (over shape) of all dependent fields...
std::unordered_map< const FieldCommon *, double * > eval_field_data_
string address_string() const
Definition: accessors.cc:184
void cache_update(FieldValueCache< typename Value::element_type > &data_cache, ElementCacheMap &cache_map, unsigned int region_patch_idx) override