adamantine
material_deposition.cc
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: Copyright (c) 2021 - 2026, the adamantine authors.
2  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3  */
4 
5 #include <material_deposition.hh>
6 #include <utils.hh>
7 
8 #include <deal.II/arborx/bvh.h>
9 #include <deal.II/grid/filtered_iterator.h>
10 
11 #include <boost/algorithm/string.hpp>
12 
13 #include <algorithm>
14 #include <fstream>
15 #include <tuple>
16 
17 namespace adamantine
18 {
19 template <int dim>
20 std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
21  std::vector<double>, std::vector<double>>
23  boost::property_tree::ptree const &geometry_database,
24  std::vector<std::shared_ptr<HeatSource<dim>>> &heat_sources)
25 {
26  // PropertyTreeInput geometry.material_deposition
27  bool material_deposition =
28  geometry_database.get("material_deposition", false);
29 
30  if (!material_deposition)
31  return {{}, {}, {}, {}};
32 
33  std::string method =
34  geometry_database.get<std::string>("material_deposition_method");
35 
36  if (method == "file")
37  {
38  return read_material_deposition<dim>(geometry_database);
39  }
40  else
41  {
42  std::vector<
43  std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
44  std::vector<double>, std::vector<double>>>
45  deposition_paths;
46  for (auto const &source : heat_sources)
47  {
48  deposition_paths.emplace_back(deposition_along_scan_path<dim>(
49  geometry_database, source->get_scan_path()));
50  }
51 
52  return merge_deposition_paths<dim>(deposition_paths);
53  }
54 }
55 
56 template <int dim>
57 std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
58  std::vector<double>, std::vector<double>>
59 read_material_deposition(boost::property_tree::ptree const &geometry_database)
60 {
61  // PropertyTreeInput geometry.material_deposition_file
62  std::string material_deposition_filename =
63  geometry_database.get<std::string>("material_deposition_file");
64 
65  std::vector<dealii::BoundingBox<dim>> material_deposition_boxes;
66  std::vector<double> material_deposition_times;
67  std::vector<double> material_deposition_cos;
68  std::vector<double> material_deposition_sin;
69 
70  // Read file
71  wait_for_file(material_deposition_filename,
72  "Waiting for material deposition file: " +
73  material_deposition_filename);
74  std::ifstream file;
75  file.open(material_deposition_filename);
76  std::string line;
77  getline(file, line);
78  int dim_ = std::stoi(line);
79  ASSERT_THROW(dim_ == dim, "Dimension in " + material_deposition_filename +
80  " does not match " + std::to_string(dim) +
81  " .");
82  while (getline(file, line))
83  {
84  std::vector<std::string> split_line;
85  boost::split(split_line, line, boost::is_any_of(" "),
86  boost::token_compress_on);
87  // First, read the center of the box
88  std::vector<double> center(dim);
89  for (int d = 0; d < dim; ++d)
90  {
91  center[d] = std::stod(split_line[d]);
92  }
93  // Next, read the size of the box
94  std::vector<double> box_size(dim);
95  for (int d = 0; d < dim; ++d)
96  {
97  box_size[d] = std::stod(split_line[dim + d]);
98  }
99  // Read the time of deposition
100  material_deposition_times.push_back(std::stod(split_line[2 * dim]));
101  // Check that the time is increasing
102  unsigned int times_size = material_deposition_times.size();
103  if (times_size > 1)
104  ASSERT_THROW(material_deposition_times[times_size - 2] <=
105  material_deposition_times[times_size - 1],
106  "Time stamp not increasing.");
107  // Read the angle of material deposition
108  double deposition_angle = std::stod(split_line[2 * dim + 1]);
109  material_deposition_cos.push_back(std::cos(deposition_angle));
110  material_deposition_sin.push_back(std::sin(deposition_angle));
111 
112  // Build the dealii::BoundingBox
113  dealii::Point<dim> bounding_pt_a;
114  dealii::Point<dim> bounding_pt_b;
115  for (int d = 0; d < dim; ++d)
116  {
117  bounding_pt_a[d] = center[d] - 0.5 * box_size[d];
118  bounding_pt_b[d] = center[d] + 0.5 * box_size[d];
119  }
120  material_deposition_boxes.emplace_back(
121  std::make_pair(bounding_pt_a, bounding_pt_b));
122  }
123  file.close();
124 
125  return std::make_tuple(material_deposition_boxes, material_deposition_times,
126  material_deposition_cos, material_deposition_sin);
127 }
128 
129 template <int dim>
130 std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
131  std::vector<double>, std::vector<double>>
132 deposition_along_scan_path(boost::property_tree::ptree const &geometry_database,
133  ScanPath const &scan_path)
134 {
135  std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
136  std::vector<double>, std::vector<double>>
137  deposition_path;
138  int constexpr tuple_box = 0;
139  int constexpr tuple_time = 1;
140  int constexpr tuple_cos = 2;
141  int constexpr tuple_sin = 3;
142 
143  // Load the box size information and lead time
144 
145  // PropertyTreeInput geometry.deposition_length
146  double deposition_length = geometry_database.get<double>("deposition_length");
147  // PropertyTreeInput geometry.deposition_height
148  double deposition_height = geometry_database.get<double>("deposition_height");
149  // PropertyTreeInput geometry.deposition_width
150  double deposition_width =
151  geometry_database.get<double>("deposition_width", 0.0);
152 
153  // PropertyTreeInput geometry.deposition_lead_time
154  double lead_time = geometry_database.get<double>("deposition_lead_time");
155 
156  // Loop through the scan path segments, adding boxes inside each one
157  std::vector<ScanPathSegment> segment_list = scan_path.get_segment_list();
158  double segment_start_time = 0.0;
159  Quaternion segment_start_rotation = segment_list.at(0).end_rotation;
160  dealii::Point<3> segment_start_point = segment_list.at(0).end_point;
161  dealii::Point<3> build_ref_segment_start_point =
162  scan_path.is_five_axis()
163  ? segment_start_rotation.inv_rotate(segment_start_point)
164  : segment_start_point;
165  for (ScanPathSegment segment : segment_list)
166  {
167  // Only add material if the power is on
168  double const eps = 1.0e-10;
169  double const eps_time = 1.0e-10;
170  if (segment.power_modifier > eps)
171  {
172  Quaternion segment_end_rotation = segment.end_rotation;
173  dealii::Point<3> segment_end_point = segment.end_point;
174  dealii::Point<3> build_ref_segment_end_point =
175  scan_path.is_five_axis()
176  ? segment_end_rotation.inv_rotate(segment_end_point)
177  : segment_end_point;
178  double const segment_length =
179  segment_end_point.distance(segment_start_point);
180  bool in_segment = true;
181  dealii::Point<3> center = build_ref_segment_start_point;
182  double const segment_velocity =
183  segment_length / (segment.end_time - segment_start_time);
184 
185  // Set the segment orientation. In spot mode, set the cos to 1 and the sin
186  // to 0.
187  double const cos =
188  segment_length != 0.
189  ? (segment_end_point[0] - segment_start_point[0]) / segment_length
190  : 1.0;
191  double const sin =
192  segment_length != 0.
193  ? (segment_end_point[1] - segment_start_point[1]) / segment_length
194  : 0.0;
195  double next_box_length = deposition_length;
196 
197  while (in_segment)
198  {
199  double distance_to_box_center =
200  center.distance(build_ref_segment_start_point);
201  double time_to_box_center =
202  segment_velocity != 0. ? distance_to_box_center / segment_velocity
203  : 0.;
204 
205  dealii::Point<dim> bounding_pt_a;
206  dealii::Point<dim> bounding_pt_b;
207 
208  if (segment_end_rotation.is_valid())
209  {
210  if constexpr (dim == 3)
211  {
212  double x_length = std::abs(cos) * next_box_length +
213  std::abs(sin) * deposition_width;
214  double y_length = std::abs(cos) * deposition_width +
215  std::abs(sin) * next_box_length;
216 
217  dealii::Point<3> max_corner(x_length / 2., y_length / 2., 0.);
218  dealii::Point<3> min_corner(-x_length / 2., -y_length / 2.,
219  -deposition_height);
220 
221  // We need to rotate the box to match the scan path
222  dealii::Point<3> rotated_max_corner =
223  segment_end_rotation.rotate(max_corner);
224  dealii::Point<3> rotated_min_corner =
225  segment_end_rotation.rotate(min_corner);
226 
227  // We need to recompute the min and max corners after rotation.
228  dealii::Point<3> new_max_corner;
229  dealii::Point<3> new_min_corner;
230  for (int d = 0; d < 3; ++d)
231  {
232  new_max_corner[d] =
233  std::max(rotated_max_corner[d], rotated_min_corner[d]);
234  new_min_corner[d] =
235  std::min(rotated_max_corner[d], rotated_min_corner[d]);
236  }
237 
238  bounding_pt_a = center + new_min_corner;
239  bounding_pt_b = center + new_max_corner;
240  }
241  else
242  {
244  }
245  }
246  else
247  {
248  std::vector<double> box_size(dim);
249  box_size.at(axis<dim>::z) = deposition_height;
250 
251  if constexpr (dim == 2)
252  {
253  box_size.at(axis<dim>::x) = next_box_length;
254  }
255  else
256  {
257  box_size.at(axis<dim>::x) = std::abs(cos) * next_box_length +
258  std::abs(sin) * deposition_width;
259  box_size.at(axis<dim>::y) = std::abs(cos) * deposition_width +
260  std::abs(sin) * next_box_length;
261  }
262 
263  for (int d = 0; d < dim - 1; ++d)
264  {
265  bounding_pt_a[d] = center[d] - 0.5 * box_size[d];
266  bounding_pt_b[d] = center[d] + 0.5 * box_size[d];
267  }
268  bounding_pt_a[dim - 1] = center[dim - 1] - box_size[dim - 1];
269  bounding_pt_b[dim - 1] = center[dim - 1];
270  }
271 
272  std::get<tuple_box>(deposition_path)
273  .push_back(std::make_pair(bounding_pt_a, bounding_pt_b));
274  std::get<tuple_time>(deposition_path)
275  .push_back(std::max(
276  segment_start_time + time_to_box_center - lead_time, eps_time));
277  std::get<tuple_cos>(deposition_path).push_back(cos);
278  std::get<tuple_sin>(deposition_path).push_back(sin);
279 
280  // Get the next box center
281  if (distance_to_box_center + eps > segment_length)
282  {
283  in_segment = false;
284  }
285  else
286  {
287  // Check to see if the next box is at the end of the segment and
288  // needs to have a modified length
289  double center_increment = deposition_length;
290  if (distance_to_box_center + deposition_length > segment_length)
291  {
292  center_increment = (segment_length - distance_to_box_center);
293  next_box_length = segment_length - distance_to_box_center;
294  }
295 
296  if (segment_end_rotation.is_valid())
297  {
298  if constexpr (dim == 3)
299  {
300  dealii::Point<3> incr_direction = build_ref_segment_end_point;
301  incr_direction -= center;
302  incr_direction /= incr_direction.norm();
303  dealii::Point<3> incr = center_increment * incr_direction;
304  center += incr;
305  }
306  }
307  else
308  {
309  center[0] += cos * center_increment;
310  center[1] += sin * center_increment;
311  }
312  }
313  }
314  }
315  segment_start_point = segment.end_point;
316  segment_start_time = segment.end_time;
317  segment_start_rotation = segment.end_rotation;
318  build_ref_segment_start_point =
319  segment_start_rotation.is_valid()
320  ? segment_start_rotation.inv_rotate(segment_start_point)
321  : segment_start_point;
322  }
323  return deposition_path;
324 }
325 
326 template <int dim>
327 std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
328  std::vector<double>, std::vector<double>>
330  std::vector<std::tuple<std::vector<dealii::BoundingBox<dim>>,
331  std::vector<double>, std::vector<double>,
332  std::vector<double>>> const &deposition_paths)
333 {
334  // Split the vector of tuples in four vectors
335  std::vector<dealii::BoundingBox<dim>> bounding_boxes;
336  std::vector<double> time;
337  std::vector<double> cos;
338  std::vector<double> sin;
339  for (auto const &path : deposition_paths)
340  {
341  bounding_boxes.insert(bounding_boxes.end(), std::get<0>(path).begin(),
342  std::get<0>(path).end());
343  time.insert(time.end(), std::get<1>(path).begin(), std::get<1>(path).end());
344  cos.insert(cos.end(), std::get<2>(path).begin(), std::get<2>(path).end());
345  sin.insert(sin.end(), std::get<3>(path).begin(), std::get<3>(path).end());
346  }
347 
348  // Create the permutation that sort the deposition times chronologically
349  unsigned int const n_boxes = bounding_boxes.size();
350  std::vector<int> permutation(n_boxes);
351  std::iota(permutation.begin(), permutation.end(), 0);
352  std::sort(permutation.begin(), permutation.end(),
353  [&](int const &i, int const &j) { return time[i] < time[j]; });
354 
355  // Apply the permutation to all the vectors. This is not the most memory
356  // efficient way to do it but I don't think it matters. We store a lot more
357  // dofs
358  std::vector<dealii::BoundingBox<dim>> permutated_bounding_boxes(n_boxes);
359  std::vector<double> permutated_time(n_boxes);
360  std::vector<double> permutated_cos(n_boxes);
361  std::vector<double> permutated_sin(n_boxes);
362  for (unsigned int i = 0; i < n_boxes; ++i)
363  {
364  permutated_bounding_boxes[i] = bounding_boxes[permutation[i]];
365  permutated_time[i] = time[permutation[i]];
366  permutated_cos[i] = cos[permutation[i]];
367  permutated_sin[i] = sin[permutation[i]];
368  }
369 
370  return std::make_tuple(permutated_bounding_boxes, permutated_time,
371  permutated_cos, permutated_sin);
372 }
373 
374 template <int dim>
375 std::vector<std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>
377  [[maybe_unused]] Geometry<dim> const &geometry,
378  dealii::DoFHandler<dim> const &dof_handler,
379  std::vector<dealii::BoundingBox<dim>> const &material_deposition_boxes)
380 {
381  // Exit early if we can
382  if (material_deposition_boxes.size() == 0)
383  return std::vector<
384  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>();
385 
386  // We activate the cells that intersect a box. To do that we use ArborX.
387  // First, we create the bounding boxes of all the non-activated cells.
388  std::vector<dealii::BoundingBox<dim>> bounding_boxes;
389  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>
390  cell_iterators;
391 #if ARBORX_VERSION_MAJOR >= 2
392  if (geometry.use_stl())
393  {
394  for (auto const &cell : dealii::filter_iterators(
395  dof_handler.active_cell_iterators(),
396  dealii::IteratorFilters::LocallyOwnedCell(),
397  dealii::IteratorFilters::ActiveFEIndexEqualTo(1)))
398  {
399  if (geometry.is_within_stl(cell))
400  {
401  bounding_boxes.push_back(cell->bounding_box());
402  cell_iterators.push_back(cell);
403  }
404  }
405  }
406  else
407 #endif
408  {
409  for (auto const &cell : dealii::filter_iterators(
410  dof_handler.active_cell_iterators(),
411  dealii::IteratorFilters::LocallyOwnedCell(),
412  dealii::IteratorFilters::ActiveFEIndexEqualTo(1)))
413  {
414  bounding_boxes.push_back(cell->bounding_box());
415  cell_iterators.push_back(cell);
416  }
417  }
418 
419  // Perform the search
420  dealii::ArborXWrappers::BVH bvh(bounding_boxes);
421  dealii::ArborXWrappers::BoundingBoxIntersectPredicate bb_intersect(
422  material_deposition_boxes);
423  auto [indices, offset] = bvh.query(bb_intersect);
424 
425  unsigned int const n_queries = material_deposition_boxes.size();
426  std::vector<
427  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>
428  elements_to_activate(n_queries);
429  for (unsigned int i = 0; i < n_queries; ++i)
430  {
431  for (int j = offset[i]; j < offset[i + 1]; ++j)
432  {
433  elements_to_activate[i].push_back(cell_iterators[indices[j]]);
434  }
435  }
436 
437  return elements_to_activate;
438 }
439 } // namespace adamantine
440 
441 //-------------------- Explicit Instantiations --------------------//
442 namespace adamantine
443 {
444 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
445  std::vector<double>, std::vector<double>>
447  boost::property_tree::ptree const &geometry_database,
448  std::vector<std::shared_ptr<HeatSource<2>>> &heat_sources);
449 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
450  std::vector<double>, std::vector<double>>
452  boost::property_tree::ptree const &geometry_database,
453  std::vector<std::shared_ptr<HeatSource<3>>> &heat_sources);
454 
455 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
456  std::vector<double>, std::vector<double>>
457 read_material_deposition(boost::property_tree::ptree const &geometry_database);
458 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
459  std::vector<double>, std::vector<double>>
460 read_material_deposition(boost::property_tree::ptree const &geometry_database);
461 
462 template std::vector<
463  std::vector<typename dealii::DoFHandler<2>::active_cell_iterator>>
465  Geometry<2> const &geometry, dealii::DoFHandler<2> const &dof_handler,
466  std::vector<dealii::BoundingBox<2>> const &material_deposition_boxes);
467 template std::vector<
468  std::vector<typename dealii::DoFHandler<3>::active_cell_iterator>>
470  Geometry<3> const &geometry, dealii::DoFHandler<3> const &dof_handler,
471  std::vector<dealii::BoundingBox<3>> const &material_deposition_boxes);
472 
473 template std::tuple<std::vector<dealii::BoundingBox<2, double>>,
474  std::vector<double>, std::vector<double>,
475  std::vector<double>>
477  std::vector<std::tuple<std::vector<dealii::BoundingBox<2, double>>,
478  std::vector<double>, std::vector<double>,
479  std::vector<double>>> const &deposition_paths);
480 template std::tuple<std::vector<dealii::BoundingBox<3, double>>,
481  std::vector<double>, std::vector<double>,
482  std::vector<double>>
484  std::vector<std::tuple<std::vector<dealii::BoundingBox<3, double>>,
485  std::vector<double>, std::vector<double>,
486  std::vector<double>>> const &deposition_paths);
487 
488 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
489  std::vector<double>, std::vector<double>>
490 deposition_along_scan_path(boost::property_tree::ptree const &geometry_database,
491  ScanPath const &scan_path);
492 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
493  std::vector<double>, std::vector<double>>
494 deposition_along_scan_path(boost::property_tree::ptree const &geometry_database,
495  ScanPath const &scan_path);
496 } // namespace adamantine
dealii::Point< 3, double > rotate(dealii::Point< 3, double > const &point) const
Definition: Quaternion.hh:126
dealii::Point< 3, double > inv_rotate(dealii::Point< 3, double > const &point) const
Definition: Quaternion.hh:139
bool is_valid() const
Definition: Quaternion.cc:50
std::vector< ScanPathSegment > get_segment_list() const
Definition: ScanPath.cc:296
bool is_five_axis() const
Definition: ScanPath.cc:303
std::tuple< std::vector< dealii::BoundingBox< dim > >, std::vector< double >, std::vector< double >, std::vector< double > > deposition_along_scan_path(boost::property_tree::ptree const &geometry_database, ScanPath const &scan_path)
void ASSERT_THROW_NOT_IMPLEMENTED()
Definition: utils.hh:85
void wait_for_file(std::string const &filename, std::string const &message)
Definition: utils.hh:24
std::tuple< std::vector< dealii::BoundingBox< dim > >, std::vector< double >, std::vector< double >, std::vector< double > > create_material_deposition_boxes(boost::property_tree::ptree const &geometry_database, std::vector< std::shared_ptr< HeatSource< dim >>> &heat_sources)
std::tuple< std::vector< dealii::BoundingBox< dim > >, std::vector< double >, std::vector< double >, std::vector< double > > merge_deposition_paths(std::vector< std::tuple< std::vector< dealii::BoundingBox< dim >>, std::vector< double >, std::vector< double >, std::vector< double >>> const &deposition_paths)
std::tuple< std::vector< dealii::BoundingBox< dim > >, std::vector< double >, std::vector< double >, std::vector< double > > read_material_deposition(boost::property_tree::ptree const &geometry_database)
std::vector< std::vector< typename dealii::DoFHandler< dim >::active_cell_iterator > > get_elements_to_activate([[maybe_unused]] Geometry< dim > const &geometry, dealii::DoFHandler< dim > const &dof_handler, std::vector< dealii::BoundingBox< dim >> const &material_deposition_boxes)
void ASSERT_THROW(bool cond, std::string const &message)
Definition: utils.hh:70