diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..4f6ca2a9861c060d40e3b6fef53072ea3bc311ed --- /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 0000000000000000000000000000000000000000..28ecdc8a0ed9fa7416e2998749e6cac8964ca501 --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/INSTALL b/INSTALL new file mode 120000 index 0000000000000000000000000000000000000000..e3f22c0e1ea44ac687aee8d986a0c9eac7cc2a63 --- /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 a0d309572afa08b9d0a356c3d91f3f03af6aa274..b9805af5eb2b30748da41e0782ed7fc4e8f936c5 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/README b/README new file mode 120000 index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 --- /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 0000000000000000000000000000000000000000..8e52b9aefd508034ef68e6988bdbfa17b1b12e12 --- /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 0000000000000000000000000000000000000000..bb670d865adb65d2c0b89524ee60adf93889d505 --- /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 3d78b60a44278005b8e78c3f0f25772cd5f374dd..06a030cd62a27be6797891753cd8a55ff59439cd 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 ee70ca744a9dfab064241fd9982558079dcc300e..6c64792692f0cc547ce8b0f1203d5d127920f6d9 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 043bfdd27a2979268b3e283b66c44c2cb776bf40..82530468b5e08684edaf60ae955123fc2a910e01 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 ebd3280ad2ae5c0d45b75943af7f002ddfb1047e..0f3a975d2e332ac8266e2d90f06cbf9d5a360b38 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 a2418492584d0c79fa6eb900ca2b0f758c4c6084..a278db36369dc4beecc923eade2c5c1dd806fa0c 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 db4a8cceb25fa3df36378a17afa07eab4a3347e1..64c5d679e5e2613cfdbabfea5137822d68d4914f 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 a28e68bf0bf5ffc6ab03ea74ed086af5513c49df..b0e694422d550b3bc54224adf9da1b6399264a64 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 0000000000000000000000000000000000000000..89d3b93b527a717a8901584144f55f4475e503f6 --- /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 0000000000000000000000000000000000000000..c9e28db0ee5b2a1623280404f0776576cff7c780 --- /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 0000000000000000000000000000000000000000..6df1c5301aab7da3e937932703b14d71417b917e --- /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 0000000000000000000000000000000000000000..6b67c4f93116916943d1abd92a485bb33330515f --- /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 0000000000000000000000000000000000000000..4995beb6032e792c209b1e045c7f58b8712f89a2 --- /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 0000000000000000000000000000000000000000..b0238c10ed0afbb2e8a39197ce0e7e1dd6a7d0d2 --- /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 e534d2fb9e90e59eec33fb8330d7e59a433356d1..3be935ce07cdea375835302552035d4225db26a4 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 0000000000000000000000000000000000000000..e026bf7d5df2c10385ba114509941b0b0d00d0a0 --- /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 0000000000000000000000000000000000000000..b9c50e4f260a6ed5559e3b07d45f12ba5c932e6e --- /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 6f1a4e167cc371bf3a2d502b3c6451968a27f525..c0926ace730e1b9f94bb4afa893ea8dc7a01fb12 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 7fe7be47a56dfe3f16d5c5b1b21883bc7466c953..62027d9d03635f454445e79537bd34753e960a29 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 0000000000000000000000000000000000000000..0ba1277893fe7be7d8b62cdcb9a8873d5709eb60 --- /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 0000000000000000000000000000000000000000..aaebf954a7a12a587ffeea11febc89b83bedc03a --- /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 0000000000000000000000000000000000000000..6c85460431e8df8d7593a978a0f7979d03da8a27 --- /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 861b1a90cb3380494dfdc1caef0c3ed181eaf8a3..4813228f521690a761b627776cc49101abc3ec45 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 43094475b4cba0abf89629e074728cfae0695f85..ff62e24fb2e2d081ee9d1547fecf795dd053debe 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 66cc61d87b651db8c340138ad6e66feca0581d74..163f88d41d665e364da4bb7e06755e22a18f5ba2 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 fb0dc27ee2bb6cdfdeb6411a9bb0941840793b26..b44be0822528ca2e5e2e6f6ea72afb0947f4fd13 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 442c33065f5e30f3b165b0fe70830a0a610c0bca..813196b7c2c38200b4450d1243a9613f7fc31503 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 0231ce3fb8265edfd91ff2ae8cad6852f6bf701e..d301c43c79b88c06b032bd8ff610d72cc3ad5a18 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 dca33ccd54a1e709dcc112447e99888fb0a877a0..07bcb40a44911360d5e07061835bb2bd36217715 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 2f040c613b63fd5f00cd82879a88f902a17560cc..e9db6b8c5c50dfcaa4627e5d61c846cc8c39a592 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 0000000000000000000000000000000000000000..e2a41fffaf2b1b095ddf2507ae31bc4b2d7b71fa --- /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 97e94191b0b68ae43855eadcb7aa06a539222dfb..d522731d7e418209cdd4a52e6993114c250331d9 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 ca7ae26b93a29442dca3a95ccb55db619abbfb1c..e546bb59249313f272411c3a35392b52acbd1390 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 55c596ded5bb3b726a72536c92c9fae686490631..82f6c38335bb575f8ce0218acc153e8d21d1d15c 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 0000000000000000000000000000000000000000..618b26864bc88c570b20c56d0f792bdf8ab0bcef --- /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 0000000000000000000000000000000000000000..4c5d6fe258884972636f8c9691e3e8ddce7dee13 --- /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 0000000000000000000000000000000000000000..119c4c5e19720264874553022e995513b2eb5d4d --- /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 e33a680eaebfc63ce6d6bae3fbaea8923dba8fce..3ecd12cfb310cc1a0970845a7be74a0466e74f14 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 431fcf53d78a004b7887e38ba871915f84a44ec5..1591b48abed3a29c3df316263c8999e67bb67140 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 0000000000000000000000000000000000000000..c0fcf95e6b25ba78350cfcb7d4eb64b14aab91b0 --- /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 0000000000000000000000000000000000000000..f1eb42bd5666d6e072e3868292169ce4037048ed --- /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 0000000000000000000000000000000000000000..8d7c770bf43a48f72888bcd8c3271019f31f4c87 --- /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 0000000000000000000000000000000000000000..e9a3572d0c97b43cae6307fd355c0c47647d8217 --- /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 f085bdc17f05ee226e7ff92328596d21fc729abe..ae443a24c74daa9f4e2546f080b2108b19370656 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 755c196bd836b73baefc6c98f801b114c300a0d6..868ca9062b0fcbd28a1c06b46f7fb3e083295a2f 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 0fc382a98fcb16639e80af215e142f6f99852b7e..81d812318533c8f3a2ece9fe9f9893975a0563d1 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 03613b3192bd7c1ed574a4650d36c166cbdea6b9..a0e20be77ae9971679b5e75af7f57ff503c9d57b 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 4b8ab14b047b1c71028d0aab211864bc301f11db..a185315cd55698450aa2cb627b5594e84555d4bd 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 19e05f5ad4de337025b982dc1210efd5856eee8e..dbe4e3231e9019a1dce8a20ef1c935be229d8cca 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 7875af497c0cc190add18cd7d60745d8575a81f3..9853a899ff6d9c8e1f9e265d93c216cc83a7b636 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 a1f796d92c9e553abe006613aa46082c6875dd06..0f16c8b1f60bbd3f0e69d52b461cbc9420fe6901 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 4b01f6a826f4bc220079c2893f15c492d61fbab1..3012f0594709ea00354558c84e526a77b73e224e 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 0000000000000000000000000000000000000000..31b3eed504536f1ba770b3ea7f6be3f57abcca6a --- /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 c9946755d39c5bc840f43ebd1881143b1bad0f7c..a5e364522680d05d4de4836d1520e9176108172b 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 421ea551cae0fd351f5d8ba67770ed972f363e9b..215adbd2221d74712996ba83daab1c5b295e9600 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 8a0ca0d2acd5da8d38e427c0f7567f731610256d..b2161ca71fef27812c2cdcfafe9afd8082dec11e 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 9592b13b9dd2a1558e0c86b9de6351ff28f98e16..4ce26bce16cf538268734325f6172ab4858a152b 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 6d1e0adcb89808511a834c4370d0ec4bebf7625f..60f29b4bded751aaa307f3ebc118e37435d7591a 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 c30bc4b97949ab6c429cb33c6d65019df09be8c8..92000033fdc6ea029f3e0c21febffa58579af396 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 f8ad6d4968f21be516181d80582095f69c6617de..f2056bcb3eb3379127fc61bd801dc21037d0e047 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 a86db3ff4ea37f2ff088b2484050637ef4739985..48122c72fd01b86c992a0319cc3b386c37664573 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 d7ec8345a08f1c4fb54caffa5ec2ceb81e36f855..1f933cf820025ed3f1eb7b16082ac27c83fd3612 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);