Flow123d  JS_before_hm-979-g397e552
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 #include "fields/field_formula.hh"
20 #include "fields/field_instances.hh" // for instantiation macros
21 #include "fields/surface_depth.hh"
22 #include "fparser.hh"
23 #include "input/input_type.hh"
24 
25 
26 /// Implementation.
27 
28 namespace it = Input::Type;
29 
30 FLOW123D_FORCE_LINK_IN_CHILD(field_formula)
31 
32 
33 template <int spacedim, class Value>
34 const Input::Type::Record & FieldFormula<spacedim, Value>::get_input_type()
35 {
36 
37  return it::Record("FieldFormula", FieldAlgorithmBase<spacedim,Value>::template_name()+" Field given by runtime interpreted formula.")
40  .declare_key("value", STI::get_input_type() , it::Default::obligatory(),
41  "String, array of strings, or matrix of strings with formulas for individual "
42  "entries of scalar, vector, or tensor value respectively.\n"
43  "For vector values, you can use just one string to enter homogeneous vector.\n"
44  "For square (($N\\times N$))-matrix values, you can use:\n\n"
45  " - array of strings of size (($N$)) to enter diagonal matrix\n"
46  " - array of strings of size (($\\frac12N(N+1)$)) to enter symmetric matrix (upper triangle, row by row)\n"
47  " - just one string to enter (spatially variable) multiple of the unit matrix.\n"
48  "Formula can contain variables ```x,y,z,t,d``` and usual operators and functions." )
49  //.declare_key("unit", FieldAlgorithmBase<spacedim, Value>::get_input_type_unit_si(), it::Default::optional(),
50  // "Definition of unit.")
51  .declare_key("surface_direction", it::String(), it::Default("\"0 0 1\""),
52  "The vector used to project evaluation point onto the surface.")
53  .declare_key("surface_region", it::String(), it::Default::optional(),
54  "The name of region set considered as the surface. You have to set surface region if you "
55  "want to use formula variable ```d```.")
56  .allow_auto_conversion("value")
57  .close();
58 }
59 
60 
61 
62 template <int spacedim, class Value>
64  Input::register_class< FieldFormula<spacedim, Value>, unsigned int >("FieldFormula") +
66 
67 
68 
69 template <int spacedim, class Value>
71 : FieldAlgorithmBase<spacedim, Value>(n_comp),
72  formula_matrix_(this->value_.n_rows(), this->value_.n_cols()),
73  first_time_set_(true)
74 {
75  this->is_constant_in_space_ = false;
76  parser_matrix_.resize(this->value_.n_rows());
77  for(unsigned int row=0; row < this->value_.n_rows(); row++) {
78  parser_matrix_[row].resize(this->value_.n_cols());
79  }
80 }
81 
82 
83 
84 template <int spacedim, class Value>
86  this->init_unit_conversion_coefficient(rec, init_data);
87 
88  // read formulas form input
89  STI::init_from_input( formula_matrix_, rec.val<typename STI::AccessType>("value") );
90  in_rec_ = rec;
91 }
92 
93 
94 template <int spacedim, class Value>
96 
97 
98  bool any_parser_changed = false;
99  std::string value_input_address = in_rec_.address_string();
100  has_depth_var_ = false;
101  this->is_constant_in_space_ = true; // set flag to true, then if found 'x', 'y', 'z' or 'd' reset to false
102 
103 
104  std::string vars = string("x,y,z").substr(0, 2*spacedim-1);
105  std::vector<bool> time_dependent(this->value_.n_rows() * this->value_.n_cols(), false);
106  // update parsers
107  for(unsigned int row=0; row < this->value_.n_rows(); row++)
108  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
109  // get all variable names from the formula
110  std::vector<std::string> var_list;
111 
112  FunctionParser tmp_parser;
113  tmp_parser.AddConstant("Pi", 3.14159265358979323846);
114  tmp_parser.AddConstant("E", 2.71828182845904523536);
115 
116 
117 #pragma GCC diagnostic push
118 #pragma GCC diagnostic ignored "-Wunused-variable"
119  {
120  int err=tmp_parser.ParseAndDeduceVariables(formula_matrix_.at(row,col), var_list);
121  ASSERT(err != FunctionParser::FP_NO_ERROR)(tmp_parser.ErrorMsg()).error("ParseAndDeduceVariables error\n");
122  }
123 #pragma GCC diagnostic pop
124 
125  for(std::string &var_name : var_list ) {
126  if (var_name == std::string("t") ) time_dependent[row*this->value_.n_rows()+col]=true;
127  else if (var_name == std::string("d") ) {
128  this->is_constant_in_space_ = false;
129  if (surface_depth_)
130  has_depth_var_=true;
131  else
132  WarningOut().fmt("Unset surface region. Variable '{}' in the FieldFormula[{}][{}] == '{}' will be set to zero\n at the input address:\n {} \n",
133  var_name, row, col, formula_matrix_.at(row,col), value_input_address );
134  }
135  else if (var_name == "x" || var_name == "y" || var_name == "z") {
136  this->is_constant_in_space_ = false;
137  continue;
138  }
139  else
140  WarningOut().fmt("Unknown variable '{}' in the FieldFormula[{}][{}] == '{}'\n at the input address:\n {} \n",
141  var_name, row, col, formula_matrix_.at(row,col), value_input_address );
142  }
143 
144  // Seems that we can not just add 't' constant to tmp_parser, since it was already Parsed.
145  parser_matrix_[row][col].AddConstant("Pi", 3.14159265358979323846);
146  parser_matrix_[row][col].AddConstant("E", 2.71828182845904523536);
147  if (time_dependent[row*this->value_.n_rows()+col]) {
148  parser_matrix_[row][col].AddConstant("t", time.end());
149  }
150  }
151 
152  if (has_depth_var_)
153  vars += string(",d");
154 
155  // update parsers
156  for(unsigned int row=0; row < this->value_.n_rows(); row++)
157  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
158  // TODO:
159  // - possibly add user defined constants and units here ...
160  // - optimization; possibly parse only if time_dependent || formula_matrix[][] has changed ...
161  //parser_matrix_[row][col] = tmp_parser;
162  if (time_dependent[row*this->value_.n_rows()+col] || first_time_set_ ) {
163  parser_matrix_[row][col].Parse(formula_matrix_.at(row,col), vars);
164 
165  if ( parser_matrix_[row][col].GetParseErrorType() != FunctionParser::FP_NO_ERROR ) {
166  xprintf(UsrErr, "ParserError: %s\n in the FieldFormula[%d][%d] == '%s'\n at the input address:\n %s \n",
167  parser_matrix_[row][col].ErrorMsg(),
168  row,col,formula_matrix_.at(row,col).c_str(),
169  value_input_address.c_str());
170  }
171 
172  parser_matrix_[row][col].Optimize();
173  any_parser_changed = true;
174  }
175 
176 
177  }
178 
179  first_time_set_ = false;
180  this->time_=time;
181  return any_parser_changed;
182 }
183 
184 
185 template <int spacedim, class Value>
186 void FieldFormula<spacedim, Value>::set_mesh(const Mesh *mesh, FMT_UNUSED bool boundary_domain) {
187  // create SurfaceDepth object if surface region is set
188  std::string surface_region;
189  if ( in_rec_.opt_val("surface_region", surface_region) ) {
190  surface_depth_ = std::make_shared<SurfaceDepth>(mesh, surface_region, in_rec_.val<std::string>("surface_direction"));
191  }
192 }
193 
194 
195 /**
196  * Returns one value in one given point. ResultType can be used to avoid some costly calculation if the result is trivial.
197  */
198 template <int spacedim, class Value>
199 typename Value::return_type const & FieldFormula<spacedim, Value>::value(const Point &p, FMT_UNUSED const ElementAccessor<spacedim> &elm)
200 {
201 
202  auto p_depth = this->eval_depth_var(p);
203  for(unsigned int row=0; row < this->value_.n_rows(); row++)
204  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
205  this->value_(row,col) = this->unit_conversion_coefficient_ * parser_matrix_[row][col].Eval(p_depth.memptr());
206  }
207  return this->r_value_;
208 }
209 
210 
211 /**
212  * Returns std::vector of scalar values in several points at once.
213  */
214 template <int spacedim, class Value>
217 {
218  ASSERT_EQ( point_list.size(), value_list.size() );
219  ASSERT_DBG( point_list.n_rows() == spacedim && point_list.n_cols() == 1).error("Invalid point size.\n");
220  for(unsigned int i=0; i< point_list.size(); i++) {
221  Value envelope(value_list[i]);
222  ASSERT_EQ( envelope.n_rows(), this->value_.n_rows() )(i)(envelope.n_rows())(this->value_.n_rows())
223  .error("value_list['i'] has wrong number of rows\n");
224  auto p_depth = this->eval_depth_var(point_list.vec<spacedim>(i));
225 
226  for(unsigned int row=0; row < this->value_.n_rows(); row++)
227  for(unsigned int col=0; col < this->value_.n_cols(); col++) {
228  envelope(row,col) = this->unit_conversion_coefficient_ * parser_matrix_[row][col].Eval(p_depth.memptr());
229  }
230  }
231 }
232 
233 
234 template <int spacedim, class Value>
236 {
238  // add value of depth
239  arma::vec p_depth(spacedim+1);
240  p_depth.subvec(0,spacedim-1) = p;
241  try {
242  p_depth(spacedim) = surface_depth_->compute_distance(p);
243  } catch (SurfaceDepth::ExcTooLargeSnapDistance &e) {
244  e << SurfaceDepth::EI_FieldTime(this->time_.end());
245  e << in_rec_.ei_address();
246  throw;
247  }
248  return p_depth;
249  } else {
250  return p;
251  }
252 }
253 
254 
255 template <int spacedim, class Value>
257 }
258 
259 
260 // Instantiations of FieldFormula
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.
unsigned int size() const
Definition: armor.hh:718
Accessor to input data conforming to declared Array.
Definition: accessors.hh:566
ArmaVec< double, N > vec
Definition: armor.hh:861
virtual void value_list(const Armor::array &point_list, const ElementAccessor< spacedim > &elm, std::vector< typename Value::return_type > &value_list)
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
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)
Definition: mesh.h:78
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:710
#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:807
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.
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
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.
#define FMT_UNUSED
Definition: posix.h:75
void set_mesh(const Mesh *mesh, bool boundary_domain) override
Accessor to the data with type Type::Record.
Definition: accessors.hh:291
const Ret val(const string &key) const
#define xprintf(...)
Definition: system.hh:93
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
uint n_rows() const
Definition: armor.hh:705
double end() const
Space< spacedim >::Point Point
Record & copy_keys(const Record &other)
Copy keys from other record.
Definition: type_record.cc:216
Definition: system.hh:65
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)
#define ASSERT_DBG(expr)
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:258
std::vector< std::vector< FunctionParser > > parser_matrix_
Record type proxy class.
Definition: type_record.hh:182
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
Representation of one time step..
#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
string address_string() const
Definition: accessors.cc:184