adamantine
material_deposition.cc
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: Copyright (c) 2021 - 2023, 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 segements, adding boxes inside each one
157  std::vector<ScanPathSegment> segment_list = scan_path.get_segment_list();
158  double segment_start_time = 0.0;
159  dealii::Point<3> segment_start_point = segment_list.at(0).end_point;
160  for (ScanPathSegment segment : segment_list)
161  {
162  // Only add material if the power is on
163  double const eps = 1.0e-12;
164  double const eps_time = 1.0e-12;
165  if (segment.power_modifier > eps)
166  {
167  dealii::Point<3> segment_end_point = segment.end_point;
168  double const segment_length =
169  segment_end_point.distance(segment_start_point);
170  bool in_segment = true;
171  dealii::Point<3> center = segment_start_point;
172  double const segment_velocity =
173  segment_length / (segment.end_time - segment_start_time);
174 
175  // Set the segment orientation. In spot mode, set the cos to 1 and the sin
176  // to 0.
177  double const cos =
178  segment_length != 0.
179  ? (segment_end_point[0] - segment_start_point[0]) / segment_length
180  : 1.0;
181  double const sin =
182  segment_length != 0.
183  ? (segment_end_point[1] - segment_start_point[1]) / segment_length
184  : 0.0;
185  bool const segment_along_x = std::abs(cos) > std::abs(sin) ? true : false;
186  double next_box_length = deposition_length;
187 
188  while (in_segment)
189  {
190  double distance_to_box_center = center.distance(segment_start_point);
191  double time_to_box_center =
192  segment_velocity != 0. ? distance_to_box_center / segment_velocity
193  : 0.;
194 
195  std::vector<double> box_size(dim);
196  box_size.at(axis<dim>::z) = deposition_height;
197 
198  if (dim == 2)
199  {
200  box_size.at(axis<dim>::x) = next_box_length;
201  }
202  else
203  {
204  if (segment_along_x)
205  {
206  box_size.at(axis<dim>::x) = std::abs(cos) * next_box_length;
207  box_size.at(axis<dim>::y) =
208  deposition_width + std::abs(sin) * next_box_length;
209  }
210  else
211  {
212  box_size.at(axis<dim>::x) =
213  deposition_width + std::abs(cos) * next_box_length;
214  box_size.at(axis<dim>::y) = std::abs(sin) * next_box_length;
215  }
216  }
217 
218  dealii::Point<dim> bounding_pt_a;
219  dealii::Point<dim> bounding_pt_b;
220  for (int d = 0; d < dim - 1; ++d)
221  {
222  bounding_pt_a[d] = center[d] - 0.5 * box_size[d];
223  bounding_pt_b[d] = center[d] + 0.5 * box_size[d];
224  }
225  bounding_pt_a[dim - 1] = center[dim - 1] - box_size[dim - 1];
226  bounding_pt_b[dim - 1] = center[dim - 1];
227 
228  std::get<tuple_box>(deposition_path)
229  .push_back(std::make_pair(bounding_pt_a, bounding_pt_b));
230  std::get<tuple_time>(deposition_path)
231  .push_back(std::max(
232  segment_start_time + time_to_box_center - lead_time, eps_time));
233  std::get<tuple_cos>(deposition_path).push_back(cos);
234  std::get<tuple_sin>(deposition_path).push_back(sin);
235 
236  // Get the next box center
237  if (distance_to_box_center + eps > segment_length)
238  {
239  in_segment = false;
240  }
241  else
242  {
243  // Check to see if the next box is at the end of the segment and
244  // needs to have a modified length
245  double center_increment = deposition_length;
246  if (distance_to_box_center + deposition_length > segment_length)
247  {
248  center_increment = deposition_length / 2.0 +
249  (segment_length - distance_to_box_center) / 2.0;
250  next_box_length = segment_length - distance_to_box_center;
251  }
252 
253  center[0] += cos * center_increment;
254  center[1] += sin * center_increment;
255  }
256  }
257  }
258  segment_start_point = segment.end_point;
259  segment_start_time = segment.end_time;
260  }
261  return deposition_path;
262 }
263 
264 template <int dim>
265 std::tuple<std::vector<dealii::BoundingBox<dim>>, std::vector<double>,
266  std::vector<double>, std::vector<double>>
268  std::vector<std::tuple<std::vector<dealii::BoundingBox<dim>>,
269  std::vector<double>, std::vector<double>,
270  std::vector<double>>> const &deposition_paths)
271 {
272  // Split the vector of tuples in four vectors
273  std::vector<dealii::BoundingBox<dim>> bounding_boxes;
274  std::vector<double> time;
275  std::vector<double> cos;
276  std::vector<double> sin;
277  for (auto const &path : deposition_paths)
278  {
279  bounding_boxes.insert(bounding_boxes.end(), std::get<0>(path).begin(),
280  std::get<0>(path).end());
281  time.insert(time.end(), std::get<1>(path).begin(), std::get<1>(path).end());
282  cos.insert(cos.end(), std::get<2>(path).begin(), std::get<2>(path).end());
283  sin.insert(sin.end(), std::get<3>(path).begin(), std::get<3>(path).end());
284  }
285 
286  // Create the permutation that sort the deposition times chronologically
287  unsigned int const n_boxes = bounding_boxes.size();
288  std::vector<int> permutation(n_boxes);
289  std::iota(permutation.begin(), permutation.end(), 0);
290  std::sort(permutation.begin(), permutation.end(),
291  [&](int const &i, int const &j) { return time[i] < time[j]; });
292 
293  // Apply the permutation to all the vectors. This is not the most memory
294  // efficient way to do it but I don't think it matters. We store a lot more
295  // dofs
296  std::vector<dealii::BoundingBox<dim>> permutated_bounding_boxes(n_boxes);
297  std::vector<double> permutated_time(n_boxes);
298  std::vector<double> permutated_cos(n_boxes);
299  std::vector<double> permutated_sin(n_boxes);
300  for (unsigned int i = 0; i < n_boxes; ++i)
301  {
302  permutated_bounding_boxes[i] = bounding_boxes[permutation[i]];
303  permutated_time[i] = time[permutation[i]];
304  permutated_cos[i] = cos[permutation[i]];
305  permutated_sin[i] = sin[permutation[i]];
306  }
307 
308  return std::make_tuple(permutated_bounding_boxes, permutated_time,
309  permutated_cos, permutated_sin);
310 }
311 
312 template <int dim>
313 std::vector<std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>
315  dealii::DoFHandler<dim> const &dof_handler,
316  std::vector<dealii::BoundingBox<dim>> const &material_deposition_boxes)
317 {
318  // Exit early if we can
319  if (material_deposition_boxes.size() == 0)
320  return std::vector<
321  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>();
322 
323  // We activate the cells that intersect a box. To do that we use ArborX.
324  // First, we create the bounding boxes of all the non-activated cells.
325  std::vector<dealii::BoundingBox<dim>> bounding_boxes;
326  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>
327  cell_iterators;
328  for (auto const &cell : dealii::filter_iterators(
329  dof_handler.active_cell_iterators(),
330  dealii::IteratorFilters::LocallyOwnedCell(),
331  dealii::IteratorFilters::ActiveFEIndexEqualTo(1)))
332  {
333  bounding_boxes.push_back(cell->bounding_box());
334  cell_iterators.push_back(cell);
335  }
336 
337  // Perform the search
338  dealii::ArborXWrappers::BVH bvh(bounding_boxes);
339  dealii::ArborXWrappers::BoundingBoxIntersectPredicate bb_intersect(
340  material_deposition_boxes);
341  auto [indices, offset] = bvh.query(bb_intersect);
342 
343  unsigned int const n_queries = material_deposition_boxes.size();
344  std::vector<
345  std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator>>
346  elements_to_activate(n_queries);
347  for (unsigned int i = 0; i < n_queries; ++i)
348  {
349  for (int j = offset[i]; j < offset[i + 1]; ++j)
350  {
351  elements_to_activate[i].push_back(cell_iterators[indices[j]]);
352  }
353  }
354 
355  return elements_to_activate;
356 }
357 } // namespace adamantine
358 
359 //-------------------- Explicit Instantiations --------------------//
360 namespace adamantine
361 {
362 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
363  std::vector<double>, std::vector<double>>
365  boost::property_tree::ptree const &geometry_database,
366  std::vector<std::shared_ptr<HeatSource<2>>> &heat_sources);
367 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
368  std::vector<double>, std::vector<double>>
370  boost::property_tree::ptree const &geometry_database,
371  std::vector<std::shared_ptr<HeatSource<3>>> &heat_sources);
372 
373 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
374  std::vector<double>, std::vector<double>>
375 read_material_deposition(boost::property_tree::ptree const &geometry_database);
376 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
377  std::vector<double>, std::vector<double>>
378 read_material_deposition(boost::property_tree::ptree const &geometry_database);
379 
380 template std::vector<
381  std::vector<typename dealii::DoFHandler<2>::active_cell_iterator>>
383  dealii::DoFHandler<2> const &dof_handler,
384  std::vector<dealii::BoundingBox<2>> const &material_deposition_boxes);
385 template std::vector<
386  std::vector<typename dealii::DoFHandler<3>::active_cell_iterator>>
388  dealii::DoFHandler<3> const &dof_handler,
389  std::vector<dealii::BoundingBox<3>> const &material_deposition_boxes);
390 
391 template std::tuple<std::vector<dealii::BoundingBox<2, double>>,
392  std::vector<double>, std::vector<double>,
393  std::vector<double>>
395  std::vector<std::tuple<std::vector<dealii::BoundingBox<2, double>>,
396  std::vector<double>, std::vector<double>,
397  std::vector<double>>> const &deposition_paths);
398 template std::tuple<std::vector<dealii::BoundingBox<3, double>>,
399  std::vector<double>, std::vector<double>,
400  std::vector<double>>
402  std::vector<std::tuple<std::vector<dealii::BoundingBox<3, double>>,
403  std::vector<double>, std::vector<double>,
404  std::vector<double>>> const &deposition_paths);
405 
406 template std::tuple<std::vector<dealii::BoundingBox<2>>, std::vector<double>,
407  std::vector<double>, std::vector<double>>
408 deposition_along_scan_path(boost::property_tree::ptree const &geometry_database,
409  ScanPath const &scan_path);
410 template std::tuple<std::vector<dealii::BoundingBox<3>>, std::vector<double>,
411  std::vector<double>, std::vector<double>>
412 deposition_along_scan_path(boost::property_tree::ptree const &geometry_database,
413  ScanPath const &scan_path);
414 } // namespace adamantine
std::vector< ScanPathSegment > get_segment_list() const
Definition: ScanPath.cc:261
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 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)
void ASSERT_THROW(bool cond, std::string const &message)
Definition: utils.hh:70
std::vector< std::vector< typename dealii::DoFHandler< dim >::active_cell_iterator > > get_elements_to_activate(dealii::DoFHandler< dim > const &dof_handler, std::vector< dealii::BoundingBox< dim >> const &material_deposition_boxes)