adamantine
adamantine.hh
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: Copyright (c) 2016 - 2026, the adamantine authors.
2  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3  */
4 
5 #ifndef ADAMANTINE_HH
6 #define ADAMANTINE_HH
7 
8 #include <Boundary.hh>
9 #include <DataAssimilator.hh>
10 #include <ExperimentalData.hh>
11 #include <Geometry.hh>
12 #include <MaterialProperty.hh>
13 #include <MechanicalPhysics.hh>
14 #include <Microstructure.hh>
15 #include <PointCloud.hh>
16 #include <PostProcessor.hh>
17 #include <RayTracing.hh>
18 #include <ThermalPhysics.hh>
20 #include <Timer.hh>
21 #include <ensemble_management.hh>
23 #include <material_deposition.hh>
24 #include <types.hh>
25 #include <utils.hh>
26 
27 #include <deal.II/arborx/bvh.h>
28 #include <deal.II/base/index_set.h>
29 #include <deal.II/base/mpi.h>
30 #include <deal.II/base/symmetric_tensor.h>
31 #include <deal.II/base/types.h>
32 #include <deal.II/distributed/cell_data_transfer.templates.h>
33 #include <deal.II/distributed/solution_transfer.h>
34 #include <deal.II/grid/filtered_iterator.h>
35 #include <deal.II/grid/grid_refinement.h>
36 #if DEAL_II_VERSION_GTE(9, 7, 0) && defined(DEAL_II_TRILINOS_WITH_TPETRA)
37 #include <deal.II/lac/trilinos_tpetra_sparse_matrix.h>
38 #else
39 #include <deal.II/lac/trilinos_sparse_matrix.h>
40 #endif
41 #include <deal.II/lac/vector_operation.h>
42 #include <deal.II/numerics/error_estimator.h>
43 
44 #include <boost/algorithm/string.hpp>
45 #include <boost/archive/text_iarchive.hpp>
46 #include <boost/archive/text_oarchive.hpp>
47 #include <boost/property_tree/ptree.hpp>
48 
49 #include <fstream>
50 #include <limits>
51 #include <memory>
52 #include <string>
53 #include <type_traits>
54 #include <unordered_map>
55 
56 #ifdef ADAMANTINE_WITH_CALIPER
57 #include <caliper/cali.h>
58 #endif
59 
60 #include <cmath>
61 #include <iostream>
62 
63 template <int dim, int n_materials, int p_order, typename MaterialStates,
64  typename MemorySpaceType,
65  std::enable_if_t<
66  std::is_same<MemorySpaceType, dealii::MemorySpace::Host>::value,
67  int> = 0>
69  adamantine::PostProcessor<dim> &post_processor, unsigned int n_time_step,
70  double time,
71  std::unique_ptr<
73  &thermal_physics,
74  dealii::LinearAlgebra::distributed::Vector<double, MemorySpaceType>
75  &temperature,
76  std::unique_ptr<adamantine::MechanicalPhysics<
77  dim, n_materials, p_order, MaterialStates, MemorySpaceType>> const
78  &mechanical_physics,
79  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
80  &displacement,
81  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
82  MemorySpaceType> const &material_properties,
83  std::vector<adamantine::Timer> &timers)
84 {
85 #ifdef ADAMANTINE_WITH_CALIPER
86  CALI_CXX_MARK_FUNCTION;
87 #endif
88  timers[adamantine::output].start();
89  if (thermal_physics)
90  {
91  thermal_physics->get_affine_constraints().distribute(temperature);
92  if (mechanical_physics)
93  {
94  mechanical_physics->get_affine_constraints().distribute(displacement);
95  post_processor.template write_output<typename Kokkos::View<
96  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
97  n_time_step, time, temperature, displacement,
98  mechanical_physics->get_stress_tensor(),
99  material_properties.get_state(), material_properties.get_dofs_map(),
100  material_properties.get_dof_handler());
101  }
102  else
103  {
104  post_processor.template write_thermal_output<typename Kokkos::View<
105  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
106  n_time_step, time, temperature, material_properties.get_state(),
107  material_properties.get_dofs_map(),
108  material_properties.get_dof_handler());
109  }
110  }
111  else
112  {
113  mechanical_physics->get_affine_constraints().distribute(displacement);
114  post_processor.template write_mechanical_output<typename Kokkos::View<
115  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
116  n_time_step, time, displacement,
117  mechanical_physics->get_stress_tensor(),
118  material_properties.get_state(), material_properties.get_dofs_map(),
119  material_properties.get_dof_handler());
120  }
121  timers[adamantine::output].stop();
122 }
123 
124 template <int dim, int n_materials, int p_order, typename MaterialStates,
125  typename MemorySpaceType,
126  std::enable_if_t<std::is_same<MemorySpaceType,
127  dealii::MemorySpace::Default>::value,
128  int> = 0>
129 void output_pvtu(
130  adamantine::PostProcessor<dim> &post_processor, unsigned int n_time_step,
131  double time,
132  std::unique_ptr<
134  &thermal_physics,
135  dealii::LinearAlgebra::distributed::Vector<double, MemorySpaceType>
136  &temperature,
137  std::unique_ptr<adamantine::MechanicalPhysics<
138  dim, n_materials, p_order, MaterialStates, MemorySpaceType>> const
139  &mechanical_physics,
140  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
141  &displacement,
142  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
143  MemorySpaceType> const &material_properties,
144  std::vector<adamantine::Timer> &timers)
145 {
146 #ifdef ADAMANTINE_WITH_CALIPER
147  CALI_CXX_MARK_FUNCTION;
148 #endif
149  timers[adamantine::output].start();
150  auto state = material_properties.get_state();
151  auto state_host = Kokkos::create_mirror_view_and_copy(
152  dealii::MemorySpace::Host::kokkos_space{}, state);
153  if (thermal_physics)
154  {
155  dealii::LinearAlgebra::distributed::Vector<double,
156  dealii::MemorySpace::Host>
157  temperature_host(temperature.get_partitioner());
158  temperature_host.import_elements(temperature,
159  dealii::VectorOperation::insert);
160  thermal_physics->get_affine_constraints().distribute(temperature_host);
161  if (mechanical_physics)
162  {
163  mechanical_physics->get_affine_constraints().distribute(displacement);
164  post_processor.template write_output<typename Kokkos::View<
165  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
166  n_time_step, time, temperature_host, displacement,
167  mechanical_physics->get_stress_tensor(), state_host,
168  material_properties.get_dofs_map(),
169  material_properties.get_dof_handler());
170  }
171  else
172  {
173  post_processor.template write_thermal_output<typename Kokkos::View<
174  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
175  n_time_step, time, temperature_host, state_host,
176  material_properties.get_dofs_map(),
177  material_properties.get_dof_handler());
178  }
179  }
180  else
181  {
182  mechanical_physics->get_affine_constraints().distribute(displacement);
183  post_processor.template write_mechanical_output<typename Kokkos::View<
184  double **, typename MemorySpaceType::kokkos_space>::array_layout>(
185  n_time_step, time, displacement,
186  mechanical_physics->get_stress_tensor(), state_host,
187  material_properties.get_dofs_map(),
188  material_properties.get_dof_handler());
189  }
190  timers[adamantine::output].stop();
191 }
192 
193 // inlining this function so we can have in the header
194 inline void initialize_timers(MPI_Comm const &communicator,
195  std::vector<adamantine::Timer> &timers)
196 {
197  timers.push_back(adamantine::Timer(communicator, "Main"));
198  timers.push_back(adamantine::Timer(communicator, "Refinement"));
199  timers.push_back(adamantine::Timer(communicator, "Add Material, Search"));
200  timers.push_back(adamantine::Timer(communicator, "Add Material, Activate"));
201  timers.push_back(
202  adamantine::Timer(communicator, "Data Assimilation, Exp. Data"));
203  timers.push_back(
204  adamantine::Timer(communicator, "Data Assimilation, DOF Mapping"));
205  timers.push_back(
206  adamantine::Timer(communicator, "Data Assimilation, Cov. Sparsity"));
207  timers.push_back(
208  adamantine::Timer(communicator, "Data Assimilation, Exp. Cov."));
209  timers.push_back(
210  adamantine::Timer(communicator, "Data Assimilation, Update Ensemble"));
211  timers.push_back(adamantine::Timer(communicator, "Evolve One Time Step"));
212  timers.push_back(adamantine::Timer(
213  communicator, "Evolve One Time Step: evaluate_thermal_physics"));
214  timers.push_back(adamantine::Timer(
215  communicator, "Evolve One Time Step: evaluate_material_properties"));
216  timers.push_back(adamantine::Timer(communicator, "Output"));
217 }
218 
219 template <int dim, int n_materials, int p_order, int fe_degree,
220  typename MaterialStates, typename MemorySpaceType,
221  typename QuadratureType>
222 std::unique_ptr<adamantine::ThermalPhysicsInterface<dim, MemorySpaceType>>
224  MPI_Comm const &communicator, boost::property_tree::ptree const &database,
225  adamantine::Geometry<dim> &geometry, adamantine::Boundary const &boundary,
226  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
227  MemorySpaceType> &material_properties)
228 {
229  return std::make_unique<adamantine::ThermalPhysics<
230  dim, n_materials, p_order, fe_degree, MaterialStates, MemorySpaceType,
231  QuadratureType>>(communicator, database, geometry, boundary,
232  material_properties);
233 }
234 
235 template <int dim, int n_materials, int p_order, int fe_degree,
236  typename MaterialStates, typename MemorySpaceType>
237 std::unique_ptr<adamantine::ThermalPhysicsInterface<dim, MemorySpaceType>>
239  std::string const &quadrature_type, MPI_Comm const &communicator,
240  boost::property_tree::ptree const &database,
241  adamantine::Geometry<dim> &geometry, adamantine::Boundary const &boundary,
242  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
243  MemorySpaceType> &material_properties)
244 {
245  if (quadrature_type.compare("gauss") == 0)
246  return initialize<dim, n_materials, p_order, fe_degree, MaterialStates,
247  MemorySpaceType, dealii::QGauss<1>>(
248  communicator, database, geometry, boundary, material_properties);
249  else
250  {
251  adamantine::ASSERT_THROW(quadrature_type.compare("lobatto") == 0,
252  "quadrature should be Gauss or Lobatto.");
253  return initialize<dim, n_materials, p_order, fe_degree, MaterialStates,
254  MemorySpaceType, dealii::QGaussLobatto<1>>(
255  communicator, database, geometry, boundary, material_properties);
256  }
257 }
258 
259 template <int dim, int n_materials, int p_order, typename MaterialStates,
260  typename MemorySpaceType>
261 std::unique_ptr<adamantine::ThermalPhysicsInterface<dim, MemorySpaceType>>
263  unsigned int fe_degree, std::string const &quadrature_type,
264  MPI_Comm const &communicator, boost::property_tree::ptree const &database,
265  adamantine::Geometry<dim> &geometry, adamantine::Boundary const &boundary,
266  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
267  MemorySpaceType> &material_properties)
268 {
269  switch (fe_degree)
270  {
271  case 1:
272  {
273  return initialize_quadrature<dim, n_materials, p_order, 1, MaterialStates,
274  MemorySpaceType>(quadrature_type, communicator,
275  database, geometry, boundary,
276  material_properties);
277  }
278  case 2:
279  {
280  return initialize_quadrature<dim, n_materials, p_order, 2, MaterialStates,
281  MemorySpaceType>(quadrature_type, communicator,
282  database, geometry, boundary,
283  material_properties);
284  }
285  case 3:
286  {
287  return initialize_quadrature<dim, n_materials, p_order, 3, MaterialStates,
288  MemorySpaceType>(quadrature_type, communicator,
289  database, geometry, boundary,
290  material_properties);
291  }
292  case 4:
293  {
294  return initialize_quadrature<dim, n_materials, p_order, 4, MaterialStates,
295  MemorySpaceType>(quadrature_type, communicator,
296  database, geometry, boundary,
297  material_properties);
298  }
299  default:
300  {
301  adamantine::ASSERT_THROW(fe_degree == 5,
302  "fe_degree should be between 1 and 5.");
303  return initialize_quadrature<dim, n_materials, p_order, 5, MaterialStates,
304  MemorySpaceType>(quadrature_type, communicator,
305  database, geometry, boundary,
306  material_properties);
307  }
308  }
309 }
310 
311 template <int dim, int n_materials, int p_order, typename MaterialStates,
312  typename MemorySpaceType>
315  &thermal_physics,
316  std::unique_ptr<adamantine::MechanicalPhysics<
317  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
318  &mechanical_physics,
319  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
320  MemorySpaceType> &material_properties,
321  dealii::DoFHandler<dim> &dof_handler,
322  dealii::LA::distributed::Vector<double, MemorySpaceType> &solution)
323 {
324 #ifdef ADAMANTINE_WITH_CALIPER
325  CALI_CXX_MARK_FUNCTION;
326 #endif
327 
328  dealii::parallel::distributed::Triangulation<dim> &triangulation =
329  dynamic_cast<dealii::parallel::distributed::Triangulation<dim> &>(
330  const_cast<dealii::Triangulation<dim> &>(
331  dof_handler.get_triangulation()));
332 
333  // Update the material state from the ThermalOperator to MaterialProperty
334  // because, for now, we need to use state from MaterialProperty to perform the
335  // transfer to the refined mesh.
336  thermal_physics->set_state_to_material_properties();
337 
338  // Transfer of the solution
339 #if DEAL_II_VERSION_GTE(9, 7, 0)
340  dealii::SolutionTransfer<
341 #else
342  dealii::parallel::distributed::SolutionTransfer<
343 #endif
344  dim, dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>>
345  solution_transfer(dof_handler);
346 
347  // Transfer material state
348  unsigned int const direction_data_size = 2;
349  unsigned int const phase_history_data_size = 1;
350  unsigned int constexpr n_material_states = MaterialStates::n_material_states;
351  std::vector<std::vector<double>> data_to_transfer;
352  std::vector<double> dummy_cell_data(n_material_states + direction_data_size +
353  phase_history_data_size,
354  std::numeric_limits<double>::infinity());
355  auto state_host = Kokkos::create_mirror_view_and_copy(
356  Kokkos::HostSpace{}, material_properties.get_state());
357  unsigned int cell_id = 0;
358  unsigned int activated_cell_id = 0;
359  for (auto const &cell : dof_handler.active_cell_iterators())
360  {
361  if (cell->is_locally_owned())
362  {
363  std::vector<double> cell_data(n_material_states + direction_data_size +
364  phase_history_data_size);
365  for (unsigned int i = 0; i < n_material_states; ++i)
366  cell_data[i] = state_host(i, cell_id);
367  if (cell->active_fe_index() == 0)
368  {
369  cell_data[n_material_states] =
370  thermal_physics->get_deposition_cos(activated_cell_id);
371  cell_data[n_material_states + 1] =
372  thermal_physics->get_deposition_sin(activated_cell_id);
373 
374  if (thermal_physics->get_has_melted(activated_cell_id))
375  cell_data[n_material_states + direction_data_size] = 1.0;
376  else
377  cell_data[n_material_states + direction_data_size] = 0.0;
378 
379  ++activated_cell_id;
380  }
381  else
382  {
383  cell_data[n_material_states] = std::numeric_limits<double>::infinity();
384  cell_data[n_material_states + 1] =
385  std::numeric_limits<double>::infinity();
386  cell_data[n_material_states + direction_data_size] =
387  std::numeric_limits<double>::infinity();
388  }
389  data_to_transfer.push_back(cell_data);
390  ++cell_id;
391  }
392  else
393  {
394  data_to_transfer.push_back(dummy_cell_data);
395  }
396  }
397 
398  // Prepare the Triangulation and the diffent data transfer objects for
399  // refinement
400  triangulation.prepare_coarsening_and_refinement();
401  // Prepare for refinement of the solution
402  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
403  solution_host;
404  if constexpr (std::is_same_v<MemorySpaceType, dealii::MemorySpace::Host>)
405  {
406  // We need to apply the constraints before the mesh transfer
407  thermal_physics->get_affine_constraints().distribute(solution);
408  // We need to update the ghost values before we can do the interpolation on
409  // the new mesh.
410  solution.update_ghost_values();
411  solution_transfer.prepare_for_coarsening_and_refinement(solution);
412  }
413  else
414  {
415  solution_host.reinit(solution.get_partitioner());
416  solution_host.import_elements(solution, dealii::VectorOperation::insert);
417  // We need to apply the constraints before the mesh transfer
418  thermal_physics->get_affine_constraints().distribute(solution_host);
419  // We need to update the ghost values before we can do the interpolation on
420  // the new mesh.
421  solution_host.update_ghost_values();
422  solution_transfer.prepare_for_coarsening_and_refinement(solution_host);
423  }
424 
425  dealii::parallel::distributed::CellDataTransfer<
426  dim, dim, std::vector<std::vector<double>>>
427  cell_data_trans(triangulation);
428  cell_data_trans.prepare_for_coarsening_and_refinement(data_to_transfer);
429 
430  if (mechanical_physics)
431  {
432  mechanical_physics->prepare_transfer_mpi();
433  }
434 
435 #ifdef ADAMANTINE_WITH_CALIPER
436  CALI_MARK_BEGIN("refine triangulation");
437 #endif
438  // Execute the refinement
439  triangulation.execute_coarsening_and_refinement();
440 #ifdef ADAMANTINE_WITH_CALIPER
441  CALI_MARK_END("refine triangulation");
442 #endif
443 
444  // Update the AffineConstraints and resize the solution
445  thermal_physics->setup_dofs();
446  thermal_physics->initialize_dof_vector(0., solution);
447 
448  // Update MaterialProperty DoFHandler and resize the state vectors
449  material_properties.reinit_dofs();
450 
451  // Interpolate the solution
452  if constexpr (std::is_same_v<MemorySpaceType, dealii::MemorySpace::Host>)
453  {
454  solution_transfer.interpolate(solution);
455  }
456  else
457  {
458  solution_host.reinit(solution.get_partitioner());
459  solution_transfer.interpolate(solution_host);
460  solution.import_elements(solution_host, dealii::VectorOperation::insert);
461  }
462 
463  // Unpack the material state and repopulate the material state
464  std::vector<std::vector<double>> transferred_data(
465  triangulation.n_active_cells(),
466  std::vector<double>(n_material_states + direction_data_size +
467  phase_history_data_size));
468  cell_data_trans.unpack(transferred_data);
469  auto state = material_properties.get_state();
470  state_host = Kokkos::create_mirror_view(state);
471  unsigned int total_cell_id = 0;
472  cell_id = 0;
473  std::vector<double> transferred_cos;
474  std::vector<double> transferred_sin;
475  std::vector<bool> has_melted;
476  for (auto const &cell : dof_handler.active_cell_iterators())
477  {
478  if (cell->is_locally_owned())
479  {
480  for (unsigned int i = 0; i < n_material_states; ++i)
481  {
482  state_host(i, cell_id) = transferred_data[total_cell_id][i];
483  }
484  if (cell->active_fe_index() == 0)
485  {
486  transferred_cos.push_back(
487  transferred_data[total_cell_id][n_material_states]);
488  transferred_sin.push_back(
489  transferred_data[total_cell_id][n_material_states + 1]);
490 
491  // Convert from double back to bool
492  if (transferred_data[total_cell_id]
493  [n_material_states + direction_data_size] > 0.5)
494  has_melted.push_back(true);
495  else
496  has_melted.push_back(false);
497  }
498  ++cell_id;
499  }
500  ++total_cell_id;
501  }
502 
503  // Update the deposition cos and sin
504  thermal_physics->set_material_deposition_orientation(transferred_cos,
505  transferred_sin);
506 
507  // Update the melted indicator
508  thermal_physics->set_has_melted_vector(has_melted);
509 
510  // Copy the data back to material_property
511  Kokkos::deep_copy(state, state_host);
512 
513  // Update the material states in the ThermalOperator
514  thermal_physics->get_state_from_material_properties();
515 
516 #if ADAMANTINE_DEBUG
517  // Check that we are not losing material
518  cell_id = 0;
519  for (auto const &cell : dof_handler.active_cell_iterators())
520  {
521  if (cell->is_locally_owned())
522  {
523  double material_ratio = 0.;
524  for (unsigned int i = 0; i < n_material_states; ++i)
525  {
526  material_ratio += state_host(i, cell_id);
527  }
528  ASSERT(std::abs(material_ratio - 1.) < 1e-14, "Material is lost.");
529  ++cell_id;
530  }
531  }
532 #endif
533 
534  if (mechanical_physics)
535  {
536  mechanical_physics->complete_transfer_mpi();
537  }
538 }
539 
540 template <int dim>
541 std::vector<typename dealii::parallel::distributed::Triangulation<
542  dim>::active_cell_iterator>
544  dealii::parallel::distributed::Triangulation<dim> &triangulation,
545  double const time, double const next_refinement_time,
546  unsigned int const n_time_steps,
547  std::vector<std::shared_ptr<adamantine::HeatSource<dim>>> const
548  &heat_sources)
549 {
550  // Compute the position of the beams between time and next_refinement_time and
551  // create a list of cells that will intersect the beam.
552 
553  // Build the bounding boxes associated with the locally owned cells
554  std::vector<dealii::BoundingBox<dim>> cell_bounding_boxes;
555  cell_bounding_boxes.reserve(triangulation.n_locally_owned_active_cells());
556  for (auto const &cell : triangulation.active_cell_iterators() |
557  dealii::IteratorFilters::LocallyOwnedCell())
558  {
559  cell_bounding_boxes.push_back(cell->bounding_box());
560  }
561  dealii::ArborXWrappers::BVH bvh(cell_bounding_boxes);
562 
563  double const bounding_box_scaling = 2.0;
564  std::vector<dealii::BoundingBox<dim>> heat_source_bounding_boxes;
565  heat_source_bounding_boxes.reserve(n_time_steps * heat_sources.size());
566  for (unsigned int i = 0; i < n_time_steps; ++i)
567  {
568  double const current_time = time + static_cast<double>(i) /
569  static_cast<double>(n_time_steps) *
570  (next_refinement_time - time);
571 
572  // Build the bounding boxes associated with the heat sources
573  for (auto &beam : heat_sources)
574  {
575  heat_source_bounding_boxes.push_back(
576  beam->get_bounding_box(current_time, bounding_box_scaling));
577  }
578  }
579 
580  // Perform the search with ArborX. Since we are only interested in locally
581  // owned cells, we use BVH.
582  dealii::ArborXWrappers::BoundingBoxIntersectPredicate bb_intersect(
583  heat_source_bounding_boxes);
584  auto [indices, offset] = bvh.query(bb_intersect);
585 
586  // Put the indices into a set to get rid of the duplicates and to make it
587  // easier to check if the indices are found.
588  std::unordered_set<int> indices_to_refine;
589  indices_to_refine.insert(indices.begin(), indices.end());
590 
591  std::vector<typename dealii::parallel::distributed::Triangulation<
592  dim>::active_cell_iterator>
593  cells_to_refine;
594  int cell_index = 0;
595  for (auto const &cell : triangulation.active_cell_iterators() |
596  dealii::IteratorFilters::LocallyOwnedCell())
597  {
598  if (indices_to_refine.count(cell_index))
599  {
600  cells_to_refine.push_back(cell);
601  }
602  ++cell_index;
603  }
604 
605  return cells_to_refine;
606 }
607 
608 template <int dim, int n_materials, int p_order, int fe_degree,
609  typename MaterialStates, typename MemorySpaceType>
612  &thermal_physics,
613  std::unique_ptr<adamantine::MechanicalPhysics<
614  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
615  &mechanical_physics,
616  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
617  MemorySpaceType> &material_properties,
618  dealii::LA::distributed::Vector<double, MemorySpaceType> &solution,
619  std::vector<std::shared_ptr<adamantine::HeatSource<dim>>> const
620  &heat_sources,
621  double const time, double const next_refinement_time,
622  unsigned int const time_steps_refinement,
623  boost::property_tree::ptree const &refinement_database)
624 {
625 #ifdef ADAMANTINE_WITH_CALIPER
626  CALI_CXX_MARK_FUNCTION;
627 #endif
628  dealii::DoFHandler<dim> &dof_handler = thermal_physics->get_dof_handler();
629  dealii::parallel::distributed::Triangulation<dim> &triangulation =
630  dynamic_cast<dealii::parallel::distributed::Triangulation<dim> &>(
631  const_cast<dealii::Triangulation<dim> &>(
632  dof_handler.get_triangulation()));
633 
634  // PropertyTreeInput refinement.n_refinements
635  unsigned int const n_refinements =
636  refinement_database.get("n_refinements", 2);
637 
638  for (unsigned int i = 0; i < n_refinements; ++i)
639  {
640  // Compute the cells to be refined.
641  auto cells_to_refine =
642  compute_cells_to_refine(triangulation, time, next_refinement_time,
643  time_steps_refinement, heat_sources);
644 
645  // PropertyTreeInput refinement.coarsen_after_beam
646  const bool coarsen_after_beam =
647  refinement_database.get<bool>("coarsen_after_beam", false);
648 
649  // If coarsening is allowed, set the coarsening flag everywhere
650  if (coarsen_after_beam)
651  {
652  for (auto cell : dealii::filter_iterators(
653  triangulation.active_cell_iterators(),
654  dealii::IteratorFilters::LocallyOwnedCell()))
655  {
656  if (cell->level() > 0)
657  cell->set_coarsen_flag();
658  }
659  }
660 
661  // Flag the cells for refinement.
662  for (auto &cell : cells_to_refine)
663  {
664  if (coarsen_after_beam)
665  cell->clear_coarsen_flag();
666 
667  if (cell->level() < static_cast<int>(n_refinements))
668  cell->set_refine_flag();
669  }
670 
671  // Execute the refinement and transfer the solution onto the new mesh.
672  refine_and_transfer(thermal_physics, mechanical_physics,
673  material_properties, dof_handler, solution);
674  }
675 
676  // Recompute the inverse of the mass matrix
677  thermal_physics->compute_inverse_mass_matrix();
678 }
679 
680 template <int dim, int n_materials, int p_order, typename MaterialStates,
681  typename MemorySpaceType>
684  &thermal_physics,
685  std::unique_ptr<adamantine::MechanicalPhysics<
686  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
687  &mechanical_physics,
688  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
689  MemorySpaceType> &material_properties,
690  dealii::LA::distributed::Vector<double, MemorySpaceType> &solution,
691  std::vector<std::shared_ptr<adamantine::HeatSource<dim>>> const
692  &heat_sources,
693  double const time, double const next_refinement_time,
694  unsigned int const time_steps_refinement,
695  boost::property_tree::ptree const &refinement_database)
696 {
697  if (!thermal_physics)
698  return;
699 
700  switch (thermal_physics->get_fe_degree())
701  {
702  case 1:
703  {
704  refine_mesh<dim, n_materials, p_order, 1, MaterialStates>(
705  thermal_physics, mechanical_physics, material_properties, solution,
706  heat_sources, time, next_refinement_time, time_steps_refinement,
707  refinement_database);
708  break;
709  }
710  case 2:
711  {
712  refine_mesh<dim, n_materials, p_order, 2, MaterialStates>(
713  thermal_physics, mechanical_physics, material_properties, solution,
714  heat_sources, time, next_refinement_time, time_steps_refinement,
715  refinement_database);
716  break;
717  }
718  case 3:
719  {
720  refine_mesh<dim, n_materials, p_order, 3, MaterialStates>(
721  thermal_physics, mechanical_physics, material_properties, solution,
722  heat_sources, time, next_refinement_time, time_steps_refinement,
723  refinement_database);
724  break;
725  }
726  case 4:
727  {
728  refine_mesh<dim, n_materials, p_order, 4, MaterialStates>(
729  thermal_physics, mechanical_physics, material_properties, solution,
730  heat_sources, time, next_refinement_time, time_steps_refinement,
731  refinement_database);
732  break;
733  }
734  case 5:
735  {
736  refine_mesh<dim, n_materials, p_order, 5, MaterialStates>(
737  thermal_physics, mechanical_physics, material_properties, solution,
738  heat_sources, time, next_refinement_time, time_steps_refinement,
739  refinement_database);
740  break;
741  }
742  default:
743  {
744  adamantine::ASSERT_THROW(false, "fe_degree should be between 1 and 5.");
745  }
746  }
747 }
748 
749 template <int dim, int n_materials, int p_order, typename MaterialStates,
750  typename MemorySpaceType>
751 std::pair<dealii::LinearAlgebra::distributed::Vector<double,
752  dealii::MemorySpace::Host>,
753  dealii::LinearAlgebra::distributed::Vector<double,
754  dealii::MemorySpace::Host>>
755 run(MPI_Comm const &communicator, boost::property_tree::ptree const &database,
756  std::vector<adamantine::Timer> &timers)
757 {
758 #ifdef ADAMANTINE_WITH_CALIPER
759  CALI_CXX_MARK_FUNCTION;
760 #endif
761  // Get optional units property tree
762  boost::optional<boost::property_tree::ptree const &> units_optional_database =
763  database.get_child_optional("units");
764 
765  // Create the Geometry
766  boost::property_tree::ptree geometry_database =
767  database.get_child("geometry");
768  adamantine::Geometry<dim> geometry(communicator, geometry_database,
769  units_optional_database);
770 
771  // Create the MaterialProperty
772  boost::property_tree::ptree material_database =
773  database.get_child("materials");
774  adamantine::MaterialProperty<dim, n_materials, p_order, MaterialStates,
775  MemorySpaceType>
776  material_properties(communicator, geometry.get_triangulation(),
777  material_database);
778 
779  // Extract the physics property tree
780  boost::property_tree::ptree physics_database = database.get_child("physics");
781  bool const use_thermal_physics = physics_database.get<bool>("thermal");
782  bool const use_mechanical_physics = physics_database.get<bool>("mechanical");
783 
784  // Extract the discretization property tree
785  boost::property_tree::ptree discretization_database =
786  database.get_child("discretization");
787 
788  // Extract the post-processor property tree
789  boost::property_tree::ptree post_processor_database =
790  database.get_child("post_processor");
791 
792  // Extract the verbosity
793  // PropertyTreeInput verbose_output
794  bool const verbose_output = database.get("verbose_output", false);
795 
796  // Create the Boundary
797  boost::property_tree::ptree boundary_database =
798  database.get_child("boundary");
799  adamantine::Boundary boundary(
800  boundary_database, geometry.get_triangulation().get_boundary_ids());
801 
802  // Create ThermalPhysics if necessary
803  std::unique_ptr<adamantine::ThermalPhysicsInterface<dim, MemorySpaceType>>
804  thermal_physics;
805  std::vector<std::shared_ptr<adamantine::HeatSource<dim>>> heat_sources;
806  if (use_thermal_physics)
807  {
808  // PropertyTreeInput discretization.thermal.fe_degree
809  unsigned int const fe_degree =
810  discretization_database.get<unsigned int>("thermal.fe_degree");
811  // PropertyTreeInput discretization.thermal.quadrature
812  std::string quadrature_type =
813  discretization_database.get("thermal.quadrature", "gauss");
814  thermal_physics = initialize_thermal_physics<dim>(
815  fe_degree, quadrature_type, communicator, database, geometry, boundary,
816  material_properties);
817  heat_sources = thermal_physics->get_heat_sources();
818  post_processor_database.put("thermal_output", true);
819  }
820 
821  // PropertyTreeInput materials.initial_temperature
822  double const initial_temperature =
823  material_database.get("initial_temperature", 300.);
824 
825  // Get the reference temperature(s) for the substrate and deposited
826  // material(s)
827  std::vector<double> material_reference_temps;
828  // PropertyTreeInput materials.n_materials
829  unsigned int const n_materials_runtime =
830  material_database.get<unsigned int>("n_materials");
831  for (unsigned int i = 0; i < n_materials_runtime; ++i)
832  {
833  // Use the solidus as the reference temperature, in the future we may want
834  // to use something else
835  // PropertyTreeInput materials.material_n.solidus
836  double const reference_temperature = material_database.get<double>(
837  "material_" + std::to_string(i) + ".solidus");
838  material_reference_temps.push_back(reference_temperature);
839  }
840  material_reference_temps.push_back(initial_temperature);
841 
842  // Create MechanicalPhysics
843  std::unique_ptr<adamantine::MechanicalPhysics<
844  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
845  mechanical_physics;
846  if (use_mechanical_physics)
847  {
848  // PropertyTreeInput discretization.mechanical.fe_degree
849  unsigned int const fe_degree =
850  discretization_database.get<unsigned int>("mechanical.fe_degree");
851  mechanical_physics = std::make_unique<adamantine::MechanicalPhysics<
852  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>(
853  communicator, fe_degree, geometry, boundary, material_properties,
854  material_reference_temps);
855  post_processor_database.put("mechanical_output", true);
856  }
857 
858  // Create PostProcessor
859  std::unique_ptr<adamantine::PostProcessor<dim>> post_processor;
860  bool thermal_output = post_processor_database.get("thermal_output", false);
861  bool mechanical_output =
862  post_processor_database.get("mechanical_output", false);
863  if ((thermal_output) && (mechanical_output))
864  {
865  post_processor = std::make_unique<adamantine::PostProcessor<dim>>(
866  communicator, post_processor_database,
867  thermal_physics->get_dof_handler(),
868  mechanical_physics->get_dof_handler());
869  }
870  else
871  {
872  post_processor = std::make_unique<adamantine::PostProcessor<dim>>(
873  communicator, post_processor_database,
874  thermal_output ? thermal_physics->get_dof_handler()
875  : mechanical_physics->get_dof_handler());
876  }
877 
878  // Get the checkpoint and restart subtrees
879  boost::optional<boost::property_tree::ptree const &>
880  checkpoint_optional_database = database.get_child_optional("checkpoint");
881  boost::optional<boost::property_tree::ptree const &>
882  restart_optional_database = database.get_child_optional("restart");
883 
884  unsigned int time_steps_checkpoint = std::numeric_limits<unsigned int>::max();
885  std::string checkpoint_filename;
886  bool checkpoint_overwrite = true;
887  if (checkpoint_optional_database)
888  {
889  auto checkpoint_database = checkpoint_optional_database.get();
890  // PropertyTreeInput checkpoint.time_steps_between_checkpoint
891  time_steps_checkpoint =
892  checkpoint_database.get<unsigned int>("time_steps_between_checkpoint");
893  // PropertyTreeInput checkpoint.filename_prefix
894  checkpoint_filename =
895  checkpoint_database.get<std::string>("filename_prefix");
896  // PropertyTreeInput checkpoint.overwrite_files
897  checkpoint_overwrite = checkpoint_database.get<bool>("overwrite_files");
898  }
899 
900  bool restart = false;
901  std::string restart_filename;
902  if (restart_optional_database)
903  {
904  restart = true;
905  auto restart_database = restart_optional_database.get();
906  // PropertyTreeInput restart.filename_prefix
907  restart_filename = restart_database.get<std::string>("filename_prefix");
908  }
909 
910  dealii::LA::distributed::Vector<double, MemorySpaceType> temperature;
911  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
912  displacement;
913  if (use_thermal_physics)
914  {
915  if (restart == false)
916  {
917  thermal_physics->setup();
918  thermal_physics->initialize_dof_vector(initial_temperature, temperature);
919  }
920  else
921  {
922 #ifdef ADAMANTINE_WITH_CALIPER
923  CALI_MARK_BEGIN("restart from file");
924 #endif
925  thermal_physics->load_checkpoint(restart_filename, temperature);
926 #ifdef ADAMANTINE_WITH_CALIPER
927  CALI_MARK_END("restart from file");
928 #endif
929  }
930  }
931 
932  if (use_mechanical_physics)
933  {
934  // We currently do not support restarting the mechanical simulation.
936  restart == false,
937  "Mechanical simulation cannot be restarted from a file");
938  if (use_thermal_physics)
939  {
940  // Thermo-mechanical simulation
941  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
942  temperature_host(temperature.get_partitioner());
943  temperature_host.import_elements(temperature,
944  dealii::VectorOperation::insert);
945  mechanical_physics->setup_dofs(thermal_physics->get_dof_handler(),
946  temperature_host,
947  thermal_physics->get_has_melted_vector());
948  }
949  else
950  {
951  // Mechanical only simulation
952  mechanical_physics->setup_dofs();
953  }
954  displacement = mechanical_physics->solve();
955  }
956 
957  unsigned int progress = 0;
958  unsigned int n_time_step = 0;
959  double time = 0.;
960  double activation_time_end = -1.;
961  unsigned int const rank =
962  dealii::Utilities::MPI::this_mpi_process(communicator);
963  if (restart == true)
964  {
965  if (rank == 0)
966  {
967  std::cout << "Restarting from file" << std::endl;
968  }
969  std::ifstream file{restart_filename + "_time.txt"};
970  boost::archive::text_iarchive ia{file};
971  ia >> time;
972  ia >> n_time_step;
973  }
974  // PropertyTreeInput geometry.deposition_time
975  double const activation_time =
976  geometry_database.get<double>("deposition_time", 0.);
977 
978  // Output the initial solution
979  output_pvtu(*post_processor, n_time_step, time, thermal_physics, temperature,
980  mechanical_physics, displacement, material_properties, timers);
981  ++n_time_step;
982 
983  // Create the bounding boxes used for material deposition
984  auto [material_deposition_boxes, deposition_times, deposition_cos,
985  deposition_sin] =
986  adamantine::create_material_deposition_boxes<dim>(geometry_database,
987  heat_sources);
988  // Extract the time-stepping database
989  boost::property_tree::ptree time_stepping_database =
990  database.get_child("time_stepping");
991  // PropertyTreeInput time_stepping.time_step
992  double time_step = time_stepping_database.get<double>("time_step");
993  // PropertyTreeInput time_stepping.mechanical_time_step_factor
994  unsigned int const mechanical_time_step_factor =
995  time_stepping_database.get<unsigned int>("mechanical_time_step_factor",
996  1);
997  // PropertyTreeInput time_stepping.scan_path_for_duration
998  bool const scan_path_for_duration =
999  time_stepping_database.get("scan_path_for_duration", false);
1000  // PropertyTreeInput time_stepping.duration
1001  double const duration = scan_path_for_duration
1002  ? std::numeric_limits<double>::max()
1003  : time_stepping_database.get<double>("duration");
1004 
1005  // Extract the refinement database
1006  boost::property_tree::ptree refinement_database =
1007  database.get_child("refinement");
1008  // PropertyTreeInput refinement.time_steps_between_refinement
1009  unsigned int const time_steps_refinement =
1010  refinement_database.get("time_steps_between_refinement", 10);
1011  // PropertyTreeInput post_processor.time_steps_between_output
1012  unsigned int const time_steps_output =
1013  post_processor_database.get("time_steps_between_output", 1);
1014  // PropertyTreeInput materials.new_material_temperature
1015  double const new_material_temperature =
1016  database.get("materials.new_material_temperature", 300.);
1017 
1018  // Extract the microstructure database if present
1019  boost::optional<boost::property_tree::ptree const &>
1020  microstructure_optional_database =
1021  database.get_child_optional("microstructure");
1022  bool const compute_microstructure =
1023  microstructure_optional_database ? true : false;
1024  std::unique_ptr<adamantine::Microstructure<dim>> microstructure;
1025  if (compute_microstructure)
1026  {
1027  auto microstructure_database = microstructure_optional_database.get();
1028  // PropertyTreeInput microstructure.filename_prefix
1029  std::string microstructure_filename =
1030  microstructure_database.get<std::string>("filename_prefix");
1031  microstructure = std::make_unique<adamantine::Microstructure<dim>>(
1032  communicator, microstructure_filename);
1033  }
1034 
1035  bool rebuild_mechanical_matrix = true;
1036 
1037 #ifdef ADAMANTINE_WITH_CALIPER
1038  CALI_CXX_MARK_LOOP_BEGIN(main_loop_id, "main_loop");
1039 #endif
1040  while (time < duration)
1041  {
1042 #ifdef ADAMANTINE_WITH_CALIPER
1043  CALI_CXX_MARK_LOOP_ITERATION(main_loop_id, n_time_step - 1);
1044 #endif
1045  if ((time + time_step) > duration)
1046  time_step = duration - time;
1047 
1048  // Refine the mesh the first time we get in the loop and after
1049  // time_steps_refinement time steps.
1050  if (((n_time_step == 1) || ((n_time_step % time_steps_refinement) == 0)) &&
1051  use_thermal_physics)
1052  {
1053  timers[adamantine::refine].start();
1054  double next_refinement_time = time + time_steps_refinement * time_step;
1055  refine_mesh(thermal_physics, mechanical_physics, material_properties,
1056  temperature, heat_sources, time, next_refinement_time,
1057  time_steps_refinement, refinement_database);
1058  timers[adamantine::refine].stop();
1059  rebuild_mechanical_matrix = true;
1060  if ((rank == 0) && (verbose_output == true))
1061  {
1062  std::cout << "n_time_step: " << n_time_step << " time: " << time
1063  << " n_dofs after mesh refinement: "
1064  << thermal_physics->get_dof_handler().n_dofs() << std::endl;
1065  }
1066  }
1067 
1068  // Add material if necessary.
1069  // We use an epsilon to get the "expected" behavior when the deposition
1070  // time and the time match should match exactly but don't because of
1071  // floating point accuracy.
1072  timers[adamantine::add_material_activate].start();
1073  if (time > activation_time_end)
1074  {
1075  // If we use scan_path_for_duration, we may need to read the scan path
1076  // file once again.
1077  if (scan_path_for_duration)
1078  {
1079  // Check if we have reached the end of current scan path.
1080  bool need_updated_scan_path = false;
1081  for (auto &source : heat_sources)
1082  {
1083  if (time > source->get_scan_path().get_segment_list().back().end_time)
1084  {
1085  need_updated_scan_path = true;
1086  break;
1087  }
1088  }
1089 
1090  if (need_updated_scan_path)
1091  {
1092  // Check if we have reached the end of the file. If not, read the
1093  // updated scan path file
1094  bool scan_path_end = true;
1095  for (auto &source : heat_sources)
1096  {
1097  if (!source->get_scan_path().is_finished())
1098  {
1099  scan_path_end = false;
1100  // This functions waits for the scan path file to be updated
1101  // before reading the file.
1102  source->get_scan_path().read_file();
1103  }
1104  }
1105 
1106  // If we have reached the end of scan path file for all the heat
1107  // sources, we just exit.
1108  if (scan_path_end)
1109  {
1110  break;
1111  }
1112 
1113  std::tie(material_deposition_boxes, deposition_times, deposition_cos,
1114  deposition_sin) =
1115  adamantine::create_material_deposition_boxes<dim>(
1116  geometry_database, heat_sources);
1117  }
1118  }
1119 
1120  double const eps = time_step / 1e10;
1121 
1122  auto activation_start =
1123  std::lower_bound(deposition_times.begin(), deposition_times.end(),
1124  time - eps) -
1125  deposition_times.begin();
1126  activation_time_end =
1127  std::min(time + std::max(activation_time, time_step), duration) - eps;
1128  auto activation_end =
1129  std::lower_bound(deposition_times.begin(), deposition_times.end(),
1130  activation_time_end) -
1131  deposition_times.begin();
1132  if (activation_start < activation_end)
1133  {
1134  if (use_thermal_physics)
1135  {
1136 #ifdef ADAMANTINE_WITH_CALIPER
1137  CALI_MARK_BEGIN("add material");
1138 #endif
1139  // Compute the elements to activate.
1140  // TODO Right now, we compute the list of cells that get activated for
1141  // the entire material deposition. We should restrict the list to the
1142  // cells that are activated between activation_start and
1143  // activation_end.
1144  timers[adamantine::add_material_search].start();
1145  auto elements_to_activate = adamantine::get_elements_to_activate(
1146  geometry, thermal_physics->get_dof_handler(),
1147  material_deposition_boxes);
1148  timers[adamantine::add_material_search].stop();
1149 
1150  // For now assume that all deposited material has never been melted
1151  // (may or may not be reasonable)
1152  std::vector<bool> has_melted(deposition_cos.size(), false);
1153 
1154  thermal_physics->add_material_start(
1155  elements_to_activate, deposition_cos, deposition_sin, has_melted,
1156  activation_start, activation_end, temperature);
1157 
1158  if (use_mechanical_physics)
1159  {
1160  mechanical_physics->prepare_transfer_mpi();
1161  }
1162 
1163 #ifdef ADAMANTINE_WITH_CALIPER
1164  CALI_MARK_BEGIN("refine triangulation");
1165 #endif
1166  dealii::parallel::distributed::Triangulation<dim> &triangulation =
1167  dynamic_cast<dealii::parallel::distributed::Triangulation<dim> &>(
1168  const_cast<dealii::Triangulation<dim> &>(
1169  thermal_physics->get_dof_handler().get_triangulation()));
1170  triangulation.execute_coarsening_and_refinement();
1171 #ifdef ADAMANTINE_WITH_CALIPER
1172  CALI_MARK_END("refine triangulation");
1173 #endif
1174 
1175  thermal_physics->add_material_end(new_material_temperature,
1176  temperature);
1177 
1178  if (use_mechanical_physics)
1179  {
1180  mechanical_physics->complete_transfer_mpi();
1181  rebuild_mechanical_matrix = true;
1182  }
1183 
1184 #ifdef ADAMANTINE_WITH_CALIPER
1185  CALI_MARK_END("add material");
1186 #endif
1187  }
1188  }
1189 
1190  if ((rank == 0) && (verbose_output == true) &&
1191  (activation_end - activation_start > 0) && use_thermal_physics)
1192  {
1193  std::cout << "n_time_step: " << n_time_step << " time: " << time
1194  << " n_dofs after cell activation: "
1195  << thermal_physics->get_dof_handler().n_dofs() << std::endl;
1196  }
1197  }
1198  timers[adamantine::add_material_activate].stop();
1199 
1200  // If thermomechanics are being solved, mark cells that are above the
1201  // solidus as cells that should have their reference temperature reset.
1202  // This cannot be in the thermomechanics solve because some cells may go
1203  // above the solidus and then back below the solidus in the time between
1204  // thermomechanical solves.
1205  if (use_thermal_physics && use_mechanical_physics)
1206  {
1207  thermal_physics->mark_has_melted(material_reference_temps[0],
1208  temperature);
1209  }
1210 
1211  timers[adamantine::evol_time].start();
1212 
1213  // Solve the thermal problem
1214  if (use_thermal_physics)
1215  {
1216  if (compute_microstructure)
1217  {
1218 #ifdef ADAMANTINE_WITH_CALIPER
1219  CALI_MARK_BEGIN("compute microstructure");
1220 #endif
1221  if constexpr (std::is_same_v<MemorySpaceType,
1222  dealii::MemorySpace::Host>)
1223  {
1224  microstructure->set_old_temperature(temperature);
1225  }
1226  else
1227  {
1228  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
1229  temperature_host(temperature.get_partitioner());
1230  temperature_host.import_elements(temperature,
1231  dealii::VectorOperation::insert);
1232  microstructure->set_old_temperature(temperature_host);
1233  }
1234 #ifdef ADAMANTINE_WITH_CALIPER
1235  CALI_MARK_END("compute microstructure");
1236 #endif
1237  }
1238 
1239  time = thermal_physics->evolve_one_time_step(time, time_step, temperature,
1240  timers);
1241 
1242  if (compute_microstructure)
1243  {
1244 #ifdef ADAMANTINE_WITH_CALIPER
1245  CALI_MARK_BEGIN("compute microstructure");
1246 #endif
1247  if constexpr (std::is_same_v<MemorySpaceType,
1248  dealii::MemorySpace::Host>)
1249  {
1250  microstructure->compute_G_and_R(material_properties,
1251  thermal_physics->get_dof_handler(),
1252  temperature, time_step);
1253  }
1254  else
1255  {
1256  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
1257  temperature_host(temperature.get_partitioner());
1258  temperature_host.import_elements(temperature,
1259  dealii::VectorOperation::insert);
1260  microstructure->compute_G_and_R(material_properties,
1261  thermal_physics->get_dof_handler(),
1262  temperature_host, time_step);
1263  }
1264 #ifdef ADAMANTINE_WITH_CALIPER
1265  CALI_MARK_END("compute microstructure");
1266 #endif
1267  }
1268  }
1269 
1270  // Solve the (thermo-)mechanical problem
1271  if (use_mechanical_physics)
1272  {
1273  // Solve if the time step is a multipe of mechanical_time_step_factor or
1274  // if we will output the results.
1275  if ((n_time_step % mechanical_time_step_factor == 0) ||
1276  (n_time_step % time_steps_output == 0))
1277  {
1278  if (use_thermal_physics)
1279  {
1280  // Update the material state
1281  thermal_physics->set_state_to_material_properties();
1282  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
1283  temperature_host(temperature.get_partitioner());
1284  temperature_host.import_elements(temperature,
1285  dealii::VectorOperation::insert);
1286  if (rebuild_mechanical_matrix)
1287  {
1288  mechanical_physics->setup_dofs(
1289  thermal_physics->get_dof_handler(), temperature_host,
1290  thermal_physics->get_has_melted_vector());
1291  rebuild_mechanical_matrix = false;
1292  }
1293  else
1294  {
1295  mechanical_physics->update_rhs(
1296  thermal_physics->get_dof_handler(), temperature_host,
1297  thermal_physics->get_has_melted_vector());
1298  }
1299  }
1300  else
1301  {
1302  if (rebuild_mechanical_matrix)
1303  {
1304  mechanical_physics->setup_dofs();
1305  rebuild_mechanical_matrix = false;
1306  }
1307  else
1308  {
1309  mechanical_physics->update_rhs();
1310  }
1311  }
1312  displacement = mechanical_physics->solve();
1313  }
1314  }
1315 
1316  timers[adamantine::evol_time].stop();
1317 
1318  if (n_time_step % time_steps_checkpoint == 0)
1319  {
1320 #ifdef ADAMANTINE_WITH_CALIPER
1321  CALI_MARK_BEGIN("save checkpoint");
1322 #endif
1323  if (rank == 0)
1324  {
1325  std::cout << "Checkpoint reached" << std::endl;
1326  }
1327 
1328  std::string output_dir =
1329  post_processor_database.get<std::string>("output_dir", "");
1330  std::string filename_prefix =
1331  checkpoint_overwrite
1332  ? checkpoint_filename
1333  : checkpoint_filename + '_' + std::to_string(n_time_step);
1334  thermal_physics->save_checkpoint(filename_prefix, temperature);
1335  std::ofstream file{output_dir + filename_prefix + "_time.txt"};
1336  boost::archive::text_oarchive oa{file};
1337  oa << time;
1338  oa << n_time_step;
1339 #ifdef ADAMANTINE_WITH_CALIPER
1340  CALI_MARK_END("save checkpoint");
1341 #endif
1342  }
1343 
1344  // Output progress on screen
1345  if (rank == 0)
1346  {
1347  double adim_time = time / (duration / 10.);
1348  double int_part = 0;
1349  std::modf(adim_time, &int_part);
1350  if (int_part > progress)
1351  {
1352  std::cout << int_part * 10 << '%' << " completed" << std::endl;
1353  progress = static_cast<unsigned int>(int_part);
1354  }
1355  }
1356 
1357  // Output the solution
1358  if (n_time_step % time_steps_output == 0)
1359  {
1360  if (use_thermal_physics)
1361  {
1362  thermal_physics->set_state_to_material_properties();
1363  }
1364  output_pvtu(*post_processor, n_time_step, time, thermal_physics,
1365  temperature, mechanical_physics, displacement,
1366  material_properties, timers);
1367  }
1368  ++n_time_step;
1369  }
1370 
1371 #ifdef ADAMANTINE_WITH_CALIPER
1372  CALI_CXX_MARK_LOOP_END(main_loop_id);
1373 #endif
1374 
1375  post_processor->write_pvd();
1376 
1377  // This is only used for integration test
1378  if constexpr (std::is_same_v<MemorySpaceType, dealii::MemorySpace::Host>)
1379  {
1380  if (use_thermal_physics)
1381  {
1382  thermal_physics->get_affine_constraints().distribute(temperature);
1383  }
1384  if (use_mechanical_physics)
1385  {
1386  mechanical_physics->get_affine_constraints().distribute(displacement);
1387  }
1388  return std::make_pair(temperature, displacement);
1389  }
1390  else
1391  {
1392  dealii::LinearAlgebra::distributed::Vector<double,
1393  dealii::MemorySpace::Host>
1394  temperature_host(temperature.get_partitioner());
1395  temperature_host.import_elements(temperature,
1396  dealii::VectorOperation::insert);
1397  if (use_thermal_physics)
1398  {
1399  thermal_physics->get_affine_constraints().distribute(temperature_host);
1400  }
1401  if (use_mechanical_physics)
1402  {
1403  mechanical_physics->get_affine_constraints().distribute(displacement);
1404  }
1405  return std::make_pair(temperature_host, displacement);
1406  }
1407 }
1408 
1409 void split_global_communicator(MPI_Comm global_communicator,
1410  unsigned int global_ensemble_size,
1411  MPI_Comm &local_communicator,
1412  unsigned int &local_ensemble_size,
1413  unsigned int &first_local_member, int &my_color)
1414 {
1415  unsigned int const global_rank =
1416  dealii::Utilities::MPI::this_mpi_process(global_communicator);
1417  unsigned int const global_n_procs =
1418  dealii::Utilities::MPI::n_mpi_processes(global_communicator);
1419 
1420  double const avg_n_procs_per_member =
1421  static_cast<double>(global_n_procs) /
1422  static_cast<double>(global_ensemble_size);
1423  if (avg_n_procs_per_member > 1)
1424  {
1425  local_ensemble_size = 1;
1426  // We need all the members to use the same partitioning otherwise the dofs
1427  // may be number differently which is problematic for the data assimulation.
1429  avg_n_procs_per_member == std::floor(avg_n_procs_per_member),
1430  "Number of MPI ranks should be less than the number of ensemble "
1431  "members or a multiple of this number.");
1432  // Assign color
1433  my_color =
1434  global_rank / static_cast<int>(std::floor(avg_n_procs_per_member));
1435 
1436  first_local_member = my_color;
1437  }
1438  else
1439  {
1440  my_color = global_rank;
1441  // Compute the local ensemble size
1442  unsigned int const members_per_proc = global_ensemble_size / global_n_procs;
1443  unsigned int const leftover_members =
1444  global_ensemble_size - global_n_procs * members_per_proc;
1445  // We assign all the leftover members to processor 0. We could do a
1446  // better job distributing the leftover
1447  local_ensemble_size = members_per_proc;
1448  if (global_rank == 0)
1449  {
1450  local_ensemble_size += leftover_members;
1451  first_local_member = 0;
1452  }
1453  else
1454  {
1455  first_local_member = global_rank * local_ensemble_size + leftover_members;
1456  }
1457  }
1458 
1459  MPI_Comm_split(global_communicator, my_color, 0, &local_communicator);
1460 }
1461 
1462 template <int dim, int n_materials, int p_order, typename MaterialStates,
1463  typename MemorySpaceType>
1464 std::vector<dealii::LA::distributed::BlockVector<double>>
1465 run_ensemble(MPI_Comm const &global_communicator,
1466  boost::property_tree::ptree const &database,
1467  std::vector<adamantine::Timer> &timers)
1468 {
1469 #ifdef ADAMANTINE_WITH_CALIPER
1470  CALI_CXX_MARK_FUNCTION;
1471 #endif
1472 
1473  // ------ Extract child property trees -----
1474  // Mandatory subtrees
1475  boost::property_tree::ptree geometry_database =
1476  database.get_child("geometry");
1477  boost::property_tree::ptree boundary_database =
1478  database.get_child("boundary");
1479  boost::property_tree::ptree discretization_database =
1480  database.get_child("discretization");
1481  boost::property_tree::ptree time_stepping_database =
1482  database.get_child("time_stepping");
1483  boost::property_tree::ptree post_processor_database =
1484  database.get_child("post_processor");
1485  boost::property_tree::ptree refinement_database =
1486  database.get_child("refinement");
1487  boost::property_tree::ptree material_database =
1488  database.get_child("materials");
1489 
1490  // Optional subtrees
1491  boost::optional<boost::property_tree::ptree const &> units_optional_database =
1492  database.get_child_optional("units");
1493  boost::optional<const boost::property_tree::ptree &>
1494  experiment_optional_database = database.get_child_optional("experiment");
1495  boost::optional<const boost::property_tree::ptree &>
1496  data_assimilation_optional_database =
1497  database.get_child_optional("data_assimilation");
1498 
1499  // Verbosity
1500  // PropertyTreeInput verbose_output
1501  bool const verbose_output = database.get("verbose_output", false);
1502 
1503  // ------ Get finite element implementation parameters -----
1504  // PropertyTreeInput discretization.thermal.fe_degree
1505  unsigned int const fe_degree =
1506  discretization_database.get<unsigned int>("thermal.fe_degree");
1507  // PropertyTreeInput discretization.thermal.quadrature
1508  std::string quadrature_type =
1509  discretization_database.get("thermal.quadrature", "gauss");
1510  std::transform(quadrature_type.begin(), quadrature_type.end(),
1511  quadrature_type.begin(),
1512  [](unsigned char c) { return std::tolower(c); });
1513 
1514  // ------ Split MPI communicator -----
1515  // PropertyTreeInput ensemble.ensemble_size
1516  unsigned int const global_ensemble_size =
1517  database.get("ensemble.ensemble_size", 5);
1518  // Distribute the processors among the ensemble members
1519  MPI_Comm local_communicator;
1520  unsigned int local_ensemble_size = std::numeric_limits<unsigned int>::max();
1521  unsigned int first_local_member = std::numeric_limits<unsigned int>::max();
1522  int my_color = -1;
1523  split_global_communicator(global_communicator, global_ensemble_size,
1524  local_communicator, local_ensemble_size,
1525  first_local_member, my_color);
1526 
1527  // ------ Set up the ensemble members -----
1528  // There might be a more efficient way to share some of these objects
1529  // between ensemble members. For now, we'll do the simpler approach of
1530  // duplicating everything.
1531 
1532  // Create a new property tree database for each ensemble member
1533  std::vector<boost::property_tree::ptree> database_ensemble =
1535  database, local_communicator, first_local_member, local_ensemble_size,
1536  global_ensemble_size);
1537 
1538  std::vector<std::unique_ptr<
1540  thermal_physics_ensemble(local_ensemble_size);
1541 
1542  std::vector<std::vector<std::shared_ptr<adamantine::HeatSource<dim>>>>
1543  heat_sources_ensemble(local_ensemble_size);
1544 
1545  std::vector<std::unique_ptr<adamantine::Geometry<dim>>> geometry_ensemble;
1546 
1547  std::vector<std::unique_ptr<adamantine::Boundary>> boundary_ensemble;
1548 
1549  std::vector<std::unique_ptr<adamantine::MaterialProperty<
1550  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>>
1551  material_properties_ensemble;
1552 
1553  std::vector<std::unique_ptr<adamantine::PostProcessor<dim>>>
1554  post_processor_ensemble;
1555 
1556  // Create the vector of augmented state vectors
1557  std::vector<dealii::LA::distributed::BlockVector<double>>
1558  solution_augmented_ensemble(local_ensemble_size);
1559 
1560  // Give names to the blocks in the augmented state vector
1561  int constexpr base_state = 0;
1562  int constexpr augmented_state = 1;
1563 
1564  // ----- Initialize the data assimilation object -----
1565  boost::property_tree::ptree data_assimilation_database;
1566  bool assimilate_data = false;
1567  std::vector<adamantine::AugmentedStateParameters> augmented_state_parameters;
1568 
1569  if (data_assimilation_optional_database)
1570  {
1571  // PropertyTreeInput data_assimilation
1572  data_assimilation_database = data_assimilation_optional_database.get();
1573 
1574  // PropertyTreeInput data_assimilation.assimilate_data
1575  if (data_assimilation_database.get("assimilate_data", false))
1576  {
1577  assimilate_data = true;
1578 
1579  // PropertyTreeInput data_assimilation.augment_with_beam_0_absorption
1580  if (data_assimilation_database.get("augment_with_beam_0_absorption",
1581  false))
1582  {
1583  augmented_state_parameters.push_back(
1585  }
1586  // PropertyTreeInput data_assimilation.augment_with_beam_0_max_power
1587  if (data_assimilation_database.get("augment_with_beam_0_max_power",
1588  false))
1589  {
1590  augmented_state_parameters.push_back(
1592  }
1593  }
1594  }
1595  adamantine::DataAssimilator data_assimilator(global_communicator,
1596  local_communicator, my_color,
1597  data_assimilation_database);
1598 
1599  // Get the checkpoint and restart subtrees
1600  boost::optional<boost::property_tree::ptree const &>
1601  checkpoint_optional_database = database.get_child_optional("checkpoint");
1602  boost::optional<boost::property_tree::ptree const &>
1603  restart_optional_database = database.get_child_optional("restart");
1604 
1605  unsigned int const global_rank =
1606  dealii::Utilities::MPI::this_mpi_process(global_communicator);
1607  unsigned int time_steps_checkpoint = std::numeric_limits<unsigned int>::max();
1608  std::string checkpoint_filename;
1609  bool checkpoint_overwrite = true;
1610  if (checkpoint_optional_database)
1611  {
1612  auto checkpoint_database = checkpoint_optional_database.get();
1613  // PropertyTreeInput checkpoint.time_steps_between_checkpoint
1614  time_steps_checkpoint =
1615  checkpoint_database.get<unsigned int>("time_steps_between_checkpoint");
1616  // PropertyTreeInput checkpoint.filename_prefix
1617  checkpoint_filename =
1618  checkpoint_database.get<std::string>("filename_prefix");
1619  // PropertyTreeInput checkpoint.overwrite_files
1620  checkpoint_overwrite = checkpoint_database.get<bool>("overwrite_files");
1621  }
1622 
1623  bool restart = false;
1624  std::string restart_filename;
1625  if (restart_optional_database)
1626  {
1627  restart = true;
1628  auto restart_database = restart_optional_database.get();
1629  // PropertyTreeInput restart.filename_prefix
1630  restart_filename = restart_database.get<std::string>("filename_prefix");
1631  }
1632  for (unsigned int member = 0; member < local_ensemble_size; ++member)
1633  {
1634  // Resize the augmented ensemble block vector to have two blocks
1635  solution_augmented_ensemble[member].reinit(2);
1636 
1637  // Edit the database for the ensemble
1638  if (database.get<unsigned int>("sources.n_beams") > 0)
1639  {
1640  // Populate the parameter augmentation block of the augmented state
1641  // ensemble
1642  if (assimilate_data)
1643  {
1644  solution_augmented_ensemble[member]
1645  .block(augmented_state)
1646  .reinit(augmented_state_parameters.size());
1647 
1648  for (unsigned int index = 0; index < augmented_state_parameters.size();
1649  ++index)
1650  {
1651  // FIXME: Need to consider how we want to generalize this. It could
1652  // get unwieldy if we want to specify every parameter of an
1653  // arbitrary number of beams.
1654  if (augmented_state_parameters.at(index) ==
1656  {
1657  solution_augmented_ensemble[member].block(augmented_state)[index] =
1658  database_ensemble[member].get<double>(
1659  "sources.beam_0.absorption_efficiency");
1660  }
1661  else if (augmented_state_parameters.at(index) ==
1663  {
1664  solution_augmented_ensemble[member].block(augmented_state)[index] =
1665  database_ensemble[member].get<double>(
1666  "sources.beam_0.max_power");
1667  }
1668  }
1669  }
1670  }
1671 
1672  solution_augmented_ensemble[member].collect_sizes();
1673 
1674  geometry_ensemble.push_back(std::make_unique<adamantine::Geometry<dim>>(
1675  local_communicator, geometry_database, units_optional_database));
1676 
1677  boundary_ensemble.push_back(std::make_unique<adamantine::Boundary>(
1678  boundary_database,
1679  geometry_ensemble.back()->get_triangulation().get_boundary_ids()));
1680 
1681  material_properties_ensemble.push_back(
1682  std::make_unique<adamantine::MaterialProperty<
1683  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>(
1684  local_communicator, geometry_ensemble.back()->get_triangulation(),
1685  material_database));
1686 
1687  thermal_physics_ensemble[member] = initialize_thermal_physics<dim>(
1688  fe_degree, quadrature_type, local_communicator,
1689  database_ensemble[member], *geometry_ensemble[member],
1690  *boundary_ensemble[member], *material_properties_ensemble[member]);
1691  heat_sources_ensemble[member] =
1692  thermal_physics_ensemble[member]->get_heat_sources();
1693 
1694  if (restart == false)
1695  {
1696  thermal_physics_ensemble[member]->setup();
1697  thermal_physics_ensemble[member]->initialize_dof_vector(
1698  database_ensemble[member].get("materials.initial_temperature", 300.),
1699  solution_augmented_ensemble[member].block(base_state));
1700  }
1701  else
1702  {
1703 #ifdef ADAMANTINE_WITH_CALIPER
1704  CALI_MARK_BEGIN("restart from file");
1705 #endif
1706  thermal_physics_ensemble[member]->load_checkpoint(
1707  restart_filename + '_' + std::to_string(member),
1708  solution_augmented_ensemble[member].block(base_state));
1709 #ifdef ADAMANTINE_WITH_CALIPER
1710  CALI_MARK_END("restart from file");
1711 #endif
1712  }
1713  solution_augmented_ensemble[member].collect_sizes();
1714 
1715  // For now we only output temperature
1716  post_processor_database.put("thermal_output", true);
1717  post_processor_ensemble.push_back(
1718  std::make_unique<adamantine::PostProcessor<dim>>(
1719  local_communicator, post_processor_database,
1720  thermal_physics_ensemble[member]->get_dof_handler(),
1721  first_local_member + member));
1722  }
1723 
1724  // PostProcessor for outputting the experimental data
1725  boost::property_tree::ptree post_processor_expt_database;
1726  // PropertyTreeInput post_processor.filename_prefix
1727  std::string expt_file_prefix =
1728  post_processor_database.get<std::string>("filename_prefix") + ".expt";
1729  post_processor_expt_database.put("filename_prefix", expt_file_prefix);
1730  post_processor_expt_database.put("thermal_output", true);
1731  std::unique_ptr<adamantine::PostProcessor<dim>> post_processor_expt;
1732  // PropertyTreeInput experiment.output_experiment_on_mesh
1733  bool const output_experiment_on_mesh =
1734  experiment_optional_database
1735  ? experiment_optional_database->get("output_experiment_on_mesh", true)
1736  : false;
1737  // Optionally output the experimental data projected onto the mesh. Since the
1738  // experimental data is unique we may not need all the processors to write the
1739  // output files.
1740  if (output_experiment_on_mesh && (my_color == 0))
1741  {
1742  post_processor_expt = std::make_unique<adamantine::PostProcessor<dim>>(
1743  local_communicator, post_processor_expt_database,
1744  thermal_physics_ensemble[0]->get_dof_handler());
1745  }
1746 
1747  // ----- Initialize time and time stepping counters -----
1748  unsigned int progress = 0;
1749  unsigned int n_time_step = 0;
1750  double time = 0.;
1751  double activation_time_end = -1.;
1752  double da_time = -1.;
1753  if (restart == true)
1754  {
1755  if (global_rank == 0)
1756  {
1757  std::cout << "Restarting from file" << std::endl;
1758  }
1759  std::ifstream file{restart_filename + "_time.txt"};
1760  boost::archive::text_iarchive ia{file};
1761  ia >> time;
1762  ia >> n_time_step;
1763  }
1764 
1765  // PropertyTreeInput geometry.deposition_time
1766  double const activation_time =
1767  geometry_database.get<double>("deposition_time", 0.);
1768 
1769  // ----- Read the experimental data -----
1770  std::vector<std::vector<double>> frame_time_stamps;
1771  std::unique_ptr<adamantine::ExperimentalData<dim>> experimental_data;
1772  unsigned int experimental_frame_index =
1773  std::numeric_limits<unsigned int>::max();
1774 
1775  if (experiment_optional_database)
1776  {
1777  auto experiment_database = experiment_optional_database.get();
1778 
1779  // PropertyTreeInput experiment.read_in_experimental_data
1780  bool const read_in_experimental_data =
1781  experiment_database.get("read_in_experimental_data", false);
1782 
1783  if (read_in_experimental_data)
1784  {
1785  if (global_rank == 0)
1786  std::cout << "Reading the experimental log file..." << std::endl;
1787 
1788  frame_time_stamps =
1789  adamantine::read_frame_timestamps(experiment_database);
1790 
1791  adamantine::ASSERT_THROW(frame_time_stamps.size() > 0,
1792  "Experimental data parsing is activated, but "
1793  "the log shows zero cameras.");
1794  adamantine::ASSERT_THROW(frame_time_stamps[0].size() > 0,
1795  "Experimental data parsing is activated, but "
1796  "the log shows zero data frames.");
1797 
1798  if (global_rank == 0)
1799  std::cout << "Done. Log entries found for " << frame_time_stamps.size()
1800  << " camera(s), with " << frame_time_stamps[0].size()
1801  << " frame(s)." << std::endl;
1802 
1803  // PropertyTreeInput experiment.format
1804  std::string experiment_format =
1805  experiment_database.get<std::string>("format");
1806 
1807  if (boost::iequals(experiment_format, "point_cloud"))
1808  {
1809  experimental_data = std::make_unique<adamantine::PointCloud<dim>>(
1810  adamantine::PointCloud<dim>(experiment_database));
1811  }
1812  else
1813  {
1814  if constexpr (dim == 3)
1815  {
1816  experimental_data =
1817  std::make_unique<adamantine::RayTracing>(adamantine::RayTracing(
1818  experiment_database,
1819  thermal_physics_ensemble[0]->get_dof_handler()));
1820  }
1821  }
1822 
1823  // Advance the experimental frame counter upon restart, if necessary
1824  if (restart)
1825  {
1826  bool found_frame = false;
1827  while (!found_frame)
1828  {
1829  if (frame_time_stamps[0][experimental_frame_index + 1] < time)
1830  {
1831  experimental_frame_index++;
1832  }
1833  else
1834  {
1835  found_frame = true;
1836  }
1837  }
1838  }
1839  }
1840  }
1841 
1842  // ----- Output the initial solution -----
1843  std::unique_ptr<adamantine::MechanicalPhysics<
1844  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
1845  mechanical_physics;
1846  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>
1847  displacement;
1848  for (unsigned int member = 0; member < local_ensemble_size; ++member)
1849  {
1850  output_pvtu(*post_processor_ensemble[member], n_time_step, time,
1851  thermal_physics_ensemble[member],
1852  solution_augmented_ensemble[member].block(base_state),
1853  mechanical_physics, displacement,
1854  *material_properties_ensemble[member], timers);
1855  }
1856 
1857  // ----- Increment the time step -----
1858  ++n_time_step;
1859 
1860  // ----- Get refinement and time stepping parameters -----
1861  // PropertyTreeInput refinement.time_steps_between_refinement
1862  unsigned int const time_steps_refinement =
1863  refinement_database.get("time_steps_between_refinement", 10);
1864  // PropertyTreeInput time_stepping.time_step
1865  double time_step = time_stepping_database.get<double>("time_step");
1866  double const da_time_half_window = 1.01 * time_step;
1867  // PropertyTreeInput time_stepping.scan_path_for_duration
1868  bool const scan_path_for_duration =
1869  time_stepping_database.get("scan_path_for_duration", false);
1870  // PropertyTreeInput time_stepping.duration
1871  double const duration = scan_path_for_duration
1872  ? std::numeric_limits<double>::max()
1873  : time_stepping_database.get<double>("duration");
1874  // PropertyTreeInput post_processor.time_steps_between_output
1875  unsigned int const time_steps_output =
1876  post_processor_database.get("time_steps_between_output", 1);
1877  // PropertyTreeInput post_processor.output_on_data_assimilation
1878  bool const output_on_da =
1879  post_processor_database.get("output_on_data_assimilation", true);
1880 
1881  // ----- Deposit material -----
1882  // For now assume that all ensemble members share the same geometry (they
1883  // have independent adamantine::Geometry objects, but all are constructed
1884  // from identical parameters), base new additions on the 0th ensemble member
1885  // since all the sources use the same scan path.
1886  auto [material_deposition_boxes, deposition_times, deposition_cos,
1887  deposition_sin] =
1888  adamantine::create_material_deposition_boxes<dim>(
1889  geometry_database, heat_sources_ensemble[0]);
1890 
1891  // ----- Compute bounding heat sources -----
1892  // When using AMR, we refine the cells that the heat sources intersect. Since
1893  // each ensemble members can have slightly different sources, we create a new
1894  // set of heat sources that encompasses the member sources. We use these new
1895  // sources when refining the mesh.
1896  auto bounding_heat_sources = adamantine::get_bounding_heat_sources<dim>(
1897  database_ensemble, global_communicator);
1898 
1899  // ----- Main time stepping loop -----
1900  if (global_rank == 0)
1901  std::cout << "Starting the main time stepping loop..." << std::endl;
1902 
1903 #ifdef ADAMANTINE_WITH_CALIPER
1904  CALI_CXX_MARK_LOOP_BEGIN(main_loop_id, "main_loop");
1905 #endif
1906  while (time < duration)
1907  {
1908 #ifdef ADAMANTINE_WITH_CALIPER
1909  CALI_CXX_MARK_LOOP_ITERATION(main_loop_id, n_time_step - 1);
1910 #endif
1911  if ((time + time_step) > duration)
1912  time_step = duration - time;
1913 
1914  // ----- Refine the mesh if necessary -----
1915  // Refine the mesh the first time we get in the loop and after
1916  // time_steps_refinement time steps.
1917  if ((n_time_step == 1) || ((n_time_step % time_steps_refinement) == 0))
1918  {
1919  timers[adamantine::refine].start();
1920  double const next_refinement_time =
1921  time + time_steps_refinement * time_step;
1922 
1923  for (unsigned int member = 0; member < local_ensemble_size; ++member)
1924  {
1925  // FIXME
1926  std::unique_ptr<adamantine::MechanicalPhysics<
1927  dim, n_materials, p_order, MaterialStates, MemorySpaceType>>
1928  dummy;
1929  refine_mesh(thermal_physics_ensemble[member], dummy,
1930  *material_properties_ensemble[member],
1931  solution_augmented_ensemble[member].block(base_state),
1932  bounding_heat_sources, time, next_refinement_time,
1933  time_steps_refinement, refinement_database);
1934  solution_augmented_ensemble[member].collect_sizes();
1935  }
1936 
1937  timers[adamantine::refine].stop();
1938  if ((global_rank == 0) && (verbose_output == true))
1939  {
1940  std::cout << "n_time_step: " << n_time_step << " time: " << time
1941  << " n_dofs: "
1942  << thermal_physics_ensemble[0]->get_dof_handler().n_dofs()
1943  << std::endl;
1944  }
1945  }
1946 
1947  // We use an epsilon to get the "expected" behavior when the deposition
1948  // time and the time match should match exactly but don't because of
1949  // floating point accuracy.
1950  timers[adamantine::add_material_activate].start();
1951  if (time > activation_time_end)
1952  {
1953  // If we use scan_path_for_duration, we may need to read the scan path
1954  // file once again.
1955  if (scan_path_for_duration)
1956  {
1957  // Check if we have reached the end of current scan path.
1958  bool need_updated_scan_path = false;
1959  {
1960  for (auto &source : heat_sources_ensemble[0])
1961  {
1962  if (time >
1963  source->get_scan_path().get_segment_list().back().end_time)
1964  {
1965  need_updated_scan_path = true;
1966  break;
1967  }
1968  }
1969  }
1970 
1971  if (need_updated_scan_path)
1972  {
1973  // Check if we have reached the end of the file. If not, read the
1974  // updated scan path file. We assume that the scan paths are identical
1975  // for all the heat sources and thus, we can use
1976  // heat_sources_ensemble[0] to get the material deposition boxes and
1977  // times. We still need every ensemble member to read the scan path in
1978  // order to compute the correct heat sources.
1979  bool scan_path_end = true;
1980  for (unsigned int member = 0; member < local_ensemble_size; ++member)
1981  {
1982  for (auto &source : heat_sources_ensemble[member])
1983  {
1984  if (!source->get_scan_path().is_finished())
1985  {
1986  scan_path_end = false;
1987  // This functions waits for the scan path file to be updated
1988  // before reading the file.
1989  source->get_scan_path().read_file();
1990  }
1991  }
1992  }
1993 
1994  // If we have reached the end of scan path file for all the heat
1995  // sources, we just exit.
1996  if (scan_path_end)
1997  {
1998  break;
1999  }
2000 
2001  std::tie(material_deposition_boxes, deposition_times, deposition_cos,
2002  deposition_sin) =
2003  adamantine::create_material_deposition_boxes<dim>(
2004  geometry_database, heat_sources_ensemble[0]);
2005  }
2006  }
2007 
2008  double const eps = time_step / 1e10;
2009  auto activation_start =
2010  std::lower_bound(deposition_times.begin(), deposition_times.end(),
2011  time - eps) -
2012  deposition_times.begin();
2013  activation_time_end =
2014  std::min(time + std::max(activation_time, time_step), duration) - eps;
2015  auto activation_end =
2016  std::lower_bound(deposition_times.begin(), deposition_times.end(),
2017  activation_time_end) -
2018  deposition_times.begin();
2019  if (activation_start < activation_end)
2020  {
2021 #ifdef ADAMANTINE_WITH_CALIPER
2022  CALI_MARK_BEGIN("add material");
2023 #endif
2024  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2025  {
2026  // Compute the elements to activate.
2027  // TODO Right now, we compute the list of cells that get activated
2028  // for the entire material deposition. We should restrict the list
2029  // to the cells that are activated between activation_start and
2030  // activation_end.
2031  timers[adamantine::add_material_search].start();
2032  auto elements_to_activate = adamantine::get_elements_to_activate(
2033  *geometry_ensemble[member],
2034  thermal_physics_ensemble[member]->get_dof_handler(),
2035  material_deposition_boxes);
2036  timers[adamantine::add_material_search].stop();
2037  // For now assume that all deposited material has never been
2038  // melted (may or may not be reasonable)
2039  std::vector<bool> has_melted(deposition_cos.size(), false);
2040 
2041  thermal_physics_ensemble[member]->add_material_start(
2042  elements_to_activate, deposition_cos, deposition_sin, has_melted,
2043  activation_start, activation_end,
2044  solution_augmented_ensemble[member].block(base_state));
2045 
2046 #ifdef ADAMANTINE_WITH_CALIPER
2047  CALI_MARK_BEGIN("refine triangulation");
2048 #endif
2049  dealii::DoFHandler<dim> &dof_handler =
2050  thermal_physics_ensemble[member]->get_dof_handler();
2051  dealii::parallel::distributed::Triangulation<dim> &triangulation =
2052  dynamic_cast<dealii::parallel::distributed::Triangulation<dim> &>(
2053  const_cast<dealii::Triangulation<dim> &>(
2054  dof_handler.get_triangulation()));
2055 
2056  triangulation.execute_coarsening_and_refinement();
2057 #ifdef ADAMANTINE_WITH_CALIPER
2058  CALI_MARK_END("refine triangulation");
2059 #endif
2060 
2061  thermal_physics_ensemble[member]->add_material_end(
2062  database_ensemble[member].get(
2063  "materials.new_material_temperature", 300.),
2064  solution_augmented_ensemble[member].block(base_state));
2065 
2066  solution_augmented_ensemble[member].collect_sizes();
2067  }
2068 #ifdef ADAMANTINE_WITH_CALIPER
2069  CALI_MARK_END("add material");
2070 #endif
2071  }
2072 
2073  if ((global_rank == 0) && (verbose_output == true) &&
2074  (activation_end - activation_start > 0))
2075  {
2076  std::cout << "n_time_step: " << n_time_step << " time: " << time
2077  << " n_dofs after cell activation: "
2078  << thermal_physics_ensemble[0]->get_dof_handler().n_dofs()
2079  << std::endl;
2080  }
2081  }
2082  timers[adamantine::add_material_activate].stop();
2083 
2084  // ----- Evolve the solution by one time step -----
2085  double const old_time = time;
2086  timers[adamantine::evol_time].start();
2087 
2088  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2089  {
2090  time = thermal_physics_ensemble[member]->evolve_one_time_step(
2091  old_time, time_step,
2092  solution_augmented_ensemble[member].block(base_state), timers);
2093  }
2094  timers[adamantine::evol_time].stop();
2095 
2096  // ----- Perform data assimilation -----
2097  if (assimilate_data)
2098  {
2099  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2100  {
2101  thermal_physics_ensemble[member]->get_affine_constraints().distribute(
2102  solution_augmented_ensemble[member].block(base_state));
2103  }
2104 
2105  // Currently assume that all frames are synced so that the 0th camera
2106  // frame time is the relevant time
2107  double frame_time = std::numeric_limits<double>::max();
2108  if ((experimental_frame_index + 1) < frame_time_stamps[0].size())
2109  {
2110  if (time > da_time + da_time_half_window)
2111  {
2112  da_time = frame_time_stamps[0][experimental_frame_index + 1];
2113  }
2114  if (frame_time_stamps[0][experimental_frame_index + 1] <= time)
2115  {
2116  experimental_frame_index = experimental_data->read_next_frame();
2117  frame_time = frame_time_stamps[0][experimental_frame_index];
2118  }
2119  }
2120 
2121  if (frame_time <= time)
2122  {
2124  frame_time > old_time || n_time_step == 1,
2125  "Unexpectedly missed a data assimilation frame.");
2126  if (global_rank == 0)
2127  std::cout << "Performing data assimilation at time " << time << "..."
2128  << std::endl;
2129 
2130  // Print out the augmented parameters
2131  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2132  {
2133  std::cout << "Rank: " << global_rank
2134  << " | Old parameters for member "
2135  << first_local_member + member << ": ";
2136  for (auto param : solution_augmented_ensemble[member].block(1))
2137  std::cout << param << " ";
2138 
2139  std::cout << std::endl;
2140  }
2141 
2142  timers[adamantine::da_experimental_data].start();
2143 #ifdef ADAMANTINE_WITH_CALIPER
2144  CALI_MARK_BEGIN("da_experimental_data");
2145 #endif
2146  auto points_values = experimental_data->get_points_values();
2147  auto const &thermal_dof_handler =
2148  thermal_physics_ensemble[0]->get_dof_handler();
2149  auto expt_to_dof_mapping = adamantine::get_expt_to_dof_mapping(
2150  points_values, thermal_dof_handler);
2151  std::cout << "Rank: " << global_rank
2152  << " | Number expt sites mapped to DOFs: "
2153  << expt_to_dof_mapping.first.size() << std::endl;
2154 #ifdef ADAMANTINE_WITH_CALIPER
2155  CALI_MARK_END("da_experimental_data");
2156 #endif
2157  timers[adamantine::da_experimental_data].stop();
2158 
2159  if (post_processor_expt)
2160  {
2161  dealii::LA::distributed::Vector<double, MemorySpaceType>
2162  temperature_expt;
2163  temperature_expt.reinit(
2164  solution_augmented_ensemble[0].block(base_state));
2165  temperature_expt.add(1.0e10);
2167  global_communicator, points_values, expt_to_dof_mapping,
2168  temperature_expt, verbose_output);
2169 
2170  thermal_physics_ensemble[0]->get_affine_constraints().distribute(
2171  temperature_expt);
2172  post_processor_expt
2173  ->template write_thermal_output<typename Kokkos::View<
2174  double **,
2175  typename MemorySpaceType::kokkos_space>::array_layout>(
2176  n_time_step, time, temperature_expt,
2177  material_properties_ensemble[0]->get_state(),
2178  material_properties_ensemble[0]->get_dofs_map(),
2179  material_properties_ensemble[0]->get_dof_handler());
2180  }
2181 
2182  // Only continue data assimilation if some of the observations are
2183  // mapped to DOFs
2184  if (expt_to_dof_mapping.first.size() > 0)
2185  {
2186  // NOTE: As is, this updates the dof mapping and covariance sparsity
2187  // pattern for every data assimilation operation. Strictly, this is
2188  // only necessary if the mesh changes (both updates) or the locations
2189  // of observations changes (the dof mapping). In practice, changes to
2190  // the mesh due to deposition likely cause the updates to be required
2191  // for each operation. If this is a bottleneck, it can be fixed in the
2192  // future.
2193  timers[adamantine::da_dof_mapping].start();
2194 #ifdef ADAMANTINE_WITH_CALIPER
2195  CALI_MARK_BEGIN("da_dof_mapping");
2196 #endif
2197  data_assimilator.update_dof_mapping<dim>(expt_to_dof_mapping);
2198 #ifdef ADAMANTINE_WITH_CALIPER
2199  CALI_MARK_END("da_dof_mapping");
2200 #endif
2201  timers[adamantine::da_dof_mapping].stop();
2202 
2203  timers[adamantine::da_covariance_sparsity].start();
2204 #ifdef ADAMANTINE_WITH_CALIPER
2205  CALI_MARK_BEGIN("da_covariance_sparsity");
2206 #endif
2207  data_assimilator.update_covariance_sparsity_pattern<dim>(
2208  thermal_dof_handler,
2209  solution_augmented_ensemble[0].block(augmented_state).size());
2210 #ifdef ADAMANTINE_WITH_CALIPER
2211  CALI_MARK_END("da_covariance_sparsity");
2212 #endif
2213  timers[adamantine::da_covariance_sparsity].start();
2214 
2215  unsigned int experimental_data_size = points_values.values.size();
2216 
2217  // Create the R matrix (the observation covariance matrix)
2218  // PropertyTreeInput experiment.estimated_uncertainty
2219  timers[adamantine::da_obs_covariance].start();
2220 #ifdef ADAMANTINE_WITH_CALIPER
2221  CALI_MARK_BEGIN("da_obs_covariance");
2222 #endif
2223  double variance_entries = experiment_optional_database.get().get(
2224  "estimated_uncertainty", 0.0);
2225  variance_entries = variance_entries * variance_entries;
2226 
2227  dealii::SparsityPattern pattern(experimental_data_size,
2228  experimental_data_size, 1);
2229  for (unsigned int i = 0; i < experimental_data_size; ++i)
2230  {
2231  pattern.add(i, i);
2232  }
2233  pattern.compress();
2234 
2235  dealii::SparseMatrix<double> R(pattern);
2236  for (unsigned int i = 0; i < experimental_data_size; ++i)
2237  {
2238  R.add(i, i, variance_entries);
2239  }
2240 #ifdef ADAMANTINE_WITH_CALIPER
2241  CALI_MARK_END("da_obs_covariance");
2242 #endif
2243  timers[adamantine::da_obs_covariance].stop();
2244 
2245  // Perform data assimilation to update the augmented state ensemble
2246  timers[adamantine::da_update_ensemble].start();
2247 #ifdef ADAMANTINE_WITH_CALIPER
2248  CALI_MARK_BEGIN("da_update_ensemble");
2249 #endif
2250  data_assimilator.update_ensemble(solution_augmented_ensemble,
2251  points_values.values, R);
2252 #ifdef ADAMANTINE_WITH_CALIPER
2253  CALI_MARK_END("da_update_ensemble");
2254 #endif
2255  timers[adamantine::da_update_ensemble].stop();
2256 
2257  // Extract the parameters from the augmented state
2258  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2259  {
2260  for (unsigned int index = 0;
2261  index < augmented_state_parameters.size(); ++index)
2262  {
2263  // FIXME: Need to consider how we want to generalize this. It
2264  // could get unwieldy if we want to specify every parameter of an
2265  // arbitrary number of beams.
2266  if (augmented_state_parameters.at(index) ==
2268  {
2269  database_ensemble[member].put(
2270  "sources.beam_0.absorption_efficiency",
2271  solution_augmented_ensemble[member].block(
2272  augmented_state)[index]);
2273  }
2274  else if (augmented_state_parameters.at(index) ==
2276  {
2277  database_ensemble[member].put(
2278  "sources.beam_0.max_power",
2279  solution_augmented_ensemble[member].block(
2280  augmented_state)[index]);
2281  }
2282  }
2283  }
2284 
2285  if (global_rank == 0)
2286  std::cout << "Done." << std::endl;
2287 
2288  // Print out the augmented parameters
2289  if (solution_augmented_ensemble[0].block(1).size() > 0)
2290  {
2291  for (unsigned int member = 0; member < local_ensemble_size;
2292  ++member)
2293  {
2294  std::cout << "Rank: " << global_rank
2295  << " | New parameters for member "
2296  << first_local_member + member << ": ";
2297  for (auto param : solution_augmented_ensemble[member].block(1))
2298  std::cout << param << " ";
2299 
2300  std::cout << std::endl;
2301  }
2302  }
2303  }
2304  else
2305  {
2306  if (global_rank == 0)
2307  std::cout
2308  << "WARNING: NO EXPERIMENTAL DATA POINTS MAPPED ONTO THE "
2309  "SIMULATION MESH. SKIPPING DATA ASSIMILATION OPERATION."
2310  << std::endl;
2311  }
2312  }
2313 
2314  // Update the heat source in the ThermalPhysics objects
2315  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2316  {
2317  thermal_physics_ensemble[member]->update_physics_parameters(
2318  database_ensemble[member].get_child("sources"));
2319  }
2320  }
2321 
2322  // ----- Checkpoint the ensemble members -----
2323  if (n_time_step % time_steps_checkpoint == 0)
2324  {
2325 #ifdef ADAMANTINE_WITH_CALIPER
2326  CALI_MARK_BEGIN("save checkpoint");
2327 #endif
2328  if (global_rank == 0)
2329  {
2330  std::cout << "Checkpoint reached" << std::endl;
2331  }
2332 
2333  std::string output_dir =
2334  post_processor_database.get<std::string>("output_dir", "");
2335  std::string filename_prefix =
2336  checkpoint_overwrite
2337  ? checkpoint_filename
2338  : checkpoint_filename + '_' + std::to_string(n_time_step);
2339  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2340  {
2341  thermal_physics_ensemble[member]->save_checkpoint(
2342  filename_prefix + '_' + std::to_string(first_local_member + member),
2343  solution_augmented_ensemble[member].block(base_state));
2344  }
2345  std::ofstream file{output_dir + filename_prefix + "_time.txt"};
2346  boost::archive::text_oarchive oa{file};
2347  oa << time;
2348  oa << n_time_step;
2349 #ifdef ADAMANTINE_WITH_CALIPER
2350  CALI_MARK_END("save checkpoint");
2351 #endif
2352  }
2353 
2354  // ----- Output progress on screen -----
2355  if (global_rank == 0)
2356  {
2357  double adim_time = time / (duration / 10.);
2358  double int_part = 0;
2359  std::modf(adim_time, &int_part);
2360  if (int_part > progress)
2361  {
2362  std::cout << int_part * 10 << '%' << " completed" << std::endl;
2363  ++progress;
2364  }
2365  }
2366 
2367  // ----- Output the solution -----
2368  if ((n_time_step % time_steps_output == 0) ||
2369  (output_on_da && time > (da_time - da_time_half_window) &&
2370  time < (da_time + da_time_half_window)))
2371  {
2372  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2373  {
2374  thermal_physics_ensemble[member]->set_state_to_material_properties();
2375  output_pvtu(*post_processor_ensemble[member], n_time_step, time,
2376  thermal_physics_ensemble[member],
2377  solution_augmented_ensemble[member].block(base_state),
2378  mechanical_physics, displacement,
2379  *material_properties_ensemble[member], timers);
2380  }
2381  }
2382 
2383  ++n_time_step;
2384  }
2385 
2386 #ifdef ADAMANTINE_WITH_CALIPER
2387  CALI_CXX_MARK_LOOP_END(main_loop_id);
2388 #endif
2389 
2390  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2391  {
2392  post_processor_ensemble[member]->write_pvd();
2393  }
2394 
2395  // This is only used for integration test
2396  if constexpr (std::is_same_v<MemorySpaceType, dealii::MemorySpace::Host>)
2397  {
2398  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2399  {
2400  thermal_physics_ensemble[member]->get_affine_constraints().distribute(
2401  solution_augmented_ensemble[member].block(base_state));
2402  }
2403  return solution_augmented_ensemble;
2404  }
2405  else
2406  {
2407  // NOTE: Currently unused. Added for the future case where run_ensemble is
2408  // functional on the device.
2409  std::vector<dealii::LA::distributed::BlockVector<double>>
2410  solution_augmented_ensemble_host(local_ensemble_size);
2411 
2412  for (unsigned int member = 0; member < local_ensemble_size; ++member)
2413  {
2414  solution_augmented_ensemble[member].reinit(2);
2415 
2416  solution_augmented_ensemble_host[member].block(0).reinit(
2417  solution_augmented_ensemble[member].block(0).get_partitioner());
2418 
2419  solution_augmented_ensemble_host[member].block(0).import_elements(
2420  solution_augmented_ensemble[member].block(0),
2421  dealii::VectorOperation::insert);
2422  thermal_physics_ensemble[member]->get_affine_constraints().distribute(
2423  solution_augmented_ensemble_host[member].block(0));
2424 
2425  solution_augmented_ensemble_host[member].block(1).reinit(
2426  solution_augmented_ensemble[member].block(0).get_partitioner());
2427 
2428  solution_augmented_ensemble_host[member].block(1).import_elements(
2429  solution_augmented_ensemble[member].block(1),
2430  dealii::VectorOperation::insert);
2431  }
2432 
2433  return solution_augmented_ensemble_host;
2434  }
2435 }
2436 #endif
std::vector< dealii::LA::distributed::BlockVector< double > > run_ensemble(MPI_Comm const &global_communicator, boost::property_tree::ptree const &database, std::vector< adamantine::Timer > &timers)
Definition: adamantine.hh:1465
std::vector< typename dealii::parallel::distributed::Triangulation< dim >::active_cell_iterator > compute_cells_to_refine(dealii::parallel::distributed::Triangulation< dim > &triangulation, double const time, double const next_refinement_time, unsigned int const n_time_steps, std::vector< std::shared_ptr< adamantine::HeatSource< dim >>> const &heat_sources)
Definition: adamantine.hh:543
void refine_and_transfer(std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType >> &thermal_physics, std::unique_ptr< adamantine::MechanicalPhysics< dim, n_materials, p_order, MaterialStates, MemorySpaceType >> &mechanical_physics, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > &material_properties, dealii::DoFHandler< dim > &dof_handler, dealii::LA::distributed::Vector< double, MemorySpaceType > &solution)
Definition: adamantine.hh:313
void split_global_communicator(MPI_Comm global_communicator, unsigned int global_ensemble_size, MPI_Comm &local_communicator, unsigned int &local_ensemble_size, unsigned int &first_local_member, int &my_color)
Definition: adamantine.hh:1409
std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType > > initialize_thermal_physics(unsigned int fe_degree, std::string const &quadrature_type, MPI_Comm const &communicator, boost::property_tree::ptree const &database, adamantine::Geometry< dim > &geometry, adamantine::Boundary const &boundary, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > &material_properties)
Definition: adamantine.hh:262
std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType > > initialize(MPI_Comm const &communicator, boost::property_tree::ptree const &database, adamantine::Geometry< dim > &geometry, adamantine::Boundary const &boundary, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > &material_properties)
Definition: adamantine.hh:223
void output_pvtu(adamantine::PostProcessor< dim > &post_processor, unsigned int n_time_step, double time, std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType >> const &thermal_physics, dealii::LinearAlgebra::distributed::Vector< double, MemorySpaceType > &temperature, std::unique_ptr< adamantine::MechanicalPhysics< dim, n_materials, p_order, MaterialStates, MemorySpaceType >> const &mechanical_physics, dealii::LA::distributed::Vector< double, dealii::MemorySpace::Host > &displacement, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > const &material_properties, std::vector< adamantine::Timer > &timers)
Definition: adamantine.hh:68
void refine_mesh(std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType >> &thermal_physics, std::unique_ptr< adamantine::MechanicalPhysics< dim, n_materials, p_order, MaterialStates, MemorySpaceType >> &mechanical_physics, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > &material_properties, dealii::LA::distributed::Vector< double, MemorySpaceType > &solution, std::vector< std::shared_ptr< adamantine::HeatSource< dim >>> const &heat_sources, double const time, double const next_refinement_time, unsigned int const time_steps_refinement, boost::property_tree::ptree const &refinement_database)
Definition: adamantine.hh:610
void initialize_timers(MPI_Comm const &communicator, std::vector< adamantine::Timer > &timers)
Definition: adamantine.hh:194
std::pair< dealii::LinearAlgebra::distributed::Vector< double, dealii::MemorySpace::Host >, dealii::LinearAlgebra::distributed::Vector< double, dealii::MemorySpace::Host > > run(MPI_Comm const &communicator, boost::property_tree::ptree const &database, std::vector< adamantine::Timer > &timers)
Definition: adamantine.hh:755
std::unique_ptr< adamantine::ThermalPhysicsInterface< dim, MemorySpaceType > > initialize_quadrature(std::string const &quadrature_type, MPI_Comm const &communicator, boost::property_tree::ptree const &database, adamantine::Geometry< dim > &geometry, adamantine::Boundary const &boundary, adamantine::MaterialProperty< dim, n_materials, p_order, MaterialStates, MemorySpaceType > &material_properties)
Definition: adamantine.hh:238
void update_dof_mapping(std::pair< std::vector< int >, std::vector< int >> const &expt_to_dof_mapping)
void update_covariance_sparsity_pattern(dealii::DoFHandler< dim > const &dof_handler, const unsigned int parameter_size)
void update_ensemble(std::vector< dealii::LA::distributed::BlockVector< double >> &augmented_state_ensemble, std::vector< double > const &expt_data, dealii::SparseMatrix< double > const &R)
dealii::parallel::distributed::Triangulation< dim > & get_triangulation()
Definition: Geometry.hh:116
double get(dealii::types::material_id material_id, Property prop) const
std::pair< std::vector< int >, std::vector< int > > get_expt_to_dof_mapping(PointsValues< dim > const &points_values, dealii::DoFHandler< dim > const &dof_handler)
@ output
Definition: types.hh:146
@ evol_time
Definition: types.hh:143
@ da_obs_covariance
Definition: types.hh:141
@ da_experimental_data
Definition: types.hh:138
@ da_dof_mapping
Definition: types.hh:139
@ add_material_search
Definition: types.hh:136
@ da_covariance_sparsity
Definition: types.hh:140
@ da_update_ensemble
Definition: types.hh:142
@ refine
Definition: types.hh:135
@ add_material_activate
Definition: types.hh:137
std::vector< boost::property_tree::ptree > create_database_ensemble(boost::property_tree::ptree const &database, MPI_Comm local_communicator, unsigned int first_local_member, unsigned int local_ensemble_size, unsigned int global_ensemble_size)
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
void set_with_experimental_data(MPI_Comm const &communicator, PointsValues< dim > const &points_values, std::pair< std::vector< int >, std::vector< int >> &expt_to_dof_mapping, dealii::LinearAlgebra::distributed::Vector< double > &temperature, bool verbose_output)
std::vector< std::vector< double > > read_frame_timestamps(boost::property_tree::ptree const &experiment_database)
#define ASSERT(condition, message)
Definition: utils.hh:68