15#include <deal.II/base/logstream.h>
16#include <deal.II/base/work_stream.h>
28using namespace dealii;
32 template <
typename Description,
int dim,
typename Number>
34 : ParameterAcceptor(
"/A - TimeLoop")
35 , mpi_ensemble_(mpi_comm,
37 if constexpr (has_n_mpi_ensembles_v<Description>)
38 return Description::n_mpi_ensembles();
42 , hyperbolic_system_(mpi_ensemble_,
"/B - Equation")
43 , parabolic_system_(mpi_ensemble_,
"/B - Equation")
44 , discretization_(mpi_ensemble_,
"/C - Discretization")
45 , offline_data_(mpi_ensemble_, discretization_,
"/D - OfflineData")
46 , initial_values_(mpi_ensemble_,
52 , hyperbolic_module_(mpi_ensemble_,
56 "/F - HyperbolicModule")
57 , parabolic_module_(mpi_ensemble_,
62 "/G - ParabolicModule")
63 , time_integrator_(mpi_ensemble_,
67 "/H - TimeIntegrator")
68 , mesh_adaptor_(mpi_ensemble_,
72 hyperbolic_module_.initial_precomputed(),
73 hyperbolic_module_.alpha(),
75 , solution_transfer_(mpi_ensemble_,
80 , postprocessor_(mpi_ensemble_,
85 , vtu_output_(mpi_ensemble_,
90 hyperbolic_module_.initial_precomputed(),
91 hyperbolic_module_.alpha(),
92 mesh_adaptor_.smoothness_indicators(),
94 , quantities_(mpi_ensemble_,
98 hyperbolic_module_.initial_precomputed(),
100 , error_evaluation_(mpi_ensemble_,
104 hyperbolic_module_.initial_precomputed(),
105 "/L - ErrorEvaluation")
110 add_parameter(
"basename", base_name_,
"Base name for all output files");
112 t_final_ = Number(5.);
113 add_parameter(
"final time", t_final_,
"Final time");
115 enforce_t_final_ =
false;
116 add_parameter(
"enforce final time",
118 "Boolean indicating whether the final time should be "
119 "enforced strictly. If set to true the last time step is "
120 "shortened so that the simulation ends precisely at t_final");
122 timer_granularity_ = Number(0.01);
123 add_parameter(
"timer granularity",
125 "The timer granularity specifies the time interval after "
126 "which compute, output, postprocessing, and mesh adaptation "
127 "routines are run. This \"baseline tick\" is further "
128 "modified by the corresponding \"*_multiplier\" options");
130 enable_output_full_ =
false;
131 add_parameter(
"enable output full",
133 "Write out full pvtu records. The frequency is determined by "
134 "\"timer granularity\" and \"timer output full multiplier\"");
136 enable_output_levelsets_ =
false;
138 "enable output levelsets",
139 enable_output_levelsets_,
140 "Write out levelsets pvtu records. The frequency is determined by "
141 "\"timer granularity\" and \"timer output levelsets multiplier\"");
143 enable_compute_error_ =
false;
145 "enable compute error",
146 enable_compute_error_,
147 "Flag to control whether we compute error norms of the difference to "
148 "an analytic solution. Implemented only for certain initial state "
149 "configurations. The frequency how often errors are logged is "
150 "determined by \"timer granularity\" and \"timer compute error "
153 enable_compute_quantities_ =
false;
155 "enable compute quantities",
156 enable_compute_quantities_,
157 "Flag to control whether we compute quantities of interest. The "
158 "frequency how often quantities are logged is determined by \"timer "
159 "granularity\" and \"timer compute quantities multiplier\"");
161 enable_mesh_adaptivity_ =
false;
163 "enable mesh adaptivity",
164 enable_mesh_adaptivity_,
165 "Flag to control whether we use an adaptive mesh refinement strategy. "
166 "The frequency how often we query MeshAdaptor::analyze() for deciding "
167 "on adapting the mesh is determined by \"timer granularity\" and "
168 "\"timer mesh refinement multiplier\"");
170 timer_output_full_multiplier_ = 1;
171 add_parameter(
"timer output full multiplier",
172 timer_output_full_multiplier_,
173 "Multiplicative modifier applied to \"timer granularity\" "
174 "that determines the full pvtu writeout granularity");
176 timer_output_levelsets_multiplier_ = 1;
177 add_parameter(
"timer output levelsets multiplier",
178 timer_output_levelsets_multiplier_,
179 "Multiplicative modifier applied to \"timer granularity\" "
180 "that determines the levelsets pvtu writeout granularity");
182 timer_compute_error_multiplier_ = 1;
183 add_parameter(
"timer compute error multiplier",
184 timer_compute_error_multiplier_,
185 "Multiplicative modifier applied to \"timer granularity\" "
186 "that determines the writeout granularity for error norms");
188 timer_compute_quantities_multiplier_ = 1;
190 "timer compute quantities multiplier",
191 timer_compute_quantities_multiplier_,
192 "Multiplicative modifier applied to \"timer granularity\" that "
193 "determines the writeout granularity for quantities of interest");
196 add_parameter(
"resume", resume_,
"Resume an interrupted computation");
198 resume_at_time_zero_ =
false;
199 add_parameter(
"resume at time zero",
200 resume_at_time_zero_,
201 "Resume from the latest checkpoint but set the time to t=0.");
203 terminal_update_interval_ = 5;
204 add_parameter(
"terminal update interval",
205 terminal_update_interval_,
206 "Number of seconds after which output statistics are "
207 "recomputed and printed on the terminal. Setting the "
208 "interval to zero disables terminal output.");
210 terminal_correct_for_hypertreadhing_ =
true;
212 "terminal correct for hyperthreading",
213 terminal_correct_for_hypertreadhing_,
214 "If set to true, the CPU throughput is corrected by dividing the total "
215 "consumed CPU time by a factor of 2. This correction is only active if "
216 "the number of threads (per MPI rank) is 2.");
218 checkpoint_update_interval_ = 0;
220 "checkpoint update interval",
221 checkpoint_update_interval_,
222 "Number of seconds after which a new checkpoint is written out to "
223 "disk. Setting the interval to zero disables checkpointing.");
226 add_parameter(
"debug command",
228 "If set to a nonempty string then the host environment's "
229 "command processor is invoked via std::system() with the "
230 "specified string as command parameter.");
232 debug_filename_ =
"";
233 add_parameter(
"debug filename",
235 "If set to a nonempty string then we output the contents of "
236 "this file at the end. This is mainly useful in the "
237 "testsuite to output files we wish to compare");
248 template <
typename Description,
int dim,
typename Number>
252 std::cout <<
"TimeLoop<dim, Number>::run()" << std::endl;
259 base_name_ensemble_ = base_name_;
260 if (mpi_ensemble_.n_ensembles() > 1) {
261 print_info(
"setting up MPI ensemble");
262 unsigned int digits =
263 dealii::Utilities::needed_digits(mpi_ensemble_.n_ensembles() - 1);
264 base_name_ensemble_ +=
266 dealii::Utilities::int_to_string(mpi_ensemble_.ensemble(), digits);
272 if (mpi_ensemble_.world_rank() == 0)
273 logfile_.open(base_name_ +
".log");
275 print_parameters(logfile_);
277 if (enable_compute_error_)
278 error_evaluation_.prepare(base_name_);
285 unsigned int timer_cycle = 0;
289 const auto prepare_compute_kernels = [&]() {
290 print_info(
"preparing compute kernels");
292 discretization_.update_mapping();
293 offline_data_.prepare(problem_dimension, n_precomputed_values);
295 hyperbolic_module_.prepare();
296 parabolic_module_.prepare();
297 time_integrator_.prepare();
298 mesh_adaptor_.prepare( t);
299 postprocessor_.prepare();
300 vtu_output_.prepare();
301 quantities_.prepare(base_name_ensemble_);
302 print_mpi_partition(logfile_);
304 if (mpi_ensemble_.ensemble_rank() == 0)
305 n_global_dofs_ = dealii::Utilities::MPI::sum(
306 offline_data_.dof_handler().n_dofs(),
307 mpi_ensemble_.ensemble_leader_communicator());
312 print_info(
"initializing data structures");
315 print_info(
"resume: reading mesh and loading state vector");
317 read_checkpoint(state_vector,
321 prepare_compute_kernels);
323 if (resume_at_time_zero_) {
330 print_info(
"creating mesh and interpolating initial values");
332 discretization_.prepare(base_name_ensemble_);
334 prepare_compute_kernels();
336 hyperbolic_module_.reinit_state_vector(state_vector);
337 parabolic_module_.reinit_state_vector(state_vector);
340 "time step [X] - interpolate data vectors");
341 std::get<0>(state_vector) =
342 initial_values_.get().interpolate_hyperbolic_vector();
348 print_device_information(logfile_);
351 time_integrator_.prepare_state_vector(state_vector, t);
357 Number last_terminal_output = terminal_update_interval_ == Number(0.)
358 ? std::numeric_limits<Number>::max()
360 Number last_checkpoint = checkpoint_update_interval_ == Number(0.)
361 ? std::numeric_limits<Number>::max()
364 print_info(
"entering main loop");
367 constexpr Number relax =
368 Number(1.) - Number(10.) * std::numeric_limits<Number>::epsilon();
371 const auto output_scheduled = [&](
const unsigned int cycle) {
372 const bool do_full_output =
373 (cycle % timer_output_full_multiplier_ == 0) && enable_output_full_;
374 const bool do_levelsets =
375 (cycle % timer_output_levelsets_multiplier_ == 0) &&
376 enable_output_levelsets_;
378 return do_full_output || do_levelsets;
381 unsigned int cycle = 1;
385 std::cout <<
"\n\n### cycle = " << cycle <<
" ###\n\n" << std::endl;
390 if (enable_compute_quantities_) {
392 quantities_.accumulate(state_vector, t);
397 if (t >= relax * timer_cycle * timer_granularity_) {
398 const bool do_compute_error =
399 enable_compute_error_ &&
400 (timer_cycle % timer_compute_error_multiplier_ == 0);
401 const bool do_output_analytic =
402 enable_compute_error_ && output_scheduled(timer_cycle);
404 if (do_compute_error || do_output_analytic) {
406 interpolate_analytic_solution(analytic, t);
408 if (do_compute_error) {
410 error_evaluation_.write_out(state_vector, analytic, t);
413 if (do_output_analytic) {
415 time_integrator_.prepare_state_vector(analytic, t);
418 base_name_ensemble_ +
"-analytic_solution",
424 output(state_vector, base_name_ensemble_ +
"-solution", t, timer_cycle);
426 if (enable_compute_quantities_ &&
427 (timer_cycle % timer_compute_quantities_multiplier_ == 0)) {
429 quantities_.write_out(state_vector, t, timer_cycle);
437 if (t >= relax * t_final_)
442 if (enable_mesh_adaptivity_) {
445 "time step [X] - analyze for mesh adaptation");
447 mesh_adaptor_.analyze(state_vector, t, cycle);
450 if (mesh_adaptor_.need_mesh_adaptation()) {
453 "time step [X] - perform mesh adaptation");
454 print_info(
"performing mesh adaptation");
456 adapt_mesh_and_transfer_state_vector(state_vector,
457 prepare_compute_kernels);
460 time_integrator_.prepare_state_vector(state_vector, t);
466 const auto tau = time_integrator_.step(
470 ? std::min(t_final_, timer_cycle * timer_granularity_)
471 : std::numeric_limits<Number>::max());
475 time_integrator_.prepare_state_vector(state_vector, t);
482 "time step [X] _ - synchronization barriers");
484 Utilities::MPI::max(wall_time, mpi_ensemble_.world_communicator());
489 const bool write_to_log_file =
490 (terminal_update_interval_ != Number(0.)) &&
491 (t >= relax * timer_cycle * timer_granularity_);
493 const bool update_terminal =
494 (wall_time >= last_terminal_output + terminal_update_interval_);
496 if (write_to_log_file || update_terminal) {
498 "time step [X] _ - synchronization barriers");
499 print_cycle_statistics(cycle,
504 last_terminal_output = wall_time;
507 const bool update_checkpoint =
508 (wall_time >= last_checkpoint + checkpoint_update_interval_);
510 if (update_checkpoint) {
513 print_info(
"scheduling checkpointing");
514 write_checkpoint(state_vector, base_name_ensemble_, t, timer_cycle);
515 last_checkpoint = wall_time;
522 if (checkpoint_update_interval_ != Number(0.)) {
525 print_info(
"scheduling checkpointing");
526 write_checkpoint(state_vector, base_name_ensemble_, t, timer_cycle);
533 if (terminal_update_interval_ != Number(0.)) {
534 print_cycle_statistics(cycle,
544 if (enable_compute_error_) {
547 interpolate_analytic_solution(analytic, t);
548 const auto norms = error_evaluation_.compute(state_vector, analytic);
550 logfile_ << std::endl <<
"Computed errors:" << std::endl << std::endl;
551 error_evaluation_.print_summary(logfile_, t, n_global_dofs_, norms);
552 error_evaluation_.print_summary(std::cout, t, n_global_dofs_, norms);
557 if (mpi_ensemble_.world_rank() == 0) {
558 if (debug_command_ !=
"") {
559 auto result [[maybe_unused]] = std::system(debug_command_.c_str());
562 if (debug_filename_ !=
"") {
563 std::ifstream f(debug_filename_);
565 std::cout << f.rdbuf();
578 template <
typename Description,
int dim,
typename Number>
579 template <
typename Callable>
581 StateVector &state_vector,
582 const std::string &base_name,
584 unsigned int &timer_cycle,
585 const Callable &prepare_compute_kernels)
588 std::cout <<
"TimeLoop<dim, Number>::read_checkpoint()" << std::endl;
595 discretization_.refinement() = 0;
596 discretization_.prepare(base_name);
597 discretization_.triangulation().load(base_name +
"-checkpoint.mesh");
599 prepare_compute_kernels();
605 std::string name = base_name +
"-checkpoint";
607 unsigned int transfer_handle;
608 if (mpi_ensemble_.ensemble_rank() == 0) {
609 std::string meta = name +
".metadata";
611 std::ifstream file(meta, std::ios::binary);
612 boost::archive::binary_iarchive ia(file);
613 ia >> t >> timer_cycle >> transfer_handle;
617 if constexpr (std::is_same_v<Number, double>)
619 &t, 1, MPI_DOUBLE, 0, mpi_ensemble_.ensemble_communicator());
622 MPI_Bcast(&t, 1, MPI_FLOAT, 0, mpi_ensemble_.ensemble_communicator());
623 AssertThrowMPI(ierr);
625 ierr = MPI_Bcast(&timer_cycle,
629 mpi_ensemble_.ensemble_communicator());
630 AssertThrowMPI(ierr);
632 ierr = MPI_Bcast(&transfer_handle,
636 mpi_ensemble_.ensemble_communicator());
637 AssertThrowMPI(ierr);
641 hyperbolic_module_.reinit_state_vector(state_vector);
642 parabolic_module_.reinit_state_vector(state_vector);
644 solution_transfer_.set_handle(transfer_handle);
645 solution_transfer_.project(state_vector);
646 solution_transfer_.reset_handle();
649 time_integrator_.prepare_state_vector(state_vector, t);
653 template <
typename Description,
int dim,
typename Number>
654 void TimeLoop<Description, dim, Number>::write_checkpoint(
655 const StateVector &state_vector,
656 const std::string &base_name,
658 const unsigned int &timer_cycle)
661 std::cout <<
"TimeLoop<dim, Number>::write_checkpoint()" << std::endl;
664 solution_transfer_.prepare_projection(state_vector);
665 const auto transfer_handle = solution_transfer_.get_handle();
666 solution_transfer_.reset_handle();
668 std::string name = base_name +
"-checkpoint";
670 if (mpi_ensemble_.ensemble_rank() == 0) {
671 for (
const std::string suffix :
672 {
".mesh",
".mesh_fixed.data",
".mesh.info",
".metadata"})
673 if (std::filesystem::exists(name + suffix))
674 std::filesystem::rename(name + suffix, name + suffix +
"~");
677 const auto &triangulation = discretization_.triangulation();
678 triangulation.save(name +
".mesh");
684 if (mpi_ensemble_.ensemble_rank() == 0) {
685 std::string meta = name +
".metadata";
686 std::ofstream file(meta, std::ios::binary | std::ios::trunc);
687 boost::archive::binary_oarchive oa(file);
688 oa << t << timer_cycle << transfer_handle;
691 const int ierr = MPI_Barrier(mpi_ensemble_.ensemble_communicator());
692 AssertThrowMPI(ierr);
696 template <
typename Description,
int dim,
typename Number>
697 template <
typename Callable>
698 void TimeLoop<Description, dim, Number>::adapt_mesh_and_transfer_state_vector(
699 StateVector &state_vector,
const Callable &prepare_compute_kernels)
702 std::cout <<
"TimeLoop<dim, Number>::adapt_mesh_and_transfer_state_vector()"
706 AssertThrow(mpi_ensemble_.n_ensembles() == 1, dealii::ExcNotImplemented());
712 auto &triangulation = discretization_.triangulation();
713 mesh_adaptor_.mark_cells_for_coarsening_and_refinement(triangulation);
715 triangulation.prepare_coarsening_and_refinement();
717 solution_transfer_.prepare_projection(state_vector);
721 triangulation.execute_coarsening_and_refinement();
722 prepare_compute_kernels();
724 hyperbolic_module_.reinit_state_vector(state_vector);
725 parabolic_module_.reinit_state_vector(state_vector);
727 solution_transfer_.project(state_vector);
728 solution_transfer_.reset_handle();
733 template <
typename Description,
int dim,
typename Number>
734 void TimeLoop<Description, dim, Number>::interpolate_analytic_solution(
735 StateVector &analytic,
const Number t)
738 std::cout <<
"TimeLoop<dim, Number>::interpolate_analytic_solution()"
742 ComputingTimer::Scope scope(
"time step [X] - interpolate data vectors");
744 hyperbolic_module_.reinit_state_vector(analytic);
745 parabolic_module_.reinit_state_vector(analytic);
746 std::get<0>(analytic) =
747 initial_values_.get().interpolate_hyperbolic_vector(t);
750 hyperbolic_system_.get().fill_precomputed_values(
751 offline_data_, analytic,
false);
752 std::get<1>(analytic).view().update_ghost_values();
756 template <
typename Description,
int dim,
typename Number>
758 TimeLoop<Description, dim, Number>::output(
const StateVector &state_vector,
759 const std::string &name,
761 const unsigned int cycle)
764 std::cout <<
"TimeLoop<dim, Number>::output(t = " << t <<
")" << std::endl;
767 const bool do_full_output =
768 (cycle % timer_output_full_multiplier_ == 0) && enable_output_full_;
769 const bool do_levelsets =
770 (cycle % timer_output_levelsets_multiplier_ == 0) &&
771 enable_output_levelsets_;
774 if (!(do_full_output || do_levelsets))
779 ComputingTimer::Scope scope(
"time step [X] - perform vtu output");
780 print_info(
"scheduling output");
782 postprocessor_.compute(state_vector);
789 postprocessor_.reset_bounds();
792 mesh_adaptor_.compute_smoothness_indicators(state_vector);
794 vtu_output_.schedule_output(
795 state_vector, name, t, cycle, do_full_output, do_levelsets);
806 template <
typename Description,
int dim,
typename Number>
808 TimeLoop<Description, dim, Number>::print_parameters(std::ostream &stream)
810 if (mpi_ensemble_.world_rank() != 0)
819 stream << std::endl <<
"Run time parameters:" << std::endl << std::endl;
820 ParameterAcceptor::prm.print_parameters(
821 stream, ParameterHandler::OutputStyle::ShortPRM);
826 std::ofstream output(base_name_ +
"-parameters.prm");
827 ParameterAcceptor::prm.print_parameters(output, ParameterHandler::ShortPRM);
831 template <
typename Description,
int dim,
typename Number>
833 TimeLoop<Description, dim, Number>::print_mpi_partition(std::ostream &stream)
842 std::vector<double> values = {
843 (double)offline_data_.n_export_indices(),
844 (double)offline_data_.n_locally_internal(),
845 (double)offline_data_.n_locally_owned(),
846 (double)offline_data_.n_locally_relevant(),
847 (double)offline_data_.n_export_indices() /
848 (double)offline_data_.n_locally_relevant(),
849 (double)offline_data_.n_locally_internal() /
850 (double)offline_data_.n_locally_relevant(),
851 (double)offline_data_.n_locally_owned() /
852 (double)offline_data_.n_locally_relevant()};
856 Utilities::MPI::min_max_avg(values, mpi_ensemble_.world_communicator());
858 if (mpi_ensemble_.world_rank() != 0)
861 std::ostringstream output;
864 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
866 const auto print_snippet = [&output, n](
const std::string &name,
867 const auto &values) {
868 output << name <<
": ";
870 output << std::setw(9) << (
unsigned int)values.min
871 <<
" [p" << std::setw(n) << values.min_index <<
"] "
872 << std::setw(9) << (
unsigned int)values.avg <<
" "
873 << std::setw(9) << (
unsigned int)values.max
874 <<
" [p" << std::setw(n) << values.max_index <<
"]";
878 const auto print_percentages = [&output, n](
const auto &percentages) {
879 output << std::endl <<
" ";
880 output <<
" (" << std::setw(3) << std::setprecision(2)
881 << percentages.min * 100 <<
"% )"
882 <<
" [p" << std::setw(n) << percentages.min_index <<
"] "
883 <<
" (" << std::setw(3) << std::setprecision(2)
884 << percentages.avg * 100 <<
"% )"
886 <<
" (" << std::setw(3) << std::setprecision(2)
887 << percentages.max * 100 <<
"% )"
888 <<
" [p" << std::setw(n) << percentages.max_index <<
"]";
891 output << std::endl << std::endl <<
"Partition: ";
892 print_snippet(
"exp", data[0]);
893 print_percentages(data[4]);
895 output << std::endl <<
" ";
896 print_snippet(
"int", data[1]);
897 print_percentages(data[5]);
899 output << std::endl <<
" ";
900 print_snippet(
"own", data[2]);
901 print_percentages(data[6]);
903 output << std::endl <<
" ";
904 print_snippet(
"rel", data[3]);
906 stream << output.str() << std::endl;
910 template <
typename Description,
int dim,
typename Number>
911 void TimeLoop<Description, dim, Number>::print_device_information(
912 std::ostream &stream)
918#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
925 using Device = std::pair<std::string, int>;
927 const Device local{dealii::Utilities::System::get_hostname(),
928 Kokkos::device_id()};
929 const auto all = dealii::Utilities::MPI::all_gather(
930 mpi_ensemble_.world_communicator(), local);
931 n_devices_ = std::set<Device>(all.begin(), all.end()).size();
935 if (mpi_ensemble_.world_rank() != 0)
938 std::ostringstream output;
940 const auto entry [[maybe_unused]] =
941 [&output](
const std::string &label) -> std::ostream & {
943 <<
" " << std::left << std::setw(22) << label;
949 <<
"Device: " << std::left << std::setw(22) <<
"backend"
950 << Kokkos::DefaultExecutionSpace::name();
957#if defined(KOKKOS_ENABLE_CUDA)
958 const auto &prop = Kokkos::Cuda{}.cuda_device_prop();
960 entry(
"device") << prop.name;
961 entry(
"compute capability") << prop.major <<
"." << prop.minor;
962 entry(
"device id") << Kokkos::device_id() <<
" of "
963 << Kokkos::num_devices();
964 entry(
"devices in use")
965 << n_devices_ <<
" on " << mpi_ensemble_.n_world_ranks() <<
" ranks";
966 entry(
"multiprocessors") << prop.multiProcessorCount;
967 entry(
"warp size") << prop.warpSize <<
" (hardware), " <<
warp_size
968 <<
" (ryujin::warp_size)";
969 entry(
"threads per SM")
970 << prop.maxThreadsPerMultiProcessor <<
" ("
971 << prop.maxThreadsPerMultiProcessor / prop.warpSize <<
" warps)";
972 entry(
"threads per block") << prop.maxThreadsPerBlock;
973 entry(
"concurrency") << Kokkos::DefaultExecutionSpace{}.concurrency();
974 entry(
"registers per SM") << prop.regsPerBlock;
975 entry(
"shared memory per SM")
976 << prop.sharedMemPerMultiprocessor / 1024 <<
" KiB";
977 entry(
"L2 cache") << prop.l2CacheSize / 1024 / 1024 <<
" MiB";
978 entry(
"global memory") << prop.totalGlobalMem / 1024 / 1024 <<
" MiB";
980#elif defined(KOKKOS_ENABLE_HIP)
981 const auto &prop = Kokkos::HIP::hip_device_prop();
983 entry(
"device") << prop.name;
984 entry(
"architecture") << prop.gcnArchName;
985 entry(
"device id") << Kokkos::device_id() <<
" of "
986 << Kokkos::num_devices();
987 entry(
"devices in use")
988 << n_devices_ <<
" on " << mpi_ensemble_.n_world_ranks() <<
" ranks";
989 entry(
"compute units") << prop.multiProcessorCount;
990 entry(
"warp size") << prop.warpSize <<
" (hardware), " <<
warp_size
991 <<
" (ryujin::warp_size)";
992 entry(
"threads per CU")
993 << prop.maxThreadsPerMultiProcessor <<
" ("
994 << prop.maxThreadsPerMultiProcessor / prop.warpSize <<
" warps)";
995 entry(
"threads per block") << prop.maxThreadsPerBlock;
996 entry(
"concurrency") << Kokkos::DefaultExecutionSpace{}.concurrency();
997 entry(
"registers per block") << prop.regsPerBlock;
998 entry(
"shared memory per block") << prop.sharedMemPerBlock / 1024 <<
" KiB";
999 entry(
"L2 cache") << prop.l2CacheSize / 1024 / 1024 <<
" MiB";
1000 entry(
"global memory") << prop.totalGlobalMem / 1024 / 1024 <<
" MiB";
1003 stream << output.str() << std::endl;
1007 template <
typename Description,
int dim,
typename Number>
1008 void TimeLoop<Description, dim, Number>::print_info(
const std::string &header)
1010 if (mpi_ensemble_.world_rank() != 0)
1013 std::cout <<
"[INFO] " << header << std::endl;
1017 template <
typename Description,
int dim,
typename Number>
1019 TimeLoop<Description, dim, Number>::print_head(
const std::string &header,
1020 const std::string &secondary,
1021 std::ostream &stream)
1023 if (mpi_ensemble_.world_rank() != 0)
1026 const int header_size = header.size();
1027 const auto padded_header =
1028 std::string(std::max(0, 34 - header_size) / 2,
' ') + header +
1029 std::string(std::max(0, 35 - header_size) / 2,
' ');
1031 const int secondary_size = secondary.size();
1032 const auto padded_secondary =
1033 std::string(std::max(0, 34 - secondary_size) / 2,
' ') + secondary +
1034 std::string(std::max(0, 35 - secondary_size) / 2,
' ');
1038 stream <<
" ####################################################\n";
1039 stream <<
" #########" << padded_header <<
"#########\n";
1040 stream <<
" #########" << padded_secondary <<
"#########\n";
1041 stream <<
" ####################################################\n";
1042 stream << std::endl;
1047 template <
typename Description,
int dim,
typename Number>
1048 void TimeLoop<Description, dim, Number>::print_information(
1049 unsigned int timer_cycle,
1050 Number last_checkpoint,
1051 std::ostream &stream,
1054 static const std::string backend_name = [] {
1055 const auto precision = [] {
1056 if constexpr (std::is_same_v<Number, double>)
1057 return std::string(
"FP64");
1058 else if constexpr (std::is_same_v<Number, float>)
1059 return std::string(
"FP32");
1064 constexpr auto simd_size = VectorizedArray<Number>::size();
1067 return "GPU, " + precision +
", warp size " + std::to_string(
warp_size);
1068 else if constexpr (simd_size == 1)
1069 return "scalar, " + precision;
1071 return "SIMD, " + precision +
", width " + std::to_string(simd_size);
1074 stream <<
"Information: (HYP) " << hyperbolic_system_.get().problem_name;
1075 if constexpr (!ParabolicSystem::is_identity) {
1076 stream <<
"\n (PAR) " << parabolic_system_.get().problem_name;
1078 stream <<
"\n [" << base_name_ <<
"] ";
1079 if (mpi_ensemble_.n_ensembles() > 1) {
1080 stream << mpi_ensemble_.n_ensembles() <<
" ensembles ";
1082 stream <<
"with " << n_global_dofs_ <<
" Qdofs on "
1083 << mpi_ensemble_.n_world_ranks() <<
" ranks "
1084#if defined(WITH_OPENMP)
1085 <<
"/ " << omp_get_max_threads() <<
" threads "
1086#ifndef WITH_DEAL_II_THREADS
1087 <<
"[serial dealii] "
1089#elif defined(WITH_DEAL_II_THREADS)
1090 <<
"/ " << MultithreadInfo::n_threads() <<
" threads "
1092 <<
"<" << backend_name <<
">\n";
1094 stream <<
" Last output cycle "
1096 <<
" at t = " << timer_granularity_ * (timer_cycle - 1)
1099 if (enable_output_full_)
1101 if (enable_output_levelsets_)
1102 stream <<
"levelsets ";
1103 if (enable_compute_error_)
1105 if (enable_compute_quantities_)
1106 stream <<
"quantities ";
1110 if (checkpoint_update_interval_ != Number(0.)) {
1111 const auto wall_time = Utilities::MPI::min_max_avg(
1113 mpi_ensemble_.world_communicator());
1116 stream <<
" Last checkpoint at FINAL TIME\n";
1118 stream <<
" Last checkpoint at wall time "
1119 << std::setprecision(2) << std::fixed << last_checkpoint
1120 <<
"s (" << std::setprecision(0)
1121 << std::max(0., wall_time.max - last_checkpoint)
1122 <<
"s ago, interval " << checkpoint_update_interval_ <<
"s)\n";
1128 template <
typename Description,
int dim,
typename Number>
1129 void TimeLoop<Description, dim, Number>::print_memory_statistics(
1130 std::ostream &stream)
1132 Utilities::System::MemoryStats stats;
1133 Utilities::System::get_memory_stats(stats);
1135 Utilities::MPI::MinMaxAvg data = Utilities::MPI::min_max_avg(
1136 stats.VmRSS / 1024., mpi_ensemble_.world_communicator());
1138 if (mpi_ensemble_.world_rank() != 0)
1141 std::ostringstream output;
1144 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
1146 output <<
"\nMemory: [MiB]"
1147 << std::setw(8) << data.min
1148 <<
" [p" << std::setw(n) << data.min_index <<
"] "
1149 << std::setw(8) << data.avg <<
" "
1150 << std::setw(8) << data.max
1151 <<
" [p" << std::setw(n) << data.max_index <<
"]";
1153 stream << output.str() << std::endl;
1157 template <
typename Description,
int dim,
typename Number>
1158 void TimeLoop<Description, dim, Number>::print_timers(std::ostream &stream)
1162 const auto equalize = [&]() {
1164 std::max_element(output.begin(),
1166 [](
const auto &left,
const auto &right) {
1167 return left.str().length() < right.str().length();
1169 const auto length = ptr->str().length();
1170 for (
auto &it : output)
1171 it << std::string(length - it.str().length() + 1,
' ');
1174 const auto print_wall_time = [&](
auto &timer,
auto &stream) {
1175 const auto wall_time = Utilities::MPI::min_max_avg(
1176 timer.wall_time(), mpi_ensemble_.world_communicator());
1178 constexpr auto eps = std::numeric_limits<double>::epsilon();
1183 const auto skew_negative = std::max(
1184 100. * (wall_time.min - wall_time.avg) / wall_time.avg - eps, -99.9);
1185 const auto skew_positive = std::min(
1186 100. * (wall_time.max - wall_time.avg) / wall_time.avg + eps, 99.9);
1188 stream << std::setprecision(2) << std::fixed << std::setw(9)
1189 << wall_time.avg <<
"s [sk: " << std::setprecision(1)
1190 << std::setw(5) << std::fixed << skew_negative <<
"%/"
1191 << std::setw(4) << std::fixed << skew_positive <<
"%]";
1193 dealii::Utilities::needed_digits(mpi_ensemble_.n_world_ranks());
1194 stream <<
" [p" << std::setw(n) << wall_time.min_index <<
"/"
1195 << wall_time.max_index <<
"]";
1198 const auto cpu_time_statistics = Utilities::MPI::min_max_avg(
1200 mpi_ensemble_.world_communicator());
1201 const double total_cpu_time = cpu_time_statistics.sum;
1203 const auto print_cpu_time =
1204 [&](
auto &timer,
auto &stream,
bool percentage) {
1205 const auto cpu_time = Utilities::MPI::min_max_avg(
1206 timer.cpu_time(), mpi_ensemble_.world_communicator());
1208 stream << std::setprecision(2) << std::fixed << std::setw(12)
1209 << cpu_time.sum <<
"s ";
1212 stream <<
"(" << std::setprecision(1) << std::setw(4)
1213 << 100. * cpu_time.sum / total_cpu_time <<
"%)";
1216 auto jt = output.begin();
1217 for (
auto &it : ComputingTimer::timers())
1218 *jt++ <<
" " << it.first;
1221 jt = output.begin();
1222 for (
auto &it : ComputingTimer::timers())
1223 print_wall_time(it.second, *jt++);
1226 jt = output.begin();
1227 bool compute_percentages =
false;
1228 for (
auto &it : ComputingTimer::timers()) {
1229 print_cpu_time(it.second, *jt++, compute_percentages);
1230 if (it.first.starts_with(
"time loop"))
1231 compute_percentages =
true;
1235 if (mpi_ensemble_.world_rank() != 0)
1238 stream << std::endl <<
"Timer statistics:\n";
1239 for (
auto &it : output)
1240 stream << it.str() << std::endl;
1244 template <
typename Description,
int dim,
typename Number>
1245 void TimeLoop<Description, dim, Number>::print_throughput(
1246 unsigned int cycle, Number t, std::ostream &stream,
bool final_time)
1252 static struct Data {
1253 unsigned int cycle = 0;
1255 double cpu_time_sum = 0.;
1256 double cpu_time_avg = 0.;
1257 double cpu_time_min = 0.;
1258 double cpu_time_max = 0.;
1259 double wall_time = 0.;
1260 double device_time_sum = 0.;
1261 } previous, current;
1263 static double time_per_second_exp = 0.;
1270 current.cycle = cycle;
1273 const auto wall_time_statistics = Utilities::MPI::min_max_avg(
1275 mpi_ensemble_.world_communicator());
1276 current.wall_time = wall_time_statistics.max;
1278 const auto cpu_time_statistics = Utilities::MPI::min_max_avg(
1280 mpi_ensemble_.world_communicator());
1281 current.cpu_time_sum = cpu_time_statistics.sum;
1282 current.cpu_time_avg = cpu_time_statistics.avg;
1283 current.cpu_time_min = cpu_time_statistics.min;
1284 current.cpu_time_max = cpu_time_statistics.max;
1287 const auto device_time_statistics = Utilities::MPI::min_max_avg(
1289 current.device_time_sum = device_time_statistics.sum;
1298 double delta_cycles = current.cycle - previous.cycle;
1299 const double cycles_per_second =
1300 delta_cycles / (current.wall_time - previous.wall_time);
1302 const auto efficiency = time_integrator_.efficiency();
1303 const auto n_dofs =
static_cast<double>(n_global_dofs_);
1305 double wall_m_dofs_per_sec = delta_cycles * n_dofs * efficiency / 1.e6 /
1306 (current.wall_time - previous.wall_time);
1308 const double delta_time =
1309 (current.t - previous.t) / (current.cycle - previous.cycle);
1310 const double time_per_second =
1311 (current.t - previous.t) / (current.wall_time - previous.wall_time);
1315 std::ostringstream output;
1318 output << std::endl;
1320 output <<
"Throughput:" << std::endl;
1325 const double delta_wall_time = current.wall_time - previous.wall_time;
1326 const double delta_device_time =
1327 current.device_time_sum - previous.device_time_sum;
1329 const double utilization =
1330 delta_device_time / (n_devices_ * delta_wall_time);
1332 const double device_m_dofs_per_sec =
1333 delta_cycles * n_dofs * efficiency / 1.e6 / delta_device_time;
1335 << std::setprecision(4) << std::fixed << device_m_dofs_per_sec
1336 <<
" MQ/s in compute kernels (on "
1337 << n_devices_ << (n_devices_ == 1 ?
" device)" :
" devices)")
1341 << std::setprecision(2) << std::fixed << delta_device_time
1343 << std::setprecision(2) << std::fixed << delta_wall_time
1345 << std::setprecision(1) << std::fixed << 100. * utilization
1346 <<
"% utilization ]" << std::endl;
1351 double cpu_m_dofs_per_sec = delta_cycles * n_dofs * efficiency / 1.e6 /
1352 (current.cpu_time_sum - previous.cpu_time_sum);
1355 const bool fudge_cpu_timings = terminal_correct_for_hypertreadhing_ &&
1356#if defined(WITH_OPENMP)
1357 (omp_get_max_threads() == 2);
1358#elif defined(WITH_DEAL_II_THREADS)
1359 (MultithreadInfo::n_threads() == 2);
1364 if (fudge_cpu_timings)
1365 cpu_m_dofs_per_sec *= 2.;
1367 double cpu_time_skew = (current.cpu_time_max - current.cpu_time_min -
1368 previous.cpu_time_max + previous.cpu_time_min) /
1371 cpu_time_skew = std::max(0., cpu_time_skew);
1373 const double cpu_time_skew_percentage =
1374 cpu_time_skew * delta_cycles /
1375 (current.cpu_time_avg - previous.cpu_time_avg);
1378 << (fudge_cpu_timings ?
"CPU*: " :
"CPU : ")
1379 << std::setprecision(4) << std::fixed << cpu_m_dofs_per_sec
1381 << std::scientific << 1. / cpu_m_dofs_per_sec * 1.e-6
1382 <<
" s/Qdof/substep)" << std::endl;
1384 output <<
" [cpu time skew: "
1385 << std::setprecision(2) << std::scientific << cpu_time_skew
1387 << std::setprecision(1) << std::setw(4) << std::setfill(
' ') << std::fixed
1388 << 100. * cpu_time_skew_percentage
1389 <<
"%)]" << std::endl;
1393 << std::setprecision(4) << std::fixed << wall_m_dofs_per_sec
1395 << std::scientific << 1. / wall_m_dofs_per_sec * 1.e-6
1396 <<
" s/Qdof/substep) ("
1397 << std::setprecision(2) << std::fixed << cycles_per_second
1398 <<
" cycles/s)" << std::endl;
1400 const auto &scheme = time_integrator_.time_stepping_scheme();
1402 << Patterns::Tools::Convert<TimeSteppingScheme>::to_string(scheme)
1404 << std::setprecision(2) << std::fixed << hyperbolic_module_.cfl()
1406 << std::setprecision(0) << std::fixed << hyperbolic_module_.n_restarts()
1408 << std::setprecision(0) << std::fixed << parabolic_module_.n_restarts()
1410 << std::setprecision(0) << std::fixed << hyperbolic_module_.n_warnings()
1412 << std::setprecision(0) << std::fixed << parabolic_module_.n_warnings()
1414 << std::setprecision(0) << std::fixed << hyperbolic_module_.n_corrections()
1416 << std::setprecision(0) << std::fixed << parabolic_module_.n_corrections()
1417 <<
" corr) ]" << std::endl;
1419 if constexpr (!ParabolicSystem::is_identity)
1420 parabolic_module_.print_solver_statistics(output);
1422 output <<
" [ dt = "
1423 << std::scientific << std::setprecision(2) << delta_time
1426 <<
" dt/s) ]" << std::endl;
1431 time_per_second_exp = 0.8 * time_per_second_exp + 0.2 * time_per_second;
1432 auto eta =
static_cast<unsigned int>(std::max(t_final_ - t, Number(0.)) /
1433 time_per_second_exp);
1435 output <<
"\n ETA : ";
1437 const unsigned int days = eta / (24 * 3600);
1439 output << days <<
" d ";
1443 const unsigned int hours = eta / 3600;
1445 output << hours <<
" h ";
1449 const unsigned int minutes = eta / 60;
1450 output << minutes <<
" min";
1452 output <<
" (terminal update every "
1453 << std::setprecision(2) << std::fixed << terminal_update_interval_
1456 if (mpi_ensemble_.world_rank() != 0)
1459 stream << output.str() << std::endl;
1463 template <
typename Description,
int dim,
typename Number>
1464 void TimeLoop<Description, dim, Number>::print_cycle_statistics(
1467 unsigned int timer_cycle,
1468 Number last_checkpoint,
1469 bool write_to_logfile,
1472 std::ostringstream output;
1476 std::ostringstream primary;
1478 primary <<
"FINAL (cycle " << Utilities::int_to_string(cycle, 6) <<
")";
1480 primary <<
"Cycle " << Utilities::int_to_string(cycle, 6)
1481 <<
" (" << std::fixed << std::setprecision(1)
1482 << t / t_final_ * 100 <<
"%)";
1485 std::ostringstream secondary;
1486 secondary <<
"at time t = " << std::setprecision(8) << std::fixed << t;
1488 print_head(primary.str(), secondary.str(), output);
1492 print_information(timer_cycle, last_checkpoint, output, final_time);
1493 print_memory_statistics(output);
1494 print_timers(output);
1495 print_throughput(cycle, t, output, final_time);
1498 if (mpi_ensemble_.world_rank() != 0)
1502 std::cout <<
"\033[2J\033[H";
1504 std::cout << output.str() << std::flush;
1506 if (write_to_logfile) {
1507 logfile_ <<
"\n" << output.str() << std::flush;
static dealii::Timer & timer(const std::string §ion)
static const std::map< std::string, dealii::Timer > & timers()
TimeLoop(const MPI_Comm &mpi_comm)
typename View::StateVector StateVector
constexpr unsigned int warp_size
constexpr bool have_separate_memory_spaces
void debug_poison_invalid_values(StateVector< Number, prob_dim, prec_dim > &state_vector, const OfflineData &offline_data)
void print_revision_and_version(std::ostream &stream)