ExaDG
Loading...
Searching...
No Matches
restart.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef EXADG_TIME_INTEGRATION_RESTART_H_
23#define EXADG_TIME_INTEGRATION_RESTART_H_
24
25// C/C++
26#include <boost/archive/binary_iarchive.hpp>
27#include <boost/archive/binary_oarchive.hpp>
28#include <boost/archive/text_iarchive.hpp>
29#include <boost/archive/text_oarchive.hpp>
30#include <fstream>
31
32// deal.II
33#include <deal.II/distributed/fully_distributed_tria.h>
34#include <deal.II/distributed/tria.h>
35#include <deal.II/grid/tria.h>
36#include <deal.II/lac/la_parallel_block_vector.h>
37#include <deal.II/lac/la_parallel_vector.h>
38#include <deal.II/numerics/solution_transfer.h>
39
40// ExaDG
41#include <exadg/grid/grid_utilities.h>
42#include <exadg/grid/mapping_dof_vector.h>
43#include <exadg/utilities/create_directories.h>
44
45namespace ExaDG
46{
47inline std::string
48restart_filename(std::string const & name, MPI_Comm const & mpi_comm)
49{
50 std::string const rank =
51 dealii::Utilities::int_to_string(dealii::Utilities::MPI::this_mpi_process(mpi_comm));
52
53 std::string const filename = name + "." + rank + ".restart";
54
55 return filename;
56}
57
58inline void
59rename_restart_files(std::string const & filename)
60{
61 // backup: rename current restart file into restart.old in case something fails while writing
62 std::string const from = filename;
63 std::string const to = filename + ".old";
64
65 std::ifstream ifile(from.c_str());
66 if((bool)ifile) // rename only if file already exists
67 {
68 int const error = rename(from.c_str(), to.c_str());
69
70 AssertThrow(error == 0, dealii::ExcMessage("Can not rename file: " + from + " -> " + to));
71 }
72}
73
74inline void
75write_restart_file(std::ostringstream & oss, std::string const & filename)
76{
77 std::ofstream stream(filename.c_str());
78
79 stream << oss.str() << std::endl;
80}
81
82template<typename VectorType>
83inline void
84print_vector_l2_norm(VectorType const & vector)
85{
86 MPI_Comm const & mpi_comm = vector.get_mpi_communicator();
87 double const l2_norm = vector.l2_norm();
88 if(dealii::Utilities::MPI::this_mpi_process(mpi_comm) == 0)
89 {
90 std::cout << " vector global l2 norm: " << std::scientific << std::setprecision(8)
91 << std::setw(20) << l2_norm << "\n";
92 }
93}
94
102template<typename VectorType, typename BoostArchiveType>
103inline void
104read_write_distributed_vector(VectorType & vector, BoostArchiveType & archive)
105{
106 // Print vector norm here only *before* writing.
107 if(std::is_same<BoostArchiveType, boost::archive::text_oarchive>::value or
108 std::is_same<BoostArchiveType, boost::archive::binary_oarchive>::value)
109 {
110 print_vector_l2_norm(vector);
111 }
112
113 // Depending on VectorType, we have to loop over the blocks to
114 // access the local entries via vector.local_element(i).
115 using Number = typename VectorType::value_type;
116 if constexpr(std::is_same<std::remove_cv_t<VectorType>,
117 dealii::LinearAlgebra::distributed::Vector<Number>>::value)
118 {
119 for(unsigned int i = 0; i < vector.locally_owned_size(); ++i)
120 {
121 archive & vector.local_element(i);
122 }
123 }
124 else if constexpr(std::is_same<std::remove_cv_t<VectorType>,
125 dealii::LinearAlgebra::distributed::BlockVector<Number>>::value)
126 {
127 for(unsigned int i = 0; i < vector.n_blocks(); ++i)
128 {
129 for(unsigned int j = 0; j < vector.block(i).locally_owned_size(); ++j)
130 {
131 archive & vector.block(i).local_element(j);
132 }
133 }
134 }
135 else
136 {
137 AssertThrow(false, dealii::ExcMessage("Reading into this VectorType not supported."));
138 }
139
140 // Print vector norm here only *after* reading.
141 if(std::is_same<BoostArchiveType, boost::archive::text_iarchive>::value or
142 std::is_same<BoostArchiveType, boost::archive::binary_iarchive>::value)
143 {
144 print_vector_l2_norm(vector);
145 }
146}
147
153template<typename VectorType, typename BlockVectorType>
154std::vector<std::vector<VectorType *>>
155get_vectors_per_block(std::vector<BlockVectorType *> const & block_vectors)
156{
157 unsigned int const n_blocks = block_vectors.at(0)->n_blocks();
158 for(unsigned int i = 0; i < block_vectors.size(); ++i)
159 {
160 AssertThrow(block_vectors[i]->n_blocks() == n_blocks,
161 dealii::ExcMessage("Provided number of blocks per "
162 "BlockVector must be equal."));
163 }
164
165 std::vector<std::vector<VectorType *>> vectors_per_block;
166 for(unsigned int i = 0; i < n_blocks; ++i)
167 {
168 std::vector<VectorType *> vectors;
169 for(unsigned int j = 0; j < block_vectors.size(); ++j)
170 {
171 vectors.push_back(&block_vectors[j]->block(i));
172 }
173 vectors_per_block.push_back(vectors);
174 }
175
176 return vectors_per_block;
177}
178
184template<int dim, typename BlockVectorType>
185std::vector<BlockVectorType>
187 unsigned int const n_vectors,
188 std::vector<dealii::DoFHandler<dim, dim> const *> const & dof_handlers)
189{
190 unsigned int const n_blocks = dof_handlers.size();
191
192 // Setup first `BlockVector`
193 BlockVectorType block_vector(n_blocks);
194 for(unsigned int i = 0; i < n_blocks; ++i)
195 {
196 block_vector.block(i).reinit(dof_handlers[i]->locally_owned_dofs(),
197 dof_handlers[i]->get_mpi_communicator());
198 }
199 block_vector.collect_sizes();
200
201 std::vector<BlockVectorType> block_vectors(n_vectors, block_vector);
202
203 return block_vectors;
204}
205
206template<typename VectorType>
207std::vector<bool>
208get_ghost_state(std::vector<VectorType *> const & vectors)
209{
210 std::vector<bool> has_ghost_elements(vectors.size());
211 for(unsigned int i = 0; i < has_ghost_elements.size(); ++i)
212 {
213 has_ghost_elements[i] = vectors[i]->has_ghost_elements();
214 }
215 return has_ghost_elements;
216}
217
218template<typename VectorType>
219void
220set_ghost_state(std::vector<VectorType *> const & vectors,
221 std::vector<bool> const & had_ghost_elements)
222{
223 AssertThrow(vectors.size() == had_ghost_elements.size(),
224 dealii::ExcMessage("Vector sizes do not match."));
225
226 for(unsigned int i = 0; i < had_ghost_elements.size(); ++i)
227 {
228 if(had_ghost_elements[i])
229 {
230 vectors[i]->update_ghost_values();
231 }
232 else
233 {
234 vectors[i]->zero_out_ghost_values();
235 }
236 }
237}
238
244template<int dim, typename TriangulationType>
245inline void
246save_coarse_triangulation(std::string const & directory,
247 std::string const & filename_base,
248 TriangulationType const & triangulation)
249{
250 if constexpr(std::is_same<std::remove_cv_t<TriangulationType>,
251 dealii::parallel::distributed::Triangulation<dim, dim>>::value or
252 std::is_same<std::remove_cv_t<TriangulationType>,
253 dealii::parallel::fullydistributed::Triangulation<dim, dim>>::value)
254 {
255 AssertThrow(false,
256 dealii::ExcMessage("Only TriangulationType::Serial, i.e., "
257 "dealii::Triangulation<dim> supported."));
258 }
259
260 MPI_Comm const & mpi_comm = triangulation.get_mpi_communicator();
261
262 // Create folder if not existent.
263 create_directories(directory, mpi_comm);
264
265 std::string const filename = directory + filename_base + ".coarse_triangulation";
266 if(dealii::Utilities::MPI::this_mpi_process(mpi_comm) == 0)
267 {
268 // Serialization only creates a single file, move with one process only.
269 rename_restart_files(filename + ".info");
270 rename_restart_files(filename + "_triangulation.data");
271
272 // For `dealii::Triangulation` the triangulation is the same for all processes.
273 triangulation.save(filename);
274 }
275}
276
286template<int dim, typename VectorType>
287inline void
289 std::string const & directory,
290 std::string const & filename_base,
291 std::vector<std::vector<VectorType const *>> const & vectors_per_dof_handler,
292 std::vector<dealii::DoFHandler<dim, dim> const *> const & dof_handlers)
293{
294 AssertThrow(vectors_per_dof_handler.size() > 0,
295 dealii::ExcMessage("No vectors to store in triangulation."));
296 AssertThrow(vectors_per_dof_handler.size() == dof_handlers.size(),
297 dealii::ExcMessage("Number of vectors of vectors and DoFHandlers do not match."));
298 auto const & triangulation = dof_handlers.at(0)->get_triangulation();
299
300 // Check if all the `Triangulation(s)` associated with the DoFHandlers point to the same object.
301 for(unsigned int i = 1; i < dof_handlers.size(); ++i)
302 {
303 AssertThrow(&dof_handlers[i]->get_triangulation() == &triangulation,
304 dealii::ExcMessage("Triangulations of DoFHandlers are not identical."));
305 }
306
307 // Loop over the `DoFHandlers` and store the vectors in the triangulation.
308 std::vector<std::shared_ptr<dealii::SolutionTransfer<dim, VectorType>>> solution_transfers;
309 std::vector<std::vector<bool>> has_ghost_elements_per_dof_handler;
310 for(unsigned int i = 0; i < dof_handlers.size(); ++i)
311 {
312 // Store ghost state.
313 std::vector<bool> has_ghost_elements = get_ghost_state(vectors_per_dof_handler[i]);
314 has_ghost_elements_per_dof_handler.push_back(has_ghost_elements);
315 for(unsigned int j = 0; j < vectors_per_dof_handler[i].size(); ++j)
316 {
317 vectors_per_dof_handler[i][j]->update_ghost_values();
318 print_vector_l2_norm(*vectors_per_dof_handler[i][j]);
319 }
320
321 solution_transfers.push_back(
322 std::make_shared<dealii::SolutionTransfer<dim, VectorType>>(*dof_handlers[i]));
323 solution_transfers[i]->prepare_for_serialization(vectors_per_dof_handler[i]);
324 }
325
326 // Serialize the triangulation keeping a maximum of two snapshots.
327 std::string const filename = directory + filename_base + ".triangulation";
328 MPI_Comm const & mpi_comm = dof_handlers[0]->get_mpi_communicator();
329 if(dealii::Utilities::MPI::this_mpi_process(mpi_comm) == 0)
330 {
331 // Serialization only creates a single file, move with one process only.
332 rename_restart_files(filename);
333 rename_restart_files(filename + ".info");
334 rename_restart_files(filename + "_fixed.data");
335 rename_restart_files(filename + "_triangulation.data");
336 }
337
338 // Collective call for serialization, general case requires ghosted vectors.
339 triangulation.save(filename);
340
341 // Recover ghost state.
342 for(unsigned int i = 0; i < dof_handlers.size(); ++i)
343 {
344 set_ghost_state(vectors_per_dof_handler[i], has_ghost_elements_per_dof_handler[i]);
345 }
346}
347
353template<int dim, typename VectorType>
354inline void
356 std::string const & directory,
357 std::string const & filename_base,
358 std::vector<std::vector<VectorType const *>> const & vectors_per_dof_handler,
359 std::vector<dealii::DoFHandler<dim, dim> const *> const & dof_handlers,
360 dealii::Mapping<dim> const & mapping,
361 dealii::DoFHandler<dim> const * dof_handler_mapping,
362 unsigned int const mapping_degree)
363{
364 AssertThrow(vectors_per_dof_handler.size() > 0,
365 dealii::ExcMessage("No vectors to store in triangulation."));
366 AssertThrow(vectors_per_dof_handler.size() == dof_handlers.size(),
367 dealii::ExcMessage("Number of vectors of vectors and DoFHandlers do not match."));
368
369 auto const & triangulation = dof_handlers.at(0)->get_triangulation();
370
371 // Check if all the `Triangulation(s)` associated with the `DoFHandlers` point to the same object.
372 for(unsigned int i = 1; i < dof_handlers.size(); ++i)
373 {
374 AssertThrow(&dof_handlers[i]->get_triangulation() == &triangulation,
375 dealii::ExcMessage("Triangulations of DoFHandlers are not identical."));
376 }
377
378 AssertThrow(triangulation.all_reference_cells_are_hyper_cube(),
379 dealii::ExcMessage("Serialization including mapping not "
380 "supported for non-hypercube cell types."));
381
382 // Initialize vector to hold grid coordinates.
383 bool vector_initialized = false;
384 VectorType vector_grid_coordinates;
385 for(unsigned int i = 0; i < dof_handlers.size(); ++i)
386 {
387 if(dof_handlers[i] == dof_handler_mapping and not vector_initialized)
388 {
389 // Cheaper setup if we already have a vector given in the input arguments.
390 vector_grid_coordinates.reinit(*vectors_per_dof_handler[i][0],
391 true /* omit_zeroing_entries */);
392 vector_initialized = true;
393 break;
394 }
395 }
396
397 if(not vector_initialized)
398 {
399 // More expensive setup extracting the `dealii::IndexSet`.
400 dealii::IndexSet const & locally_owned_dofs = dof_handler_mapping->locally_owned_dofs();
401 dealii::IndexSet const locally_relevant_dofs =
402 dealii::DoFTools::extract_locally_relevant_dofs(*dof_handler_mapping);
403 vector_grid_coordinates.reinit(locally_owned_dofs,
404 locally_relevant_dofs,
405 dof_handler_mapping->get_mpi_communicator());
406 }
407
408 // Fill vector with mapping.
409 MappingDoFVector<dim, typename VectorType::value_type> mapping_dof_vector(mapping_degree);
410 mapping_dof_vector.fill_grid_coordinates_vector(mapping,
411 vector_grid_coordinates,
412 *dof_handler_mapping);
413
414 // Attach vector holding mapping and corresponding `dof_handler_mapping`.
415 std::vector<std::vector<VectorType const *>> vectors_per_dof_handler_extended =
416 vectors_per_dof_handler;
417 std::vector<VectorType const *> tmp = {&vector_grid_coordinates};
418 vectors_per_dof_handler_extended.push_back(tmp);
419
420 std::vector<dealii::DoFHandler<dim, dim> const *> dof_handlers_extended = dof_handlers;
421 dof_handlers_extended.push_back(dof_handler_mapping);
422
423 // Use utility function that ignores the mapping.
425 filename_base,
426 vectors_per_dof_handler_extended,
427 dof_handlers_extended);
428}
429
433template<int dim>
434inline std::shared_ptr<dealii::Triangulation<dim>>
435deserialize_triangulation(std::string const & directory,
436 std::string const & filename_base,
437 TriangulationType const triangulation_type,
438 MPI_Comm const & mpi_communicator)
439{
440 std::shared_ptr<dealii::Triangulation<dim>> triangulation_old;
441
442 std::string const filename = directory + filename_base;
443
444 // Deserialize the checkpointed triangulation,
445 if(triangulation_type == TriangulationType::Serial)
446 {
447 triangulation_old = std::make_shared<dealii::Triangulation<dim>>();
448 triangulation_old->load(filename + ".triangulation");
449 }
450 else if(triangulation_type == TriangulationType::Distributed)
451 {
452 // Deserialize the coarse triangulation to be stored by the user
453 // during `create_grid` in the respective application.
454 dealii::Triangulation<dim, dim> coarse_triangulation;
455 try
456 {
457 coarse_triangulation.load(filename + ".coarse_triangulation");
458 }
459 catch(...)
460 {
461 AssertThrow(false,
462 dealii::ExcMessage(
463 "Deserializing coarse triangulation expected in\n" + filename +
464 ".coarse_triangulation\n"
465 "make sure to store the coarse grid during `create_grid`\n"
466 "in the respective application.h using TriangulationType::Serial."));
467 }
468
469 std::shared_ptr<dealii::parallel::distributed::Triangulation<dim>> tmp =
470 std::make_shared<dealii::parallel::distributed::Triangulation<dim>>(mpi_communicator);
471
472 tmp->copy_triangulation(coarse_triangulation);
473 coarse_triangulation.clear();
474
475 // We do not need manifolds when applying the refinements, since we recover the mapping
476 // separately.
477 tmp->reset_all_manifolds();
478 tmp->set_all_manifold_ids(dealii::numbers::flat_manifold_id);
479 tmp->load(filename + ".triangulation");
480
481 triangulation_old = std::dynamic_pointer_cast<dealii::Triangulation<dim>>(tmp);
482 }
483 else if(triangulation_type == TriangulationType::FullyDistributed)
484 {
485 // Note that the number of MPI processes the triangulation was
486 // saved with cannot change and hence autopartitioning is disabled.
487 std::shared_ptr<dealii::parallel::fullydistributed::Triangulation<dim>> tmp =
488 std::make_shared<dealii::parallel::fullydistributed::Triangulation<dim>>(mpi_communicator);
489 tmp->load(filename + ".triangulation");
490
491 triangulation_old = std::dynamic_pointer_cast<dealii::Triangulation<dim>>(tmp);
492 }
493 else
494 {
495 AssertThrow(false, dealii::ExcMessage("TriangulationType not supported."));
496 }
497
498 return triangulation_old;
499}
500
506template<int dim, typename VectorType>
507inline void
508load_vectors(std::vector<std::vector<VectorType *>> & vectors_per_dof_handler,
509 std::vector<dealii::DoFHandler<dim, dim> const *> const & dof_handlers)
510{
511 // The `dof_handlers` and `vectors_per_dof_handler` are already initialized and
512 // `vectors_per_dof_handler` contain only owned DoFs.
513 AssertThrow(vectors_per_dof_handler.size() > 0,
514 dealii::ExcMessage("No vectors to load into from triangulation."));
515 AssertThrow(vectors_per_dof_handler.size() == dof_handlers.size(),
516 dealii::ExcMessage("Number of vectors of vectors and DoFHandlers do not match."));
517
518 // Loop over the DoFHandlers and load the vectors stored in
519 // the triangulation the DoFHandlers were initialized with.
520 for(unsigned int i = 0; i < dof_handlers.size(); ++i)
521 {
522 dealii::SolutionTransfer<dim, VectorType> solution_transfer(*dof_handlers[i]);
523
524 // Reinit vectors that do not already have ghost entries.
525 bool all_ghosted = false;
526 for(unsigned int j = 0; j < vectors_per_dof_handler[i].size(); ++j)
527 {
528 if(not vectors_per_dof_handler[i][j]->has_ghost_elements())
529 {
530 all_ghosted = false;
531 break;
532 }
533 }
534 if(not all_ghosted)
535 {
536 dealii::IndexSet const & locally_owned_dofs = dof_handlers[i]->locally_owned_dofs();
537 dealii::IndexSet const locally_relevant_dofs =
538 dealii::DoFTools::extract_locally_relevant_dofs(*dof_handlers[i]);
539 for(unsigned int j = 0; j < vectors_per_dof_handler[i].size(); ++j)
540 {
541 if(not vectors_per_dof_handler[i][j]->has_ghost_elements())
542 {
543 vectors_per_dof_handler[i][j]->reinit(locally_owned_dofs,
544 locally_relevant_dofs,
545 dof_handlers[i]->get_mpi_communicator());
546 }
547 }
548 }
549
550 solution_transfer.deserialize(vectors_per_dof_handler[i]);
551
552 for(unsigned int j = 0; j < vectors_per_dof_handler[i].size(); ++j)
553 {
554 print_vector_l2_norm(*vectors_per_dof_handler[i][j]);
555 }
556 }
557}
558
563template<int dim, typename VectorType>
564inline std::shared_ptr<MappingDoFVector<dim, typename VectorType::value_type>>
565load_vectors(std::vector<std::vector<VectorType *>> & vectors_per_dof_handler,
566 std::vector<dealii::DoFHandler<dim, dim> const *> const & dof_handlers,
567 dealii::DoFHandler<dim> const * dof_handler_mapping,
568 unsigned int const mapping_degree)
569{
570 // We need a collective call to `SolutionTransfer::deserialize()` with all vectors in a
571 // single container. Hence, create a mapping vector and add a pointer to the input argument.
572 dealii::IndexSet const & locally_owned_dofs = dof_handler_mapping->locally_owned_dofs();
573 VectorType vector_grid_coordinates(locally_owned_dofs,
574 dof_handler_mapping->get_mpi_communicator());
575
576 // Standard utility function, sequence as in `store_vectors_in_triangulation_and_serialize()`.
577 std::vector<std::vector<VectorType *>> vectors_per_dof_handler_extended = vectors_per_dof_handler;
578 std::vector<VectorType *> tmp = {&vector_grid_coordinates};
579 vectors_per_dof_handler_extended.push_back(tmp);
580 std::vector<dealii::DoFHandler<dim, dim> const *> dof_handlers_extended = dof_handlers;
581 dof_handlers_extended.push_back(dof_handler_mapping);
582
583 load_vectors(vectors_per_dof_handler_extended, dof_handlers_extended);
584
585 // Reconstruct the mapping given the deserialized grid coordinate vector.
586 std::shared_ptr<MappingDoFVector<dim, typename VectorType::value_type>> mapping_dof_vector =
587 std::make_shared<MappingDoFVector<dim, typename VectorType::value_type>>(mapping_degree);
588 mapping_dof_vector->initialize_mapping_from_dof_vector(nullptr /* mapping */,
589 vector_grid_coordinates,
590 *dof_handler_mapping);
591 return mapping_dof_vector;
592}
593
594} // namespace ExaDG
595
596#endif /* EXADG_TIME_INTEGRATION_RESTART_H_ */
Definition mapping_dof_vector.h:54
void fill_grid_coordinates_vector(VectorType &grid_coordinates, dealii::DoFHandler< dim > const &dof_handler) const
Definition mapping_dof_vector.h:105
Definition driver.cpp:33
void save_coarse_triangulation(std::string const &directory, std::string const &filename_base, TriangulationType const &triangulation)
Definition restart.h:246
void store_vectors_in_triangulation_and_serialize(std::string const &directory, std::string const &filename_base, std::vector< std::vector< VectorType const * > > const &vectors_per_dof_handler, std::vector< dealii::DoFHandler< dim, dim > const * > const &dof_handlers)
Definition restart.h:288
std::vector< BlockVectorType > get_block_vectors_from_dof_handlers(unsigned int const n_vectors, std::vector< dealii::DoFHandler< dim, dim > const * > const &dof_handlers)
Definition restart.h:186
void read_write_distributed_vector(VectorType &vector, BoostArchiveType &archive)
Definition restart.h:104
std::vector< std::vector< VectorType * > > get_vectors_per_block(std::vector< BlockVectorType * > const &block_vectors)
Definition restart.h:155
std::shared_ptr< dealii::Triangulation< dim > > deserialize_triangulation(std::string const &directory, std::string const &filename_base, TriangulationType const triangulation_type, MPI_Comm const &mpi_communicator)
Definition restart.h:435
void load_vectors(std::vector< std::vector< VectorType * > > &vectors_per_dof_handler, std::vector< dealii::DoFHandler< dim, dim > const * > const &dof_handlers)
Definition restart.h:508
void create_directories(std::string const &directory, MPI_Comm const &mpi_comm)
Definition create_directories.h:35