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