From a6d9f0a21a867f288ed55ded8fd00c9d3f64d2d3 Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Mon, 18 Jun 2018 15:02:56 +0200
Subject: [PATCH] Thin Logging & MPI abstraction for data handling

---
 datahandling/datahandling_interface.py | 14 ++++++++++++++
 datahandling/parallel_datahandling.py  | 26 ++++++++++++++++++++++++++
 datahandling/serial_datahandling.py    | 22 ++++++++++++++++++++++
 timeloop.py                            |  5 +++--
 4 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/datahandling/datahandling_interface.py b/datahandling/datahandling_interface.py
index 606a0a5cf..2ffe06a07 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 26cdc95a9..70caacb3c 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 3bd2ff95c..79221add8 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 ec5b32e47..55129afb6 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
-- 
GitLab