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