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, ¶ms); + + /* Initialize the logger. */ + logger_init(&log, ¶ms); + + /* 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, ¶ms); + + /* 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, ¶ms); + + /* 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(×); + + /* 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(×, 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(×, 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, ×tamp, 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", ¶ms); logger_init(&log, ¶ms); @@ -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;