ryujin 2.1.1 revision 0348cbb53a3e4b1da2a4c037e81f88f2d21ce219
time_loop.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2024 by the ryujin authors
4//
5
6#pragma once
7
8#include "scope.h"
9#include "solution_transfer.h"
10#include "state_vector.h"
11#include "time_loop.h"
12#include "version_info.h"
13
14#include <deal.II/base/logstream.h>
15#include <deal.II/base/work_stream.h>
16#include <deal.II/distributed/solution_transfer.h>
17#include <deal.II/numerics/vector_tools.h>
18#include <deal.II/numerics/vector_tools.templates.h>
19
20#include <filesystem>
21#include <fstream>
22#include <iomanip>
23
24using namespace dealii;
25
26namespace ryujin
27{
28 template <typename Description, int dim, typename Number>
30 : ParameterAcceptor("/A - TimeLoop")
31 , mpi_ensemble_(mpi_comm)
32 , hyperbolic_system_("/B - Equation")
33 , parabolic_system_("/B - Equation")
34 , discretization_(mpi_ensemble_, "/C - Discretization")
35 , offline_data_(mpi_ensemble_, discretization_, "/D - OfflineData")
36 , initial_values_(hyperbolic_system_, offline_data_, "/E - InitialValues")
37 , hyperbolic_module_(mpi_ensemble_,
38 computing_timer_,
39 offline_data_,
40 hyperbolic_system_,
41 initial_values_,
42 "/F - HyperbolicModule")
43 , parabolic_module_(mpi_ensemble_,
44 computing_timer_,
45 offline_data_,
46 hyperbolic_system_,
47 parabolic_system_,
48 initial_values_,
49 "/G - ParabolicModule")
50 , time_integrator_(mpi_ensemble_,
51 offline_data_,
52 hyperbolic_module_,
53 parabolic_module_,
54 "/H - TimeIntegrator")
55 , mesh_adaptor_(mpi_ensemble_,
56 offline_data_,
57 hyperbolic_system_,
58 parabolic_system_,
59 hyperbolic_module_.initial_precomputed(),
60 hyperbolic_module_.alpha(),
61 "/I - MeshAdaptor")
62 , postprocessor_(mpi_ensemble_,
63 offline_data_,
64 hyperbolic_system_,
65 parabolic_system_,
66 "/J - VTUOutput")
67 , vtu_output_(mpi_ensemble_,
68 offline_data_,
69 hyperbolic_system_,
70 parabolic_system_,
71 postprocessor_,
72 hyperbolic_module_.initial_precomputed(),
73 hyperbolic_module_.alpha(),
74 "/J - VTUOutput")
75 , quantities_(mpi_ensemble_,
76 offline_data_,
77 hyperbolic_system_,
78 parabolic_system_,
79 "/K - Quantities")
80 {
81 base_name_ = "test";
82 add_parameter("basename", base_name_, "Base name for all output files");
83
84 t_final_ = Number(5.);
85 add_parameter("final time", t_final_, "Final time");
86
87 enforce_t_final_ = false;
88 add_parameter("enforce final time",
89 enforce_t_final_,
90 "Boolean indicating whether the final time should be "
91 "enforced strictly. If set to true the last time step is "
92 "shortened so that the simulation ends precisely at t_final");
93
94 timer_granularity_ = Number(0.01);
95 add_parameter("timer granularity",
96 timer_granularity_,
97 "The timer granularity specifies the time interval after "
98 "which compute, output, postprocessing, and mesh adaptation "
99 "routines are run. This \"baseline tick\" is further "
100 "modified by the corresponding \"*_multiplier\" options");
101
102 enable_checkpointing_ = false;
103 add_parameter(
104 "enable checkpointing",
105 enable_checkpointing_,
106 "Write out checkpoints to resume an interrupted computation at timer "
107 "granularity intervals. The frequency is determined by \"timer "
108 "granularity\" and \"timer checkpoint multiplier\"");
109
110 enable_output_full_ = false;
111 add_parameter("enable output full",
112 enable_output_full_,
113 "Write out full pvtu records. The frequency is determined by "
114 "\"timer granularity\" and \"timer output full multiplier\"");
115
116 enable_output_levelsets_ = false;
117 add_parameter(
118 "enable output levelsets",
119 enable_output_levelsets_,
120 "Write out levelsets pvtu records. The frequency is determined by "
121 "\"timer granularity\" and \"timer output levelsets multiplier\"");
122
123 enable_compute_error_ = false;
124 add_parameter("enable compute error",
125 enable_compute_error_,
126 "Flag to control whether we compute the Linfty Linf_norm of "
127 "the difference to an analytic solution. Implemented only "
128 "for certain initial state configurations.");
129
130 enable_compute_quantities_ = false;
131 add_parameter(
132 "enable compute quantities",
133 enable_compute_quantities_,
134 "Flag to control whether we compute quantities of interest. The "
135 "frequency how often quantities are logged is determined by \"timer "
136 "granularity\" and \"timer compute quantities multiplier\"");
137
138 enable_mesh_adaptivity_ = false;
139 add_parameter(
140 "enable mesh adaptivity",
141 enable_mesh_adaptivity_,
142 "Flag to control whether we use an adaptive mesh refinement strategy. "
143 "The frequency how often we query MeshAdaptor::analyze() for deciding "
144 "on adapting the mesh is determined by \"timer granularity\" and "
145 "\"timer mesh refinement multiplier\"");
146
147 timer_checkpoint_multiplier_ = 1;
148 add_parameter("timer checkpoint multiplier",
149 timer_checkpoint_multiplier_,
150 "Multiplicative modifier applied to \"timer granularity\" "
151 "that determines the checkpointing granularity");
152
153 timer_output_full_multiplier_ = 1;
154 add_parameter("timer output full multiplier",
155 timer_output_full_multiplier_,
156 "Multiplicative modifier applied to \"timer granularity\" "
157 "that determines the full pvtu writeout granularity");
158
159 timer_output_levelsets_multiplier_ = 1;
160 add_parameter("timer output levelsets multiplier",
161 timer_output_levelsets_multiplier_,
162 "Multiplicative modifier applied to \"timer granularity\" "
163 "that determines the levelsets pvtu writeout granularity");
164
165 timer_compute_quantities_multiplier_ = 1;
166 add_parameter(
167 "timer compute quantities multiplier",
168 timer_compute_quantities_multiplier_,
169 "Multiplicative modifier applied to \"timer granularity\" that "
170 "determines the writeout granularity for quantities of interest");
171
172 std::copy(std::begin(View::component_names),
173 std::end(View::component_names),
174 std::back_inserter(error_quantities_));
175
176 add_parameter("error quantities",
177 error_quantities_,
178 "List of conserved quantities used in the computation of the "
179 "error norms.");
180
181 error_normalize_ = true;
182 add_parameter("error normalize",
183 error_normalize_,
184 "Flag to control whether the error should be normalized by "
185 "the corresponding norm of the analytic solution.");
186
187 resume_ = false;
188 add_parameter("resume", resume_, "Resume an interrupted computation");
189
190 resume_at_time_zero_ = false;
191 add_parameter("resume at time zero",
192 resume_at_time_zero_,
193 "Resume from the latest checkpoint but set the time to t=0.");
194
195 terminal_update_interval_ = 5;
196 add_parameter("terminal update interval",
197 terminal_update_interval_,
198 "Number of seconds after which output statistics are "
199 "recomputed and printed on the terminal");
200
201 terminal_show_rank_throughput_ = true;
202 add_parameter("terminal show rank throughput",
203 terminal_show_rank_throughput_,
204 "If set to true an average per rank throughput is computed "
205 "by dividing the total consumed CPU time (per rank) by the "
206 "number of threads (per rank). If set to false then a plain "
207 "average per thread \"CPU\" throughput value is computed by "
208 "using the umodified total accumulated CPU time.");
209
210 debug_filename_ = "";
211 add_parameter("debug filename",
212 debug_filename_,
213 "If set to a nonempty string then we output the contents of "
214 "this file at the end. This is mainly useful in the "
215 "testsuite to output files we wish to compare");
216 }
217
218
219 template <typename Description, int dim, typename Number>
221 {
222#ifdef DEBUG_OUTPUT
223 std::cout << "TimeLoop<dim, Number>::run()" << std::endl;
224#endif
225
226 {
227 mpi_ensemble_.prepare();
228
229 base_name_ensemble_ = base_name_;
230 if (mpi_ensemble_.n_ensembles() > 1) {
231 print_info("setting up MPI ensemble");
232 base_name_ensemble_ += "-ensemble_" + dealii::Utilities::int_to_string(
233 mpi_ensemble_.ensemble(),
234 mpi_ensemble_.n_ensembles());
235 }
236 }
237
238 /* Attach log file and record runtime parameters: */
239
240 if (mpi_ensemble_.world_rank() == 0)
241 logfile_.open(base_name_ + ".log");
242
243 print_parameters(logfile_);
244
245 /*
246 * Prepare data structures:
247 */
248
249 Number t = 0.;
250 unsigned int timer_cycle = 0;
251 StateVector state_vector;
252
253 /* Create a small lambda for preparing compute kernels: */
254 const auto prepare_compute_kernels = [&]() {
255 print_info("preparing compute kernels");
256
257 unsigned int n_parabolic_state_vectors =
258 parabolic_system_.n_parabolic_state_vectors();
259
260 offline_data_.prepare(
261 problem_dimension, n_precomputed_values, n_parabolic_state_vectors);
262
263 hyperbolic_module_.prepare();
264 parabolic_module_.prepare();
265 time_integrator_.prepare();
266 mesh_adaptor_.prepare(/*needs current timepoint*/ t);
267 postprocessor_.prepare();
268 vtu_output_.prepare();
269 quantities_.prepare(base_name_ensemble_);
270 print_mpi_partition(logfile_);
271
272 if (mpi_ensemble_.ensemble_rank() == 0)
273 n_global_dofs_ = dealii::Utilities::MPI::sum(
274 offline_data_.dof_handler().n_dofs(),
275 mpi_ensemble_.ensemble_leader_communicator());
276 };
277
278 {
279 Scope scope(computing_timer_, "(re)initialize data structures");
280 print_info("initializing data structures");
281
282 if (resume_) {
283 print_info("resume: reading mesh and loading state vector");
284
285 read_checkpoint(state_vector,
286 base_name_ensemble_,
287 t,
288 timer_cycle,
289 prepare_compute_kernels);
290
291 if (resume_at_time_zero_) {
292 /* Reset the current time t and the output cycle count to zero: */
293 t = 0.;
294 timer_cycle = 0;
295 }
296
297 } else {
298 print_info("creating mesh and interpolating initial values");
299
300 discretization_.prepare(base_name_ensemble_);
301
302 prepare_compute_kernels();
303
304 Vectors::reinit_state_vector<Description>(state_vector, offline_data_);
305 std::get<0>(state_vector) =
306 initial_values_.interpolate_hyperbolic_vector();
307 }
308 }
309
310 /*
311 * In debug mode poison constrained degrees of freedom and precomputed
312 * values:
313 */
314 Vectors::debug_poison_constrained_dofs<Description>(state_vector,
315 offline_data_);
316 Vectors::debug_poison_precomputed_values<Description>(state_vector,
317 offline_data_);
318
319 unsigned int cycle = 1;
320 Number last_terminal_output = (terminal_update_interval_ == Number(0.)
321 ? std::numeric_limits<Number>::max()
322 : std::numeric_limits<Number>::lowest());
323
324 /*
325 * The honorable main loop:
326 */
327
328 print_info("entering main loop");
329 computing_timer_["time loop"].start();
330
331 constexpr Number relax =
332 Number(1.) - Number(10.) * std::numeric_limits<Number>::epsilon();
333
334 for (;; ++cycle) {
335
336#ifdef DEBUG_OUTPUT
337 std::cout << "\n\n### cycle = " << cycle << " ###\n\n" << std::endl;
338#endif
339
340 /* Accumulate quantities of interest: */
341
342 if (enable_compute_quantities_) {
343 Scope scope(computing_timer_,
344 "time step [X] - accumulate quantities");
345 quantities_.accumulate(state_vector, t);
346 }
347
348 /* Perform various tasks whenever we reach a timer tick: */
349
350 if (t >= relax * timer_cycle * timer_granularity_) {
351 if (enable_compute_error_) {
352 StateVector analytic;
353 {
354 /*
355 * FIXME: We interpolate the analytic solution at every timer
356 * tick. If we happen to actually not output anything then this
357 * is terribly inefficient...
358 */
359 Scope scope(computing_timer_,
360 "time step [X] - interpolate analytic solution");
361 Vectors::reinit_state_vector<Description>(analytic, offline_data_);
362 std::get<0>(analytic) =
363 initial_values_.interpolate_hyperbolic_vector(t);
364 }
365
366 /*
367 * FIXME: a call to output() will also write a checkpoint (if
368 * enabled). So as a workaround we simply call the output()
369 * function for the analytic solution first...
370 */
371 output(analytic,
372 base_name_ensemble_ + "-analytic_solution",
373 t,
374 timer_cycle);
375 }
376
377 output(state_vector, base_name_ensemble_ + "-solution", t, timer_cycle);
378
379 if (enable_compute_quantities_ &&
380 (timer_cycle % timer_compute_quantities_multiplier_ == 0)) {
381 Scope scope(computing_timer_,
382 "time step [X] - write out quantities");
383 quantities_.write_out(state_vector, t, timer_cycle);
384 }
385
386 ++timer_cycle;
387 }
388
389 /* Break if we have reached the final time. */
390
391 if (t >= relax * t_final_)
392 break;
393
394 /* Peform a mesh adaptation cycle: */
395
396 if (enable_mesh_adaptivity_) {
397 {
398 Scope scope(computing_timer_,
399 "time step [X] - analyze for mesh adaptation");
400
401 mesh_adaptor_.analyze(state_vector, t, cycle);
402 }
403
404 if (mesh_adaptor_.need_mesh_adaptation()) {
405 Scope scope(computing_timer_, "(re)initialize data structures");
406 print_info("performing mesh adaptation");
407
408 hyperbolic_module_.prepare_state_vector(state_vector, t);
409 adapt_mesh_and_transfer_state_vector(state_vector,
410 prepare_compute_kernels);
411 }
412 }
413
414 /* Perform a time step: */
415
416 const auto tau = time_integrator_.step(
417 state_vector,
418 t,
419 enforce_t_final_
420 ? std::min(t_final_, timer_cycle * timer_granularity_)
421 : std::numeric_limits<Number>::max());
422
423 t += tau;
424
425 /* Print and record cycle statistics: */
426 if (terminal_update_interval_ != Number(0.)) {
427
428 /* Do we need to update the log file? */
429 const bool write_to_log_file =
430 (t >= relax * timer_cycle * timer_granularity_);
431
432 /* Do we need to update the terminal? */
433 const auto wall_time = computing_timer_["time loop"].wall_time();
434 int update_terminal =
435 (wall_time >= last_terminal_output + terminal_update_interval_);
436
437 /* Broadcast boolean from rank 0 to all other ranks: */
438 const auto ierr = MPI_Bcast(&update_terminal,
439 1,
440 MPI_INT,
441 0,
442 mpi_ensemble_.world_communicator());
443 AssertThrowMPI(ierr);
444
445 if (write_to_log_file || update_terminal) {
446 print_cycle_statistics(
447 cycle, t, timer_cycle, /*logfile*/ write_to_log_file);
448 last_terminal_output = wall_time;
449 }
450 }
451 } /* end of loop */
452
453 /* We have actually performed one cycle less. */
454 --cycle;
455
456 computing_timer_["time loop"].stop();
457
458 if (terminal_update_interval_ != Number(0.)) {
459 /* Write final timing statistics to screen and logfile: */
460 print_cycle_statistics(
461 cycle, t, timer_cycle, /*logfile*/ true, /*final*/ true);
462 }
463
464 if (enable_compute_error_) {
465 /* Output final error: */
466 compute_error(state_vector, t);
467 }
468
469 if (mpi_ensemble_.world_rank() == 0 && debug_filename_ != "") {
470 std::ifstream f(debug_filename_);
471 if (f.is_open())
472 std::cout << f.rdbuf();
473 }
474
475#ifdef WITH_VALGRIND
476 CALLGRIND_DUMP_STATS;
477#endif
478 }
479
480
481 template <typename Description, int dim, typename Number>
482 template <typename Callable>
484 StateVector &state_vector,
485 const std::string &base_name,
486 Number &t,
487 unsigned int &output_cycle,
488 const Callable &prepare_compute_kernels)
489 {
490#ifdef DEBUG_OUTPUT
491 std::cout << "TimeLoop<dim, Number>::read_checkpoint()" << std::endl;
492#endif
493
494 AssertThrow(have_distributed_triangulation<dim>,
495 dealii::ExcMessage(
496 "read_checkpoint() is not implemented for "
497 "distributed::shared::Triangulation which we use in 1D"));
498
499 /*
500 * Initialize discretization, read in the mesh, and initialize everything:
501 */
502
503#if !DEAL_II_VERSION_GTE(9, 6, 0)
504 if constexpr (have_distributed_triangulation<dim>) {
505#endif
506 discretization_.refinement() = 0; /* do not refine */
507 discretization_.prepare(base_name);
508 discretization_.triangulation().load(base_name + "-checkpoint.mesh");
509#if !DEAL_II_VERSION_GTE(9, 6, 0)
510 }
511#endif
512
513 prepare_compute_kernels();
514
515 /*
516 * Read in and broadcast metadata:
517 */
518
519 std::string name = base_name + "-checkpoint";
520
521 unsigned int transfer_handle;
522 if (mpi_ensemble_.ensemble_rank() == 0) {
523 std::string meta = name + ".metadata";
524
525 std::ifstream file(meta, std::ios::binary);
526 boost::archive::binary_iarchive ia(file);
527 ia >> t >> output_cycle >> transfer_handle;
528 }
529
530 int ierr;
531 if constexpr (std::is_same_v<Number, double>)
532 ierr = MPI_Bcast(
533 &t, 1, MPI_DOUBLE, 0, mpi_ensemble_.ensemble_communicator());
534 else
535 ierr =
536 MPI_Bcast(&t, 1, MPI_FLOAT, 0, mpi_ensemble_.ensemble_communicator());
537 AssertThrowMPI(ierr);
538
539 ierr = MPI_Bcast(&output_cycle,
540 1,
541 MPI_UNSIGNED,
542 0,
543 mpi_ensemble_.ensemble_communicator());
544 AssertThrowMPI(ierr);
545
546 ierr = MPI_Bcast(&transfer_handle,
547 1,
548 MPI_UNSIGNED,
549 0,
550 mpi_ensemble_.ensemble_communicator());
551 AssertThrowMPI(ierr);
552
553 /*
554 * Now read in the state vector:
555 */
556
557 Vectors::reinit_state_vector<Description>(state_vector, offline_data_);
558
560 mpi_ensemble_,
561 /* we need write access: */ discretization_.triangulation(),
562 offline_data_,
563 hyperbolic_system_,
564 parabolic_system_);
565
566 solution_transfer.set_handle(transfer_handle);
567 solution_transfer.project(state_vector);
568 }
569
570
571 template <typename Description, int dim, typename Number>
573 const StateVector &state_vector,
574 const std::string &base_name,
575 const Number &t,
576 const unsigned int &output_cycle)
577 {
578#ifdef DEBUG_OUTPUT
579 std::cout << "TimeLoop<dim, Number>::write_checkpoint()" << std::endl;
580#endif
581
582 AssertThrow(have_distributed_triangulation<dim>,
583 dealii::ExcMessage(
584 "write_checkpoint() is not implemented for "
585 "distributed::shared::Triangulation which we use in 1D"));
586
587 /*
588 * Create SolutionTransfer object, attach state vector and write out:
589 */
590
592 mpi_ensemble_,
593 /* we need write access: */ discretization_.triangulation(),
594 offline_data_,
595 hyperbolic_system_,
596 parabolic_system_);
597
598 /* need hyperbolic_module.prepare_state_vector() prior to this call: */
599 solution_transfer.prepare_projection(state_vector);
600 const auto transfer_handle = solution_transfer.get_handle();
601
602 std::string name = base_name + "-checkpoint";
603
604 if (mpi_ensemble_.ensemble_rank() == 0) {
605 for (const std::string suffix :
606 {".mesh", ".mesh_fixed.data", ".mesh.info", ".metadata"})
607 if (std::filesystem::exists(name + suffix))
608 std::filesystem::rename(name + suffix, name + suffix + "~");
609 }
610
611#if !DEAL_II_VERSION_GTE(9, 6, 0)
612 if constexpr (have_distributed_triangulation<dim>) {
613#endif
614 const auto &triangulation = discretization_.triangulation();
615 triangulation.save(name + ".mesh");
616#if !DEAL_II_VERSION_GTE(9, 6, 0)
617 }
618#endif
619
620 /*
621 * Now, write out metadata on rank 0:
622 */
623
624 if (mpi_ensemble_.ensemble_rank() == 0) {
625 std::string meta = name + ".metadata";
626 std::ofstream file(meta, std::ios::binary | std::ios::trunc);
627 boost::archive::binary_oarchive oa(file);
628 oa << t << output_cycle << transfer_handle;
629 }
630
631 const int ierr = MPI_Barrier(mpi_ensemble_.ensemble_communicator());
632 AssertThrowMPI(ierr);
633 }
634
635
636 template <typename Description, int dim, typename Number>
637 template <typename Callable>
639 StateVector &state_vector, const Callable &prepare_compute_kernels)
640 {
641#ifdef DEBUG_OUTPUT
642 std::cout << "TimeLoop<dim, Number>::adapt_mesh_and_transfer_state_vector()"
643 << std::endl;
644#endif
645
646 AssertThrow(mpi_ensemble_.n_ensembles() == 1, dealii::ExcNotImplemented());
647
648 /*
649 * Mark cells for coarsening and refinement and set up triangulation:
650 */
651
652 auto &triangulation = discretization_.triangulation();
653 mesh_adaptor_.mark_cells_for_coarsening_and_refinement(triangulation);
654
655 triangulation.prepare_coarsening_and_refinement();
656
657 /*
658 * Set up SolutionTransfer:
659 */
660
662 mpi_ensemble_,
663 /* we need write access: */ discretization_.triangulation(),
664 offline_data_,
665 hyperbolic_system_,
666 parabolic_system_);
667
668 /* need hyperbolic_module.prepare_state_vector() prior to this call: */
669 solution_transfer.prepare_projection(state_vector);
670
671 /*
672 * Execute mesh adaptation and project old state to new state vector:
673 */
674
675 triangulation.execute_coarsening_and_refinement();
676 prepare_compute_kernels();
677
678 Vectors::reinit_state_vector<Description>(state_vector, offline_data_);
679 solution_transfer.project(state_vector);
680 }
681
682
683 template <typename Description, int dim, typename Number>
684 void
686 const Number t)
687 {
688#ifdef DEBUG_OUTPUT
689 std::cout << "TimeLoop<dim, Number>::compute_error()" << std::endl;
690#endif
691
692 hyperbolic_module_.prepare_state_vector(state_vector, t);
693
694 Vector<Number> difference_per_cell(
695 discretization_.triangulation().n_active_cells());
696
697 Number linf_norm = 0.;
698 Number l1_norm = 0;
699 Number l2_norm = 0;
700
701 const auto analytic_U = initial_values_.interpolate_hyperbolic_vector(t);
702 const auto &U = std::get<0>(state_vector);
703
704 ScalarVector analytic_component;
705 ScalarVector error_component;
706 analytic_component.reinit(offline_data_.scalar_partitioner());
707 error_component.reinit(offline_data_.scalar_partitioner());
708
709 /* Loop over all selected components: */
710 for (const auto &entry : error_quantities_) {
711 const auto &names = View::component_names;
712 const auto pos = std::find(std::begin(names), std::end(names), entry);
713 if (pos == std::end(names)) {
714 AssertThrow(
715 false,
716 dealii::ExcMessage("Unknown component name »" + entry + "«"));
717 __builtin_trap();
718 }
719
720 const auto index = std::distance(std::begin(names), pos);
721
722 analytic_U.extract_component(analytic_component, index);
723
724 /* Compute norms of analytic solution: */
725
726 Number linf_norm_analytic = 0.;
727 Number l1_norm_analytic = 0.;
728 Number l2_norm_analytic = 0.;
729
730 if (error_normalize_) {
731 linf_norm_analytic =
732 Utilities::MPI::max(analytic_component.linfty_norm(),
733 mpi_ensemble_.ensemble_communicator());
734
735 VectorTools::integrate_difference(
736 offline_data_.dof_handler(),
737 analytic_component,
738 Functions::ZeroFunction<dim, Number>(),
739 difference_per_cell,
740 QGauss<dim>(3),
741 VectorTools::L1_norm);
742
743 l1_norm_analytic =
744 Utilities::MPI::sum(difference_per_cell.l1_norm(),
745 mpi_ensemble_.ensemble_communicator());
746
747 VectorTools::integrate_difference(
748 offline_data_.dof_handler(),
749 analytic_component,
750 Functions::ZeroFunction<dim, Number>(),
751 difference_per_cell,
752 QGauss<dim>(3),
753 VectorTools::L2_norm);
754
755 l2_norm_analytic = Number(std::sqrt(
756 Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),
757 mpi_ensemble_.ensemble_communicator())));
758 }
759
760 /* Compute norms of error: */
761
762 U.extract_component(error_component, index);
763 /* Populate constrained dofs due to periodicity: */
764 offline_data_.affine_constraints().distribute(error_component);
765 error_component.update_ghost_values();
766 error_component -= analytic_component;
767
768 const Number linf_norm_error = Utilities::MPI::max(
769 error_component.linfty_norm(), mpi_ensemble_.ensemble_communicator());
770
771 VectorTools::integrate_difference(offline_data_.dof_handler(),
772 error_component,
773 Functions::ZeroFunction<dim, Number>(),
774 difference_per_cell,
775 QGauss<dim>(3),
776 VectorTools::L1_norm);
777
778 const Number l1_norm_error = Utilities::MPI::sum(
779 difference_per_cell.l1_norm(), mpi_ensemble_.ensemble_communicator());
780
781 VectorTools::integrate_difference(offline_data_.dof_handler(),
782 error_component,
783 Functions::ZeroFunction<dim, Number>(),
784 difference_per_cell,
785 QGauss<dim>(3),
786 VectorTools::L2_norm);
787
788 const Number l2_norm_error = Number(std::sqrt(
789 Utilities::MPI::sum(std::pow(difference_per_cell.l2_norm(), 2),
790 mpi_ensemble_.ensemble_communicator())));
791
792 if (error_normalize_) {
793 linf_norm += linf_norm_error / linf_norm_analytic;
794 l1_norm += l1_norm_error / l1_norm_analytic;
795 l2_norm += l2_norm_error / l2_norm_analytic;
796 } else {
797 linf_norm += linf_norm_error;
798 l1_norm += l1_norm_error;
799 l2_norm += l2_norm_error;
800 }
801 }
802
803 if (mpi_ensemble_.ensemble_rank() != 0)
804 return;
805
806 /*
807 * Sum up over all participating MPI ranks. Note: we only perform this
808 * operation on "peer" ranks zero:
809 */
810
811 if (mpi_ensemble_.n_ensembles() > 1) {
812 linf_norm = Utilities::MPI::sum(
813 linf_norm, mpi_ensemble_.ensemble_leader_communicator());
814 l1_norm = Utilities::MPI::sum(
815 l1_norm, mpi_ensemble_.ensemble_leader_communicator());
816 l2_norm = Utilities::MPI::sum(
817 l2_norm, mpi_ensemble_.ensemble_leader_communicator());
818 }
819
820 if (mpi_ensemble_.world_rank() != 0)
821 return;
822
823 logfile_ << std::endl << "Computed errors:" << std::endl << std::endl;
824 logfile_ << std::setprecision(16);
825
826 std::string description =
827 error_normalize_ ? "Normalized consolidated" : "Consolidated";
828
829 logfile_ << description + " Linf, L1, and L2 errors at final time \n";
830 logfile_ << std::setprecision(16);
831 logfile_ << "#dofs = " << n_global_dofs_ << std::endl;
832 logfile_ << "t = " << t << std::endl;
833 logfile_ << "Linf = " << linf_norm << std::endl;
834 logfile_ << "L1 = " << l1_norm << std::endl;
835 logfile_ << "L2 = " << l2_norm << std::endl;
836
837 std::cout << description + " Linf, L1, and L2 errors at final time \n";
838 std::cout << std::setprecision(16);
839 std::cout << "#dofs = " << n_global_dofs_ << std::endl;
840 std::cout << "t = " << t << std::endl;
841 std::cout << "Linf = " << linf_norm << std::endl;
842 std::cout << "L1 = " << l1_norm << std::endl;
843 std::cout << "L2 = " << l2_norm << std::endl;
844 }
845
846
847 template <typename Description, int dim, typename Number>
849 const std::string &name,
850 const Number t,
851 const unsigned int cycle)
852 {
853#ifdef DEBUG_OUTPUT
854 std::cout << "TimeLoop<dim, Number>::output(t = " << t << ")" << std::endl;
855#endif
856
857 const bool do_full_output =
858 (cycle % timer_output_full_multiplier_ == 0) && enable_output_full_;
859 const bool do_levelsets =
860 (cycle % timer_output_levelsets_multiplier_ == 0) &&
861 enable_output_levelsets_;
862 const bool do_checkpointing =
863 (cycle % timer_checkpoint_multiplier_ == 0) && enable_checkpointing_;
864
865 /* There is nothing to do: */
866 if (!(do_full_output || do_levelsets || do_checkpointing))
867 return;
868
869 hyperbolic_module_.prepare_state_vector(state_vector, t);
870
871 /* Data output: */
872 if (do_full_output || do_levelsets) {
873 Scope scope(computing_timer_, "time step [X] - perform vtu output");
874 print_info("scheduling output");
875
876 postprocessor_.compute(state_vector);
877 /*
878 * Workaround: Manually reset bounds during the first output cycle
879 * (which is often just a uniform flow field) to obtain a better
880 * normailization:
881 */
882 if (cycle == 0)
883 postprocessor_.reset_bounds();
884
885 vtu_output_.schedule_output(
886 state_vector, name, t, cycle, do_full_output, do_levelsets);
887 }
888
889 /* Checkpointing: */
890 if (do_checkpointing) {
891 Scope scope(computing_timer_, "time step [X] - perform checkpointing");
892 print_info("scheduling checkpointing");
893 write_checkpoint(state_vector, base_name_ensemble_, t, cycle);
894 }
895 }
896
897
898 /*
899 * Output and logging related functions:
900 */
901
902
903 template <typename Description, int dim, typename Number>
904 void
906 {
907 if (mpi_ensemble_.world_rank() != 0)
908 return;
909
910 /* Output commit and library information: */
911
913
914 /* Print run time parameters: */
915
916 stream << std::endl << "Run time parameters:" << std::endl << std::endl;
917 ParameterAcceptor::prm.print_parameters(
918 stream, ParameterHandler::OutputStyle::ShortPRM);
919 stream << std::endl;
920
921 /* Also print out parameters to a prm file: */
922
923 std::ofstream output(base_name_ + "-parameters.prm");
924 ParameterAcceptor::prm.print_parameters(output, ParameterHandler::ShortPRM);
925 }
926
927
928 template <typename Description, int dim, typename Number>
929 void
931 {
932 /*
933 * Fixme: this conversion to double is really not elegant. We should
934 * improve the Utilities::MPI::min_max_avg function in deal.II to
935 * handle different data types
936 */
937
938 // NOLINTBEGIN
939 std::vector<double> values = {
940 (double)offline_data_.n_export_indices(),
941 (double)offline_data_.n_locally_internal(),
942 (double)offline_data_.n_locally_owned(),
943 (double)offline_data_.n_locally_relevant(),
944 (double)offline_data_.n_export_indices() /
945 (double)offline_data_.n_locally_relevant(),
946 (double)offline_data_.n_locally_internal() /
947 (double)offline_data_.n_locally_relevant(),
948 (double)offline_data_.n_locally_owned() /
949 (double)offline_data_.n_locally_relevant()};
950 // NOLINTEND
951
952 const auto data =
953 Utilities::MPI::min_max_avg(values, mpi_ensemble_.world_communicator());
954
955 if (mpi_ensemble_.world_rank() != 0)
956 return;
957
958 std::ostringstream output;
959
960 unsigned int n =
961 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
962
963 const auto print_snippet = [&output, n](const std::string &name,
964 const auto &values) {
965 output << name << ": ";
966 // NOLINTBEGIN
967 output << std::setw(9) << (unsigned int)values.min //
968 << " [p" << std::setw(n) << values.min_index << "] " //
969 << std::setw(9) << (unsigned int)values.avg << " " //
970 << std::setw(9) << (unsigned int)values.max //
971 << " [p" << std::setw(n) << values.max_index << "]"; //
972 // NOLINTEND
973 };
974
975 const auto print_percentages = [&output, n](const auto &percentages) {
976 output << std::endl << " ";
977 output << " (" << std::setw(3) << std::setprecision(2)
978 << percentages.min * 100 << "% )"
979 << " [p" << std::setw(n) << percentages.min_index << "] "
980 << " (" << std::setw(3) << std::setprecision(2)
981 << percentages.avg * 100 << "% )"
982 << " "
983 << " (" << std::setw(3) << std::setprecision(2)
984 << percentages.max * 100 << "% )"
985 << " [p" << std::setw(n) << percentages.max_index << "]";
986 };
987
988 output << std::endl << std::endl << "Partition: ";
989 print_snippet("exp", data[0]);
990 print_percentages(data[4]);
991
992 output << std::endl << " ";
993 print_snippet("int", data[1]);
994 print_percentages(data[5]);
995
996 output << std::endl << " ";
997 print_snippet("own", data[2]);
998 print_percentages(data[6]);
999
1000 output << std::endl << " ";
1001 print_snippet("rel", data[3]);
1002
1003 stream << output.str() << std::endl;
1004 }
1005
1006
1007 template <typename Description, int dim, typename Number>
1009 std::ostream &stream)
1010 {
1011 Utilities::System::MemoryStats stats;
1012 Utilities::System::get_memory_stats(stats);
1013
1014 Utilities::MPI::MinMaxAvg data = Utilities::MPI::min_max_avg(
1015 stats.VmRSS / 1024., mpi_ensemble_.world_communicator());
1016
1017 if (mpi_ensemble_.world_rank() != 0)
1018 return;
1019
1020 std::ostringstream output;
1021
1022 unsigned int n =
1023 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
1024
1025 output << "\nMemory: [MiB]" //
1026 << std::setw(8) << data.min //
1027 << " [p" << std::setw(n) << data.min_index << "] " //
1028 << std::setw(8) << data.avg << " " //
1029 << std::setw(8) << data.max //
1030 << " [p" << std::setw(n) << data.max_index << "]"; //
1031
1032 stream << output.str() << std::endl;
1033 }
1034
1035
1036 template <typename Description, int dim, typename Number>
1038 {
1039 std::vector<std::ostringstream> output(computing_timer_.size());
1040
1041 const auto equalize = [&]() {
1042 const auto ptr =
1043 std::max_element(output.begin(),
1044 output.end(),
1045 [](const auto &left, const auto &right) {
1046 return left.str().length() < right.str().length();
1047 });
1048 const auto length = ptr->str().length();
1049 for (auto &it : output)
1050 it << std::string(length - it.str().length() + 1, ' ');
1051 };
1052
1053 const auto print_wall_time = [&](auto &timer, auto &stream) {
1054 const auto wall_time = Utilities::MPI::min_max_avg(
1055 timer.wall_time(), mpi_ensemble_.world_communicator());
1056
1057 constexpr auto eps = std::numeric_limits<double>::epsilon();
1058 /*
1059 * Cut off at 99.9% to avoid silly percentages cluttering up the
1060 * output.
1061 */
1062 const auto skew_negative = std::max(
1063 100. * (wall_time.min - wall_time.avg) / wall_time.avg - eps, -99.9);
1064 const auto skew_positive = std::min(
1065 100. * (wall_time.max - wall_time.avg) / wall_time.avg + eps, 99.9);
1066
1067 stream << std::setprecision(2) << std::fixed << std::setw(8)
1068 << wall_time.avg << "s [sk: " << std::setprecision(1)
1069 << std::setw(5) << std::fixed << skew_negative << "%/"
1070 << std::setw(4) << std::fixed << skew_positive << "%]";
1071 unsigned int n =
1072 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
1073 stream << " [p" << std::setw(n) << wall_time.min_index << "/"
1074 << wall_time.max_index << "]";
1075 };
1076
1077 const auto cpu_time_statistics =
1078 Utilities::MPI::min_max_avg(computing_timer_["time loop"].cpu_time(),
1079 mpi_ensemble_.world_communicator());
1080 const double total_cpu_time = cpu_time_statistics.sum;
1081
1082 const auto print_cpu_time =
1083 [&](auto &timer, auto &stream, bool percentage) {
1084 const auto cpu_time = Utilities::MPI::min_max_avg(
1085 timer.cpu_time(), mpi_ensemble_.world_communicator());
1086
1087 stream << std::setprecision(2) << std::fixed << std::setw(9)
1088 << cpu_time.sum << "s ";
1089
1090 if (percentage)
1091 stream << "(" << std::setprecision(1) << std::setw(4)
1092 << 100. * cpu_time.sum / total_cpu_time << "%)";
1093 };
1094
1095 auto jt = output.begin();
1096 for (auto &it : computing_timer_)
1097 *jt++ << " " << it.first;
1098 equalize();
1099
1100 jt = output.begin();
1101 for (auto &it : computing_timer_)
1102 print_wall_time(it.second, *jt++);
1103 equalize();
1104
1105 jt = output.begin();
1106 bool compute_percentages = false;
1107 for (auto &it : computing_timer_) {
1108 print_cpu_time(it.second, *jt++, compute_percentages);
1109 if (it.first.starts_with("time loop"))
1110 compute_percentages = true;
1111 }
1112 equalize();
1113
1114 if (mpi_ensemble_.world_rank() != 0)
1115 return;
1116
1117 stream << std::endl << "Timer statistics:\n";
1118 for (auto &it : output)
1119 stream << it.str() << std::endl;
1120 }
1121
1122
1123 template <typename Description, int dim, typename Number>
1125 unsigned int cycle, Number t, std::ostream &stream, bool final_time)
1126 {
1127 /*
1128 * Fixme: The global state kept in this function should be refactored
1129 * into its own class object.
1130 */
1131 static struct Data {
1132 unsigned int cycle = 0;
1133 double t = 0.;
1134 double cpu_time_sum = 0.;
1135 double cpu_time_avg = 0.;
1136 double cpu_time_min = 0.;
1137 double cpu_time_max = 0.;
1138 double wall_time = 0.;
1139 } previous, current;
1140
1141 static double time_per_second_exp = 0.;
1142
1143 /* Update statistics: */
1144
1145 {
1146 previous = current;
1147
1148 current.cycle = cycle;
1149 current.t = t;
1150
1151 const auto wall_time_statistics =
1152 Utilities::MPI::min_max_avg(computing_timer_["time loop"].wall_time(),
1153 mpi_ensemble_.world_communicator());
1154 current.wall_time = wall_time_statistics.max;
1155
1156 const auto cpu_time_statistics =
1157 Utilities::MPI::min_max_avg(computing_timer_["time loop"].cpu_time(),
1158 mpi_ensemble_.world_communicator());
1159 current.cpu_time_sum = cpu_time_statistics.sum;
1160 current.cpu_time_avg = cpu_time_statistics.avg;
1161 current.cpu_time_min = cpu_time_statistics.min;
1162 current.cpu_time_max = cpu_time_statistics.max;
1163 }
1164
1165 if (final_time)
1166 previous = Data();
1167
1168 /* Take averages: */
1169
1170 double delta_cycles = current.cycle - previous.cycle;
1171 const double cycles_per_second =
1172 delta_cycles / (current.wall_time - previous.wall_time);
1173
1174 const auto efficiency = time_integrator_.efficiency();
1175 const auto n_dofs = static_cast<double>(n_global_dofs_);
1176
1177 const double wall_m_dofs_per_sec =
1178 delta_cycles * n_dofs / 1.e6 /
1179 (current.wall_time - previous.wall_time) * efficiency;
1180
1181 double cpu_m_dofs_per_sec = delta_cycles * n_dofs / 1.e6 /
1182 (current.cpu_time_sum - previous.cpu_time_sum) *
1183 efficiency;
1184#ifdef WITH_OPENMP
1185 if (terminal_show_rank_throughput_)
1186 cpu_m_dofs_per_sec *= MultithreadInfo::n_threads();
1187#endif
1188
1189 double cpu_time_skew = (current.cpu_time_max - current.cpu_time_min - //
1190 previous.cpu_time_max + previous.cpu_time_min) /
1191 delta_cycles;
1192 /* avoid printing small negative numbers: */
1193 cpu_time_skew = std::max(0., cpu_time_skew);
1194
1195 const double cpu_time_skew_percentage =
1196 cpu_time_skew * delta_cycles /
1197 (current.cpu_time_avg - previous.cpu_time_avg);
1198
1199 const double delta_time =
1200 (current.t - previous.t) / (current.cycle - previous.cycle);
1201 const double time_per_second =
1202 (current.t - previous.t) / (current.wall_time - previous.wall_time);
1203
1204 /* Print Jean-Luc and Martin metrics: */
1205
1206 std::ostringstream output;
1207
1208 /* clang-format off */
1209 output << std::endl;
1210
1211 output << "Throughput:\n "
1212 << (terminal_show_rank_throughput_? "RANK: " : "CPU : ")
1213 << std::setprecision(4) << std::fixed << cpu_m_dofs_per_sec
1214 << " MQ/s ("
1215 << std::scientific << 1. / cpu_m_dofs_per_sec * 1.e-6
1216 << " s/Qdof/substep)" << std::endl;
1217
1218 output << " [cpu time skew: "
1219 << std::setprecision(2) << std::scientific << cpu_time_skew
1220 << "s/cycle ("
1221 << std::setprecision(1) << std::setw(4) << std::setfill(' ') << std::fixed
1222 << 100. * cpu_time_skew_percentage
1223 << "%)]" << std::endl;
1224
1225 output << " WALL: "
1226 << std::setprecision(4) << std::fixed << wall_m_dofs_per_sec
1227 << " MQ/s ("
1228 << std::scientific << 1. / wall_m_dofs_per_sec * 1.e-6
1229 << " s/Qdof/substep) ("
1230 << std::setprecision(2) << std::fixed << cycles_per_second
1231 << " cycles/s)" << std::endl;
1232
1233 const auto &scheme = time_integrator_.time_stepping_scheme();
1234 output << " [ "
1235 << Patterns::Tools::Convert<TimeSteppingScheme>::to_string(scheme)
1236 << " with CFL = "
1237 << std::setprecision(2) << std::fixed << hyperbolic_module_.cfl()
1238 << " ("
1239 << std::setprecision(0) << std::fixed << hyperbolic_module_.n_restarts()
1240 << "/"
1241 << std::setprecision(0) << std::fixed << parabolic_module_.n_restarts()
1242 << " rsts) ("
1243 << std::setprecision(0) << std::fixed << hyperbolic_module_.n_warnings()
1244 << "/"
1245 << std::setprecision(0) << std::fixed << parabolic_module_.n_warnings()
1246 << " warn) ]" << std::endl;
1247
1248 if constexpr (!ParabolicSystem::is_identity)
1249 parabolic_module_.print_solver_statistics(output);
1250
1251 output << " [ dt = "
1252 << std::scientific << std::setprecision(2) << delta_time
1253 << " ( "
1254 << time_per_second
1255 << " dt/s) ]" << std::endl;
1256 /* clang-format on */
1257
1258 /* and print an ETA */
1259 time_per_second_exp = 0.8 * time_per_second_exp + 0.2 * time_per_second;
1260 auto eta = static_cast<unsigned int>(std::max(t_final_ - t, Number(0.)) /
1261 time_per_second_exp);
1262
1263 output << "\n ETA : ";
1264
1265 const unsigned int days = eta / (24 * 3600);
1266 if (days > 0) {
1267 output << days << " d ";
1268 eta %= 24 * 3600;
1269 }
1270
1271 const unsigned int hours = eta / 3600;
1272 if (hours > 0) {
1273 output << hours << " h ";
1274 eta %= 3600;
1275 }
1276
1277 const unsigned int minutes = eta / 60;
1278 output << minutes << " min";
1279
1280 if (mpi_ensemble_.world_rank() != 0)
1281 return;
1282
1283 stream << output.str() << std::endl;
1284 }
1285
1286
1287 template <typename Description, int dim, typename Number>
1289 {
1290 if (mpi_ensemble_.world_rank() != 0)
1291 return;
1292
1293 std::cout << "[INFO] " << header << std::endl;
1294 }
1295
1296
1297 template <typename Description, int dim, typename Number>
1298 void
1300 const std::string &secondary,
1301 std::ostream &stream)
1302 {
1303 if (mpi_ensemble_.world_rank() != 0)
1304 return;
1305
1306 const int header_size = header.size();
1307 const auto padded_header =
1308 std::string(std::max(0, 34 - header_size) / 2, ' ') + header +
1309 std::string(std::max(0, 35 - header_size) / 2, ' ');
1310
1311 const int secondary_size = secondary.size();
1312 const auto padded_secondary =
1313 std::string(std::max(0, 34 - secondary_size) / 2, ' ') + secondary +
1314 std::string(std::max(0, 35 - secondary_size) / 2, ' ');
1315
1316 /* clang-format off */
1317 stream << "\n";
1318 stream << " ####################################################\n";
1319 stream << " #########" << padded_header << "#########\n";
1320 stream << " #########" << padded_secondary << "#########\n";
1321 stream << " ####################################################\n";
1322 stream << std::endl;
1323 /* clang-format on */
1324 }
1325
1326
1327 template <typename Description, int dim, typename Number>
1329 unsigned int cycle,
1330 Number t,
1331 unsigned int timer_cycle,
1332 bool write_to_logfile,
1333 bool final_time)
1334 {
1335 static const std::string vectorization_name = [] {
1336 constexpr auto width = VectorizedArray<Number>::size();
1337
1338 std::string result;
1339 if (width == 1)
1340 result = "scalar ";
1341 else
1342 result = std::to_string(width * 8 * sizeof(Number)) + " bit packed ";
1343
1344 if constexpr (std::is_same_v<Number, double>)
1345 return result + "double";
1346 else if constexpr (std::is_same_v<Number, float>)
1347 return result + "float";
1348 else
1349 __builtin_trap();
1350 }();
1351
1352 std::ostringstream output;
1353
1354 std::ostringstream primary;
1355 if (final_time) {
1356 primary << "FINAL (cycle " << Utilities::int_to_string(cycle, 6) << ")";
1357 } else {
1358 primary << "Cycle " << Utilities::int_to_string(cycle, 6) //
1359 << " (" << std::fixed << std::setprecision(1) //
1360 << t / t_final_ * 100 << "%)";
1361 }
1362
1363 std::ostringstream secondary;
1364 secondary << "at time t = " << std::setprecision(8) << std::fixed << t;
1365
1366 print_head(primary.str(), secondary.str(), output);
1367
1368 output << "Information: (HYP) " << hyperbolic_system_.problem_name;
1369 if constexpr (!ParabolicSystem::is_identity) {
1370 output << "\n (PAR) " << parabolic_system_.problem_name;
1371 }
1372 output << "\n [" << base_name_ << "] ";
1373 if (mpi_ensemble_.n_ensembles() > 1) {
1374 output << mpi_ensemble_.n_ensembles() << " ensembles ";
1375 }
1376 output << "with " //
1377 << n_global_dofs_ << " Qdofs on " //
1378 << mpi_ensemble_.n_world_ranks() << " ranks / " //
1379#ifdef WITH_OPENMP
1380 << MultithreadInfo::n_threads() << " threads <" //
1381#else
1382 << "[openmp disabled] <" //
1383#endif
1384 << vectorization_name //
1385 << ">\n Last output cycle " //
1386 << timer_cycle - 1 //
1387 << " at t = " << timer_granularity_ * (timer_cycle - 1) //
1388 << " (terminal update interval " << terminal_update_interval_ //
1389 << "s)\n";
1390
1391 print_memory_statistics(output);
1392 print_timers(output);
1393 print_throughput(cycle, t, output, final_time);
1394
1395 if (mpi_ensemble_.world_rank() == 0) {
1396#ifndef DEBUG_OUTPUT
1397 std::cout << "\033[2J\033[H";
1398#endif
1399 std::cout << output.str() << std::flush;
1400
1401 if (write_to_logfile) {
1402 logfile_ << "\n" << output.str() << std::flush;
1403 }
1404 }
1405 }
1406
1407} // namespace ryujin
unsigned int get_handle() const
void prepare_projection(const StateVector &old_state_vector)
void set_handle(unsigned int handle)
void project(StateVector &new_state_vector)
void write_checkpoint(const StateVector &state_vector, const std::string &base_name, const Number &t, const unsigned int &output_cycle)
Vectors::ScalarVector< Number > ScalarVector
Definition: time_loop.h:62
void print_timers(std::ostream &stream)
void output(StateVector &state_vector, const std::string &name, const Number t, const unsigned int cycle)
void print_memory_statistics(std::ostream &stream)
void print_mpi_partition(std::ostream &stream)
void print_parameters(std::ostream &stream)
void compute_error(StateVector &state_vector, Number t)
void read_checkpoint(StateVector &state_vector, const std::string &base_name, Number &t, unsigned int &output_cycle, const Callable &prepare_compute_kernels)
TimeLoop(const MPI_Comm &mpi_comm)
typename View::StateVector StateVector
Definition: time_loop.h:60
void print_head(const std::string &header, const std::string &secondary, std::ostream &stream)
void print_throughput(unsigned int cycle, Number t, std::ostream &stream, bool final_time=false)
void print_cycle_statistics(unsigned int cycle, Number t, unsigned int output_cycle, bool write_to_logfile=false, bool final_time=false)
void print_info(const std::string &header)
void adapt_mesh_and_transfer_state_vector(StateVector &state_vector, const Callable &prepare_compute_kernels)
T pow(const T x, const T b)
void print_revision_and_version(std::ostream &stream)