Flow123d  intersections_paper-476-gbe68821
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 #include <queue>
14 
15 #include "system/global_defs.h"
16 #include "input/accessors.hh"
17 #include "input/input_type.hh"
19 
20 #include "mesh/mesh.h"
21 #include "mesh/bih_tree.hh"
22 #include "mesh/region.hh"
23 #include "io/observe.hh"
24 #include "io/output_data.hh"
25 #include "fem/mapping_p1.hh"
26 
27 
28 namespace IT = Input::Type;
29 
30 
31 /**
32  * Helper class allows to work with ObservePoint above elements with different dimensions
33  *
34  * Allows:
35  * - calculate projection of points by dimension
36  * - snap to subelement
37  */
38 template<unsigned int dim>
40 public:
41  /// Constructor
43 
44  ObservePointData projection(arma::vec3 input_point, unsigned int i_elm, Element &elm) {
45  arma::mat::fixed<3, dim+1> elm_map = mapping_.element_map(elm);
46  arma::vec::fixed<dim+1> projection = mapping_.project_point(input_point, elm_map);
47  projection = mapping_.clip_to_element(projection);
48  projection[0] = 1.0; // use first coordinates for translation
49 
50  ObservePointData data;
51  data.element_idx_ = i_elm;
52  data.local_coords_ = projection.rows(1, elm.dim());
53  data.global_coords_ = elm_map*projection;
54  data.distance_ = arma::norm(data.global_coords_ - input_point, 2);
55 
56  return data;
57  }
58 
59  /**
60  * Snap local coords to the subelement. Called by the ObservePoint::snap method.
61  */
62  void snap_to_subelement(ObservePointData & observe_data, Element & elm, unsigned int snap_dim)
63  {
64  if (snap_dim <= dim) {
65  double min_dist = 2.0; // on the ref element the max distance should be about 1.0, smaler then 2.0
66  arma::vec min_center;
67  for(auto &center : RefElement<dim>::centers_of_subelements(snap_dim))
68  {
69  double dist = arma::norm(center - observe_data.local_coords_, 2);
70  if ( dist < min_dist) {
71  min_dist = dist;
72  min_center = center;
73  }
74  }
75  observe_data.local_coords_ = min_center;
76  }
77 
78  arma::mat::fixed<3, dim+1> elm_map = mapping_.element_map(elm);
79  observe_data.global_coords_ = elm_map * arma::join_cols(arma::ones(1), observe_data.local_coords_);
80  }
81 
82 private:
83  /// Mapping object.
85 };
86 
87 template class ProjectionHandler<1>;
88 template class ProjectionHandler<2>;
89 template class ProjectionHandler<3>;
90 
91 
92 /**
93  * Helper struct, used as comparator of priority queue in ObservePoint::find_observe_point.
94  */
96 {
97  bool operator()(const ObservePointData& lhs, const ObservePointData& rhs) const
98  {
99  return lhs.distance_ > rhs.distance_;
100  }
101 };
102 
103 
104 /*******************************************************************
105  * implementation of ObservePoint
106  */
107 
109  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"
110  "1. Find an initial element containing the initial point. If no such element exists we report the error.\n"
111  "2. Use BFS starting from the inital element to find the 'observe element'. The observe element is the closest element "
112  "3. Find the closest projection of the inital point on the observe element and snap this projection according to the 'snap_dim'.\n")
113  .allow_auto_conversion("point")
114  .declare_key("name", IT::String(),
116  "Default name have the form 'obs_<id>', where 'id' "
117  "is the rank of the point on the input."),
118  "Optional point name. Has to be unique. Any string that is valid YAML key in record without any quoting can be used however"
119  "using just alpha-numerical characters and underscore instead of the space is recommended. "
120  )
122  "Initial point for the observe point search.")
123  .declare_key("snap_dim", IT::Integer(0, 4), IT::Default("4"),
124  "The dimension of the sub-element to which center we snap. For value 4 no snapping is done. "
125  "For values 0 up to 3 the element containing the initial point is found and then the observe"
126  "point is snapped to the nearest center of the sub-element of the given dimension. "
127  "E.g. for dimension 2 we snap to the nearest center of the face of the initial element."
128  )
129  .declare_key("snap_region", IT::String(), IT::Default("\"ALL\""),
130  "The region of the initial element for snapping. Without snapping we make a projection to the initial element.")
131  .declare_key("search_radius", IT::Double(0.0),
132  IT::Default::read_time("Maximal distance of observe point from Mesh relative to its size (bounding box). "),
133  "Global value is define in Mesh by the key global_observe_search_radius.")
134  .close();
135 }
136 
138 {}
139 
140 
141 ObservePoint::ObservePoint(Input::Record in_rec, Mesh &mesh, unsigned int point_idx)
142 {
143  in_rec_ = in_rec;
144 
145  string default_label = string("obs_") + std::to_string(point_idx);
146  name_ = in_rec.val<string>("name", default_label );
147 
148  vector<double> tmp_coords;
149  in_rec.val<Input::Array>("point").copy_to(tmp_coords);
150  input_point_= arma::vec(tmp_coords);
151 
152  snap_dim_ = in_rec.val<unsigned int>("snap_dim");
153 
154  snap_region_name_ = in_rec.val<string>("snap_region");
155 
156  const BoundingBox &main_box = mesh.get_bih_tree().tree_box();
157  double max_mesh_size = arma::max(main_box.max() - main_box.min());
158  max_search_radius_ = in_rec_.val<double>("search_radius", mesh.global_observe_radius()) * max_mesh_size;
159 }
160 
161 
162 
164  return observe_data_.distance_ < numeric_limits<double>::infinity();
165 }
166 
167 
168 
170 {
171  Element & elm = mesh.element[observe_data_.element_idx_];
172  switch (elm.dim()) {
173  case 1:
174  {
176  ph.snap_to_subelement(observe_data_, elm, snap_dim_);
177  break;
178  }
179  case 2:
180  {
182  ph.snap_to_subelement(observe_data_, elm, snap_dim_);
183  break;
184  }
185  case 3:
186  {
188  ph.snap_to_subelement(observe_data_, elm, snap_dim_);
189  break;
190  }
191  default: ASSERT(false).error("Clipping supported only for dim=1,2,3.");
192  }
193 }
194 
195 
196 
198  RegionSet region_set = mesh.region_db().get_region_set(snap_region_name_);
199  if (region_set.size() == 0)
200  THROW( RegionDB::ExcUnknownSet() << RegionDB::EI_Label(snap_region_name_) << in_rec_.ei_address() );
201 
202 
203  const BIHTree &bih_tree=mesh.get_bih_tree();
204  vector<unsigned int> candidate_list;
205  std::unordered_set<unsigned int> closed_elements(1023);
206  std::priority_queue< ObservePointData, std::vector<ObservePointData>, CompareByDist > candidate_queue;
207 
208  // search for the initial element
209  bih_tree.find_point(input_point_, candidate_list, true);
210 
211  for (unsigned int i_candidate=0; i_candidate<candidate_list.size(); ++i_candidate) {
212  unsigned int i_elm=candidate_list[i_candidate];
213  Element & elm = mesh.element[i_elm];
214 
215  // project point, add candidate to queue
216  auto observe_data = point_projection(i_elm, elm);
217  if (observe_data.distance_ <= max_search_radius_)
218  candidate_queue.push(observe_data);
219  closed_elements.insert(i_elm);
220  }
221 
222  while (!candidate_queue.empty())
223  {
224  auto candidate_data = candidate_queue.top();
225  candidate_queue.pop();
226 
227  unsigned int i_elm=candidate_data.element_idx_;
228  Element & elm = mesh.element[i_elm];
229 
230  // test if candidate is in region and update projection
231  if (elm.region().is_in_region_set(region_set)) {
232  ASSERT_LE(candidate_data.distance_, observe_data_.distance_).error();
233 
234  observe_data_.distance_ = candidate_data.distance_;
235  observe_data_.element_idx_ = candidate_data.element_idx_;
236  observe_data_.local_coords_ = candidate_data.local_coords_;
237  observe_data_.global_coords_ = candidate_data.global_coords_;
238  break;
239  }
240 
241  // add candidates to queue
242  for (unsigned int n=0; n < elm.n_nodes(); n++)
243  for(unsigned int i_node_ele : mesh.node_elements[mesh.node_vector.index(elm.node[n])]) {
244  if (closed_elements.find(i_node_ele) == closed_elements.end()) {
245  Element & neighbor_elm = mesh.element[i_node_ele];
246  auto observe_data = point_projection(i_node_ele, neighbor_elm);
247  if (observe_data.distance_ <= max_search_radius_)
248  candidate_queue.push(observe_data);
249  closed_elements.insert(i_node_ele);
250  }
251  }
252  }
253 
254  if (! have_observe_element()) {
255  THROW(ExcNoObserveElement() << EI_RegionName(snap_region_name_) );
256  }
257  snap( mesh );
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_) << 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(observe_data_.global_coords_, precision) << endl;
269 }
270 
271 
272 
274  switch (elm.dim()) {
275  case 1:
276  {
278  return ph.projection(input_point_, i_elm, elm);
279  break;
280  }
281  case 2:
282  {
284  return ph.projection(input_point_, i_elm, elm);
285  break;
286  }
287  case 3:
288  {
290  return ph.projection(input_point_, i_elm, elm);
291  break;
292  }
293  default:
294  ASSERT(false).error("Invalid element dimension!");
295  }
296 
297  return ObservePointData(); // Should not happen.
298 }
299 
300 
301 
302 
303 /*******************************************************************
304  * implementation of Observe
305  */
306 
307 Observe::Observe(string observe_name, Mesh &mesh, Input::Array in_array, unsigned int precision)
308 : mesh_(&mesh),
309  observe_values_time_(numeric_limits<double>::signaling_NaN()),
310  observe_name_(observe_name),
311  precision_(precision)
312 {
313  // in_rec is Output input record.
314 
315  for(auto it = in_array.begin<Input::Record>(); it != in_array.end(); ++it) {
316  ObservePoint point(*it, *mesh_, points_.size());
317  point.find_observe_point(*mesh_);
318  points_.push_back( point );
319  observed_element_indices_.push_back(point.observe_data_.element_idx_);
320  }
321  // make indices unique
322  std::sort(observed_element_indices_.begin(), observed_element_indices_.end());
323  auto last = std::unique(observed_element_indices_.begin(), observed_element_indices_.end());
325 
326  time_unit_str_ = "s";
327  time_unit_seconds_ = 1.0;
328 
329  if (points_.size() == 0) return;
331  if (rank_==0) {
332  FilePath observe_file_path(observe_name_ + "_observe.yaml", FilePath::output_file);
333  try {
334  observe_file_path.open_stream(observe_file_);
335  } INPUT_CATCH(FilePath::ExcFileOpen, FilePath::EI_Address_String, in_array)
336  output_header();
337  }
338 }
339 
341  observe_file_.close();
342 }
343 
344 
345 template<int spacedim, class Value>
347 {
348  if (points_.size() == 0) return;
349 
350  // check that all fields of one time frame are evaluated at the same time
351  double field_time = field.time();
353  observe_values_time_ = field_time;
354  else
356  (field_time)(observe_values_time_);
357 
358  OutputDataFieldMap::iterator it=observe_field_values_.find(field.name());
359  if (it == observe_field_values_.end()) {
360  observe_field_values_[field.name()] = std::make_shared< OutputData<Value> >(field, points_.size());
361  it=observe_field_values_.find(field.name());
362  }
363  OutputData<Value> &output_data = dynamic_cast<OutputData<Value> &>(*(it->second));
364 
365  unsigned int i_data=0;
366  for(ObservePoint &o_point : points_) {
367  unsigned int ele_index = o_point.observe_data_.element_idx_;
368  const Value &obs_value =
369  Value( const_cast<typename Value::return_type &>(
370  field.value(o_point.observe_data_.global_coords_,
371  ElementAccessor<spacedim>(this->mesh_, ele_index,false)) ));
372  output_data.store_value(i_data, obs_value);
373  i_data++;
374  }
375 
376 }
377 
378 // Instantiation of the method template for particular dimension.
379 #define INSTANCE_DIM(dim) \
380 template void Observe::compute_field_values(Field<dim, FieldValue<0>::Enum> &); \
381 template void Observe::compute_field_values(Field<dim, FieldValue<0>::Integer> &); \
382 template void Observe::compute_field_values(Field<dim, FieldValue<0>::Scalar> &); \
383 template void Observe::compute_field_values(Field<dim, FieldValue<dim>::VectorFixed> &); \
384 template void Observe::compute_field_values(Field<dim, FieldValue<dim>::TensorFixed> &);
385 
386 // Make all instances for both dimensions.
387 INSTANCE_DIM(2)
388 INSTANCE_DIM(3)
389 
390 
392  unsigned int indent = 2;
393  observe_file_ << "# Observation file: " << observe_name_ << endl;
394  observe_file_ << "time_unit: " << time_unit_str_ << endl;
395  observe_file_ << "time_unit_in_secodns: " << time_unit_seconds_ << endl;
396  observe_file_ << "points:" << endl;
397  for(auto &point : points_)
398  point.output(observe_file_, indent, precision_);
399  observe_file_ << "data:" << endl;
400 
401 }
402 
403 void Observe::output_time_frame(double time) {
404  if (points_.size() == 0) return;
405 
406  if ( ! no_fields_warning ) {
407  no_fields_warning=true;
408  // check that observe fields are set
410  // first call and no fields
411  ASSERT(observe_field_values_.size() == 0);
412  WarningOut() << "No observe fields for the observation stream: " << observe_name_ << endl;
413  }
414  }
415 
417  ASSERT(observe_field_values_.size() == 0);
418  return;
419  }
420 
421  if (rank_ == 0) {
422  unsigned int indent = 2;
423  observe_file_ << setw(indent) << "" << "- time: " << observe_values_time_ << endl;
424  for(auto &field_data : observe_field_values_) {
425  observe_file_ << setw(indent) << "" << " " << field_data.second->field_name << ": ";
426  field_data.second->print_all_yaml(observe_file_, precision_);
427  observe_file_ << endl;
428  }
429  }
430 
431  observe_values_time_ = numeric_limits<double>::signaling_NaN();
432 
433 }
std::vector< ObservePoint > points_
Full information about observe points.
Definition: observe.hh:199
Class MappingP1 implements the affine transformation of the unit cell onto the actual cell...
Iterator< ValueType > begin() const
Bounding box in 3d ambient space.
Definition: bounding_box.hh:45
OutputDataFieldMap observe_field_values_
Stored field values.
Definition: observe.hh:207
unsigned int n_nodes() const
std::ofstream observe_file_
Output file stream.
Definition: observe.hh:217
vector< vector< unsigned int > > node_elements
Definition: mesh.h:306
Accessor to input data conforming to declared Array.
Definition: accessors.hh:561
string field_value_to_yaml(const T &mat, unsigned int prec)
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:56
ProjectionHandler()
Constructor.
Definition: observe.cc:42
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:108
#define INPUT_CATCH(ExceptionType, AddressEITag, input_accessor)
Definition: accessors.hh:57
bool have_observe_element()
Definition: observe.cc:163
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:262
Observe(string observe_name, Mesh &mesh, Input::Array in_array, unsigned int precision)
Definition: observe.cc:307
#define ASSERT_LE(a, b)
Definition of comparative assert macro (Less or Equal)
Definition: asserts.hh:304
void snap_to_subelement(ObservePointData &observe_data, Element &elm, unsigned int snap_dim)
Definition: observe.cc:62
Node ** node
Definition: elements.h:90
unsigned int element_idx_
Final element of the observe point. The index in the mesh.
Definition: observe.hh:30
std::string time_unit_str_
String representation of the time unit.
Definition: observe.hh:220
int rank_
Definition: observe.hh:193
const RegionDB & region_db() const
Definition: mesh.h:160
#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:196
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:214
Class for declaration of the integral input data.
Definition: type_base.hh:483
ObservePointData point_projection(unsigned int i_elm, Element &elm)
Project point to given element by dimension of this element.
Definition: observe.cc:273
arma::vec local_coords_
Local (barycentric) coordinates on the element.
Definition: observe.hh:39
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
unsigned int dim() const
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
arma::vec3 global_coords_
Global coordinates of the observation point.
Definition: observe.hh:36
unsigned int precision_
Precision of float output.
Definition: observe.hh:224
void find_observe_point(Mesh &mesh)
Definition: observe.cc:197
DummyInt isnan(...)
Definition: format.h:306
ObservePointData projection(arma::vec3 input_point, unsigned int i_elm, Element &elm)
Definition: observe.cc:44
Region region() const
Definition: elements.cc:164
bool operator()(const ObservePointData &lhs, const ObservePointData &rhs) const
Definition: observe.cc:97
const BoundingBox & tree_box() const
Definition: bih_tree.cc:210
Accessor to the data with type Type::Record.
Definition: accessors.hh:286
const Ret val(const string &key) const
arma::vec::fixed< dim+1 > clip_to_element(arma::vec::fixed< dim+1 > &barycentric)
Definition: mapping_p1.cc:294
void output_header()
Definition: observe.cc:391
std::vector< unsigned int > observed_element_indices_
Elements of the o_points.
Definition: observe.hh:201
double distance_
Definition: observe.hh:43
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:211
const BIHTree & get_bih_tree()
Getter for BIH. Creates and compute BIH at first call.
Definition: mesh.cc:793
#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:346
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::mat::fixed< 3, dim+1 > element_map(Element &elm) const
Definition: mapping_p1.hh:113
void find_point(const Space< 3 >::Point &point, std::vector< unsigned int > &result_list, bool full_list=false) const
Definition: bih_tree.cc:265
double time_unit_seconds_
Time unit in seconds.
Definition: observe.hh:222
bool is_in_region_set(const RegionSet &set) const
Definition: region.cc:46
MappingP1< dim, 3 > mapping_
Mapping object.
Definition: observe.cc:84
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:244
FieldCommon & name(const string &name)
Definition: field_common.hh:97
#define MPI_COMM_WORLD
Definition: mpi.h:123
Record type proxy class.
Definition: type_record.hh:177
void snap(Mesh &mesh)
Definition: observe.cc:169
bool no_fields_warning
Definition: observe.hh:227
#define INSTANCE_DIM(dim)
Definition: observe.cc:379
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:340
arma::vec::fixed< dim+1 > project_point(const arma::vec3 &point, const arma::mat::fixed< 3, dim+1 > &map) const
Definition: mapping_p1.cc:281
NodeVector node_vector
Vector of nodes of the mesh.
Definition: mesh.h:233
double global_observe_radius() const
Maximal distance of observe point from Mesh relative to its size.
Definition: mesh.cc:799
void store_value(unsigned int idx, const Value &value)
Definition: output_data.cc:149
void output_time_frame(double time)
Definition: observe.cc:403
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:235