Flow123d  release_2.2.0-41-g0958a8d
observe.cc
Go to the documentation of this file.
1 /*
2  * observe.cc
3  *
4  * Created on: Jun 28, 2016
5  * Author: jb
6  */
7 
8 #include <string>
9 #include <numeric>
10 #include <cmath>
11 #include <algorithm>
12 #include <unordered_set>
13 
14 #include "system/global_defs.h"
15 #include "input/accessors.hh"
16 #include "input/input_type.hh"
18 
19 #include "mesh/mesh.h"
20 #include "mesh/bih_tree.hh"
21 #include "mesh/region.hh"
22 #include "io/observe.hh"
23 #include "io/element_data_cache.hh"
24 
25 
26 namespace IT = Input::Type;
27 
28 
30  return IT::Record("ObservePoint", "Specification of the observation point.\n"
31  "The actual observation element and the observation point on it is determined as follows:\n\n"
32  "1. Find an initial element containing the initial point. If no such element exists, we report an error.\n"
33  "2. Use BFS (Breadth-first search) starting from the inital element to find the 'observe element'. The observe element is the closest element.\n"
34  "3. Find the closest projection of the inital point on the observe element and snap this projection according to the ``snap_dim``.\n")
35  .allow_auto_conversion("point")
36  .declare_key("name", IT::String(),
38  "Default name have the form 'obs_<id>', where 'id' "
39  "is the rank of the point on the input."),
40  "Optional point name, which has to be unique.\n"
41  "Any string that is a valid YAML key in record without any quoting can be used, however,"
42  "using just alpha-numerical characters, and underscore instead of the space, is recommended."
43  )
45  "Initial point for the observe point search.")
46  .declare_key("snap_dim", IT::Integer(0, 4), IT::Default("4"),
47  "The dimension of the sub-element to which center we snap. For value 4 no snapping is done. "
48  "For values 0 up to 3 the element containing the initial point is found and then the observe"
49  "point is snapped to the nearest center of the sub-element of the given dimension. "
50  "E.g. for dimension 2 we snap to the nearest center of the face of the initial element."
51  )
52  .declare_key("snap_region", IT::String(), IT::Default("\"ALL\""),
53  "The region of the initial element for snapping. Without snapping we make a projection to the initial element.")
54  .declare_key("n_search_levels", IT::Integer(0), IT::Default("1"),
55  "Maximum number of levels of the breadth first search used to find the observe element from the initial element. Value zero means to search only the initial element itself.")
56  .close();
57 }
58 
60 {}
61 
62 
63 ObservePoint::ObservePoint(Input::Record in_rec, unsigned int point_idx)
64 : distance_(numeric_limits<double>::infinity())
65 {
66  in_rec_ = in_rec;
67 
68  string default_label = string("obs_") + std::to_string(point_idx);
69  name_ = in_rec.val<string>("name", default_label );
70 
71  vector<double> tmp_coords;
72  in_rec.val<Input::Array>("point").copy_to(tmp_coords);
73  input_point_= arma::vec(tmp_coords);
74 
75  snap_dim_ = in_rec.val<unsigned int>("snap_dim");
76 
77  snap_region_name_ = in_rec.val<string>("snap_region");
78 
79  max_levels_ = in_rec_.val<unsigned int>("n_search_levels");
80 }
81 
82 
83 
84 void ObservePoint::update_projection(unsigned int i_elm, arma::vec local_coords, arma::vec3 global_coords)
85 {
86  double dist = arma::norm(global_coords - input_point_, 2);
87  //cout << "dist: " << dist << endl;
88  if (dist < distance_) {
89  distance_ = dist;
90  element_idx_ = i_elm;
91  local_coords_ = local_coords;
93  }
94 }
95 
96 
97 
99  return distance_ < numeric_limits<double>::infinity();
100 }
101 
102 
103 
104 template <int ele_dim>
106 {
107  if (this->snap_dim_ > ele_dim) return;
108 
109  double min_dist = 2.0; // on the ref element the max distance should be about 1.0, smaler then 2.0
110  arma::vec min_center;
111  for(auto &center : RefElement<ele_dim>::centers_of_subelements(this->snap_dim_))
112  {
113  double dist = arma::norm(center-local_coords_, 2);
114  if ( dist < min_dist) {
115  min_dist = dist;
116  min_center = center;
117  }
118  }
119  this->local_coords_ = min_center;
120 }
121 
122 
124 {
125  Element & elm = mesh.element[element_idx_];
126  switch (elm.dim()) {
127  case 1: snap_to_subelement<1>(); break;
128  case 2: snap_to_subelement<2>(); break;
129  case 3: snap_to_subelement<3>(); break;
130  default: ASSERT(false).error("Clipping supported only for dim=1,2,3.");
131  }
132  this->global_coords_ = elm.element_map() * arma::join_cols(this->local_coords_, arma::ones(1));
133 }
134 
135 
136 
139  if (region_set.size() == 0)
140  THROW( RegionDB::ExcUnknownSet() << RegionDB::EI_Label(snap_region_name_) << in_rec_.ei_address() );
141 
142 
143  const BIHTree &bih_tree=mesh.get_bih_tree();
144  vector<unsigned int> candidate_list, process_list;
145  std::unordered_set<unsigned int> closed_elements(1023);
146 
147  // search for the initial element
148  auto projected_point = bih_tree.tree_box().project_point(input_point_);
149  bih_tree.find_point( projected_point, candidate_list );
150  process_list.swap(candidate_list);
151  candidate_list.clear();
152 
153  unsigned int min_dist_idx=0;
154  double min_dist=numeric_limits<double>::max();
155  for (unsigned int i_candidate=0; i_candidate<process_list.size(); ++i_candidate) {
156  unsigned int i_elm=process_list[i_candidate];
157  Element & elm = mesh.element[i_elm];
158  arma::mat map = elm.element_map();
159 
160  // get barycentric coordinates (1,2,0)
161  arma::vec projection = elm.project_point(input_point_, map);
162 
163  // check that point is on the element
164  if (projection.min() >= -BoundingBox::epsilon) {
165  // This is initial element.
166  //input_point_.print(cout, "input_point");
167  //cout << "i_el: " << i_elm << endl;
168  //projection.print(cout, "projection");
169 
170 
171  // if element match region filter store it as observe element to the obs. point
172  if (elm.region().is_in_region_set(region_set)) {
173  projection[elm.dim()] = 1.0; // use last coordinates for translation
174  arma::vec global_coord = map*projection;
175  update_projection(i_elm, projection.rows(0, elm.dim()-1), global_coord);
176  }
177 
178  closed_elements.insert(i_elm);
179  // add all node neighbours to the next level list
180  for (unsigned int n=0; n < elm.n_nodes(); n++) {
181  for(unsigned int i_node_ele : mesh.node_elements()[mesh.node_vector.index(elm.node[n])])
182  candidate_list.push_back(i_node_ele);
183  }
184  } else {
185  // Point out of the element. Keep the closest element.
186  double distance = fabs(projection.min());
187  if (distance < min_dist) {
188  min_dist=distance;
189  min_dist_idx = i_candidate;
190  }
191  //DebugOut() << print_var(i_candidate);
192  //DebugOut() << print_var(projection);
193  }
194  }
195 
196  if (candidate_list.size() == 0) {
197 
198  unsigned int i_elm=process_list[min_dist_idx];
199  Element & elm = mesh.element[i_elm];
200  // if element match region filter store it as observe element to the obs. point
201  if (elm.region().is_in_region_set(region_set)) {
202  arma::mat map = elm.element_map();
203  arma::vec projection = elm.project_point(input_point_, map);
204  projection = elm.clip_to_element(projection);
205 
206  projection[elm.dim()] = 1.0; // use last coordinates for translation
207  arma::vec global_coord = map*projection;
208  update_projection(i_elm, projection.rows(0, elm.dim()-1), global_coord);
209  }
210 
211  WarningOut().fmt("Failed to find the element containing the initial observe point ({}).\n"
212  "Using the closest element instead.\n", in_rec_.address_string());
213 
214  closed_elements.insert(i_elm);
215  // add all node neighbours to the next level list
216  for (unsigned int n=0; n < elm.n_nodes(); n++) {
217  for(unsigned int i_node_ele : mesh.node_elements()[mesh.node_vector.index(elm.node[n])])
218  candidate_list.push_back(i_node_ele);
219  }
220 
221  }
222 
223  // Try to snap to the observe element with required snap_region
224  for(unsigned int i_level=0; i_level < max_levels_; i_level++) {
225  if (have_observe_element()) break;
226  process_list.swap(candidate_list);
227  candidate_list.clear();
228  for(unsigned int i_elm : process_list) {
229  if (closed_elements.find(i_elm) != closed_elements.end()) continue;
230  Element & elm = mesh.element[i_elm];
231 
232  // if element match region filter, update the obs. point
233  if (elm.region().is_in_region_set(region_set)) {
234  arma::mat map = elm.element_map();
235  arma::vec projection = elm.project_point(input_point_, map);
236  arma::vec point_on_element = elm.clip_to_element(projection);
237 
238  point_on_element[elm.dim()] = 1.0; // use last coordinates for translation
239  arma::vec global_coord = map*point_on_element;
240  update_projection(i_elm, point_on_element.rows(0, elm.dim()-1), global_coord);
241  }
242  // add all node neighbours to the next level list
243  for (unsigned int n=0; n < elm.n_nodes(); n++) {
244  for(unsigned int i_node_ele : mesh.node_elements()[mesh.node_vector.index(elm.node[n])])
245  candidate_list.push_back(i_node_ele);
246  }
247  }
248  }
249  if (! have_observe_element()) {
250  THROW(ExcNoObserveElement() << EI_RegionName(snap_region_name_) << EI_NLevels(max_levels_) );
251  }
252  snap( mesh );
253  Element & elm = mesh.element[element_idx_];
254  double dist = arma::norm(elm.centre() - input_point_, 2);
255  double elm_norm = arma::norm(elm.bounding_box().max() - elm.bounding_box().min(), 2);
256  if (dist > 2*elm_norm)
257  WarningOut().fmt("Observe point ({}) is too distant from the mesh.\n", name_);
258 }
259 
260 
261 
262 void ObservePoint::output(ostream &out, unsigned int indent_spaces, unsigned int precision)
263 {
264  out << setw(indent_spaces) << "" << "- name: " << name_ << endl;
265  out << setw(indent_spaces) << "" << " init_point: " << field_value_to_yaml(input_point_, precision) << endl;
266  out << setw(indent_spaces) << "" << " snap_dim: " << snap_dim_ << endl;
267  out << setw(indent_spaces) << "" << " snap_region: " << snap_region_name_ << endl;
268  out << setw(indent_spaces) << "" << " observe_point: " << field_value_to_yaml(global_coords_, precision) << endl;
269 }
270 
271 
272 
273 
274 Observe::Observe(string observe_name, Mesh &mesh, Input::Array in_array, unsigned int precision)
275 : observe_values_time_(numeric_limits<double>::signaling_NaN()),
276  observe_name_(observe_name),
277  precision_(precision)
278 {
279  // in_rec is Output input record.
280 
281  for(auto it = in_array.begin<Input::Record>(); it != in_array.end(); ++it) {
282  ObservePoint point(*it, points_.size());
283  point.find_observe_point(mesh);
284  points_.push_back( point );
285  observed_element_indices_.push_back(point.element_idx_);
286  }
287  // make indices unique
288  std::sort(observed_element_indices_.begin(), observed_element_indices_.end());
289  auto last = std::unique(observed_element_indices_.begin(), observed_element_indices_.end());
291 
292  time_unit_str_ = "s";
293  time_unit_seconds_ = 1.0;
294 
295  if (points_.size() == 0) return;
297  if (rank_==0) {
298  FilePath observe_file_path(observe_name_ + "_observe.yaml", FilePath::output_file);
299  try {
300  observe_file_path.open_stream(observe_file_);
301  //observe_file_.setf(std::ios::scientific);
302  observe_file_.precision(this->precision_);
303 
304  } INPUT_CATCH(FilePath::ExcFileOpen, FilePath::EI_Address_String, in_array)
305  output_header();
306  }
307 }
308 
310  observe_file_.close();
311 }
312 
313 
314 template <typename T>
315 ElementDataCache<T> & Observe::prepare_compute_data(std::string field_name, double field_time, unsigned int n_rows,
316  unsigned int n_cols)
317 {
319  observe_values_time_ = field_time;
320  else
322  (field_time)(observe_values_time_);
323 
324  OutputDataFieldMap::iterator it=observe_field_values_.find(field_name);
325  if (it == observe_field_values_.end()) {
326  observe_field_values_[field_name]
327  = std::make_shared< ElementDataCache<T> >(field_name, n_rows, n_cols, points_.size());
328  it=observe_field_values_.find(field_name);
329  }
330  return dynamic_cast<ElementDataCache<T> &>(*(it->second));
331 }
332 
333 // explicit instantiation of template method
334 #define OBSERVE_PREPARE_COMPUTE_DATA(TYPE) \
335 template ElementDataCache<TYPE> & Observe::prepare_compute_data<TYPE>(std::string field_name, double field_time, \
336  unsigned int n_rows, unsigned int n_cols)
337 
339 OBSERVE_PREPARE_COMPUTE_DATA(unsigned int);
341 
342 
344  unsigned int indent = 2;
345  observe_file_ << "# Observation file: " << observe_name_ << endl;
346  observe_file_ << "time_unit: " << time_unit_str_ << endl;
347  observe_file_ << "time_unit_in_seconds: " << time_unit_seconds_ << endl;
348  observe_file_ << "points:" << endl;
349  for(auto &point : points_)
350  point.output(observe_file_, indent, precision_);
351  observe_file_ << "data:" << endl;
352 
353 }
354 
355 void Observe::output_time_frame(double time) {
356  if (points_.size() == 0) return;
357 
358  if ( ! no_fields_warning ) {
359  no_fields_warning=true;
360  // check that observe fields are set
362  // first call and no fields
363  ASSERT(observe_field_values_.size() == 0);
364  WarningOut() << "No observe fields for the observation stream: " << observe_name_ << endl;
365  }
366  }
367 
369  ASSERT(observe_field_values_.size() == 0);
370  return;
371  }
372 
373  if (rank_ == 0) {
374  unsigned int indent = 2;
375  observe_file_ << setw(indent) << "" << "- time: " << observe_values_time_ << endl;
376  for(auto &field_data : observe_field_values_) {
377  observe_file_ << setw(indent) << "" << " " << field_data.second->field_input_name() << ": ";
378  field_data.second->print_all_yaml(observe_file_, precision_);
379  observe_file_ << endl;
380  }
381  }
382 
383  observe_values_time_ = numeric_limits<double>::signaling_NaN();
384 
385 }
std::vector< ObservePoint > points_
Full information about observe points.
Definition: observe.hh:219
unsigned int max_levels_
Definition: observe.hh:128
arma::vec project_point(const arma::vec3 &point, const arma::mat &map) const
Definition: elements.cc:205
Iterator< ValueType > begin() const
#define OBSERVE_PREPARE_COMPUTE_DATA(TYPE)
Definition: observe.cc:334
OutputDataFieldMap observe_field_values_
Stored field values.
Definition: observe.hh:224
unsigned int n_nodes() const
vector< vector< unsigned int > > const & node_elements()
Definition: mesh.cc:787
std::ofstream observe_file_
Output file stream.
Definition: observe.hh:234
Accessor to input data conforming to declared Array.
Definition: accessors.hh:567
string field_value_to_yaml(const T &mat, unsigned int prec)
arma::vec3 input_point_
Input coordinates of the initial position of the observation point.
Definition: observe.hh:112
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
arma::mat element_map() const
Definition: elements.h:112
double distance_
Definition: observe.hh:141
static const Input::Type::Record & get_input_type()
Definition: observe.cc:29
#define INPUT_CATCH(ExceptionType, AddressEITag, input_accessor)
Definition: accessors.hh:64
bool have_observe_element()
Definition: observe.cc:98
static Default obligatory()
The factory function to make an empty default value which is obligatory.
Definition: type_record.hh:110
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:328
Definition: mesh.h:97
void output(ostream &out, unsigned int indent_spaces, unsigned int precision)
Definition: observe.cc:262
Observe(string observe_name, Mesh &mesh, Input::Array in_array, unsigned int precision)
Definition: observe.cc:274
static const double epsilon
stabilization parameter
Definition: bounding_box.hh:57
Node ** node
Definition: elements.h:79
std::string time_unit_str_
String representation of the time unit.
Definition: observe.hh:237
int rank_
Definition: observe.hh:216
const RegionDB & region_db() const
Definition: mesh.h:155
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:346
std::string to_string(const T &value)
Definition: string.h:29
std::string observe_name_
Definition: observe.hh:231
Class for declaration of the integral input data.
Definition: type_base.hh:489
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:303
Class for declaration of inputs sequences.
Definition: type_base.hh:345
IteratorBase end() const
arma::vec clip_to_element(arma::vec &barycentric)
Definition: elements.h:144
unsigned int dim() const
Input::Record in_rec_
Index in the input array.
Definition: observe.hh:106
EI_Address ei_address() const
Definition: accessors.cc:178
void open_stream(Stream &stream) const
Definition: file_path.cc:211
Global macros to enhance readability and debugging, general constants.
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:132
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:540
unsigned int precision_
Precision of float output.
Definition: observe.hh:241
void find_observe_point(Mesh &mesh)
Definition: observe.cc:137
ObservePoint()
Definition: observe.cc:59
DummyInt isnan(...)
Definition: format.h:306
void snap_to_subelement()
Definition: observe.cc:105
std::string name_
Observation point name.
Definition: observe.hh:109
Region region() const
Definition: elements.cc:155
const BoundingBox & tree_box() const
Definition: bih_tree.cc:188
Accessor to the data with type Type::Record.
Definition: accessors.hh:292
const Ret val(const string &key) const
void output_header()
Definition: observe.cc:343
std::vector< unsigned int > observed_element_indices_
Elements of the o_points.
Definition: observe.hh:221
const Point & max() const
Class for O(log N) lookup for intersections with a set of bounding boxes.
Definition: bih_tree.hh:36
unsigned int index(const T *pointer) const
Definition: sys_vector.hh:373
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:490
ElementDataCache< T > & prepare_compute_data(std::string field_name, double field_time, unsigned int n_rows, unsigned int n_cols)
Definition: observe.cc:315
double observe_values_time_
Common evaluation time of the fields for single time frame.
Definition: observe.hh:228
void find_point(const Space< 3 >::Point &point, std::vector< unsigned int > &result_list) const
Definition: bih_tree.cc:240
unsigned int snap_dim_
Definition: observe.hh:118
const BIHTree & get_bih_tree()
Definition: mesh.cc:735
#define MPI_Comm_rank
Definition: mpi.h:236
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
static Default read_time(const std::string &description)
The factory function to make an default value that will be specified at the time when a key will be r...
Definition: type_record.hh:97
const double epsilon
Definition: mathfce.h:23
arma::vec3 global_coords_
Global coordinates of the observation point.
Definition: observe.hh:134
arma::vec local_coords_
Local (barycentric) coordinates on the element.
Definition: observe.hh:137
void update_projection(unsigned int i_elm, arma::vec local_coords, arma::vec3 global_coords)
Definition: observe.cc:84
arma::vec3 centre() const
Definition: elements.cc:120
double time_unit_seconds_
Time unit in seconds.
Definition: observe.hh:239
bool is_in_region_set(const RegionSet &set) const
Definition: region.cc:46
Point project_point(const Point &point) const
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:236
#define MPI_COMM_WORLD
Definition: mpi.h:123
const Point & min() const
string snap_region_name_
Definition: observe.hh:123
Record type proxy class.
Definition: type_record.hh:182
unsigned int element_idx_
Final element of the observe point. The index in the mesh.
Definition: observe.hh:131
arma::vec3 global_coords() const
Definition: observe.hh:48
void snap(Mesh &mesh)
Definition: observe.cc:123
bool no_fields_warning
Definition: observe.hh:244
Class for declaration of the input data that are in string format.
Definition: type_base.hh:588
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
~Observe()
Destructor, must close the file.
Definition: observe.cc:309
NodeVector node_vector
Vector of nodes of the mesh.
Definition: mesh.h:226
void output_time_frame(double time)
Definition: observe.cc:355
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:228
string address_string() const
Definition: accessors.cc:184
BoundingBox bounding_box()
Definition: elements.h:102