diff --git a/Makefile.am b/Makefile.am
index c71cc8d00c797f0e2afc034cb1abfff7eba14c88..40ba64dcdd1c7270712288bce938ab56e918694d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,9 @@ SUBDIRS = src argparse examples doc tests tools
 if HAVEEAGLECOOLING
 SUBDIRS += examples/Cooling/CoolingRates
 endif
+if HAVELOGGER
+SUBDIRS += logger
+endif
 
 # Non-standard files that should be part of the distribution.
 EXTRA_DIST = INSTALL.swift .clang-format format.sh
diff --git a/configure.ac b/configure.ac
index 338edec60f956c37f666f7592a931b2c20a9f6e8..8d189c1210abf48304ca39b0fc6450323091eb7e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -86,6 +86,7 @@ AC_ARG_ENABLE([logger],
 if test "$with_logger" = "yes"; then
    AC_DEFINE([WITH_LOGGER], 1, [logger enabled])
 fi
+AM_CONDITIONAL([HAVELOGGER],[test $with_logger = "yes"])
 
 # Interprocedural optimization support. Needs special handling for linking and
 # archiving as well as compilation with Intels, needs to be done before
@@ -996,6 +997,41 @@ fi
 AC_SUBST([TBBMALLOC_LIBS])
 AM_CONDITIONAL([HAVETBBMALLOC],[test -n "$TBBMALLOC_LIBS"])
 
+# Check for python.
+have_python="no"
+AC_ARG_WITH([python],
+    [AS_HELP_STRING([--with-python=PATH],
+       [root directory where python is installed @<:@yes/no@:>@]
+    )],
+    [with_python="$withval"],
+    [with_python="no"]
+)
+if test "x$with_python" != "xno"; then
+   if test "$with_python" == ""; then
+      # use linux default python
+      with_python="/usr/"
+   fi
+   AM_PATH_PYTHON([3], [], [AC_MSG_ERROR(python not found)])   
+   AC_ARG_VAR([PYTHON_INCS], [Include flags for python, bypassing python-config])
+   AC_ARG_VAR([PYTHON_CONFIG], [Path to python-config])
+   AS_IF([test -z "$PYTHON_INCS"], [
+      AS_IF([test -z "$PYTHON_CONFIG"], [
+      	AC_PATH_PROGS([PYTHON_CONFIG],
+	          [python$PYTHON_VERSION-config python-config],
+                  [no],
+                  [`dirname $PYTHON`])
+    	AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
+      ])
+      AC_MSG_CHECKING([python include flags])
+      PYTHON_INCS=`$PYTHON_CONFIG --includes`
+      AC_MSG_RESULT([$PYTHON_INCS])
+  ])
+  have_python="yes"
+fi
+AC_SUBST([PYTHON_INCS])
+AM_CONDITIONAL([HAVEPYTHON],[test -n "$PYTHON_INCS"])
+
+
 # Check for HDF5. This is required.
 AX_LIB_HDF5
 if test "$with_hdf5" != "yes"; then
@@ -1991,7 +2027,7 @@ AM_CONDITIONAL([HAVEEAGLEFEEDBACK], [test $with_feedback = "EAGLE"])
 
 # Handle .in files.
 AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile])
-AC_CONFIG_FILES([argparse/Makefile tools/Makefile])
+AC_CONFIG_FILES([argparse/Makefile tools/Makefile logger/Makefile logger/tests/Makefile])
 AC_CONFIG_FILES([tests/testReading.sh], [chmod +x tests/testReading.sh])
 AC_CONFIG_FILES([tests/testActivePair.sh], [chmod +x tests/testActivePair.sh])
 AC_CONFIG_FILES([tests/test27cells.sh], [chmod +x tests/test27cells.sh])
