From ff157d3a1694e52e56b23f4347ab4e63b67c306c Mon Sep 17 00:00:00 2001 From: Loic Hausammann <loic.hausammann@protonmail.ch> Date: Sun, 11 Jul 2021 08:25:12 +0000 Subject: [PATCH] Make CSDS independent from SWIFT --- AUTHORS | 1 + COPYING | 1 + ChangeLog | 0 INSTALL | 1 + Makefile.am | 11 +- NEWS | 0 README | 1 + autogen.sh | 8 + configure.ac | 300 ++++++ doc/source/QuickStart/compiling_code.rst | 11 +- doc/source/QuickStart/index.rst | 12 +- doc/source/QuickStart/running_first.rst | 2 +- doc/source/QuickStart/using_python.rst | 12 +- examples/SimpleOrbits/makeIC.py | 2 +- examples/SimpleOrbits/plotSolution.py | 2 +- examples/reader_example.py | 2 +- m4/ax_check_compile_flag.m4 | 74 ++ m4/ax_check_enable_debug.m4 | 125 +++ m4/ax_compare_version.m4 | 178 ++++ m4/ax_compiler_vendor.m4 | 118 +++ m4/ax_compiler_version.m4 | 530 +++++++++++ m4/ax_prog_doxygen.m4 | 587 ++++++++++++ src/Makefile.am | 53 +- src/csds_clocks.c | 318 +++++++ src/csds_clocks.h | 59 ++ src/csds_cosmology.c | 6 +- src/csds_cosmology.h | 8 +- src/csds_cycle.h | 556 ++++++++++++ src/csds_definitions.h | 81 ++ src/csds_error.h | 100 ++ src/csds_fields.c | 32 +- src/csds_fields.h | 17 +- src/csds_hashmap.c | 10 +- src/csds_hashmap.h | 7 +- src/csds_header.c | 22 +- src/csds_header.h | 21 +- src/csds_index.c | 14 +- src/csds_index.h | 6 +- src/csds_inline.h | 45 + src/csds_interpolation.h | 42 +- src/csds_loader_io.h | 6 +- src/csds_logfile.c | 9 +- src/csds_logfile_writer.c | 249 +++++ src/csds_logfile_writer.h | 173 ++++ src/csds_openmp.h | 43 + src/csds_parameters.c | 54 +- src/csds_parameters.h | 11 +- src/csds_parser.c | 1051 ++++++++++++++++++++++ src/csds_parser.h | 110 +++ src/csds_part_type.c | 24 + src/csds_part_type.h | 40 + src/csds_particle.h | 10 +- src/csds_python_tools.h | 10 +- src/csds_python_wrapper.c | 120 ++- src/csds_reader.c | 375 ++++---- src/csds_reader.h | 5 +- src/csds_reader_generate_index.c | 308 +++---- src/csds_time.c | 14 +- src/csds_tools.c | 19 +- src/csds_tools.h | 57 +- src/csds_version.h.in | 31 + src/quick_sort.c | 3 + tests/Makefile.am | 19 +- tests/generate_log.h | 320 ++++--- tests/testHashmap.c | 8 +- tests/testLogfileHeader.c | 59 +- tests/testLogfileReader.c | 106 +-- tests/testQuickSort.c | 9 +- tests/testTimeArray.c | 5 +- tests/testVirtualReality.c | 36 +- 70 files changed, 5647 insertions(+), 1012 deletions(-) create mode 100644 AUTHORS create mode 120000 COPYING create mode 100644 ChangeLog create mode 120000 INSTALL create mode 100644 NEWS create mode 120000 README create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_check_enable_debug.m4 create mode 100644 m4/ax_compare_version.m4 create mode 100644 m4/ax_compiler_vendor.m4 create mode 100644 m4/ax_compiler_version.m4 create mode 100644 m4/ax_prog_doxygen.m4 create mode 100644 src/csds_clocks.c create mode 100644 src/csds_clocks.h create mode 100644 src/csds_cycle.h create mode 100644 src/csds_definitions.h create mode 100644 src/csds_error.h create mode 100644 src/csds_inline.h create mode 100644 src/csds_logfile_writer.c create mode 100644 src/csds_logfile_writer.h create mode 100644 src/csds_openmp.h create mode 100644 src/csds_parser.c create mode 100644 src/csds_parser.h create mode 100644 src/csds_part_type.c create mode 100644 src/csds_part_type.h create mode 100644 src/csds_version.h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..4f6ca2a --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Loic Hausammann \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..28ecdc8 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +/usr/share/automake-1.16/COPYING \ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 120000 index 0000000..e3f22c0 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +/usr/share/automake-1.16/INSTALL \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index a0d3095..b9805af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -# This file is part of SWIFT. +# This file is part of CSDS. # Copyright (c) 2021 Loic Hausammann (loic.hausammann@epfl.ch) # # This program is free software: you can redistribute it and/or modify @@ -14,9 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# subdirectories -SUBDIRS = src +# Automake stuff +ACLOCAL_AMFLAGS = -I m4 -if !HAVEPYTHON -SUBDIRS += tests -endif +# subdirectories +SUBDIRS = src tests diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..8e52b9a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,8 @@ +#! /bin/sh + +# Update generated configuration files, i.e. do work so that a +# developer checkout can be configured. + +autoreconf --install --symlink +exit + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..bb670d8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,300 @@ +# This file is part of CSDS. +# Copyright (C) 2021 loic.hausammann@epfl.ch. +# SWIFT +# +# 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/>. + +# Define the version +m4_define([major_version], [1]) +m4_define([minor_version], [5]) + +# Init the project. +AC_INIT([CSDS],[major_version.minor_version], + [https://gitlab.cosma.dur.ac.uk/lhausammann/csds-reader]) +csds_config_flags="$*" + +# Save the version +AC_SUBST([MAJOR_VERSION],[major_version]) +AC_SUBST([MINOR_VERSION],[minor_version]) + +# We want to stop when given unrecognised options. No subdirs so this is safe. +enable_option_checking=${enable_option_checking:-fatal} +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) + ;; + fatal) + { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } + ;; + *) + $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 + ;; + esac +fi + +AC_COPYRIGHT +AC_CONFIG_SRCDIR([src/csds_header.c]) +AC_CONFIG_AUX_DIR([.]) +AM_INIT_AUTOMAKE([subdir-objects]) + +# Add local macro collection. +AC_CONFIG_MACRO_DIR([m4]) + +# Stop default CFLAGS from anyone except the environment. +: ${CFLAGS=""} + +# Generate header file. +AM_CONFIG_HEADER(config.h) + +# Find and test the compiler. +AX_CHECK_ENABLE_DEBUG +AC_PROG_CC +AM_PROG_CC_C_O +AC_OPENMP + +# Check if openmp is disabled +with_openmp="yes" +if [[ -z "$ac_cv_prog_c_openmp" ]] +then + CFLAGS="$CFLAGS -Wno-unknown-pragmas" + with_openmp="no" +else + AC_DEFINE([CSDS_WITH_OPENMP], 1, [Compile the code with OpenMP]) +fi + +# Check for compiler version and vendor. +AX_COMPILER_VENDOR +AX_COMPILER_VERSION + +# Restrict support. +AC_C_RESTRICT + +# Add libtool support (now that CC is defined). +LT_INIT + +# Need C99 and inline support. +AC_PROG_CC_C99 +AC_C_INLINE + +# If debugging try to show inlined functions. +if test "x$enable_debug" = "xyes"; then + # Show inlined functions. + if test "$ax_cv_c_compiler_vendor" = "gnu"; then + # Would like to use -gdwarf and let the compiler pick a good version + # but that doesn't always work. + AX_CHECK_COMPILE_FLAG([-gdwarf -fvar-tracking-assignments], + [inline_EXTRA_FLAGS="-gdwarf -fvar-tracking-assignments"], + [inline_EXTRA_FLAGS="-gdwarf-2 -fvar-tracking-assignments"]) + CFLAGS="$CFLAGS $inline_EXTRA_FLAGS" + elif test "$ax_cv_c_compiler_vendor" = "intel"; then + CFLAGS="$CFLAGS -debug inline-debug-info" + fi +fi + +# Check if expensive debugging is on. +AC_ARG_ENABLE([debugging-checks], + [AS_HELP_STRING([--enable-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [enable_debugging_checks="$enableval"], + [enable_debugging_checks="no"] +) +if test "$enable_debugging_checks" = "yes"; then + AC_DEFINE([CSDS_DEBUG_CHECKS],1,[Enable expensive debugging]) +fi + +# Add address sanitizer options to flags, if requested. Only useful for GCC +# version 4.8 and later and clang. +AC_ARG_ENABLE([sanitizer], + [AS_HELP_STRING([--enable-sanitizer], + [Enable memory error detection using address sanitizer @<:@no/yes@:>@] + )], + [enable_san="$enableval"], + [enable_san="no"] +) + +if test "$enable_san" = "yes"; then + if test "$ax_cv_c_compiler_vendor" = "gnu"; then + AX_COMPARE_VERSION( $ax_cv_c_compiler_version, [ge], [4.8.0], + [enable_san="yes"], [enable_san="no"] ) + elif test "$ax_cv_c_compiler_vendor" = "clang"; then + AX_COMPARE_VERSION( $ax_cv_c_compiler_version, [ge], [3.2.0], + [enable_san="yes"], [enable_san="no"] ) + fi + if test "$enable_san" = "yes"; then + CFLAGS="$CFLAGS -fsanitize=address -fno-omit-frame-pointer" + AC_MSG_RESULT([added address sanitizer support]) + else + AC_MSG_WARN([Compiler does not support address sanitizer option]) + fi +fi + +# Add the undefined sanitizer option to flags. Only useful for GCC +# version 4.9 and later and clang to detected undefined code behaviour +# such as integer overflow and memory alignment issues. +AC_ARG_ENABLE([undefined-sanitizer], + [AS_HELP_STRING([--enable-undefined-sanitizer], + [Enable detection of code that causes undefined behaviour @<:@no/yes@:>@] + )], + [enable_ubsan="$enableval"], + [enable_ubsan="no"] +) + +if test "$enable_ubsan" = "yes"; then + if test "$ax_cv_c_compiler_vendor" = "gnu"; then + AX_COMPARE_VERSION( $ax_cv_c_compiler_version, [ge], [4.9.0], + [enable_ubsan="yes"], [enable_ubsan="no"] ) + elif test "$ax_cv_c_compiler_vendor" = "clang"; then + AX_COMPARE_VERSION( $ax_cv_c_compiler_version, [ge], [3.7.0], + [enable_ubsan="yes"], [enable_ubsan="no"] ) + fi + if test "$enable_ubsan" = "yes"; then + CFLAGS="$CFLAGS -fsanitize=undefined" + AC_MSG_RESULT([added undefined sanitizer support]) + else + AC_MSG_WARN([Compiler does not support undefined sanitizer option]) + fi +fi + +# Autoconf stuff. +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_HEADER_STDC + +# Check for the libraries we will need. +AC_CHECK_LIB(m,sqrt,,AC_MSG_ERROR(something is wrong with the math library!)) + +# 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_LIBS], [Linking flags for python, bypassing python-config]) + 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` + PYTHON_LIBS=`$PYTHON_CONFIG --ldflags --embed` + ]) + have_python="yes" + AC_DEFINE([HAVE_PYTHON],1,[Python appears to be present.]) +fi +AC_SUBST([PYTHON_INCS]) +AC_SUBST([PYTHON_LIBS]) +AM_CONDITIONAL([HAVEPYTHON],[test -n "$PYTHON_INCS"]) + + +# Check for timing functions needed by cycle.h. +AC_HEADER_TIME +AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) +AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t +is defined in <sys/time.h>])],, +[#if HAVE_SYS_TIME_H +#include <sys/time.h> +#endif]) +AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time]) +AC_MSG_CHECKING([for _rtc intrinsic]) +rtc_ok=yes +AC_LINK_IFELSE([AC_LANG_PROGRAM( +[[#ifdef HAVE_INTRINSICS_H +#include <intrinsics.h> +#endif]], +[[_rtc()]])], +[AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])],[rtc_ok=no]) +AC_MSG_RESULT($rtc_ok) + +# Add warning flags by default, if these can be used. Option =error adds +# -Werror to GCC, clang and Intel. Note do this last as compiler tests may +# become errors, if that's an issue don't use CFLAGS for these, use an AC_SUBST(). +AC_ARG_ENABLE([compiler-warnings], + [AS_HELP_STRING([--enable-compiler-warnings], + [Enable compile time warning flags, if compiler is known @<:@error/no/yes)@:>@] + )], + [enable_warn="$enableval"], + [enable_warn="error"] +) +if test "$enable_warn" != "no"; then + + # AX_CFLAGS_WARN_ALL does not give good warning flags for the Intel compiler + # We will do this by hand instead and only default to the macro for unknown compilers + case "$ax_cv_c_compiler_vendor" in + gnu | clang) + CFLAGS="$CFLAGS -Wall -Wextra -Wshadow" + ;; + intel) + CFLAGS="$CFLAGS -w2 -Wunused-variable -Wshadow" + ;; + *) + AX_CFLAGS_WARN_ALL + ;; + esac + + # Add a "choke on warning" flag if it exists + if test "$enable_warn" = "error"; then + case "$ax_cv_c_compiler_vendor" in + intel | gnu | clang) + CFLAGS="$CFLAGS -Werror" + ;; + esac + fi + + # We want strict-prototypes, but this must still work even if warnings + # are an error. + AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes],[CFLAGS="$CFLAGS -Wstrict-prototypes"], + [CFLAGS="$CFLAGS"],[$CFLAGS],[AC_LANG_SOURCE([int main(void){return 0;}])]) +fi + +# Handle .in files. +AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile]) + +# Save the compilation options +AC_DEFINE_UNQUOTED([CSDS_CONFIG_FLAGS],["$csds_config_flags"],[Flags passed to configure]) + +# Generate output. +AC_OUTPUT + +# Report general configuration. +AC_MSG_RESULT([ + ------- Summary -------- + + $PACKAGE_NAME v.$PACKAGE_VERSION + + Compiler : $CC + - vendor : $ax_cv_c_compiler_vendor + - version : $ax_cv_c_compiler_version + - flags : $CFLAGS $OPENMP_CFLAGS + OpenMP enabled : $with_openmp + Debugging checks : $enable_debugging_checks + Python enabled : $have_python + +------------------------]) diff --git a/doc/source/QuickStart/compiling_code.rst b/doc/source/QuickStart/compiling_code.rst index 3d78b60..06a030c 100644 --- a/doc/source/QuickStart/compiling_code.rst +++ b/doc/source/QuickStart/compiling_code.rst @@ -6,23 +6,18 @@ How to Compile the Code ======================= -To compile this code, you need to download `SWIFT's repository <www.swiftsim.com>`_ -and follow the instruction on how to compile it. -Currently, the CSDS is implemented only within a few different scheme. -If you wish to implement a new one, please follow the implementation -within the other schems and :ref:`implementation`. -During the configuration step, the CSDS needs to be enabled along with python wrapper if needed: +To configure the CSDS (with the python wrapper), you need to use the following commands: .. code-block:: bash ./autogen.sh - ./configure --enable-csds --with-python=$PYTHON_PATH + ./configure --with-python=$PYTHON_PATH On a standard ubuntu, ``$PYTHON_PATH`` is given by ``/usr/``. It is worth to mention that the CSDS will not work with python 2.7.x and that the user should move to 3.0 as soon as possible. -Then both SWIFT and the CSDS can be compiled: +Then the CSDS can be compiled: .. code-block:: bash diff --git a/doc/source/QuickStart/index.rst b/doc/source/QuickStart/index.rst index ee70ca7..6c64792 100644 --- a/doc/source/QuickStart/index.rst +++ b/doc/source/QuickStart/index.rst @@ -7,11 +7,13 @@ QuickStart ========== Here you will find how to compile the CSDS and run it on your first simulations. -The CSDS is currently fully based on SWIFT and thus cannot be used directly without this code. -Anyway, this code can still be used without running SWIFT at the condition that the output file -correspond. -In the future, we are planning to make this library fully independent from SWIFT. - +This code was initially designed for SWIFT, but can be used with other codes +if the output files are correctly written. +SWIFT is the reference implementation for the CSDS writer. +Due to the large number of schemes, not all of them have a CSDS implementation. +Therefore you should try with the default ones first. +If you wish to implement a new one, please follow the implementation +within the other schems and :ref:`implementation`. .. toctree:: :maxdepth: 2 diff --git a/doc/source/QuickStart/running_first.rst b/doc/source/QuickStart/running_first.rst index 043bfdd..8253046 100644 --- a/doc/source/QuickStart/running_first.rst +++ b/doc/source/QuickStart/running_first.rst @@ -19,7 +19,7 @@ Let's start with the compilation of SWIFT and running our first example. cd examples/HydroTests/SedovBlast_3D ./run.sh -The file ``run.sh`` is not running with the CSDS by default and needs to add +The file ``run.sh`` is not running with the CSDS by default and you will need to add the runtime parameter ``--csds`` to the command calling SWIFT (e.g. ``../../swift --csds --hydro --limiter --threads=4 sedov.yml``). If everything is fine, the last message from SWIFT should be ``Done`` diff --git a/doc/source/QuickStart/using_python.rst b/doc/source/QuickStart/using_python.rst index ebd3280..0f3a975 100644 --- a/doc/source/QuickStart/using_python.rst +++ b/doc/source/QuickStart/using_python.rst @@ -8,13 +8,13 @@ Python API The python API is relatively simple and fully self documented. Here, I will explain the main features and how they work. -As the CSDS is currently depend on SWIFT and the scheme used, -the library should be manually imported: +If you do not wish to install the CSDS as a python library, +you will need be manually import it: .. code-block:: python import sys - sys.path.append("SWIFT_PATH/csds/src/.libs/") + sys.path.append("CSDS_PATH/src/.libs/") import libcsds as csds This module contains a single object (``Reader``) that fully deals with @@ -23,13 +23,13 @@ To open a logfile, this object is call in a with statement: .. code-block:: python - with csds.Reader(filename, verbose=0, number_threads=1, number_index=10, + with csds.Reader(filename, verbose=0, number_index=10, restart_init=False) as reader: # Use the Reader In this example, all the parameters are set with their default values. The Reader is able to make some operations in parallel and -will use the number of threads given by ``number_threads``. +will openmp to make them. The index files are used to speedup the reading and are set at regular interval of simulation time (e.g. every :math:`(t_e - t_b) / (N - 1)` where :math:`t_b` (:math:`t_e`) is the initial (final) time and :math:`N` the number of index file. @@ -87,7 +87,7 @@ Obviously, the parameter ``time`` is the requested time (in internal units) that should be within the limits provided by ``get_time_limits``. ``filter_by_ids`` allows to pick only a subset of the particles. This parameter takes a list containing an array for each type of particle (or None). -The list should have the same size than the number of particle types in SWIFT. +The list should have the same size than the number of particle types in the CSDS. The last parameter allows to read only some type of particles (e.g. ``part_type=0`` or ``part_type=[0, 1]``). The two last parameters cannot be used together. diff --git a/examples/SimpleOrbits/makeIC.py b/examples/SimpleOrbits/makeIC.py index a241849..a278db3 100644 --- a/examples/SimpleOrbits/makeIC.py +++ b/examples/SimpleOrbits/makeIC.py @@ -1,5 +1,5 @@ ############################################################################### -# This file is part of SWIFT. +# This file is part of CSDS. # Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/SimpleOrbits/plotSolution.py b/examples/SimpleOrbits/plotSolution.py index db4a8cc..64c5d67 100644 --- a/examples/SimpleOrbits/plotSolution.py +++ b/examples/SimpleOrbits/plotSolution.py @@ -1,5 +1,5 @@ ############################################################################### -# This file is part of SWIFT. +# This file is part of CSDS. # Copyright (c) 2020 Loic Hausammann (loic.hausammann@epfl.ch) # # This program is free software: you can redistribute it and/or modify diff --git a/examples/reader_example.py b/examples/reader_example.py index a28e68b..b0e6944 100644 --- a/examples/reader_example.py +++ b/examples/reader_example.py @@ -62,7 +62,7 @@ for f in args.files: filename = f[:-5] else: raise Exception("It seems that you are not providing a logfile (.dump)") - with csds.Reader(filename, verbose=0, number_threads=1, number_index=10, + with csds.Reader(filename, verbose=0, number_index=10, restart_init=False) as reader: # Ensure that the fields are present diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..89d3b93 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# +# 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/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.63)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4 new file mode 100644 index 0000000..c9e28db --- /dev/null +++ b/m4/ax_check_enable_debug.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) +# +# DESCRIPTION +# +# Check for the presence of an --enable-debug option to configure, with +# the specified default value used when the option is not present. Return +# the value in the variable $ax_enable_debug. +# +# Specifying 'yes' adds '-g -O0' to the compilation flags for all +# languages. Specifying 'info' adds '-g' to the compilation flags. +# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to +# the linking flags. Otherwise, nothing is added. +# +# Define the variables listed in the second argument if debug is enabled, +# defaulting to no variables. Defines the variables listed in the third +# argument if debug is disabled, defaulting to NDEBUG. All lists of +# variables should be space-separated. +# +# If debug is not enabled, ensure AC_PROG_* will not add debugging flags. +# Should be invoked prior to any AC_PROG_* compiler checks. +# +# IS-RELEASE can be used to change the default to 'no' when making a +# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it +# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE +# macro, there is no need to pass this parameter. +# +# AX_IS_RELEASE([git-directory]) +# AX_CHECK_ENABLE_DEBUG() +# +# LICENSE +# +# Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com> +# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 5 + +AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ + AC_BEFORE([$0],[AC_PROG_CC])dnl + AC_BEFORE([$0],[AC_PROG_CXX])dnl + AC_BEFORE([$0],[AC_PROG_F77])dnl + AC_BEFORE([$0],[AC_PROG_FC])dnl + + AC_MSG_CHECKING(whether to enable debugging) + + ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) + ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, + [$ax_is_release], + [$4]))) + + # If this is a release, override the default. + AS_IF([test "$ax_enable_debug_is_release" = "yes"], + [ax_enable_debug_default="no"]) + + m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) + m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) + + AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], + [],enable_debug=$ax_enable_debug_default) + + # empty mean debug yes + AS_IF([test "x$enable_debug" = "x"], + [enable_debug="yes"]) + + # case of debug + AS_CASE([$enable_debug], + [yes],[ + AC_MSG_RESULT(yes) + CFLAGS="-g -O0 ${CFLAGS}" + CXXFLAGS="-g -O0 ${CXXFLAGS}" + FFLAGS="-g -O0 ${FFLAGS}" + FCFLAGS="-g -O0 ${FCFLAGS}" + OBJCFLAGS="-g -O0 ${OBJCFLAGS}" + ], + [info],[ + AC_MSG_RESULT(info) + CFLAGS="${CFLAGS} -g" + CXXFLAGS="${CXXFLAGS} -g" + FFLAGS="${FFLAGS} -g" + FCFLAGS="${FCFLAGS} -g" + OBJCFLAGS="${OBJCFLAGS} -g" + ], + [profile],[ + AC_MSG_RESULT(profile) + CFLAGS="${CFLAGS} -g -pg" + CXXFLAGS="${CXXFLAGS} -g -pg" + FFLAGS="${FFLAGS} -g -pg" + FCFLAGS="${FCFLAGS} -g -pg" + OBJCFLAGS="${OBJCFLAGS} -g -pg" + LDFLAGS="${LDFLAGS} -pg" + ], + [ + AC_MSG_RESULT(no) + dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags + dnl by setting any unset environment flag variables + AS_IF([test "x${CFLAGS+set}" != "xset"], + [CFLAGS=""]) + AS_IF([test "x${CXXFLAGS+set}" != "xset"], + [CXXFLAGS=""]) + AS_IF([test "x${FFLAGS+set}" != "xset"], + [FFLAGS=""]) + AS_IF([test "x${FCFLAGS+set}" != "xset"], + [FCFLAGS=""]) + AS_IF([test "x${OBJCFLAGS+set}" != "xset"], + [OBJCFLAGS=""]) + ]) + + dnl Define various variables if debugging is disabled. + dnl assert.h is a NOP if NDEBUG is defined, so define it by default. + dnl XXX disabled as m4_map_args_w requires 2.64, and COSMA has 2.63. + dnl AS_IF([test "x$enable_debug" = "xyes"], + dnl [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], + dnl [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) + ax_enable_debug=$enable_debug +]) diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 new file mode 100644 index 0000000..6df1c53 --- /dev/null +++ b/m4/ax_compare_version.m4 @@ -0,0 +1,178 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compare_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# This macro compares two version strings. Due to the various number of +# minor-version numbers that can exist, and the fact that string +# comparisons are not compatible with numeric comparisons, this is not +# necessarily trivial to do in a autoconf script. This macro makes doing +# these comparisons easy. +# +# The six basic comparisons are available, as well as checking equality +# limited to a certain number of minor-version levels. +# +# The operator OP determines what type of comparison to do, and can be one +# of: +# +# eq - equal (test A == B) +# ne - not equal (test A != B) +# le - less than or equal (test A <= B) +# ge - greater than or equal (test A >= B) +# lt - less than (test A < B) +# gt - greater than (test A > B) +# +# Additionally, the eq and ne operator can have a number after it to limit +# the test to that number of minor versions. +# +# eq0 - equal up to the length of the shorter version +# ne0 - not equal up to the length of the shorter version +# eqN - equal up to N sub-version levels +# neN - not equal up to N sub-version levels +# +# When the condition is true, shell commands ACTION-IF-TRUE are run, +# otherwise shell commands ACTION-IF-FALSE are run. The environment +# variable 'ax_compare_version' is always set to either 'true' or 'false' +# as well. +# +# Examples: +# +# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) +# +# would both be true. +# +# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) +# +# would both be false. +# +# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) +# +# would be true because it is only comparing two minor versions. +# +# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) +# +# would be true because it is only comparing the lesser number of minor +# versions of the two values. +# +# Note: The characters that separate the version numbers do not matter. An +# empty string is the same as version 0. OP is evaluated by autoconf, not +# configure, so must be a string, not a variable. +# +# The author would like to acknowledge Guido Draheim whose advice about +# the m4_case and m4_ifvaln functions make this macro only include the +# portions necessary to perform the specific comparison specified by the +# OP argument in the final configure script. +# +# LICENSE +# +# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +dnl ######################################################################### +AC_DEFUN([AX_COMPARE_VERSION], [ + AC_REQUIRE([AC_PROG_AWK]) + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` + B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + ], + [.+],[ + AC_WARNING( + [invalid OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + AC_WARNING([invalid OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION + diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 0000000..6b67c4f --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -0,0 +1,118 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C, C++ or Fortran compiler. The vendor is +# returned in the cache variable $ax_cv_c_compiler_vendor for C, +# $ax_cv_cxx_compiler_vendor for C++ or $ax_cv_fc_compiler_vendor for +# (modern) Fortran. The value is one of "intel", "ibm", "pathscale", +# "clang" (LLVM), "cray", "fujitsu", "sdcc", "sx", "portland" (PGI), "gnu" +# (GCC), "sun" (Oracle Developer Studio), "hp", "dec", "borland", +# "comeau", "kai", "lcc", "sgi", "microsoft", "metrowerks", "watcom", +# "tcc" (Tiny CC) or "unknown" (if the compiler cannot be determined). +# +# To check for a Fortran compiler, you must first call AC_FC_PP_SRCEXT +# with an appropriate preprocessor-enabled extension. For example: +# +# AC_LANG_PUSH([Fortran]) +# AC_PROG_FC +# AC_FC_PP_SRCEXT([F]) +# AX_COMPILER_VENDOR +# AC_LANG_POP([Fortran]) +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> +# Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2018-19 John Zaitseff <J.Zaitseff@zap.org.au> +# +# 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 <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 30 + +AC_DEFUN([AX_COMPILER_VENDOR], [dnl + AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [dnl + dnl If you modify this list of vendors, please add similar support + dnl to ax_compiler_version.m4 if at all possible. + dnl + dnl Note: Do NOT check for GCC first since some other compilers + dnl define __GNUC__ to remain compatible with it. Compilers that + dnl are very slow to start (such as Intel) are listed first. + + vendors=" + intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__,__ibmxl__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + sdcc: SDCC,__SDCC + sx: _SX + portland: __PGI + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC,__SUNPRO_F90,__SUNPRO_F95 + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + tcc: __TINYC__ + unknown: UNKNOWN + " + for ventest in $vendors; do + case $ventest in + *:) + vendor=$ventest + continue + ;; + *) + vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" + ;; + esac + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ +#if !($vencpp) + thisisanerror; +#endif + ]])], [break]) + done + + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +])dnl + diff --git a/m4/ax_compiler_version.m4 b/m4/ax_compiler_version.m4 new file mode 100644 index 0000000..4995beb --- /dev/null +++ b/m4/ax_compiler_version.m4 @@ -0,0 +1,530 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compiler_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VERSION +# +# DESCRIPTION +# +# This macro retrieves the compiler version and returns it in the cache +# variable $ax_cv_c_compiler_version for C and $ax_cv_cxx_compiler_version +# for C++. +# +# Version is returned as epoch:major.minor.patchversion +# +# Epoch is used in order to have an increasing version number in case of +# marketing change. +# +# Epoch use: * borland compiler use chronologically 0turboc for turboc +# era, +# +# 1borlanc BORLANDC++ before 5, 2cppbuilder for cppbuilder era, +# 3borlancpp for return of BORLANDC++ (after version 5.5), +# 4cppbuilder for cppbuilder with year version, +# and 5xe for XE era. +# +# An empty string is returned otherwise. +# +# LICENSE +# +# Copyright (c) 2014 Bastien ROUCARIES <roucaries.bastien+autoconf@gmail.com> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +# for intel +AC_DEFUN([_AX_COMPILER_VERSION_INTEL], + [ dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [__INTEL_COMPILER/100],, + AC_MSG_FAILURE([[[$0]] unknown intel compiler version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [(__INTEL_COMPILER%100)/10],, + AC_MSG_FAILURE([[[$0]] unknown intel compiler version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [(__INTEL_COMPILER%10)],, + AC_MSG_FAILURE([[[$0]] unknown intel compiler version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# for IBM +AC_DEFUN([_AX_COMPILER_VERSION_IBM], + [ dnl + dnl check between z/OS C/C++ and XL C/C++ + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([], + [ + #if defined(__COMPILER_VER__) + choke me; + #endif + ])], + [ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [__xlC__/100],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler major version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [__xlC__%100],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__xlC_ver__/0x100],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_build, + [__xlC_ver__%0x100],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler build version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_build" + ], + [ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__xlC__%1000],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [(__xlC__/10000)%10],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [(__xlC__/100000)%10],, + AC_MSG_FAILURE([[[$0]] unknown IBM compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) +]) + +# for pathscale +AC_DEFUN([_AX_COMPILER_VERSION_PATHSCALE],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + __PATHCC__,, + AC_MSG_FAILURE([[[$0]] unknown pathscale major])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + __PATHCC_MINOR__,, + AC_MSG_FAILURE([[[$0]] unknown pathscale minor])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__PATHCC_PATCHLEVEL__],, + AC_MSG_FAILURE([[[$0]] unknown pathscale patch level])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# for clang +AC_DEFUN([_AX_COMPILER_VERSION_CLANG],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + __clang_major__,, + AC_MSG_FAILURE([[[$0]] unknown clang major])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + __clang_minor__,, + AC_MSG_FAILURE([[[$0]] unknown clang minor])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__clang_patchlevel__],,0) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# for crayc +AC_DEFUN([_AX_COMPILER_VERSION_CRAY],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + _RELEASE,, + AC_MSG_FAILURE([[[$0]] unknown crayc release])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + _RELEASE_MINOR,, + AC_MSG_FAILURE([[[$0]] unknown crayc minor])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor" + ]) + +# for fujitsu +AC_DEFUN([_AX_COMPILER_VERSION_FUJITSU],[ + AC_COMPUTE_INT(ax_cv_[]_AC_LANG_ABBREV[]_compiler_version, + __FCC_VERSION,, + AC_MSG_FAILURE([[[$0]]unknown fujitsu release])) + ]) + +# for GNU +AC_DEFUN([_AX_COMPILER_VERSION_GNU],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + __GNUC__,, + AC_MSG_FAILURE([[[$0]] unknown gcc major])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + __GNUC_MINOR__,, + AC_MSG_FAILURE([[[$0]] unknown gcc minor])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__GNUC_PATCHLEVEL__],, + AC_MSG_FAILURE([[[$0]] unknown gcc patch level])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# For sun +AC_DEFUN([_AX_COMPILER_VERSION_SUN],[ + m4_define([_AX_COMPILER_VERSION_SUN_NUMBER], + [ + #if defined(__SUNPRO_CC) + __SUNPRO_CC + #else + __SUNPRO_C + #endif + ]) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_until59, + !!(_AX_COMPILER_VERSION_SUN_NUMBER < 0x1000),, + AC_MSG_FAILURE([[[$0]] unknown sun release version])) + AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_until59" = X1], + [dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + _AX_COMPILER_VERSION_SUN_NUMBER % 0x10,, + AC_MSG_FAILURE([[[$0]] unknown sun patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + (_AX_COMPILER_VERSION_SUN_NUMBER / 0x10) % 0x10,, + AC_MSG_FAILURE([[[$0]] unknown sun minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (_AX_COMPILER_VERSION_SUN_NUMBER / 0x100),, + AC_MSG_FAILURE([[[$0]] unknown sun major version])) + ], + [dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + _AX_COMPILER_VERSION_SUN_NUMBER % 0x10,, + AC_MSG_FAILURE([[[$0]] unknown sun patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + (_AX_COMPILER_VERSION_SUN_NUMBER / 0x100) % 0x100,, + AC_MSG_FAILURE([[[$0]] unknown sun minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (_AX_COMPILER_VERSION_SUN_NUMBER / 0x1000),, + AC_MSG_FAILURE([[[$0]] unknown sun major version])) + ]) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" +]) + +AC_DEFUN([_AX_COMPILER_VERSION_HP],[ + m4_define([_AX_COMPILER_VERSION_HP_NUMBER], + [ + #if defined(__HP_cc) + __HP_cc + #else + __HP_aCC + #endif + ]) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_untilA0121, + !!(_AX_COMPILER_VERSION_HP_NUMBER <= 1),, + AC_MSG_FAILURE([[[$0]] unknown hp release version])) + AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_untilA0121" = X1], + [dnl By default output last version with this behavior. + dnl it is so old + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="01.21.00" + ], + [dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + (_AX_COMPILER_VERSION_HP_NUMBER % 100),, + AC_MSG_FAILURE([[[$0]] unknown hp release version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + ((_AX_COMPILER_VERSION_HP_NUMBER / 100)%100),, + AC_MSG_FAILURE([[[$0]] unknown hp minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + ((_AX_COMPILER_VERSION_HP_NUMBER / 10000)%100),, + AC_MSG_FAILURE([[[$0]] unknown hp major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) +]) + +AC_DEFUN([_AX_COMPILER_VERSION_DEC],[dnl + m4_define([_AX_COMPILER_VERSION_DEC_NUMBER], + [ + #if defined(__DECC_VER) + __DECC_VER + #else + __DECCXX_VER + #endif + ]) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + (_AX_COMPILER_VERSION_DEC_NUMBER % 10000),, + AC_MSG_FAILURE([[[$0]] unknown dec release version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + ((_AX_COMPILER_VERSION_DEC_NUMBER / 100000UL)%100),, + AC_MSG_FAILURE([[[$0]] unknown dec minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + ((_AX_COMPILER_VERSION_DEC_NUMBER / 10000000UL)%100),, + AC_MSG_FAILURE([[[$0]] unknown dec major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# borland +AC_DEFUN([_AX_COMPILER_VERSION_BORLAND],[dnl + m4_define([_AX_COMPILER_VERSION_TURBOC_NUMBER], + [ + #if defined(__TURBOC__) + __TURBOC__ + #else + choke me + #endif + ]) + m4_define([_AX_COMPILER_VERSION_BORLANDC_NUMBER], + [ + #if defined(__BORLANDC__) + __BORLANDC__ + #else + __CODEGEARC__ + #endif + ]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM(, + _AX_COMPILER_VERSION_TURBOC_NUMBER)], + [dnl TURBOC + AC_COMPUTE_INT( + _ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw, + _AX_COMPILER_VERSION_TURBOC_NUMBER,, + AC_MSG_FAILURE([[[$0]] unknown turboc version])) + AS_IF( + [test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw -lt 661 || test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw -gt 1023], + [dnl compute normal version + AC_COMPUTE_INT( + _ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + _AX_COMPILER_VERSION_TURBOC_NUMBER % 0x100,, + AC_MSG_FAILURE([[[$0]] unknown turboc minor version])) + AC_COMPUTE_INT( + _ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (_AX_COMPILER_VERSION_TURBOC_NUMBER/0x100)%0x100,, + AC_MSG_FAILURE([[[$0]] unknown turboc major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor"], + [dnl special version + AS_CASE([$_ax_[]_AC_LANG_ABBREV[]_compiler_version_turboc_raw], + [661],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:1.00"], + [662],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:1.01"], + [663],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="0turboc:2.00"], + [ + AC_MSG_WARN([[[$0]] unknown turboc version between 0x295 and 0x400 please report bug]) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="" + ]) + ]) + ], + # borlandc + [ + AC_COMPUTE_INT( + _ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw, + _AX_COMPILER_VERSION_BORLANDC_NUMBER,, + AC_MSG_FAILURE([[[$0]] unknown borlandc version])) + AS_CASE([$_ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw], + dnl BORLANDC++ before 5.5 + [512] ,[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:2.00"], + [1024],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.00"], + [1024],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.00"], + [1040],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:3.1"], + [1106],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:4.0"], + [1280],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:5.0"], + [1312],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="1borlanc:5.02"], + dnl C++ Builder era + [1328],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="2cppbuilder:3.0"], + [1344],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="2cppbuilder:4.0"], + dnl BORLANDC++ after 5.5 + [1360],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.5"], + [1361],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.51"], + [1378],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="3borlancpp:5.6.4"], + dnl C++ Builder with year number + [1392],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2006"], + [1424],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2007"], + [1555],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2009"], + [1569],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="4cppbuilder:2010"], + dnl XE version + [1584],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe"], + [1600],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:2"], + [1616],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:3"], + [1632],[ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="5xe:4"], + [ + AC_MSG_WARN([[[$0]] Unknown borlandc compiler version $_ax_[]_AC_LANG_ABBREV[]_compiler_version_borlandc_raw please report bug]) + ]) + ]) + ]) + +# COMO +AC_DEFUN([_AX_COMPILER_VERSION_COMEAU], + [ dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [__COMO_VERSION__%100],, + AC_MSG_FAILURE([[[$0]] unknown comeau compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [(__COMO_VERSION__/100)%10],, + AC_MSG_FAILURE([[[$0]] unknown comeau compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor" + ]) + +# KAI +AC_DEFUN([_AX_COMPILER_VERSION_KAI],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__KCC_VERSION%100],, + AC_MSG_FAILURE([[[$0]] unknown kay compiler patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [(__KCC_VERSION/100)%10],, + AC_MSG_FAILURE([[[$0]] unknown kay compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [(__KCC_VERSION/1000)%10],, + AC_MSG_FAILURE([[[$0]] unknown kay compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +dnl LCC +dnl LCC does not output version... + +# SGI +AC_DEFUN([_AX_COMPILER_VERSION_SGI],[ + m4_define([_AX_COMPILER_VERSION_SGI_NUMBER], + [ + #if defined(_COMPILER_VERSION) + _COMPILER_VERSION + #else + _SGI_COMPILER_VERSION + #endif + ]) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [_AX_COMPILER_VERSION_SGI_NUMBER%10],, + AC_MSG_FAILURE([[[$0]] unknown SGI compiler patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + [(_AX_COMPILER_VERSION_SGI_NUMBER/10)%10],, + AC_MSG_FAILURE([[[$0]] unknown SGI compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + [(_AX_COMPILER_VERSION_SGI_NUMBER/100)%10],, + AC_MSG_FAILURE([[[$0]] unknown SGI compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# microsoft +AC_DEFUN([_AX_COMPILER_VERSION_MICROSOFT],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + _MSC_VER%100,, + AC_MSG_FAILURE([[[$0]] unknown microsoft compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (_MSC_VER/100)%100,, + AC_MSG_FAILURE([[[$0]] unknown microsoft compiler major version])) + dnl could be overridden + _ax_[]_AC_LANG_ABBREV[]_compiler_version_patch=0 + _ax_[]_AC_LANG_ABBREV[]_compiler_version_build=0 + # special case for version 6 + AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major" = "X12"], + [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + _MSC_FULL_VER%1000,, + _ax_[]_AC_LANG_ABBREV[]_compiler_version_patch=0)]) + # for version 7 + AS_IF([test "X$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major" = "X13"], + [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + _MSC_FULL_VER%1000,, + AC_MSG_FAILURE([[[$0]] unknown microsoft compiler patch version])) + ]) + # for version > 8 + AS_IF([test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_major -ge 14], + [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + _MSC_FULL_VER%10000,, + AC_MSG_FAILURE([[[$0]] unknown microsoft compiler patch version])) + ]) + AS_IF([test $_ax_[]_AC_LANG_ABBREV[]_compiler_version_major -ge 15], + [AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_build, + _MSC_BUILD,, + AC_MSG_FAILURE([[[$0]] unknown microsoft compiler build version])) + ]) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_build" + ]) + +# for metrowerks +AC_DEFUN([_AX_COMPILER_VERSION_METROWERKS],[dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + __MWERKS__%0x100,, + AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler patch version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + (__MWERKS__/0x100)%0x10,, + AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (__MWERKS__/0x1000)%0x10,, + AC_MSG_FAILURE([[[$0]] unknown metrowerks compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# for watcom +AC_DEFUN([_AX_COMPILER_VERSION_WATCOM],[dnl + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + __WATCOMC__%100,, + AC_MSG_FAILURE([[[$0]] unknown watcom compiler minor version])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + (__WATCOMC__/100)%100,, + AC_MSG_FAILURE([[[$0]] unknown watcom compiler major version])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor" + ]) + +# for PGI +AC_DEFUN([_AX_COMPILER_VERSION_PORTLAND],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + __PGIC__,, + AC_MSG_FAILURE([[[$0]] unknown pgi major])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + __PGIC_MINOR__,, + AC_MSG_FAILURE([[[$0]] unknown pgi minor])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [__PGIC_PATCHLEVEL__],, + AC_MSG_FAILURE([[[$0]] unknown pgi patch level])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# tcc +AC_DEFUN([_AX_COMPILER_VERSION_TCC],[ + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version=[`tcc -v | $SED 's/^[ ]*tcc[ ]\+version[ ]\+\([0-9.]\+\).*/\1/g'`] + ]) + +# for GNU +AC_DEFUN([_AX_COMPILER_VERSION_SDCC],[ + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_major, + /* avoid parse error with comments */ + #if(defined(__SDCC_VERSION_MAJOR)) + __SDCC_VERSION_MAJOR + #else + SDCC/100 + #endif + ,, + AC_MSG_FAILURE([[[$0]] unknown sdcc major])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor, + /* avoid parse error with comments */ + #if(defined(__SDCC_VERSION_MINOR)) + __SDCC_VERSION_MINOR + #else + (SDCC%100)/10 + #endif + ,, + AC_MSG_FAILURE([[[$0]] unknown sdcc minor])) + AC_COMPUTE_INT(_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch, + [ + /* avoid parse error with comments */ + #if(defined(__SDCC_VERSION_PATCH)) + __SDCC_VERSION_PATCH + #elsif(defined(_SDCC_VERSION_PATCHLEVEL)) + __SDCC_VERSION_PATCHLEVEL + #else + SDCC%10 + #endif + ],, + AC_MSG_FAILURE([[[$0]] unknown sdcc patch level])) + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version="$_ax_[]_AC_LANG_ABBREV[]_compiler_version_major.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_minor.$_ax_[]_AC_LANG_ABBREV[]_compiler_version_patch" + ]) + +# main entry point +AC_DEFUN([AX_COMPILER_VERSION],[dnl + AC_REQUIRE([AX_COMPILER_VENDOR]) + AC_REQUIRE([AC_PROG_SED]) + AC_CACHE_CHECK([for _AC_LANG compiler version], + ax_cv_[]_AC_LANG_ABBREV[]_compiler_version, + [ dnl + AS_CASE([$ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor], + [intel],[_AX_COMPILER_VERSION_INTEL], + [ibm],[_AX_COMPILER_VERSION_IBM], + [pathscale],[_AX_COMPILER_VERSION_PATHSCALE], + [clang],[_AX_COMPILER_VERSION_CLANG], + [cray],[_AX_COMPILER_VERSION_CRAY], + [fujitsu],[_AX_COMPILER_VERSION_FUJITSU], + [gnu],[_AX_COMPILER_VERSION_GNU], + [sun],[_AX_COMPILER_VERSION_SUN], + [hp],[_AX_COMPILER_VERSION_HP], + [dec],[_AX_COMPILER_VERSION_DEC], + [borland],[_AX_COMPILER_VERSION_BORLAND], + [comeau],[_AX_COMPILER_VERSION_COMEAU], + [kai],[_AX_COMPILER_VERSION_KAI], + [sgi],[_AX_COMPILER_VERSION_SGI], + [microsoft],[_AX_COMPILER_VERSION_MICROSOFT], + [metrowerks],[_AX_COMPILER_VERSION_METROWERKS], + [watcom],[_AX_COMPILER_VERSION_WATCOM], + [portland],[_AX_COMPILER_VERSION_PORTLAND], + [tcc],[_AX_COMPILER_VERSION_TCC], + [sdcc],[_AX_COMPILER_VERSION_SDCC], + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_version=""]) + ]) +]) + diff --git a/m4/ax_prog_doxygen.m4 b/m4/ax_prog_doxygen.m4 new file mode 100644 index 0000000..b0238c1 --- /dev/null +++ b/m4/ax_prog_doxygen.m4 @@ -0,0 +1,587 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html +# =========================================================================== +# +# SYNOPSIS +# +# DX_INIT_DOXYGEN(PROJECT-NAME, [DOXYFILE-PATH], [OUTPUT-DIR], ...) +# DX_DOXYGEN_FEATURE(ON|OFF) +# DX_DOT_FEATURE(ON|OFF) +# DX_HTML_FEATURE(ON|OFF) +# DX_CHM_FEATURE(ON|OFF) +# DX_CHI_FEATURE(ON|OFF) +# DX_MAN_FEATURE(ON|OFF) +# DX_RTF_FEATURE(ON|OFF) +# DX_XML_FEATURE(ON|OFF) +# DX_PDF_FEATURE(ON|OFF) +# DX_PS_FEATURE(ON|OFF) +# +# DESCRIPTION +# +# The DX_*_FEATURE macros control the default setting for the given +# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for +# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML +# help (for MS users), 'CHI' for generating a separate .chi file by the +# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate +# output formats. The environment variable DOXYGEN_PAPER_SIZE may be +# specified to override the default 'a4wide' paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to +# be the most popular and portable combination. MAN pages created by +# Doxygen are usually problematic, though by picking an appropriate subset +# and doing some massaging they might be better than nothing. CHM and RTF +# are specific for MS (note that you can't generate both HTML and CHM at +# the same time). The XML is rather useless unless you apply specialized +# post-processing to it. +# +# The macros mainly control the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros +# ensure that contradictory flags are not given (e.g., +# --enable-doxygen-html and --enable-doxygen-chm, +# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each +# feature will be automatically disabled (with a warning) if the required +# programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN +# with the following parameters: a one-word name for the project for use +# as a filename base etc., an optional configuration file name (the +# default is '$(srcdir)/Doxyfile', the same as Doxygen's default), and an +# optional output directory name (the default is 'doxygen-doc'). To run +# doxygen multiple times for different configuration files and output +# directories provide more parameters: the second, forth, sixth, etc +# parameter are configuration file names and the third, fifth, seventh, +# etc parameter are output directories. No checking is done to catch +# duplicates. +# +# Automake Support +# +# The DX_RULES substitution can be used to add all needed rules to the +# Makefile. Note that this is a substitution without being a variable: +# only the @DX_RULES@ syntax will work. +# +# The provided targets are: +# +# doxygen-doc: Generate all doxygen documentation. +# +# doxygen-run: Run doxygen, which will generate some of the +# documentation (HTML, CHM, CHI, MAN, RTF, XML) +# but will not do the post processing required +# for the rest of it (PS, PDF). +# +# doxygen-ps: Generate doxygen PostScript documentation. +# +# doxygen-pdf: Generate doxygen PDF documentation. +# +# Note that by default these are not integrated into the automake targets. +# If doxygen is used to generate man pages, you can achieve this +# integration by setting man3_MANS to the list of man pages generated and +# then adding the dependency: +# +# $(man3_MANS): doxygen-doc +# +# This will cause make to run doxygen and generate all the documentation. +# +# The following variable is intended for use in Makefile.am: +# +# DX_CLEANFILES = everything to clean. +# +# Then add this variable to MOSTLYCLEANFILES. +# +# LICENSE +# +# Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org> +# Copyright (c) 2015 Olaf Mandel <olaf@mandel.name> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 24 + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], ON) +AC_DEFUN([DX_FEATURE_dot], OFF) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen and add it +# as a substitution (but not a Makefile variable). The substitution +# is skipped if the variable name is VERSION. +AC_DEFUN([DX_ENV_APPEND], +[AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])dnl +m4_if([$1], [VERSION], [], [AC_SUBST([$1], [$2])dnl +AM_SUBST_NOTMAKE([$1])])dnl +]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-$1]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +]) + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +if DX_TEST_FEATURE([$1]); then + $6 + : +else + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_DOT_FEATURE], [AC_DEFUN([DX_FEATURE_dot], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR], ...) +# -------------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "$(srcdir)/Doxyfile" and OUTPUT-DOC-DIR is +# "doxygen-doc". +# More arguments are interpreted as interleaved CONFIG-FILE and +# OUTPUT-DOC-DIR values. +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], ['ifelse([$2], [], [$(srcdir)/Doxyfile], [$2])']) +AC_SUBST([DX_DOCDIR], ['ifelse([$3], [], [doxygen-doc], [$3])']) +m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2, + [AC_SUBST([DX_CONFIG]m4_eval(DX_i[/2]), + 'm4_default_nblank_quoted(m4_argn(DX_i, $@), + [$(srcdir)/Doxyfile])')])])dnl +m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 5, m4_count($@,), 2, + [AC_SUBST([DX_DOCDIR]m4_eval([(]DX_i[-1)/2]), + 'm4_default_nblank_quoted(m4_argn(DX_i, $@), + [doxygen-doc])')])])dnl +m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1, + [m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])], + [])))dnl + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Separate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +# Rules: +AS_IF([[test $DX_FLAG_html -eq 1]], +[[DX_SNIPPET_html="## ------------------------------- ## +## Rules specific for HTML output. ## +## ------------------------------- ## + +DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/html]])[ + +"]], +[[DX_SNIPPET_html=""]]) +AS_IF([[test $DX_FLAG_chi -eq 1]], +[[DX_SNIPPET_chi=" +DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]], +[[DX_SNIPPET_chi=""]]) +AS_IF([[test $DX_FLAG_chm -eq 1]], +[[DX_SNIPPET_chm="## ------------------------------ ## +## Rules specific for CHM output. ## +## ------------------------------ ## + +DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/chm]])[\ +${DX_SNIPPET_chi} + +"]], +[[DX_SNIPPET_chm=""]]) +AS_IF([[test $DX_FLAG_man -eq 1]], +[[DX_SNIPPET_man="## ------------------------------ ## +## Rules specific for MAN output. ## +## ------------------------------ ## + +DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/man]])[ + +"]], +[[DX_SNIPPET_man=""]]) +AS_IF([[test $DX_FLAG_rtf -eq 1]], +[[DX_SNIPPET_rtf="## ------------------------------ ## +## Rules specific for RTF output. ## +## ------------------------------ ## + +DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/rtf]])[ + +"]], +[[DX_SNIPPET_rtf=""]]) +AS_IF([[test $DX_FLAG_xml -eq 1]], +[[DX_SNIPPET_xml="## ------------------------------ ## +## Rules specific for XML output. ## +## ------------------------------ ## + +DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/xml]])[ + +"]], +[[DX_SNIPPET_xml=""]]) +AS_IF([[test $DX_FLAG_ps -eq 1]], +[[DX_SNIPPET_ps="## ----------------------------- ## +## Rules specific for PS output. ## +## ----------------------------- ## + +DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[ + +DX_PS_GOAL = doxygen-ps + +doxygen-ps: \$(DX_CLEAN_PS) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag + \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ + \$(DX_LATEX) refman.tex; \\ + \$(DX_MAKEINDEX) refman.idx; \\ + \$(DX_LATEX) refman.tex; \\ + countdown=5; \\ + while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ + refman.log > /dev/null 2>&1 \\ + && test \$\$countdown -gt 0; do \\ + \$(DX_LATEX) refman.tex; \\ + countdown=\`expr \$\$countdown - 1\`; \\ + done; \\ + \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi + +]])["]], +[[DX_SNIPPET_ps=""]]) +AS_IF([[test $DX_FLAG_pdf -eq 1]], +[[DX_SNIPPET_pdf="## ------------------------------ ## +## Rules specific for PDF output. ## +## ------------------------------ ## + +DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[ + +DX_PDF_GOAL = doxygen-pdf + +doxygen-pdf: \$(DX_CLEAN_PDF) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag + \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\ + \$(DX_PDFLATEX) refman.tex; \\ + \$(DX_MAKEINDEX) refman.idx; \\ + \$(DX_PDFLATEX) refman.tex; \\ + countdown=5; \\ + while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\ + refman.log > /dev/null 2>&1 \\ + && test \$\$countdown -gt 0; do \\ + \$(DX_PDFLATEX) refman.tex; \\ + countdown=\`expr \$\$countdown - 1\`; \\ + done; \\ + mv refman.pdf ../\$(PACKAGE).pdf + +]])["]], +[[DX_SNIPPET_pdf=""]]) +AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]], +[[DX_SNIPPET_latex="## ------------------------------------------------- ## +## Rules specific for LaTeX (shared for PS and PDF). ## +## ------------------------------------------------- ## + +DX_V_LATEX = \$(_DX_v_LATEX_\$(V)) +_DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY)) +_DX_v_LATEX_0 = @echo \" LATEX \" \$][@; + +DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl +m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\ + \$(DX_DOCDIR]DX_i[)/latex]])[ + +"]], +[[DX_SNIPPET_latex=""]]) + +AS_IF([[test $DX_FLAG_doc -eq 1]], +[[DX_SNIPPET_doc="## --------------------------------- ## +## Format-independent Doxygen rules. ## +## --------------------------------- ## + +${DX_SNIPPET_html}\ +${DX_SNIPPET_chm}\ +${DX_SNIPPET_man}\ +${DX_SNIPPET_rtf}\ +${DX_SNIPPET_xml}\ +${DX_SNIPPET_ps}\ +${DX_SNIPPET_pdf}\ +${DX_SNIPPET_latex}\ +DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V)) +_DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY)) +_DX_v_DXGEN_0 = @echo \" DXGEN \" \$<; + +.PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +.INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +doxygen-run:]m4_foreach([DX_i], [DX_loop], + [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[ + +doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL) + +]m4_foreach([DX_i], [DX_loop], +[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS) + \$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[) + \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[) + \$(A""M_V_at)echo Timestamp >\$][@ + +]])dnl +[DX_CLEANFILES = \\] +m4_foreach([DX_i], [DX_loop], +[[ \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\ + \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\ +]])dnl +[ -r \\ + \$(DX_CLEAN_HTML) \\ + \$(DX_CLEAN_CHM) \\ + \$(DX_CLEAN_CHI) \\ + \$(DX_CLEAN_MAN) \\ + \$(DX_CLEAN_RTF) \\ + \$(DX_CLEAN_XML) \\ + \$(DX_CLEAN_PS) \\ + \$(DX_CLEAN_PDF) \\ + \$(DX_CLEAN_LATEX)"]], +[[DX_SNIPPET_doc=""]]) +AC_SUBST([DX_RULES], +["${DX_SNIPPET_doc}"])dnl +AM_SUBST_NOTMAKE([DX_RULES]) + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) + diff --git a/src/Makefile.am b/src/Makefile.am index e534d2f..3be935c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,5 @@ -# 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 file is part of CSDS. +# Copyright (c) 2021 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 @@ -17,48 +15,53 @@ # 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 +AM_CFLAGS = $(OPENMP_CFLAGS) $(PYTHON_INCS) +AM_LDFLAGS = # Build the libcsds library lib_LTLIBRARIES = libcsds.la -GRAVITY_SRC = gravity/MultiSoftening/csds_gravity.c +lib_LTLIBRARIES += libcsds_writer.la # List required headers include_HEADERS = csds_header.h csds_loader_io.h csds_particle.h csds_time.h csds_tools.h include_HEADERS += csds_reader.h csds_logfile.h csds_index.h quick_sort.h csds_python_tools.h include_HEADERS += csds_interpolation.h csds_parameters.h csds_cosmology.h csds_fields.h -include_HEADERS += csds_hashmap.h +include_HEADERS += csds_hashmap.h csds_error.h csds_clocks.h csds_cycle.h csds_definitions.h +include_HEADERS += csds_parser.h csds_inline.h csds_part_type.h csds_openmp.h +include_HEADERS += csds_logfile_writer.h csds_version.h -# Common source files +# Source files for the reader AM_SOURCES = csds_header.c csds_loader_io.c csds_time.c csds_tools.c csds_reader.c AM_SOURCES += csds_logfile.c csds_index.c quick_sort.c csds_parameters.c csds_reader_generate_index.c -AM_SOURCES += csds_cosmology.c csds_fields.c csds_hashmap.c +AM_SOURCES += csds_cosmology.c csds_fields.c csds_hashmap.c csds_clocks.c +AM_SOURCES += csds_parser.c csds_part_type.c if HAVEPYTHON AM_SOURCES += csds_python_wrapper.c endif +# Source files for writer +AM_SOURCES_WRITER = csds_logfile_writer.c if HAVEPYTHON # The main python structure is dependent on the version, thus we need to # remove this one in order to be safe. -PYTHON_EXTRA_COMPILER_FLAG = -Wno-missing-field-initializers -Wno-cast-function-type -Wno-unused-function -Wno-unused-function +PYTHON_EXTRA_COMPILER_FLAG = -Wno-missing-field-initializers -Wno-cast-function-type -Wno-unused-function endif +AM_CFLAGS += $(PYTHON_EXTRA_COMPILER_FLAG) + # Sources and flags for regular library libcsds_la_SOURCES = $(AM_SOURCES) -libcsds_la_CFLAGS = $(AM_CFLAGS) $(PYTHON_EXTRA_COMPILER_FLAG) -libcsds_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS) $(BIN_LDFLAGS) +libcsds_la_CFLAGS = $(AM_CFLAGS) +libcsds_la_LDFLAGS = $(AM_LDFLAGS) + +# Now for the writer +libcsds_writer_la_SOURCES = $(AM_SOURCES_WRITER) +libcsds_writer_la_CFLAGS = $(AM_CFLAGS) +libcsds_writer_la_LDFLAGS = $(AM_LDFLAGS) + +# Generate the version file +csds_version.h: csds_version.h.in Makefile $(AM_SOURCES_WRITER) $(AM_SOURCES) $(include_HEADERS) + sed -e "s,@major_version\@,$(MAJOR_VERSION)," \ + -e "s,@minor_version\@,$(MINOR_VERSION)," $< > csds_version.h diff --git a/src/csds_clocks.c b/src/csds_clocks.c new file mode 100644 index 0000000..e026bf7 --- /dev/null +++ b/src/csds_clocks.c @@ -0,0 +1,318 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * SWIFT + * + * 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 clocks.c + * @brief support for measuring intervals in milli seconds, when that + * is possible, otherwise ticks. + * + * Use cycle.h or timers.h for relative times. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Standard headers. */ +#include <limits.h> +#include <stdio.h> +#include <unistd.h> + +/* Local headers. */ +#include "csds_clocks.h" + +/* 0.25 of a second in nanoseconds. */ +#define SLEEPTIME 250000000 + +/* The CPU frequency used to convert ticks to seconds. */ +static unsigned long long clocks_cpufreq = 0; + +/* Ticks when the CPU frequency was initialised, this marks the start of + * time. */ +ticks clocks_start_ticks = 0; + +/* The units of any returned times. */ +static const char *clocks_units[] = {"ms", "~ms"}; +static int clocks_units_index = 0; +static double clocks_units_scale = 1000.0; + +/* Local prototypes. */ +static void clocks_estimate_cpufreq(void); + +/** + * @brief Get the current time. + * + * @param time the current time. + */ +void clocks_gettime(struct clocks_time *time) { + +#ifdef HAVE_CLOCK_GETTIME + clock_gettime(CLOCK_REALTIME, &time->time); +#else + time->time = getticks(); +#endif +} + +/** + * @brief Get difference in between two times. + * + * @param start the start time. + * @param end the end time. + * + * @return the difference. + */ +double clocks_diff(struct clocks_time *start, struct clocks_time *end) { +#ifdef HAVE_CLOCK_GETTIME + struct timespec temp; + if ((end->time.tv_nsec - start->time.tv_nsec) < 0) { + temp.tv_sec = end->time.tv_sec - start->time.tv_sec - 1; + temp.tv_nsec = 1000000000 + end->time.tv_nsec - start->time.tv_nsec; + } else { + temp.tv_sec = end->time.tv_sec - start->time.tv_sec; + temp.tv_nsec = end->time.tv_nsec - start->time.tv_nsec; + } + return (double)temp.tv_sec * 1000.0 + (double)temp.tv_nsec * 1.0E-6; +#else + return elapsed(end->time, start->time) / clocks_get_cpufreq() * + clocks_units_scale; +#endif +} + +/** + * @brief Set the CPU frequency. + * + * This function should be called at least once to set the CPU frequency. + * To use the builtin estimation techniques give a value of 0. + * + * @param freq the CPU frequency in Hz or 0 to estimate one. + */ +void clocks_set_cpufreq(unsigned long long freq) { + if (freq > 0) { + clocks_cpufreq = freq; + } else { + clocks_estimate_cpufreq(); + } + clocks_start_ticks = getticks(); +} + +/** + * @brief Get the CPU frequency in Hz. + * + * @result the CPU frequency. + */ +unsigned long long clocks_get_cpufreq(void) { + + if (clocks_cpufreq > 0) return clocks_cpufreq; + + /* It not already set estimate it. */ + clocks_estimate_cpufreq(); + return clocks_cpufreq; +} + +/** + * @brief Estimate the CPU frequency in Hz. + * + * If already set return the CPU frequency, then estimate the CPU frequency. + * + * The technique is either use a clock timed nanosleep (this was the best + * method on i7), to read the value from the cpuinfo_max_freq + * file (probably a overestimate) or finally just use a value of 1 with + * time units of ticks. + */ +static void clocks_estimate_cpufreq(void) { + +#ifdef HAVE_CLOCK_GETTIME + /* Try to time a nanosleep() in ticks. */ + struct clocks_time time1; + struct clocks_time time2; + + struct timespec sleep; + sleep.tv_sec = 0; + sleep.tv_nsec = SLEEPTIME; + + clocks_gettime(&time1); + ticks tic = getticks(); + + /* Could do some calculation, but constant_tsc should protect us. */ + nanosleep(&sleep, NULL); + + clocks_gettime(&time2); + ticks toc = getticks(); + double realsleep = clocks_diff(&time1, &time2); + + clocks_cpufreq = + (signed long long)(double)(toc - tic) * 1.0 / realsleep * 1000.0; + clocks_units_index = 0; + clocks_units_scale = 1000.0; +#endif + +/* Look for the system value, if available. Tends to be too large. */ +#ifdef __linux__ + if (clocks_cpufreq == 0) { + FILE *file = + fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r"); + if (file != NULL) { + unsigned long long maxfreq; + if (fscanf(file, "%llu", &maxfreq) == 1) { + clocks_cpufreq = maxfreq * 1000; + clocks_units_index = 0; + clocks_units_scale = 1000.0; + } + fclose(file); + } + } +#endif + + /* If all fails just report ticks for a notional 2.6GHz machine. */ + if (clocks_cpufreq == 0) { + unsigned long long maxfreq = 2600000; + clocks_cpufreq = maxfreq * 1000; + clocks_units_index = 1; + clocks_units_scale = 1000.0; + } +} + +/** + * @brief Return the difference between two ticks. + * + * Only an approximation as based on how well we have estimated the + * rtc frequency. Should be good for machines that support constant_rtc + * and clock_gettime(). + * + * @param tic a number of ticks returned by the cycle.h getticks() function. + * @param toc a number of ticks returned by the cycle.h getticks() function. + * + * @result the difference. + */ +double clocks_diff_ticks(ticks tic, ticks toc) { + return clocks_from_ticks(tic - toc); +} + +/** + * @brief Convert a number of ticks into milli seconds, if possible. + * + * Only an approximation as based on how well we have estimated the + * rtc frequency. Should be good for machines that support constant_rtc + * and clock_gettime(), and reasonable for most Linux machines, otherwise + * ticks will just be returned. See clocks_getunit() for the actual units. + * + * @param tics a number of ticks returned by the cycle.h getticks() function. + * + * @result the milli seconds, if possible. + */ +double clocks_from_ticks(ticks tics) { + return ((double)tics / (double)clocks_get_cpufreq() * clocks_units_scale); +} + +/** + * @brief Convert a number of milli seconds into ticks, if possible. + * + * Only an approximation as based on how well we have estimated the + * rtc frequency. Should be good for machines that support constant_rtc + * and clock_gettime(), and reasonable for most Linux machines, otherwise + * a guess will just be returned. See clocks_getunit() for the actual units. + * + * @param ms a number of "milliseconds" to convert to ticks + * + * @result the number of ticks, if possible. + */ +ticks clocks_to_ticks(double ms) { + return (ticks)(ms * (double)clocks_get_cpufreq() / clocks_units_scale); +} + +/** + * @brief return the time units. + * + * Normally "ms" for milliseconds, but can be "ticks" when no conversion + * factor is available. + * + * @result the current time units. + */ +const char *clocks_getunit(void) { return clocks_units[clocks_units_index]; } + +/** + * @brief returns the time since the start of the execution in seconds + * + * Need to call clocks_set_cpufreq() to mark the start of execution. + * + * The time is return in the format [sssss.s]. + * + * @result the time since the start of the execution + */ +const char *clocks_get_timesincestart(void) { + + static char buffer[40]; + + sprintf(buffer, "[%07.1f]", + clocks_diff_ticks(getticks(), clocks_start_ticks) / 1000.0); + + return buffer; +} + +/** + * Returns the wall-clock time since the start of execution in hours. + * + * Need to call clocks_set_cpufreq() to mark the start of execution. + * + * @result the time since the start of the execution + */ +double clocks_get_hours_since_start(void) { + return clocks_diff_ticks(getticks(), clocks_start_ticks) / (3600. * 1000.0); +} + +/** + * @brief return the cpu times used. + * + * Uses the times(2) function to access the user and system cpu times and + * returns the sum of these for the process tree, i.e. current process plus + * "waited-for" children. This may be pthread implementation specific as to + * what that exactly means. + * + * Note cpu times are reported in sysconf(_SC_CLK_TCK) ticks, usually 100/s + * not our usual ticks. + * + * @param usertime the user time. + * @param systime the system time. + */ +void clocks_get_cputimes_used(double *usertime, double *systime) { + struct tms tmstic; + times(&tmstic); + *usertime = (tmstic.tms_utime + tmstic.tms_cutime); + *systime = (tmstic.tms_stime + tmstic.tms_cstime); +} + +/** + * @brief Return an integer based on the current time. + * + * Normally this will be the remainder of the current number of nanoseconds + * so not very dissimilar in the most significant figures unless the time + * between calls is greater than INT_MAX nanoseconds. For faster calls use + * fewer figures, if that matters. + * + * @result an integer. + */ +int clocks_random_seed(void) { +#ifdef HAVE_CLOCK_GETTIME + struct timespec timespec; + clock_gettime(CLOCK_REALTIME, ×pec); + return (timespec.tv_nsec % INT_MAX); +#else + return (getticks() % INT_MAX); +#endif +} diff --git a/src/csds_clocks.h b/src/csds_clocks.h new file mode 100644 index 0000000..b9c50e4 --- /dev/null +++ b/src/csds_clocks.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * SWIFT + * + * 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 CSDS_CLOCKS_H +#define CSDS_CLOCKS_H + +/* Config parameters. */ +#include "../config.h" + +/* System includes. */ +#include <sys/times.h> + +/* Local includes */ +#include "csds_cycle.h" + +/* Struct to record a time for the clocks functions. */ +struct clocks_time { +#ifdef HAVE_CLOCK_GETTIME + struct timespec time; +#else + ticks time; +#endif +}; + +/* Ticks used as the start of time. */ +extern ticks clocks_start_ticks; + +void clocks_gettime(struct clocks_time *time); +double clocks_diff(struct clocks_time *start, struct clocks_time *end); +const char *clocks_getunit(void); + +void clocks_set_cpufreq(unsigned long long freq); +unsigned long long clocks_get_cpufreq(void); +double clocks_from_ticks(ticks tics); +ticks clocks_to_ticks(double interval); +double clocks_diff_ticks(ticks tic, ticks toc); +const char *clocks_get_timesincestart(void); +double clocks_get_hours_since_start(void); + +void clocks_get_cputimes_used(double *usertime, double *systime); +int clocks_random_seed(void); + +#endif /* CSDS_CLOCKS_H */ diff --git a/src/csds_cosmology.c b/src/csds_cosmology.c index 6f1a4e1..c0926ac 100644 --- a/src/csds_cosmology.c +++ b/src/csds_cosmology.c @@ -21,11 +21,9 @@ #include "csds_cosmology.h" /* Include CSDS */ +#include "csds_parser.h" #include "csds_tools.h" -/* Include SWIFT */ -#include "parser.h" - /** * @brief Compute the first time derivative of the scale factor * @@ -35,7 +33,7 @@ * @return The first derivative of the scale factor. */ void csds_cosmology_init(struct csds_cosmology *cosmo, - struct swift_params *params) { + struct csds_params *params) { /* Read Omega_cdm */ cosmo->Omega_cdm = parser_get_param_double(params, "Cosmology:Omega_cdm"); diff --git a/src/csds_cosmology.h b/src/csds_cosmology.h index 7fe7be4..62027d9 100644 --- a/src/csds_cosmology.h +++ b/src/csds_cosmology.h @@ -25,13 +25,13 @@ #ifndef CSDS_COSMOLOGY_H #define CSDS_COSMOLOGY_H -/* SWIFT includes */ -#include "inline.h" +/* local includes */ +#include "csds_inline.h" /* Some standard headers */ #include <math.h> -struct swift_params; +struct csds_params; /** * @brief Structure containing the cosmological parameters. @@ -192,5 +192,5 @@ csds_cosmology_add_factor_velocity(const struct csds_cosmology *cosmo, } void csds_cosmology_init(struct csds_cosmology *cosmo, - struct swift_params *params); + struct csds_params *params); #endif // CSDS_CSDS_PARAMETERS_H diff --git a/src/csds_cycle.h b/src/csds_cycle.h new file mode 100644 index 0000000..0ba1277 --- /dev/null +++ b/src/csds_cycle.h @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2003, 2007-14 Matteo Frigo + * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* machine-dependent cycle counters code. Needs to be inlined. */ + +/***************************************************************************/ +/* To use the cycle counters in your code, simply #include "cycle.h" (this + file), and then use the functions/macros: + + ticks getticks(void); + + ticks is an opaque typedef defined below, representing the current time. + You extract the elapsed time between two calls to gettick() via: + + double elapsed(ticks t1, ticks t0); + + which returns a double-precision variable in arbitrary units. You + are not expected to convert this into human units like seconds; it + is intended only for *comparisons* of time intervals. + + (In order to use some of the OS-dependent timer routines like + Solaris' gethrtime, you need to paste the autoconf snippet below + into your configure.ac file and #include "config.h" before cycle.h, + or define the relevant macros manually if you are not using autoconf.) +*/ + +/***************************************************************************/ +/* This file uses macros like HAVE_GETHRTIME that are assumed to be + defined according to whether the corresponding function/type/header + is available on your system. The necessary macros are most + conveniently defined if you are using GNU autoconf, via the tests: + + dnl --------------------------------------------------------------------- + + AC_C_INLINE + AC_HEADER_TIME + AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) + + AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if +hrtime_t is defined in <sys/time.h>])],,[#if HAVE_SYS_TIME_H +#include <sys/time.h> +#endif]) + + AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime +mach_absolute_time]) + + dnl Cray UNICOS _rtc() (real-time clock) intrinsic + AC_MSG_CHECKING([for _rtc intrinsic]) + rtc_ok=yes + AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H +#include <intrinsics.h> +#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() +intrinsic.])], [rtc_ok=no]) + AC_MSG_RESULT($rtc_ok) + + dnl --------------------------------------------------------------------- +*/ + +/***************************************************************************/ + +#include <stdint.h> +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#define INLINE_ELAPSED(INL) \ + static INL double elapsed(ticks t1, ticks t0) { \ + return (double)t1 - (double)t0; \ + } + +/*----------------------------------------------------------------*/ +/* Solaris */ +#if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && \ + !defined(HAVE_TICK_COUNTER) +typedef hrtime_t ticks; + +#define getticks gethrtime + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* AIX v. 4+ routines to read the real-time clock or time-base register */ +#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && \ + !defined(HAVE_TICK_COUNTER) +typedef timebasestruct_t ticks; + +static __inline ticks getticks(void) { + ticks t; + read_real_time(&t, TIMEBASE_SZ); + return t; +} + +static __inline double elapsed(ticks t1, ticks t0) /* time in nanoseconds */ +{ + time_base_to_time(&t1, TIMEBASE_SZ); + time_base_to_time(&t0, TIMEBASE_SZ); + return (((double)t1.tb_high - (double)t0.tb_high) * 1.0e9 + + ((double)t1.tb_low - (double)t0.tb_low)); +} + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PowerPC ``cycle'' counter using the time base register. + */ +#if ((((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || \ + (defined(__MWERKS__) && defined(macintosh)))) || \ + (defined(__IBM_GCC_ASM) && \ + (defined(__powerpc__) || defined(__ppc__)))) && \ + !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) { + unsigned int tbl, tbu0, tbu1; + + do { + __asm__ __volatile__("mftbu %0" : "=r"(tbu0)); + __asm__ __volatile__("mftb %0" : "=r"(tbl)); + __asm__ __volatile__("mftbu %0" : "=r"(tbu1)); + } while (tbu0 != tbu1); + + return (((unsigned long long)tbu0) << 32) | tbl; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, + from Carbon, requires no additional libraries to be linked). */ +#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && \ + !defined(HAVE_TICK_COUNTER) +#include <mach/mach_time.h> +typedef uint64_t ticks; +#define getticks mach_absolute_time +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * Pentium cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) && \ + !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) { + ticks ret; + + __asm__ __volatile__("rdtsc" : "=A"(ret)); + /* no input, nothing else clobbered */ + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/* Visual C++ -- thanks to Morten Nissov for his help with this */ +#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER) +#include <windows.h> +typedef LARGE_INTEGER ticks; +#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */ + +static __inline ticks getticks(void) { + ticks retval; + + __asm { + RDTSC + mov retval.HighPart, edx + mov retval.LowPart, eax + } + return retval; +} + +static __inline double elapsed(ticks t1, ticks t0) { + return (double)t1.QuadPart - (double)t0.QuadPart; +} + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/*----------------------------------------------------------------*/ +/* + * X86-64 cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC) || defined(__SUNPRO_C)) && \ + defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) { + unsigned a, d; + __asm__ __volatile__("rdtsc" : "=a"(a), "=d"(d)); + return ((ticks)a) | (((ticks)d) << 32); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 +#endif + +/* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori. + NOTE: this code will fail to link unless you use the -Masmkeyword compiler + option (grrr). */ +#if defined(__PGI) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; +static ticks getticks(void) { + asm(" rdtsc; shl $0x20,%rdx; mov %eax,%eax; or %rdx,%rax; "); +} +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 +#endif + +/* Visual C++, courtesy of Dirk Michaelis */ +#if _MSC_VER >= 1400 && (defined(_M_AMD64) || defined(_M_X64)) && \ + !defined(HAVE_TICK_COUNTER) + +#include <intrin.h> +#pragma intrinsic(__rdtsc) +typedef unsigned __int64 ticks; +#define getticks __rdtsc +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 +#endif + +/*----------------------------------------------------------------*/ +/* + * IA64 cycle counter + */ + +/* intel's icc/ecc compiler */ +#if (defined(__EDG_VERSION) || defined(__ECC)) && defined(__ia64__) && \ + !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; +#include <ia64intrin.h> + +static __inline__ ticks getticks(void) { return __getReg(_IA64_REG_AR_ITC); } + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* gcc */ +#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) { + ticks ret; + + __asm__ __volatile__("mov %0=ar.itc" : "=r"(ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */ +#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER) +#include <machine/sys/inline.h> +typedef unsigned long ticks; + +static inline ticks getticks(void) { + ticks ret; + + ret = _Asm_mov_from_ar(_AREG_ITC); + return ret; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/* Microsoft Visual C++ */ +#if defined(_MSC_VER) && defined(_M_IA64) && !defined(HAVE_TICK_COUNTER) +typedef unsigned __int64 ticks; + +#ifdef __cplusplus +extern "C" +#endif + ticks + __getReg(int whichReg); +#pragma intrinsic(__getReg) + +static __inline ticks getticks(void) { + volatile ticks temp; + temp = __getReg(3116); + return temp; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PA-RISC cycle counter + */ +#if (defined(__hppa__) || defined(__hppa)) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +#ifdef __GNUC__ +static __inline__ ticks getticks(void) { + ticks ret; + + __asm__ __volatile__("mfctl 16, %0" : "=r"(ret)); + /* no input, nothing else clobbered */ + return ret; +} +#else +#include <machine/inline.h> +static inline unsigned long getticks(void) { + register ticks ret; + _MFCTL(16, ret); + return ret; +} +#endif + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* S390, courtesy of James Treacy */ +#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) { + ticks cycles; + __asm__("stck 0(%0)" : : "a"(&(cycles)) : "memory", "cc"); + return cycles; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER) +/* + * The 32-bit cycle counter on alpha overflows pretty quickly, + * unfortunately. A 1GHz machine overflows in 4 seconds. + */ +typedef unsigned int ticks; + +static __inline__ ticks getticks(void) { + unsigned long cc; + __asm__ __volatile__("rpcc %0" : "=r"(cc)); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) { + ticks ret; + __asm__ __volatile__("rd %%tick, %0" : "=r"(ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if (defined(__DECC) || defined(__DECCXX)) && defined(__alpha) && \ + defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER) +#include <c_asm.h> +typedef unsigned int ticks; + +static __inline ticks getticks(void) { + unsigned long cc; + cc = asm("rpcc %v0"); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +/* SGI/Irix */ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && \ + !defined(HAVE_TICK_COUNTER) && !defined(__ANDROID__) +typedef struct timespec ticks; + +static inline ticks getticks(void) { + struct timespec t; + clock_gettime(CLOCK_SGI_CYCLE, &t); + return t; +} + +static inline double elapsed(ticks t1, ticks t0) { + return ((double)t1.tv_sec - (double)t0.tv_sec) * 1.0E9 + + ((double)t1.tv_nsec - (double)t0.tv_nsec); +} +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* Cray UNICOS _rtc() intrinsic function */ +#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER) +#ifdef HAVE_INTRINSICS_H +#include <intrinsics.h> +#endif + +typedef long long ticks; + +#define getticks _rtc + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* MIPS ZBus */ +#if HAVE_MIPS_ZBUS_TIMER +#if defined(__mips__) && !defined(HAVE_TICK_COUNTER) +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +typedef uint64_t ticks; + +static inline ticks getticks(void) { + static uint64_t* addr = 0; + + if (addr == 0) { + uint32_t rq_addr = 0x10030000; + int fd; + int pgsize; + + pgsize = getpagesize(); + fd = open("/dev/mem", O_RDONLY | O_SYNC, 0); + if (fd < 0) { + perror("open"); + return NULL; + } + addr = mmap(0, pgsize, PROT_READ, MAP_SHARED, fd, rq_addr); + close(fd); + if (addr == (uint64_t*)-1) { + perror("mmap"); + return NULL; + } + } + + return *addr; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif +#endif /* HAVE_MIPS_ZBUS_TIMER */ + +#if defined(HAVE_ARMV7A_CNTVCT) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint32_t Rt, Rt2 = 0; + asm volatile("mrrc p15, 1, %0, %1, c14" : "=r"(Rt), "=r"(Rt2)); + return ((uint64_t)Rt) | (((uint64_t)Rt2) << 32); +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif + +#if defined(HAVE_ARMV7A_PMCCNTR) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint32_t r; + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(r)); + return r; +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif + +#if defined(__aarch64__) && defined(HAVE_ARMV8_CNTVCT_EL0) && \ + !defined(HAVE_TICK_COUNTER) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint64_t Rt; + asm volatile("mrs %0, CNTVCT_EL0" : "=r"(Rt)); + return Rt; +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif + +#if defined(__aarch64__) && defined(HAVE_ARMV8_PMCCNTR_EL0) && \ + !defined(HAVE_TICK_COUNTER) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint64_t cc = 0; + asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(cc)); + return cc; +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif diff --git a/src/csds_definitions.h b/src/csds_definitions.h new file mode 100644 index 0000000..aaebf95 --- /dev/null +++ b/src/csds_definitions.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * This file is part of CSDS. + * Copyright (c) 2021 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 a few definitions useful for + * both the reader and writer. + */ +#ifndef CSDS_SPECIAL_FLAGS_H +#define CSDS_SPECIAL_FLAGS_H + +/* WARNING THIS FILE SHOULD NOT INCLUDE CONFIG.H*/ +#include "csds_version.h" + +/* Size of the strings. */ +#define CSDS_STRING_SIZE 200 +#define CSDS_FORMAT_STRING "CSDS" + +/* Defines the special flags */ +#define SPECIAL_FLAGS_NAME "SpecialFlags" +// The fields arrays are constructed such as the special +// flag is always first (see csds_header.c) +#define SPECIAL_FLAGS_INDEX 0 +#define SPECIAL_FLAGS_SIZE sizeof(uint32_t) +#define SPECIAL_FLAGS_MASK (1 << SPECIAL_FLAGS_INDEX) + +/* Number of bytes for the mask information in the headers + (need to be large enough for the larges mask) */ +#define CSDS_HEADER_SIZE 8 +#define CSDS_MASK_SIZE 2 +/* Number of bytes for the offset size in the headers */ +#define CSDS_OFFSET_SIZE (CSDS_HEADER_SIZE - CSDS_MASK_SIZE) + +/** @brief type to use for the masks */ +typedef short int mask_type; + +/* Defines the timestamp */ +typedef long long integertime_t; +#define TIMESTAMP_INDEX 1 +#define TIMESTAMP_MASK (1 << TIMESTAMP_INDEX) +#define TIMESTAMP_NAME "Timestamp" +#define TIMESTAMP_SIZE sizeof(integertime_t) + sizeof(double) + +/** @brief Value of the special flag */ +enum csds_special_flags { + csds_flag_none = 0, /* No flag */ + csds_flag_change_type = 1, /* Flag for a change of particle type */ + csds_flag_mpi_enter, /* Flag for a particle received from another MPI rank + */ + csds_flag_mpi_exit, /* Flag for a particle sent to another MPI rank */ + csds_flag_delete, /* Flag for a deleted particle */ + csds_flag_create, /* Flag for a created particle */ +} __attribute__((packed)); + + +/* + * @brief Direction of the offsets. + */ +enum csds_offset_direction { + csds_offset_backward = 0, + csds_offset_forward, + csds_offset_corrupted, + /* Number of offset type. */ + csds_offset_count, +}; + +#endif // CSDS_SPECIAL_FLAGS_H diff --git a/src/csds_error.h b/src/csds_error.h new file mode 100644 index 0000000..6c85460 --- /dev/null +++ b/src/csds_error.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) + * SWIFT + * + * 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 deal with messages. + */ +#ifndef CSDS_ERROR_H +#define CSDS_ERROR_H + +#include "../config.h" +#include "csds_clocks.h" +#include "csds_inline.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> + +/* Use exit when not developing, avoids core dumps. */ +#ifdef CSDS_DEBUG_CHECKS +#define csds_abort(errcode) abort() +#else +#define csds_abort(errcode) exit(errcode) +#endif + +/** + * @brief Error macro. Prints the message given in argument and aborts. + * + */ +#define error(s, ...) \ + ({ \ + fflush(stdout); \ + fprintf(stderr, "%s %s:%s():%i: " s "\n", clocks_get_timesincestart(), \ + __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + csds_abort(1); \ + }) + +/** + * @brief Macro to print a localized message with variable arguments. + * + */ +#define message(s, ...) \ + ({ \ + printf("%s %s: " s "\n", clocks_get_timesincestart(), __FUNCTION__, \ + ##__VA_ARGS__); \ + }) + +#ifndef HAVE_PYTHON +#define error_python(s, ...) error(s, ##__VA_ARGS__); +#else +#include "frameobject.h" + +/** + * @brief Print the python trace back + */ +__attribute__((always_inline)) INLINE static void csds_loader_print_traceback( + void) { + + PyGILState_STATE gstate = PyGILState_Ensure(); + + PyFrameObject *frame = PyEval_GetFrame(); + int lineno = PyFrame_GetLineNumber(frame); + PyObject *py_filename = frame->f_code->co_filename; + const char *filename = PyUnicode_AsUTF8(py_filename); + message("File %s, line: %i", filename, lineno); + + Py_DECREF(py_filename); + Py_DECREF(frame); + PyGILState_Release(gstate); +} + +#define error_python(s, ...) \ + ({ \ + csds_loader_print_traceback(); \ + error(s, ##__VA_ARGS__); \ + }) +#endif + +#endif // CSDS_ERROR_H diff --git a/src/csds_fields.c b/src/csds_fields.c index 861b1a9..4813228 100644 --- a/src/csds_fields.c +++ b/src/csds_fields.c @@ -21,19 +21,19 @@ #include "csds_fields.h" /* Task type names. */ -const char *fields_names[task_type_count] = {SPECIAL_FLAGS_NAME, - "Coordinates", - "Velocities", - "Accelerations", - "Masses", - "SmoothingLengths", - "InternalEnergies", - "Entropies", - "ParticleIDs", - "Densities", - "SPHENIXSecondaryFields", - "BirthTimes", - "GEARStarFormation", - "GEARChemistryParts", - "GEARChemistrySparts", - "None"}; +const char *fields_names[field_enum_count] = {SPECIAL_FLAGS_NAME, + "Coordinates", + "Velocities", + "Accelerations", + "Masses", + "SmoothingLengths", + "InternalEnergies", + "Entropies", + "ParticleIDs", + "Densities", + "SPHENIXSecondaryFields", + "BirthTimes", + "GEARStarFormation", + "GEARChemistryParts", + "GEARChemistrySparts", + "None"}; diff --git a/src/csds_fields.h b/src/csds_fields.h index 4309447..ff62e24 100644 --- a/src/csds_fields.h +++ b/src/csds_fields.h @@ -23,27 +23,14 @@ #define CSDS_FIELDS_H /* Include CSDS headers */ +#include "csds_definitions.h" #include "csds_python_tools.h" #include "csds_tools.h" -#define SPECIAL_FLAGS_NAME "SpecialFlags" -// The fields arrays are constructed such as the special -// flag is always first (see csds_header.c) -#define SPECIAL_FLAGS_INDEX 0 -#define SPECIAL_FLAGS_SIZE sizeof(uint32_t) -#define SPECIAL_FLAGS_MASK (1 << 0) - -#define TIMESTAMP_MASK (1 << 1) -#define TIMESTAMP_NAME "Timestamp" -#define TIMESTAMP_SIZE sizeof(integertime_t) + sizeof(double) - #ifndef GEAR_CHEMISTRY_ELEMENT_COUNT #define GEAR_CHEMISTRY_ELEMENT_COUNT 10 #endif -/** @brief type to use for the masks */ -typedef short int mask_type; - /** * @brief All the fields that are implemented */ enum field_enum { @@ -310,7 +297,7 @@ struct mask_data { mask_type mask; /* Name of the mask. */ - char name[STRING_SIZE]; + char name[CSDS_STRING_SIZE]; }; /** diff --git a/src/csds_hashmap.c b/src/csds_hashmap.c index 66cc61d..163f88d 100644 --- a/src/csds_hashmap.c +++ b/src/csds_hashmap.c @@ -6,11 +6,9 @@ #include <string.h> /* CSDS headers */ +#include "csds_error.h" #include "csds_hashmap.h" -/* SWIFT headers */ -#include "error.h" - /** * @brief returns a new hash map. * @@ -166,7 +164,7 @@ void *csds_hashmap_set(struct csds_hashmap *map, struct index_data *item) { /* Create a bucket from the item */ struct bucket *entry = map->edata; - entry->hash = get_hash(map, item->id); + entry->hash = get_hash(item->id); entry->dib = 1; memcpy(bucket_item(entry), item, sizeof(struct index_data)); @@ -205,7 +203,7 @@ void *csds_hashmap_set(struct csds_hashmap *map, struct index_data *item) { * found then NULL is returned. */ struct index_data *csds_hashmap_get(struct csds_hashmap *map, id_type key) { - uint64_t hash = get_hash(map, key); + uint64_t hash = get_hash(key); size_t i = hash & map->mask; while (1) { struct bucket *bucket = bucket_at(map, i); @@ -228,7 +226,7 @@ struct index_data *csds_hashmap_get(struct csds_hashmap *map, id_type key) { * item is not found then NULL is returned. */ void *csds_hashmap_delete(struct csds_hashmap *map, id_type key) { - uint64_t hash = get_hash(map, key); + uint64_t hash = get_hash(key); size_t i = hash & map->mask; while (1) { struct bucket *bucket = bucket_at(map, i); diff --git a/src/csds_hashmap.h b/src/csds_hashmap.h index fb0dc27..b44be08 100644 --- a/src/csds_hashmap.h +++ b/src/csds_hashmap.h @@ -13,8 +13,8 @@ #include <stdint.h> #include <stdio.h> -/* SWIFT headers */ -#include "inline.h" +/* CSDS headers */ +#include "csds_inline.h" #define id_type int64_t #define hashmap_overallocation 4. / 3. @@ -214,8 +214,7 @@ static uint64_t SIP64(const id_type *key) { } // hashmap_sip returns a hash value for `data` using SipHash-2-4. -__attribute__((always_inline)) INLINE static uint64_t get_hash( - struct csds_hashmap *map, id_type x) { +__attribute__((always_inline)) INLINE static uint64_t get_hash(id_type x) { return SIP64(&x) & ((1LU << 48) - 1); } diff --git a/src/csds_header.c b/src/csds_header.c index 442c330..813196b 100644 --- a/src/csds_header.c +++ b/src/csds_header.c @@ -40,13 +40,13 @@ const char *csds_offset_name[csds_offset_count] = { * @param h The #header. */ void header_print(const struct header *h) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS message("Debug checks enabled."); #endif message("First Offset: %lu.", h->offset_first_record); message("Offset direction: %s.", csds_offset_name[h->offset_direction]); - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { message("Masks for particle type %i:", type); for (int k = 0; k < h->number_fields[type]; k++) { /* Do the field */ @@ -76,7 +76,7 @@ void header_print(const struct header *h) { * @param h The #header. */ void header_free(struct header *h) { - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { if (h->fields[i]) { free(h->fields[i]); h->fields[i] = NULL; @@ -96,7 +96,7 @@ void header_change_offset_direction(struct header *h, enum csds_offset_direction new_value) { h->offset_direction = new_value; /* Skip file format and version numbers. */ - size_t offset = CSDS_VERSION_SIZE + 2 * sizeof(int); + size_t offset = CSDS_STRING_SIZE + 2 * sizeof(int); csds_loader_io_write_data(h->log->log.map + offset, sizeof(unsigned int), &new_value); @@ -115,9 +115,9 @@ void header_read(struct header *h, struct csds_logfile *log) { h->log = log; /* read the file format. */ - char file_format[STRING_SIZE]; - map = csds_loader_io_read_data(map, CSDS_VERSION_SIZE, &file_format); - if (strcmp(file_format, "SWIFT_CSDS")) + char file_format[CSDS_STRING_SIZE]; + map = csds_loader_io_read_data(map, CSDS_STRING_SIZE, &file_format); + if (strcmp(file_format, CSDS_FORMAT_STRING)) error_python("Wrong file format (%s).", file_format); /* Read the major version number. */ @@ -154,7 +154,7 @@ void header_read(struct header *h, struct csds_logfile *log) { map = csds_loader_io_read_data(map, sizeof(unsigned int), &string_length); /* Check if value defined in this file is large enough. */ - if (STRING_SIZE < string_length) { + if (CSDS_STRING_SIZE < string_length) { error_python("Name too large in log file %i.", string_length); } @@ -226,7 +226,7 @@ void header_read(struct header *h, struct csds_logfile *log) { csds_loader_io_read_data(map, sizeof(h->number_fields), h->number_fields); /* Read the order of the fields */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { if (h->number_fields[type] == 0) { h->fields[type] = NULL; continue; @@ -292,7 +292,7 @@ size_t header_get_record_size_from_mask(const struct header *h, } /* Loop over each masks. */ - for (int part_type = 0; part_type < swift_type_count; part_type++) { + for (int part_type = 0; part_type < csds_type_count; part_type++) { for (int i = 0; i < h->number_fields[part_type]; i++) { if (mask & h->fields[part_type][i].field.mask) { count += h->fields[part_type][i].field.size; @@ -300,7 +300,7 @@ size_t header_get_record_size_from_mask(const struct header *h, /* Early exit */ if (mask == 0) { - part_type = swift_type_count; + part_type = csds_type_count; break; } } diff --git a/src/csds_header.h b/src/csds_header.h index 0231ce3..d301c43 100644 --- a/src/csds_header.h +++ b/src/csds_header.h @@ -20,27 +20,12 @@ #define CSDS_CSDS_HEADER_H #include "csds_fields.h" +#include "csds_part_type.h" #include "csds_tools.h" #include <stdio.h> #include <stdlib.h> -/* Number of character for the version information */ -#define CSDS_VERSION_SIZE 20 -/* Number of bytes for the mask information in the headers - (need to be large enough for the larges mask) */ -#define CSDS_MASK_SIZE 2 -/* Number of bytes for the offset size in the headers */ -#define CSDS_OFFSET_SIZE (8 - CSDS_MASK_SIZE) - -enum csds_offset_direction { - csds_offset_backward = 0, - csds_offset_forward, - csds_offset_corrupted, - /* Number of offset type. */ - csds_offset_count, -}; - /** * @brief Names of the offset directions. */ @@ -76,11 +61,11 @@ struct header { struct csds_logfile *log; /* Number of mask per particle type */ - int number_fields[swift_type_count]; + int number_fields[csds_type_count]; /* The fields for each particle type in the correct order. By construction, the special flag is the first element in each array. */ - struct field_information *fields[swift_type_count]; + struct field_information *fields[csds_type_count]; }; void header_print(const struct header *h); diff --git a/src/csds_index.c b/src/csds_index.c index dca33cc..07bcb40 100644 --- a/src/csds_index.c +++ b/src/csds_index.c @@ -40,7 +40,7 @@ /* The number of particle (for each type). */ #define csds_index_npart_offset \ csds_index_integer_time_offset + csds_index_integer_time_size -#define csds_index_npart_size sizeof(uint64_t) * swift_type_count +#define csds_index_npart_size sizeof(uint64_t) * csds_type_count /* The flag used for checking if the file is sorted or not. */ #define csds_index_is_sorted_offset \ csds_index_npart_offset + csds_index_npart_size @@ -81,7 +81,7 @@ void csds_index_read_header(struct csds_index *index, const char *filename) { /* Compute the position of the history of new particles. */ size_t count = 0; - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { count += index->nparts[i]; } count *= sizeof(struct index_data); @@ -93,7 +93,7 @@ void csds_index_read_header(struct csds_index *index, const char *filename) { /* Compute the position of the history of particles removed. */ size_t count_new = 0; - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { count_new += index->nparts_created[i]; } count_new *= sizeof(struct index_data); @@ -159,7 +159,7 @@ struct index_data *csds_index_get_created_history(struct csds_index *index, int type) { /* Get the position after the previous array. */ - char *ret = (char *)csds_index_get_data(index, swift_type_count); + char *ret = (char *)csds_index_get_data(index, csds_type_count); /* Get the offset of particles of this type by skipping entries of lower * types. */ @@ -186,7 +186,7 @@ struct index_data *csds_index_get_removed_history(struct csds_index *index, int type) { /* Get the position after the previous array. */ - char *ret = (char *)csds_index_get_created_history(index, swift_type_count); + char *ret = (char *)csds_index_get_created_history(index, csds_type_count); /* Get the offset of particles of this type by skipping entries of lower * types. */ @@ -215,7 +215,7 @@ int csds_index_contains_time_array(struct csds_index *index) { const size_t file_size = index->index.mmap_size; /* Get the position after the previous array. */ - char *ret = (char *)csds_index_get_removed_history(index, swift_type_count); + char *ret = (char *)csds_index_get_removed_history(index, csds_type_count); const size_t before_time_array = ret - index->index.map; return file_size != before_time_array; @@ -244,7 +244,7 @@ void csds_index_map_file(struct csds_index *index, const char *filename, csds_loader_io_mmap_file(&index->index, filename, /* read_only */ 0); /* Sort the file */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { if (index->nparts[i] != 0) { struct index_data *data = csds_index_get_data(index, i); quick_sort(data, index->nparts[i]); diff --git a/src/csds_index.h b/src/csds_index.h index 2f040c6..e9db6b8 100644 --- a/src/csds_index.h +++ b/src/csds_index.h @@ -51,7 +51,7 @@ struct csds_index { integertime_t integer_time; /* Number of particles in the file */ - uint64_t nparts[swift_type_count]; + uint64_t nparts[csds_type_count]; /* Is the file sorted ? */ char is_sorted; @@ -60,10 +60,10 @@ struct csds_index { struct mapped_file index; /* Number of particles created. */ - uint64_t nparts_created[swift_type_count]; + uint64_t nparts_created[csds_type_count]; /* Number of particles removed. */ - uint64_t nparts_removed[swift_type_count]; + uint64_t nparts_removed[csds_type_count]; }; int csds_index_contains_time_array(struct csds_index *index); diff --git a/src/csds_inline.h b/src/csds_inline.h new file mode 100644 index 0000000..e2a41ff --- /dev/null +++ b/src/csds_inline.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * SWIFT + * + * 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 CSDS_INLINE_H +#define CSDS_INLINE_H + +/* WARNING THIS FILE SHOULD NOT INCLUDE CONFIG.H*/ + +/** + * @brief Defines inline + */ +#ifndef INLINE +#ifdef __cplusplus +#define INLINE inline +#else +#if __GNUC__ && !__GNUC_STDC_INLINE__ +#define INLINE extern inline +#else +#define INLINE inline +#endif +#endif +#endif + +/** + * @brief Defines unused + */ +#define ATTR_UNUSED __attribute__((unused)) + +#endif /* CSDS_INLINE_H */ diff --git a/src/csds_interpolation.h b/src/csds_interpolation.h index 97e9419..d522731 100644 --- a/src/csds_interpolation.h +++ b/src/csds_interpolation.h @@ -23,6 +23,20 @@ #include "csds_parameters.h" #include "csds_tools.h" +/** + * @brief Limits the value of x to be between a and b + * + * Only wraps once. If x > a + 2(b - a), the returned value will be larger than + * b. Similarly for x < a - (b - a), the value will be smaller than a. + */ +#define box_wrap(x, a, b) \ + ({ \ + const __typeof__(x) _x = (x); \ + const __typeof__(a) _a = (a); \ + const __typeof__(b) _b = (b); \ + _x < _a ? (_x + (_b - _a)) : ((_x >= _b) ? (_x - (_b - _a)) : _x); \ + }) + /** * @brief Compute the quintic hermite spline interpolation. * See https://en.wikipedia.org/wiki/Hermite_interpolation for more information. @@ -224,7 +238,7 @@ interpolate_quintic_double_float_ND( otherwise something really bad happened */ x[i] = box_wrap(x[i], 0., params->box_size[i]); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* Check if the periodic box is correctly applied */ if (x[i] >= params->box_size[i] || x[i] < 0) { error_python( @@ -255,10 +269,10 @@ interpolate_quintic_double_float_ND( __attribute__((always_inline)) INLINE static void interpolate_cubic_float_ND( const double t_before, const struct csds_reader_field *restrict before, const double t_after, const struct csds_reader_field *restrict after, - void *restrict output, const double t, const int dimension, int periodic, - const struct csds_parameters *params) { + void *restrict output, const double t, const int dimension, + ATTR_UNUSED int periodic, const struct csds_parameters *params) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* The position is expected to be a double => error with periodic */ if (periodic) { error_python("Not implemented yet"); @@ -314,10 +328,11 @@ __attribute__((always_inline)) INLINE static void interpolate_cubic_float_ND( __attribute__((always_inline)) INLINE static void interpolate_linear_float_ND( const double t_before, const struct csds_reader_field *restrict before, const double t_after, const struct csds_reader_field *restrict after, - void *restrict output, const double t, const int dimension, int periodic, - const struct csds_parameters *params) { + void *restrict output, const double t, const int dimension, + ATTR_UNUSED int periodic, + ATTR_UNUSED const struct csds_parameters *params) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* The position is expected to be a double => error with periodic */ if (periodic) { error_python("Not implemented yet"); @@ -354,10 +369,11 @@ __attribute__((always_inline)) INLINE static void interpolate_linear_float_ND( __attribute__((always_inline)) INLINE static void interpolate_linear_double_ND( const double t_before, const struct csds_reader_field *restrict before, const double t_after, const struct csds_reader_field *restrict after, - void *restrict output, const double t, const int dimension, int periodic, - const struct csds_parameters *params) { + void *restrict output, const double t, const int dimension, + ATTR_UNUSED int periodic, + ATTR_UNUSED const struct csds_parameters *params) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* The velocity and acceleration are supposed to be provided => quintic => * error here */ if (periodic) { @@ -393,10 +409,10 @@ __attribute__((always_inline)) INLINE static void interpolate_linear_double_ND( __attribute__((always_inline)) INLINE static void interpolate_linear_float( const double t_before, const struct csds_reader_field *restrict before, const double t_after, const struct csds_reader_field *restrict after, - void *restrict output, const double t, int periodic, - const struct csds_parameters *params) { + void *restrict output, const double t, ATTR_UNUSED int periodic, + ATTR_UNUSED const struct csds_parameters *params) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* The position is expected to be a double => error with periodic */ if (periodic) { error_python("Not implemented yet"); diff --git a/src/csds_loader_io.h b/src/csds_loader_io.h index ca7ae26..e546bb5 100644 --- a/src/csds_loader_io.h +++ b/src/csds_loader_io.h @@ -20,8 +20,8 @@ * @file csds_loader_io.h * @brief This file contains basic IO function. */ -#ifndef CSDS_CSDS_LOADER_IO_H -#define CSDS_CSDS_LOADER_IO_H +#ifndef CSDS_LOADER_IO_H +#define CSDS_LOADER_IO_H #include "csds_fields.h" #include "csds_header.h" @@ -141,4 +141,4 @@ __attribute__((always_inline)) INLINE static char *csds_loader_io_write_data( } \ } -#endif // CSDS_CSDS_LOADER_IO_H +#endif // CSDS_LOADER_IO_H diff --git a/src/csds_logfile.c b/src/csds_logfile.c index 55c596d..82f6c38 100644 --- a/src/csds_logfile.c +++ b/src/csds_logfile.c @@ -102,8 +102,9 @@ void csds_logfile_free(struct csds_logfile *log) { * * @return position after the record. */ -void csds_logfile_check_record_consistency(struct csds_logfile *log) { -#ifdef SWIFT_DEBUG_CHECKS +void csds_logfile_check_record_consistency( + ATTR_UNUSED struct csds_logfile *log) { +#ifdef CSDS_DEBUG_CHECKS struct header *header = &log->header; const struct csds_reader *reader = log->reader; @@ -176,7 +177,7 @@ void csds_logfile_reverse_offset(struct csds_logfile *log, char *filename) { error_python("The offsets are already reversed."); } -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS csds_logfile_check_record_consistency(log); #endif @@ -230,7 +231,7 @@ void csds_logfile_reverse_offset(struct csds_logfile *log, char *filename) { message("WARNING: Modification done, you can now safely kill the job."); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS csds_logfile_check_record_consistency(log); #endif diff --git a/src/csds_logfile_writer.c b/src/csds_logfile_writer.c new file mode 100644 index 0000000..618b268 --- /dev/null +++ b/src/csds_logfile_writer.c @@ -0,0 +1,249 @@ +/******************************************************************************* + * This file is part of CSDS. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "csds_logfile_writer.h" + +/* Include local headers */ +#include "csds_error.h" + +/* Some standard headers. */ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +/** + * @brief Provide the current size used by the CSDS in GB (not the allocated + * one). + * + * @param log The #csds_logfile_writer. + */ +float csds_logfile_writer_get_current_filesize_used_gb( + const struct csds_logfile_writer *log) { + return log->count / (1024.f * 1024.f * 1024.f); +} + +/** + * @brief Ensure that at least size bytes are available in the + * #csds_logfile_writer. + * + * @param d The #csds_logfile_writer. + * @param required_size The required size for the #csds_logfile_writer + * @param increase_size If not enough size, increase by this amount + */ +void csds_logfile_writer_ensure(struct csds_logfile_writer *d, + size_t required_size, size_t increase_size) { + + /* If we have enough space already, just bail. */ + if (d->size - d->count > required_size) return; + + /* Unmap the current data. */ + if (munmap(d->data, d->size) != 0) { + error("Failed to unmap %zi bytes (%s).", d->size, strerror(errno)); + } + + /* Update the size and count. */ + const size_t trunc_count = d->count & d->page_mask; + d->file_offset += trunc_count; + d->count -= trunc_count; + d->size = (d->count + increase_size + ~d->page_mask) & d->page_mask; + + /* Re-allocate the file size. */ + if (posix_fallocate(d->fd, d->file_offset, d->size) != 0) { + error("Failed to pre-allocate the logfile."); + } + + /* Re-map starting at the end of the file. */ + if ((d->data = mmap(NULL, d->size, PROT_WRITE, MAP_SHARED, d->fd, + d->file_offset)) == MAP_FAILED) { + error("Failed to allocate map of size %zi bytes (%s).", d->size, + strerror(errno)); + } +} + +/** + * @brief Flush the #csds_logfile_writer to disk. + */ +void csds_logfile_writer_sync(struct csds_logfile_writer *d) { + if (msync(d->data, d->count, MS_SYNC) != 0) + error("Failed to sync memory-mapped data."); +} + +/** + * @brief Finalize the #csds_logfile_writer. + */ +void csds_logfile_writer_close(struct csds_logfile_writer *d) { + /* Unmap the data in memory. */ + if (munmap(d->data, d->count) != 0) { + error("Failed to unmap the logfile (%s).", strerror(errno)); + } + + /* Truncate the file to the correct length. */ + if (ftruncate(d->fd, d->file_offset + d->count) != 0) { + error("Failed to truncate the logfile (%s).", strerror(errno)); + } + + /* Close the memory-mapped file. */ + if (close(d->fd) != 0) error("Failed to close memory-mapped file."); +} + +/** + * @brief Initialize a file csds_logfile_writer. + * + * @param d The #csds_logfile_writer to initialize. + * @param filename The fully qualified name of the file in which to + * csds_logfile_writer, note that it will be overwritten. + * @param size The initial buffer size for this #csds_logfile_writer. + */ +void csds_logfile_writer_init(struct csds_logfile_writer *d, + const char *filename, size_t size) { + + /* Create the output file. + The option O_RDWR seems to be required by mmap. + */ + if ((d->fd = open(filename, O_CREAT | O_RDWR, 0660)) == -1) { + error("Failed to create the logfile '%s' (%s).", filename, strerror(errno)); + } + + /* Adjust the size to be at least the page size. */ + const size_t page_mask = ~(sysconf(_SC_PAGE_SIZE) - 1); + size = (size + ~page_mask) & page_mask; + + /* Pre-allocate the file size. */ + if (posix_fallocate(d->fd, 0, size) != 0) { + error("Failed to pre-allocate the logfile."); + } + + /* Map memory to the created file. */ + if ((d->data = mmap(NULL, size, PROT_WRITE, MAP_SHARED, d->fd, 0)) == + MAP_FAILED) { + error("Failed to allocate map of size %zi bytes (%s).", size, + strerror(errno)); + } + + /* Init some counters. */ + d->size = size; + d->count = 0; + d->file_offset = 0; + d->page_mask = page_mask; +} + +/** + * @brief Restart a file csds_logfile_writer. + * + * @param d The #csds_logfile_writer to restart. + * @param filename The fully qualified name of the file in which to + * csds_logfile_writer, note that it will be overwritten. + */ +void csds_logfile_writer_restart(struct csds_logfile_writer *d, + const char *filename) { + /* Create the output file. + The option O_RDWR seems to be required by mmap. + */ + if ((d->fd = open(filename, O_RDWR, 0660)) == -1) { + error("Failed to open the logfile '%s' (%s).", filename, strerror(errno)); + } + + /* Adjust the size to be at least the page size. */ + const size_t page_mask = ~(sysconf(_SC_PAGE_SIZE) - 1); + size_t size = (d->size + ~page_mask) & page_mask; + + /* Pre-allocate the file size. */ + if (posix_fallocate(d->fd, 0, size) != 0) { + error("Failed to pre-allocate the logfile."); + } + + /* Map memory to the created file. */ + if ((d->data = mmap(NULL, size, PROT_WRITE, MAP_SHARED, d->fd, + d->file_offset)) == MAP_FAILED) { + error("Failed to allocate map of size %zi bytes (%s).", d->size, + strerror(errno)); + } +} + +/** + * @brief Write the first part of the header. + * The next part should be all the informations about the + * masks. + * + * @param logfile The empty #csds_logfile_writer + * + * @return The buffer where to write the missing information + * (should not be modified and simply provided to + * #csds_logfile_writer_write_end_header). + */ +char *csds_logfile_writer_write_begining_header( + struct csds_logfile_writer *logfile) { + + /* Check if the logfile is empty */ + uint64_t file_offset = logfile->file_offset; + if (file_offset != 0) + error( + "The CSDS is not empty." + "This function should be called before writing anything in the CSDS"); + + /* Write format information. */ + char file_format[CSDS_STRING_SIZE] = CSDS_FORMAT_STRING; + csds_write_data(logfile, &file_offset, CSDS_STRING_SIZE, &file_format); + + /* Write the major version number. */ + int major = CSDS_MAJOR_VERSION; + csds_write_data(logfile, &file_offset, sizeof(int), &major); + + /* Write the minor version number. */ + int minor = CSDS_MINOR_VERSION; + csds_write_data(logfile, &file_offset, sizeof(int), &minor); + + /* write offset direction. */ + const int reversed = csds_offset_backward; + csds_write_data(logfile, &file_offset, sizeof(int), &reversed); + + /* placeholder to write the offset of the first log here. */ + char *skip_header = + csds_logfile_writer_get(logfile, CSDS_OFFSET_SIZE, &file_offset); + + /* write number of bytes used for names. */ + const unsigned int label_size = CSDS_STRING_SIZE; + csds_write_data(logfile, &file_offset, sizeof(unsigned int), &label_size); + + return skip_header; +} + +/** + * @brief Conclude the logfile header. + * + * @param logfile The #csds_logfile_writer. + * @param offset The place where to write the offset + * to the first record (provided by + * #csds_logfile_writer_write_beginning_header) + */ +void csds_logfile_writer_write_end_header( + ATTR_UNUSED struct csds_logfile_writer *logfile, char *offset) { + /* last step: write first offset. */ + size_t file_offset = logfile->file_offset + logfile->count; + memcpy(offset, &file_offset, CSDS_OFFSET_SIZE); +} diff --git a/src/csds_logfile_writer.h b/src/csds_logfile_writer.h new file mode 100644 index 0000000..4c5d6fe --- /dev/null +++ b/src/csds_logfile_writer.h @@ -0,0 +1,173 @@ +/******************************************************************************* + * This file is part of CSDS + * Copyright (c) 2021 loic.hausammann@epfl.ch + * 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * 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 CSDS_LOGFILE_WRITER_H +#define CSDS_LOGFILE_WRITER_H + +/* WARNING THIS FILE SHOULD NOT INCLUDE CONFIG.H*/ + +/* Include local headers */ +#include "csds_definitions.h" +#include "csds_inline.h" + +/* Standard headers */ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** The structure for writing the logfile. */ +struct csds_logfile_writer { + + /* The memory-mapped data of this dump. */ + void *data; + + /* The size of the memory-mapped data, in bytes. */ + size_t size; + + /* The number of bytes that have been dumped. */ + size_t count; + + /* The offset of the data within the current file. */ + size_t file_offset; + + /* The file with which this memory is associated. */ + int fd; + + /* Mask containing the significant bits for page addresses. */ + size_t page_mask; +}; + +/* Function prototypes. */ +void csds_logfile_writer_init(struct csds_logfile_writer *d, + const char *filename, size_t size); +void csds_logfile_writer_restart(struct csds_logfile_writer *d, + const char *filename); +void csds_logfile_writer_ensure(struct csds_logfile_writer *d, + size_t required_size, size_t increase_size); +void csds_logfile_writer_sync(struct csds_logfile_writer *d); +void csds_logfile_writer_close(struct csds_logfile_writer *d); +float csds_logfile_writer_get_current_filesize_used_gb( + const struct csds_logfile_writer *log); +void csds_logfile_writer_write_end_header(struct csds_logfile_writer *logfile, + char *offset); +char *csds_logfile_writer_write_begining_header( + struct csds_logfile_writer *logfile); + +/** + * @brief Write the header of a record (offset + mask). + * + * This is maybe broken for big(?) endian. + * + * @param buff The buffer where to write the mask and offset. + * @param mask The mask to write inside the buffer. + * @param offset The offset of the previous record. + * @param offset_new The offset of the current record. + * + * @return updated buff + */ +__attribute__((always_inline)) INLINE static char *csds_write_record_header( + char *buff, const unsigned int *mask, const size_t *offset, + const size_t offset_new) { + /* write mask. */ + memcpy(buff, mask, CSDS_MASK_SIZE); + buff += CSDS_MASK_SIZE; + + /* write offset. */ + uint64_t diff_offset = offset_new - *offset; + memcpy(buff, &diff_offset, CSDS_OFFSET_SIZE); + buff += CSDS_OFFSET_SIZE; + + return buff; +} + +/** + * @brief Obtain a chunk of memory from a csds_logfile_writer. + * + * @param d The #csds_logfile_writer. + * @param count The number of bytes requested. + * @param offset The offset of the returned memory address within the + * csds_logfile_writer file. + * @return A pointer to the memory-mapped chunk of data. + */ +__attribute__((always_inline)) INLINE static void *csds_logfile_writer_get( + struct csds_logfile_writer *d, size_t count, size_t *offset) { + size_t local_offset = 0; + + // Without OMP, this is strongly thread unsafe +#pragma omp atomic capture + { + local_offset = d->count; + d->count += count; + } + + if (d->count > d->size) { + printf("The logfile is too small.\n"); + exit(1); + } + *offset = local_offset + d->file_offset; + return (char *)d->data + local_offset; +} + +/** + * @brief Generate the data for the special flags. + * + * @param flag The special flag to use. + * @param flag_data The data to write in the record. + * @param type The type of the particle. + */ +INLINE static uint32_t csds_pack_flags_and_data(enum csds_special_flags flag, + int flag_data, int type) { + if (flag & 0xFFFFFF00) { + printf( + "The special flag in the particle CSDS cannot" + "be larger than 1 byte.\n"); + exit(1); + } + if (flag_data & ~0xFFFF) { + printf( + "The data for the special flag in the particle " + "CSDS cannot be larger than 2 bytes."); + exit(1); + } + return ((uint32_t)flag << (3 * 8)) | ((flag_data & 0xFFFF) << 8) | + (type & 0xFF); +} + +/** + * @brief Write to the logfile. + * + * @param d #csds_logfile_writer file + * @param offset (return) offset of the data + * @param size number of bytes to write + * @param p pointer to the data + */ +__attribute__((always_inline)) INLINE static void csds_write_data( + struct csds_logfile_writer *d, size_t *offset, size_t size, const void *p) { + /* get buffer. */ + char *buff = csds_logfile_writer_get(d, size, offset); + + /* write data to the buffer. */ + memcpy(buff, p, size); + + /* Update offset to end of record. */ + *offset += size; +} + +#endif /* CSDS_LOGFILE_WRITER_H */ diff --git a/src/csds_openmp.h b/src/csds_openmp.h new file mode 100644 index 0000000..119c4c5 --- /dev/null +++ b/src/csds_openmp.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * This file is part of CSDS. + * Copyright (c) 2021 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 related to openmp. + */ +#ifndef CSDS_OPENMP_H +#define CSDS_OPENMP_H + +#include "../config.h" + +/* Include standard headers */ +#ifdef CSDS_WITH_OPENMP +#include <omp.h> +#endif + +/** + * @brief Wrapper for omp_get_thread_num + */ +__attribute__((always_inline)) INLINE static int csds_get_thread_num(void) { +#ifdef CSDS_WITH_OPENMP + return omp_get_thread_num(); +#else + return 0; +#endif +} + +#endif // CSDS_OPENMP_H diff --git a/src/csds_parameters.c b/src/csds_parameters.c index e33a680..3ecd12c 100644 --- a/src/csds_parameters.c +++ b/src/csds_parameters.c @@ -21,27 +21,22 @@ #include "csds_parameters.h" /* Include the csds */ +#include "csds_parser.h" #include "csds_reader.h" #include "csds_tools.h" -/* Include swift */ -#include "parser.h" - /** * @brief Initialize the parameters from the yaml file. * * @param reader The #csds_reader. * @param params The #csds_parameters to initialize. - * @param number_threads The number of threads for the threadpool - * (-1 in order to use the default value). * */ void csds_parameters_init(struct csds_parameters *params, - const struct csds_reader *reader, - int number_threads) { + const struct csds_reader *reader) { /* Generate filename */ - char filename[STRING_SIZE]; + char filename[CSDS_STRING_SIZE]; strcpy(filename, reader->basename); size_t len = strlen(filename); /* Remove the rank number */ @@ -49,50 +44,39 @@ void csds_parameters_init(struct csds_parameters *params, strcat(filename, ".yml"); /* Initialize the parser */ - struct swift_params swift_params; - parser_read_file(filename, &swift_params); + struct csds_params csds_params; + parser_read_file(filename, &csds_params); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS /* Print the parameters */ - if (reader->verbose > 0) parser_print_params(&swift_params); + if (reader->verbose > 0) parser_print_params(&csds_params); #endif /* Read the number of dimension */ - params->dimension = parser_get_param_int(&swift_params, "Header:Dimension"); + params->dimension = parser_get_param_int(&csds_params, "Header:Dimension"); /* Read the box size */ - parser_get_param_double_array(&swift_params, "Header:BoxSize", 3, + parser_get_param_double_array(&csds_params, "Header:BoxSize", 3, params->box_size); /* Read the periodicity */ - params->periodic = parser_get_param_int(&swift_params, "Header:Periodic"); + params->periodic = parser_get_param_int(&csds_params, "Header:Periodic"); /* Read if we are running with cosmology */ params->with_cosmology = - parser_get_param_int(&swift_params, "Policy:cosmological integration"); + parser_get_param_int(&csds_params, "Policy:cosmological integration"); /* Initialize the cosmology */ - if (params->with_cosmology) - csds_cosmology_init(¶ms->cosmo, &swift_params); - - /* Save the number of threads */ - params->number_threads = number_threads; - if (reader->verbose > 0) { - message("Running with %i threads", params->number_threads); - } + if (params->with_cosmology) csds_cosmology_init(¶ms->cosmo, &csds_params); /* Read the initial number of particles. */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { params->approximate_number_particles[i] = 0; } - params->approximate_number_particles[swift_type_gas] = - parser_get_param_longlong(&swift_params, "Header:NumberParts"); - params->approximate_number_particles[swift_type_stars] = - parser_get_param_longlong(&swift_params, "Header:NumberSParts"); - params->approximate_number_particles[swift_type_dark_matter] = - parser_get_param_longlong(&swift_params, "Header:NumberGParts"); - - /* Read the maximal fraction of tagged elements */ - params->arrays_maximal_tagged_fraction = parser_get_opt_param_float( - &swift_params, "ReaderOptions:arrays_maximal_tagged_fraction", 0.15); + params->approximate_number_particles[csds_type_gas] = + parser_get_param_longlong(&csds_params, "Header:NumberParts"); + params->approximate_number_particles[csds_type_stars] = + parser_get_param_longlong(&csds_params, "Header:NumberSParts"); + params->approximate_number_particles[csds_type_dark_matter] = + parser_get_param_longlong(&csds_params, "Header:NumberGParts"); } diff --git a/src/csds_parameters.h b/src/csds_parameters.h index 431fcf5..1591b48 100644 --- a/src/csds_parameters.h +++ b/src/csds_parameters.h @@ -27,9 +27,7 @@ /* Local include */ #include "csds_cosmology.h" - -/* SWIFT include */ -#include "part_type.h" +#include "csds_part_type.h" struct csds_reader; @@ -50,11 +48,8 @@ struct csds_parameters { /* Are we doing a cosmological simulation? */ int with_cosmology; - /* Number of threads to use in the threadpools */ - int number_threads; - /* Number of particles initially present. */ - long long approximate_number_particles[swift_type_count]; + long long approximate_number_particles[csds_type_count]; /* Maximal fraction of tagged for removal element within an array. */ float arrays_maximal_tagged_fraction; @@ -64,6 +59,6 @@ struct csds_parameters { }; void csds_parameters_init(struct csds_parameters *params, - const struct csds_reader *reader, int number_threads); + const struct csds_reader *reader); #endif // CSDS_CSDS_PARAMETERS_H diff --git a/src/csds_parser.c b/src/csds_parser.c new file mode 100644 index 0000000..c0fcf95 --- /dev/null +++ b/src/csds_parser.c @@ -0,0 +1,1051 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * SWIFT + * + * 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +/* Needs to be included so that strtok returns char * instead of a int *. */ +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +/* This object's header. */ +#include "csds_parser.h" + +/* Local headers. */ +#include "csds_error.h" + +#define FIELD_BUFFER_SIZE 64 +#define PARSER_COMMENT_STRING "#" +#define PARSER_COMMENT_CHAR '#' +#define PARSER_VALUE_CHAR ':' +#define PARSER_VALUE_STRING ":" +#define PARSER_START_OF_FILE "---" +#define PARSER_END_OF_FILE "..." + +#define CHUNK 10 + +/* Alias of long long needed and used only for the preprocessor definitions */ +typedef long long longlong; + +/* Private functions. */ +static int is_empty(const char *str); +static int count_indentation(const char *str); +static void parse_line(char *line, struct csds_params *params); +static void parse_value(char *line, struct csds_params *params); +static void parse_section_param(char *line, int *isFirstParam, + char *sectionName, struct csds_params *params); +static void find_duplicate_params(const struct csds_params *params, + const char *param_name); +static void find_duplicate_section(const struct csds_params *params, + const char *section_name); +static int lineNumber = 0; + +/** + * @brief trim leading white space from a string. + * + * Returns pointer to first character. + * + * @param s the string. + * @result the result. + */ +char *trim_leading(char *s) { + if (s == NULL || strlen(s) < 2) return s; + while (isspace(*s)) s++; + return s; +} + +/** + * @brief trim trailing white space from a string. + * + * Modifies the string by adding a NULL to the end. + * + * @param s the string. + * @result the result. + */ +char *trim_trailing(char *s) { + if (s == NULL || strlen(s) < 2) return s; + char *end = s + strlen(s) - 1; + while (isspace(*end)) end--; + *(end + 1) = '\0'; + return s; +} + +/** + * @brief trim leading and trailing white space from a string. + * + * Can modify the string by adding a NULL to the end. + * + * @param s the string. + * @result the result. + */ +char *trim_both(char *s) { + if (s == NULL || strlen(s) < 2) return s; + return trim_trailing(trim_leading(s)); +} + +/** + * @brief Initialize the parser structure. + * + * @param file_name Name of file to be read + * @param params Structure to be populated from file + */ +void parser_init(const char *file_name, struct csds_params *params) { + params->paramCount = 0; + params->sectionCount = 0; + strcpy(params->fileName, file_name); +} + +/** + * @brief Reads an input file and stores each parameter in a structure. + * + * @param file_name Name of file to be read + * @param params Structure to be populated from file + */ +void parser_read_file(const char *file_name, struct csds_params *params) { + /* Open file for reading */ + FILE *file = fopen(file_name, "r"); + + /* Line to parsed. */ + char line[PARSER_MAX_LINE_SIZE]; + + /* Initialise parameter count. */ + parser_init(file_name, params); + + /* Check if parameter file exits. */ + if (file == NULL) { + error("Error opening parameter file: %s", file_name); + } + + /* Read until the end of the file is reached.*/ + while (!feof(file)) { + if (fgets(line, PARSER_MAX_LINE_SIZE, file) != NULL) { + lineNumber++; + parse_line(line, params); + } + } + + fclose(file); +} + +/** + * @brief Set or update a parameter using a compressed format. + * + * The compressed format allows a value to be given as a single + * string and has the format "section:parameter:value", with all + * names as would be given in the parameter file. + * + * @param params Structure that holds the parameters. + * @param namevalue the parameter name and value as described. + */ +void parser_set_param(struct csds_params *params, const char *namevalue) { + + /* Get the various parts. */ + char name[PARSER_MAX_LINE_SIZE]; + char value[PARSER_MAX_LINE_SIZE]; + char section[PARSER_MAX_LINE_SIZE]; + name[0] = '\0'; + value[0] = '\0'; + + /* Name is part until second colon. */ + const char *p1 = strchr(namevalue, ':'); + if (p1 != NULL) { + + /* Section is first part until a colon. */ + memcpy(section, namevalue, p1 - namevalue); + section[p1 - namevalue] = ':'; + section[p1 - namevalue + 1] = '\0'; + + const char *p2 = strchr(p1 + 1, ':'); + if (p2 != NULL) { + memcpy(name, namevalue, p2 - namevalue); + name[p2 - namevalue] = '\0'; + + /* Value is rest after second colon. */ + p2++; + strcpy(value, p2); + } + } + + /* Sanity check. */ + if (strlen(name) == 0 || strlen(value) == 0 || strchr(value, ':') != NULL) + error( + "Cannot parse compressed parameter string: '%s', check syntax " + "should be section:parameter:value", + namevalue); + + /* And update or set. */ + int updated = 0; + for (int i = 0; i < params->paramCount; i++) { + if (strcmp(name, params->data[i].name) == 0) { + message("Value of '%s' changed from '%s' to '%s'", params->data[i].name, + params->data[i].value, value); + strcpy(params->data[i].value, trim_both(value)); + updated = 1; + } + } + if (!updated) { + /* Is this a new section? */ + int newsection = 1; + for (int i = 0; i < params->sectionCount; i++) { + if (strcmp(section, params->section[i].name) == 0) { + newsection = 0; + break; + } + } + if (newsection) { + strcpy(params->section[params->sectionCount].name, section); + params->sectionCount++; + if (params->sectionCount == PARSER_MAX_NO_OF_SECTIONS) + error("Too many sections, current maximum is %d.", + params->sectionCount); + } + + strcpy(params->data[params->paramCount].name, name); + strcpy(params->data[params->paramCount].value, value); + params->data[params->paramCount].used = 0; + params->data[params->paramCount].is_default = 0; + params->paramCount++; + if (params->paramCount == PARSER_MAX_NO_OF_PARAMS) + error("Too many parameters, current maximum is %d.", params->paramCount); + } +} + +/** + * @brief Counts the number of white spaces that prefix a string. + * + * @param str String to be checked + * + * @return Number of white spaces prefixing str + */ + +static int count_indentation(const char *str) { + int count = 0; + + /* Check if the line contains the character */ + while (*(++str) == ' ') { + count++; + } + return count; +} + +/** + * @brief Checks if a string is empty. + * + * @param str String to be checked + * + * @return Returns 1 if str is empty, 0 otherwise + */ + +static int is_empty(const char *str) { + int retParam = 1; + while (*str != '\0') { + if (!isspace(*str)) { + retParam = 0; + break; + } + str++; + } + + return retParam; +} + +/** + * @brief Look for duplicate parameters. + * + * @param params Structure that holds the parameters + * @param param_name Name of parameter to be searched for + */ + +static void find_duplicate_params(const struct csds_params *params, + const char *param_name) { + for (int i = 0; i < params->paramCount; i++) { + if (!strcmp(param_name, params->data[i].name)) { + error("Invalid line:%d '%s', parameter is a duplicate.", lineNumber, + param_name); + } + } +} + +/** + * @brief Look for duplicate sections. + * + * @param params Structure that holds the parameters + * @param section_name Name of section to be searched for + */ + +static void find_duplicate_section(const struct csds_params *params, + const char *section_name) { + for (int i = 0; i < params->sectionCount; i++) { + if (!strcmp(section_name, params->section[i].name)) { + error("Invalid line:%d '%s', section is a duplicate.", lineNumber, + section_name); + } + } +} +/** + * @brief Parses a line from a file and stores any parameters in a structure. + * + * @param line Line to be parsed. + * @param params Structure to be populated from file. + */ + +static void parse_line(char *line, struct csds_params *params) { + /* Parse line if it doesn't begin with a comment. */ + if (line[0] != PARSER_COMMENT_CHAR) { + char trim_line[PARSER_MAX_LINE_SIZE]; + char tmp_str[PARSER_MAX_LINE_SIZE]; + char *token; + + /* Remove comments at the end of a line. */ + token = strtok(line, PARSER_COMMENT_STRING); + strcpy(tmp_str, token); + + /* Check if the line is just white space. */ + if (!is_empty(tmp_str)) { + /* Trim '\n' characters from string. */ + token = strtok(tmp_str, "\n"); + strcpy(trim_line, token); + + /* Check if the line contains a value and parse it. */ + if (strchr(trim_line, PARSER_VALUE_CHAR)) { + + /* Trim trailing space before parsing line for a value. */ + char no_space_line[PARSER_MAX_LINE_SIZE]; + strcpy(no_space_line, trim_trailing(trim_line)); + + parse_value(no_space_line, params); + } + /* Check for invalid lines,not including the start and end of file. */ + else if (strcmp(trim_line, PARSER_START_OF_FILE) && + strcmp(trim_line, PARSER_END_OF_FILE)) { + error("Invalid line:%d '%s'.", lineNumber, trim_line); + } + } + } +} + +/** + * @brief Performs error checking and stores a parameter in a structure. + * + * @param line Line containing the parameter + * @param params Structure to be written to + * + */ + +static void parse_value(char *line, struct csds_params *params) { + static int inSection = 0; + static char section[PARSER_MAX_LINE_SIZE]; /* Keeps track of current section + name. */ + static int isFirstParam = 1; + char tmpStr[PARSER_MAX_LINE_SIZE]; + char tmpSectionName[PARSER_MAX_LINE_SIZE]; + + char *token; + + /* Check that standalone parameters have correct indentation. */ + if (!inSection && line[0] == ' ') { + error( + "Invalid line:%d '%s', standalone parameter defined with incorrect " + "indentation.", + lineNumber, line); + } + + /* Check that it is a parameter inside a section.*/ + if (line[0] == ' ' || line[0] == '\t') { + parse_section_param(line, &isFirstParam, section, params); + } else { + /* It is the start of a new section or standalone parameter. + * Take first token as the parameter name. */ + token = strtok(line, ":\t"); + strcpy(tmpStr, trim_trailing(token)); + + /* Take second token as the parameter value. */ + token = trim_both(strtok(NULL, "#\n")); + + /* If second token is NULL or empty then the line must be a section + * heading. */ + if (token == NULL || strlen(token) == 0) { + strcpy(tmpSectionName, tmpStr); + strcat(tmpSectionName, PARSER_VALUE_STRING); + + /* Check for duplicate section name. */ + find_duplicate_section(params, tmpSectionName); + + /* Check for duplicate standalone parameter name used as a section name. + */ + find_duplicate_params(params, tmpStr); + + strcpy(section, tmpSectionName); + strcpy(params->section[params->sectionCount].name, tmpSectionName); + if (params->sectionCount == PARSER_MAX_NO_OF_SECTIONS - 1) { + error( + "Maximal number of sections in parameter file reached. Aborting !"); + } else { + params->sectionCount++; + } + inSection = 1; + isFirstParam = 1; + } else { + /* Create string with standalone parameter name appended with ":" to aid + * duplicate search as section names are stored with ":" at the end.*/ + strcpy(tmpSectionName, tmpStr); + strcat(tmpSectionName, PARSER_VALUE_STRING); + + /* Check for duplicate parameter name. */ + find_duplicate_params(params, tmpStr); + + /* Check for duplicate section name used as standalone parameter name. */ + find_duplicate_section(params, tmpSectionName); + + /* Must be a standalone parameter so no need to prefix name with a + * section. */ + strcpy(params->data[params->paramCount].name, tmpStr); + strcpy(params->data[params->paramCount].value, token); + params->data[params->paramCount].used = 0; + params->data[params->paramCount].is_default = 0; + if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) { + error( + "Maximal number of parameters in parameter file reached. Aborting " + "!"); + } else { + params->paramCount++; + } + inSection = 0; + isFirstParam = 1; + } + } +} + +/** + * @brief Parses a parameter that appears in a section and stores it in a + *structure. + * + * @param line Line containing the parameter + * @param isFirstParam Shows if the first parameter of a section has been found + * @param sectionName String containing the current section name + * @param params Structure to be written to + * + */ + +static void parse_section_param(char *line, int *isFirstParam, + char *sectionName, struct csds_params *params) { + static int sectionIndent = 0; + char tmpStr[PARSER_MAX_LINE_SIZE]; + char paramName[PARSER_MAX_LINE_SIZE]; + char *token; + + /* Count indentation of each parameter and check that it + * is consistent with the first parameter in the section. */ + if (*isFirstParam) { + sectionIndent = count_indentation(line); + *isFirstParam = 0; + } else if (count_indentation(line) != sectionIndent) { + error("Invalid line:%d '%s', parameter has incorrect indentation.", + lineNumber, line); + } + + /* Take first token as the parameter name and trim leading white space. */ + token = trim_both(strtok(line, ":\t")); + strcpy(tmpStr, token); + + /* Take second token as the parameter value. */ + token = trim_both(strtok(NULL, "#\n")); + + /* Prefix the parameter name with its section name and + * copy it into the parameter structure. */ + strcpy(paramName, sectionName); + strcat(paramName, tmpStr); + + /* Check for duplicate parameter name. */ + find_duplicate_params(params, paramName); + + strcpy(params->data[params->paramCount].name, paramName); + strcpy(params->data[params->paramCount].value, token); + params->data[params->paramCount].used = 0; + params->data[params->paramCount].is_default = 0; + if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) { + error("Maximal number of parameters in parameter file reached. Aborting !"); + } else { + params->paramCount++; + } +} + +// Retrieve parameter value from structure. TYPE is the data type, float, int +// etc. FMT the format required for that data type, i.e. %f, %d etc. and DESC +// a one word description of the type, "float", "int" etc. +#define PARSER_GET_VALUE(TYPE, FMT, DESC) \ + static int get_param_##TYPE(struct csds_params *params, const char *name, \ + TYPE *def, TYPE *result) { \ + char str[PARSER_MAX_LINE_SIZE]; \ + for (int i = 0; i < params->paramCount; i++) { \ + if (strcmp(name, params->data[i].name) == 0) { \ + /* Check that exactly one number is parsed, capture junk. */ \ + if (sscanf(params->data[i].value, " " FMT "%s ", result, str) != 1) { \ + error("Tried parsing " DESC \ + " '%s' but found '%s' with " \ + "illegal trailing characters '%s'.", \ + params->data[i].name, params->data[i].value, str); \ + } \ + /* Ensure same behavior if called multiple times for same parameter */ \ + if (params->data[i].is_default && def == NULL) \ + error( \ + "Tried parsing %s again but cannot parse a default " \ + "parameter as mandatory", \ + name); \ + if (params->data[i].is_default && *def != *result) \ + error( \ + "Tried parsing %s again but cannot parse a parameter with " \ + "two different default value (" FMT "!=" FMT ")", \ + name, *def, *result); \ + /* This parameter has been used */ \ + params->data[i].used = 1; \ + return 1; \ + } \ + } \ + if (def == NULL) \ + error("Cannot find '%s' in the structure, in file '%s'.", name, \ + params->fileName); \ + return 0; \ + } + +// Set a parameter to a value and save for dumping. +#define PARSER_SAVE_VALUE(PREFIX, TYPE, FMT) \ + static void save_param_##PREFIX(struct csds_params *params, \ + const char *name, TYPE value) { \ + char str[PARSER_MAX_LINE_SIZE]; \ + sprintf(str, "%s:" FMT, name, value); \ + parser_set_param(params, str); \ + params->data[params->paramCount - 1].used = 1; \ + params->data[params->paramCount - 1].is_default = 0; \ + } + +/* Instantiations. */ +PARSER_GET_VALUE(char, "%c", "char"); +PARSER_GET_VALUE(int, "%d", "int"); +PARSER_GET_VALUE(float, "%f", "float"); +PARSER_GET_VALUE(double, "%lf", "double"); +PARSER_GET_VALUE(longlong, "%lld", "long long"); +PARSER_SAVE_VALUE(char, char, "%c"); +PARSER_SAVE_VALUE(int, int, "%d"); +PARSER_SAVE_VALUE(float, float, "%g"); +PARSER_SAVE_VALUE(double, double, "%g"); +PARSER_SAVE_VALUE(longlong, longlong, "%lld"); +PARSER_SAVE_VALUE(string, const char *, "%s"); + +/** + * @brief Retrieve integer parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @return Value of the parameter found + */ +int parser_get_param_int(struct csds_params *params, const char *name) { + int result = 0; + get_param_int(params, name, NULL, &result); + return result; +} + +/** + * @brief Retrieve char parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @return Value of the parameter found + */ +char parser_get_param_char(struct csds_params *params, const char *name) { + char result = 0; + get_param_char(params, name, NULL, &result); + return result; +} + +/** + * @brief Retrieve float parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @return Value of the parameter found + */ +float parser_get_param_float(struct csds_params *params, const char *name) { + float result = 0; + get_param_float(params, name, NULL, &result); + return result; +} + +/** + * @brief Retrieve double parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @return Value of the parameter found + */ +double parser_get_param_double(struct csds_params *params, const char *name) { + double result = 0; + get_param_double(params, name, NULL, &result); + return result; +} + +/** + * @brief Retrieve long long parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @return Value of the parameter found + */ +long long parser_get_param_longlong(struct csds_params *params, + const char *name) { + long long result = 0; + get_param_longlong(params, name, NULL, &result); + return result; +} + +/** + * @brief Retrieve string parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param retParam (return) Value of the parameter found + */ +void parser_get_param_string(struct csds_params *params, const char *name, + char *retParam) { + + for (int i = 0; i < params->paramCount; i++) { + if (!strcmp(name, params->data[i].name)) { + if (params->data[i].is_default) + error( + "Tried parsing %s again but cannot parse a " + "default parameter as mandatory", + name); + strcpy(retParam, params->data[i].value); + /* this parameter has been used */ + params->data[i].used = 1; + return; + } + } + + error("Cannot find '%s' in the structure.", name); +} + +/** + * @brief Retrieve optional integer parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @return Value of the parameter found + */ +int parser_get_opt_param_int(struct csds_params *params, const char *name, + int def) { + int result = 0; + if (get_param_int(params, name, &def, &result)) return result; + save_param_int(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + return def; +} + +/** + * @brief Retrieve optional char parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @return Value of the parameter found + */ +char parser_get_opt_param_char(struct csds_params *params, const char *name, + char def) { + char result = 0; + if (get_param_char(params, name, &def, &result)) return result; + save_param_char(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + return def; +} + +/** + * @brief Retrieve optional float parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @return Value of the parameter found + */ +float parser_get_opt_param_float(struct csds_params *params, const char *name, + float def) { + float result = 0; + if (get_param_float(params, name, &def, &result)) return result; + save_param_float(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + return def; +} + +/** + * @brief Retrieve optional double parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @return Value of the parameter found + */ +double parser_get_opt_param_double(struct csds_params *params, const char *name, + double def) { + double result = 0; + if (get_param_double(params, name, &def, &result)) return result; + save_param_double(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + return def; +} + +/** + * @brief Retrieve optional long long parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @return Value of the parameter found + */ +long long parser_get_opt_param_longlong(struct csds_params *params, + const char *name, long long def) { + long long result = 0; + if (get_param_longlong(params, name, &def, &result)) return result; + save_param_longlong(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + return def; +} + +/** + * @brief Retrieve string parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param def Default value of the parameter of not found. + * @param retParam (return) Value of the parameter found + */ +void parser_get_opt_param_string(struct csds_params *params, const char *name, + char *retParam, const char *def) { + + for (int i = 0; i < params->paramCount; i++) { + if (!strcmp(name, params->data[i].name)) { + strcpy(retParam, params->data[i].value); + + /* Ensure same behavior if called multiple times for same parameter */ + if (params->data[i].is_default && strcmp(def, retParam) != 0) + error( + "Tried parsing %s again but cannot parse a parameter with " + "two different default values ('%s' != '%s')", + name, def, retParam); + /* this parameter has been used */ + params->data[i].used = 1; + return; + } + } + save_param_string(params, name, def); + params->data[params->paramCount - 1].is_default = 1; + strcpy(retParam, def); +} + +/* Macro defining functions that get primitive types as simple one-line YAML + * arrays, that is SEC: [v1,v2,v3...] format, with the extension that the [] + * are optional. TYPE is the data type, float etc. FMT a format to parse a + * single value, so "%f" for a float and DESC the type description + * i.e. "float". + */ +#define PARSER_GET_ARRAY(TYPE, FMT, DESC) \ + static int get_param_##TYPE##_array(struct csds_params *params, \ + const char *name, int required, \ + int nval, TYPE *values) { \ + char str[PARSER_MAX_LINE_SIZE]; \ + char cpy[PARSER_MAX_LINE_SIZE]; \ + \ + for (int i = 0; i < params->paramCount; i++) { \ + if (!strcmp(name, params->data[i].name)) { \ + if (params->data[i].is_default && required) \ + error( \ + "Tried parsing %s again but cannot parse a default " \ + "parameter as mandatory", \ + name); \ + char *cp = cpy; \ + strcpy(cp, params->data[i].value); \ + cp = trim_both(cp); \ + \ + /* Strip off [], if present. */ \ + if (cp[0] == '[') cp++; \ + int l = strlen(cp); \ + if (cp[l - 1] == ']') cp[l - 1] = '\0'; \ + cp = trim_both(cp); \ + \ + /* Format that captures spaces and trailing junk. */ \ + char fmt[20]; \ + sprintf(fmt, " %s%%s ", FMT); \ + \ + /* Parse out values which should now be "v, v, v" with \ + * internal whitespace variations. */ \ + char *p = strtok(cp, ","); \ + for (int k = 0; k < nval; k++) { \ + if (p != NULL) { \ + TYPE tmp_value; \ + if (sscanf(p, fmt, &tmp_value, str) != 1) { \ + error("Tried parsing " DESC \ + " '%s' but found '%s' with " \ + "illegal " DESC " characters '%s'.", \ + name, p, str); \ + } \ + if (params->data[i].is_default && tmp_value != values[k]) \ + error( \ + "Tried parsing %s again but cannot parse a " \ + "parameter with two different default value " \ + "(" FMT "!=" FMT ")", \ + name, tmp_value, values[k]); \ + values[k] = tmp_value; \ + } else { \ + error( \ + "Array '%s' with value '%s' has too few values, " \ + "expected %d", \ + name, params->data[i].value, nval); \ + } \ + if (k < nval - 1) p = strtok(NULL, ","); \ + } \ + params->data[i].used = 1; \ + return 1; \ + } \ + } \ + if (required) \ + error("Cannot find '%s' in the structure, in file '%s'.", name, \ + params->fileName); \ + return 0; \ + } + +// Set values of a default parameter so they will be saved correctly. +#define PARSER_SAVE_ARRAY(TYPE, FMT) \ + static int save_param_##TYPE##_array( \ + struct csds_params *params, const char *name, int nval, TYPE *values) { \ + /* Save values against the parameter. */ \ + char str[PARSER_MAX_LINE_SIZE]; \ + int k = sprintf(str, "%s: [", name); \ + for (int i = 0; i < nval - 1; i++) \ + k += sprintf(&str[k], FMT ", ", values[i]); \ + sprintf(&str[k], FMT "]", values[nval - 1]); \ + parser_set_param(params, str); \ + params->data[params->paramCount - 1].used = 1; \ + params->data[params->paramCount - 1].is_default = 0; \ + return 0; \ + } + +/* Instantiations. */ +PARSER_GET_ARRAY(char, "%c", "char"); +PARSER_GET_ARRAY(int, "%d", "int"); +PARSER_GET_ARRAY(float, "%f", "float"); +PARSER_GET_ARRAY(double, "%lf", "double"); +PARSER_GET_ARRAY(longlong, "%lld", "long long"); +PARSER_SAVE_ARRAY(char, "%c"); +PARSER_SAVE_ARRAY(int, "%d"); +PARSER_SAVE_ARRAY(float, "%g"); +PARSER_SAVE_ARRAY(double, "%g"); +PARSER_SAVE_ARRAY(longlong, "%lld"); + +/** + * @brief Retrieve char array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. + */ +void parser_get_param_char_array(struct csds_params *params, const char *name, + int nval, char *values) { + get_param_char_array(params, name, 1, nval, values); +} + +/** + * @brief Retrieve optional char array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. If the + * parameter is not found these values will be returned + * unmodified, so should be set to the default values. + * @return whether the parameter has been found. + */ +int parser_get_opt_param_char_array(struct csds_params *params, + const char *name, int nval, char *values) { + if (get_param_char_array(params, name, 0, nval, values) != 1) { + save_param_char_array(params, name, nval, values); + params->data[params->paramCount - 1].is_default = 1; + return 0; + } + return 1; +} + +/** + * @brief Retrieve int array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. + */ +void parser_get_param_int_array(struct csds_params *params, const char *name, + int nval, int *values) { + get_param_int_array(params, name, 1, nval, values); +} + +/** + * @brief Retrieve optional int array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. If the + * parameter is not found these values will be returned + * unmodified, so should be set to the default values. + * @return whether the parameter has been found. + */ +int parser_get_opt_param_int_array(struct csds_params *params, const char *name, + int nval, int *values) { + if (get_param_int_array(params, name, 0, nval, values) != 1) { + save_param_int_array(params, name, nval, values); + params->data[params->paramCount - 1].is_default = 1; + return 0; + } + return 1; +} + +/** + * @brief Retrieve float array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. + */ +void parser_get_param_float_array(struct csds_params *params, const char *name, + int nval, float *values) { + get_param_float_array(params, name, 1, nval, values); +} + +/** + * @brief Retrieve optional float array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. If the + * parameter is not found these values will be returned + * unmodified, so should be set to the default values. + * @return whether the parameter has been found. + */ +int parser_get_opt_param_float_array(struct csds_params *params, + const char *name, int nval, + float *values) { + if (get_param_float_array(params, name, 0, nval, values) != 1) { + save_param_float_array(params, name, nval, values); + params->data[params->paramCount - 1].is_default = 1; + return 0; + } + return 1; +} + +/** + * @brief Retrieve double array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. + */ +void parser_get_param_double_array(struct csds_params *params, const char *name, + int nval, double *values) { + get_param_double_array(params, name, 1, nval, values); +} + +/** + * @brief Retrieve optional double array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. If the + * parameter is not found these values will be returned + * unmodified, so should be set to the default values. + * @return whether the parameter has been found. + */ +int parser_get_opt_param_double_array(struct csds_params *params, + const char *name, int nval, + double *values) { + if (get_param_double_array(params, name, 0, nval, values) != 1) { + save_param_double_array(params, name, nval, values); + params->data[params->paramCount - 1].is_default = 1; + return 0; + } + return 1; +} + +/** + * @brief Retrieve long long array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. + */ +void parser_get_param_longlong_array(struct csds_params *params, + const char *name, int nval, + long long *values) { + get_param_longlong_array(params, name, 1, nval, values); +} + +/** + * @brief Retrieve optional long long array parameter from structure. + * + * @param params Structure that holds the parameters + * @param name Name of the parameter to be found + * @param nval number of values expected. + * @param values Values of the parameter found, of size at least nvals. If the + * parameter is not found these values will be returned + * unmodified, so should be set to the default values. + * @return whether the parameter has been found. + */ +int parser_get_opt_param_longlong_array(struct csds_params *params, + const char *name, int nval, + long long *values) { + if (get_param_longlong_array(params, name, 0, nval, values) != 1) { + save_param_longlong_array(params, name, nval, values); + params->data[params->paramCount - 1].is_default = 1; + return 0; + } + return 1; +} + +/** + * @brief Prints the contents of the parameter structure. + * + * @param params Structure that holds the parameters + */ +void parser_print_params(const struct csds_params *params) { + printf("\n--------------------------\n"); + printf("| CSDS Parameter File |\n"); + printf("--------------------------\n"); + + for (int i = 0; i < params->paramCount; i++) { + printf("Parameter name: %s\n", params->data[i].name); + printf("Parameter value: %s\n", params->data[i].value); + printf("Parameter used: %i\n", params->data[i].used); + } +} diff --git a/src/csds_parser.h b/src/csds_parser.h new file mode 100644 index 0000000..f1eb42b --- /dev/null +++ b/src/csds_parser.h @@ -0,0 +1,110 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 loic.hausammann@epfl.ch + * SWIFT + * + * 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 CSDS_PARSER_H +#define CSDS_PARSER_H + +/* Config parameters. */ +#include "../config.h" + +/* Standard headers */ +#include <stdio.h> + +#if defined(HAVE_HDF5) +#include <hdf5.h> +#endif + +/* Some constants. */ +#define PARSER_MAX_LINE_SIZE 256 +#define PARSER_MAX_NO_OF_PARAMS 600 +#define PARSER_MAX_NO_OF_SECTIONS 64 + +/* A parameter in the input file */ +struct parameter { + char name[PARSER_MAX_LINE_SIZE]; + char value[PARSER_MAX_LINE_SIZE]; + int used; + int is_default; +}; + +struct section { + char name[PARSER_MAX_LINE_SIZE]; +}; + +/* The array of parameters read from a file */ +struct csds_params { + struct section section[PARSER_MAX_NO_OF_SECTIONS]; + struct parameter data[PARSER_MAX_NO_OF_PARAMS]; + int sectionCount; + int paramCount; + char fileName[PARSER_MAX_LINE_SIZE]; +}; + +/* Public API. */ +void parser_init(const char *file_name, struct csds_params *params); +void parser_read_file(const char *file_name, struct csds_params *params); +void parser_print_params(const struct csds_params *params); +void parser_set_param(struct csds_params *params, const char *desc); + +char parser_get_param_char(struct csds_params *params, const char *name); +int parser_get_param_int(struct csds_params *params, const char *name); +float parser_get_param_float(struct csds_params *params, const char *name); +double parser_get_param_double(struct csds_params *params, const char *name); +long long parser_get_param_longlong(struct csds_params *params, + const char *name); +void parser_get_param_string(struct csds_params *params, const char *name, + char *retParam); + +char parser_get_opt_param_char(struct csds_params *params, const char *name, + char def); +int parser_get_opt_param_int(struct csds_params *params, const char *name, + int def); +float parser_get_opt_param_float(struct csds_params *params, const char *name, + float def); +double parser_get_opt_param_double(struct csds_params *params, const char *name, + double def); +long long parser_get_opt_param_longlong(struct csds_params *params, + const char *name, long long def); +void parser_get_opt_param_string(struct csds_params *params, const char *name, + char *retParam, const char *def); +void parser_get_param_char_array(struct csds_params *params, const char *name, + int nval, char *values); +void parser_get_param_int_array(struct csds_params *params, const char *name, + int nval, int *values); +void parser_get_param_float_array(struct csds_params *params, const char *name, + int nval, float *values); +void parser_get_param_double_array(struct csds_params *params, const char *name, + int nval, double *values); +void parser_get_param_longlong_array(struct csds_params *params, + const char *name, int nval, + long long *values); +int parser_get_opt_param_char_array(struct csds_params *params, + const char *name, int nval, char *values); +int parser_get_opt_param_int_array(struct csds_params *params, const char *name, + int nval, int *values); +int parser_get_opt_param_float_array(struct csds_params *params, + const char *name, int nval, float *values); +int parser_get_opt_param_double_array(struct csds_params *params, + const char *name, int nval, + double *values); +int parser_get_opt_param_longlong_array(struct csds_params *params, + const char *name, int nval, + long long *values); + +#endif /* CSDS_PARSER_H */ diff --git a/src/csds_part_type.c b/src/csds_part_type.c new file mode 100644 index 0000000..8d7c770 --- /dev/null +++ b/src/csds_part_type.c @@ -0,0 +1,24 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 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/>. + * + ******************************************************************************/ + +/* This object's header. */ +#include "csds_part_type.h" + +const char* part_type_names[csds_type_count] = { + "Gas", "DM", "DMBackground", "Sink", "Stars", "BH", "Neutrino"}; diff --git a/src/csds_part_type.h b/src/csds_part_type.h new file mode 100644 index 0000000..e9a3572 --- /dev/null +++ b/src/csds_part_type.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * This file is part of CSDS and was copied from SWIFT. + * Copyright (c) 2021 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 CSDS_PART_TYPES_H +#define CSDS_PART_TYPES_H + +/** + * @brief The different types of particles a #gpart can link to. + * + * Note we use the historical values from Gadget for these fields. + */ +enum part_type { + csds_type_gas = 0, + csds_type_dark_matter = 1, + csds_type_dark_matter_background = 2, + csds_type_sink = 3, + csds_type_stars = 4, + csds_type_black_hole = 5, + csds_type_neutrino = 6, + csds_type_count +} __attribute__((packed)); + +extern const char* part_type_names[]; + +#endif /* CSDS_PART_TYPES_H */ diff --git a/src/csds_particle.h b/src/csds_particle.h index f085bdc..ae443a2 100644 --- a/src/csds_particle.h +++ b/src/csds_particle.h @@ -23,6 +23,7 @@ #include "csds_tools.h" /* Include the other local files. */ +#include "csds_definitions.h" #include "csds_header.h" #include "csds_interpolation.h" #include "csds_loader_io.h" @@ -73,7 +74,7 @@ __attribute__((always_inline)) INLINE static size_t csds_particle_read_field( error_python("Cannot read a particle from timestep record."); } -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (derivative == 1 && field_read->first.field == field_enum_none) { error("Trying to read the non existing first derivative."); } @@ -88,7 +89,7 @@ __attribute__((always_inline)) INLINE static size_t csds_particle_read_field( : derivative == 1 ? field_read->first.position : field_read->second.position; -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (position < 0) { error("Position not set (derivative %i of %s)", derivative, field_enum_get_name(field_read->field.field)); @@ -135,8 +136,8 @@ INLINE static enum csds_special_flags csds_unpack_flags_and_data( const enum csds_special_flags flag = ((logfile_data & 0xFF000000) >> 24); -#ifdef SWIFT_DEBUG_CHECKS - if (*part_type >= swift_type_count || *part_type < 0) { +#ifdef CSDS_DEBUG_CHECKS + if (*part_type >= csds_type_count || *part_type < 0) { error("Invalid particle type found (%i)", *part_type); } #endif @@ -196,7 +197,6 @@ csds_particle_interpolate_field(const double time_before, const struct csds_reader_field *restrict after, void *restrict output, const double time, const struct field_information *field, - enum part_type type, const struct csds_parameters *params) { switch (field->field.field) { diff --git a/src/csds_python_tools.h b/src/csds_python_tools.h index 755c196..868ca90 100644 --- a/src/csds_python_tools.h +++ b/src/csds_python_tools.h @@ -16,8 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_CSDS_PYTHON_TOOLS_H -#define SWIFT_CSDS_PYTHON_TOOLS_H +#ifndef CSDS_PYTHON_TOOLS_H +#define CSDS_PYTHON_TOOLS_H #include "../config.h" @@ -34,7 +34,7 @@ subfield in a numpy array */ struct csds_python_subfield { /* Name of the subfield */ - char name[STRING_SIZE]; + char name[CSDS_STRING_SIZE]; /* Offset in the bytes */ size_t offset; @@ -181,7 +181,7 @@ csds_python_tools_get_format_string(int typenum, int dimension) { } /* Now construct the complete string */ - char format[STRING_SIZE]; + char format[CSDS_STRING_SIZE]; if (dimension == 1) { strcpy(format, type_format); } else { @@ -194,4 +194,4 @@ csds_python_tools_get_format_string(int typenum, int dimension) { #endif // HAVE_PYTHON -#endif // SWIFT_CSDS_PYTHON_TOOLS_H +#endif // CSDS_PYTHON_TOOLS_H diff --git a/src/csds_python_wrapper.c b/src/csds_python_wrapper.c index 0fc382a..81d8123 100644 --- a/src/csds_python_wrapper.c +++ b/src/csds_python_wrapper.c @@ -68,9 +68,6 @@ typedef struct { /* Verbose level */ int verbose; - /* Number of threads to use */ - int number_threads; - /* Number of index file to write */ int number_index; @@ -78,7 +75,7 @@ typedef struct { int restart_init; /* Reader for each type of particles */ - PyParticleReader *part_reader[swift_type_count]; + PyParticleReader *part_reader[csds_type_count]; } PyObjectReader; @@ -105,7 +102,8 @@ static void Reader_dealloc(PyObjectReader *self) { * @brief Allocate the memory of a particle reader. */ __attribute__((always_inline)) INLINE static PyObject *particle_reader_new( - PyTypeObject *type, PyObject *args, PyObject *kwds) { + PyTypeObject *type, ATTR_UNUSED PyObject *args, + ATTR_UNUSED PyObject *kwds) { PyParticleReader *self = (PyParticleReader *)type->tp_alloc(type, 0); return (PyObject *)self; } @@ -113,15 +111,15 @@ __attribute__((always_inline)) INLINE static PyObject *particle_reader_new( /** * @brief Allocate the memory. */ -static PyObject *Reader_new(PyTypeObject *type, PyObject *args, - PyObject *kwds) { +static PyObject *Reader_new(PyTypeObject *type, ATTR_UNUSED PyObject *args, + ATTR_UNUSED PyObject *kwds) { PyObjectReader *self; self = (PyObjectReader *)type->tp_alloc(type, 0); self->ready = 0; self->basename = NULL; - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { self->part_reader[i] = NULL; } @@ -140,23 +138,20 @@ static int Reader_init(PyObjectReader *self, PyObject *args, PyObject *kwds) { /* input variables. */ char *basename = NULL; int verbose = 0; - int number_threads = 1; int number_index = 10; int restart_init = 0; /* List of keyword arguments. */ - static char *kwlist[] = {"basename", "verbose", "number_threads", - "number_index", "restart_init", NULL}; + static char *kwlist[] = {"basename", "verbose", "number_index", + "restart_init", NULL}; /* parse the arguments. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iiip", kwlist, &basename, - &verbose, &number_threads, &number_index, - &restart_init)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iip", kwlist, &basename, + &verbose, &number_index, &restart_init)) return -1; /* Copy the arguments */ self->verbose = verbose; - self->number_threads = number_threads; self->number_index = number_index; self->restart_init = restart_init; @@ -175,7 +170,7 @@ static int Reader_init(PyObjectReader *self, PyObject *args, PyObject *kwds) { * * <b>returns</b> tuple containing min and max time. */ -static PyObject *getTimeLimits(PyObject *self, PyObject *Py_UNUSED(ignored)) { +static PyObject *getTimeLimits(PyObject *self, ATTR_UNUSED PyObject *args) { if (!((PyObjectReader *)self)->ready) { error_python( "The CSDS is not ready yet." @@ -204,7 +199,7 @@ static PyObject *getTimeLimits(PyObject *self, PyObject *Py_UNUSED(ignored)) { * * <b>returns</b> tuple containing the box size. */ -static PyObject *get_box_size(PyObject *self, PyObject *Py_UNUSED(ignored)) { +static PyObject *get_box_size(PyObject *self, ATTR_UNUSED PyObject *args) { if (!((PyObjectReader *)self)->ready) { error_python( "The CSDS is not ready yet." @@ -236,8 +231,7 @@ static PyObject *get_box_size(PyObject *self, PyObject *Py_UNUSED(ignored)) { */ __attribute__((always_inline)) INLINE static PyObject * csds_loader_create_output(void **output, const enum field_enum *fields, - const int n_fields, const uint64_t *n_part, - uint64_t n_tot) { + const int n_fields, uint64_t n_tot) { /* Create the python dictionary */ PyObject *dict = PyDict_New(); @@ -324,18 +318,18 @@ csds_loader_create_output(void **output, const enum field_enum *fields, return dict; } -static PyObject *pyEnter(__attribute__((unused)) PyObject *self, - PyObject *args) { +static PyObject *pyEnter(ATTR_UNUSED PyObject *self, + ATTR_UNUSED PyObject *args) { PyObjectReader *self_reader = (PyObjectReader *)self; csds_reader_init(&self_reader->reader, self_reader->basename, - self_reader->verbose, self_reader->number_threads, - self_reader->number_index, self_reader->restart_init); + self_reader->verbose, self_reader->number_index, + self_reader->restart_init); self_reader->ready = 1; /* Allocate the particle readers */ PyObject *args_init = PyTuple_New(0); - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { PyParticleReader *part_reader = (PyParticleReader *)PyObject_CallObject( (PyObject *)&PyParticleReader_Type, args_init); @@ -353,8 +347,8 @@ static PyObject *pyEnter(__attribute__((unused)) PyObject *self, return self; } -static PyObject *pyExit(__attribute__((unused)) PyObject *self, - PyObject *args) { +static PyObject *pyExit(ATTR_UNUSED PyObject *self, + ATTR_UNUSED PyObject *args) { PyObjectReader *self_reader = (PyObjectReader *)self; if (!self_reader->ready) { error_python("It seems that the reader was not initialized"); @@ -363,7 +357,7 @@ static PyObject *pyExit(__attribute__((unused)) PyObject *self, self_reader->ready = 0; /* Do the same for the particle readers */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { self_reader->part_reader[i]->reader = NULL; Py_DecRef((PyObject *)self_reader->part_reader[i]); } @@ -371,8 +365,8 @@ static PyObject *pyExit(__attribute__((unused)) PyObject *self, Py_RETURN_NONE; } -static PyObject *pyGetListFields(__attribute__((unused)) PyObject *self, - PyObject *args, PyObject *kwds) { +static PyObject *pyGetListFields(ATTR_UNUSED PyObject *self, PyObject *args, + PyObject *kwds) { PyObjectReader *self_reader = (PyObjectReader *)self; if (!self_reader->ready) { error_python( @@ -391,17 +385,17 @@ static PyObject *pyGetListFields(__attribute__((unused)) PyObject *self, return NULL; /* Get the type of particles to read. */ - int read_types[swift_type_count] = {0}; + int read_types[csds_type_count] = {0}; /* By default, we read everything */ if (types == Py_None) { - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { read_types[i] = 1; } } /* Deal with the case of a single int. */ else if (PyLong_Check(types)) { const size_t type = PyLong_AsSize_t(types); - if (type >= swift_type_count) { + if (type >= csds_type_count) { error_python("Unexpected particle type %zi", type); } read_types[type] = 1; @@ -412,7 +406,7 @@ static PyObject *pyGetListFields(__attribute__((unused)) PyObject *self, for (size_t i = 0; i < size; i++) { PyObject *cur = PyList_GetItem(types, i); const size_t type = PyLong_AsSize_t(cur); - if (type >= swift_type_count) { + if (type >= csds_type_count) { error_python("Unexpected particle type %zi", type); } read_types[type] = 1; @@ -434,7 +428,7 @@ static PyObject *pyGetListFields(__attribute__((unused)) PyObject *self, field_present[i] = 1; /* Check all the types */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { /* Skip the types that are not required and the field not found */ if (read_types[type] == 0 || field_present[i] == 0) continue; @@ -484,7 +478,7 @@ static PyObject *pyGetListFields(__attribute__((unused)) PyObject *self, /** * @brief shortcut to PyGetListFields for a single particle type */ -static PyObject *pyParticleGetListFields(__attribute__((unused)) PyObject *self, +static PyObject *pyParticleGetListFields(ATTR_UNUSED PyObject *self, PyObject *args, PyObject *kwds) { PyParticleReader *part_reader = (PyParticleReader *)self; PyObject *reader = (PyObject *)part_reader->reader; @@ -548,11 +542,11 @@ void pyGetData_CheckInput(PyObject *fields, PyObject *types, PyObject *part_ids, /* Check each element */ const size_t size = PyList_Size(part_ids); - if (size != swift_type_count) { + if (size != csds_type_count) { error_python( "The list of ids should contain %i elements (one for each particle " "type)", - swift_type_count); + csds_type_count); } for (size_t i = 0; i < size; i++) { PyArrayObject *el = (PyArrayObject *)PyList_GetItem(part_ids, i); @@ -575,14 +569,14 @@ void pyGetData_CheckInput(PyObject *fields, PyObject *types, PyObject *part_ids, /* Get the type of particles to read. */ /* By default, we read everything */ if (types == Py_None) { - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { read_types[i] = 1; } } /* Deal with the case of a single int. */ else if (PyLong_Check(types)) { const size_t type = PyLong_AsSize_t(types); - if (type >= swift_type_count) { + if (type >= csds_type_count) { error_python("Unexpected particle type %zi", type); } read_types[type] = 1; @@ -593,7 +587,7 @@ void pyGetData_CheckInput(PyObject *fields, PyObject *types, PyObject *part_ids, for (size_t i = 0; i < size; i++) { PyObject *cur = PyList_GetItem(types, i); const size_t type = PyLong_AsSize_t(cur); - if (type >= swift_type_count) { + if (type >= csds_type_count) { error_python("Unexpected particle type %zi", type); } read_types[type] = 1; @@ -612,8 +606,8 @@ void pyGetData_CheckInput(PyObject *fields, PyObject *types, PyObject *part_ids, * * @return Dictionary of numpy array containing the fields requested. */ -static PyObject *pyGetData(__attribute__((unused)) PyObject *self, - PyObject *args, PyObject *kwds) { +static PyObject *pyGetData(ATTR_UNUSED PyObject *self, PyObject *args, + PyObject *kwds) { PyObjectReader *self_reader = (PyObjectReader *)self; if (!self_reader->ready) { @@ -637,7 +631,7 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, &part_ids, &types)) return NULL; - int read_types[swift_type_count] = {0}; + int read_types[csds_type_count] = {0}; pyGetData_CheckInput(fields, types, part_ids, read_types); /* initialize the reader. */ @@ -666,14 +660,14 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, csds_reader_set_time(reader, time); /* Get the number of particles. */ - uint64_t n_part[swift_type_count]; + uint64_t n_part[csds_type_count]; if (part_ids == Py_None) { /* If no ids provided, read everything from the index files */ csds_reader_get_number_particles(reader, n_part, read_types); } /* If the ids are provided, use them to get the number of particles */ else { - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { PyObject *el = PyList_GetItem(part_ids, i); n_part[i] = el == Py_None ? 0 : PyArray_Size(el); } @@ -681,7 +675,7 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, /* Count the total number of particles. */ uint64_t n_tot = 0; - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { n_tot += n_part[i]; } @@ -711,9 +705,9 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, } else { /* Get the arrays in a C compatible way */ - PyObject *list[swift_type_count] = {NULL}; - long long *c_part_ids[swift_type_count]; - for (int i = 0; i < swift_type_count; i++) { + PyObject *list[csds_type_count] = {NULL}; + long long *c_part_ids[csds_type_count]; + for (int i = 0; i < csds_type_count; i++) { PyObject *el = PyList_GetItem(part_ids, i); /* Check if a list is provided */ @@ -740,7 +734,7 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, /* Free the memory and recompute n_tot */ n_tot = 0; - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { if (list[i] != NULL) Py_DECREF(list[i]); n_tot += n_part[i]; @@ -749,7 +743,7 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, /* Create the python output. */ PyObject *array = - csds_loader_create_output(output, fields_wanted, n_fields, n_part, n_tot); + csds_loader_create_output(output, fields_wanted, n_fields, n_tot); /* Free the reader. */ free(fields_wanted); @@ -760,8 +754,8 @@ static PyObject *pyGetData(__attribute__((unused)) PyObject *self, /** * @brief Shortcut to #pyGetData for a single particle type. */ -static PyObject *pyParticleGetData(__attribute__((unused)) PyObject *self, - PyObject *args, PyObject *kwds) { +static PyObject *pyParticleGetData(ATTR_UNUSED PyObject *self, PyObject *args, + PyObject *kwds) { PyParticleReader *part_reader = (PyParticleReader *)self; PyObject *reader = (PyObject *)part_reader->reader; @@ -792,8 +786,8 @@ static PyObject *pyParticleGetData(__attribute__((unused)) PyObject *self, error("You need to provide a numpy array of ids"); } - part_ids_list = PyList_New(swift_type_count); - for (int i = 0; i < swift_type_count; i++) { + part_ids_list = PyList_New(csds_type_count); + for (int i = 0; i < csds_type_count; i++) { PyList_SetItem(part_ids_list, i, Py_None); } PyList_SetItem(part_ids_list, part_reader->type, part_ids); @@ -918,8 +912,6 @@ static PyTypeObject PyObjectReader_Type = { " Basename of the logfile.\n" "verbose: int (optional)\n" " Verbose level\n\n" - "number_threads: int (optional)\n" - " Number of threads to use.\n\n" "number_index: int (optional)\n" " Number of index files to generate\n\n" "restart_init: bool (optional)\n" @@ -997,7 +989,7 @@ PyMODINIT_FUNC PyInit_libcsds(void) { m = PyModule_Create(&libcsdsmodule); if (m == NULL) return NULL; - /* Deal with SWIFT clock */ + /* Deal with CSDS clock */ clocks_set_cpufreq(0); /* Prepare the classes */ @@ -1015,13 +1007,13 @@ PyMODINIT_FUNC PyInit_libcsds(void) { /* Add the reader object */ PyModule_AddObject(m, "Reader", (PyObject *)&PyObjectReader_Type); - PyModule_AddIntConstant(m, "gas", swift_type_gas); - PyModule_AddIntConstant(m, "dark_matter", swift_type_dark_matter); + PyModule_AddIntConstant(m, "gas", csds_type_gas); + PyModule_AddIntConstant(m, "dark_matter", csds_type_dark_matter); PyModule_AddIntConstant(m, "dark_matter_background", - swift_type_dark_matter_background); - PyModule_AddIntConstant(m, "sinks", swift_type_sink); - PyModule_AddIntConstant(m, "stars", swift_type_stars); - PyModule_AddIntConstant(m, "black_holes", swift_type_black_hole); + csds_type_dark_matter_background); + PyModule_AddIntConstant(m, "sinks", csds_type_sink); + PyModule_AddIntConstant(m, "stars", csds_type_stars); + PyModule_AddIntConstant(m, "black_holes", csds_type_black_hole); /* Import numpy. */ import_array(); diff --git a/src/csds_reader.c b/src/csds_reader.c index 03613b3..a0e20be 100644 --- a/src/csds_reader.c +++ b/src/csds_reader.c @@ -22,30 +22,24 @@ /* Include CSDS headers */ #include "csds_fields.h" +#include "csds_openmp.h" #include "csds_parameters.h" /* Include standard library */ #include <sys/sysinfo.h> #include <unistd.h> -/* Include local headers */ -#include "threadpool.h" - -#define nr_threads get_nprocs() - /** * @brief Initialize the reader. * * @param reader The #csds_reader. * @param basename The basename of the csds files. * @param verbose The verbose level. - * @param number_threads The number of threads to use. * @param number_index The number of index files to write. * @param restart_init Are we restarting the initial reading? */ void csds_reader_init(struct csds_reader *reader, const char *basename, - int verbose, int number_threads, int number_index, - int restart_init) { + int verbose, int number_index, int restart_init) { if (verbose > 1) message("Initializing the reader."); /* Set the variable to the default values */ @@ -58,10 +52,10 @@ void csds_reader_init(struct csds_reader *reader, const char *basename, /* Initialize the reader variables. */ reader->verbose = verbose; - csds_parameters_init(&reader->params, reader, number_threads); + csds_parameters_init(&reader->params, reader); /* Generate the logfile filename */ - char logfile_name[STRING_SIZE]; + char logfile_name[CSDS_STRING_SIZE]; sprintf(logfile_name, "%s.dump", basename); /* Initialize the log file. */ @@ -93,7 +87,7 @@ void csds_reader_init_index(struct csds_reader *reader, int number_index, /* Count the number of files */ int count = 0; while (1) { - char filename[STRING_SIZE + 50]; + char filename[CSDS_STRING_SIZE + 50]; sprintf(filename, "%s_%04i.index", reader->basename, count); /* Check if file exists */ @@ -136,7 +130,7 @@ void csds_reader_init_index(struct csds_reader *reader, int number_index, /* Get the information contained in the headers */ for (int i = 0; i < reader->index.n_files; i++) { - char filename[STRING_SIZE + 50]; + char filename[CSDS_STRING_SIZE + 50]; sprintf(filename, "%s_%04i.index", reader->basename, i); /* Read the header */ @@ -200,9 +194,9 @@ void csds_reader_set_time(struct csds_reader *reader, double time) { } /* Generate the filename */ - char filename_prev[STRING_SIZE + 50]; + char filename_prev[CSDS_STRING_SIZE + 50]; sprintf(filename_prev, "%s_%04u.index", reader->basename, left); - char filename_next[STRING_SIZE + 50]; + char filename_next[CSDS_STRING_SIZE + 50]; sprintf(filename_next, "%s_%04u.index", reader->basename, left + 1); /* Check if the file is already mapped */ @@ -353,7 +347,7 @@ size_t csds_reader_count_number_removed_particles(struct csds_reader *reader, void csds_reader_get_number_particles(struct csds_reader *reader, uint64_t *n_parts, const int *read_types) { - for (enum part_type i = (enum part_type)0; i < swift_type_count; i++) { + for (enum part_type i = (enum part_type)0; i < csds_type_count; i++) { /* Should we skip this type of particles? */ if (read_types[i] == 0) { n_parts[i] = 0; @@ -397,7 +391,7 @@ void csds_reader_get_number_particles(struct csds_reader *reader, * * @return Is the particle removed from the logfile? */ -int csds_reader_read_field(struct csds_reader *reader, double time, +int csds_reader_read_field(const struct csds_reader *reader, double time, size_t offset_time, enum csds_reader_type interp_type, const size_t offset_last_full_record, @@ -437,7 +431,7 @@ int csds_reader_read_field(struct csds_reader *reader, double time, enum csds_special_flags flag = csds_particle_read_special_flag( offset, &mask, &h_offset, &data, &part_type, reader->log.log.map); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (part_type != type) { error("Found particles that do not correspond."); } @@ -578,7 +572,7 @@ int csds_reader_read_field(struct csds_reader *reader, double time, /* Interpolate the data. */ csds_particle_interpolate_field(time_before, &before, time_after, &after, - output, time, field_wanted, type, + output, time, field_wanted, &reader->params); } return 0; @@ -630,162 +624,44 @@ void csds_reader_get_fields_wanted( } /** - * @brief Structure for the mapper function - * #csds_reader_read_all_particles_single_type_mapper. - */ -struct extra_data_read_all { - - /*! The #csds_reader. */ - struct csds_reader *reader; - - /*! The requested time. */ - double time; - - /*! The interpolation type. */ - enum csds_reader_type interp_type; - - /*! The #field_information for all the fields requested. */ - const struct field_information **fields_wanted; - - /*! The number of fields requested. */ - int n_fields_wanted; - - /*! The array of output arrays. */ - void **output; - - /*! The number of particles at the requested time. */ - const uint64_t *n_part; - - /*! The type of particle to read. */ - enum part_type type; - - /*! The current position in the index file. */ - size_t current_index; - - /*! The current position in the output arrays. */ - size_t current_output; - - /*! The number of jumps done in order to find the particles */ - long int total_number_jumps; -}; - -/** - * @brief Mapper function of #csds_reader_read_all_particles_single_type. + * @brief Read a single particle. * - * @param map_data (size_t) The current position (not used) - * @param num_elements The number of elements to process with the current - * thread. - * @param extra_data Pointer to a #extra_data_read_all. + * @param reader The #csds_reader + * @param interp_type The type of interpolation + * @param fields_wanted The list of fields to return. + * @param n_fields_wanted The number of fields to return + * @param output The pointers to the output arrays + * @param offset The offset of the particle to read + * @param output_index The index of the particle within the output arrays + * @param type The type of particle + * @parma number_jumps (out) The number of jumps required to get the particle */ -void csds_reader_read_all_particles_single_type_mapper(void *map_data, - int num_elements, - void *extra_data) { - - /* Extract the data */ - struct extra_data_read_all *data_tp = - (struct extra_data_read_all *)extra_data; - struct csds_reader *reader = data_tp->reader; - double time = data_tp->time; - enum csds_reader_type interp_type = data_tp->interp_type; - const struct field_information **fields_wanted = data_tp->fields_wanted; - int n_fields_wanted = data_tp->n_fields_wanted; - void **output = data_tp->output; - const uint64_t *n_part = data_tp->n_part; - enum part_type type = data_tp->type; - - /* Create a few variables for later */ - const size_t size_index = reader->index.index_prev.nparts[type]; - const size_t size_history = - csds_reader_count_number_new_particles(reader, type); - - struct index_data *data = - csds_index_get_data(&reader->index.index_prev, type); - struct index_data *data_created = - csds_index_get_created_history(&reader->index.index_next, type); - - /* Count the number of previous parts for the shift in output */ - uint64_t prev_npart = 0; - for (int i = 0; i < type; i++) { - prev_npart += n_part[i]; - } - - /* Allocate the temporary memory. */ - void **output_tmp = malloc(n_fields_wanted * sizeof(void *)); - if (output_tmp == NULL) { - error_python("Failed to allocate the temporary output buffer"); - } - for (int i = 0; i < n_fields_wanted; i++) { - output_tmp[i] = malloc(fields_wanted[i]->field.size); - if (output_tmp[i] == NULL) { - error_python("Failed to allocate the temporary output buffer"); - } - } - - /* Read the particles */ - long int total_number_jumps = 0; - for (int i = 0; i < num_elements; i++) { - - /* Get the next index. */ - size_t current_index = atomic_inc(&data_tp->current_index); - - /* Are we reading the history? */ - int reading_history = current_index >= size_index; - - /* Check if we still have some particles available. */ - if (reading_history && current_index == size_history + size_index) { - error_python("The CSDS was not able to find enough particles."); - } - - /* Get the offset */ - const size_t current = - reading_history ? current_index - size_index : current_index; - size_t offset = - reading_history ? data_created[current].offset : data[current].offset; - - /* Loop over each field. */ - int particle_removed = 0; - for (int field = 0; field < n_fields_wanted; field++) { - - /* Read the field. */ - int number_jumps = 0; - particle_removed = csds_reader_read_field( - reader, time, reader->time.time_offset, interp_type, offset, - fields_wanted[field], output_tmp[field], type, &number_jumps); - - /* Should we continue to read the fields of this particle? */ - if (particle_removed) { - i--; - break; - } - - total_number_jumps += number_jumps; - } - - /* Write the particle into the output if it was not removed */ - if (!particle_removed) { - size_t current_output = atomic_inc(&data_tp->current_output); - for (int field = 0; field < n_fields_wanted; field++) { - - /* Get the array */ - const size_t size = fields_wanted[field]->field.size; - void *output_single = - (char *)output[field] + (current_output + prev_npart) * size; - - /* Copy the temporary buffer into the global one */ - memcpy(output_single, output_tmp[field], size); - } +int csds_reader_read_particle(const struct csds_reader *reader, double time, + enum csds_reader_type interp_type, + const struct field_information **fields_wanted, + int n_fields_wanted, void **output, size_t offset, + size_t output_index, enum part_type type, + int *number_jumps) { + + /* Loop over each field. */ + // TODO cache the last full offset + for (int field = 0; field < n_fields_wanted; field++) { + const struct field_information *current_field = fields_wanted[field]; + void *current_output = + output[field] + output_index * current_field->field.size; + + /* Read the field. */ + int particle_removed = csds_reader_read_field( + reader, time, reader->time.time_offset, interp_type, offset, + current_field, current_output, type, number_jumps); + + /* Should we continue to read the fields of this particle? */ + if (particle_removed) { + return 1; } } - /* Free the allocated memory */ - for (int i = 0; i < n_fields_wanted; i++) { - free(output_tmp[i]); - } - free(output_tmp); - - /* Write the number of jumps */ - __atomic_add_fetch(&data_tp->total_number_jumps, total_number_jumps, - __ATOMIC_RELAXED); + return 0; } /** @@ -818,41 +694,102 @@ void csds_reader_read_all_particles_single_type( csds_reader_get_fields_wanted(reader, fields_wanted, fields_info_wanted, n_fields_wanted, type); - /* Compact the data */ - struct extra_data_read_all extra = {reader, - time, - interp_type, - fields_info_wanted, - n_fields_wanted, - output, - n_part, - type, - /* current_index */ 0, - /* current_output */ 0, - /* total_number_jumps */ 0}; - - /* Create the threadpool */ - struct threadpool tp; - threadpool_init(&tp, reader->params.number_threads); + /* Count the number of previous parts for the shift in output */ + uint64_t prev_npart = 0; + for (int i = 0; i < type; i++) { + prev_npart += n_part[i]; + } + + /* Create some information from the index files */ + const size_t size_index = reader->index.index_prev.nparts[type]; + const size_t size_history = + csds_reader_count_number_new_particles(reader, type); + + struct index_data *data = + csds_index_get_data(&reader->index.index_prev, type); + struct index_data *data_created = + csds_index_get_created_history(&reader->index.index_next, type); + + /* Current index to read */ + size_t current_index = 0; + size_t particles_read = 0; + int local_counter = 0; + size_t total_number_jumps = 0; + const int local_update_limit = 1000; + const ticks tic2 = getticks(); /* Read the particles */ - threadpool_map(&tp, csds_reader_read_all_particles_single_type_mapper, NULL, - n_part[type], 1, threadpool_auto_chunk_size, &extra); +#pragma omp parallel for firstprivate(local_counter) reduction(+:total_number_jumps) + for (size_t i = 0; i < n_part[type]; i++) { + + /* Try to find a particle that is not removed */ + int particle_removed = 1; + while (particle_removed) { + + size_t local_index = 0; +#pragma omp atomic capture + local_index = current_index++; + + /* Are we reading the history? */ + int reading_history = local_index >= size_index; + + /* Check if we still have some particles available. */ + if (reading_history && local_index == size_history + size_index) { + error_python("The CSDS was not able to find enough particles."); + } + + /* Get the offset */ + const size_t current = + reading_history ? local_index - size_index : local_index; + size_t offset = + reading_history ? data_created[current].offset : data[current].offset; + + /* Get the offset of the particle */ + int number_jumps = 0; + particle_removed = csds_reader_read_particle( + reader, time, interp_type, fields_info_wanted, n_fields_wanted, + output, offset, i, type, &number_jumps); + + /* Update the number of jumps */ + if (!particle_removed) { + total_number_jumps += number_jumps; + } + } - /* Check if we have all the expected particles. */ - if (n_part[type] != extra.current_output) { - error("The reader was not able to find all the expected particles."); + /* Do we need to print something? */ + if (!(reader->verbose > 0)) continue; + + /* Update the counters */ + local_counter++; + if (local_counter < local_update_limit) continue; + + /* Add the local counter to the global one */ +#pragma omp atomic + particles_read += local_counter; + local_counter = 0; + + /* Only the master is printing */ + int current_thread = csds_get_thread_num(); + if (current_thread != 0) continue; + + float ratio = (float)particles_read / (float)n_part[type]; + + /* Compute the remaining time */ + const int delta_time = clocks_diff_ticks(getticks(), tic2) / 1000.0; + const int remaining_time = delta_time * (1. - ratio) / ratio; + + /* Print the message */ + tools_print_progress(100 * ratio, remaining_time, "Reading particles"); } /* Print the number of jumps if required */ - if (reader->verbose > 1 || reader->verbose == CSDS_VERBOSE_TIMERS) { - float avg = (float)extra.total_number_jumps / - (float)(n_fields_wanted * n_part[type]); + if (reader->verbose > 0 || reader->verbose == CSDS_VERBOSE_TIMERS) { + float avg = + (float)total_number_jumps / (float)(n_fields_wanted * n_part[type]); message("Average number of jumps for %s: %.1f", part_type_names[type], avg); } /* Free the memory */ - threadpool_clean(&tp); free(fields_info_wanted); } @@ -876,35 +813,35 @@ void csds_reader_read_all_particles(struct csds_reader *reader, double time, const ticks tic = getticks(); /* Read the gas */ - if (n_part[swift_type_gas] != 0) { + if (n_part[csds_type_gas] != 0) { csds_reader_read_all_particles_single_type(reader, time, interp_type, fields_wanted, n_fields_wanted, - output, n_part, swift_type_gas); + output, n_part, csds_type_gas); } /* Read the dark matter. */ - if (n_part[swift_type_dark_matter] != 0) { + if (n_part[csds_type_dark_matter] != 0) { csds_reader_read_all_particles_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_dark_matter); + n_part, csds_type_dark_matter); } /* Read the dark matter background. */ - if (n_part[swift_type_dark_matter_background] != 0) { + if (n_part[csds_type_dark_matter_background] != 0) { csds_reader_read_all_particles_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_dark_matter_background); + n_part, csds_type_dark_matter_background); } /* Read the neutrino dark matter. */ - if (n_part[swift_type_neutrino] != 0) { + if (n_part[csds_type_neutrino] != 0) { csds_reader_read_all_particles_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_neutrino); + n_part, csds_type_neutrino); } /* Read the stars. */ - if (n_part[swift_type_stars] != 0) { - csds_reader_read_all_particles_single_type( - reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_stars); + if (n_part[csds_type_stars] != 0) { + csds_reader_read_all_particles_single_type(reader, time, interp_type, + fields_wanted, n_fields_wanted, + output, n_part, csds_type_stars); } /* Print the time */ @@ -1026,30 +963,30 @@ void csds_reader_read_particles_from_ids( const ticks tic = getticks(); /* Read the gas */ - if (n_part[swift_type_gas] != 0) { + if (n_part[csds_type_gas] != 0) { csds_reader_read_particles_from_ids_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_gas, ids[swift_type_gas]); + n_part, csds_type_gas, ids[csds_type_gas]); } /* Read the dark matter. */ - if (n_part[swift_type_dark_matter] != 0) { + if (n_part[csds_type_dark_matter] != 0) { csds_reader_read_particles_from_ids_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_dark_matter, ids[swift_type_dark_matter]); + n_part, csds_type_dark_matter, ids[csds_type_dark_matter]); } /* Read the dark matter background. */ - if (n_part[swift_type_dark_matter_background] != 0) { + if (n_part[csds_type_dark_matter_background] != 0) { csds_reader_read_particles_from_ids_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_dark_matter_background, - ids[swift_type_dark_matter_background]); + n_part, csds_type_dark_matter_background, + ids[csds_type_dark_matter_background]); } /* Read the stars. */ - if (n_part[swift_type_stars] != 0) { + if (n_part[csds_type_stars] != 0) { csds_reader_read_particles_from_ids_single_type( reader, time, interp_type, fields_wanted, n_fields_wanted, output, - n_part, swift_type_stars, ids[swift_type_stars]); + n_part, csds_type_stars, ids[csds_type_stars]); } /* Print the time */ @@ -1131,11 +1068,11 @@ size_t csds_reader_read_record(struct csds_reader *reader, void **output, /* The record is a particle. */ if (*is_particle) { size_t offset_tmp = offset; - for (int i = 0; i < h->number_fields[swift_type_gas]; i++) { + for (int i = 0; i < h->number_fields[csds_type_gas]; i++) { offset = csds_particle_read_field( - offset_tmp, output[i], &h->fields[swift_type_gas][i], - /* derivative */ 0, &mask, &h_offset, h->fields[swift_type_gas], - h->number_fields[swift_type_gas], reader->log.log.map); + offset_tmp, output[i], &h->fields[csds_type_gas][i], + /* derivative */ 0, &mask, &h_offset, h->fields[csds_type_gas], + h->number_fields[csds_type_gas], reader->log.log.map); } } /* The record is a timestamp. */ diff --git a/src/csds_reader.h b/src/csds_reader.h index 4b8ab14..a185315 100644 --- a/src/csds_reader.h +++ b/src/csds_reader.h @@ -65,7 +65,7 @@ struct csds_reader { /* Base name of the files */ - char basename[STRING_SIZE]; + char basename[CSDS_STRING_SIZE]; struct { /* Information contained in the previous index file. */ @@ -118,8 +118,7 @@ enum csds_reader_event { void csds_reader_init_index(struct csds_reader *reader, int number_index, int current_index); void csds_reader_init(struct csds_reader *reader, const char *basename, - int verbose, int number_threads, int number_index, - int restart_init); + int verbose, int number_index, int restart_init); void csds_reader_free(struct csds_reader *reader); void csds_reader_set_time(struct csds_reader *reader, double time); diff --git a/src/csds_reader_generate_index.c b/src/csds_reader_generate_index.c index 19e05f5..dbe4e32 100644 --- a/src/csds_reader_generate_index.c +++ b/src/csds_reader_generate_index.c @@ -24,6 +24,8 @@ #include "csds_hashmap.h" #include "csds_index.h" #include "csds_logfile.h" +#include "csds_openmp.h" +#include "csds_part_type.h" /** * @brief Structure that contains all the information @@ -126,7 +128,7 @@ void index_writer_log(struct index_writer *writer, const int64_t id, "please increase the value of ReaderOptions:Number*."); } -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (id < 0) { error("Negative ID for a particle."); } @@ -163,9 +165,9 @@ void index_writer_log(struct index_writer *writer, const int64_t id, *@brief Check if we have some unimplemented particles. */ void index_writer_check_implemented(const struct index_writer *writers) { - if (writers[swift_type_sink].size != 0 || - writers[swift_type_black_hole].size != 0 || - writers[swift_type_neutrino].size != 0) { + if (writers[csds_type_sink].size != 0 || + writers[csds_type_black_hole].size != 0 || + writers[csds_type_neutrino].size != 0) { error("TODO implement additional particle types"); } } @@ -176,14 +178,14 @@ void index_writer_check_implemented(const struct index_writer *writers) { void index_writer_write_in_index(const struct index_writer *writers, FILE *f) { /* Write the number of particles */ - uint64_t N_total[swift_type_count]; - for (int type = 0; type < swift_type_count; type++) { + uint64_t N_total[csds_type_count]; + for (int type = 0; type < csds_type_count; type++) { N_total[type] = writers[type].size; } - fwrite(N_total, sizeof(uint64_t), swift_type_count, f); + fwrite(N_total, sizeof(uint64_t), csds_type_count, f); /* Write the index data */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { if (N_total[type] == 0) continue; fwrite(writers[type].data, sizeof(struct index_data), writers[type].size, @@ -196,13 +198,13 @@ void index_writer_write_in_index(const struct index_writer *writers, FILE *f) { * * @param reader The #csds_reader. * @param current_state The arrays containing the current - * state with the last full offset (size given by swift_type_count). + * state with the last full offset (size given by csds_type_count). * @param parts_created The arrays containing the first reference * (since last index file) of created particles - * (size given by swift_type_count). + * (size given by csds_type_count). * @param parts_removed The arrays containing the last reference * (since last index file) of removed particles (size given by - * swift_type_count). + * csds_type_count). * @param time The time corresponding to the current index file. * @param file_number The current file number. */ @@ -213,13 +215,13 @@ void csds_reader_write_index(const struct csds_reader *reader, const struct time_record *time, int file_number) { /* Get the filename */ - char filename[STRING_SIZE + 15]; + char filename[CSDS_STRING_SIZE + 15]; sprintf(filename, "%s_%04i.index", reader->basename, file_number); /* Check that we have only implemented particles */ - if (csds_hashmap_count(current_state[swift_type_sink]) != 0 || - csds_hashmap_count(current_state[swift_type_black_hole]) != 0 || - csds_hashmap_count(current_state[swift_type_neutrino]) != 0) + if (csds_hashmap_count(current_state[csds_type_sink]) != 0 || + csds_hashmap_count(current_state[csds_type_black_hole]) != 0 || + csds_hashmap_count(current_state[csds_type_neutrino]) != 0) error("Not implemented"); index_writer_check_implemented(parts_created); index_writer_check_implemented(parts_removed); @@ -238,11 +240,11 @@ void csds_reader_write_index(const struct csds_reader *reader, fwrite(&time->int_time, sizeof(integertime_t), 1, f); /* Write number of particles */ - uint64_t N_total[swift_type_count]; - for (int type = 0; type < swift_type_count; type++) { + uint64_t N_total[csds_type_count]; + for (int type = 0; type < csds_type_count; type++) { N_total[type] = csds_hashmap_count(current_state[type]); } - fwrite(N_total, sizeof(uint64_t), swift_type_count, f); + fwrite(N_total, sizeof(uint64_t), csds_type_count, f); /* Write if the file is sorted */ // TODO change it if the array is sorted @@ -259,7 +261,7 @@ void csds_reader_write_index(const struct csds_reader *reader, } /* Write the current state */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { if (N_total[type] == 0) continue; // TODO memory map the file @@ -274,7 +276,7 @@ void csds_reader_write_index(const struct csds_reader *reader, fclose(f); /* Cleanup the arrays */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { index_writer_reset(&parts_created[type]); index_writer_reset(&parts_removed[type]); } @@ -285,7 +287,7 @@ void csds_reader_write_index(const struct csds_reader *reader, * * @param reader The #csds_reader. * @param current_state The arrays that will be updated with (size - * swift_type_count). + * csds_type_count). * @param time_record (output) The first #time_record in the logfile (for * writing the index file). * @@ -336,8 +338,8 @@ size_t csds_reader_get_initial_state(const struct csds_reader *reader, } /* TODO Implement missing particle types */ - if (part_type == swift_type_neutrino || part_type == swift_type_sink || - part_type == swift_type_black_hole) { + if (part_type == csds_type_neutrino || part_type == csds_type_sink || + part_type == csds_type_black_hole) { error("Particle type not implemented (%s)", part_type_names[part_type]); } @@ -381,135 +383,68 @@ size_t csds_reader_get_initial_state(const struct csds_reader *reader, } /** - * @brief Structure for #csds_reader_update_particles_to_next_index_mapper - */ -struct update_particle_data { - - /* The reader */ - const struct csds_reader *reader; - - /* The time offset to reach */ - size_t time_offset; - - /* Current percentage */ - float percentage; - - /* Total number of particles */ - size_t number_particles; - - /* Lock for printing */ - swift_lock_type print_lock; - - /* Time when starting the update. */ - int init_time; - - /* The hashmap to udpate */ - struct csds_hashmap *current_state; -}; - -/** - * @brief Mapper function of #csds_reader_update_particles_to_next_index. + * @brief Get the last offset of a record before a given offset. * - * This function loops over all the known particles and updates their offset - * until reaching the required one. + * @param reader The #csds_reader. + * @param data The #index_data of the particle. + * @param offset_limit The upper limit of the offset to get. * - * @param map_data The #index_data to process - * @param num_elements The number of elements in map_data. - * @param extra_data The extra parameters (#update_particle_data) + * @return The last offset before an index file. */ -void csds_reader_update_particles_to_next_index_mapper(void *map_data, - int num_elements, - void *extra_data) { +size_t csds_reader_get_last_offset_before(const struct csds_reader *reader, + const struct index_data *data, + size_t offset_limit) { - /* Get a few pointers */ - struct update_particle_data *data = (struct update_particle_data *)extra_data; - const struct csds_reader *reader = data->reader; + /* Get a the logfile */ const struct csds_logfile *log = &reader->log; - struct csds_hashmap *current_state = data->current_state; - /* Loop over the particles */ - for (int local = 0; local < num_elements; local++) { - size_t i = (size_t)map_data + local; - struct index_data *index_data = - csds_hashmap_get_from_position(current_state, i); + size_t current_offset = data->offset; - /* Did we get an item? */ - if (index_data == NULL) { - continue; - } - - size_t current_offset = index_data->offset; + /* Get the full mask */ + mask_type full_mask = 0; + size_t h_offset = 0; + csds_loader_io_read_mask(log->log.map + current_offset, &full_mask, + &h_offset); - /* Get the full mask */ - mask_type full_mask = 0; - size_t h_offset = 0; - csds_loader_io_read_mask(log->log.map + current_offset, &full_mask, - &h_offset); + /* Ensures that a special flag is present in the mask */ + full_mask |= SPECIAL_FLAGS_MASK; - /* Ensures that a special flag is present in the mask */ - full_mask |= SPECIAL_FLAGS_MASK; + /* Now remove it */ + full_mask = full_mask ^ SPECIAL_FLAGS_MASK; - /* Now remove it */ - full_mask = full_mask ^ SPECIAL_FLAGS_MASK; + /* Find the last offset before the current time */ + size_t last_full_offset = current_offset; + current_offset += h_offset; + while (1) { + /* Get the mask */ + mask_type mask = 0; + h_offset = 0; + csds_loader_io_read_mask(log->log.map + current_offset, &mask, &h_offset); - /* Find the last offset before the current time */ - size_t last_full_offset = current_offset; + /* update the offset */ current_offset += h_offset; - while (1) { - /* Get the mask */ - mask_type mask = 0; - h_offset = 0; - csds_loader_io_read_mask(log->log.map + current_offset, &mask, &h_offset); - - /* update the offset */ - current_offset += h_offset; - if (current_offset > data->time_offset) { - break; - } - - /* The particle should not have a special flag - due to the previous loop */ - if (mask & SPECIAL_FLAGS_MASK) { - error("Found a special flag when updating the particles."); - } - - /* Update the last full offset */ - if (full_mask == mask) { - last_full_offset = current_offset; - } - - /* Are we at the end of the file? */ - if (h_offset == 0) { - break; - } + if (current_offset > offset_limit) { + break; } - /* Update the offset */ - index_data->offset = last_full_offset; - } - - if (reader->verbose > 0) { - /* Update the counter */ - atomic_add_f(&data->percentage, - num_elements / (float)data->number_particles); - - /* Update the message */ - if (lock_trylock(&data->print_lock)) { - /* Get the current state */ - float percent = 0; - __atomic_load(&data->percentage, &percent, __ATOMIC_RELAXED); - percent *= 100.f; + /* The particle should not have a special flag + due to the previous loop */ + if (mask & SPECIAL_FLAGS_MASK) { + error("Found a special flag when updating the particles"); + } - /* Compute the remaining time */ - const int current_time = - clocks_diff_ticks(getticks(), clocks_start_ticks) / 1000.0; - const int remaining_time = - (current_time - data->init_time) * (100. - percent) / percent; + /* Update the last full offset */ + if (full_mask == mask) { + last_full_offset = current_offset; + } - /* Print the message */ - tools_print_progress(percent, remaining_time, "Updating offsets"); + /* Are we at the end of the file? */ + if (h_offset == 0) { + break; } } + + return last_full_offset; } /** @@ -520,11 +455,11 @@ void csds_reader_update_particles_to_next_index_mapper(void *map_data, * @param init_offset The initial offset to read. * @param time_record The #time_record of the next index file. * @param current_state The arrays containing the state of the simulation (size - * swift_type_count). + * csds_type_count). * @param parts_created The arrays containing the particles created since last - * index (size swift_type_count). + * index (size csds_type_count). * @param parts_removed The arrays containing the particles removed since last - * index (size swift_type_count). + * index (size csds_type_count). * * @return The starting offset for the update. */ @@ -595,7 +530,7 @@ size_t csds_reader_update_state_to_next_index( enum csds_special_flags flag = csds_particle_read_special_flag( old_offset, &mask, &h_offset, &data, &part_type, log->log.map); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (flag == csds_flag_none) { error( "A record should not have a mask " @@ -648,40 +583,63 @@ size_t csds_reader_update_state_to_next_index( message("Finding new/removed particles took %.3f %s", clocks_from_ticks(getticks() - tic), clocks_getunit()); - /* Create the threadpool */ - struct threadpool tp; - threadpool_init(&tp, reader->params.number_threads); - - /* Create the input */ - struct update_particle_data extra_data; - extra_data.reader = reader; - extra_data.time_offset = time_record.offset; - extra_data.init_time = - clocks_diff_ticks(getticks(), clocks_start_ticks) / 1000.0; - extra_data.percentage = 0.f; - extra_data.number_particles = 0; - - /* Initialize variables */ - if (lock_init(&extra_data.print_lock) != 0) - error("Failed to initialize the lock"); - - for (int type = 0; type < swift_type_count; type++) { - extra_data.number_particles += - csds_hashmap_get_number_buckets(current_state[type]); + /* Initialize the total number of particles for the progress bar */ + size_t total_number_particles = 0; + for (int type = 0; type < csds_type_count; type++) { + total_number_particles += csds_hashmap_count(current_state[type]); } /* Record the time */ const ticks tic2 = getticks(); + size_t current_number = 0; + int local_counter = 0; + const int local_update_limit = 1000; /* Update the offsets of current_state * No need to update the others as they contain * data about when particles are removed/created*/ - for (int type = 0; type < swift_type_count; type++) { - /* Update the offsets */ - extra_data.current_state = current_state[type]; - threadpool_map(&tp, csds_reader_update_particles_to_next_index_mapper, NULL, - csds_hashmap_get_number_buckets(extra_data.current_state), 1, - threadpool_auto_chunk_size, &extra_data); +#pragma omp parallel for firstprivate(local_counter) + for (int type = 0; type < csds_type_count; type++) { + /* Loop over the buckets ~ particles */ + size_t n_buckets = csds_hashmap_get_number_buckets(current_state[type]); + for (size_t i = 0; i < n_buckets; i++) { + struct index_data *index_data = + csds_hashmap_get_from_position(current_state[type], i); + + /* Did we get an item? */ + if (index_data == NULL) { + continue; + } + + /* Update the offset */ + index_data->offset = csds_reader_get_last_offset_before( + reader, index_data, time_record.offset); + + /* Are we done or should we print something? */ + if (!(reader->verbose > 0)) continue; + + /* Update the counters */ + local_counter++; + if (local_counter < local_update_limit) continue; + + /* Add the local counter to the global one */ +#pragma omp atomic + current_number += local_counter; + local_counter = 0; + + /* Only the master is printing */ + int current_thread = csds_get_thread_num(); + if (current_thread != 0) continue; + + float ratio = (float)current_number / (float)total_number_particles; + + /* Compute the remaining time */ + const int delta_time = clocks_diff_ticks(getticks(), tic2) / 1000.0; + const int remaining_time = delta_time * (1. - ratio) / ratio; + + /* Print the message */ + tools_print_progress(100 * ratio, remaining_time, "Updating offsets"); + } } /* Cleanup the output */ @@ -692,12 +650,6 @@ size_t csds_reader_update_state_to_next_index( message("Updating particles took %.3f %s", clocks_from_ticks(getticks() - tic2), clocks_getunit()); - /* Free the memory */ - threadpool_clean(&tp); - if (lock_destroy(&extra_data.print_lock) != 0) { - error("Failed to destroy the lock"); - } - return offset; } @@ -733,13 +685,13 @@ void csds_reader_generate_index_files(const struct csds_reader *reader, } /* Create the different arrays that will store the information */ - struct csds_hashmap *current_state[swift_type_count]; - struct index_writer parts_created[swift_type_count]; - struct index_writer parts_removed[swift_type_count]; + struct csds_hashmap *current_state[csds_type_count]; + struct index_writer parts_created[csds_type_count]; + struct index_writer parts_removed[csds_type_count]; const size_t default_size = 1024; /* Allocate the arrays for new / removed particles */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { index_writer_init(&parts_created[i], default_size, reader->params.arrays_maximal_tagged_fraction); index_writer_init(&parts_removed[i], default_size, @@ -758,7 +710,7 @@ void csds_reader_generate_index_files(const struct csds_reader *reader, if (current_index == 0) { /* Allocate the arrays for the current state */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { current_state[i] = csds_hashmap_new(hashmap_overallocation * reader->params.approximate_number_particles[i]); @@ -783,7 +735,7 @@ void csds_reader_generate_index_files(const struct csds_reader *reader, csds_index_init(&index, reader); /* Get its name */ - char filename[STRING_SIZE + 50]; + char filename[CSDS_STRING_SIZE + 50]; sprintf(filename, "%s_%04u.index", reader->basename, current_index - 1); /* Read it */ @@ -791,7 +743,7 @@ void csds_reader_generate_index_files(const struct csds_reader *reader, csds_index_map_file(&index, filename, /* sorted */ 1); /* Loop over all the particle types */ - for (int i = 0; i < swift_type_count; i++) { + for (int i = 0; i < csds_type_count; i++) { /* Allocate the array for the current state */ current_state[i] = csds_hashmap_new(hashmap_overallocation * index.nparts[i]); @@ -855,7 +807,7 @@ void csds_reader_generate_index_files(const struct csds_reader *reader, } /* Free the memory */ - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { csds_hashmap_free(current_state[type]); index_writer_free(&parts_created[type]); index_writer_free(&parts_removed[type]); diff --git a/src/csds_time.c b/src/csds_time.c index 7875af4..9853a89 100644 --- a/src/csds_time.c +++ b/src/csds_time.c @@ -171,7 +171,7 @@ int time_array_get_filename_savefile(const struct csds_logfile *log, */ void time_array_save(struct time_array *t, const struct csds_logfile *log) { /* Get the filename of the saved file. */ - char filename[STRING_SIZE + 50]; + char filename[CSDS_STRING_SIZE + 50]; int savefile = time_array_get_filename_savefile(log, filename); if (!savefile) { @@ -207,7 +207,7 @@ void time_array_load(struct time_array *t, struct csds_index *index) { if (t->capacity != 0) time_array_free(t); /* Get the position of the time array. */ - char *map = (char *)csds_index_get_removed_history(index, swift_type_count); + char *map = (char *)csds_index_get_removed_history(index, csds_type_count); /* Read the array */ uint64_t size = 0; @@ -232,7 +232,7 @@ void time_array_load(struct time_array *t, struct csds_index *index) { */ int time_array_restore(struct time_array *t, struct csds_logfile *log) { /* Get the filename of the saved file. */ - char filename[STRING_SIZE + 50]; + char filename[CSDS_STRING_SIZE + 50]; int savefile = time_array_get_filename_savefile(log, filename); if (!savefile) return 0; @@ -330,7 +330,7 @@ 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) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (!t) error_python("NULL pointer."); if (offset < t->records[0].offset || offset > t->records[t->size - 1].offset) @@ -362,7 +362,7 @@ size_t time_array_get_index(const struct time_array *t, const size_t offset) { right = right - 1; } -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (t->records[right].offset > offset || t->records[right + 1].offset <= offset) { error_python("Found the wrong element"); @@ -384,7 +384,7 @@ size_t time_array_get_index(const struct time_array *t, const size_t offset) { size_t time_array_get_index_from_time(const struct time_array *t, const double time) { -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (!t) error_python("NULL pointer."); if (time < t->records[0].time || time > t->records[t->size - 1].time) @@ -415,7 +415,7 @@ size_t time_array_get_index_from_time(const struct time_array *t, right = right - 1; } -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS if (t->records[right].time > time || t->records[right + 1].time <= time) { error_python("Found the wrong element"); } diff --git a/src/csds_tools.c b/src/csds_tools.c index a1f796d..0f16c8b 100644 --- a/src/csds_tools.c +++ b/src/csds_tools.c @@ -37,8 +37,7 @@ */ int tools_get_next_record(const struct header *h, char *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_forward(h)) return _tools_get_next_record_forward(map, offset); if (header_is_backward(h)) return _tools_get_next_record_backward(h, map, offset, file_size); else @@ -49,14 +48,12 @@ int tools_get_next_record(const struct header *h, char *map, size_t *offset, * @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, char *map, - size_t *offset) { +int _tools_get_next_record_forward(char *map, size_t *offset) { size_t diff_offset = 0; /* Read the offset. */ @@ -82,7 +79,7 @@ int _tools_get_next_record_forward(const struct header *h, char *map, */ int _tools_get_next_record_backward(const struct header *h, char *map, size_t *offset, size_t file_size) { -#ifndef SWIFT_DEBUG_CHECKS +#ifndef CSDS_DEBUG_CHECKS error_python("Should not be used, method too slow"); #endif size_t current_offset = *offset; @@ -144,7 +141,7 @@ size_t tools_reverse_offset(const struct header *h, char *file_map, map = file_map + cur_offset - prev_offset + CSDS_MASK_SIZE; map = csds_loader_io_write_data(map, CSDS_OFFSET_SIZE, &prev_offset); -#ifdef SWIFT_DEBUG_CHECKS +#ifdef CSDS_DEBUG_CHECKS mask_type prev_mask = 0; map = map - CSDS_MASK_SIZE - CSDS_OFFSET_SIZE; csds_loader_io_read_mask(map, &prev_mask, NULL); @@ -152,9 +149,9 @@ size_t tools_reverse_offset(const struct header *h, char *file_map, /* Check if we are not mixing timestamp and particles */ if ((prev_mask != TIMESTAMP_MASK && mask == TIMESTAMP_MASK) || (prev_mask == TIMESTAMP_MASK && mask != TIMESTAMP_MASK)) - error_python("Unexpected mask: %lu, got %lu.", mask, prev_mask); + error_python("Unexpected mask: %i, got %i.", mask, prev_mask); -#endif // SWIFT_DEBUG_CHECKS +#endif // CSDS_DEBUG_CHECKS return after_current_record; } @@ -172,7 +169,7 @@ size_t tools_reverse_offset(const struct header *h, char *file_map, */ size_t tools_check_record_consistency(const struct csds_reader *reader, size_t offset) { -#ifndef SWIFT_DEBUG_CHECKS +#ifndef CSDS_DEBUG_CHECKS error_python("Should not check in non debug mode."); #endif @@ -262,6 +259,6 @@ void tools_print_progress(float percentage, int remaining_time, current += sprintf(current, "%.2is", sec); /* Print the string */ - printf("\r%s", output); + printf("\r%s %s", clocks_get_timesincestart(), output); fflush(stdout); } diff --git a/src/csds_tools.h b/src/csds_tools.h index 4b01f6a..3012f05 100644 --- a/src/csds_tools.h +++ b/src/csds_tools.h @@ -23,26 +23,9 @@ #define CSDS_CSDS_TOOLS_H #include "../config.h" - -/* Swift include */ -#include "../src/csds.h" -#include "../src/csds_io.h" -#include "../src/dimension.h" -#include "../src/error.h" -#include "../src/inline.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 +#include "csds_error.h" +#include "csds_inline.h" +#include "csds_logfile_writer.h" /* Define the size of all the fields. */ #define member_size(type, member) sizeof(((type *)0)->member) @@ -68,8 +51,7 @@ int tools_get_next_record(const struct header *h, char *map, size_t *offset, size_t file_size); int _tools_get_next_record_backward(const struct header *h, char *map, size_t *offset, size_t file_size); -int _tools_get_next_record_forward(const struct header *h, char *map, - size_t *offset); +int _tools_get_next_record_forward(char *map, size_t *offset); size_t tools_reverse_offset(const struct header *h, char *map, size_t offset); size_t tools_check_record_consistency(const struct csds_reader *reader, size_t offset); @@ -77,35 +59,4 @@ size_t tools_check_record_consistency(const struct csds_reader *reader, void tools_print_progress(float percentage, int remaining_time, const char *message); -#ifndef HAVE_PYTHON -#define error_python(s, ...) error(s, ##__VA_ARGS__); -#else -#include "frameobject.h" - -/** - * @brief Print the python trace back - */ -__attribute__((always_inline)) INLINE static void csds_loader_print_traceback( - void) { - - PyGILState_STATE gstate = PyGILState_Ensure(); - - PyFrameObject *frame = PyEval_GetFrame(); - int lineno = PyFrame_GetLineNumber(frame); - PyObject *py_filename = frame->f_code->co_filename; - const char *filename = PyUnicode_AsUTF8(py_filename); - message("File %s, line: %i", filename, lineno); - - Py_DECREF(py_filename); - Py_DECREF(frame); - PyGILState_Release(gstate); -} - -#define error_python(s, ...) \ - ({ \ - csds_loader_print_traceback(); \ - error(s, ##__VA_ARGS__); \ - }) -#endif - #endif // CSDS_CSDS_TOOLS_H diff --git a/src/csds_version.h.in b/src/csds_version.h.in new file mode 100644 index 0000000..31b3eed --- /dev/null +++ b/src/csds_version.h.in @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of CSDS. + * Copyright (c) 2021 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 only contains the information about the version. + */ +#ifndef CSDS_VERSION_H +#define CSDS_VERSION_H + +/* WARNING THIS FILE SHOULD NOT INCLUDE CONFIG.H*/ + +/* Version numbers */ +#define CSDS_MAJOR_VERSION @major_version@ +#define CSDS_MINOR_VERSION @minor_version@ + +#endif // CSDS_VERSION_H diff --git a/src/quick_sort.c b/src/quick_sort.c index c994675..a5e3645 100644 --- a/src/quick_sort.c +++ b/src/quick_sort.c @@ -23,6 +23,9 @@ /* Include local headers */ #include "csds_index.h" +/* Include standard headers */ +#include <math.h> + struct qstack { int64_t lo; int64_t hi; diff --git a/tests/Makefile.am b/tests/Makefile.am index 421ea55..215adbd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -# This file is part of SWIFT. +# This file is part of CSDS. # Copyright (c) 2019 loic.hausammann@epfl.ch. # # This program is free software: you can redistribute it and/or modify @@ -15,9 +15,16 @@ # 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 = $(PYTHON_INCS) -I$(top_srcdir)/src -I$(top_srcdir)/csds/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) +AM_CFLAGS = $(OPENMP_CFLAGS) $(PYTHON_INCS) -I$(top_srcdir)/src -AM_LDFLAGS = -L../../src/.libs/ ../src/.libs/libcsds.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) $(PYTHON_LIBS) -lswiftsim +AM_LDFLAGS = ../src/.libs/libcsds.a ../src/.libs/libcsds_writer.a $(PYTHON_LIBS) + +if HAVEPYTHON +# The main python structure is dependent on the version, thus we need to +# remove this one in order to be safe. +PYTHON_EXTRA_COMPILER_FLAG = -Wno-missing-field-initializers -Wno-cast-function-type -Wno-unused-function +endif +AM_CFLAGS += $(PYTHON_EXTRA_COMPILER_FLAG) # List of programs and scripts to run in the test suite TESTS = testLogfileHeader testLogfileReader testTimeArray testQuickSort testVirtualReality @@ -27,8 +34,8 @@ TESTS += testHashmap check_PROGRAMS = testLogfileHeader testLogfileReader testTimeArray testQuickSort testVirtualReality check_PROGRAMS += testHashmap -# Rebuild tests when SWIFT is updated. -$(check_PROGRAMS): ../../src/.libs/libswiftsim.a ../src/.libs/libcsds.a +# Rebuild tests when CSDS is updated. +$(check_PROGRAMS): ../src/.libs/libcsds.a ../src/.libs/libcsds_writer.a # Sources for the individual programs testLogfileHeader_SOURCES = testLogfileHeader.c @@ -39,4 +46,4 @@ testVirtualReality_SOURCES = testVirtualReality.c testHashmap_SOURCES = testHashmap.c # Files necessary for distribution -EXTRA_DIST = testLogfileHeader.yml testLogfileReader.yml +EXTRA_DIST = diff --git a/tests/generate_log.h b/tests/generate_log.h index 8a0ca0d..b2161ca 100644 --- a/tests/generate_log.h +++ b/tests/generate_log.h @@ -1,5 +1,5 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify @@ -18,63 +18,153 @@ ******************************************************************************/ /* Include config */ -#include "../../config.h" +#include "config.h" /* Local headers */ -#include "csds.h" -#include "csds_io.h" -#include "engine.h" -#include "hydro.h" +#include "csds_logfile_writer.h" -/* 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 -#define const_time_base 1e-4 +/* Define the masks */ +// The two first are already picked (special flags + timestamp) +#define MASK_POSITION (1 << 2) +#define MASK_ID (1 << 3) + +#define TEST_NUMBER_MASKS 2 +#define number_steps 100 + +struct csds_part { + /* Position */ + double x[3]; + + /* IDs of the particle */ + long long id; + + /* Offset of the last record */ + uint64_t last_offset; + + /* Time bin (number of steps skipped) */ + int time_bin; +}; /** * @brief Generate the data of a bunch of particles. * * @param parts The list of particles. - * @param xparts The list of extra particles. * @param nparts The number of particles. */ -void generate_particles(struct part *parts, struct xpart *xparts, - size_t nparts) { - struct hydro_space hs; +void generate_particles(struct csds_part *parts, size_t nparts) { for (size_t i = 0; i < nparts; i++) { - /* Set internal energy. */ - hydro_set_init_internal_energy(&parts[i], 100); - - /* Initialize particle. */ - hydro_first_init_part(&parts[i], &xparts[i]); - hydro_init_part(&parts[i], &hs); - csds_part_data_init(&xparts[i].csds_data); - parts[i].gpart = NULL; + parts[i].id = i; + parts[i].last_offset = 0; for (int j = 0; j < 3; j++) { - parts[i].x[j] = 0; - parts[i].v[j] = (j == 0) ? -1 : 0; - parts[i].a_hydro[j] = (j == 1) ? 1e-2 : 0; - xparts[i].a_grav[j] = 0; + parts[i].x[j] = j; } - parts[i].h = 15; - parts[i].rho = 50; - parts[i].id = i; - hydro_set_mass(&parts[i], 1.5); /* Add time bin in order to skip particles. */ parts[i].time_bin = (i % 10) + 1; } } -/** Provides a integer time given the step number.*/ -integertime_t get_integer_time(int step) { return step; } +/** @brief Get a double value from an int */ +double get_double_time(int i) { return i * 0.12; } + +/** @brief Get an integertime_t from an int */ +integertime_t get_integer_time(int i) { return 100 * i; } + +/** + * @brief Write a timestamp into the logfile. + * + * @param log The #csds_logfile_writer. + * @param ti_int The time as an integertime_t + * @param ti_double The time as a double + * @param offset (in) The last offset of the timestamp. (out) The new offset. + */ +void csds_log_timestamp(struct csds_logfile_writer *log, integertime_t ti_int, + double ti_double, size_t *offset) { + + /* Start by computing the size of the message. */ + const int size = TIMESTAMP_SIZE + CSDS_HEADER_SIZE; + + /* Allocate a chunk of memory in the logfile of the right size. */ + size_t offset_new; + char *buff = (char *)csds_logfile_writer_get(log, size, &offset_new); + + /* Write the header. */ + unsigned int mask = TIMESTAMP_MASK; + buff = csds_write_record_header(buff, &mask, offset, offset_new); + + /* Store the timestamp. */ + memcpy(buff, &ti_int, sizeof(integertime_t)); + buff += sizeof(integertime_t); + + /* Store the time. */ + memcpy(buff, &ti_double, sizeof(double)); + + /* Update the log message offset. */ + *offset = offset_new; +} + +/** + * @brief Write a particle into the logfile. + * + * @param log The #csds_logfile_writer. + * @param part The #csds_part to log. + */ +void csds_log_part(struct csds_logfile_writer *log, struct csds_part *part, + const enum csds_special_flags flag) { + const size_t size_flag = flag == csds_flag_none ? 0 : SPECIAL_FLAGS_SIZE; + const size_t size_total = + sizeof(part->x) + sizeof(part->id) + CSDS_HEADER_SIZE + size_flag; + + // For performances, this should never be done on a single + // particle at a time. + size_t offset = 0; + char *buff = (char *)csds_logfile_writer_get(log, size_total, &offset); + const unsigned int mask_flag = + flag == csds_flag_none ? 0 : SPECIAL_FLAGS_MASK; + const unsigned int mask = MASK_POSITION | MASK_ID | mask_flag; + + /* Write the header */ + buff = csds_write_record_header(buff, &mask, &part->last_offset, offset); + part->last_offset = offset; + + /* Write the special flag */ + if (flag != csds_flag_none) { + const uint32_t data = csds_pack_flags_and_data(flag, 0, csds_type_gas); + memcpy(buff, &data, sizeof(uint32_t)); + buff += sizeof(uint32_t); + } + + /* Write the position */ + memcpy(buff, part->x, sizeof(part->x)); + buff += sizeof(part->x); + + /* Write the ID */ + memcpy(buff, &part->id, sizeof(part->id)); + buff += sizeof(part->id); +} + +/** + * @brief Write all the particles into the logfile. + * + * @param log The #csds_logfile_writer. + * @param parts The #csds_part to log. + * @param nparts The number of particles to log. + * @param first_log Is it the first or last log? + */ +void csds_log_all_particles(struct csds_logfile_writer *log, + struct csds_part *part, size_t nparts, + int first_log) { + + const enum csds_special_flags flag = + first_log ? csds_flag_create : csds_flag_delete; -/** Provides a double time given the step number. */ -double get_double_time(int step) { return step * const_time_base; } +#pragma omp parallel for + for (size_t i = 0; i < nparts; i++) { + csds_log_part(log, &part[i], flag); + } +} /** * @brief Write a few particles during multiple time steps. @@ -82,31 +172,29 @@ double get_double_time(int step) { return step * const_time_base; } * As only the csds is tested, there is no need to really * evolve the particles. * - * @param log The #csds_writer. - * @param e The #engine. + * @param log The #csds_logfile_writer. + * @param parts The particles to "simulate". + * @param nparts The number of particles */ -void write_particles(struct csds_writer *log, struct engine *e) { - - size_t nparts = e->total_nr_parts; - struct part *parts = e->s->parts; - struct xpart *xparts = e->s->xparts; - - const int number_steps = 100; +void write_particles(struct csds_logfile_writer *log, struct csds_part *parts, + size_t nparts, size_t *timestamp_offset) { /* Loop over all the steps. */ for (int i = 0; i < number_steps; i++) { - e->time = get_double_time(i); - e->ti_current = get_integer_time(i); integertime_t ti_int = get_integer_time(i); double ti_double = get_double_time(i); /* Mark the current time step in the particle csds file. */ - csds_log_timestamp(log, ti_int, ti_double, &log->timestamp_offset); + csds_log_timestamp(log, ti_int, ti_double, timestamp_offset); + /* Make sure that we have enough space in the particle csds file * to store the particles in current time step. */ - csds_ensure_size(log, e); + // Here we need to have enough place for the part + the record header. + csds_logfile_writer_ensure(log, 1.2 * nparts * sizeof(struct csds_part), + 5 * nparts * sizeof(struct csds_part)); /* Loop over all the particles. */ +#pragma omp parallel for for (size_t j = 0; j < nparts; j++) { /* Skip some particles. */ @@ -116,15 +204,26 @@ void write_particles(struct csds_writer *log, struct engine *e) { parts[j].x[0] = i; // TODO write only a few masks at the time - - csds_log_part(log, &parts[j], &xparts[j], e, /* log_all */ 0, - /* flag */ 0, /* flag_data */ 0); + csds_log_part(log, &parts[j], csds_flag_none); } } } -void generate_log(struct swift_params *params, struct part *parts, - struct xpart *xparts, size_t nparts) { +#define max(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +/** + * @brief Generate a logfile. + * + * @param parts The array of #csds_part to use + * @param nparts The number of particles. + * @param filename The filename of the logfile. + */ +void generate_log(struct csds_part *parts, size_t nparts, char *filename) { #ifdef HAVE_PYTHON message( @@ -132,72 +231,91 @@ void generate_log(struct swift_params *params, struct part *parts, "initialized. If you obtain a segmentation fault, please recompile " "without python"); #endif + /* Initialize the particles */ - generate_particles(parts, xparts, nparts); - - /* initialize the engine */ - struct engine e; - e.policy = engine_policy_hydro; - e.total_nr_parts = nparts; - e.total_nr_gparts = 0; - e.total_nr_sparts = 0; - e.total_nr_bparts = 0; - e.verbose = 1; - e.ti_current = 0; - e.time = 0; - e.time_base = const_time_base; - e.time_begin = 0; - threadpool_init(&e.threadpool, 1); - struct space s; - e.s = &s; - s.xparts = xparts; - s.parts = parts; - s.gparts = NULL; - s.nr_parts = nparts; - s.nr_gparts = 0; - s.nr_sparts = 0; - s.nr_bparts = 0; - s.nr_sinks = 0; - s.nr_inhibited_parts = 0; - s.nr_inhibited_gparts = 0; - s.nr_inhibited_sparts = 0; - s.nr_inhibited_bparts = 0; - s.nr_inhibited_sinks = 0; - s.nr_extra_gparts = 0; - s.nr_extra_parts = 0; - s.nr_extra_sparts = 0; - s.nr_extra_bparts = 0; - s.nr_extra_sinks = 0; - struct csds_writer log; - e.csds = &log; + generate_particles(parts, nparts); /* Initialize the writer */ - csds_init(&log, &e, params); + struct csds_logfile_writer log; + const size_t init_size = max(sizeof(struct csds_part) * 1000, + 1.5 * nparts * sizeof(struct csds_part)); + csds_logfile_writer_init(&log, filename, init_size); /* Write file header */ - csds_write_file_header(&log); + char *first_offset = csds_logfile_writer_write_begining_header(&log); + + /* Write the number of masks */ + size_t file_offset = 0; + // Add timestamp + special flags + const unsigned int number_masks = TEST_NUMBER_MASKS + 2; + csds_write_data(&log, &file_offset, sizeof(unsigned int), &number_masks); + + /* Write the masks */ + const char *names[TEST_NUMBER_MASKS + 2] = { + SPECIAL_FLAGS_NAME, + TIMESTAMP_NAME, + "Coordinates", + "ParticleIDs", + }; + const unsigned int sizes[TEST_NUMBER_MASKS + 2] = { + SPECIAL_FLAGS_SIZE, + TIMESTAMP_SIZE, + sizeof(parts->x), + sizeof(parts->id), + }; + for (unsigned int i = 0; i < number_masks; i++) { + char tmp[CSDS_STRING_SIZE]; + strcpy(tmp, names[i]); + csds_write_data(&log, &file_offset, CSDS_STRING_SIZE, tmp); + csds_write_data(&log, &file_offset, sizeof(unsigned int), &sizes[i]); + } + + /* Write the number of fields per particle types */ + const int n_fields_type = 2; + int number_fields[csds_type_count]; + for (int i = 0; i < csds_type_count; i++) { + number_fields[i] = n_fields_type; + } + csds_write_data(&log, &file_offset, csds_type_count * sizeof(int), + number_fields); + + /* Write the order for each particle type */ + for (int i = 0; i < csds_type_count; i++) { + const int order[] = {2, 3}; + csds_write_data(&log, &file_offset, n_fields_type * sizeof(int), order); + } + + /* Write the end of the header */ + csds_logfile_writer_write_end_header(&log, first_offset); /* Mark the current time step in the particle csds file. */ - csds_log_timestamp(&log, e.ti_current, e.time, &log.timestamp_offset); + integertime_t ti_current = 0; + double time = 0; + size_t timestamp_offset = 0; + csds_log_timestamp(&log, ti_current, time, ×tamp_offset); + /* Make sure that we have enough space in the particle csds file * to store the particles in current time step. */ - csds_ensure_size(&log, &e); + csds_logfile_writer_ensure(&log, 1.2 * nparts * sizeof(struct csds_part), + 5 * nparts * sizeof(struct csds_part)); /* Log all the particles before starting */ - csds_log_all_particles(&log, &e, /* first_log */ 1); + csds_log_all_particles(&log, parts, nparts, /* first_log */ 1); /* Write particles */ - write_particles(&log, &e); + write_particles(&log, parts, nparts, ×tamp_offset); /* Write a sentinel timestamp */ - csds_log_timestamp(e.csds, e.ti_current, e.time, &e.csds->timestamp_offset); + ti_current = get_integer_time(number_steps); + time = get_double_time(number_steps); + csds_log_timestamp(&log, ti_current, time, ×tamp_offset); /* Write all the particles at the end */ - csds_log_all_particles(e.csds, &e, /* first_log */ 0); + csds_log_all_particles(&log, parts, nparts, /* first_log */ 0); /* Write a sentinel timestamp */ - csds_log_timestamp(e.csds, e.ti_current, e.time, &e.csds->timestamp_offset); + csds_log_timestamp(&log, ti_current, time, ×tamp_offset); /* Cleanup the memory */ - csds_free(&log); + csds_logfile_writer_close(&log); } diff --git a/tests/testHashmap.c b/tests/testHashmap.c index 9592b13..4ce26bc 100644 --- a/tests/testHashmap.c +++ b/tests/testHashmap.c @@ -6,17 +6,16 @@ // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file +#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> /* CSDS header */ +#include "csds_error.h" #include "csds_hashmap.h" -/* SWIFT headers */ -#include "error.h" - #define N 10000 static void shuffle(struct index_data *array, size_t numels) { @@ -41,7 +40,7 @@ static size_t deepcount(struct csds_hashmap *map) { static void all(void) { struct index_data *vals; - vals = malloc(N * sizeof(struct index_data)); + vals = (struct index_data *)malloc(N * sizeof(struct index_data)); if (vals == NULL) { error("Failed to allocate the index array"); } @@ -108,6 +107,7 @@ static void all(void) { free(test); csds_hashmap_free(map); + free(vals); } #define bench(name, N, code) \ diff --git a/tests/testLogfileHeader.c b/tests/testLogfileHeader.c index 6d1e0ad..60f29b4 100644 --- a/tests/testLogfileHeader.c +++ b/tests/testLogfileHeader.c @@ -1,5 +1,5 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify @@ -17,12 +17,17 @@ * ******************************************************************************/ +/* Include local headers */ #include "csds_header.h" #include "csds_logfile.h" +#include "csds_parser.h" #include "csds_reader.h" -#include "swift.h" +#include "generate_log.h" -int main(int argc, char *argv[]) { +/* Include standard headers */ +#include <assert.h> + +int main(void) { /* First generate the file. @@ -30,38 +35,13 @@ int main(int argc, char *argv[]) { message("Generating the dump."); /* Create required structures. */ - struct csds_writer log; - struct swift_params params; - char filename[200] = "testLogfileHeader.yml"; - - /* Read parameters. */ - parser_read_file(filename, ¶ms); - - /* Initialize the engine */ - struct engine e; - e.policy = - engine_policy_hydro | engine_policy_stars | engine_policy_self_gravity; - - /* Initialize the csds. */ - csds_init(&log, &e, ¶ms); - - /* get dump filename. */ - char dump_filename[PARSER_MAX_LINE_SIZE]; - strcpy(dump_filename, log.base_name); - strcat(dump_filename, "_0000.dump"); + char filename[200] = "test_header_0000.dump"; /* Write file header. */ - csds_write_file_header(&log); - - /* Copy the masks */ - const int total_number_fields = log.total_number_fields; - struct csds_field *list_fields = (struct csds_field *)malloc( - total_number_fields * sizeof(struct csds_field)); - memcpy(list_fields, log.list_fields, - total_number_fields * sizeof(struct csds_field)); + struct csds_part *parts = NULL; + size_t nparts = 0; + generate_log(parts, nparts, filename); - /* clean memory. */ - csds_free(&log); /* Then read the file. */ @@ -76,7 +56,7 @@ int main(int argc, char *argv[]) { reader.verbose = 1; /* Read the header */ - csds_logfile_init_from_file(logfile, dump_filename, &reader, + csds_logfile_init_from_file(logfile, filename, &reader, /* only_header */ 1); /* Finally check everything. @@ -84,26 +64,23 @@ int main(int argc, char *argv[]) { struct header *h = &logfile->header; message("Checking versions."); - assert(h->major_version == csds_major_version); - assert(h->minor_version == csds_minor_version); - - message("Checking offset of first record"); - assert(h->offset_first_record == logfile->log.mmap_size); + assert(h->major_version == CSDS_MAJOR_VERSION); + assert(h->minor_version == CSDS_MINOR_VERSION); message("Checking number of masks"); /* Compute the number of masks */ int count_fields = 2; // Timestamp + special flags - for (int type = 0; type < swift_type_count; type++) { + for (int type = 0; type < csds_type_count; type++) { count_fields += h->number_fields[type]; /* Remove the copies of the special flags */ if (h->number_fields[type] != 0) count_fields -= 1; } - assert(total_number_fields == count_fields); + const int number_masks_ref = csds_type_count * TEST_NUMBER_MASKS + 2; + assert(number_masks_ref == count_fields); message("Checking offset direction"); assert(h->offset_direction == csds_offset_backward); - free(list_fields); csds_logfile_free(logfile); return 0; } diff --git a/tests/testLogfileReader.c b/tests/testLogfileReader.c index c30bc4b..9200003 100644 --- a/tests/testLogfileReader.c +++ b/tests/testLogfileReader.c @@ -1,5 +1,5 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch). * * This program is free software: you can redistribute it and/or modify @@ -22,16 +22,16 @@ #include "csds_loader_io.h" #include "csds_particle.h" #include "csds_reader.h" -#include "swift.h" /* Tests header */ #include "generate_log.h" +#include <assert.h> + #define number_parts 100 -#define max_step 99 /** Count the number of active particles. */ -int get_number_active_particles(int step, struct part *p) { +int get_number_active_particles(int step, struct csds_part *p) { int count = 0; for (int i = 0; i < number_parts; i++) { if (step % p[i].time_bin == 0) count += 1; @@ -44,8 +44,7 @@ int get_number_active_particles(int step, struct part *p) { * * @param reader The #csds_reader. */ -void check_data(struct csds_reader *reader, struct part *parts, - struct xpart *xparts) { +void check_data(struct csds_reader *reader, struct csds_part *parts) { /* No need to check the header, this is already done in testHeader.c */ @@ -54,11 +53,11 @@ void check_data(struct csds_reader *reader, struct part *parts, struct header *h = &reader->log.header; /* Create a particle */ - void **output = malloc(h->number_fields[swift_type_gas] * sizeof(void *)); - for (int i = 0; i < h->number_fields[swift_type_gas]; i++) { - output[i] = malloc(h->fields[swift_type_gas][i].field.size); - message("%i %i %i", i, h->fields[swift_type_gas][i].field.field, - h->fields[swift_type_gas][i].field.position); + void **output = malloc(h->number_fields[csds_type_gas] * sizeof(void *)); + for (int i = 0; i < h->number_fields[csds_type_gas]; i++) { + output[i] = malloc(h->fields[csds_type_gas][i].field.size); + message("%i %i %i", i, h->fields[csds_type_gas][i].field.field, + h->fields[csds_type_gas][i].field.position); } /* Define a few variables */ @@ -75,21 +74,9 @@ void check_data(struct csds_reader *reader, struct part *parts, /* Get all the fields */ const struct field_information *field_id = - header_get_field_from_name(h, "ParticleIDs", swift_type_gas); + header_get_field_from_name(h, "ParticleIDs", csds_type_gas); const struct field_information *field_pos = - header_get_field_from_name(h, "Coordinates", swift_type_gas); - const struct field_information *field_vel = - header_get_field_from_name(h, "Velocities", swift_type_gas); - const struct field_information *field_acc = - header_get_field_from_name(h, "Accelerations", swift_type_gas); - const struct field_information *field_mass = - header_get_field_from_name(h, "Masses", swift_type_gas); - const struct field_information *field_u = - header_get_field_from_name(h, "InternalEnergies", swift_type_gas); - const struct field_information *field_h = - header_get_field_from_name(h, "SmoothingLengths", swift_type_gas); - const struct field_information *field_rho = - header_get_field_from_name(h, "Densities", swift_type_gas); + header_get_field_from_name(h, "Coordinates", csds_type_gas); /* Loop over each record. */ for (size_t offset = @@ -107,7 +94,6 @@ void check_data(struct csds_reader *reader, struct part *parts, Check that we are really increasing the id in the logfile. See the writing part to see that we are always increasing the id. */ - const uint64_t current_id = *(uint64_t *)output[field_id->field.position]; if (previous_id != id_flag && previous_id >= current_id) { error("Wrong particle found"); @@ -117,10 +103,8 @@ void check_data(struct csds_reader *reader, struct part *parts, /* Get the corresponding particle */ if (current_id >= number_parts) error("Wrong id %li", current_id); - struct part *p = &parts[current_id]; + struct csds_part *p = &parts[current_id]; const double *pos = (double *)output[field_pos->field.position]; - const float *vel = (float *)output[field_vel->field.position]; - const float *acc = (float *)output[field_acc->field.position]; /* Check the record's data. */ for (int i = 0; i < 3; i++) { @@ -128,38 +112,15 @@ void check_data(struct csds_reader *reader, struct part *parts, if (i == 0) { double tmp = step; /* At the end, we are not updating the particle */ - if (step >= max_step) { - tmp = max_step - max_step % p->time_bin; + if (step >= number_steps) { + int n_1 = number_steps - 1; + tmp = n_1 - (n_1 % p->time_bin); } assert(tmp == pos[i]); } else - assert(p->x[i] == pos[i]); - assert(p->v[i] == vel[i]); - assert(p->a_hydro[i] == acc[i]); + assert(p->x[i] == i); } - const float energy = *(float *)output[field_u->field.position]; - assert(p->u == energy); - const float mass = *(float *)output[field_mass->field.position]; - assert(p->mass == mass); - - /* Check optional fields. */ - // int number_steps = step / p->time_bin; - // TODO check only every few steps - const float current_h = *(float *)output[field_h->field.position]; - assert(p->h == current_h); - /* if (number_steps % period_h == 0 || step > max_step) { */ - /* assert(p->h == lp.h); */ - /* } else { */ - /* assert(-1 == lp.h); */ - /* } */ - const float rho = *(float *)output[field_rho->field.position]; - assert(p->rho == rho); - /* if (number_steps % period_rho == 0 || step > max_step) { */ - /* assert(p->rho == lp.rho); */ - /* } else { */ - /* assert(-1 == lp.rho); */ - /* } */ } /* Time stamp case. */ else { @@ -183,19 +144,19 @@ void check_data(struct csds_reader *reader, struct part *parts, count = 0; /* Check the record's data. */ - const int tmp_step = step >= max_step ? max_step : step; + const int tmp_step = step >= number_steps ? number_steps : step; assert(time == get_double_time(tmp_step)); } } /* Cleanup */ - for (int i = 0; i < h->number_fields[swift_type_gas]; i++) { + for (int i = 0; i < h->number_fields[csds_type_gas]; i++) { free(output[i]); } free(output); } -int main(int argc, char *argv[]) { +int main(void) { /* First generate the file. @@ -204,25 +165,16 @@ int main(int argc, char *argv[]) { message("Generating the dump."); /* Create required structures. */ - struct swift_params params; - char filename[200] = "testLogfileReader.yml"; - - /* Read parameters. */ - parser_read_file(filename, ¶ms); + char filename[200] = "test_reader_0000.dump"; /* Initialize the particles. */ - struct part *parts; - if ((parts = (struct part *)malloc(sizeof(struct part) * number_parts)) == - NULL) + struct csds_part *parts; + if ((parts = (struct csds_part *)malloc(sizeof(struct csds_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."); - /* Write a 'simulation' */ - generate_log(¶ms, parts, xparts, number_parts); + generate_log(parts, number_parts, filename); /* Then read the file. @@ -237,11 +189,8 @@ int main(int argc, char *argv[]) { reader.verbose = 1; /* Read the header. */ - char basename[200]; - parser_get_param_string(¶ms, "CSDS:basename", basename); - strcat(basename, "_0000"); + char basename[200] = "test_reader_0000"; csds_reader_init(&reader, basename, /* verbose */ 1, - /* number_threads */ 1, /* number_index*/ 5, /* restart */ 0); @@ -249,11 +198,10 @@ int main(int argc, char *argv[]) { Finally check everything. */ - check_data(&reader, parts, xparts); + check_data(&reader, parts); /* Do some cleanup. */ free(parts); - free(xparts); csds_reader_free(&reader); return 0; diff --git a/tests/testQuickSort.c b/tests/testQuickSort.c index f8ad6d4..f2056bc 100644 --- a/tests/testQuickSort.c +++ b/tests/testQuickSort.c @@ -1,6 +1,6 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify @@ -18,7 +18,6 @@ * ******************************************************************************/ -#include "memswap.h" #include "quick_sort.h" #define N 10000 @@ -41,7 +40,9 @@ void init_array(struct index_data *data) { int j = rand() % N; /* Swap the two elements */ - memswap(&data[i], &data[j], sizeof(struct index_data)); + struct index_data tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; } } @@ -66,7 +67,7 @@ void print_array(struct index_data *data) { } } -int main(int argc, char *argv[]) { +int main(void) { /* Create the array */ struct index_data *data = diff --git a/tests/testTimeArray.c b/tests/testTimeArray.c index a86db3f..48122c7 100644 --- a/tests/testTimeArray.c +++ b/tests/testTimeArray.c @@ -1,5 +1,5 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch) * * This program is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "csds_time.h" +#include <assert.h> #include <stdlib.h> #include <time.h> @@ -26,7 +27,7 @@ #define TIME_BASE 0.04 #define OFFSET_BASE 1000 -int main(int argc, char *argv[]) { +int main(void) { /* Check that we are really testing the reallocation */ if (NUMBER_OF_ELEMENT < CSDS_TIME_INIT_SIZE) { diff --git a/tests/testVirtualReality.c b/tests/testVirtualReality.c index d7ec834..1f933cf 100644 --- a/tests/testVirtualReality.c +++ b/tests/testVirtualReality.c @@ -1,5 +1,5 @@ /******************************************************************************* - * This file is part of SWIFT. + * This file is part of CSDS. * Copyright (C) 2019 Loic Hausammann (loic.hausammann@epfl.ch) * Florian Cabot (florian.cabot@epfl.ch) * @@ -25,12 +25,10 @@ #include <stdlib.h> /* Local include */ -#include "csds.h" +#include "csds_logfile_writer.h" #include "csds_reader.h" #include "generate_log.h" -#include "hydro.h" -#define number_steps 10. #define number_parts 100 /** @@ -38,35 +36,24 @@ * The idea is to simply read a snapshot at a given time and * then simply advance in time the particles. */ -int main(int argc, char *argv[]) { +int main(void) { /* Create required structures. */ - struct swift_params params; - char filename[200] = "testVirtualReality.yml"; - - /* Read parameters. */ - parser_read_file(filename, ¶ms); + char filename[200] = "testvr_0000.dump"; /* Initialize the particles. */ - struct part *parts; - if ((parts = (struct part *)malloc(sizeof(struct part) * number_parts)) == - NULL) + struct csds_part *parts; + if ((parts = (struct csds_part *)malloc(sizeof(struct csds_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."); - /* Write a 'simulation' */ - generate_log(¶ms, parts, xparts, number_parts); + generate_log(parts, number_parts, filename); /* Initialize the reader */ struct csds_reader reader; - char basename[200]; - parser_get_param_string(¶ms, "CSDS:basename", basename); - strcat(basename, "_0000"); + char basename[200] = "testvr_0000"; csds_reader_init(&reader, basename, - /* Verbose */ 2, /* number_threads */ 1, + /* Verbose */ 2, /* number_index */ 5, /* restart */ 0); @@ -79,7 +66,7 @@ int main(int argc, char *argv[]) { csds_reader_set_time(&reader, begin); /* Create the variables for the number of particles */ - const int n_type = swift_type_count; + const int n_type = csds_type_count; uint64_t *n_parts = (uint64_t *)malloc(n_type * sizeof(uint64_t)); int *read_types = (int *)malloc(n_type * sizeof(int)); if (read_types == NULL || n_parts == NULL) { @@ -134,7 +121,6 @@ int main(int argc, char *argv[]) { free(ids); free(pos); free(parts); - free(xparts); free(n_parts); free(read_types); csds_reader_free(&reader); -- GitLab