diff --git a/datahandling/datahandling_interface.py b/datahandling/datahandling_interface.py index 606a0a5cf681abc56e13e3e0781b1bd5cbaf306c..2ffe06a0752e5b7b2dfb9006310bce03d8ed903c 100644 --- a/datahandling/datahandling_interface.py +++ b/datahandling/datahandling_interface.py @@ -316,6 +316,20 @@ class DataHandling(ABC): result += row_format.format(arr_name, inner_min_max, with_gl_min_max) return result + def log(self, *args, level='INFO'): + """Similar to print with additional information (time, rank).""" + + def log_on_root(self, *args, level='INFO'): + """Logs only on root process. For serial setups this is equivalent to log""" + + @property + def is_root(self): + """Returns True for exactly one process in the simulation""" + + @property + def world_rank(self): + """Number of current process""" + class Block: """Represents locally stored part of domain. diff --git a/datahandling/parallel_datahandling.py b/datahandling/parallel_datahandling.py index 26cdc95a90abf302360eddbc2b3bd889117bbf3a..70caacb3c55ec16368756f2281df1a95bfdfb63f 100644 --- a/datahandling/parallel_datahandling.py +++ b/datahandling/parallel_datahandling.py @@ -341,3 +341,29 @@ class ParallelDataHandling(DataHandling): w = wlb.field.createBinarizationVTKWriter(self.blocks, data_name, mask, name) output.addCellDataWriter(w) return output + + @staticmethod + def log(*args, level='INFO'): + level = level.upper() + message = " ".join(str(e) for e in args) + ParallelDataHandling._log_map[level](message) + + def log_on_root(self, *args, level='INFO'): + if self.is_root: + ParallelDataHandling.log(*args, level=level) + + @property + def is_root(self): + return wlb.mpi.worldRank() == 0 + + @property + def world_rank(self): + return wlb.mpi.worldRank() + + _log_map = { + 'DEVEL': wlb.log_devel, + 'RESULT': wlb.log_result, + 'INFO': wlb.log_info, + 'WARNING': wlb.log_warning, + 'PROGRESS': wlb.log_progress, + } diff --git a/datahandling/serial_datahandling.py b/datahandling/serial_datahandling.py index 3bd2ff95c896c38be989a930293b3d010f3608c0..79221add8fe1606289eb6a5a4ebdce6ba0ba5543 100644 --- a/datahandling/serial_datahandling.py +++ b/datahandling/serial_datahandling.py @@ -1,6 +1,7 @@ import itertools from typing import Sequence, Union import numpy as np +import time from pystencils import Field from pystencils.datahandling.datahandling_interface import DataHandling from pystencils.field import layout_string_to_tuple, spatial_layout_string_to_tuple, create_numpy_array_with_layout @@ -48,6 +49,7 @@ class SerialDataHandling(DataHandling): self._periodicity = periodicity self._field_information = {} self.default_target = default_target + self._start_time = time.perf_counter() @property def dim(self): @@ -356,3 +358,23 @@ class SerialDataHandling(DataHandling): gl_to_remove = actual_ghost_layers - ghost_layers ind_dims = 1 if self._field_information[name]['values_per_cell'] > 1 else 0 return remove_ghost_layers(self.cpu_arrays[name], ind_dims, gl_to_remove) + + def log(self, *args, level='INFO'): + level = level.upper() + message = " ".join(str(e) for e in args) + + time_running = time.perf_counter() - self._start_time + spacing = 7 - len(str(int(time_running))) + message = "[{: <8}]{}({:.3f} sec) {} ".format(level, spacing * '-', time_running, message) + print(message, flush=True) + + def log_on_root(self, *args, level='INFO'): + self.log(*args, level=level) + + @property + def is_root(self): + return True + + @property + def world_rank(self): + return 0 diff --git a/timeloop.py b/timeloop.py index ec5b32e472a08f6e6448d375d4c4e75920206134..55129afb6991b4e8524486112838243d055ad067 100644 --- a/timeloop.py +++ b/timeloop.py @@ -63,16 +63,17 @@ class TimeLoop: def benchmark_run(self, time_steps=0, init_time_steps=0): init_time_steps_rounded = modulo_ceil(init_time_steps, self._fixed_steps) time_steps_rounded = modulo_ceil(time_steps, self._fixed_steps) + call_data = self._call_data self.pre_run() for i in range(init_time_steps_rounded // self._fixed_steps): - for func, kwargs in self._call_data: + for func, kwargs in call_data: func(**kwargs) self.time_steps_run += init_time_steps_rounded start = time.perf_counter() for i in range(time_steps_rounded // self._fixed_steps): - for func, kwargs in self._call_data: + for func, kwargs in call_data: func(**kwargs) end = time.perf_counter() self.time_steps_run += time_steps_rounded