@@ -2044,7 +2080,6 @@ AC_MSG_RESULT([
    CPU profiler         : $have_profiler
    Pthread barriers     : $have_pthread_barrier
    VELOCIraptor enabled : $have_velociraptor
-   Particle Logger      : $with_logger
    FoF activated:       : $enable_fof
 
    Hydro scheme       : $with_hydro
@@ -2082,4 +2117,7 @@ AC_MSG_RESULT([
    Custom icbrtf               : $enable_custom_icbrtf
    Boundary particles          : $boundary_particles
 
+   Particle Logger      : $with_logger
+   Python enabled       : $have_python
+
  ------------------------])
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index c6b3046d2d3591c937dfd98cf75fb7697b90110f..94424f644e2f9e6dc4c436a42423ba667186e02b 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -777,6 +777,7 @@ INPUT		       += @top_srcdir@/src/tracers/EAGLE
 INPUT		       += @top_srcdir@/src/stars/EAGLE
 INPUT		       += @top_srcdir@/src/feedback/EAGLE
 INPUT		       += @top_srcdir@/src/black_holes/EAGLE
+INPUT		       += @top_srcdir@/logger
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/examples/main.c b/examples/main.c
index 1d7aa8777f97627615bd23568c66d7bc4e7ecb88..9f9c0a471370a208251fe1c3628d3d980b476af4 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -556,9 +556,12 @@ int main(int argc, char *argv[]) {
   if (with_mpole_reconstruction && nr_nodes > 1)
     error("Cannot reconstruct m-poles every step over MPI (yet).");
   if (with_limiter) error("Can't run with time-step limiter over MPI (yet)");
+#ifdef WITH_LOGGER
+  error("Can't run with the particle logger over MPI (yet)");
+#endif
 #endif
 
-    /* Temporary early aborts for modes not supported with hand-vec. */
+  /* Temporary early aborts for modes not supported with hand-vec. */
 #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \
     !defined(CHEMISTRY_NONE)
   error(
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 7d8a5886ebcde64a39defa24b9f1e9fbd2232642..db7b8fddc4afac64d539d8e55952670ae937a57b 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -134,9 +134,9 @@ Snapshots:
 # Parameters governing the logger snapshot system
 Logger:
   delta_step:           10     # Update the particle log every this many updates
-  initial_buffer_size:  1      # buffer size in GB
-  buffer_scale:		10     # (Optional) When buffer size is too small, update it with required memory times buffer_scale
   basename:             index  # Common part of the filenames
+  initial_buffer_size:  1      # (Optional) Buffer size in GB
+  buffer_scale:	        10     # (Optional) When buffer size is too small, update it with required memory times buffer_scale
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/logger/Makefile.am b/logger/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..3bfd5af848c504d50fe201e02f49186287fbfb5a
--- /dev/null
+++ b/logger/Makefile.am
@@ -0,0 +1,73 @@
+# This file is part of SWIFT.
+# Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
+#                    Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+#                    Loic Hausammann (loic.hausammann@epfl.ch)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Add the non-standard paths to the included library headers
+AM_CFLAGS = $(PYTHON_INCS) -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS)
+
+
+AM_LDFLAGS = $(HDF5_LDFLAGS)
+
+# Assign a "safe" version number
+BIN_LDFLAGS = -version-info 0:0:0
+
+# The git command, if available.
+GIT_CMD = @GIT_CMD@
+
+# Additional dependencies for shared libraries.
+EXTRA_LIBS = $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) \
+	$(VELOCIRAPTOR_LIBS) $(GSL_LIBS) -L../src/.libs -lswiftsim
+
+# MPI libraries.
+# MPI_LIBS = $(MPI_THREAD_LIBS)
+# MPI_FLAGS = -DWITH_MPI
+
+# Build the liblogger library
+lib_LTLIBRARIES = liblogger.la
+# Build a MPI-enabled version too?
+# if HAVEMPI
+# lib_LTLIBRARIES += liblogger_mpi.la
+# endif
+
+# subdirectories
+SUBDIRS = tests
+
+# List required headers
+include_HEADERS = logger_header.h logger_loader_io.h logger_particle.h logger_time.h logger_tools.h logger_reader.h \
+	logger_logfile.h
+
+# Common source files
+AM_SOURCES = logger_header.c logger_loader_io.c logger_particle.c logger_time.c logger_tools.c logger_reader.c \
+	logger_logfile.c
+if HAVEPYTHON
+AM_SOURCES += logger_python_wrapper.c
+endif
+
+# Include files for distribution, not installation.
+nobase_noinst_HEADERS = 
+
+# Sources and flags for regular library
+liblogger_la_SOURCES = $(AM_SOURCES)
+liblogger_la_CFLAGS = $(AM_CFLAGS)
+liblogger_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS) $(BIN_LDFLAGS)
+
+# Sources and flags for MPI library
+# liblogger_mpi_la_SOURCES = $(AM_SOURCES)
+# liblogger_mpi_la_CFLAGS = $(AM_CFLAGS) $(MPI_FLAGS)
+# liblogger_mpi_la_LDFLAGS = $(AM_LDFLAGS) $(MPI_LIBS) $(EXTRA_LIBS)
+# liblogger_mpi_la_SHORTNAME = mpi
+# liblogger_mpi_la_LIBADD =
diff --git a/logger/logger_header.c b/logger/logger_header.c
new file mode 100644
index 0000000000000000000000000000000000000000..61e5da246c9aa07eeeb42e751832f017fa04ca0a
--- /dev/null
+++ b/logger/logger_header.c
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_header.h"
+
+#include "logger_loader_io.h"
+#include "logger_logfile.h"
+#include "logger_reader.h"
+#include "logger_tools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Name of each offset direction. */
+const char *logger_offset_name[logger_offset_count] = {
+    "Forward",
+    "Backward",
+    "Corrupted",
+};
+
+/**
+ * @brief Print the properties of the header to stdout.
+ *
+ * @param h The #header.
+ */
+void header_print(const struct header *h) {
+#ifdef SWIFT_DEBUG_CHECKS
+  message("Debug checks enabled.");
+#endif
+  message("First Offset:     %lu.", h->offset_first_record);
+  message("Offset direction: %s.", logger_offset_name[h->offset_direction]);
+  message("Number masks:     %i.", h->number_mask);
+
+  for (size_t i = 0; i < h->number_mask; i++) {
+    message("  Mask:  %s.", h->masks[i].name);
+    message("  Value: %u.", h->masks[i].mask);
+    message("  Size:  %i.", h->masks[i].size);
+    message("");
+  }
+};
+
+/**
+ * @brief free the allocated memory.
+ *
+ * @param h The #header.
+ */
+void header_free(struct header *h) { free(h->masks); };
+
+/**
+ * @brief Check if a field is present in the header.
+ *
+ * @param h The #header.
+ * @param field name of the requested field.
+ * @return Index of the field (-1 if not found).
+ */
+int header_get_field_index(const struct header *h, const char *field) {
+  for (size_t i = 0; i < h->number_mask; i++) {
+    if (strcmp(h->masks[i].name, field) == 0) {
+      return i;
+    }
+  }
+
+  return -1;
+};
+
+/**
+ * @brief Update the offset direction in the structure and
+ * write it to the logfile.
+ *
+ * @param h #header file structure.
+ * @param new_value The new value to write.
+ *
+ */
+void header_change_offset_direction(struct header *h,
+                                    enum logger_offset_direction new_value) {
+  h->offset_direction = new_value;
+  /* Skip file format and version numbers. */
+  size_t offset = LOGGER_VERSION_SIZE + 2 * sizeof(int);
+
+  logger_loader_io_write_data(h->log->log.map + offset, sizeof(unsigned int),
+                              &new_value);
+}
+
+/**
+ * @brief read the logger header.
+ *
+ * @param h out: The #header.
+ * @param log The #logger_logfile.
+ */
+void header_read(struct header *h, struct logger_logfile *log) {
+  void *map = log->log.map;
+
+  /* Set pointer to log. */
+  h->log = log;
+
+  /* read the file format. */
+  char file_format[STRING_SIZE];
+  map = logger_loader_io_read_data(map, LOGGER_VERSION_SIZE, &file_format);
+  if (strcmp(file_format, "SWIFT_LOGGER"))
+    error("Wrong file format (%s).", file_format);
+
+  /* Read the major version number. */
+  map = logger_loader_io_read_data(map, sizeof(int), &h->major_version);
+
+  /* Read the minor version number. */
+  map = logger_loader_io_read_data(map, sizeof(int), &h->minor_version);
+
+  struct logger_reader *reader = log->reader;
+  if (&reader->log != log) error("Wrong link to the reader.");
+
+  if (reader->verbose > 0)
+    message("File version %i.%i.", h->major_version, h->minor_version);
+
+  /* Read the offset directions. */
+  map = logger_loader_io_read_data(map, sizeof(int), &h->offset_direction);
+
+  if (!header_is_forward(h) && !header_is_backward(h) &&
+      !header_is_corrupted(h))
+    error("Wrong offset value in the header (%i).", h->offset_direction);
+
+  /* Read offset to first record. */
+  map = logger_loader_io_read_data(map, LOGGER_OFFSET_SIZE,
+                                   &h->offset_first_record);
+
+  /* Read the size of the strings. */
+  map =
+      logger_loader_io_read_data(map, sizeof(unsigned int), &h->string_length);
+
+  /* Check if value defined in this file is large enough. */
+  if (STRING_SIZE < h->string_length) {
+    error("Name too large in log file %i.", h->string_length);
+  }
+
+  /* Read the number of masks. */
+  map = logger_loader_io_read_data(map, sizeof(unsigned int), &h->number_mask);
+
+  /* Allocate the masks memory. */
+  h->masks = malloc(sizeof(struct mask_data) * h->number_mask);
+
+  /* Loop over all masks. */
+  for (size_t i = 0; i < h->number_mask; i++) {
+    /* Read the mask name. */
+    map = logger_loader_io_read_data(map, h->string_length, h->masks[i].name);
+
+    /* Set the mask value. */
+    h->masks[i].mask = 1 << i;
+
+    /* Read the mask data size. */
+    map = logger_loader_io_read_data(map, sizeof(unsigned int),
+                                     &h->masks[i].size);
+  }
+
+  /* Check the logfile header's size. */
+  if (map != log->log.map + h->offset_first_record) {
+    header_print(h);
+    size_t offset = map - log->log.map;
+    error("Wrong header size (in header %zi, current %zi).",
+          h->offset_first_record, offset);
+  }
+};
+
+/**
+ * @brief Count number of bits in a given mask (without the record header).
+ *
+ * @param h #header file structure.
+ * @param mask Mask to compute.
+ *
+ * @return number of bits in mask.
+ */
+size_t header_get_record_size_from_mask(const struct header *h,
+                                        const size_t mask) {
+  size_t count = 0;
+  /* Loop over each masks. */
+  for (size_t i = 0; i < h->number_mask; i++) {
+    if (mask & h->masks[i].mask) {
+      count += h->masks[i].size;
+    }
+  }
+  return count;
+}
diff --git a/logger/logger_header.h b/logger/logger_header.h
new file mode 100644
index 0000000000000000000000000000000000000000..c388ef65cda21d00f53ddc54e97f43671edf1aeb
--- /dev/null
+++ b/logger/logger_header.h
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef LOGGER_LOGGER_HEADER_H
+#define LOGGER_LOGGER_HEADER_H
+
+#include "logger_tools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define LOGGER_VERSION_SIZE 20
+#define LOGGER_OFFSET_SIZE 7
+#define LOGGER_MASK_SIZE 1
+
+enum logger_offset_direction {
+  logger_offset_backward = 0,
+  logger_offset_forward,
+  logger_offset_corrupted,
+  /* Number of offset type. */
+  logger_offset_count,
+};
+
+/**
+ * @brief Names of the offset directions.
+ */
+extern const char *logger_offset_name[];
+
+struct logger_logfile;
+
+/**
+ * @brief This structure contains everything from the file header.
+ *
+ * This structure is initialized by #header_read and need to be freed
+ * with #header_free.
+ *
+ * The information contained by the header can be easily access with
+ * the functions #header_get_record_size_from_mask and #header_get_field_index.
+ *
+ * The only function that modify the file is #header_change_offset_direction.
+ */
+struct header {
+  /* Dump's major version. */
+  int major_version;
+
+  /* Dump's minor version. */
+  int minor_version;
+
+  /* Offset of the first record. */
+  size_t offset_first_record;
+
+  /* Number of bytes for strings. */
+  unsigned int string_length;
+
+  /* Number of masks. */
+  unsigned int number_mask;
+
+  /* List of masks. */
+  struct mask_data *masks;
+
+  /* Direction of the offset in the records. */
+  enum logger_offset_direction offset_direction;
+
+  /* The corresponding log. */
+  struct logger_logfile *log;
+};
+
+void header_print(const struct header *h);
+void header_free(struct header *h);
+int header_get_field_index(const struct header *h, const char *field);
+void header_read(struct header *h, struct logger_logfile *log);
+size_t header_get_record_size_from_mask(const struct header *h,
+                                        const size_t mask);
+void header_change_offset_direction(struct header *h,
+                                    enum logger_offset_direction new_value);
+
+/**
+ * @brief Check if the offset are forward.
+ * @param h The #header.
+ */
+__attribute__((always_inline)) INLINE static int header_is_forward(
+    const struct header *h) {
+  return h->offset_direction == logger_offset_forward;
+}
+
+/**
+ * @brief Check if the offset are backward.
+ * @param h The #header.
+ */
+__attribute__((always_inline)) INLINE static int header_is_backward(
+    const struct header *h) {
+  return h->offset_direction == logger_offset_backward;
+}
+
+/**
+ * @brief Check if the offset are corrupted.
+ * @param h The #header.
+ */
+__attribute__((always_inline)) INLINE static int header_is_corrupted(
+    const struct header *h) {
+  return h->offset_direction == logger_offset_corrupted;
+}
+
+#endif  // LOGGER_LOGGER_HEADER_H
diff --git a/logger/logger_loader_io.c b/logger/logger_loader_io.c
new file mode 100644
index 0000000000000000000000000000000000000000..f18f9bb7eb2eaf88ba11eaf916c0a68a27cfd2d2
--- /dev/null
+++ b/logger/logger_loader_io.c
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "logger_header.h"
+#include "logger_loader_io.h"
+#include "logger_tools.h"
+
+/**
+ * @brief get the size of a file.
+ *
+ * @param fd file id.
+ *
+ * @return file size.
+ */
+size_t logger_loader_io_get_file_size(int fd) {
+  struct stat s;
+  int status = fstat(fd, &s);
+  if (status != 0) error("Unable to get file size (%s).", strerror(errno));
+  return s.st_size;
+}
+
+/**
+ * @brief Map a file.
+ *
+ * #logger_loader_io_munmap_file should be called to unmap the file.
+ *
+ * @param filename file to read.
+ * @param file_size (out) size of the file.
+ * @param read_only Open the file in read only mode?
+ *
+ */
+void *logger_loader_io_mmap_file(char *filename, size_t *file_size,
+                                 int read_only) {
+  /* open the file. */
+  int fd;
+
+  if (read_only)
+    fd = open(filename, O_RDONLY);
+  else
+    fd = open(filename, O_RDWR);
+
+  if (fd == -1)
+    error("Unable to open file %s (%s).", filename, strerror(errno));
+
+  /* get the file size. */
+  *file_size = logger_loader_io_get_file_size(fd);
+
+  /* map the memory. */
+  int mode = PROT_READ;
+  if (!read_only) mode |= PROT_WRITE;
+
+  void *map = mmap(NULL, *file_size, mode, MAP_SHARED, fd, 0);
+  if (map == MAP_FAILED)
+    error("Failed to allocate map of size %zi bytes (%s).", *file_size,
+          strerror(errno));
+
+  /* Close the file. */
+  close(fd);
+
+  return map;
+}
+
+/**
+ * @brief Unmap a file.
+ *
+ * @param map file mapping.
+ * @param file_size The file size.
+ *
+ */
+void logger_loader_io_munmap_file(void *map, size_t file_size) {
+  /* unmap the file. */
+  if (munmap(map, file_size) != 0) {
+    error("Unable to unmap the file (%s).", strerror(errno));
+  }
+}
diff --git a/logger/logger_loader_io.h b/logger/logger_loader_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..d44fea673017644306e73261afdbc6dec26948c6
--- /dev/null
+++ b/logger/logger_loader_io.h
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+/**
+ * @file logger_loader_io.h
+ * @brief This file contains basic IO function.
+ */
+#ifndef LOGGER_LOGGER_LOADER_IO_H
+#define LOGGER_LOGGER_LOADER_IO_H
+
+#include "logger_header.h"
+#include "logger_tools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+size_t logger_loader_io_get_file_size(int fd);
+void *logger_loader_io_mmap_file(char *filename, size_t *file_size,
+                                 int read_only);
+void logger_loader_io_munmap_file(void *map, size_t file_size);
+
+/**
+ * @brief read a mask with its offset.
+ *
+ * @param h #header file structure.
+ * @param data Pointer to the data to read.
+ * @param mask (output) mask read from the data.
+ * @param diff_offset (output) offset difference to previous/next corresponding
+ * record.
+ *
+ * @return memory after the record header.
+ */
+__attribute__((always_inline)) INLINE static void *logger_loader_io_read_mask(
+    const struct header *h, void *data, size_t *mask, size_t *diff_offset) {
+  /* read mask */
+  if (mask) {
+    *mask = 0;
+    memcpy(mask, data, LOGGER_MASK_SIZE);
+  }
+  data += LOGGER_MASK_SIZE;
+
+  /* read offset */
+  if (diff_offset) {
+    *diff_offset = 0;
+    memcpy(diff_offset, data, LOGGER_OFFSET_SIZE);
+  }
+  data += LOGGER_OFFSET_SIZE;
+
+  return data;
+}
+
+/**
+ * @brief read a single value from a file.
+ *
+ * @param data Pointer to the data to read.
+ * @param size size of the data to read.
+ * @param p pointer where to store the data.
+
+ * @return memory after the data written.
+ */
+__attribute__((always_inline)) INLINE static void *logger_loader_io_read_data(
+    void *data, const size_t size, void *p) {
+  memcpy(p, data, size);
+  return data + size;
+};
+
+/**
+ * @brief write a single value in a file.
+ *
+ * @param data Pointer to the data to read.
+ * @param size size of the data to write.
+ * @param p pointer to the data.
+ *
+ * @return memory after the data written.
+ */
+__attribute__((always_inline)) INLINE static void *logger_loader_io_write_data(
+    void *data, const size_t size, const void *p) {
+  memcpy(data, p, size);
+
+  return data + size;
+};
+
+#endif  // LOGGER_LOGGER_LOADER_IO_H
diff --git a/logger/logger_logfile.c b/logger/logger_logfile.c
new file mode 100644
index 0000000000000000000000000000000000000000..c70068cd24c01a5ba231e97e343a0c076dc0ecb4
--- /dev/null
+++ b/logger/logger_logfile.c
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_logfile.h"
+#include "logger_loader_io.h"
+#include "logger_reader.h"
+
+/**
+ * @brief Initialize the #logger_logfile.
+ *
+ * If required this function will also reverse the offsets.
+ * @param log The #logger_logfile.
+ * @param filename the log's filename.
+ * @param reader The #logger_reader.
+ * @param only_header Read only the header.
+ */
+void logger_logfile_init_from_file(struct logger_logfile *log, char *filename,
+                                   struct logger_reader *reader,
+                                   int only_header) {
+
+  /* Set the pointer to the reader. */
+  log->reader = reader;
+  if (&reader->log != log) error("Wrong link to the reader.");
+
+  /* Set pointers to zero. */
+  time_array_init(&log->times);
+
+  /* Open file, map it and get its size. */
+  if (reader->verbose > 1) message("Mapping the log file.");
+  log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size,
+                                            /* read_only */ 1);
+
+  /* Read the header. */
+  if (reader->verbose > 1) message("Reading the header.");
+  header_read(&log->header, log);
+
+  /* Print the header. */
+  if (reader->verbose > 0) {
+    header_print(&log->header);
+  }
+
+  /* No need to continue if only the
+     header is required. */
+  if (only_header) return;
+
+  /* Check if the offset are corrupted. */
+  if (header_is_corrupted(&log->header)) {
+    error("The offsets have been corrupted.");
+  }
+
+  /* Reverse the offsets direction. */
+  if (header_is_backward(&log->header)) {
+    logger_logfile_reverse_offset(log, filename);
+  }
+
+  /* Initialize the time array. */
+  if (reader->verbose > 1) message("Reading the time stamps.");
+  time_array_populate(&log->times, log);
+
+  /* Print the time array. */
+  if (reader->verbose > 0) {
+    time_array_print(&log->times);
+  }
+}
+
+/**
+ * @brief Free the allocated memory and unmap the file.
+ *
+ * @param log The #logger_logfile.
+ */
+void logger_logfile_free(struct logger_logfile *log) {
+  logger_loader_io_munmap_file(log->log.map, log->log.file_size);
+
+  time_array_free(&log->times);
+}
+
+/**
+ * @brief Reverse offset in log file
+ *
+ * @param log The #logger_logfile
+ * @param filename The log's filename.
+ */
+void logger_logfile_reverse_offset(struct logger_logfile *log, char *filename) {
+
+  /* Close and reopen the file in write mode. */
+  logger_loader_io_munmap_file(log->log.map, log->log.file_size);
+  log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size,
+                                            /* read_only */ 0);
+
+  /* Get pointers */
+  struct header *header = &log->header;
+  const struct logger_reader *reader = log->reader;
+  if (&reader->log != log) error("Wrong link to the reader.");
+
+  /* Check if the offsets need to be reversed. */
+  if (!header_is_backward(header)) {
+    error("The offsets are already reversed.");
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (reader->verbose > 0) {
+    message("Check record's headers...");
+  }
+
+  /* check that the record offset points to another record. */
+  for (size_t offset_debug = header->offset_first_record;
+       offset_debug < log->log.file_size;
+       offset_debug = tools_check_record_consistency(reader, offset_debug)) {
+  }
+
+  if (reader->verbose > 0) {
+    message("Record's headers are correct.");
+  }
+#endif
+
+  message("WARNING: Modifying the logfile, do not kill the job!");
+
+  /* Set the offset direction to a corrupted status. */
+  header_change_offset_direction(header, logger_offset_corrupted);
+
+  if (reader->verbose > 0) {
+    message("Reversing offsets...");
+  }
+
+  /* reverse the record's offset. */
+  for (size_t offset = header->offset_first_record; offset < log->log.file_size;
+       offset = tools_reverse_offset(header, log->log.map, offset)) {
+  }
+
+  if (reader->verbose > 0) {
+    message("Reversing done.");
+  }
+
+  /* Now that the offset are effectively reversed, can set the direction to
+     forward. */
+  header_change_offset_direction(header, logger_offset_forward);
+
+  message("WARNING: Modification done, you can now safely kill the job.");
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (reader->verbose > 0) {
+    message("Check record's headers...");
+  }
+
+  /* check that the record offset points to another record. */
+  for (size_t offset_debug = header->offset_first_record;
+       offset_debug < log->log.file_size;
+       offset_debug = tools_check_record_consistency(reader, offset_debug)) {
+  }
+
+  if (reader->verbose > 0) {
+    message("Record's headers are correct.");
+  }
+#endif
+
+  /* Close and reopen the file in read mode. */
+  logger_loader_io_munmap_file(log->log.map, log->log.file_size);
+  log->log.map = logger_loader_io_mmap_file(filename, &log->log.file_size,
+                                            /* read_only */ 1);
+}
diff --git a/logger/logger_logfile.h b/logger/logger_logfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b6ef728d524bb104b83fc28b9250c51a764dfd4
--- /dev/null
+++ b/logger/logger_logfile.h
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+/**
+ * @file logger_logfile.h
+ * @brief This file contains the high level function for the log.
+ */
+#ifndef LOGGER_LOGGER_LOGFILE_H
+#define LOGGER_LOGGER_LOGFILE_H
+
+#include "logger_header.h"
+#include "logger_time.h"
+
+struct logger_reader;
+
+/**
+ * @brief This structure deals with the log file.
+ *
+ * This structure is initialized by the #logger_reader
+ * and deals with the log file.
+ * It maps it, reverse the offsets (if required) and unmap it.
+ *
+ * The structure is initialized with #logger_logfile_init_from_file and
+ * freed with #logger_logfile_free.
+ */
+struct logger_logfile {
+
+  /* Information contained in the file header. */
+  struct header header;
+
+  /* The reader that is using this log file. */
+  struct logger_reader *reader;
+
+  /* Information about the time records. */
+  struct time_array times;
+
+  /* The log's variables. */
+  struct {
+    /* Mapped data. */
+    void *map;
+
+    /* File size. */
+    size_t file_size;
+
+  } log;
+};
+
+void logger_logfile_init_from_file(struct logger_logfile *log, char *filename,
+                                   struct logger_reader *reader,
+                                   int only_header);
+void logger_logfile_reverse_offset(struct logger_logfile *log, char *filename);
+void logger_logfile_free(struct logger_logfile *log);
+
+#endif  // LOGGER_LOGGER_LOGFILE_H
diff --git a/logger/logger_particle.c b/logger/logger_particle.c
new file mode 100644
index 0000000000000000000000000000000000000000..6809e0edf6125e66cbb8807cc98eeb31b5e04ecd
--- /dev/null
+++ b/logger/logger_particle.c
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_particle.h"
+#include "logger_header.h"
+#include "logger_loader_io.h"
+#include "logger_reader.h"
+#include "logger_time.h"
+#include "logger_tools.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * @brief Print the properties of a logger_particle.
+ *
+ * @param p The #logger_particle to print
+ */
+void logger_particle_print(const struct logger_particle *p) {
+  message("ID:            %lu.", p->id);
+  message("Mass:          %g", p->mass);
+  message("Time:          %g.", p->time);
+  message("Cutoff Radius: %g.", p->h);
+  message("Positions:     (%g, %g, %g).", p->pos[0], p->pos[1], p->pos[2]);
+  message("Velocities:    (%g, %g, %g).", p->vel[0], p->vel[1], p->vel[2]);
+  message("Accelerations: (%g, %g, %g).", p->acc[0], p->acc[1], p->acc[2]);
+  message("Entropy:       %g.", p->entropy);
+  message("Density:       %g.", p->density);
+}
+
+/**
+ * @brief Initialize a logger_particle.
+ *
+ * @param part The #logger_particle to initialize.
+ */
+void logger_particle_init(struct logger_particle *part) {
+  for (size_t k = 0; k < DIM; k++) {
+    part->pos[k] = 0;
+    part->vel[k] = 0;
+    part->acc[k] = 0;
+  }
+
+  part->entropy = -1;
+  part->density = -1;
+  part->h = -1;
+  part->mass = -1;
+  part->id = SIZE_MAX;
+}
+
+/**
+ * @brief Read a single named entry for a particle.
+ *
+ * @param part The #logger_particle to update.
+ * @param map The mapped data.
+ * @param field field to read.
+ * @param size number of bits to read.
+ *
+ * @return mapped data after the block read.
+ */
+void *logger_particle_read_field(struct logger_particle *part, void *map,
+                                 const char *field, const size_t size) {
+  void *p = NULL;
+
+  /* Get the correct pointer. */
+  if (strcmp("positions", field) == 0) {
+    p = &part->pos;
+  } else if (strcmp("velocities", field) == 0) {
+    p = &part->vel;
+  } else if (strcmp("accelerations", field) == 0) {
+    p = &part->acc;
+  } else if (strcmp("entropy", field) == 0) {
+    p = &part->entropy;
+  } else if (strcmp("smoothing length", field) == 0) {
+    p = &part->h;
+  } else if (strcmp("density", field) == 0) {
+    p = &part->density;
+  } else if (strcmp("consts", field) == 0) {
+    p = malloc(size);
+  } else {
+    error("Type %s not defined.", field);
+  }
+
+  /* read the data. */
+  map = logger_loader_io_read_data(map, size, p);
+
+  /* Split the required fields. */
+  if (strcmp("consts", field) == 0) {
+    part->mass = 0;
+    part->id = 0;
+    memcpy(&part->mass, p, sizeof(float));
+    p += sizeof(float);
+    memcpy(&part->id, p, sizeof(size_t));
+    p -= sizeof(float);
+    free(p);
+  }
+
+  return map;
+}
+
+/**
+ * @brief Read a particle entry in the log file.
+ *
+ * @param reader The #logger_reader.
+ * @param part The #logger_particle to update.
+ * @param offset offset of the record to read.
+ * @param time time to interpolate.
+ * @param reader_type #logger_reader_type.
+ *
+ * @return position after the record.
+ */
+size_t logger_particle_read(struct logger_particle *part,
+                            const struct logger_reader *reader, size_t offset,
+                            const double time,
+                            const enum logger_reader_type reader_type) {
+
+  /* Get a few pointers. */
+  const struct header *h = &reader->log.header;
+  void *map = reader->log.log.map;
+
+  const struct time_array *times = &reader->log.times;
+
+  size_t mask = 0;
+  size_t h_offset = 0;
+
+  logger_particle_init(part);
+
+  /* Read the record's mask. */
+  map = logger_loader_io_read_mask(h, map + offset, &mask, &h_offset);
+
+  /* Check if it is not a time record. */
+  if (mask == 128) error("Unexpected mask: %lu.", mask);
+
+  /* Read all the fields. */
+  for (size_t i = 0; i < h->number_mask; i++) {
+    if (mask & h->masks[i].mask) {
+      map = logger_particle_read_field(part, map, h->masks[i].name,
+                                       h->masks[i].size);
+    }
+  }
+
+  /* Get the time of current record.
+     This check is required for the manipulating the file before
+     the initialization of the time_array. */
+  if (times->size != 0) {
+    part->time = time_array_get_time(times, offset);
+  } else
+    part->time = -1;
+
+  /* update the offset. */
+  offset = (size_t)(map - reader->log.log.map);
+
+  /* Check if an interpolation is required. */
+  if (reader_type == logger_reader_const) return offset;
+
+  /* Start reading next record. */
+  struct logger_particle part_next;
+
+  /* Check that the offset are in the correct direction. */
+  if (!header_is_forward(h)) {
+    error("Cannot read a particle with non forward offsets.");
+  }
+
+  /* No next particle. */
+  if (h_offset == 0) return (size_t)(map - reader->log.log.map);
+
+  /* get absolute offset of next particle. */
+  h_offset += offset - header_get_record_size_from_mask(h, mask) -
+              LOGGER_MASK_SIZE - LOGGER_OFFSET_SIZE;
+
+  /* Get time of next record. */
+  part_next.time = time_array_get_time(times, h_offset);
+
+  /* Read next record. */
+  h_offset = logger_particle_read(&part_next, reader, h_offset, part_next.time,
+                                  logger_reader_const);
+
+  /* Interpolate the two particles. */
+  logger_particle_interpolate(part, &part_next, time);
+
+  return offset;
+}
+
+/**
+ * @brief interpolate two particles at a given time
+ *
+ * @param part_curr #logger_particle In: current particle (before time), Out:
+ * interpolated particle
+ * @param part_next #logger_particle next particle (after time)
+ * @param time interpolation time
+ *
+ */
+void logger_particle_interpolate(struct logger_particle *part_curr,
+                                 const struct logger_particle *part_next,
+                                 const double time) {
+
+  /* Check that a particle is provided. */
+  if (!part_curr) error("part_curr is NULL.");
+  if (!part_next) error("part_next is NULL.");
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check the particle order. */
+  if (part_next->time <= part_curr->time)
+    error("Wrong particle order (next before current).");
+  if ((time < part_curr->time) || (part_next->time < time))
+    error(
+        "Cannot extrapolate (particle time: %f, "
+        "interpolating time: %f, next particle time: %f).",
+        part_curr->time, time, part_next->time);
+#endif
+
+  /* Compute the interpolation scaling. */
+  double scaling = part_next->time - part_curr->time;
+
+  scaling = (time - part_curr->time) / scaling;
+
+  double tmp;
+  float ftmp;
+
+  /* interpolate vectors. */
+  for (size_t i = 0; i < DIM; i++) {
+    tmp = (part_next->pos[i] - part_curr->pos[i]);
+    part_curr->pos[i] += tmp * scaling;
+
+    ftmp = (part_next->vel[i] - part_curr->vel[i]);
+    part_curr->vel[i] += ftmp * scaling;
+
+    ftmp = (part_next->acc[i] - part_curr->acc[i]);
+    part_curr->acc[i] += ftmp * scaling;
+  }
+
+  /* interpolate scalars. */
+  ftmp = (part_next->entropy - part_curr->entropy);
+  part_curr->entropy += ftmp * scaling;
+
+  /* set time. */
+  part_curr->time = time;
+}
diff --git a/logger/logger_particle.h b/logger/logger_particle.h
new file mode 100644
index 0000000000000000000000000000000000000000..addd23564b65a734152ae8f538596d79019dd36f
--- /dev/null
+++ b/logger/logger_particle.h
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef LOGGER_LOGGER_PARTICLE_H
+#define LOGGER_LOGGER_PARTICLE_H
+
+#include "logger_header.h"
+#include "logger_time.h"
+#include "logger_tools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(HYDRO_DIMENSION_1D)
+#define DIM 1
+#elif defined(HYDRO_DIMENSION_2D)
+#define DIM 2
+#elif defined(HYDRO_DIMENSION_3D)
+#define DIM 3
+#endif
+
+struct logger_reader;
+
+/**
+ * @brief Store the data from a record.
+ *
+ * This structure contains all the required fields
+ * present in a file.
+ *
+ * As we need only a few particles, no need to keep it small.
+ *
+ * The particle is initialized with #logger_particle_init
+ * and can be updated with a record through #logger_particle_read.
+ *
+ * In #logger_particle_read, we use #logger_particle_read_field on
+ * each field and #logger_particle_interpolate if a linear
+ * interpolation is required.
+ */
+struct logger_particle {
+  /* position. */
+  double pos[DIM];
+
+  /* velocity. */
+  float vel[DIM];
+
+  /* acceleration. */
+  float acc[DIM];
+
+  /* entropy. */
+  float entropy;
+
+  /* smoothing length. */
+  float h;
+
+  /* density. */
+  float density;
+
+  /* mass. */
+  float mass;
+
+  /* unique id. */
+  size_t id;
+
+  /* time of the record. */
+  double time;
+};
+
+/**
+ * @brief Defines the type of interpolation
+ */
+enum logger_reader_type {
+  logger_reader_const, /* Constant interpolation. */
+  logger_reader_lin,   /* Linear interpolation. */
+};
+
+void logger_particle_print(const struct logger_particle *p);
+
+size_t logger_particle_read(struct logger_particle *part,
+                            const struct logger_reader *reader, size_t offset,
+                            const double time,
+                            const enum logger_reader_type reader_type);
+
+void logger_particle_init(struct logger_particle *part);
+
+void *logger_particle_read_field(struct logger_particle *part, void *map,
+                                 const char *field, const size_t size);
+
+void logger_particle_interpolate(struct logger_particle *part_curr,
+                                 const struct logger_particle *part_next,
+                                 const double time);
+
+#endif  // LOGGER_LOGGER_PARTICLE_H
diff --git a/logger/logger_python_wrapper.c b/logger/logger_python_wrapper.c
new file mode 100644
index 0000000000000000000000000000000000000000..07c87b4989896977c56ddff4df243a5310d393a7
--- /dev/null
+++ b/logger/logger_python_wrapper.c
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_header.h"
+#include "logger_loader_io.h"
+#include "logger_particle.h"
+#include "logger_reader.h"
+#include "logger_time.h"
+
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+
+#include <Python.h>
+#include <errno.h>
+#include <numpy/arrayobject.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * @brief load data from the offset without any interpolation
+ *
+ * <b>offset</b> PyArrayObject list of offset for each particle.
+ *
+ * <b>filename</b> string filename of the log file.
+ *
+ * <b>verbose</b> Verbose level.
+ *
+ * <b>returns</b> dictionnary containing the data read.
+ */
+static PyObject *loadFromIndex(__attribute__((unused)) PyObject *self,
+                               PyObject *args) {
+
+  /* input variables. */
+  PyArrayObject *offset = NULL;
+  char *filename = NULL;
+
+  /* output variables. */
+  PyArrayObject *pos = NULL;
+  PyArrayObject *vel = NULL;
+  PyArrayObject *acc = NULL;
+  PyArrayObject *entropy = NULL;
+  PyArrayObject *h_sph = NULL;
+  PyArrayObject *rho = NULL;
+  PyArrayObject *mass = NULL;
+  PyArrayObject *id = NULL;
+
+  size_t time_offset;
+  int verbose = 2;
+
+  /* parse arguments. */
+  if (!PyArg_ParseTuple(args, "OsL|i", &offset, &filename, &time_offset,
+                        &verbose))
+    return NULL;
+
+  if (!PyArray_Check(offset)) {
+    error("Offset is not a numpy array.");
+  }
+  if (PyArray_NDIM(offset) != 1) {
+    error("Offset is not a 1 dimensional array.");
+  }
+  if (PyArray_TYPE(offset) != NPY_UINT64) {
+    error("Offset does not contain unsigned int.");
+  }
+
+  /* initialize the reader. */
+  struct logger_reader reader;
+  logger_reader_init(&reader, filename, verbose);
+  struct header *h = &reader.log.header;
+
+  /* init array. */
+  npy_intp dim[2];
+  dim[0] = PyArray_DIMS(offset)[0];
+  dim[1] = DIM;
+
+  /* Get required time. */
+  double time = time_array_get_time(&reader.log.times, time_offset);
+
+  /* init output. */
+  if (header_get_field_index(h, "positions") != -1) {
+    pos = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_DOUBLE);
+  }
+
+  if (header_get_field_index(h, "velocities") != -1) {
+    vel = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_FLOAT);
+  }
+
+  if (header_get_field_index(h, "accelerations") != -1) {
+    acc = (PyArrayObject *)PyArray_SimpleNew(2, dim, NPY_FLOAT);
+  }
+
+  if (header_get_field_index(h, "entropy") != -1) {
+    entropy =
+        (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT);
+  }
+
+  if (header_get_field_index(h, "smoothing length") != -1) {
+    h_sph =
+        (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT);
+  }
+
+  if (header_get_field_index(h, "density") != -1) {
+    rho =
+        (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT);
+  }
+
+  if (header_get_field_index(h, "consts") != -1) {
+    mass =
+        (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_FLOAT);
+    id = (PyArrayObject *)PyArray_SimpleNew(1, PyArray_DIMS(offset), NPY_ULONG);
+  }
+
+  if (verbose > 1) message("Reading particles.");
+
+  /* loop over all particles. */
+  for (npy_intp i = 0; i < PyArray_DIMS(offset)[0]; i++) {
+    struct logger_particle part;
+
+    /* Get the offset. */
+    size_t offset_particle = *(size_t *)PyArray_GETPTR1(offset, i);
+
+    /* Read the particle. */
+    logger_particle_read(&part, &reader, offset_particle, time,
+                         logger_reader_lin);
+
+    double *dtmp;
+    float *ftmp;
+    size_t *stmp;
+
+    /* copy the data. */
+    for (size_t k = 0; k < DIM; k++) {
+      if (pos) {
+        dtmp = PyArray_GETPTR2(pos, i, k);
+        *dtmp = part.pos[k];
+      }
+
+      if (vel) {
+        ftmp = PyArray_GETPTR2(vel, i, k);
+        *ftmp = part.vel[k];
+      }
+
+      if (acc) {
+        ftmp = PyArray_GETPTR2(acc, i, k);
+        *ftmp = part.acc[k];
+      }
+    }
+
+    if (entropy) {
+      ftmp = PyArray_GETPTR1(entropy, i);
+      *ftmp = part.entropy;
+    }
+
+    if (rho) {
+      ftmp = PyArray_GETPTR1(rho, i);
+      *ftmp = part.density;
+    }
+
+    if (h_sph) {
+      ftmp = PyArray_GETPTR1(h_sph, i);
+      *ftmp = part.h;
+    }
+
+    if (mass) {
+      ftmp = PyArray_GETPTR1(mass, i);
+      *ftmp = part.mass;
+    }
+
+    if (id) {
+      stmp = PyArray_GETPTR1(id, i);
+      *stmp = part.id;
+    }
+  }
+
+  /* Free the memory. */
+  logger_reader_free(&reader);
+
+  /* construct return value. */
+  PyObject *dict = PyDict_New();
+  PyObject *key = PyUnicode_FromString("positions");
+  PyDict_SetItem(dict, key, PyArray_Return(pos));
+
+  if (vel) {
+    key = PyUnicode_FromString("velocities");
+    PyDict_SetItem(dict, key, PyArray_Return(vel));
+  }
+
+  if (acc) {
+    key = PyUnicode_FromString("accelerations");
+    PyDict_SetItem(dict, key, PyArray_Return(acc));
+  }
+
+  if (entropy) {
+    key = PyUnicode_FromString("entropy");
+    PyDict_SetItem(dict, key, PyArray_Return(entropy));
+  }
+
+  if (rho) {
+    key = PyUnicode_FromString("rho");
+    PyDict_SetItem(dict, key, PyArray_Return(rho));
+  }
+
+  if (h_sph) {
+    key = PyUnicode_FromString("h_sph");
+    PyDict_SetItem(dict, key, PyArray_Return(h_sph));
+  }
+
+  if (mass) {
+    key = PyUnicode_FromString("mass");
+    PyDict_SetItem(dict, key, PyArray_Return(mass));
+  }
+
+  if (id) {
+    key = PyUnicode_FromString("id");
+    PyDict_SetItem(dict, key, PyArray_Return(id));
+  }
+
+  return dict;
+}
+
+/**
+ * @brief Reverse offset in log file
+ *
+ * <b>filename</b> string filename of the log file
+ * <b>verbose</b> Verbose level
+ */
+static PyObject *pyReverseOffset(__attribute__((unused)) PyObject *self,
+                                 PyObject *args) {
+  /* input variables. */
+  char *filename = NULL;
+
+  int verbose = 0;
+
+  /* parse the arguments. */
+  if (!PyArg_ParseTuple(args, "s|i", &filename, &verbose)) return NULL;
+
+  /* initialize the reader which reverse the offset if necessary. */
+  struct logger_reader reader;
+  logger_reader_init(&reader, filename, verbose);
+
+  /* Free the reader. */
+  logger_reader_free(&reader);
+
+  return Py_BuildValue("");
+}
+
+/* definition of the method table. */
+
+static PyMethodDef libloggerMethods[] = {
+    {"loadFromIndex", loadFromIndex, METH_VARARGS,
+     "Load snapshot directly from the offset in an index file."},
+    {"reverseOffset", pyReverseOffset, METH_VARARGS,
+     "Reverse the offset (from pointing backward to forward)."},
+
+    {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+static struct PyModuleDef libloggermodule = {
+    PyModuleDef_HEAD_INIT,
+    "liblogger",
+    "Module reading a SWIFTsim logger snapshot",
+    -1,
+    libloggerMethods,
+    NULL, /* m_slots */
+    NULL, /* m_traverse */
+    NULL, /* m_clear */
+    NULL  /* m_free */
+};
+
+PyMODINIT_FUNC PyInit_liblogger(void) {
+  PyObject *m;
+  m = PyModule_Create(&libloggermodule);
+  if (m == NULL) return NULL;
+
+  import_array();
+
+  return m;
+}
diff --git a/logger/logger_reader.c b/logger/logger_reader.c
new file mode 100644
index 0000000000000000000000000000000000000000..0404ab348999a33210c2f01b6b70373757a5b8f9
--- /dev/null
+++ b/logger/logger_reader.c
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "logger_reader.h"
+
+/**
+ * @brief Initialize the reader.
+ *
+ * @param reader The #logger_reader.
+ * @param filename The log filename.
+ * @param verbose The verbose level.
+ */
+void logger_reader_init(struct logger_reader *reader, char *filename,
+                        int verbose) {
+  if (verbose > 1) message("Initializing the reader.");
+
+  /* Initialize the reader variables. */
+  reader->verbose = verbose;
+
+  /* Initialize the log file. */
+  logger_logfile_init_from_file(&reader->log, filename, reader,
+                                /* only_header */ 0);
+
+  if (verbose > 1) message("Initialization done.");
+}
+
+/**
+ * @brief Free the reader.
+ *
+ * @param reader The #logger_reader.
+ */
+void logger_reader_free(struct logger_reader *reader) {
+  /* Free the log. */
+  logger_logfile_free(&reader->log);
+}
+
+/**
+ * @brief Read a record (timestamp or particle)
+ *
+ * @param reader The #reader.
+ * @param lp (out) The #logger_particle (if the record is a particle).
+ * @param time (out) The time read (if the record is a timestamp).
+ * @param is_particle Is the record a particle (or a timestamp)?
+ * @param offset The offset in the file.
+ *
+ * @return The offset after this record.
+ */
+size_t reader_read_record(struct logger_reader *reader,
+                          struct logger_particle *lp, double *time,
+                          int *is_particle, size_t offset) {
+
+  struct logger_logfile *log = &reader->log;
+
+  /* Read mask to find out if timestamp or particle. */
+  size_t mask = 0;
+  logger_loader_io_read_mask(&log->header, log->log.map + offset, &mask, NULL);
+
+  /* Check if timestamp or not. */
+  int ind = header_get_field_index(&log->header, "timestamp");
+  if (ind == -1) {
+    error("File header does not contain a mask for time.");
+  }
+  if (log->header.masks[ind].mask == mask) {
+    *is_particle = 0;
+    integertime_t int_time = 0;
+    offset = time_read(&int_time, time, reader, offset);
+  } else {
+    *is_particle = 1;
+    offset =
+        logger_particle_read(lp, reader, offset, *time, logger_reader_const);
+  }
+
+  return offset;
+}
diff --git a/logger/logger_reader.h b/logger/logger_reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..124d271f57587a26dbfb59299678f0ce5cfbdf79
--- /dev/null
+++ b/logger/logger_reader.h
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+/**
+ * @file logger_reader.h
+ * @brief This file contains the C functions shown to the external user.
+ *
+ * Here is a quick summary of our different elements:
+ *
+ * The logger is a time adaptive way to write snapshots.
+ * It consists of a set of files: the log file, the parameter file and the index
+ * files.
+ *
+ * The <b>parameter file</b> contains all the information related to the code
+ * (e.g. boxsize).
+ *
+ * The <b>index files</b> are not mandatory files that indicates the position of
+ * the particles in the log file at a given time step. They are useful to
+ * speedup the reading.
+ *
+ * The <b>log file</b> consists in a large file where the particles are logged
+ * one after the other. It contains a <b>log file header</b> at the beginning of
+ * the file and a large collection of <b>records</b>.
+ *
+ * The records are logged one after the other and each contains a <b>record
+ * header</b> and then a list of <b>named entries</b>. In the record header, a
+ * <b>mask</b> is provided that corresponds to the type of named entries present
+ * in this record. It also contains the <b>offset</b> to the previous or next
+ * record for this particle.
+ */
+
+#ifndef LOGGER_LOGGER_READER_H
+#define LOGGER_LOGGER_READER_H
+
+#include "logger_loader_io.h"
+#include "logger_logfile.h"
+#include "logger_particle.h"
+
+/**
+ * @brief Main structure of the logger.
+ *
+ * This structure contains all the variables required for the logger.
+ * It should be the only structure that the user see.
+ *
+ * It is initialized with #logger_reader_init and freed with
+ * #logger_reader_free.
+ */
+struct logger_reader {
+
+  /* Time of each index file. #TODO */
+  double *times;
+
+  /* Informations contained in the file header. */
+  struct logger_logfile log;
+
+  /* Level of verbosity. */
+  int verbose;
+};
+
+void logger_reader_init(struct logger_reader *reader, char *filename,
+                        int verbose);
+void logger_reader_free(struct logger_reader *reader);
+size_t reader_read_record(struct logger_reader *reader,
+                          struct logger_particle *lp, double *time,
+                          int *is_particle, size_t offset);
+#endif  // LOGGER_LOGGER_READER_H
diff --git a/logger/logger_time.c b/logger/logger_time.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2c6ebc3f9e3171ba7fdec6c6a63eb23d7001df6
--- /dev/null
+++ b/logger/logger_time.c
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_time.h"
+#include "logger_loader_io.h"
+#include "logger_logfile.h"
+#include "logger_reader.h"
+
+/**
+ * @brief Check if enough space is available and increase it if required.
+ *
+ * @param t The #time_array.
+ */
+void time_array_ensure_size(struct time_array *t) {
+  /* Check if we still have some place. */
+  if (t->size < t->capacity) return;
+
+  /* Increase the size */
+  t->capacity *= 2;
+
+  /* Allocate the new array */
+  struct time_record *tmp = malloc(sizeof(struct time_record) * t->capacity);
+  if (tmp == NULL) error("Failed to allocate the time records.");
+
+  /* Copy the memory */
+  memcpy(tmp, t->records, sizeof(struct time_record) * t->size);
+
+  /* Cleanup the memory */
+  free(t->records);
+
+  /* Set the pointer to the new array */
+  t->records = tmp;
+}
+
+/**
+ * @brief Add an element to the #time_array.
+ *
+ * @param t The #time_array.
+ * @param int_time The time in integer.
+ * @param time The time in double.
+ * @param offset The offset of the record.
+ */
+void time_array_append(struct time_array *t, const integertime_t int_time,
+                       const double time, const size_t offset) {
+
+  /* Increase the available space if required */
+  time_array_ensure_size(t);
+
+  /* Copy the values */
+  t->records[t->size].time = time;
+  t->records[t->size].int_time = int_time;
+  t->records[t->size].offset = offset;
+
+  /* Increase the size used. */
+  t->size += 1;
+}
+
+/**
+ * @brief read a time record.
+ *
+ * @param int_time integer time read.
+ * @param time time read.
+ * @param reader The #logger_reader.
+ * @param offset position in the file.
+ *
+ */
+size_t time_read(integertime_t *int_time, double *time,
+                 const struct logger_reader *reader, size_t offset) {
+
+  /* Initialize variables. */
+  const struct header *h = &reader->log.header;
+  void *map = h->log->log.map;
+
+  size_t mask = 0;
+  size_t prev_offset = 0;
+  *int_time = 0;
+  *time = 0;
+
+  /* read record header. */
+  map = logger_loader_io_read_mask(h, map + offset, &mask, &prev_offset);
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* check if time mask is present in log file header. */
+  int ind = header_get_field_index(h, "timestamp");
+  if (ind == -1) error("File header does not contain a mask for time.");
+
+  /* check if reading a time record. */
+  if (h->masks[ind].mask != mask) error("Not a time record.");
+#endif
+
+  /* read the record. */
+  map =
+      logger_loader_io_read_data(map, sizeof(unsigned long long int), int_time);
+  map = logger_loader_io_read_data(map, sizeof(double), time);
+
+  return map - h->log->log.map;
+}
+
+/**
+ * @brief get offset of first time record
+ *
+ * @param h file #header
+ * @return offset of first time record
+ *
+ */
+size_t time_offset_first_record(const struct header *h) {
+
+  /* Initialize a few variables. */
+  size_t offset = h->offset_first_record;
+  void *map = h->log->log.map;
+
+  /* Check that the first record is really a time record. */
+  int i = header_get_field_index(h, "timestamp");
+
+  if (i == -1) error("Time mask not present in the log file header.");
+
+  size_t mask = 0;
+  logger_loader_io_read_mask(h, map + offset, &mask, NULL);
+
+  if (mask != h->masks[i].mask) error("Log file should begin by timestep.");
+
+  return h->offset_first_record;
+}
+
+/**
+ * @brief Initialize an empty time array.
+ *
+ * @param t #time_array to initialize.
+ */
+void time_array_init(struct time_array *t) {
+  /* Allocate the arrays */
+  t->records = malloc(sizeof(struct time_record) * LOGGER_TIME_INIT_SIZE);
+  if (t->records == NULL) error("Failed to initialize the time records.");
+
+  /* Initialize the sizes */
+  t->size = 0;
+  t->capacity = LOGGER_TIME_INIT_SIZE;
+}
+
+/**
+ * @brief Initialize a time array from a file.
+ *
+ * @param t #time_array to initialize.
+ * @param log The #logger_logfile.
+ */
+void time_array_populate(struct time_array *t, struct logger_logfile *log) {
+
+  /* Initialize a few variables. */
+  integertime_t int_time = 0;
+  double time = 0;
+
+  /* get file size. */
+  size_t file_size = log->log.file_size;
+
+  /* get first time stamp. */
+  size_t offset = time_offset_first_record(&log->header);
+  while (offset < file_size) {
+    /* read current time record and store it. */
+    size_t tmp_offset = offset;
+    time_read(&int_time, &time, log->reader, tmp_offset);
+    time_array_append(t, int_time, time, offset);
+
+    /* get next record. */
+    int test = tools_get_next_record(&log->header, log->log.map, &offset,
+                                     log->log.file_size);
+    if (test == -1) break;
+  }
+}
+
+/**
+ * @brief access the time of a given record (by its offset).
+ *
+ * @param t #time_array to access.
+ * @param offset offset of the record.
+ *
+ * @return integer time of the record.
+ */
+integertime_t time_array_get_integertime(struct time_array *t,
+                                         const size_t offset) {
+  size_t ind = time_array_get_index(t, offset);
+  return t->records[ind].int_time;
+}
+
+/**
+ * @brief access the time of a given record (by its offset).
+ *
+ * @param t #time_array to access.
+ * @param offset offset of the record.
+ *
+ * @return time of the record.
+ */
+double time_array_get_time(const struct time_array *t, const size_t offset) {
+  size_t ind = time_array_get_index(t, offset);
+  return t->records[ind].time;
+}
+
+/**
+ * @brief Find the index of the last time record written before a given offset.
+ *
+ * @param t #time_array to access.
+ * @param offset offset of the record.
+ *
+ * @return The index of the last time record.
+ */
+size_t time_array_get_index(const struct time_array *t, const size_t offset) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (!t) error("NULL pointer.");
+
+  if (offset < t->records[0].offset || offset > t->records[t->size - 1].offset)
+    error("Offset outside of range.");
+#endif
+
+  /* left will contain the index at the end of the loop */
+  size_t left = 0;
+  size_t right = t->size - 1;
+
+  /* Find the time_array with the correct offset through a bisection method. */
+  while (left <= right) {
+    size_t center = (left + right) / 2;
+    const size_t offset_center = t->records[center].offset;
+
+    if (offset > offset_center) {
+      left = center + 1;
+    } else if (offset < offset_center) {
+      right = center - 1;
+    } else {
+      return center;
+    }
+  }
+
+  return right;
+}
+
+/**
+ * @brief free memory of a #time_array
+ *
+ * @param t #time_array to free
+ */
+void time_array_free(struct time_array *t) {
+  /* Free the arrays */
+  free(t->records);
+  t->records = NULL;
+
+  /* Reset the counters */
+  t->size = 0;
+  t->capacity = 0;
+}
+
+/**
+ * @brief print a #time_array
+ *
+ * @param t #time_array to print
+ */
+void time_array_print(const struct time_array *t) {
+  const size_t threshold = 4;
+
+  size_t n = t->size;
+  size_t up_threshold = n - threshold;
+
+  printf("Times (size %lu): [%lli (%g)", n, t->records[0].int_time,
+         t->records[0].time);
+
+  /* Loop over all elements. */
+  for (size_t i = 1; i < n; i++) {
+    /* Skip the times at the center of the array. */
+    if (i < threshold || i > up_threshold)
+      printf(", %lli (%g)", t->records[i].int_time, t->records[i].time);
+
+    if (i == threshold) printf(", ...");
+  }
+
+  printf("]\n");
+}
+
+/**
+ * @brief print a #time_array (offset)
+ *
+ * @param t #time_array to print
+ */
+void time_array_print_offset(const struct time_array *t) {
+  const size_t threshold = 4;
+
+  size_t n = t->size;
+  size_t up_threshold = n - threshold;
+
+  printf("Times (size %lu): [%lu", n, t->records[0].offset);
+
+  /* Loop over all elements. */
+  for (size_t i = 1; i < n; i++) {
+    /* Skip the offset in the middle of the array. */
+    if (i < threshold || i > up_threshold)
+      printf(", %lu", t->records[i].offset);
+
+    if (i == threshold) printf(", ...");
+  }
+
+  printf("]\n");
+}
diff --git a/logger/logger_time.h b/logger/logger_time.h
new file mode 100644
index 0000000000000000000000000000000000000000..b27abffb9c1b3aa02c82c1739d1206b43f3ac431
--- /dev/null
+++ b/logger/logger_time.h
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef LOGGER_LOGGER_TIMELINE_H
+#define LOGGER_LOGGER_TIMELINE_H
+
+#include "logger_header.h"
+#include "logger_tools.h"
+
+typedef int8_t timebin_t;
+typedef long long integertime_t;
+
+struct logger_reader;
+
+#define LOGGER_TIME_INIT_SIZE 1024
+
+/**
+ * @brief This structure contains all the information present in a time record.
+ */
+struct time_record {
+  /* Integertime of the records. */
+  integertime_t int_time;
+
+  /* Double time of the records. */
+  double time;
+
+  /* Offset in the file of the time records. */
+  size_t offset;
+};
+
+/**
+ * @brief This structure contains all the time record.
+ *
+ * In order to obtain easily the time step of a record,
+ * this structure is required. It contains all the time step
+ * with their integer time, double time and position in the file.
+ *
+ * This structure is initialized with #time_array_init and #time_array_populate,
+ * and freed with #time_array_free.
+ *
+ * The time step of an offset can be obtained with
+ * #time_array_get_integertime, #time_array_get_time and
+ * #time_array_get_index.
+ */
+struct time_array {
+
+  /* The complete list of time record */
+  struct time_record *records;
+
+  /* Number of element in the arrays. */
+  size_t size;
+
+  /* Maximum number of element available */
+  size_t capacity;
+};
+
+void time_array_append(struct time_array *t, const integertime_t int_time,
+                       const double time, const size_t offset);
+size_t time_read(integertime_t *int_time, double *time,
+                 const struct logger_reader *reader, size_t offset);
+
+void time_array_init(struct time_array *t);
+void time_array_populate(struct time_array *t, struct logger_logfile *log);
+
+integertime_t time_array_get_integertime(struct time_array *t,
+                                         const size_t offset);
+
+double time_array_get_time(const struct time_array *t, const size_t offset);
+
+size_t time_array_get_index(const struct time_array *t, const size_t offset);
+
+void time_array_free(struct time_array *t);
+
+void time_array_print(const struct time_array *t);
+
+void time_array_print_offset(const struct time_array *t);
+
+size_t time_offset_first_record(const struct header *h);
+
+#endif  // LOGGER_LOGGER_TIMELINE_H
diff --git a/logger/logger_tools.c b/logger/logger_tools.c
new file mode 100644
index 0000000000000000000000000000000000000000..a9a6ecfcb0acf72b11898d00fdfeff90fd70406d
--- /dev/null
+++ b/logger/logger_tools.c
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include "logger_tools.h"
+#include "logger_header.h"
+#include "logger_loader_io.h"
+#include "logger_reader.h"
+
+#include "logger_particle.h"
+
+#include <stdio.h>
+
+/**
+ * @brief get the offset of the next corresponding record.
+ *
+ * @param h #header structure of the file
+ * @param map file mapping
+ * @param offset In: initial offset, Out: offset of the next record
+ * @param file_size The file size.
+ *
+ * @return -1 if no next record, otherwise 0
+ */
+int tools_get_next_record(const struct header *h, void *map, size_t *offset,
+                          size_t file_size) {
+  if (header_is_forward(h))
+    return _tools_get_next_record_forward(h, map, offset);
+  if (header_is_backward(h))
+    return _tools_get_next_record_backward(h, map, offset, file_size);
+  else
+    error("Offsets are corrupted.");
+}
+
+/**
+ * @brief internal function of #tools_get_next_record. Should not be used
+ * outside.
+ *
+ * @param h #header structure of the file
+ * @param map file mapping
+ * @param offset (Out) offset of the next record
+ *
+ * @return error code, -1 if no next record
+ */
+int _tools_get_next_record_forward(const struct header *h, void *map,
+                                   size_t *offset) {
+  size_t diff_offset = 0;
+
+  /* Read the offset. */
+  map = logger_loader_io_read_mask(h, map + *offset, NULL, &diff_offset);
+
+  if (diff_offset == 0) return -1;
+
+  /* Set the absolute offset. */
+  *offset += diff_offset;
+  return 0;
+}
+
+/**
+ * @brief internal function of #tools_get_next_record. Should not be used (very
+ * slow)
+ *
+ * @param h #header structure of the file
+ * @param map file mapping
+ * @param offset In: initial offset, Out: offset of the next record
+ * @param file_size The file size.
+ *
+ * @return error code, -1 if no next record
+ */
+int _tools_get_next_record_backward(const struct header *h, void *map,
+                                    size_t *offset, size_t file_size) {
+#ifndef SWIFT_DEBUG_CHECKS
+  error("Should not be used, method too slow");
+#endif
+  size_t current_offset = *offset;
+  size_t record_header = LOGGER_MASK_SIZE + LOGGER_OFFSET_SIZE;
+
+  while (current_offset < file_size) {
+    size_t mask = 0;
+    size_t prev_offset;
+    logger_loader_io_read_mask(h, map + current_offset, &mask, &prev_offset);
+
+    prev_offset = current_offset - prev_offset - record_header;
+    if (*offset == prev_offset) {
+      *offset = current_offset - record_header;
+      return 0;
+    }
+
+    current_offset += header_get_record_size_from_mask(h, mask);
+  }
+
+  return -1;
+}
+
+/**
+ * @brief switch side offset.
+ *
+ * From current record, switch side of the offset of the previous one.
+ * @param h #header structure of the file.
+ * @param file_map file mapping.
+ * @param offset position of the record.
+ *
+ * @return position after the record.
+ */
+size_t tools_reverse_offset(const struct header *h, void *file_map,
+                            size_t offset) {
+  size_t mask = 0;
+  size_t prev_offset = 0;
+  const size_t cur_offset = offset;
+  void *map = file_map;
+
+  /* read mask + offset. */
+  map = logger_loader_io_read_mask(h, map + offset, &mask, &prev_offset);
+
+  /* write offset of zero (in case it is the last record). */
+  const size_t zero = 0;
+  map -= LOGGER_OFFSET_SIZE;
+  map = logger_loader_io_write_data(map, LOGGER_OFFSET_SIZE, &zero);
+
+  /* set offset after current record. */
+  map += header_get_record_size_from_mask(h, mask);
+  size_t after_current_record = (size_t)(map - file_map);
+
+  /* first records do not have a previous partner. */
+  if (prev_offset == cur_offset) return after_current_record;
+
+  if (prev_offset > cur_offset)
+    error("Unexpected offset: header %lu, current %lu.", prev_offset,
+          cur_offset);
+
+  /* modify previous offset. */
+  map = file_map + cur_offset - prev_offset + LOGGER_MASK_SIZE;
+  map = logger_loader_io_write_data(map, LOGGER_OFFSET_SIZE, &prev_offset);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  size_t prev_mask = 0;
+  map -= LOGGER_MASK_SIZE + LOGGER_OFFSET_SIZE;
+  logger_loader_io_read_mask(h, map, &prev_mask, NULL);
+
+  /* Check if we are not mixing time stamp and particles */
+  if ((prev_mask != 128 && mask == 128) || (prev_mask == 128 && mask != 128))
+    error("Unexpected mask: %lu, got %lu.", mask, prev_mask);
+
+#endif  // SWIFT_DEBUG_CHECKS
+
+  return after_current_record;
+}
+
+/**
+ * @brief debugging function checking the offset and the mask of a record.
+ *
+ * Compare the mask with the one pointed by the header.
+ * if the record is a particle, check the id too.
+ *
+ * @param reader The #logger_reader.
+ * @param offset position of the record.
+ *
+ * @return position after the record.
+ */
+size_t tools_check_record_consistency(const struct logger_reader *reader,
+                                      size_t offset) {
+#ifndef SWIFT_DEBUG_CHECKS
+  error("Should not check in non debug mode.");
+#endif
+
+  const struct header *h = &reader->log.header;
+  void *file_init = reader->log.log.map;
+  void *map = file_init + offset;
+
+  size_t mask;
+  size_t pointed_offset;
+
+  /* read mask + offset. */
+  map = logger_loader_io_read_mask(h, map, &mask, &pointed_offset);
+
+  /* get absolute offset. */
+  if (header_is_forward(h))
+    pointed_offset += offset;
+  else if (header_is_backward(h)) {
+    if (offset < pointed_offset)
+      error("Offset too large (%lu) at %lu with mask %lu.", pointed_offset,
+            offset, mask);
+    pointed_offset = offset - pointed_offset;
+  } else {
+    error("Offset are corrupted.");
+  }
+
+  /* set offset after current record. */
+  map += header_get_record_size_from_mask(h, mask);
+
+  if (pointed_offset == offset || pointed_offset == 0)
+    return (size_t)(map - file_init);
+
+  /* read mask of the pointed record. */
+  size_t pointed_mask = 0;
+  logger_loader_io_read_mask(h, file_init + pointed_offset, &pointed_mask,
+                             NULL);
+
+  /* check if not mixing time stamp and particles. */
+  if ((pointed_mask != 128 && mask == 128) ||
+      (pointed_mask == 128 && mask != 128))
+    error("Error in the offset (mask %lu at %lu != %lu at %lu).", mask, offset,
+          pointed_mask, pointed_offset);
+
+  if (pointed_mask == 128) return (size_t)(map - file_init);
+
+  struct logger_particle part;
+  logger_particle_read(&part, reader, offset, 0, logger_reader_const);
+
+  size_t id = part.id;
+  logger_particle_read(&part, reader, pointed_offset, 0, logger_reader_const);
+
+  if (id != part.id)
+    error("Offset wrong, id incorrect (%lu != %lu) at %lu.", id, part.id,
+          pointed_offset);
+
+  return (size_t)(map - file_init);
+}
diff --git a/logger/logger_tools.h b/logger/logger_tools.h
new file mode 100644
index 0000000000000000000000000000000000000000..21a59e42fca144a0381b15e8771ca14ceed46b33
--- /dev/null
+++ b/logger/logger_tools.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+/**
+ * @brief This file contains functions that help to navigate in the logs.
+ */
+#ifndef LOGGER_LOGGER_TOOLS_H
+#define LOGGER_LOGGER_TOOLS_H
+
+#include "../config.h"
+
+/* Swift include */
+#include "../src/dimension.h"
+#include "../src/error.h"
+#include "../src/inline.h"
+#include "../src/logger.h"
+#include "../src/part_type.h"
+
+#ifdef HAVE_PYTHON
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define STRING_SIZE 200
+
+struct header;
+struct logger_reader;
+
+int tools_get_next_record(const struct header *h, void *map, size_t *offset,
+                          size_t file_size);
+int _tools_get_next_record_backward(const struct header *h, void *map,
+                                    size_t *offset, size_t file_size);
+int _tools_get_next_record_forward(const struct header *h, void *map,
+                                   size_t *offset);
+size_t tools_reverse_offset(const struct header *h, void *map, size_t offset);
+size_t tools_check_record_consistency(const struct logger_reader *reader,
+                                      size_t offset);
+
+#endif  // LOGGER_LOGGER_TOOLS_H
diff --git a/logger/python/reader_example.py b/logger/python/reader_example.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ace309c5b68b4fc4f1088b6206cd1ae3ccd69a5
--- /dev/null
+++ b/logger/python/reader_example.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+"""
+Read a logger file by using an index.
+Example: ./reader_example.py ../../examples/SedovBlast_3D/index.dump ../../examples/SedovBlast_3D/index_0005.hdf5
+"""
+import sys
+from h5py import File
+import numpy as np
+import matplotlib.pyplot as plt
+sys.path.append("../.libs/")
+
+import liblogger as logger
+
+# Get filenames
+if len(sys.argv) != 3:
+    print("WARNING missing arguments. Will use the default ones")
+    index = "../../examples/HydroTests/SedovBlast_3D/index_0002.hdf5"
+    dump = "../../examples/HydroTests/SedovBlast_3D/index.dump"
+else:
+    index = sys.argv[-1]
+    dump = sys.argv[-2]
+
+# constant
+offset_name = "PartType0/Offset"
+header = "Header"
+time_name = "Time Offset"
+
+# Read index file
+with File(index, "r") as f:
+    if offset_name not in f:
+        raise Exception("Unable to find the offset dataset")
+    offset = f[offset_name][:]
+
+    if header not in f:
+        raise Exception("Unable to find the header")
+    if time_name not in f[header].attrs:
+        raise Exception("Unable to find the time offset")
+    time_offset = f[header].attrs[time_name]
+
+# read dump
+data = logger.loadFromIndex(offset, dump, time_offset)
+
+# Compute distance from center
+pos = data["positions"]
+center = pos.mean()
+r2 = np.sum((pos - center)**2, axis=1)
+
+# plot entropy vs distance
+plt.plot(np.sqrt(r2), data["entropy"], '.')
+
+plt.xlim(0., 0.5)
+plt.ylim(-5, 50)
+plt.xlabel("Radius")
+plt.ylabel("Entropy")
+plt.show()
diff --git a/logger/tests/Makefile.am b/logger/tests/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..dd94462b8b98b0a089d0f959b81c603c29911a76
--- /dev/null
+++ b/logger/tests/Makefile.am
@@ -0,0 +1,37 @@
+# This file is part of SWIFT.
+# Copyright (c) 2019 loic.hausammann@epfl.ch.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Add the source directory and the non-standard paths to the included library headers to CFLAGS
+AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/logger $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
+
+AM_LDFLAGS = ../../src/.libs/libswiftsim.a ../.libs/liblogger.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
+
+# List of programs and scripts to run in the test suite
+TESTS = testLogfileHeader testLogfileReader testTimeArray
+
+# List of test programs to compile
+check_PROGRAMS = testLogfileHeader testLogfileReader testTimeArray
+
+# Rebuild tests when SWIFT is updated.
+$(check_PROGRAMS): ../../src/.libs/libswiftsim.a ../.libs/liblogger.a
+
+# Sources for the individual programs
+testLogfileHeader_SOURCES = testLogfileHeader.c
+testLogfileReader_SOURCES = testLogfileReader.c
+testTimeArray_SOURCES = testTimeArray.c
+
+# Files necessary for distribution
+EXTRA_DIST = testLogfileHeader.yml testLogfileReader.yml
diff --git a/logger/tests/testLogfileHeader.c b/logger/tests/testLogfileHeader.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f2c8a5df7942d50cbb641b99e3173a05fe1d539
--- /dev/null
+++ b/logger/tests/testLogfileHeader.c
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "logger_header.h"
+#include "logger_logfile.h"
+#include "logger_reader.h"
+#include "swift.h"
+
+int main(int argc, char *argv[]) {
+
+  /*
+    First generate the file.
+  */
+
+  message("Generating the dump.");
+  /* Create required structures. */
+  struct logger_writer log;
+  struct swift_params params;
+  char filename[200] = "testLogfileHeader.yml";
+
+  /* Read parameters. */
+  parser_read_file(filename, &params);
+
+  /* Initialize the logger. */
+  logger_init(&log, &params);
+
+  /* get dump filename. */
+  char dump_filename[PARSER_MAX_LINE_SIZE];
+  strcpy(dump_filename, log.base_name);
+  strcat(dump_filename, ".dump");
+
+  /* Write file header. */
+  logger_write_file_header(&log);
+
+  /* clean memory. */
+  logger_free(&log);
+  /*
+    Then read the file.
+  */
+
+  message("Reading the header.");
+  /* Generate required structure for reading. */
+  struct logger_reader reader;
+  struct logger_logfile *logfile = &reader.log;
+  logfile->reader = &reader;
+
+  /* Set verbose level. */
+  reader.verbose = 1;
+
+  /* Read the header */
+  logger_logfile_init_from_file(logfile, dump_filename, &reader,
+                                /* only_header */ 1);
+  /*
+    Finally check everything.
+  */
+
+  struct header *h = &logfile->header;
+  message("Checking versions.");
+  assert(h->major_version == logger_major_version);
+  assert(h->minor_version == logger_minor_version);
+
+  message("Checking offset of first record");
+  assert(h->offset_first_record == logfile->log.file_size);
+
+  message("Checking number of masks");
+  assert(h->number_mask == logger_count_mask);
+
+  message("Checking masks");
+  for (int i = 0; i < logger_count_mask; i++) {
+    assert(logger_mask_data[i].size == h->masks[i].size);
+    assert(logger_mask_data[i].mask == h->masks[i].mask);
+    assert(strcmp(logger_mask_data[i].name, h->masks[i].name) == 0);
+  }
+
+  message("Checking offset direction");
+  assert(h->offset_direction == logger_offset_backward);
+
+  return 0;
+}
diff --git a/logger/tests/testLogfileHeader.yml b/logger/tests/testLogfileHeader.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b97c513fa9ee1c3d9816b54afed38f4124dc3957
--- /dev/null
+++ b/logger/tests/testLogfileHeader.yml
@@ -0,0 +1,6 @@
+# Parameter file for the tests
+Logger:
+  delta_step: 10
+  initial_buffer_size: 0.1 # in GB
+  buffer_scale: 10
+  basename: test_header
\ No newline at end of file
diff --git a/logger/tests/testLogfileReader.c b/logger/tests/testLogfileReader.c
new file mode 100644
index 0000000000000000000000000000000000000000..751c6b7d628fcd1191e8deba9135cddd8cd04bf8
--- /dev/null
+++ b/logger/tests/testLogfileReader.c
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "logger_header.h"
+#include "logger_loader_io.h"
+#include "logger_particle.h"
+#include "logger_reader.h"
+#include "swift.h"
+
+#define number_parts 100
+/* Not all the fields are written at every step.
+ * Here we define how often a few fields are written.
+ */
+#define period_rho 2
+#define period_h 4
+
+/**
+ * @brief Initialize the particles.
+ *
+ * @param p The array of #part.
+ * @param xp The array of #xpart.
+ */
+void init_particles(struct part *p, struct xpart *xp) {
+  struct hydro_space hs;
+
+  for (int i = 0; i < number_parts; i++) {
+    /* Set internal energy. */
+    hydro_set_init_internal_energy(&p[i], 100);
+
+    /* Initialize particle. */
+    hydro_first_init_part(&p[i], &xp[i]);
+    hydro_init_part(&p[i], &hs);
+
+    for (int j = 0; j < 3; j++) {
+      p[i].x[j] = i;
+      p[i].v[j] = (j == 0) ? -1 : 0;
+      p[i].a_hydro[j] = (j == 1) ? 1e-2 : 0;
+    }
+    p[i].h = 15;
+    p[i].rho = 50;
+    p[i].id = i;
+    hydro_set_mass(&p[i], 1.5);
+    xp[i].logger_data.last_offset = 0;
+
+    /* Add time bin in order to skip particles. */
+    p[i].time_bin = (i % 10) + 1;
+  }
+}
+
+/** Provides a integer time given the step number.*/
+integertime_t get_integer_time(int step) { return step; }
+
+/** Provides a double time given the step number. */
+double get_double_time(int step) {
+  const double time_base = 1e-4;
+  return step * time_base;
+}
+
+/**
+ * @brief Write a few particles during multiple time steps.
+ *
+ * As only the logger is tested, there is no need to really
+ * evolve the particles.
+ */
+void write_particles(struct logger_writer *log, struct part *parts,
+                     struct xpart *xparts) {
+
+  const int number_steps = 100;
+
+  /* Loop over all the steps. */
+  for (int i = 0; i < number_steps; i++) {
+    integertime_t ti_int = get_integer_time(i);
+    double ti_double = get_double_time(i);
+
+    /* Mark the current time step in the particle logger file. */
+    logger_log_timestamp(log, ti_int, ti_double, &log->timestamp_offset);
+    /* Make sure that we have enough space in the particle logger file
+     * to store the particles in current time step. */
+    logger_ensure_size(log, number_parts, /* number gpart */ 0, 0);
+
+    /* Loop over all the particles. */
+    for (int j = 0; j < number_parts; j++) {
+
+      /* Skip some particles. */
+      if (i % parts[j].time_bin != 0) continue;
+
+      /* Write a time information to check that the correct particle is read. */
+      parts[j].x[0] = i;
+
+      /* Write this particle. */
+      unsigned int mask =
+          logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask |
+          logger_mask_data[logger_a].mask | logger_mask_data[logger_u].mask |
+          logger_mask_data[logger_consts].mask;
+
+      int number_particle_step = i / parts[j].time_bin;
+
+      if (number_particle_step % period_h == 0)
+        mask |= logger_mask_data[logger_h].mask;
+      if (number_particle_step % period_rho == 0)
+        mask |= logger_mask_data[logger_rho].mask;
+
+      logger_log_part(log, &parts[j], mask, &xparts[j].logger_data.last_offset);
+    }
+
+    // TODO write index files.
+  }
+
+  /* Mark the current time step in the particle logger file. */
+  integertime_t ti_int = get_integer_time(number_steps);
+  double ti_double = get_double_time(number_steps);
+  logger_log_timestamp(log, ti_int, ti_double, &log->timestamp_offset);
+}
+
+/** Count the number of active particles. */
+int get_number_active_particles(int step, struct part *p) {
+  int count = 0;
+  for (int i = 0; i < number_parts; i++) {
+    if (step % p[i].time_bin == 0) count += 1;
+  }
+  return count;
+}
+/**
+ * @brief Check that the reader contains the correct data
+ *
+ * @param reader The #logger_reader.
+ */
+void check_data(struct logger_reader *reader, struct part *parts,
+                struct xpart *xparts) {
+
+  /* No need to check the header, this is already done in testHeader.c */
+
+  /* Get required structures. */
+  struct logger_logfile *logfile = &reader->log;
+
+  struct logger_particle lp;
+  logger_particle_init(&lp);
+
+  /* Define a few variables */
+  double time = get_double_time(0);
+  int is_particle = 0;
+  int step = -1;
+
+  /* Number of particle found during this time step. */
+  int count = 0;
+  /* Set it to an impossible value in order to flag it. */
+  const size_t id_flag = 5 * number_parts;
+  size_t previous_id = id_flag;
+
+  /* Loop over each record. */
+  for (size_t offset = reader_read_record(reader, &lp, &time, &is_particle,
+                                          logfile->header.offset_first_record);
+       offset < logfile->log.file_size;
+       offset = reader_read_record(reader, &lp, &time, &is_particle, offset)) {
+
+    /* Do the particle case */
+    if (is_particle) {
+      count += 1;
+
+      /*
+        Check that we are really increasing the id in the logfile.
+        See the writing part to see that we are always increasing the id.
+      */
+      if (previous_id != id_flag && previous_id >= lp.id) {
+        error("Wrong particle found");
+        previous_id = lp.id;
+      }
+
+      /* Get the corresponding particle */
+      if (lp.id >= number_parts) error("Wrong id %zi", lp.id);
+
+      struct part *p = &parts[lp.id];
+
+      /* Check the record's data. */
+      for (int i = 0; i < 3; i++) {
+        /* in the first index, we are storing the step information. */
+        if (i == 0)
+          assert(step == lp.pos[i]);
+        else
+          assert(p->x[i] == lp.pos[i]);
+        assert(p->v[i] == lp.vel[i]);
+        assert(p->a_hydro[i] == lp.acc[i]);
+      }
+
+      assert(p->entropy == lp.entropy);
+      assert(p->mass == lp.mass);
+
+      /* Check optional fields. */
+      int number_steps = step / p->time_bin;
+      if (number_steps % period_h == 0) {
+        assert(p->h == lp.h);
+      } else {
+        assert(-1 == lp.h);
+      }
+      if (number_steps % period_rho == 0) {
+        assert(p->rho == lp.density);
+      } else {
+        assert(-1 == lp.density);
+      }
+    }
+    /* Time stamp case. */
+    else {
+
+      /* Check if we have the current amount of particles in previous step. */
+      if (step != -1 && count != get_number_active_particles(step, parts))
+        error(
+            "The reader did not find the correct number of particles during "
+            "step %i",
+            step);
+
+      step += 1;
+
+      /* Reset some variables. */
+      previous_id = id_flag;
+      count = 0;
+
+      /* Check the record's data. */
+      assert(time == get_double_time(step));
+    }
+  }
+}
+
+int main(int argc, char *argv[]) {
+
+  /*
+    First generate the file.
+  */
+
+  message("Generating the dump.");
+
+  /* Create required structures. */
+  struct logger_writer log;
+  struct swift_params params;
+  char filename[200] = "testLogfileReader.yml";
+
+  /* Read parameters. */
+  parser_read_file(filename, &params);
+
+  /* Initialize the particles. */
+  struct part *parts;
+  if ((parts = (struct part *)malloc(sizeof(struct part) * number_parts)) ==
+      NULL)
+    error("Failed to allocate particles array.");
+
+  struct xpart *xparts;
+  if ((xparts = (struct xpart *)malloc(sizeof(struct xpart) * number_parts)) ==
+      NULL)
+    error("Failed to allocate xparticles array.");
+
+  init_particles(parts, xparts);
+
+  /* Initialize the logger. */
+  logger_init(&log, &params);
+
+  /* get dump filename. */
+  char dump_filename[PARSER_MAX_LINE_SIZE];
+  message("%s", log.base_name);
+  strcpy(dump_filename, log.base_name);
+  strcat(dump_filename, ".dump");
+
+  /* Write file header. */
+  logger_write_file_header(&log);
+
+  /* Write particles. */
+  write_particles(&log, parts, xparts);
+
+  /* clean memory */
+  logger_free(&log);
+  /*
+    Then read the file.
+  */
+
+  message("Reading the header.");
+
+  /* Generate required structure for reading. */
+  struct logger_reader reader;
+
+  /* Set verbose level. */
+  reader.verbose = 1;
+
+  /* Read the header. */
+  logger_reader_init(&reader, dump_filename, /* verbose */ 1);
+
+  /*
+    Finally check everything.
+  */
+
+  check_data(&reader, parts, xparts);
+
+  /* Do some cleanup. */
+  free(parts);
+  free(xparts);
+
+  return 0;
+}
diff --git a/logger/tests/testLogfileReader.yml b/logger/tests/testLogfileReader.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1ac5e2da909f1fe53cba052bbd24c5c3ce98dfed
--- /dev/null
+++ b/logger/tests/testLogfileReader.yml
@@ -0,0 +1,6 @@
+# Parameter file for the tests
+Logger:
+  delta_step: 10
+  initial_buffer_size: 0.01 # in GB
+  buffer_scale: 10
+  basename: test_reader
\ No newline at end of file
diff --git a/logger/tests/testTimeArray.c b/logger/tests/testTimeArray.c
new file mode 100644
index 0000000000000000000000000000000000000000..929a7124baa8ab05fd3452f87076d95c88c2f3b2
--- /dev/null
+++ b/logger/tests/testTimeArray.c
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <time.h>
+#include "logger_time.h"
+
+#define NUMBER_OF_ELEMENT 10000
+#define TIME_BASE 0.04
+#define OFFSET_BASE 1000
+
+int main(int argc, char *argv[]) {
+
+  /* Check that we are really testing the reallocation */
+  if (NUMBER_OF_ELEMENT < LOGGER_TIME_INIT_SIZE) {
+    error("Not testing the reallocation.");
+  }
+
+  /* Fix the random seed in order to reproduce the results */
+  srand(100);
+
+  /* Initialize the time array */
+  struct time_array times;
+  time_array_init(&times);
+
+  /* Add elements */
+  for (size_t i = 0; i < NUMBER_OF_ELEMENT; i++) {
+    integertime_t int_time = i;
+    double time = i * TIME_BASE;
+    size_t offset = i * OFFSET_BASE;
+
+    time_array_append(&times, int_time, time, offset);
+  }
+
+  /* Check the elements */
+  for (size_t i = 0; i < NUMBER_OF_ELEMENT; i++) {
+    integertime_t int_time = i;
+    double time = i * TIME_BASE;
+    size_t offset = i * OFFSET_BASE;
+
+    /* Ensure that we can get the correct offset when looking
+       in between the records. */
+    int r = rand() % OFFSET_BASE;
+    size_t read_offset = offset + r;
+
+    /* The offset cannot be larger than the largest one */
+    if (i == NUMBER_OF_ELEMENT - 1) {
+      read_offset = offset;
+    }
+
+    /* Get the index from the offset */
+    size_t ind = time_array_get_index(&times, read_offset);
+
+    /* Check the values obtained */
+    assert(i == ind);
+    assert(int_time == times.records[ind].int_time);
+    assert(time == times.records[ind].time);
+    assert(offset == times.records[ind].offset);
+  }
+
+  return 0;
+}
diff --git a/src/engine.c b/src/engine.c
index 35b203f3ecec0629e4fbd28502245074acc17720..04cfa15c48e2d0b8e7a040f374c09a93c0de74b5 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -4791,7 +4791,7 @@ void engine_dump_snapshot(struct engine *e) {
  */
 void engine_dump_index(struct engine *e) {
 
-#if defined(WITH_LOGGER)
+#if defined(WITH_LOGGER) && !defined(WITH_MPI)
   struct clocks_time time1, time2;
   clocks_gettime(&time1);
 
@@ -5008,7 +5008,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
   e->total_nr_tasks = 0;
 
 #if defined(WITH_LOGGER)
-  e->logger = (struct logger *)malloc(sizeof(struct logger));
+  e->logger = (struct logger_writer *)malloc(sizeof(struct logger_writer));
   logger_init(e->logger, params);
 #endif
 
@@ -5751,7 +5751,7 @@ void engine_config(int restart, int fof, struct engine *e,
 
 #ifdef WITH_LOGGER
   /* Write the particle logger header */
-  logger_write_file_header(e->logger, e);
+  logger_write_file_header(e->logger);
 #endif
 
   /* Initialise the structure finder */
@@ -6269,7 +6269,7 @@ void engine_clean(struct engine *e, const int fof) {
 
   swift_free("links", e->links);
 #if defined(WITH_LOGGER)
-  logger_clean(e->logger);
+  logger_free(e->logger);
   free(e->logger);
 #endif
   scheduler_clean(&e->sched);
diff --git a/src/engine.h b/src/engine.h
index 3c74f89dfcade93bb67d21eb6607cdd4cb80155d..3484336039c64baa43469b1152f1856f70ee2823 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -379,7 +379,7 @@ struct engine {
   struct repartition *reparttype;
 
 #ifdef WITH_LOGGER
-  struct logger *logger;
+  struct logger_writer *logger;
 #endif
 
   /* How many steps have we done with the same set of tasks? */
diff --git a/src/logger.c b/src/logger.c
index 8be521b27f949ea0d496a5207335f1ec68208489..762eb516077ef82f08b6c34da09cd7bc9eb6a280 100644
--- a/src/logger.c
+++ b/src/logger.c
@@ -44,44 +44,41 @@
 /*
  * Thoses are definitions from the format and therefore should not be changed!
  */
-/* number of bytes for a mask */
+/* Number of bytes for a mask. */
 // TODO change this to number of bits
 #define logger_mask_size 1
 
-/* number of bits for chunk header */
+/* Number of bits for chunk header. */
 #define logger_header_bytes 8
 
-/* number bytes for an offset */
+/* Number bytes for an offset. */
 #define logger_offset_size logger_header_bytes - logger_mask_size
 
-/* number of bytes for the version information */
-#define logger_version_size 20
+/* Number of bytes for the file format information. */
+#define logger_format_size 20
 
-/* number of bytes for the labels in the header */
+/* Number of bytes for the labels in the header. */
 #define logger_label_size 20
 
-/* number of bytes for the number in the header */
-#define logger_number_size 4
-
-char logger_version[logger_version_size] = "0.1";
+char logger_file_format[logger_format_size] = "SWIFT_LOGGER";
 
 const struct mask_data logger_mask_data[logger_count_mask] = {
-    /* Particle's position */
+    /* Particle's position. */
     {3 * sizeof(double), 1 << logger_x, "positions"},
-    /* Particle's velocity */
+    /* Particle's velocity. */
     {3 * sizeof(float), 1 << logger_v, "velocities"},
-    /* Particle's acceleration */
+    /* Particle's acceleration. */
     {3 * sizeof(float), 1 << logger_a, "accelerations"},
-    /* Particle's entropy */
+    /* Particle's entropy. */
     {sizeof(float), 1 << logger_u, "entropy"},
-    /* Particle's smoothing length */
+    /* Particle's smoothing length. */
     {sizeof(float), 1 << logger_h, "smoothing length"},
-    /* Particle's density */
+    /* Particle's density. */
     {sizeof(float), 1 << logger_rho, "density"},
-    /* Particle's constants: mass (float) and ID (long long) */
+    /* Particle's constants: mass (float) and ID (long long). */
     {sizeof(float) + sizeof(long long), 1 << logger_consts, "consts"},
     /* Simulation time stamp: integertime and double time (e.g. scale
-       factor or time) */
+       factor or time). */
     {sizeof(integertime_t) + sizeof(double), 1 << logger_timestamp,
      "timestamp"}};
 
@@ -99,11 +96,11 @@ const struct mask_data logger_mask_data[logger_count_mask] = {
  */
 char *logger_write_chunk_header(char *buff, const unsigned int *mask,
                                 const size_t *offset, const size_t offset_new) {
-  /* write mask */
+  /* write mask. */
   memcpy(buff, mask, logger_mask_size);
   buff += logger_mask_size;
 
-  /* write offset */
+  /* write offset. */
   size_t diff_offset = offset_new - *offset;
   memcpy(buff, &diff_offset, logger_offset_size);
   buff += logger_offset_size;
@@ -112,7 +109,7 @@ char *logger_write_chunk_header(char *buff, const unsigned int *mask,
 }
 
 /**
- * @brief Write to the dump
+ * @brief Write to the dump.
  *
  * @param d #dump file
  * @param offset (return) offset of the data
@@ -121,13 +118,13 @@ char *logger_write_chunk_header(char *buff, const unsigned int *mask,
  */
 void logger_write_data(struct dump *d, size_t *offset, size_t size,
                        const void *p) {
-  /* get buffer */
+  /* get buffer. */
   char *buff = dump_get(d, size, offset);
 
-  /* write data to the buffer */
+  /* write data to the buffer. */
   memcpy(buff, p, size);
 
-  /* Update offset to end of chunk */
+  /* Update offset to end of chunk. */
   *offset += size;
 }
 
@@ -171,15 +168,15 @@ int logger_compute_chunk_size(unsigned int mask) {
  * @param log The #logger
  * @param e The #engine
  */
-void logger_log_all(struct logger *log, const struct engine *e) {
+void logger_log_all(struct logger_writer *log, const struct engine *e) {
 
-  /* Ensure that enough space is available */
+  /* Ensure that enough space is available. */
   logger_ensure_size(log, e->total_nr_parts, e->total_nr_gparts, 0);
 #ifdef SWIFT_DEBUG_CHECKS
   message("Need to implement stars");
 #endif
 
-  /* some constants */
+  /* some constants. */
   const struct space *s = e->s;
   const unsigned int mask =
       logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask |
@@ -187,17 +184,17 @@ void logger_log_all(struct logger *log, const struct engine *e) {
       logger_mask_data[logger_h].mask | logger_mask_data[logger_rho].mask |
       logger_mask_data[logger_consts].mask;
 
-  /* loop over all parts */
+  /* loop over all parts. */
   for (long long i = 0; i < e->total_nr_parts; i++) {
     logger_log_part(log, &s->parts[i], mask,
                     &s->xparts[i].logger_data.last_offset);
     s->xparts[i].logger_data.steps_since_last_output = 0;
   }
 
-  /* loop over all gparts */
+  /* loop over all gparts. */
   if (e->total_nr_gparts > 0) error("Not implemented");
 
-  /* loop over all sparts */
+  /* loop over all sparts. */
   // TODO
 }
 
@@ -210,7 +207,7 @@ void logger_log_all(struct logger *log, const struct engine *e) {
  * @param offset Pointer to the offset of the previous log of this particle;
  * (return) offset of this log.
  */
-void logger_log_part(struct logger *log, const struct part *p,
+void logger_log_part(struct logger_writer *log, const struct part *p,
                      unsigned int mask, size_t *offset) {
 
   /* Make sure we're not writing a timestamp. */
@@ -289,7 +286,7 @@ void logger_log_part(struct logger *log, const struct part *p,
  * @param offset Pointer to the offset of the previous log of this particle;
  * (return) offset of this log.
  */
-void logger_log_gpart(struct logger *log, const struct gpart *p,
+void logger_log_gpart(struct logger_writer *log, const struct gpart *p,
                       unsigned int mask, size_t *offset) {
 
   /* Make sure we're not writing a timestamp. */
@@ -331,7 +328,7 @@ void logger_log_gpart(struct logger *log, const struct gpart *p,
 
   /* Particle constants, which is a bit more complicated. */
   if (mask & logger_mask_data[logger_consts].mask) {
-    // TODO make it dependent of logger_mask_data
+    // TODO make it dependent of logger_mask_data.
     memcpy(buff, &p->mass, sizeof(float));
     buff += sizeof(float);
     memcpy(buff, &p->id_or_neg_offset, sizeof(long long));
@@ -351,7 +348,7 @@ void logger_log_gpart(struct logger *log, const struct gpart *p,
  * @param offset Pointer to the offset of the previous log of this particle;
  * (return) offset of this log.
  */
-void logger_log_timestamp(struct logger *log, integertime_t timestamp,
+void logger_log_timestamp(struct logger_writer *log, integertime_t timestamp,
                           double time, size_t *offset) {
   struct dump *dump = &log->dump;
 
@@ -368,11 +365,11 @@ void logger_log_timestamp(struct logger *log, integertime_t timestamp,
   buff = logger_write_chunk_header(buff, &mask, offset, offset_new);
 
   /* Store the timestamp. */
-  // TODO make it dependent of logger_mask_data
+  // TODO make it dependent of logger_mask_data.
   memcpy(buff, &timestamp, sizeof(integertime_t));
   buff += sizeof(integertime_t);
 
-  /* Store the time */
+  /* Store the time. */
   memcpy(buff, &time, sizeof(double));
 
   /* Update the log message offset. */
@@ -390,21 +387,21 @@ void logger_log_timestamp(struct logger *log, integertime_t timestamp,
  * @param total_nr_gparts total number of gpart
  * @param total_nr_sparts total number of spart
  */
-void logger_ensure_size(struct logger *log, size_t total_nr_parts,
+void logger_ensure_size(struct logger_writer *log, size_t total_nr_parts,
                         size_t total_nr_gparts, size_t total_nr_sparts) {
 
-  /* count part memory */
+  /* count part memory. */
   size_t limit = log->max_chunk_size;
 
   limit *= total_nr_parts;
 
-  /* count gpart memory */
+  /* count gpart memory. */
   if (total_nr_gparts > 0) error("Not implemented");
 
-  /* count spart memory */
+  /* count spart memory. */
   if (total_nr_sparts > 0) error("Not implemented");
 
-  /* ensure enough space in dump */
+  /* ensure enough space in dump. */
   dump_ensure(&log->dump, limit, log->buffer_scale * limit);
 }
 
@@ -414,8 +411,8 @@ void logger_ensure_size(struct logger *log, size_t total_nr_parts,
  * @param log The #logger
  * @param params The #swift_params
  */
-void logger_init(struct logger *log, struct swift_params *params) {
-  /* read parameters */
+void logger_init(struct logger_writer *log, struct swift_params *params) {
+  /* read parameters. */
   log->delta_step = parser_get_param_int(params, "Logger:delta_step");
   size_t buffer_size =
       parser_get_opt_param_float(params, "Logger:initial_buffer_size", 0.5) *
@@ -424,24 +421,24 @@ void logger_init(struct logger *log, struct swift_params *params) {
       parser_get_opt_param_float(params, "Logger:buffer_scale", 10);
   parser_get_param_string(params, "Logger:basename", log->base_name);
 
-  /* set initial value of parameters */
+  /* set initial value of parameters. */
   log->timestamp_offset = 0;
 
-  /* generate dump filename */
+  /* generate dump filename. */
   char logger_name_file[PARSER_MAX_LINE_SIZE];
   strcpy(logger_name_file, log->base_name);
   strcat(logger_name_file, ".dump");
 
-  /* Compute max size for a particle chunk */
+  /* Compute max size for a particle chunk. */
   int max_size = logger_offset_size + logger_mask_size;
 
-  /* Loop over all fields except timestamp */
+  /* Loop over all fields except timestamp. */
   for (int i = 0; i < logger_count_mask - 1; i++) {
     max_size += logger_mask_data[i].size;
   }
   log->max_chunk_size = max_size;
 
-  /* init dump */
+  /* init dump. */
   dump_init(&log->dump, logger_name_file, buffer_size);
 }
 
@@ -450,18 +447,17 @@ void logger_init(struct logger *log, struct swift_params *params) {
  *
  * @param log The #logger
  */
-void logger_clean(struct logger *log) { dump_close(&log->dump); }
+void logger_free(struct logger_writer *log) { dump_close(&log->dump); }
 
 /**
  * @brief Write a file header to a logger file
  *
  * @param log The #logger
- * @param dump The #dump in which to log the particle data.
  *
  */
-void logger_write_file_header(struct logger *log, const struct engine *e) {
+void logger_write_file_header(struct logger_writer *log) {
 
-  /* get required variables */
+  /* get required variables. */
   struct dump *dump = &log->dump;
 
   size_t file_offset = dump->file_offset;
@@ -471,37 +467,46 @@ void logger_write_file_header(struct logger *log, const struct engine *e) {
         "The logger is not empty."
         "This function should be called before writing anything in the logger");
 
-  /* Write version information */
-  logger_write_data(dump, &file_offset, logger_version_size, &logger_version);
+  /* Write format information. */
+  logger_write_data(dump, &file_offset, logger_format_size,
+                    &logger_file_format);
+
+  /* Write the major version number. */
+  int major = logger_major_version;
+  logger_write_data(dump, &file_offset, sizeof(int), &major);
 
-  /* write offset direction */
+  /* Write the minor version number. */
+  int minor = logger_minor_version;
+  logger_write_data(dump, &file_offset, sizeof(int), &minor);
+
+  /* write offset direction. */
   const int reversed = 0;
-  logger_write_data(dump, &file_offset, logger_number_size, &reversed);
+  logger_write_data(dump, &file_offset, sizeof(int), &reversed);
 
-  /* placeholder to write the offset of the first log here */
+  /* placeholder to write the offset of the first log here. */
   char *skip_header = dump_get(dump, logger_offset_size, &file_offset);
 
-  /* write number of bytes used for names */
-  const int label_size = logger_label_size;
-  logger_write_data(dump, &file_offset, logger_number_size, &label_size);
+  /* write number of bytes used for names. */
+  const unsigned int label_size = logger_label_size;
+  logger_write_data(dump, &file_offset, sizeof(unsigned int), &label_size);
 
-  /* write number of masks */
-  int count_mask = logger_count_mask;
-  logger_write_data(dump, &file_offset, logger_number_size, &count_mask);
+  /* write number of masks. */
+  const unsigned int count_mask = logger_count_mask;
+  logger_write_data(dump, &file_offset, sizeof(unsigned int), &count_mask);
 
-  /* write masks */
-  // loop over all mask type
+  /* write masks. */
+  // loop over all mask type.
   for (int i = 0; i < logger_count_mask; i++) {
-    // mask name
+    // mask name.
     logger_write_data(dump, &file_offset, logger_label_size,
                       &logger_mask_data[i].name);
 
-    // mask size
-    logger_write_data(dump, &file_offset, logger_number_size,
+    // mask size.
+    logger_write_data(dump, &file_offset, sizeof(unsigned int),
                       &logger_mask_data[i].size);
   }
 
-  /* last step: write first offset */
+  /* last step: write first offset. */
   memcpy(skip_header, &file_offset, logger_offset_size);
 }
 
@@ -591,7 +596,7 @@ int logger_read_part(struct part *p, size_t *offset, const char *buff) {
 
   /* Particle constants, which is a bit more complicated. */
   if (mask & logger_mask_data[logger_rho].mask) {
-    // TODO make it dependent of logger_mask_data
+    // TODO make it dependent of logger_mask_data.
     memcpy(&p->mass, buff, sizeof(float));
     buff += sizeof(float);
     memcpy(&p->id, buff, sizeof(long long));
@@ -694,7 +699,7 @@ int logger_read_timestamp(unsigned long long int *t, double *time,
     error("Timestamp message contains extra fields.");
 
   /* Copy the timestamp value from the buffer. */
-  // TODO make it dependent of logger_mask_data
+  // TODO make it dependent of logger_mask_data.
   memcpy(t, buff, sizeof(unsigned long long int));
   buff += sizeof(unsigned long long int);
 
diff --git a/src/logger.h b/src/logger.h
index 56e2c8ab94c66b24df1800877bb9cfb129c3e645..ed2d6374fa9031f526e79e790572c89f6176df4b 100644
--- a/src/logger.h
+++ b/src/logger.h
@@ -28,13 +28,15 @@
 #include "timeline.h"
 #include "units.h"
 
-/* Forward declaration */
+/* Forward declaration. */
 struct dump;
 struct gpart;
 struct part;
-/* TODO remove dependency */
 struct engine;
 
+#define logger_major_version 0
+#define logger_minor_version 1
+
 /**
  * Logger entries contain messages representing the particle data at a given
  * point in time during the simulation.
@@ -82,16 +84,18 @@ enum logger_masks_number {
   logger_h = 4,
   logger_rho = 5,
   logger_consts = 6,
-  logger_timestamp = 7,  /* expect it to be before count */
-  logger_count_mask = 8, /* Need to be the last */
+  logger_timestamp = 7,  /* expect it to be before count. */
+  logger_count_mask = 8, /* Need to be the last. */
 } __attribute__((packed));
 
 struct mask_data {
-  /* Number of bytes for a mask */
+  /* Number of bytes for a mask. */
   int size;
-  /* Mask value */
+
+  /* Mask value. */
   unsigned int mask;
-  /* name of the mask */
+
+  /* Name of the mask. */
   char name[100];
 };
 
@@ -100,51 +104,52 @@ extern const struct mask_data logger_mask_data[logger_count_mask];
 /* Size of the strings. */
 #define logger_string_length 200
 
-/* structure containing global data */
-struct logger {
-  /* Number of particle steps between dumping a chunk of data */
+/* structure containing global data. */
+struct logger_writer {
+  /* Number of particle steps between dumping a chunk of data. */
   short int delta_step;
 
-  /* Logger basename */
+  /* Logger basename. */
   char base_name[logger_string_length];
 
-  /* Dump file */
+  /*  Dump file (In the reader, the dump is cleaned, therefore it is renamed
+   * logfile). */
   struct dump dump;
 
-  /* timestamp offset for logger*/
+  /* timestamp offset for logger. */
   size_t timestamp_offset;
 
-  /* scaling factor when buffer is too small */
+  /* scaling factor when buffer is too small. */
   float buffer_scale;
 
-  /* Size of a chunk if every mask are activated */
+  /* Size of a chunk if every mask are activated. */
   int max_chunk_size;
 
 } SWIFT_STRUCT_ALIGN;
 
-/* required structure for each particle type */
+/* required structure for each particle type. */
 struct logger_part_data {
-  /* Number of particle updates since last output */
+  /* Number of particle updates since last output. */
   int steps_since_last_output;
 
-  /* offset of last particle log entry */
+  /* offset of last particle log entry. */
   size_t last_offset;
 };
 
 /* Function prototypes. */
 int logger_compute_chunk_size(unsigned int mask);
-void logger_log_all(struct logger *log, const struct engine *e);
-void logger_log_part(struct logger *log, const struct part *p,
+void logger_log_all(struct logger_writer *log, const struct engine *e);
+void logger_log_part(struct logger_writer *log, const struct part *p,
                      unsigned int mask, size_t *offset);
-void logger_log_gpart(struct logger *log, const struct gpart *p,
+void logger_log_gpart(struct logger_writer *log, const struct gpart *p,
                       unsigned int mask, size_t *offset);
-void logger_init(struct logger *log, struct swift_params *params);
-void logger_clean(struct logger *log);
-void logger_log_timestamp(struct logger *log, integertime_t t, double time,
-                          size_t *offset);
-void logger_ensure_size(struct logger *log, size_t total_nr_parts,
+void logger_init(struct logger_writer *log, struct swift_params *params);
+void logger_free(struct logger_writer *log);
+void logger_log_timestamp(struct logger_writer *log, integertime_t t,
+                          double time, size_t *offset);
+void logger_ensure_size(struct logger_writer *log, size_t total_nr_parts,
                         size_t total_nr_gparts, size_t total_nr_sparts);
-void logger_write_file_header(struct logger *log, const struct engine *e);
+void logger_write_file_header(struct logger_writer *log);
 
 int logger_read_part(struct part *p, size_t *offset, const char *buff);
 int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff);
@@ -164,12 +169,14 @@ INLINE static void logger_part_data_init(struct logger_part_data *logger) {
 /**
  * @brief Should this particle write its data now ?
  *
- * @param xp The #xpart.
- * @param e The #engine containing information about the current time.
- * @return 1 if the #part should write, 0 otherwise.
+ * @param logger_data The #logger_part_data of a particle.
+ * @param log The #logger.
+ *
+ * @return 1 if the particule should be writen, 0 otherwise.
  */
 __attribute__((always_inline)) INLINE static int logger_should_write(
-    const struct logger_part_data *logger_data, const struct logger *log) {
+    const struct logger_part_data *logger_data,
+    const struct logger_writer *log) {
 
   return (logger_data->steps_since_last_output > log->delta_step);
 }
diff --git a/src/logger_io.c b/src/logger_io.c
index 3cef3497b2912411cea6763f5418bc76a7f5ece0..c6be1f292434c759e20064542e91caa2cd238a4d 100644
--- a/src/logger_io.c
+++ b/src/logger_io.c
@@ -21,7 +21,7 @@
 /* Config parameters. */
 #include "../config.h"
 
-#ifdef WITH_LOGGER
+#if defined(WITH_LOGGER) && defined(HAVE_HDF5) && !defined(WITH_MPI)
 
 /* Some standard headers. */
 #include <hdf5.h>
@@ -87,7 +87,7 @@ void write_index_single(struct engine* e, const char* baseName,
   // struct spart* sparts = e->s->sparts;
   static int outputCount = 0;
 
-  struct logger* log = e->logger;
+  struct logger_writer* log = e->logger;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -296,4 +296,4 @@ void write_index_single(struct engine* e, const char* baseName,
   ++outputCount;
 }
 
-#endif /* HAVE_HDF5 */
+#endif /* WITH_LOGGER && HAVE_HDF5 && !WITH_MPI */
diff --git a/src/logger_io.h b/src/logger_io.h
index f5b1274fb7b957d5b48bc8425bf784c586ac6a08..a424c5c104b9f1090c69f7e0bb37e72635636f82 100644
--- a/src/logger_io.h
+++ b/src/logger_io.h
@@ -50,11 +50,13 @@ __attribute__((always_inline)) INLINE static void hydro_write_index(
   *num_fields = 2;
 
   /* List what we want to write */
-  list[0] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
-                                 UNIT_CONV_NO_UNITS, parts, id);
+  list[0] =
+      io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f,
+                           parts, id, "will be erased");
 
-  list[1] = io_make_output_field("Offset", ULONGLONG, 1, UNIT_CONV_NO_UNITS,
-                                 xparts, logger_data.last_offset);
+  list[1] =
+      io_make_output_field("Offset", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f,
+                           xparts, logger_data.last_offset, "will be erased");
 }
 #endif
 
diff --git a/tests/testLogger.c b/tests/testLogger.c
index c5be0d7cc18742bdc2fa6167462579c45fd43e92..d2c64e7fa3330ebd20cf8abc01a76e1dff08c8fc 100644
--- a/tests/testLogger.c
+++ b/tests/testLogger.c
@@ -32,8 +32,8 @@
 /* Local headers. */
 #include "swift.h"
 
-void test_log_parts(struct logger *log) {
-  struct dump *d = log->dump;
+void test_log_parts(struct logger_writer *log) {
+  struct dump *d = &log->dump;
 
   /* Write several copies of a part to the dump. */
   struct part p;
@@ -45,22 +45,27 @@ void test_log_parts(struct logger *log) {
   size_t offset = d->count;
 
   /* Write the full part. */
-  logger_log_part(log, &p,
-                  logger_mask_x | logger_mask_v | logger_mask_a |
-                      logger_mask_u | logger_mask_h | logger_mask_rho |
-                      logger_mask_consts,
-                  &offset);
+  logger_log_part(
+      log, &p,
+      logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask |
+          logger_mask_data[logger_a].mask | logger_mask_data[logger_u].mask |
+          logger_mask_data[logger_h].mask | logger_mask_data[logger_rho].mask |
+          logger_mask_data[logger_consts].mask,
+      &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Write only the position. */
   p.x[0] = 2.0;
-  logger_log_part(log, &p, logger_mask_x, &offset);
+  logger_log_part(log, &p, logger_mask_data[logger_x].mask, &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Write the position and velocity. */
   p.x[0] = 3.0;
   p.v[0] = 0.3;
-  logger_log_part(log, &p, logger_mask_x | logger_mask_v, &offset);
+  logger_log_part(
+      log, &p,
+      logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask,
+      &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Recover the last part from the dump. */
@@ -103,8 +108,8 @@ void test_log_parts(struct logger *log) {
   }
 }
 
-void test_log_gparts(struct logger *log) {
-  struct dump *d = log->dump;
+void test_log_gparts(struct logger_writer *log) {
+  struct dump *d = &log->dump;
 
   /* Write several copies of a part to the dump. */
   struct gpart p;
@@ -116,21 +121,26 @@ void test_log_gparts(struct logger *log) {
   size_t offset = d->count;
 
   /* Write the full part. */
-  logger_log_gpart(log, &p,
-                   logger_mask_x | logger_mask_v | logger_mask_a |
-                       logger_mask_h | logger_mask_consts,
-                   &offset);
+  logger_log_gpart(
+      log, &p,
+      logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask |
+          logger_mask_data[logger_a].mask | logger_mask_data[logger_h].mask |
+          logger_mask_data[logger_consts].mask,
+      &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Write only the position. */
   p.x[0] = 2.0;
-  logger_log_gpart(log, &p, logger_mask_x, &offset);
+  logger_log_gpart(log, &p, logger_mask_data[logger_x].mask, &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Write the position and velocity. */
   p.x[0] = 3.0;
   p.v_full[0] = 0.3;
-  logger_log_gpart(log, &p, logger_mask_x | logger_mask_v, &offset);
+  logger_log_gpart(
+      log, &p,
+      logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask,
+      &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Recover the last part from the dump. */
@@ -173,8 +183,8 @@ void test_log_gparts(struct logger *log) {
   }
 }
 
-void test_log_timestamps(struct logger *log) {
-  struct dump *d = log->dump;
+void test_log_timestamps(struct logger_writer *log) {
+  struct dump *d = &log->dump;
 
   /* The timestamp to log. */
   unsigned long long int t = 10;
@@ -245,7 +255,7 @@ void test_log_timestamps(struct logger *log) {
 int main(int argc, char *argv[]) {
 
   /* Prepare a logger. */
-  struct logger log;
+  struct logger_writer log;
   struct swift_params params;
   parser_read_file("logger.yml", &params);
   logger_init(&log, &params);
@@ -265,7 +275,7 @@ int main(int argc, char *argv[]) {
   remove(filename);
 
   /* Clean the logger. */
-  logger_clean(&log);
+  logger_free(&log);
 
   /* Return a happy number. */
   return 0;