diff --git a/.gitignore b/.gitignore index 241871b63c885dc2809f1c76399c58daa2545ba4..93fe6cebf3419e49523b014edb9f3b44ac346ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,35 +26,26 @@ examples/swift examples/swift_mpi examples/fof examples/fof_mpi -examples/*/*.xmf -examples/*/*.h5 -examples/*/*.png -examples/*/*.mp4 -examples/*/*.txt -examples/*/*.dot -examples/*/restart/* -examples/*/used_parameters.yml -examples/*/unused_parameters.yml examples/*/*/*.xmf examples/*/*/*.png examples/*/*/*.mp4 examples/*/*/*.txt -examples/*/*/*.dot examples/*/*/*.rst examples/*/*/*.hdf5 -examples/*/snapshots* -examples/*/restart/* +examples/*/*/restart/* examples/*/*/used_parameters.yml -examples/*/err_file* -examples/*/out_file* examples/*/stf_output* examples/*/stf.* -examples/*/*.slurm examples/*/fof_output* examples/*/log* examples/*/*/unused_parameters.yml examples/*/*.mpg -examples/*/gravity_checks_*.dat +examples/*/*/gravity_checks_*.dat +examples/*/*/coolingtables.tar.gz +examples/*/*/coolingtables +examples/Cooling/CoolingRates/cooling_rates +examples/Cooling/CoolingRates/cooling_element_*.dat +examples/Cooling/CoolingRates/cooling_output.dat tests/testActivePair tests/testActivePair.sh @@ -110,6 +101,7 @@ tests/testInteractions tests/testInteractions.sh tests/testSymmetry tests/testMaths +tests/testRandom tests/testThreadpool tests/testParser tests/parser_output.yml @@ -148,6 +140,7 @@ tests/testCosmology tests/testOutputList tests/testCbrt tests/testFormat.sh +tests/testCooling theory/latex/swift.pdf theory/SPH/Kernels/kernels.pdf @@ -164,6 +157,7 @@ theory/Multipoles/potential_long.pdf theory/Multipoles/potential_short.pdf theory/Multipoles/force_short.pdf theory/Cosmology/cosmology.pdf +theory/Cooling/eagle_cooling.pdf m4/libtool.m4 m4/ltoptions.m4 diff --git a/INSTALL.swift b/INSTALL.swift index db6c6677b202e55e76114373f3e037cf50de10cc..4fa82c60838cf417961682318095f090f1bb709f 100644 --- a/INSTALL.swift +++ b/INSTALL.swift @@ -138,14 +138,15 @@ before you can build it. ===================== - - METIS: - a build of the METIS library can be optionally used to - optimize the load between MPI nodes (requires an MPI - library). This should be found in the standard installation - directories, or pointed at using the "--with-metis" - configuration option. In this case the top-level installation - directory of the METIS build should be given. Note to use - METIS you should supply at least "--with-metis". + - METIS/ParMETIS: + a build of the METIS or ParMETIS library should be used to + optimize the load between MPI nodes. This should be found in the + standard installation directories, or pointed at using the + "--with-metis" or "--with-parmetis" configuration options. + In this case the top-level installation directory of the build + should be given. Note to use METIS or ParMETIS you should supply at + least "--with-metis". ParMETIS is preferred over METIS when there + is a choice. - libNUMA: a build of the NUMA library can be used to pin the threads to @@ -154,6 +155,12 @@ before you can build it. distributing the threads among the different cores on each computing node. + Note that if you have libNUMA outside of the system include + directories it may fail to compile as the headers do not pass + the -Wstrict-prototype check of GCC. In that case you will need + to use --enable-compiler-warnings=yes configure option to stop + this being an error. + - tcmalloc / jemalloc / TBBmalloc: a build of the tcmalloc library (part of gperftools), jemalloc or TBBmalloc can be used be used to obtain faster and more diff --git a/Makefile.am b/Makefile.am index b98b855f9902c55782924888aeafa2185f19dc8a..c71cc8d00c797f0e2afc034cb1abfff7eba14c88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,10 @@ ACLOCAL_AMFLAGS = -I m4 # Show the way... -SUBDIRS = src examples doc tests tools +SUBDIRS = src argparse examples doc tests tools +if HAVEEAGLECOOLING +SUBDIRS += examples/Cooling/CoolingRates +endif # Non-standard files that should be part of the distribution. EXTRA_DIST = INSTALL.swift .clang-format format.sh diff --git a/README b/README index 747fe6da4766ca6b7ce272f3d4e15ff105a4b04a..7060589401d80e205733fb5770f258708263d966 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ Welcome to the cosmological hydrodynamical code ______ _________________ / ___/ | / / _/ ___/_ __/ - \__ \| | /| / // // /_ / / - ___/ /| |/ |/ // // __/ / / - /____/ |__/|__/___/_/ /_/ + \__ \| | /| / // // /_ / / + ___/ /| |/ |/ // // __/ / / + /____/ |__/|__/___/_/ /_/ SPH With Inter-dependent Fine-grained Tasking Website: www.swiftsim.com @@ -11,39 +11,63 @@ See INSTALL.swift for install instructions. -Usage: swift [OPTION]... PARAMFILE - swift_mpi [OPTION]... PARAMFILE +Usage: swift [options] [[--] param-file] + or: swift [options] param-file + or: swift_mpi [options] [[--] param-file] + or: swift_mpi [options] param-file -Valid options are: - -a Pin runners using processor affinity. - -c Run with cosmological time integration. - -C Run with cooling. - -d Dry run. Read the parameter file, allocate memory but does not read - the particles from ICs and exit before the start of time integration. - Allows user to check validity of parameter and IC files as well as memory limits. - -D Always drift all particles even the ones far from active particles. This emulates - Gadget-[23] and GIZMO's default behaviours. - -e Enable floating-point exceptions (debugging mode). - -f {int} Overwrite the CPU frequency (Hz) to be used for time measurements. - -g Run with an external gravitational potential. - -G Run with self-gravity. - -M Reconstruct the multipoles every time-step. - -n {int} Execute a fixed number of time steps. When unset use the time_end parameter to stop. - -o {str} Generate a default output parameter file. - -P {sec:par:val} Set parameter value and overwrites values read from the parameters file. Can be used more than once. - -r Continue using restart files. - -s Run with hydrodynamics. - -S Run with stars. - -b Run with stars feedback. - -t {int} The number of threads to use on each MPI rank. Defaults to 1 if not specified. - -T Print timers every time-step. - -v [12] Increase the level of verbosity: - 1: MPI-rank 0 writes, - 2: All MPI-ranks write. - -x Run with structure finding. - -y {int} Time-step frequency at which task graphs are dumped. - -Y {int} Time-step frequency at which threadpool tasks are dumped. - -h Print this help message and exit. +Parameters: -See the file parameter_example.yml for an example of parameter file. + -h, --help show this help message and exit + Simulation options: + + -b, --feedback Run with stars feedback. + -c, --cosmology Run with cosmological time integration. + --temperature Run with temperature calculation. + -C, --cooling Run with cooling (also switches on --with-temperature). + -D, --drift-all Always drift all particles even the ones + far from active particles. This emulates + Gadget-[23] and GIZMO's default behaviours. + -F, --star-formation Run with star formation. + -g, --external-gravity Run with an external gravitational potential. + -G, --self-gravity Run with self-gravity. + -M, --multipole-reconstruction Reconstruct the multipoles every time-step. + -s, --hydro Run with hydrodynamics. + -S, --stars Run with stars. + -x, --velociraptor Run with structure finding. + --limiter Run with time-step limiter. + + Control options: + + -a, --pin Pin runners using processor affinity. + -d, --dry-run Dry run. Read the parameter file, allocates + memory but does not read the particles + from ICs. Exits before the start of time + integration. Checks the validity of + parameters and IC files as well as memory + limits. + -e, --fpe Enable floating-point exceptions (debugging + mode). + -f, --cpu-frequency=<str> Overwrite the CPU frequency (Hz) to be + used for time measurements. + -n, --steps=<int> Execute a fixed number of time steps. + When unset use the time_end parameter + to stop. + -o, --output-params=<str> Generate a default output parameter + file. + -P, --param=<str> Set parameter value, overiding the value + read from the parameter file. Can be used + more than once {sec:par:value}. + -r, --restart Continue using restart files. + -t, --threads=<int> The number of threads to use on each MPI + rank. Defaults to 1 if not specified. + -T, --timers=<int> Print timers every time-step. + -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs + from all ranks. + -y, --task-dumps=<int> Time-step frequency at which task analysis + files and/or tasks are dumped. + -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool + tasks are dumped. + +See the file examples/parameter_example.yml for an example of parameter file. diff --git a/README.md b/README.md index 25f8e14b5b881149270a7e7b8a14ffe9535149ef..c160a21adb921da79ae660196d5fa33e20af74fc 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ are highly encouraged. Welcome to the cosmological hydrodynamical code ______ _________________ / ___/ | / / _/ ___/_ __/ - \__ \| | /| / // // /_ / / - ___/ /| |/ |/ // // __/ / / - /____/ |__/|__/___/_/ /_/ + \__ \| | /| / // // /_ / / + ___/ /| |/ |/ // // __/ / / + /____/ |__/|__/___/_/ /_/ SPH With Inter-dependent Fine-grained Tasking Website: www.swiftsim.com @@ -59,38 +59,63 @@ are highly encouraged. See INSTALL.swift for install instructions. -Usage: swift [OPTION]... PARAMFILE - swift_mpi [OPTION]... PARAMFILE - -Valid options are: - -a Pin runners using processor affinity. - -c Run with cosmological time integration. - -C Run with cooling. - -d Dry run. Read the parameter file, allocate memory but does not read - the particles from ICs and exit before the start of time integration. - Allows user to check validity of parameter and IC files as well as memory limits. - -D Always drift all particles even the ones far from active particles. This emulates - Gadget-[23] and GIZMO's default behaviours. - -e Enable floating-point exceptions (debugging mode). - -f {int} Overwrite the CPU frequency (Hz) to be used for time measurements. - -g Run with an external gravitational potential. - -G Run with self-gravity. - -M Reconstruct the multipoles every time-step. - -n {int} Execute a fixed number of time steps. When unset use the time_end parameter to stop. - -o {str} Generate a default output parameter file. - -P {sec:par:val} Set parameter value and overwrites values read from the parameters file. Can be used more than once. - -r Continue using restart files. - -s Run with hydrodynamics. - -S Run with stars. - -t {int} The number of threads to use on each MPI rank. Defaults to 1 if not specified. - -T Print timers every time-step. - -v [12] Increase the level of verbosity: - 1: MPI-rank 0 writes, - 2: All MPI-ranks write. - -x Run with structure finding. - -y {int} Time-step frequency at which task graphs are dumped. - -Y {int} Time-step frequency at which threadpool tasks are dumped. - -h Print this help message and exit. +Usage: swift [options] [[--] param-file] + or: swift [options] param-file + or: swift_mpi [options] [[--] param-file] + or: swift_mpi [options] param-file + +Parameters: + + -h, --help show this help message and exit + + Simulation options: + + -b, --feedback Run with stars feedback. + -c, --cosmology Run with cosmological time integration. + --temperature Run with temperature calculation. + -C, --cooling Run with cooling (also switches on --with-temperature). + -D, --drift-all Always drift all particles even the ones + far from active particles. This emulates + Gadget-[23] and GIZMO's default behaviours. + -F, --star-formation Run with star formation. + -g, --external-gravity Run with an external gravitational potential. + -G, --self-gravity Run with self-gravity. + -M, --multipole-reconstruction Reconstruct the multipoles every time-step. + -s, --hydro Run with hydrodynamics. + -S, --stars Run with stars. + -x, --velociraptor Run with structure finding. + --limiter Run with time-step limiter. + + Control options: + + -a, --pin Pin runners using processor affinity. + -d, --dry-run Dry run. Read the parameter file, allocates + memory but does not read the particles + from ICs. Exits before the start of time + integration. Checks the validity of + parameters and IC files as well as memory + limits. + -e, --fpe Enable floating-point exceptions (debugging + mode). + -f, --cpu-frequency=<str> Overwrite the CPU frequency (Hz) to be + used for time measurements. + -n, --steps=<int> Execute a fixed number of time steps. + When unset use the time_end parameter + to stop. + -o, --output-params=<str> Generate a default output parameter + file. + -P, --param=<str> Set parameter value, overiding the value + read from the parameter file. Can be used + more than once {sec:par:value}. + -r, --restart Continue using restart files. + -t, --threads=<int> The number of threads to use on each MPI + rank. Defaults to 1 if not specified. + -T, --timers=<int> Print timers every time-step. + -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs + from all ranks. + -y, --task-dumps=<int> Time-step frequency at which task analysis + files and/or tasks are dumped. + -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool + tasks are dumped. See the file examples/parameter_example.yml for an example of parameter file. -``` diff --git a/argparse/FAQs.md b/argparse/FAQs.md new file mode 100644 index 0000000000000000000000000000000000000000..c760807070b192c33e624e3f98af4cd24fe16fca --- /dev/null +++ b/argparse/FAQs.md @@ -0,0 +1,36 @@ +# FAQs + +## Why removing parsed command-line switches/options? + +It destroys the original `argv` array, not compatible with other arguments parsing +library. + +This is because this library is used for short-lived programs, e.g. cli tools +at beginning. It's very convenient to process remain arguments if we remove +parsed command-line arguments, e.g. `<comamnd> [-[s]|--switch]... arguments`. + +If you want keep original `argc/argv`, you can make a copy, then pass them to +`argparse_parse`, e.g. + +```c +int copy_argc = argc; +const char **copy_argv = argv; +copy_argv = malloc(copy_argc * sizeof(char *)); +for (int i = 0; i < argc; i++) { + copy_argv[i] = (char *)argv[i]; +} +argparse_parse(&argparse, copy_argc, copy_argv); +``` + +Issues: + +- https://github.com/cofyc/argparse/issues/3 +- https://github.com/cofyc/argparse/issues/9 + +## Why using `intptr_t` to hold associated data? Why not `void *`? + +I choose `intptr_t` because it's a integer type which also can be used to hold +a pointer value. Most of the time, we only need a integer to hold +user-provided value, see `OPT_BIT` as example. If you want to provide a pointer +which points to a large amount of data, you can cast it to `intptr_t` and cast +it back to original pointer in callback function. diff --git a/argparse/LICENSE b/argparse/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..3c777497645ca9998899db5d8a8041e9831a4604 --- /dev/null +++ b/argparse/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2013 Yecheng Fu <cofyc.jackson@gmail.com> + +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. diff --git a/argparse/Makefile.am b/argparse/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..2fa6fb9e9ef4c014697a2c434cd86741cc74d79c --- /dev/null +++ b/argparse/Makefile.am @@ -0,0 +1,28 @@ +# This file is part of SWIFT. +# Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk) +# +# 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/>. + +lib_LTLIBRARIES = libargparse.la + +include_HEADERS = argparse.h + +AM_SOURCES = argparse.c + +# Sources and flags for regular library +libargparse_la_SOURCES = $(AM_SOURCES) +libargparse_la_CFLAGS = $(AM_CFLAGS) +libargparse_la_LDFLAGS = $(AM_LDFLAGS) + +EXTRA_DIST = LICENSE README.md diff --git a/argparse/OWNERS b/argparse/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..8cad69dd488010bcaa66ed80d5e3d425f647064c --- /dev/null +++ b/argparse/OWNERS @@ -0,0 +1,2 @@ +approvers: +- cofyc diff --git a/argparse/README.md b/argparse/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ddf66b4777e9baaf68be270df3e8994cb96a2998 --- /dev/null +++ b/argparse/README.md @@ -0,0 +1,103 @@ +# argparse [](https://travis-ci.org/cofyc/argparse) + +argparse - A command line arguments parsing library in C (compatible with C++). + +## Description + +This module is inspired by parse-options.c (git) and python's argparse +module. + +Arguments parsing is common task in cli program, but traditional `getopt` +libraries are not easy to use. This library provides high-level arguments +parsing solutions. + +The program defines what arguments it requires, and `argparse` will figure +out how to parse those out of `argc` and `argv`, it also automatically +generates help and usage messages and issues errors when users give the +program invalid arguments. + +## Features + + - handles both optional and positional arguments + - produces highly informative usage messages + - issues errors when given invalid arguments + +There are basically three types of options: + + - boolean options + - options with mandatory argument + - options with optional argument + +There are basically two forms of options: + + - short option consist of one dash (`-`) and one alphanumeric character. + - long option begin with two dashes (`--`) and some alphanumeric characters. + +Short options may be bundled, e.g. `-a -b` can be specified as `-ab`. + +Options are case-sensitive. + +Options and non-option arguments can clearly be separated using the `--` option. + +## Examples + +```c +#include "argparse.h" + +static const char *const usage[] = { + "test_argparse [options] [[--] args]", + "test_argparse [options]", + NULL, +}; + +#define PERM_READ (1<<0) +#define PERM_WRITE (1<<1) +#define PERM_EXEC (1<<2) + +int +main(int argc, const char **argv) +{ + int force = 0; + int test = 0; + int num = 0; + const char *path = NULL; + int perms = 0; + struct argparse_option options[] = { + OPT_HELP(), + OPT_GROUP("Basic options"), + OPT_BOOLEAN('f', "force", &force, "force to do"), + OPT_BOOLEAN('t', "test", &test, "test only"), + OPT_STRING('p', "path", &path, "path to read"), + OPT_INTEGER('n', "num", &num, "selected num"), + OPT_GROUP("Bits options"), + OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), + OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE), + OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, usage, 0); + argparse_describe(&argparse, "\nA brief description of what the program does and how it works.", "\nAdditional description of the program after the description of the arguments."); + argc = argparse_parse(&argparse, argc, argv); + if (force != 0) + printf("force: %d\n", force); + if (test != 0) + printf("test: %d\n", test); + if (path != NULL) + printf("path: %s\n", path); + if (num != 0) + printf("num: %d\n", num); + if (argc != 0) { + printf("argc: %d\n", argc); + int i; + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, *(argv + i)); + } + } + if (perms) { + printf("perms: %d\n", perms); + } + return 0; +} +``` diff --git a/argparse/argparse.c b/argparse/argparse.c new file mode 100644 index 0000000000000000000000000000000000000000..60d03518e3e0a82e035e0fe81abdc7ca27e13ef7 --- /dev/null +++ b/argparse/argparse.c @@ -0,0 +1,382 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com> + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#include "../config.h" + +#include "argparse.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define OPT_UNSET 1 +#define OPT_LONG (1 << 1) + +static const char *prefix_skip(const char *str, const char *prefix) { + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int prefix_cmp(const char *str, const char *prefix) { + for (;; str++, prefix++) + if (!*prefix) { + return 0; + } else if (*str != *prefix) { + return (unsigned char)*prefix - (unsigned char)*str; + } +} + +static void argparse_error(struct argparse *self, + const struct argparse_option *opt, + const char *reason, int flags) { + (void)self; + if (flags & OPT_LONG) { + fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); + } else { + fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); + } + exit(1); +} + +static int argparse_getvalue(struct argparse *self, + const struct argparse_option *opt, int flags) { + const char *s = NULL; + if (!opt->value) goto skipped; + switch (opt->type) { + case ARGPARSE_OPT_BOOLEAN: + if (flags & OPT_UNSET) { + *(int *)opt->value = *(int *)opt->value - 1; + } else { + *(int *)opt->value = *(int *)opt->value + 1; + } + if (*(int *)opt->value < 0) { + *(int *)opt->value = 0; + } + break; + case ARGPARSE_OPT_BIT: + if (flags & OPT_UNSET) { + *(int *)opt->value &= ~opt->data; + } else { + *(int *)opt->value |= opt->data; + } + break; + case ARGPARSE_OPT_STRING: + if (self->optvalue) { + *(const char **)opt->value = self->optvalue; + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(const char **)opt->value = *++self->argv; + } else { + argparse_error(self, opt, "requires a value", flags); + } + break; + case ARGPARSE_OPT_INTEGER: + errno = 0; + if (self->optvalue) { + *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno) argparse_error(self, opt, strerror(errno), flags); + if (s[0] != '\0') + argparse_error(self, opt, "expects an integer value", flags); + break; + case ARGPARSE_OPT_FLOAT: + errno = 0; + if (self->optvalue) { + *(float *)opt->value = strtof(self->optvalue, (char **)&s); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(float *)opt->value = strtof(*++self->argv, (char **)&s); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno) argparse_error(self, opt, strerror(errno), flags); + if (s[0] != '\0') + argparse_error(self, opt, "expects a numerical value", flags); + break; + default: + assert(0); + } + +skipped: + if (opt->callback) { + return opt->callback(self, opt); + } + + return 0; +} + +static void argparse_options_check(const struct argparse_option *options) { + for (; options->type != ARGPARSE_OPT_END; options++) { + switch (options->type) { + case ARGPARSE_OPT_END: + case ARGPARSE_OPT_BOOLEAN: + case ARGPARSE_OPT_BIT: + case ARGPARSE_OPT_INTEGER: + case ARGPARSE_OPT_FLOAT: + case ARGPARSE_OPT_STRING: + case ARGPARSE_OPT_GROUP: + continue; + default: + fprintf(stderr, "wrong option type: %d", options->type); + break; + } + } +} + +static int argparse_short_opt(struct argparse *self, + const struct argparse_option *options) { + for (; options->type != ARGPARSE_OPT_END; options++) { + if (options->short_name == *self->optvalue) { + self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; + return argparse_getvalue(self, options, 0); + } + } + return -2; +} + +static int argparse_long_opt(struct argparse *self, + const struct argparse_option *options) { + for (; options->type != ARGPARSE_OPT_END; options++) { + const char *rest; + int opt_flags = 0; + if (!options->long_name) continue; + + rest = prefix_skip(self->argv[0] + 2, options->long_name); + if (!rest) { + // negation disabled? + if (options->flags & OPT_NONEG) { + continue; + } + // only OPT_BOOLEAN/OPT_BIT supports negation + if (options->type != ARGPARSE_OPT_BOOLEAN && + options->type != ARGPARSE_OPT_BIT) { + continue; + } + + if (prefix_cmp(self->argv[0] + 2, "no-")) { + continue; + } + rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); + if (!rest) continue; + opt_flags |= OPT_UNSET; + } + if (*rest) { + if (*rest != '=') continue; + self->optvalue = rest + 1; + } + return argparse_getvalue(self, options, opt_flags | OPT_LONG); + } + return -2; +} + +int argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags) { + memset(self, 0, sizeof(*self)); + self->options = options; + self->usages = usages; + self->flags = flags; + self->description = NULL; + self->epilog = NULL; + return 0; +} + +void argparse_describe(struct argparse *self, const char *description, + const char *epilog) { + self->description = description; + self->epilog = epilog; +} + +int argparse_parse(struct argparse *self, int argc, const char **argv) { + self->argc = argc - 1; + self->argv = argv + 1; + self->out = argv; + + argparse_options_check(self->options); + + for (; self->argc; self->argc--, self->argv++) { + const char *arg = self->argv[0]; + if (arg[0] != '-' || !arg[1]) { + if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { + goto end; + } + // if it's not option or is a single char '-', copy verbatim + self->out[self->cpidx++] = self->argv[0]; + continue; + } + // short option + if (arg[1] != '-') { + self->optvalue = arg + 1; + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + while (self->optvalue) { + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + } + continue; + } + // if '--' presents + if (!arg[2]) { + self->argc--; + self->argv++; + break; + } + // long option + switch (argparse_long_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + continue; + + unknown: + fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); + argparse_usage(self); + exit(1); + } + +end: + memmove(self->out + self->cpidx, self->argv, self->argc * sizeof(*self->out)); + self->out[self->cpidx + self->argc] = NULL; + + return self->cpidx + self->argc; +} + +void argparse_usage(struct argparse *self) { + if (self->usages) { + fprintf(stdout, "Usage: %s\n", *self->usages++); + while (*self->usages && **self->usages) + fprintf(stdout, " or: %s\n", *self->usages++); + } else { + fprintf(stdout, "Usage:\n"); + } + + // print description + if (self->description) fprintf(stdout, "%s\n", self->description); + + fputc('\n', stdout); + + const struct argparse_option *options; + + // figure out best width + size_t usage_opts_width = 0; + size_t len; + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + len = 0; + if ((options)->short_name) { + len += 2; + } + if ((options)->short_name && (options)->long_name) { + len += 2; // separator ", " + } + if ((options)->long_name) { + len += strlen((options)->long_name) + 2; + } + if (options->type == ARGPARSE_OPT_INTEGER) { + len += strlen("=<int>"); + } + if (options->type == ARGPARSE_OPT_FLOAT) { + len += strlen("=<flt>"); + } else if (options->type == ARGPARSE_OPT_STRING) { + len += strlen("=<str>"); + } + len = (len + 3) - ((len + 3) & 3); + if (usage_opts_width < len) { + usage_opts_width = len; + } + } + usage_opts_width += 4; // 4 spaces prefix + + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + size_t pos = 0; + int pad = 0; + if (options->type == ARGPARSE_OPT_GROUP) { + fputc('\n', stdout); + fprintf(stdout, "%s", options->help); + fputc('\n', stdout); + continue; + } + pos = fprintf(stdout, " "); + if (options->short_name) { + pos += fprintf(stdout, "-%c", options->short_name); + } + if (options->long_name && options->short_name) { + pos += fprintf(stdout, ", "); + } + if (options->long_name) { + pos += fprintf(stdout, "--%s", options->long_name); + } + if (options->type == ARGPARSE_OPT_INTEGER) { + pos += fprintf(stdout, "=<int>"); + } + if (options->type == ARGPARSE_OPT_FLOAT) { + pos += fprintf(stdout, "=<flt>"); + } else if (options->type == ARGPARSE_OPT_STRING) { + pos += fprintf(stdout, "=<str>"); + } + if (pos <= usage_opts_width) { + pad = usage_opts_width - pos; + } else { + fputc('\n', stdout); + pad = usage_opts_width; + } + if (options->help != NULL && strlen(options->help) > 0) { + char *str = strdup(options->help); + char *token = strtok(str, " "); + fprintf(stdout, "%*s%s ", pad + 2, "", token); + int count = strlen(token); + int dangling = 1; + while ((token = strtok(NULL, " ")) != NULL) { + if (count == 0) { + fprintf(stdout, "%*s", (int)pos + pad + 2, ""); + dangling = 1; + } + printf("%s ", token); + count += strlen(token); + if (count > 30) { + count = 0; + fprintf(stdout, "\n"); + dangling = 0; + } + } + if (dangling) fprintf(stdout, "\n"); + free(str); + } else { + fprintf(stdout, "\n"); + } + } + + // print epilog + if (self->epilog) fprintf(stdout, "%s\n", self->epilog); +} + +int argparse_help_cb(struct argparse *self, + const struct argparse_option *option) { + (void)option; + argparse_usage(self); + exit(0); +} diff --git a/argparse/argparse.h b/argparse/argparse.h new file mode 100644 index 0000000000000000000000000000000000000000..186214b4bc90cea90ef141380bf0017cc50af128 --- /dev/null +++ b/argparse/argparse.h @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com> + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#ifndef ARGPARSE_H +#define ARGPARSE_H + +/* For c++ compatibility */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +struct argparse; +struct argparse_option; + +typedef int argparse_callback(struct argparse *self, + const struct argparse_option *option); + +enum argparse_flag { + ARGPARSE_STOP_AT_NON_OPTION = 1, +}; + +enum argparse_option_type { + /* special */ + ARGPARSE_OPT_END, + ARGPARSE_OPT_GROUP, + /* options with no arguments */ + ARGPARSE_OPT_BOOLEAN, + ARGPARSE_OPT_BIT, + /* options with arguments (optional or required) */ + ARGPARSE_OPT_INTEGER, + ARGPARSE_OPT_FLOAT, + ARGPARSE_OPT_STRING, +}; + +enum argparse_option_flags { + OPT_NONEG = 1, /* disable negation */ +}; + +/** + * argparse option + * + * `type`: + * holds the type of the option, you must have an ARGPARSE_OPT_END last in + * your array. + * + * `short_name`: + * the character to use as a short option name, '\0' if none. + * + * `long_name`: + * the long option name, without the leading dash, NULL if none. + * + * `value`: + * stores pointer to the value to be filled. + * + * `help`: + * the short help message associated to what the option does. + * Must never be NULL (except for ARGPARSE_OPT_END). + * + * `callback`: + * function is called when corresponding argument is parsed. + * + * `data`: + * associated data. Callbacks can use it like they want. + * + * `flags`: + * option flags. + */ +struct argparse_option { + enum argparse_option_type type; + const char short_name; + const char *long_name; + void *value; + const char *help; + argparse_callback *callback; + intptr_t data; + int flags; +}; + +/** + * argpparse + */ +struct argparse { + // user supplied + const struct argparse_option *options; + const char *const *usages; + int flags; + const char *description; // a description after usage + const char *epilog; // a description at the end + // internal context + int argc; + const char **argv; + const char **out; + int cpidx; + const char *optvalue; // current option value +}; + +// built-in callbacks +int argparse_help_cb(struct argparse *self, + const struct argparse_option *option); + +// built-in option macros +#define OPT_END() \ + { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } +#define OPT_BOOLEAN(...) \ + { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } +#define OPT_BIT(...) \ + { ARGPARSE_OPT_BIT, __VA_ARGS__ } +#define OPT_INTEGER(...) \ + { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } +#define OPT_FLOAT(...) \ + { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } +#define OPT_STRING(...) \ + { ARGPARSE_OPT_STRING, __VA_ARGS__ } +#define OPT_GROUP(h) \ + { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } +#define OPT_HELP() \ + OPT_BOOLEAN('h', "help", NULL, "show this help message and exit", \ + argparse_help_cb, 0, 0) + +int argparse_init(struct argparse *self, struct argparse_option *options, + const char *const *usages, int flags); +void argparse_describe(struct argparse *self, const char *description, + const char *epilog); +int argparse_parse(struct argparse *self, int argc, const char **argv); +void argparse_usage(struct argparse *self); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/argparse/tap-functions b/argparse/tap-functions new file mode 100644 index 0000000000000000000000000000000000000000..84f700e644c0c09246ca747ce5c22cc884dfff6c --- /dev/null +++ b/argparse/tap-functions @@ -0,0 +1,445 @@ +#!/bin/bash + + +_version='1.02' + +_plan_set=0 +_no_plan=0 +_skip_all=0 +_test_died=0 +_expected_tests=0 +_executed_tests=0 +_failed_tests=0 +TODO= + + +usage(){ + cat <<'USAGE' +tap-functions: A TAP-producing BASH library + +PLAN: + plan_no_plan + plan_skip_all [REASON] + plan_tests NB_TESTS + +TEST: + ok RESULT [NAME] + okx COMMAND + is RESULT EXPECTED [NAME] + isnt RESULT EXPECTED [NAME] + like RESULT PATTERN [NAME] + unlike RESULT PATTERN [NAME] + pass [NAME] + fail [NAME] + +SKIP: + skip [CONDITION] [REASON] [NB_TESTS=1] + + skip $feature_not_present "feature not present" 2 || { + is $a "a" + is $b "b" + } + +TODO: + Specify TODO mode by setting $TODO: + TODO="not implemented yet" + ok $result "some not implemented test" + unset TODO + +OTHER: + diag MSG + +EXAMPLE: + #!/bin/bash + + . tap-functions + + plan_tests 7 + + me=$USER + is $USER $me "I am myself" + like $HOME $me "My home is mine" + like "`id`" $me "My id matches myself" + + /bin/ls $HOME 1>&2 + ok $? "/bin/ls $HOME" + # Same thing using okx shortcut + okx /bin/ls $HOME + + [[ "`id -u`" != "0" ]] + i_am_not_root=$? + skip $i_am_not_root "Must be root" || { + okx ls /root + } + + TODO="figure out how to become root..." + okx [ "$HOME" == "/root" ] + unset TODO +USAGE + exit +} + +opt= +set_u= +while getopts ":sx" opt ; do + case $_opt in + u) set_u=1 ;; + *) usage ;; + esac +done +shift $(( OPTIND - 1 )) +# Don't allow uninitialized variables if requested +[[ -n "$set_u" ]] && set -u +unset opt set_u + +# Used to call _cleanup on shell exit +trap _exit EXIT + + + +plan_no_plan(){ + (( _plan_set != 0 )) && "You tried to plan twice!" + + _plan_set=1 + _no_plan=1 + + return 0 +} + + +plan_skip_all(){ + local reason=${1:-''} + + (( _plan_set != 0 )) && _die "You tried to plan twice!" + + _print_plan 0 "Skip $reason" + + _skip_all=1 + _plan_set=1 + _exit 0 + + return 0 +} + + +plan_tests(){ + local tests=${1:?} + + (( _plan_set != 0 )) && _die "You tried to plan twice!" + (( tests == 0 )) && _die "You said to run 0 tests! You've got to run something." + + _print_plan $tests + _expected_tests=$tests + _plan_set=1 + + return $tests +} + + +_print_plan(){ + local tests=${1:?} + local directive=${2:-''} + + echo -n "1..$tests" + [[ -n "$directive" ]] && echo -n " # $directive" + echo +} + + +pass(){ + local name=$1 + ok 0 "$name" +} + + +fail(){ + local name=$1 + ok 1 "$name" +} + + +# This is the workhorse method that actually +# prints the tests result. +ok(){ + local result=${1:?} + local name=${2:-''} + + (( _plan_set == 0 )) && _die "You tried to run a test without a plan! Gotta have a plan." + + _executed_tests=$(( $_executed_tests + 1 )) + + if [[ -n "$name" ]] ; then + if _matches "$name" "^[0-9]+$" ; then + diag " You named your test '$name'. You shouldn't use numbers for your test names." + diag " Very confusing." + fi + fi + + if (( result != 0 )) ; then + echo -n "not " + _failed_tests=$(( _failed_tests + 1 )) + fi + echo -n "ok $_executed_tests" + + if [[ -n "$name" ]] ; then + local ename=${name//\#/\\#} + echo -n " - $ename" + fi + + if [[ -n "$TODO" ]] ; then + echo -n " # TODO $TODO" ; + if (( result != 0 )) ; then + _failed_tests=$(( _failed_tests - 1 )) + fi + fi + + echo + if (( result != 0 )) ; then + local file='tap-functions' + local func= + local line= + + local i=0 + local bt=$(caller $i) + while _matches "$bt" "tap-functions$" ; do + i=$(( $i + 1 )) + bt=$(caller $i) + done + local backtrace= + eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\"")) + + local t= + [[ -n "$TODO" ]] && t="(TODO) " + + if [[ -n "$name" ]] ; then + diag " Failed ${t}test '$name'" + diag " in $backtrace" + else + diag " Failed ${t}test in $backtrace" + fi + fi + + return $result +} + + +okx(){ + local command="$@" + + local line= + diag "Output of '$command':" + $command | while read line ; do + diag "$line" + done + ok ${PIPESTATUS[0]} "$command" +} + + +_equals(){ + local result=${1:?} + local expected=${2:?} + + if [[ "$result" == "$expected" ]] ; then + return 0 + else + return 1 + fi +} + + +# Thanks to Aaron Kangas for the patch to allow regexp matching +# under bash < 3. + _bash_major_version=${BASH_VERSION%%.*} +_matches(){ + local result=${1:?} + local pattern=${2:?} + + if [[ -z "$result" || -z "$pattern" ]] ; then + return 1 + else + if (( _bash_major_version >= 3 )) ; then + eval '[[ "$result" =~ "$pattern" ]]' + else + echo "$result" | egrep -q "$pattern" + fi + fi +} + + +_is_diag(){ + local result=${1:?} + local expected=${2:?} + + diag " got: '$result'" + diag " expected: '$expected'" +} + + +is(){ + local result=${1:?} + local expected=${2:?} + local name=${3:-''} + + _equals "$result" "$expected" + (( $? == 0 )) + ok $? "$name" + local r=$? + (( r != 0 )) && _is_diag "$result" "$expected" + return $r +} + + +isnt(){ + local result=${1:?} + local expected=${2:?} + local name=${3:-''} + + _equals "$result" "$expected" + (( $? != 0 )) + ok $? "$name" + local r=$? + (( r != 0 )) && _is_diag "$result" "$expected" + return $r +} + + +like(){ + local result=${1:?} + local pattern=${2:?} + local name=${3:-''} + + _matches "$result" "$pattern" + (( $? == 0 )) + ok $? "$name" + local r=$? + (( r != 0 )) && diag " '$result' doesn't match '$pattern'" + return $r +} + + +unlike(){ + local result=${1:?} + local pattern=${2:?} + local name=${3:-''} + + _matches "$result" "$pattern" + (( $? != 0 )) + ok $? "$name" + local r=$? + (( r != 0 )) && diag " '$result' matches '$pattern'" + return $r +} + + +skip(){ + local condition=${1:?} + local reason=${2:-''} + local n=${3:-1} + + if (( condition == 0 )) ; then + local i= + for (( i=0 ; i<$n ; i++ )) ; do + _executed_tests=$(( _executed_tests + 1 )) + echo "ok $_executed_tests # skip: $reason" + done + return 0 + else + return + fi +} + + +diag(){ + local msg=${1:?} + + if [[ -n "$msg" ]] ; then + echo "# $msg" + fi + + return 1 +} + + +_die(){ + local reason=${1:-'<unspecified error>'} + + echo "$reason" >&2 + _test_died=1 + _exit 255 +} + + +BAIL_OUT(){ + local reason=${1:-''} + + echo "Bail out! $reason" >&2 + _exit 255 +} + + +_cleanup(){ + local rc=0 + + if (( _plan_set == 0 )) ; then + diag "Looks like your test died before it could output anything." + return $rc + fi + + if (( _test_died != 0 )) ; then + diag "Looks like your test died just after $_executed_tests." + return $rc + fi + + if (( _skip_all == 0 && _no_plan != 0 )) ; then + _print_plan $_executed_tests + fi + + local s= + if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then + s= ; (( _expected_tests > 1 )) && s=s + local extra=$(( _executed_tests - _expected_tests )) + diag "Looks like you planned $_expected_tests test$s but ran $extra extra." + rc=-1 ; + fi + + if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then + s= ; (( _expected_tests > 1 )) && s=s + diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests." + fi + + if (( _failed_tests > 0 )) ; then + s= ; (( _failed_tests > 1 )) && s=s + diag "Looks like you failed $_failed_tests test$s of $_executed_tests." + fi + + return $rc +} + + +_exit_status(){ + if (( _no_plan != 0 || _plan_set == 0 )) ; then + return $_failed_tests + fi + + if (( _expected_tests < _executed_tests )) ; then + return $(( _executed_tests - _expected_tests )) + fi + + return $(( _failed_tests + ( _expected_tests - _executed_tests ))) +} + + +_exit(){ + local rc=${1:-''} + if [[ -z "$rc" ]] ; then + _exit_status + rc=$? + fi + + _cleanup + local alt_rc=$? + (( alt_rc != 0 )) && rc=$alt_rc + trap - EXIT + exit $rc +} + diff --git a/argparse/test.sh b/argparse/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..192357d3cc43947593b1db50f2ff46b3092340e6 --- /dev/null +++ b/argparse/test.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +. tap-functions +plan_no_plan + +is "$(./test_argparse -f --path=/path/to/file a 2>&1)" 'force: 1 +path: /path/to/file +argc: 1 +argv[0]: a' + +is "$(./test_argparse -f -f --force --no-force 2>&1)" 'force: 2' + +is "$(./test_argparse -i 2>&1)" 'error: option `-i` requires a value' + +is "$(./test_argparse -i 2 2>&1)" 'int_num: 2' + +is "$(./test_argparse -i2 2>&1)" 'int_num: 2' + +is "$(./test_argparse -ia 2>&1)" 'error: option `-i` expects an integer value' + +is "$(./test_argparse -i 0xFFFFFFFFFFFFFFFFF 2>&1)" \ + 'error: option `-i` Numerical result out of range' + +is "$(./test_argparse -s 2.4 2>&1)" 'flt_num: 2.4' + +is "$(./test_argparse -s2.4 2>&1)" 'flt_num: 2.4' + +is "$(./test_argparse -sa 2>&1)" 'error: option `-s` expects a numerical value' + +is "$(./test_argparse -s 1e999 2>&1)" \ + 'error: option `-s` Numerical result out of range' + +is "$(./test_argparse -f -- do -f -h 2>&1)" 'force: 1 +argc: 3 +argv[0]: do +argv[1]: -f +argv[2]: -h' + +is "$(./test_argparse -tf 2>&1)" 'force: 1 +test: 1' + +is "$(./test_argparse --read --write 2>&1)" 'perms: 3' + +is "$(./test_argparse -h)" 'Usage: test_argparse [options] [[--] args] + or: test_argparse [options] + +A brief description of what the program does and how it works. + + -h, --help show this help message and exit + +Basic options + -f, --force force to do + -t, --test test only + -p, --path=<str> path to read + -i, --int=<int> selected integer + -s, --float=<flt> selected float + +Bits options + --read read perm + --write write perm + --exec exec perm + +Additional description of the program after the description of the arguments.' diff --git a/argparse/test_argparse.c b/argparse/test_argparse.c new file mode 100644 index 0000000000000000000000000000000000000000..5f411833aafa603d085258f11b8bbb35ff1c6d39 --- /dev/null +++ b/argparse/test_argparse.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "argparse.h" + +static const char *const usages[] = { + "test_argparse [options] [[--] args]", + "test_argparse [options]", + NULL, +}; + +#define PERM_READ (1 << 0) +#define PERM_WRITE (1 << 1) +#define PERM_EXEC (1 << 2) + +struct stuff { + const char *path[10]; + int npath; +}; + +static int callback(struct argparse *self, const struct argparse_option *opt) { + printf("Called back... %s\n", *(char **)opt->value); + struct stuff *data = (struct stuff *)opt->data; + data->path[data->npath] = *(char **)opt->value; + data->npath++; + return 1; +} + +int main(int argc, const char **argv) { + int force = 0; + int self_gravity = 0; + int int_num = 0; + float flt_num = 0.f; + struct stuff data; + data.npath = 0; + data.path[0] = NULL; + const char *buffer; + int perms = 0; + int npath; + + struct argparse_option options[] = { + OPT_HELP(), + OPT_GROUP("Basic options"), + OPT_BOOLEAN('f', "force", &force, "force to do", NULL, 0, 0), + OPT_BOOLEAN(0, "self-gravity", &self_gravity, "use self gravity", NULL, 0, + 0), + OPT_STRING('P', "path", &buffer, "path to read", &callback, + (intptr_t)&data, 0), + OPT_INTEGER('i', "int", &int_num, "selected integer", NULL, 0, 0), + OPT_FLOAT('s', "float", &flt_num, "selected float", NULL, 0, 0), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, usages, 0); + argparse_describe( + &argparse, + "\nA brief description of what the program does and how it works.", + "\nAdditional description of the program after the description of the " + "arguments."); + argc = argparse_parse(&argparse, argc, argv); + if (force != 0) printf("force: %d\n", force); + if (self_gravity != 0) printf("self_gravity: %d\n", self_gravity); + if (data.npath > 0) { + for (int i = 0; i < data.npath; i++) printf("path: %s\n", data.path[i]); + } + if (int_num != 0) printf("int_num: %d\n", int_num); + if (flt_num != 0) printf("flt_num: %g\n", flt_num); + if (argc != 0) { + printf("argc: %d\n", argc); + int i; + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, *(argv + i)); + } + } + if (perms) { + printf("perms: %d\n", perms); + } + return 0; +} diff --git a/configure.ac b/configure.ac index 656690d624e067b899ce3960831d36a5e69d776d..521d5aa0bc743f70cae64d210c33cecff47364ae 100644 --- a/configure.ac +++ b/configure.ac @@ -16,9 +16,25 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Init the project. -AC_INIT([SWIFT],[0.7.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) +AC_INIT([SWIFT],[0.8.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) swift_config_flags="$*" +# 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/space.c]) AC_CONFIG_AUX_DIR([.]) @@ -88,9 +104,18 @@ if test "$enable_ipo" = "yes"; then elif test "$ax_cv_c_compiler_vendor" = "gnu"; then CFLAGS="$CFLAGS -flto" LDFLAGS="$LDFLAGS -flto" + AX_COMPARE_VERSION($ax_cv_c_compiler_version, [ge], [5.0.0], + [ + : ${AR="gcc-ar"} + : ${RANLIB="gcc-ranlib"} + ], [:] ) AC_MSG_RESULT([added GCC interprocedural optimization support]) elif test "$ax_cv_c_compiler_vendor" = "clang"; then - CFLAGS="$CFLAGS -emit-llvm" + CFLAGS="$CFLAGS -flto -fuse-ld=gold" + LDFLAGS="$LDFLAGS -XCClinker -fuse-ld=gold" + : ${AR="llvm-ar"} + : ${LD="ld.gold"} + : ${RANLIB="llvm-ranlib"} AC_MSG_RESULT([added LLVM interprocedural optimization support]) else AC_MSG_WARN([Compiler does not support interprocedural optimization]) @@ -199,7 +224,7 @@ fi # Check if task debugging is on. AC_ARG_ENABLE([task-debugging], [AS_HELP_STRING([--enable-task-debugging], - [Store task timing information and generate task dump files @<:@yes/no@:>@] + [Store extra information for generating task dump files @<:@yes/no@:>@] )], [enable_task_debugging="$enableval"], [enable_task_debugging="no"] @@ -309,6 +334,9 @@ elif test "$no_gravity_below_id" != "no"; then AC_DEFINE_UNQUOTED([SWIFT_NO_GRAVITY_BELOW_ID], [$enableval] ,[Particles with smaller ID than this will have zero gravity forces]) fi +# Check whether we have any of the ARM v8.1 tick timers +AX_ASM_ARM_PMCCNTR +AX_ASM_ARM_CNTVCT # Define HAVE_POSIX_MEMALIGN if it works. AX_FUNC_POSIX_MEMALIGN @@ -335,7 +363,7 @@ AC_ARG_ENABLE([vec], [enable_vec="yes"] ) -# Disable hand written vectorisation. Slightly odd implementation as want +# Disable hand written vectorisation. Slightly odd implementation as want # to describe as --disable-hand-vec, but macro is enable (there is no enable action). AC_ARG_ENABLE([hand-vec], [AS_HELP_STRING([--disable-hand-vec], @@ -676,6 +704,71 @@ if test "x$with_fftw" != "xno"; then fi fi fi + +AC_ARG_WITH([arm-fftw], + [AS_HELP_STRING([--with-arm-fftw=PATH], + [root directory where arm fft library is installed @<:@yes/no@:>@] + )], + [with_arm_fftw="$withval"], + [with_arm_fftw=no] +) +if test "x$with_arm_fftw" != "xno"; then + + # Was FFTW's location specifically given? + if test "x$with_arm_fftw" != "xyes" -a "x$with_arm_fftw" != "xtest" -a "x$with_arm_fftw" != "x"; then + FFTW_LIBS="-L$with_arm_fftw/lib -larmpl_lp64" + FFTW_INCS="-I$with_arm_fftw/include" + else + FFTW_LIBS="-larmpl_lp64" + FFTW_INCS="" + fi + + # FFTW is not specified, so just check if we have it. + if test "x$with_arm_fftw" = "xtest"; then + AC_CHECK_LIB([armpl_lp64],[fftw_malloc],[have_fftw="yes"],[have_fftw="no"],$FFTW_LIBS) + if test "x$have_arm_fftw" != "xno"; then + AC_DEFINE([HAVE_FFTW],1,[The FFTW library appears to be present.]) + have_fftw="yes - ARM" + fi + # FFTW was specified, check that it was a valid location. + else + AC_CHECK_LIB([armpl_lp64],[fftw_malloc], + AC_DEFINE([HAVE_FFTW],1,[The FFTW library appears to be present.]), + AC_MSG_ERROR(something is wrong with the FFTW library!), $FFTW_LIBS) + have_fftw="yes - ARM" + fi + + # FFTW was requested not to be used. + if test "$have_arm_fftw" = "no"; then + FFTW_LIBS="" + FFTW_INCS="" + fi + + # Now, check whether we have the threaded version of FFTW + if test "x$have_arm_fftw" = "xyes"; then + + # Was FFTW's location specifically given? + if test "x$with_arm_fftw" != "xyes" -a "x$with_arm_fftw" != "xtest" -a "x$with_arm_fftw" != "x"; then + FFTW_THREADED_LIBS="-L$with_arm_fftw/lib -larmpl_lp64_threads -larmpl_lp64" + FFTW_THREADED_INCS="-I$with_arm_fftw/include" + else + FFTW_THREADED_LIBS="-larmpl_lp64_threads -larmpl_lp64" + FFTW_THREADED_INCS="" + fi + + # Verify that the library is threaded + AC_CHECK_LIB([armpl_lp64],[fftw_init_threads],[have_threaded_fftw="yes"], + [have_threaded_fftw="no"], $FFTW_THREADED_LIBS) + + # If found, update things + if test "x$have_threaded_fftw" = "xyes"; then + AC_DEFINE([HAVE_THREADED_FFTW],1,[The threaded FFTW library appears to be present.]) + FFTW_LIBS=$FFTW_THREADED_LIBS + FFTW_INCS=$FFTW_THREADED_INCS + have_fftw="yes - ARM - threaded" + fi + fi +fi AC_SUBST([FFTW_LIBS]) AC_SUBST([FFTW_INCS]) AM_CONDITIONAL([HAVEFFTW],[test -n "$FFTW_LIBS"]) @@ -875,7 +968,8 @@ if test "$with_hdf5" = "yes"; then AC_MSG_CHECKING([for HDF5 parallel support]) # Check if the library is capable, the header should define H5_HAVE_PARALLEL. - + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $HDF5_CPPFLAGS" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include "hdf5.h" #ifndef H5_HAVE_PARALLEL @@ -887,6 +981,7 @@ if test "$with_hdf5" = "yes"; then AC_DEFINE([HAVE_PARALLEL_HDF5],1,[HDF5 library supports parallel access]) fi AC_MSG_RESULT($parallel) + CPPFLAGS="$old_CPPFLAGS" fi fi AM_CONDITIONAL([HAVEPARALLELHDF5],[test "$have_parallel_hdf5" = "yes"]) @@ -941,7 +1036,7 @@ if test "x$with_velociraptor" != "xno"; then AC_PROG_FC AC_FC_LIBRARY_LDFLAGS if test "x$with_velociraptor" != "xyes" -a "x$with_velociraptor" != "x"; then - VELOCIRAPTOR_LIBS="-L$with_velociraptor -lstf -lstdc++ -lhdf5_cpp" + VELOCIRAPTOR_LIBS="-L$with_velociraptor -lvelociraptor -lmpi -lstdc++ -lhdf5_cpp" CFLAGS="$CFLAGS -fopenmp" else VELOCIRAPTOR_LIBS="" @@ -950,7 +1045,7 @@ if test "x$with_velociraptor" != "xno"; then have_velociraptor="yes" AC_CHECK_LIB( - [stf], + [velociraptor], [InitVelociraptor], [AC_DEFINE([HAVE_VELOCIRAPTOR],1,[The VELOCIraptor library appears to be present.])], [AC_MSG_ERROR(Cannot find VELOCIraptor library at $with_velociraptor or incompatible HDF5 library loaded.)], @@ -986,20 +1081,67 @@ AC_CHECK_FUNC(pthread_setaffinity_np, AC_DEFINE([HAVE_SETAFFINITY],[1], AM_CONDITIONAL(HAVESETAFFINITY, [test "$ac_cv_func_pthread_setaffinity_np" = "yes"]) +# If available check for NUMA as well. There is a problem with the headers of +# this library, mainly that they do not pass the strict prototypes check when +# installed outside of the system directories. So we actually do this check +# in two phases. The basic ones first (before strict-prototypes is added to CFLAGS). have_numa="no" -if test "$ac_cv_func_pthread_setaffinity_np" = "yes"; then - # Check for libnuma. - AC_CHECK_HEADER([numa.h]) - if test "$ac_cv_header_numa_h" = "yes"; then - AC_CHECK_LIB([numa], [numa_available]) - have_numa="yes" - fi -fi +AC_ARG_WITH([numa], + [AS_HELP_STRING([--with-numa=PATH], + [Directory where the NUMA library exists @<:@yes/no@:>@] + )], + [with_numa="$withval"], + [with_numa="yes"] +) +if test "$ac_cv_func_pthread_setaffinity_np" = "yes" -a "x$with_numa" != "xno"; then + + if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then + NUMA_LIBS="-L$with_numa/lib -lnuma" + NUMA_INCS="-I$with_numa/include" + else + NUMA_LIBS="-lnuma" + NUMA_INCS="" + fi + + # Test for header file. + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $NUMA_INCS" + AC_CHECK_HEADER([numa.h]) + CPPFLAGS="$old_CPPFLAGS" + if test "$ac_cv_header_numa_h" = "yes"; then + + # If NUMA location is specified check if we have it. + if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then + AC_CHECK_LIB([numa],[numa_available], + AC_DEFINE([HAVE_LIBNUMA],1,[The NUMA library appears to be present.]), + AC_MSG_ERROR(something is wrong with the NUMA library!), $NUMA_LIBS) + have_numa="yes" + else + AC_CHECK_LIB([numa],[numa_available],[have_numa="yes"],[have_numa="no"],$NUMA_LIBS) + if test "x$have_numa" != "xno"; then + AC_DEFINE([HAVE_LIBNUMA],1,[The NUMA library appears to be present.]) + fi + fi + fi + # We can live without this. + if test "$have_numa" = "no"; then + NUMA_LIBS="" + fi +fi +AC_SUBST([NUMA_LIBS]) # Check for Intel and PowerPC intrinsics header optionally used by vector.h. -AC_CHECK_HEADERS([immintrin.h]) -AC_CHECK_HEADERS([altivec.h]) +AC_CHECK_HEADERS([immintrin.h], [], [], +[#ifdef HAVE_IMMINTRIN_H +# include <immintrin.h> +#endif +]) +AC_CHECK_HEADERS([altivec.h], [], [], +[#ifdef HAVE_ALTIVEC_H +# include <altivec.h> +#endif +]) # Check for timing functions needed by cycle.h. AC_HEADER_TIME @@ -1020,17 +1162,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM( [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])],[rtc_ok=no]) AC_MSG_RESULT($rtc_ok) -# Special timers for the ARM v7 and ARM v8 platforms (taken from FFTW-3 to match their cycle.h) -AC_ARG_ENABLE(armv8-pmccntr-el0, [AC_HELP_STRING([--enable-armv8-pmccntr-el0],[enable the cycle counter on ARMv8 via the PMCCNTR_EL0 register])], have_armv8pmccntrel0=$enableval) -if test "$have_armv8pmccntrel0"x = "yes"x; then - AC_DEFINE(HAVE_ARMV8_PMCCNTR_EL0,1,[Define if you have enabled the PMCCNTR_EL0 cycle counter on ARMv8]) -fi - -AC_ARG_ENABLE(armv8-cntvct-el0, [AC_HELP_STRING([--enable-armv8-cntvct-el0],[enable the cycle counter on ARMv8 via the CNTVCT_EL0 register])], have_armv8cntvctel0=$enableval) -if test "$have_armv8cntvctel0"x = "yes"x; then - AC_DEFINE(HAVE_ARMV8_CNTVCT_EL0,1,[Define if you have enabled the CNTVCT_EL0 cycle counter on ARMv8]) -fi - +# Special timers for the ARM v7 platforms (taken from FFTW-3 to match their cycle.h) AC_ARG_ENABLE(armv7a-cntvct, [AC_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval) if test "$have_armv7acntvct"x = "yes"x; then AC_DEFINE(HAVE_ARMV7A_CNTVCT,1,[Define if you have enabled the CNTVCT cycle counter on ARMv7a]) @@ -1082,6 +1214,35 @@ if test "$enable_warn" != "no"; then [CFLAGS="$CFLAGS"],[$CFLAGS],[AC_LANG_SOURCE([int main(void){return 0;}])]) fi +# Second part of the NUMA library checks. We now decide if we need to use +# -isystem to get around the strict-prototypes problem. Assumes isystem +# is available when strict-prototypes is. +if test "$have_numa" != "no"; then + if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then + case "$CFLAGS" in + *strict-prototypes*) + NUMA_INCS="-isystem$with_numa/include" + # This may still fail if CPATH is used, so we check if the + # headers are usable. + AS_UNSET(ac_cv_header_numa_h) + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $NUMA_INCS" + numa_failed="no" + AC_CHECK_HEADER([numa.h],[numa_failed="no"], + [numa_failed="yes"]) + if test "$numa_failed" = "yes"; then + AC_MSG_ERROR([Failed to compile the numa.h header file: you may need to set --enable-compiler-warnings to yes or no]) + fi + CPPFLAGS="$old_CPPFLAGS" + ;; + *) + NUMA_INCS="-I$with_numa/include" + ;; + esac + fi +fi +AC_SUBST([NUMA_INCS]) + # Various package configuration options. # Master subgrid options @@ -1099,7 +1260,11 @@ AC_ARG_WITH([subgrid], # Default values with_subgrid_cooling=none with_subgrid_chemistry=none -with_subgrid_hydro=none +with_subgrid_tracers=none +with_subgrid_entropy_floor=none +with_subgrid_stars=none +with_subgrid_star_formation=none +with_subgrid_feedback=none case "$with_subgrid" in yes) @@ -1110,15 +1275,19 @@ case "$with_subgrid" in GEAR) with_subgrid_cooling=grackle with_subgrid_chemistry=GEAR - with_subgrid_hydro=gadget2 + with_subgrid_tracers=none + with_subgrid_entropy_floor=none with_subgrid_stars=GEAR + with_subgrid_star_formation=none with_subgrid_feedback=thermal ;; EAGLE) with_subgrid_cooling=EAGLE with_subgrid_chemistry=EAGLE - with_subgrid_hydro=gadget2 - with_subgrid_stars=none + with_subgrid_tracers=EAGLE + with_subgrid_entropy_floor=EAGLE + with_subgrid_stars=EAGLE + with_subgrid_star_formation=EAGLE with_subgrid_feedback=none ;; *) @@ -1150,20 +1319,12 @@ esac # Hydro scheme. AC_ARG_WITH([hydro], [AS_HELP_STRING([--with-hydro=<scheme>], - [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, pressure-energy-monaghan, default, gizmo-mfv, gizmo-mfm, shadowfax, planetary, debug default: gadget2@:>@] + [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, pressure-energy-monaghan, default, gizmo-mfv, gizmo-mfm, shadowfax, planetary, anarchy-pu debug default: gadget2@:>@] )], [with_hydro="$withval"], [with_hydro="gadget2"] ) -if test "$with_subgrid" != "none"; then - if test "$with_hydro" != "gadget2"; then - AC_MSG_ERROR([Cannot provide with-subgrid and with-hydro together]) - else - with_hydro="$with_subgrid_hydro" - fi -fi - case "$with_hydro" in gadget2) AC_DEFINE([GADGET2_SPH], [1], [Gadget-2 SPH]) @@ -1195,6 +1356,9 @@ case "$with_hydro" in planetary) AC_DEFINE([PLANETARY_SPH], [1], [Planetary SPH]) ;; + anarchy-pu) + AC_DEFINE([ANARCHY_PU_SPH], [1], [ANARCHY (PU) SPH]) + ;; *) @@ -1460,10 +1624,39 @@ case "$with_chemistry" in ;; esac +# Particle tracers +AC_ARG_WITH([tracers], + [AS_HELP_STRING([--with-tracers=<function>], + [chemistry function @<:@none, EAGLE default: none@:>@] + )], + [with_tracers="$withval"], + [with_tracers="none"] +) + +if test "$with_subgrid" != "none"; then + if test "$with_tracers" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-tracers together]) + else + with_tracers="$with_subgrid_tracers" + fi +fi + +case "$with_tracers" in + none) + AC_DEFINE([TRACERS_NONE], [1], [No tracers function]) + ;; + EAGLE) + AC_DEFINE([TRACERS_EAGLE], [1], [Tracers taken from the EAGLE model]) + ;; + *) + AC_MSG_ERROR([Unknown tracers choice: $with_tracers]) + ;; +esac + # Stellar model. AC_ARG_WITH([stars], [AS_HELP_STRING([--with-stars=<model>], - [Stellar model to use @<:@none, GEAR, debug default: none@:>@] + [Stellar model to use @<:@none, EAGLE, GEAR, debug default: none@:>@] )], [with_stars="$withval"], [with_stars="none"] @@ -1478,10 +1671,14 @@ if test "$with_subgrid" != "none"; then fi case "$with_stars" in + EAGLE) + AC_DEFINE([STARS_EAGLE], [1], [EAGLE stellar model]) + ;; GEAR) AC_DEFINE([STARS_GEAR], [1], [GEAR stellar model]) ;; none) + AC_DEFINE([STARS_NONE], [1], [None stellar model]) ;; *) @@ -1559,6 +1756,62 @@ case "$with_potential" in ;; esac +# Entropy floor +AC_ARG_WITH([entropy-floor], + [AS_HELP_STRING([--with-entropy-floor=<floor>], + [entropy floor @<:@none, EAGLE, default: none@:>@] + )], + [with_entropy_floor="$withval"], + [with_entropy_floor="none"] +) +if test "$with_subgrid" != "none"; then + if test "$with_entropy_floor" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-entropy-floor together]) + else + with_entropy_floor="$with_subgrid_entropy_floor" + fi +fi + +case "$with_entropy_floor" in + none) + AC_DEFINE([ENTROPY_FLOOR_NONE], [1], [No entropy floor]) + ;; + EAGLE) + AC_DEFINE([ENTROPY_FLOOR_EAGLE], [1], [EAGLE entropy floor]) + ;; + *) + AC_MSG_ERROR([Unknown entropy floor model]) + ;; +esac + +# Star formation +AC_ARG_WITH([star-formation], + [AS_HELP_STRING([--with-star-formation=<sfm>], + [star formation @<:@none, EAGLE, default: none@:>@] + )], + [with_star_formation="$withval"], + [with_star_formation="none"] +) +if test "$with_subgrid" != "none"; then + if test "$with_star_formation" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-star-formation together]) + else + with_star_formation="$with_subgrid_star_formation" + fi +fi + +case "$with_star_formation" in + none) + AC_DEFINE([STAR_FORMATION_NONE], [1], [No star formation]) + ;; + EAGLE) + AC_DEFINE([STAR_FORMATION_EAGLE], [1], [EAGLE star formation model (Schaye and Dalla Vecchia (2008))]) + ;; + *) + AC_MSG_ERROR([Unknown star formation model]) + ;; +esac + # Gravity multipole order AC_ARG_WITH([multipole-order], [AS_HELP_STRING([--with-multipole-order=<order>], @@ -1577,9 +1830,12 @@ AC_SUBST([GIT_CMD]) DX_INIT_DOXYGEN(libswift,doc/Doxyfile,doc/) AM_CONDITIONAL([HAVE_DOXYGEN], [test "$ac_cv_path_ac_pt_DX_DOXYGEN" != ""]) +# Check if using EAGLE cooling +AM_CONDITIONAL([HAVEEAGLECOOLING], [test $with_cooling = "EAGLE"]) + # Handle .in files. -AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) -AC_CONFIG_FILES([tools/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) +AC_CONFIG_FILES([argparse/Makefile tools/Makefile]) AC_CONFIG_FILES([tests/testReading.sh], [chmod +x tests/testReading.sh]) AC_CONFIG_FILES([tests/testActivePair.sh], [chmod +x tests/testActivePair.sh]) AC_CONFIG_FILES([tests/test27cells.sh], [chmod +x tests/test27cells.sh]) @@ -1647,10 +1903,13 @@ AC_MSG_RESULT([ Make gravity glass : $gravity_glass_making External potential : $with_potential - Cooling function : $with_cooling - Chemistry : $with_chemistry - Stellar model : $with_stars - Feedback model : $with_feedback + Entropy floor : $with_entropy_floor + Cooling function : $with_cooling + Chemistry : $with_chemistry + Tracers : $with_tracers + Stellar model : $with_stars + Star formation model : $with_star_formation + Feedback model : $with_feedback Individual timers : $enable_timers Task debugging : $enable_task_debugging diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index d2dd87257ea7da2b78bfe0503870112b830ee22c..fe48398e4b0dd4bfdede6781e708bd222b6eedae 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -772,6 +772,9 @@ INPUT += @top_srcdir@/src/cooling/const_lambda INPUT += @top_srcdir@/src/cooling/Compton INPUT += @top_srcdir@/src/cooling/EAGLE INPUT += @top_srcdir@/src/chemistry/EAGLE +INPUT += @top_srcdir@/src/entropy_floor/EAGLE +INPUT += @top_srcdir@/src/star_formation/EAGLE +INPUT += @top_srcdir@/src/tracers/EAGLE # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1975,7 +1978,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = @top_srcdir@/src # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst index a8ce7af082fd7b2bf3ec285b5bb0b2b5bb509401..88493ddb10ff2a4978e5e4b31a55efc87ba45d3b 100644 --- a/doc/RTD/source/CommandLineOptions/index.rst +++ b/doc/RTD/source/CommandLineOptions/index.rst @@ -11,33 +11,56 @@ For instance, just running the ``swift`` binary will not use any SPH or gravity; the particles will just sit still! Below is a list of the command line options and when they should be used. The same list -can be found by typing ``./swift -h``. - -+ ``-a``: Pin runners using processor affinity. -+ ``-c``: Run with cosmological time integration. -+ ``-C``: Run with cooling. -+ ``-d``: Dry run. Read the parameter file, allocate memory but does not read - the particles from ICs and exit before the start of time integration. Allows - user to check validity of parameter and IC files as well as memory limits. -+ ``-D``: Always drift all particles even the ones far from active particles. - This emulates Gadget-[23] and GIZMO's default behaviours. -+ ``-e``: Enable floating-point exceptions (debugging mode). -+ ``-f``: {int} Overwrite the CPU frequency (Hz) to be used for time measurements. -+ ``-g``: Run with an external gravitational potential. -+ ``-G``: Run with self-gravity. -+ ``-M``: Reconstruct the multipoles every time-step. -+ ``-n``: {int} Execute a fixed number of time steps. When unset use the - time_end parameter to stop. -+ ``-o``: {str} Generate a default output parameter file. -+ ``-P``: {sec:par:val} Set parameter value and overwrites values read from the - parameters file. Can be used more than once. -+ ``-s``: Run with hydrodynamics. -+ ``-S``: Run with stars. -+ ``-t``: {int} The number of threads to use on each MPI rank. Defaults to 1 if - not specified. -+ ``-T``: Print timers every time-step. -+ ``-v``: [12] Increase the level of verbosity: 1, MPI-rank 0 writes, 2, All - MPI-ranks write. -+ ``-y``: {int} Time-step frequency at which task graphs are dumped. -+ ``-Y``: {int} Time-step frequency at which threadpool tasks are dumped. -+ ``-h``: Print a help message and exit. +can be found by typing ``./swift -h``:: + + -h, --help show this help message and exit + + Simulation options: + + -b, --feedback Run with stars feedback. + -c, --cosmology Run with cosmological time integration. + --temperature Run with temperature calculation. + -C, --cooling Run with cooling (also switches on --with-temperature). + -D, --drift-all Always drift all particles even the ones + far from active particles. This emulates + Gadget-[23] and GIZMO's default behaviours. + -F, --star-formation Run with star formation. + -g, --external-gravity Run with an external gravitational potential. + -G, --self-gravity Run with self-gravity. + -M, --multipole-reconstruction Reconstruct the multipoles every time-step. + -s, --hydro Run with hydrodynamics. + -S, --stars Run with stars. + -x, --velociraptor Run with structure finding. + --limiter Run with time-step limiter. + + Control options: + + -a, --pin Pin runners using processor affinity. + -d, --dry-run Dry run. Read the parameter file, allocates + memory but does not read the particles + from ICs. Exits before the start of time + integration. Checks the validity of + parameters and IC files as well as memory + limits. + -e, --fpe Enable floating-point exceptions (debugging + mode). + -f, --cpu-frequency=<str> Overwrite the CPU frequency (Hz) to be + used for time measurements. + -n, --steps=<int> Execute a fixed number of time steps. + When unset use the time_end parameter + to stop. + -o, --output-params=<str> Generate a default output parameter + file. + -P, --param=<str> Set parameter value, overiding the value + read from the parameter file. Can be used + more than once {sec:par:value}. + -r, --restart Continue using restart files. + -t, --threads=<int> The number of threads to use on each MPI + rank. Defaults to 1 if not specified. + -T, --timers=<int> Print timers every time-step. + -v, --verbose=<int> Run in verbose mode, in MPI mode 2 outputs + from all ranks. + -y, --task-dumps=<int> Time-step frequency at which task analysis + files and/or tasks are dumped. + -Y, --threadpool-dumps=<int> Time-step frequency at which threadpool + tasks are dumped. diff --git a/doc/RTD/source/Cooling/index.rst b/doc/RTD/source/Cooling/index.rst deleted file mode 100644 index 0ca3f62f3ca6fdff2abb95501681dc7bf4676fd2..0000000000000000000000000000000000000000 --- a/doc/RTD/source/Cooling/index.rst +++ /dev/null @@ -1,80 +0,0 @@ -.. Equation of State - Loic Hausammann, 7th April 2018 - -.. _cooling: - -Cooling -======= - -Currently, we have 5 different cooling (EAGLE, Grackle, const-lambda, const-du -and none). Three of them are easily solved analytically (const-lambda, -const-du and none) while the two last requires complex chemical networks. - - -Equations ---------- - -The first table compares the different analytical cooling while the next ones -are specific to a given cooling. The quantities are the internal energy (\\( u -\\)), the density \\( rho \\), the element mass fraction (\\( X_i \\)), the -cooling function (\\(\\Lambda\\), the proton mass (\\( m_H \\)) and the time -step condition (\\( t\_\\text{step}\\)). If not specified otherwise, all -cooling contains a temperature floor avoiding negative temperature. - -.. csv-table:: Analytical Cooling - :header: "Variable", "Const-Lambda", "Const-du", "None" - - "\\( \\frac{ \\mathrm{d}u }{ \\mathrm{d}t } \\)", "\\( -\\Lambda \\frac{\\rho^2 X_H^2}{\\rho m_H^2} \\)", "const", "0" - "\\( \\Delta t\_\\text{max} \\)", "\\( t\_\\text{step} \\frac{u}{\\left|\\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)", "\\( t\_\\text{step} \\frac{u}{\\ \\left| \\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)", "None" - - -Grackle -~~~~~~~ - -Grackle is a chemistry and cooling library presented in B. Smith et al. 2016 -(do not forget to cite if used). Four different modes are available: -equilibrium, 6 species network (H, H\\( ^+ \\), e\\( ^- \\), He, He\\( ^+ \\) -and He\\( ^{++} \\)), 9 species network (adds H\\(^-\\), H\\(_2\\) and -H\\(_2^+\\)) and 12 species (adds D, D\\(^+\\) and HD). Following the same -order, the swift cooling options are ``grackle``, ``grackle1``, ``grackle2`` -and ``grackle3`` (the numbers correspond to the value of -``primordial_chemistry`` in Grackle). It also includes some self-shielding -methods and UV background. In order to use the Grackle cooling, you will need -to provide an HDF5 table computed by Cloudy. - -When starting a simulation without providing the different fractions, the code -supposes an equilibrium and computes the fractions automatically. - -In order to compile SWIFT with Grackle, you need to provide the options ``with-grackle`` -and ``with-chemistry``. - -You will need a Grackle version later than 3.1. To compile it, run -the following commands from the root directory of Grackle: -``./configure; cd src/clib``. -Update the variables ``LOCAL_HDF5_INSTALL`` and ``MACH_INSTALL_PREFIX`` in -the file ``src/clib/Make.mach.linux-gnu``. -Finish with ``make machine-linux-gnu; make && make install``. -If you encounter any problem, you can look at the `Grackle documentation <https://grackle.readthedocs.io/en/latest/>`_ - -You can now provide the path given for ``MACH_INSTALL_PREFIX`` to ``with-grackle``. - -Eagle -~~~~~ - -TODO - -How to Implement a New Cooling ------------------------------- - -The developper should provide at least one function for: - * writing the cooling name in HDF5 - * cooling a particle - * the maximal time step possible - * initializing a particle - * computing the total energy radiated by a particle - * initializing the cooling parameters - * printing the cooling type - -For implementation details, see ``src/cooling/none/cooling.h`` - -See :ref:`new_option` for the full list of changes required. diff --git a/doc/RTD/source/EquationOfState/index.rst b/doc/RTD/source/EquationOfState/index.rst index 3558041e9513b967a2530165acec5e5f4f11a364..11e6069130a84ba54dec01e9892464574d9c2c6b 100644 --- a/doc/RTD/source/EquationOfState/index.rst +++ b/doc/RTD/source/EquationOfState/index.rst @@ -1,19 +1,21 @@ -.. Equation of State +.. Equations of State Loic Hausammann, 6th April 2018 + Jacob Kegerreis, 3rd February 2019 .. _equation_of_state: -Equation of State -================= +Equations of State +================== -Currently (if the documentation was well updated), we have two different -equation of states implemented: ideal gas and isothermal. They describe the -relations between our main thermodynamical variables: the internal energy -(\\(u\\)), the density (\\(\\rho\\)), the entropy (\\(A\\)) and the pressure -(\\(P\\)). +Currently (if the documentation was well updated), we have two different gas +equations of state (EoS) implemented: ideal and isothermal; as well as a variety +of EoS for "planetary" materials. +The EoS describe the relations between our main thermodynamical variables: +the internal energy (\\(u\\)), the density (\\(\\rho\\)), the entropy (\\(A\\)) +and the pressure (\\(P\\)). -Equations ---------- +Gas EoS +------- In the following section, the variables not yet defined are: \\(\\gamma\\) for the adiabatic index and \\( c_s \\) for the speed of sound. @@ -37,12 +39,55 @@ the adiabatic index and \\( c_s \\) for the speed of sound. "\\( c_s\\)", "", "\\(\\sqrt{ u \\gamma \\left( \\gamma - 1 \\right) } \\)", "" + +Planetary EoS +------------- +Configuring SWIFT with the ``--with-equation-of-state=planetary`` and +``--with-hydro=planetary`` options enables the use of multiple EoS. +Every SPH particle then requires and carries the additional ``MaterialID`` flag +from the initial conditions file. This flag indicates the particle's material +and which EoS it should use. + +So far, we have implemented several Tillotson, SESAME, and Hubbard \& MacFarlane +(1980) materials, with more on their way. +The material's ID is set by a base type ID (multiplied by 100), plus a minor +type: + ++ Tillotson (Melosh, 2007): ``1`` + + Iron: ``100`` + + Granite: ``101`` + + Water: ``102`` ++ Hubbard \& MacFarlane (1980): ``2`` + + Hydrogen-helium atmosphere: ``200`` + + Ice H20-CH4-NH3 mix: ``201`` + + Rock SiO2-MgO-FeS-FeO mix: ``202`` ++ SESAME (and similar): ``3`` + + Iron (2140): ``300`` + + Basalt (7530): ``301`` + + Water (7154): ``302`` + + Senft \& Stewart (2008) water (in a SESAME-style table): ``303`` + +Unlike the EoS for an ideal or isothermal gas, these more complicated materials +do not always include transformations between the internal energy, +temperature, and entropy. At the moment, we have only implemented +\\(P(\\rho, u)\\) and \\(c_s(\\rho, u)\\). +This is sufficient for the simple :ref:`planetary_sph` hydrodynamics scheme, +but makes these materials currently incompatible with other entropy-based +schemes. + +The Tillotson sound speed was derived using +\\(c_s^2 = \\left. \\dfrac{\\partial P}{\\partial \\rho} \\right|_S \\) +as described in Kegerreis et al. (2019). +The table files for the HM80 and SESAME-style EoS can be downloaded using +the ``examples/EoSTables/get_eos_tables.sh`` script. + + How to Implement a New Equation of State ---------------------------------------- See :ref:`new_option` for a full list of required changes. -You will need to provide a ``equation_of_state.h`` file containing: the +You will need to provide an ``equation_of_state.h`` file containing: the definition of ``eos_parameters``, IO functions and transformations between the different variables: \\(u(\\rho, A)\\), \\(u(\\rho, P)\\), \\(P(\\rho,A)\\), \\(P(\\rho, u)\\), \\(A(\\rho, P)\\), \\(A(\\rho, u)\\), \\(c_s(\\rho, A)\\), diff --git a/doc/RTD/source/ExternalPotentials/index.rst b/doc/RTD/source/ExternalPotentials/index.rst index e3138162465c200ec71d7d31b02c575e3e663416..ca33eb8189eea216863feb02579344aa22916696 100644 --- a/doc/RTD/source/ExternalPotentials/index.rst +++ b/doc/RTD/source/ExternalPotentials/index.rst @@ -17,7 +17,7 @@ give a short overview of the potentials that are implemented in the code: 1. No potential (none) 2. Point mass potential (point-mass): classical point mass, can be placed at a position with a mass. -3. Plummer potential (point-mass-softened): in the code a softended point mass +3. Plummer potential (point-mass-softened): in the code a softened point mass corresponds to a Plummer potential, can be placed at a position with a mass. 4. Isothermal potential (isothermal): An isothermal potential which corresponds to a density profile which is :math:`\propto r^{-2}` and a potential which is @@ -28,7 +28,7 @@ give a short overview of the potentials that are implemented in the code: :math:`\Phi(r) = - \frac{GM}{r+a}.` - The free paramters of Hernquist potential are mass, scale length, + The free parameters of Hernquist potential are mass, scale length, and softening. The potential can be set at any position in the box. 6. NFW potential (nfw): The most used potential to describe dark matter halos, the potential is given by: @@ -36,7 +36,7 @@ give a short overview of the potentials that are implemented in the code: :math:`\Phi(r) = - \frac{4\pi G \rho_0 R_s^3}{r} \ln \left( 1+ \frac{r}{R_s} \right).` - This potential has as free paramters the concentration of the DM halo, the + This potential has as free parameters the concentration of the DM halo, the virial mass (:math:`M_{200}`) and the critical density. 7. Sine wave (sine-wave) 8. Point mass ring (point-mass-ring) diff --git a/doc/RTD/source/GettingStarted/compiling_code.rst b/doc/RTD/source/GettingStarted/compiling_code.rst index c40f06965e15146c41bf210aec3b195032cef0e7..0cfde4d18db62c2e0b41e652c73a6b6ad268440e 100644 --- a/doc/RTD/source/GettingStarted/compiling_code.rst +++ b/doc/RTD/source/GettingStarted/compiling_code.rst @@ -24,6 +24,15 @@ MPI A recent implementation of MPI, such as Open MPI (v2.x or higher), is required, or any library that implements at least the MPI 3 standard. +Running SWIFT on OmniPath atchitechtures with Open MPI +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running SWIFT on an OmniPath system we suggest that Open MPI v3.1.3 or higher +is used. A bug in the ``psm2`` library causes communications to be lost. It is +possible to run SWIFT with older versions (tested with v2.1.x) of Open MPI so +long as ``psm`` is used instead of ``psm2``, i.e. that you invoke ``mpirun`` +with ``--mca btl vader,self -mca mtl psm``. + Libtool ~~~~~~~ The build system depends on libtool. @@ -32,13 +41,13 @@ FFTW ~~~~ Version 3.3.x or higher is required for periodic gravity. -METIS -~~~~~ -METIS is used for domain decomposition and load balancing. +ParMETIS or METIS +~~~~~~~~~~~~~~~~~ +One is required for domain decomposition and load balancing. libNUMA ~~~~~~~ -libNUMA is used to pin threads. +libNUMA is used to pin threads (but see INSTALL.swift). GSL ~~~ diff --git a/doc/RTD/source/GettingStarted/configuration_options.rst b/doc/RTD/source/GettingStarted/configuration_options.rst index e37384cfd1c29cb1df82cc180a763f4859650b2e..7dca5cddb0012b9a2146640b9373cfbe81c8dbdd 100644 --- a/doc/RTD/source/GettingStarted/configuration_options.rst +++ b/doc/RTD/source/GettingStarted/configuration_options.rst @@ -45,6 +45,6 @@ Several cooling implementations (including GRACKLE) are available. Many external potentials are available for use with SWIFT. You can choose between them at compile time. Some examples include a central potential, a softened central potential, and a sinusoidal potential. You will need to -configure, for example, the mass in your parameterfile at runtime. +configure, for example, the mass in your parameter file at runtime. diff --git a/doc/RTD/source/GettingStarted/running_example.rst b/doc/RTD/source/GettingStarted/running_example.rst index 7c9e333ca971ec73f3c2e25b56cc8e1381385567..9dfbdd8c8ec98ea59892a551691aa5f230052e2e 100644 --- a/doc/RTD/source/GettingStarted/running_example.rst +++ b/doc/RTD/source/GettingStarted/running_example.rst @@ -14,7 +14,7 @@ as ``wget`` for grabbing the glass). cd examples/SodShock_3D ./getGlass.sh python makeIC.py - ../swift -s -t 4 sodShock.yml + ../swift --hydro --threads=4 sodShock.yml python plotSolution.py 1 diff --git a/doc/RTD/source/GettingStarted/running_on_large_systems.rst b/doc/RTD/source/GettingStarted/running_on_large_systems.rst index 55eb812cef21474045931490591b3978841a4085..42beedf790dc3d87895cd9bd6db13f25942b0a16 100644 --- a/doc/RTD/source/GettingStarted/running_on_large_systems.rst +++ b/doc/RTD/source/GettingStarted/running_on_large_systems.rst @@ -26,9 +26,8 @@ system (i.e. over MPI on several nodes). Here are some recommendations: + Run with threads pinned. You can do this by passing the ``-a`` flag to the SWIFT binary. This ensures that processes stay on the same core that spawned them, ensuring that cache is accessed more efficiently. -+ Ensure that you compile with METIS. More information is available in an - upcoming paper, but using METIS allows for work to be distributed in a - more efficient way between your nodes. ++ Ensure that you compile with ParMETIS or METIS. These are required if + want to load balance between MPI ranks. Your batch script should look something like the following (to run on 16 nodes each with 2x16 core processors for a total of 512 cores): @@ -38,5 +37,5 @@ each with 2x16 core processors for a total of 512 cores): #SBATCH -N 16 # Number of nodes to run on #SBATCH --tasks-per-node=2 # This system has 2 chips per node - mpirun -np 32 swift_mpi -t 16 -a parameter.yml + mpirun -np 32 swift_mpi --threads=16 --pin parameter.yml diff --git a/doc/RTD/source/GettingStarted/what_about_mpi.rst b/doc/RTD/source/GettingStarted/what_about_mpi.rst index 098fd35d80d71866cb86d2342d5d54710cd73a82..98141049f3e36506d6033259e7f5bb9394daf997 100644 --- a/doc/RTD/source/GettingStarted/what_about_mpi.rst +++ b/doc/RTD/source/GettingStarted/what_about_mpi.rst @@ -9,4 +9,4 @@ and the other ``swift_mpi``. Current wisdom is to run ``swift`` if you are only using one node (i.e. without any interconnect), and one MPI rank per NUMA region using ``swift_mpi`` for anything larger. You will need some GADGET-2 HDF5 initial conditions to run SWIFT, as well as a compatible yaml -parameterfile. +parameter file. diff --git a/doc/RTD/source/HydroSchemes/adding_your_own.rst b/doc/RTD/source/HydroSchemes/adding_your_own.rst index 2d7e640f66153a17e19f4e4c456cd37eed19a95a..549a7a42a22e7f755ad342b86b24c28f67118838 100644 --- a/doc/RTD/source/HydroSchemes/adding_your_own.rst +++ b/doc/RTD/source/HydroSchemes/adding_your_own.rst @@ -13,7 +13,7 @@ Adding Hydro Schemes SWIFT is engineered to enable you to add your own hydrodynamics schemes easily. We enable this through the use of header files to encapsulate each scheme. -Note that it's unlikely you will ever have to consider paralellism or 'loops over +Note that it's unlikely you will ever have to consider parallelism or 'loops over neighbours' for SWIFT; all of this is handled by the tasking system. All we ask for is the interaction functions that tell us how to a) compute the density and b) compute forces. @@ -69,7 +69,7 @@ will need to 'fill out' the following: + ``hydro_compute_timestep(p, xp, hydro_props, cosmo)`` returns the timestep for the hydrodynamics particles. + ``hydro_timestep_extra(p, dt)`` does some extra hydro operations once the - physical timestel for the particle is known. + physical timestep for the particle is known. + ``hydro_init_part(p, hydro_space)`` initialises the particle in preparation for the density calculation. This essentially sets properties, such as the density, to zero. diff --git a/doc/RTD/source/HydroSchemes/gizmo.rst b/doc/RTD/source/HydroSchemes/gizmo.rst index 365e1dc41c27f7c92bfb33859bedad2d96f35248..bbfcae04e1abac57b1476e4533bf92e051e6769d 100644 --- a/doc/RTD/source/HydroSchemes/gizmo.rst +++ b/doc/RTD/source/HydroSchemes/gizmo.rst @@ -10,7 +10,7 @@ GIZMO-Like Scheme :caption: Contents: -There is a meshless finite volume (MFV) GIZMO-like scheme implemented in SWIFT +There is a mesh-less finite volume (MFV) GIZMO-like scheme implemented in SWIFT (see Hopkins 2015 for more information). You will need a Riemann solver to run this, and configure as follows: @@ -19,7 +19,7 @@ this, and configure as follows: ./configure --with-hydro="gizmo-mfv" --with-riemann-solver="hllc" -We also have the meshless finite mass (MFM) GIZMO-like scheme. You can select +We also have the mesh-less finite mass (MFM) GIZMO-like scheme. You can select this at compile-time with the following configuration flags: .. code-block:: bash diff --git a/doc/RTD/source/HydroSchemes/index.rst b/doc/RTD/source/HydroSchemes/index.rst index cd6c169245e83440a1258d216991763488586c0c..462bb7378162ff1addab3212a6901412195a3377 100644 --- a/doc/RTD/source/HydroSchemes/index.rst +++ b/doc/RTD/source/HydroSchemes/index.rst @@ -15,6 +15,7 @@ schemes available in SWIFT, as well as how to implement your own. traditional_sph minimal_sph + planetary hopkins_sph gizmo adding_your_own diff --git a/doc/RTD/source/HydroSchemes/planetary.rst b/doc/RTD/source/HydroSchemes/planetary.rst new file mode 100755 index 0000000000000000000000000000000000000000..20f41758baadba2cddb99e79d3435bb3301065e0 --- /dev/null +++ b/doc/RTD/source/HydroSchemes/planetary.rst @@ -0,0 +1,26 @@ +.. Planetary SPH + Jacob Kegerreis, 3rd February 2019 + +.. _planetary_sph: + +Planetary (Density-Energy, Multi-Material) SPH +============================================== + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Contents: + +This scheme is the same as the Minimal SPH scheme but also allows multiple +materials, meaning that different SPH particles can be assigned different +:ref:`equation_of_state` (EoS). + +To use the planetary scheme and the corresponding planetary EoS, use + +.. code-block:: bash + + ./configure --with-hydro=planetary --with-equation-of-state=planetary + +Every SPH particle then requires and carries the additional ``MaterialID`` flag +from the initial conditions file. This flag indicates the particle's material +and which EoS it should use. \ No newline at end of file diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst index 00e7b98c5916a29b31d9396c317dfc2851d7389b..e585c9aa55f269ebbbf9b2d83034b96a688a99f4 100644 --- a/doc/RTD/source/InitialConditions/index.rst +++ b/doc/RTD/source/InitialConditions/index.rst @@ -11,17 +11,21 @@ conditions format as the popular `GADGET-2 its type 3 format. Note that we do not support the GADGET-2 types 1 and 2 formats. +One crucial difference is that whilst GADGET-2 can have initial conditions split +over many files SWIFT only supports initial conditions in one single file. **ICs +split over multiple files cannot be read by SWIFT**. See the +":ref:`multiple_files_ICs`" section below for possible solutions. In GADGET-2 +having multiple files allows multiple ones to be read in parallel and is the +only way the code can handle more than 2^31 particles. This limitation is not in +place in SWIFT. A single file can contain any number of particles (well... up to +2^64...) and the file is read in parallel by HDF5 when running on more than one +compute node. + The original GADGET-2 file format only contains 2 types of particles: gas -particles and 5 sorts of collisionless particles that allow users to run with 5 +particles and 5 sorts of collision-less particles that allow users to run with 5 separate particle masses and softenings. In SWIFT, we expand on this by using two of these types for stars and black holes. -GADGET-2 can have initial conditions split over many files. This allow multiple -ones to be read in parallel and is the only way the code can handle more than -2^31 particles. This limitation is not in place in SWIFT. A single file can -contain any number of particles (well... up to 2^64...) and the file is read in -parallel by HDF5 when running on more than one compute node. - As the original documentation for the GADGET-2 initial conditions format is quite sparse, we lay out here all of the necessary components. If you are generating your initial conditions from python, we recommend you use the h5py @@ -35,7 +39,7 @@ You can find out more about the HDF5 format on their `webpages Structure of the File --------------------- -There are several groups that contain 'auxilliary' information, such as +There are several groups that contain 'auxiliary' information, such as ``Header``. Particle data is placed in separate groups depending of the type of the particles. Some types are currently ignored by SWIFT but are kept in the file format for compatibility reasons. @@ -98,7 +102,7 @@ In the ``/Header/`` group, the following attributes are required: ``NumPart_Total`` to be >2^31, the use of ``NumPart_Total_HighWord`` is only here for compatibility reasons. + ``Flag_Entropy_ICs``, a historical value that tells the code if you have - included entropy or internal energy values in your intial conditions files. + included entropy or internal energy values in your initial conditions files. Acceptable values are 0 or 1. We recommend using internal energies over entropy in the ICs and hence have this flag set to 0. @@ -113,7 +117,9 @@ GADGET-2 based analysis programs: exactly the same as the ``NumPart_Total`` array. As SWIFT only uses ICs contained in a single file, this is not necessary for SWIFT-only ICs. + ``NumFilesPerSnapshot``, again a historical integer value that tells the code - how many files there are per snapshot. You will probably want to set this to 1. + how many files there are per snapshot. You will probably want to set + this to 1. If this field is present in a SWIFT IC file and has a + value different from 1, the code will return an error message. + ``Time``, time of the start of the simulation in internal units or expressed as a scale-factor for cosmological runs. SWIFT ignores this and reads it from the parameter file. @@ -141,7 +147,7 @@ individual particle type (e.g. ``/PartType0/``) that have the following *dataset + ``Masses``, an array of length N that gives the masses of the particles. For ``PartType0`` (i.e. particles that interact through hydro-dynamics), you will -need the following auxilliary items: +need the following auxiliary items: + ``SmoothingLength``, the smoothing lengths of the particles. These will be tidied up a bit, but it is best if you provide accurate numbers. In @@ -163,7 +169,7 @@ h-free quantities. Switching this parameter on will also affect the box size read from the ``/Header/`` group (see above). Similarly, GADGET cosmological ICs have traditionally used velocities expressed -as peculiar velocities divided by ``sqrt(a)``. This can be undone by swicthing +as peculiar velocities divided by ``sqrt(a)``. This can be undone by switching on the parameter ``InitialConditions:cleanup_velocity_factors`` in the :ref:`Parameter_File_label`. @@ -226,4 +232,27 @@ You should have an HDF5 file with the following structure: ParticleIDs=[...] Masses=[...] +.. _multiple_files_ICs: + +ICs split over multiple files +----------------------------- + +A basic script ``tools/combine_ics.py`` is provided to merge basic GADGET-2 +initial conditions split into multiple files into one single valid file. This +script can handle simple HDF5 files (GADGET-2 type 3 ICs) that follow the format +described above but split over multiple files. + +The script can also convert ICs using a ``MassTable`` and create the +corresponding particle fields. Note that additional fields present in ICs beyond +the simple GADGET-2 specification will not be merged. + +One additional option is to compress the fields in the files using HDF5's gzip +compression. This is very effective for the fields such as masses or particle +IDs which are very similar. A checksum filter is also applied in all cases to +help with data curation. + +**We caution that this script is very basic and should only be used with great +caution.** + + diff --git a/doc/RTD/source/NewOption/index.rst b/doc/RTD/source/NewOption/index.rst index 441cd860ed79dabad2005b39ae4549d1496ab98d..08f1ff04efa9508145c1f7e04d72d2f40fe22f0d 100644 --- a/doc/RTD/source/NewOption/index.rst +++ b/doc/RTD/source/NewOption/index.rst @@ -7,8 +7,8 @@ General information for adding new schemes ========================================== The following steps are required for any new options (such as new -:ref:`hydro`, :ref:`chemistry`, :ref:`cooling`, -:ref:`equation_of_state`, :ref:`stars` or :ref:`gravity`) +:ref:`hydro`, chemistry, cooling, +:ref:`equation_of_state`, stars, or gravity) In order to add a new scheme, you will need to: diff --git a/doc/RTD/source/ParameterFiles/index.rst b/doc/RTD/source/ParameterFiles/index.rst index ec938c6062056e1c1e081c10813ed61b8bfc936f..4cd0ab7bff1396c90c4dfe978b3e109db64bcab5 100644 --- a/doc/RTD/source/ParameterFiles/index.rst +++ b/doc/RTD/source/ParameterFiles/index.rst @@ -1,442 +1,17 @@ .. Parameter Files - Matthieu Schaller, 21st October 2018 + Josh Borrow 22nd January 2019 .. _Parameter_File_label: Parameter Files =============== -File format and basic information ---------------------------------- - -The parameter file uses a format similar to the `YAML format -<https://en.wikipedia.org/wiki/YAML>`_ but reduced to only the -elements required for the SWIFT parameters. Options are given by a -name followed by a column and the value of the parameter: - -.. code:: YAML - - ICs: santa_barbara.hdf5 - dt_max: 1.5 - shift: [2., 4., 5.] - -Comments can be inserted anywhere and start with a hash: - -.. code:: YAML - - # Descrption of the physics - viscosity_alpha: 2.0 - dt_max: 1.5 # seconds - -A typical SWIFT parameter file is split into multiple sections that -may or may not be present depending on the different configuration -options. The sections start with a label and can contain any number of -parameters: - -.. code:: YAML - - Cosmology: # Planck13 - Omega_m: 0.307 - Omega_lambda: 0.693 - Omega_b: 0.0455 - h: 0.6777 - a_begin: 0.0078125 # z = 127 - -The options can be integer values, floating point numbers, characters -or strings. If SWIFT expects a number and string is given, an error -will be raised. The code can also read an array of values: - -.. code:: YAML - - shift: [2., 4., 5.] - -Some options in the parameter file are optional and -when not provided, SWIFT will run with the default value. However, if -a compulsory parameter is missing an error will be raised at -start-up. - -Finally, SWIFT outputs two YAML files at the start of a run. The first -one ``used_parameters.yml`` contains all the parameters that were used -for this run, **including all the optional parameters with their -default values**. This file can be used to start an exact copy of the -run. The second file, ``unused_parameters.yml`` contains all the -values that were not read from the parameter file. This can be used to -simplify the parameter file or check that nothing important was -ignored (for instance because the code is not configured to use some -options). - -The rest of this page describes all the SWIFT parameters, split by -section. A list of all the possible parameters is kept in the file -``examples/parameter_examples.yml``. - -Internal Unit System --------------------- - -This section describes the units used internally by the code. This is -the system of units in which all the equations are solved. All -physical constants are converted to this system and if the ICs use a -different system (see :ref:`ICs_units_label`) the particle quantities -will be converted when read in. - -The system of units is described using the value of the 5 basic units -of any system with respect to the CGS system. Instead of using a unit -of time we use a unit of velocity as this is more intuitive. Users -hence need to provide: - -* a unit of length: ``UnitLength_in_cgs``, -* a unit of mass: ``UnitMass_in_cgs``, -* a unit of velocity ``UnitVelocity_in_cgs``, -* a unit of electric current ``UnitCurrent_in_cgs``, -* a unit of temperature ``UnitTemp_in_cgs``. - -All these need to be expressed with respect to their cgs counter-part -(i.e. :math:`cm`, :math:`g`, :math:`cm/s`, :math:`A` and :math:`K`). Recall -that there are no h-factors in any of SWIFT's quantities; we, for instance, -use :math:`cm` and not :math:`cm/h`. - -For instance to use the commonly adopted system of 10^10 Msun as a -unit for mass, mega-parsec as a unit of length and km/s as a unit of -speed, we would use: - -.. code:: YAML - - # Common unit system for cosmo sims - InternalUnitSystem: - UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams - UnitLength_in_cgs: 3.08567758e24 # 1 Mpc in centimeters - UnitVelocity_in_cgs: 1e5 # 1 km/s in centimeters per second - UnitCurrent_in_cgs: 1 # 1 Ampere - UnitTemp_in_cgs: 1 # 1 Kelvin - -Note that there are currently no variables in any of the SWIFT physics -schemes that make use of the unit of electric current. There is also -no incentive to use anything else than Kelvin but that makes the whole -system consistent with any possible unit system. - -If one is interested in using the more humourous `FFF unit -system <https://en.wikipedia.org/wiki/FFF_system>`_ one would use - -.. code:: YAML - - # FFF unit system - InternalUnitSystem: - UnitMass_in_cgs: 40823.3133 # 1 Firkin (fir) in grams - UnitLength_in_cgs: 20116.8 # 1 Furlong (fur) in cm - UnitVelocity_in_cgs: 0.01663095 # 1 Furlong (fur) per Fortnight (ftn) in cm/s - UnitCurrent_in_cgs: 1 # 1 Ampere - UnitTemp_in_cgs: 1 # 1 Kelvin - -The value of the physical constants in this system is left as an -exercise for the reader [#f1]_. - -Cosmology ---------- - -When running a cosmological simulation, this section set the values of the -cosmological model. The epanded :math:`\Lambda\rm{CDM}` parameters governing the -background evolution of the Univese need to be specified here. These are: - -* The reduced Hubble constant: :math:`h`: ``h``, -* The matter density parameter :math:`\Omega_m`: ``Omega_m``, -* The cosmological constant density parameter :math:`\Omega_\Lambda`: ``Omega_lambda``, -* The baryon density parameter :math:`\Omega_b`: ``Omega_b``, -* The radiation density parameter :math:`\Omega_r`: ``Omega_r``. - -The last parameter can be omitted and will default to :math:`\Omega_r = 0`. Note -that SWIFT will verify on start-up that the matter content of the initial conditions -matches the cosmology specified in this section. - -This section als specifies the start and end of the simulation expressed in -terms of scale-factors. The two parameters are: - -* Initial scale-factor: ``a_begin``, -* Final scale-factor: ``a_end``. - -Two additional optional parameters can be used to change the equation of -state of dark energy :math:`w(a)`. We use the evolution law :math:`w(a) = -w_0 + w_a (1 - a)`. The two parameters in the YAML file are: - -* The :math:`z=0` dark energy equation of state parameter :math:`w_0`: ``w_0`` -* The dark energy equation of state evolutio parameter :math:`w_a`: ``w_a`` - -If unspecified these parameters default to the default -:math:`\Lambda\rm{CDM}` values of :math:`w_0 = -1` and :math:`w_a = 0`. - -For a Planck+13 cosmological model (ignoring radiation density as is -commonly done) and running from :math:`z=127` to :math:`z=0`, one would hence -use the following parameters: - -.. code:: YAML - - Cosmology: - a_begin: 0.0078125 # z = 127 - a_end: 1.0 # z = 0 - h: 0.6777 - Omega_m: 0.307 - Omega_lambda: 0.693 - Omega_b: 0.0455 - Omega_r: 0. # (Optional) - w_0: -1.0 # (Optional) - w_a: 0. # (Optional) - -When running a non-cosmological simulation (i.e. without the ``-c`` runtime -flag) this section of the YAML file is entirely ignored. - -Gravity -------- - -The behaviour of the self-gravity solver can be modifed by the parameters -provided in this section. The theory document puts these parameters into the -context of the equations being solved. We give a brief overview here. - -* The Plummer-equivalent co-moving softening length used for all particles :math:`\epsilon_{com}`: ``comoving_softening``, -* The Plummer-equivalent maximal physical softening length used for all particles :math:`\epsilon_{max}`: ``comoving_softening``, - -At any redshift :math:`z`, the Plummer-equivalent softening length used by the -code will be :math:`\epsilon=\min(\epsilon_{max}, -\frac{\epsilon_{com}}{z+1})`. This is expressed in internal units. - -* The opening angle (multipole acceptance criterion) used in the FMM :math:`\theta`: ``theta``, -* The time-step size pre-factor :math:`\eta`: ``eta``, - -The time-step of a given particle is given by :math:`\Delta t = -\eta\sqrt{\frac{\epsilon}{|\overrightarrow{a}|}}`, where -:math:`\overrightarrow{a}` is the particle's acceleration. Power et al. (2003) recommend using :math:`\eta=0.025`. -The last tree-related parameter is - -* The tree rebuild frequency: ``rebuild_frequency``. - -Thqe tree rebuild frequency is an optional parameter defaulting to -:math:`0.01`. It is used to trigger the re-construction of the tree every time a -fraction of the particles have been integrated (kicked) forward in time. - -Simulations using periodic boundary conditions use additional parameters for the -Particle-Mesh part of the calculation. The last three are optional: - -* The number cells along each axis of the mesh :math:`N`: ``mesh_side_length``, -* The mesh smoothing scale in units of the mesh cell-size :math:`a_{\rm - smooth}`: ``a_smooth`` (default: ``1.25``), -* The scale above which the short-range forces are assumed to be 0 (in units of - the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm - cut,max}`: ``r_cut_max`` (default: ``4.5``), -* The scale bewlo which the short-range forces are assumed to be exactly Newtonian (in units of - the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm - cut,min}`: ``r_cut_min`` (default: ``0.1``), - -For most runs, the default values can be used. Only the number of cells along -each axis needs to be sepcified. The remaining three values are best described -in the context of the full set of equations in the theory documents. - -As a summary, here are the values used for the EAGLE :math:`100^3~{\rm Mpc}^3` -simulation: - -.. code:: YAML - - # Parameters for the self-gravity scheme for the EAGLE-100 box - Gravity: - eta: 0.025 - theta: 0.7 - comoving_softening: 0.0026994 # 0.7 proper kpc at z=2.8. - max_physical_softening: 0.0007 # 0.7 proper kpc - rebuild_frequency: 0.01 # Default optional value - mesh_side_length: 512 - a_smooth: 1.25 # Default optional value - r_cut_max: 4.5 # Default optional value - r_cut_min: 0.1 # Default optional value - - -SPH ---- - -Time Integration ----------------- - -Initial Conditions ------------------- - -This section of the parameter file contains all the options related to -the initial conditions. The main two parameters are - -* The name of the initial conditions file: ``file_name``, -* Whether the problem uses periodic boundary conditions or not: ``periodic``. - -The file path is relative to where the code is being executed. These -parameters can be complemented by some optional values to drive some -specific behaviour of the code. - -* Whether to generate gas particles from the DM particles: ``generate_gas_in_ics`` (default: ``0``), -* Whether to activate an additional clean-up of the SPH smoothing lengths: ``cleanup_smoothing_lengths`` (default: ``0``) - -The procedure used to generate gas particles from the DM ones is -outlined in the theory documents and is too long for a full -description here. The cleaning of the smoothing lengths is an -expensive operation but can be necessary in the cases where the -initial conditions are of poor quality and the values of the smoothing -lengths are far from the values they should have. - -When starting from initial conditions created for Gadget, some -additional flags can be used to convert the values from h-full to -h-free and remove the additional :math:`\sqrt{a}` in the velocities: - -* Whether to re-scale all the fields to remove powers of h from the quantities: ``cleanup_h_factors`` (default: ``0``), -* Whether to re-scale the velocities to remove the :math:`\sqrt{a}` assumed by Gadget : ``cleanup_velocity_factors`` (default: ``0``). - -The h-factors are self-consistently removed according to their units -and this is applied to all the quantities irrespective of particle -types. The correct power of ``h`` is always calculated for each -quantity. - -Finally, SWIFT also offers these options: - -* A factor to re-scale all the smoothing-lengths by a fixed amount: ``smoothing_length_scaling`` (default: ``1.``), -* A shift to apply to all the particles: ``shift`` (default: ``[0.0,0.0,0.0]``), -* Whether to replicate the box along each axis: ``replicate`` (default: ``1``). - -The shift is expressed in internal units. The option to replicate the -box is especially useful for weak-scaling tests. When set to an -integer >1, the box size is multiplied by this integer along each axis -and the particles are duplicated and shifted such as to create exact -copies of the simulation volume. - -The full section to start a DM+hydro run from Gadget DM-only ICs would -be: - -.. code:: YAML - - InitialConditions: - file_name: my_ics.hdf5 - periodic: 1 - cleanup_h_factors: 1 - cleanup_velocity_factors: 1 - generate_gas_in_ics: 1 - cleanup_smoothing_lengths: 1 - - -Physical Constants ------------------- - -For some idealised test it can be useful to overwrite the value of -some physical constants; in particular the value of the gravitational -constant. SWIFT offers an optional parameter to overwrite the value of -:math:`G_N`. - -.. code:: YAML - - PhysicalConstants: - G: 1 - -Note that this set :math:`G` to the specified value in the internal system -of units. Setting a value of `1` when using the system of units (10^10 Msun, -Mpc, km/s) will mean that :math:`G_N=1` in these units [#f2]_ instead of the -normal value :math:`G_N=43.00927`. - -This option is only used for specific tests and debugging. This entire -section of the YAML file can typically be left out. More constants may -be handled in the same way in future versions. - -Snapshots ---------- - -Some additional specific options for the snapshot outputs are described in the -following pages: +This section desrcibes the options that are available in the +parameter files. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 + :caption: Contents: + parameter_description output_selection - -Statistics ----------- - -Restarts --------- - -SWIFT can write check-pointing files and restart from them. The behaviour of -this mechanism is driven by the options int he `Restarts` section of the YAML -parameter file. All the parameters are optional but default to values that -ensure a reasonable behaviour. - -* Wether or not to enable the dump of restart files: ``enable`` (default: - ``1``). - -This parameter acts a master-switch for the check-pointing capabilities. All the -other options require the ``enable`` parameter to be set to ``1``. - -* Wether or not to save a copy of the previous set of check-pointing files: - ``save`` (default: ``1``), -* Wether or not to dump a set of restart file on regular exit: ``onexit`` - (default: ``0``), -* The wall-clock time in hours between two sets of restart files: - ``delta_hours`` (default: ``6.0``). - -Note that there is no buffer time added to the ``delta_hours`` value. If the -system's batch queue run time limit is set to 6 hours, the user must specify a -smaller value to allow for enough time to safely dump the check-point files. - -* The sub-directory in which to store the restart files: ``subdir`` (default: - ``restart``), -* The basename of the restart files: ``basename`` (default: ``swift``) - -If the directory does not exist, SWIFT will create it. When resuming a run, -SWIFT, will look for files with the name provided in the sub-directory specified -here. The files themselves are named ``basename_000001.rst`` where the basenme -is replaced by the user-specified name and the 6-digits number corresponds to -the MPI-rank. SWIFT writes one file per MPI rank. If the ``save`` option has -been activated, the previous set of restart files will be named -``basename_000000.rst.prev``. - -SWIFT can also be stopped by creating an empty file called ``stop`` in the -directory where the code runs. This will make SWIFT dump a fresh set of restart -file (irrespective of the specified ``delta_time`` between dumps) and exit -cleanly. One parameter governs this behaviour: - -* Number of steps between two checks for the presence of a ``stop`` file: - ``stop_steps`` (default: ``100``). - -The default value is chosen such that SWIFT does not need to poll the -file-system to often, which can take a significant amount of time on distributed -systems. For runs where the small time-steps take a much larger amount of time, -a smaller value is recommended to allow for a finer control over when the code -can be stopped. - -Finally, SWIFT can automatically stop after a specified amount of wall-clock -time. The code can also run a command when exiting in this fashion, which can be -used, for instance, to interact with the batch queue system: - -* Maximal wall-clock run time in hours: ``max_run_time`` (default: ``24.0``), -* Whether or not to run a command on exit: ``resubmit_on_exit`` (default: - ``0``), -* The command to run on exit: ``resubmit_command`` (default: ``./resub.sh``). - -Note that no check is performed on the validity of the command to run. SWIFT -simply calls ``system()`` with the user-specified command. - -To run SWIFT, dumping check-pointing files every 6 hours and running for 24 -hours after which a shell command will be run, one would use: - -.. code:: YAML - - Restarts: - enable: 1 - save: 1 # Keep copies - onexit: 0 - subdir: restart # Sub-directory of the directory where SWIFT is run - basename: swift - delta_hours: 6.0 - stop_steps: 100 - max_run_time: 24.0 # In hours - resubmit_on_exit: 1 - resubmit_command: ./resub.sh - - - -Scheduler ---------- - -Domain Decomposition --------------------- - -.. [#f1] The thorough reader (or overly keen SWIFT tester) would find that the speed of light is :math:`c=1.8026\times10^{12}\,\rm{fur}\,\rm{ftn}^{-1}`, Newton's contant becomes :math:`G_N=4.896735\times10^{-4}~\rm{fur}^3\,\rm{fir}^{-1}\,\rm{ftn}^{-2}` and Planck's constant turns into :math:`h=4.851453\times 10^{-34}~\rm{fur}^2\,\rm{fir}\,\rm{ftn}^{-1}`. - - -.. [#f2] which would translate into a constant :math:`G_N=1.5517771\times10^{-9}~cm^{3}\,g^{-1}\,s^{-2}` if expressed in the CGS system. diff --git a/doc/RTD/source/ParameterFiles/output_selection.rst b/doc/RTD/source/ParameterFiles/output_selection.rst index ca905c5e613082cdf9de9db718bdd16c4a3c8951..b84a776c7dcac2136dedd2324cfef43d7a5455ea 100644 --- a/doc/RTD/source/ParameterFiles/output_selection.rst +++ b/doc/RTD/source/ParameterFiles/output_selection.rst @@ -1,5 +1,5 @@ .. Parameter File - Loic Hausammann, 1 june 2018 + Loic Hausammann, 1 June 2018 .. _Output_list_label: @@ -36,8 +36,12 @@ Example of file with redshift:: 10 5 -.. _Output_selection_label: +If an output list is specified, the basic values for the first +snapshot (``time_first``, ``scale_factor_first``) and difference +(``delta_time``) are ignored. +.. _Output_selection_label: + Output Selection ~~~~~~~~~~~~~~~~ @@ -53,13 +57,13 @@ default all fields are written. This field is mostly used to remove unnecessary output by listing them with 0's. A classic use-case for this feature is a DM-only simulation -(pure n-body) where all particles have the same mass. Outputing the +(pure n-body) where all particles have the same mass. Outputting the mass field in the snapshots results in extra i/o time and unnecessary waste of disk space. The corresponding section of the ``yaml`` parameter file would look like this:: SelectOutput: - Masses_DM: 0 + Masses_DM: 0 You can generate a ``yaml`` file containing all the possible fields -available for a given configuration of SWIFT by running ``./swift -o output.yml``. +available for a given configuration of SWIFT by running ``./swift --output-params output.yml``. diff --git a/doc/RTD/source/ParameterFiles/parameter_description.rst b/doc/RTD/source/ParameterFiles/parameter_description.rst new file mode 100644 index 0000000000000000000000000000000000000000..207c173001437f283688699fb507999c0225ec21 --- /dev/null +++ b/doc/RTD/source/ParameterFiles/parameter_description.rst @@ -0,0 +1,934 @@ +.. Parameter Description + Matthieu Schaller, 21st October 2018 + +.. _Parameters_basics: + +File format and basic information +--------------------------------- + +The parameter file uses a format similar to the `YAML format +<https://en.wikipedia.org/wiki/YAML>`_ but reduced to only the +elements required for the SWIFT parameters. Options are given by a +name followed by a column and the value of the parameter: + +.. code:: YAML + + ICs: santa_barbara.hdf5 + dt_max: 1.5 + shift: [2., 4., 5.] + +Comments can be inserted anywhere and start with a hash: + +.. code:: YAML + + # Description of the physics + viscosity_alpha: 2.0 + dt_max: 1.5 # seconds + +A typical SWIFT parameter file is split into multiple sections that +may or may not be present depending on the different configuration +options. The sections start with a label and can contain any number of +parameters: + +.. code:: YAML + + Cosmology: # Planck13 + Omega_m: 0.307 + Omega_lambda: 0.693 + Omega_b: 0.0455 + h: 0.6777 + a_begin: 0.0078125 # z = 127 + +The options can be integer values, floating point numbers, characters +or strings. If SWIFT expects a number and string is given, an error +will be raised. The code can also read an array of values: + +.. code:: YAML + + shift: [2., 4., 5.] + +Some options in the parameter file are optional and +when not provided, SWIFT will run with the default value. However, if +a compulsory parameter is missing an error will be raised at +start-up. + +Finally, SWIFT outputs two YAML files at the start of a run. The first one +``used_parameters.yml`` contains all the parameters that were used for this run, +**including all the optional parameters left unspecified with their default +values**. This file can be used to start an exact copy of the run. The second +file, ``unused_parameters.yml`` contains all the values that were not read from +the parameter file. This can be used to simplify the parameter file or check +that nothing important was ignored (for instance because the code is not +configured to use some options). + +The rest of this page describes all the SWIFT parameters, split by +section. A list of all the possible parameters is kept in the file +``examples/parameter_examples.yml``. + +.. _Parameters_units: + +Internal Unit System +-------------------- + +The ``InternalUnitSystem`` section describes the units used internally by the +code. This is the system of units in which all the equations are solved. All +physical constants are converted to this system and if the ICs use a different +system (see the snapshots' ref:`ICs_units_label` section of the documentation) +the particle quantities will be converted when read in. + +The system of units is described using the value of the 5 basic units +of any system with respect to the CGS system. Instead of using a unit +of time we use a unit of velocity as this is more intuitive. Users +hence need to provide: + +* a unit of length: ``UnitLength_in_cgs``, +* a unit of mass: ``UnitMass_in_cgs``, +* a unit of velocity ``UnitVelocity_in_cgs``, +* a unit of electric current ``UnitCurrent_in_cgs``, +* a unit of temperature ``UnitTemp_in_cgs``. + +All these need to be expressed with respect to their cgs counter-part +(i.e. :math:`cm`, :math:`g`, :math:`cm/s`, :math:`A` and :math:`K`). Recall +that there are no h-factors in any of SWIFT's quantities; we, for instance, +use :math:`cm` and not :math:`cm/h`. + +For instance to use the commonly adopted system of 10^10 Msun as a +unit for mass, mega-parsec as a unit of length and km/s as a unit of +speed, we would use: + +.. code:: YAML + + # Common unit system for cosmo sims + InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # 1 Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # 1 km/s in centimeters per second + UnitCurrent_in_cgs: 1 # 1 Ampere + UnitTemp_in_cgs: 1 # 1 Kelvin + +Note that there are currently no variables in any of the SWIFT physics +schemes that make use of the unit of electric current. There is also +no incentive to use anything else than Kelvin but that makes the whole +system consistent with any possible unit system. + +If one is interested in using the more humorous `FFF unit +system <https://en.wikipedia.org/wiki/FFF_system>`_ one would use + +.. code:: YAML + + # FFF unit system + InternalUnitSystem: + UnitMass_in_cgs: 40823.3133 # 1 Firkin (fir) in grams + UnitLength_in_cgs: 20116.8 # 1 Furlong (fur) in cm + UnitVelocity_in_cgs: 0.01663095 # 1 Furlong (fur) per Fortnight (ftn) in cm/s + UnitCurrent_in_cgs: 1 # 1 Ampere + UnitTemp_in_cgs: 1 # 1 Kelvin + +The value of the physical constants in this system is left as an +exercise for the reader [#f1]_. + +.. _Parameters_cosmology: + +Cosmology +--------- + +When running a cosmological simulation, the section ``Cosmology`` sets the values of the +cosmological model. The expanded :math:`\Lambda\rm{CDM}` parameters governing the +background evolution of the Universe need to be specified here. These are: + +* The reduced Hubble constant: :math:`h`: ``h``, +* The matter density parameter :math:`\Omega_m`: ``Omega_m``, +* The cosmological constant density parameter :math:`\Omega_\Lambda`: ``Omega_lambda``, +* The baryon density parameter :math:`\Omega_b`: ``Omega_b``, +* The radiation density parameter :math:`\Omega_r`: ``Omega_r``. + +The last parameter can be omitted and will default to :math:`\Omega_r = 0`. Note +that SWIFT will verify on start-up that the matter content of the initial conditions +matches the cosmology specified in this section. + +This section also specifies the start and end of the simulation expressed in +terms of scale-factors. The two parameters are: + +* Initial scale-factor: ``a_begin``, +* Final scale-factor: ``a_end``. + +Two additional optional parameters can be used to change the equation of +state of dark energy :math:`w(a)`. We use the evolution law :math:`w(a) = +w_0 + w_a (1 - a)`. The two parameters in the YAML file are: + +* The :math:`z=0` dark energy equation of state parameter :math:`w_0`: ``w_0`` +* The dark energy equation of state evolution parameter :math:`w_a`: ``w_a`` + +If unspecified these parameters default to the default +:math:`\Lambda\rm{CDM}` values of :math:`w_0 = -1` and :math:`w_a = 0`. + +For a Planck+13 cosmological model (ignoring radiation density as is +commonly done) and running from :math:`z=127` to :math:`z=0`, one would hence +use the following parameters: + +.. code:: YAML + + Cosmology: + a_begin: 0.0078125 # z = 127 + a_end: 1.0 # z = 0 + h: 0.6777 + Omega_m: 0.307 + Omega_lambda: 0.693 + Omega_b: 0.0455 + Omega_r: 0. # (Optional) + w_0: -1.0 # (Optional) + w_a: 0. # (Optional) + +When running a non-cosmological simulation (i.e. without the ``-c`` run-time +flag) this section of the YAML file is entirely ignored. + +.. _Parameters_gravity: + +Gravity +------- + +The behaviour of the self-gravity solver can be modified by the parameters +provided in the ``Gravity`` section. The theory document puts these parameters into the +context of the equations being solved. We give a brief overview here. + +* The Plummer-equivalent co-moving softening length used for all particles :math:`\epsilon_{com}`: ``comoving_softening``, +* The Plummer-equivalent maximal physical softening length used for all particles :math:`\epsilon_{max}`: ``comoving_softening``, + +At any redshift :math:`z`, the Plummer-equivalent softening length used by the +code will be :math:`\epsilon=\min(\epsilon_{max}, +\frac{\epsilon_{com}}{z+1})`. This is expressed in internal units. + +* The opening angle (multipole acceptance criterion) used in the FMM :math:`\theta`: ``theta``, +* The time-step size pre-factor :math:`\eta`: ``eta``, + +The time-step of a given particle is given by :math:`\Delta t = +\eta\sqrt{\frac{\epsilon}{|\overrightarrow{a}|}}`, where +:math:`\overrightarrow{a}` is the particle's acceleration. `Power et al. (2003) <http://adsabs.harvard.edu/abs/2003MNRAS.338...14P>`_ recommend using :math:`\eta=0.025`. +The last tree-related parameter is + +* The tree rebuild frequency: ``rebuild_frequency``. + +The tree rebuild frequency is an optional parameter defaulting to +:math:`0.01`. It is used to trigger the re-construction of the tree every time a +fraction of the particles have been integrated (kicked) forward in time. + +Simulations using periodic boundary conditions use additional parameters for the +Particle-Mesh part of the calculation. The last three are optional: + +* The number cells along each axis of the mesh :math:`N`: ``mesh_side_length``, +* The mesh smoothing scale in units of the mesh cell-size :math:`a_{\rm + smooth}`: ``a_smooth`` (default: ``1.25``), +* The scale above which the short-range forces are assumed to be 0 (in units of + the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm + cut,max}`: ``r_cut_max`` (default: ``4.5``), +* The scale below which the short-range forces are assumed to be exactly Newtonian (in units of + the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm + cut,min}`: ``r_cut_min`` (default: ``0.1``), + +For most runs, the default values can be used. Only the number of cells along +each axis needs to be specified. The remaining three values are best described +in the context of the full set of equations in the theory documents. + +As a summary, here are the values used for the EAGLE :math:`100^3~{\rm Mpc}^3` +simulation: + +.. code:: YAML + + # Parameters for the self-gravity scheme for the EAGLE-100 box + Gravity: + eta: 0.025 + theta: 0.7 + comoving_softening: 0.0026994 # 0.7 proper kpc at z=2.8. + max_physical_softening: 0.0007 # 0.7 proper kpc + rebuild_frequency: 0.01 # Default optional value + mesh_side_length: 512 + a_smooth: 1.25 # Default optional value + r_cut_max: 4.5 # Default optional value + r_cut_min: 0.1 # Default optional value + + +.. _Parameters_SPH: + +SPH +--- + +.. _Parameters_time_integration: + +Time Integration +---------------- + +The ``TimeIntegration`` section is used to set some general parameters related to time +integration. In all cases, users have to provide a minimal and maximal time-step +size: + +* Maximal time-step size: ``dt_max`` +* Minimal time-step size: ``dt_min`` + +These quantities are expressed in internal units. All particles will have their +time-step limited by the maximal value on top of all the other criteria that may +apply to them (gravity acceleration, Courant condition, etc.). If a particle +demands a time-step size smaller than the minimum, SWIFT will abort with an +error message. This is a safe-guard against simulations that would never +complete due to the number of steps to run being too large. + +When running a non-cosmological simulation, the user also has to provide the +time of the start and the time of the end of the simulation: + +* Start time: ``time_begin`` +* End time: ``time_end`` + +Both are expressed in internal units. The start time is typically set to ``0`` +but SWIFT can handle any value here. For cosmological runs, these values are +ignored and the start- and end-points of the runs are specified by the start and +end scale-factors in the cosmology section of the parameter file. + +Additionally, when running a cosmological volume, advanced users can specify the +value of the dimensionless pre-factor entering the time-step condition linked +with the motion of particles with respect to the background expansion and mesh +size. See the theory document for the exact equations. + +* Dimensionless pre-factor of the maximal allowed displacement: + ``max_dt_RMS_factor`` (default: ``0.25``) + +This value rarely needs altering. + +A full time-step section for a non-cosmological run would be: + +.. code:: YAML + + TimeIntegration: + time_begin: 0 # Start time in internal units. + time_end: 10. # End time in internal units. + dt_max: 1e-2 + dt_min: 1e-6 + +Whilst for a cosmological run, one would need: + +.. code:: YAML + + TimeIntegration: + dt_max: 1e-4 + dt_min: 1e-10 + max_dt_RMS_factor: 0.25 # Default optional value + +.. _Parameters_ICs: + +Initial Conditions +------------------ + +The ``InitialConditions`` section of the parameter file contains all the options related to +the initial conditions. The main two parameters are + +* The name of the initial conditions file: ``file_name``, +* Whether the problem uses periodic boundary conditions or not: ``periodic``. + +The file path is relative to where the code is being executed. These +parameters can be complemented by some optional values to drive some +specific behaviour of the code. + +* Whether to generate gas particles from the DM particles: ``generate_gas_in_ics`` (default: ``0``), +* Whether to activate an additional clean-up of the SPH smoothing lengths: ``cleanup_smoothing_lengths`` (default: ``0``) + +The procedure used to generate gas particles from the DM ones is +outlined in the theory documents and is too long for a full +description here. The cleaning of the smoothing lengths is an +expensive operation but can be necessary in the cases where the +initial conditions are of poor quality and the values of the smoothing +lengths are far from the values they should have. + +When starting from initial conditions created for Gadget, some +additional flags can be used to convert the values from h-full to +h-free and remove the additional :math:`\sqrt{a}` in the velocities: + +* Whether to re-scale all the fields to remove powers of h from the quantities: ``cleanup_h_factors`` (default: ``0``), +* Whether to re-scale the velocities to remove the :math:`\sqrt{a}` assumed by Gadget : ``cleanup_velocity_factors`` (default: ``0``). + +The h-factors are self-consistently removed according to their units +and this is applied to all the quantities irrespective of particle +types. The correct power of ``h`` is always calculated for each +quantity. + +Finally, SWIFT also offers these options: + +* A factor to re-scale all the smoothing-lengths by a fixed amount: ``smoothing_length_scaling`` (default: ``1.``), +* A shift to apply to all the particles: ``shift`` (default: ``[0.0,0.0,0.0]``), +* Whether to replicate the box along each axis: ``replicate`` (default: ``1``). + +The shift is expressed in internal units. The option to replicate the +box is especially useful for weak-scaling tests. When set to an +integer >1, the box size is multiplied by this integer along each axis +and the particles are duplicated and shifted such as to create exact +copies of the simulation volume. + +The full section to start a DM+hydro run from Gadget DM-only ICs would +be: + +.. code:: YAML + + InitialConditions: + file_name: my_ics.hdf5 + periodic: 1 + cleanup_h_factors: 1 + cleanup_velocity_factors: 1 + generate_gas_in_ics: 1 + cleanup_smoothing_lengths: 1 + + +.. _Parameters_constants: + +Physical Constants +------------------ + +For some idealised test it can be useful to overwrite the value of +some physical constants; in particular the value of the gravitational +constant. SWIFT offers an optional parameter to overwrite the value of +:math:`G_N`. + +.. code:: YAML + + PhysicalConstants: + G: 1 + +Note that this set :math:`G` to the specified value in the internal system +of units. Setting a value of `1` when using the system of units (10^10 Msun, +Mpc, km/s) will mean that :math:`G_N=1` in these units [#f2]_ instead of the +normal value :math:`G_N=43.00927`. + +This option is only used for specific tests and debugging. This entire +section of the YAML file can typically be left out. More constants may +be handled in the same way in future versions. + +.. _Parameters_snapshots: + +Snapshots +--------- + +The ``Snapshots`` section of the parameter file contains all the options related to +the dump of simulation outputs in the form of HDF5 :ref:`snapshots`. The main +parameter is the base name that will be used for all the outputs in the run: + +* The base name of the HDF5 snapshots: ``basename``. + +This name will then be appended by an under-score and 4 digits followed by +``.hdf5`` (e.g. ``base_name_1234.hdf5``). The 4 digits are used to label the +different outputs, starting at ``0000``. In the default setup the digits simply +increase by one for each snapshot. However, if the optional parameter +``int_time_label_on`` is switched on, then we use 6 digits and these will the +physical time of the simulation rounded to the nearest integer +(e.g. ``base_name_001234.hdf5``) [#f3]_. + +The time of the first snapshot is controlled by the two following options: + +* Time of the first snapshot (non-cosmological runs): ``time_first``, +* Scale-factor of the first snapshot (cosmological runs): ``scale_factor_first``. + +One of those two parameters has to be provided depending on the type of run. In +the case of non-cosmological runs, the time of the first snapshot is expressed +in the internal units of time. Users also have to provide the difference in time +(or scale-factor) between consecutive outputs: + +* Time difference between consecutive outputs: ``delta_time``. + +In non-cosmological runs this is also expressed in internal units. For +cosmological runs, this value is *multiplied* to obtain the +scale-factor of the next snapshot. This implies that the outputs are +equally space in :math:`\log(a)` (See :ref:`Output_list_label` to have +snapshots not regularly spaced in time). + +When running the code with structure finding activated, it is often +useful to have a structure catalog written at the same simulation time +as the snapshots. To activate this, the following parameter can be +switched on: + +* Run VELOCIraptor every time a snapshot is dumped: ``invoke_stf`` + (default: ``0``). + +This produces catalogs using the options specified for the stand-alone +VELOCIraptor outputs (see the section :ref:`Parameters_structure_finding`) but +with a base name and output number that matches the snapshot name +(e.g. ``stf_base_name_1234.hdf5``) irrespective of the name specified in the +section dedicated to VELOCIraptor. Note that the invocation of VELOCIraptor at +every dump is done additionally to the stand-alone dumps that can be specified +in the corresponding section of the YAML parameter file. + +Users can optionally specify the level of compression used by the HDF5 library +using the parameter: + +* GZIP compression level of the HDF5 arrays: ``compression`` (default: ``0``). + +The default level of ``0`` implies no compression and values have to be in the +range :math:`[0-9]`. This integer is passed to the i/o library and used for the +loss-less GZIP compression algorithm. Higher values imply higher compression but +also more time spent deflating and inflating the data. Note that up until HDF5 +1.10.x this option is not available when using the MPI-parallel version of the +i/o routines. + +Finally, it is possible to specify a different system of units for the snapshots +than the one that was used internally by SWIFT. The format is identical to the +one described above (See the :ref:`Parameters_units` section) and read: + +* a unit of length: ``UnitLength_in_cgs`` (default: ``InternalUnitSystem:UnitLength_in_cgs``), +* a unit of mass: ``UnitMass_in_cgs`` (default: ``InternalUnitSystem:UnitMass_in_cgs``), +* a unit of velocity ``UnitVelocity_in_cgs`` (default: ``InternalUnitSystem:UnitVelocity_in_cgs``), +* a unit of electric current ``UnitCurrent_in_cgs`` (default: ``InternalUnitSystem:UnitCurrent_in_cgs``), +* a unit of temperature ``UnitTemp_in_cgs`` (default: ``InternalUnitSystem:UnitTemp_in_cgs``). + +When un-specified, these all take the same value as assumed by the internal +system of units. These are rarely used but can offer a practical alternative to +converting data in the post-processing of the simulations. + +For a standard cosmological run with structure finding activated, the +full section would be: + +.. code:: YAML + + Snapshots: + basename: output + scale_factor_first: 0.02 # z = 49 + delta_time: 1.02 + invoke_stf: 1 + +Showing all the parameters for a basic hydro test-case, one would have: + +.. code:: YAML + + Snapshots: + basename: sedov + time_first: 0.01 + delta_time: 0.005 + invoke_stf: 0 + int_time_label_on: 0 + compression: 3 + UnitLength_in_cgs: 1. # Use cm in outputs + UnitMass_in_cgs: 1. # Use grams in outputs + UnitVelocity_in_cgs: 1. # Use cm/s in outputs + UnitCurrent_in_cgs: 1. # Use Ampere in outputs + UnitTemp_in_cgs: 1. # Use Kelvin in outputs + +Some additional specific options for the snapshot outputs are described in the +following pages: + +* :ref:`Output_list_label` (to have snapshots not evenly spaced in time), +* :ref:`Output_selection_label` (to select what particle fields to write). + + +.. _Parameters_statistics: + +Statistics +---------- + +Some additional specific options for the statistics outputs are described in the +following page: + +* :ref:`Output_list_label` (to have statistics outputs not evenly spaced in time). + +.. _Parameters_restarts: + +Restarts +-------- + +SWIFT can write check-pointing files and restart from them. The behaviour of +this mechanism is driven by the options in the ``Restarts`` section of the YAML +parameter file. All the parameters are optional but default to values that +ensure a reasonable behaviour. + +* Whether or not to enable the dump of restart files: ``enable`` (default: + ``1``). + +This parameter acts a master-switch for the check-pointing capabilities. All the +other options require the ``enable`` parameter to be set to ``1``. + +* Whether or not to save a copy of the previous set of check-pointing files: + ``save`` (default: ``1``), +* Whether or not to dump a set of restart file on regular exit: ``onexit`` + (default: ``0``), +* The wall-clock time in hours between two sets of restart files: + ``delta_hours`` (default: ``6.0``). + +Note that there is no buffer time added to the ``delta_hours`` value. If the +system's batch queue run time limit is set to 6 hours, the user must specify a +smaller value to allow for enough time to safely dump the check-point files. + +* The sub-directory in which to store the restart files: ``subdir`` (default: + ``restart``), +* The basename of the restart files: ``basename`` (default: ``swift``) + +If the directory does not exist, SWIFT will create it. When resuming a run, +SWIFT, will look for files with the name provided in the sub-directory specified +here. The files themselves are named ``basename_000001.rst`` where the basename +is replaced by the user-specified name and the 6-digits number corresponds to +the MPI-rank. SWIFT writes one file per MPI rank. If the ``save`` option has +been activated, the previous set of restart files will be named +``basename_000000.rst.prev``. + +SWIFT can also be stopped by creating an empty file called ``stop`` in the +directory where the code runs. This will make SWIFT dump a fresh set of restart +file (irrespective of the specified ``delta_time`` between dumps) and exit +cleanly. One parameter governs this behaviour: + +* Number of steps between two checks for the presence of a ``stop`` file: + ``stop_steps`` (default: ``100``). + +The default value is chosen such that SWIFT does not need to poll the +file-system to often, which can take a significant amount of time on distributed +systems. For runs where the small time-steps take a much larger amount of time, +a smaller value is recommended to allow for a finer control over when the code +can be stopped. + +Finally, SWIFT can automatically stop after a specified amount of wall-clock +time. The code can also run a command when exiting in this fashion, which can be +used, for instance, to interact with the batch queue system: + +* Maximal wall-clock run time in hours: ``max_run_time`` (default: ``24.0``), +* Whether or not to run a command on exit: ``resubmit_on_exit`` (default: + ``0``), +* The command to run on exit: ``resubmit_command`` (default: ``./resub.sh``). + +Note that no check is performed on the validity of the command to run. SWIFT +simply calls ``system()`` with the user-specified command. + +To run SWIFT, dumping check-pointing files every 6 hours and running for 24 +hours after which a shell command will be run, one would use: + +.. code:: YAML + + Restarts: + enable: 1 + save: 1 # Keep copies + onexit: 0 + subdir: restart # Sub-directory of the directory where SWIFT is run + basename: swift + delta_hours: 6.0 + stop_steps: 100 + max_run_time: 24.0 # In hours + resubmit_on_exit: 1 + resubmit_command: ./resub.sh + +.. _Parameters_scheduler: + +Scheduler +--------- + +The Scheduler section contains various parameters that control how the cell +tree is configured and defines some values for the related tasks. In general +these should be considered as tuning parameters, both for speed and memory +use. + +.. code:: YAML + + nr_queues: 0 + +Defines the number of task queues used. These are normally set to one per +thread and should be at least that number. + +A number of parameters decide how the cell tree will be split into sub-cells, +according to the number of particles and their expected interaction count, +and the type of interaction. These are: + +.. code:: YAML + + cell_max_size: 8000000 + cell_sub_size_pair_hydro: 256000000 + cell_sub_size_self_hydro: 32000 + cell_sub_size_pair_grav: 256000000 + cell_sub_size_self_grav: 32000 + cell_sub_size_pair_stars: 256000000 + cell_sub_size_self_stars: 32000 + cell_split_size: 400 + +when possible cells that exceed these constraints will be split into a further +level of sub-cells. So for instance a sub-cell should not contain more than +400 particles (this number defines the scale of most `N*N` interactions). + +To control the number of self-gravity tasks we have the parameter: + +.. code:: YAML + + cell_subdepth_diff_grav: 4 + +which stops these from being done at the scale of the leaf cells, of which +there can be a large number. In this case cells with gravity tasks must be at +least 4 levels above the leaf cells (when possible). + +Extra space is required when particles are created in the system (to the time +of the next rebuild). These are controlled by: + +.. code:: YAML + + cell_extra_parts: 0 + cell_extra_gparts: 0 + cell_extra_sparts: 400 + + +The number of top-level cells is controlled by the parameter: + +.. code:: YAML + + max_top_level_cells: 12 + +this is the number per dimension, we will have 12x12x12 cells. There must be +at least 3 top-level cells per dimension. + +The number of top-level cells should be set so that the number of particles +per cell is not too large, this is particularly important when using MPI as +this defines the maximum size of cell exchange and also the size of non-local +cells (these are used for cell interactions with local cells), which can have +a large influence on memory use. Best advice for this is to at least scale for +additional nodes. + +The memory used for holding the task and task-link lists needs to be +pre-allocated, but cannot be pre-calculated, so we have the two parameters: + +.. code:: YAML + + tasks_per_cell: 0.0 + links_per_tasks: 10 + +which are guesses at the mean numbers of tasks per cell and number of links +per task. The tasks_per_cell value will be conservatively guessed when set to +0.0, but you will be able to save memory by setting a value. The way to get a +better estimate is to run SWIFT with verbose reporting on (```--verbose=1```) +and check for the lines that report the ```per cell``` or with MPI ``maximum +per cell``` values. This number can vary as the balance between MPI ranks +does, so it is probably best to leave some head room. + +If these are exceeded you should get an obvious error message. + +Finally the parameter: + +.. code:: YAML + + mpi_message_limit: 4096 + +Defines the size (in bytes) below which MPI communication will be sent using +non-buffered calls. These should have lower latency, but how that works or +is honoured is an implementation question. + + +.. _Parameters_domain_decomposition: + +Domain Decomposition: +--------------------- + +This section determines how the top-level cells are distributed between the +ranks of an MPI run. An ideal decomposition should result in each rank having +a similar amount of work to do, so that all the ranks complete at the same +time. Achieving a good balance requires that SWIFT is compiled with either the +ParMETIS or METIS libraries. ParMETIS is an MPI version of METIS, so is +preferred for performance reasons. + +When we use ParMETIS/METIS the top-level cells of the volume are considered as +a graph, with a cell at each vertex and edges that connect the vertices to all +the neighbouring cells (so we have 26 edges connected to each vertex). +Decomposing such a graph into domains is known as partitioning, so in SWIFT we +refer to domain decomposition as partitioning. + +This graph of cells can have weights associated with the vertices and the +edges. These weights are then used to guide the partitioning, seeking to +balance the total weight of the vertices and minimize the weights of the edges +that are cut by the domain boundaries (known as the edgecut). We can consider +the edge weights as a proxy for the exchange of data between cells, so +minimizing this reduces communication. + +The Initial Partition: +^^^^^^^^^^^^^^^^^^^^^^ + +When SWIFT first starts it reads the initial conditions and then does an +initial distribution of the top-level cells. At this time the only information +available is the cell structure and, by geometry, the particles each cell +should contain. The type of partitioning attempted is controlled by the:: + + DomainDecomposition: + initial_type: + +parameter. Which can have the values *memory*, *region*, *grid* or +*vectorized*: + + + * *memory* + + This is the default if METIS or ParMETIS is available. It performs a + partition based on the memory use of all the particles in each cell, + attempting to equalize the memory used by all the ranks. + How successful this attempt is depends on the granularity of cells and particles + and the number of ranks, clearly if most of the particles are in one cell, + or a small region of the volume, balance is impossible or + difficult. Having more top-level cells makes it easier to calculate a + good distribution (but this comes at the cost of greater overheads). + + * *region* + + The one other METIS/ParMETIS option is "region". This attempts to assign equal + numbers of cells to each rank, with the surface area of the regions minimised + (so we get blobs, rather than rectangular volumes of cells). + +If ParMETIS and METIS are not available two other options are possible, but +will give a poorer partition: + + * *grid* + + Split the cells into a number of axis aligned regions. The number of + splits per axis is controlled by the:: + + initial_grid + + parameter. It takes an array of three values. The product of these values + must equal the number of MPI ranks. If not set a suitable default will be used. + + * *vectorized* + + Allocate the cells on the basis of proximity to a set of seed + positions. The seed positions are picked every nranks along a vectorized + cell list (1D representation). This is guaranteed to give an initial + partition for all cases when the number of cells is greater equal to the + number of MPI ranks, so can be used if the others fail. Don't use this. + +If ParMETIS and METIS are not available then only an initial partition will be +performed. So the balance will be compromised by the quality of the initial +partition. + +Repartitioning: +^^^^^^^^^^^^^^^ + +When ParMETIS or METIS is available we can consider adjusting the balance +during the run, so we can improve from the initial partition and also track +changes in the run that require a different balance. The initial partition is +usually not optimal as although it may have balanced the distribution of +particles it has not taken account of the fact that different particles types +require differing amounts of processing and we have not considered that we +also need to do work requiring communication between cells. This latter point +is important as we are running an MPI job, as inter-cell communication may be +very expensive. + +There are a number of possible repartition strategies which are defined using +the:: + + DomainDecomposition: + repartition_type: + +parameter. The possible values for this are *none*, *fullcosts*, *edgecosts*, +*memory*, *timecosts*. + + * *none* + + Rather obviously, don't repartition. You are happy to run with the + initial partition. + + * *fullcosts* + + Use computation weights derived from the running tasks for the vertex and + edge weights. This is the default. + + * *edgecosts* + + Only use computation weights derived from the running tasks for the edge + weights. + + * *memory* + + Repeat the initial partition with the current particle positions + re-balancing the memory use. + + * *timecosts* + + Only use computation weights derived from the running tasks for the vertex + weights and the expected time the particles will interact in the cells as + the edge weights. Using time as the edge weight has the effect of keeping + very active cells on single MPI ranks, so can reduce MPI communication. + +The computation weights are actually the measured times, in CPU ticks, that +tasks associated with a cell take. So these automatically reflect the relative +cost of the different task types (SPH, self-gravity etc.), and other factors +like how well they run on the current hardware and are optimized by the +compiler used, but this means that we have a constraint on how often we can +consider repartitioning, namely when all (or nearly all) the tasks of the +system have been invoked in a step. To control this we have the:: + + minfrac: 0.9 + +parameter. Which defines the minimum fraction of all the particles in the +simulation that must have been actively updated in the last step, before +repartitioning is considered. + +That then leaves the question of when a run is considered to be out of balance +and should benefit from a repartition. That is controlled by the:: + + trigger: 0.05 + +parameter. This value is the CPU time difference between MPI ranks, as a +fraction, if less than this value a repartition will not be +done. Repartitioning can be expensive not just in CPU time, but also because +large numbers of particles can be exchanged between MPI ranks, so is best +avoided. + +If you are using ParMETIS there additional ways that you can tune the +repartition process. + +METIS only offers the ability to create a partition from a graph, which means +that each solution is independent of those that have already been made, that +can make the exchange of particles very large (although SWIFT attempts to +minimize this), however, using ParMETIS we can use the existing partition to +inform the new partition, this has two algorithms that are controlled using:: + + adaptive: 1 + +which means use adaptive repartition, otherwise simple refinement. The +adaptive algorithm is further controlled by the:: + + itr: 100 + +parameter, which defines the ratio of inter node communication time to data +redistribution time, in the range 0.00001 to 10000000.0. Lower values give +less data movement during redistributions. The best choice for these can only +be determined by experimentation (the gains are usually small, so not really +recommended). + +Finally we have the parameter:: + + usemetis: 0 + +Forces the use of the METIS API, probably only useful for developers. + +**Fixed cost repartitioning:** + +So far we have assumed that repartitioning will only happen after a step that +meets the `minfrac:` and `trigger:` criteria, but we may want to repartition +at some arbitrary steps, and indeed do better than the initial partition +earlier in the run. This can be done using *fixed cost* repartitioning. + +Fixed costs are output during each repartition step into the file +`partition_fixed_costs.h`, this should be created by a test run of your your +full simulation (with possibly with a smaller volume, but all the physics +enabled). This file can then be used to replace the same file found in the +`src/` directory and SWIFT should then be recompiled. Once you have that, you +can use the parameter:: + + use_fixed_costs: 1 + +to control whether they are used or not. If enabled these will be used to +repartition after the second step, which will generally give as good a +repartition immediately as you get at the first unforced repartition. + +Also once these have been enabled you can change the `trigger:` value to +numbers greater than 2, and repartitioning will be forced every `trigger` +steps. This latter option is probably only useful for developers, but tuning +the second step to use fixed costs can give some improvements. + +.. _Parameters_structure_finding: + +Structure finding (VELOCIraptor) +-------------------------------- + + +.. [#f1] The thorough reader (or overly keen SWIFT tester) would find that the speed of light is :math:`c=1.8026\times10^{12}\,\rm{fur}\,\rm{ftn}^{-1}`, Newton's constant becomes :math:`G_N=4.896735\times10^{-4}~\rm{fur}^3\,\rm{fir}^{-1}\,\rm{ftn}^{-2}` and Planck's constant turns into :math:`h=4.851453\times 10^{-34}~\rm{fur}^2\,\rm{fir}\,\rm{ftn}^{-1}`. + + +.. [#f2] which would translate into a constant :math:`G_N=1.5517771\times10^{-9}~cm^{3}\,g^{-1}\,s^{-2}` if expressed in the CGS system. + +.. [#f3] This feature only makes sense for non-cosmological runs for which the + internal time unit is such that when rounded to the nearest integer a + sensible number is obtained. A use-case for this feature would be to + compare runs over the same physical time but with different numbers of + snapshots. Snapshots at a given time would always have the same set of + digits irrespective of the number of snapshots produced before. + diff --git a/doc/RTD/source/Snapshots/index.rst b/doc/RTD/source/Snapshots/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..30cdc0e1281ae0420b44d88001992ccbbe588136 --- /dev/null +++ b/doc/RTD/source/Snapshots/index.rst @@ -0,0 +1,199 @@ +.. Snapshots + Matthieu Schaller, 5th January 2019 + +.. _snapshots: + +Snapshots +========= + +The snapshots are stored using the HDF5 format and are almost compatible with +Gadget-2 (fully compatible outside of cosmological runs). They do, however, +contain a large set of extensions including units, meta-data about the code and +runs as well as facilities to quickly access the particles in a specific region +of the simulation volume. + +Header +------ + +Meta-data about the code and run +-------------------------------- + +Several groups at the root of the files only contain attributes and are used to +store some meta-data about the simulation and the code itself. + +Code +~~~~ + +The group ``/Code`` contains basic information about the version of the code +that was used to run the simulation that dumped this snapshot. Versions of the +libraries used to compile the code as well as information about the compiler and +the flags used are stored. The most important element here are the git SHA and +configuration parameters of the code. Alongside the compiler flags, policies and +used parameters, these allow to reproduce exactly an older run. + +Cosmology +~~~~~~~~~ + +The group ``/Cosmology`` contains information about the cosmological model used +for this simulation. The first important field is the attribute ``Cosmological +run`` which is set to ``1`` for cosmological runs and to ``0`` otherwise. This +allows users to quickly distinguish between these two main modes. Most values in +this section only make sense for cosmological runs. + +All quantities are expressed in the internal system of units (note that this may +differ from the units used in the particle arrays). Values like the look-back +time are given for the redshift (or scale-factor) of this snapshot. + +Policy +~~~~~~ + +The group ``/Policy`` list the engine policies (defined in ``src/engine.h``) +that were activated in the run that dumped this snapshot. The policies roughly +translate to the main run-time parameters of SWIFT. + +GravityScheme +~~~~~~~~~~~~~ + +HydroScheme +~~~~~~~~~~~ + +StarsScheme +~~~~~~~~~~~ + +SubgridScheme +~~~~~~~~~~~~~ + +Unit systems +------------ + +The snapshots contain *two* groups at the root containing information about the +unit systems used in the snapshots. + +The main one ``Units`` contains the units used in the snapshot. In a similar +fashion to what is done for the parameter files (see :ref:`Parameters_units`), +SWIFT specifies only the basic units. These are the units of mass (``U_M``), +length (``U_L``), time (``U_t``), electric current (``U_I``) and temperature +(``U_T``). These are specified in units of their CGS equivalents (gram, +centimeter, second, Ampere, Kelvin). All the quantities present in the particle +arrays are expressed in this system of units. For each quantity, SWIFT gives the +conversion factor in terms of these units. For instance, the internal energy per +unit mass would be expressed as ``U_L^2 U_t^-2``, which in the CGS unit system +translates to :math:`cm/s^2 = erg/g`. + +The second group ``InternalCodeUnits`` contains the unit system that was used +internally by the code when running the simulation. This is in most cases the +same system as given in ``Units`` but since users can specify a different +system for the snapshots, there might be cases where they differ. As this system +only relates to what was used inside the code and not in the snapshots +themselves, this group is mostly here to report on the code's run-time behaviour +and is used to express all the quantities in the meta-data (e.g. in the +cosmology group or the softening lengths in the gravity group). + +Used and unused run-time parameters +----------------------------------- + +The groups ``/Parameters`` and ``UnusedParameters`` located at the root of the file +contain the list of all the run-time parameters used by the run with their +values and the list of parameters that were in the YAML but were not read. The +content of these two groups is identical to the ``used_parameters.yml`` and +``unused_parameters.yml`` files produced by SWIFT when starting a run (See +the :ref:`Parameters_basics` section of the documentation). + +Structure of the particle arrays +-------------------------------- + +There are several groups that contain 'auxiliary' information, such as +``Header``. Particle data is placed in separate groups depending of the type of +the particles. The type use the naming convention of Gadget-2 (with +the OWLS and EAGLE extensions). + ++---------------------+------------------------+----------------------------+ +| HDF5 Group Name | Physical Particle Type | In code ``enum part_type`` | ++=====================+========================+============================+ +| ``/PartType0/`` | Gas | ``swift_type_gas`` | ++---------------------+------------------------+----------------------------+ +| ``/PartType1/`` | Dark Matter | ``swift_type_dark_matter`` | ++---------------------+------------------------+----------------------------+ +| ``/PartType4/`` | Stars | ``swift_type_star`` | ++---------------------+------------------------+----------------------------+ +| ``/PartType5/`` | Black Holes | ``swift_type_black_hole`` | ++---------------------+------------------------+----------------------------+ + +The last column in the table gives the ``enum`` value from ``part_type.h`` +corresponding to a given entry in the files. + +Quick access to particles via hash-tables +----------------------------------------- + +The particles are not sorted in a specific order when they are written to the +snapshots. However, the particles are sorted into the top-level cell structure +used internally by the code every time a tree rebuild is triggered. The +top-level cells are a coarse-grained mesh but knowing which particle belongs to +which cell can nevertheless be useful to rapidly access particles in a given +region only. + +One important caveat is that particles are free to drift out of their cells +between rebuilds of the tree (but not by more than one cell-length). If one +wants to have all the particles in a given cell, one has to read all the +neighbouring cells as well. We note that for image making purposes, for instance +to generate a slice, this is typically not necessary and reading just the cells +of interest is sufficient. + +At the root of the HDF5 file, the ``Cells`` group contains all the relevant +information. The dimension of the top-level grid (a triplet of integers) is +given by the attribute ``Cells/Meta-data/dimension`` and the size of each cell (a +triplet of floating-point numbers) is given by the attribute +``Cells/Meta-data/size``. All the cells have the same size but for non-cubic +simulation volumes the cells themselves can have different sizes along each +axis. + +The ``/Cells/Centres`` array gives the centre of each of the top-level cells in the +simulation volume. Both the cell sizes and positions of the centres are +expressed in the unit system used for the snapshots (see above) and are hence +consistent with the particle positions themselves. + +Once the cell(s) containing the region of interest has been located, users can +use the ``/Cells/Offsets/PartTypeN/Counts`` and +``/Cells/Offsets/PartTypeN/Offsets`` to retrieve the location of the particles +of type ``N`` in the ``/PartTypeN`` arrays. For instance, if one is interested +in retriving all the densities of the gas particles in the cell around the +position `[1, 1, 1]` one could use a piece of code similar to: + +.. code-block:: python + :linenos: + + import numpy as np + import h5py + + snapshot_file = h5py.File("snapshot.hdf5", "r") + + my_pos = [1, 1, 1] + + # Read in the cell centres and size + nr_cells = f["/Cells/Meta-data"].attrs["nr_cells"] + centres = f["/Cells/Centres"][:,:] + size = f["/Cells/Meta-data"].attrs["size"] + half_size = size / 2. + + # Look for the cell containing the position of interest + my_cell = -1 + for i in range(nr_cells): + if my_pos[0] > centres[i, 0] - half_size[0] and my_pos[0] < centres[i, 0] + half_size[0] and + my_pos[1] > centres[i, 1] - half_size[1] and my_pos[1] < centres[i, 1] + half_size[1] and + my_pos[2] > centres[i, 2] - half_size[2] and my_pos[2] < centres[i, 2] + half_size[2]: + my_cell = i + break + + # Print the position of the centre of the cell of interest + centre = snapshot_file["/Cells/Centres"][my_cell, :] + print("Centre of the cell:", centre) + + # Retrieve the offset and counts + my_offset = snapshot_file["/Cells/Offsets/PartType0"][my_cell] + my_count = snapshot_file["/Cells/Counts/PartType0"][my_cell] + + # Get the densities of the particles in this cell + rho = snapshot_file["/PartType0/Density"][my_offset:my_offset + my_count] + +For large simulations, this vastly reduces the amount of data that needs to be read +from the disk. diff --git a/doc/RTD/source/SubgridModels/Basic/index.rst b/doc/RTD/source/SubgridModels/Basic/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..031a1ed61bb1cae4f916b58b926f5269b10c7057 --- /dev/null +++ b/doc/RTD/source/SubgridModels/Basic/index.rst @@ -0,0 +1,51 @@ +.. Basic sub-grid model + Matthieu Schaller, 20th December 2018 + + +Basic model +=========== + + +Cooling: Analytic models +~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, we have 3 different simple cooling models (const-lambda, const-du +and Compton). These are all based on analytic formulas and can be used +to quickly understand how the cooling interacts with the rest of the +code before moving to more complex models. + +Equations +--------- + +The first table compares the different analytical cooling while the next ones +are specific to a given cooling. The quantities are the internal energy (\\( u +\\)), the density \\( rho \\), the element mass fraction (\\( X_i \\)), the +cooling function (\\(\\Lambda\\), the proton mass (\\( m_H \\)) and the time +step condition (\\( t\_\\text{step}\\)). If not specified otherwise, all +cooling contains a temperature floor avoiding negative temperature. + +.. csv-table:: Analytical Cooling + :header: "Variable", "Const-Lambda", "Const-du" + + "\\( \\frac{ \\mathrm{d}u }{ \\mathrm{d}t } \\)", "\\( -\\Lambda \\frac{\\rho^2 X_H^2}{\\rho m_H^2} \\)", "const" + "\\( \\Delta t\_\\text{max} \\)", "\\( t\_\\text{step} \\frac{u}{\\left|\\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)", "\\( t\_\\text{step} \\frac{u}{\\ \\left| \\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)" + +TODO: Add description of the parameters and units. + +TODO: Add Compton cooling model + +How to Implement a New Cooling +------------------------------ + +The developer should provide at least one function for: + * writing the cooling name in HDF5 + * cooling a particle + * the maximal time step possible + * initializing a particle + * computing the total energy radiated by a particle + * initializing the cooling parameters + * printing the cooling type + +For implementation details, see ``src/cooling/none/cooling.h`` + +See :ref:`new_option` for the full list of changes required. diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg new file mode 100644 index 0000000000000000000000000000000000000000..383b074fc947ed072dc50015152ba694b929c611 --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg @@ -0,0 +1,2102 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Created with matplotlib (http://matplotlib.org/) --> +<svg height="226pt" version="1.1" viewBox="0 0 226 226" width="226pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <style type="text/css"> +*{stroke-linecap:butt;stroke-linejoin:round;} + </style> + </defs> + <g id="figure_1"> + <g id="patch_1"> + <path d="M 0 226.8 +L 226.8 226.8 +L 226.8 0 +L 0 0 +z +" style="fill:#ffffff;"/> + </g> + <g id="axes_1"> + <g id="patch_2"> + <path d="M 34.02 197.316 +L 224.532 197.316 +L 224.532 2.268 +L 34.02 2.268 +z +" style="fill:#ffffff;"/> + </g> + <g id="PolyCollection_1"> + <defs> + <path d="M 77.714425 -156.365649 +L 77.714425 -14.805175 +L 250.907152 -14.805175 +L 250.907152 -156.365649 +L 250.907152 -156.365649 +L 77.714425 -156.365649 +z +" id="m8f9d87720a" style="stroke:#e6e6e6;"/> + </defs> + <g clip-path="url(#p107a2e5a22)"> + <use style="fill:#e6e6e6;stroke:#e6e6e6;" x="0" xlink:href="#m8f9d87720a" y="226.8"/> + </g> + </g> + <g id="PolyCollection_2"> + <path clip-path="url(#p107a2e5a22)" d="M 146.991516 49022.756825 +L 146.991516 85.113175 +L 250.907152 -12.410825 +L 250.907152 49022.756825 +L 250.907152 49022.756825 +L 146.991516 49022.756825 +z +" style="fill:#e6e6e6;stroke:#e6e6e6;"/> + </g> + <g id="PathCollection_1"> + <defs> + <path d="M 0 1 +C 0.265203 1 0.51958 0.894634 0.707107 0.707107 +C 0.894634 0.51958 1 0.265203 1 0 +C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 +C 0.51958 -0.894634 0.265203 -1 0 -1 +C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 +C -0.894634 -0.51958 -1 -0.265203 -1 0 +C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 +C -0.51958 0.894634 -0.265203 1 0 1 +z +" id="me37d9803e9" style="stroke:#000000;"/> + </defs> + <g clip-path="url(#p107a2e5a22)"> + <use style="stroke:#000000;" x="77.714425" xlink:href="#me37d9803e9" y="70.434351"/> + </g> + </g> + <g id="PathCollection_2"> + <g clip-path="url(#p107a2e5a22)"> + <use style="stroke:#000000;" x="146.991516" xlink:href="#me37d9803e9" y="85.113175"/> + </g> + </g> + <g id="matplotlib.axis_1"> + <g id="xtick_1"> + <g id="line2d_1"> + <defs> + <path d="M 0 0 +L 0 3.5 +" id="mddab006ef2" style="stroke:#000000;stroke-width:0.8;"/> + </defs> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#mddab006ef2" y="197.316"/> + </g> + </g> + <g id="text_1"> + <!-- $10^{-6}$ --> + <defs> + <path d="M 29.09375 67.796875 +L 11.09375 58.703125 +L 11.09375 57.296875 +C 12.296875 57.796875 13.40625 58.203125 13.796875 58.40625 +C 15.59375 59.109375 17.296875 59.5 18.296875 59.5 +C 20.40625 59.5 21.296875 57.984375 21.296875 54.765625 +L 21.296875 9.25 +C 21.296875 5.921875 20.5 3.625 18.90625 2.703125 +C 17.40625 1.8125 16 1.5 11.796875 1.5 +L 11.796875 0 +L 39.40625 0 +L 39.40625 1.5 +C 31.5 1.5 29.90625 2.5 29.90625 7.34375 +L 29.90625 67.609375 +z +" id="Nimbus_Roman_No9_L_Regular-49"/> + <path d="M 25.40625 68 +C 19.90625 68 15.703125 66.3125 12 62.8125 +C 6.203125 57.203125 2.40625 45.703125 2.40625 34 +C 2.40625 23.109375 5.703125 11.40625 10.40625 5.8125 +C 14.09375 1.40625 19.203125 -1 25 -1 +C 30.09375 -1 34.40625 0.703125 38 4.203125 +C 43.796875 9.703125 47.59375 21.3125 47.59375 33.40625 +C 47.59375 53.90625 38.5 68 25.40625 68 +z +M 25.09375 65.40625 +C 33.5 65.40625 38 54.109375 38 33.203125 +C 38 12.3125 33.59375 1.609375 25 1.609375 +C 16.40625 1.609375 12 12.3125 12 33.109375 +C 12 54.3125 16.5 65.40625 25.09375 65.40625 +z +" id="Nimbus_Roman_No9_L_Regular-48"/> + <path d="M 65.90625 23 +C 67.59375 23 69.40625 23 69.40625 25 +C 69.40625 27 67.59375 27 65.90625 27 +L 11.796875 27 +C 10.09375 27 8.296875 27 8.296875 25 +C 8.296875 23 10.09375 23 11.796875 23 +z +" id="CMSY10-0"/> + <path d="M 44.59375 68.609375 +C 33.203125 67.609375 27.40625 65.703125 20.09375 60.609375 +C 9.296875 52.90625 3.40625 41.5 3.40625 28.15625 +C 3.40625 19.5 6.09375 10.75 10.40625 5.78125 +C 14.203125 1.390625 19.59375 -1 25.796875 -1 +C 38.203125 -1 46.796875 8.453125 46.796875 22.203125 +C 46.796875 34.9375 39.5 43 28 43 +C 23.59375 43 21.5 42.296875 15.203125 38.5 +C 17.90625 53.609375 29.09375 64.40625 44.796875 67 +z +M 24.203125 38.40625 +C 32.796875 38.40625 37.796875 31.25 37.796875 18.8125 +C 37.796875 7.875 33.90625 1.8125 26.90625 1.8125 +C 18.09375 1.8125 12.703125 11.15625 12.703125 26.5625 +C 12.703125 31.640625 13.5 34.421875 15.5 35.921875 +C 17.59375 37.5 20.703125 38.40625 24.203125 38.40625 +z +" id="Nimbus_Roman_No9_L_Regular-54"/> + </defs> + <g transform="translate(50.706631 211.234498)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> + <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-54"/> + </g> + </g> + </g> + <g id="xtick_2"> + <g id="line2d_2"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#mddab006ef2" y="197.316"/> + </g> + </g> + <g id="text_2"> + <!-- $10^{-4}$ --> + <defs> + <path d="M 47.203125 23.390625 +L 37 23.390625 +L 37 68 +L 32.59375 68 +L 1.203125 23.390625 +L 1.203125 17 +L 29.296875 17 +L 29.296875 0.5 +L 37 0.5 +L 37 17 +L 47.203125 17 +z +M 29.203125 23.390625 +L 5.203125 23.390625 +L 29.203125 57.78125 +z +" id="Nimbus_Roman_No9_L_Regular-52"/> + </defs> + <g transform="translate(85.345176 211.234498)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> + <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-52"/> + </g> + </g> + </g> + <g id="xtick_3"> + <g id="line2d_3"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#mddab006ef2" y="197.316"/> + </g> + </g> + <g id="text_3"> + <!-- $10^{-2}$ --> + <defs> + <path d="M 47.5 13.671875 +L 46.203125 14.171875 +C 42.5 8.5 41.203125 7.59375 36.703125 7.59375 +L 12.796875 7.59375 +L 29.59375 25.140625 +C 38.5 34.40625 42.40625 41.984375 42.40625 49.765625 +C 42.40625 59.734375 34.296875 67.390625 23.90625 67.390625 +C 18.40625 67.390625 13.203125 65.203125 9.5 61.21875 +C 6.296875 57.8125 4.796875 54.625 3.09375 47.546875 +L 5.203125 47.046875 +C 9.203125 56.8125 12.796875 60 19.703125 60 +C 28.09375 60 33.796875 54.3125 33.796875 45.9375 +C 33.796875 38.171875 29.203125 28.90625 20.796875 20.03125 +L 3 1.203125 +L 3 0 +L 42 0 +z +" id="Nimbus_Roman_No9_L_Regular-50"/> + </defs> + <g transform="translate(119.983722 211.234498)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> + <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + </g> + </g> + </g> + <g id="xtick_4"> + <g id="line2d_4"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#mddab006ef2" y="197.316"/> + </g> + </g> + <g id="text_4"> + <!-- $10^{0}$ --> + <g transform="translate(157.486382 211.234498)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + </g> + </g> + </g> + <g id="xtick_5"> + <g id="line2d_5"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#mddab006ef2" y="197.316"/> + </g> + </g> + <g id="text_5"> + <!-- $10^{2}$ --> + <g transform="translate(192.124927 211.234498)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + </g> + </g> + </g> + <g id="text_6"> + <!-- ${\rm Density}~n_{\rm H}~[{\rm cm^{-3}}]$ --> + <defs> + <path d="M 10.40625 10.609375 +C 10.40625 3.390625 9.203125 2.09375 1.59375 2.09375 +L 1.59375 0 +L 30 0 +C 41.59375 0 52.09375 3.28125 58.296875 8.875 +C 64.796875 14.75 68.5 23.625 68.5 33.296875 +C 68.5 42.28125 65.5 50.046875 60 55.53125 +C 53.203125 62.40625 42.296875 66 28.59375 66 +L 1.59375 66 +L 1.59375 64.09375 +C 9.5 63.390625 10.40625 62.5 10.40625 55.078125 +z +M 20.59375 58.40625 +C 20.59375 61.5 21.703125 62.296875 25.796875 62.296875 +C 34.40625 62.296875 40.90625 60.703125 45.703125 57.3125 +C 53.5 51.921875 57.59375 43.359375 57.59375 32.609375 +C 57.59375 20.84375 53.5 12.46875 45.40625 7.78125 +C 40.296875 4.90625 34.5 3.703125 25.796875 3.703125 +C 21.796875 3.703125 20.59375 4.59375 20.59375 7.78125 +z +" id="Nimbus_Roman_No9_L_Regular-68"/> + <path d="M 40.796875 16.453125 +C 36 8.8125 31.703125 5.90625 25.296875 5.90625 +C 19.59375 5.90625 15.296875 8.8125 12.40625 14.53125 +C 10.59375 18.34375 9.90625 21.65625 9.703125 27.796875 +L 40.5 27.796875 +C 39.703125 34.265625 38.703125 37.140625 36.203125 40.328125 +C 33.203125 43.921875 28.59375 46 23.40625 46 +C 18.40625 46 13.703125 44.203125 9.90625 40.796875 +C 5.203125 36.703125 2.5 29.59375 2.5 21.40625 +C 2.5 7.59375 9.703125 -1 21.203125 -1 +C 30.703125 -1 38.203125 4.921875 42.40625 15.75 +z +M 9.90625 31 +C 11 38.734375 14.40625 42.40625 20.5 42.40625 +C 26.59375 42.40625 29 39.609375 30.296875 31 +z +" id="Nimbus_Roman_No9_L_Regular-101"/> + <path d="M 1.59375 40.296875 +C 2.203125 40.59375 3.203125 40.703125 4.296875 40.703125 +C 7.09375 40.703125 8 39.203125 8 34.296875 +L 8 9.5 +C 8 3.796875 6.90625 2.40625 1.796875 2 +L 1.796875 0.5 +L 23 0.5 +L 23 2 +C 17.90625 2.40625 16.40625 3.59375 16.40625 7.203125 +L 16.40625 35.296875 +C 21.203125 39.796875 23.40625 41 26.703125 41 +C 31.59375 41 34 37.90625 34 31.296875 +L 34 10.40625 +C 34 4.09375 32.703125 2.40625 27.703125 2 +L 27.703125 0.5 +L 48.5 0.5 +L 48.5 2 +C 43.59375 2.5 42.40625 3.703125 42.40625 8.59375 +L 42.40625 31.5 +C 42.40625 40.90625 38 46.5 30.59375 46.5 +C 26 46.5 22.90625 44.796875 16.09375 38.40625 +L 16.09375 46.296875 +L 15.40625 46.5 +C 10.5 44.703125 7.09375 43.59375 1.59375 42 +z +" id="Nimbus_Roman_No9_L_Regular-110"/> + <path d="M 31.5 31.40625 +L 31.09375 45 +L 30 45 +L 29.796875 44.796875 +C 28.90625 44.09375 28.796875 44 28.40625 44 +C 27.796875 44 26.796875 44.203125 25.703125 44.703125 +C 23.5 45.546875 21.296875 46 18.703125 46 +C 10.796875 46 5.09375 40.875 5.09375 33.65625 +C 5.09375 28.0625 8.296875 24.046875 16.796875 19.234375 +L 22.59375 15.9375 +C 26.09375 13.9375 27.796875 11.515625 27.796875 8.421875 +C 27.796875 4 24.59375 1.203125 19.5 1.203125 +C 16.09375 1.203125 13 2.484375 11.09375 4.65625 +C 9 7.109375 8.09375 9.375 6.796875 15 +L 5.203125 15 +L 5.203125 -0.609375 +L 6.5 -0.609375 +C 7.203125 0.515625 7.59375 0.75 8.796875 0.75 +C 9.703125 0.75 11.09375 0.546875 13.40625 -0.03125 +C 16.203125 -0.609375 18.90625 -1 20.703125 -1 +C 28.40625 -1 34.796875 4.8125 34.796875 11.828125 +C 34.796875 16.828125 32.40625 20.140625 26.40625 23.75 +L 15.59375 30.15625 +C 12.796875 31.765625 11.296875 34.28125 11.296875 36.984375 +C 11.296875 41 14.40625 43.796875 19 43.796875 +C 24.703125 43.796875 27.703125 40.359375 30 31.40625 +z +" id="Nimbus_Roman_No9_L_Regular-115"/> + <path d="M 17.5 45.765625 +L 2 40.296875 +L 2 38.8125 +L 2.796875 38.90625 +C 4 39.09375 5.296875 39.203125 6.203125 39.203125 +C 8.59375 39.203125 9.5 37.59375 9.5 33.203125 +L 9.5 10 +C 9.5 2.796875 8.5 1.703125 1.59375 1.703125 +L 1.59375 0 +L 25.296875 0 +L 25.296875 1.5 +C 18.703125 2 17.90625 2.984375 17.90625 10.15625 +L 17.90625 45.46875 +z +M 12.796875 68 +C 10.09375 68 7.796875 65.703125 7.796875 62.90625 +C 7.796875 60.109375 10 57.796875 12.796875 57.796875 +C 15.703125 57.796875 18 60 18 62.90625 +C 18 65.703125 15.703125 68 12.796875 68 +z +" id="Nimbus_Roman_No9_L_Regular-105"/> + <path d="M 25.5 45 +L 15.40625 45 +L 15.40625 56.59375 +C 15.40625 57.59375 15.296875 57.90625 14.703125 57.90625 +C 14 57 13.40625 56.09375 12.703125 55.09375 +C 8.90625 49.59375 4.59375 44.796875 3 44.390625 +C 1.90625 43.65625 1.296875 42.9375 1.296875 42.421875 +C 1.296875 42.109375 1.40625 41.90625 1.703125 41.90625 +L 7 41.90625 +L 7 11.734375 +C 7 3.3125 10 -1 15.90625 -1 +C 20.796875 -1 24.59375 1.40625 27.90625 6.609375 +L 26.59375 7.703125 +C 24.5 5.203125 22.796875 4.203125 20.59375 4.203125 +C 16.90625 4.203125 15.40625 6.921875 15.40625 13.234375 +L 15.40625 41.90625 +L 25.5 41.90625 +z +" id="Nimbus_Roman_No9_L_Regular-116"/> + <path d="M 47.5 45 +L 34 45 +L 34 43.5 +C 37.203125 43.5 38.796875 42.59375 38.796875 40.984375 +C 38.796875 40.578125 38.703125 39.984375 38.40625 39.28125 +L 28.703125 11.59375 +L 17.203125 37.140625 +C 16.59375 38.5625 16.203125 39.875 16.203125 40.96875 +C 16.203125 42.796875 17.703125 43.5 22 43.5 +L 22 45 +L 1.40625 45 +L 1.40625 43.59375 +C 4 43.203125 5.703125 42.09375 6.5 40.390625 +L 17.90625 15.703125 +L 18.203125 14.890625 +L 19.703125 11.890625 +C 22.5 6.875 24.09375 3.265625 24.09375 1.75 +C 24.09375 0.25 21.796875 -6.078125 20.09375 -9.09375 +C 18.703125 -11.703125 16.5 -13.609375 15.09375 -13.609375 +C 14.5 -13.609375 13.59375 -13.40625 12.59375 -12.90625 +C 10.703125 -12.203125 9 -11.796875 7.296875 -11.796875 +C 5 -11.796875 3 -13.796875 3 -16.203125 +C 3 -19.5 6.203125 -22 10.40625 -22 +C 17.09375 -22 21.90625 -16.390625 27.296875 -1.9375 +L 42.703125 38.984375 +C 44 42.203125 45.09375 43.203125 47.5 43.5 +z +" id="Nimbus_Roman_No9_L_Regular-121"/> + <path d="M 46 11.546875 +L 43.90625 8.9375 +C 41 5.234375 39.203125 3.625 37.796875 3.625 +C 37 3.625 36.203125 4.421875 36.203125 5.21875 +C 36.203125 5.9375 36.203125 5.9375 37.59375 11.53125 +L 43.296875 32.1875 +C 43.796875 34.28125 44.203125 36.484375 44.203125 37.890625 +C 44.203125 41.5 41.5 44 37.59375 44 +C 31.203125 44 24.90625 38 14.59375 21.96875 +L 21.296875 43.796875 +L 21 43.984375 +C 15.59375 42.890625 13.5 42.5 4.796875 40.90625 +L 4.796875 39.21875 +C 9.90625 39.21875 11.203125 38.625 11.203125 36.609375 +C 11.203125 36 11.09375 35.40625 11 34.90625 +L 1.40625 -0.1875 +L 8.90625 -0.1875 +C 13.59375 15.65625 14.5 17.875 18.90625 24.6875 +C 24.90625 33.890625 30 38.921875 33.703125 38.921875 +C 35.203125 38.921875 36.09375 37.8125 36.09375 36 +C 36.09375 34.8125 35.5 31.5 34.703125 28.390625 +L 30.296875 11.84375 +C 29 6.734375 28.703125 5.328125 28.703125 4.328125 +C 28.703125 0.515625 30.09375 -1.078125 33.40625 -1.078125 +C 37.90625 -1.078125 40.5 1.03125 47.40625 10.25 +z +" id="Nimbus_Roman_No9_L_Regular_Italic-110"/> + <path d="M 20.90625 36 +L 20.90625 55.203125 +C 20.90625 62.34375 22 63.421875 29.703125 64.125 +L 29.703125 66 +L 1.90625 66 +L 1.90625 64.125 +C 9.59375 63.421875 10.703125 62.328125 10.703125 55.109375 +L 10.703125 11.703125 +C 10.703125 3.28125 9.703125 2.09375 1.90625 2.09375 +L 1.90625 0 +L 29.703125 0 +L 29.703125 1.890625 +C 22.203125 2.484375 20.90625 3.78125 20.90625 10.921875 +L 20.90625 31.59375 +L 51.203125 31.59375 +L 51.203125 11.828125 +C 51.203125 3.296875 50.203125 2.09375 42.40625 2.09375 +L 42.40625 0 +L 70.203125 0 +L 70.203125 1.890625 +C 62.703125 2.484375 61.40625 3.78125 61.40625 10.875 +L 61.40625 55.140625 +C 61.40625 62.328125 62.5 63.421875 70.203125 64.125 +L 70.203125 66 +L 42.40625 66 +L 42.40625 64.125 +C 50.09375 63.421875 51.203125 62.34375 51.203125 55.203125 +L 51.203125 36 +z +" id="Nimbus_Roman_No9_L_Regular-72"/> + <path d="M 25.5 -25 +L 25.5 -20.984375 +L 15.796875 -20.984375 +L 15.796875 71 +L 25.5 71 +L 25.5 75.015625 +L 11.796875 75.015625 +L 11.796875 -25 +z +" id="CMR10-91"/> + <path d="M 39.796875 15.59375 +C 35 8.59375 31.40625 6.203125 25.703125 6.203125 +C 16.59375 6.203125 10.203125 14.203125 10.203125 25.703125 +C 10.203125 36 15.703125 43.09375 23.796875 43.09375 +C 27.40625 43.09375 28.703125 42 29.703125 38.296875 +L 30.296875 36.09375 +C 31.09375 33.296875 32.90625 31.5 35 31.5 +C 37.59375 31.5 39.796875 33.40625 39.796875 35.703125 +C 39.796875 41.296875 32.796875 46 24.40625 46 +C 19.5 46 14.40625 44 10.296875 40.40625 +C 5.296875 36 2.5 29.203125 2.5 21.296875 +C 2.5 8.296875 10.40625 -1 21.5 -1 +C 26 -1 30 0.59375 33.59375 3.703125 +C 36.296875 6.09375 38.203125 8.796875 41.203125 14.703125 +z +" id="Nimbus_Roman_No9_L_Regular-99"/> + <path d="M 1.90625 39.796875 +C 3.203125 40.09375 4 40.203125 5.09375 40.203125 +C 7.703125 40.203125 8.59375 38.59375 8.59375 33.78125 +L 8.59375 8.421875 +C 8.59375 3 7.203125 1.5 1.59375 1.5 +L 1.59375 0 +L 23.796875 0 +L 23.796875 1.5 +C 18.5 1.5 17 2.59375 17 6.53125 +L 17 34.90625 +C 17 35.09375 17.796875 36.09375 18.5 36.796875 +C 21 39.09375 25.296875 40.796875 28.796875 40.796875 +C 33.203125 40.796875 35.40625 37.265625 35.40625 30.1875 +L 35.40625 8.25 +C 35.40625 2.609375 34.296875 1.5 28.59375 1.5 +L 28.59375 0 +L 51 0 +L 51 1.5 +C 45.296875 1.5 43.796875 3.203125 43.796875 9.421875 +L 43.796875 34.703125 +C 46.796875 39 50.09375 40.796875 54.703125 40.796875 +C 60.40625 40.796875 62.203125 38.09375 62.203125 29.796875 +L 62.203125 8.703125 +C 62.203125 3 61.40625 2.203125 55.59375 1.5 +L 55.59375 0 +L 77.5 0 +L 77.5 1.5 +L 74.90625 1.796875 +C 71.90625 1.796875 70.59375 3.59375 70.59375 7.5 +L 70.59375 28.15625 +C 70.59375 39.984375 66.703125 46 59 46 +C 53.203125 46 48.09375 43.40625 42.703125 37.609375 +C 40.90625 43.296875 37.5 46 32.09375 46 +C 27.703125 46 24.90625 44.59375 16.59375 38.296875 +L 16.59375 45.796875 +L 15.90625 46 +C 10.796875 44.09375 7.40625 43 1.90625 41.5 +z +" id="Nimbus_Roman_No9_L_Regular-109"/> + <path d="M 15.296875 33.40625 +C 21.203125 33.40625 23.5 33.203125 25.90625 32.3125 +C 32.09375 30.109375 36 24.40625 36 17.5 +C 36 9.109375 30.296875 2.609375 22.90625 2.609375 +C 20.203125 2.609375 18.203125 3.3125 14.5 5.703125 +C 11.5 7.5 9.796875 8.203125 8.09375 8.203125 +C 5.796875 8.203125 4.296875 6.8125 4.296875 4.703125 +C 4.296875 1.203125 8.59375 -1 15.59375 -1 +C 23.296875 -1 31.203125 1.609375 35.90625 5.703125 +C 40.59375 9.8125 43.203125 15.609375 43.203125 22.3125 +C 43.203125 27.40625 41.59375 32.109375 38.703125 35.203125 +C 36.703125 37.40625 34.796875 38.609375 30.40625 40.5 +C 37.296875 45.203125 39.796875 48.90625 39.796875 54.3125 +C 39.796875 62.40625 33.40625 68 24.203125 68 +C 19.203125 68 14.796875 66.3125 11.203125 63.109375 +C 8.203125 60.40625 6.703125 57.796875 4.5 51.796875 +L 6 51.40625 +C 10.09375 58.703125 14.59375 62 20.90625 62 +C 27.40625 62 31.90625 57.609375 31.90625 51.3125 +C 31.90625 47.703125 30.40625 44.109375 27.90625 41.609375 +C 24.90625 38.609375 22.09375 37.109375 15.296875 34.703125 +z +" id="Nimbus_Roman_No9_L_Regular-51"/> + <path d="M 15.90625 75.015625 +L 2.203125 75.015625 +L 2.203125 71 +L 11.90625 71 +L 11.90625 -20.984375 +L 2.203125 -20.984375 +L 2.203125 -25 +L 15.90625 -25 +z +" id="CMR10-93"/> + </defs> + <g transform="translate(91.250474 222.339856)scale(0.1 -0.1)"> + <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-68"/> + <use transform="translate(71.929985 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> + <use transform="translate(116.163554 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-110"/> + <use transform="translate(165.976754 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-115"/> + <use transform="translate(204.73046 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-105"/> + <use transform="translate(232.42563 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-116"/> + <use transform="translate(260.120799 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-121"/> + <use transform="translate(341.163113 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> + <use transform="translate(390.976313 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> + <use transform="translate(475.932143 15.050129)scale(0.996264)" xlink:href="#CMR10-91"/> + <use transform="translate(503.52765 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-99"/> + <use transform="translate(547.761218 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-109"/> + <use transform="translate(625.269588 51.213361)scale(0.737241)" xlink:href="#CMSY10-0"/> + <use transform="translate(682.552458 51.213361)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-51"/> + <use transform="translate(719.912632 15.050129)scale(0.996264)" xlink:href="#CMR10-93"/> + </g> + </g> + </g> + <g id="matplotlib.axis_2"> + <g id="ytick_1"> + <g id="line2d_6"> + <defs> + <path d="M 0 0 +L -3.5 0 +" id="mec8c027021" style="stroke:#000000;stroke-width:0.8;"/> + </defs> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mec8c027021" y="163.232825"/> + </g> + </g> + <g id="text_7"> + <!-- $10^{2}$ --> + <g transform="translate(13.371186 166.692074)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + </g> + </g> + </g> + <g id="ytick_2"> + <g id="line2d_7"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mec8c027021" y="114.470825"/> + </g> + </g> + <g id="text_8"> + <!-- $10^{3}$ --> + <g transform="translate(13.371186 117.930074)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-51"/> + </g> + </g> + </g> + <g id="ytick_3"> + <g id="line2d_8"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mec8c027021" y="65.708825"/> + </g> + </g> + <g id="text_9"> + <!-- $10^{4}$ --> + <g transform="translate(13.371186 69.168074)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-52"/> + </g> + </g> + </g> + <g id="ytick_4"> + <g id="line2d_9"> + <g> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mec8c027021" y="16.946825"/> + </g> + </g> + <g id="text_10"> + <!-- $10^{5}$ --> + <defs> + <path d="M 18.09375 59 +L 37.703125 59 +C 39.296875 59 39.703125 59.203125 40 59.90625 +L 43.796875 68.796875 +L 42.90625 69.5 +C 41.40625 67.40625 40.40625 66.90625 38.296875 66.90625 +L 17.40625 66.90625 +L 6.5 43.109375 +C 6.40625 42.90625 6.40625 42.8125 6.40625 42.609375 +C 6.40625 42.109375 6.796875 41.8125 7.59375 41.8125 +C 10.796875 41.8125 14.796875 41.109375 18.90625 39.796875 +C 30.40625 36.078125 35.703125 29.84375 35.703125 19.90625 +C 35.703125 10.25 29.59375 2.703125 21.796875 2.703125 +C 19.796875 2.703125 18.09375 3.40625 15.09375 5.625 +C 11.90625 7.9375 9.59375 8.9375 7.5 8.9375 +C 4.59375 8.9375 3.203125 7.734375 3.203125 5.21875 +C 3.203125 1.40625 7.90625 -1 15.40625 -1 +C 23.796875 -1 31 1.71875 36 6.84375 +C 40.59375 11.375 42.703125 17.09375 42.703125 24.734375 +C 42.703125 31.96875 40.796875 36.59375 35.796875 41.609375 +C 31.40625 46.046875 25.703125 48.34375 13.90625 50.453125 +z +" id="Nimbus_Roman_No9_L_Regular-53"/> + </defs> + <g transform="translate(13.371186 20.406074)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> + <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-53"/> + </g> + </g> + </g> + <g id="ytick_5"> + <g id="line2d_10"> + <defs> + <path d="M 0 0 +L -2 0 +" id="mc0ad7d39b4" style="stroke:#000000;stroke-width:0.6;"/> + </defs> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="197.316"/> + </g> + </g> + </g> + <g id="ytick_6"> + <g id="line2d_11"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="188.729438"/> + </g> + </g> + </g> + <g id="ytick_7"> + <g id="line2d_12"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="182.637175"/> + </g> + </g> + </g> + <g id="ytick_8"> + <g id="line2d_13"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="177.911649"/> + </g> + </g> + </g> + <g id="ytick_9"> + <g id="line2d_14"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="174.050613"/> + </g> + </g> + </g> + <g id="ytick_10"> + <g id="line2d_15"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="170.786154"/> + </g> + </g> + </g> + <g id="ytick_11"> + <g id="line2d_16"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="167.958351"/> + </g> + </g> + </g> + <g id="ytick_12"> + <g id="line2d_17"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="165.464051"/> + </g> + </g> + </g> + <g id="ytick_13"> + <g id="line2d_18"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="148.554"/> + </g> + </g> + </g> + <g id="ytick_14"> + <g id="line2d_19"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="139.967438"/> + </g> + </g> + </g> + <g id="ytick_15"> + <g id="line2d_20"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="133.875175"/> + </g> + </g> + </g> + <g id="ytick_16"> + <g id="line2d_21"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="129.149649"/> + </g> + </g> + </g> + <g id="ytick_17"> + <g id="line2d_22"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="125.288613"/> + </g> + </g> + </g> + <g id="ytick_18"> + <g id="line2d_23"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="122.024154"/> + </g> + </g> + </g> + <g id="ytick_19"> + <g id="line2d_24"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="119.196351"/> + </g> + </g> + </g> + <g id="ytick_20"> + <g id="line2d_25"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="116.702051"/> + </g> + </g> + </g> + <g id="ytick_21"> + <g id="line2d_26"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="99.792"/> + </g> + </g> + </g> + <g id="ytick_22"> + <g id="line2d_27"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="91.205438"/> + </g> + </g> + </g> + <g id="ytick_23"> + <g id="line2d_28"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="85.113175"/> + </g> + </g> + </g> + <g id="ytick_24"> + <g id="line2d_29"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="80.387649"/> + </g> + </g> + </g> + <g id="ytick_25"> + <g id="line2d_30"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="76.526613"/> + </g> + </g> + </g> + <g id="ytick_26"> + <g id="line2d_31"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="73.262154"/> + </g> + </g> + </g> + <g id="ytick_27"> + <g id="line2d_32"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="70.434351"/> + </g> + </g> + </g> + <g id="ytick_28"> + <g id="line2d_33"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="67.940051"/> + </g> + </g> + </g> + <g id="ytick_29"> + <g id="line2d_34"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="51.03"/> + </g> + </g> + </g> + <g id="ytick_30"> + <g id="line2d_35"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="42.443438"/> + </g> + </g> + </g> + <g id="ytick_31"> + <g id="line2d_36"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="36.351175"/> + </g> + </g> + </g> + <g id="ytick_32"> + <g id="line2d_37"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="31.625649"/> + </g> + </g> + </g> + <g id="ytick_33"> + <g id="line2d_38"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="27.764613"/> + </g> + </g> + </g> + <g id="ytick_34"> + <g id="line2d_39"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="24.500154"/> + </g> + </g> + </g> + <g id="ytick_35"> + <g id="line2d_40"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="21.672351"/> + </g> + </g> + </g> + <g id="ytick_36"> + <g id="line2d_41"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="19.178051"/> + </g> + </g> + </g> + <g id="ytick_37"> + <g id="line2d_42"> + <g> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="2.268"/> + </g> + </g> + </g> + <g id="text_11"> + <!-- ${\rm Temperature}~T~[{\rm K}]$ --> + <defs> + <path d="M 25.40625 62 +L 25.40625 11.734375 +C 25.40625 3.1875 24.296875 2.09375 16 2.09375 +L 16 0 +L 45.203125 0 +L 45.203125 2.09375 +C 37 2.09375 35.59375 3.390625 35.59375 10.734375 +L 35.59375 62 +L 41 62 +C 52.40625 62 54.59375 60.203125 56.90625 49.203125 +L 59.296875 49.203125 +L 58.703125 66.203125 +L 2.296875 66.203125 +L 1.703125 49.203125 +L 4.09375 49.203125 +C 6.5 60.09375 8.796875 62 20 62 +z +" id="Nimbus_Roman_No9_L_Regular-84"/> + <path d="M 0.90625 39.3125 +C 1.796875 39.40625 2.5 39.40625 3.40625 39.40625 +C 6.796875 39.40625 7.5 38.40625 7.5 33.671875 +L 7.5 -13.359375 +C 7.5 -18.578125 6.40625 -19.703125 0.5 -20.296875 +L 0.5 -22 +L 24.703125 -22 +L 24.703125 -20.203125 +C 17.203125 -20.203125 15.90625 -19.09375 15.90625 -12.75 +L 15.90625 3.109375 +C 19.40625 -0.203125 21.796875 -1.203125 26 -1.203125 +C 37.796875 -1.203125 47 10.046875 47 24.609375 +C 47 37.0625 40 46 30.296875 46 +C 24.703125 46 20.296875 43.5 15.90625 38.09375 +L 15.90625 45.796875 +L 15.296875 46 +C 9.90625 43.90625 6.40625 42.59375 0.90625 40.90625 +z +M 15.90625 33.375 +C 15.90625 36.390625 21.5 40 26.09375 40 +C 33.5 40 38.40625 32.359375 38.40625 20.6875 +C 38.40625 9.546875 33.5 2 26.296875 2 +C 21.59375 2 15.90625 5.609375 15.90625 8.625 +z +" id="Nimbus_Roman_No9_L_Regular-112"/> + <path d="M 0.703125 39 +C 2.09375 39.296875 3 39.40625 4.203125 39.40625 +C 6.703125 39.40625 7.59375 37.796875 7.59375 33.40625 +L 7.59375 8.40625 +C 7.59375 3.40625 6.90625 2.703125 0.5 1.5 +L 0.5 0 +L 24.5 0 +L 24.5 1.59375 +C 17.703125 1.59375 16 3.109375 16 8.828125 +L 16 31.453125 +C 16 34.671875 20.296875 39.703125 23 39.703125 +C 23.59375 39.703125 24.5 39.203125 25.59375 38.203125 +C 27.203125 36.796875 28.296875 36.203125 29.59375 36.203125 +C 32 36.203125 33.5 37.90625 33.5 40.703125 +C 33.5 44 31.40625 46 28 46 +C 23.796875 46 20.90625 43.703125 16 36.609375 +L 16 45.796875 +L 15.5 46 +C 10.203125 43.796875 6.59375 42.515625 0.703125 40.609375 +z +" id="Nimbus_Roman_No9_L_Regular-114"/> + <path d="M 44.203125 6.609375 +C 42.5 5.203125 41.296875 4.703125 39.796875 4.703125 +C 37.5 4.703125 36.796875 6.09375 36.796875 10.5 +L 36.796875 30 +C 36.796875 35.203125 36.296875 38.09375 34.796875 40.5 +C 32.59375 44.09375 28.296875 46 22.40625 46 +C 13 46 5.59375 41.09375 5.59375 34.796875 +C 5.59375 32.5 7.59375 30.5 9.90625 30.5 +C 12.296875 30.5 14.40625 32.5 14.40625 34.703125 +C 14.40625 35.09375 14.296875 35.59375 14.203125 36.296875 +C 14 37.203125 13.90625 38 13.90625 38.703125 +C 13.90625 41.40625 17.09375 43.59375 21.09375 43.59375 +C 26 43.59375 28.703125 40.703125 28.703125 35.296875 +L 28.703125 29.203125 +C 13.296875 23 11.59375 22.203125 7.296875 18.40625 +C 5.09375 16.40625 3.703125 13 3.703125 9.703125 +C 3.703125 3.40625 8.09375 -1 14.203125 -1 +C 18.59375 -1 22.703125 1.09375 28.796875 6.296875 +C 29.296875 1.09375 31.09375 -1 35.203125 -1 +C 38.59375 -1 40.703125 0.203125 44.203125 4 +z +M 28.703125 12.234375 +C 28.703125 9.125 28.203125 8.21875 26.09375 7.015625 +C 23.703125 5.609375 20.90625 4.703125 18.796875 4.703125 +C 15.296875 4.703125 12.5 8.125 12.5 12.4375 +L 12.5 12.84375 +C 12.5 18.765625 16.59375 22.390625 28.703125 26.796875 +z +" id="Nimbus_Roman_No9_L_Regular-97"/> + <path d="M 47.90625 5 +L 47.40625 5 +C 42.796875 5 41.703125 6.09375 41.703125 10.703125 +L 41.703125 45 +L 25.90625 45 +L 25.90625 43.09375 +C 32.09375 43.09375 33.296875 42.09375 33.296875 37.109375 +L 33.296875 13.671875 +C 33.296875 10.890625 32.796875 9.484375 31.40625 8.390625 +C 28.703125 6.203125 25.59375 5 22.59375 5 +C 18.703125 5 15.5 8.390625 15.5 12.578125 +L 15.5 45 +L 0.90625 45 +L 0.90625 43.40625 +C 5.703125 43.40625 7.09375 41.90625 7.09375 37.296875 +L 7.09375 12.03125 +C 7.09375 4.109375 11.90625 -1 19.203125 -1 +C 22.90625 -1 26.796875 0.59375 29.5 3.296875 +L 33.796875 7.59375 +L 33.796875 -0.703125 +L 34.203125 -0.90625 +C 39.203125 1.09375 42.796875 2.203125 47.90625 3.59375 +z +" id="Nimbus_Roman_No9_L_Regular-117"/> + <path d="M 63.296875 65 +L 10.09375 65 +L 5.90625 49.671875 +L 7.703125 49.265625 +C 13 59.921875 16.203125 61.609375 31.5 61.609375 +L 17.09375 8.984375 +C 15.5 3.6875 13.09375 2.09375 6.5 1.59375 +L 6.5 0 +L 35.5 0 +L 35.5 1.59375 +C 33.796875 1.59375 32.296875 2 31.703125 2 +C 27.703125 2 26.5 2.890625 26.5 5.90625 +C 26.5 7.203125 26.796875 8.390625 27.703125 11.78125 +L 41.59375 61.609375 +L 47.09375 61.609375 +C 54.296875 61.609375 57.5 59.09375 57.5 53.46875 +C 57.5 52.171875 57.40625 50.6875 57.203125 48.96875 +L 58.90625 48.765625 +z +" id="Nimbus_Roman_No9_L_Regular_Italic-84"/> + <path d="M 41.296875 64.203125 +C 42.5 64 43.5 64 43.90625 64 +C 46.90625 64 48.09375 63.203125 48.09375 61.203125 +C 48.09375 58.984375 45.796875 55.984375 40.296875 50.890625 +L 22.59375 34.75 +L 22.59375 55.21875 +C 22.59375 62.5 23.703125 63.609375 31.796875 64.203125 +L 31.796875 66.109375 +L 3.40625 66.109375 +L 3.40625 64.203125 +C 11.203125 63.609375 12.40625 62.3125 12.40625 55.171875 +L 12.40625 11.71875 +C 12.40625 3.390625 11.296875 2.09375 3.40625 2.09375 +L 3.40625 0 +L 31.59375 0 +L 31.59375 2.09375 +C 23.796875 2.09375 22.59375 3.296875 22.59375 10.671875 +L 22.59375 29.546875 +L 25.203125 31.65625 +L 35.796875 21.171875 +C 43.40625 13.6875 48.796875 6.59375 48.796875 4.203125 +C 48.796875 2.796875 47.40625 2.09375 44.59375 2.09375 +C 44.09375 2.09375 43 2.09375 41.796875 1.890625 +L 41.796875 0 +L 72.296875 0 +L 72.296875 2.09375 +C 67.09375 2.09375 65.703125 3.09375 56.59375 12.75 +L 33.296875 37.640625 +L 52.296875 56.421875 +C 59.09375 62.90625 60.703125 63.703125 67.5 64.203125 +L 67.5 66.109375 +L 41.296875 66.109375 +z +" id="Nimbus_Roman_No9_L_Regular-75"/> + </defs> + <g transform="translate(8.880526 137.975632)rotate(-90)scale(0.1 -0.1)"> + <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-84"/> + <use transform="translate(53.898284 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> + <use transform="translate(98.131853 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-109"/> + <use transform="translate(175.640223 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-112"/> + <use transform="translate(225.453424 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> + <use transform="translate(269.686992 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-114"/> + <use transform="translate(302.862222 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-97"/> + <use transform="translate(347.095791 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-116"/> + <use transform="translate(374.79096 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-117"/> + <use transform="translate(424.604161 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-114"/> + <use transform="translate(457.779391 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> + <use transform="translate(533.242072 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-84"/> + <use transform="translate(632.514439 0)scale(0.996264)" xlink:href="#CMR10-91"/> + <use transform="translate(660.109945 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-75"/> + <use transform="translate(732.03993 0)scale(0.996264)" xlink:href="#CMR10-93"/> + </g> + </g> + </g> + <g id="line2d_43"> + <path clip-path="url(#p107a2e5a22)" d="M 77.714425 70.434351 +L 227.8 70.434351 +L 227.8 70.434351 +" style="fill:none;stroke:#000000;stroke-linecap:square;"/> + </g> + <g id="line2d_44"> + <path clip-path="url(#p107a2e5a22)" d="M 146.991516 85.113175 +L 227.8 9.275054 +L 227.8 9.275054 +" style="fill:none;stroke:#000000;stroke-linecap:square;"/> + </g> + <g id="line2d_45"> + <path clip-path="url(#p107a2e5a22)" d="M -1 70.434351 +L 77.714425 70.434351 +" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="line2d_46"> + <path clip-path="url(#p107a2e5a22)" d="M -1 85.113175 +L 146.991516 85.113175 +" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="line2d_47"> + <path clip-path="url(#p107a2e5a22)" d="M 77.714425 197.316 +L 77.714425 70.434351 +" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="line2d_48"> + <path clip-path="url(#p107a2e5a22)" d="M 146.991516 197.316 +L 146.991516 85.113175 +" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="line2d_49"> + <path clip-path="url(#p107a2e5a22)" d="M 68.658545 43.904505 +L 103.297091 43.904505 +" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="line2d_50"> + <path clip-path="url(#p107a2e5a22)" d="M 155.254909 57.122263 +L 189.893455 24.614263 +" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/> + </g> + <g id="patch_3"> + <path d="M 34.02 197.316 +L 34.02 2.268 +" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> + </g> + <g id="patch_4"> + <path d="M 224.532 197.316 +L 224.532 2.268 +" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> + </g> + <g id="patch_5"> + <path d="M 34.02 197.316 +L 224.532 197.316 +" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> + </g> + <g id="patch_6"> + <path d="M 34.02 2.268 +L 224.532 2.268 +" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> + </g> + <g id="text_12"> + <!-- $n_{\rm H}~\widehat{}~{\tt Cool\_gamma\_effective}$ --> + <defs> + <path d="M 27.703125 68.5 +L 54.90625 56.203125 +L 56.09375 58.40625 +L 27.796875 74.40625 +L -0.5 58.40625 +L 0.59375 56.203125 +z +" id="CMEX10-98"/> + <path d="M 47.40625 58.296875 +C 47.40625 59.890625 47.40625 62.09375 44.59375 62.09375 +C 42.796875 62.09375 42.296875 61 41.90625 60.296875 +C 41.59375 59.5 40.796875 57.703125 40.5 57 +C 36.40625 60.59375 31.796875 62.09375 27.59375 62.09375 +C 14.703125 62.09375 4 48.390625 4 30.59375 +C 4 12.40625 15 -1 27.59375 -1 +C 39.296875 -1 47.40625 8.21875 47.40625 16.96875 +C 47.40625 20.03125 45.296875 20.03125 44.09375 20.03125 +C 42.703125 20.03125 41.09375 19.53125 40.90625 17.859375 +C 40.09375 5.109375 30 5.109375 28.296875 5.109375 +C 19 5.109375 10.5 15.859375 10.5 30.75 +C 10.5 45.65625 19.09375 56.296875 28.203125 56.296875 +C 33.59375 56.296875 39.5 52 40.90625 42.796875 +C 41.203125 40.59375 42.09375 40 44.09375 40 +C 47.40625 40 47.40625 41.796875 47.40625 43.796875 +z +" id="CMTT12-67"/> + <path d="M 45.59375 21.640625 +C 45.59375 34.109375 36.5 44 25.703125 44 +C 14.90625 44 5.796875 34.109375 5.796875 21.640625 +C 5.796875 9.078125 15 -0.5 25.703125 -0.5 +C 36.40625 -0.5 45.59375 9.078125 45.59375 21.640625 +z +M 25.703125 5.296875 +C 18.5 5.296875 12.296875 12.875 12.296875 22.34375 +C 12.296875 31.609375 18.703125 38.203125 25.703125 38.203125 +C 32.703125 38.203125 39.09375 31.609375 39.09375 22.34375 +C 39.09375 12.78125 32.90625 5.296875 25.703125 5.296875 +z +" id="CMTT12-111"/> + <path d="M 29 56.90625 +C 29 60 28.40625 61 25 61 +L 10.296875 61 +C 8.90625 61 6.296875 61 6.296875 58.109375 +C 6.296875 55.203125 8.90625 55.203125 10.296875 55.203125 +L 22.5 55.203125 +L 22.5 5.796875 +L 10.296875 5.796875 +C 8.90625 5.796875 6.296875 5.796875 6.296875 2.90625 +C 6.296875 0 8.90625 0 10.296875 0 +L 41.203125 0 +C 42.59375 0 45.203125 0 45.203125 2.90625 +C 45.203125 5.796875 42.59375 5.796875 41.203125 5.796875 +L 29 5.796875 +z +" id="CMTT12-108"/> + <path d="M 19 6.59375 +C 17.796875 6.59375 13.203125 6.59375 13.203125 12.015625 +C 13.203125 13.921875 13.5 14.3125 13.90625 15.109375 +C 15.90625 13.703125 19.203125 12.40625 22.796875 12.40625 +C 31.90625 12.40625 38.90625 19.703125 38.90625 28.28125 +C 38.90625 30.78125 38.296875 33.890625 36.203125 37.1875 +C 38.796875 38.421875 41.203125 38.703125 42.703125 38.703125 +C 43.296875 36 46.203125 36 46.296875 36 +C 47.5 36 50 36.796875 50 39.796875 +C 50 41.703125 48.5 45 42.90625 45 +C 41.09375 45 36.59375 44.5625 32.40625 40.90625 +C 29.09375 43.203125 25.703125 44 22.90625 44 +C 13.796875 44 6.796875 36.765625 6.796875 28.21875 +C 6.796875 26.03125 7.296875 22.265625 10.09375 18.703125 +C 8.203125 15.90625 7.90625 12.90625 7.90625 11.703125 +C 7.90625 8.203125 9.59375 5.40625 10.40625 4.5 +C 4.90625 1.484375 2.90625 -3.9375 2.90625 -7.84375 +C 2.90625 -16.171875 13 -22.5 25.703125 -22.5 +C 38.40625 -22.5 48.5 -16.28125 48.5 -7.84375 +C 48.5 6.59375 30.796875 6.59375 27.5 6.59375 +z +M 22.796875 18 +C 17.796875 18 13.296875 22.359375 13.296875 28.203125 +C 13.296875 34.046875 17.90625 38.390625 22.796875 38.390625 +C 28.09375 38.390625 32.40625 33.84375 32.40625 28.203125 +C 32.40625 22.546875 28.09375 18 22.796875 18 +z +M 25.703125 -16.890625 +C 15.90625 -16.890625 8.59375 -12.484375 8.59375 -7.84375 +C 8.59375 -6.328125 9.09375 -2.8125 12.796875 -0.515625 +C 15.09375 1 16.09375 1 23 1 +C 31.796875 1 42.796875 1 42.796875 -7.84375 +C 42.796875 -12.484375 35.5 -16.890625 25.703125 -16.890625 +z +" id="CMTT12-103"/> + <path d="M 41.40625 28.375 +C 41.40625 35.984375 35.703125 44 22.203125 44 +C 18 44 8.296875 44 8.296875 37.1875 +C 8.296875 34.53125 10.203125 33.046875 12.40625 33.046875 +C 13 33.046875 16.296875 33.046875 16.40625 37.3125 +C 16.40625 37.796875 16.5 37.90625 18.59375 38.09375 +C 19.796875 38.203125 21.09375 38.203125 22.296875 38.203125 +C 24.59375 38.203125 28 38.203125 31.296875 35.65625 +C 34.90625 32.8125 34.90625 29.9375 34.90625 27 +C 29 27 23.203125 27 17 24.984375 +C 12 23.265625 5.59375 19.625 5.59375 12.75 +C 5.59375 5.5625 11.90625 -0.5 21.203125 -0.5 +C 24.40625 -0.5 30.59375 0 35.796875 3.53125 +C 37.796875 0 42.796875 0 46.59375 0 +C 49 0 51.40625 0 51.40625 2.859375 +C 51.40625 5.703125 48.796875 5.703125 47.40625 5.703125 +C 44.796875 5.703125 42.796875 5.90625 41.40625 6.515625 +z +M 34.90625 13.296875 +C 34.90625 11 34.90625 8.890625 30.796875 7 +C 27.296875 5.296875 22.59375 5.296875 22.296875 5.296875 +C 16.40625 5.296875 12.09375 8.5 12.09375 12.59375 +C 12.09375 18.5 22.796875 21.890625 34.90625 21.890625 +z +" id="CMTT12-97"/> + <path d="M 45.296875 30.359375 +C 45.296875 32.5625 45.296875 43.796875 36.5 43.796875 +C 33 43.796875 29.59375 42.1875 26.90625 38.484375 +C 26.296875 39.984375 24.09375 43.796875 19.296875 43.796875 +C 14.796875 43.796875 11.703125 40.828125 10.90625 39.921875 +C 10.796875 43 8.703125 43 6.90625 43 +L 3.90625 43 +C 2.5 43 -0.09375 43 -0.09375 40.109375 +C -0.09375 37.203125 2.203125 37.203125 5.296875 37.203125 +L 5.296875 5.796875 +C 2.09375 5.796875 -0.09375 5.796875 -0.09375 2.90625 +C -0.09375 0 2.5 0 3.90625 0 +L 12.296875 0 +C 13.703125 0 16.296875 0 16.296875 2.90625 +C 16.296875 5.796875 14 5.796875 10.90625 5.796875 +L 10.90625 24.25 +C 10.90625 32.671875 14.5 38 18.90625 38 +C 21.703125 38 22.5 34.484375 22.5 29.765625 +L 22.5 5.796875 +C 20.796875 5.796875 18.09375 5.796875 18.09375 2.90625 +C 18.09375 0 20.796875 0 22.203125 0 +L 29.5 0 +C 30.90625 0 33.5 0 33.5 2.90625 +C 33.5 5.796875 31.203125 5.796875 28.09375 5.796875 +L 28.09375 24.25 +C 28.09375 32.671875 31.703125 38 36.09375 38 +C 38.90625 38 39.703125 34.484375 39.703125 29.765625 +L 39.703125 5.796875 +C 38 5.796875 35.296875 5.796875 35.296875 2.90625 +C 35.296875 0 38 0 39.40625 0 +L 46.703125 0 +C 48.09375 0 50.703125 0 50.703125 2.90625 +C 50.703125 5.796875 48.40625 5.796875 45.296875 5.796875 +z +" id="CMTT12-109"/> + <path d="M 41.59375 19.5 +C 43.703125 19.5 45.59375 19.5 45.59375 23.15625 +C 45.59375 34.703125 39 44 26.5 44 +C 14.90625 44 5.59375 34.015625 5.59375 21.84375 +C 5.59375 9.28125 15.703125 -0.5 28 -0.5 +C 40.90625 -0.5 45.59375 8.453125 45.59375 11.078125 +C 45.59375 11.6875 45.40625 13.921875 42.296875 13.921875 +C 40.40625 13.921875 39.796875 13.203125 39.203125 11.6875 +C 36.703125 5.796875 30.203125 5.296875 28.296875 5.296875 +C 20 5.296875 13.40625 11.6875 12.296875 19.5 +z +M 12.40625 25 +C 13.703125 33.3125 20.203125 38.203125 26.5 38.203125 +C 36.5 38.203125 38.59375 29.890625 39 25 +z +" id="CMTT12-101"/> + <path d="M 24.703125 37.203125 +L 36.796875 37.203125 +C 38.203125 37.203125 40.796875 37.203125 40.796875 40.109375 +C 40.796875 43 38.203125 43 36.796875 43 +L 24.703125 43 +L 24.703125 48.078125 +C 24.703125 56 31.703125 56 34.90625 56 +C 34.90625 55.796875 35.703125 52.03125 39 52.03125 +C 40.59375 52.03125 42.90625 53.234375 42.90625 56.0625 +C 42.90625 61.796875 35.296875 61.796875 33.796875 61.796875 +C 26.203125 61.796875 18.203125 57.453125 18.203125 48.453125 +L 18.203125 43 +L 8.296875 43 +C 6.90625 43 4.203125 43 4.203125 40.109375 +C 4.203125 37.203125 6.796875 37.203125 8.203125 37.203125 +L 18.203125 37.203125 +L 18.203125 5.796875 +L 8.703125 5.796875 +C 7.296875 5.796875 4.703125 5.796875 4.703125 2.90625 +C 4.703125 0 7.296875 0 8.703125 0 +L 34.203125 0 +C 35.59375 0 38.203125 0 38.203125 2.90625 +C 38.203125 5.796875 35.59375 5.796875 34.203125 5.796875 +L 24.703125 5.796875 +z +" id="CMTT12-102"/> + <path d="M 45.59375 10.96875 +C 45.59375 13.5625 43.09375 13.5625 42.296875 13.5625 +C 40 13.5625 39.59375 12.765625 39.09375 11.375 +C 36.90625 5.890625 32 5.296875 29.59375 5.296875 +C 21.09375 5.296875 13.90625 12.375 13.90625 21.640625 +C 13.90625 26.734375 16.796875 38.203125 29.90625 38.203125 +C 32.59375 38.203125 34.703125 38 35.59375 37.90625 +C 36.296875 37.703125 36.40625 37.578125 36.40625 37.078125 +C 36.703125 32.921875 39.796875 32.921875 40.40625 32.921875 +C 42.59375 32.921875 44.5 34.421875 44.5 37.109375 +C 44.5 44 34.40625 44 30 44 +C 12.90625 44 7.40625 30.03125 7.40625 21.640625 +C 7.40625 9.484375 16.703125 -0.5 28.703125 -0.5 +C 42.09375 -0.5 45.59375 9.375 45.59375 10.96875 +z +" id="CMTT12-99"/> + <path d="M 21.59375 37.203125 +L 37.796875 37.203125 +C 39.203125 37.203125 41.796875 37.203125 41.796875 40.109375 +C 41.796875 43 39.203125 43 37.796875 43 +L 21.59375 43 +L 21.59375 51.328125 +C 21.59375 53.15625 21.59375 55.5 18.40625 55.5 +C 15.09375 55.5 15.09375 53.15625 15.09375 51.328125 +L 15.09375 43 +L 6.59375 43 +C 5.203125 43 2.5 43 2.5 40.109375 +C 2.5 37.203125 5.09375 37.203125 6.5 37.203125 +L 15.09375 37.203125 +L 15.09375 12.125 +C 15.09375 2.875 21.5 -0.5 28.703125 -0.5 +C 34.09375 -0.5 44 2.171875 44 12.34375 +C 44 14.34375 44 16.53125 40.703125 16.53125 +C 37.5 16.53125 37.5 14.34375 37.5 12.265625 +C 37.40625 6.296875 31.703125 5.296875 29.40625 5.296875 +C 21.59375 5.296875 21.59375 10.265625 21.59375 12.640625 +z +" id="CMTT12-116"/> + <path d="M 30.203125 56.5 +C 30.203125 59 28.203125 61 25.703125 61 +C 23.203125 61 21.203125 59 21.203125 56.5 +C 21.203125 54 23.203125 52 25.703125 52 +C 28.203125 52 30.203125 54 30.203125 56.5 +z +M 13 43 +C 11.59375 43 9 43 9 40.109375 +C 9 37.203125 11.59375 37.203125 13 37.203125 +L 23.703125 37.203125 +L 23.703125 5.796875 +L 12.296875 5.796875 +C 10.90625 5.796875 8.203125 5.796875 8.203125 2.90625 +C 8.203125 0 10.90625 0 12.296875 0 +L 40.09375 0 +C 41.5 0 44.09375 0 44.09375 2.90625 +C 44.09375 5.796875 41.5 5.796875 40.09375 5.796875 +L 30.203125 5.796875 +L 30.203125 38.921875 +C 30.203125 42 29.59375 43 26.203125 43 +z +" id="CMTT12-105"/> + <path d="M 42.40625 37.203125 +L 45 37.203125 +C 46.40625 37.203125 49.09375 37.203125 49.09375 40.109375 +C 49.09375 43 46.40625 43 45 43 +L 34 43 +C 32.59375 43 29.90625 43 29.90625 40.109375 +C 29.90625 37.203125 32.59375 37.203125 34 37.203125 +L 36.5 37.203125 +L 25.703125 5.078125 +L 14.90625 37.203125 +L 17.40625 37.203125 +C 18.796875 37.203125 21.5 37.203125 21.5 40.109375 +C 21.5 43 18.796875 43 17.40625 43 +L 6.40625 43 +C 5 43 2.296875 43 2.296875 40.109375 +C 2.296875 37.203125 5 37.203125 6.40625 37.203125 +L 9 37.203125 +L 20.5 2.890625 +C 21.59375 -0.5 23.796875 -0.5 25.703125 -0.5 +C 27.59375 -0.5 29.796875 -0.5 30.90625 2.890625 +z +" id="CMTT12-118"/> + </defs> + <g transform="translate(68.658545 53.766087)scale(0.07 -0.07)"> + <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> + <use transform="translate(49.8132 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> + <use transform="translate(107.123069 15.050129)scale(0.996264)" xlink:href="#CMEX10-98"/> + <use transform="translate(165.998143 15.050129)scale(0.996264)" xlink:href="#CMTT12-67"/> + <use transform="translate(217.264254 15.050129)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(268.530365 15.050129)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(319.796475 15.050129)scale(0.996264)" xlink:href="#CMTT12-108"/> + <use transform="translate(404.790082 15.050129)scale(0.996264)" xlink:href="#CMTT12-103"/> + <use transform="translate(456.056192 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(507.322303 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(558.588414 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(609.854525 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(694.848131 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(746.114242 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> + <use transform="translate(797.380353 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> + <use transform="translate(848.646464 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(899.912575 15.050129)scale(0.996264)" xlink:href="#CMTT12-99"/> + <use transform="translate(951.178685 15.050129)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(1002.444796 15.050129)scale(0.996264)" xlink:href="#CMTT12-105"/> + <use transform="translate(1053.710907 15.050129)scale(0.996264)" xlink:href="#CMTT12-118"/> + <use transform="translate(1104.977018 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <path d="M 376.683597 15.050129 +L 404.790082 15.050129 +L 404.790082 15.448629 +L 376.683597 15.448629 +L 376.683597 15.050129 +z +"/> + <path d="M 666.741647 15.050129 +L 694.848131 15.050129 +L 694.848131 15.448629 +L 666.741647 15.448629 +L 666.741647 15.050129 +z +"/> + </g> + </g> + <g id="text_13"> + <!-- $n_{\rm H}~\widehat{}~{\tt Jeans\_gamma\_effective}$ --> + <defs> + <path d="M 40 55.203125 +L 42.703125 55.203125 +C 44.09375 55.203125 46.796875 55.203125 46.796875 58.109375 +C 46.796875 61 44.09375 61 42.703125 61 +L 25.796875 61 +C 24.40625 61 21.796875 61 21.796875 58.109375 +C 21.796875 55.203125 24.40625 55.203125 25.796875 55.203125 +L 33.5 55.203125 +L 33.5 14.234375 +C 33.5 5.515625 25.40625 4.90625 23.703125 4.90625 +C 22.5 4.90625 17.5 4.90625 14.90625 7.984375 +C 15.40625 8.59375 15.703125 9.46875 15.703125 10.375 +C 15.703125 12.453125 14.09375 14.34375 11.703125 14.34375 +C 9.40625 14.34375 7.59375 12.84375 7.59375 10.0625 +C 7.59375 3.6875 14.40625 -1 23.59375 -1 +C 32 -1 40 4.265625 40 13.53125 +z +" id="CMTT12-74"/> + <path d="M 41.796875 29.359375 +C 41.796875 39.171875 37.203125 43.796875 29 43.796875 +C 22.203125 43.796875 17.796875 40 16.09375 38.09375 +C 16.09375 41.65625 16.09375 43 12.09375 43 +L 5.5 43 +C 4.09375 43 1.5 43 1.5 40.109375 +C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 +L 9.59375 37.203125 +L 9.59375 5.796875 +L 5.5 5.796875 +C 4.09375 5.796875 1.5 5.796875 1.5 2.90625 +C 1.5 0 4.09375 0 5.5 0 +L 20.203125 0 +C 21.59375 0 24.203125 0 24.203125 2.90625 +C 24.203125 5.796875 21.59375 5.796875 20.203125 5.796875 +L 16.09375 5.796875 +L 16.09375 23.953125 +C 16.09375 33.671875 22.90625 38 28.296875 38 +C 34.09375 38 35.296875 34.671875 35.296875 28.96875 +L 35.296875 5.796875 +L 31.203125 5.796875 +C 29.796875 5.796875 27.203125 5.796875 27.203125 2.90625 +C 27.203125 0 29.796875 0 31.203125 0 +L 45.90625 0 +C 47.296875 0 49.90625 0 49.90625 2.90625 +C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 +L 41.796875 5.796875 +z +" id="CMTT12-110"/> + <path d="M 42 40.203125 +C 42 41.796875 42 44 39.203125 44 +C 36.796875 44 36.203125 41.703125 36.203125 41.59375 +C 32.203125 44 27.59375 44 25.59375 44 +C 9.296875 44 7.09375 35.71875 7.09375 32.328125 +C 7.09375 28.234375 9.5 25.453125 12.90625 23.546875 +C 16.09375 21.75 19 21.265625 27.203125 19.96875 +C 31.09375 19.265625 39.09375 17.96875 39.09375 12.484375 +C 39.09375 8.78125 35.703125 5.296875 26.40625 5.296875 +C 20 5.296875 15.796875 7.796875 13.703125 15 +C 13.203125 16.390625 12.90625 17.5 10.40625 17.5 +C 7.09375 17.5 7.09375 15.59375 7.09375 13.59375 +L 7.09375 3.296875 +C 7.09375 1.6875 7.09375 -0.5 9.90625 -0.5 +C 11.09375 -0.5 12 -0.5 13.5 3.59375 +C 18.09375 -0.5 23.296875 -0.5 26.40625 -0.5 +C 44.90625 -0.5 44.90625 11.46875 44.90625 12.46875 +C 44.90625 22.828125 32.5 24.9375 27.703125 25.625 +C 18.90625 27.125 12.90625 28.125 12.90625 32.3125 +C 12.90625 35.015625 16 38.203125 25.40625 38.203125 +C 34.90625 38.203125 35.296875 33.703125 35.5 31.09375 +C 35.703125 29.09375 37.5 28.796875 38.703125 28.796875 +C 42 28.796875 42 30.59375 42 32.59375 +z +" id="CMTT12-115"/> + </defs> + <g transform="translate(158.822024 66.279355)rotate(-43)scale(0.07 -0.07)"> + <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> + <use transform="translate(49.8132 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> + <use transform="translate(107.123069 15.050129)scale(0.996264)" xlink:href="#CMEX10-98"/> + <use transform="translate(165.998143 15.050129)scale(0.996264)" xlink:href="#CMTT12-74"/> + <use transform="translate(217.264254 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(268.530365 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(319.796475 15.050129)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(371.062586 15.050129)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(456.056192 15.050129)scale(0.996264)" xlink:href="#CMTT12-103"/> + <use transform="translate(507.322303 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(558.588414 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(609.854525 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(661.120636 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(746.114242 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(797.380353 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> + <use transform="translate(848.646464 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> + <use transform="translate(899.912575 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(951.178685 15.050129)scale(0.996264)" xlink:href="#CMTT12-99"/> + <use transform="translate(1002.444796 15.050129)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(1053.710907 15.050129)scale(0.996264)" xlink:href="#CMTT12-105"/> + <use transform="translate(1104.977018 15.050129)scale(0.996264)" xlink:href="#CMTT12-118"/> + <use transform="translate(1156.243129 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> + <path d="M 427.949708 15.050129 +L 456.056192 15.050129 +L 456.056192 15.448629 +L 427.949708 15.448629 +L 427.949708 15.050129 +z +"/> + <path d="M 718.007758 15.050129 +L 746.114242 15.050129 +L 746.114242 15.448629 +L 718.007758 15.448629 +L 718.007758 15.050129 +z +"/> + </g> + </g> + <g id="text_14"> + <!-- ${\tt Cool\_density\_threshold\_H\_p\_cm3}$ --> + <defs> + <path d="M 41.796875 56.90625 +C 41.796875 60 41.203125 61 37.796875 61 +L 31.203125 61 +C 29.796875 61 27.203125 61 27.203125 58.109375 +C 27.203125 55.203125 29.796875 55.203125 31.203125 55.203125 +L 35.296875 55.203125 +L 35.296875 38.796875 +C 33.703125 40.390625 29.59375 43.796875 23.5 43.796875 +C 12.90625 43.796875 3.703125 34.1875 3.703125 21.59375 +C 3.703125 9.296875 12.296875 -0.5 22.59375 -0.5 +C 29.09375 -0.5 33.40625 3.359375 35.296875 5.546875 +C 35.296875 1.3125 35.296875 0 39.296875 0 +L 45.90625 0 +C 47.296875 0 49.90625 0 49.90625 2.90625 +C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 +L 41.796875 5.796875 +z +M 35.296875 19.09375 +C 35.296875 13.5 30.703125 5.296875 23.203125 5.296875 +C 16 5.296875 10.203125 12.59375 10.203125 21.59375 +C 10.203125 31.1875 17 38 24.09375 38 +C 30.59375 38 35.296875 32.09375 35.296875 26.890625 +z +" id="CMTT12-100"/> + <path d="M 42.40625 37.203125 +L 45 37.203125 +C 46.40625 37.203125 49.09375 37.203125 49.09375 40.109375 +C 49.09375 43 46.40625 43 45 43 +L 34 43 +C 32.59375 43 29.90625 43 29.90625 40.109375 +C 29.90625 37.203125 32.59375 37.203125 34 37.203125 +L 36.5 37.203125 +C 33.703125 29.0625 28 12.515625 26.59375 6.859375 +L 26.5 6.859375 +C 26 9.046875 25.59375 10.125 24.59375 12.8125 +L 15.296875 37.203125 +L 17.59375 37.203125 +C 19 37.203125 21.703125 37.203125 21.703125 40.109375 +C 21.703125 43 19 43 17.59375 43 +L 6.59375 43 +C 5.203125 43 2.5 43 2.5 40.109375 +C 2.5 37.203125 5.203125 37.203125 6.59375 37.203125 +L 9.296875 37.203125 +L 23.296875 1.46875 +C 23.703125 0.46875 23.703125 0.265625 23.703125 0.171875 +C 23.703125 -0.03125 21.09375 -8.671875 19.59375 -11.4375 +C 19 -12.4375 16.5 -17.109375 11.703125 -16.609375 +C 11.796875 -16.296875 12.09375 -15.703125 12.09375 -14.609375 +C 12.09375 -12.296875 10.5 -10.703125 8.203125 -10.703125 +C 5.703125 -10.703125 4.203125 -12.40625 4.203125 -14.703125 +C 4.203125 -18.5 7.40625 -22.5 12.40625 -22.5 +C 22.09375 -22.5 26.40625 -9.6875 26.703125 -8.890625 +z +" id="CMTT12-121"/> + <path d="M 41.796875 29.359375 +C 41.796875 39.171875 37.203125 43.796875 29 43.796875 +C 22.203125 43.796875 17.796875 40 16.09375 38.09375 +L 16.09375 56.9375 +C 16.09375 60.015625 15.5 61 12.09375 61 +L 5.5 61 +C 4.09375 61 1.5 61 1.5 58.109375 +C 1.5 55.203125 4.09375 55.203125 5.5 55.203125 +L 9.59375 55.203125 +L 9.59375 5.796875 +L 5.5 5.796875 +C 4.09375 5.796875 1.5 5.796875 1.5 2.90625 +C 1.5 0 4.09375 0 5.5 0 +L 20.203125 0 +C 21.59375 0 24.203125 0 24.203125 2.90625 +C 24.203125 5.796875 21.59375 5.796875 20.203125 5.796875 +L 16.09375 5.796875 +L 16.09375 23.953125 +C 16.09375 33.671875 22.90625 38 28.296875 38 +C 34.09375 38 35.296875 34.671875 35.296875 28.96875 +L 35.296875 5.796875 +L 31.203125 5.796875 +C 29.796875 5.796875 27.203125 5.796875 27.203125 2.90625 +C 27.203125 0 29.796875 0 31.203125 0 +L 45.90625 0 +C 47.296875 0 49.90625 0 49.90625 2.90625 +C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 +L 41.796875 5.796875 +z +" id="CMTT12-104"/> + <path d="M 21.59375 18.9375 +C 21.59375 30.875 30 38 38.5 38 +C 39 38 39.59375 38 40.09375 37.890625 +C 40.40625 34.140625 43.203125 33.828125 43.90625 33.828125 +C 46.203125 33.828125 47.796875 35.546875 47.796875 37.78125 +C 47.796875 42.15625 43.59375 43.796875 38.59375 43.796875 +C 31.90625 43.796875 26.203125 40.734375 21.59375 35.015625 +L 21.59375 38.921875 +C 21.59375 42 21 43 17.59375 43 +L 7.5 43 +C 6.09375 43 3.5 43 3.5 40.109375 +C 3.5 37.203125 6.09375 37.203125 7.5 37.203125 +L 15.09375 37.203125 +L 15.09375 5.796875 +L 7.5 5.796875 +C 6.09375 5.796875 3.5 5.796875 3.5 2.90625 +C 3.5 0 6.09375 0 7.5 0 +L 32.09375 0 +C 33.5 0 36.09375 0 36.09375 2.90625 +C 36.09375 5.796875 33.5 5.796875 32.09375 5.796875 +L 21.59375 5.796875 +z +" id="CMTT12-114"/> + <path d="M 42.796875 55.203125 +L 45 55.203125 +C 46.40625 55.203125 49 55.203125 49 58.109375 +C 49 61 46.5 61 45 61 +L 34.09375 61 +C 32.703125 61 30 61 30 58.109375 +C 30 55.203125 32.703125 55.203125 34.09375 55.203125 +L 36.296875 55.203125 +L 36.296875 34.796875 +L 15.09375 34.796875 +L 15.09375 55.203125 +L 17.296875 55.203125 +C 18.703125 55.203125 21.40625 55.203125 21.40625 58.109375 +C 21.40625 61 18.703125 61 17.296875 61 +L 6.40625 61 +C 4.90625 61 2.40625 61 2.40625 58.109375 +C 2.40625 55.203125 5 55.203125 6.40625 55.203125 +L 8.59375 55.203125 +L 8.59375 5.796875 +L 6.40625 5.796875 +C 5 5.796875 2.40625 5.796875 2.40625 2.90625 +C 2.40625 0 4.90625 0 6.40625 0 +L 17.296875 0 +C 18.703125 0 21.40625 0 21.40625 2.90625 +C 21.40625 5.796875 18.703125 5.796875 17.296875 5.796875 +L 15.09375 5.796875 +L 15.09375 29 +L 36.296875 29 +L 36.296875 5.796875 +L 34.09375 5.796875 +C 32.703125 5.796875 30 5.796875 30 2.90625 +C 30 0 32.703125 0 34.09375 0 +L 45 0 +C 46.5 0 49 0 49 2.90625 +C 49 5.796875 46.40625 5.796875 45 5.796875 +L 42.796875 5.796875 +z +" id="CMTT12-72"/> + <path d="M 20.203125 -22 +C 21.59375 -22 24.203125 -22 24.203125 -19.09375 +C 24.203125 -16.203125 21.59375 -16.203125 20.203125 -16.203125 +L 16.09375 -16.203125 +L 16.09375 5.09375 +C 18.5 2.390625 22.203125 -0.5 27.796875 -0.5 +C 38.40625 -0.5 47.703125 9.046875 47.703125 21.796875 +C 47.703125 34.15625 39.296875 44 28.90625 44 +C 21.59375 44 17 39.578125 16.09375 38.578125 +C 16.09375 41.625 16.09375 43 12.09375 43 +L 5.5 43 +C 4.09375 43 1.5 43 1.5 40.109375 +C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 +L 9.59375 37.203125 +L 9.59375 -16.203125 +L 5.5 -16.203125 +C 4.09375 -16.203125 1.5 -16.203125 1.5 -19.09375 +C 1.5 -22 4.09375 -22 5.5 -22 +z +M 16.09375 26.828125 +C 16.09375 32.5625 21.703125 38.203125 28.203125 38.203125 +C 35.40625 38.203125 41.203125 30.84375 41.203125 21.796875 +C 41.203125 12.140625 34.40625 5.296875 27.296875 5.296875 +C 19.796875 5.296875 16.09375 13.84375 16.09375 18.875 +z +" id="CMTT12-112"/> + <path d="M 25.59375 30.59375 +C 36 30.59375 40.59375 23.21875 40.59375 17.765625 +C 40.59375 11.078125 34.796875 4.796875 26 4.796875 +C 16 4.796875 11.5 10.25 11.5 11.671875 +C 11.5 11.859375 11.59375 12.0625 11.703125 12.171875 +C 12.09375 12.875 12.40625 13.703125 12.40625 14.609375 +C 12.40625 16.734375 10.796875 18.65625 8.40625 18.65625 +C 6.296875 18.65625 4.296875 17.34375 4.296875 14.234375 +C 4.296875 5.453125 13.796875 -1 26 -1 +C 38.59375 -1 47.09375 8.078125 47.09375 17.65625 +C 47.09375 22.8125 44.296875 29.96875 35.5 33.796875 +C 41.59375 37.609375 44.296875 43.625 44.296875 48.84375 +C 44.296875 56.375 36.796875 62.890625 26 62.890625 +C 14.90625 62.890625 7.09375 58.078125 7.09375 50.65625 +C 7.09375 47.34375 9.59375 46.34375 11.203125 46.34375 +C 13 46.34375 15.203125 47.765625 15.203125 50.515625 +C 15.203125 52.125 14.40625 53.046875 14.40625 53.140625 +C 17.40625 57 24.296875 57 26 57 +C 32.796875 57 37.796875 53.3125 37.796875 48.484375 +C 37.796875 45.515625 35.90625 37 25.40625 37 +C 21.703125 37 20.09375 36.796875 19.703125 36.796875 +C 17.703125 36.578125 17.203125 35.203125 17.203125 33.703125 +C 17.203125 30.59375 19.296875 30.59375 21 30.59375 +z +" id="CMTT12-51"/> + </defs> + <g transform="translate(75.778884 192.590474)rotate(-90)scale(0.07 -0.07)"> + <use transform="scale(0.996264)" xlink:href="#CMTT12-67"/> + <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-108"/> + <use transform="translate(238.791939 0)scale(0.996264)" xlink:href="#CMTT12-100"/> + <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-105"/> + <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-121"/> + <use transform="translate(631.38221 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(682.648321 0)scale(0.996264)" xlink:href="#CMTT12-104"/> + <use transform="translate(733.914432 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(785.180542 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-104"/> + <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-108"/> + <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-100"/> + <use transform="translate(1126.504703 0)scale(0.996264)" xlink:href="#CMTT12-72"/> + <use transform="translate(1211.498309 0)scale(0.996264)" xlink:href="#CMTT12-112"/> + <use transform="translate(1296.491915 0)scale(0.996264)" xlink:href="#CMTT12-99"/> + <use transform="translate(1347.758026 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(1399.024137 0)scale(0.996264)" xlink:href="#CMTT12-51"/> + <path d="M 210.685454 0 +L 238.791939 0 +L 238.791939 0.3985 +L 210.685454 0.3985 +L 210.685454 0 +z +"/> + <path d="M 603.275726 0 +L 631.38221 0 +L 631.38221 0.3985 +L 603.275726 0.3985 +L 603.275726 0 +z +"/> + <path d="M 1098.398219 0 +L 1126.504703 0 +L 1126.504703 0.3985 +L 1098.398219 0.3985 +L 1098.398219 0 +z +"/> + <path d="M 1183.391825 0 +L 1211.498309 0 +L 1211.498309 0.3985 +L 1183.391825 0.3985 +L 1183.391825 0 +z +"/> + <path d="M 1268.385431 0 +L 1296.491915 0 +L 1296.491915 0.3985 +L 1268.385431 0.3985 +L 1268.385431 0 +z +"/> + </g> + </g> + <g id="text_15"> + <!-- ${\tt Jeans\_density\_threshold\_H\_p\_cm3}$ --> + <g transform="translate(145.055974 192.590474)rotate(-90)scale(0.07 -0.07)"> + <use transform="scale(0.996264)" xlink:href="#CMTT12-74"/> + <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(205.064443 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-100"/> + <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-105"/> + <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-121"/> + <use transform="translate(682.648321 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(733.914432 0)scale(0.996264)" xlink:href="#CMTT12-104"/> + <use transform="translate(785.180542 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-104"/> + <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-108"/> + <use transform="translate(1092.777208 0)scale(0.996264)" xlink:href="#CMTT12-100"/> + <use transform="translate(1177.770814 0)scale(0.996264)" xlink:href="#CMTT12-72"/> + <use transform="translate(1262.76442 0)scale(0.996264)" xlink:href="#CMTT12-112"/> + <use transform="translate(1347.758026 0)scale(0.996264)" xlink:href="#CMTT12-99"/> + <use transform="translate(1399.024137 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(1450.290248 0)scale(0.996264)" xlink:href="#CMTT12-51"/> + <path d="M 261.951565 0 +L 290.05805 0 +L 290.05805 0.3985 +L 261.951565 0.3985 +L 261.951565 0 +z +"/> + <path d="M 654.541837 0 +L 682.648321 0 +L 682.648321 0.3985 +L 654.541837 0.3985 +L 654.541837 0 +z +"/> + <path d="M 1149.664329 0 +L 1177.770814 0 +L 1177.770814 0.3985 +L 1149.664329 0.3985 +L 1149.664329 0 +z +"/> + <path d="M 1234.657936 0 +L 1262.76442 0 +L 1262.76442 0.3985 +L 1234.657936 0.3985 +L 1234.657936 0 +z +"/> + <path d="M 1319.651542 0 +L 1347.758026 0 +L 1347.758026 0.3985 +L 1319.651542 0.3985 +L 1319.651542 0 +z +"/> + </g> + </g> + <g id="text_16"> + <!-- ${\tt Cool\_temperature\_norm\_K}$ --> + <defs> + <path d="M 41.796875 38.921875 +C 41.796875 42 41.203125 43 37.796875 43 +L 31.203125 43 +C 29.796875 43 27.203125 43 27.203125 40.109375 +C 27.203125 37.203125 29.796875 37.203125 31.203125 37.203125 +L 35.296875 37.203125 +L 35.296875 15.625 +C 35.296875 7.484375 28.5 5.296875 24 5.296875 +C 16.09375 5.296875 16.09375 8.875 16.09375 11.96875 +L 16.09375 38.921875 +C 16.09375 42 15.5 43 12.09375 43 +L 5.5 43 +C 4.09375 43 1.5 43 1.5 40.109375 +C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 +L 9.59375 37.203125 +L 9.59375 11.4375 +C 9.59375 2.28125 15.59375 -0.5 23.296875 -0.5 +C 29.40625 -0.5 33.203125 1.984375 35.203125 3.671875 +C 35.203125 0 37.59375 0 39.296875 0 +L 45.90625 0 +C 47.296875 0 49.90625 0 49.90625 2.90625 +C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 +L 41.796875 5.796875 +z +" id="CMTT12-117"/> + <path d="M 27.09375 35.734375 +L 42.40625 55.203125 +C 45.296875 55.203125 47.59375 55.203125 47.59375 58.109375 +C 47.59375 61 45 61 43.59375 61 +L 35.203125 61 +C 33.796875 61 31.203125 61 31.203125 58.109375 +C 31.203125 55.203125 33.796875 55.203125 35.203125 55.203125 +L 14.203125 28.453125 +L 14.203125 55.203125 +L 16.203125 55.203125 +C 17.59375 55.203125 20.203125 55.203125 20.203125 58.109375 +C 20.203125 61 17.59375 61 16.203125 61 +L 6.59375 61 +C 5.203125 61 2.59375 61 2.59375 58.109375 +C 2.59375 55.203125 5.203125 55.203125 6.59375 55.203125 +L 8.59375 55.203125 +L 8.59375 5.796875 +L 6.59375 5.796875 +C 5.203125 5.796875 2.59375 5.796875 2.59375 2.90625 +C 2.59375 0 5.203125 0 6.59375 0 +L 16.203125 0 +C 17.59375 0 20.203125 0 20.203125 2.90625 +C 20.203125 5.796875 17.59375 5.796875 16.203125 5.796875 +L 14.203125 5.796875 +L 14.203125 19.375 +L 23.296875 30.953125 +L 37.203125 5.796875 +C 35.90625 5.796875 33.296875 5.796875 33.296875 2.90625 +C 33.296875 0 35.90625 0 37.296875 0 +L 44.5 0 +C 45.90625 0 48.5 0 48.5 2.90625 +C 48.5 5.796875 46.09375 5.796875 43.59375 5.796875 +z +" id="CMTT12-75"/> + </defs> + <g transform="translate(37.862259 66.86623)scale(0.07 -0.07)"> + <use transform="scale(0.996264)" xlink:href="#CMTT12-67"/> + <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-108"/> + <use transform="translate(238.791939 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-112"/> + <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(648.920825 0)scale(0.996264)" xlink:href="#CMTT12-117"/> + <use transform="translate(700.186936 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(751.453047 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(1075.238592 0)scale(0.996264)" xlink:href="#CMTT12-75"/> + <path d="M 210.685454 0 +L 238.791939 0 +L 238.791939 0.3985 +L 210.685454 0.3985 +L 210.685454 0 +z +"/> + <path d="M 808.340169 0 +L 836.446653 0 +L 836.446653 0.3985 +L 808.340169 0.3985 +L 808.340169 0 +z +"/> + <path d="M 1047.132108 0 +L 1075.238592 0 +L 1075.238592 0.3985 +L 1047.132108 0.3985 +L 1047.132108 0 +z +"/> + </g> + </g> + <g id="text_17"> + <!-- ${\tt Jeans\_temperature\_norm\_K}$ --> + <g transform="translate(37.862259 81.545055)scale(0.07 -0.07)"> + <use transform="scale(0.996264)" xlink:href="#CMTT12-74"/> + <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(205.064443 0)scale(0.996264)" xlink:href="#CMTT12-115"/> + <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-112"/> + <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-97"/> + <use transform="translate(648.920825 0)scale(0.996264)" xlink:href="#CMTT12-116"/> + <use transform="translate(700.186936 0)scale(0.996264)" xlink:href="#CMTT12-117"/> + <use transform="translate(751.453047 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(802.719158 0)scale(0.996264)" xlink:href="#CMTT12-101"/> + <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-110"/> + <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-111"/> + <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-114"/> + <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-109"/> + <use transform="translate(1126.504703 0)scale(0.996264)" xlink:href="#CMTT12-75"/> + <path d="M 261.951565 0 +L 290.05805 0 +L 290.05805 0.3985 +L 261.951565 0.3985 +L 261.951565 0 +z +"/> + <path d="M 859.60628 0 +L 887.712764 0 +L 887.712764 0.3985 +L 859.60628 0.3985 +L 859.60628 0 +z +"/> + <path d="M 1098.398219 0 +L 1126.504703 0 +L 1126.504703 0.3985 +L 1098.398219 0.3985 +L 1098.398219 0 +z +"/> + </g> + </g> + </g> + </g> + <defs> + <clipPath id="p107a2e5a22"> + <rect height="195.048" width="190.512" x="34.02" y="2.268"/> + </clipPath> + </defs> +</svg> diff --git a/doc/RTD/source/SubgridModels/EAGLE/index.rst b/doc/RTD/source/SubgridModels/EAGLE/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..620572f962948308884414622ca2ae7d555d158f --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/index.rst @@ -0,0 +1,426 @@ +.. EAGLE sub-grid model + Matthieu Schaller, 20th December 2018 + + +EAGLE model +=========== + +This section of the documentation gives a brief description of the +different components of the EAGLE sub-grid model. We mostly focus on +the parameters and values output in the snapshots. + +.. _EAGLE_entropy_floors: + +Entropy floors +~~~~~~~~~~~~~~ + +The gas particles in the EAGLE model are prevented from cooling below a +certain temperature. The temperature limit depends on the density of the +particles. Two floors are used in conjonction. Both are implemented as +polytropic "equations of states" :math:`P = P_c +\left(\rho/\rho_c\right)^\gamma`, with the constants derived from the user +input given in terms of temperature and Hydrogen number density. + +The first limit, labelled as ``Cool``, is typically used to prevent +low-density high-metallicity particles to cool below the warm phase because +of over-cooling induced by the absence of metal diffusion. This limit plays +only a small role in practice. The second limit, labelled as ``Jeans``, is +used to prevent the fragmentation of high-density gas into clumps that +cannot be resolved by the coupled hydro+gravity solver. The two limits are +sketched on the following figure. An additional over-density criterion is +applied to prevent gas not collapsed into structures from being +affected. This criterion demands that :math:`\rho > \Delta_{\rm floor} +\Omega_b \rho_{\rm crit}`, with :math:`\Delta_{\rm floor}` specified by the +user and :math:`\rho_{\rm crit}` the critical density at that redshift +[#f1]_. + +.. figure:: EAGLE_entropy_floor.svg + :width: 400px + :align: center + :figclass: align-center + :alt: Phase-space diagram displaying the two entropy floors used + in the EAGLE model. + + Temperature-density plane with the two entropy floors used in the EAGLE + model indicated by the black lines. Gas particles are not allowed to be + below either of these two floors; they are hence forbidden to enter the + grey-shaded region. The floors are specified by the position in the + plane of the starting point of each line (black circle) and their slope + (dashed lines). The parameter names governing the behaviour of the + floors are indicated on the figure. Note that unlike what is shown on + the figure for clarity reasons, typical values for EAGLE runs place + both anchors at the same temperature. + + +The model is governed by 4 parameters for each of the two +limits. These are given in the ``EAGLEEntropyFloor`` section of the +YAML file. The parameters are the Hydrogen number density (in +:math:`cm^{-3}`) and temperature (in :math:`K`) of the anchor point of +each floor as well as the power-law slope of each floor and the +minimal over-density required to apply the limit. For a normal +EAGLE run, that section of the parameter file reads: + +.. code:: YAML + + EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in, expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold, expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in, expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold, expressed in Kelvin. + Cool_gamma_effective: 1. # Slope of the EAGLE Cool limiter entropy floor + +SWIFT will convert the temperature normalisations and Hydrogen number +density thresholds into internal energies and densities respectively +assuming a neutral gas with primoridal abundance pattern. This implies +that the floor may not be exactly at the position given in the YAML +file if the gas has different properties. This is especially the case +for the temperature limit which will often be lower than the imposed +floor by a factor :math:`\frac{\mu_{\rm neutral}}{\mu_{ionised}} +\approx \frac{1.22}{0.59} \approx 2` due to the different ionisation +states of the gas. + +Note that the model only makes sense if the ``Cool`` threshold is at a lower +density than the ``Jeans`` threshold. + +.. _EAGLE_chemical_tracers: + +Chemical tracers +~~~~~~~~~~~~~~~~ + +The gas particles in the EAGLE model carry metal abundance information in the +form of metal mass fractions. We follow explicitly 9 of the 11 elements that +`Wiersma et al. (2009)b <http://adsabs.harvard.edu/abs/2009MNRAS.399..574W>`_ +traced in their chemical enrichment model. These are: `H`, `He`, `C`, `N`, `O`, +`Ne`, `Mg`, `Si` and `Fe` [#f2]_. We additionally follow the total metal mass fraction +(i.e. absolute metallicity) `Z`. This is typically larger than the sum of the 7 +metals that are individually traced since this will also contain the +contribution of all the elements that are not individually followed. We note +that all of definitions are independent of any definition of solar the solar +metallicity :math:`Z_\odot` or of any solar abundance pattern. + +As part of the diagnostics, we additionally trace the elements coming +from the different stellar evolution channels. We store for each +particle the total mass coming from all the SNIa that enriched that +particle and the metal mass fraction from SNIa. This is the fraction +of the *total* gas mass that is in the form of metals originating from +SNIa stars. By construction this fraction will be smaller than the +total metal mass fraction. The same tracers exist for the SNII and AGB +channels. Finally, we also compute the iron gas fraction from +SNIa. This it the fraction of the *total* gas mass that is made of +iron originating from SNIa explosions. + +We finally also compute the smoothed versions of the individual +element mass fractions, of the total metal mass fractions, and of the +iron gas fraction from SNIa. + +The chemistry module in ``src/chemistry/EAGLE/`` includes all the arrays +that are added to the particles and the functions used to compute the +smoothed elements. + +When a star is formed (see the section :ref:`EAGLE_star_formation` below), it +inherits all the chemical tracers of its parent gas particle. + +In the snapshots, we output for each gas and star particle: + ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| Name | Description | Units | Comments | ++==================================+=====================================+===========+=============================+ +| ``ElementAbundance`` | | Fraction of the gas/star mass | [-] | | Array of length | +| | | in the different elements | | | 9 for each particle | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``SmoothedElementAbundance`` | | Fraction of the gas/star mass | [-] | | Array of length | +| | | in the different elements | | | 9 for each particle | +| | | smoothed over SPH neighbours | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``Metallicity`` | | Fraction of the gas/star mass | [-] | | +| | | in *all* metals | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``SmoothedMetallicity`` | | Fraction of the gas/star mass | [-] | | +| | | in *all* metals | | | +| | | smoothed over SPH neighbours | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``TotalMassFromSNIa`` | | Total mass of the gas/star | [U_M] | | +| | | that was produced by enrichment | | | +| | | from SNIa stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``MetalMassFracFromSNIa`` | | Fraction of the *total* gas/star | [-] | | +| | | mass that is in metals produced | | | +| | | by enrichment from SNIa stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``TotalMassFromAGB`` | | Total mass of the gas/star | [U_M] | | +| | | that was produced by enrichment | | | +| | | from AGB stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``MetalMassFracFromAGB`` | | Fraction of the *total* gas/star | [-] | | +| | | mass that is in metals produced | | | +| | | by enrichment from AGB star | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``TotalMassFromSNII`` | | Total mass of the gas/star | [U_M] | | +| | | that was produced by enrichment | | | +| | | from SNII stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``MetalMassFracFromSNII`` | | Fraction of the gas/star mass | [-] | | +| | | that is in metals produced by | | | +| | | enrichment from SNII stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``IronMassFracFromSNIa`` | | Fraction of the *total* gas/star | [-] | | +| | | mass in *iron* produced produced | | | +| | | by enrichment from SNIa stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``SmoothedIronMassFracFromSNIa`` | | Fraction of the *total* gas/star | [-] | | +| | | mass in *iron* produced produced | | | +| | | by enrichment from SNIa stars | | | +| | | smoothed over SPH neighbours | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ + +The stars will lose mass over their lifetime (up to ~45%). The fractions will +remain unchanged but if one is interested in computing an absolute metal mass +(say) for a star, the ``InitialMass`` (see the section +:ref:`EAGLE_star_formation` below) of the star must be used. + +The chemistry model only requires a small number of parameters to be specified +in the `EAGLEChemistry` section of the YAML file. These are the initial values +of the metallicity and element mass fractions. These are then applied at the +start of a simulation to *all* the *gas* particles. All 9 traced elements have +to be specified An example section, for primordial abundances (typical for a +cosmological run), is: + +.. code:: YAML + + EAGLEChemistry: + init_abundance_metal: 0. # Mass fraction in *all* metals + init_abundance_Hydrogen: 0.755 # Mass fraction in Hydrogen + init_abundance_Helium: 0.245 # Mass fraction in Helium + init_abundance_Carbon: 0. # Mass fraction in Carbon + init_abundance_Nitrogen: 0. # Mass fraction in Nitrogen + init_abundance_Oxygen: 0. # Mass fraction in Oxygen + init_abundance_Neon: 0. # Mass fraction in Neon + init_abundance_Magnesium: 0. # Mass fraction in Magnesium + init_abundance_Silicon: 0. # Mass fraction in Silicon + init_abundance_Iron: 0. # Mass fraction in Iron + +Whilst one would use the following values for solar abundances +(typical for an idealised low-redshift run): + +.. code:: YAML + + EAGLEChemistry: + init_abundance_metal: 0.014 # Mass fraction in *all* metals + init_abundance_Hydrogen: 0.70649785 # Mass fraction in Hydrogen + init_abundance_Helium: 0.28055534 # Mass fraction in Helium + init_abundance_Carbon: 2.0665436e-3 # Mass fraction in Carbon + init_abundance_Nitrogen: 8.3562563e-4 # Mass fraction in Nitrogen + init_abundance_Oxygen: 5.4926244e-3 # Mass fraction in Oxygen + init_abundance_Neon: 1.4144605e-3 # Mass fraction in Neon + init_abundance_Magnesium: 5.907064e-4 # Mass fraction in Magnesium + init_abundance_Silicon: 6.825874e-4 # Mass fraction in Silicon + init_abundance_Iron: 1.1032152e-3 # Mass fraction in Iron + +Individual element abundances for each particle can also be read +directly from the ICs. By default these are overwritten in the code by +the values read from the YAML file. However, users can set the +parameter ``init_abundance_metal`` to ``-1`` to make SWIFT ignore the +values provided in the parameter file: + +.. code:: YAML + + EAGLEChemistry: + init_abundance_metal: -1 # Read the particles' metal mass fractions from the ICs. + + +The ICs must then contain values for these three fields (same as what +is written to the snapshots): + ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| Name | Description | Units | Comments | ++==================================+=====================================+===========+=============================+ +| ``ElementAbundance`` | | Fraction of the gas/star mass | [-] | | Array of length | +| | | in the different elements | | | 9 for each particle | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``Metallicity`` | | Fraction of the gas/star mass | [-] | | +| | | in *all* metals | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ +| ``IronMassFracFromSNIa`` | | Fraction of the *total* gas/star | [-] | | +| | | mass in *iron* produced produced | | | +| | | by enrichment from SNIa stars | | | ++----------------------------------+-------------------------------------+-----------+-----------------------------+ + +If these fields are absent, then a value of ``0`` will be used for all +of them, likely leading to issues in the way the code will run. + +.. _EAGLE_cooling: + +Gas cooling: Wiersma+2009a +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The gas cooling is based on the redshift-dependent tables of `Wiersma et +al. (2009)a <http://adsabs.harvard.edu/abs/2009MNRAS.393...99W>`_ that include +element-by-element cooling rates for the 11 elements (`H`, `He`, `C`, `N`, `O`, +`Ne`, `Mg`, `Si`, `S`, `Ca` and `Fe`) that dominate the total rates. The tables +assume that the gas is in ionization equilibrium with the cosmic microwave +background (CMB) as well as with the evolving X-ray and UV background from +galaxies and quasars described by the model of `Haardt & Madau (2001) +<http://adsabs.harvard.edu/abs/2001cghr.confE..64H>`_. Note that this model +ignores *local* sources of ionization, self-shielding and non-equilibrium +cooling/heating. The tables can be obtained from this `link +<http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/EAGLE/coolingtables.tar.gz>`_ +which is a re-packaged version of the `original tables +<http://www.strw.leidenuniv.nl/WSS08/>`_. The code reading and interpolating the +table is located in the directory ``src/cooling/EAGLE/``. + +The Wiersma tables containing the cooling rates as a function of redshift, +Hydrogen number density, Helium fraction (:math:`X_{He} / (X_{He} + X_{H})`) and +element abundance relative to the solar abundance pattern assumed by the tables +(see equation 4 in the original paper). As the particles do not carry the mass +fraction of `S` and `Ca`, we compute the contribution to the cooling rate of +these elements from the abundance of `Si`. More specifically, we assume that +their abundance by mass relative to the table's solar abundance pattern is the +same as the relative abundance of `Si` (i.e. :math:`[Ca/Si] = 0` and +:math:`[S/Si] = 0`). Users can optionally modify the ratios used for `S` and +`Ca`. + +Above the redshift of Hydrogen re-ionization we use the extra table containing +net cooling rates for gas exposed to the CMB and a UV + X-ray background at +redshift nine truncated above 1 Rydberg. At the redshift or re-ionization, we +additionally inject a fixed user-defined amount of energy per unit mass to all +the gas particles. + +In addition to the tables we inject extra energy from Helium re-ionization using +a Gaussian model with a user-defined redshift for the centre, width and total +amount of energy injected per unit mass. + +For non-cosmological run, we use the :math:`z = 0` table and the interpolation +along the redshift dimension then becomes a trivial operation. + +The cooling itself is performed using an implicit scheme (see the theory +documents) which for small values of the cooling rates is solved explicitly. For +larger values we use a bisection scheme. Users can alternatively use a +Newton-Raphson method that in some cases runs faster than the bisection +method. If the Newton-Raphson method does not converge after a few steps, the +code reverts to a bisection scheme, that is guaranteed to converge. The cooling +rate is added to the calculated change in energy over time from the other +dynamical equations. This is different from other commonly used codes in the +literature where the cooling is done instantaneously. + +We note that the EAGLE cooling model does not impose any restriction on the +particles' individual time-steps. The cooling takes place over the time span +given by the other conditions (e.g the Courant condition). + +Finelly, the cooling module also provides a function to compute the temperature +of a given gas particle based on its density, internal energy, abundances and +the current redshift. This temperature is the one used to compute the cooling +rate from the tables and similarly to the cooling rates, they assume that the +gas is in collisional equilibrium with the background radiation. The +temperatures are, in particular, computed every time a snapshot is written and +they are listed for every gas particle: + ++---------------------+-------------------------------------+-----------+-------------------------------------+ +| Name | Description | Units | Comments | ++=====================+=====================================+===========+=====================================+ +| ``Temperature`` | | Temperature of the gas as | [U_T] | | The calculation is performed | +| | | computed from the tables. | | | using quantities at the last | +| | | | | time-step the particle was active | ++---------------------+-------------------------------------+-----------+-------------------------------------+ + +Note that if one is running without cooling switched on at runtime, the +temperatures can be computed by passing the ``--temparature`` runtime flag (see +:ref:`cmdline-options`). Note that the tables then have to be available as in +the case with cooling switched on. + +The cooling model is driven by a small number of parameter files in the +`EAGLECooling` section of the YAML file. These are the re-ionization parameters, +the path to the tables and optionally the modified abundances of `Ca` and `S` as +well as the flag to attempt using the Newton-Raphson scheme to solve the +implicit problem. A valid section of the YAML file looks like: + +.. code:: YAML + + EAGLECooling: + dir_name: /path/to/the/Wiersma/tables/directory # Absolute or relative path + H_reion_z: 11.5 # Redhift of Hydrogen re-ionization + He_reion_z_centre: 3.5 # Centre of the Gaussian used for Helium re-ionization + He_reion_z_sigma: 0.5 # Width of the Gaussian used for Helium re-ionization + He_reion_ev_p_H: 2.0 # Energy injected in eV per Hydrogen atom for Helium re-ionization. + +And the optional parameters are: + +.. code:: YAML + + EAGLECooling: + Ca_over_Si_in_solar: 1.0 # (Optional) Value of the Calcium mass abundance ratio to solar in units of the Silicon ratio to solar. Default value: 1. + S_over_Si_in_solar: 1.0 # (Optional) Value of the Sulphur mass abundance ratio to solar in units of the Silicon ratio to solar. Default value: 1. + newton_integration: 0 # (Optional) Set to 1 to use the Newton-Raphson scheme for the explicit cooling problem. + +.. _EAGLE_tracers: + +Particle tracers +~~~~~~~~~~~~~~~~ + +Over the course of the simulation, the gas particles record some information +about their evolution. These are updated for a given particle every time it is +active. The EAGLE tracers module is located in the directory +``src/tracers/EAGLE/``. + +In the EAGLE model, we trace the maximal tempearature a particle has reached and +the time at which this happened. When a star is formed (see the section +:ref:`EAGLE_star_formation` below), it inherits all the tracer values of its parent +gas particle. There are no parameters to the model but two values are added to +the snapshots for each gas and star particle: + ++----------------------------------------+---------------------------------------+-----------+-----------------------------+ +| Name | Description | Units | Comments | ++========================================+=======================================+===========+=============================+ +| | ``Maximal Temperature`` | | Mximal temperature reached by | | [U_T] | | +| | | this particle. | | | ++----------------------------------------+---------------------------------------+-----------+-----------------------------+ +| | ``Maximal Temperature scale-factor`` | | Scale-factor (cosmological runs) | | [-] | | +| | OR | | or time (non-cosmological runs) at | | OR | | +| | ``Maximal Temperature time`` | | which the maximum value was reached.| | [U_t] | | ++----------------------------------------+---------------------------------------+-----------+-----------------------------+ + + +.. _EAGLE_star_formation: + +Star formation: Schaye+2008 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _EAGLE_enrichment: + +Stellar enrichment: Wiersma+2009b +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _EAGLE_feedback: + +Supernova feedback: Dalla Vecchia+2012 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _EAGLE_black_hole_seeding: + +Black-hole creation +~~~~~~~~~~~~~~~~~~~ + +.. _EAGLE_black_hole_accretion: + +Black-hole accretion +~~~~~~~~~~~~~~~~~~~~ + +.. _EAGLE_black_hole_feedback: + +AGN feedback +~~~~~~~~~~~~ + +.. [#f1] Recall that in a non-cosmological run the critical density is + set to 0, effectively removing the over-density + constraint of the floors. + +.. [#f2] `Wiersma et al. (2009)b + <http://adsabs.harvard.edu/abs/2009MNRAS.399..574W>`_ originally also + followed explicitly `Ca` and and `S`. They are omitted in the EAGLE + model but, when needed, their abundance with respect to solar is + assumed to be the same as the abundance of `Si` with respect to solar + (See the section :ref:`EAGLE_cooling`) + + diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py new file mode 100644 index 0000000000000000000000000000000000000000..5b02dd657fdb32e411961f04d0d758119344b809 --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py @@ -0,0 +1,60 @@ +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (3.15, 3.15), + "figure.subplot.left": 0.15, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.13, + "figure.subplot.top": 0.99, + "figure.subplot.wspace": 0.15, + "figure.subplot.hspace": 0.12, + "lines.markersize": 6, + "lines.linewidth": 2.0, + "text.latex.unicode": True, +} +rcParams.update(params) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +# Equations of state +eos_cool_rho = np.logspace(-5, 5, 1000) +eos_cool_T = eos_cool_rho**0. * 8000. +eos_Jeans_rho = np.logspace(-1, 5, 1000) +eos_Jeans_T = (eos_Jeans_rho/ 10**(-1))**(1./3.) * 4000. + +# Plot the phase space diagram +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_cool_rho, eos_cool_T, 'k-', lw=1.) +plot(eos_Jeans_rho, eos_Jeans_T, 'k-', lw=1.) +plot([1e-10, 1e-5], [8000, 8000], 'k:', lw=0.6) +plot([1e-10, 1e-1], [4000, 4000], 'k:', lw=0.6) +plot([1e-5, 1e-5], [20, 8000], 'k:', lw=0.6) +plot([1e-1, 1e-1], [20, 4000], 'k:', lw=0.6) +plot([3e-6, 3e-4], [28000, 28000], 'k--', lw=0.6) +text(3e-6, 22500, "$n_{\\rm H}~\\widehat{}~{\\tt Cool\\_gamma\\_effective}$", va="top", fontsize=7) +plot([3e-1, 3e1], [15000., 15000.*10.**(2./3.)], 'k--', lw=0.6) +text(3e-1, 200000, "$n_{\\rm H}~\\widehat{}~{\\tt Jeans\\_gamma\\_effective}$", va="top", rotation=43, fontsize=7) +text(0.95e-5, 25, "${\\tt Cool\\_density\\_threshold\\_H\\_p\\_cm3}$", rotation=90, va="bottom", ha="right", fontsize=7) +text(0.95e-1, 25, "${\\tt Jeans\\_density\\_threshold\\_H\\_p\\_cm3}$", rotation=90, va="bottom", ha="right", fontsize=7) +text(5e-8, 8800, "${\\tt Cool\\_temperature\\_norm\\_K}$", va="bottom", fontsize=7) +text(5e-8, 4400, "${\\tt Jeans\\_temperature\\_norm\\_K}$", va="bottom", fontsize=7) +fill_between([1e-5, 1e5], [10, 10], [8000, 8000], color='0.9') +fill_between([1e-1, 1e5], [4000, 400000], color='0.9') +scatter([1e-5], [8000], s=4, color='k') +scatter([1e-1], [4000], s=4, color='k') +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(3e-8, 3e3) +ylim(20., 2e5) +savefig("EAGLE_entropy_floor.png", dpi=200) diff --git a/doc/RTD/source/SubgridModels/GEAR/index.rst b/doc/RTD/source/SubgridModels/GEAR/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..2a211759bfc4895fd07279b72f78200d6ea47546 --- /dev/null +++ b/doc/RTD/source/SubgridModels/GEAR/index.rst @@ -0,0 +1,37 @@ +.. GEAR sub-grid model + Matthieu Schaller, 20th December 2018 + + +GEAR model +=========== + + +Cooling: Grackle +~~~~~~~~~~~~~~~~ + +Grackle is a chemistry and cooling library presented in `B. Smith et al. 2016 <https://arxiv.org/abs/1610.09591>`_ +(do not forget to cite if used). Four different modes are available: +equilibrium, 6 species network (H, H\\( ^+ \\), e\\( ^- \\), He, He\\( ^+ \\) +and He\\( ^{++} \\)), 9 species network (adds H\\(^-\\), H\\(_2\\) and +H\\(_2^+\\)) and 12 species (adds D, D\\(^+\\) and HD). Following the same +order, the swift cooling options are ``grackle``, ``grackle1``, ``grackle2`` +and ``grackle3`` (the numbers correspond to the value of +``primordial_chemistry`` in Grackle). It also includes some self-shielding +methods and UV background. In order to use the Grackle cooling, you will need +to provide an HDF5 table computed by Cloudy. + +When starting a simulation without providing the different fractions, the code +supposes an equilibrium and computes the fractions automatically. + +In order to compile SWIFT with Grackle, you need to provide the options ``with-grackle`` +and ``with-chemistry``. + +You will need a Grackle version later than 3.1. To compile it, run +the following commands from the root directory of Grackle: +``./configure; cd src/clib``. +Update the variables ``LOCAL_HDF5_INSTALL`` and ``MACH_INSTALL_PREFIX`` in +the file ``src/clib/Make.mach.linux-gnu``. +Finish with ``make machine-linux-gnu; make && make install``. +If you encounter any problem, you can look at the `Grackle documentation <https://grackle.readthedocs.io/en/latest/>`_ + +You can now provide the path given for ``MACH_INSTALL_PREFIX`` to ``with-grackle``. diff --git a/doc/RTD/source/SubgridModels/index.rst b/doc/RTD/source/SubgridModels/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..c1b8bc858c527f0ec6834e8de163306dd1be66cc --- /dev/null +++ b/doc/RTD/source/SubgridModels/index.rst @@ -0,0 +1,18 @@ +.. Subgrid Models + Matthieu Schaller, 20th December 2018 + +.. _subgrid: + +Galaxy Formation Subgrid Models +=============================== + +Multiple models are available in SWIFT. The 'Basic' model can +be use as an empty canvas to be copied to create additional models. + +.. toctree:: + :maxdepth: 2 + :caption: Available models: + + Basic/index + EAGLE/index + GEAR/index diff --git a/doc/RTD/source/Task/index.rst b/doc/RTD/source/Task/index.rst index e701e924a79f9256d4c0b034b6c15651f41ff405..82210895618618f87faaada472c72afe321b1d04 100644 --- a/doc/RTD/source/Task/index.rst +++ b/doc/RTD/source/Task/index.rst @@ -9,10 +9,12 @@ Task System This section of the documentation includes information on the task system available in SWIFT, as well as how to implement your own task. -SWIFT produces at the beginning of each simulation a ``dot`` file (see the graphviz library for more information). -It contains the full hierarchy of tasks used in this simulation. +SWIFT can produce a graph containing all the dependencies using graphviz. +At the beginning of each simulation a ``csv`` file is generated and can be transformed into a ``png`` with the script ``tools/plot_task_dependencies.py``. +This script has also the possibility to generate a list of function calls for each task with the option ``--with-calls``. You can convert the ``dot`` file into a ``png`` with the following command -``dot -Tpng dependency_graph.dot -o dependency_graph.png``. +``dot -Tpng dependency_graph.dot -o dependency_graph.png`` or directly read it with the python module ``xdot`` with ``python -m xdot dependency_graph.dot``. + .. toctree:: :maxdepth: 2 diff --git a/doc/RTD/source/VELOCIraptorInterface/index.rst b/doc/RTD/source/VELOCIraptorInterface/index.rst index 3ae17f04b9950d30d1484ac5117b65063b7739c6..312b9cd3f893dd44f814ad80ac40748db12bd4d5 100644 --- a/doc/RTD/source/VELOCIraptorInterface/index.rst +++ b/doc/RTD/source/VELOCIraptorInterface/index.rst @@ -5,7 +5,7 @@ VELOCIraptor Interface ====================== This section includes information on the VELOCIraptor interface implemented in -SWIFT. There are mainly four subsection; the first section explains shortly +SWIFT. There are mainly four subsections; the first section explains shortly how VELOCIraptor works, the second subsection explains how to configure SWIFT with VELOCIraptor, the third subsection explains how to configure a standalone version of VELOCIraptor and the last subsection explains how the output format diff --git a/doc/RTD/source/VELOCIraptorInterface/output.rst b/doc/RTD/source/VELOCIraptorInterface/output.rst index cf0e386d4a7de982dcca5a2ddc5ebd642c46ec34..946841fbece0207430846725b6a995cbc3f12613 100644 --- a/doc/RTD/source/VELOCIraptorInterface/output.rst +++ b/doc/RTD/source/VELOCIraptorInterface/output.rst @@ -46,7 +46,7 @@ The second file that is produced by VELOCIraptor is the ``.catalog_particles`` file, this file contains mainly all the IDs of the particles and has two interesting parameters: -+ The ``Num_of_particles_in_groups`` and ``Num_of_particles_in_groups`` ++ The ``Num_of_particles_in_groups`` and ``Total_num_of_particles_in_all_groups`` parameter: Gives the total number of particles in the file or the total number of particles that are in halos. + The ``Particle_IDs``: The list of particles as sorted by halo, in which halo @@ -67,7 +67,7 @@ wish to extract. The python snippet below should give you an idea of how to go about doing this for the bound particles. First, we need to extract the offset from the ``.catalog_group`` file, and -work out how many _bound_ partices are in our halo. We can do this by +work out how many _bound_ particles are in our halo. We can do this by looking at the next offset. Then, we can ID match those with the snapshot file, and get the mask for the _positions_ in the file that correspond to our bound particles. (Note this requires ``numpy > 1.15.0``). @@ -97,7 +97,7 @@ to our bound particles. (Note this requires ``numpy > 1.15.0``). # Again, we're done with that file. particles_file.close() - # Now, the tricky bit. We need to create the correspondance between the + # Now, the tricky bit. We need to create the correspondence between the # positions in the snapshot file, and the ids. # Let's look for the dark matter particles in that halo. @@ -136,7 +136,7 @@ the unbound particles. Properties file --------------- -The Fourth file is the ``.properties`` file, this file contains many physical +The fourth file is the ``.properties`` file, this file contains many physical useful information of the corresponding halos. This can be divided in several useful groups of physical parameters, on this page we have divided the several variables which are present in the ``.properties`` file. This file has most @@ -166,7 +166,7 @@ Mean Density related: :math:`\Delta=200` based on the mean density of the Universe (:math:`M_{200}`). + ``R_200mean``: The :math:`R_{200}` radius of the halo based on the - mean density ofthe Universe. + mean density of the Universe. Virial properties: """""""""""""""""" @@ -179,7 +179,7 @@ Bryan and Norman 1998 properties: + ``Mass_BN98``, The Bryan and Norman (1998) determination of the mass of the halo [#BN98]_. -+ ``R_BN98``, the Bryan and Norman (1998) corresponding radius[#BN98]_. ++ ``R_BN98``, the Bryan and Norman (1998) corresponding radius [#BN98]_. Several Mass types: """"""""""""""""""" @@ -190,7 +190,7 @@ properties. + ``M_gas``: The gas mass in the halo. + ``Mass_tot``: The total mass of the halo + ``M_gas_30kpc``: The gas mass within 30 kpc of the halo centre. -+ ``M_gas_500c``: The gas mass of the overdensity of 500 times the critical ++ ``M_gas_500c``: The gas mass of the over-density of 500 times the critical density + ``M_gas_Rvmax``: The gas mass within the maximum rotation velocity. @@ -232,7 +232,7 @@ NFW profile properties: + ``VXc_gas``, ``VYc_gas`` and ``VZc_gas`` are the velocities of the gas in the centre of the halo [#check]_. -Intertia Tensor properties: +Inertia Tensor properties: """"""""""""""""""""""""""" + ``eig_ij``: Are the normalized eigenvectors of the inertia tensor. diff --git a/doc/RTD/source/VELOCIraptorInterface/stfalone.rst b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst index 113cff53a51e446d321f6a912222c565f2bdb38e..e6bd0a72b207d7ca54bae67283326e7dcff51c02 100644 --- a/doc/RTD/source/VELOCIraptorInterface/stfalone.rst +++ b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst @@ -23,36 +23,28 @@ git repository as:: git clone https://github.com/pelahi/VELOCIraptor-STF Similar to the SWIFT with VELOCIraptor configuration, we can use the -swift-interface branch to analyse individual snapshots. We can use this branch +master to analyse individual snapshots. We can use this branch by doing:: cd VELOCIraptor-STF git fetch - git checkout swift-interface -Again we need to copy the default SWIFT config file to a other config file by -doing:: +Again we need to configure VELOCIraptor:: - cd stf - cp Makefile.config.SWIFT-template Makefile.config + cmake . -DVR_USE_GAS=ON -Similar to configuring VELOCIraptor with swift we need to change the first 20 -lines of ``Makefile.config`` to work with our compiler, but we also need to -change the fact that we do not use the swift-interface but the standalone -version of the code, so change ``SWIFTINTERFACE="on"`` to -``SWIFTINTERFACE="off"``. +In this case, we do not need the SWIFT interface, therefore we can drop +this option (disabled by default). Compiling VELOCIraptor ---------------------- -Compoling goes completely different as compared to the on the fly halo finder +Compiling goes completely different as compared to the on the fly halo finder configuration with SWIFT. In this case we can compile the code as:: make -After this an additional folder is created in ``VELOCIraptor-stf/stf`` called -``bin``, in which the binary files of ``stf-gas`` is present (assuming you -run a simulation with SPH [#nosph]_) +After this an executable is created (``VELOCIraptor-stf/stf``). Running VELOCIraptor on a Snapshot ---------------------------------- @@ -61,10 +53,12 @@ After the code is compile the next step is using VELOCIraptor on a single snapshot of a simulation. The code has several options which can be used, which can be displayed by running a terminal command of an invalid letter like:: - ./stf-gas -h + ./stf -h which gives the information about the usage of the command:: + VELOCIraptor/STF running with MPI. Number of mpi threads: 1 + VELOCIraptor/STF running with OpenMP. Number of openmp threads: 8 USAGE: -C <configuration file (overrides other options)> @@ -80,13 +74,6 @@ which gives the information about the usage of the command:: ===== EXTRA OPTIONS REQUIRED FOR RAMSES INPUT ====== -t <ramses snapnumber> -After this we can run a VELOCIraptor on a snapshot as:: +After this we can run VELOCIraptor on a snapshot as:: - ./stf-gas -i input -o output -C configfile.txt - - -.. [#nosph] In the case that in the ``Makefile.config`` it is indicate that the - simulation does only contain dark matter this will reflect back on the - generated binary file. So ``stf-gas`` will change to ``stf`` in the case of - a dark matter only simulation. - + ./stf -i input -o output -C configfile.txt diff --git a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst index ac8dcc5b0b3c4b6b77dbd5dc79be2613eae0edc6..ed261b76abbcefaf5643a69069bb4b8ea1a0894c 100644 --- a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst +++ b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst @@ -22,23 +22,17 @@ VELOCIraptor. This can be done by cloning the repository on GitHub_:: git clone https://github.com/pelahi/VELOCIraptor-STF -Currently the best version that works with SWIFT is the swift-interface branch +Currently the best version that works with SWIFT is the master of VELOCIraptor, to get this branch use:: cd VELOCIraptor-STF git fetch - git checkout swift-interface -To get the default that works with SWIFT simply copy the SWIFT template file in -the ``Makefile.config``:: +To get VELOCIraptor working with SWIFT simply use:: - cd stf - cp Makefile.config.SWIFT-template Makefile.config - -Depending on your compiler you want to change the first 20 lines of your -``Makefile.config`` to work with your compiler and whether you want to use MPI -or not. + cmake . -DVR_USE_SWIFT_INTERFACE=ON -DCMAKE_CXX_FLAGS="-fPIC" -DVR_USE_GAS=ON +If you wish to run swift without MPI, you will need to add ``-DVR_MPI=OFF``. Compiling VELOCIraptor ---------------------- @@ -46,38 +40,38 @@ Compiling VELOCIraptor After we downloaded the files and made a configuration file we can compile VELOCIraptor as follows:: - make lib - make libstf + make -j 4 -After the compilation of your code, there is an additional folder created in -the ``VELOCIraptor-stf/stf`` directory called ``lib`` this directory has the -library of VELOCIraptor and is required to run SWIFT with -VELOCIraptor. Note that VELOCIraptor needs a serial version of the +After the compilation of your code, you will find a static library ``libvelociraptor.a``, +that is required to run SWIFT with VELOCIraptor. +Note that VELOCIraptor needs a serial version of the HDF5 library, not a parallel build. Compiling SWIFT --------------- The next part is compiling SWIFT with VELOCIraptor and assumes you already -downloaded SWIFT from the GitLab_, this can be done by running:: +downloaded SWIFT from the GitLab_, this can be done by running +.. code:: bash + ./autogen.sh - ./configure --with-velociraptor=/path/to/VELOCIraptor-STF/stf/lib + ./configure --with-velociraptor=/path/to/VELOCIraptor-STF/src make In which ``./autogen.sh`` only needs to be run once after the code is cloned from the GitLab_, and ``/path/to/`` is the path to the ``VELOCIraptor-STF`` directory on your machine. In general ``./configure`` can be run with other options as desired. After this we can run SWIFT with VELOCIraptor, but for this -we first need to add several lines to the yaml file of our simulation:: +we first need to add several lines to the yaml file of our simulation - #structure finding options - StructureFinding: - config_file_name: stf_input_6dfof_dmonly_sub.cfg - basename: ./stf - output_time_format: 1 - scale_factor_first: 0.02 - delta_time: 1.02 +.. code:: YAML + + StructureFinding: + config_file_name: stf_input_6dfof_dmonly_sub.cfg + basename: ./stf + scale_factor_first: 0.02 + delta_time: 1.02 In which we specify the ``.cfg`` file that is used by VELOCIraptor and the other parameters which SWIFT needs to use. In the case of @@ -85,10 +79,9 @@ the Small Cosmological Volume DMO example we can run a simulation with halo finder as:: cd examples/SmallCosmoVolume_DM - ../swift -c -s -G -x -t 8 small_cosmo_volume_dm.yml + ../swift --cosmology --hydro --self-gravity --velociraptor --threads=8 small_cosmo_volume_dm.yml -In which there is an additional ``-x`` option which activates the VELOCIraptor -interface. +Which activates the VELOCIraptor interface. .. _GitHub: https://github.com/pelahi/VELOCIraptor-STF diff --git a/doc/RTD/source/VELOCIraptorInterface/whatis.rst b/doc/RTD/source/VELOCIraptorInterface/whatis.rst index e7f067ec4723e41da0f3a95dddad8f55d9897e85..a0a2a7441c52c188cc603910b43c112b3e24029e 100644 --- a/doc/RTD/source/VELOCIraptorInterface/whatis.rst +++ b/doc/RTD/source/VELOCIraptorInterface/whatis.rst @@ -12,7 +12,7 @@ What is VELOCIraptor? In SWIFT it is possible to run a cosmological simulation and at the same time do on the fly halo finding at specific predefined intervals. For finding the -Halos SWIFT uses VELOCIraptor (Elahi, Thacker and Widrow; 2011) [#velo]_, this +halos SWIFT uses VELOCIraptor (Elahi, Thacker and Widrow; 2011) [#velo]_, this is a C++ halo finder that can use MPI. It differs from other halo finder algorithms in the sense that it uses the velocity distributions of the particles in the simulations and the the positions of the particles to get @@ -22,7 +22,7 @@ whether there are substructures in halos. The Algorithm ------------- -The VELOCIraptor algorithm consist basically off the following steps [#ref]_: +The VELOCIraptor algorithm consists basically of the following steps [#ref]_: 1. A kd-tree is constructed based on the maximization of the Shannon-entropy, this means that every level in the kd-tree an equal number of particles @@ -32,21 +32,21 @@ The VELOCIraptor algorithm consist basically off the following steps [#ref]_: takes into account the absolute positions of the particles. 2. The next part is calculating the the centre of mass velocity and the velocity distribution for every individual node in the kd-tree. -3. Than the algorithm estimates the background velocity density function for +3. Then the algorithm estimates the background velocity density function for every particle based on the cell of the particle and the six nearest neighbour cells. This prevents the background velocity density function to be over sensitive for variations between different cells due to dominant halo features in the velocity density function. 4. After this the algorithm searches for the nearest velocity neighbours (:math:`N_v`) from a set of nearest position neighbours (:math:`N_x>N_v`). - The position neighbours do not need to be in the cell of the particles, in + The neighbours' positions do not need to be in the cell of the particles, in general the set of nearest position neighbours is substantially larger than the nearest velocity neighbours, the default is set as :math:`N_x=32 N_v`. 5. The individual local velocity density function is calculated for every particle. 6. The fractional difference is calculated between the local velocity density function and the background velocity density function. -7. Based on the calculated ratio outliers are picked and the outliers are +7. Based on the calculated ratio, outliers are picked and the outliers are grouped together in halos and subhalos. @@ -55,7 +55,7 @@ The VELOCIraptor algorithm consist basically off the following steps [#ref]_: .. 1. The algorithm is mostly sensitive to substructures that are on the tail of the Gaussian velocity density function, this means that VELOCIraptor - is most sensitive for subhalos which are cold (slow ratating) but have + is most sensitive for subhalos which are cold (slow rotating) but have a large bulk velocity diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py index 031687ea5228252e2d2e44ec0bd6f53b1b64d732..fac755bbb4ee9cd25bf3526bc435c69be3a9d5b5 100644 --- a/doc/RTD/source/conf.py +++ b/doc/RTD/source/conf.py @@ -18,14 +18,14 @@ # -- Project information ----------------------------------------------------- -project = 'SWIFT: SPH WIth Fine-grained inter-dependent Tasking' +project = 'SWIFT: SPH With Inter-dependent Fine-grained Tasking' copyright = '2018, SWIFT Collaboration' author = 'SWIFT Team' # The short X.Y version -version = '0.7' +version = '0.8' # The full version, including alpha/beta/rc tags -release = '0.7.0' +release = '0.8.0' # -- General configuration --------------------------------------------------- @@ -87,7 +87,7 @@ html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['.static'] +# html_static_path = ['.static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst index d148398c1bd77eafbce5e0037457b34efddb4eca..83422b4e5caf05bacb3824d06426b9cdeba3921e 100644 --- a/doc/RTD/source/index.rst +++ b/doc/RTD/source/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to SWIFT: SPH WIth Fine-grained inter-dependent Tasking's documentation! +Welcome to SWIFT: SPH With Inter-dependent Fine-grained Tasking's documentation! ================================================================================ Want to get started using SWIFT? Check out the on-boarding guide available @@ -18,8 +18,9 @@ difference is the parameter file that will need to be adapted for SWIFT. CommandLineOptions/index ParameterFiles/index InitialConditions/index + Snapshots/index HydroSchemes/index - Cooling/index + SubgridModels/index EquationOfState/index ExternalPotentials/index NewOption/index diff --git a/examples/CoolingBox/coolingBox.yml b/examples/Cooling/CoolingBox/coolingBox.yml similarity index 80% rename from examples/CoolingBox/coolingBox.yml rename to examples/Cooling/CoolingBox/coolingBox.yml index 1a06168cef739f33301a113cf1247b52cd3e2129..97b452e5ea376ab10bba155e0ff3e9acb8ed02bd 100644 --- a/examples/CoolingBox/coolingBox.yml +++ b/examples/Cooling/CoolingBox/coolingBox.yml @@ -52,19 +52,24 @@ GrackleCooling: MaxSteps: 1000 ConvergenceLimit: 1e-2 -EAGLEChemistry: - InitMetallicity: 0. - InitAbundance_Hydrogen: 0.752 - InitAbundance_Helium: 0.248 - InitAbundance_Carbon: 0.000 - InitAbundance_Nitrogen: 0.000 - InitAbundance_Oxygen: 0.000 - InitAbundance_Neon: 0.000 - InitAbundance_Magnesium: 0.000 - InitAbundance_Silicon: 0.000 - InitAbundance_Iron: 0.000 - CalciumOverSilicon: 0.0941736 - SulphurOverSilicon: 0.6054160 +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_center: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 GearChemistry: InitialMetallicity: 0.01295 diff --git a/examples/CoolingBox/energy_plot.py b/examples/Cooling/CoolingBox/energy_plot.py similarity index 100% rename from examples/CoolingBox/energy_plot.py rename to examples/Cooling/CoolingBox/energy_plot.py diff --git a/examples/CoolingBox/getGlass.sh b/examples/Cooling/CoolingBox/getGlass.sh similarity index 100% rename from examples/CoolingBox/getGlass.sh rename to examples/Cooling/CoolingBox/getGlass.sh diff --git a/examples/CoolingBox/makeIC.py b/examples/Cooling/CoolingBox/makeIC.py similarity index 100% rename from examples/CoolingBox/makeIC.py rename to examples/Cooling/CoolingBox/makeIC.py diff --git a/examples/CoolingBox/run.sh b/examples/Cooling/CoolingBox/run.sh similarity index 88% rename from examples/CoolingBox/run.sh rename to examples/Cooling/CoolingBox/run.sh index 30b2177a6e8bb95a20146397f8b6a5021161b27f..8a0d717e49324cdc49301fa0c551d5b7da198d5e 100755 --- a/examples/CoolingBox/run.sh +++ b/examples/Cooling/CoolingBox/run.sh @@ -21,7 +21,7 @@ then fi # Run SWIFT -../swift -s -C -t 1 coolingBox.yml +../../swift --cosmology --hydro --cooling --threads=4 -n 1000 coolingBox.yml # Check energy conservation and cooling rate python energy_plot.py diff --git a/examples/CoolingHalo/README b/examples/Cooling/CoolingHalo/README similarity index 100% rename from examples/CoolingHalo/README rename to examples/Cooling/CoolingHalo/README diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/Cooling/CoolingHalo/cooling_halo.yml similarity index 100% rename from examples/CoolingHalo/cooling_halo.yml rename to examples/Cooling/CoolingHalo/cooling_halo.yml diff --git a/examples/CoolingHalo/density_profile.py b/examples/Cooling/CoolingHalo/density_profile.py similarity index 100% rename from examples/CoolingHalo/density_profile.py rename to examples/Cooling/CoolingHalo/density_profile.py diff --git a/examples/CoolingHalo/internal_energy_profile.py b/examples/Cooling/CoolingHalo/internal_energy_profile.py similarity index 100% rename from examples/CoolingHalo/internal_energy_profile.py rename to examples/Cooling/CoolingHalo/internal_energy_profile.py diff --git a/examples/CoolingHalo/makeIC.py b/examples/Cooling/CoolingHalo/makeIC.py similarity index 100% rename from examples/CoolingHalo/makeIC.py rename to examples/Cooling/CoolingHalo/makeIC.py diff --git a/examples/CoolingHalo/makeIC_random_box.py b/examples/Cooling/CoolingHalo/makeIC_random_box.py similarity index 100% rename from examples/CoolingHalo/makeIC_random_box.py rename to examples/Cooling/CoolingHalo/makeIC_random_box.py diff --git a/examples/CoolingHalo/run.sh b/examples/Cooling/CoolingHalo/run.sh similarity index 75% rename from examples/CoolingHalo/run.sh rename to examples/Cooling/CoolingHalo/run.sh index 60ceae649d183dce3a7e5019a1ff94ce7bc4f08d..ca086fc93d5c4c34567c4405d7f3670972bbde9d 100755 --- a/examples/CoolingHalo/run.sh +++ b/examples/Cooling/CoolingHalo/run.sh @@ -4,7 +4,7 @@ echo "Generating initial conditions for the isothermal potential box example..." python makeIC.py 10000 -../swift -g -s -C -t 16 cooling_halo.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --cooling --threads=16 cooling_halo.yml 2>&1 | tee output.log python radial_profile.py 2. 200 100 diff --git a/examples/CoolingHalo/test_energy_conservation.py b/examples/Cooling/CoolingHalo/test_energy_conservation.py similarity index 100% rename from examples/CoolingHalo/test_energy_conservation.py rename to examples/Cooling/CoolingHalo/test_energy_conservation.py diff --git a/examples/CoolingHalo/velocity_profile.py b/examples/Cooling/CoolingHalo/velocity_profile.py similarity index 100% rename from examples/CoolingHalo/velocity_profile.py rename to examples/Cooling/CoolingHalo/velocity_profile.py diff --git a/examples/CoolingHaloWithSpin/README b/examples/Cooling/CoolingHaloWithSpin/README similarity index 100% rename from examples/CoolingHaloWithSpin/README rename to examples/Cooling/CoolingHaloWithSpin/README diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml similarity index 100% rename from examples/CoolingHaloWithSpin/cooling_halo.yml rename to examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml diff --git a/examples/CoolingHaloWithSpin/density_profile.py b/examples/Cooling/CoolingHaloWithSpin/density_profile.py similarity index 100% rename from examples/CoolingHaloWithSpin/density_profile.py rename to examples/Cooling/CoolingHaloWithSpin/density_profile.py diff --git a/examples/CoolingHaloWithSpin/internal_energy_profile.py b/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py similarity index 100% rename from examples/CoolingHaloWithSpin/internal_energy_profile.py rename to examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py diff --git a/examples/CoolingHaloWithSpin/makeIC.py b/examples/Cooling/CoolingHaloWithSpin/makeIC.py similarity index 100% rename from examples/CoolingHaloWithSpin/makeIC.py rename to examples/Cooling/CoolingHaloWithSpin/makeIC.py diff --git a/examples/CoolingHaloWithSpin/makeIC_random_box.py b/examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py similarity index 100% rename from examples/CoolingHaloWithSpin/makeIC_random_box.py rename to examples/Cooling/CoolingHaloWithSpin/makeIC_random_box.py diff --git a/examples/CoolingHaloWithSpin/run.sh b/examples/Cooling/CoolingHaloWithSpin/run.sh similarity index 77% rename from examples/CoolingHaloWithSpin/run.sh rename to examples/Cooling/CoolingHaloWithSpin/run.sh index 131fbf3cb10d2014546683b5f43194840544fd55..17ec5251a1071e36413ba926d14a179c1d6ed36b 100755 --- a/examples/CoolingHaloWithSpin/run.sh +++ b/examples/Cooling/CoolingHaloWithSpin/run.sh @@ -5,7 +5,7 @@ echo "Generating initial conditions for the isothermal potential box example..." python makeIC.py 10000 # Run SWIFT with external potential, SPH and cooling -../swift -g -s -C -t 1 cooling_halo.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --cooling --threads=1 cooling_halo.yml 2>&1 | tee output.log # python radial_profile.py 10 diff --git a/examples/CoolingHaloWithSpin/test_energy_conservation.py b/examples/Cooling/CoolingHaloWithSpin/test_energy_conservation.py similarity index 100% rename from examples/CoolingHaloWithSpin/test_energy_conservation.py rename to examples/Cooling/CoolingHaloWithSpin/test_energy_conservation.py diff --git a/examples/CoolingHaloWithSpin/velocity_profile.py b/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py similarity index 100% rename from examples/CoolingHaloWithSpin/velocity_profile.py rename to examples/Cooling/CoolingHaloWithSpin/velocity_profile.py diff --git a/examples/Cooling/CoolingRates/Makefile.am b/examples/Cooling/CoolingRates/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..17bcfad7f6b831cdb5f7b222d0e2b6e2cacaed9f --- /dev/null +++ b/examples/Cooling/CoolingRates/Makefile.am @@ -0,0 +1,32 @@ +# tHIS FIle is part of SWIFT. +# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Add the source directory and the non-standard paths to the included library headers to CFLAGS +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) + +AM_LDFLAGS = $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) + +# Extra libraries. +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) + +# Programs. +bin_PROGRAMS = cooling_rates + +# Sources +cooling_rates_SOURCES = cooling_rates.c +cooling_rates_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) +cooling_rates_LDADD = ../../../src/.libs/libswiftsim.a $(EXTRA_LIBS) + diff --git a/examples/Cooling/CoolingRates/README b/examples/Cooling/CoolingRates/README new file mode 100644 index 0000000000000000000000000000000000000000..7ac84c0e0aa64dc75d54a520c9997750b4977a39 --- /dev/null +++ b/examples/Cooling/CoolingRates/README @@ -0,0 +1,19 @@ +This is a test that produces a plot of the contribution to the cooling +rate from each of the elements depending on internal energy, density +and redshift based on the EAGLE tables. To do so, the function in +src/cooling/EAGLE returning the cooling rate is run for multiple +values of the internal energy. The resulting cooling rates are written +to files and plotted with a python script (cooling_rates_plot.py). + +The test may be run by: +./getCoolingTables.sh +./cooling_rates -z X -d Y +python plot_cooling_rates.py + +where X is the redshift at which the cooling rates are evaluated and Y +is the base 10 logarithm of the hydrogen number density expressed in +cgs (i.e. cm^-3). Different metallicities may be specified in +testCooling.yml + +Running with -z 3 -d -4 should reproduce the Fig.4 of Wiersma+09 with +the added Compton cooling contribution. diff --git a/examples/Cooling/CoolingRates/cooling_rates.c b/examples/Cooling/CoolingRates/cooling_rates.c new file mode 100644 index 0000000000000000000000000000000000000000..f96701f4c5645818bc5c72d0c9b607ce29f1b597 --- /dev/null +++ b/examples/Cooling/CoolingRates/cooling_rates.c @@ -0,0 +1,314 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@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/>. + * + ******************************************************************************/ +#include "../config.h" + +/* Some standard headers. */ +#include <fenv.h> +#include <unistd.h> + +/* Local headers. */ +#include "swift.h" + +#if defined(COOLING_EAGLE) && defined(CHEMISTRY_EAGLE) && defined(GADGET2_SPH) +#include "cooling/EAGLE/cooling_rates.h" +#include "cooling/EAGLE/cooling_tables.h" + +/* Flag used for printing cooling rate contribution from each + * element. For testing only. Incremented by 1/(number of elements) + * until reaches 1 after which point append to files instead of + * writing new file. */ +static float print_cooling_rate_contribution_flag = 0; + +/** + * @brief Wrapper function used to calculate cooling rate and dLambda_du. + * Writes to file contribution from each element to cooling rate for testing + * purposes (this function is not used when running SWIFT). Table indices + * and offsets for redshift, hydrogen number density and helium fraction are + * passed in so as to compute them only once per particle. + * + * @param n_h_i Particle hydrogen number density index + * @param d_n_h Particle hydrogen number density offset + * @param He_i Particle helium fraction index + * @param d_He Particle helium fraction offset + * @param p Particle structure + * @param cooling #cooling_function_data structure + * @param cosmo #cosmology structure + * @param phys_const #phys_const structure + * @param abundance_ratio Ratio of element abundance to solar + */ +INLINE static double eagle_print_metal_cooling_rate( + int n_h_i, float d_n_h, int He_i, float d_He, const struct part *restrict p, + const struct xpart *restrict xp, + const struct cooling_function_data *restrict cooling, + const struct cosmology *restrict cosmo, const struct phys_const *phys_const, + float *abundance_ratio) { + + /* array to store contributions to cooling rates from each of the + * elements */ + double *element_lambda; + element_lambda = malloc((eagle_cooling_N_metal + 2) * sizeof(double)); + + /* Get the H and He mass fractions */ + const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + + /* convert Hydrogen mass fraction in Hydrogen number density */ + const double n_h = hydro_get_physical_density(p, cosmo) * XH / + phys_const->const_proton_mass * + cooling->number_density_to_cgs; + + /* cooling rate, derivative of cooling rate and internal energy */ + double lambda_net = 0.0; + double u = hydro_get_physical_internal_energy(p, xp, cosmo) * + cooling->internal_energy_to_cgs; + + /* Open files for writing contributions to cooling rate. Each element + * gets its own file. */ + char output_filename[32]; + FILE **output_file = malloc((eagle_cooling_N_metal + 2) * sizeof(FILE *)); + + /* Once this flag reaches 1 we stop overwriting and start appending. */ + print_cooling_rate_contribution_flag += 1.0 / (eagle_cooling_N_metal + 2); + + /* Loop over each element */ + for (int element = 0; element < eagle_cooling_N_metal + 2; element++) { + sprintf(output_filename, "%s%d%s", "cooling_element_", element, ".dat"); + if (print_cooling_rate_contribution_flag < 1) { + /* If this is the first time we're running this function, overwrite the + * output files */ + output_file[element] = fopen(output_filename, "w"); + print_cooling_rate_contribution_flag += 1.0 / (eagle_cooling_N_metal + 2); + } else { + /* append to existing files */ + output_file[element] = fopen(output_filename, "a"); + } + if (output_file == NULL) { + error("Error opening file!\n"); + } + } + + /* calculate cooling rates */ + for (int j = 0; j < eagle_cooling_N_metal + 2; j++) element_lambda[j] = 0.0; + lambda_net = eagle_metal_cooling_rate( + log10(u), cosmo->z, n_h, abundance_ratio, n_h_i, d_n_h, He_i, d_He, + cooling, /*dLambdaNet_du=*/NULL, element_lambda); + + /* write cooling rate contributions to their own files. */ + for (int j = 0; j < eagle_cooling_N_metal + 2; j++) { + fprintf(output_file[j], "%.5e\n", element_lambda[j]); + } + + for (int i = 0; i < eagle_cooling_N_metal + 2; i++) fclose(output_file[i]); + free(output_file); + free(element_lambda); + + return lambda_net; +} + +/** + * @brief Assign particle density and entropy corresponding to the + * hydrogen number density and internal energy specified. + * + * @param p Particle data structure + * @param cooling Cooling function data structure + * @param cosmo Cosmology data structure + * @param internal_const Physical constants data structure + * @param nh Hydrogen number density (cgs units) + * @param u Internal energy (cgs units) + */ +void set_quantities(struct part *restrict p, struct xpart *restrict xp, + const struct unit_system *restrict us, + const struct cooling_function_data *restrict cooling, + const struct cosmology *restrict cosmo, + const struct phys_const *restrict internal_const, float nh, + double u) { + + double hydrogen_number_density = + nh * pow(units_cgs_conversion_factor(us, UNIT_CONV_LENGTH), 3); + p->rho = hydrogen_number_density * internal_const->const_proton_mass / + p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + + float pressure = (u * cosmo->a * cosmo->a) * + cooling->internal_energy_from_cgs * p->rho * + (hydro_gamma_minus_one); + p->entropy = pressure * (pow(p->rho, -hydro_gamma)); + xp->entropy_full = p->entropy; +} + +/** + * @brief Produces contributions to cooling rates for different + * hydrogen number densities, from different metals, + * tests 1d and 4d table interpolations produce + * same results for cooling rate, dlambda/du and temperature. + */ +int main(int argc, char **argv) { + // Declare relevant structs + struct swift_params *params = malloc(sizeof(struct swift_params)); + struct unit_system us; + struct chemistry_global_data chem_data; + struct part p; + struct xpart xp; + struct phys_const internal_const; + struct cooling_function_data cooling; + struct cosmology cosmo; + const char *parametersFileName = "./cooling_rates.yml"; + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FP-exceptions */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + const int npts = 250; // number of values for the internal energy at which + // cooling rate is evaluated + + // Set some default values + float redshift = 0.0, log_10_nh = -1; + + // Read options + int param; + while ((param = getopt(argc, argv, "z:d:")) != -1) switch (param) { + case 'z': + // read redshift + redshift = atof(optarg); + break; + case 'd': + // read log10 of hydrogen number density + log_10_nh = atof(optarg); + break; + case '?': + if (optopt == 'z') + printf("Option -%c requires an argument.\n", optopt); + else + printf("Unknown option character `\\x%x'.\n", optopt); + error("invalid option(s) to cooling_rates"); + } + + // Read the parameter file + if (params == NULL) error("Error allocating memory for the parameter file."); + message("Reading runtime parameters from file '%s'", parametersFileName); + parser_read_file(parametersFileName, params); + + // Init units + units_init_from_params(&us, params, "InternalUnitSystem"); + phys_const_init(&us, params, &internal_const); + + // Init chemistry + chemistry_init(params, &us, &internal_const, &chem_data); + chemistry_first_init_part(&internal_const, &us, &cosmo, &chem_data, &p, &xp); + chemistry_print(&chem_data); + + // Init cosmology + cosmology_init(params, &us, &internal_const, &cosmo); + + // Set redshift and associated quantities + const float scale_factor = 1.0 / (1.0 + redshift); + integertime_t ti_current = + log(scale_factor / cosmo.a_begin) / cosmo.time_base; + cosmology_update(&cosmo, &internal_const, ti_current); + message("Redshift is %f", cosmo.z); + + // Init cooling + cooling_init(params, &us, &internal_const, &cooling); + cooling_print(&cooling); + cooling_update(&cosmo, &cooling); + + // Calculate abundance ratios + float abundance_ratio[(chemistry_element_count + 2)]; + abundance_ratio_to_solar(&p, &cooling, abundance_ratio); + + // extract mass fractions, calculate table indices and offsets + float XH = p.chemistry_data.metal_mass_fraction[chemistry_element_H]; + float HeFrac = + p.chemistry_data.metal_mass_fraction[chemistry_element_He] / + (XH + p.chemistry_data.metal_mass_fraction[chemistry_element_He]); + int He_i, n_h_i; + float d_He, d_n_h; + get_index_1d(cooling.HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_i, &d_He); + + // Calculate contributions from metals to cooling rate + // open file + FILE *output_file = fopen("cooling_output.dat", "w"); + if (output_file == NULL) { + error("Error opening output file!\n"); + } + + // set hydrogen number density + const float nh = exp(M_LN10 * log_10_nh); + + /* Initial internal energy */ + double u = 1.0e14; + + // set internal energy to dummy value, will get reset when looping over + // internal energies + set_quantities(&p, &xp, &us, &cooling, &cosmo, &internal_const, nh, u); + float inn_h = hydro_get_physical_density(&p, &cosmo) * XH / + internal_const.const_proton_mass * + cooling.number_density_to_cgs; + get_index_1d(cooling.nH, eagle_cooling_N_density, log10(inn_h), &n_h_i, + &d_n_h); + + // Loop over internal energy + for (int j = 0; j < npts; j++) { + + // Update the particle with the new values + set_quantities(&p, &xp, &us, &cooling, &cosmo, &internal_const, nh, + pow(10.0, 10.0 + j * 8.0 / npts)); + + // New internal energy + u = hydro_get_physical_internal_energy(&p, &xp, &cosmo) * + cooling.internal_energy_to_cgs; + + // calculate cooling rates + const double temperature = eagle_convert_u_to_temp( + log10(u), cosmo.z, 0, NULL, n_h_i, He_i, d_n_h, d_He, &cooling); + + const double cooling_du_dt = eagle_print_metal_cooling_rate( + n_h_i, d_n_h, He_i, d_He, &p, &xp, &cooling, &cosmo, &internal_const, + abundance_ratio); + + // Dump... + fprintf(output_file, "%.5e %.5e\n", exp(M_LN10 * temperature), + cooling_du_dt); + } + fclose(output_file); + message("done cooling rates test"); + + /* Clean everything */ + cosmology_clean(&cosmo); + cooling_clean(&cooling); + + free(params); + return 0; +} + +#else + +int main(int argc, char **argv) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + + message("This test is only defined for the EAGLE cooling model."); + return 0; +} +#endif diff --git a/examples/Cooling/CoolingRates/cooling_rates.yml b/examples/Cooling/CoolingRates/cooling_rates.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d67ad0af79368c39c24879b101a540d0bccb3d1 --- /dev/null +++ b/examples/Cooling/CoolingRates/cooling_rates.yml @@ -0,0 +1,36 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.989e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.085678e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.04 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +EAGLEChemistry: + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + diff --git a/examples/Cooling/CoolingRates/getCoolingTable.sh b/examples/Cooling/CoolingRates/getCoolingTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..5cfd93ef0f4603e40b7675f3f2c254b2250f699f --- /dev/null +++ b/examples/Cooling/CoolingRates/getCoolingTable.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/EAGLE/coolingtables.tar.gz +tar -xf coolingtables.tar.gz diff --git a/examples/Cooling/CoolingRates/plot_cooling_rates.py b/examples/Cooling/CoolingRates/plot_cooling_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..cca12468befd8c407d769ee00fcb03ecd52db3ec --- /dev/null +++ b/examples/Cooling/CoolingRates/plot_cooling_rates.py @@ -0,0 +1,53 @@ +# Plots contribution to cooling rates from each of the different metals +# based on cooling_output.dat and cooling_element_*.dat files produced +# by testCooling. + +import matplotlib.pyplot as plt +import numpy as np + +# Number of metals tracked by EAGLE cooling +elements = 11 + +# Declare arrays of internal energy and cooling rate +u = [] +cooling_rate = [[] for i in range(elements + 1)] +Temperature = [[] for i in range(elements + 1)] + +# Read in total cooling rate +file_in = open("cooling_output.dat", "r") +for line in file_in: + data = line.split() + u.append(float(data[0])) + cooling_rate[0].append(-float(data[1])) +file_in.close() + +# Read in contributions to cooling rates from each of the elements +for elem in range(elements): + file_in = open("cooling_element_" + str(elem) + ".dat", "r") + for line in file_in: + data = line.split() + cooling_rate[elem + 1].append(-float(data[0])) + file_in.close() + +# Plot +ax = plt.subplot(111) +p0, = plt.loglog(u, cooling_rate[0], linewidth=0.5, color="k", label="Total") +p1, = plt.loglog( + u, cooling_rate[1], linewidth=0.5, color="k", linestyle="--", label="H + He" +) +p2, = plt.loglog(u, cooling_rate[3], linewidth=0.5, color="b", label="C") +p3, = plt.loglog(u, cooling_rate[4], linewidth=0.5, color="g", label="N") +p4, = plt.loglog(u, cooling_rate[5], linewidth=0.5, color="r", label="O") +p5, = plt.loglog(u, cooling_rate[6], linewidth=0.5, color="c", label="Ne") +p6, = plt.loglog(u, cooling_rate[7], linewidth=0.5, color="m", label="Mg") +p7, = plt.loglog(u, cooling_rate[8], linewidth=0.5, color="y", label="Si") +p8, = plt.loglog(u, cooling_rate[9], linewidth=0.5, color="lightgray", label="S") +p9, = plt.loglog(u, cooling_rate[10], linewidth=0.5, color="olive", label="Ca") +p10, = plt.loglog(u, cooling_rate[11], linewidth=0.5, color="saddlebrown", label="Fe") +ax.set_position([0.15, 0.15, 0.75, 0.75]) +plt.xlim([1e3, 1e8]) +plt.ylim([1e-24, 1e-21]) +plt.xlabel("Temperature ${\\rm{[K]}}$", fontsize=14) +plt.ylabel("${\Lambda/n_H^2 }$ ${\\rm{[erg \cdot cm^3 \cdot s^{-1}]}}$", fontsize=14) +plt.legend(handles=[p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10]) +plt.savefig("cooling_rates", dpi=200) diff --git a/examples/getCoolingTable.sh b/examples/Cooling/getCoolingTable.sh similarity index 100% rename from examples/getCoolingTable.sh rename to examples/Cooling/getCoolingTable.sh diff --git a/examples/Cosmology/ComovingSodShock_1D/makeIC.py b/examples/Cosmology/ComovingSodShock_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..371acc685cd00bd211e024ecc17c976ea5a08f68 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_1D/makeIC.py @@ -0,0 +1,123 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the 1D Sod Shock in a periodic box + +unit_l_in_cgs = 3.086e18 +unit_m_in_cgs = 2.94e55 +unit_t_in_cgs = 3.086e18 + +# Parameters +gamma = 5./3. # Gas adiabatic index +numPart_L = 800 # Number of particles in the left state +x_min = -1. +x_max = 1. +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state +a_beg = 0.001 +fileName = "sodShock.hdf5" + + +#--------------------------------------------------- + +# Find how many particles we actually have +boxSize = x_max - x_min +numPart_R = int(numPart_L * (rho_R / rho_L)) +numPart = numPart_L + numPart_R + +# Now get the distances +delta_L = (boxSize/2) / numPart_L +delta_R = (boxSize/2) / numPart_R +offset_L = delta_L / 2 +offset_R = delta_R / 2 + +# Build the arrays +coords = zeros((numPart, 3)) +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +h = zeros(numPart) +u = zeros(numPart) + +# Set the particles on the left +for i in range(numPart_L): + coords[i,0] = x_min + offset_L + i * delta_L + u[i] = P_L / (rho_L * (gamma - 1.)) + h[i] = 1.2348 * delta_L + m[i] = boxSize * rho_L / (2. * numPart_L) + v[i,0] = v_L + +# Set the particles on the right +for j in range(numPart_R): + i = numPart_L + j + coords[i,0] = offset_R + j * delta_R + u[i] = P_R / (rho_R * (gamma - 1.)) + h[i] = 1.2348 * delta_R + m[i] = boxSize * rho_R / (2. * numPart_R) + v[i,0] = v_R + +# Shift particles +coords[:,0] -= x_min + +u /= (a_beg**(3. * (gamma - 1.))) + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=coords, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + + +file.close() + + diff --git a/examples/Cosmology/ComovingSodShock_1D/plotSolution.py b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..95674c04bfafd0cd549b69814df82f9a4f80a949 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_1D/plotSolution.py @@ -0,0 +1,310 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +# Computes the analytical solution of the Sod shock and plots the SPH answer + + +# Generates the analytical solution for the Sod shock test case +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("sodShock_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +anow = sim["/Header"].attrs["Scale-factor"] +a_i = sim["/Cosmology"].attrs["a_beg"] +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] +time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0 +scheme = str(sim["/HydroScheme"].attrs["Scheme"]) +kernel = str(sim["/HydroScheme"].attrs["Kernel function"]) +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = str(sim["Code"].attrs["Git Revision"]) + +x = sim["/PartType0/Coordinates"][:,0] +v = sim["/PartType0/Velocities"][:,0] * anow +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] +try: + alpha = sim["/PartType0/Viscosity"][:] + plot_alpha = True +except: + plot_alpha = False + +N = 1000 # Number of points +x_min = -1. +x_max = 1. + +x += x_min + +# --------------------------------------------------------------- +# Don't touch anything after this. +# --------------------------------------------------------------- + +c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave +c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front + +# Helpful variable +Gama = (gas_gamma - 1.) / (gas_gamma + 1.) +beta = (gas_gamma - 1.) / (2. * gas_gamma) + +# Characteristic function and its derivative, following Toro (2009) +def compute_f(P_3, P, c): + u = P_3 / P + if u > 1: + term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.) + term2 = sqrt(2./term1) + fp = (u - 1.)*c*term2 + dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P + else: + fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.)) + dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P + return (fp, dfdp) + +# Solution of the Riemann problem following Toro (2009) +def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): + P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta) + P_3 = 0.5*(P_R + P_L) + f_L = 1. + while fabs(P_3 - P_new) > 1e-6: + P_3 = P_new + (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) + (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) + f = f_L + f_R + (v_R - v_L) + df = dfdp_L + dfdp_R + dp = -f/df + prnew = P_3 + dp + v_3 = v_L - f_L + return (P_new, v_3) + + +# Solve Riemann problem for post-shock region +(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) + +# Check direction of shocks and wave +shock_R = (P_3 > P_R) +shock_L = (P_3 > P_L) + +# Velocity of shock front and and rarefaction wave +if shock_R: + v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R)) +else: + v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R + +if shock_L: + v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L)) +else: + v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L + +# Compute position of the transitions +x_23 = -fabs(v_left) * time +if shock_L : + x_12 = -fabs(v_left) * time +else: + x_12 = -(c_L - v_L) * time + +x_34 = v_3 * time + +x_45 = fabs(v_right) * time +if shock_R: + x_56 = fabs(v_right) * time +else: + x_56 = (c_R + v_R) * time + + +# Prepare arrays +delta_x = (x_max - x_min) / N +x_s = arange(x_min, x_max, delta_x) +rho_s = zeros(N) +P_s = zeros(N) +v_s = zeros(N) + +# Compute solution in the different regions +for i in range(N): + if x_s[i] <= x_12: + rho_s[i] = rho_L + P_s[i] = P_L + v_s[i] = v_L + if x_s[i] >= x_12 and x_s[i] < x_23: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L) + P_s[i] = P_3 + v_s[i] = v_3 + else: + rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma + v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L + if x_s[i] >= x_23 and x_s[i] < x_34: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L) + else: + rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_34 and x_s[i] < x_45: + if shock_R: + rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R) + else: + rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_45 and x_s[i] < x_56: + if shock_R: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + else: + rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma + v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R + if x_s[i] >= x_56: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.)) #internal energy +s_s = P_s / rho_s**gas_gamma # entropic function + + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, '.', color='r', ms=4.0) +plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) +xlim(-0.5, 0.5) +ylim(-0.1, 0.95) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, '.', color='r', ms=4.0) +plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.05, 1.1) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, '.', color='r', ms=4.0) +plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.01, 1.1) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, '.', color='r', ms=4.0) +plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.8, 2.2) + +# Entropy/alpha profile --------------------------------- +subplot(235) + +if plot_alpha: + plot(x, alpha, '.', color='r', ms=4.0) + ylabel(r"${\rm{Viscosity}}~\alpha$", labelpad=0) + # Show location of shock + plot([x_56, x_56], [-100, 100], color="k", alpha=0.5, ls="dashed", lw=1.2) + ylim(0, 1) +else: + plot(x, S, '.', color='r', ms=4.0) + plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(0.8, 3.8) + +xlabel("${\\rm{Position}}~x$", labelpad=0) +xlim(-0.5, 0.5) + +# Information ------------------------------------- +subplot(236, frameon=False) + +z_now = 1. / anow - 1. +text(-0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 1D at $z=%.2f$"%(gas_gamma,z_now), fontsize=10) +text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10) +text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10) +z_i = 1. / a_i - 1. +text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize=10) +plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1) +text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.3, scheme, fontsize=10) +text(-0.49, 0.2, kernel, fontsize=10) +text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() + +savefig("SodShock.png", dpi=200) diff --git a/examples/Cosmology/ComovingSodShock_1D/run.sh b/examples/Cosmology/ComovingSodShock_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..2eae1729e007ac087654cb7b04d0701542cb4c75 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_1D/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e sodShock.hdf5 ] +then + echo "Generating initial conditions for the 1D SodShock example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --cosmology --hydro --threads=1 sodShock.yml 2>&1 | tee output.log + +# Plot the result +python plotSolution.py 1 diff --git a/examples/Cosmology/ComovingSodShock_1D/sodShock.yml b/examples/Cosmology/ComovingSodShock_1D/sodShock.yml new file mode 100644 index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_1D/sodShock.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 2.94e55 # Grams + UnitLength_in_cgs: 3.086e18 # pc + UnitVelocity_in_cgs: 1. # km per s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: sodShock # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1.06638 # Time difference between consecutive outputs (in internal units) + scale_factor_first: 0.001 + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sodShock.hdf5 # The file to read + periodic: 1 + +Cosmology: + Omega_m: 1. + Omega_lambda: 0. + Omega_b: 1. + h: 1. + a_begin: 0.001 + a_end: 0.00106638 + diff --git a/examples/SodShockSpherical_2D/getGlass.sh b/examples/Cosmology/ComovingSodShock_2D/getGlass.sh similarity index 100% rename from examples/SodShockSpherical_2D/getGlass.sh rename to examples/Cosmology/ComovingSodShock_2D/getGlass.sh diff --git a/examples/Cosmology/ComovingSodShock_2D/makeIC.py b/examples/Cosmology/ComovingSodShock_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..51a408866047534f86fbded071d604ec294ed0b7 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_2D/makeIC.py @@ -0,0 +1,127 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the 2D Sod Shock in a periodic box + +unit_l_in_cgs = 3.086e18 +unit_m_in_cgs = 2.94e55 +unit_t_in_cgs = 3.086e18 + +# Parameters +gamma = 5./3. # Gas adiabatic index +x_min = -1. +x_max = 1. +rho_L = 1. # Density left state +rho_R = 0.140625 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state +a_beg = 0.001 +fileName = "sodShock.hdf5" + + +#--------------------------------------------------- +boxSize = (x_max - x_min) + +glass_L = h5py.File("glassPlane_128.hdf5", "r") +glass_R = h5py.File("glassPlane_48.hdf5", "r") + +pos_L = glass_L["/PartType0/Coordinates"][:,:] * 0.5 +pos_R = glass_R["/PartType0/Coordinates"][:,:] * 0.5 +h_L = glass_L["/PartType0/SmoothingLength"][:] * 0.5 +h_R = glass_R["/PartType0/SmoothingLength"][:] * 0.5 + +# Merge things +aa = pos_L - array([0.5, 0., 0.]) +pos_LL = append(pos_L, pos_L + array([0.5, 0., 0.]), axis=0) +pos_RR = append(pos_R, pos_R + array([0.5, 0., 0.]), axis=0) +pos = append(pos_LL - array([1.0, 0., 0.]), pos_RR, axis=0) +h_LL = append(h_L, h_L) +h_RR = append(h_R, h_R) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = 0.5 +vol_R = 0.5 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = pos[i,0] + + if x < 0: #left + u[i] = P_L / (rho_L * (gamma - 1.)) + m[i] = rho_L * vol_L / numPart_L + v[i,0] = v_L + else: #right + u[i] = P_R / (rho_R * (gamma - 1.)) + m[i] = rho_R * vol_R / numPart_R + v[i,0] = v_R + +# Shift particles +pos[:,0] -= x_min + +u /= (a_beg**(3. * (gamma - 1.))) + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxSize, 0.5, 1.0] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 2 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + + +file.close() diff --git a/examples/Cosmology/ComovingSodShock_2D/plotSolution.py b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..8adb3cf5c550ab9724f6a8f34c1a1260a25712e1 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_2D/plotSolution.py @@ -0,0 +1,318 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +# Computes the analytical solution of the Sod shock and plots the SPH answer + + +# Generates the analytical solution for the Sod shock test case +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.140625 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("sodShock_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +anow = sim["/Header"].attrs["Scale-factor"] +a_i = sim["/Cosmology"].attrs["a_beg"] +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] +time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0 +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +x = sim["/PartType0/Coordinates"][:,0] +v = sim["/PartType0/Velocities"][:,0] * anow +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] + +N = 1000 # Number of points +x_min = -1. +x_max = 1. +x += x_min + + +# Bin te data +x_bin_edge = np.arange(-0.6, 0.6, 0.02) +x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1]) +rho_bin,_,_ = stats.binned_statistic(x, rho, statistic='mean', bins=x_bin_edge) +v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge) +P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge) +S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge) +u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge) +v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge) +P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge) +S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge) +u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + + +# Analytic solution +c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave +c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front + +# Helpful variable +Gama = (gas_gamma - 1.) / (gas_gamma + 1.) +beta = (gas_gamma - 1.) / (2. * gas_gamma) + +# Characteristic function and its derivative, following Toro (2009) +def compute_f(P_3, P, c): + u = P_3 / P + if u > 1: + term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.) + term2 = sqrt(2./term1) + fp = (u - 1.)*c*term2 + dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P + else: + fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.)) + dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P + return (fp, dfdp) + +# Solution of the Riemann problem following Toro (2009) +def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): + P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta) + P_3 = 0.5*(P_R + P_L) + f_L = 1. + while fabs(P_3 - P_new) > 1e-6: + P_3 = P_new + (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) + (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) + f = f_L + f_R + (v_R - v_L) + df = dfdp_L + dfdp_R + dp = -f/df + prnew = P_3 + dp + v_3 = v_L - f_L + return (P_new, v_3) + + +# Solve Riemann problem for post-shock region +(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) + +# Check direction of shocks and wave +shock_R = (P_3 > P_R) +shock_L = (P_3 > P_L) + +# Velocity of shock front and and rarefaction wave +if shock_R: + v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R)) +else: + v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R + +if shock_L: + v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L)) +else: + v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L + +# Compute position of the transitions +x_23 = -fabs(v_left) * time +if shock_L : + x_12 = -fabs(v_left) * time +else: + x_12 = -(c_L - v_L) * time + +x_34 = v_3 * time + +x_45 = fabs(v_right) * time +if shock_R: + x_56 = fabs(v_right) * time +else: + x_56 = (c_R + v_R) * time + + +# Prepare arrays +delta_x = (x_max - x_min) / N +x_s = arange(x_min, x_max, delta_x) +rho_s = zeros(N) +P_s = zeros(N) +v_s = zeros(N) + +# Compute solution in the different regions +for i in range(N): + if x_s[i] <= x_12: + rho_s[i] = rho_L + P_s[i] = P_L + v_s[i] = v_L + if x_s[i] >= x_12 and x_s[i] < x_23: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L) + P_s[i] = P_3 + v_s[i] = v_3 + else: + rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma + v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L + if x_s[i] >= x_23 and x_s[i] < x_34: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L) + else: + rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_34 and x_s[i] < x_45: + if shock_R: + rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R) + else: + rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_45 and x_s[i] < x_56: + if shock_R: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + else: + rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma + v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R + if x_s[i] >= x_56: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.)) #internal energy +s_s = P_s / rho_s**gas_gamma # entropic function + + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, '.', color='r', ms=0.2) +plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) +xlim(-0.5, 0.5) +ylim(-0.1, 0.95) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, '.', color='r', ms=0.2) +plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.05, 1.1) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, '.', color='r', ms=0.2) +plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.01, 1.1) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, '.', color='r', ms=0.2) +plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.8, 2.2) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, '.', color='r', ms=0.2) +plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.8, 3.8) + +# Information ------------------------------------- +subplot(236, frameon=False) + +z_now = 1. / anow - 1. +text(-0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 2D at $z=%.2f$"%(gas_gamma,z_now), fontsize=10) +text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10) +text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10) +z_i = 1. / a_i - 1. +text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize=10) +plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1) +text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.3, scheme, fontsize=10) +text(-0.49, 0.2, kernel, fontsize=10) +text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() +savefig("SodShock.png", dpi=200) diff --git a/examples/Cosmology/ComovingSodShock_2D/run.sh b/examples/Cosmology/ComovingSodShock_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..1723153e3eff1f7d49970ed4e3d5f69b39b67a1a --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_2D/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the Sod shock example..." + ./getGlass.sh +fi +if [ ! -e sodShock.hdf5 ] +then + echo "Generating initial conditions for the Sod shock example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log + +python plotSolution.py 1 diff --git a/examples/Cosmology/ComovingSodShock_2D/sodShock.yml b/examples/Cosmology/ComovingSodShock_2D/sodShock.yml new file mode 100644 index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_2D/sodShock.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 2.94e55 # Grams + UnitLength_in_cgs: 3.086e18 # pc + UnitVelocity_in_cgs: 1. # km per s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: sodShock # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1.06638 # Time difference between consecutive outputs (in internal units) + scale_factor_first: 0.001 + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sodShock.hdf5 # The file to read + periodic: 1 + +Cosmology: + Omega_m: 1. + Omega_lambda: 0. + Omega_b: 1. + h: 1. + a_begin: 0.001 + a_end: 0.00106638 + diff --git a/examples/Cosmology/ComovingSodShock_3D/README.txt b/examples/Cosmology/ComovingSodShock_3D/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..2b2f0d16207079f5d29e9ec281fe5255cc5886fb --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_3D/README.txt @@ -0,0 +1,20 @@ +Cosmological version of the standard Sod shock test. + +In the co-moving coordinates that SWIFT uses, the Euler equations of +hydrodynamics have an elegant form with a few additional factors that +involve the scale factor a. For the specific case of a polytropic index +gamma = 5/3, all additional factors are in fact the same: 1/a^2. For +this case, hydrodynamics in the co-moving frame is identical to +hydrodynamics in a physical non-cosmological frame with a rescaled time +variable dt'=a^2*dt. + +We choose an Einstein-de Sitter cosmology with H(a)=H_0*a^(3/2) and a +box size of 1 pc and rescale the Sod shock initial conditions so that +the internal coordinates, density and pressure values are still the same +as for the original Sod shock in the non-cosmological case. We then +evolve the initial condition from z=999 to z=960.5, which corresponds to +a rescaled time interval dt'~0.12. If the co-moving coordinates are +implemented correctly, the resulting co-moving density, internal +velocity and co-moving pressure profiles should match those for the +non-co-moving variables in the ordinary non-cosmological Sod shock at +t=0.12. diff --git a/examples/SodShockSpherical_3D/getGlass.sh b/examples/Cosmology/ComovingSodShock_3D/getGlass.sh similarity index 100% rename from examples/SodShockSpherical_3D/getGlass.sh rename to examples/Cosmology/ComovingSodShock_3D/getGlass.sh diff --git a/examples/Cosmology/ComovingSodShock_3D/makeIC.py b/examples/Cosmology/ComovingSodShock_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..b6528bc5ab3670d4423945d194fc537c1bb672a1 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_3D/makeIC.py @@ -0,0 +1,126 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the 3D Sod Shock in a periodic box + +unit_l_in_cgs = 3.086e18 +unit_m_in_cgs = 2.94e55 +unit_t_in_cgs = 3.086e18 + +# Parameters +gamma = 5./3. # Gas adiabatic index +x_min = -1. +x_max = 1. +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state +a_beg = 0.001 +fileName = "sodShock.hdf5" + + +#--------------------------------------------------- +boxSize = (x_max - x_min) + +glass_L = h5py.File("glassCube_64.hdf5", "r") +glass_R = h5py.File("glassCube_32.hdf5", "r") + +pos_L = glass_L["/PartType0/Coordinates"][:,:] * 0.5 +pos_R = glass_R["/PartType0/Coordinates"][:,:] * 0.5 +h_L = glass_L["/PartType0/SmoothingLength"][:] * 0.5 +h_R = glass_R["/PartType0/SmoothingLength"][:] * 0.5 + +# Merge things +aa = pos_L - array([0.5, 0., 0.]) +pos_LL = append(pos_L, pos_L + array([0.5, 0., 0.]), axis=0) +pos_RR = append(pos_R, pos_R + array([0.5, 0., 0.]), axis=0) +pos = append(pos_LL - array([1.0, 0., 0.]), pos_RR, axis=0) +h_LL = append(h_L, h_L) +h_RR = append(h_R, h_R) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = (0.25 * boxSize)**2 * (0.5 * boxSize) +vol_R = (0.25 * boxSize)**2 * (0.5 * boxSize) + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = pos[i,0] + + if x < 0: #left + u[i] = P_L / (rho_L * (gamma - 1.)) + m[i] = rho_L * vol_L / numPart_L + v[i,0] = v_L + else: #right + u[i] = P_R / (rho_R * (gamma - 1.)) + m[i] = rho_R * vol_R / numPart_R + v[i,0] = v_R + +# Shift particles +pos[:,0] -= x_min + +u /= (a_beg**(3. * (gamma - 1.))) + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxSize, 0.25 * boxSize, 0.25 * boxSize] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..f05a385e8620b18189d2e7abca8aebb8ae65060e --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py @@ -0,0 +1,316 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) + # + # 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/>. + # + ############################################################################## + +# Computes the analytical solution of the Sod shock and plots the SPH answer + + +# Generates the analytical solution for the Sod shock test case +# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t. +# This follows the solution given in (Toro, 2009) + + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("sodShock_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +anow = sim["/Header"].attrs["Scale-factor"] +a_i = sim["/Cosmology"].attrs["a_beg"] +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"] +time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0 +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +x = sim["/PartType0/Coordinates"][:,0] +v = sim["/PartType0/Velocities"][:,0] * anow +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] + +x_min = -1. +x_max = 1. +x += x_min +N = 1000 + +# Bin te data +x_bin_edge = np.arange(-0.6, 0.6, 0.02) +x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1]) +rho_bin,_,_ = stats.binned_statistic(x, rho, statistic='mean', bins=x_bin_edge) +v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge) +P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge) +S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge) +u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge) +v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge) +P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge) +S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge) +u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + + +# Analytic solution +c_L = sqrt(gas_gamma * P_L / rho_L) # Speed of the rarefaction wave +c_R = sqrt(gas_gamma * P_R / rho_R) # Speed of the shock front + +# Helpful variable +Gama = (gas_gamma - 1.) / (gas_gamma + 1.) +beta = (gas_gamma - 1.) / (2. * gas_gamma) + +# Characteristic function and its derivative, following Toro (2009) +def compute_f(P_3, P, c): + u = P_3 / P + if u > 1: + term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.) + term2 = sqrt(2./term1) + fp = (u - 1.)*c*term2 + dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P + else: + fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.)) + dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P + return (fp, dfdp) + +# Solution of the Riemann problem following Toro (2009) +def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R): + P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta) + P_3 = 0.5*(P_R + P_L) + f_L = 1. + while fabs(P_3 - P_new) > 1e-6: + P_3 = P_new + (f_L, dfdp_L) = compute_f(P_3, P_L, c_L) + (f_R, dfdp_R) = compute_f(P_3, P_R, c_R) + f = f_L + f_R + (v_R - v_L) + df = dfdp_L + dfdp_R + dp = -f/df + prnew = P_3 + dp + v_3 = v_L - f_L + return (P_new, v_3) + + +# Solve Riemann problem for post-shock region +(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R) + +# Check direction of shocks and wave +shock_R = (P_3 > P_R) +shock_L = (P_3 > P_L) + +# Velocity of shock front and and rarefaction wave +if shock_R: + v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R)) +else: + v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R + +if shock_L: + v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L)) +else: + v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L + +# Compute position of the transitions +x_23 = -fabs(v_left) * time +if shock_L : + x_12 = -fabs(v_left) * time +else: + x_12 = -(c_L - v_L) * time + +x_34 = v_3 * time + +x_45 = fabs(v_right) * time +if shock_R: + x_56 = fabs(v_right) * time +else: + x_56 = (c_R + v_R) * time + + +# Prepare arrays +delta_x = (x_max - x_min) / N +x_s = arange(x_min, x_max, delta_x) +rho_s = zeros(N) +P_s = zeros(N) +v_s = zeros(N) + +# Compute solution in the different regions +for i in range(N): + if x_s[i] <= x_12: + rho_s[i] = rho_L + P_s[i] = P_L + v_s[i] = v_L + if x_s[i] >= x_12 and x_s[i] < x_23: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L) + P_s[i] = P_3 + v_s[i] = v_3 + else: + rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma + v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L + if x_s[i] >= x_23 and x_s[i] < x_34: + if shock_L: + rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L) + else: + rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_34 and x_s[i] < x_45: + if shock_R: + rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R) + else: + rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma) + P_s[i] = P_3 + v_s[i] = v_3 + if x_s[i] >= x_45 and x_s[i] < x_56: + if shock_R: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + else: + rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.)) + P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma + v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R + if x_s[i] >= x_56: + rho_s[i] = rho_R + P_s[i] = P_R + v_s[i] = v_R + + +# Additional arrays +u_s = P_s / (rho_s * (gas_gamma - 1.)) #internal energy +s_s = P_s / rho_s**gas_gamma # entropic function + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, '.', color='r', ms=0.5, alpha=0.2) +plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_x$", labelpad=0) +xlim(-0.5, 0.5) +ylim(-0.1, 0.95) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, '.', color='r', ms=0.5, alpha=0.2) +plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.05, 1.1) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, '.', color='r', ms=0.5, alpha=0.2) +plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.01, 1.1) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, '.', color='r', ms=0.5, alpha=0.2) +plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.8, 2.2) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, '.', color='r', ms=0.5, alpha=0.2) +plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Position}}~x$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) +xlim(-0.5, 0.5) +ylim(0.8, 3.8) + +# Information ------------------------------------- +subplot(236, frameon=False) + +znow = 1. / anow - 1. +text(-0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 3D at $z=%.2f$"%(gas_gamma,znow), fontsize=10) +text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10) +text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10) +z_i = 1. / a_i - 1. +text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize = 10) +plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1) +text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.3, scheme, fontsize=10) +text(-0.49, 0.2, kernel, fontsize=10) +text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() +savefig("SodShock.png", dpi=200) diff --git a/examples/Cosmology/ComovingSodShock_3D/run.sh b/examples/Cosmology/ComovingSodShock_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b2ee90ecf8d7eabacc8b2b848b406a233953c9e6 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_3D/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the Sod shock example..." + ./getGlass.sh +fi +if [ ! -e sodShock.hdf5 ] +then + echo "Generating initial conditions for the Sod shock example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log + +python plotSolution.py 1 diff --git a/examples/Cosmology/ComovingSodShock_3D/sodShock.yml b/examples/Cosmology/ComovingSodShock_3D/sodShock.yml new file mode 100644 index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71 --- /dev/null +++ b/examples/Cosmology/ComovingSodShock_3D/sodShock.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 2.94e55 # Grams + UnitLength_in_cgs: 3.086e18 # pc + UnitVelocity_in_cgs: 1. # km per s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: sodShock # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1.06638 # Time difference between consecutive outputs (in internal units) + scale_factor_first: 0.001 + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sodShock.hdf5 # The file to read + periodic: 1 + +Cosmology: + Omega_m: 1. + Omega_lambda: 0. + Omega_b: 1. + h: 1. + a_begin: 0.001 + a_end: 0.00106638 + diff --git a/examples/ConstantCosmoVolume/Gadget2/README b/examples/Cosmology/ConstantCosmoVolume/Gadget2/README similarity index 100% rename from examples/ConstantCosmoVolume/Gadget2/README rename to examples/Cosmology/ConstantCosmoVolume/Gadget2/README diff --git a/examples/ConstantCosmoVolume/Gadget2/constant_volume.param b/examples/Cosmology/ConstantCosmoVolume/Gadget2/constant_volume.param similarity index 100% rename from examples/ConstantCosmoVolume/Gadget2/constant_volume.param rename to examples/Cosmology/ConstantCosmoVolume/Gadget2/constant_volume.param diff --git a/examples/ConstantCosmoVolume/README b/examples/Cosmology/ConstantCosmoVolume/README similarity index 100% rename from examples/ConstantCosmoVolume/README rename to examples/Cosmology/ConstantCosmoVolume/README diff --git a/examples/ConstantCosmoVolume/constant_volume.yml b/examples/Cosmology/ConstantCosmoVolume/constant_volume.yml similarity index 100% rename from examples/ConstantCosmoVolume/constant_volume.yml rename to examples/Cosmology/ConstantCosmoVolume/constant_volume.yml diff --git a/examples/ConstantCosmoVolume/getGlass.sh b/examples/Cosmology/ConstantCosmoVolume/getGlass.sh similarity index 100% rename from examples/ConstantCosmoVolume/getGlass.sh rename to examples/Cosmology/ConstantCosmoVolume/getGlass.sh diff --git a/examples/ConstantCosmoVolume/makeIC.py b/examples/Cosmology/ConstantCosmoVolume/makeIC.py similarity index 100% rename from examples/ConstantCosmoVolume/makeIC.py rename to examples/Cosmology/ConstantCosmoVolume/makeIC.py diff --git a/examples/ConstantCosmoVolume/plotSolution.py b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py similarity index 100% rename from examples/ConstantCosmoVolume/plotSolution.py rename to examples/Cosmology/ConstantCosmoVolume/plotSolution.py diff --git a/examples/ConstantCosmoVolume/run.sh b/examples/Cosmology/ConstantCosmoVolume/run.sh similarity index 80% rename from examples/ConstantCosmoVolume/run.sh rename to examples/Cosmology/ConstantCosmoVolume/run.sh index a2d9cdc63fbef6208caa44e05f91e94a27c4ff20..4a30410e868aef58b1a9dac0a3225e5844c5873f 100755 --- a/examples/ConstantCosmoVolume/run.sh +++ b/examples/Cosmology/ConstantCosmoVolume/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -c -G -t 8 constant_volume.yml 2>&1 | tee output.log +../../swift --hydro --cosmology --self-gravity --threads=8 constant_volume.yml 2>&1 | tee output.log # Plot the result python plotSolution.py $i diff --git a/examples/ZeldovichPancake_3D/makeIC.py b/examples/Cosmology/ZeldovichPancake_3D/makeIC.py similarity index 100% rename from examples/ZeldovichPancake_3D/makeIC.py rename to examples/Cosmology/ZeldovichPancake_3D/makeIC.py diff --git a/examples/ZeldovichPancake_3D/plotSolution.py b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py similarity index 100% rename from examples/ZeldovichPancake_3D/plotSolution.py rename to examples/Cosmology/ZeldovichPancake_3D/plotSolution.py diff --git a/examples/ZeldovichPancake_3D/run.sh b/examples/Cosmology/ZeldovichPancake_3D/run.sh similarity index 74% rename from examples/ZeldovichPancake_3D/run.sh rename to examples/Cosmology/ZeldovichPancake_3D/run.sh index b3f802f978377a9615f7cdd1cdd14e85ae3baad2..0be82b2f003143f3a783b2939a4ae932952d02c0 100755 --- a/examples/ZeldovichPancake_3D/run.sh +++ b/examples/Cosmology/ZeldovichPancake_3D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -c -G -t 8 zeldovichPancake.yml 2>&1 | tee output.log +../../swift --hydro --cosmology --self-gravity --threads=8 zeldovichPancake.yml 2>&1 | tee output.log # Plot the result for i in {0..119} diff --git a/examples/ZeldovichPancake_3D/zeldovichPancake.yml b/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml similarity index 98% rename from examples/ZeldovichPancake_3D/zeldovichPancake.yml rename to examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml index a1d2342b56d6816c3cfbe7da70220ab244104fbd..6a7c5166635b7fa0ed5f69c41461d867c3b254ad 100644 --- a/examples/ZeldovichPancake_3D/zeldovichPancake.yml +++ b/examples/Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml @@ -35,6 +35,7 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + h_min_ratio: 0.1 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EAGLE_12/getIC.sh b/examples/EAGLE_12/getIC.sh deleted file mode 100755 index 1983a1c19fbfd67d2a13d7a59847423d217f0e4e..0000000000000000000000000000000000000000 --- a/examples/EAGLE_12/getIC.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs_12.hdf5 diff --git a/examples/EAGLE_25/getIC.sh b/examples/EAGLE_25/getIC.sh deleted file mode 100755 index 4577db3a351f5b9ce16962897c664cd12108b01c..0000000000000000000000000000000000000000 --- a/examples/EAGLE_25/getIC.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs_25.hdf5 diff --git a/examples/EAGLE_50/getIC.sh b/examples/EAGLE_50/getIC.sh deleted file mode 100755 index f898a02fac4f66f1d186d61a8d48d7b1f81a2af4..0000000000000000000000000000000000000000 --- a/examples/EAGLE_50/getIC.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs_50.hdf5 diff --git a/examples/EAGLE_6/getIC.sh b/examples/EAGLE_6/getIC.sh deleted file mode 100755 index 08daa32a9b708532ab3e78924fb44f7c5dd06795..0000000000000000000000000000000000000000 --- a/examples/EAGLE_6/getIC.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs_6.hdf5 diff --git a/examples/EAGLE_DMO_100/README b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/README similarity index 100% rename from examples/EAGLE_DMO_100/README rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_100/README diff --git a/examples/EAGLE_DMO_100/eagle_100.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml similarity index 100% rename from examples/EAGLE_DMO_100/eagle_100.yml rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml diff --git a/examples/EAGLE_DMO_50/getIC.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/getIC.sh similarity index 70% rename from examples/EAGLE_DMO_50/getIC.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_100/getIC.sh index d100cbba766200238ae1d91b9e959f143d25f869..d13bd4fea4ca0a2b3400f7d17b15f6fafd74021f 100755 --- a/examples/EAGLE_DMO_50/getIC.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_ICs_50.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_low_z/EAGLE_DMO_ICs_100.hdf5 diff --git a/examples/EAGLE_DMO_100/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh similarity index 70% rename from examples/EAGLE_DMO_100/run.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh index 642c9247cf4aefa299e8f11c9674d737f4770296..be3cdd8e3cbc4cdbb0d8ab039bbbaa0f8e9ce2a1 100755 --- a/examples/EAGLE_DMO_100/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -G -t 16 eagle_100.yml 2>&1 | tee output.log +../../swift --cosmology --self-gravity --threads=16 eagle_100.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_DMO_12/README b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/README similarity index 100% rename from examples/EAGLE_DMO_12/README rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_12/README diff --git a/examples/EAGLE_DMO_12/eagle_12.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml similarity index 100% rename from examples/EAGLE_DMO_12/eagle_12.yml rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml diff --git a/examples/EAGLE_DMO_100/getIC.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/getIC.sh similarity index 71% rename from examples/EAGLE_DMO_100/getIC.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_12/getIC.sh index 22e8be4afe2a9533dd6a25bf057b54cee6bf847b..3b2e613c5e21079a542eeb33758bf0808b84df67 100755 --- a/examples/EAGLE_DMO_100/getIC.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_ICs_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_low_z/EAGLE_DMO_ICs_12.hdf5 diff --git a/examples/EAGLE_DMO_12/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh similarity index 70% rename from examples/EAGLE_DMO_12/run.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh index ebf24ee6a5c873d595c58e74a31838eb2d013d92..669fa49823cd65ff336c60964e5e565e925c53c5 100755 --- a/examples/EAGLE_DMO_12/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -G -t 16 eagle_12.yml 2>&1 | tee output.log +../../swift --cosmology --self-gravity --threads=16 eagle_12.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_DMO_25/README b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/README similarity index 100% rename from examples/EAGLE_DMO_25/README rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_25/README diff --git a/examples/EAGLE_DMO_25/eagle_25.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml similarity index 100% rename from examples/EAGLE_DMO_25/eagle_25.yml rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml diff --git a/examples/EAGLE_DMO_12/getIC.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/getIC.sh similarity index 71% rename from examples/EAGLE_DMO_12/getIC.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_25/getIC.sh index a05321e1b1f4a0c48189a3b9ce05c39549c6fda5..6a796a9d0c1e650a611c3cf7a79606066e02e438 100755 --- a/examples/EAGLE_DMO_12/getIC.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_ICs_12.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_low_z/EAGLE_DMO_ICs_25.hdf5 diff --git a/examples/EAGLE_DMO_25/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh similarity index 70% rename from examples/EAGLE_DMO_25/run.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh index ae0a6d3c49b89239da973c7417530204b4751729..6d96edda655c7bf2d18ab8312417722f78bdb7e0 100755 --- a/examples/EAGLE_DMO_25/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -G -t 16 eagle_25.yml 2>&1 | tee output.log +../../swift --cosmology --self-gravity --threads=16 eagle_25.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_DMO_50/README b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/README similarity index 100% rename from examples/EAGLE_DMO_50/README rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_50/README diff --git a/examples/EAGLE_DMO_50/eagle_50.yml b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml similarity index 100% rename from examples/EAGLE_DMO_50/eagle_50.yml rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml diff --git a/examples/EAGLE_DMO_25/getIC.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/getIC.sh similarity index 71% rename from examples/EAGLE_DMO_25/getIC.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_50/getIC.sh index 72b08086d77f619b2a89a820557975af8da5ce75..752d58d0096fb3bbbf41ff0ade789f045ff7f90c 100755 --- a/examples/EAGLE_DMO_25/getIC.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_ICs_25.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_DMO_low_z/EAGLE_DMO_ICs_50.hdf5 diff --git a/examples/EAGLE_DMO_50/run.sh b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh similarity index 70% rename from examples/EAGLE_DMO_50/run.sh rename to examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh index 31980a5a883e62c972b27a41bbdebe06c7c71539..8a08d0b0408c39f58a53aeff660123f6afc5777b 100755 --- a/examples/EAGLE_DMO_50/run.sh +++ b/examples/EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -G -t 16 eagle_50.yml 2>&1 | tee output.log +../../swift --cosmology --self-gravity --threads=16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_DMO_low_z/README b/examples/EAGLE_DMO_low_z/README new file mode 100644 index 0000000000000000000000000000000000000000..a141084b971a2a08592dd4cd42a7f54025b056fc --- /dev/null +++ b/examples/EAGLE_DMO_low_z/README @@ -0,0 +1,3 @@ +This directory contains initial conditions generated from +the z=0.1 DMONLY snapshots of the EAGLE suite of simulations. They +are ideal for testing the late-time behaviour of the code. diff --git a/examples/EAGLE_ICs/EAGLE_12/README b/examples/EAGLE_ICs/EAGLE_12/README new file mode 100644 index 0000000000000000000000000000000000000000..1c2b4fc1eb0dee875bf187f05e039c943d0b8b84 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_12/README @@ -0,0 +1,3 @@ +Initial conditions corresponding to the 12.5 Mpc volume +of the EAGLE suite. The ICs only contain DM particles. The +gas particles will be generated in SWIFT. diff --git a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml new file mode 100644 index 0000000000000000000000000000000000000000..4c3d7f0a3516ea7919a7ecd28efa6808c2f0f046 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml @@ -0,0 +1,113 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0078125 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: eagle # Common part of the name of output files + scale_factor_first: 0.05 + delta_time: 1.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 + scale_factor_first: 0.05 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + mesh_side_length: 64 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + initial_temperature: 268.7 + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 100 + tasks_per_cell: 5 + +Restarts: + delta_hours: 1.0 + +# Parameters related to the initial conditions +InitialConditions: + file_name: EAGLE_L0012N0188_ICs.hdf5 + periodic: 1 + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget + cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0. + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0.0 + init_abundance_Nitrogen: 0.0 + init_abundance_Oxygen: 0.0 + init_abundance_Neon: 0.0 + init_abundance_Magnesium: 0.0 + init_abundance_Silicon: 0.0 + init_abundance_Iron: 0.0 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# EAGLE star formation parameters +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + diff --git a/examples/EAGLE_100/getIC.sh b/examples/EAGLE_ICs/EAGLE_12/getIC.sh similarity index 72% rename from examples/EAGLE_100/getIC.sh rename to examples/EAGLE_ICs/EAGLE_12/getIC.sh index 227df3f9f79d294cd8ccbfd3b72b02dfbea2ebd6..fd4db0384b208f9aecdc4a90084b71ccd2eed444 100755 --- a/examples/EAGLE_100/getIC.sh +++ b/examples/EAGLE_ICs/EAGLE_12/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs_100.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs/EAGLE_L0012N0188_ICs.hdf5 diff --git a/examples/EAGLE_12/run.sh b/examples/EAGLE_ICs/EAGLE_12/run.sh similarity index 65% rename from examples/EAGLE_12/run.sh rename to examples/EAGLE_ICs/EAGLE_12/run.sh index 67f1c24a1ead927823b9240cdeb718b35580d573..bceddf338ae797abcc32c24fb2642320d9091ba9 100755 --- a/examples/EAGLE_12/run.sh +++ b/examples/EAGLE_ICs/EAGLE_12/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -s -G -S -t 16 eagle_12.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_12.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_ICs/README b/examples/EAGLE_ICs/README new file mode 100644 index 0000000000000000000000000000000000000000..3a44c6fc97c8e1759479561780960d19df43c97f --- /dev/null +++ b/examples/EAGLE_ICs/README @@ -0,0 +1,4 @@ +This directory contains initial conditions generated for +the EAGLE suite of simulations. The cosmology, resolution +and phases are the same as used in the original suite. The only +difference is the file format, adapted for SWIFT. diff --git a/examples/EAGLE_100/README b/examples/EAGLE_low_z/EAGLE_100/README similarity index 100% rename from examples/EAGLE_100/README rename to examples/EAGLE_low_z/EAGLE_100/README diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml similarity index 97% rename from examples/EAGLE_100/eagle_100.yml rename to examples/EAGLE_low_z/EAGLE_100/eagle_100.yml index 3bcda091bdac5b740f3568de9c0814cc84c3b846..5275f709c710d2da1d5396e98f1d4d918e482c6d 100644 --- a/examples/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_low_z/EAGLE_100/eagle_100.yml @@ -49,6 +49,7 @@ Gravity: # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) diff --git a/examples/EAGLE_low_z/EAGLE_100/getIC.sh b/examples/EAGLE_low_z/EAGLE_100/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..36053deb5ff80c2ddf740b2379d5d145d091466c --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_100/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_low_z/EAGLE_ICs_100.hdf5 diff --git a/examples/EAGLE_100/run.sh b/examples/EAGLE_low_z/EAGLE_100/run.sh similarity index 65% rename from examples/EAGLE_100/run.sh rename to examples/EAGLE_low_z/EAGLE_100/run.sh index 9c990a902a6350eff348aad40c482723d1ba954c..28571d4803cb8c26bf67d84870c10a2e7dcf534c 100755 --- a/examples/EAGLE_100/run.sh +++ b/examples/EAGLE_low_z/EAGLE_100/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -s -G -S -t 16 eagle_100.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_100.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_12/README b/examples/EAGLE_low_z/EAGLE_12/README similarity index 100% rename from examples/EAGLE_12/README rename to examples/EAGLE_low_z/EAGLE_12/README diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml similarity index 85% rename from examples/EAGLE_12/eagle_12.yml rename to examples/EAGLE_low_z/EAGLE_12/eagle_12.yml index aa42d2d00db776e114a25dc52d4207b6dad8f4ff..7196fbbdb2583582ed0235c77afab53c0c415c68 100644 --- a/examples/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml @@ -47,6 +47,7 @@ Gravity: # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) @@ -67,3 +68,21 @@ InitialConditions: cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 diff --git a/examples/EAGLE_low_z/EAGLE_12/getIC.sh b/examples/EAGLE_low_z/EAGLE_12/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..18bac13c4fb0006cf9da00d3dd869693ef0a589d --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_12/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_low_z/EAGLE_ICs_12.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_12/run.sh b/examples/EAGLE_low_z/EAGLE_12/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..bceddf338ae797abcc32c24fb2642320d9091ba9 --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_12/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e EAGLE_ICs_12.hdf5 ] +then + echo "Fetching initial conditions for the EAGLE 12Mpc example..." + ./getIC.sh +fi + +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_12.yml 2>&1 | tee output.log + diff --git a/examples/EAGLE_25/README b/examples/EAGLE_low_z/EAGLE_25/README similarity index 100% rename from examples/EAGLE_25/README rename to examples/EAGLE_low_z/EAGLE_25/README diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml similarity index 86% rename from examples/EAGLE_25/eagle_25.yml rename to examples/EAGLE_low_z/EAGLE_25/eagle_25.yml index c5b2b9ec49135e818dd6bd97843ef7876554b41f..ea8becf2ee7080aa364c5dca7204ac03e24eb634 100644 --- a/examples/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml @@ -10,10 +10,8 @@ InternalUnitSystem: StructureFinding: config_file_name: stf_input.cfg # Name of the STF config file. basename: ./stf # Common part of the name of output files. - output_time_format: 0 # Specifies the frequency format of structure finding. 0 for simulation steps (delta_step) and 1 for simulation time intervals (delta_time). scale_factor_first: 0.92 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first structure finding output (in internal units). - delta_step: 1000 # Time difference between consecutive structure finding outputs (in internal units) in simulation steps. delta_time: 1.10 # Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals. # Cosmological parameters @@ -56,6 +54,7 @@ Gravity: # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) @@ -76,4 +75,22 @@ InitialConditions: cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 diff --git a/examples/EAGLE_low_z/EAGLE_25/getIC.sh b/examples/EAGLE_low_z/EAGLE_25/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..9f884b57aa7d9e3980237a652d2adaa28c51be68 --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_25/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_low_z/EAGLE_ICs_25.hdf5 diff --git a/examples/EAGLE_25/run.sh b/examples/EAGLE_low_z/EAGLE_25/run.sh similarity index 65% rename from examples/EAGLE_25/run.sh rename to examples/EAGLE_low_z/EAGLE_25/run.sh index 0b6cf77d7b2461864fc24055811ee00c7dd00613..ea14dbde3293bb28f98de29eb035d53bc7caa1e6 100755 --- a/examples/EAGLE_25/run.sh +++ b/examples/EAGLE_low_z/EAGLE_25/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -s -G -S -t 16 eagle_25.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_25.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_50/README b/examples/EAGLE_low_z/EAGLE_50/README similarity index 100% rename from examples/EAGLE_50/README rename to examples/EAGLE_low_z/EAGLE_50/README diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml similarity index 85% rename from examples/EAGLE_50/eagle_50.yml rename to examples/EAGLE_low_z/EAGLE_50/eagle_50.yml index 51f607eadd60d10a585641ab54769aa012931e5f..b6c814e4b96a6f235d2ef73680368d7ce9773513 100644 --- a/examples/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml @@ -49,6 +49,7 @@ Gravity: # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) @@ -68,3 +69,22 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 diff --git a/examples/EAGLE_low_z/EAGLE_50/getIC.sh b/examples/EAGLE_low_z/EAGLE_50/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..09154fb9b8933df8eaec368a2026a14704ddeca0 --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_50/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_low_z/EAGLE_ICs_50.hdf5 diff --git a/examples/EAGLE_50/run.sh b/examples/EAGLE_low_z/EAGLE_50/run.sh similarity index 65% rename from examples/EAGLE_50/run.sh rename to examples/EAGLE_low_z/EAGLE_50/run.sh index a0d5dee11dc58e8d19d4d0e551c5ad8eceb90548..e2f2836900bacf612acb289154de973cda90eccb 100755 --- a/examples/EAGLE_50/run.sh +++ b/examples/EAGLE_low_z/EAGLE_50/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -s -G -S -t 16 eagle_50.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_6/README b/examples/EAGLE_low_z/EAGLE_6/README similarity index 100% rename from examples/EAGLE_6/README rename to examples/EAGLE_low_z/EAGLE_6/README diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml similarity index 86% rename from examples/EAGLE_6/eagle_6.yml rename to examples/EAGLE_low_z/EAGLE_6/eagle_6.yml index c69e5a24d8d4812f84ba88c17ca5ed11bc6b9bb6..3d4f24ff2e8367d598a641a880c9dda6b2e65b66 100644 --- a/examples/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml @@ -10,10 +10,8 @@ InternalUnitSystem: StructureFinding: config_file_name: stf_input.cfg # Name of the STF config file. basename: ./stf # Common part of the name of output files. - output_time_format: 0 # Specifies the frequency format of structure finding. 0 for simulation steps (delta_step) and 1 for simulation time intervals (delta_time). scale_factor_first: 0.92 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first structure finding output (in internal units). - delta_step: 1000 # Time difference between consecutive structure finding outputs (in internal units) in simulation steps. delta_time: 1.10 # Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals. # Cosmological parameters @@ -60,6 +58,7 @@ Gravity: # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + h_min_ratio: 0.1 # Minimal smoothing in units of softening. CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. minimal_temperature: 100 # (internal units) @@ -79,3 +78,22 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget cleanup_velocity_factors: 1 # Remove the sqrt(a) factor in the velocities inherited from Gadget + +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0.014 + init_abundance_Hydrogen: 0.70649785 + init_abundance_Helium: 0.28055534 + init_abundance_Carbon: 2.0665436e-3 + init_abundance_Nitrogen: 8.3562563e-4 + init_abundance_Oxygen: 5.4926244e-3 + init_abundance_Neon: 1.4144605e-3 + init_abundance_Magnesium: 5.907064e-4 + init_abundance_Silicon: 6.825874e-4 + init_abundance_Iron: 1.1032152e-3 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 diff --git a/examples/EAGLE_low_z/EAGLE_6/getIC.sh b/examples/EAGLE_low_z/EAGLE_6/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..c1643f1b7e2550f80cc286ad9b8a09020d63f07f --- /dev/null +++ b/examples/EAGLE_low_z/EAGLE_6/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_low_z/EAGLE_ICs_6.hdf5 diff --git a/examples/EAGLE_6/run.sh b/examples/EAGLE_low_z/EAGLE_6/run.sh similarity index 65% rename from examples/EAGLE_6/run.sh rename to examples/EAGLE_low_z/EAGLE_6/run.sh index 7ef3fc2abdd1bb3fed1a228bf993bf09fb13f42c..47dbd952549137d8c2baab8c22361a227eb35ca9 100755 --- a/examples/EAGLE_6/run.sh +++ b/examples/EAGLE_low_z/EAGLE_6/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -c -s -G -S -t 16 eagle_6.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_6.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_6/testVELOCIraptor.sh b/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh similarity index 76% rename from examples/EAGLE_6/testVELOCIraptor.sh rename to examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh index 14ec30487006f0b7e86356837c9a801950c15c83..3f0ae1d6f0da9736b867f53b898752efbfd50324 100755 --- a/examples/EAGLE_6/testVELOCIraptor.sh +++ b/examples/EAGLE_low_z/EAGLE_6/testVELOCIraptor.sh @@ -36,8 +36,8 @@ if [ "$RUN_DM" = "1" ]; then rm $VEL_OUTPUT/vel_$TEST* # Run test using SWIFT + VELOCIraptor - echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi -G -t 8 eagle_6.yml -x -n 5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly" - mpirun -np $NUM_MPI_PROC ../swift_mpi -G -t 8 eagle_6.yml -x -n 5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly + echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly" + mpirun -np $NUM_MPI_PROC ../swift_mpi --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_dmonly # Run test using VELOCIraptor echo "Running: mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_dmonly_0000 -C $VELOCIRAPTOR_PATH/stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST" @@ -80,8 +80,8 @@ if [ "$RUN_GAS" = "1" ]; then rm $VEL_OUTPUT/vel_$TEST* # Run test using SWIFT + VELOCIraptor - echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi -s -G -t 8 eagle_6.yml -x -n 5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas" - mpirun -np $NUM_MPI_PROC ../swift_mpi -s -G -t 8 eagle_6.yml -x -n 5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas + echo "Running: mpirun -np $NUM_MPI_PROC ../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas" + mpirun -np $NUM_MPI_PROC ../swift_mpi --hydro --self-gravity --threads=8 eagle_6.yml --velociraptor --steps=5 -P StructureFinding:basename:./$OUTPUT/stf -P StructureFinding:config_file_name:./stf_input_$TEST.cfg -P Snapshots:basename:./eagle_gas # Run test using VELOCIraptor echo "Running: mpirun -np $NUM_MPI_PROC $VELOCIRAPTOR_PATH/bin/stf-gas -I 2 -i eagle_gas_0000 -C ./stf_input_$TEST.cfg -o ./$VEL_OUTPUT/vel_$TEST" diff --git a/examples/EAGLE_low_z/README b/examples/EAGLE_low_z/README new file mode 100644 index 0000000000000000000000000000000000000000..d17b8bec79adc7ebd8f9db2114a8e290a9908548 --- /dev/null +++ b/examples/EAGLE_low_z/README @@ -0,0 +1,3 @@ +This directory contains initial conditions generated from +the z=0.1 snapshots of the EAGLE suite of simulations. They +are ideal for testing the late-time behaviour of the code. diff --git a/examples/AgoraDisk/agora_disk.yml b/examples/GEAR/AgoraDisk/agora_disk.yml similarity index 100% rename from examples/AgoraDisk/agora_disk.yml rename to examples/GEAR/AgoraDisk/agora_disk.yml diff --git a/examples/AgoraDisk/changeType.py b/examples/GEAR/AgoraDisk/changeType.py similarity index 100% rename from examples/AgoraDisk/changeType.py rename to examples/GEAR/AgoraDisk/changeType.py diff --git a/examples/AgoraDisk/cleanupSwift.py b/examples/GEAR/AgoraDisk/cleanupSwift.py similarity index 100% rename from examples/AgoraDisk/cleanupSwift.py rename to examples/GEAR/AgoraDisk/cleanupSwift.py diff --git a/examples/AgoraDisk/getIC.sh b/examples/GEAR/AgoraDisk/getIC.sh similarity index 100% rename from examples/AgoraDisk/getIC.sh rename to examples/GEAR/AgoraDisk/getIC.sh diff --git a/examples/AgoraDisk/getSolution.sh b/examples/GEAR/AgoraDisk/getSolution.sh similarity index 100% rename from examples/AgoraDisk/getSolution.sh rename to examples/GEAR/AgoraDisk/getSolution.sh diff --git a/examples/AgoraDisk/plotSolution.py b/examples/GEAR/AgoraDisk/plotSolution.py similarity index 100% rename from examples/AgoraDisk/plotSolution.py rename to examples/GEAR/AgoraDisk/plotSolution.py diff --git a/examples/AgoraDisk/run.sh b/examples/GEAR/AgoraDisk/run.sh similarity index 92% rename from examples/AgoraDisk/run.sh rename to examples/GEAR/AgoraDisk/run.sh index d7e284db52c2e6750fd713b3607a7f423bac7769..5b85be7df875cee69b513e36a327b1469e35b60e 100755 --- a/examples/AgoraDisk/run.sh +++ b/examples/GEAR/AgoraDisk/run.sh @@ -38,7 +38,7 @@ cp $sim.hdf5 agora_disk.hdf5 python3 changeType.py agora_disk.hdf5 # Run SWIFT -#../swift $flag -s -G -t 4 agora_disk.yml 2>&1 | tee output.log +#../../swift $flag --hydro --self-gravity --threads=4 agora_disk.yml 2>&1 | tee output.log echo "Changing smoothing length to be Gadget compatible" diff --git a/examples/DwarfGalaxy/README b/examples/GEAR/DwarfGalaxy/README similarity index 100% rename from examples/DwarfGalaxy/README rename to examples/GEAR/DwarfGalaxy/README diff --git a/examples/DwarfGalaxy/dwarf_galaxy.yml b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml similarity index 91% rename from examples/DwarfGalaxy/dwarf_galaxy.yml rename to examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml index 0d815a99c42249bcbbdaf21dbaa34a55f61731aa..4c5e2a82b017725929138de011b1f3ed1fe9f1ef 100644 --- a/examples/DwarfGalaxy/dwarf_galaxy.yml +++ b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml @@ -10,10 +10,8 @@ InternalUnitSystem: StructureFinding: config_file_name: stf_input.cfg # Name of the STF config file. basename: ./stf # Common part of the name of output files. - output_time_format: 0 # Specifies the frequency format of structure finding. 0 for simulation steps (delta_step) and 1 for simulation time intervals (delta_time). scale_factor_first: 0.92 # Scale-factor of the first snaphot (cosmological run) time_first: 0.01 # Time of the first structure finding output (in internal units). - delta_step: 1000 # Time difference between consecutive structure finding outputs (in internal units) in simulation steps. delta_time: 1.10 # Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals. # Cosmological parameters diff --git a/examples/DwarfGalaxy/getIC.sh b/examples/GEAR/DwarfGalaxy/getIC.sh similarity index 100% rename from examples/DwarfGalaxy/getIC.sh rename to examples/GEAR/DwarfGalaxy/getIC.sh diff --git a/examples/DwarfGalaxy/run.sh b/examples/GEAR/DwarfGalaxy/run.sh similarity index 64% rename from examples/DwarfGalaxy/run.sh rename to examples/GEAR/DwarfGalaxy/run.sh index 4cc87880215a37c9eac59b19e584f4887cba2c38..1f63556e7f7af57885606c08fc8e1a923c2e440d 100755 --- a/examples/DwarfGalaxy/run.sh +++ b/examples/GEAR/DwarfGalaxy/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -b -G -s -S -t 8 dwarf_galaxy.yml 2>&1 | tee output.log +../../swift --feedback --self-gravity --hydro --stars --threads=8 $@ dwarf_galaxy.yml 2>&1 | tee output.log diff --git a/examples/ZoomIn/README b/examples/GEAR/ZoomIn/README similarity index 100% rename from examples/ZoomIn/README rename to examples/GEAR/ZoomIn/README diff --git a/examples/ZoomIn/getIC.sh b/examples/GEAR/ZoomIn/getIC.sh similarity index 100% rename from examples/ZoomIn/getIC.sh rename to examples/GEAR/ZoomIn/getIC.sh diff --git a/examples/ZoomIn/run.sh b/examples/GEAR/ZoomIn/run.sh similarity index 62% rename from examples/ZoomIn/run.sh rename to examples/GEAR/ZoomIn/run.sh index 99eda1cfc779c1958d19d0c7ae234b6c211f8127..59c8ff0d63b504978e4d74abbce8680f65695ffa 100755 --- a/examples/ZoomIn/run.sh +++ b/examples/GEAR/ZoomIn/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -b -c -G -s -S -t 8 zoom_in.yml 2>&1 | tee output.log +../../swift --feedback --cosmology --self-gravity --hydro --stars --threads=8 zoom_in.yml 2>&1 | tee output.log diff --git a/examples/ZoomIn/zoom_in.yml b/examples/GEAR/ZoomIn/zoom_in.yml similarity index 100% rename from examples/ZoomIn/zoom_in.yml rename to examples/GEAR/ZoomIn/zoom_in.yml diff --git a/examples/GiantImpacts/EoSTables/get_eos_tables.sh b/examples/GiantImpacts/EoSTables/get_eos_tables.sh new file mode 100755 index 0000000000000000000000000000000000000000..979431777fceee421468138aa0180295f45adef2 --- /dev/null +++ b/examples/GiantImpacts/EoSTables/get_eos_tables.sh @@ -0,0 +1,9 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/HM80_HHe.txt +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/HM80_ice.txt +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/HM80_rock.txt + +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/SESAME_basalt_7530.txt +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/SESAME_iron_2140.txt +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/SESAME_water_7154.txt +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/EoS/SS08_water.txt diff --git a/examples/GiantImpacts/GiantImpact/README.md b/examples/GiantImpacts/GiantImpact/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7d8687886ee9b737ecb492d20d73e780233e9df --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/README.md @@ -0,0 +1,2 @@ +An example planetary simulation of a giant impact onto the young Uranus with +~10^6 SPH particles, as described in Kegerreis et al. (2018), ApJ, 861, 52. diff --git a/examples/GiantImpacts/GiantImpact/configuration.yml b/examples/GiantImpacts/GiantImpact/configuration.yml new file mode 100644 index 0000000000000000000000000000000000000000..ccce852862bec6d1eeba2c132457678564979b8a --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/configuration.yml @@ -0,0 +1,2 @@ +with-hydro: planetary +with-equation-of-state: planetary diff --git a/examples/GiantImpacts/GiantImpact/get_init_cond.sh b/examples/GiantImpacts/GiantImpact/get_init_cond.sh new file mode 100755 index 0000000000000000000000000000000000000000..e81035ead7e50ef204bb6bb476e94c7fe0eae0f6 --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/get_init_cond.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/GiantImpacts/uranus_1e6.hdf5 diff --git a/examples/GiantImpacts/GiantImpact/output_list.txt b/examples/GiantImpacts/GiantImpact/output_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d233fb11ceea02e40c42b8d512e9c1afbe6e835 --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/output_list.txt @@ -0,0 +1,7 @@ +# Time +4000 +9000 +14000 +20000 +30000 +40000 \ No newline at end of file diff --git a/examples/GiantImpacts/GiantImpact/plot_solution.py b/examples/GiantImpacts/GiantImpact/plot_solution.py new file mode 100644 index 0000000000000000000000000000000000000000..faeb071487adc04f0ba37d20967f693ed84652ec --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/plot_solution.py @@ -0,0 +1,144 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2019 Jacob Kegerreis (jacob.kegerreis@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/>. + # + ############################################################################## + +# Plot the snapshots from the example giant impact on Uranus, showing the +# particles in a thin slice near z=0, coloured by their material, similarish +# (but not identical) to Fig. 2 in Kegerreis et al. (2018). + +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import numpy as np +# import swiftsimio as sw +import h5py + +font_size = 20 +params = { + 'axes.labelsize' : font_size, + 'font.size' : font_size, + 'xtick.labelsize' : font_size, + 'ytick.labelsize' : font_size, + 'text.usetex' : True, + 'font.family' : 'serif', + } +matplotlib.rcParams.update(params) + +# Snapshot output times +output_list = [4000, 9000, 14000, 20000, 30000, 40000] + +# Material IDs ( = type_id * type_factor + unit_id ) +type_factor = 100 +type_HM80 = 2 +id_body = 10000 +# Name and ID +Di_mat_id = { + 'HM80_HHe' : type_HM80 * type_factor, # Hydrogen-helium atmosphere + 'HM80_ice' : type_HM80 * type_factor + 1, # H20-CH4-NH3 ice mix + 'HM80_ice_2' : type_HM80 * type_factor + 1 + id_body, + 'HM80_rock' : type_HM80 * type_factor + 2, # SiO2-MgO-FeS-FeO rock mix + 'HM80_rock_2' : type_HM80 * type_factor + 2 + id_body, + } +# ID and colour +Di_id_colour = { + Di_mat_id['HM80_HHe'] : '#33DDFF', + Di_mat_id['HM80_ice'] : 'lightsteelblue', + Di_mat_id['HM80_ice_2'] : '#A080D0', + Di_mat_id['HM80_rock'] : 'slategrey', + Di_mat_id['HM80_rock_2'] : '#706050', + } + +def get_snapshot_slice(snapshot): + """ Load and select the particles to plot. """ + # Load particle data + # data = load("uranus_1e6_%06d.hdf5" % snapshot) + # id = data.gas.particle_ids + # pos = data.gas.coordinates + # mat_id = data.gas.material + with h5py.File("uranus_1e6_%06d.hdf5" % snapshot, 'r') as f: + id = f['PartType0/ParticleIDs'].value + pos = (f['PartType0/Coordinates'].value + - 0.5 * f['Header'].attrs['BoxSize']) + mat_id = f['PartType0/MaterialID'].value + + # Edit the material ID of particles in the impactor + num_in_target = 869104 + sel_id = np.where(num_in_target < id)[0] + mat_id[sel_id] += id_body + + # Select particles in a thin slice around z=0 + z_min = -0.1 + z_max = 0.1 + sel_z = np.where((z_min < pos[:, 2]) & (pos[:, 2] < z_max))[0] + pos = pos[sel_z] + mat_id = mat_id[sel_z] + + return pos, mat_id + +def plot_snapshot_slice(pos, mat_id): + """ Plot the particles, coloured by their material. """ + colour = np.empty(len(pos), dtype=object) + for id, c in Di_id_colour.items(): + sel_c = np.where(mat_id == id)[0] + colour[sel_c] = c + + ax.scatter(pos[:, 0], pos[:, 1], c=colour, edgecolors='none', marker='.', + s=10, alpha=0.5, zorder=0) + +# Set up the figure +fig = plt.figure(figsize=(12, 8)) +gs = matplotlib.gridspec.GridSpec(2, 3) +axes = [plt.subplot(gs[i_y, i_x]) for i_y in range(2) for i_x in range(3)] + +# Plot each snapshot +for i_ax, ax in enumerate(axes): + plt.sca(ax) + ax.set_rasterization_zorder(1) + + # Load and select the particles to plot + pos, mat_id = get_snapshot_slice(output_list[i_ax]) + + # Plot the particles, coloured by their material + plot_snapshot_slice(pos, mat_id) + + # Axes etc. + ax.set_aspect('equal') + ax.set_facecolor('k') + + ax.set_xlim(-13, 13) + ax.set_ylim(-13, 13) + + if i_ax in [0, 3]: + ax.set_ylabel(r"y Postion $(R_\oplus)$") + else: + ax.set_yticklabels([]) + if 2 < i_ax: + ax.set_xlabel(r"x Postion $(R_\oplus)$") + else: + ax.set_xticklabels([]) + + # Corner time labels + x = ax.get_xlim()[0] + 0.04 * (ax.get_xlim()[1] - ax.get_xlim()[0]) + y = ax.get_ylim()[0] + 0.89 * (ax.get_ylim()[1] - ax.get_ylim()[0]) + ax.text(x, y, "%.1f h" % (output_list[i_ax] / 60**2), color='w') + +plt.subplots_adjust(wspace=0, hspace=0) +plt.tight_layout() + +# Save +plt.savefig("uranus_1e6.pdf", dpi=200) \ No newline at end of file diff --git a/examples/GiantImpacts/GiantImpact/run.sh b/examples/GiantImpacts/GiantImpact/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..5a4f0a74dd098b1fff4659a7d72be3845ad47fc6 --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Get the initial conditions if they are not present. +if [ ! -e uranus_1e6.hdf5 ] +then + echo "Fetching initial conditions file for the Uranus impact example..." + ./get_init_cond.sh +fi + +# Get the EoS tables if they are not present. +cd ../EoSTables +if [ ! -e HM80_HHe.txt ] || [ ! -e HM80_ice.txt ] || [ ! -e HM80_rock.txt ] +then + echo "Fetching equations of state tables for the Uranus impact example..." + ./get_eos_tables.sh +fi +cd ../GiantImpact + +# Run SWIFT +../../swift -s -G -t 8 uranus_1e6.yml 2>&1 | tee output.log + +# Plot the solution +python3 plot_solution.py diff --git a/examples/GiantImpacts/GiantImpact/system.yml b/examples/GiantImpacts/GiantImpact/system.yml new file mode 100644 index 0000000000000000000000000000000000000000..c99fc7158854ec538e68c44ff74bbb0ba1adfb48 --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/system.yml @@ -0,0 +1,9 @@ +input: + - uranus_1e6.yml + - output_list.txt + +output: + - uranus_1e6.pdf + +swift_parameters: -s -G +swift_threads: 8 diff --git a/examples/GiantImpacts/GiantImpact/uranus_1e6.yml b/examples/GiantImpacts/GiantImpact/uranus_1e6.yml new file mode 100644 index 0000000000000000000000000000000000000000..355748d847097623f171078c2ca8372e06a06efa --- /dev/null +++ b/examples/GiantImpacts/GiantImpact/uranus_1e6.yml @@ -0,0 +1,63 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 5.9724e27 # Grams + UnitLength_in_cgs: 6.371e8 # Centimeters + UnitVelocity_in_cgs: 6.371e8 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: uranus_1e6.hdf5 # The initial conditions file to read + periodic: 0 # Are we running with periodic ICs? + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0 # The starting time of the simulation (in internal units). + time_end: 40000 # The end time of the simulation (in internal units). + dt_min: 0.0001 # The minimal time-step size of the simulation (in internal units). + dt_max: 100 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: uranus_1e6 # Common part of the name of output files + time_first: 0 # Time of the first output (in internal units) + delta_time: 1000 # Time difference between consecutive outputs (in internal units) + int_time_label_on: 1 # Enable to label the snapshots using the time rounded to an integer (in internal units) + output_list_on: 1 # Enable the output list + output_list: output_list.txt # File containing the output times (see documentation in "Parameter File" section) + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0 # Time of the first output (in internal units) + delta_time: 1000 # Time between statistics output + +# Parameters controlling restarts +Restarts: + enable: 0 # Whether to enable dumping restarts at fixed intervals. + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.2 # Courant-Friedrich-Levy condition for time integration. + h_max: 0.5 # Maximal allowed smoothing length (in internal units). + viscosity_alpha: 1.5 # Override for the initial value of the artificial viscosity. + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.003 # Comoving softening length (in internal units). + max_physical_softening: 0.003 # Physical softening length (in internal units). + +# Parameters for the task scheduling +Scheduler: + max_top_level_cells: 64 # Maximal number of top-level cells in any dimension. The nu + +# Parameters related to the equation of state +EoS: + planetary_use_HM80: 1 # Whether to initialise the Hubbard & MacFarlane (1980) EOS + planetary_HM80_HHe_table_file: ../EoSTables/HM80_HHe.txt + planetary_HM80_ice_table_file: ../EoSTables/HM80_ice.txt + planetary_HM80_rock_table_file: ../EoSTables/HM80_rock.txt diff --git a/examples/DiscPatch/GravityOnly/README b/examples/GravityTests/DiscPatch/GravityOnly/README similarity index 100% rename from examples/DiscPatch/GravityOnly/README rename to examples/GravityTests/DiscPatch/GravityOnly/README diff --git a/examples/DiscPatch/GravityOnly/disc-patch.yml b/examples/GravityTests/DiscPatch/GravityOnly/disc-patch.yml similarity index 100% rename from examples/DiscPatch/GravityOnly/disc-patch.yml rename to examples/GravityTests/DiscPatch/GravityOnly/disc-patch.yml diff --git a/examples/DiscPatch/GravityOnly/makeIC.py b/examples/GravityTests/DiscPatch/GravityOnly/makeIC.py similarity index 100% rename from examples/DiscPatch/GravityOnly/makeIC.py rename to examples/GravityTests/DiscPatch/GravityOnly/makeIC.py diff --git a/examples/DiscPatch/GravityOnly/run.sh b/examples/GravityTests/DiscPatch/GravityOnly/run.sh similarity index 77% rename from examples/DiscPatch/GravityOnly/run.sh rename to examples/GravityTests/DiscPatch/GravityOnly/run.sh index 9af1011ee653253f0d1b2cd26db0ac13cf11adc0..ab05b603254f62cdaebef49882407f441d006a2c 100755 --- a/examples/DiscPatch/GravityOnly/run.sh +++ b/examples/GravityTests/DiscPatch/GravityOnly/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 1000 fi -../../swift -g -t 2 disc-patch.yml +../../../swift --external-gravity --threads=2 disc-patch.yml diff --git a/examples/DiscPatch/GravityOnly/test.pro b/examples/GravityTests/DiscPatch/GravityOnly/test.pro similarity index 100% rename from examples/DiscPatch/GravityOnly/test.pro rename to examples/GravityTests/DiscPatch/GravityOnly/test.pro diff --git a/examples/DiscPatch/HydroStatic/README b/examples/GravityTests/DiscPatch/HydroStatic/README similarity index 100% rename from examples/DiscPatch/HydroStatic/README rename to examples/GravityTests/DiscPatch/HydroStatic/README diff --git a/examples/DiscPatch/HydroStatic/disc-patch-icc.yml b/examples/GravityTests/DiscPatch/HydroStatic/disc-patch-icc.yml similarity index 100% rename from examples/DiscPatch/HydroStatic/disc-patch-icc.yml rename to examples/GravityTests/DiscPatch/HydroStatic/disc-patch-icc.yml diff --git a/examples/DiscPatch/HydroStatic/disc-patch.yml b/examples/GravityTests/DiscPatch/HydroStatic/disc-patch.yml similarity index 100% rename from examples/DiscPatch/HydroStatic/disc-patch.yml rename to examples/GravityTests/DiscPatch/HydroStatic/disc-patch.yml diff --git a/examples/DiscPatch/HydroStatic/getGlass.sh b/examples/GravityTests/DiscPatch/HydroStatic/getGlass.sh similarity index 100% rename from examples/DiscPatch/HydroStatic/getGlass.sh rename to examples/GravityTests/DiscPatch/HydroStatic/getGlass.sh diff --git a/examples/DiscPatch/HydroStatic/makeIC.py b/examples/GravityTests/DiscPatch/HydroStatic/makeIC.py similarity index 100% rename from examples/DiscPatch/HydroStatic/makeIC.py rename to examples/GravityTests/DiscPatch/HydroStatic/makeIC.py diff --git a/examples/DiscPatch/HydroStatic/plotSolution.py b/examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py similarity index 100% rename from examples/DiscPatch/HydroStatic/plotSolution.py rename to examples/GravityTests/DiscPatch/HydroStatic/plotSolution.py diff --git a/examples/DiscPatch/HydroStatic/run.sh b/examples/GravityTests/DiscPatch/HydroStatic/run.sh similarity index 79% rename from examples/DiscPatch/HydroStatic/run.sh rename to examples/GravityTests/DiscPatch/HydroStatic/run.sh index e1f47ecad54e7e171d78b7da080d56579e985d1e..44b933f79c1b0f699010932f285c62396de37a72 100755 --- a/examples/DiscPatch/HydroStatic/run.sh +++ b/examples/GravityTests/DiscPatch/HydroStatic/run.sh @@ -13,6 +13,6 @@ then fi # Run SWIFT -../../swift -g -s -t 4 disc-patch-icc.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log python plotSolution.py diff --git a/examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml b/examples/GravityTests/DiscPatch/HydroStatic_1D/disc-patch-icc.yml similarity index 100% rename from examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml rename to examples/GravityTests/DiscPatch/HydroStatic_1D/disc-patch-icc.yml diff --git a/examples/DiscPatch/HydroStatic_1D/makeIC.py b/examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py similarity index 100% rename from examples/DiscPatch/HydroStatic_1D/makeIC.py rename to examples/GravityTests/DiscPatch/HydroStatic_1D/makeIC.py diff --git a/examples/DiscPatch/HydroStatic_1D/plotSolution.py b/examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py similarity index 100% rename from examples/DiscPatch/HydroStatic_1D/plotSolution.py rename to examples/GravityTests/DiscPatch/HydroStatic_1D/plotSolution.py diff --git a/examples/DiscPatch/HydroStatic_1D/run.sh b/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh similarity index 71% rename from examples/DiscPatch/HydroStatic_1D/run.sh rename to examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh index e9d073a6cc7a06ec9ebd9fdb556c44778d32c7f4..a76db9422e12ee18251083ee8cf26dd28e861e69 100755 --- a/examples/DiscPatch/HydroStatic_1D/run.sh +++ b/examples/GravityTests/DiscPatch/HydroStatic_1D/run.sh @@ -8,6 +8,6 @@ then fi # Run SWIFT -../../swift -g -s -t 4 disc-patch-icc.yml 2>&1 | tee output.log +../../../swift --external-gravity --hydro --threads=4 disc-patch-icc.yml 2>&1 | tee output.log python plotSolution.py diff --git a/examples/ExternalPointMass/energy_plot.py b/examples/GravityTests/ExternalPointMass/energy_plot.py similarity index 95% rename from examples/ExternalPointMass/energy_plot.py rename to examples/GravityTests/ExternalPointMass/energy_plot.py index 1863305614c226f64faac3d86fa2f809d49b9d74..5644e48f8bd954800526369cc152da7024d069dd 100644 --- a/examples/ExternalPointMass/energy_plot.py +++ b/examples/GravityTests/ExternalPointMass/energy_plot.py @@ -91,8 +91,8 @@ for i in range(402): E_tot_snap[i] = E_kin_snap[i] + E_pot_snap[i] Lz_snap[i] = np.sum(Lz) -print "Starting energy:", E_kin_stats[0], E_pot_stats[0], E_tot_stats[0] -print "Ending energy:", E_kin_stats[-1], E_pot_stats[-1], E_tot_stats[-1] +print("Starting energy:", E_kin_stats[0], E_pot_stats[0], E_tot_stats[0]) +print("Ending energy:", E_kin_stats[-1], E_pot_stats[-1], E_tot_stats[-1]) # Plot energy evolution figure() diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/GravityTests/ExternalPointMass/externalPointMass.yml similarity index 100% rename from examples/ExternalPointMass/externalPointMass.yml rename to examples/GravityTests/ExternalPointMass/externalPointMass.yml diff --git a/examples/ExternalPointMass/makeIC.py b/examples/GravityTests/ExternalPointMass/makeIC.py similarity index 86% rename from examples/ExternalPointMass/makeIC.py rename to examples/GravityTests/ExternalPointMass/makeIC.py index 7a9e2e1fd555e4823957721e3c7bf53da9eff48d..6780430d22e39350e7efeb52190708c78141bd4f 100644 --- a/examples/ExternalPointMass/makeIC.py +++ b/examples/GravityTests/ExternalPointMass/makeIC.py @@ -36,16 +36,16 @@ const_unit_length_in_cgs = (1000*PARSEC_IN_CGS) const_unit_mass_in_cgs = (SOLAR_MASS_IN_CGS) const_unit_velocity_in_cgs = (1e5) -print "UnitMass_in_cgs: ", const_unit_mass_in_cgs -print "UnitLength_in_cgs: ", const_unit_length_in_cgs -print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs -print "UnitTime_in_cgs: ", const_unit_length_in_cgs / const_unit_velocity_in_cgs +print("UnitMass_in_cgs: ", const_unit_mass_in_cgs) +print("UnitLength_in_cgs: ", const_unit_length_in_cgs) +print("UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs) +print("UnitTime_in_cgs: ", const_unit_length_in_cgs / const_unit_velocity_in_cgs) # derived units const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) const_G = ((NEWTON_GRAVITY_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) -print '---------------------' -print 'G in internal units: ', const_G +print('---------------------') +print('G in internal units: ', const_G) # Parameters @@ -53,7 +53,7 @@ periodic = 1 # 1 For periodic box boxSize = 100. # max_radius = boxSize / 4. # maximum radius of particles Mass = 1e10 -print "Mass at the centre: ", Mass +print("Mass at the centre: ", Mass) numPart = int(sys.argv[1]) # Number of particles mass = 1. @@ -93,9 +93,9 @@ grp1 = file.create_group("/PartType1") #generate particle positions radius = max_radius * (numpy.random.rand(numPart))**(1./3.) -print '---------------------' -print 'Radius: minimum = ',min(radius) -print 'Radius: maximum = ',max(radius) +print('---------------------') +print('Radius: minimum = ',min(radius)) +print('Radius: maximum = ',max(radius)) radius = numpy.sort(radius) r = numpy.zeros((numPart, 3)) r[:,0] = radius @@ -104,9 +104,9 @@ r[:,0] = radius speed = numpy.sqrt(const_G * Mass / radius) omega = speed / radius period = 2.*math.pi/omega -print '---------------------' -print 'Period: minimum = ',min(period) -print 'Period: maximum = ',max(period) +print('---------------------') +print('Period: minimum = ',min(period)) +print('Period: maximum = ',max(period)) v = numpy.zeros((numPart, 3)) v[:,0] = -omega * r[:,1] diff --git a/examples/ExternalPointMass/run.sh b/examples/GravityTests/ExternalPointMass/run.sh similarity index 75% rename from examples/ExternalPointMass/run.sh rename to examples/GravityTests/ExternalPointMass/run.sh index e074c384c4e002a161c7d8258e9068663204099f..6f96200e45ceabcece487005560c293cdc084780 100755 --- a/examples/ExternalPointMass/run.sh +++ b/examples/GravityTests/ExternalPointMass/run.sh @@ -8,6 +8,6 @@ then fi rm -rf pointMass_*.hdf5 -../swift -g -t 1 externalPointMass.yml 2>&1 | tee output.log +../../swift --external-gravity --threads=1 externalPointMass.yml 2>&1 | tee output.log python energy_plot.py diff --git a/examples/Gravity_glass/README b/examples/GravityTests/Gravity_glass/README similarity index 100% rename from examples/Gravity_glass/README rename to examples/GravityTests/Gravity_glass/README diff --git a/examples/Gravity_glass/makeIC.py b/examples/GravityTests/Gravity_glass/makeIC.py similarity index 100% rename from examples/Gravity_glass/makeIC.py rename to examples/GravityTests/Gravity_glass/makeIC.py diff --git a/examples/Gravity_glass/uniform_DM_box.yml b/examples/GravityTests/Gravity_glass/uniform_DM_box.yml similarity index 100% rename from examples/Gravity_glass/uniform_DM_box.yml rename to examples/GravityTests/Gravity_glass/uniform_DM_box.yml diff --git a/examples/Hernquist_circularorbit/hernquistcirc.yml b/examples/GravityTests/Hernquist_circularorbit/hernquistcirc.yml similarity index 100% rename from examples/Hernquist_circularorbit/hernquistcirc.yml rename to examples/GravityTests/Hernquist_circularorbit/hernquistcirc.yml diff --git a/examples/Hernquist_circularorbit/makeIC.py b/examples/GravityTests/Hernquist_circularorbit/makeIC.py similarity index 100% rename from examples/Hernquist_circularorbit/makeIC.py rename to examples/GravityTests/Hernquist_circularorbit/makeIC.py diff --git a/examples/Hernquist_circularorbit/plotprog.py b/examples/GravityTests/Hernquist_circularorbit/plotprog.py similarity index 97% rename from examples/Hernquist_circularorbit/plotprog.py rename to examples/GravityTests/Hernquist_circularorbit/plotprog.py index 849ae4e365e0712faac7436a9b01a4e51c9794f0..a19c66e7f30e0e4012a23a4d38dd23045deea6e2 100755 --- a/examples/Hernquist_circularorbit/plotprog.py +++ b/examples/GravityTests/Hernquist_circularorbit/plotprog.py @@ -49,7 +49,6 @@ for i in range(0, lengthrun): zz[:, i] = positions[:, 2] - 500.0 col = ["b", "r", "c", "y", "k"] -print(np.shape(xx), np.shape(yy), np.shape(zz)) for i in range(0, numbpar): plt.plot(xx[i, :], yy[i, :], col[i]) diff --git a/examples/Hernquist_circularorbit/run.sh b/examples/GravityTests/Hernquist_circularorbit/run.sh similarity index 84% rename from examples/Hernquist_circularorbit/run.sh rename to examples/GravityTests/Hernquist_circularorbit/run.sh index a2fedd0914edece43995d20776e048fd85e33963..15cd929a5d2ba62efd4f7af378c9cad4788e7c5f 100755 --- a/examples/Hernquist_circularorbit/run.sh +++ b/examples/GravityTests/Hernquist_circularorbit/run.sh @@ -12,7 +12,7 @@ then fi # self gravity G, external potential g, hydro s, threads t and high verbosity v -../swift -g -t 6 hernquistcirc.yml 2>&1 | tee output.log +../../swift --external-gravity --threads=6 hernquistcirc.yml 2>&1 | tee output.log echo "Save plots of the circular orbits" diff --git a/examples/Hernquist_radialinfall/README b/examples/GravityTests/Hernquist_radialinfall/README similarity index 100% rename from examples/Hernquist_radialinfall/README rename to examples/GravityTests/Hernquist_radialinfall/README diff --git a/examples/Hernquist_radialinfall/hernquist.yml b/examples/GravityTests/Hernquist_radialinfall/hernquist.yml similarity index 100% rename from examples/Hernquist_radialinfall/hernquist.yml rename to examples/GravityTests/Hernquist_radialinfall/hernquist.yml diff --git a/examples/Hernquist_radialinfall/makeIC.py b/examples/GravityTests/Hernquist_radialinfall/makeIC.py similarity index 100% rename from examples/Hernquist_radialinfall/makeIC.py rename to examples/GravityTests/Hernquist_radialinfall/makeIC.py diff --git a/examples/Hernquist_radialinfall/plotprog.py b/examples/GravityTests/Hernquist_radialinfall/plotprog.py similarity index 100% rename from examples/Hernquist_radialinfall/plotprog.py rename to examples/GravityTests/Hernquist_radialinfall/plotprog.py diff --git a/examples/Hernquist_radialinfall/run.sh b/examples/GravityTests/Hernquist_radialinfall/run.sh similarity index 85% rename from examples/Hernquist_radialinfall/run.sh rename to examples/GravityTests/Hernquist_radialinfall/run.sh index b2bcd99f9e7b0e571cffcd18b0fc29e50cad8b06..7381f3ecd4f8aa6711d33607cec52d5cdfedccaf 100755 --- a/examples/Hernquist_radialinfall/run.sh +++ b/examples/GravityTests/Hernquist_radialinfall/run.sh @@ -12,7 +12,7 @@ then fi rm -rf hernquist_*.hdf5 -../swift -g -t 1 hernquist.yml 2>&1 | tee output.log +../../swift --external-gravity --threads=1 hernquist.yml 2>&1 | tee output.log diff --git a/examples/HydrostaticHalo/README b/examples/GravityTests/HydrostaticHalo/README similarity index 100% rename from examples/HydrostaticHalo/README rename to examples/GravityTests/HydrostaticHalo/README diff --git a/examples/HydrostaticHalo/density_profile.py b/examples/GravityTests/HydrostaticHalo/density_profile.py similarity index 100% rename from examples/HydrostaticHalo/density_profile.py rename to examples/GravityTests/HydrostaticHalo/density_profile.py diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/GravityTests/HydrostaticHalo/hydrostatic.yml similarity index 100% rename from examples/HydrostaticHalo/hydrostatic.yml rename to examples/GravityTests/HydrostaticHalo/hydrostatic.yml diff --git a/examples/HydrostaticHalo/internal_energy_profile.py b/examples/GravityTests/HydrostaticHalo/internal_energy_profile.py similarity index 100% rename from examples/HydrostaticHalo/internal_energy_profile.py rename to examples/GravityTests/HydrostaticHalo/internal_energy_profile.py diff --git a/examples/HydrostaticHalo/makeIC.py b/examples/GravityTests/HydrostaticHalo/makeIC.py similarity index 100% rename from examples/HydrostaticHalo/makeIC.py rename to examples/GravityTests/HydrostaticHalo/makeIC.py diff --git a/examples/HydrostaticHalo/run.sh b/examples/GravityTests/HydrostaticHalo/run.sh similarity index 88% rename from examples/HydrostaticHalo/run.sh rename to examples/GravityTests/HydrostaticHalo/run.sh index 82584282559c1fceb0492aada671ff83fb74c924..a3f8b04b1115e316736c1177ecfd8288ed2a045e 100755 --- a/examples/HydrostaticHalo/run.sh +++ b/examples/GravityTests/HydrostaticHalo/run.sh @@ -8,7 +8,7 @@ then fi # Run for 10 dynamical times -../swift -g -s -t 1 hydrostatic.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --threads=1 hydrostatic.yml 2>&1 | tee output.log echo "Plotting density profiles" mkdir plots diff --git a/examples/HydrostaticHalo/test_energy_conservation.py b/examples/GravityTests/HydrostaticHalo/test_energy_conservation.py similarity index 100% rename from examples/HydrostaticHalo/test_energy_conservation.py rename to examples/GravityTests/HydrostaticHalo/test_energy_conservation.py diff --git a/examples/HydrostaticHalo/velocity_profile.py b/examples/GravityTests/HydrostaticHalo/velocity_profile.py similarity index 100% rename from examples/HydrostaticHalo/velocity_profile.py rename to examples/GravityTests/HydrostaticHalo/velocity_profile.py diff --git a/examples/IsothermalPotential/README b/examples/GravityTests/IsothermalPotential/README similarity index 100% rename from examples/IsothermalPotential/README rename to examples/GravityTests/IsothermalPotential/README diff --git a/examples/IsothermalPotential/energy_plot.py b/examples/GravityTests/IsothermalPotential/energy_plot.py similarity index 100% rename from examples/IsothermalPotential/energy_plot.py rename to examples/GravityTests/IsothermalPotential/energy_plot.py diff --git a/examples/IsothermalPotential/isothermal.yml b/examples/GravityTests/IsothermalPotential/isothermal.yml similarity index 100% rename from examples/IsothermalPotential/isothermal.yml rename to examples/GravityTests/IsothermalPotential/isothermal.yml diff --git a/examples/IsothermalPotential/makeIC.py b/examples/GravityTests/IsothermalPotential/makeIC.py similarity index 100% rename from examples/IsothermalPotential/makeIC.py rename to examples/GravityTests/IsothermalPotential/makeIC.py diff --git a/examples/IsothermalPotential/run.sh b/examples/GravityTests/IsothermalPotential/run.sh similarity index 77% rename from examples/IsothermalPotential/run.sh rename to examples/GravityTests/IsothermalPotential/run.sh index a5f03f32f82e27660d0a950335d731cf0ff7401d..4e6a502eddcc081549bc1c967cde9edab9f0b835 100755 --- a/examples/IsothermalPotential/run.sh +++ b/examples/GravityTests/IsothermalPotential/run.sh @@ -8,6 +8,6 @@ then fi rm -rf Isothermal_*.hdf5 -../swift -g -t 1 isothermal.yml 2>&1 | tee output.log +../../swift --external-gravity --threads=1 isothermal.yml 2>&1 | tee output.log python energy_plot.py diff --git a/examples/NFW_Halo/README b/examples/GravityTests/NFW_Halo/README similarity index 100% rename from examples/NFW_Halo/README rename to examples/GravityTests/NFW_Halo/README diff --git a/examples/NFW_Halo/makeIC.py b/examples/GravityTests/NFW_Halo/makeIC.py similarity index 100% rename from examples/NFW_Halo/makeIC.py rename to examples/GravityTests/NFW_Halo/makeIC.py diff --git a/examples/NFW_Halo/makePlots.py b/examples/GravityTests/NFW_Halo/makePlots.py similarity index 100% rename from examples/NFW_Halo/makePlots.py rename to examples/GravityTests/NFW_Halo/makePlots.py diff --git a/examples/NFW_Halo/run.sh b/examples/GravityTests/NFW_Halo/run.sh similarity index 84% rename from examples/NFW_Halo/run.sh rename to examples/GravityTests/NFW_Halo/run.sh index 5501f09ef4076715ae9650daf2cded91dbc0be9e..7f16def75f760d2e4cef4a9303b7c370ebdd1916 100755 --- a/examples/NFW_Halo/run.sh +++ b/examples/GravityTests/NFW_Halo/run.sh @@ -11,7 +11,7 @@ then fi # self gravity G, external potential g, hydro s, threads t and high verbosity v -../swift -g -t 6 test.yml 2>&1 | tee output.log +../../swift --external-gravity --threads=6 test.yml 2>&1 | tee output.log if command -v python3 &>/dev/null; then python3 makePlots.py diff --git a/examples/NFW_Halo/test.yml b/examples/GravityTests/NFW_Halo/test.yml similarity index 100% rename from examples/NFW_Halo/test.yml rename to examples/GravityTests/NFW_Halo/test.yml diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/HydroTests/EvrardCollapse_3D/evrard.yml similarity index 100% rename from examples/EvrardCollapse_3D/evrard.yml rename to examples/HydroTests/EvrardCollapse_3D/evrard.yml diff --git a/examples/EvrardCollapse_3D/getReference.sh b/examples/HydroTests/EvrardCollapse_3D/getReference.sh similarity index 100% rename from examples/EvrardCollapse_3D/getReference.sh rename to examples/HydroTests/EvrardCollapse_3D/getReference.sh diff --git a/examples/EvrardCollapse_3D/makeIC.py b/examples/HydroTests/EvrardCollapse_3D/makeIC.py similarity index 100% rename from examples/EvrardCollapse_3D/makeIC.py rename to examples/HydroTests/EvrardCollapse_3D/makeIC.py diff --git a/examples/EvrardCollapse_3D/plotSolution.py b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py similarity index 100% rename from examples/EvrardCollapse_3D/plotSolution.py rename to examples/HydroTests/EvrardCollapse_3D/plotSolution.py diff --git a/examples/EvrardCollapse_3D/run.sh b/examples/HydroTests/EvrardCollapse_3D/run.sh similarity index 85% rename from examples/EvrardCollapse_3D/run.sh rename to examples/HydroTests/EvrardCollapse_3D/run.sh index abb7614f66fc877aa670db9b0e1335fbfe2e85d2..ae02bcc0baed1aff87c3866b77075f6cb0f89a27 100755 --- a/examples/EvrardCollapse_3D/run.sh +++ b/examples/HydroTests/EvrardCollapse_3D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -G -t 4 evrard.yml 2>&1 | tee output.log +../../swift --hydro --self-gravity --threads=4 evrard.yml 2>&1 | tee output.log # Get the high resolution 1D reference result if not present. if [ ! -e evrardCollapse3D_exact.txt ] diff --git a/examples/Gradients/gradientsCartesian.yml b/examples/HydroTests/Gradients/gradientsCartesian.yml similarity index 100% rename from examples/Gradients/gradientsCartesian.yml rename to examples/HydroTests/Gradients/gradientsCartesian.yml diff --git a/examples/Gradients/gradientsRandom.yml b/examples/HydroTests/Gradients/gradientsRandom.yml similarity index 100% rename from examples/Gradients/gradientsRandom.yml rename to examples/HydroTests/Gradients/gradientsRandom.yml diff --git a/examples/Gradients/gradientsStretched.yml b/examples/HydroTests/Gradients/gradientsStretched.yml similarity index 100% rename from examples/Gradients/gradientsStretched.yml rename to examples/HydroTests/Gradients/gradientsStretched.yml diff --git a/examples/Gradients/makeICs.py b/examples/HydroTests/Gradients/makeICs.py similarity index 100% rename from examples/Gradients/makeICs.py rename to examples/HydroTests/Gradients/makeICs.py diff --git a/examples/Gradients/plot.py b/examples/HydroTests/Gradients/plot.py similarity index 100% rename from examples/Gradients/plot.py rename to examples/HydroTests/Gradients/plot.py diff --git a/examples/Gradients/run.sh b/examples/HydroTests/Gradients/run.sh similarity index 61% rename from examples/Gradients/run.sh rename to examples/HydroTests/Gradients/run.sh index 44c25ac5695175c40483d9f8b3bbd160b2fcbc0a..86ee9a68389319b1cb5f7327e8bd689b6212e6c1 100755 --- a/examples/Gradients/run.sh +++ b/examples/HydroTests/Gradients/run.sh @@ -1,13 +1,13 @@ #! /bin/bash python makeICs.py stretched -../swift -s -t 2 gradientsStretched.yml +../../swift --hydro --threads=2 gradientsStretched.yml python plot.py gradients_stretched_0001.hdf5 stretched python makeICs.py cartesian -../swift -s -t 2 gradientsCartesian.yml +../../swift --hydro --threads=2 gradientsCartesian.yml python plot.py gradients_cartesian_0001.hdf5 cartesian python makeICs.py random -../swift -s -t 2 gradientsRandom.yml +../../swift --hydro --threads=2 gradientsRandom.yml python plot.py gradients_random_0001.hdf5 random diff --git a/examples/GreshoVortex_2D/getGlass.sh b/examples/HydroTests/GreshoVortex_2D/getGlass.sh similarity index 100% rename from examples/GreshoVortex_2D/getGlass.sh rename to examples/HydroTests/GreshoVortex_2D/getGlass.sh diff --git a/examples/GreshoVortex_2D/gresho.yml b/examples/HydroTests/GreshoVortex_2D/gresho.yml similarity index 100% rename from examples/GreshoVortex_2D/gresho.yml rename to examples/HydroTests/GreshoVortex_2D/gresho.yml diff --git a/examples/GreshoVortex_2D/makeIC.py b/examples/HydroTests/GreshoVortex_2D/makeIC.py similarity index 100% rename from examples/GreshoVortex_2D/makeIC.py rename to examples/HydroTests/GreshoVortex_2D/makeIC.py diff --git a/examples/GreshoVortex_2D/plotSolution.py b/examples/HydroTests/GreshoVortex_2D/plotSolution.py similarity index 100% rename from examples/GreshoVortex_2D/plotSolution.py rename to examples/HydroTests/GreshoVortex_2D/plotSolution.py diff --git a/examples/GreshoVortex_2D/run.sh b/examples/HydroTests/GreshoVortex_2D/run.sh similarity index 86% rename from examples/GreshoVortex_2D/run.sh rename to examples/HydroTests/GreshoVortex_2D/run.sh index 6d537bcc96c68385434f685bd551a2d423f469e0..0e24112a0faafcd38a06216494f87888f3e132e2 100755 --- a/examples/GreshoVortex_2D/run.sh +++ b/examples/HydroTests/GreshoVortex_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 1 gresho.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 gresho.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 11 diff --git a/examples/GreshoVortex_3D/getGlass.sh b/examples/HydroTests/GreshoVortex_3D/getGlass.sh similarity index 100% rename from examples/GreshoVortex_3D/getGlass.sh rename to examples/HydroTests/GreshoVortex_3D/getGlass.sh diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/HydroTests/GreshoVortex_3D/gresho.yml similarity index 100% rename from examples/GreshoVortex_3D/gresho.yml rename to examples/HydroTests/GreshoVortex_3D/gresho.yml diff --git a/examples/GreshoVortex_3D/makeIC.py b/examples/HydroTests/GreshoVortex_3D/makeIC.py similarity index 100% rename from examples/GreshoVortex_3D/makeIC.py rename to examples/HydroTests/GreshoVortex_3D/makeIC.py diff --git a/examples/GreshoVortex_3D/plotSolution.py b/examples/HydroTests/GreshoVortex_3D/plotSolution.py similarity index 100% rename from examples/GreshoVortex_3D/plotSolution.py rename to examples/HydroTests/GreshoVortex_3D/plotSolution.py diff --git a/examples/GreshoVortex_3D/run.sh b/examples/HydroTests/GreshoVortex_3D/run.sh similarity index 86% rename from examples/GreshoVortex_3D/run.sh rename to examples/HydroTests/GreshoVortex_3D/run.sh index da7d6cee111aebcfd2fcb0f3508af80ef73cbeb0..15b613782e685d86321460b33c52fe9109230840 100755 --- a/examples/GreshoVortex_3D/run.sh +++ b/examples/HydroTests/GreshoVortex_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 4 gresho.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 gresho.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 11 diff --git a/examples/InteractingBlastWaves_1D/getReference.sh b/examples/HydroTests/InteractingBlastWaves_1D/getReference.sh similarity index 100% rename from examples/InteractingBlastWaves_1D/getReference.sh rename to examples/HydroTests/InteractingBlastWaves_1D/getReference.sh diff --git a/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml b/examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml similarity index 100% rename from examples/InteractingBlastWaves_1D/interactingBlastWaves.yml rename to examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml diff --git a/examples/InteractingBlastWaves_1D/makeIC.py b/examples/HydroTests/InteractingBlastWaves_1D/makeIC.py similarity index 100% rename from examples/InteractingBlastWaves_1D/makeIC.py rename to examples/HydroTests/InteractingBlastWaves_1D/makeIC.py diff --git a/examples/InteractingBlastWaves_1D/plotSolution.py b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py similarity index 100% rename from examples/InteractingBlastWaves_1D/plotSolution.py rename to examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py diff --git a/examples/InteractingBlastWaves_1D/run.sh b/examples/HydroTests/InteractingBlastWaves_1D/run.sh similarity index 85% rename from examples/InteractingBlastWaves_1D/run.sh rename to examples/HydroTests/InteractingBlastWaves_1D/run.sh index 31717bd806ddd6c98c24dfc1def6f79dddff42ff..42034d5e541c4e038a9284e88651cb6a9fa9013f 100755 --- a/examples/InteractingBlastWaves_1D/run.sh +++ b/examples/HydroTests/InteractingBlastWaves_1D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 interactingBlastWaves.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 interactingBlastWaves.yml 2>&1 | tee output.log # Get the high resolution reference solution if not present. if [ ! -e interactingBlastWaves1D_exact.txt ] diff --git a/examples/KelvinHelmholtzGrowthRate_2D/getGlass.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/getGlass.sh similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_2D/getGlass.sh rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/getGlass.sh diff --git a/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_2D/makeIC.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC.py diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py diff --git a/examples/KelvinHelmholtzGrowthRate_2D/plotSolution.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/plotSolution.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_2D/plotSolution.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/plotSolution.py diff --git a/examples/KelvinHelmholtzGrowthRate_2D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh similarity index 78% rename from examples/KelvinHelmholtzGrowthRate_2D/run.sh rename to examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh index 3e6e026f66b14846a5c6e8e9daf99797dc3ff87a..4e565a2de588c53f54c928b3b9f7dfec483c8220 100755 --- a/examples/KelvinHelmholtzGrowthRate_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh @@ -9,7 +9,7 @@ then fi # Run SWIFT -../swift -s -t 1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 100 diff --git a/examples/KelvinHelmholtzGrowthRate_3D/getGlass.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/getGlass.sh similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_3D/getGlass.sh rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/getGlass.sh diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_3D/makeIC.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC.py diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py diff --git a/examples/KelvinHelmholtzGrowthRate_3D/plotSolution.py b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/plotSolution.py similarity index 100% rename from examples/KelvinHelmholtzGrowthRate_3D/plotSolution.py rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/plotSolution.py diff --git a/examples/KelvinHelmholtzGrowthRate_3D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh similarity index 78% rename from examples/KelvinHelmholtzGrowthRate_3D/run.sh rename to examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh index 3e6e026f66b14846a5c6e8e9daf99797dc3ff87a..4e565a2de588c53f54c928b3b9f7dfec483c8220 100755 --- a/examples/KelvinHelmholtzGrowthRate_3D/run.sh +++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh @@ -9,7 +9,7 @@ then fi # Run SWIFT -../swift -s -t 1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 100 diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/HydroTests/KelvinHelmholtz_2D/kelvinHelmholtz.yml similarity index 100% rename from examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml rename to examples/HydroTests/KelvinHelmholtz_2D/kelvinHelmholtz.yml diff --git a/examples/KelvinHelmholtz_2D/makeIC.py b/examples/HydroTests/KelvinHelmholtz_2D/makeIC.py similarity index 100% rename from examples/KelvinHelmholtz_2D/makeIC.py rename to examples/HydroTests/KelvinHelmholtz_2D/makeIC.py diff --git a/examples/KelvinHelmholtz_2D/makeMovie.py b/examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py similarity index 100% rename from examples/KelvinHelmholtz_2D/makeMovie.py rename to examples/HydroTests/KelvinHelmholtz_2D/makeMovie.py diff --git a/examples/KelvinHelmholtz_2D/plotSolution.py b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py similarity index 100% rename from examples/KelvinHelmholtz_2D/plotSolution.py rename to examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py diff --git a/examples/KelvinHelmholtz_2D/run.sh b/examples/HydroTests/KelvinHelmholtz_2D/run.sh similarity index 79% rename from examples/KelvinHelmholtz_2D/run.sh rename to examples/HydroTests/KelvinHelmholtz_2D/run.sh index dbb39caf383279dbc71c2baa125499d115538654..355bf052a7ad124bcb4d88254ad780a7ffa97aba 100755 --- a/examples/KelvinHelmholtz_2D/run.sh +++ b/examples/HydroTests/KelvinHelmholtz_2D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 4 kelvinHelmholtz.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 kelvinHelmholtz.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 6 diff --git a/examples/KeplerianRing/README.md b/examples/HydroTests/KeplerianRing/README.md similarity index 97% rename from examples/KeplerianRing/README.md rename to examples/HydroTests/KeplerianRing/README.md index 1c361f275d60ef1ca46d696e2e9507bb749e531c..1cb2e2119d0f0bb093abf194ab18da91dd587d32 100644 --- a/examples/KeplerianRing/README.md +++ b/examples/HydroTests/KeplerianRing/README.md @@ -69,7 +69,7 @@ Plotting Once you have ran swift (we suggest that you use the following) - ../swift -g -S -s -t 16 keplerian_ring.yml 2>&1 | tee output.log + ../swift --external-gravity --stars --hydro --threads=16 keplerian_ring.yml 2>&1 | tee output.log there will be around 350 ```.hdf5``` files in your directory. To check out the results of the example use the plotting script: diff --git a/examples/KeplerianRing/keplerian_ring.yml b/examples/HydroTests/KeplerianRing/keplerian_ring.yml similarity index 100% rename from examples/KeplerianRing/keplerian_ring.yml rename to examples/HydroTests/KeplerianRing/keplerian_ring.yml diff --git a/examples/KeplerianRing/makeIC.py b/examples/HydroTests/KeplerianRing/makeIC.py similarity index 100% rename from examples/KeplerianRing/makeIC.py rename to examples/HydroTests/KeplerianRing/makeIC.py diff --git a/examples/KeplerianRing/make_movie.py b/examples/HydroTests/KeplerianRing/make_movie.py similarity index 100% rename from examples/KeplerianRing/make_movie.py rename to examples/HydroTests/KeplerianRing/make_movie.py diff --git a/examples/KeplerianRing/plotSolution.py b/examples/HydroTests/KeplerianRing/plotSolution.py similarity index 100% rename from examples/KeplerianRing/plotSolution.py rename to examples/HydroTests/KeplerianRing/plotSolution.py diff --git a/examples/KeplerianRing/run.sh b/examples/HydroTests/KeplerianRing/run.sh similarity index 79% rename from examples/KeplerianRing/run.sh rename to examples/HydroTests/KeplerianRing/run.sh index 0195846a8839a27083594c20569b1fd4d49f4c16..b11a0c1e52f2792447ffe39efbdf5c7b2ddda437 100755 --- a/examples/KeplerianRing/run.sh +++ b/examples/HydroTests/KeplerianRing/run.sh @@ -9,7 +9,7 @@ then fi rm -rf keplerian_ring_*.hdf5 -../swift -g -s -t 1 -v 1 keplerian_ring.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --threads=1 --verbose=1 keplerian_ring.yml 2>&1 | tee output.log echo echo diff --git a/examples/KeplerianRing/testplots.py b/examples/HydroTests/KeplerianRing/testplots.py similarity index 100% rename from examples/KeplerianRing/testplots.py rename to examples/HydroTests/KeplerianRing/testplots.py diff --git a/examples/KeplerianRing/write_gadget.py b/examples/HydroTests/KeplerianRing/write_gadget.py similarity index 100% rename from examples/KeplerianRing/write_gadget.py rename to examples/HydroTests/KeplerianRing/write_gadget.py diff --git a/examples/Noh_1D/makeIC.py b/examples/HydroTests/Noh_1D/makeIC.py similarity index 100% rename from examples/Noh_1D/makeIC.py rename to examples/HydroTests/Noh_1D/makeIC.py diff --git a/examples/Noh_1D/noh.yml b/examples/HydroTests/Noh_1D/noh.yml similarity index 100% rename from examples/Noh_1D/noh.yml rename to examples/HydroTests/Noh_1D/noh.yml diff --git a/examples/Noh_1D/plotSolution.py b/examples/HydroTests/Noh_1D/plotSolution.py similarity index 100% rename from examples/Noh_1D/plotSolution.py rename to examples/HydroTests/Noh_1D/plotSolution.py diff --git a/examples/Noh_1D/run.sh b/examples/HydroTests/Noh_1D/run.sh similarity index 79% rename from examples/Noh_1D/run.sh rename to examples/HydroTests/Noh_1D/run.sh index 77788bfa8429e2fbf0502068baa70598acaaa791..0a7bd0574c19428a7f82141e619aed1f49e677be 100755 --- a/examples/Noh_1D/run.sh +++ b/examples/HydroTests/Noh_1D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 noh.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 noh.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 12 diff --git a/examples/Noh_2D/getGlass.sh b/examples/HydroTests/Noh_2D/getGlass.sh similarity index 100% rename from examples/Noh_2D/getGlass.sh rename to examples/HydroTests/Noh_2D/getGlass.sh diff --git a/examples/Noh_2D/makeIC.py b/examples/HydroTests/Noh_2D/makeIC.py similarity index 100% rename from examples/Noh_2D/makeIC.py rename to examples/HydroTests/Noh_2D/makeIC.py diff --git a/examples/Noh_2D/noh.yml b/examples/HydroTests/Noh_2D/noh.yml similarity index 100% rename from examples/Noh_2D/noh.yml rename to examples/HydroTests/Noh_2D/noh.yml diff --git a/examples/Noh_2D/plotSolution.py b/examples/HydroTests/Noh_2D/plotSolution.py similarity index 100% rename from examples/Noh_2D/plotSolution.py rename to examples/HydroTests/Noh_2D/plotSolution.py diff --git a/examples/Noh_2D/run.sh b/examples/HydroTests/Noh_2D/run.sh similarity index 85% rename from examples/Noh_2D/run.sh rename to examples/HydroTests/Noh_2D/run.sh index cff200801018e04ea560bd2c3fbd84057aec2d7c..36e2d7db554823c60bacbd2d907b9d06789a9fcd 100755 --- a/examples/Noh_2D/run.sh +++ b/examples/HydroTests/Noh_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 2 noh.yml 2>&1 | tee output.log +../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 12 diff --git a/examples/Noh_3D/getGlass.sh b/examples/HydroTests/Noh_3D/getGlass.sh similarity index 100% rename from examples/Noh_3D/getGlass.sh rename to examples/HydroTests/Noh_3D/getGlass.sh diff --git a/examples/Noh_3D/makeIC.py b/examples/HydroTests/Noh_3D/makeIC.py similarity index 100% rename from examples/Noh_3D/makeIC.py rename to examples/HydroTests/Noh_3D/makeIC.py diff --git a/examples/Noh_3D/noh.yml b/examples/HydroTests/Noh_3D/noh.yml similarity index 100% rename from examples/Noh_3D/noh.yml rename to examples/HydroTests/Noh_3D/noh.yml diff --git a/examples/Noh_3D/plotSolution.py b/examples/HydroTests/Noh_3D/plotSolution.py similarity index 100% rename from examples/Noh_3D/plotSolution.py rename to examples/HydroTests/Noh_3D/plotSolution.py diff --git a/examples/Noh_3D/run.sh b/examples/HydroTests/Noh_3D/run.sh similarity index 85% rename from examples/Noh_3D/run.sh rename to examples/HydroTests/Noh_3D/run.sh index b9e4fb145b2465433aa2bc0362aba19cc1267461..7845b5cfb592f0f8ac4c3951b48689623c06b21c 100755 --- a/examples/Noh_3D/run.sh +++ b/examples/HydroTests/Noh_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 2 noh.yml 2>&1 | tee output.log +../../swift --hydro --threads=2 noh.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 12 diff --git a/examples/PerturbedBox_2D/makeIC.py b/examples/HydroTests/PerturbedBox_2D/makeIC.py similarity index 100% rename from examples/PerturbedBox_2D/makeIC.py rename to examples/HydroTests/PerturbedBox_2D/makeIC.py diff --git a/examples/PerturbedBox_2D/perturbedPlane.yml b/examples/HydroTests/PerturbedBox_2D/perturbedPlane.yml similarity index 100% rename from examples/PerturbedBox_2D/perturbedPlane.yml rename to examples/HydroTests/PerturbedBox_2D/perturbedPlane.yml diff --git a/examples/PerturbedBox_3D/makeIC.py b/examples/HydroTests/PerturbedBox_3D/makeIC.py similarity index 100% rename from examples/PerturbedBox_3D/makeIC.py rename to examples/HydroTests/PerturbedBox_3D/makeIC.py diff --git a/examples/PerturbedBox_3D/perturbedBox.yml b/examples/HydroTests/PerturbedBox_3D/perturbedBox.yml similarity index 100% rename from examples/PerturbedBox_3D/perturbedBox.yml rename to examples/HydroTests/PerturbedBox_3D/perturbedBox.yml diff --git a/examples/PerturbedBox_3D/run.sh b/examples/HydroTests/PerturbedBox_3D/run.sh similarity index 74% rename from examples/PerturbedBox_3D/run.sh rename to examples/HydroTests/PerturbedBox_3D/run.sh index e20bff52d18322ce377fb589900fd9e13eefe64d..463f6fecf16e13c76d713bba3ef4112ffbde509c 100755 --- a/examples/PerturbedBox_3D/run.sh +++ b/examples/HydroTests/PerturbedBox_3D/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 50 fi -../swift -s -t 16 perturbedBox.yml 2>&1 | tee output.log +../../swift --hydro --threads=16 perturbedBox.yml 2>&1 | tee output.log diff --git a/examples/SedovBlast_1D/makeIC.py b/examples/HydroTests/SedovBlast_1D/makeIC.py similarity index 100% rename from examples/SedovBlast_1D/makeIC.py rename to examples/HydroTests/SedovBlast_1D/makeIC.py diff --git a/examples/SedovBlast_1D/plotSolution.py b/examples/HydroTests/SedovBlast_1D/plotSolution.py similarity index 94% rename from examples/SedovBlast_1D/plotSolution.py rename to examples/HydroTests/SedovBlast_1D/plotSolution.py index 2738b7c8f301a7351d962ac0f29faccd0a770fc9..c6d4a989da7493f7b500946610eea8832696bf6f 100644 --- a/examples/SedovBlast_1D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_1D/plotSolution.py @@ -83,6 +83,12 @@ S = sim["/PartType0/Entropy"][:] P = sim["/PartType0/Pressure"][:] rho = sim["/PartType0/Density"][:] +try: + alpha = sim["/PartType0/Viscosity"][:] + plot_alpha = True +except: + plot_alpha = False + # Now, work our the solution.... @@ -246,14 +252,23 @@ ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) xlim(0, 1.3 * r_shock) ylim(-2, 22) -# Entropy profile --------------------------------- +# Entropy/alpha profile --------------------------------- subplot(235) -plot(r, S, '.', color='r', ms=2.) -plot(r_s, s_s, '--', color='k', alpha=0.8, lw=1.2) + +if plot_alpha: + plot(r, alpha, '.', color='r', ms=2.0) + plot([r_shock, r_shock], [-1, 2], "--", color="k", alpha=0.8, lw=1.2) + ylabel(r"${\rm{Viscosity}}~\alpha$", labelpad=0) + # Show location of shock + ylim(0, 2) +else: + plot(r, S, '.', color='r', ms=2.0) + plot(r_s, s_s, '--', color='k', alpha=0.8, lw=1.2) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(-5, 50) + xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=0) xlim(0, 1.3 * r_shock) -ylim(-5, 50) # Information ------------------------------------- subplot(236, frameon=False) diff --git a/examples/SedovBlast_1D/run.sh b/examples/HydroTests/SedovBlast_1D/run.sh similarity index 77% rename from examples/SedovBlast_1D/run.sh rename to examples/HydroTests/SedovBlast_1D/run.sh index 4b9a84f069673bd6def3b96faec71b9d4fdd0dda..2888790eb1877541166c04002f9ae9539e9ef6d7 100755 --- a/examples/SedovBlast_1D/run.sh +++ b/examples/HydroTests/SedovBlast_1D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 sedov.yml 2>&1 | tee output.log +../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 5 diff --git a/examples/SedovBlast_2D/sedov.yml b/examples/HydroTests/SedovBlast_1D/sedov.yml similarity index 95% rename from examples/SedovBlast_2D/sedov.yml rename to examples/HydroTests/SedovBlast_1D/sedov.yml index 84177ece31ef98ec55c41513276c9c0158e69bcf..b4252581d6eb3b2932a074e7545b2d308be51865 100644 --- a/examples/SedovBlast_2D/sedov.yml +++ b/examples/HydroTests/SedovBlast_1D/sedov.yml @@ -11,7 +11,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 5e-2 # The end time of the simulation (in internal units). dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: diff --git a/examples/SedovBlast_2D/getGlass.sh b/examples/HydroTests/SedovBlast_2D/getGlass.sh similarity index 100% rename from examples/SedovBlast_2D/getGlass.sh rename to examples/HydroTests/SedovBlast_2D/getGlass.sh diff --git a/examples/SedovBlast_2D/makeIC.py b/examples/HydroTests/SedovBlast_2D/makeIC.py similarity index 100% rename from examples/SedovBlast_2D/makeIC.py rename to examples/HydroTests/SedovBlast_2D/makeIC.py diff --git a/examples/SedovBlast_2D/plotSolution.py b/examples/HydroTests/SedovBlast_2D/plotSolution.py similarity index 100% rename from examples/SedovBlast_2D/plotSolution.py rename to examples/HydroTests/SedovBlast_2D/plotSolution.py diff --git a/examples/SedovBlast_2D/run.sh b/examples/HydroTests/SedovBlast_2D/run.sh similarity index 83% rename from examples/SedovBlast_2D/run.sh rename to examples/HydroTests/SedovBlast_2D/run.sh index a32c8f0d6f3116d5486fe1bd086bf8df49d06020..0ad75f9d378712bd62cdd47c66240976cc57f04c 100755 --- a/examples/SedovBlast_2D/run.sh +++ b/examples/HydroTests/SedovBlast_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 1 sedov.yml 2>&1 | tee output.log +../../swift --hydro --limiter --threads=1 sedov.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 5 diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/HydroTests/SedovBlast_2D/sedov.yml similarity index 91% rename from examples/SedovBlast_1D/sedov.yml rename to examples/HydroTests/SedovBlast_2D/sedov.yml index b4912a95e797440dc6eb0c9f48806a5954adbc41..b4252581d6eb3b2932a074e7545b2d308be51865 100644 --- a/examples/SedovBlast_1D/sedov.yml +++ b/examples/HydroTests/SedovBlast_2D/sedov.yml @@ -11,7 +11,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 5e-2 # The end time of the simulation (in internal units). dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-5 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: @@ -21,7 +21,7 @@ Snapshots: # Parameters governing the conserved quantities statistics Statistics: - delta_time: 1e-5 # Time between statistics output + delta_time: 1e-3 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: diff --git a/examples/SedovBlast_3D/getGlass.sh b/examples/HydroTests/SedovBlast_3D/getGlass.sh similarity index 100% rename from examples/SedovBlast_3D/getGlass.sh rename to examples/HydroTests/SedovBlast_3D/getGlass.sh diff --git a/examples/SedovBlast_3D/makeIC.py b/examples/HydroTests/SedovBlast_3D/makeIC.py similarity index 100% rename from examples/SedovBlast_3D/makeIC.py rename to examples/HydroTests/SedovBlast_3D/makeIC.py diff --git a/examples/SedovBlast_3D/plotSolution.py b/examples/HydroTests/SedovBlast_3D/plotSolution.py similarity index 100% rename from examples/SedovBlast_3D/plotSolution.py rename to examples/HydroTests/SedovBlast_3D/plotSolution.py diff --git a/examples/SedovBlast_3D/run.sh b/examples/HydroTests/SedovBlast_3D/run.sh similarity index 83% rename from examples/SedovBlast_3D/run.sh rename to examples/HydroTests/SedovBlast_3D/run.sh index 00d5e5b91c31e64f824a3d2a28c8e1a126684a74..62af72bacca62cd18fa77300d887b0bf0dd20789 100755 --- a/examples/SedovBlast_3D/run.sh +++ b/examples/HydroTests/SedovBlast_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 4 sedov.yml 2>&1 | tee output.log +../../swift --hydro --limiter --threads=4 sedov.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 5 diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/HydroTests/SedovBlast_3D/sedov.yml similarity index 95% rename from examples/SedovBlast_3D/sedov.yml rename to examples/HydroTests/SedovBlast_3D/sedov.yml index 6cf5b02427b8004787b646e6bcdd4bacaa25bc06..19e8c72538a748304ca4da076458c9ae27dc8f46 100644 --- a/examples/SedovBlast_3D/sedov.yml +++ b/examples/HydroTests/SedovBlast_3D/sedov.yml @@ -11,7 +11,7 @@ TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 5e-2 # The end time of the simulation (in internal units). dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots Snapshots: diff --git a/examples/SineWavePotential_1D/makeIC.py b/examples/HydroTests/SineWavePotential_1D/makeIC.py similarity index 100% rename from examples/SineWavePotential_1D/makeIC.py rename to examples/HydroTests/SineWavePotential_1D/makeIC.py diff --git a/examples/SineWavePotential_1D/plotSolution.py b/examples/HydroTests/SineWavePotential_1D/plotSolution.py similarity index 100% rename from examples/SineWavePotential_1D/plotSolution.py rename to examples/HydroTests/SineWavePotential_1D/plotSolution.py diff --git a/examples/SineWavePotential_1D/run.sh b/examples/HydroTests/SineWavePotential_1D/run.sh similarity index 70% rename from examples/SineWavePotential_1D/run.sh rename to examples/HydroTests/SineWavePotential_1D/run.sh index 077cf1c0cc64ef7a85cfd0e67f8f490b0de4ba37..920bd413a71a99e043cc7d31be088e0037c6c7c1 100755 --- a/examples/SineWavePotential_1D/run.sh +++ b/examples/HydroTests/SineWavePotential_1D/run.sh @@ -6,7 +6,7 @@ then python makeIC.py fi -../swift -g -s -t 2 sineWavePotential.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do diff --git a/examples/SineWavePotential_1D/sineWavePotential.yml b/examples/HydroTests/SineWavePotential_1D/sineWavePotential.yml similarity index 100% rename from examples/SineWavePotential_1D/sineWavePotential.yml rename to examples/HydroTests/SineWavePotential_1D/sineWavePotential.yml diff --git a/examples/SineWavePotential_2D/makeIC.py b/examples/HydroTests/SineWavePotential_2D/makeIC.py similarity index 100% rename from examples/SineWavePotential_2D/makeIC.py rename to examples/HydroTests/SineWavePotential_2D/makeIC.py diff --git a/examples/SineWavePotential_2D/plotSolution.py b/examples/HydroTests/SineWavePotential_2D/plotSolution.py similarity index 100% rename from examples/SineWavePotential_2D/plotSolution.py rename to examples/HydroTests/SineWavePotential_2D/plotSolution.py diff --git a/examples/SineWavePotential_2D/run.sh b/examples/HydroTests/SineWavePotential_2D/run.sh similarity index 70% rename from examples/SineWavePotential_2D/run.sh rename to examples/HydroTests/SineWavePotential_2D/run.sh index 077cf1c0cc64ef7a85cfd0e67f8f490b0de4ba37..920bd413a71a99e043cc7d31be088e0037c6c7c1 100755 --- a/examples/SineWavePotential_2D/run.sh +++ b/examples/HydroTests/SineWavePotential_2D/run.sh @@ -6,7 +6,7 @@ then python makeIC.py fi -../swift -g -s -t 2 sineWavePotential.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do diff --git a/examples/SineWavePotential_2D/sineWavePotential.yml b/examples/HydroTests/SineWavePotential_2D/sineWavePotential.yml similarity index 100% rename from examples/SineWavePotential_2D/sineWavePotential.yml rename to examples/HydroTests/SineWavePotential_2D/sineWavePotential.yml diff --git a/examples/SineWavePotential_3D/makeIC.py b/examples/HydroTests/SineWavePotential_3D/makeIC.py similarity index 100% rename from examples/SineWavePotential_3D/makeIC.py rename to examples/HydroTests/SineWavePotential_3D/makeIC.py diff --git a/examples/SineWavePotential_3D/plotSolution.py b/examples/HydroTests/SineWavePotential_3D/plotSolution.py similarity index 100% rename from examples/SineWavePotential_3D/plotSolution.py rename to examples/HydroTests/SineWavePotential_3D/plotSolution.py diff --git a/examples/SineWavePotential_3D/run.sh b/examples/HydroTests/SineWavePotential_3D/run.sh similarity index 70% rename from examples/SineWavePotential_3D/run.sh rename to examples/HydroTests/SineWavePotential_3D/run.sh index 077cf1c0cc64ef7a85cfd0e67f8f490b0de4ba37..920bd413a71a99e043cc7d31be088e0037c6c7c1 100755 --- a/examples/SineWavePotential_3D/run.sh +++ b/examples/HydroTests/SineWavePotential_3D/run.sh @@ -6,7 +6,7 @@ then python makeIC.py fi -../swift -g -s -t 2 sineWavePotential.yml 2>&1 | tee output.log +../../swift --external-gravity --hydro --threads=2 sineWavePotential.yml 2>&1 | tee output.log for f in sineWavePotential_*.hdf5 do diff --git a/examples/SineWavePotential_3D/sineWavePotential.yml b/examples/HydroTests/SineWavePotential_3D/sineWavePotential.yml similarity index 100% rename from examples/SineWavePotential_3D/sineWavePotential.yml rename to examples/HydroTests/SineWavePotential_3D/sineWavePotential.yml diff --git a/examples/SodShock_2D/getGlass.sh b/examples/HydroTests/SodShockSpherical_2D/getGlass.sh similarity index 100% rename from examples/SodShock_2D/getGlass.sh rename to examples/HydroTests/SodShockSpherical_2D/getGlass.sh diff --git a/examples/SodShockSpherical_2D/getReference.sh b/examples/HydroTests/SodShockSpherical_2D/getReference.sh similarity index 100% rename from examples/SodShockSpherical_2D/getReference.sh rename to examples/HydroTests/SodShockSpherical_2D/getReference.sh diff --git a/examples/SodShockSpherical_2D/makeIC.py b/examples/HydroTests/SodShockSpherical_2D/makeIC.py similarity index 100% rename from examples/SodShockSpherical_2D/makeIC.py rename to examples/HydroTests/SodShockSpherical_2D/makeIC.py diff --git a/examples/SodShockSpherical_2D/plotSolution.py b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py similarity index 100% rename from examples/SodShockSpherical_2D/plotSolution.py rename to examples/HydroTests/SodShockSpherical_2D/plotSolution.py diff --git a/examples/SodShockSpherical_2D/run.sh b/examples/HydroTests/SodShockSpherical_2D/run.sh similarity index 89% rename from examples/SodShockSpherical_2D/run.sh rename to examples/HydroTests/SodShockSpherical_2D/run.sh index d662d20f40ef9e221285d5820e867607804e9dbe..609f2e0ae065a1fa76ee7bcfa90efa9cb1aa020a 100755 --- a/examples/SodShockSpherical_2D/run.sh +++ b/examples/HydroTests/SodShockSpherical_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 1 sodShock.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log # Get the high resolution 1D reference solution if not present. if [ ! -e sodShockSpherical2D_exact.txt ] diff --git a/examples/SodShockSpherical_2D/sodShock.yml b/examples/HydroTests/SodShockSpherical_2D/sodShock.yml similarity index 100% rename from examples/SodShockSpherical_2D/sodShock.yml rename to examples/HydroTests/SodShockSpherical_2D/sodShock.yml diff --git a/examples/SodShock_3D/getGlass.sh b/examples/HydroTests/SodShockSpherical_3D/getGlass.sh similarity index 100% rename from examples/SodShock_3D/getGlass.sh rename to examples/HydroTests/SodShockSpherical_3D/getGlass.sh diff --git a/examples/SodShockSpherical_3D/getReference.sh b/examples/HydroTests/SodShockSpherical_3D/getReference.sh similarity index 100% rename from examples/SodShockSpherical_3D/getReference.sh rename to examples/HydroTests/SodShockSpherical_3D/getReference.sh diff --git a/examples/SodShockSpherical_3D/makeIC.py b/examples/HydroTests/SodShockSpherical_3D/makeIC.py similarity index 100% rename from examples/SodShockSpherical_3D/makeIC.py rename to examples/HydroTests/SodShockSpherical_3D/makeIC.py diff --git a/examples/SodShockSpherical_3D/plotSolution.py b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py similarity index 100% rename from examples/SodShockSpherical_3D/plotSolution.py rename to examples/HydroTests/SodShockSpherical_3D/plotSolution.py diff --git a/examples/SodShockSpherical_3D/run.sh b/examples/HydroTests/SodShockSpherical_3D/run.sh similarity index 89% rename from examples/SodShockSpherical_3D/run.sh rename to examples/HydroTests/SodShockSpherical_3D/run.sh index faf979869e175172ce31db6ac5021daf1758f3b0..c511dbcc6b18248bcfd33a9e0216cd22cf26aead 100755 --- a/examples/SodShockSpherical_3D/run.sh +++ b/examples/HydroTests/SodShockSpherical_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 4 sodShock.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log # Get the high resolution 1D reference solution if not present. if [ ! -e sodShockSpherical3D_exact.txt ] diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/HydroTests/SodShockSpherical_3D/sodShock.yml similarity index 100% rename from examples/SodShockSpherical_3D/sodShock.yml rename to examples/HydroTests/SodShockSpherical_3D/sodShock.yml diff --git a/examples/SodShock_1D/makeIC.py b/examples/HydroTests/SodShock_1D/makeIC.py similarity index 100% rename from examples/SodShock_1D/makeIC.py rename to examples/HydroTests/SodShock_1D/makeIC.py diff --git a/examples/SodShock_1D/plotSolution.py b/examples/HydroTests/SodShock_1D/plotSolution.py similarity index 93% rename from examples/SodShock_1D/plotSolution.py rename to examples/HydroTests/SodShock_1D/plotSolution.py index 0149d66a0c28c777a4265da10d86ed160086995d..12ae8a9cf26fb715281ed00bf565c5c8d9a234fb 100644 --- a/examples/SodShock_1D/plotSolution.py +++ b/examples/HydroTests/SodShock_1D/plotSolution.py @@ -87,6 +87,11 @@ try: plot_alpha = True except: plot_alpha = False +try: + alpha_diff = sim["PartType0/Diffusion"][:] + plot_alpha_diff = True +except: + plot_alpha_diff = False N = 1000 # Number of points x_min = -1. @@ -239,12 +244,20 @@ ylim(-0.1, 0.95) # Density profile -------------------------------- subplot(232) -plot(x, rho, '.', color='r', ms=4.0) -plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2) +if plot_alpha_diff: + plot(x, alpha_diff, '.', color='r', ms=4.0) + ylabel(r"${\rm{Diffusion}}~\alpha$", labelpad=0) + # Show location of contact discontinuity + plot([x_34, x_34], [-100, 100], color="k", alpha=0.5, ls="dashed", lw=1.2) + ylim(0, 1) +else: + plot(x, rho, '.', color='r', ms=4.0) + plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2) + ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + ylim(0.05, 1.1) + xlabel("${\\rm{Position}}~x$", labelpad=0) -ylabel("${\\rm{Density}}~\\rho$", labelpad=0) xlim(-0.5, 0.5) -ylim(0.05, 1.1) # Pressure profile -------------------------------- subplot(233) @@ -257,7 +270,7 @@ ylim(0.01, 1.1) # Internal energy profile ------------------------- subplot(234) -plot(x, u, '.', color='r', ms=4.0) +scatter(x, u, marker='.', c=alpha_diff, s=4.0) plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2) xlabel("${\\rm{Position}}~x$", labelpad=0) ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) diff --git a/examples/SodShock_1D/run.sh b/examples/HydroTests/SodShock_1D/run.sh similarity index 79% rename from examples/SodShock_1D/run.sh rename to examples/HydroTests/SodShock_1D/run.sh index 4be4254baa4a87b105a5f3c1bfbf9059348a1e9e..8be97f7b34c9947268ed44b44b2c445ddb8a717f 100755 --- a/examples/SodShock_1D/run.sh +++ b/examples/HydroTests/SodShock_1D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 sodShock.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log # Plot the result python plotSolution.py 1 diff --git a/examples/SodShock_1D/sodShock.yml b/examples/HydroTests/SodShock_1D/sodShock.yml similarity index 92% rename from examples/SodShock_1D/sodShock.yml rename to examples/HydroTests/SodShock_1D/sodShock.yml index 69554b4db733166fc5dbb6d198966fd8f9b8d49c..b936f50f6c2c7d9078c49fbad868aa5334498957 100644 --- a/examples/SodShock_1D/sodShock.yml +++ b/examples/HydroTests/SodShock_1D/sodShock.yml @@ -27,6 +27,10 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + viscosity_alpha_min: 0.01 + viscosity_alpha: 0.01 + viscosity_alpha_max: 2.0 + viscosity_length: 0.02 # Parameters related to the initial conditions InitialConditions: diff --git a/examples/HydroTests/SodShock_2D/getGlass.sh b/examples/HydroTests/SodShock_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..f4cb4ebcb4b452b2b123462bc97eed532f43ba25 --- /dev/null +++ b/examples/HydroTests/SodShock_2D/getGlass.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_48.hdf5 diff --git a/examples/SodShock_2D/makeIC.py b/examples/HydroTests/SodShock_2D/makeIC.py similarity index 100% rename from examples/SodShock_2D/makeIC.py rename to examples/HydroTests/SodShock_2D/makeIC.py diff --git a/examples/SodShock_2D/plotSolution.py b/examples/HydroTests/SodShock_2D/plotSolution.py similarity index 100% rename from examples/SodShock_2D/plotSolution.py rename to examples/HydroTests/SodShock_2D/plotSolution.py diff --git a/examples/SodShock_2D/run.sh b/examples/HydroTests/SodShock_2D/run.sh similarity index 84% rename from examples/SodShock_2D/run.sh rename to examples/HydroTests/SodShock_2D/run.sh index 9e6bbfdf1c0a7c206ce6966fdca7b20a28047dd8..a11c6291a48447b2f64aef458c01036e4ed73441 100755 --- a/examples/SodShock_2D/run.sh +++ b/examples/HydroTests/SodShock_2D/run.sh @@ -13,6 +13,6 @@ then fi # Run SWIFT -../swift -s -t 1 sodShock.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 sodShock.yml 2>&1 | tee output.log python plotSolution.py 1 diff --git a/examples/SodShock_2D/sodShock.yml b/examples/HydroTests/SodShock_2D/sodShock.yml similarity index 100% rename from examples/SodShock_2D/sodShock.yml rename to examples/HydroTests/SodShock_2D/sodShock.yml diff --git a/examples/HydroTests/SodShock_3D/getGlass.sh b/examples/HydroTests/SodShock_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..f61b61d4e6c51b44576fd7cdd6242cb9f0133039 --- /dev/null +++ b/examples/HydroTests/SodShock_3D/getGlass.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/SodShock_3D/makeIC.py b/examples/HydroTests/SodShock_3D/makeIC.py similarity index 100% rename from examples/SodShock_3D/makeIC.py rename to examples/HydroTests/SodShock_3D/makeIC.py diff --git a/examples/SodShock_3D/plotSolution.py b/examples/HydroTests/SodShock_3D/plotSolution.py similarity index 100% rename from examples/SodShock_3D/plotSolution.py rename to examples/HydroTests/SodShock_3D/plotSolution.py diff --git a/examples/SodShock_3D/run.sh b/examples/HydroTests/SodShock_3D/run.sh similarity index 84% rename from examples/SodShock_3D/run.sh rename to examples/HydroTests/SodShock_3D/run.sh index 8ed85baf73425b75f402c491a3c66785f6c6fce0..aceeacd331e9d2e467e1cf42079dcb492ad0c631 100755 --- a/examples/SodShock_3D/run.sh +++ b/examples/HydroTests/SodShock_3D/run.sh @@ -13,6 +13,6 @@ then fi # Run SWIFT -../swift -s -t 4 sodShock.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 sodShock.yml 2>&1 | tee output.log python plotSolution.py 1 diff --git a/examples/SodShock_3D/sodShock.yml b/examples/HydroTests/SodShock_3D/sodShock.yml similarity index 100% rename from examples/SodShock_3D/sodShock.yml rename to examples/HydroTests/SodShock_3D/sodShock.yml diff --git a/examples/SquareTest_2D/makeIC.py b/examples/HydroTests/SquareTest_2D/makeIC.py similarity index 100% rename from examples/SquareTest_2D/makeIC.py rename to examples/HydroTests/SquareTest_2D/makeIC.py diff --git a/examples/SquareTest_2D/plotSolution.py b/examples/HydroTests/SquareTest_2D/plotSolution.py similarity index 100% rename from examples/SquareTest_2D/plotSolution.py rename to examples/HydroTests/SquareTest_2D/plotSolution.py diff --git a/examples/SquareTest_2D/run.sh b/examples/HydroTests/SquareTest_2D/run.sh similarity index 79% rename from examples/SquareTest_2D/run.sh rename to examples/HydroTests/SquareTest_2D/run.sh index 7d77e9c5bd89732970b47feb3a297ef92b345a01..dae0d742e706d73f3a5efc26a9d82ac59c883757 100755 --- a/examples/SquareTest_2D/run.sh +++ b/examples/HydroTests/SquareTest_2D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 square.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 square.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 5 diff --git a/examples/SquareTest_2D/square.yml b/examples/HydroTests/SquareTest_2D/square.yml similarity index 100% rename from examples/SquareTest_2D/square.yml rename to examples/HydroTests/SquareTest_2D/square.yml diff --git a/examples/UniformBox_2D/makeIC.py b/examples/HydroTests/UniformBox_2D/makeIC.py similarity index 100% rename from examples/UniformBox_2D/makeIC.py rename to examples/HydroTests/UniformBox_2D/makeIC.py diff --git a/examples/UniformBox_2D/run.sh b/examples/HydroTests/UniformBox_2D/run.sh similarity index 74% rename from examples/UniformBox_2D/run.sh rename to examples/HydroTests/UniformBox_2D/run.sh index ee3ef109968a65e2437ea17b42013266195d3314..aad344187dfac468c58baa7b32e45024c8ef49b1 100755 --- a/examples/UniformBox_2D/run.sh +++ b/examples/HydroTests/UniformBox_2D/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 100 fi -../swift -s -t 16 uniformPlane.yml 2>&1 | tee output.log +../../swift --hydro --threads=16 uniformPlane.yml 2>&1 | tee output.log diff --git a/examples/UniformBox_2D/uniformPlane.yml b/examples/HydroTests/UniformBox_2D/uniformPlane.yml similarity index 100% rename from examples/UniformBox_2D/uniformPlane.yml rename to examples/HydroTests/UniformBox_2D/uniformPlane.yml diff --git a/examples/UniformBox_3D/makeIC.py b/examples/HydroTests/UniformBox_3D/makeIC.py similarity index 100% rename from examples/UniformBox_3D/makeIC.py rename to examples/HydroTests/UniformBox_3D/makeIC.py diff --git a/examples/UniformBox_3D/makeICbig.py b/examples/HydroTests/UniformBox_3D/makeICbig.py similarity index 100% rename from examples/UniformBox_3D/makeICbig.py rename to examples/HydroTests/UniformBox_3D/makeICbig.py diff --git a/examples/UniformBox_3D/run.sh b/examples/HydroTests/UniformBox_3D/run.sh similarity index 74% rename from examples/UniformBox_3D/run.sh rename to examples/HydroTests/UniformBox_3D/run.sh index 08891cdd08fccf8f43089951e94dddb33e162030..f93c53a14c5441a7df8936f3507ce9cbe8c2294c 100755 --- a/examples/UniformBox_3D/run.sh +++ b/examples/HydroTests/UniformBox_3D/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 100 fi -../swift -s -t 16 uniformBox.yml 2>&1 | tee output.log +../../swift --hydro --threads=16 uniformBox.yml 2>&1 | tee output.log diff --git a/examples/UniformBox_3D/uniformBox.yml b/examples/HydroTests/UniformBox_3D/uniformBox.yml similarity index 100% rename from examples/UniformBox_3D/uniformBox.yml rename to examples/HydroTests/UniformBox_3D/uniformBox.yml diff --git a/examples/VacuumSpherical_2D/getGlass.sh b/examples/HydroTests/VacuumSpherical_2D/getGlass.sh similarity index 100% rename from examples/VacuumSpherical_2D/getGlass.sh rename to examples/HydroTests/VacuumSpherical_2D/getGlass.sh diff --git a/examples/VacuumSpherical_2D/getReference.sh b/examples/HydroTests/VacuumSpherical_2D/getReference.sh similarity index 100% rename from examples/VacuumSpherical_2D/getReference.sh rename to examples/HydroTests/VacuumSpherical_2D/getReference.sh diff --git a/examples/VacuumSpherical_2D/makeIC.py b/examples/HydroTests/VacuumSpherical_2D/makeIC.py similarity index 100% rename from examples/VacuumSpherical_2D/makeIC.py rename to examples/HydroTests/VacuumSpherical_2D/makeIC.py diff --git a/examples/VacuumSpherical_2D/plotSolution.py b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py similarity index 100% rename from examples/VacuumSpherical_2D/plotSolution.py rename to examples/HydroTests/VacuumSpherical_2D/plotSolution.py diff --git a/examples/VacuumSpherical_2D/run.sh b/examples/HydroTests/VacuumSpherical_2D/run.sh similarity index 90% rename from examples/VacuumSpherical_2D/run.sh rename to examples/HydroTests/VacuumSpherical_2D/run.sh index 51d32b4de679877741b7ecd74238fecb785579e7..54c5efdf9623cd11e504753514052f05ad1b36eb 100755 --- a/examples/VacuumSpherical_2D/run.sh +++ b/examples/HydroTests/VacuumSpherical_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 4 vacuum.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log # Get the 1D high resolution reference result if not present. if [ ! -e vacuumSpherical2D_exact.txt ] diff --git a/examples/VacuumSpherical_2D/vacuum.yml b/examples/HydroTests/VacuumSpherical_2D/vacuum.yml similarity index 100% rename from examples/VacuumSpherical_2D/vacuum.yml rename to examples/HydroTests/VacuumSpherical_2D/vacuum.yml diff --git a/examples/VacuumSpherical_3D/getGlass.sh b/examples/HydroTests/VacuumSpherical_3D/getGlass.sh old mode 100755 new mode 100644 similarity index 100% rename from examples/VacuumSpherical_3D/getGlass.sh rename to examples/HydroTests/VacuumSpherical_3D/getGlass.sh diff --git a/examples/VacuumSpherical_3D/getReference.sh b/examples/HydroTests/VacuumSpherical_3D/getReference.sh similarity index 100% rename from examples/VacuumSpherical_3D/getReference.sh rename to examples/HydroTests/VacuumSpherical_3D/getReference.sh diff --git a/examples/VacuumSpherical_3D/makeIC.py b/examples/HydroTests/VacuumSpherical_3D/makeIC.py similarity index 100% rename from examples/VacuumSpherical_3D/makeIC.py rename to examples/HydroTests/VacuumSpherical_3D/makeIC.py diff --git a/examples/VacuumSpherical_3D/plotSolution.py b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py similarity index 100% rename from examples/VacuumSpherical_3D/plotSolution.py rename to examples/HydroTests/VacuumSpherical_3D/plotSolution.py diff --git a/examples/VacuumSpherical_3D/run.sh b/examples/HydroTests/VacuumSpherical_3D/run.sh similarity index 89% rename from examples/VacuumSpherical_3D/run.sh rename to examples/HydroTests/VacuumSpherical_3D/run.sh index a136929678f745f6a3d0859ba146e1bc1c6c43d0..32a55a7478cbb70d8042154b19964269f337488c 100755 --- a/examples/VacuumSpherical_3D/run.sh +++ b/examples/HydroTests/VacuumSpherical_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 16 vacuum.yml 2>&1 | tee output.log +../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log # Get the reference solution if it is not present. if [ ! -e vacuumSpherical3D_exact.txt ] diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/HydroTests/VacuumSpherical_3D/vacuum.yml similarity index 100% rename from examples/VacuumSpherical_3D/vacuum.yml rename to examples/HydroTests/VacuumSpherical_3D/vacuum.yml diff --git a/examples/Vacuum_1D/makeIC.py b/examples/HydroTests/Vacuum_1D/makeIC.py similarity index 100% rename from examples/Vacuum_1D/makeIC.py rename to examples/HydroTests/Vacuum_1D/makeIC.py diff --git a/examples/Vacuum_1D/plotSolution.py b/examples/HydroTests/Vacuum_1D/plotSolution.py similarity index 100% rename from examples/Vacuum_1D/plotSolution.py rename to examples/HydroTests/Vacuum_1D/plotSolution.py diff --git a/examples/Vacuum_1D/run.sh b/examples/HydroTests/Vacuum_1D/run.sh similarity index 80% rename from examples/Vacuum_1D/run.sh rename to examples/HydroTests/Vacuum_1D/run.sh index b141f91f877c5b553281e53cdf02fbea948b0a97..c5f7e0344e7a6517dae51390e2eae4acd80a5f5f 100755 --- a/examples/Vacuum_1D/run.sh +++ b/examples/HydroTests/Vacuum_1D/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -s -t 1 vacuum.yml 2>&1 | tee output.log +../../swift --hydro --threads=1 vacuum.yml 2>&1 | tee output.log # Plot the result python plotSolution.py 1 diff --git a/examples/Vacuum_1D/vacuum.yml b/examples/HydroTests/Vacuum_1D/vacuum.yml similarity index 100% rename from examples/Vacuum_1D/vacuum.yml rename to examples/HydroTests/Vacuum_1D/vacuum.yml diff --git a/examples/Vacuum_2D/getGlass.sh b/examples/HydroTests/Vacuum_2D/getGlass.sh similarity index 100% rename from examples/Vacuum_2D/getGlass.sh rename to examples/HydroTests/Vacuum_2D/getGlass.sh diff --git a/examples/Vacuum_2D/makeIC.py b/examples/HydroTests/Vacuum_2D/makeIC.py similarity index 100% rename from examples/Vacuum_2D/makeIC.py rename to examples/HydroTests/Vacuum_2D/makeIC.py diff --git a/examples/Vacuum_2D/plotSolution.py b/examples/HydroTests/Vacuum_2D/plotSolution.py similarity index 100% rename from examples/Vacuum_2D/plotSolution.py rename to examples/HydroTests/Vacuum_2D/plotSolution.py diff --git a/examples/Vacuum_2D/run.sh b/examples/HydroTests/Vacuum_2D/run.sh similarity index 85% rename from examples/Vacuum_2D/run.sh rename to examples/HydroTests/Vacuum_2D/run.sh index 5c0b2ca5e19e33e813b7ff478ed4494752c0a2a5..08f3ba19d69865112db6ac68f0264e19b6b4363b 100755 --- a/examples/Vacuum_2D/run.sh +++ b/examples/HydroTests/Vacuum_2D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 4 vacuum.yml 2>&1 | tee output.log +../../swift --hydro --threads=4 vacuum.yml 2>&1 | tee output.log # Plot the result python plotSolution.py 1 diff --git a/examples/Vacuum_2D/vacuum.yml b/examples/HydroTests/Vacuum_2D/vacuum.yml similarity index 100% rename from examples/Vacuum_2D/vacuum.yml rename to examples/HydroTests/Vacuum_2D/vacuum.yml diff --git a/examples/Vacuum_3D/getGlass.sh b/examples/HydroTests/Vacuum_3D/getGlass.sh similarity index 100% rename from examples/Vacuum_3D/getGlass.sh rename to examples/HydroTests/Vacuum_3D/getGlass.sh diff --git a/examples/Vacuum_3D/makeIC.py b/examples/HydroTests/Vacuum_3D/makeIC.py similarity index 100% rename from examples/Vacuum_3D/makeIC.py rename to examples/HydroTests/Vacuum_3D/makeIC.py diff --git a/examples/Vacuum_3D/plotSolution.py b/examples/HydroTests/Vacuum_3D/plotSolution.py similarity index 100% rename from examples/Vacuum_3D/plotSolution.py rename to examples/HydroTests/Vacuum_3D/plotSolution.py diff --git a/examples/Vacuum_3D/run.sh b/examples/HydroTests/Vacuum_3D/run.sh similarity index 85% rename from examples/Vacuum_3D/run.sh rename to examples/HydroTests/Vacuum_3D/run.sh index 5029626f67659bba1f22600bb5bd38859dd805ce..b75803f97fdd60a78efe9b05267e265d51fb1f1f 100755 --- a/examples/Vacuum_3D/run.sh +++ b/examples/HydroTests/Vacuum_3D/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -s -t 16 vacuum.yml 2>&1 | tee output.log +../../swift --hydro --threads=16 vacuum.yml 2>&1 | tee output.log # Plot the result python plotSolution.py 1 diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/HydroTests/Vacuum_3D/vacuum.yml similarity index 100% rename from examples/Vacuum_3D/vacuum.yml rename to examples/HydroTests/Vacuum_3D/vacuum.yml diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/angularmomentum.py b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/angularmomentum.py new file mode 100755 index 0000000000000000000000000000000000000000..4398dfeb8b079143886c5565e7667f72fc0bdcef --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/angularmomentum.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +import h5py +import matplotlib.pyplot as plt +import scipy.optimize as sco + + +Nmax = 2001 +steps = 10 +angmomcomp = False + +iterarray = np.arange(0, Nmax + 1, steps) +Lxtot = np.zeros(len(iterarray)) +Lytot = np.zeros(len(iterarray)) +Lztot = np.zeros(len(iterarray)) +Ltot = np.zeros(len(iterarray)) +time_array = np.zeros(len(iterarray)) + + +for i in iterarray: + f = h5py.File("output_%04d.hdf5" % i, "r") + + boxsize = f["Header"].attrs["BoxSize"] / 2.0 + + time_array[int(i / steps)] = f["Header"].attrs["Time"] + + particles = f["PartType4"] + coordinates = particles["Coordinates"][:, :] + velocities = particles["Velocities"][:, :] + masses = particles["Masses"][:] + + R = ( + (coordinates[:, 0] - boxsize[0]) ** 2 + (coordinates[:, 1] - boxsize[1]) ** 2 + ) ** 0.5 + X = np.abs(coordinates[:, 0] - boxsize[0]) + Y = np.abs(coordinates[:, 1] - boxsize[1]) + Z = np.abs(coordinates[:, 2] - boxsize[2]) + + vx = velocities[:, 0] + vy = velocities[:, 1] + vz = velocities[:, 2] + + Lx = (Y * vz - Z * vy) * masses + Ly = (Z * vx - X * vz) * masses + Lz = (X * vy - Y * vx) * masses + + L = (Lx ** 2 + Ly ** 2 + Lz ** 2) ** 0.5 + + Lxtot[int(i / steps)] = np.sum(Lx) + Lytot[int(i / steps)] = np.sum(Ly) + Lztot[int(i / steps)] = np.sum(Lz) + Ltot[int(i / steps)] = np.sum(L) + +time_array[-1] = 2.0 +if angmomcomp: + plt.plot(time_array, Lxtot / Lxtot[0] - 1, label="Lx total") + plt.plot(time_array, Lytot / Lytot[0] - 1, label="Ly total") + plt.plot(time_array, Lztot / Lztot[0] - 1, label="Lz total") +plt.plot(time_array, Ltot / Ltot[0] - 1, label="L total") +plt.xlabel("Time") +plt.ylabel("ratio between current and zero angular momentum") +plt.legend() +plt.show() + +plt.semilogy(time_array, np.absolute(Ltot / Ltot[0] - 1)) +plt.xlabel("Time (Gyr)") +plt.ylabel("Fractional change of total angular momentum") +plt.savefig("Total_angular_momentum.png") +plt.show() +plt.close() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/getIC.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..a3d16db27aac06abda683a7bd75e72a275f8b9d4 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/3e11-star-only-DM-halo-galaxy.hdf5 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml new file mode 100644 index 0000000000000000000000000000000000000000..dccfb28a3f1c888d2a83b5e28b759a30a6928754 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/isolated_galaxy.yml @@ -0,0 +1,43 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9891E43 # 10^10 solar masses + UnitLength_in_cgs: 3.08567758E21 # 1 kpc + UnitVelocity_in_cgs: 1E5 # km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters for the self-gravity scheme +Gravity: + mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + +Scheduler: + max_top_level_cells: 96 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + time_first: 0. # (Optional) Time of the first stats output if non-cosmological time-integration (in internal units) + +# Parameters related to the initial conditions +InitialConditions: + file_name: 3e11-star-only-DM-halo-galaxy.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + + diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/profilefit.py b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/profilefit.py new file mode 100755 index 0000000000000000000000000000000000000000..e7755062ea45de4f42716b14c5896b0da676f001 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/profilefit.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +import h5py +import matplotlib.pyplot as plt +from matplotlib.colors import BoundaryNorm +from matplotlib.ticker import MaxNLocator +import scipy.optimize as sco +import os + + +def linearfunc(x, a, b): + return a * x + b + + +def radialfunc(r, h, A): + return A * np.exp(-r / h) * r + + +def verticalfunc(z, A, z0, zoff): + return 2 * A * np.exp(-(z - zoff) / z0) + + +def verticalfunc2(z, A, z0): + return 2 * A * np.exp(-(z) / z0) + + +def verticalfunc3(z, A, z0, zoff, b): + return 2 * A * np.exp(-(z - zoff) / z0) + b + + +Nmax = 2001 +steps = 10 +storefits = False +logfit = True +normalfit = False + +# if the user wants to store the indivudal fits +if storefits: + if not os.path.exists("radial"): + os.mkdir("radial") + os.mkdir("vertical") + os.mkdir("histsnap") + + +# Initialize the arrays +R_ideal = np.linspace(0, 40, 100) +Z_ideal = np.linspace(0, 10, 100) + +iterarray = np.arange(0, Nmax + 1, steps) + +Z0t = np.zeros(len(iterarray)) +Z0terr = np.zeros(len(iterarray)) +h0t = np.zeros(len(iterarray)) +h0terr = np.zeros(len(iterarray)) +Ar = np.zeros(len(iterarray)) +Arerr = np.zeros(len(iterarray)) +Az = np.zeros(len(iterarray)) +Azerr = np.zeros(len(iterarray)) +time_array = np.zeros(len(iterarray)) + +ar = np.zeros(len(iterarray)) +arerr = np.zeros(len(iterarray)) +br = np.zeros(len(iterarray)) +brerr = np.zeros(len(iterarray)) +az = np.zeros(len(iterarray)) +azerr = np.zeros(len(iterarray)) +bz = np.zeros(len(iterarray)) +bzerr = np.zeros(len(iterarray)) +eps = 1e-6 + + +for i in iterarray: + # Getting the data from the snapshots + f = h5py.File("output_%04d.hdf5" % i, "r") + + boxsize = f["Header"].attrs["BoxSize"] / 2.0 + + time_array[int(i / steps)] = f["Header"].attrs["Time"] + + particles = f["PartType4"] + coordinates = particles["Coordinates"][:, :] + masses = particles["Masses"][:] + + R = ( + (coordinates[:, 0] - boxsize[0]) ** 2 + (coordinates[:, 1] - boxsize[1]) ** 2 + ) ** 0.5 + Z = np.abs(coordinates[:, 1] - boxsize[1]) + + # Bin the coordinates to make them suitable for fitting + Rhist = np.histogram(R, bins=100, range=[0, 40], normed=True) + Zhist = np.histogram(Z, bins=100, range=[0, 10.0], normed=True) + + # Create correct variables for fitting + Ry = Rhist[0] + Rx = (Rhist[1][1:] + Rhist[1][: len(Rhist[0])]) / 2.0 + + Zy = Zhist[0] + Zx = (Zhist[1][1:] + Zhist[1][: len(Zhist[0])]) / 2.0 + + # Fit with two methods: non-linear LSQ and linear LSQ in log space + bestsolR = sco.curve_fit(radialfunc, Rx[10:], Ry[10:], p0=[2.0, 0.2]) + bestsolZ = sco.curve_fit(verticalfunc, Zx[40:], Zy[40:]) + bestsolRlog = sco.curve_fit(linearfunc, Rx[10:], np.log10(Ry[10:] + eps)) + bestsolZlog = sco.curve_fit(linearfunc, Zx[40:], np.log10(Zy[40:] + eps)) + + # Store variables + h0t[int(i / steps)] = bestsolR[0][0] + Z0t[int(i / steps)] = bestsolZ[0][1] + Ar[int(i / steps)] = bestsolR[0][1] + Az[int(i / steps)] = bestsolZ[0][0] + Z0terr[int(i / steps)] = (bestsolZ[1][1, 1]) ** 0.5 + h0terr[int(i / steps)] = (bestsolR[1][0, 0]) ** 0.5 + Arerr[int(i / steps)] = (bestsolR[1][1, 1]) ** 0.5 + Azerr[int(i / steps)] = (bestsolZ[1][0, 0]) ** 0.5 + + ar[int(i / steps)] = bestsolRlog[0][0] + arerr[int(i / steps)] = (bestsolRlog[1][0, 0]) ** 0.5 + br[int(i / steps)] = bestsolRlog[0][1] + brerr[int(i / steps)] = (bestsolRlog[1][1, 1]) ** 0.5 + az[int(i / steps)] = bestsolZlog[0][0] + azerr[int(i / steps)] = (bestsolZlog[1][0, 0]) ** 0.5 + bz[int(i / steps)] = bestsolZlog[0][1] + bzerr[int(i / steps)] = (bestsolZlog[1][1, 1]) ** 0.5 + + if storefits: + plt.step(Rx, Ry) + plt.plot( + R_ideal, + radialfunc(R_ideal, bestsolR[0][0], bestsolR[0][1]), + label="Non linear LSQ", + ) + plt.plot( + R_ideal, + 10 ** (linearfunc(R_ideal, bestsolRlog[0][0], bestsolRlog[0][1])), + label="Linear LSQ", + ) + plt.xlim(0, 40) + plt.ylim(0, 0.25) + plt.xlabel("R (kpc)") + plt.ylabel("Probability") + plt.savefig("./radial/radialsnap%04d.png" % i) + plt.close() + + plt.step(Zx, Zy) + plt.plot( + Z_ideal, + verticalfunc(Z_ideal, bestsolZ[0][0], bestsolZ[0][1], bestsolZ[0][2]), + label="Non linear LSQ", + ) + plt.plot( + Z_ideal, + 10 ** (linearfunc(Z_ideal, bestsolZlog[0][0], bestsolZlog[0][1])), + label="Linear LSQ", + ) + plt.xlim(0, 10.0) + plt.ylim(0, 0.6) + plt.xlabel("z (kpc)") + plt.ylabel("Probability") + plt.savefig("./vertical/verticalsnap%04d.png" % i) + plt.close() + +time_array[-1] = 2.0 + +ax = plt.subplot(111) +ax.set_yscale("log") +if logfit: + plt.errorbar( + time_array, + np.absolute(az / (az[0]) - 1), + yerr=azerr / (az[0]), + label="z0 scale height (Log space)", + ) + plt.errorbar( + time_array, + np.absolute(ar / (ar[0]) - 1), + yerr=arerr / (ar[0]), + label="h scale lenght (Log space)", + ) +if normalfit: + plt.errorbar( + time_array, + np.absolute(Z0t / (Z0t[0]) - 1), + yerr=Z0terr / (Z0t[0]), + label="z0 scale height (normal space)", + ) + plt.errorbar( + time_array, + np.absolute(h0t / (h0t[0]) - 1), + yerr=h0terr / (h0t[0]), + label="h scale height (normal space)", + ) +ax.set_xlabel("Time (Gyr)") +ax.set_ylabel("Fractional difference") +plt.legend() +plt.savefig("Fitdifference-witherror.pdf") +plt.close() + + +ax = plt.subplot(111) +ax.set_yscale("log") +if logfit: + plt.plot( + time_array, np.absolute(az / (az[0]) - 1), label="z0 scale height (Log space)" + ) + plt.plot( + time_array, np.absolute(ar / (ar[0]) - 1), label="h scale lenght (Log space)" + ) +if normalfit: + plt.plot( + time_array, + np.absolute(Z0t / (Z0t[0]) - 1), + label="z0 scale height (normal space)", + ) + plt.plot( + time_array, + np.absolute(h0t / (h0t[0]) - 1), + label="h scale height (normal space)", + ) +ax.set_xlabel("Time (Gyr)") +ax.set_ylabel("Fractional difference") +plt.legend() +plt.savefig("Fitdifference.pdf") +plt.show() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..604c01d09a3553c598bb5691e0078ae52592670d --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_dmparticles/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ ! -e reddeathgalaxywithDM.hdf5 ] +then + echo "Fetching initial conditons for the isolated galaxy with an external potential ..." + ./getIC.sh +fi + +../../swift --external-gravity --self-gravity --stars --threads=16 isolated_galaxy.yml 2>&1 | tee output.log + + +echo "Make plots of conservation of total angular momentum" +if command -v python3 &>/dev/null; then + python3 angularmomentum.py +else + python angularmomentum.py +fi + +echo "Make plots of change of vertical and radial profile" +if command -v python3 &>/dev/null; then + python3 profilefit.py +else + python profilefit.py +fi diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/angularmomentum.py b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/angularmomentum.py new file mode 100755 index 0000000000000000000000000000000000000000..4398dfeb8b079143886c5565e7667f72fc0bdcef --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/angularmomentum.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +import h5py +import matplotlib.pyplot as plt +import scipy.optimize as sco + + +Nmax = 2001 +steps = 10 +angmomcomp = False + +iterarray = np.arange(0, Nmax + 1, steps) +Lxtot = np.zeros(len(iterarray)) +Lytot = np.zeros(len(iterarray)) +Lztot = np.zeros(len(iterarray)) +Ltot = np.zeros(len(iterarray)) +time_array = np.zeros(len(iterarray)) + + +for i in iterarray: + f = h5py.File("output_%04d.hdf5" % i, "r") + + boxsize = f["Header"].attrs["BoxSize"] / 2.0 + + time_array[int(i / steps)] = f["Header"].attrs["Time"] + + particles = f["PartType4"] + coordinates = particles["Coordinates"][:, :] + velocities = particles["Velocities"][:, :] + masses = particles["Masses"][:] + + R = ( + (coordinates[:, 0] - boxsize[0]) ** 2 + (coordinates[:, 1] - boxsize[1]) ** 2 + ) ** 0.5 + X = np.abs(coordinates[:, 0] - boxsize[0]) + Y = np.abs(coordinates[:, 1] - boxsize[1]) + Z = np.abs(coordinates[:, 2] - boxsize[2]) + + vx = velocities[:, 0] + vy = velocities[:, 1] + vz = velocities[:, 2] + + Lx = (Y * vz - Z * vy) * masses + Ly = (Z * vx - X * vz) * masses + Lz = (X * vy - Y * vx) * masses + + L = (Lx ** 2 + Ly ** 2 + Lz ** 2) ** 0.5 + + Lxtot[int(i / steps)] = np.sum(Lx) + Lytot[int(i / steps)] = np.sum(Ly) + Lztot[int(i / steps)] = np.sum(Lz) + Ltot[int(i / steps)] = np.sum(L) + +time_array[-1] = 2.0 +if angmomcomp: + plt.plot(time_array, Lxtot / Lxtot[0] - 1, label="Lx total") + plt.plot(time_array, Lytot / Lytot[0] - 1, label="Ly total") + plt.plot(time_array, Lztot / Lztot[0] - 1, label="Lz total") +plt.plot(time_array, Ltot / Ltot[0] - 1, label="L total") +plt.xlabel("Time") +plt.ylabel("ratio between current and zero angular momentum") +plt.legend() +plt.show() + +plt.semilogy(time_array, np.absolute(Ltot / Ltot[0] - 1)) +plt.xlabel("Time (Gyr)") +plt.ylabel("Fractional change of total angular momentum") +plt.savefig("Total_angular_momentum.png") +plt.show() +plt.close() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/getIC.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..a88fb05df5663993ddadd87be476b0444ac1d132 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/getIC.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/3e11-star-only-static-potential-galaxy.hdf5 + diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml new file mode 100644 index 0000000000000000000000000000000000000000..deee132ee38ae5e04397839a21a677f4851e6bac --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/isolated_galaxy.yml @@ -0,0 +1,56 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9891E43 # 10^10 solar masses + UnitLength_in_cgs: 3.08567758E21 # 1 kpc + UnitVelocity_in_cgs: 1E5 # km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters for the self-gravity scheme +Gravity: + mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + comoving_softening: 0.0300 # Comoving softening length (in internal units). + max_physical_softening: 0.0300 # Physical softening length (in internal units). + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 2. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + time_first: 0. # (Optional) Time of the first stats output if non-cosmological time-integration (in internal units) + +Scheduler: + max_top_level_cells: 96 + +# Parameters related to the initial conditions +InitialConditions: + file_name: 3e11-star-only-static-potential-galaxy.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + +# Hernquist potential parameters +HernquistPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + idealizeddisk: 1 # Run with an idealized galaxy disk + M200: 30.0 # M200 of the galaxy disk + h: 0.704 # reduced Hubble constant (value does not specify the used units!) + concentration: 7.1 # concentration of the Halo + diskfraction: 0.0434370991372 # Disk mass fraction + bulgefraction: 0.00705852860979 # Bulge mass fraction + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration + epsilon: 0.030 # Softening size (internal units) + diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/profilefit.py b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/profilefit.py new file mode 100755 index 0000000000000000000000000000000000000000..e7755062ea45de4f42716b14c5896b0da676f001 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/profilefit.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +import h5py +import matplotlib.pyplot as plt +from matplotlib.colors import BoundaryNorm +from matplotlib.ticker import MaxNLocator +import scipy.optimize as sco +import os + + +def linearfunc(x, a, b): + return a * x + b + + +def radialfunc(r, h, A): + return A * np.exp(-r / h) * r + + +def verticalfunc(z, A, z0, zoff): + return 2 * A * np.exp(-(z - zoff) / z0) + + +def verticalfunc2(z, A, z0): + return 2 * A * np.exp(-(z) / z0) + + +def verticalfunc3(z, A, z0, zoff, b): + return 2 * A * np.exp(-(z - zoff) / z0) + b + + +Nmax = 2001 +steps = 10 +storefits = False +logfit = True +normalfit = False + +# if the user wants to store the indivudal fits +if storefits: + if not os.path.exists("radial"): + os.mkdir("radial") + os.mkdir("vertical") + os.mkdir("histsnap") + + +# Initialize the arrays +R_ideal = np.linspace(0, 40, 100) +Z_ideal = np.linspace(0, 10, 100) + +iterarray = np.arange(0, Nmax + 1, steps) + +Z0t = np.zeros(len(iterarray)) +Z0terr = np.zeros(len(iterarray)) +h0t = np.zeros(len(iterarray)) +h0terr = np.zeros(len(iterarray)) +Ar = np.zeros(len(iterarray)) +Arerr = np.zeros(len(iterarray)) +Az = np.zeros(len(iterarray)) +Azerr = np.zeros(len(iterarray)) +time_array = np.zeros(len(iterarray)) + +ar = np.zeros(len(iterarray)) +arerr = np.zeros(len(iterarray)) +br = np.zeros(len(iterarray)) +brerr = np.zeros(len(iterarray)) +az = np.zeros(len(iterarray)) +azerr = np.zeros(len(iterarray)) +bz = np.zeros(len(iterarray)) +bzerr = np.zeros(len(iterarray)) +eps = 1e-6 + + +for i in iterarray: + # Getting the data from the snapshots + f = h5py.File("output_%04d.hdf5" % i, "r") + + boxsize = f["Header"].attrs["BoxSize"] / 2.0 + + time_array[int(i / steps)] = f["Header"].attrs["Time"] + + particles = f["PartType4"] + coordinates = particles["Coordinates"][:, :] + masses = particles["Masses"][:] + + R = ( + (coordinates[:, 0] - boxsize[0]) ** 2 + (coordinates[:, 1] - boxsize[1]) ** 2 + ) ** 0.5 + Z = np.abs(coordinates[:, 1] - boxsize[1]) + + # Bin the coordinates to make them suitable for fitting + Rhist = np.histogram(R, bins=100, range=[0, 40], normed=True) + Zhist = np.histogram(Z, bins=100, range=[0, 10.0], normed=True) + + # Create correct variables for fitting + Ry = Rhist[0] + Rx = (Rhist[1][1:] + Rhist[1][: len(Rhist[0])]) / 2.0 + + Zy = Zhist[0] + Zx = (Zhist[1][1:] + Zhist[1][: len(Zhist[0])]) / 2.0 + + # Fit with two methods: non-linear LSQ and linear LSQ in log space + bestsolR = sco.curve_fit(radialfunc, Rx[10:], Ry[10:], p0=[2.0, 0.2]) + bestsolZ = sco.curve_fit(verticalfunc, Zx[40:], Zy[40:]) + bestsolRlog = sco.curve_fit(linearfunc, Rx[10:], np.log10(Ry[10:] + eps)) + bestsolZlog = sco.curve_fit(linearfunc, Zx[40:], np.log10(Zy[40:] + eps)) + + # Store variables + h0t[int(i / steps)] = bestsolR[0][0] + Z0t[int(i / steps)] = bestsolZ[0][1] + Ar[int(i / steps)] = bestsolR[0][1] + Az[int(i / steps)] = bestsolZ[0][0] + Z0terr[int(i / steps)] = (bestsolZ[1][1, 1]) ** 0.5 + h0terr[int(i / steps)] = (bestsolR[1][0, 0]) ** 0.5 + Arerr[int(i / steps)] = (bestsolR[1][1, 1]) ** 0.5 + Azerr[int(i / steps)] = (bestsolZ[1][0, 0]) ** 0.5 + + ar[int(i / steps)] = bestsolRlog[0][0] + arerr[int(i / steps)] = (bestsolRlog[1][0, 0]) ** 0.5 + br[int(i / steps)] = bestsolRlog[0][1] + brerr[int(i / steps)] = (bestsolRlog[1][1, 1]) ** 0.5 + az[int(i / steps)] = bestsolZlog[0][0] + azerr[int(i / steps)] = (bestsolZlog[1][0, 0]) ** 0.5 + bz[int(i / steps)] = bestsolZlog[0][1] + bzerr[int(i / steps)] = (bestsolZlog[1][1, 1]) ** 0.5 + + if storefits: + plt.step(Rx, Ry) + plt.plot( + R_ideal, + radialfunc(R_ideal, bestsolR[0][0], bestsolR[0][1]), + label="Non linear LSQ", + ) + plt.plot( + R_ideal, + 10 ** (linearfunc(R_ideal, bestsolRlog[0][0], bestsolRlog[0][1])), + label="Linear LSQ", + ) + plt.xlim(0, 40) + plt.ylim(0, 0.25) + plt.xlabel("R (kpc)") + plt.ylabel("Probability") + plt.savefig("./radial/radialsnap%04d.png" % i) + plt.close() + + plt.step(Zx, Zy) + plt.plot( + Z_ideal, + verticalfunc(Z_ideal, bestsolZ[0][0], bestsolZ[0][1], bestsolZ[0][2]), + label="Non linear LSQ", + ) + plt.plot( + Z_ideal, + 10 ** (linearfunc(Z_ideal, bestsolZlog[0][0], bestsolZlog[0][1])), + label="Linear LSQ", + ) + plt.xlim(0, 10.0) + plt.ylim(0, 0.6) + plt.xlabel("z (kpc)") + plt.ylabel("Probability") + plt.savefig("./vertical/verticalsnap%04d.png" % i) + plt.close() + +time_array[-1] = 2.0 + +ax = plt.subplot(111) +ax.set_yscale("log") +if logfit: + plt.errorbar( + time_array, + np.absolute(az / (az[0]) - 1), + yerr=azerr / (az[0]), + label="z0 scale height (Log space)", + ) + plt.errorbar( + time_array, + np.absolute(ar / (ar[0]) - 1), + yerr=arerr / (ar[0]), + label="h scale lenght (Log space)", + ) +if normalfit: + plt.errorbar( + time_array, + np.absolute(Z0t / (Z0t[0]) - 1), + yerr=Z0terr / (Z0t[0]), + label="z0 scale height (normal space)", + ) + plt.errorbar( + time_array, + np.absolute(h0t / (h0t[0]) - 1), + yerr=h0terr / (h0t[0]), + label="h scale height (normal space)", + ) +ax.set_xlabel("Time (Gyr)") +ax.set_ylabel("Fractional difference") +plt.legend() +plt.savefig("Fitdifference-witherror.pdf") +plt.close() + + +ax = plt.subplot(111) +ax.set_yscale("log") +if logfit: + plt.plot( + time_array, np.absolute(az / (az[0]) - 1), label="z0 scale height (Log space)" + ) + plt.plot( + time_array, np.absolute(ar / (ar[0]) - 1), label="h scale lenght (Log space)" + ) +if normalfit: + plt.plot( + time_array, + np.absolute(Z0t / (Z0t[0]) - 1), + label="z0 scale height (normal space)", + ) + plt.plot( + time_array, + np.absolute(h0t / (h0t[0]) - 1), + label="h scale height (normal space)", + ) +ax.set_xlabel("Time (Gyr)") +ax.set_ylabel("Fractional difference") +plt.legend() +plt.savefig("Fitdifference.pdf") +plt.show() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..8c0f2011d4a2b633d69ab454d0309b98a05b7f24 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_potential/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ ! -e reddeathgalaxy.hdf5 ] +then + echo "Fetching initial conditons for the isolated galaxy with an external potential ..." + ./getIC.sh +fi + +../../swift --external-gravity --self-gravity --stars --threads=16 isolated_galaxy.yml 2>&1 | tee output.log + + +echo "Make plots of conservation of total angular momentum" +if command -v python3 &>/dev/null; then + python3 angularmomentum.py +else + python angularmomentum.py +fi + +echo "Make plots of change of vertical and radial profile" +if command -v python3 &>/dev/null; then + python3 profilefit.py +else + python profilefit.py +fi diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/README b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/README new file mode 100644 index 0000000000000000000000000000000000000000..719ed3356701983e60e0f032791e4bb9a0524978 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/README @@ -0,0 +1,43 @@ +Isolated Galaxy generated by the MakeNewDisk code from Springel, Di Matteo & +Hernquist (2005). The done analysis in this example is similar to the work done +by Schaye and Dalla Vecchia (2008) (After this SD08). The default example runs +the simulation for a galaxy with similar mass of their fiducial model and should +produce plots similar to their middle pannel Figure 4. The code needs to be +configured to run with the Hernquist external potential as well as the cooling & +star-formation model of interest. Using the EAGLE model allows to reproduce the +results of SD08. + +The code can also be run for other situations to check to verify the law using +different parameters, changes that were done in SD08 are given by: + - gas fraction of 10% instead of 30%, change the IC to f10.hdf5, see getIC.sh, + should reproduce something similar to Figure 4 left hand pannel. Requires + change of fg=.1 + - gas fraction of 90% instead of 30%, change the IC to f90.hdf5, see getIC.sh, + should reproduce something similar to Figure 4 right hand pannel. Requires + change of fg=.9 + - Changing the effective equation of state to adiabatic, Jeans_gamma_effective + = 1.666667. Should result in something similar to Figure 5 left hand pannel + of SD08. + - Changing the effective equation of state to isothermal, Jeans_gamma_effective + = 1.0000. Should result in something similar to Figure 5 middle hand pannel + of SD08. + - Changing the slope of the Kennicutt-Schmidt law to 1.7, SchmidtLawExponent = + 1.7, this should result in a plot similar to Figure 6 of SD08. + - Increasing the density threshold by a factor of 10. thresh_norm_HpCM3 = 1.0, + should reproduce plot similar to Figure 7. + - Decreasing the density threshold by a factor of 10. thresh_norm_HpCM3 = 0.01, + should reproduce plot similar to Figure 7. + - Running with a lower resultion of a factor 8, change the IC to lowres8.hdf5, + see getIC.sh. + - Running with a lower resultion of a factor 64, change the IC to lowres64.hdf5, + see getIC.sh. + - Running with a lower resultion of a factor 512, change the IC to lowres512.hdf5, + see getIC.sh. + +Other options to verify the correctness of the code is by chaning the following +parameters: + - Changing the normalization to A/2 or 2A. + - Running the code with zero metallicity. + - Running the code with a factor 6 higher resolution idealized disks, use + highres6.hdf5, see getIC.sh. + - Running with different SPH schemes like Anarchy-PU. diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py new file mode 100644 index 0000000000000000000000000000000000000000..fa9d9258530396fb7f95237a45af5db9c0da4603 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py @@ -0,0 +1,100 @@ +""" +Makes a movie using sphviewer and ffmpeg. + +Edit low_frac and up_frac to focus on a certain view of the box. +The colour map can also be changed via colour_map. + +Usage: python3 makeMovie.py CoolingHalo_ + +Written by James Willis (james.s.willis@durham.ac.uk) +""" + +import glob +import h5py as h5 +import numpy as np +import matplotlib.pyplot as plt + +from tqdm import tqdm + + +def getSFH(filename): + + # Read the data + with h5.File(filename, "r") as f: + box_size = f["/Header"].attrs["BoxSize"][0] + coordinates = f["/PartType4/Coordinates"][:, :] + mass = f["/PartType4/Masses"][:] + # flag = f["/PartType4/NewStarFlag"][:] + birth_time = f["/PartType4/Birth_time"][:] + + absmaxz = 2 # kpc + absmaxxy = 10 # kpc + + part_mask = ( + ((coordinates[:, 0] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 0] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) + & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) + ) # & (flag==1) + + birth_time = birth_time[part_mask] + mass = mass[part_mask] + + histogram = np.histogram(birth_time, bins=200, weights=mass * 2e4, range=[0, 0.1]) + values = histogram[0] + xvalues = (histogram[1][:-1] + histogram[1][1:]) / 2.0 + return xvalues, values + + +def getsfrsnapwide(): + + time = np.arange(1, 101, 1) + SFR_sparticles = np.zeros(100) + SFR_gparticles = np.zeros(100) + new_sparticles = np.zeros(100) + previous_mass = 0 + previous_numb = 0 + for i in tqdm(range(1, 100)): + # Read the data + filename = "output_%04d.hdf5" % i + with h5.File(filename, "r") as f: + box_size = f["/Header"].attrs["BoxSize"][0] + coordinates = f["/PartType0/Coordinates"][:, :] + SFR = f["/PartType0/SFR"][:] + coordinates_star = f["/PartType4/Coordinates"][:, :] + masses_star = f["/PartType4/Masses"][:] + + absmaxz = 2 # kpc + absmaxxy = 10 # kpc + + part_mask = ( + ((coordinates[:, 0] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 0] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) + & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) + & (SFR > 0) + ) + + SFR = SFR[part_mask] + + total_SFR = np.sum(SFR) + SFR_gparticles[i] = total_SFR * 10 + + return time[:-1], SFR_gparticles[1:] + + +if __name__ == "__main__": + + time, SFR1 = getsfrsnapwide() # , SFR2, SFR_error = getsfrsnapwide() + time2, SFR3 = getSFH("output_%04d.hdf5" % 100) + plt.plot(time2[1:] * 1e3, SFR3[1:], label="Using birth_time of star particles") + plt.plot(time, SFR1, label="Using SFR of gas particles", color="g") + plt.xlabel("Time (Myr)") + plt.ylabel("SFH ($\\rm M_\odot \\rm yr^{-1}$)") + plt.ylim(0, 20) + plt.legend() + plt.savefig("SFH.png") diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/getIC.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..eddbe4cc4a0157e5f888e263a9562a539f178cbe --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/getIC.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/fid.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/f10.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/f90.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/lowres8.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/lowres64.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/lowres512.hdf5 +# wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/highres6.hdf5 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a885f5770f88c3ee6dd75e194d0fa713160e39e --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml @@ -0,0 +1,108 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9891E43 # 10^10 solar masses + UnitLength_in_cgs: 3.08567758E21 # 1 kpc + UnitVelocity_in_cgs: 1E5 # km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters for the self-gravity scheme +Gravity: + mesh_side_length: 32 # Number of cells along each axis for the periodic gravity mesh. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + comoving_softening: 0.01 # Comoving softening length (in internal units). + max_physical_softening: 0.01 # Physical softening length (in internal units). + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-9 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: output # Common part of the name of output files + time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) + delta_time: 0.001 # Time difference between consecutive outputs (in internal units) + + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + time_first: 0. # (Optional) Time of the first stats output if non-cosmological time-integration (in internal units) + +# Parameters related to the initial conditions +InitialConditions: + file_name: fid.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + h_min_ratio: 0.1 # Minimal smoothing in units of softening. + h_max: 10. + +# Standard EAGLE cooling options +EAGLECooling: + dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables + H_reion_z: 11.5 # Redshift of Hydrogen re-ionization + He_reion_z_centre: 3.5 # Redshift of the centre of the Helium re-ionization Gaussian + He_reion_z_sigma: 0.5 # Spread in redshift of the Helium re-ionization Gaussian + He_reion_eV_p_H: 2.0 # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom + +# Primordial abundances +EAGLEChemistry: + init_abundance_metal: 0.0129 # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.7065 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.2806 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.00207 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000836 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.00549 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.00141 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000591 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000683 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.0011 # Inital fraction of particle mass in Iron + +# Hernquist potential parameters +HernquistPotential: + useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions + position: [0.,0.,0.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) + idealizeddisk: 1 # Run with an idealized galaxy disk + M200: 137.0 # M200 of the galaxy disk + h: 0.704 # reduced Hubble constant (value does not specify the used units!) + concentration: 9.0 # concentration of the Halo + diskfraction: 0.040 # Disk mass fraction + bulgefraction: 0.014 # Bulge mass fraction + timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration + epsilon: 0.01 # Softening size (internal units) + +# EAGLE star formation parameters +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + gas_fraction: 0.3 # The gas fraction used internally by the model. + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..4e5fcf2ceb2b3f0682871cc533fab93039ff38e9 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py @@ -0,0 +1,368 @@ +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py as h5 + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (3.15, 3.15), + "figure.subplot.left": 0.15, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.13, + "figure.subplot.top": 0.99, + "figure.subplot.wspace": 0.15, + "figure.subplot.hspace": 0.12, + "lines.markersize": 6, + "lines.linewidth": 2.0, + "text.latex.unicode": True, +} +rcParams.update(params) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +snap = int(sys.argv[1]) +filename = "output_%.4d.hdf5"%snap + +f = h5.File(filename, "r") + +# Physical constants +k_in_cgs = 1.38064852e-16 +mH_in_cgs = 1.6737236e-24 +year_in_cgs = 3600.0 * 24 * 365.0 +Msun_in_cgs = 1.98848e33 +G_in_cgs = 6.67259e-8 +pc_in_cgs = 3.08567758e18 +Msun_p_pc2 = Msun_in_cgs / pc_in_cgs**2 + +# Gemoetry info +boxsize = f["/Header"].attrs["BoxSize"] +centre = boxsize / 2.0 + +# Read units +unit_length_in_cgs = f["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = f["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = f["/Units"].attrs["Unit time in cgs (U_t)"] + +# Calculate Gravitational constant in internal units +G = G_in_cgs * ( unit_length_in_cgs**3 / unit_mass_in_cgs / unit_time_in_cgs**2)**(-1) + +# Read parameters of the SF model +KS_law_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_exponent"]) +KS_law_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_normalisation"]) +KS_thresh_Z0 = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_Z0"]) +KS_thresh_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_slope"]) +KS_thresh_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"]) +KS_gas_fraction = float(f["/Parameters"].attrs["EAGLEStarFormation:gas_fraction"]) +KS_thresh_max_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"]) +KS_high_den_thresh = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"]) +KS_law_slope_high_den = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"]) +EOS_gamma_effective = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"]) +EOS_density_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"]) +EOS_temp_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"]) + +# Read reference metallicity +EAGLE_Z = float(f["/Parameters"].attrs["EAGLEChemistry:init_abundance_metal"]) + +# Read parameters of the entropy floor +EAGLEfloor_Jeans_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"]) +EAGLEfloor_Jeans_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"]) +EAGLEfloor_Jeans_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"]) +EAGLEfloor_cool_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"]) +EAGLEfloor_cool_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"]) +EAGLEfloor_cool_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"]) + +# Properties of the KS law +KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / ( 1e6 * pc_in_cgs**2 * year_in_cgs ) +gamma = 5./3. +EOS_press_norm = k_in_cgs * EOS_temp_norm * EOS_density_norm + +# Star formation threshold +SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0)**(KS_thresh_slope) + +# Read gas properties +gas_pos = f["/PartType0/Coordinates"][:, :] +gas_mass = f["/PartType0/Masses"][:] +gas_rho = f["/PartType0/Density"][:] +gas_T = f["/PartType0/Temperature"][:] +gas_SFR = f["/PartType0/SFR"][:] +gas_XH = f["/PartType0/ElementAbundance"][:, 0] +gas_Z = f["/PartType0/Metallicity"][:] +gas_hsml = f["/PartType0/SmoothingLength"][:] +gas_sSFR = gas_SFR / gas_mass + +# Read the Star properties +stars_pos = f["/PartType4/Coordinates"][:, :] +stars_BirthDensity = f["/PartType4/BirthDensity"][:] +stars_BirthTime = f["/PartType4/Birth_time"][:] +stars_XH = f["/PartType4/ElementAbundance"][:,0] + +# Centre the box +gas_pos[:, 0] -= centre[0] +gas_pos[:, 1] -= centre[1] +gas_pos[:, 2] -= centre[2] + +stars_pos[:,0] -= centre[0] +stars_pos[:,1] -= centre[1] +stars_pos[:,2] -= centre[2] + +# Turn the mass into better units +gas_mass *= unit_mass_in_cgs / Msun_in_cgs + +# Turn the SFR into better units +gas_SFR = np.maximum(gas_SFR, np.zeros(np.size(gas_SFR))) +gas_SFR /= unit_time_in_cgs / year_in_cgs +gas_SFR *= unit_mass_in_cgs / Msun_in_cgs + +# Make it a Hydrogen number density +gas_nH = gas_rho * unit_mass_in_cgs / unit_length_in_cgs ** 3 +gas_nH /= mH_in_cgs +gas_nH *= gas_XH + +stars_BirthDensity *= unit_mass_in_cgs / unit_length_in_cgs ** 3 +stars_BirthDensity /= mH_in_cgs +stars_BirthDensity *= stars_XH + +# Equations of state +eos_cool_rho = np.logspace(-5, 5, 1000) +eos_cool_T = EAGLEfloor_cool_temperature_norm_K * (eos_cool_rho / EAGLEfloor_cool_rho_norm) ** ( EAGLEfloor_cool_gamma_effective - 1.0 ) +eos_Jeans_rho = np.logspace(-1, 5, 1000) +eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * (eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm) ** (EAGLEfloor_Jeans_gamma_effective - 1.0 ) + +########################################################################3 + +# Plot the phase space diagram +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) +plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) +scatter(gas_nH, gas_T, s=0.2) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(3e-6, 3e3) +ylim(500.0, 2e5) +savefig("rhoT.png", dpi=200) + +# Plot the phase space diagram for SF gas +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) +plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) +plot([SF_thresh, SF_thresh], [1, 1e10], "k:", lw=0.6) +text(SF_thresh*0.9, 2e4, "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$"%SF_thresh, fontsize=8, rotation=90, ha="right", va="bottom") +scatter(gas_nH[gas_SFR > 0.0], gas_T[gas_SFR > 0.0], s=0.2) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(3e-6, 3e3) +ylim(500.0, 2e5) +savefig("rhoT_SF.png", dpi=200) + +########################################################################3 + +# 3D Density vs SFR +figure() +subplot(111, xscale="log", yscale="log") +scatter(gas_nH, gas_SFR, s=0.2) +plot([1, 100], 2e-5 * np.array([1, 100]) ** 0.266667, "k--", lw=1) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm SFR}~[{\\rm M_\\odot~\\cdot~yr^{-1}}]$", labelpad=-7) +xlim(1e-4, 3e3) +ylim(8e-6, 2.5e-4) +savefig("rho_SFR.png", dpi=200) + +########################################################################3 + +star_mask = ( + (stars_pos[:, 0] > -15) + & (stars_pos[:, 0] < 15) + & (stars_pos[:, 1] > -15) + & (stars_pos[:, 1] < 15) + & (stars_pos[:, 2] < 1.0) + & (stars_pos[:, 2] > -1.0) +) + +stars_BirthDensity = stars_BirthDensity[star_mask] +#stars_BirthFlag = stars_BirthFlag[star_mask] +stars_BirthTime = stars_BirthTime[star_mask] + +# Histogram of the birth density +figure() +subplot(111, xscale="linear", yscale="linear") +hist(np.log10(stars_BirthDensity),density=True,bins=20,range=[-2,5]) +xlabel("${\\rm Stellar~birth~density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Probability}$", labelpad=-7) +savefig("BirthDensity.png", dpi=200) + +# Plot of the specific star formation rate in the galaxy +rhos = 10**np.linspace(-1,np.log10(KS_high_den_thresh),100) +rhoshigh = 10**np.linspace(np.log10(KS_high_den_thresh),5,100) + +P_effective = EOS_press_norm * ( rhos / EOS_density_norm)**(EOS_gamma_effective) +P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm)**(EOS_gamma_effective) +sSFR = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction *P_effective)**((KS_law_slope-1.)/2.) +KS_law_norm_high_den_cgs = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction * P_norm_high)**((KS_law_slope-1.)/2.) +sSFR_high_den = KS_law_norm_high_den_cgs * ((rhoshigh/KS_high_den_thresh)**EOS_gamma_effective)**((KS_law_slope_high_den-1)/2.) + +# density - sSFR plane +figure() +subplot(111) +hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50,range=[[-1.5,5],[-.5,2.5]]) +plot(np.log10(rhos),np.log10(sSFR)+np.log10(year_in_cgs)+9.,'k--',label='sSFR low density EAGLE') +plot(np.log10(rhoshigh),np.log10(sSFR_high_den)+np.log10(year_in_cgs)+9.,'k--',label='sSFR high density EAGLE') +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=2) +ylabel("${\\rm sSFR}~[{\\rm Gyr^{-1}}]$", labelpad=0) +xticks([-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"]) +yticks([0, 1, 2], ["$10^0$", "$10^1$", "$10^2$"]) +xlim(-1.4, 4.9) +ylim(-0.5, 2.2) +savefig("density-sSFR.png", dpi=200) + +########################################################################3 + +# Select gas in a pillow box around the galaxy +mask = ( + (gas_pos[:, 0] > -15) + & (gas_pos[:, 0] < 15) + & (gas_pos[:, 1] > -15) + & (gas_pos[:, 1] < 15) + & (gas_pos[:, 2] < 1.0) + & (gas_pos[:, 2] > -1.0) +) +gas_pos = gas_pos[mask, :] +gas_SFR = gas_SFR[mask] +gas_nH = gas_nH[mask] +gas_rho = gas_rho[mask] +gas_T = gas_T[mask] +gas_mass = gas_mass[mask] +gas_Z = gas_Z[mask] +gas_hsml = gas_hsml[mask] + + +# Make a crude map of the gas +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 1], s=0.1) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +xlim(-12, 12) +ylim(-12, 12) +savefig("face_on.png", dpi=200) + +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 2], s=0.1) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~z~[{\\rm kpc}]$", labelpad=-3) +xlim(-12, 12) +ylim(-12, 12) +savefig("edge_on.png", dpi=200) + +# Now a SF map +rcParams.update({"figure.figsize": (4.15, 3.15)}) +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 1], s=0.1, c=gas_SFR) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +savefig("SF_face_on.png", dpi=200) + + +########################################################################3 + +# Bin the data in kpc-size patches + +x_edges = np.linspace(-15, 15, 31) +y_edges = np.linspace(-15, 15, 31) + +map_mass, _, _, _ = stats.binned_statistic_2d( + gas_pos[:, 0], gas_pos[:, 1], gas_mass, statistic="sum", bins=(x_edges, y_edges) +) +map_SFR, _, _, _ = stats.binned_statistic_2d( + gas_pos[:, 0], gas_pos[:, 1], gas_SFR, statistic="sum", bins=(x_edges, y_edges) +) + +# Mass map +figure() +subplot(111) +pcolormesh(x_edges, y_edges, np.log10(map_mass)) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +savefig("Map_mass.png", dpi=200) + +# SF map +figure() +subplot(111) +pcolormesh(x_edges, y_edges, np.log10(map_SFR), vmax=-0.5, vmin=-4.5) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +savefig("Map_SFR.png", dpi=200) + +######################################################################### + +# Give a minimum SF surface density for the plots +map_SFR[map_SFR < 1e-6] = 1e-6 + +# Theoretical threshold (assumes all gas has the same Z) +KS_n_thresh = KS_thresh_norm * (gas_Z[0] / KS_thresh_Z0) ** KS_thresh_slope +if np.isfinite(KS_n_thresh) == False: + KS_n_thresh = KS_thresh_max_norm +KS_sigma_thresh = 29.0 * np.sqrt(KS_gas_fraction) * np.sqrt(KS_n_thresh) + +# Theoretical KS law +KS_sigma_mass = np.logspace(-1, 3, 100) +KS_sigma_SFR = KS_law_norm * KS_sigma_mass ** KS_law_slope + +# KS relation +rcParams.update({"figure.figsize": (3.15, 3.15), "figure.subplot.left": 0.18}) +figure() +subplot(111, xscale="log", yscale="log") +plot(KS_sigma_mass, KS_sigma_SFR, "k--", lw=0.6) +plot([KS_sigma_thresh, KS_sigma_thresh], [1e-8, 1e8], "k--", lw=0.6) +text( + KS_sigma_thresh * 0.95, + 2.2, + "$\\Sigma_{\\rm c} = %.2f~{\\rm M_\\odot\\cdot pc^{-2}}$" % KS_sigma_thresh, + va="top", + ha="right", + rotation=90, + fontsize=7, +) +text(16, 10 ** (-3.5), "$n_{\\rm H,c} = %.3f~{\\rm cm^{-3}}$" % KS_n_thresh, fontsize=7) +text( + 16, + 2e-6, + "${\\rm K\\textendash S~law}$:\n$\\Sigma_{\\rm SFR} = A \\times \\Sigma_g^n$\n$n=%.1f$\n$A=%.3f\\times10^{-4}~{\\rm M_\\odot / yr^{1} / kpc^{2}}$\n$f_{\\rm g} = %.1f$\n$\gamma_{\\rm eos} = %.3f$\n$Z=%1.4f$" + % ( + KS_law_slope, + KS_law_norm * 10 ** 4, + KS_gas_fraction, + EOS_gamma_effective, + EAGLE_Z, + ), + fontsize=7, +) +scatter(map_mass.flatten() / 1e6, map_SFR.flatten(), s=0.4) +xlim(0.3, 900) +ylim(3e-7, 3) +xlabel("$\\Sigma_g~[{\\rm M_\\odot\\cdot pc^{-2}}]$", labelpad=0) +ylabel( + "$\\Sigma_{\\rm SFR}~[{\\rm M_\\odot \\cdot yr^{-1} \\cdot kpc^{-2}}]$", labelpad=0 +) +savefig("KS_law.png", dpi=200) +close() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..f2aa93fd0e11cd9b07be991187cf0780a82ebec8 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ ! -e fid.hdf5 ] +then + echo "Fetching initial conditions for the isolated galaxy example..." + ./getIC.sh +fi + +../../swift --threads=32 --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro isolated_galaxy.yml 2>&1 | tee output.log + +# Kennicutt-Schmidt law plot +python3 plotSolution.py + +# Plot that the random star formation matches the expected SFH +python3 SFH.py diff --git a/examples/IsolatedGalaxy/README b/examples/IsolatedGalaxy/README new file mode 100644 index 0000000000000000000000000000000000000000..ddc340ca1ccc76289805a442fc208811cee897c8 --- /dev/null +++ b/examples/IsolatedGalaxy/README @@ -0,0 +1,3 @@ +This directory contains a series of examples using an isolated galaxy +disk embedded in an external Hernquist dark matter halo. These are +ideal for testing galaxy formation subgrid model implementation. diff --git a/examples/Makefile.am b/examples/Makefile.am index c2bf34677829a5acac8e430cc2cb145e242b222c..6af9fbc74d6c94c174673d37a7c2834daae89d71 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,12 +19,15 @@ MYFLAGS = # Add the source directory and the non-standard paths to the included library headers to CFLAGS -AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS) +AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/argparse $(HDF5_CPPFLAGS) \ + $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) AM_LDFLAGS = $(HDF5_LDFLAGS) # Extra libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) \ + $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) \ + $(VELOCIRAPTOR_LIBS) $(GSL_LIBS) # MPI libraries. MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) @@ -51,12 +54,12 @@ endif # Sources for swift swift_SOURCES = main.c swift_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_LDADD = ../src/.libs/libswiftsim.a $(EXTRA_LIBS) +swift_LDADD = ../src/.libs/libswiftsim.a ../argparse/.libs/libargparse.a $(EXTRA_LIBS) # Sources for swift_mpi, do we need an affinity policy for MPI? swift_mpi_SOURCES = main.c swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS) +swift_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a ../argparse/.libs/libargparse.a $(MPI_LIBS) $(EXTRA_LIBS) # Sources for fof fof_SOURCES = main_fof.c @@ -69,52 +72,51 @@ fof_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_po fof_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS) # Scripts to generate ICs -EXTRA_DIST = CoolingBox/coolingBox.yml CoolingBox/energy_plot.py CoolingBox/makeIC.py CoolingBox/run.sh \ - ConstantCosmoVolume/run.sh ConstantCosmoVolume/makeIC.py ConstantCosmoVolume/plotSolution.py ConstantCosmoVolume/constant_volume.yml \ - EAGLE_6/eagle_6.yml EAGLE_6/getIC.sh EAGLE_6/README EAGLE_6/run.sh \ - EAGLE_12/eagle_12.yml EAGLE_12/getIC.sh EAGLE_12/README EAGLE_12/run.sh \ - EAGLE_25/eagle_25.yml EAGLE_25/getIC.sh EAGLE_25/README EAGLE_25/run.sh \ - EAGLE_50/eagle_50.yml EAGLE_50/getIC.sh EAGLE_50/README EAGLE_50/run.sh \ - EAGLE_100/eagle_100.yml EAGLE_100/getIC.sh EAGLE_100/README EAGLE_100/run.sh \ - EAGLE_DMO_12/eagle_12.yml EAGLE_DMO_12/getIC.sh EAGLE_DMO_12/README EAGLE_DMO_12/run.sh \ - EAGLE_DMO_25/eagle_25.yml EAGLE_DMO_25/getIC.sh EAGLE_DMO_25/README EAGLE_DMO_25/run.sh \ - EAGLE_DMO_50/eagle_50.yml EAGLE_DMO_50/getIC.sh EAGLE_DMO_50/README EAGLE_DMO_50/run.sh \ - EAGLE_DMO_100/eagle_100.yml EAGLE_DMO_100/getIC.sh EAGLE_DMO_100/README EAGLE_DMO_100/run.sh \ - EvrardCollapse_3D/evrard.yml EvrardCollapse_3D/makeIC.py EvrardCollapse_3D/plotSolution.py EvrardCollapse_3D/run.sh EvrardCollapse_3D/getReference.sh \ - ExternalPointMass/externalPointMass.yml ExternalPointMass/makeIC.py ExternalPointMass/run.sh ExternalPointMass/energy_plot.py \ - GreshoVortex_2D/getGlass.sh GreshoVortex_2D/gresho.yml GreshoVortex_2D/makeIC.py GreshoVortex_2D/plotSolution.py GreshoVortex_2D/run.sh \ - GreshoVortex_3D/getGlass.sh GreshoVortex_3D/gresho.yml GreshoVortex_3D/makeIC.py GreshoVortex_3D/plotSolution.py GreshoVortex_3D/run.sh \ - HydrostaticHalo/README HydrostaticHalo/hydrostatic.yml HydrostaticHalo/makeIC.py HydrostaticHalo/run.sh \ - HydrostaticHalo/density_profile.py HydrostaticHalo/velocity_profile.py HydrostaticHalo/internal_energy_profile.py HydrostaticHalo/test_energy_conservation.py \ - InteractingBlastWaves_1D/run.sh InteractingBlastWaves_1D/makeIC.py InteractingBlastWaves_1D/plotSolution.py InteractingBlastWaves_1D/interactingBlastWaves.yml InteractingBlastWaves_1D/getReference.sh \ - IsothermalPotential/README IsothermalPotential/run.sh IsothermalPotential/energy_plot.py IsothermalPotential/isothermal.yml IsothermalPotential/makeIC.py \ - KelvinHelmholtz_2D/kelvinHelmholtz.yml KelvinHelmholtz_2D/makeIC.py KelvinHelmholtz_2D/plotSolution.py KelvinHelmholtz_2D/run.sh \ - MultiTypes/makeIC.py MultiTypes/multiTypes.yml MultiTypes/run.sh \ - Noh_1D/makeIC.py Noh_1D/noh.yml Noh_1D/plotSolution.py Noh_1D/run.sh \ - Noh_2D/makeIC.py Noh_2D/noh.yml Noh_2D/plotSolution.py Noh_2D/run.sh Noh_2D/getGlass.sh \ - Noh_3D/makeIC.py Noh_3D/noh.yml Noh_3D/plotSolution.py Noh_3D/run.sh Noh_3D/getGlass.sh \ - PerturbedBox_2D/makeIC.py PerturbedBox_2D/perturbedPlane.yml \ - PerturbedBox_3D/makeIC.py PerturbedBox_3D/perturbedBox.yml PerturbedBox_3D/run.sh \ - PMillennium-384/p-mill-384.yml \ - PMillennium-768/p-mill-768.yml \ - SantaBarbara/README SantaBarbara/getIC.sh SantaBarbara/santa_barbara.yml SantaBarbara/run.sh \ - SantaBarbara_low/README SantaBarbara_low/getIC.sh SantaBarbara_low/santa_barbara.yml SantaBarbara_low/run.sh \ - SedovBlast_1D/makeIC.py SedovBlast_1D/plotSolution.py SedovBlast_1D/run.sh SedovBlast_1D/sedov.yml \ - SedovBlast_2D/getGlass.sh SedovBlast_2D/makeIC.py SedovBlast_2D/plotSolution.py SedovBlast_2D/run.sh SedovBlast_2D/sedov.yml \ - SedovBlast_3D/getGlass.sh SedovBlast_3D/makeIC.py SedovBlast_3D/plotSolution.py SedovBlast_3D/run.sh SedovBlast_3D/sedov.yml \ - SineWavePotential_1D/makeIC.py SineWavePotential_1D/plotSolution.py SineWavePotential_1D/run.sh SineWavePotential_1D/sineWavePotential.yml \ - SineWavePotential_2D/makeIC.py SineWavePotential_2D/plotSolution.py SineWavePotential_2D/run.sh SineWavePotential_2D/sineWavePotential.yml \ - SineWavePotential_3D/makeIC.py SineWavePotential_3D/plotSolution.py SineWavePotential_3D/run.sh SineWavePotential_3D/sineWavePotential.yml \ - SmallCosmoVolume/README SmallCosmoVolume/getIC.sh SmallCosmoVolume/run.sh SmallCosmoVolume/small_cosmo_volume.yml SmallCosmoVolume/plotTempEvolution.py \ - SmallCosmoVolume_DM/README SmallCosmoVolume_DM/getIC.sh SmallCosmoVolume_DM/run.sh SmallCosmoVolume_DM/small_cosmo_volume_dm.yml SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg \ - SodShock_1D/makeIC.py SodShock_1D/plotSolution.py SodShock_1D/run.sh SodShock_1D/sodShock.yml \ - SodShock_2D/getGlass.sh SodShock_2D/makeIC.py SodShock_2D/plotSolution.py SodShock_2D/run.sh SodShock_2D/sodShock.yml \ - SodShock_3D/getGlass.sh SodShock_3D/makeIC.py SodShock_3D/plotSolution.py SodShock_3D/run.sh SodShock_3D/sodShock.yml \ - SquareTest_2D/makeIC.py SquareTest_2D/plotSolution.py SquareTest_2D/run.sh SquareTest_2D/square.yml \ - UniformBox_2D/makeIC.py UniformBox_2D/run.sh UniformBox_2D/uniformPlane.yml \ - UniformBox_3D/makeICbig.py UniformBox_3D/makeIC.py UniformBox_3D/run.sh UniformBox_3D/uniformBox.yml \ - Gravity_glass/makeIC.py Gravity_glass/README Gravity_glass/uniform_DM_box.yml \ - ZeldovichPancake_3D/makeIC.py ZeldovichPancake_3D/zeldovichPancake.yml ZeldovichPancake_3D/run.sh ZeldovichPancake_3D/plotSolution.py +EXTRA_DIST = Cooling/CoolingBox/coolingBox.yml Cooling/CoolingBox/energy_plot.py Cooling/CoolingBox/makeIC.py Cooling/CoolingBox/run.sh \ + Cosmology/ConstantCosmoVolume/run.sh Cosmology/ConstantCosmoVolume/makeIC.py Cosmology/ConstantCosmoVolume/plotSolution.py Cosmology/ConstantCosmoVolume/constant_volume.yml \ + Cosmology/ZeldovichPancake_3D/makeIC.py Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml Cosmology/ZeldovichPancake_3D/run.sh Cosmology/ZeldovichPancake_3D/plotSolution.py \ + EAGLE_low_z/EAGLE_6/eagle_6.yml EAGLE_low_z/EAGLE_6/getIC.sh EAGLE_low_z/EAGLE_6/README EAGLE_low_z/EAGLE_6/run.sh \ + EAGLE_low_z/EAGLE_12/eagle_12.yml EAGLE_low_z/EAGLE_12/getIC.sh EAGLE_low_z/EAGLE_12/README EAGLE_low_z/EAGLE_12/run.sh \ + EAGLE_low_z/EAGLE_25/eagle_25.yml EAGLE_low_z/EAGLE_25/getIC.sh EAGLE_low_z/EAGLE_25/README EAGLE_low_z/EAGLE_25/run.sh \ + EAGLE_low_z/EAGLE_50/eagle_50.yml EAGLE_low_z/EAGLE_50/getIC.sh EAGLE_low_z/EAGLE_50/README EAGLE_low_z/EAGLE_50/run.sh \ + EAGLE_low_z/EAGLE_100/eagle_100.yml EAGLE_low_z/EAGLE_100/getIC.sh EAGLE_low_z/EAGLE_100/README EAGLE_low_z/EAGLE_100/run.sh \ + EAGLE_DMO_low_z/EAGLE_DMO_12/eagle_12.yml EAGLE_DMO_low_z/EAGLE_DMO_12/getIC.sh EAGLE_DMO_low_z/EAGLE_DMO_12/README EAGLE_DMO_low_z/EAGLE_DMO_12/run.sh \ + EAGLE_DMO_low_z/EAGLE_DMO_25/eagle_25.yml EAGLE_DMO_low_z/EAGLE_DMO_25/getIC.sh EAGLE_DMO_low_z/EAGLE_DMO_25/README EAGLE_DMO_low_z/EAGLE_DMO_25/run.sh \ + EAGLE_DMO_low_z/EAGLE_DMO_50/eagle_50.yml EAGLE_DMO_low_z/EAGLE_DMO_50/getIC.sh EAGLE_DMO_low_z/EAGLE_DMO_50/README EAGLE_DMO_low_z/EAGLE_DMO_50/run.sh \ + EAGLE_DMO_low_z/EAGLE_DMO_100/eagle_100.yml EAGLE_DMO_low_z/EAGLE_DMO_100/getIC.sh EAGLE_DMO_low_z/EAGLE_DMO_100/README EAGLE_DMO_low_z/EAGLE_DMO_100/run.sh \ + GravityTests/ExternalPointMass/externalPointMass.yml GravityTests/ExternalPointMass/makeIC.py GravityTests/ExternalPointMass/run.sh GravityTests/ExternalPointMass/energy_plot.py \ + GravityTests/HydrostaticHalo/README GravityTests/HydrostaticHalo/hydrostatic.yml GravityTests/HydrostaticHalo/makeIC.py GravityTests/HydrostaticHalo/run.sh \ + GravityTests/HydrostaticHalo/density_profile.py GravityTests/HydrostaticHalo/velocity_profile.py GravityTests/HydrostaticHalo/internal_energy_profile.py GravityTests/HydrostaticHalo/test_energy_conservation.py \ + GravityTests/IsothermalPotential/README GravityTests/IsothermalPotential/run.sh GravityTests/IsothermalPotential/energy_plot.py GravityTests/IsothermalPotential/isothermal.yml GravityTests/IsothermalPotential/makeIC.py \ + HydroTests/GreshoVortex_2D/getGlass.sh HydroTests/GreshoVortex_2D/gresho.yml HydroTests/GreshoVortex_2D/makeIC.py HydroTests/GreshoVortex_2D/plotSolution.py HydroTests/GreshoVortex_2D/run.sh \ + HydroTests/GreshoVortex_3D/getGlass.sh HydroTests/GreshoVortex_3D/gresho.yml HydroTests/GreshoVortex_3D/makeIC.py HydroTests/GreshoVortex_3D/plotSolution.py HydroTests/GreshoVortex_3D/run.sh \ + HydroTests/EvrardCollapse_3D/evrard.yml HydroTests/EvrardCollapse_3D/makeIC.py HydroTests/EvrardCollapse_3D/plotSolution.py HydroTests/EvrardCollapse_3D/run.sh HydroTests/EvrardCollapse_3D/getReference.sh \ + HydroTests/InteractingBlastWaves_1D/run.sh HydroTests/InteractingBlastWaves_1D/makeIC.py HydroTests/InteractingBlastWaves_1D/plotSolution.py HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml HydroTests/InteractingBlastWaves_1D/getReference.sh \ + HydroTests/KelvinHelmholtz_2D/kelvinHelmholtz.yml HydroTests/KelvinHelmholtz_2D/makeIC.py HydroTests/KelvinHelmholtz_2D/plotSolution.py HydroTests/KelvinHelmholtz_2D/run.sh \ + HydroTests/Noh_1D/makeIC.py HydroTests/Noh_1D/noh.yml HydroTests/Noh_1D/plotSolution.py HydroTests/Noh_1D/run.sh \ + HydroTests/Noh_2D/makeIC.py HydroTests/Noh_2D/noh.yml HydroTests/Noh_2D/plotSolution.py HydroTests/Noh_2D/run.sh HydroTests/Noh_2D/getGlass.sh \ + HydroTests/Noh_3D/makeIC.py HydroTests/Noh_3D/noh.yml HydroTests/Noh_3D/plotSolution.py HydroTests/Noh_3D/run.sh HydroTests/Noh_3D/getGlass.sh \ + HydroTests/PerturbedBox_2D/makeIC.py HydroTests/PerturbedBox_2D/perturbedPlane.yml \ + HydroTests/PerturbedBox_3D/makeIC.py HydroTests/PerturbedBox_3D/perturbedBox.yml HydroTests/PerturbedBox_3D/run.sh \ + HydroTests/SedovBlast_1D/makeIC.py HydroTests/SedovBlast_1D/plotSolution.py HydroTests/SedovBlast_1D/run.sh HydroTests/SedovBlast_1D/sedov.yml \ + HydroTests/SedovBlast_2D/getGlass.sh HydroTests/SedovBlast_2D/makeIC.py HydroTests/SedovBlast_2D/plotSolution.py HydroTests/SedovBlast_2D/run.sh HydroTests/SedovBlast_2D/sedov.yml \ + HydroTests/SedovBlast_3D/getGlass.sh HydroTests/SedovBlast_3D/makeIC.py HydroTests/SedovBlast_3D/plotSolution.py HydroTests/SedovBlast_3D/run.sh HydroTests/SedovBlast_3D/sedov.yml \ + HydroTests/SineWavePotential_1D/makeIC.py HydroTests/SineWavePotential_1D/plotSolution.py HydroTests/SineWavePotential_1D/run.sh HydroTests/SineWavePotential_1D/sineWavePotential.yml \ + HydroTests/SineWavePotential_2D/makeIC.py HydroTests/SineWavePotential_2D/plotSolution.py HydroTests/SineWavePotential_2D/run.sh HydroTests/SineWavePotential_2D/sineWavePotential.yml \ + HydroTests/SineWavePotential_3D/makeIC.py HydroTests/SineWavePotential_3D/plotSolution.py HydroTests/SineWavePotential_3D/run.sh HydroTests/SineWavePotential_3D/sineWavePotential.yml \ + HydroTests/SodShock_1D/makeIC.py HydroTests/SodShock_1D/plotSolution.py HydroTests/SodShock_1D/run.sh HydroTests/SodShock_1D/sodShock.yml \ + HydroTests/SodShock_2D/getGlass.sh HydroTests/SodShock_2D/makeIC.py HydroTests/SodShock_2D/plotSolution.py HydroTests/SodShock_2D/run.sh HydroTests/SodShock_2D/sodShock.yml \ + HydroTests/SodShock_3D/getGlass.sh HydroTests/SodShock_3D/makeIC.py HydroTests/SodShock_3D/plotSolution.py HydroTests/SodShock_3D/run.sh HydroTests/SodShock_3D/sodShock.yml \ + HydroTests/SquareTest_2D/makeIC.py HydroTests/SquareTest_2D/plotSolution.py HydroTests/SquareTest_2D/run.sh HydroTests/SquareTest_2D/square.yml \ + HydroTests/UniformBox_2D/makeIC.py HydroTests/UniformBox_2D/run.sh HydroTests/UniformBox_2D/uniformPlane.yml \ + HydroTests/UniformBox_3D/makeICbig.py HydroTests/UniformBox_3D/makeIC.py HydroTests/UniformBox_3D/run.sh HydroTests/UniformBox_3D/uniformBox.yml \ + SmallCosmoVolume/SmallCosmoVolume_hydro/README SmallCosmoVolume/SmallCosmoVolume_hydro/getIC.sh SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py \ + SmallCosmoVolume/SmallCosmoVolume_DM/README SmallCosmoVolume/SmallCosmoVolume_DM/getIC.sh SmallCosmoVolume/SmallCosmoVolume_DM/run.sh SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml SmallCosmoVolume/SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg \ + PMillennium/PMillennium-384/p-mill-384.yml \ + PMillennium/PMillennium-768/p-mill-768.yml \ + PMillennium/PMillennium-1536/p-mill-1536.yml \ + SantaBarbara/SantaBarbara-256/README SantaBarbara/SantaBarbara-256/getIC.sh SantaBarbara/SantaBarbara-256/santa_barbara.yml SantaBarbara/SantaBarbara-256/run.sh \ + SantaBarbara/SantaBarbara-128/README SantaBarbara/SantaBarbara-128/getIC.sh SantaBarbara/SantaBarbara-128/santa_barbara.yml SantaBarbara/SantaBarbara-128/run.sh # Default parameter file EXTRA_DIST += parameter_example.yml diff --git a/examples/MultiTypes/makeIC.py b/examples/MultiTypes/makeIC.py deleted file mode 100644 index 80d49c762b1fe13bbfafd05c6818d3f202e5b033..0000000000000000000000000000000000000000 --- a/examples/MultiTypes/makeIC.py +++ /dev/null @@ -1,185 +0,0 @@ -############################################################################### - # This file is part of SWIFT. - # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), - # Matthieu Schaller (matthieu.schaller@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/>. - # - ############################################################################## - -import h5py -import sys -from numpy import * - -# Generates a swift IC file containing a cartesian distribution of DM particles -# with a density of 1 - -# Parameters -periodic= 1 # 1 For periodic box -boxSize = 1. -Lgas = int(sys.argv[1]) # Number of particles along one axis -rhoGas = 2. # Density -P = 1. # Pressure -gamma = 5./3. # Gas adiabatic index -eta = 1.2349 # 48 ngbs with cubic spline kernel -rhoDM = 1. -Ldm = int(sys.argv[2]) # Number of particles along one axis - -massStars = 0.1 -Lstars = int(sys.argv[3]) # Number of particles along one axis - -fileBaseName = "multiTypes" -num_files = int(sys.argv[4]) - -#--------------------------------------------------- -numGas_tot = Lgas**3 -massGas = boxSize**3 * rhoGas / numGas_tot -internalEnergy = P / ((gamma - 1.)*rhoGas) - -numDM_tot = Ldm**3 -massDM = boxSize**3 * rhoDM / numDM_tot - -numStars_tot = Lstars**3 -massStars = massDM * massStars - - -#-------------------------------------------------- - -offsetGas = 0 -offsetDM = 0 -offsetStars = 0 - -for n in range(num_files): - - # File name - if num_files == 1: - fileName = fileBaseName + ".hdf5" - else: - fileName = fileBaseName + ".%d.hdf5"%n - - # File - file = h5py.File(fileName, 'w') - - # Number of particles - numGas = numGas_tot / num_files - numDM = numDM_tot / num_files - numStars = numStars_tot / num_files - - if n == num_files - 1: - numGas += numGas_tot % num_files - numDM += numDM_tot % num_files - numStars += numStars_tot % num_files - - - # Header - grp = file.create_group("/Header") - grp.attrs["BoxSize"] = boxSize - grp.attrs["NumPart_Total"] = [numGas_tot, numDM_tot, 0, 0, numStars_tot, 0] - grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] - grp.attrs["NumPart_ThisFile"] = [numGas, numDM, 0, 0, numStars, 0] - grp.attrs["Time"] = 0.0 - grp.attrs["NumFilesPerSnapshot"] = num_files - grp.attrs["MassTable"] = [0.0, massDM, 0.0, 0.0, 0.0, 0.0] - grp.attrs["Flag_Entropy_ICs"] = 0 - grp.attrs["Dimension"] = 3 - - #Units - grp = file.create_group("/Units") - grp.attrs["Unit length in cgs (U_L)"] = 1. - grp.attrs["Unit mass in cgs (U_M)"] = 1. - grp.attrs["Unit time in cgs (U_t)"] = 1. - grp.attrs["Unit current in cgs (U_I)"] = 1. - grp.attrs["Unit temperature in cgs (U_T)"] = 1. - - - # Gas Particle group - grp = file.create_group("/PartType0") - - v = zeros((numGas, 3)) - ds = grp.create_dataset('Velocities', (numGas, 3), 'f', data=v) - - m = full((numGas, 1), massGas) - ds = grp.create_dataset('Masses', (numGas,1), 'f', data=m) - - h = full((numGas, 1), eta * boxSize / Lgas) - ds = grp.create_dataset('SmoothingLength', (numGas,1), 'f', data=h) - - u = full((numGas, 1), internalEnergy) - ds = grp.create_dataset('InternalEnergy', (numGas,1), 'f', data=u) - - ids = linspace(offsetGas, offsetGas+numGas, numGas, endpoint=False).reshape((numGas,1)) - ds = grp.create_dataset('ParticleIDs', (numGas, 1), 'L', data=ids+1) - x = ids % Lgas; - y = ((ids - x) / Lgas) % Lgas; - z = (ids - x - Lgas * y) / Lgas**2; - coords = zeros((numGas, 3)) - coords[:,0] = z[:,0] * boxSize / Lgas + boxSize / (2*Lgas) - coords[:,1] = y[:,0] * boxSize / Lgas + boxSize / (2*Lgas) - coords[:,2] = x[:,0] * boxSize / Lgas + boxSize / (2*Lgas) - ds = grp.create_dataset('Coordinates', (numGas, 3), 'd', data=coords) - - - - # DM Particle group - grp = file.create_group("/PartType1") - - v = zeros((numDM, 3)) - ds = grp.create_dataset('Velocities', (numDM, 3), 'f', data=v) - - m = full((numDM, 1), massDM) - ds = grp.create_dataset('Masses', (numDM,1), 'f', data=m) - - ids = linspace(offsetDM, offsetDM+numDM, numDM, endpoint=False).reshape((numDM,1)) - ds = grp.create_dataset('ParticleIDs', (numDM, 1), 'L', data=ids + numGas_tot + 1) - ds[()] = ids + Lgas**3 + 1 - x = ids % Ldm; - y = ((ids - x) / Ldm) % Ldm; - z = (ids - x - Ldm * y) / Ldm**2; - coords = zeros((numDM, 3)) - coords[:,0] = z[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - coords[:,1] = y[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - coords[:,2] = x[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - ds = grp.create_dataset('Coordinates', (numDM, 3), 'd', data=coords) - - - - # Star Particle group - grp = file.create_group("/PartType4") - - v = zeros((numStars, 3)) - ds = grp.create_dataset('Velocities', (numStars, 3), 'f', data=v) - - m = full((numStars, 1), massStars) - ds = grp.create_dataset('Masses', (numStars,1), 'f', data=m) - - ids = linspace(0, numStars, numStars, endpoint=False).reshape((numStars,1)) - ds = grp.create_dataset('ParticleIDs', (numStars, 1), 'L', data=ids + numGas_tot + numDM_tot + 1) - x = ids % Ldm; - y = ((ids - x) / Ldm) % Ldm; - z = (ids - x - Ldm * y) / Ldm**2; - coords = zeros((numStars, 3)) - coords[:,0] = z[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - coords[:,1] = y[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - coords[:,2] = x[:,0] * boxSize / Ldm + boxSize / (2*Ldm) - ds = grp.create_dataset('Coordinates', (numStars, 3), 'd', data=coords) - - - - # Shift stuff - offsetGas += numGas - offsetDM += numDM - offsetStars += numStars - - file.close() - diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml deleted file mode 100644 index 121a15b0837df19e4d2e9e64a56107c24fbde066..0000000000000000000000000000000000000000 --- a/examples/MultiTypes/multiTypes.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Define the system of units to use internally. -InternalUnitSystem: - UnitMass_in_cgs: 1 # Grams - UnitLength_in_cgs: 1 # Centimeters - UnitVelocity_in_cgs: 1 # Centimeters per second - UnitCurrent_in_cgs: 1 # Amperes - UnitTemp_in_cgs: 1 # Kelvin - -# Parameters governing the time integration -TimeIntegration: - time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 1. # The end time of the simulation (in internal units). - dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). - -# Parameters governing the snapshots -Snapshots: - basename: multiTypes # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) - delta_time: 0.01 # Time difference between consecutive outputs (in internal units) - -# Parameters governing the conserved quantities statistics -Statistics: - delta_time: 1e-2 # Time between statistics output - -# Parameters for the hydrodynamics scheme -SPH: - resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). - CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - -# Parameters related to the initial conditions -InitialConditions: - file_name: ./multiTypes.hdf5 # The file to read - periodic: 1 - replicate: 2 # Replicate all particles twice along each axis - -# External potential parameters -PointMassPotential: - position: [50.,50.,50.] # location of external point mass in internal units - mass: 1e10 # mass of external point mass in internal units - timestep_mult: 1e-2 diff --git a/examples/MultiTypes/run.sh b/examples/MultiTypes/run.sh deleted file mode 100755 index 38cba70393861539f18bf9fa360d51f46dd3367d..0000000000000000000000000000000000000000 --- a/examples/MultiTypes/run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Generate the initial conditions if they are not present. -if [ ! -e multiTypes.hdf5 ] -then - echo "Generating initial conditions for the multitype box example..." - python makeIC.py 9 13 7 1 -fi - -../swift -s -g -S -t 1 multiTypes.yml 2>&1 | tee output.log diff --git a/examples/PMillennium/PMillennium-1536/getIC.sh b/examples/PMillennium/PMillennium-1536/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..0ba78a0fd11025b12a7a9a7be9de47c2b5ad2898 --- /dev/null +++ b/examples/PMillennium/PMillennium-1536/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/PMillennium/PMill-1536.hdf5 diff --git a/examples/PMillennium/PMillennium-1536/p-mill-1536.yml b/examples/PMillennium/PMillennium-1536/p-mill-1536.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea44572f90eb69330c973946cb5533a4c58b2c82 --- /dev/null +++ b/examples/PMillennium/PMillennium-1536/p-mill-1536.yml @@ -0,0 +1,51 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun + UnitLength_in_cgs: 3.08567758e24 # 1 Mpc + UnitVelocity_in_cgs: 1e5 # 1 km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Planck-13 cosmology +Cosmology: + h: 0.6777 + a_begin: 0.02 # z_ini = 49 + a_end: 1.0 # z_end = 0 + Omega_m: 0.307 + Omega_lambda: 0.693 + Omega_b: 0.0455 + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-6 + dt_max: 1e-2 + +Scheduler: + max_top_level_cells: 32 + cell_split_size: 100 + +# Parameters governing the snapshots +Snapshots: + basename: PMill + delta_time: 1.02 + scale_factor_first: 0.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 + scale_factor_first: 0.02 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 + theta: 0.5 + comoving_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation + max_physical_softening: 0.0208333 # 20.8333 kpc = 1/25 mean inter-particle separation + mesh_side_length: 256 + +# Parameters related to the initial conditions +InitialConditions: + file_name: PMill-1536.hdf5 + periodic: 1 + cleanup_h_factors: 1 + cleanup_velocity_factors: 1 diff --git a/examples/PMillennium/PMillennium-384/getIC.sh b/examples/PMillennium/PMillennium-384/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..eef95aac218a55e3e432fd1afb59ee7823c930fd --- /dev/null +++ b/examples/PMillennium/PMillennium-384/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/PMillennium/PMill-384.hdf5 diff --git a/examples/PMillennium-384/p-mill-384.yml b/examples/PMillennium/PMillennium-384/p-mill-384.yml similarity index 95% rename from examples/PMillennium-384/p-mill-384.yml rename to examples/PMillennium/PMillennium-384/p-mill-384.yml index 4aede77c0c1a8a6818c95c318364150ede919a01..0040f580c8e67182949875d7d85cee2f75851654 100644 --- a/examples/PMillennium-384/p-mill-384.yml +++ b/examples/PMillennium/PMillennium-384/p-mill-384.yml @@ -41,11 +41,11 @@ Gravity: theta: 0.5 comoving_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation max_physical_softening: 0.08333 # 83.333 kpc = 1/25 mean inter-particle separation - mesh_side_length: 128 + mesh_side_length: 64 # Parameters related to the initial conditions InitialConditions: - file_name: ics.hdf5 + file_name: PMill-384.hdf5 periodic: 1 cleanup_h_factors: 1 cleanup_velocity_factors: 1 diff --git a/examples/PMillennium/PMillennium-768/getIC.sh b/examples/PMillennium/PMillennium-768/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..6c020a5bb4269d4fd4284c617cfd94ab19e87326 --- /dev/null +++ b/examples/PMillennium/PMillennium-768/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/PMillennium/PMill-768.hdf5 diff --git a/examples/PMillennium-768/p-mill-768.yml b/examples/PMillennium/PMillennium-768/p-mill-768.yml similarity index 94% rename from examples/PMillennium-768/p-mill-768.yml rename to examples/PMillennium/PMillennium-768/p-mill-768.yml index a70c9c70831af9c237a466165b25b6300df69336..5ba97af72513dca5fdccd216f0ede78d0e279b0a 100644 --- a/examples/PMillennium-768/p-mill-768.yml +++ b/examples/PMillennium/PMillennium-768/p-mill-768.yml @@ -41,11 +41,11 @@ Gravity: theta: 0.5 comoving_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation max_physical_softening: 0.041666 # 41.6666 kpc = 1/25 mean inter-particle separation - mesh_side_length: 256 + mesh_side_length: 128 # Parameters related to the initial conditions InitialConditions: - file_name: ics.hdf5 + file_name: PMill-768.hdf5 periodic: 1 cleanup_h_factors: 1 cleanup_velocity_factors: 1 diff --git a/examples/PMillennium/README b/examples/PMillennium/README new file mode 100644 index 0000000000000000000000000000000000000000..e4755bc214d438a282521b77a34ad853d0f36871 --- /dev/null +++ b/examples/PMillennium/README @@ -0,0 +1,9 @@ +Initial conditions for the P-Millennium simulation (Baugh et al. 2018) +at various resolution. These are all DMONLY runs in a 800^3 Mpc^3 +volume with the cosmology given by Planck-13: + + Om = 0.307, Ol = 0.693, h = 0.6777, Ob = 0.0455 + +The ICs exist at different resolution. The Millennium simulation +(Springel 2005) has a resolution in between the 1536^3 and 3072^3 +examples given here. diff --git a/examples/SantaBarbara_low/README b/examples/SantaBarbara/SantaBarbara-128/README similarity index 100% rename from examples/SantaBarbara_low/README rename to examples/SantaBarbara/SantaBarbara-128/README diff --git a/examples/SantaBarbara_low/getIC.sh b/examples/SantaBarbara/SantaBarbara-128/getIC.sh similarity index 73% rename from examples/SantaBarbara_low/getIC.sh rename to examples/SantaBarbara/SantaBarbara-128/getIC.sh index 759cef50dcfc346b389b1400054fe38358793fdd..46500552980564e186d9c38ecfebd8e3f258f2b7 100755 --- a/examples/SantaBarbara_low/getIC.sh +++ b/examples/SantaBarbara/SantaBarbara-128/getIC.sh @@ -1,2 +1,2 @@ #!/bin/bash -wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/SantaBarbara_128.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/SantaBarbara/SantaBarbara_128.hdf5 diff --git a/examples/SantaBarbara/SantaBarbara-128/run.sh b/examples/SantaBarbara/SantaBarbara-128/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..72c219acb201b3a3541b6a08d799b21ba4638009 --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-128/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Run SWIFT +../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml + diff --git a/examples/SantaBarbara_low/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml similarity index 98% rename from examples/SantaBarbara_low/santa_barbara.yml rename to examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml index 0e3c66b1a1c1e04bf6fad30e806327b83f03737e..35a6e2762f91707ed43bd5f0107c3dd8a53e12e4 100644 --- a/examples/SantaBarbara_low/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-128/santa_barbara.yml @@ -47,6 +47,7 @@ Gravity: SPH: resolution_eta: 1.2348 # "48 Ngb" with the cubic spline kernel CFL_condition: 0.1 + h_min_ratio: 0.1 initial_temperature: 1200. # (1 + z_ini)^2 * 2.72K minimal_temperature: 100. diff --git a/examples/SantaBarbara/README b/examples/SantaBarbara/SantaBarbara-256/README similarity index 100% rename from examples/SantaBarbara/README rename to examples/SantaBarbara/SantaBarbara-256/README diff --git a/examples/SantaBarbara/getIC.sh b/examples/SantaBarbara/SantaBarbara-256/getIC.sh similarity index 100% rename from examples/SantaBarbara/getIC.sh rename to examples/SantaBarbara/SantaBarbara-256/getIC.sh diff --git a/examples/SantaBarbara/makeImage.py b/examples/SantaBarbara/SantaBarbara-256/makeImage.py similarity index 100% rename from examples/SantaBarbara/makeImage.py rename to examples/SantaBarbara/SantaBarbara-256/makeImage.py diff --git a/examples/SantaBarbara/SantaBarbara-256/make_plots.sh b/examples/SantaBarbara/SantaBarbara-256/make_plots.sh new file mode 100644 index 0000000000000000000000000000000000000000..f0ed6b3ae13acf03cb6abf8c00203462800da681 --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/make_plots.sh @@ -0,0 +1,4 @@ +python3 makeImage.py santabarbara_0153.hdf5 0 twilight white +python3 plotSolution.py 153 halo +python3 plotTempEvolution.py +python3 rhoTHaloComparison.py diff --git a/examples/SantaBarbara/SantaBarbara-256/plotSmoothingLength.py b/examples/SantaBarbara/SantaBarbara-256/plotSmoothingLength.py new file mode 100644 index 0000000000000000000000000000000000000000..634cc2758d911eedbf4d2889e48011144da3d3ee --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/plotSmoothingLength.py @@ -0,0 +1,162 @@ +""" +Plots the smoothing length (compared to the softening). +""" + +import numpy as np +import matplotlib.pyplot as plt +import h5py + +from collections import namedtuple + +SnapshotData = namedtuple( + "SnapshotData", + [ + "smoothing_lengths", + "particle_ids", + "softening", + "internal_length", + "snapshot_length", + ], +) + +HaloCatalogueData = namedtuple( + "HaloCatalogueData", ["largest_halo", "particle_ids_in_largest_halo"] +) + + +def load_data(filename: str) -> SnapshotData: + """ + Loads the data that we need, i.e. the smoothing lengths and the + softening length, from the snapshot. + """ + + with h5py.File(filename, "r") as handle: + data = SnapshotData( + smoothing_lengths=handle["PartType0/SmoothingLength"][...], + particle_ids=handle["PartType0/ParticleIDs"][...], + softening=handle["GravityScheme"].attrs[ + "Comoving softening length [internal units]" + ][0], + internal_length=handle["InternalCodeUnits"].attrs[ + "Unit length in cgs (U_L)" + ][0], + snapshot_length=handle["Units"].attrs["Unit length in cgs (U_L)"][0], + ) + + return data + + +def load_halo_data(halo_filename: str) -> HaloCatalogueData: + """ + Loads the halo data and finds the particle IDs that belong to + the largest halo. The halo filename should be given without + any extension as we need a couple of files to complete this. + """ + + catalogue_filename = f"{halo_filename}.properties" + groups_filename = f"{halo_filename}.catalog_groups" + particles_filename = f"{halo_filename}.catalog_particles" + + with h5py.File(catalogue_filename, "r") as handle: + largest_halo = np.where( + handle["Mass_200crit"][...] == handle["Mass_200crit"][...].max() + )[0][0] + + with h5py.File(groups_filename, "r") as handle: + offset_begin = handle["Offset"][largest_halo] + offset_end = handle["Offset"][largest_halo + 1] + + with h5py.File(particles_filename, "r") as handle: + particle_ids = handle["Particle_IDs"][offset_begin:offset_end] + + return HaloCatalogueData( + largest_halo=largest_halo, particle_ids_in_largest_halo=particle_ids + ) + + +def make_plot( + snapshot_filename: str, + halo_filename: str, + output_filename="smoothing_length_variation.png", +) -> None: + """ + Makes the plot and saves it in output_filename. + + The halo filename should be provided without extension. + """ + + data = load_data(filename) + halo_data = load_halo_data(halo_filename) + + smoothing_lengths_in_halo = data.smoothing_lengths[ + np.in1d(data.particle_ids, halo_data.particle_ids_in_largest_halo) + ] + + softening = data.softening * (data.snapshot_length / data.internal_length) + + fig, ax = plt.subplots(1) + + ax.semilogy() + + ax.hist(data.smoothing_lengths, bins="auto", label="All particles") + ax.hist( + smoothing_lengths_in_halo, + bins="auto", + label=f"Particles in largest halo (ID={halo_data.largest_halo})", + ) + ax.axvline(x=softening, label="Softening", ls="--", color="grey") + + ax.legend() + + ax.set_xlabel("Smoothing length") + ax.set_ylabel("Number of particles") + + ax.set_xlim(0, ax.get_xlim()[1]) + + fig.tight_layout() + + fig.savefig(output_filename, dpi=300) + + return + + +if __name__ == "__main__": + import argparse as ap + + PARSER = ap.ArgumentParser( + description=""" + Makes a plot of the smoothing lengths in the box, compared + to the gravitational softening. Also splits out the particles + that are contained in the largest halo according to the + velociraptor outputs. + """ + ) + + PARSER.add_argument( + "-s", + "--snapshot", + help=""" + Filename and path for the snapshot (without the .hdf5), + Default: ./santabarbara_0153 + """, + required=False, + default="./santabarbara_0153", + ) + + PARSER.add_argument( + "-v", + "--velociraptor", + help=""" + The filename and path of the velociraptor files, excluding + the descriptors (i.e. without .catalog_particles). + Default: ./halo/santabarbara + """, + required=False, + default="./halo/santabarbara", + ) + + ARGS = vars(PARSER.parse_args()) + + filename = f"{ARGS['snapshot']}.hdf5" + + make_plot(filename, ARGS["velociraptor"]) diff --git a/examples/SantaBarbara/plotSolution.py b/examples/SantaBarbara/SantaBarbara-256/plotSolution.py similarity index 96% rename from examples/SantaBarbara/plotSolution.py rename to examples/SantaBarbara/SantaBarbara-256/plotSolution.py index a23aa2089a0f82a9dad989134d1ebf11a97af2fe..131fae8f44dc3d0092e26e87a80a7862094dd4ba 100644 --- a/examples/SantaBarbara/plotSolution.py +++ b/examples/SantaBarbara/SantaBarbara-256/plotSolution.py @@ -106,13 +106,18 @@ def get_halo_data(catalogue_filename: str) -> HaloData: that is given by VELOCIraptor. """ + with h5py.File(catalogue_filename, "r") as file: - x = file["Xc"][0] - y = file["Yc"][0] - z = file["Zc"][0] - Mvir = file["Mass_200crit"][0] - Rvir = file["R_200crit"][0] - c = file["cNFW"][0] + largest_halo = np.where( + file["Mass_200crit"][...] == file["Mass_200crit"][...].max() + ) + + x = float(np.take(file["Xc"], largest_halo)) + y = float(np.take(file["Yc"], largest_halo)) + z = float(np.take(file["Zc"], largest_halo)) + Mvir = float(np.take(file["Mass_200crit"], largest_halo)) + Rvir = float(np.take(file["R_200crit"], largest_halo)) + c = float(np.take(file["cNFW"], largest_halo)) return HaloData(c=c, Rvir=Rvir, Mvir=Mvir, center=np.array([x, y, z])) diff --git a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py new file mode 100644 index 0000000000000000000000000000000000000000..90de6cb712744359dbdfbf07cc4ed81546ea38bf --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py @@ -0,0 +1,183 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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/>. +# +################################################################################ + +# Computes the temperature evolution of the gas in a cosmological box + +# Physical constants needed for internal energy to temperature conversion +k_in_J_K = 1.38064852e-23 +mH_in_kg = 1.6737236e-27 + +# Number of snapshots generated +n_snapshots = 153 +snapname = "santabarbara" + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.14, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.12, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("%s_0000.hdf5" % snapname, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] + +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Primoridal ean molecular weight as a function of temperature +def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): + if T > T_trans: + return 4. / (8. - 5. * (1. - H_frac)) + else: + return 4. / (1. + 3. * H_frac) + +# Temperature of some primoridal gas with a given internal energy +def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp): + T_over_mu = (gas_gamma - 1.) * u * mH_in_kg / k_in_J_K + ret = np.ones(np.size(u)) * T_trans + + # Enough energy to be ionized? + mask_ionized = (T_over_mu > (T_trans+1) / mu(T_trans+1, H_frac, T_trans)) + if np.sum(mask_ionized) > 0: + ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans*10, H_frac, T_trans) + + # Neutral gas? + mask_neutral = (T_over_mu < (T_trans-1) / mu((T_trans-1), H_frac, T_trans)) + if np.sum(mask_neutral) > 0: + ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans) + + return ret + + +z = np.zeros(n_snapshots) +a = np.zeros(n_snapshots) +T_mean = np.zeros(n_snapshots) +T_std = np.zeros(n_snapshots) +T_log_mean = np.zeros(n_snapshots) +T_log_std = np.zeros(n_snapshots) +T_median = np.zeros(n_snapshots) +T_min = np.zeros(n_snapshots) +T_max = np.zeros(n_snapshots) + +# Loop over all the snapshots +for i in range(n_snapshots): + sim = h5py.File("%s_%04d.hdf5"% (snapname, i), "r") + + z[i] = sim["/Cosmology"].attrs["Redshift"][0] + a[i] = sim["/Cosmology"].attrs["Scale-factor"][0] + + u = sim["/PartType0/InternalEnergy"][:] + + # Compute the temperature + u *= (unit_length_in_si**2 / unit_time_in_si**2) + u /= a[i]**(3 * (gas_gamma - 1.)) + Temp = T(u) + + # Gather statistics + T_median[i] = np.median(Temp) + T_mean[i] = Temp.mean() + T_std[i] = Temp.std() + T_log_mean[i] = np.log10(Temp).mean() + T_log_std[i] = np.log10(Temp).std() + T_min[i] = Temp.min() + T_max[i] = Temp.max() + +# CMB evolution +a_evol = np.logspace(-3, 0, 60) +T_cmb = (1. / a_evol)**2 * 2.72 + +# Plot the interesting quantities +figure() +subplot(111, xscale="log", yscale="log") + +fill_between(a, T_mean-T_std, T_mean+T_std, color='C0', alpha=0.1) +plot(a, T_max, ls='-.', color='C0', lw=1., label="${\\rm max}~T$") +plot(a, T_min, ls=':', color='C0', lw=1., label="${\\rm min}~T$") +plot(a, T_mean, color='C0', label="${\\rm mean}~T$", lw=1.5) +fill_between(a, 10**(T_log_mean-T_log_std), 10**(T_log_mean+T_log_std), color='C1', alpha=0.1) +plot(a, 10**T_log_mean, color='C1', label="${\\rm mean}~{\\rm log} T$", lw=1.5) +plot(a, T_median, color='C2', label="${\\rm median}~T$", lw=1.5) + +legend(loc="upper left", frameon=False, handlelength=1.5) + +# Expected lines +plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], 'k--', lw=0.5, alpha=0.7) +text(2.5e-2, H_transition_temp*1.07, "$T_{\\rm HII\\rightarrow HI}$", va="bottom", alpha=0.7, fontsize=8) +plot([1e-10, 1e10], [T_minimal, T_minimal], 'k--', lw=0.5, alpha=0.7) +text(1e-2, T_minimal*0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8) +plot(a_evol, T_cmb, 'k--', lw=0.5, alpha=0.7) +text(a_evol[20], T_cmb[20]*0.55, "$(1+z)^2\\times T_{\\rm CMB,0}$", rotation=-34, alpha=0.7, fontsize=8, va="top", bbox=dict(facecolor='w', edgecolor='none', pad=1.0, alpha=0.9)) + + +redshift_ticks = np.array([0., 1., 2., 5., 10., 20., 50., 100.]) +redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"] +a_ticks = 1. / (redshift_ticks + 1.) + +xticks(a_ticks, redshift_labels) +minorticks_off() + +xlabel("${\\rm Redshift}~z$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0) +xlim(9e-3, 1.1) +ylim(20, 2.5e7) + +savefig("Temperature_evolution.png", dpi=200) + diff --git a/examples/SantaBarbara/SantaBarbara-256/rhoTHaloComparison.py b/examples/SantaBarbara/SantaBarbara-256/rhoTHaloComparison.py new file mode 100644 index 0000000000000000000000000000000000000000..edf4e47527bd61e3f9b017b5a53510036dbcacac --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/rhoTHaloComparison.py @@ -0,0 +1,221 @@ +""" +This script finds the temperatures inside all of the halos and +compares it against the virial temperature. This uses velociraptor +and the SWIFT snapshot. + +Folkert Nobels (2018) nobels@strw.leidenuniv.nl +Josh Borrow (2019) joshua.borrow@durham.ac.uk +""" + +import numpy as np +import h5py +import matplotlib.pyplot as plt +from matplotlib.colors import LogNorm + +mH = 1.6733e-24 # g +kB = 1.38e-16 # erg/K + + +def virial_temp(mu, M, h=0.703, a=1.0): + """ + Calculates the virial temperature according to + + https://arxiv.org/pdf/1105.5701.pdf + + Equation 1. + """ + return 4e4 * (mu / 1.2) * (M * h / 1e8) ** (2 / 3) / (10 * a) + + +def calculate_group_sizes_array(offsets: np.array, total_size: int) -> np.array: + """ + Calculates the group sizes array from the offsets and total size, i.e. it + calculates the diff between all of the offsets. + """ + + # Does not include the LAST one + group_sizes = [x - y for x, y in zip(offsets[1:], offsets[:-1])] + group_sizes += [total_size - offsets[-1]] + group_sizes = np.array(group_sizes, dtype=type(offsets[0])) + + return group_sizes + + +def create_group_array(group_sizes: np.array) -> np.array: + """ + Creates an array that looks like: + [GroupID0, GroupID0, ..., GroupIDN, GroupIDN] + i.e. for each group create the correct number of group ids. + This is used to be sorted alongside the particle IDs to track + the placement of group IDs. + """ + + slices = [] + running_total_of_particles = type(offsets[0])(0) + + for group in group_sizes: + slices.append([running_total_of_particles, group + running_total_of_particles]) + running_total_of_particles += group + + groups = np.empty(group_sizes.sum(), dtype=int) + + for group_id, group in enumerate(slices): + groups[group[0] : group[1]] = group_id + + return groups + + +if __name__ == "__main__": + import argparse as ap + + PARSER = ap.ArgumentParser( + description=""" + Makes many plots comparing the virial temperature and the + temperature of halos. Requires the velociraptor files and + the SWIFT snapshot. + """ + ) + + PARSER.add_argument( + "-s", + "--snapshot", + help=""" + Filename and path for the snapshot (without the .hdf5), + Default: ./santabarbara_0153 + """, + required=False, + default="./santabarbara_0153", + ) + + PARSER.add_argument( + "-v", + "--velociraptor", + help=""" + The filename and path of the velociraptor files, excluding + the descriptors (i.e. without .catalog_particles). + Default: ./halo/santabarbara + """, + required=False, + default="./halo/santabarbara", + ) + + ARGS = vars(PARSER.parse_args()) + + # Grab some metadata before we begin. + with h5py.File("%s.hdf5" % ARGS["snapshot"], "r") as handle: + # Cosmology + a = handle["Cosmology"].attrs["Scale-factor"][0] + h = handle["Cosmology"].attrs["h"][0] + + # Gas + hydro = handle["HydroScheme"].attrs + X = hydro["Hydrogen mass fraction"][0] + gamma = hydro["Adiabatic index"][0] + mu = 1 / (X + (1 - X) / 4) + + # First we must construct a group array so we know which particles belong + # to which group. + with h5py.File("%s.catalog_groups" % ARGS["velociraptor"], "r") as handle: + offsets = handle["Offset"][...] + + # Then, extract the particles that belong to the halos. For that, we need + # the particle IDs: + with h5py.File("%s.catalog_particles" % ARGS["velociraptor"], "r") as handle: + ids_in_halos = handle["Particle_IDs"][...] + + number_of_groups = len(offsets) + group_sizes = calculate_group_sizes_array(offsets, ids_in_halos.size) + group_array = create_group_array(group_sizes) + + # We can now load the particle data from the snapshot. + with h5py.File("%s.hdf5" % ARGS["snapshot"], "r") as handle: + gas_particles = handle["PartType0"] + + particle_ids = gas_particles["ParticleIDs"][...] + + # Requires numpy 1.15 or greater. + _, particles_in_halos_mask, group_array_mask = np.intersect1d( + particle_ids, + ids_in_halos, + assume_unique=True, + return_indices=True, + ) + + # We also need to re-index the group array to cut out DM particles + group_array = group_array[group_array_mask] + + # Kill the spare + del particle_ids + + # Now we can only read the properties that we require from the snapshot! + temperatures = np.take(gas_particles["InternalEnergy"], particles_in_halos_mask) + # This 1e10 should probably be explained somewhere... + temperatures *= 1e10 * (gamma - 1) * mu * mH / kB + + densities = np.take(gas_particles["Density"], particles_in_halos_mask) + + # Just a quick check to make sure nothing's gone wrong. + assert len(group_array) == len(temperatures) + + # Now we can loop through all the particles and find out the mean temperature and + # density in each halo. + + particles_in_group = np.zeros(number_of_groups, dtype=int) + temp_in_group = np.zeros(number_of_groups, dtype=float) + dens_in_group = np.zeros(number_of_groups, dtype=float) + + for group, T, rho in zip(group_array, temperatures, densities): + particles_in_group[group] += 1 + temp_in_group[group] += T + dens_in_group[group] += rho + + # First get a mask to ensure no runtime warnings + mask = particles_in_group != 0 + + # Normalize + temp_in_group[mask] /= particles_in_group[mask] + dens_in_group[mask] /= particles_in_group[mask] + + # Now we can load the data according to the halo finder to compare with. + with h5py.File("%s.properties" % ARGS["velociraptor"], "r") as handle: + halo_masses = handle["Mass_200crit"][...] + + halo_temperatures = virial_temp(mu, halo_masses * 1e10, h=h, a=a) + + # Finally, the plotting! + + fig, ax = plt.subplots() + ax.loglog() + + mask = np.logical_and.reduce([ + halo_temperatures != 0.0, + temp_in_group != 0.0, + ]) + + temp_in_group = temp_in_group[mask] + halo_temperatures = halo_temperatures[mask] + + mean_range = [temp_in_group.min(), temp_in_group.max()] + halo_range = [halo_temperatures.min(), halo_temperatures.max()] + + bottom = min([halo_range[0], mean_range[0]]) + top = max([halo_range[1], mean_range[1]]) + + plt.plot( + [bottom, top], + [bottom, top], + lw=2, linestyle="--", color="grey", label="1:1" + ) + + ax.scatter(halo_temperatures, temp_in_group, s=2, edgecolor="none", label="Halos") + + ax.set_ylabel("Mean Group Temperature [K]") + ax.set_xlabel("Halo Virial Temperature [K]") + + ax.set_ylim(mean_range) + ax.set_xlim(halo_range) + + ax.legend(frameon=False) + + fig.tight_layout() + fig.savefig("temperature_comparison.png", dpi=300) diff --git a/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py b/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py new file mode 100644 index 0000000000000000000000000000000000000000..c290268eaa548e188bb652104ea9e726ea88a267 --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/rhoTPlot.py @@ -0,0 +1,258 @@ +""" +Makes a rho-T plot. Uses the swiftsimio library. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from swiftsimio import SWIFTDataset, SWIFTMetadata, SWIFTUnits + +from unyt import mh, cm, Gyr +from tqdm import tqdm +from matplotlib.colors import LogNorm +from matplotlib.animation import FuncAnimation + +# Constants; these could be put in the parameter file but are rarely changed. +density_bounds = [1e-8, 1e4] # in nh/cm^3 +temperature_bounds = [1e2, 1e8] # in K +bins = 128 + +# Plotting controls +cmap = "viridis" + + +def get_data(filename): + """ + Grabs the data (T in Kelvin and density in mh / cm^3). + """ + + data = SWIFTDataset(filename) + + data.gas.density.convert_to_units(mh / (cm ** 3)) + data.gas.temperature.convert_to_cgs() + + return data.gas.density, data.gas.temperature + + +def make_hist(filename, density_bounds, temperature_bounds, bins): + """ + Makes the histogram for filename with bounds as lower, higher + for the bins and "bins" the number of bins along each dimension. + + Also returns the edges for pcolormesh to use. + """ + + density_bins = np.logspace( + np.log10(density_bounds[0]), np.log10(density_bounds[1]), bins + ) + temperature_bins = np.logspace( + np.log10(temperature_bounds[0]), np.log10(temperature_bounds[1]), bins + ) + + H, density_edges, temperature_edges = np.histogram2d( + *get_data(filename), bins=[density_bins, temperature_bins] + ) + + return H.T, density_edges, temperature_edges + + +def setup_axes(): + """ + Creates the figure and axis object. + """ + fig, ax = plt.subplots(1, figsize=(6, 5), dpi=300) + + ax.set_xlabel("Density [$n_H$ cm$^{-3}$]") + ax.set_ylabel("Temperature [K]") + + ax.loglog() + + return fig, ax + + +def make_single_image(filename, density_bounds, temperature_bounds, bins): + """ + Makes a single image and saves it to rhoTPlot_{filename}.png. + + Filename should be given _without_ hdf5 extension. + """ + + fig, ax = setup_axes() + hist, d, T = make_hist( + "{:s}.hdf5".format(filename), density_bounds, temperature_bounds, bins + ) + + mappable = ax.pcolormesh(d, T, hist, cmap=cmap, norm=LogNorm()) + fig.colorbar(mappable, label="Number of particles", pad=0) + + fig.tight_layout() + + fig.savefig("rhoTPlot_{:s}.png".format(filename)) + + return + + +def make_movie(args, density_bounds, temperature_bounds, bins): + """ + Makes a movie and saves it to rhoTPlot_{stub}.mp4. + """ + + fig, ax = setup_axes() + + def grab_metadata(n): + filename = "{:s}_{:04d}.hdf5".format(args["stub"], n) + data = SWIFTMetadata(filename) + + return data + + def grab_data(n): + filename = "{:s}_{:04d}.hdf5".format(args["stub"], n) + + H, _, _ = make_hist(filename, density_bounds, temperature_bounds, bins) + + # Need to ravel because pcolormesh's set_array takes a 1D array. Might + # as well do it here, beacuse 1d arrays are easier to max() than 2d. + return H.ravel() + + histograms = [ + grab_data(n) + for n in tqdm( + range(args["initial"], args["final"] + 1), desc="Histogramming data" + ) + ] + + metadata = [ + grab_metadata(n) + for n in tqdm( + range(args["initial"], args["final"] + 1), desc="Grabbing metadata" + ) + ] + + units = SWIFTUnits("{:s}_{:04d}.hdf5".format(args["stub"], args["initial"])) + + # Need to get a reasonable norm so that we don't overshoot. + max_particles = max([x.max() for x in histograms]) + + norm = LogNorm(vmin=1, vmax=max_particles) + + # First, let's make the initial frame (we need this for our d, T values that we + # got rid of in grab_data. + hist, d, T = make_hist( + "{:s}_{:04d}.hdf5".format(args["stub"], args["initial"]), + density_bounds, + temperature_bounds, + bins, + ) + + mappable = ax.pcolormesh(d, T, hist, cmap=cmap, norm=norm) + fig.colorbar(mappable, label="Number of particles", pad=0) + + fig.tight_layout() + + # Once we've rearranged the figure with tight_layout(), we can start laing + # Down the metadata text. + + def format_metadata(metadata: SWIFTMetadata): + t = metadata.t * units.units["Unit time in cgs (U_t)"] + t.convert_to_units(Gyr) + + x = "$a$: {:2.2f}\n$z$: {:2.2f}\n$t$: {:2.2f}".format( + metadata.a, metadata.z, t + ) + + return x + + text = ax.text( + 0.025, + 0.975, + format_metadata(metadata[0]), + ha="left", + va="top", + transform=ax.transAxes, + ) + + ax.text( + 0.975, + 0.975, + metadata[0].code["Git Revision"].decode("utf-8"), + ha="right", + va="top", + transform=ax.transAxes, + ) + + def animate(data): + mappable.set_array(histograms[data]) + text.set_text(format_metadata(metadata[data])) + + return mappable + + animation = FuncAnimation( + fig, animate, range(len(histograms)), fargs=[], interval=1000 / 25 + ) + + animation.save("rhoTPlot_{:s}.mp4".format(args["stub"])) + + return + + +if __name__ == "__main__": + import argparse as ap + + parser = ap.ArgumentParser( + description=""" + Plotting script for making a rho-T plot. + Takes the filename handle, start, and (optionally) stop + snapshots. If stop is not given, png plot is produced for + that snapshot. If given, a movie is made. + """ + ) + + parser.add_argument( + "-i", + "--initial", + help="""Initial snapshot number. Default: 0.""", + default=0, + required=False, + type=int, + ) + + parser.add_argument( + "-f", + "--final", + help="""Final snapshot number. Default: 0.""", + default=0, + required=False, + type=int, + ) + + parser.add_argument( + "-s", + "--stub", + help="""Stub for the filename (e.g. santabarbara). This is + the first part of the filename for the snapshots, + not including the final underscore. Required.""", + type=str, + required=True, + ) + + args = vars(parser.parse_args()) + + if args["final"] <= args["initial"]: + # Run in single image mode. + filename = "{:s}_{:04d}".format(args["stub"], args["initial"]) + + make_single_image( + filename, + density_bounds=density_bounds, + temperature_bounds=temperature_bounds, + bins=bins, + ) + + else: + # Movie mode! + make_movie( + args, + density_bounds=density_bounds, + temperature_bounds=temperature_bounds, + bins=bins, + ) diff --git a/examples/SantaBarbara/SantaBarbara-256/run.sh b/examples/SantaBarbara/SantaBarbara-256/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..72c219acb201b3a3541b6a08d799b21ba4638009 --- /dev/null +++ b/examples/SantaBarbara/SantaBarbara-256/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Run SWIFT +../../swift --cosmology --hydro --self-gravity --threads=28 santa_barbara.yml + diff --git a/examples/SantaBarbara/run_velociraptor.sh b/examples/SantaBarbara/SantaBarbara-256/run_velociraptor.sh similarity index 100% rename from examples/SantaBarbara/run_velociraptor.sh rename to examples/SantaBarbara/SantaBarbara-256/run_velociraptor.sh diff --git a/examples/SantaBarbara/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml similarity index 74% rename from examples/SantaBarbara/santa_barbara.yml rename to examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml index 65e6c239e152d46647a6b0e40b2f100a9d7a3d32..0abbc91019957952276b51db01a3a1b71d6e4fdf 100644 --- a/examples/SantaBarbara/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml @@ -46,15 +46,37 @@ Gravity: # Parameters of the hydro scheme SPH: resolution_eta: 1.2348 # "48 Ngb" with the cubic spline kernel + h_min_ratio: 0.1 CFL_condition: 0.1 initial_temperature: 1200. # (1 + z_ini)^2 * 2.72K minimal_temperature: 100. # Parameters related to the initial conditions InitialConditions: - file_name: ./SantaBarbara.hdf5 + file_name: ./SantaBarbara_256.hdf5 periodic: 1 cleanup_h_factors: 1 # ICs were generated for Gadget, we need to get rid of h-factors cleanup_velocity_factors: 1 # ICs were generated for Gadget, we need to get rid of sqrt(a) factors in the velocity generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs - cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. \ No newline at end of file + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.0 + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0.0 + init_abundance_Nitrogen: 0.0 + init_abundance_Oxygen: 0.0 + init_abundance_Neon: 0.0 + init_abundance_Magnesium: 0.0 + init_abundance_Silicon: 0.0 + init_abundance_Iron: 0.0 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + diff --git a/examples/SantaBarbara/velociraptor_cfg.cfg b/examples/SantaBarbara/SantaBarbara-256/velociraptor_cfg.cfg similarity index 95% rename from examples/SantaBarbara/velociraptor_cfg.cfg rename to examples/SantaBarbara/SantaBarbara-256/velociraptor_cfg.cfg index 4b9b441b71cc65a85a9731018d38ffc2f003c0ff..b904d2a419ad6010e12073209bd77e8f59eef7c4 100644 --- a/examples/SantaBarbara/velociraptor_cfg.cfg +++ b/examples/SantaBarbara/SantaBarbara-256/velociraptor_cfg.cfg @@ -11,7 +11,19 @@ Cosmological_input=1 #Type of snapshot to read. Ignored when using within SWIFT. HDF_name_convention=6 # ILLUSTRIS 0, GADGETX 1, EAGLE 2, GIZMO 3, SIMBA 4, MUFASA 5, SWIFTEAGLE 6 -Particle_search_type=2 #search all particles, see allvars for other types +#whether star particles are present in the input +Input_includes_star_particle=0 +#bhs present +Input_includes_bh_particle=0 +#no wind present +Input_includes_wind_particle=0 +#no tracers present +Input_includes_tracer_particle=0 +#no low res/extra dm particle types present +Input_includes_extradm_particle=0 + + +Particle_search_type=1 #search all particles, see allvars for other types Baryon_searchflag=0 #if 1 search for baryons separately using phase-space search when identifying substructures, 2 allows special treatment in field FOF linking and phase-space substructure search, 0 treat the same as dark matter particles Search_for_substructure=0 #if 0, end search once field objects are found FoF_Field_search_type=5 #5 3DFOF search for field halos, 4 for 6DFOF clean up of field halos, 3 for 6DFOF with velocity scale distinct for each halo diff --git a/examples/SantaBarbara/run.sh b/examples/SantaBarbara/run.sh deleted file mode 100755 index f98b13aac0ea15658319a57b07417d305d3ff509..0000000000000000000000000000000000000000 --- a/examples/SantaBarbara/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# Run SWIFT -../swift -c -s -r -G -t 28 santa_barbara.yml - diff --git a/examples/SantaBarbara_low/run.sh b/examples/SantaBarbara_low/run.sh deleted file mode 100755 index 044f9f4ef340ab95ffe643104294552469a676ce..0000000000000000000000000000000000000000 --- a/examples/SantaBarbara_low/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# Run SWIFT -../swift -c -s -G -t 28 santa_barbara.yml - diff --git a/examples/SmallCosmoVolume_DM/Gadget2/README b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/Gadget2/README similarity index 100% rename from examples/SmallCosmoVolume_DM/Gadget2/README rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/Gadget2/README diff --git a/examples/SmallCosmoVolume_DM/Gadget2/small_cosmo_volume_dm.param b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/Gadget2/small_cosmo_volume_dm.param similarity index 100% rename from examples/SmallCosmoVolume_DM/Gadget2/small_cosmo_volume_dm.param rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/Gadget2/small_cosmo_volume_dm.param diff --git a/examples/SmallCosmoVolume_DM/README b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/README similarity index 100% rename from examples/SmallCosmoVolume_DM/README rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/README diff --git a/examples/SmallCosmoVolume/getIC.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/getIC.sh similarity index 100% rename from examples/SmallCosmoVolume/getIC.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/getIC.sh diff --git a/examples/SmallCosmoVolume_DM/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh similarity index 69% rename from examples/SmallCosmoVolume_DM/run.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh index 8a4cd944c713b387fcc38a6328bcb1d763c524a9..5d166e00a630ca93ff92a42f6d26b012b132e097 100755 --- a/examples/SmallCosmoVolume_DM/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/run.sh @@ -8,5 +8,5 @@ then fi # Run SWIFT -../swift -c -G -t 8 small_cosmo_volume_dm.yml 2>&1 | tee output.log +../../swift --cosmology --self-gravity --threads=8 small_cosmo_volume_dm.yml 2>&1 | tee output.log diff --git a/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml similarity index 98% rename from examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml index 910137edc442c994a9f31a8c62e16818ca4ae97d..ebe3a78ee0d03eb53752b1dfa8fa749931a754a9 100644 --- a/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml @@ -10,7 +10,6 @@ InternalUnitSystem: StructureFinding: config_file_name: stf_input_6dfof_dmonly_sub.cfg basename: ./stf - output_time_format: 1 scale_factor_first: 0.02 delta_time: 1.02 diff --git a/examples/SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg b/examples/SmallCosmoVolume/SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg similarity index 100% rename from examples/SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg rename to examples/SmallCosmoVolume/SmallCosmoVolume_DM/stf_input_6dfof_dmonly_sub.cfg diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/README b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/README new file mode 100644 index 0000000000000000000000000000000000000000..76eab82c0c434ceab334f82be8bd52e0d2dd4d08 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/README @@ -0,0 +1,16 @@ +Small LCDM cosmological simulation generated by C. Power. Cosmology +is WMAP9 and the box is 100Mpc/h in size with 64^3 particles. +We use a softening length of 1/25th of the mean inter-particle separation. + +The ICs have been generated to run with Gadget-2 so we need to switch +on the options to cancel the h-factors and a-factors at reading time. +We generate gas from the ICs using SWIFT's internal mechanism and set the +temperature to the expected gas temperature at this redshift. + +This example runs with Hydrodynamics and a halo finder, the halo finder +is run while running the simulation. At the end it is possible to +calculate the halo mass function of the halos in the simulated +volume, this is done by using haloevol.py. + +MD5 checksum of the ICs: +08736c3101fd738e22f5159f78e6022b small_cosmo_volume.hdf5 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/getHMF.py b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/getHMF.py new file mode 100755 index 0000000000000000000000000000000000000000..e56df323b004dfcfcd2c75c427fa6f3ecbe37a29 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/getHMF.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +from hmf import MassFunction +import hmf +from astropy.cosmology import FlatLambdaCDM + + + +def getHMFz(z, H0=70.3, Om0=0.276, Ob0=0.0455, Tcmb0=2.725, Mmin=1e10, Mmax=1e15): + """ Fast function to call the HMF from hmf, this function only has + 7 variables and will return the dn/d(log10 M) and M array. + z: redshift + H0: Hubble constant + Om0: Matter density + Ob0: Baryon density + Tcmb0: CMB temperature at z=0 + Mmin: minimum mass (solar masses) + Mmax: Maximum mass (solar masses) + """ + new_model = FlatLambdaCDM(H0=H0, Om0=Om0, Ob0=Ob0, Tcmb0=Tcmb0) + hmff = MassFunction( + cosmo_model=new_model, + Mmax=np.log10(Mmax), + Mmin=np.log10(Mmin), + z=z, + hmf_model="ST", + ) + return hmff.m, hmff.dndlog10m + + +def getHMFztinker(z, H0=70.3, Om0=0.276, Ob0=0.0455, Tcmb0=2.725, Mmin=1e10, Mmax=1e15): + """ Fast function to call the HMF from hmf, this function only has + 6 variables and will return the dn/d(log10 M) and M array. + H0: Hubble constant + Om0: Matter density + Ob0: Baryon density + Tcmb0: CMB temperature at z=0 + Mmin: minimum mass (solar masses) + Mmax: Maximum mass (solar masses) + """ + new_model = FlatLambdaCDM(H0=H0, Om0=Om0, Ob0=Ob0, Tcmb0=Tcmb0) + hmff = MassFunction( + cosmo_model=new_model, Mmax=np.log10(Mmax), Mmin=np.log10(Mmin), z=z + ) + return hmff.m, hmff.dndlog10m + + diff --git a/examples/SmallCosmoVolume_DM/getIC.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/getIC.sh similarity index 100% rename from examples/SmallCosmoVolume_DM/getIC.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/getIC.sh diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/haloevol.py b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/haloevol.py new file mode 100755 index 0000000000000000000000000000000000000000..94e206cdf686ef5d2d3676d6fc36d6dfe8aea558 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/haloevol.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# 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/>. +# +################################################################################ +import numpy as np +import h5py +import matplotlib.pyplot as plt +from getHMF import getHMFz, getHMFztinker + +dlogm = 0.2 +bins = 10 ** (np.arange(12, 15.2, dlogm)) +V = 142.0 ** 3 + +itervalues = np.array([175, 185, 192, 198]) + +for j in itervalues: + # Load the data + g = h5py.File("stf_%04d.VELOCIraptor.properties" % j, "r") + mass = g["Mass_200crit"][:] * 1e10 # convert to the correct unit + binnedmass, massrange = np.histogram(mass, bins=bins) + + massnlarger = np.zeros(len(binnedmass)) + for i in range(0, len(massnlarger)): + massnlarger[i] = np.sum(binnedmass[i:]) + + f = h5py.File("snap_%04d.hdf5" % (j + 1)) + cosmo = f["Cosmology"] + redshift = cosmo.attrs["Redshift"][0] + a = cosmo.attrs["Scale-factor"][0] + + # Determine the HMF + errormassn = massnlarger ** 0.5 + numbden = massnlarger / V / a ** 3 + numbdenerr = errormassn / V / a ** 3 + massplot = (massrange[0:15] + massrange[1:16]) / 2 + dernumbden = -np.diff(numbden) / np.diff(np.log10(massplot)) + dererr = 2 ** 0.5 / dlogm * (numbdenerr[0:14] + numbdenerr[1:15]) / 2 + + plt.plot( + (massplot[0:14] + massplot[1:15]) / 2, dernumbden, label="SWIFT - SPH $64^3$" + ) + plt.fill_between( + (massplot[0:14] + massplot[1:15]) / 2, + dernumbden - dererr, + dernumbden + dererr, + alpha=0.4, + ) + plt.xscale("log") + plt.ylim(1e-6, 1e-1) + plt.xlim(10 ** 11, 10 ** 15.5) + + xplace = 10 ** 14.5 + plt.text(xplace, 10 ** -2.3, "$\Omega_m=0.276$") + plt.text(xplace, 10 ** -2.6, "$\Omega_b=0.0455$") + plt.text(xplace, 10 ** -2.9, "$\Omega_\Lambda=0.724$") + plt.text(xplace, 10 ** -3.2, "$h=0.703$") + plt.text(xplace, 10 ** -3.5, "$z=%2.2f$" % redshift) + + m, dndlogm = getHMFz(redshift) + plt.plot(m / 0.7, dndlogm * 0.7 ** 3, label="Sheth et al. 2001") + + m, dndlogm = getHMFztinker(redshift) + plt.plot(m / 0.7, dndlogm * 0.7 ** 3, label="Tinker et al. 2008") + + plt.xlabel("M${}_{200}$ ($M_\odot$)") + plt.ylabel("dn/d($\log$10(M${}_{200}$) ($Mpc^{-3}$)") + plt.axvline(x=32 * 3.5e11, linestyle="--", color="k") + plt.yscale("log") + plt.legend() + plt.savefig("./HMF_%04d.png" % j) + plt.close() diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b89662ed7ae621c6fbc29ccfb566fa61367693a9 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e small_cosmo_volume.hdf5 ] +then + echo "Fetching initial conditions for the small cosmological volume example..." + ./getIC.sh +fi + +# Run SWIFT +../../swift --cosmology --hydro --self-gravity --velociraptor --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log + +echo "Make a plot of the HMF" +if command -v python3 &>/dev/null; then + python3 haloevol.py +else + python haloevol.py +fi diff --git a/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml similarity index 77% rename from examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml rename to examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml index b091207fa98d8b6604124fb9d99c80d65e97dfb2..15007ca8d39a328166a208a2d4cbbc6aea580009 100644 --- a/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/small_cosmo_volume.yml @@ -30,6 +30,7 @@ Gravity: # Parameters of the hydro scheme SPH: resolution_eta: 1.2348 # "48 Ngb" with the cubic spline kernel + h_min_ratio: 0.1 CFL_condition: 0.1 initial_temperature: 7075. # (1 + z_ini)^2 * 2.72K minimal_temperature: 100. @@ -37,8 +38,9 @@ SPH: # Parameters governing the snapshots Snapshots: basename: snap - delta_time: 1.02 + delta_time: 1.05 scale_factor_first: 0.02 + invoke_stf: 1 # Parameters governing the conserved quantities statistics Statistics: @@ -55,9 +57,13 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 cleanup_velocity_factors: 1 - generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs - cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. -# Constant lambda cooling function -LambdaCooling: - lambda_nH2_cgs: 1e-26 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) +# Structure finding options (requires velociraptor) +StructureFinding: + config_file_name: stfconfig_input.cfg + basename: ./stf + scale_factor_first: 0.02 + delta_time: 1.02 + diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/stfconfig_input.cfg b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/stfconfig_input.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4306bae3d23aab924ce8fa3a5c50e839823fbc2f --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_VELOCIraptor/stfconfig_input.cfg @@ -0,0 +1,165 @@ +#suggested configuration file for hydro run and subhalo (and galaxy ie: associated baryons) catalog + +################################ +#input related +################################ +#input is from a cosmological so can use parameters like box size, h, Omega_m to calculate length and density scales +Cosmological_input=1 + +#sets the total buffer size in bytes used to store temporary particle information +#of mpi read threads before they are broadcast to the appropriate waiting non-read threads +#if not set, default value is equivalent to 1e6 particles per mpi process, quite large +#but significantly minimises the number of send/receives +#in this example the buffer size is roughly that for a send/receive of 10000 particles +#for 100 mpi processes +MPI_particle_total_buf_size=100000000 + +#gadget input related +#NSPH_extra_blocks=0 #read extra sph blocks +#NStar_extra_blocks=0 #read extra star blocks +#NBH_extra_blocks=0 #read extra black hole blocks + +#HDF related input +#Set the HDF name convection, 0 is illustris, 1 is gadget x, 2 is Eagle, 3 is gizmo +HDF_name_convention=0 +#whether star particles are present in the input +Input_includes_star_particle=1 +#bhs present +Input_includes_bh_particle=1 +#no wind present +Input_includes_wind_particle=0 +#no tracers present +Input_includes_tracer_particle=0 +#no low res/extra dm particle types present +Input_includes_extradm_particle=0 + +################################ +#unit options, should always be provided +################################ +#EDIT THIS SECTION!!!! +#conversion of output length units to kpc +Length_unit_to_kpc=1.0 +#conversion of output velocity units to km/s +Velocity_to_kms=1.0 +#conversion of output mass units to solar masses +Mass_to_solarmass=1.0 +#units conversion from input input to desired internal unit +Length_unit=1.0 #default code unit, +Velocity_unit=1.0 #default velocity unit, +Mass_unit=1.0 #default mass unit, +Gravity=43.0211349 #for 1e10 Msun, km/s and Mpc +Hubble_unit=100.0 # assuming units are km/s and Mpc, then value of Hubble in km/s/Mpc + +################################ +#search related options +################################ + +#how to search a simulation +Particle_search_type=1 #search all particles, see allvars for other types +#for baryon search +Baryon_searchflag=2 #if 1 search for baryons separately using phase-space search when identifying substructures, 2 allows special treatment in field FOF linking and phase-space substructure search, 0 treat the same as dark matter particles +#for search for substruture +Search_for_substructure=1 #if 0, end search once field objects are found +#also useful for zoom simulations or simulations of individual objects, setting this flag means no field structure search is run +Singlehalo_search=0 #if file is single halo in which one wishes to search for substructure +#additional option for field haloes +Keep_FOF=0 #if field 6DFOF search is done, allows to keep structures found in 3DFOF (can be interpreted as the inter halo stellar mass when only stellar search is used).\n + +#minimum size for structures +Minimum_size=20 #min 20 particles +Minimum_halo_size=-1 #if field halos have different minimum sizes, otherwise set to -1. + +#for field fof halo search +FoF_Field_search_type=3 #5 3DFOF search for field halos, 4 for 6DFOF clean up of field halos, 3 for 6DFOF with velocity scale distinct for each halo +Halo_linking_length_factor=2.0 #factor by which Physical_linking_length is changed when searching for field halos. Typical values are ~2 when using iterative substructure search. +Halo_velocity_linking_length_factor=5.0 #for 6d fof halo search increase ellv from substructure search + +#for mean field estimates and local velocity density distribution funciton estimator related quantiites, rarely need to change this +Cell_fraction = 0.01 #fraction of field fof halo used to determine mean velocity distribution function. Typical values are ~0.005-0.02 +Grid_type=1 #normal entropy based grid, shouldn't have to change +Nsearch_velocity=32 #number of velocity neighbours used to calculate local velocity distribution function. Typial values are ~32 +Nsearch_physical=256 #numerof physical neighbours from which the nearest velocity neighbour set is based. Typical values are 128-512 + +#for substructure search, rarely ever need to change this +FoF_search_type=1 #default phase-space FOF search. Don't really need to change +Iterative_searchflag=1 #iterative substructure search, for substructure find initial candidate substructures with smaller linking lengths then expand search region +Outlier_threshold=2.5 #outlier threshold for a particle to be considered residing in substructure, that is how dynamically distinct a particle is. Typical values are >2 +Velocity_ratio=2.0 #ratio of speeds used in phase-space FOF +Velocity_opening_angle=0.10 #angle between velocities. 18 degrees here, typical values are ~10-30 +Physical_linking_length=0.10 #physical linking length. IF reading periodic volumes in gadget/hdf/ramses, in units of the effective inter-particle spacing. Otherwise in user defined code units. Here set to 0.10 as iterative flag one, values of 0.1-0.3 are typical. +Velocity_linking_length=0.20 #where scaled by structure dispersion +Significance_level=1.0 #how significant a substructure is relative to Poisson noise. Values >= 1 are fine. + +#for iterative substructure search, rarely ever need to change this +Iterative_threshold_factor=1.0 #change in threshold value when using iterative search. Here no increase in threshold if iterative or not +Iterative_linking_length_factor=2.0 #increase in final linking final iterative substructure search will be sqrt(2.25)*this factor +Iterative_Vratio_factor=1.0 #change in Vratio when using iterative search. no change in vratio +Iterative_ThetaOp_factor=1.0 #change in velocity opening angle. no change in velocity opening angle + +#for checking for halo merger remnants, which are defined as large, well separated phase-space density maxima +Halo_core_search=2 # searches for separate 6dfof cores in field haloes, and then more than just flags halo as merging, assigns particles to each merging "halo". 2 is full separation, 1 is flagging, 0 is off +#if searching for cores, linking lengths. likely does not need to change much +Use_adaptive_core_search=0 #calculate dispersions in configuration & vel space to determine linking lengths +Use_phase_tensor_core_growth=2 #use full stepped phase-space tensor assignment +Halo_core_ellx_fac=0.7 #how linking lengths are changed when searching for local 6DFOF cores, +Halo_core_ellv_fac=2.0 #how velocity lengths based on dispersions are changed when searching for local 6DFOF cores +Halo_core_ncellfac=0.005 #fraction of total halo particle number setting min size of a local 6DFOF core +Halo_core_num_loops=8 #number of loops to iteratively search for cores +Halo_core_loop_ellx_fac=0.75 #how much to change the configuration space linking per iteration +Halo_core_loop_ellv_fac=1.0 #how much to change the velocity space linking per iteration +Halo_core_loop_elln_fac=1.2 #how much to change the min number of particles per iteration +Halo_core_phase_significance=2.0 #how significant a core must be in terms of dispersions (sigma) significance + +################################ +#Unbinding options (VELOCIraptor is able to accurately identify tidal debris so particles need not be bound to a structure) +################################ + +#unbinding related items +Unbind_flag=1 #run unbinding +#alpha factor used to determine whether particle is "bound" alaph*T+W<0. For standard subhalo catalogues use >0.9 but if interested in tidal debris 0.2-0.5 +Allowed_kinetic_potential_ratio=0.95 +#run unbinding of field structures, aka halos +Bound_halos=0 +#simple Plummer softening length when calculating gravitational energy. If cosmological simulation with period, is fraction of interparticle spacing +Softening_length=0. +#don't keep background potential when unbinding +Keep_background_potential=0 + +################################ +#Cosmological parameters +#this is typically overwritten by information in the gadget/hdf header if those input file types are read +################################ +h_val=1.0 +Omega_m=0.3 +Omega_Lambda=0.7 +Critical_density=1.0 +Virial_density=200 #so-called virial overdensity value +Omega_b=0. #no baryons + +################################ +#Calculation of properties related options +################################ +#when calculating properties, for field objects calculate inclusive masses +Inclusive_halo_masses=1 #calculate inclusive masses +#ensures that output is comoving distances per little h +Comoving_units=0 + +################################ +#output related +################################ + +Write_group_array_file=0 #write a group array file +Separate_output_files=0 #separate output into field and substructure files similar to subfind +Binary_output=2 #binary output 1, ascii 0, and HDF 2 + +#halo ids are adjusted by this value * 1000000000000 (or 1000000 if code compiled with the LONGINTS option turned off) +#to ensure that halo ids are temporally unique. So if you had 100 snapshots, for snap 100 set this to 100 and 100*1000000000000 will +#be added to the halo id as set for this snapshot, so halo 1 becomes halo 100*1000000000000+1 and halo 1 of snap 0 would just have ID=1 + +#ALTER THIS as part of a script to get temporally unique ids +Snapshot_value=SNAP + +################################ +#other options +################################ +Verbose=0 #how talkative do you want the code to be, 0 not much, 1 a lot, 2 chatterbox diff --git a/examples/SmallCosmoVolume_cooling/README b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/README similarity index 81% rename from examples/SmallCosmoVolume_cooling/README rename to examples/SmallCosmoVolume/SmallCosmoVolume_cooling/README index 2ee364ca2f00a4280a492a11520bf7b9a1ac1f7d..357250f79e5e2b5d5408b3685c95767838f4bb70 100644 --- a/examples/SmallCosmoVolume_cooling/README +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/README @@ -12,8 +12,11 @@ model chosen at the time SWIFT was configured, the answer will be different. Interesting cases to compare to the no-cooling case are a constant cooling rate or Compton cooling. -The 'plotTempEvolution.py' plots the temperature evolution of the gas -in the simulated volume. +The 'plotTempEvolution.py' script plots the temperature evolution of +the gas in the simulated volume. + +The 'plotRhoT.py script plots the phase-space diagram for a given +snapshot. MD5 checksum of the ICs: 08736c3101fd738e22f5159f78e6022b small_cosmo_volume.hdf5 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/getCoolingTables.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/getCoolingTables.sh new file mode 100755 index 0000000000000000000000000000000000000000..ecd581fd3dd44a13af1218d7dee6af72a25a324a --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/getCoolingTables.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/EAGLE/coolingtables.tar.gz +tar -xvzf coolingtables.tar.gz diff --git a/examples/SmallCosmoVolume_cooling/getIC.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/getIC.sh similarity index 100% rename from examples/SmallCosmoVolume_cooling/getIC.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_cooling/getIC.sh diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py new file mode 100644 index 0000000000000000000000000000000000000000..4ba8ad66daca1d9614be8917a77407dd99209dea --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py @@ -0,0 +1,163 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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/>. +# +################################################################################ + +# Computes the temperature evolution of the gas in a cosmological box + +# Physical constants needed for internal energy to temperature conversion +k_in_J_K = 1.38064852e-23 +mH_in_kg = 1.6737236e-27 + +import matplotlib + +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": True, + "figure.figsize": (3.15, 3.15), + "figure.subplot.left": 0.15, + "figure.subplot.right": 0.99, + "figure.subplot.bottom": 0.13, + "figure.subplot.top": 0.99, + "figure.subplot.wspace": 0.15, + "figure.subplot.hspace": 0.12, + "lines.markersize": 6, + "lines.linewidth": 2.0, + "text.latex.unicode": True, +} +rcParams.update(params) +rc("font", **{"family": "sans-serif", "sans-serif": ["Times"]}) + +snap = int(sys.argv[1]) + +# Read the simulation data +sim = h5py.File("snap_%04d.hdf5" % snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +z = sim["/Cosmology"].attrs["Redshift"][0] +a = sim["/Cosmology"].attrs["Scale-factor"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs[ + "Hydrogen ionization transition temperature" +][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] + +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Primoridal ean molecular weight as a function of temperature +def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp): + if T > T_trans: + return 4.0 / (8.0 - 5.0 * (1.0 - H_frac)) + else: + return 4.0 / (1.0 + 3.0 * H_frac) + + +# Temperature of some primoridal gas with a given internal energy +def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp): + T_over_mu = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K + ret = np.ones(np.size(u)) * T_trans + + # Enough energy to be ionized? + mask_ionized = T_over_mu > (T_trans + 1) / mu(T_trans + 1, H_frac, T_trans) + if np.sum(mask_ionized) > 0: + ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans * 10, H_frac, T_trans) + + # Neutral gas? + mask_neutral = T_over_mu < (T_trans - 1) / mu((T_trans - 1), H_frac, T_trans) + if np.sum(mask_neutral) > 0: + ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans) + + return ret + + +rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergy"][:] + +# Compute the temperature +u *= unit_length_in_si ** 2 / unit_time_in_si ** 2 +u /= a ** (3 * (gas_gamma - 1.0)) +Temp = T(u) + +# Compute the physical density +rho *= unit_mass_in_cgs / unit_length_in_cgs ** 3 +rho /= a ** 3 +rho /= mH_in_kg + +# Life is better in log-space +log_T = np.log10(Temp) +log_rho = np.log10(rho) + + +# Make a 2D histogram +log_rho_min = -6 +log_rho_max = 3 +log_T_min = 1 +log_T_max = 8 + +bins_x = np.linspace(log_rho_min, log_rho_max, 54) +bins_y = np.linspace(log_T_min, log_T_max, 54) +H, _, _ = histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True) + + +# Plot the interesting quantities +figure() + +pcolormesh(bins_x, bins_y, np.log10(H).T) + +text(-5, 8.0, "$z=%.2f$" % z) + +xticks( + [-5, -4, -3, -2, -1, 0, 1, 2, 3], + ["", "$10^{-4}$", "", "$10^{-2}$", "", "$10^0$", "", "$10^2$", ""], +) +yticks( + [2, 3, 4, 5, 6, 7, 8], ["$10^{2}$", "", "$10^{4}$", "", "$10^{6}$", "", "$10^8$"] +) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(-5.2, 3.2) +ylim(1, 8.5) + +savefig("rhoT_%04d.png" % snap, dpi=200) diff --git a/examples/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py similarity index 98% rename from examples/SmallCosmoVolume_cooling/plotTempEvolution.py rename to examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py index d3e4e9047ed7f2cc57ef581526488e64ccc26092..988ea36163203a50928cc7fd8f9c81f4d3a377ff 100644 --- a/examples/SmallCosmoVolume_cooling/plotTempEvolution.py +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py @@ -71,7 +71,7 @@ git = sim["Code"].attrs["Git Revision"] cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"] if cooling_model == "Constant Lambda": - Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0] + Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0] # Cosmological parameters H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] @@ -109,7 +109,6 @@ def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp): return ret - z = np.zeros(n_snapshots) a = np.zeros(n_snapshots) T_mean = np.zeros(n_snapshots) @@ -164,6 +163,8 @@ legend(loc="upper left", frameon=False, handlelength=1.5) # Cooling model if cooling_model == "Constant Lambda": text(1e-2, 6e4, "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"%(Lambda/10.**(int(log10(Lambda))), log10(Lambda)), fontsize=7) +elif cooling_model == "EAGLE": + text(1e-2, 6e4, "EAGLE (Wiersma et al. (2009)") else: text(1e-2, 6e4, "No cooling") diff --git a/examples/SmallCosmoVolume_cooling/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh similarity index 53% rename from examples/SmallCosmoVolume_cooling/run.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh index 454ed279503db17ea2deddb2ae982206238ec23f..5ce9e2bb3b544eb915f189d4aa417fcc9c316b64 100755 --- a/examples/SmallCosmoVolume_cooling/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh @@ -7,8 +7,14 @@ then ./getIC.sh fi +if [ ! -e coolingtables ] +then + echo "Fetching cooling tables for the small cosmological volume example..." + ./getCoolingTables.sh +fi + # Run SWIFT -../swift -c -s -G -C -t 8 small_cosmo_volume.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --cooling --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log # Plot the temperature evolution python plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml similarity index 63% rename from examples/SmallCosmoVolume/small_cosmo_volume.yml rename to examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml index a6ce1f28198a99422ea1c80178fc8000b66d777e..60046cadc3bd3af324b5f967c76315cf5a27fe52 100644 --- a/examples/SmallCosmoVolume/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml @@ -30,6 +30,7 @@ Gravity: # Parameters of the hydro scheme SPH: resolution_eta: 1.2348 # "48 Ngb" with the cubic spline kernel + h_min_ratio: 0.1 CFL_condition: 0.1 initial_temperature: 7075. # (1 + z_ini)^2 * 2.72K minimal_temperature: 100. @@ -55,5 +56,30 @@ InitialConditions: periodic: 1 cleanup_h_factors: 1 cleanup_velocity_factors: 1 - generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs - cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + +# Constant lambda cooling function +LambdaCooling: + lambda_nH2_cgs: 1e-26 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) + +# EAGLE cooling function +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Impose primoridal metallicity +EAGLEChemistry: + init_abundance_metal: 0.0 + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0.0 + init_abundance_Nitrogen: 0.0 + init_abundance_Oxygen: 0.0 + init_abundance_Neon: 0.0 + init_abundance_Magnesium: 0.0 + init_abundance_Silicon: 0.0 + init_abundance_Iron: 0.0 diff --git a/examples/SmallCosmoVolume/README b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/README similarity index 100% rename from examples/SmallCosmoVolume/README rename to examples/SmallCosmoVolume/SmallCosmoVolume_hydro/README diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/getIC.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..3b8136cc5aca00a25792655c6c505cfeeb0f2bc9 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/getIC.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/small_cosmo_volume.hdf5 + diff --git a/examples/SmallCosmoVolume/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py similarity index 100% rename from examples/SmallCosmoVolume/plotTempEvolution.py rename to examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh similarity index 73% rename from examples/SmallCosmoVolume/run.sh rename to examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh index e243215813598fade53c07b017e380be08558942..b2585d70b7cd2b717af02f005d690d0e8a9f932e 100755 --- a/examples/SmallCosmoVolume/run.sh +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/run.sh @@ -8,7 +8,7 @@ then fi # Run SWIFT -../swift -c -s -G -t 8 small_cosmo_volume.yml 2>&1 | tee output.log +../../swift --cosmology --hydro --self-gravity --threads=8 small_cosmo_volume.yml 2>&1 | tee output.log # Plot the temperature evolution python plotTempEvolution.py diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml new file mode 100644 index 0000000000000000000000000000000000000000..e0d633079e941ade161b7e2fde0fbc063cbac254 --- /dev/null +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml @@ -0,0 +1,72 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun + UnitLength_in_cgs: 3.08567758e24 # 1 Mpc + UnitVelocity_in_cgs: 1e5 # 1 km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +Cosmology: # WMAP9 cosmology + Omega_m: 0.276 + Omega_lambda: 0.724 + Omega_b: 0.0455 + h: 0.703 + a_begin: 0.019607843 # z_ini = 50. + a_end: 1.0 # z_end = 0. + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-6 + dt_max: 1e-2 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 + theta: 0.3 + comoving_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + max_physical_softening: 0.0889 # 1/25th of the mean inter-particle separation: 88.9 kpc + mesh_side_length: 64 + +# Parameters of the hydro scheme +SPH: + resolution_eta: 1.2348 # "48 Ngb" with the cubic spline kernel + h_min_ratio: 0.1 + CFL_condition: 0.1 + initial_temperature: 7075. # (1 + z_ini)^2 * 2.72K + minimal_temperature: 100. + +# Parameters governing the snapshots +Snapshots: + basename: snap + delta_time: 1.02 + scale_factor_first: 0.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 + scale_factor_first: 0.02 + +Scheduler: + max_top_level_cells: 8 + cell_split_size: 50 + +# Parameters related to the initial conditions +InitialConditions: + file_name: small_cosmo_volume.hdf5 + periodic: 1 + cleanup_h_factors: 1 + cleanup_velocity_factors: 1 + generate_gas_in_ics: 1 # Generate gas particles from the DM-only ICs + cleanup_smoothing_lengths: 1 # Since we generate gas, make use of the (expensive) cleaning-up procedure. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + diff --git a/examples/SmoothedMetallicity/getGlass.sh b/examples/SubgridTests/SmoothedMetallicity/getGlass.sh similarity index 100% rename from examples/SmoothedMetallicity/getGlass.sh rename to examples/SubgridTests/SmoothedMetallicity/getGlass.sh diff --git a/examples/SmoothedMetallicity/makeIC.py b/examples/SubgridTests/SmoothedMetallicity/makeIC.py similarity index 100% rename from examples/SmoothedMetallicity/makeIC.py rename to examples/SubgridTests/SmoothedMetallicity/makeIC.py diff --git a/examples/SmoothedMetallicity/plotSolution.py b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py similarity index 100% rename from examples/SmoothedMetallicity/plotSolution.py rename to examples/SubgridTests/SmoothedMetallicity/plotSolution.py diff --git a/examples/SmoothedMetallicity/run.sh b/examples/SubgridTests/SmoothedMetallicity/run.sh similarity index 82% rename from examples/SmoothedMetallicity/run.sh rename to examples/SubgridTests/SmoothedMetallicity/run.sh index de8c55d678bcb611934af450940d8ed8e6c15d6b..736a16fc14ece7b09e13b61cd8e04f9735e6cfc6 100755 --- a/examples/SmoothedMetallicity/run.sh +++ b/examples/SubgridTests/SmoothedMetallicity/run.sh @@ -13,7 +13,7 @@ then fi # Run SWIFT -../swift -n 1 -s -t 4 smoothed_metallicity.yml 2>&1 | tee output.log +../../swift --steps=1 --hydro --threads=4 smoothed_metallicity.yml 2>&1 | tee output.log # Plot the solution python plotSolution.py 1 diff --git a/examples/SmoothedMetallicity/smoothed_metallicity.yml b/examples/SubgridTests/SmoothedMetallicity/smoothed_metallicity.yml similarity index 100% rename from examples/SmoothedMetallicity/smoothed_metallicity.yml rename to examples/SubgridTests/SmoothedMetallicity/smoothed_metallicity.yml diff --git a/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml b/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml new file mode 100644 index 0000000000000000000000000000000000000000..a59ae302ff4052a3daf0535e93a0c2cd5e9904f5 --- /dev/null +++ b/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml @@ -0,0 +1,44 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Values of some physical constants +PhysicalConstants: + G: 0 # (Optional) Overwrite the value of Newton's constant used internally by the code. + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 5e-2 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: SN_feedback # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1e-2 # Time difference between consecutive outputs (in internal units) + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-3 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./SN_feedback.hdf5 + smoothing_length_scaling: 1. + periodic: 1 # Are we running with periodic ICs? + +# Parameters for the stellar models +Stars: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). diff --git a/examples/SubgridTests/SupernovaeFeedback/getGlass.sh b/examples/SubgridTests/SupernovaeFeedback/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/SubgridTests/SupernovaeFeedback/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/SubgridTests/SupernovaeFeedback/makeIC.py b/examples/SubgridTests/SupernovaeFeedback/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..8a0fca2bfd21f2cf1d5052660581d97184705480 --- /dev/null +++ b/examples/SubgridTests/SupernovaeFeedback/makeIC.py @@ -0,0 +1,117 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@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/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the Sedov blast test in a periodic cubic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +rho0 = 1. # Background density +P0 = 1.e-6 # Background pressure +E0= 1. # Energy of the explosion +N_inject = 15 # Number of particles in which to inject energy +fileName = "SN_feedback.hdf5" + +#--------------------------------------------------- +glass = h5py.File("glassCube_64.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +eps = 1e-6 +pos = (pos - pos.min()) / (pos.max() - pos.min() + eps) +h = glass["/PartType0/SmoothingLength"][:] * 0.3 * 3.3 + +numPart = size(h) +vol = 1. +Boxsize = 1. + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) +r = zeros(numPart) + +r = sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2 + (pos[:,2] - 0.5)**2) +m[:] = rho0 * vol / numPart +u[:] = P0 / (rho0 * (gamma - 1)) + +#-------------------------------------------------- + +star_pos = zeros((1, 3)) +star_pos[:,:] = 0.5 * Boxsize + +star_v = zeros((1, 3)) +star_v[:,:] = 0. + +# increase mass to keep it at center +star_m = 1e3 * array([rho0 * vol / numPart]) +star_ids = array([numPart + 1]) +star_h = array([h.max()]) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [Boxsize]*3 +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 1, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 1, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 0 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +# stellar group +grp = file.create_group("/PartType4") +grp.create_dataset("Coordinates", data=star_pos, dtype="d") +grp.create_dataset('Velocities', data=star_v, dtype='f') +grp.create_dataset('Masses', data=star_m, dtype='f') +grp.create_dataset('SmoothingLength', data=star_h, dtype='f') +grp.create_dataset('ParticleIDs', data=star_ids, dtype='L') + + +file.close() diff --git a/examples/SubgridTests/SupernovaeFeedback/run.sh b/examples/SubgridTests/SupernovaeFeedback/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..af8802164c528fe14b3d525827107868a8ac5720 --- /dev/null +++ b/examples/SubgridTests/SupernovaeFeedback/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the Supernovae feedback example..." + ./getGlass.sh +fi +if [ ! -e SN_feedback.hdf5 ] +then + echo "Generating initial conditions for the Supernovae feedback example..." + python makeIC.py +fi + +# Run SWIFT +../../swift --external-gravity --feedback --hydro --stars --threads=4 SN_feedback.yml 2>&1 | tee output.log + +# Plot the solution +# TODO diff --git a/examples/main.c b/examples/main.c index 381a7793d6a531942fb9af470fa56ac92c21f5f5..cd94503849ef13d3f14cd798c074ec4fe01d78d1 100644 --- a/examples/main.c +++ b/examples/main.c @@ -41,6 +41,7 @@ #endif /* Local headers. */ +#include "argparse.h" #include "swift.h" /* Engine policy flags. */ @@ -51,70 +52,27 @@ /* Global profiler. */ struct profiler prof; -/** - * @brief Help messages for the command line parameters. - */ -void print_help_message(void) { - - printf("\nUsage: swift [OPTION]... PARAMFILE\n"); - printf(" swift_mpi [OPTION]... PARAMFILE\n\n"); - - printf("Valid options are:\n"); - printf(" %2s %14s %s\n", "-a", "", "Pin runners using processor affinity."); - printf(" %2s %14s %s\n", "-c", "", - "Run with cosmological time integration."); - printf(" %2s %14s %s\n", "-C", "", "Run with cooling."); - printf( - " %2s %14s %s\n", "-d", "", - "Dry run. Read the parameter file, allocate memory but does not read "); - printf( - " %2s %14s %s\n", "", "", - "the particles from ICs and exit before the start of time integration."); - printf(" %2s %14s %s\n", "", "", - "Allows user to check validy of parameter and IC files as well as " - "memory limits."); - printf(" %2s %14s %s\n", "-D", "", - "Always drift all particles even the ones far from active particles. " - "This emulates"); - printf(" %2s %14s %s\n", "", "", - "Gadget-[23] and GIZMO's default behaviours."); - printf(" %2s %14s %s\n", "-e", "", - "Enable floating-point exceptions (debugging mode)."); - printf(" %2s %14s %s\n", "-f", "{int}", - "Overwrite the CPU frequency (Hz) to be used for time measurements."); - printf(" %2s %14s %s\n", "-g", "", - "Run with an external gravitational potential."); - printf(" %2s %14s %s\n", "-G", "", "Run with self-gravity."); - printf(" %2s %14s %s\n", "-M", "", - "Reconstruct the multipoles every time-step."); - printf(" %2s %14s %s\n", "-n", "{int}", - "Execute a fixed number of time steps. When unset use the time_end " - "parameter to stop."); - printf(" %2s %14s %s\n", "-o", "{str}", - "Generate a default output parameter file."); - printf(" %2s %14s %s\n", "-P", "{sec:par:val}", - "Set parameter value and overwrites values read from the parameters " - "file. Can be used more than once."); - printf(" %2s %14s %s\n", "-r", "", "Continue using restart files."); - printf(" %2s %14s %s\n", "-s", "", "Run with hydrodynamics."); - printf(" %2s %14s %s\n", "-S", "", "Run with stars."); - printf(" %2s %14s %s\n", "-b", "", "Run with stars feedback."); - printf(" %2s %14s %s\n", "-t", "{int}", - "The number of threads to use on each MPI rank. Defaults to 1 if not " - "specified."); - printf(" %2s %14s %s\n", "-T", "", "Print timers every time-step."); - printf(" %2s %14s %s\n", "-v", "[12]", "Increase the level of verbosity:"); - printf(" %2s %14s %s\n", "", "", "1: MPI-rank 0 writes,"); - printf(" %2s %14s %s\n", "", "", "2: All MPI-ranks write."); - printf(" %2s %14s %s\n", "-x", "", "Run with structure finding."); - printf(" %2s %14s %s\n", "-y", "{int}", - "Time-step frequency at which task graphs are dumped."); - printf(" %2s %14s %s\n", "-Y", "{int}", - "Time-step frequency at which threadpool tasks are dumped."); - printf(" %2s %14s %s\n", "-h", "", "Print this help message and exit."); - printf( - "\nSee the file parameter_example.yml for an example of " - "parameter file.\n"); +/* Usage string. */ +static const char *const swift_usage[] = { + "swift [options] [[--] param-file]", + "swift [options] param-file", + "swift_mpi [options] [[--] param-file]", + "swift_mpi [options] param-file", + NULL, +}; + +/* Function to handle multiple -P arguments. */ +struct cmdparams { + const char *param[PARSER_MAX_NO_OF_PARAMS]; + int nparam; +}; + +static int handle_cmdparam(struct argparse *self, + const struct argparse_option *opt) { + struct cmdparams *cmdps = (struct cmdparams *)opt->data; + cmdps->param[cmdps->nparam] = *(char **)opt->value; + cmdps->nparam++; + return 1; } /** @@ -132,14 +90,15 @@ int main(int argc, char *argv[]) { struct cooling_function_data cooling_func; struct cosmology cosmo; struct external_potential potential; + struct star_formation starform; struct pm_mesh mesh; struct gpart *gparts = NULL; struct gravity_props gravity_properties; struct hydro_props hydro_properties; struct stars_props stars_properties; + struct entropy_floor_properties entropy_floor; struct part *parts = NULL; struct phys_const prog_const; - struct sourceterms sourceterms; struct space s; struct spart *sparts = NULL; struct unit_system us; @@ -189,13 +148,15 @@ int main(int argc, char *argv[]) { int restart = 0; int with_cosmology = 0; int with_external_gravity = 0; - int with_sourceterms = 0; + int with_temperature = 0; int with_cooling = 0; int with_self_gravity = 0; int with_hydro = 0; int with_stars = 0; int with_fof = 0; + int with_star_formation = 0; int with_feedback = 0; + int with_limiter = 0; int with_fp_exceptions = 0; int with_drift_all = 0; int with_mpole_reconstruction = 0; @@ -203,194 +164,185 @@ int main(int argc, char *argv[]) { int verbose = 0; int nr_threads = 1; int with_verbose_timers = 0; - int nparams = 0; - char output_parameters_filename[200] = ""; - char *cmdparams[PARSER_MAX_NO_OF_PARAMS]; - char paramFileName[200] = ""; + char *output_parameters_filename = NULL; + char *cpufreqarg = NULL; + char *param_filename = NULL; char restart_file[200] = ""; unsigned long long cpufreq = 0; + struct cmdparams cmdps; + cmdps.nparam = 0; + cmdps.param[0] = NULL; + char *buffer = NULL; + + /* Parse the command-line parameters. */ + struct argparse_option options[] = { + OPT_HELP(), + + OPT_GROUP(" Simulation options:\n"), + OPT_BOOLEAN('b', "feedback", &with_feedback, "Run with stars feedback.", + NULL, 0, 0), + OPT_BOOLEAN('c', "cosmology", &with_cosmology, + "Run with cosmological time integration.", NULL, 0, 0), + OPT_BOOLEAN(0, "temperature", &with_temperature, + "Run with temperature calculation.", NULL, 0, 0), + OPT_BOOLEAN('C', "cooling", &with_cooling, + "Run with cooling (also switches on --with-temperature).", + NULL, 0, 0), + OPT_BOOLEAN('D', "drift-all", &with_drift_all, + "Always drift all particles even the ones far from active " + "particles. This emulates Gadget-[23] and GIZMO's default " + "behaviours.", + NULL, 0, 0), + OPT_BOOLEAN('F', "star-formation", &with_star_formation, + "Run with star formation.", NULL, 0, 0), + OPT_BOOLEAN('g', "external-gravity", &with_external_gravity, + "Run with an external gravitational potential.", NULL, 0, 0), + OPT_BOOLEAN('G', "self-gravity", &with_self_gravity, + "Run with self-gravity.", NULL, 0, 0), + OPT_BOOLEAN('M', "multipole-reconstruction", &with_mpole_reconstruction, + "Reconstruct the multipoles every time-step.", NULL, 0, 0), + OPT_BOOLEAN('s', "hydro", &with_hydro, "Run with hydrodynamics.", NULL, 0, + 0), + OPT_BOOLEAN('S', "stars", &with_stars, "Run with stars.", NULL, 0, 0), + OPT_BOOLEAN('x', "velociraptor", &with_structure_finding, + "Run with structure finding.", NULL, 0, 0), + OPT_BOOLEAN(0, "limiter", &with_limiter, "Run with time-step limiter.", + NULL, 0, 0), + + OPT_GROUP(" Control options:\n"), + OPT_BOOLEAN('a', "pin", &with_aff, + "Pin runners using processor affinity.", NULL, 0, 0), + OPT_BOOLEAN('d', "dry-run", &dry_run, + "Dry run. Read the parameter file, allocates memory but does " + "not read the particles from ICs. Exits before the start of " + "time integration. Checks the validity of parameters and IC " + "files as well as memory limits.", + NULL, 0, 0), + OPT_BOOLEAN('e', "fpe", &with_fp_exceptions, + "Enable floating-point exceptions (debugging mode).", NULL, 0, + 0), + OPT_STRING('f', "cpu-frequency", &cpufreqarg, + "Overwrite the CPU " + "frequency (Hz) to be used for time measurements.", + NULL, 0, 0), + OPT_INTEGER('n', "steps", &nsteps, + "Execute a fixed number of time steps. When unset use the " + "time_end parameter to stop.", + NULL, 0, 0), + OPT_STRING('o', "output-params", &output_parameters_filename, + "Generate a default output parameter file.", NULL, 0, 0), + OPT_STRING('P', "param", &buffer, + "Set parameter value, overiding the value read from the " + "parameter file. Can be used more than once {sec:par:value}.", + handle_cmdparam, (intptr_t)&cmdps, 0), + OPT_BOOLEAN('r', "restart", &restart, "Continue using restart files.", + NULL, 0, 0), + OPT_INTEGER('t', "threads", &nr_threads, + "The number of threads to use on each MPI rank. Defaults to " + "1 if not specified.", + NULL, 0, 0), + OPT_INTEGER('T', "timers", &with_verbose_timers, + "Print timers every time-step.", NULL, 0, 0), + OPT_INTEGER('u', "fof", &with_fof, + "Run Friends-of-Friends algorithm.", NULL, 0, 0), + OPT_INTEGER('v', "verbose", &verbose, + "Run in verbose mode, in MPI mode 2 outputs from all ranks.", + NULL, 0, 0), + OPT_INTEGER('y', "task-dumps", &dump_tasks, + "Time-step frequency at which task graphs are dumped.", NULL, + 0, 0), + OPT_INTEGER('Y', "threadpool-dumps", &dump_threadpool, + "Time-step frequency at which threadpool tasks are dumped.", + NULL, 0, 0), + OPT_END(), + }; + struct argparse argparse; + argparse_init(&argparse, options, swift_usage, 0); + argparse_describe(&argparse, "\nParameters:", + "\nSee the file examples/parameter_example.yml for an " + "example of parameter file."); + int nargs = argparse_parse(&argparse, argc, (const char **)argv); + + /* Need a parameter file. */ + if (nargs != 1) { + if (myrank == 0) argparse_usage(&argparse); + printf("\nError: no parameter file was supplied.\n"); + return 1; + } + param_filename = argv[0]; - /* Parse the parameters */ - int c; - while ((c = getopt(argc, argv, "abcCdDef:FgGhMn:o:P:rsSt:Tuv:xy:Y:")) != -1) - switch (c) { - case 'a': -#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) - with_aff = 1; -#else - error("Need NUMA support for thread affinity"); + /* Checks of options. */ +#if !defined(HAVE_SETAFFINITY) || !defined(HAVE_LIBNUMA) + if (with_aff) { + printf("Error: no NUMA support for thread affinity\n"); + return 1; + } #endif - break; - case 'b': - with_feedback = 1; - break; - case 'c': - with_cosmology = 1; - break; - case 'C': - with_cooling = 1; - break; - case 'd': - dry_run = 1; - break; - case 'D': - with_drift_all = 1; - break; - case 'e': -#ifdef HAVE_FE_ENABLE_EXCEPT - with_fp_exceptions = 1; -#else - error("Need support for floating point exception on this platform"); + +#ifndef HAVE_FE_ENABLE_EXCEPT + if (with_fp_exceptions) { + printf("Error: no support for floating point exceptions\n"); + return 1; + } #endif - break; - case 'f': - if (sscanf(optarg, "%llu", &cpufreq) != 1) { - if (myrank == 0) printf("Error parsing CPU frequency (-f).\n"); - if (myrank == 0) print_help_message(); - return 1; - } - break; - case 'F': - with_sourceterms = 1; - break; - case 'g': - with_external_gravity = 1; - break; - case 'G': - with_self_gravity = 1; - break; - case 'h': - if (myrank == 0) print_help_message(); - return 0; - case 'M': - with_mpole_reconstruction = 1; - break; - case 'n': - if (sscanf(optarg, "%d", &nsteps) != 1) { - if (myrank == 0) printf("Error parsing fixed number of steps.\n"); - if (myrank == 0) print_help_message(); - return 1; - } - break; - case 'o': - if (sscanf(optarg, "%s", output_parameters_filename) != 1) { - if (myrank == 0) { - printf("Error parsing output fields filename"); - print_help_message(); - } - return 1; - } - break; - case 'P': - cmdparams[nparams] = optarg; - nparams++; - break; - case 'r': - restart = 1; - break; - case 's': - with_hydro = 1; - break; - case 'S': - with_stars = 1; - break; - case 't': - if (sscanf(optarg, "%d", &nr_threads) != 1) { - if (myrank == 0) - printf("Error parsing the number of threads (-t).\n"); - if (myrank == 0) print_help_message(); - return 1; - } - break; - case 'T': - with_verbose_timers = 1; - break; - case 'u': - with_fof = 1; - break; - case 'v': - if (sscanf(optarg, "%d", &verbose) != 1) { - if (myrank == 0) printf("Error parsing verbosity level (-v).\n"); - if (myrank == 0) print_help_message(); - return 1; - } - break; - case 'x': -#ifdef HAVE_VELOCIRAPTOR - with_structure_finding = 1; -#else - error( - "Error: (-x) needs to have the code compiled with VELOCIraptor " - "linked in."); + +#ifndef HAVE_VELOCIRAPTOR + if (with_structure_finding) { + printf("Error: VELOCIraptor is not available\n"); + return 1; + } #endif - break; - case 'y': - if (sscanf(optarg, "%d", &dump_tasks) != 1) { - if (myrank == 0) printf("Error parsing dump_tasks (-y). \n"); - if (myrank == 0) print_help_message(); - return 1; - } + #ifndef SWIFT_DEBUG_TASKS - if (dump_tasks) { - error( - "Task dumping is only possible if SWIFT was configured with the " - "--enable-task-debugging option."); - } + if (dump_tasks) { + if (myrank == 0) { + message( + "WARNING: complete task dumps are only created when " + "configured with --enable-task-debugging."); + message(" Basic task statistics will be output."); + } + } #endif - break; - case 'Y': - if (sscanf(optarg, "%d", &dump_threadpool) != 1) { - if (myrank == 0) printf("Error parsing dump_threadpool (-Y). \n"); - if (myrank == 0) print_help_message(); - return 1; - } + #ifndef SWIFT_DEBUG_THREADPOOL - if (dump_threadpool) { - error( - "Threadpool dumping is only possible if SWIFT was configured " - "with the " - "--enable-threadpool-debugging option."); - } + if (dump_threadpool) { + printf( + "Error: threadpool dumping is only possible if SWIFT was " + "configured with the --enable-threadpool-debugging option.\n"); + return 1; + } #endif - break; - case '?': - if (myrank == 0) print_help_message(); - return 1; - break; + + /* The CPU frequency is a long long, so we need to parse that ourselves. */ + if (cpufreqarg != NULL) { + if (sscanf(cpufreqarg, "%llu", &cpufreq) != 1) { + if (myrank == 0) + printf("Error parsing CPU frequency (%s).\n", cpufreqarg); + return 1; } + } /* Write output parameter file */ - if (myrank == 0 && strcmp(output_parameters_filename, "") != 0) { + if (myrank == 0 && output_parameters_filename != NULL) { io_write_output_field_parameter(output_parameters_filename); printf("End of run.\n"); return 0; } - /* check inputs */ - if (optind == argc - 1) { - if (!strcpy(paramFileName, argv[optind++])) - error("Error reading parameter file name."); - } else if (optind > argc - 1) { - if (myrank == 0) printf("Error: A parameter file name must be provided\n"); - if (myrank == 0) print_help_message(); - return 1; - } else { - if (myrank == 0) printf("Error: Too many parameters given\n"); - if (myrank == 0) print_help_message(); - return 1; - } if (!with_self_gravity && !with_hydro && !with_external_gravity) { - if (myrank == 0) - printf("Error: At least one of -s, -g or -G must be chosen.\n"); - if (myrank == 0) print_help_message(); + if (myrank == 0) { + argparse_usage(&argparse); + printf("\nError: At least one of -s, -g or -G must be chosen.\n"); + } return 1; } if (with_stars && !with_external_gravity && !with_self_gravity) { - if (myrank == 0) + if (myrank == 0) { + argparse_usage(&argparse); printf( - "Error: Cannot process stars without gravity, -g or -G must be " - "chosen.\n"); - if (myrank == 0) print_help_message(); + "\nError: Cannot process stars without gravity, -g or -G " + "must be chosen.\n"); + } return 1; } if (with_fof && !with_external_gravity && !with_self_gravity) { @@ -398,16 +350,16 @@ int main(int argc, char *argv[]) { printf( "Error: Cannot perform FOF search without gravity, -g or -G must be " "chosen.\n"); - if (myrank == 0) print_help_message(); return 1; } if (!with_stars && with_feedback) { - if (myrank == 0) + if (myrank == 0) { + argparse_usage(&argparse); printf( - "Error: Cannot process feedback without stars, -S must be " + "\nError: Cannot process feedback without stars, -S must be " "chosen.\n"); - if (myrank == 0) print_help_message(); + } return 1; } @@ -497,15 +449,16 @@ int main(int argc, char *argv[]) { (struct swift_params *)malloc(sizeof(struct swift_params)); if (params == NULL) error("Error allocating memory for the parameter file."); if (myrank == 0) { - message("Reading runtime parameters from file '%s'", paramFileName); - parser_read_file(paramFileName, params); + message("Reading runtime parameters from file '%s'", param_filename); + parser_read_file(param_filename, params); /* Handle any command-line overrides. */ - if (nparams > 0) { + if (cmdps.nparam > 0) { message( "Overwriting values read from the YAML file with command-line " "values."); - for (int k = 0; k < nparams; k++) parser_set_param(params, cmdparams[k]); + for (int k = 0; k < cmdps.nparam; k++) + parser_set_param(params, cmdps.param[k]); } } #ifdef WITH_MPI @@ -517,15 +470,17 @@ int main(int argc, char *argv[]) { #ifdef WITH_MPI if (with_mpole_reconstruction && nr_nodes > 1) error("Cannot reconstruct m-poles every step over MPI (yet)."); -#endif - -#ifdef WITH_MPI if (with_feedback) error("Can't run with feedback over MPI (yet)."); + if (with_star_formation) + error("Can't run with star formation over MPI (yet)"); + if (with_limiter) error("Can't run with time-step limiter over MPI (yet)"); #endif -#if defined(WITH_MPI) && defined(HAVE_VELOCIRAPTOR) - if (with_structure_finding && nr_nodes > 1) - error("VEOCIraptor not yet enabled over MPI."); + /* Temporary early aborts for modes not supported with hand-vec. */ +#if defined(WITH_VECTORIZATION) && !defined(CHEMISTRY_NONE) + error( + "Cannot run with chemistry and hand-vectorization (yet). " + "Use --disable-hand-vec at configure time."); #endif /* Check that we can write the snapshots by testing if the output @@ -538,8 +493,7 @@ int main(int argc, char *argv[]) { } /* Check that we can write the structure finding catalogues by testing if the - * output - * directory exists and is searchable and writable. */ + * output directory exists and is searchable and writable. */ if (with_structure_finding) { char stfbasename[PARSER_MAX_LINE_SIZE]; parser_get_param_string(params, "StructureFinding:basename", stfbasename); @@ -563,8 +517,10 @@ int main(int argc, char *argv[]) { message("Using METIS serial partitioning:"); else message("Using ParMETIS partitioning:"); -#else +#elif defined(HAVE_METIS) message("Using METIS serial partitioning:"); +#else + message("Non-METIS partitioning:"); #endif message(" initial partitioning: %s", initial_partition_name[initial_partition.type]); @@ -748,6 +704,13 @@ int main(int argc, char *argv[]) { else bzero(&eos, sizeof(struct eos_parameters)); + /* Initialise the entropy floor */ + if (with_hydro) + entropy_floor_init(&entropy_floor, &prog_const, &us, &hydro_properties, + params); + else + bzero(&entropy_floor, sizeof(struct entropy_floor_properties)); + /* Initialise the stars properties */ if (with_stars) stars_props_init(&stars_properties, &prog_const, &us, params, @@ -847,7 +810,7 @@ int main(int argc, char *argv[]) { if (myrank == 0) clocks_gettime(&tic); space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart, periodic, replicate, generate_gas_in_ics, with_hydro, - with_self_gravity, talking, dry_run); + with_self_gravity, with_star_formation, talking, dry_run); if (myrank == 0) { clocks_gettime(&toc); @@ -901,6 +864,18 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Verify that we are not using basic modes incorrectly */ + if (with_hydro && N_total[0] == 0) { + error( + "ERROR: Running with hydrodynamics but no gas particles found in the " + "ICs!"); + } + if ((with_self_gravity || with_external_gravity) && N_total[1] == 0) { + error( + "ERROR: Running with gravity but no gravity particles found in " + "the ICs!"); + } + /* Verify that each particle is in it's proper cell. */ if (talking && !dry_run) { int icount = 0; @@ -916,22 +891,28 @@ int main(int argc, char *argv[]) { } /* Initialise the external potential properties */ + bzero(&potential, sizeof(struct external_potential)); if (with_external_gravity) potential_init(params, &prog_const, &us, &s, &potential); if (myrank == 0) potential_print(&potential); /* Initialise the cooling function properties */ - if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); + bzero(&cooling_func, sizeof(struct cooling_function_data)); + if (with_cooling || with_temperature) + cooling_init(params, &us, &prog_const, &cooling_func); if (myrank == 0) cooling_print(&cooling_func); + /* Initialise the star formation law and its properties */ + if (with_star_formation) + starformation_init(params, &prog_const, &us, &hydro_properties, + &starform); + if (myrank == 0) starformation_print(&starform); + /* Initialise the chemistry */ + bzero(&chemistry, sizeof(struct chemistry_global_data)); chemistry_init(params, &us, &prog_const, &chemistry); if (myrank == 0) chemistry_print(&chemistry); - /* Initialise the feedback properties */ - if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); - if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); - /* Construct the engine policy */ int engine_policies = ENGINE_POLICY | engine_policy_steal; if (with_drift_all) engine_policies |= engine_policy_drift_all; @@ -942,23 +923,23 @@ int main(int argc, char *argv[]) { if (with_external_gravity) engine_policies |= engine_policy_external_gravity; if (with_cosmology) engine_policies |= engine_policy_cosmology; + if (with_temperature) engine_policies |= engine_policy_temperature; + if (with_limiter) engine_policies |= engine_policy_limiter; if (with_cooling) engine_policies |= engine_policy_cooling; - if (with_sourceterms) engine_policies |= engine_policy_sourceterms; if (with_stars) engine_policies |= engine_policy_stars; + if (with_star_formation) engine_policies |= engine_policy_star_formation; if (with_feedback) engine_policies |= engine_policy_feedback; if (with_structure_finding) engine_policies |= engine_policy_structure_finding; if (with_fof) engine_policies |= engine_policy_fof; - // MATTHIEU: Temporary star formation law - // engine_policies |= engine_policy_star_formation; - /* Initialize the engine with the space and policies. */ if (myrank == 0) clocks_gettime(&tic); engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, - &hydro_properties, &gravity_properties, &stars_properties, - &mesh, &potential, &cooling_func, &chemistry, &sourceterms); + &hydro_properties, &entropy_floor, &gravity_properties, + &stars_properties, &mesh, &potential, &cooling_func, &starform, + &chemistry); engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, restart_file); @@ -1037,14 +1018,6 @@ int main(int argc, char *argv[]) { /* Is there a dump before the end of the first time-step? */ engine_check_for_dumps(&e); -#ifdef HAVE_VELOCIRAPTOR - /* Call VELOCIraptor for the first time after the first snapshot dump. */ - // if (e.policy & engine_policy_structure_finding) { - // velociraptor_init(&e); - // velociraptor_invoke(&e); - //} -#endif - /* Perform first FOF search after the first snapshot dump. */ if (e.policy & engine_policy_fof) fof_search_tree(&s); } @@ -1108,99 +1081,17 @@ int main(int argc, char *argv[]) { if (force_stop || (e.restart_onexit && e.step - 1 == nsteps)) engine_dump_restarts(&e, 0, 1); -#ifdef SWIFT_DEBUG_TASKS /* Dump the task data using the given frequency. */ if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) { -#ifdef WITH_MPI - - /* Make sure output file is empty, only on one rank. */ - char dumpfile[35]; - snprintf(dumpfile, sizeof(dumpfile), "thread_info_MPI-step%d.dat", j + 1); - FILE *file_thread; - if (myrank == 0) { - file_thread = fopen(dumpfile, "w"); - fclose(file_thread); - } - MPI_Barrier(MPI_COMM_WORLD); - - for (int i = 0; i < nr_nodes; i++) { - - /* Rank 0 decides the index of writing node, this happens one-by-one. */ - int kk = i; - MPI_Bcast(&kk, 1, MPI_INT, 0, MPI_COMM_WORLD); - - if (i == myrank) { - - /* Open file and position at end. */ - file_thread = fopen(dumpfile, "a"); - - fprintf(file_thread, - " %03d 0 0 0 0 %lld %lld %lld %lld %lld 0 0 %lld\n", myrank, - e.tic_step, e.toc_step, e.updates, e.g_updates, e.s_updates, - cpufreq); - int count = 0; - for (int l = 0; l < e.sched.nr_tasks; l++) { - if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) { - fprintf(file_thread, - " %03i %i %i %i %i %lli %lli %i %i %i %i %lli %i\n", - myrank, e.sched.tasks[l].rid, e.sched.tasks[l].type, - e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL), - e.sched.tasks[l].tic, e.sched.tasks[l].toc, - (e.sched.tasks[l].ci != NULL) - ? e.sched.tasks[l].ci->hydro.count - : 0, - (e.sched.tasks[l].cj != NULL) - ? e.sched.tasks[l].cj->hydro.count - : 0, - (e.sched.tasks[l].ci != NULL) - ? e.sched.tasks[l].ci->grav.count - : 0, - (e.sched.tasks[l].cj != NULL) - ? e.sched.tasks[l].cj->grav.count - : 0, - e.sched.tasks[l].flags, e.sched.tasks[l].sid); - } - fflush(stdout); - count++; - } - fclose(file_thread); - } - - /* And we wait for all to synchronize. */ - MPI_Barrier(MPI_COMM_WORLD); - } +#ifdef SWIFT_DEBUG_TASKS + task_dump_all(&e, j + 1); +#endif -#else - char dumpfile[32]; - snprintf(dumpfile, sizeof(dumpfile), "thread_info-step%d.dat", j + 1); - FILE *file_thread; - file_thread = fopen(dumpfile, "w"); - /* Add some information to help with the plots */ - fprintf(file_thread, " %d %d %d %d %lld %lld %lld %lld %lld %d %lld\n", - -2, -1, -1, 1, e.tic_step, e.toc_step, e.updates, e.g_updates, - e.s_updates, 0, cpufreq); - for (int l = 0; l < e.sched.nr_tasks; l++) { - if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) { - fprintf( - file_thread, " %i %i %i %i %lli %lli %i %i %i %i %i\n", - e.sched.tasks[l].rid, e.sched.tasks[l].type, - e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL), - e.sched.tasks[l].tic, e.sched.tasks[l].toc, - (e.sched.tasks[l].ci == NULL) ? 0 - : e.sched.tasks[l].ci->hydro.count, - (e.sched.tasks[l].cj == NULL) ? 0 - : e.sched.tasks[l].cj->hydro.count, - (e.sched.tasks[l].ci == NULL) ? 0 - : e.sched.tasks[l].ci->grav.count, - (e.sched.tasks[l].cj == NULL) ? 0 - : e.sched.tasks[l].cj->grav.count, - e.sched.tasks[l].sid); - } - } - fclose(file_thread); -#endif // WITH_MPI + /* Generate the task statistics. */ + char dumpfile[40]; + snprintf(dumpfile, 40, "thread_stats-step%d.dat", j + 1); + task_dump_stats(dumpfile, &e, /* header = */ 0, /* allranks = */ 1); } -#endif // SWIFT_DEBUG_TASKS #ifdef SWIFT_DEBUG_THREADPOOL /* Dump the task data using the given frequency. */ @@ -1253,21 +1144,24 @@ int main(int argc, char *argv[]) { /* Write final output. */ if (!force_stop) { - engine_drift_all(&e); + engine_drift_all(&e, /*drift_mpole=*/0); engine_print_stats(&e); #ifdef WITH_LOGGER logger_log_all(e.logger, &e); engine_dump_index(&e); #endif - // write a final snapshot with logger, in order to facilitate a restart + +#ifdef HAVE_VELOCIRAPTOR + if (with_structure_finding && e.snapshot_invoke_stf) + velociraptor_invoke(&e, /*linked_with_snap=*/1); +#endif + + /* write a final snapshot */ engine_dump_snapshot(&e); #ifdef HAVE_VELOCIRAPTOR - /* Call VELOCIraptor at the end of the run to find groups. */ - if (e.policy & engine_policy_structure_finding) { - velociraptor_init(&e); - velociraptor_invoke(&e); - } + if (with_structure_finding && e.snapshot_invoke_stf) + free(e.s->gpart_group_data); #endif } @@ -1293,6 +1187,7 @@ int main(int argc, char *argv[]) { if (with_verbose_timers) timers_close_file(); if (with_cosmology) cosmology_clean(e.cosmology); if (with_self_gravity) pm_mesh_clean(e.mesh); + if (with_cooling || with_temperature) cooling_clean(&cooling_func); engine_clean(&e); free(params); diff --git a/examples/main_fof.c b/examples/main_fof.c index 56037726425555659d042b317ad1776033fad94c..fe591b760253cf92ac58fc107f6f7d317f5d19e6 100644 --- a/examples/main_fof.c +++ b/examples/main_fof.c @@ -132,14 +132,15 @@ int main(int argc, char *argv[]) { struct cooling_function_data cooling_func; struct cosmology cosmo; struct external_potential potential; + struct star_formation starform; struct pm_mesh mesh; struct gpart *gparts = NULL; struct gravity_props gravity_properties; struct hydro_props hydro_properties; struct stars_props stars_properties; + struct entropy_floor_properties entropy_floor; struct part *parts = NULL; struct phys_const prog_const; - struct sourceterms sourceterms; struct space s; struct spart *sparts = NULL; struct unit_system us; @@ -195,6 +196,7 @@ int main(int argc, char *argv[]) { int with_hydro = 0; int with_stars = 0; int with_fof = 1; + int with_star_formation = 0; int with_feedback = 0; int with_fp_exceptions = 0; int with_drift_all = 0; @@ -729,6 +731,13 @@ int main(int argc, char *argv[]) { else bzero(&eos, sizeof(struct eos_parameters)); + /* Initialise the entropy floor */ + if (with_hydro) + entropy_floor_init(&entropy_floor, &prog_const, &us, &hydro_properties, + params); + else + bzero(&entropy_floor, sizeof(struct entropy_floor_properties)); + /* Initialise the stars properties */ if (with_stars) stars_props_init(&stars_properties, &prog_const, &us, params, @@ -828,7 +837,7 @@ int main(int argc, char *argv[]) { if (myrank == 0) clocks_gettime(&tic); space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart, periodic, replicate, generate_gas_in_ics, with_hydro, - with_self_gravity, talking, dry_run); + with_self_gravity, with_star_formation, talking, dry_run); if (myrank == 0) { clocks_gettime(&toc); @@ -905,14 +914,16 @@ int main(int argc, char *argv[]) { if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); if (myrank == 0) cooling_print(&cooling_func); + /* Initialise the star formation law and its properties */ + if (with_star_formation) + starformation_init(params, &prog_const, &us, &hydro_properties, + &starform); + if (myrank == 0) starformation_print(&starform); + /* Initialise the chemistry */ chemistry_init(params, &us, &prog_const, &chemistry); if (myrank == 0) chemistry_print(&chemistry); - /* Initialise the feedback properties */ - if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); - if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); - /* Construct the engine policy */ int engine_policies = ENGINE_POLICY | engine_policy_steal; if (with_drift_all) engine_policies |= engine_policy_drift_all; @@ -924,7 +935,6 @@ int main(int argc, char *argv[]) { engine_policies |= engine_policy_external_gravity; if (with_cosmology) engine_policies |= engine_policy_cosmology; if (with_cooling) engine_policies |= engine_policy_cooling; - if (with_sourceterms) engine_policies |= engine_policy_sourceterms; if (with_stars) engine_policies |= engine_policy_stars; if (with_fof) engine_policies |= engine_policy_fof; if (with_feedback) engine_policies |= engine_policy_feedback; @@ -936,8 +946,9 @@ int main(int argc, char *argv[]) { if (myrank == 0) clocks_gettime(&tic); engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, - &hydro_properties, &gravity_properties, &stars_properties, - &mesh, &potential, &cooling_func, &chemistry, &sourceterms); + &hydro_properties, &entropy_floor, &gravity_properties, + &stars_properties, &mesh, &potential, &cooling_func, &starform, + &chemistry); engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, restart_file); @@ -1066,7 +1077,7 @@ int main(int argc, char *argv[]) { parser_write_params_to_file(params, "unused_parameters.yml", 0); /* Write final output. */ - engine_drift_all(&e); + engine_drift_all(&e, /*drift_mpole=*/0); engine_print_stats(&e); engine_dump_snapshot(&e); diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 9ae5fef66546aca2822a2887f9ca2104a169b2d0..43d6f4818f88d2451e639c60407dbb8a36f5a80a 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -28,6 +28,7 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. h_max: 10. # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified. + h_min_ratio: 0. # (Optional) Minimal allowed smoothing length in units of the softening. Defaults to 0 if unspecified. max_volume_change: 1.4 # (Optional) Maximal allowed change of kernel volume over one time-step. max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. initial_temperature: 0 # (Optional) Initial temperature (in internal units) to set the gas particles at start-up. Value is ignored if set to 0. @@ -35,9 +36,14 @@ SPH: H_mass_fraction: 0.755 # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy. Default value is derived from the physical constants. H_ionization_temperature: 1e4 # (Optional) Temperature of the transition from neutral to ionized Hydrogen for primoridal gas. viscosity_alpha: 0.8 # (Optional) Override for the initial value of the artificial viscosity. In schemes that have a fixed AV, this remains as alpha throughout the run. - viscosity_alpha_max: 2.0 # (Optional) Maximal value for the artificial viscosity in schemes that allow alpha to vary - viscosity_alpha_min: 0.1 # (Optional) Minimal value for the artificial viscosity in schemes that allow alpha to vary - viscosity_length: 0.1 # (Optional) Decay length for the artificial viscosity in schemes that allow alpha to vary + viscosity_alpha_max: 2.0 # (Optional) Maximal value for the artificial viscosity in schemes that allow alpha to vary. + viscosity_alpha_min: 0.1 # (Optional) Minimal value for the artificial viscosity in schemes that allow alpha to vary. + viscosity_length: 0.1 # (Optional) Decay length for the artificial viscosity in schemes that allow alpha to vary. + diffusion_alpha: 0.0 # (Optional) Override the initial value for the thermal diffusion coefficient in schemes with thermal diffusion. + diffusion_beta: 0.01 # (Optional) Override the decay/rise rate tuning parameter for the thermal diffusion. + diffusion_alpha_max: 1.0 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. + diffusion_alpha_min: 0.0 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. + # Parameters for the self-gravity scheme Gravity: @@ -63,8 +69,12 @@ Scheduler: cell_sub_size_self_stars: 32000 # (Optional) Maximal number of interactions per sub-self stars task (this is the default value). cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). cell_subdepth_diff_grav: 4 # (Optional) Maximal depth difference between leaves and a cell that gravity tasks can be pushed down to (this is the default value). + cell_extra_parts: 0 # (Optional) Number of spare parts per top-level allocated at rebuild time for on-the-fly creation. + cell_extra_gparts: 0 # (Optional) Number of spare gparts per top-level allocated at rebuild time for on-the-fly creation. + cell_extra_sparts: 400 # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation. max_top_level_cells: 12 # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value). - tasks_per_cell: 0 # (Optional) The average number of tasks per cell. If not large enough the simulation will fail (means guess...). + tasks_per_cell: 0.0 # (Optional) The average number of tasks per cell. If not large enough the simulation will fail (means guess...). + links_per_tasks: 10 # (Optional) The average number of links per tasks (before adding the communication tasks). If not large enough the simulation will fail (means guess...). Defaults to 10. mpi_message_limit: 4096 # (Optional) Maximum MPI task message size to send non-buffered, KB. # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) @@ -81,6 +91,7 @@ Snapshots: scale_factor_first: 0.1 # (Optional) Scale-factor of the first snapshot if cosmological time-integration. time_first: 0. # (Optional) Time of the first output if non-cosmological time-integration (in internal units) delta_time: 0.01 # Time difference between consecutive outputs (in internal units) + invoke_stf: 0 # (Optional) Call VELOCIraptor every time a snapshot is written irrespective of the VELOCIraptor output strategy. compression: 0 # (Optional) Set the level of compression of the HDF5 datasets [0-9]. 0 does no compression. int_time_label_on: 0 # (Optional) Enable to label the snapshots using the time rounded to an integer (in internal units) UnitMass_in_cgs: 1 # (Optional) Unit system for the outputs (Grams) @@ -135,23 +146,34 @@ Restarts: # Parameters governing domain decomposition DomainDecomposition: - initial_type: memory # (Optional) The initial decomposition strategy: "grid", - # "region", "memory", or "vectorized". - initial_grid: [10,10,10] # (Optional) Grid sizes if the "grid" strategy is chosen. + initial_type: memory # (Optional) The initial decomposition strategy: "grid", + # "region", "memory", or "vectorized". + initial_grid: [10,10,10] # (Optional) Grid sizes if the "grid" strategy is chosen. - repartition_type: costs/costs # (Optional) The re-decomposition strategy, one of: - # "none/none", "costs/costs", "none/costs", "costs/none" or "costs/time". - # These are vertex/edge weights with "costs" as task timing - # and "time" as the expected time of the next updates + repartition_type: fullcosts # (Optional) The re-decomposition strategy, one of: + # "none", "fullcosts", "edgecosts", "memory" or + # "timecosts". + trigger: 0.05 # (Optional) Fractional (<1) CPU time difference between MPI ranks required to trigger a + # new decomposition, or number of steps (>1) between decompositions + minfrac: 0.9 # (Optional) Fractional of all particles that should be updated in previous step when + # using CPU time trigger + usemetis: 0 # Use serial METIS when ParMETIS is also available. + adaptive: 1 # Use adaptive repartition when ParMETIS is available, otherwise simple refinement. + itr: 100 # When adaptive defines the ratio of inter node communication time to data redistribution time, in the range 0.00001 to 10000000.0. + # Lower values give less data movement during redistributions, at the cost of global balance which may require more communication. + use_fixed_costs: 0 # If 1 then use any compiled in fixed costs for + # task weights in first repartition, if 0 only use task timings, if > 1 only use + # fixed costs, unless none are available. - trigger: 0.05 # (Optional) Fractional (<1) CPU time difference between MPI ranks required to trigger a - # new decomposition, or number of steps (>1) between decompositions - minfrac: 0.9 # (Optional) Fractional of all particles that should be updated in previous step when - # using CPU time trigger - usemetis: 0 # Use serial METIS when ParMETIS is also available. - adaptive: 1 # Use adaptive repartition when ParMETIS is available, otherwise simple refinement. - itr: 100 # When adaptive defines the ratio of inter node communication time to data redistribution time, in the range 0.00001 to 10000000.0. - # Lower values give less data movement during redistributions, at the cost of global balance which may require more communication. +# Structure finding options (requires velociraptor) +StructureFinding: + config_file_name: stf_input.cfg # Name of the STF config file. + basename: ./stf # Common part of the name of output files. + scale_factor_first: 0.92 # (Optional) Scale-factor of the first snaphot (cosmological run) + time_first: 0.01 # (Optional) Time of the first structure finding output (in internal units). + delta_time: 1.10 # (Optional) Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals. + output_list_on: 0 # (Optional) Enable the output list + output_list: stflist.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) # Parameters related to the equation of state ------------------------------------------ @@ -163,13 +185,13 @@ EoS: planetary_use_ANEOS: 0 # (Optional) Whether to prepare the ANEOS EOS planetary_use_SESAME: 0 # (Optional) Whether to prepare the SESAME EOS # (Optional) Table file paths - planetary_HM80_HHe_table_file: ./equation_of_state/planetary_HM80_HHe.txt - planetary_HM80_ice_table_file: ./equation_of_state/planetary_HM80_ice.txt - planetary_HM80_rock_table_file: ./equation_of_state/planetary_HM80_rock.txt - planetary_SESAME_iron_table_file: ./equation_of_state/planetary_SESAME_iron_2140.txt - planetary_SESAME_basalt_table_file: ./equation_of_state/planetary_SESAME_basalt_7530.txt - planetary_SESAME_water_table_file: ./equation_of_state/planetary_SESAME_water_7154.txt - planetary_SS08_water_table_file: ./equation_of_state/planetary_SS08_water.txt + planetary_HM80_HHe_table_file: ./EoSTables/planetary_HM80_HHe.txt + planetary_HM80_ice_table_file: ./EoSTables/planetary_HM80_ice.txt + planetary_HM80_rock_table_file: ./EoSTables/planetary_HM80_rock.txt + planetary_SESAME_iron_table_file: ./EoSTables/planetary_SESAME_iron_2140.txt + planetary_SESAME_basalt_table_file: ./EoSTables/planetary_SESAME_basalt_7530.txt + planetary_SESAME_water_table_file: ./EoSTables/planetary_SESAME_water_7154.txt + planetary_SS08_water_table_file: ./EoSTables/planetary_SS08_water.txt # Parameters related to external potentials -------------------------------------------- @@ -193,8 +215,17 @@ IsothermalPotential: HernquistPotential: useabspos: 0 # 0 -> positions based on centre, 1 -> absolute positions position: [100.,100.,100.] # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units) - mass: 1e10 # Mass of the Hernquist potential - scalelength: 10.0 # Scale length of the potential + idealizeddisk: 0 # (Optional) Whether to run with idealizeddisk or without, 0 used the mass and scalelength as mandatory parameters, while 1 uses more advanced disk dependent paramters + mass: 1e10 # (Optional 0) default parameter, Mass of the Hernquist potential + scalelength: 10.0 # (Optional 0) default parameter, Scale length of the potential + # If multiple X200 values are given, only one is used, in the order M200 > V200 > R200. + M200: 3e11 # (Optional 1a) M200 of the galaxy+halo (when used V200 and R200 are not used) + V200: 100. # (Optional 1b) V200 of the galaxy+halo (when used M200 and R200 are not used, if M200 is given M200 is used) + R200: 10. # (Optional 1c) R200 of the galaxy+halo (when used M200 and V200 are not used, if M200 or V200 are given they are used) + h: 0.704 # (Optional 1) reduced Hubble constant + concentration: 7.1 # (Optional 1) concentration of the Halo + diskfraction: 0.0434370991372 # (Optional 1) Disk mass fraction (equal to MD in MakeNewDisk and GalIC) + bulgefraction: 0.00705852860979 # (Optional 1) Bulge mass fraction (equal to MB in MakeNewDisk and GalIC) timestep_mult: 0.01 # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration epsilon: 0.1 # Softening size (internal units) @@ -223,6 +254,18 @@ SineWavePotential: timestep_limit: 1. # Time-step dimensionless pre-factor. growth_time: 0. # (Optional) Time for the potential to grow to its final size. +# Parameters related to entropy floors ---------------------------------------------- + +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + # Parameters related to cooling function ---------------------------------------------- # Constant du/dt cooling function @@ -236,6 +279,17 @@ LambdaCooling: lambda_nH2_cgs: 1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) cooling_tstep_mult: 1.0 # (Optional) Dimensionless pre-factor for the time-step condition. +# Parameters of the EAGLE cooling model (Wiersma+08 cooling tables). +EAGLECooling: + dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables + H_reion_z: 8.5 # Redshift of Hydrogen re-ionization + He_reion_z_centre: 3.5 # Redshift of the centre of the Helium re-ionization Gaussian + He_reion_z_sigma: 0.5 # Spread in redshift of the Helium re-ionization Gaussian + He_reion_eV_p_H: 2.0 # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom + newton_integration: 0 # (Optional) Set to 1 to use the Newton-Raphson method to solve the xplicit cooling problem. + Ca_over_Si_in_solar: 1. # (Optional) Ratio of Ca/Si to use in units of solar. If set to 1, the code uses [Ca/Si] = 0, i.e. Ca/Si = 0.0941736. + S_over_Si_in_solar: 1. # (Optional) Ratio of S/Si to use in units of solar. If set to 1, the code uses [S/Si] = 0, i.e. S/Si = 0.6054160. + # Cooling with Grackle 3.0 GrackleCooling: CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) @@ -253,27 +307,34 @@ GrackleCooling: # EAGLE model EAGLEChemistry: - InitMetallicity: 0. # Inital fraction of particle mass in *all* metals - InitAbundance_Hydrogen: 0.752 # Inital fraction of particle mass in Hydrogen - InitAbundance_Helium: 0.248 # Inital fraction of particle mass in Helium - InitAbundance_Carbon: 0.000 # Inital fraction of particle mass in Carbon - InitAbundance_Nitrogen: 0.000 # Inital fraction of particle mass in Nitrogen - InitAbundance_Oxygen: 0.000 # Inital fraction of particle mass in Oxygen - InitAbundance_Neon: 0.000 # Inital fraction of particle mass in Neon - InitAbundance_Magnesium: 0.000 # Inital fraction of particle mass in Magnesium - InitAbundance_Silicon: 0.000 # Inital fraction of particle mass in Silicon - InitAbundance_Iron: 0.000 # Inital fraction of particle mass in Iron - CalciumOverSilicon: 0.0941736 # Constant ratio of Calcium over Silicon abundance - SulphurOverSilicon: 0.6054160 # Constant ratio of Sulphur over Silicon abundance + init_abundance_metal: 0. # Inital fraction of particle mass in *all* metals + init_abundance_Hydrogen: 0.752 # Inital fraction of particle mass in Hydrogen + init_abundance_Helium: 0.248 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.000 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.000 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.000 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.000 # Inital fraction of particle mass in Iron -# Structure finding options (requires velociraptor) -StructureFinding: - config_file_name: stf_input.cfg # Name of the STF config file. - basename: ./stf # Common part of the name of output files. - output_time_format: 0 # Specifies the frequency format of structure finding. 0 for simulation steps (delta_step) and 1 for simulation time intervals (delta_time). - scale_factor_first: 0.92 # Scale-factor of the first snaphot (cosmological run) - time_first: 0.01 # Time of the first structure finding output (in internal units). - delta_step: 1000 # Time difference between consecutive structure finding outputs (in internal units) in simulation steps. - delta_time: 1.10 # Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals. - output_list_on: 0 # (Optional) Enable the output list - output_list: stflist.txt # (Optional) File containing the output times (see documentation in "Parameter File" section) +# Parameters related to star formation models ----------------------------------------------- + +# EAGLE star formation model (Schaye and Dalla Vecchia 2008) +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + gas_fraction: 0.25 # (Optional) The gas fraction used internally by the model (Defaults to 1). + KS_normalisation: 1.515e-4 # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr. + KS_exponent: 1.4 # The exponent of the Kennicutt-Schmidt law. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3. + KS_high_density_exponent: 2.0 # Slope of the Kennicut-Schmidt law above the high-density threshold. + KS_max_density_threshold_H_p_cm3: 1e5 # (Optional) Density above which a gas particle gets automatically turned into a star in Hydrogen atoms per cm^3 (Defaults to FLT_MAX). + KS_temperature_margin_dex: 0.5 # (Optional) Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars (Defaults to FLT_MAX). + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + diff --git a/m4/ax_asm_arm_cntvct.m4 b/m4/ax_asm_arm_cntvct.m4 new file mode 100644 index 0000000000000000000000000000000000000000..9a9c7d799d46ab1654a804dc3ee09ce5b616df2c --- /dev/null +++ b/m4/ax_asm_arm_cntvct.m4 @@ -0,0 +1,43 @@ +# +# SYNOPSIS +# +# AX_ASM_ARM_CNTVCT +# +# DESCRIPTION +# +# Check whether the CNTVCT_EL0 exists on this platform. Defines +# HAVE_ARMV8_CNTVCT_EL0 if true. +# +# LICENSE +# +# Copyright (c) 2019 Matthieu Schaller <schaller@strw.leidenuniv.nl> +# +# 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 1 + +AC_DEFUN([AX_ASM_ARM_CNTVCT], +[AC_CACHE_CHECK([for CNTVCT_EL0 asm instruction on ARM v8.1a], + [ax_cv_asm_arm_cntvct_works], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include <stdint.h> + +int +main() +{ + uint64_t cc = 0; + __asm__ __volatile__("mrs %0, CNTVCT_EL0" : "=r"(cc)); + return 0; +} + ]])], + [ax_cv_asm_arm_cntvct_works=yes], + [ax_cv_asm_arm_cntvct_works=no], + [ax_cv_asm_arm_cntvct_works=no])]) +if test "$ax_cv_asm_arm_cntvct_works" = "yes" ; then + AC_DEFINE([HAVE_ARMV8_CNTVCT_EL0], [1], + [Define to 1 if the ARM v8.1a instruction CNTVCT_EL0 exists.]) +fi +]) diff --git a/m4/ax_asm_arm_pmccntr.m4 b/m4/ax_asm_arm_pmccntr.m4 new file mode 100644 index 0000000000000000000000000000000000000000..ded3bbbc04a5270acb8045d8375ccd2e5986ecd2 --- /dev/null +++ b/m4/ax_asm_arm_pmccntr.m4 @@ -0,0 +1,43 @@ +# +# SYNOPSIS +# +# AX_ASM_ARM_PMCCNTR +# +# DESCRIPTION +# +# Check whether the PMCCNTR_EL0 exists on this platform. Defines +# HAVE_ARMV8_PMCCNTR_EL0 if true. +# +# LICENSE +# +# Copyright (c) 2019 Matthieu Schaller <schaller@strw.leidenuniv.nl> +# +# 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 1 + +AC_DEFUN([AX_ASM_ARM_PMCCNTR], +[AC_CACHE_CHECK([for PMCCNTR_EL0 asm instruction on ARM v8.1a], + [ax_cv_asm_arm_pmccntr_works], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include <stdint.h> + +int +main() +{ + uint64_t cc = 0; + __asm__ __volatile__("mrs %0, PMCCNTR_EL0" : "=r"(cc)); + return 0; +} + ]])], + [ax_cv_asm_arm_pmccntr_works=yes], + [ax_cv_asm_arm_pmccntr_works=no], + [ax_cv_asm_arm_pmccntr_works=no])]) +if test "$ax_cv_asm_arm_pmccntr_works" = "yes" ; then + AC_DEFINE([HAVE_ARMV8_PMCCNTR_EL0], [1], + [Define to 1 if the ARM v8.1a instruction PMCCNTR_EL0 exists.]) +fi +]) diff --git a/m4/ax_ext.m4 b/m4/ax_ext.m4 index 8da8c61decc9aa35737fb977def82d51ade5ef0c..0db44374f35e4605d6ca3da14ba22de0663cb87a 100644 --- a/m4/ax_ext.m4 +++ b/m4/ax_ext.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_ext.html +# https://www.gnu.org/software/autoconf-archive/ax_ext.html # =========================================================================== # # SYNOPSIS @@ -31,12 +31,13 @@ # HAVE_SHA / HAVE_AES / HAVE_AVX / HAVE_FMA3 / HAVE_FMA4 / HAVE_XOP # HAVE_AVX2 / HAVE_AVX512_F / HAVE_AVX512_CD / HAVE_AVX512_PF # HAVE_AVX512_ER / HAVE_AVX512_VL / HAVE_AVX512_BW / HAVE_AVX512_DQ -# HAVE_AVX512_IFMA / HAVE_AVX512_VBMI +# HAVE_AVX512_IFMA / HAVE_AVX512_VBMI / HAVE_ALTIVEC / HAVE_VSX # # LICENSE # # Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net> # Copyright (c) 2013,2015 Michael Petch <mpetch@capp-sysware.com> +# Copyright (c) 2017 Rafael de Lucena Valle <rafaeldelucena@gmail.com> # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice @@ -47,7 +48,7 @@ # the order of the flags when more than one is used. Given that we just # set SIMD_FLAGS to the most specific value, rather than all accepted ones. -#serial 15 +#serial 18 AC_DEFUN([AX_EXT], [ @@ -59,19 +60,43 @@ AC_DEFUN([AX_EXT], case $host_cpu in powerpc*) - AC_CACHE_CHECK([whether altivec is supported], [ax_cv_have_altivec_ext], + AC_CACHE_CHECK([whether altivec is supported for old distros], [ax_cv_have_altivec_old_ext], [ if test `/usr/sbin/sysctl -a 2>/dev/null| grep -c hw.optional.altivec` != 0; then if test `/usr/sbin/sysctl -n hw.optional.altivec` = 1; then - ax_cv_have_altivec_ext=yes + ax_cv_have_altivec_old_ext=yes fi fi ]) - if test "$ax_cv_have_altivec_ext" = yes; then + if test "$ax_cv_have_altivec_old_ext" = yes; then AC_DEFINE(HAVE_ALTIVEC,,[Support Altivec instructions]) AX_CHECK_COMPILE_FLAG(-faltivec, SIMD_FLAGS="$SIMD_FLAGS -faltivec", []) fi + + AC_CACHE_CHECK([whether altivec is supported], [ax_cv_have_altivec_ext], + [ + if test `LD_SHOW_AUXV=1 /bin/true 2>/dev/null|grep -c altivec` != 0; then + ax_cv_have_altivec_ext=yes + fi + ]) + + if test "$ax_cv_have_altivec_ext" = yes; then + AC_DEFINE(HAVE_ALTIVEC,,[Support Altivec instructions]) + AX_CHECK_COMPILE_FLAG(-maltivec, SIMD_FLAGS="$SIMD_FLAGS -maltivec", []) + fi + + AC_CACHE_CHECK([whether vsx is supported], [ax_cv_have_vsx_ext], + [ + if test `LD_SHOW_AUXV=1 /bin/true 2>/dev/null|grep -c vsx` != 0; then + ax_cv_have_vsx_ext=yes + fi + ]) + + if test "$ax_cv_have_vsx_ext" = yes; then + AC_DEFINE(HAVE_VSX,,[Support VSX instructions]) + AX_CHECK_COMPILE_FLAG(-mvsx, SIMD_FLAGS="$SIMD_FLAGS -mvsx", []) + fi ;; i[[3456]]86*|x86_64*|amd64*) @@ -139,7 +164,7 @@ AC_DEFUN([AX_EXT], ax_cv_have_sse_os_support_ext=no, if test "$((0x$edx_cpuid1>>25&0x01))" = 1; then AC_LANG_PUSH([C]) - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <signal.h> #include <stdlib.h> /* No way at ring1 to ring3 in protected mode to check the CR0 and CR4 @@ -151,10 +176,10 @@ AC_DEFUN([AX_EXT], /* SSE instruction xorps %xmm0,%xmm0 */ __asm__ __volatile__ (".byte 0x0f, 0x57, 0xc0"); return 0; - }], - ax_cv_have_sse_os_support_ext=yes, - ax_cv_have_sse_os_support_ext=no, - ax_cv_have_sse_os_support_ext=no) + }]])], + [ax_cv_have_sse_os_support_ext=yes], + [ax_cv_have_sse_os_support_ext=no], + [ax_cv_have_sse_os_support_ext=no]) AC_LANG_POP([C]) fi ]) diff --git a/m4/ax_func_posix_memalign.m4 b/m4/ax_func_posix_memalign.m4 index bd60adcbc81a5ce5c9e68f71081e6872e5139b0a..2442ceca74c3e40ceaffb2859336527964b22b52 100644 --- a/m4/ax_func_posix_memalign.m4 +++ b/m4/ax_func_posix_memalign.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_func_posix_memalign.html +# https://www.gnu.org/software/autoconf-archive/ax_func_posix_memalign.html # =========================================================================== # # SYNOPSIS @@ -22,12 +22,12 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 7 +#serial 9 AC_DEFUN([AX_FUNC_POSIX_MEMALIGN], [AC_CACHE_CHECK([for working posix_memalign], [ax_cv_func_posix_memalign_works], - [AC_TRY_RUN([ + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <stdlib.h> int @@ -39,7 +39,7 @@ main () * the size word. */ exit (posix_memalign (&buffer, sizeof(void *), 123) != 0); } - ], + ]])], [ax_cv_func_posix_memalign_works=yes], [ax_cv_func_posix_memalign_works=no], [ax_cv_func_posix_memalign_works=no])]) diff --git a/m4/ax_gcc_archflag.m4 b/m4/ax_gcc_archflag.m4 index ef8e7c199da1622354a029ec142386b7f1f9e442..ec600016ad3bd7a6afea4ab36c434a91172de9af 100644 --- a/m4/ax_gcc_archflag.m4 +++ b/m4/ax_gcc_archflag.m4 @@ -216,15 +216,15 @@ case $host_cpu in case $cpuimpl in 0x42) case $cpuarch in 8) case $cpuvar in - 0x0) ax_gcc_arch="thunderx2t99 vulcan armv8.1-a armv8-a+lse armv8-a native" ;; + 0x0) ax_gcc_arch="native" ;; esac ;; esac ;; 0x43) case $cpuarch in 8) case $cpuvar in - 0x0) ax_gcc_arch="thunderx armv8-a native" ;; - 0x1) ax_gcc_arch="thunderx+lse armv8.1-a armv8-a+lse armv8-a native" ;; + 0x0) ax_gcc_arch="native" ;; + 0x1) ax_gcc_arch="native" ;; esac ;; esac diff --git a/src/Makefile.am b/src/Makefile.am index 839ad42a724195896da0a5fea8d05141d6656f59..3b9c3b3f64ed665affe03c0757a7b8fd6aeebd9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Add the non-standard paths to the included library headers -AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS) +AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) # Assign a "safe" version number AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0 @@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0 GIT_CMD = @GIT_CMD@ # Additional dependencies for shared libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) # MPI libraries. MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS) @@ -41,41 +41,48 @@ endif # List required headers include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \ - common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h \ - physical_constants.h physical_constants_cgs.h potential.h version.h \ + common_io.h single_io.h multipole.h map.h tools.h partition.h partition_fixed_costs.h \ + clocks.h parser.h physical_constants.h physical_constants_cgs.h potential.h version.h \ hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h \ - sourceterms.h sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \ + statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h entropy_floor.h \ dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \ gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \ chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h space_getsid.h utilities.h \ - mesh_gravity.h cbrt.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h fof.h \ - logger_io.h + mesh_gravity.h cbrt.h exp10.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h \ + logger_io.h tracers_io.h tracers.h tracers_struct.h star_formation_io.h fof.h \ + star_formation_struct.h star_formation.h velociraptor_struct.h velociraptor_io.h \ + random.h + +# source files for EAGLE cooling +EAGLE_COOLING_SOURCES = +if HAVEEAGLECOOLING +EAGLE_COOLING_SOURCES += cooling/EAGLE/cooling.c cooling/EAGLE/cooling_tables.c +endif # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c \ - engine_marktasks.c serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \ - units.c common_io.c single_io.c multipole.c version.c map.c \ + engine_marktasks.c engine_drift.c serial_io.c timers.c debug.c scheduler.c \ + proxy.c parallel_io.c units.c common_io.c single_io.c multipole.c version.c map.c \ kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \ physical_constants.c potential.c hydro_properties.c \ - threadpool.c cooling.c sourceterms.c \ + threadpool.c cooling.c star_formation.c \ statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \ part_type.c xmf.c gravity_properties.c gravity.c \ collectgroup.c hydro_space.c equation_of_state.c \ chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \ - outputlist.c velociraptor_dummy.c logger_io.c fof.c + outputlist.c velociraptor_dummy.c logger_io.c fof.c $(EAGLE_COOLING_SOURCES) # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ gravity_iact.h kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h \ runner_doiact_nosort.h runner_doiact_stars.h units.h intrinsics.h minmax.h kick.h timestep.h drift.h \ adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h dump.h logger.h sign.h \ - logger_io.h \ + logger_io.h timestep_limiter.h \ gravity.h gravity_io.h gravity_cache.h \ gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \ gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ gravity/Potential/gravity.h gravity/Potential/gravity_iact.h gravity/Potential/gravity_io.h \ gravity/Potential/gravity_debug.h gravity/Potential/gravity_part.h \ - sourceterms.h \ equation_of_state.h \ equation_of_state/ideal_gas/equation_of_state.h equation_of_state/isothermal/equation_of_state.h \ hydro.h hydro_io.h \ @@ -130,9 +137,15 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h stars.h stars_io.h \ stars/Default/stars.h stars/Default/stars_iact.h stars/Default/stars_io.h \ stars/Default/stars_debug.h stars/Default/stars_part.h \ + stars/EAGLE/stars.h stars/EAGLE/stars_iact.h stars/EAGLE/stars_io.h \ + stars/EAGLE/stars_debug.h stars/EAGLE/stars_part.h \ potential/none/potential.h potential/point_mass/potential.h \ potential/isothermal/potential.h potential/disc_patch/potential.h \ potential/sine_wave/potential.h \ + star_formation/none/star_formation.h star_formation/none/star_formation_struct.h \ + star_formation/none/star_formation_io.h \ + star_formation/EAGLE/star_formation.h star_formation/EAGLE/star_formation_struct.h \ + star_formation/EAGLE/star_formation_io.h \ cooling/none/cooling.h cooling/none/cooling_struct.h \ cooling/none/cooling_io.h \ cooling/Compton/cooling.h cooling/Compton/cooling_struct.h \ @@ -144,7 +157,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h cooling/grackle/cooling.h cooling/grackle/cooling_struct.h \ cooling/grackle/cooling_io.h \ cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h \ - cooling/EAGLE/cooling_io.h \ + cooling/EAGLE/cooling_io.h cooling/EAGLE/interpolate.h cooling/EAGLE/cooling_rates.h \ chemistry/none/chemistry.h \ chemistry/none/chemistry_io.h \ chemistry/none/chemistry_struct.h \ @@ -156,7 +169,13 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h chemistry/EAGLE/chemistry.h \ chemistry/EAGLE/chemistry_io.h \ chemistry/EAGLE/chemistry_struct.h\ - chemistry/EAGLE/chemistry_iact.h + chemistry/EAGLE/chemistry_iact.h \ + entropy_floor/none/entropy_floor.h \ + entropy_floor/EAGLE/entropy_floor.h \ + tracers/none/tracers.h tracers/none/tracers_struct.h \ + tracers/none/tracers_io.h \ + tracers/EAGLE/tracers.h tracers/EAGLE/tracers_struct.h \ + tracers/EAGLE/tracers_io.h # Sources and flags for regular library diff --git a/src/active.h b/src/active.h index a3d2d5ad90c93b06bbd4853a2633d087304ee570..5bbbd3803cb09e7aa05ddb15e2e5c2a15b27602c 100644 --- a/src/active.h +++ b/src/active.h @@ -74,6 +74,22 @@ __attribute__((always_inline)) INLINE static int cell_are_gpart_drifted( return (c->grav.ti_old_part == e->ti_current); } +/** + * @brief Check that the #spart in a #cell have been drifted to the current + * time. + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + * @return 1 if the #cell has been drifted to the current time, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int cell_are_spart_drifted( + const struct cell *c, const struct engine *e) { + + /* Currently just use the gpart drift + * This function is just for clarity */ + return cell_are_gpart_drifted(c, e); +} + /* Are cells / particles active for regular tasks ? */ /** @@ -185,9 +201,16 @@ __attribute__((always_inline)) INLINE static int cell_is_all_active_gravity( __attribute__((always_inline)) INLINE static int cell_is_active_stars( const struct cell *c, const struct engine *e) { - // LOIC: Need star-specific implementation +#ifdef SWIFT_DEBUG_CHECKS + if (c->stars.ti_end_min < e->ti_current) + error( + "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and " + "e->ti_current=%lld (t=%e, a=%e)", + c->stars.ti_end_min, c->stars.ti_end_min * e->time_base, e->ti_current, + e->ti_current * e->time_base, e->cosmology->a); +#endif - return cell_is_active_gravity(c, e); + return (c->stars.ti_end_min == e->ti_current); } /** diff --git a/src/cache.h b/src/cache.h index 5dd8164b1dc80795a8593cc2af42c2c9e7e68885..b6735f381da5c6e615d50e0c5768ca201022aa59 100644 --- a/src/cache.h +++ b/src/cache.h @@ -179,8 +179,9 @@ __attribute__((always_inline)) INLINE void cache_init(struct cache *c, * * @param ci The #cell. * @param ci_cache The cache. + * @return uninhibited_count The no. of uninhibited particles. */ -__attribute__((always_inline)) INLINE void cache_read_particles( +__attribute__((always_inline)) INLINE int cache_read_particles( const struct cell *restrict const ci, struct cache *restrict const ci_cache) { @@ -197,12 +198,29 @@ __attribute__((always_inline)) INLINE void cache_read_particles( swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT); + const int count = ci->hydro.count; const struct part *restrict parts = ci->hydro.parts; const double loc[3] = {ci->loc[0], ci->loc[1], ci->loc[2]}; + const double max_dx = ci->hydro.dx_max_part; + const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + const float h_padded = ci->hydro.h_max / 4.; /* Shift the particles positions to a local frame so single precision can be * used instead of double precision. */ - for (int i = 0; i < ci->hydro.count; i++) { + for (int i = 0; i < count; i++) { + + /* Pad inhibited particles. */ + if (parts[i].time_bin >= time_bin_inhibited) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + h[i] = h_padded; + + continue; + } + x[i] = (float)(parts[i].x[0] - loc[0]); y[i] = (float)(parts[i].x[1] - loc[1]); z[i] = (float)(parts[i].x[2] - loc[2]); @@ -213,6 +231,103 @@ __attribute__((always_inline)) INLINE void cache_read_particles( vz[i] = parts[i].v[2]; } + /* Pad cache if the no. of particles is not a multiple of double the vector + * length. */ + int count_align = count; + const int rem = count % (NUM_VEC_PROC * VEC_SIZE); + if (rem != 0) { + count_align += (NUM_VEC_PROC * VEC_SIZE) - rem; + + /* Set positions to something outside of the range of any particle */ + for (int i = count; i < count_align; i++) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + } + } + + return count_align; + +#else + error("Can't call the cache reading function with this flavour of SPH!"); + return 0; +#endif +} + +/** + * @brief Populate cache by reading in the particles in unsorted order for + * doself_subset. + * + * @param ci The #cell. + * @param ci_cache The cache. + * @return uninhibited_count The no. of uninhibited particles. + */ +__attribute__((always_inline)) INLINE int cache_read_particles_subset_self( + const struct cell *restrict const ci, + struct cache *restrict const ci_cache) { + +#if defined(GADGET2_SPH) + + /* Let the compiler know that the data is aligned and create pointers to the + * arrays inside the cache. */ + swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, m, ci_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, vx, ci_cache->vx, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT); + + const int count = ci->hydro.count; + const struct part *restrict parts = ci->hydro.parts; + const double loc[3] = {ci->loc[0], ci->loc[1], ci->loc[2]}; + const double max_dx = ci->hydro.dx_max_part; + const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + + /* Shift the particles positions to a local frame so single precision can be + * used instead of double precision. */ + for (int i = 0; i < count; i++) { + + /* Pad inhibited particles. */ + if (parts[i].time_bin >= time_bin_inhibited) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + + continue; + } + + x[i] = (float)(parts[i].x[0] - loc[0]); + y[i] = (float)(parts[i].x[1] - loc[1]); + z[i] = (float)(parts[i].x[2] - loc[2]); + m[i] = parts[i].mass; + vx[i] = parts[i].v[0]; + vy[i] = parts[i].v[1]; + vz[i] = parts[i].v[2]; + } + + /* Pad cache if the no. of particles is not a multiple of double the vector + * length. */ + int count_align = count; + const int rem = count % (NUM_VEC_PROC * VEC_SIZE); + if (rem != 0) { + count_align += (NUM_VEC_PROC * VEC_SIZE) - rem; + + /* Set positions to something outside of the range of any particle */ + for (int i = count; i < count_align; i++) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + } + } + + return count_align; + +#else + error("Can't call the cache reading function with this flavour of SPH!"); + return 0; #endif } @@ -230,7 +345,7 @@ __attribute__((always_inline)) INLINE void cache_read_particles( * @param loc The cell location to remove from the particle positions. * @param flipped Flag to check whether the cells have been flipped or not. */ -__attribute__((always_inline)) INLINE void cache_read_particles_subset( +__attribute__((always_inline)) INLINE void cache_read_particles_subset_pair( const struct cell *restrict const ci, struct cache *restrict const ci_cache, const struct entry *restrict sort_i, int *first_pi, int *last_pi, const double *loc, const int flipped) { @@ -242,7 +357,6 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_declare_aligned_ptr(float, h, ci_cache->h, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, m, ci_cache->m, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, vx, ci_cache->vx, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT); @@ -261,14 +375,32 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( if (*last_pi + pad < ci->hydro.count) *last_pi += pad; } + const double max_dx = ci->hydro.dx_max_part; + const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + /* Shift the particles positions to a local frame so single precision can be * used instead of double precision. */ for (int i = 0; i < *last_pi; i++) { const int idx = sort_i[i].i; + + /* Put inhibited particles out of range. */ + if (parts[idx].time_bin >= time_bin_inhibited) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + m[i] = 1.f; + vx[i] = 1.f; + vy[i] = 1.f; + vz[i] = 1.f; + + continue; + } + x[i] = (float)(parts[idx].x[0] - loc[0]); y[i] = (float)(parts[idx].x[1] - loc[1]); z[i] = (float)(parts[idx].x[2] - loc[2]); - h[i] = parts[idx].h; m[i] = parts[idx].mass; vx[i] = parts[idx].v[0]; vy[i] = parts[idx].v[1]; @@ -278,17 +410,10 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const double max_dx = ci->hydro.dx_max_part; - const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), - -(2. * ci->width[1] + max_dx), - -(2. * ci->width[2] + max_dx)}; - const float h_padded = ci->hydro.parts[0].h; - for (int i = *last_pi; i < *last_pi + VEC_SIZE; i++) { x[i] = pos_padded[0]; y[i] = pos_padded[1]; z[i] = pos_padded[2]; - h[i] = h_padded; m[i] = 1.f; vx[i] = 1.f; @@ -308,15 +433,33 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( } const int ci_cache_count = ci->hydro.count - *first_pi; + const double max_dx = ci->hydro.dx_max_part; + const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; /* Shift the particles positions to a local frame so single precision can be * used instead of double precision. */ for (int i = 0; i < ci_cache_count; i++) { const int idx = sort_i[i + *first_pi].i; + + /* Put inhibited particles out of range. */ + if (parts[idx].time_bin >= time_bin_inhibited) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + + m[i] = 1.f; + vx[i] = 1.f; + vy[i] = 1.f; + vz[i] = 1.f; + + continue; + } + x[i] = (float)(parts[idx].x[0] - loc[0]); y[i] = (float)(parts[idx].x[1] - loc[1]); z[i] = (float)(parts[idx].x[2] - loc[2]); - h[i] = parts[idx].h; m[i] = parts[idx].mass; vx[i] = parts[idx].v[0]; vy[i] = parts[idx].v[1]; @@ -326,18 +469,11 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const double max_dx = ci->hydro.dx_max_part; - const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), - -(2. * ci->width[1] + max_dx), - -(2. * ci->width[2] + max_dx)}; - const float h_padded = ci->hydro.parts[0].h; - for (int i = ci->hydro.count - *first_pi; i < ci->hydro.count - *first_pi + VEC_SIZE; i++) { x[i] = pos_padded[0]; y[i] = pos_padded[1]; z[i] = pos_padded[2]; - h[i] = h_padded; m[i] = 1.f; vx[i] = 1.f; @@ -355,8 +491,9 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset( * * @param ci The #cell. * @param ci_cache The cache. + * @return uninhibited_count The no. of uninhibited particles. */ -__attribute__((always_inline)) INLINE void cache_read_force_particles( +__attribute__((always_inline)) INLINE int cache_read_force_particles( const struct cell *restrict const ci, struct cache *restrict const ci_cache) { @@ -382,12 +519,34 @@ __attribute__((always_inline)) INLINE void cache_read_force_particles( swift_declare_aligned_ptr(float, soundspeed, ci_cache->soundspeed, SWIFT_CACHE_ALIGNMENT); + const int count = ci->hydro.count; const struct part *restrict parts = ci->hydro.parts; const double loc[3] = {ci->loc[0], ci->loc[1], ci->loc[2]}; + const double max_dx = ci->hydro.dx_max_part; + const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + const float h_padded = ci->hydro.h_max / 4.; /* Shift the particles positions to a local frame so single precision can be * used instead of double precision. */ - for (int i = 0; i < ci->hydro.count; i++) { + for (int i = 0; i < count; i++) { + + /* Skip inhibited particles. */ + if (parts[i].time_bin >= time_bin_inhibited) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + h[i] = h_padded; + rho[i] = 1.f; + grad_h[i] = 1.f; + pOrho2[i] = 1.f; + balsara[i] = 1.f; + soundspeed[i] = 1.f; + + continue; + } + x[i] = (float)(parts[i].x[0] - loc[0]); y[i] = (float)(parts[i].x[1] - loc[1]); z[i] = (float)(parts[i].x[2] - loc[2]); @@ -403,6 +562,32 @@ __attribute__((always_inline)) INLINE void cache_read_force_particles( soundspeed[i] = parts[i].force.soundspeed; } + /* Pad cache if there is a serial remainder. */ + int count_align = count; + const int rem = count % VEC_SIZE; + if (rem != 0) { + count_align += VEC_SIZE - rem; + + /* Set positions to the same as particle pi so when the r2 > 0 mask is + * applied these extra contributions are masked out.*/ + for (int i = count; i < count_align; i++) { + x[i] = pos_padded[0]; + y[i] = pos_padded[1]; + z[i] = pos_padded[2]; + h[i] = h_padded; + rho[i] = 1.f; + grad_h[i] = 1.f; + pOrho2[i] = 1.f; + balsara[i] = 1.f; + soundspeed[i] = 1.f; + } + } + + return count_align; + +#else + error("Can't call the cache reading function with this flavour of SPH!"); + return 0; #endif } @@ -472,11 +657,32 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted( swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT); int ci_cache_count = ci->hydro.count - first_pi_align; + const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + const float pos_padded_i[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + const float h_padded_i = ci->hydro.h_max / 4.; /* Shift the particles positions to a local frame (ci frame) so single * precision can be used instead of double precision. */ for (int i = 0; i < ci_cache_count; i++) { const int idx = sort_i[i + first_pi_align].i; + + /* Put inhibited particles out of range. */ + if (parts_i[idx].time_bin >= time_bin_inhibited) { + x[i] = pos_padded_i[0]; + y[i] = pos_padded_i[1]; + z[i] = pos_padded_i[2]; + h[i] = h_padded_i; + + m[i] = 1.f; + vx[i] = 1.f; + vy[i] = 1.f; + vz[i] = 1.f; + + continue; + } + x[i] = (float)(parts_i[idx].x[0] - total_ci_shift[0]); y[i] = (float)(parts_i[idx].x[1] - total_ci_shift[1]); z[i] = (float)(parts_i[idx].x[2] - total_ci_shift[2]); @@ -532,18 +738,12 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); - const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), - -(2. * ci->width[1] + max_dx), - -(2. * ci->width[2] + max_dx)}; - const float h_padded = ci->hydro.parts[0].h; - for (int i = ci->hydro.count - first_pi_align; i < ci->hydro.count - first_pi_align + VEC_SIZE; i++) { - x[i] = pos_padded[0]; - y[i] = pos_padded[1]; - z[i] = pos_padded[2]; - h[i] = h_padded; + x[i] = pos_padded_i[0]; + y[i] = pos_padded_i[1]; + z[i] = pos_padded_i[2]; + h[i] = h_padded_i; m[i] = 1.f; vx[i] = 1.f; @@ -562,8 +762,29 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted( swift_declare_aligned_ptr(float, vyj, cj_cache->vy, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, vzj, cj_cache->vz, SWIFT_CACHE_ALIGNMENT); + const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx), + -(2. * cj->width[1] + max_dx), + -(2. * cj->width[2] + max_dx)}; + const float h_padded_j = cj->hydro.h_max / 4.; + for (int i = 0; i <= last_pj_align; i++) { const int idx = sort_j[i].i; + + /* Put inhibited particles out of range. */ + if (parts_j[idx].time_bin >= time_bin_inhibited) { + xj[i] = pos_padded_j[0]; + yj[i] = pos_padded_j[1]; + zj[i] = pos_padded_j[2]; + hj[i] = h_padded_j; + + mj[i] = 1.f; + vxj[i] = 1.f; + vyj[i] = 1.f; + vzj[i] = 1.f; + + continue; + } + xj[i] = (float)(parts_j[idx].x[0] - total_cj_shift[0]); yj[i] = (float)(parts_j[idx].x[1] - total_cj_shift[1]); zj[i] = (float)(parts_j[idx].x[2] - total_cj_shift[2]); @@ -609,11 +830,6 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx), - -(2. * cj->width[1] + max_dx), - -(2. * cj->width[2] + max_dx)}; - const float h_padded_j = cj->hydro.parts[0].h; - for (int i = last_pj_align + 1; i < last_pj_align + 1 + VEC_SIZE; i++) { xj[i] = pos_padded_j[0]; yj[i] = pos_padded_j[1]; @@ -701,11 +917,37 @@ cache_read_two_partial_cells_sorted_force( SWIFT_CACHE_ALIGNMENT); int ci_cache_count = ci->hydro.count - first_pi_align; + const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); + const float pos_padded_i[3] = {-(2. * ci->width[0] + max_dx), + -(2. * ci->width[1] + max_dx), + -(2. * ci->width[2] + max_dx)}; + const float h_padded_i = ci->hydro.h_max / 4.; + /* Shift the particles positions to a local frame (ci frame) so single * precision can be used instead of double precision. */ for (int i = 0; i < ci_cache_count; i++) { const int idx = sort_i[i + first_pi_align].i; + + /* Put inhibited particles out of range. */ + if (parts_i[idx].time_bin >= time_bin_inhibited) { + x[i] = pos_padded_i[0]; + y[i] = pos_padded_i[1]; + z[i] = pos_padded_i[2]; + h[i] = h_padded_i; + m[i] = 1.f; + vx[i] = 1.f; + vy[i] = 1.f; + vz[i] = 1.f; + rho[i] = 1.f; + grad_h[i] = 1.f; + pOrho2[i] = 1.f; + balsara[i] = 1.f; + soundspeed[i] = 1.f; + + continue; + } + x[i] = (float)(parts_i[idx].x[0] - total_ci_shift[0]); y[i] = (float)(parts_i[idx].x[1] - total_ci_shift[1]); z[i] = (float)(parts_i[idx].x[2] - total_ci_shift[2]); @@ -726,18 +968,12 @@ cache_read_two_partial_cells_sorted_force( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part); - const float pos_padded[3] = {-(2. * ci->width[0] + max_dx), - -(2. * ci->width[1] + max_dx), - -(2. * ci->width[2] + max_dx)}; - const float h_padded = ci->hydro.parts[0].h; - for (int i = ci->hydro.count - first_pi_align; i < ci->hydro.count - first_pi_align + VEC_SIZE; i++) { - x[i] = pos_padded[0]; - y[i] = pos_padded[1]; - z[i] = pos_padded[2]; - h[i] = h_padded; + x[i] = pos_padded_i[0]; + y[i] = pos_padded_i[1]; + z[i] = pos_padded_i[2]; + h[i] = h_padded_i; m[i] = 1.f; vx[i] = 1.f; vy[i] = 1.f; @@ -769,8 +1005,33 @@ cache_read_two_partial_cells_sorted_force( swift_declare_aligned_ptr(float, soundspeedj, cj_cache->soundspeed, SWIFT_CACHE_ALIGNMENT); + const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx), + -(2. * cj->width[1] + max_dx), + -(2. * cj->width[2] + max_dx)}; + const float h_padded_j = cj->hydro.h_max / 4.; + for (int i = 0; i <= last_pj_align; i++) { const int idx = sort_j[i].i; + + /* Put inhibited particles out of range. */ + if (parts_j[idx].time_bin == time_bin_inhibited) { + xj[i] = pos_padded_j[0]; + yj[i] = pos_padded_j[1]; + zj[i] = pos_padded_j[2]; + hj[i] = h_padded_j; + mj[i] = 1.f; + vxj[i] = 1.f; + vyj[i] = 1.f; + vzj[i] = 1.f; + rhoj[i] = 1.f; + grad_hj[i] = 1.f; + pOrho2j[i] = 1.f; + balsaraj[i] = 1.f; + soundspeedj[i] = 1.f; + + continue; + } + xj[i] = (float)(parts_j[idx].x[0] - total_cj_shift[0]); yj[i] = (float)(parts_j[idx].x[1] - total_cj_shift[1]); zj[i] = (float)(parts_j[idx].x[2] - total_cj_shift[2]); @@ -791,11 +1052,6 @@ cache_read_two_partial_cells_sorted_force( /* Pad cache with fake particles that exist outside the cell so will not * interact. We use values of the same magnitude (but negative!) as the real * particles to avoid overflow problems. */ - const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx), - -(2. * cj->width[1] + max_dx), - -(2. * cj->width[2] + max_dx)}; - const float h_padded_j = cj->hydro.parts[0].h; - for (int i = last_pj_align + 1; i < last_pj_align + 1 + VEC_SIZE; i++) { xj[i] = pos_padded_j[0]; yj[i] = pos_padded_j[1]; @@ -813,7 +1069,8 @@ cache_read_two_partial_cells_sorted_force( } } -/* @brief Clean the memory allocated by a #cache object. +/** + * @brief Clean the memory allocated by a #cache object. * * @param c The #cache to clean. */ diff --git a/src/cell.c b/src/cell.c index 645dfbd3eaa94c4266243f19309c205824ce79f8..4f44abd924be97e54e52fffda8559f7a0dcfe68a 100644 --- a/src/cell.c +++ b/src/cell.c @@ -64,6 +64,7 @@ #include "stars.h" #include "timers.h" #include "tools.h" +#include "tracers.h" /* Global variables. */ int cell_next_tag = 0; @@ -97,6 +98,14 @@ int cell_getsize(struct cell *c) { */ int cell_link_parts(struct cell *c, struct part *parts) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); + + if (c->hydro.parts != NULL) + error("Linking parts into a cell that was already linked"); +#endif + c->hydro.parts = parts; /* Fill the progeny recursively, depth-first. */ @@ -122,6 +131,14 @@ int cell_link_parts(struct cell *c, struct part *parts) { */ int cell_link_gparts(struct cell *c, struct gpart *gparts) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); + + if (c->grav.parts != NULL) + error("Linking gparts into a cell that was already linked"); +#endif + c->grav.parts = gparts; /* Fill the progeny recursively, depth-first. */ @@ -147,6 +164,14 @@ int cell_link_gparts(struct cell *c, struct gpart *gparts) { */ int cell_link_sparts(struct cell *c, struct spart *sparts) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); + + if (c->stars.parts != NULL) + error("Linking sparts into a cell that was already linked"); +#endif + c->stars.parts = sparts; /* Fill the progeny recursively, depth-first. */ @@ -162,6 +187,182 @@ int cell_link_sparts(struct cell *c, struct spart *sparts) { return c->stars.count; } +/** + * @brief Recurse down foreign cells until reaching one with hydro + * tasks; then trigger the linking of the #part array from that + * level. + * + * @param c The #cell. + * @param parts The #part array. + * + * @return The number of particles linked. + */ +int cell_link_foreign_parts(struct cell *c, struct part *parts) { + +#ifdef WITH_MPI + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); +#endif + + /* Do we have a hydro task at this level? */ + if (c->mpi.hydro.recv_xv != NULL) { + + /* Recursively attach the parts */ + const int counts = cell_link_parts(c, parts); +#ifdef SWIFT_DEBUG_CHECKS + if (counts != c->hydro.count) + error("Something is wrong with the foreign counts"); +#endif + return counts; + } + + /* Go deeper to find the level where the tasks are */ + if (c->split) { + int count = 0; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + count += cell_link_foreign_parts(c->progeny[k], &parts[count]); + } + } + return count; + } else { + return 0; + } + +#else + error("Calling linking of foregin particles in non-MPI mode."); +#endif +} + +/** + * @brief Recurse down foreign cells until reaching one with gravity + * tasks; then trigger the linking of the #gpart array from that + * level. + * + * @param c The #cell. + * @param gparts The #gpart array. + * + * @return The number of particles linked. + */ +int cell_link_foreign_gparts(struct cell *c, struct gpart *gparts) { + +#ifdef WITH_MPI + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); +#endif + + /* Do we have a hydro task at this level? */ + if (c->mpi.grav.recv != NULL) { + + /* Recursively attach the gparts */ + const int counts = cell_link_gparts(c, gparts); +#ifdef SWIFT_DEBUG_CHECKS + if (counts != c->grav.count) + error("Something is wrong with the foreign counts"); +#endif + return counts; + } + + /* Go deeper to find the level where the tasks are */ + if (c->split) { + int count = 0; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + count += cell_link_foreign_gparts(c->progeny[k], &gparts[count]); + } + } + return count; + } else { + return 0; + } + +#else + error("Calling linking of foregin particles in non-MPI mode."); +#endif +} + +/** + * @brief Recursively count the number of #part in foreign cells that + * are in cells with hydro-related tasks. + * + * @param c The #cell. + * + * @return The number of particles linked. + */ +int cell_count_parts_for_tasks(const struct cell *c) { + +#ifdef WITH_MPI + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Counting foreign particles in a local cell!"); +#endif + + /* Do we have a hydro task at this level? */ + if (c->mpi.hydro.recv_xv != NULL) { + return c->hydro.count; + } + + if (c->split) { + int count = 0; + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) { + count += cell_count_parts_for_tasks(c->progeny[k]); + } + } + return count; + } else { + return 0; + } + +#else + error("Calling linking of foregin particles in non-MPI mode."); +#endif +} + +/** + * @brief Recursively count the number of #gpart in foreign cells that + * are in cells with gravity-related tasks. + * + * @param c The #cell. + * + * @return The number of particles linked. + */ +int cell_count_gparts_for_tasks(const struct cell *c) { + +#ifdef WITH_MPI + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Counting foreign particles in a local cell!"); +#endif + + /* Do we have a hydro task at this level? */ + if (c->mpi.grav.recv != NULL) { + return c->grav.count; + } + + if (c->split) { + int count = 0; + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) { + count += cell_count_gparts_for_tasks(c->progeny[k]); + } + } + return count; + } else { + return 0; + } + +#else + error("Calling linking of foregin particles in non-MPI mode."); +#endif +} + /** * @brief Pack the data of the given cell and all it's sub-cells. * @@ -184,6 +385,7 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc, pc->hydro.ti_end_max = c->hydro.ti_end_max; pc->grav.ti_end_min = c->grav.ti_end_min; pc->grav.ti_end_max = c->grav.ti_end_max; + pc->stars.ti_end_min = c->stars.ti_end_min; pc->hydro.ti_old_part = c->hydro.ti_old_part; pc->grav.ti_old_part = c->grav.ti_old_part; pc->grav.ti_old_multipole = c->grav.ti_old_multipole; @@ -287,6 +489,7 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, c->hydro.ti_end_max = pc->hydro.ti_end_max; c->grav.ti_end_min = pc->grav.ti_end_min; c->grav.ti_end_max = pc->grav.ti_end_max; + c->stars.ti_end_min = pc->stars.ti_end_min; c->hydro.ti_old_part = pc->hydro.ti_old_part; c->grav.ti_old_part = pc->grav.ti_old_part; c->grav.ti_old_multipole = pc->grav.ti_old_multipole; @@ -342,6 +545,7 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c, temp->hydro.dx_max_part = 0.f; temp->hydro.dx_max_sort = 0.f; temp->stars.dx_max_part = 0.f; + temp->stars.dx_max_sort = 0.f; temp->nodeID = c->nodeID; temp->parent = c; c->progeny[k] = temp; @@ -414,6 +618,7 @@ int cell_pack_end_step(struct cell *restrict c, pcells[0].hydro.ti_end_max = c->hydro.ti_end_max; pcells[0].grav.ti_end_min = c->grav.ti_end_min; pcells[0].grav.ti_end_max = c->grav.ti_end_max; + pcells[0].stars.ti_end_min = c->stars.ti_end_min; pcells[0].hydro.dx_max_part = c->hydro.dx_max_part; pcells[0].stars.dx_max_part = c->stars.dx_max_part; @@ -451,6 +656,7 @@ int cell_unpack_end_step(struct cell *restrict c, c->hydro.ti_end_max = pcells[0].hydro.ti_end_max; c->grav.ti_end_min = pcells[0].grav.ti_end_min; c->grav.ti_end_max = pcells[0].grav.ti_end_max; + c->stars.ti_end_min = pcells[0].stars.ti_end_min; c->hydro.dx_max_part = pcells[0].hydro.dx_max_part; c->stars.dx_max_part = pcells[0].stars.dx_max_part; @@ -967,6 +1173,7 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->hydro.count = bucket_count[k]; + c->progeny[k]->hydro.count_total = c->progeny[k]->hydro.count; c->progeny[k]->hydro.parts = &c->hydro.parts[bucket_offset[k]]; c->progeny[k]->hydro.xparts = &c->hydro.xparts[bucket_offset[k]]; } @@ -1084,6 +1291,7 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->stars.count = bucket_count[k]; + c->progeny[k]->stars.count_total = c->progeny[k]->stars.count; c->progeny[k]->stars.parts = &c->stars.parts[bucket_offset[k]]; } @@ -1146,6 +1354,7 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { c->progeny[k]->grav.count = bucket_count[k]; + c->progeny[k]->grav.count_total = c->progeny[k]->grav.count; c->progeny[k]->grav.parts = &c->grav.parts[bucket_offset[k]]; } } @@ -1223,8 +1432,11 @@ void cell_clean_links(struct cell *c, void *data) { c->hydro.density = NULL; c->hydro.gradient = NULL; c->hydro.force = NULL; + c->hydro.limiter = NULL; c->grav.grav = NULL; c->grav.mm = NULL; + c->stars.density = NULL; + c->stars.feedback = NULL; } /** @@ -1561,12 +1773,20 @@ void cell_check_multipole(struct cell *c) { */ void cell_clean(struct cell *c) { + /* Hydro */ for (int i = 0; i < 13; i++) if (c->hydro.sort[i] != NULL) { free(c->hydro.sort[i]); c->hydro.sort[i] = NULL; } + /* Stars */ + for (int i = 0; i < 13; i++) + if (c->stars.sort[i] != NULL) { + free(c->stars.sort[i]); + c->stars.sort[i] = NULL; + } + /* Recurse */ for (int k = 0; k < 8; k++) if (c->progeny[k]) cell_clean(c->progeny[k]); @@ -1582,6 +1802,14 @@ void cell_clear_drift_flags(struct cell *c, void *data) { c->grav.do_sub_drift = 0; } +/** + * @brief Clear the limiter flags on the given cell. + */ +void cell_clear_limiter_flags(struct cell *c, void *data) { + c->hydro.do_limiter = 0; + c->hydro.do_sub_limiter = 0; +} + /** * @brief Activate the #part drifts on the given cell. */ @@ -1605,7 +1833,10 @@ void cell_activate_drift_part(struct cell *c, struct scheduler *s) { for (struct cell *parent = c->parent; parent != NULL && !parent->hydro.do_sub_drift; parent = parent->parent) { + + /* Mark this cell for drifting */ parent->hydro.do_sub_drift = 1; + if (parent == c->hydro.super) { #ifdef SWIFT_DEBUG_CHECKS if (parent->hydro.drift == NULL) @@ -1665,13 +1896,53 @@ void cell_activate_drift_gpart(struct cell *c, struct scheduler *s) { * @brief Activate the #spart drifts on the given cell. */ void cell_activate_drift_spart(struct cell *c, struct scheduler *s) { + // MATTHIEU: This will need changing cell_activate_drift_gpart(c, s); } +/** + * @brief Activate the drifts on the given cell. + */ +void cell_activate_limiter(struct cell *c, struct scheduler *s) { + + /* If this cell is already marked for drift, quit early. */ + if (c->hydro.do_limiter) return; + + /* Mark this cell for limiting. */ + c->hydro.do_limiter = 1; + + /* Set the do_sub_limiter all the way up and activate the super limiter + if this has not yet been done. */ + if (c == c->super) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->timestep_limiter == NULL) + error("Trying to activate un-existing c->timestep_limiter"); +#endif + scheduler_activate(s, c->timestep_limiter); + } else { + for (struct cell *parent = c->parent; + parent != NULL && !parent->hydro.do_sub_limiter; + parent = parent->parent) { + + /* Mark this cell for limiting */ + parent->hydro.do_sub_limiter = 1; + + if (parent == c->super) { +#ifdef SWIFT_DEBUG_CHECKS + if (parent->timestep_limiter == NULL) + error("Trying to activate un-existing parent->timestep_limiter"); +#endif + scheduler_activate(s, parent->timestep_limiter); + break; + } + } + } +} + /** * @brief Activate the sorts up a cell hierarchy. */ -void cell_activate_sorts_up(struct cell *c, struct scheduler *s) { +void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) { if (c == c->hydro.super) { #ifdef SWIFT_DEBUG_CHECKS @@ -1702,7 +1973,7 @@ void cell_activate_sorts_up(struct cell *c, struct scheduler *s) { /** * @brief Activate the sorts on a given cell, if needed. */ -void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s) { +void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s) { /* Do we need to re-sort? */ if (c->hydro.dx_max_sort > space_maxreldx * c->dmin) { @@ -1711,7 +1982,7 @@ void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s) { for (struct cell *finger = c; finger != NULL; finger = finger->parent) { if (finger->hydro.requires_sorts) { atomic_or(&finger->hydro.do_sort, finger->hydro.requires_sorts); - cell_activate_sorts_up(finger, s); + cell_activate_hydro_sorts_up(finger, s); } finger->hydro.sorted = 0; } @@ -1720,7 +1991,70 @@ void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s) { /* Has this cell been sorted at all for the given sid? */ if (!(c->hydro.sorted & (1 << sid)) || c->nodeID != engine_rank) { atomic_or(&c->hydro.do_sort, (1 << sid)); - cell_activate_sorts_up(c, s); + cell_activate_hydro_sorts_up(c, s); + } +} + +/** + * @brief Activate the sorts up a cell hierarchy. + */ +void cell_activate_stars_sorts_up(struct cell *c, struct scheduler *s) { + + if (c == c->super) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->stars.sorts == NULL) + error("Trying to activate un-existing c->stars.sorts"); +#endif + scheduler_activate(s, c->stars.sorts); + if (c->nodeID == engine_rank) { + // MATTHIEU: to do: do we actually need both drifts here? + cell_activate_drift_part(c, s); + cell_activate_drift_spart(c, s); + } + } else { + + for (struct cell *parent = c->parent; + parent != NULL && !parent->stars.do_sub_sort; + parent = parent->parent) { + parent->stars.do_sub_sort = 1; + if (parent == c->super) { +#ifdef SWIFT_DEBUG_CHECKS + if (parent->stars.sorts == NULL) + error("Trying to activate un-existing parents->stars.sorts"); +#endif + scheduler_activate(s, parent->stars.sorts); + if (parent->nodeID == engine_rank) { + cell_activate_drift_part(parent, s); + cell_activate_drift_spart(parent, s); + } + break; + } + } + } +} + +/** + * @brief Activate the sorts on a given cell, if needed. + */ +void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s) { + + /* Do we need to re-sort? */ + if (c->stars.dx_max_sort > space_maxreldx * c->dmin) { + + /* Climb up the tree to active the sorts in that direction */ + for (struct cell *finger = c; finger != NULL; finger = finger->parent) { + if (finger->stars.requires_sorts) { + atomic_or(&finger->stars.do_sort, finger->stars.requires_sorts); + cell_activate_stars_sorts_up(finger, s); + } + finger->stars.sorted = 0; + } + } + + /* Has this cell been sorted at all for the given sid? */ + if (!(c->stars.sorted & (1 << sid)) || c->nodeID != engine_rank) { + atomic_or(&c->stars.do_sort, (1 << sid)); + cell_activate_stars_sorts_up(c, s); } } @@ -1735,6 +2069,7 @@ void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s) { void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj, struct scheduler *s) { const struct engine *e = s->space->e; + const int with_limiter = (e->policy & engine_policy_limiter); /* Store the current dx_max and h_max values. */ ci->hydro.dx_max_part_old = ci->hydro.dx_max_part; @@ -1768,6 +2103,7 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj, /* We have reached the bottom of the tree: activate drift */ cell_activate_drift_part(ci, s); + if (with_limiter) cell_activate_limiter(ci, s); } } @@ -2073,9 +2409,15 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj, if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s); if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s); + /* Also activate the time-step limiter */ + if (ci->nodeID == engine_rank && with_limiter) + cell_activate_limiter(ci, s); + if (cj->nodeID == engine_rank && with_limiter) + cell_activate_limiter(cj, s); + /* Do we need to sort the cells? */ - cell_activate_sorts(ci, sid, s); - cell_activate_sorts(cj, sid, s); + cell_activate_hydro_sorts(ci, sid, s); + cell_activate_hydro_sorts(cj, sid, s); } } /* Otherwise, pair interation */ } @@ -2105,7 +2447,9 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, if (cj == NULL) { /* Do anything? */ - if (ci->stars.count == 0 || !cell_is_active_stars(ci, e)) return; + if (!cell_is_active_stars(ci, e) || ci->hydro.count == 0 || + ci->stars.count == 0) + return; /* Recurse? */ if (cell_can_recurse_in_self_stars_task(ci)) { @@ -2133,7 +2477,10 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, /* Should we even bother? */ if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return; - if (ci->stars.count == 0 || cj->stars.count == 0) return; + + int should_do = ci->stars.count != 0 && cj->hydro.count != 0; + should_do |= cj->stars.count != 0 && ci->hydro.count != 0; + if (!should_do) return; /* Get the orientation of the pair. */ double shift[3]; @@ -2418,23 +2765,43 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, } /* Otherwise, activate the sorts and drifts. */ - else if (cell_is_active_stars(ci, e) || cell_is_active_stars(cj, e)) { + else { - /* We are going to interact this pair, so store some values. */ - atomic_or(&ci->hydro.requires_sorts, 1 << sid); - atomic_or(&cj->hydro.requires_sorts, 1 << sid); - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; - cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + if (cell_is_active_stars(ci, e) && cj->hydro.count != 0 && + ci->stars.count != 0) { + /* We are going to interact this pair, so store some values. */ + atomic_or(&cj->hydro.requires_sorts, 1 << sid); + atomic_or(&ci->stars.requires_sorts, 1 << sid); - /* Activate the drifts if the cells are local. */ - if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s); - if (ci->nodeID == engine_rank) cell_activate_drift_spart(ci, s); - if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s); - if (cj->nodeID == engine_rank) cell_activate_drift_spart(cj, s); + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; - /* Do we need to sort the cells? */ - cell_activate_sorts(ci, sid, s); - cell_activate_sorts(cj, sid, s); + /* Activate the drifts if the cells are local. */ + if (ci->nodeID == engine_rank) cell_activate_drift_spart(ci, s); + if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s); + + /* Do we need to sort the cells? */ + cell_activate_hydro_sorts(cj, sid, s); + cell_activate_stars_sorts(ci, sid, s); + } + + if (cell_is_active_stars(cj, e) && ci->hydro.count != 0 && + cj->stars.count != 0) { + /* We are going to interact this pair, so store some values. */ + atomic_or(&cj->stars.requires_sorts, 1 << sid); + atomic_or(&ci->hydro.requires_sorts, 1 << sid); + + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; + + /* Activate the drifts if the cells are local. */ + if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s); + if (cj->nodeID == engine_rank) cell_activate_drift_spart(cj, s); + + /* Do we need to sort the cells? */ + cell_activate_hydro_sorts(ci, sid, s); + cell_activate_stars_sorts(cj, sid, s); + } } } /* Otherwise, pair interation */ } @@ -2612,6 +2979,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { struct engine *e = s->space->e; const int nodeID = e->nodeID; + const int with_limiter = (e->policy & engine_policy_limiter); int rebuild = 0; /* Un-skip the density tasks involved with this cell. */ @@ -2637,6 +3005,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* Activate hydro drift */ if (t->type == task_type_self) { if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + if (ci_nodeID == nodeID && with_limiter) cell_activate_limiter(ci, s); } /* Set the correct sorting flags and activate hydro drifts */ @@ -2651,9 +3020,13 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + /* Activate the limiter tasks. */ + if (ci_nodeID == nodeID && with_limiter) cell_activate_limiter(ci, s); + if (cj_nodeID == nodeID && with_limiter) cell_activate_limiter(cj, s); + /* Check the sorts and activate them if needed. */ - cell_activate_sorts(ci, t->flags, s); - cell_activate_sorts(cj, t->flags, s); + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); } /* Store current values of dx_max and h_max. */ else if (t->type == task_type_sub_pair || t->type == task_type_sub_self) { @@ -2666,7 +3039,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* Check whether there was too much particle motion, i.e. the cell neighbour conditions were violated. */ - if (cell_need_rebuild_for_pair(ci, cj)) rebuild = 1; + if (cell_need_rebuild_for_hydro_pair(ci, cj)) rebuild = 1; #ifdef WITH_MPI /* Activate the send/recv tasks. */ @@ -2685,7 +3058,11 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the foreign cell is active, we want its ti_end values. */ - if (ci_active) scheduler_activate(s, ci->mpi.recv_ti); + if (ci_active || with_limiter) scheduler_activate(s, ci->mpi.recv_ti); + + if (with_limiter) scheduler_activate(s, ci->mpi.limiter.recv); + if (with_limiter) + scheduler_activate_send(s, cj->mpi.limiter.send, ci->nodeID); /* Is the foreign cell active and will need stuff from us? */ if (ci_active) { @@ -2695,6 +3072,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* Drift the cell which will be sent; note that not all sent particles will be drifted, only those that are needed. */ cell_activate_drift_part(cj, s); + if (with_limiter) cell_activate_limiter(cj, s); /* If the local cell is also active, more stuff will be needed. */ if (cj_active) { @@ -2707,7 +3085,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the local cell is active, send its ti_end values. */ - if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + if (cj_active || with_limiter) + scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); } else if (cj_nodeID != nodeID) { @@ -2724,7 +3103,11 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the foreign cell is active, we want its ti_end values. */ - if (cj_active) scheduler_activate(s, cj->mpi.recv_ti); + if (cj_active || with_limiter) scheduler_activate(s, cj->mpi.recv_ti); + + if (with_limiter) scheduler_activate(s, cj->mpi.limiter.recv); + if (with_limiter) + scheduler_activate_send(s, ci->mpi.limiter.send, cj->nodeID); /* Is the foreign cell active and will need stuff from us? */ if (cj_active) { @@ -2734,6 +3117,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* Drift the cell which will be sent; note that not all sent particles will be drifted, only those that are needed. */ cell_activate_drift_part(ci, s); + if (with_limiter) cell_activate_limiter(ci, s); /* If the local cell is also active, more stuff will be needed. */ if (ci_active) { @@ -2747,7 +3131,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the local cell is active, send its ti_end values. */ - if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + if (ci_active || with_limiter) + scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); } #endif } @@ -2760,6 +3145,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { scheduler_activate(s, l->t); for (struct link *l = c->hydro.force; l != NULL; l = l->next) scheduler_activate(s, l->t); + for (struct link *l = c->hydro.limiter; l != NULL; l = l->next) + scheduler_activate(s, l->t); if (c->hydro.extra_ghost != NULL) scheduler_activate(s, c->hydro.extra_ghost); @@ -2773,7 +3160,6 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { if (c->hydro.cooling != NULL) scheduler_activate(s, c->hydro.cooling); if (c->hydro.star_formation != NULL) scheduler_activate(s, c->hydro.star_formation); - if (c->sourceterms != NULL) scheduler_activate(s, c->sourceterms); if (c->logger != NULL) scheduler_activate(s, c->logger); } @@ -2946,6 +3332,7 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { * @return 1 If the space needs rebuilding. 0 otherwise. */ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { + struct engine *e = s->space->e; const int nodeID = e->nodeID; int rebuild = 0; @@ -2965,25 +3352,48 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { /* Activate drifts */ if (t->type == task_type_self) { - if (ci->nodeID == nodeID) cell_activate_drift_part(ci, s); - if (ci->nodeID == nodeID) cell_activate_drift_gpart(ci, s); + if (ci->nodeID == nodeID) { + cell_activate_drift_part(ci, s); + cell_activate_drift_spart(ci, s); + } } /* Set the correct sorting flags and activate hydro drifts */ else if (t->type == task_type_pair) { - /* Store some values. */ - atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + /* Do ci */ + /* stars for ci */ + atomic_or(&ci->stars.requires_sorts, 1 << t->flags); + ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; + + /* hydro for cj */ atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; /* Activate the drift tasks. */ - if (ci->nodeID == nodeID) cell_activate_drift_part(ci, s); + if (ci->nodeID == nodeID) cell_activate_drift_spart(ci, s); if (cj->nodeID == nodeID) cell_activate_drift_part(cj, s); /* Check the sorts and activate them if needed. */ - cell_activate_sorts(ci, t->flags, s); - cell_activate_sorts(cj, t->flags, s); + cell_activate_stars_sorts(ci, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); + + /* Do cj */ + /* hydro for ci */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + + /* stars for cj */ + atomic_or(&cj->stars.requires_sorts, 1 << t->flags); + cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; + + /* Activate the drift tasks. */ + if (cj->nodeID == nodeID) cell_activate_drift_spart(cj, s); + if (ci->nodeID == nodeID) cell_activate_drift_part(ci, s); + + /* Check the sorts and activate them if needed. */ + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_stars_sorts(cj, t->flags, s); + } /* Store current values of dx_max and h_max. */ else if (t->type == task_type_sub_pair || t->type == task_type_sub_self) { @@ -2996,7 +3406,7 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { /* Check whether there was too much particle motion, i.e. the cell neighbour conditions were violated. */ - if (cell_need_rebuild_for_pair(ci, cj)) rebuild = 1; + if (cell_need_rebuild_for_stars_pair(ci, cj)) rebuild = 1; #ifdef WITH_MPI error("MPI with stars not implemented"); @@ -3083,6 +3493,10 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { /* Unskip all the other task types. */ if (c->nodeID == nodeID && cell_is_active_stars(c, e)) { + /* Un-skip the feedback tasks involved with this cell. */ + for (struct link *l = c->stars.feedback; l != NULL; l = l->next) + scheduler_activate(s, l->t); + if (c->stars.ghost_in != NULL) scheduler_activate(s, c->stars.ghost_in); if (c->stars.ghost_out != NULL) scheduler_activate(s, c->stars.ghost_out); if (c->stars.ghost != NULL) scheduler_activate(s, c->stars.ghost); @@ -3173,7 +3587,7 @@ void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data) { /* All top-level cells get an MPI tag. */ #ifdef WITH_MPI - if (c->mpi.tag < 0 && c->mpi.sendto) cell_tag(c); + cell_ensure_tagged(c); #endif /* Super-pointer for hydro */ @@ -3224,7 +3638,11 @@ int cell_has_tasks(struct cell *c) { */ void cell_drift_part(struct cell *c, const struct engine *e, int force) { + const int periodic = e->s->periodic; + const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; + const int with_cosmology = (e->policy & engine_policy_cosmology); const float hydro_h_max = e->hydro_properties->h_max; + const float hydro_h_min = e->hydro_properties->h_min; const integertime_t ti_old_part = c->hydro.ti_old_part; const integertime_t ti_current = e->ti_current; struct part *const parts = c->hydro.parts; @@ -3245,6 +3663,21 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { if (ti_current < ti_old_part) error("Attempt to drift to the past"); #endif + /* Early abort? */ + if (c->hydro.count == 0) { + + /* Clear the drift flags. */ + c->hydro.do_drift = 0; + c->hydro.do_sub_drift = 0; + + /* Update the time of the last drift */ + c->hydro.ti_old_part = ti_current; + + return; + } + + /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Are we not in a leaf ? */ if (c->split && (force || c->hydro.do_sub_drift)) { @@ -3275,7 +3708,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift, dt_kick_grav, dt_kick_hydro, dt_therm; - if (e->policy & engine_policy_cosmology) { + if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_part, ti_current); dt_kick_grav = @@ -3306,6 +3739,11 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { drift_part(p, xp, dt_drift, dt_kick_hydro, dt_kick_grav, dt_therm, ti_old_part, ti_current); + /* Update the tracers properties */ + tracers_after_drift(p, xp, e->internal_units, e->physical_constants, + with_cosmology, e->cosmology, e->hydro_properties, + e->cooling_func, e->time); + #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(xp->v_full[0] * dt_drift) > e->s->dim[0] || @@ -3315,38 +3753,31 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { } #endif -#ifdef PLANETARY_SPH - /* Remove particles that cross the non-periodic box edge */ - if (!(e->s->periodic)) { - for (int i = 0; i < 3; i++) { - if ((p->x[i] - xp->v_full[i] * dt_drift > e->s->dim[i]) || - (p->x[i] - xp->v_full[i] * dt_drift < 0.f) || - ((p->mass != 0.f) && ((p->x[i] < 0.01f * e->s->dim[i]) || - (p->x[i] > 0.99f * e->s->dim[i])))) { - /* (TEMPORARY) Crudely stop the particle manually */ - message( - "Particle %lld hit a box edge. \n" - " pos=%.4e %.4e %.4e vel=%.2e %.2e %.2e", - p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2]); - for (int j = 0; j < 3; j++) { - p->v[j] = 0.f; - p->gpart->v_full[j] = 0.f; - xp->v_full[j] = 0.f; - } - p->h = hydro_h_max; - p->time_bin = time_bin_inhibited; - p->gpart->time_bin = time_bin_inhibited; - hydro_part_has_no_neighbours(p, xp, e->cosmology); - p->mass = 0.f; - p->gpart->mass = 0.f; - break; - } + /* In non-periodic BC runs, remove particles that crossed the border */ + if (!periodic) { + + /* Did the particle leave the box? */ + if ((p->x[0] > dim[0]) || (p->x[0] < 0.) || // x + (p->x[1] > dim[1]) || (p->x[1] < 0.) || // y + (p->x[2] > dim[2]) || (p->x[2] < 0.)) { // z + + /* One last action before death? */ + hydro_remove_part(p, xp); + + /* Remove the particle entirely */ + struct gpart *gp = p->gpart; + cell_remove_part(e, c, p, xp); + + /* and it's gravity friend */ + if (gp != NULL) cell_remove_gpart(e, c, gp); + + continue; } } -#endif /* Limit h to within the allowed range */ p->h = min(p->h, hydro_h_max); + p->h = max(p->h, hydro_h_min); /* Compute (square of) motion since last cell construction */ const float dx2 = xp->x_diff[0] * xp->x_diff[0] + @@ -3365,6 +3796,9 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { if (part_is_active(p, e)) { hydro_init_part(p, &e->s->hs); chemistry_init_part(p, e->chemistry); + tracers_after_init(p, xp, e->internal_units, e->physical_constants, + with_cosmology, e->cosmology, e->hydro_properties, + e->cooling_func, e->time); } } @@ -3395,6 +3829,9 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { */ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { + const int periodic = e->s->periodic; + const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; + const int with_cosmology = (e->policy & engine_policy_cosmology); const float stars_h_max = e->stars_properties->h_max; const integertime_t ti_old_gpart = c->grav.ti_old_part; const integertime_t ti_current = e->ti_current; @@ -3402,6 +3839,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { struct spart *const sparts = c->stars.parts; float dx_max = 0.f, dx2_max = 0.f; + float dx_max_sort = 0.0f, dx2_max_sort = 0.f; float cell_h_max = 0.f; /* Drift irrespective of cell flags? */ @@ -3415,6 +3853,21 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { if (ti_current < ti_old_gpart) error("Attempt to drift to the past"); #endif + /* Early abort? */ + if (c->grav.count == 0) { + + /* Clear the drift flags. */ + c->grav.do_drift = 0; + c->grav.do_sub_drift = 0; + + /* Update the time of the last drift */ + c->grav.ti_old_part = ti_current; + + return; + } + + /* Ok, we have some particles somewhere in the hierarchy to drift */ + /* Are we not in a leaf ? */ if (c->split && (force || c->grav.do_sub_drift)) { @@ -3428,6 +3881,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { /* Update */ dx_max = max(dx_max, cp->stars.dx_max_part); + dx_max_sort = max(dx_max_sort, cp->stars.dx_max_sort); cell_h_max = max(cell_h_max, cp->stars.h_max); } } @@ -3435,6 +3889,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { /* Store the values */ c->stars.h_max = cell_h_max; c->stars.dx_max_part = dx_max; + c->stars.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->grav.ti_old_part = ti_current; @@ -3443,11 +3898,12 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift; - if (e->policy & engine_policy_cosmology) + if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_gpart, ti_current); - else + } else { dt_drift = (ti_current - ti_old_gpart) * e->time_base; + } /* Loop over all the g-particles in the cell */ const size_t nr_gparts = c->grav.count; @@ -3471,25 +3927,20 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { } #endif -#ifdef PLANETARY_SPH - /* Remove particles that cross the non-periodic box edge */ - if (!(e->s->periodic)) { - for (int i = 0; i < 3; i++) { - if ((gp->x[i] - gp->v_full[i] * dt_drift > e->s->dim[i]) || - (gp->x[i] - gp->v_full[i] * dt_drift < 0.f) || - ((gp->mass != 0.f) && ((gp->x[i] < 0.01f * e->s->dim[i]) || - (gp->x[i] > 0.99f * e->s->dim[i])))) { - /* (TEMPORARY) Crudely stop the particle manually */ - for (int j = 0; j < 3; j++) { - gp->v_full[j] = 0.f; - } - gp->time_bin = time_bin_inhibited; - gp->mass = 0.f; - break; - } + /* In non-periodic BC runs, remove particles that crossed the border */ + if (!periodic) { + + /* Did the particle leave the box? */ + if ((gp->x[0] > dim[0]) || (gp->x[0] < 0.) || // x + (gp->x[1] > dim[1]) || (gp->x[1] < 0.) || // y + (gp->x[2] > dim[2]) || (gp->x[2] < 0.)) { // z + + /* Remove the particle entirely */ + if (gp->type == swift_type_dark_matter) cell_remove_gpart(e, c, gp); + + continue; } } -#endif /* Init gravity force fields. */ if (gpart_is_active(gp, e)) { @@ -3519,6 +3970,25 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { } #endif + /* In non-periodic BC runs, remove particles that crossed the border */ + if (!periodic) { + + /* Did the particle leave the box? */ + if ((sp->x[0] > dim[0]) || (sp->x[0] < 0.) || // x + (sp->x[1] > dim[1]) || (sp->x[1] < 0.) || // y + (sp->x[2] > dim[2]) || (sp->x[2] < 0.)) { // z + + /* Remove the particle entirely */ + struct gpart *gp = sp->gpart; + cell_remove_spart(e, c, sp); + + /* and it's gravity friend */ + cell_remove_gpart(e, c, gp); + + continue; + } + } + /* Limit h to within the allowed range */ sp->h = min(sp->h, stars_h_max); @@ -3528,6 +3998,12 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { sp->x_diff[2] * sp->x_diff[2]; dx2_max = max(dx2_max, dx2); + const float dx2_sort = sp->x_diff_sort[0] * sp->x_diff_sort[0] + + sp->x_diff_sort[1] * sp->x_diff_sort[1] + + sp->x_diff_sort[2] * sp->x_diff_sort[2]; + + dx2_max_sort = max(dx2_max_sort, dx2_sort); + /* Maximal smoothing length */ cell_h_max = max(cell_h_max, sp->h); @@ -3535,10 +4011,12 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { /* Now, get the maximal particle motion from its square */ dx_max = sqrtf(dx2_max); + dx_max_sort = sqrtf(dx2_max_sort); /* Store the values */ c->stars.h_max = cell_h_max; c->stars.dx_max_part = dx_max; + c->stars.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->grav.ti_old_part = ti_current; @@ -3627,7 +4105,8 @@ void cell_drift_multipole(struct cell *c, const struct engine *e) { void cell_check_timesteps(struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS - if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && c->nr_tasks > 0) + if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && + c->stars.ti_end_min == 0 && c->nr_tasks > 0) error("Cell without assigned time-step"); if (c->split) { @@ -3645,6 +4124,190 @@ void cell_check_timesteps(struct cell *c) { #endif } +void cell_check_spart_pos(const struct cell *c, + const struct spart *global_sparts) { + +#ifdef SWIFT_DEBUG_CHECKS + + /* Recurse */ + if (c->split) { + for (int k = 0; k < 8; ++k) + if (c->progeny[k] != NULL) + cell_check_spart_pos(c->progeny[k], global_sparts); + } + + struct spart *sparts = c->stars.parts; + const int count = c->stars.count; + for (int i = 0; i < count; ++i) { + + const struct spart *sp = &sparts[i]; + if ((sp->x[0] < c->loc[0] / space_stretch) || + (sp->x[1] < c->loc[1] / space_stretch) || + (sp->x[2] < c->loc[2] / space_stretch) || + (sp->x[0] >= (c->loc[0] + c->width[0]) * space_stretch) || + (sp->x[1] >= (c->loc[1] + c->width[1]) * space_stretch) || + (sp->x[2] >= (c->loc[2] + c->width[2]) * space_stretch)) + error("spart not in its cell!"); + + if (sp->time_bin != time_bin_not_created && + sp->time_bin != time_bin_inhibited) { + + const struct gpart *gp = sp->gpart; + if (gp == NULL && sp->time_bin != time_bin_not_created) + error("Unlinked spart!"); + + if (&global_sparts[-gp->id_or_neg_offset] != sp) + error("Incorrectly linked spart!"); + } + } + +#else + error("Calling a degugging function outside debugging mode."); +#endif +} + +/** + * @brief Recursively update the pointer and counter for #spart after the + * addition of a new particle. + * + * @param c The cell we are working on. + * @param progeny_list The list of the progeny index at each level for the + * leaf-cell where the particle was added. + * @param main_branch Are we in a cell directly above the leaf where the new + * particle was added? + */ +void cell_recursively_shift_sparts(struct cell *c, + const int progeny_list[space_cell_maxdepth], + const int main_branch) { + if (c->split) { + + /* No need to recurse in progenies located before the insestion point */ + const int first_progeny = main_branch ? progeny_list[(int)c->depth] : 0; + + for (int k = first_progeny; k < 8; ++k) { + + if (c->progeny[k] != NULL) + cell_recursively_shift_sparts(c->progeny[k], progeny_list, + main_branch && (k == first_progeny)); + } + } + + /* When directly above the leaf with the new particle: increase the particle + * count */ + /* When after the leaf with the new particle: shift by one position */ + if (main_branch) + c->stars.count++; + else + c->stars.parts++; +} + +/** + * @brief "Add" a #spart in a given #cell. + * + * This function will a a #spart at the start of the current cell's array by + * shifting all the #spart in the top-level cell by one position. All the + * pointers and cell counts are updated accordingly. + * + * @param e The #engine. + * @param c The leaf-cell in which to add the #spart. + * + * @return A pointer to the newly added #spart. The spart has a been zeroed and + * given a position within the cell as well as set to the minimal active time + * bin. + */ +struct spart *cell_add_spart(struct engine *e, struct cell *const c) { + + /* Perform some basic consitency checks */ + if (c->nodeID != engine_rank) error("Adding spart on a foreign node"); + if (c->grav.ti_old_part != e->ti_current) error("Undrifted cell!"); + if (c->split) error("Addition of spart performed above the leaf level"); + + /* Progeny number at each level */ + int progeny[space_cell_maxdepth]; +#ifdef SWIFT_DEBUG_CHECKS + for (int i = 0; i < space_cell_maxdepth; ++i) progeny[i] = -1; +#endif + + /* Get the top-level this leaf cell is in and compute the progeny indices at + each level */ + struct cell *top = c; + while (top->parent != NULL) { + for (int k = 0; k < 8; ++k) { + if (top->parent->progeny[k] == top) { + progeny[(int)top->parent->depth] = k; + } + } + top = top->parent; + } + + /* Are there any extra particles left? */ + if (top->stars.count == top->stars.count_total - 1) { + message("We ran out of star particles!"); + atomic_inc(&e->forcerebuild); + return NULL; + } + + /* Number of particles to shift in order to get a free space. */ + const size_t n_copy = &top->stars.parts[top->stars.count] - c->stars.parts; + +#ifdef SWIFT_DEBUG_CHECKS + if (c->stars.parts + n_copy > top->stars.parts + top->stars.count) + error("Copying beyond the allowed range"); +#endif + + if (n_copy > 0) { + + // MATTHIEU: This can be improved. We don't need to copy everything, just + // need to swap a few particles. + memmove(&c->stars.parts[1], &c->stars.parts[0], + n_copy * sizeof(struct spart)); + + /* Update the gpart->spart links (shift by 1) */ + for (size_t i = 0; i < n_copy; ++i) { +#ifdef SWIFT_DEBUG_CHECKS + if (c->stars.parts[i + 1].gpart == NULL) { + error("Incorrectly linked spart!"); + } +#endif + c->stars.parts[i + 1].gpart->id_or_neg_offset--; + } + } + + /* Recursively shift all the stars to get a free spot at the start of the + * current cell*/ + cell_recursively_shift_sparts(top, progeny, /* main_branch=*/1); + + /* We now have an empty spart as the first particle in that cell */ + struct spart *sp = &c->stars.parts[0]; + bzero(sp, sizeof(struct spart)); + + /* Give it a decent position */ + sp->x[0] = c->loc[0] + 0.5 * c->width[0]; + sp->x[1] = c->loc[1] + 0.5 * c->width[1]; + sp->x[2] = c->loc[2] + 0.5 * c->width[2]; + + /* Set it to the current time-bin */ + sp->time_bin = e->min_active_bin; + + top = c; + while (top->parent != NULL) { + top->grav.ti_end_min = e->ti_current; + top = top->parent; + } + top->grav.ti_end_min = e->ti_current; + +#ifdef SWIFT_DEBUG_CHECKS + /* Specify it was drifted to this point */ + sp->ti_drift = e->ti_current; +#endif + + /* Register that we used one of the free slots. */ + const size_t one = 1; + atomic_sub(&e->s->nr_extra_sparts, one); + + return sp; +} + /** * @brief "Remove" a gas particle from the calculation. * @@ -3731,15 +4394,21 @@ void cell_remove_spart(const struct engine *e, struct cell *c, * @brief "Remove" a gas particle from the calculation and convert its gpart * friend to a dark matter particle. * + * Note that the #part is not destroyed. The pointer is still valid + * after this call and the properties of the #part are not altered + * apart from the time-bin and #gpart pointer. * The particle is inhibited and will officially be removed at the next rebuild. * * @param e The #engine running on this node. * @param c The #cell from which to remove the particle. * @param p The #part to remove. * @param xp The extended data of the particle to remove. + * + * @return Pointer to the #gpart the #part has become. It carries the + * ID of the #part and has a dark matter type. */ -void cell_convert_part_to_gpart(const struct engine *e, struct cell *c, - struct part *p, struct xpart *xp) { +struct gpart *cell_convert_part_to_gpart(const struct engine *e, struct cell *c, + struct part *p, struct xpart *xp) { /* Quick cross-checks */ if (c->nodeID != e->nodeID) @@ -3764,20 +4433,28 @@ void cell_convert_part_to_gpart(const struct engine *e, struct cell *c, #ifdef SWIFT_DEBUG_CHECKS gp->ti_kick = p->ti_kick; #endif + + return gp; } /** * @brief "Remove" a spart particle from the calculation and convert its gpart * friend to a dark matter particle. * + * Note that the #spart is not destroyed. The pointer is still valid + * after this call and the properties of the #spart are not altered + * apart from the time-bin and #gpart pointer. * The particle is inhibited and will officially be removed at the next rebuild. * * @param e The #engine running on this node. * @param c The #cell from which to remove the particle. * @param sp The #spart to remove. + * + * @return Pointer to the #gpart the #spart has become. It carries the + * ID of the #spart and has a dark matter type. */ -void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c, - struct spart *sp) { +struct gpart *cell_convert_spart_to_gpart(const struct engine *e, + struct cell *c, struct spart *sp) { /* Quick cross-check */ if (c->nodeID != e->nodeID) @@ -3802,6 +4479,210 @@ void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c, #ifdef SWIFT_DEBUG_CHECKS gp->ti_kick = sp->ti_kick; #endif + + return gp; +} + +/** + * @brief "Remove" a #part from a #cell and replace it with a #spart + * connected to the same #gpart. + * + * Note that the #part is not destroyed. The pointer is still valid + * after this call and the properties of the #part are not altered + * apart from the time-bin and #gpart pointer. + * The particle is inhibited and will officially be removed at the next rebuild. + * + * @param e The #engine. + * @param c The #cell from which to remove the #part. + * @param p The #part to remove (must be inside c). + * @param xp The extended data of the #part. + * + * @return A fresh #spart with the same ID, position, velocity and + * time-bin as the original #part. + */ +struct spart *cell_convert_part_to_spart(struct engine *e, struct cell *c, + struct part *p, struct xpart *xp) { + + /* Quick cross-check */ + if (c->nodeID != e->nodeID) + error("Can't remove a particle in a foreign cell."); + + if (p->gpart == NULL) + error("Trying to convert part without gpart friend to star!"); + + /* Create a fresh (empty) spart */ + struct spart *sp = cell_add_spart(e, c); + + /* Did we run out of free spart slots? */ + if (sp == NULL) return NULL; + + /* Destroy the gas particle and get it's gpart friend */ + struct gpart *gp = cell_convert_part_to_gpart(e, c, p, xp); + + /* Assign the ID back */ + sp->id = gp->id_or_neg_offset; + gp->type = swift_type_stars; + + /* Re-link things */ + sp->gpart = gp; + gp->id_or_neg_offset = -(sp - e->s->sparts); + + /* Synchronize clocks */ + gp->time_bin = sp->time_bin; + + /* Synchronize masses, positions and velocities */ + sp->mass = gp->mass; + sp->x[0] = gp->x[0]; + sp->x[1] = gp->x[1]; + sp->x[2] = gp->x[2]; + sp->v[0] = gp->v_full[0]; + sp->v[1] = gp->v_full[1]; + sp->v[2] = gp->v_full[2]; + +#ifdef SWIFT_DEBUG_CHECKS + sp->ti_kick = gp->ti_kick; + gp->ti_drift = sp->ti_drift; +#endif + + /* Set a smoothing length */ + sp->h = max(c->stars.h_max, c->hydro.h_max); + + /* Here comes the Sun! */ + return sp; +} + +/** + * @brief Re-arrange the #part in a top-level cell such that all the extra ones + * for on-the-fly creation are located at the end of the array. + * + * @param c The #cell to sort. + * @param parts_offset The offset between the first #part in the array and the + * first #part in the global array in the space structure (for re-linking). + */ +void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset) { + + struct part *parts = c->hydro.parts; + struct xpart *xparts = c->hydro.xparts; + const int count_real = c->hydro.count; + + if (c->depth != 0 || c->nodeID != engine_rank) + error("This function should only be called on local top-level cells!"); + + int first_not_extra = count_real; + + /* Find extra particles */ + for (int i = 0; i < count_real; ++i) { + if (parts[i].time_bin == time_bin_not_created) { + + /* Find the first non-extra particle after the end of the + real particles */ + while (parts[first_not_extra].time_bin == time_bin_not_created) { + ++first_not_extra; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (first_not_extra >= count_real + space_extra_parts) + error("Looking for extra particles beyond this cell's range!"); +#endif + + /* Swap everything, including g-part pointer */ + memswap(&parts[i], &parts[first_not_extra], sizeof(struct part)); + memswap(&xparts[i], &xparts[first_not_extra], sizeof(struct xpart)); + if (parts[i].gpart) + parts[i].gpart->id_or_neg_offset = -(i + parts_offset); + } + } +} + +/** + * @brief Re-arrange the #spart in a top-level cell such that all the extra ones + * for on-the-fly creation are located at the end of the array. + * + * @param c The #cell to sort. + * @param sparts_offset The offset between the first #spart in the array and the + * first #spart in the global array in the space structure (for re-linking). + */ +void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset) { + + struct spart *sparts = c->stars.parts; + const int count_real = c->stars.count; + + if (c->depth != 0 || c->nodeID != engine_rank) + error("This function should only be called on local top-level cells!"); + + int first_not_extra = count_real; + + /* Find extra particles */ + for (int i = 0; i < count_real; ++i) { + if (sparts[i].time_bin == time_bin_not_created) { + + /* Find the first non-extra particle after the end of the + real particles */ + while (sparts[first_not_extra].time_bin == time_bin_not_created) { + ++first_not_extra; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (first_not_extra >= count_real + space_extra_sparts) + error("Looking for extra particles beyond this cell's range!"); +#endif + + /* Swap everything, including g-part pointer */ + memswap(&sparts[i], &sparts[first_not_extra], sizeof(struct spart)); + if (sparts[i].gpart) + sparts[i].gpart->id_or_neg_offset = -(i + sparts_offset); + sparts[first_not_extra].gpart = NULL; +#ifdef SWIFT_DEBUG_CHECKS + if (sparts[first_not_extra].time_bin != time_bin_not_created) + error("Incorrect swap occured!"); +#endif + } + } +} + +/** + * @brief Re-arrange the #gpart in a top-level cell such that all the extra ones + * for on-the-fly creation are located at the end of the array. + * + * @param c The #cell to sort. + * @param parts The global array of #part (for re-linking). + * @param sparts The global array of #spart (for re-linking). + */ +void cell_reorder_extra_gparts(struct cell *c, struct part *parts, + struct spart *sparts) { + + struct gpart *gparts = c->grav.parts; + const int count_real = c->grav.count; + + if (c->depth != 0 || c->nodeID != engine_rank) + error("This function should only be called on local top-level cells!"); + + int first_not_extra = count_real; + + /* Find extra particles */ + for (int i = 0; i < count_real; ++i) { + if (gparts[i].time_bin == time_bin_not_created) { + + /* Find the first non-extra particle after the end of the + real particles */ + while (gparts[first_not_extra].time_bin == time_bin_not_created) { + ++first_not_extra; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (first_not_extra >= count_real + space_extra_gparts) + error("Looking for extra particles beyond this cell's range!"); +#endif + + /* Swap everything (including pointers) */ + memswap(&gparts[i], &gparts[first_not_extra], sizeof(struct gpart)); + if (gparts[i].type == swift_type_gas) { + parts[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; + } else if (gparts[i].type == swift_type_stars) { + sparts[-gparts[i].id_or_neg_offset].gpart = &gparts[i]; + } + } + } } /** diff --git a/src/cell.h b/src/cell.h index 13ae2462050c7b42365d879258de89235a4af1df..7582dfe24b4b793d2685cb27fc885e3161544c32 100644 --- a/src/cell.h +++ b/src/cell.h @@ -147,6 +147,9 @@ struct pcell { /*! Maximal smoothing length. */ double h_max; + /*! Minimal integer end-of-timestep in this cell for stars tasks */ + integertime_t ti_end_min; + } stars; /*! Maximal depth in that part of the tree */ @@ -198,6 +201,9 @@ struct pcell_step { /*! Maximal distance any #part has travelled since last rebuild */ float dx_max_part; + /*! Minimal integer end-of-timestep in this cell (stars) */ + integertime_t ti_end_min; + } stars; }; @@ -214,12 +220,12 @@ struct cell { /*! The cell dimensions. */ double width[3]; - /*! Linking pointer for "memory management". */ - struct cell *next; - /*! Pointers to the next level of cells. */ struct cell *progeny[8]; + /*! Linking pointer for "memory management". */ + struct cell *next; + /*! Parent cell. */ struct cell *parent; @@ -242,18 +248,48 @@ struct cell { * pair/self tasks */ struct cell *super; - /*! Last (integer) time the cell's part were drifted forward in time. */ - integertime_t ti_old_part; + /*! The task computing this cell's sorts. */ + struct task *sorts; - /*! Maximum part movement in this cell since last construction. */ - float dx_max_part; + /*! The drift task for parts */ + struct task *drift; - /*! Maximum particle movement in this cell since the last sort. */ - float dx_max_sort; + /*! Linked list of the tasks computing this cell's hydro density. */ + struct link *density; + + /* Linked list of the tasks computing this cell's hydro gradients. */ + struct link *gradient; + + /*! Linked list of the tasks computing this cell's hydro forces. */ + struct link *force; + + /*! Linked list of the tasks computing this cell's limiter. */ + struct link *limiter; + + /*! Dependency implicit task for the ghost (in->ghost->out)*/ + struct task *ghost_in; + + /*! Dependency implicit task for the ghost (in->ghost->out)*/ + struct task *ghost_out; + + /*! The ghost task itself */ + struct task *ghost; + + /*! The extra ghost task for complex hydro schemes */ + struct task *extra_ghost; + + /*! Task for cooling */ + struct task *cooling; + + /*! Task for star formation */ + struct task *star_formation; /*! Max smoothing length in this cell. */ double h_max; + /*! Last (integer) time the cell's part were drifted forward in time. */ + integertime_t ti_old_part; + /*! Minimum end of (integer) time step in this cell for hydro tasks. */ integertime_t ti_end_min; @@ -264,20 +300,14 @@ struct cell { */ integertime_t ti_beg_max; - /*! Nr of #part in this cell. */ - int count; - /*! Spin lock for various uses (#part case). */ swift_lock_type lock; - /*! Number of #part updated in this cell. */ - int updated; - - /*! Number of #part inhibited in this cell. */ - int inhibited; + /*! Maximum part movement in this cell since last construction. */ + float dx_max_part; - /*! Is the #part data of this cell being used in a sub-cell? */ - int hold; + /*! Maximum particle movement in this cell since the last sort. */ + float dx_max_sort; /*! Values of h_max before the drifts, used for sub-cell tasks. */ float h_max_old; @@ -288,12 +318,30 @@ struct cell { /*! Values of dx_max_sort before the drifts, used for sub-cell tasks. */ float dx_max_sort_old; + /*! Nr of #part in this cell. */ + int count; + + /*! Nr of #part this cell can hold after addition of new #part. */ + int count_total; + + /*! Number of #part updated in this cell. */ + int updated; + + /*! Number of #part inhibited in this cell. */ + int inhibited; + + /*! Is the #part data of this cell being used in a sub-cell? */ + int hold; + /*! Bit mask of sort directions that will be needed in the next timestep. */ unsigned int requires_sorts; /*! Bit mask of sorts that need to be computed for this cell. */ unsigned int do_sort; + /*! Bit-mask indicating the sorted directions */ + unsigned int sorted; + /*! Does this cell need to be drifted (hydro)? */ char do_drift; @@ -303,41 +351,11 @@ struct cell { /*! Do any of this cell's sub-cells need to be sorted? */ char do_sub_sort; - /*! Bit-mask indicating the sorted directions */ - unsigned int sorted; - - /*! The task computing this cell's sorts. */ - struct task *sorts; - - /*! The drift task for parts */ - struct task *drift; - - /*! Linked list of the tasks computing this cell's hydro density. */ - struct link *density; - - /* Linked list of the tasks computing this cell's hydro gradients. */ - struct link *gradient; - - /*! Linked list of the tasks computing this cell's hydro forces. */ - struct link *force; - - /*! Dependency implicit task for the ghost (in->ghost->out)*/ - struct task *ghost_in; - - /*! Dependency implicit task for the ghost (in->ghost->out)*/ - struct task *ghost_out; + /*! Does this cell need to be limited? */ + char do_limiter; - /*! The ghost task itself */ - struct task *ghost; - - /*! The extra ghost task for complex hydro schemes */ - struct task *extra_ghost; - - /*! Task for cooling */ - struct task *cooling; - - /*! Task for star formation */ - struct task *star_formation; + /*! Do any of this cell's sub-cells need to be limited? */ + char do_sub_limiter; #ifdef SWIFT_DEBUG_CHECKS @@ -361,6 +379,36 @@ struct cell { * tasks */ struct cell *super; + /*! The drift task for gparts */ + struct task *drift; + + /*! Implicit task (going up- and down the tree) for the #gpart drifts */ + struct task *drift_out; + + /*! Linked list of the tasks computing this cell's gravity forces. */ + struct link *grav; + + /*! Linked list of the tasks computing this cell's gravity M-M forces. */ + struct link *mm; + + /*! The multipole initialistation task */ + struct task *init; + + /*! Implicit task for the gravity initialisation */ + struct task *init_out; + + /*! Task computing long range non-periodic gravity interactions */ + struct task *long_range; + + /*! Implicit task for the down propagation */ + struct task *down_in; + + /*! Task propagating the mesh forces to the particles */ + struct task *mesh; + + /*! Task propagating the multipole to the particles */ + struct task *down; + /*! Minimum end of (integer) time step in this cell for gravity tasks. */ integertime_t ti_end_min; @@ -377,15 +425,18 @@ struct cell { /*! Last (integer) time the cell's multipole was drifted forward in time. */ integertime_t ti_old_multipole; - /*! Nr of #gpart in this cell. */ - int count; - /*! Spin lock for various uses (#gpart case). */ swift_lock_type plock; /*! Spin lock for various uses (#multipole case). */ swift_lock_type mlock; + /*! Nr of #gpart in this cell. */ + int count; + + /*! Nr of #gpart this cell can hold after addition of new #gpart. */ + int count_total; + /*! Number of #gpart updated in this cell. */ int updated; @@ -398,58 +449,52 @@ struct cell { /*! Is the #multipole data of this cell being used in a sub-cell? */ int mhold; + /*! Number of M-M tasks that are associated with this cell. */ + short int nr_mm_tasks; + /*! Does this cell need to be drifted (gravity)? */ char do_drift; /*! Do any of this cell's sub-cells need to be drifted (gravity)? */ char do_sub_drift; - /*! The drift task for gparts */ - struct task *drift; - - /*! Implicit task (going up- and down the tree) for the #gpart drifts */ - struct task *drift_out; - - /*! Linked list of the tasks computing this cell's gravity forces. */ - struct link *grav; - - /*! Linked list of the tasks computing this cell's gravity M-M forces. */ - struct link *mm; + } grav; - /*! The multipole initialistation task */ - struct task *init; + /*! Stars variables */ + struct { - /*! Implicit task for the gravity initialisation */ - struct task *init_out; + /*! Pointer to the #spart data. */ + struct spart *parts; - /*! Task computing long range non-periodic gravity interactions */ - struct task *long_range; + /*! Dependency implicit task for the star ghost (in->ghost->out)*/ + struct task *ghost_in; - /*! Implicit task for the down propagation */ - struct task *down_in; + /*! Dependency implicit task for the star ghost (in->ghost->out)*/ + struct task *ghost_out; - /*! Task propagating the mesh forces to the particles */ - struct task *mesh; + /*! The star ghost task itself */ + struct task *ghost; - /*! Task propagating the multipole to the particles */ - struct task *down; + /*! Linked list of the tasks computing this cell's star density. */ + struct link *density; - /*! Number of M-M tasks that are associated with this cell. */ - short int nr_mm_tasks; + /*! Linked list of the tasks computing this cell's star feedback. */ + struct link *feedback; - } grav; + /*! The task computing this cell's sorts. */ + struct task *sorts; - /*! Stars variables */ - struct { + /*! Max smoothing length in this cell. */ + double h_max; - /*! Pointer to the #spart data. */ - struct spart *parts; + /*! Spin lock for various uses (#spart case). */ + swift_lock_type lock; /*! Nr of #spart in this cell. */ int count; - /*! Max smoothing length in this cell. */ - double h_max; + /*! Nr of #spart this cell can hold after addition of new #spart. */ + int count_total; /*! Values of h_max before the drifts, used for sub-cell tasks. */ float h_max_old; @@ -460,17 +505,29 @@ struct cell { /*! Values of dx_max before the drifts, used for sub-cell tasks. */ float dx_max_part_old; - /*! Dependency implicit task for the star ghost (in->ghost->out)*/ - struct task *ghost_in; + /*! Maximum particle movement in this cell since the last sort. */ + float dx_max_sort; - /*! Dependency implicit task for the star ghost (in->ghost->out)*/ - struct task *ghost_out; + /*! Values of dx_max_sort before the drifts, used for sub-cell tasks. */ + float dx_max_sort_old; - /*! The star ghost task itself */ - struct task *ghost; + /*! Bit mask of sort directions that will be needed in the next timestep. */ + unsigned int requires_sorts; - /*! Linked list of the tasks computing this cell's star density. */ - struct link *density; + /*! Pointer for the sorted indices. */ + struct entry *sort[13]; + + /*! Bit-mask indicating the sorted directions */ + unsigned int sorted; + + /*! Bit mask of sorts that need to be computed for this cell. */ + unsigned int do_sort; + + /*! Do any of this cell's sub-cells need to be sorted? */ + char do_sub_sort; + + /*! Maximum end of (integer) time step in this cell for gravity tasks. */ + integertime_t ti_end_min; /*! Number of #spart updated in this cell. */ int updated; @@ -481,8 +538,10 @@ struct cell { /*! Is the #spart data of this cell being used in a sub-cell? */ int hold; - /*! Spin lock for various uses (#spart case). */ - swift_lock_type lock; +#ifdef SWIFT_DEBUG_CHECKS + /*! Last (integer) time the cell's sort arrays were updated. */ + integertime_t ti_sort; +#endif } stars; @@ -520,6 +579,15 @@ struct cell { struct link *send; } grav; + struct { + + /* Task receiving gpart data. */ + struct task *recv; + + /* Linked list for sending gpart data. */ + struct link *send; + } limiter; + /* Task receiving data (time-step). */ struct task *recv_ti; @@ -553,8 +621,8 @@ struct cell { /*! The task to compute time-steps */ struct task *timestep; - /*! Task for source terms */ - struct task *sourceterms; + /*! The task to limit the time-step of inactive particles */ + struct task *timestep_limiter; /*! The logger task */ struct task *logger; @@ -623,6 +691,10 @@ int cell_getsize(struct cell *c); int cell_link_parts(struct cell *c, struct part *parts); int cell_link_gparts(struct cell *c, struct gpart *gparts); int cell_link_sparts(struct cell *c, struct spart *sparts); +int cell_link_foreign_parts(struct cell *c, struct part *parts); +int cell_link_foreign_gparts(struct cell *c, struct gpart *gparts); +int cell_count_parts_for_tasks(const struct cell *c); +int cell_count_gparts_for_tasks(const struct cell *c); void cell_clean_links(struct cell *c, void *data); void cell_make_multipoles(struct cell *c, integertime_t ti_current); void cell_check_multipole(struct cell *c); @@ -652,9 +724,15 @@ void cell_activate_subcell_external_grav_tasks(struct cell *ci, struct scheduler *s); void cell_activate_drift_part(struct cell *c, struct scheduler *s); void cell_activate_drift_gpart(struct cell *c, struct scheduler *s); -void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s); +void cell_activate_drift_spart(struct cell *c, struct scheduler *s); +void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s); +void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s); +void cell_activate_limiter(struct cell *c, struct scheduler *s); void cell_clear_drift_flags(struct cell *c, void *data); +void cell_clear_limiter_flags(struct cell *c, void *data); void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data); +void cell_check_spart_pos(const struct cell *c, + const struct spart *global_sparts); int cell_has_tasks(struct cell *c); void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, struct xpart *xp); @@ -662,15 +740,84 @@ void cell_remove_gpart(const struct engine *e, struct cell *c, struct gpart *gp); void cell_remove_spart(const struct engine *e, struct cell *c, struct spart *sp); -void cell_convert_part_to_gpart(const struct engine *e, struct cell *c, - struct part *p, struct xpart *xp); -void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c, - struct spart *sp); +struct spart *cell_add_spart(struct engine *e, struct cell *c); +struct gpart *cell_convert_part_to_gpart(const struct engine *e, struct cell *c, + struct part *p, struct xpart *xp); +struct gpart *cell_convert_spart_to_gpart(const struct engine *e, + struct cell *c, struct spart *sp); +struct spart *cell_convert_part_to_spart(struct engine *e, struct cell *c, + struct part *p, struct xpart *xp); +void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset); +void cell_reorder_extra_gparts(struct cell *c, struct part *parts, + struct spart *sparts); +void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset); int cell_can_use_pair_mm(const struct cell *ci, const struct cell *cj, const struct engine *e, const struct space *s); int cell_can_use_pair_mm_rebuild(const struct cell *ci, const struct cell *cj, const struct engine *e, const struct space *s); +/** + * @brief Compute the square of the minimal distance between any two points in + * two cells of the same size + * + * @param ci The first #cell. + * @param cj The second #cell. + * @param periodic Are we using periodic BCs? + * @param dim The dimensions of the simulation volume + */ +__attribute__((always_inline)) INLINE static double cell_min_dist2_same_size( + const struct cell *restrict ci, const struct cell *restrict cj, + const int periodic, const double dim[3]) { + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->width[0] != cj->width[0]) error("Cells of different size!"); + if (ci->width[1] != cj->width[1]) error("Cells of different size!"); + if (ci->width[2] != cj->width[2]) error("Cells of different size!"); +#endif + + const double cix_min = ci->loc[0]; + const double ciy_min = ci->loc[1]; + const double ciz_min = ci->loc[2]; + const double cjx_min = cj->loc[0]; + const double cjy_min = cj->loc[1]; + const double cjz_min = cj->loc[2]; + + const double cix_max = ci->loc[0] + ci->width[0]; + const double ciy_max = ci->loc[1] + ci->width[1]; + const double ciz_max = ci->loc[2] + ci->width[2]; + const double cjx_max = cj->loc[0] + cj->width[0]; + const double cjy_max = cj->loc[1] + cj->width[1]; + const double cjz_max = cj->loc[2] + cj->width[2]; + + if (periodic) { + + const double dx = min4(fabs(nearest(cix_min - cjx_min, dim[0])), + fabs(nearest(cix_min - cjx_max, dim[0])), + fabs(nearest(cix_max - cjx_min, dim[0])), + fabs(nearest(cix_max - cjx_max, dim[0]))); + + const double dy = min4(fabs(nearest(ciy_min - cjy_min, dim[1])), + fabs(nearest(ciy_min - cjy_max, dim[1])), + fabs(nearest(ciy_max - cjy_min, dim[1])), + fabs(nearest(ciy_max - cjy_max, dim[1]))); + + const double dz = min4(fabs(nearest(ciz_min - cjz_min, dim[2])), + fabs(nearest(ciz_min - cjz_max, dim[2])), + fabs(nearest(ciz_max - cjz_min, dim[2])), + fabs(nearest(ciz_max - cjz_max, dim[2]))); + + return dx * dx + dy * dy + dz * dz; + + } else { + + const double dx = min(fabs(cix_max - cjx_min), fabs(cix_min - cjx_max)); + const double dy = min(fabs(ciy_max - cjy_min), fabs(ciy_min - cjy_max)); + const double dz = min(fabs(ciz_max - cjz_min), fabs(ciz_min - cjz_max)); + + return dx * dx + dy * dy + dz * dz; + } +} + /* Inlined functions (for speed). */ /** @@ -846,14 +993,15 @@ __attribute__((always_inline)) INLINE static int cell_can_split_self_fof_task( } /** - * @brief Have particles in a pair of cells moved too much and require a rebuild + * @brief Have gas particles in a pair of cells moved too much and require a + * rebuild * ? * * @param ci The first #cell. * @param cj The second #cell. */ -__attribute__((always_inline)) INLINE static int cell_need_rebuild_for_pair( - const struct cell *ci, const struct cell *cj) { +__attribute__((always_inline)) INLINE static int +cell_need_rebuild_for_hydro_pair(const struct cell *ci, const struct cell *cj) { /* Is the cut-off radius plus the max distance the parts in both cells have */ /* moved larger than the cell size ? */ @@ -864,20 +1012,42 @@ __attribute__((always_inline)) INLINE static int cell_need_rebuild_for_pair( } /** - * @brief Add a unique tag to a cell mostly for MPI communications. + * @brief Have star particles in a pair of cells moved too much and require a + * rebuild + * ? + * + * @param ci The first #cell. + * @param cj The second #cell. + */ +__attribute__((always_inline)) INLINE static int +cell_need_rebuild_for_stars_pair(const struct cell *ci, const struct cell *cj) { + + /* Is the cut-off radius plus the max distance the parts in both cells have */ + /* moved larger than the cell size ? */ + /* Note ci->dmin == cj->dmin */ + return (kernel_gamma * max(ci->stars.h_max, cj->stars.h_max) + + ci->stars.dx_max_part + cj->stars.dx_max_part > + cj->dmin); +} + +/** + * @brief Add a unique tag to a cell, mostly for MPI communications. + * + * This function locks the cell so that tags can be added concurrently. * * @param c The #cell to tag. */ -__attribute__((always_inline)) INLINE static void cell_tag(struct cell *c) { +__attribute__((always_inline)) INLINE static void cell_ensure_tagged( + struct cell *c) { #ifdef WITH_MPI -#ifdef SWIFT_DEBUG_CHECKS - if (c->mpi.tag > 0) error("setting tag for already tagged cell"); -#endif - + lock_lock(&c->hydro.lock); if (c->mpi.tag < 0 && (c->mpi.tag = atomic_inc(&cell_next_tag)) > cell_max_tag) error("Ran out of cell tags."); + if (lock_unlock(&c->hydro.lock) != 0) { + error("Failed to unlock cell."); + } #else error("SWIFT was not compiled with MPI enabled."); #endif // WITH_MPI diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h index 7f8a672669e1c5b1f8997ecf5971c63efee7522f..7cb61d11fc5578da4cf545448c7fdc2e6b0b12ed 100644 --- a/src/chemistry/EAGLE/chemistry.h +++ b/src/chemistry/EAGLE/chemistry.h @@ -60,7 +60,17 @@ chemistry_get_element_name(enum chemistry_element elem) { * @param cd #chemistry_global_data containing chemistry informations. */ __attribute__((always_inline)) INLINE static void chemistry_init_part( - struct part* restrict p, const struct chemistry_global_data* cd) {} + struct part* restrict p, const struct chemistry_global_data* cd) { + + struct chemistry_part_data* cpd = &p->chemistry_data; + + for (int i = 0; i < chemistry_element_count; i++) { + cpd->smoothed_metal_mass_fraction[i] = 0.f; + } + + cpd->smoothed_metal_mass_fraction_total = 0.f; + cpd->smoothed_iron_mass_fraction_from_SNIa = 0.f; +} /** * @brief Finishes the smooth metal calculation. @@ -76,7 +86,35 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part( */ __attribute__((always_inline)) INLINE static void chemistry_end_density( struct part* restrict p, const struct chemistry_global_data* cd, - const struct cosmology* cosmo) {} + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float factor = pow_dimension(h_inv) / p->rho; /* 1 / h^d * rho */ + const float m = hydro_get_mass(p); + + struct chemistry_part_data* cpd = &p->chemistry_data; + + for (int i = 0; i < chemistry_element_count; i++) { + /* Final operation on the density (add self-contribution). */ + cpd->smoothed_metal_mass_fraction[i] += + m * cpd->metal_mass_fraction[i] * kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + cpd->smoothed_metal_mass_fraction[i] *= factor; + } + + /* Smooth mass fraction of all metals */ + cpd->smoothed_metal_mass_fraction_total += + m * cpd->metal_mass_fraction_total * kernel_root; + cpd->smoothed_metal_mass_fraction_total *= factor; + + /* Smooth iron mass fraction from SNIa */ + cpd->smoothed_iron_mass_fraction_from_SNIa += + m * cpd->iron_mass_fraction_from_SNIa * kernel_root; + cpd->smoothed_iron_mass_fraction_from_SNIa *= factor; +} /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. @@ -91,7 +129,21 @@ chemistry_part_has_no_neighbours(struct part* restrict p, struct xpart* restrict xp, const struct chemistry_global_data* cd, const struct cosmology* cosmo) { - error("Needs implementing!"); + + /* Just make all the smoothed fields default to the un-smoothed values */ + struct chemistry_part_data* cpd = &p->chemistry_data; + + /* Total metal mass fraction */ + cpd->smoothed_metal_mass_fraction_total = cpd->metal_mass_fraction_total; + + /* Iron frac from SNIa */ + cpd->smoothed_iron_mass_fraction_from_SNIa = + cpd->iron_mass_fraction_from_SNIa; + + /* Individual metal mass fractions */ + for (int i = 0; i < chemistry_element_count; i++) { + cpd->smoothed_metal_mass_fraction[i] = cpd->metal_mass_fraction[i]; + } } /** @@ -112,11 +164,16 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( const struct chemistry_global_data* data, struct part* restrict p, struct xpart* restrict xp) { - p->chemistry_data.metal_mass_fraction_total = - data->initial_metal_mass_fraction_total; - for (int elem = 0; elem < chemistry_element_count; ++elem) - p->chemistry_data.metal_mass_fraction[elem] = - data->initial_metal_mass_fraction[elem]; + // Add initialization of all other fields in chemistry_part_data struct. + if (data->initial_metal_mass_fraction_total != -1) { + p->chemistry_data.metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + + for (int elem = 0; elem < chemistry_element_count; ++elem) + p->chemistry_data.metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; + } + chemistry_init_part(p, data); } /** @@ -133,24 +190,20 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file, struct chemistry_global_data* data) { /* Read the total metallicity */ - data->initial_metal_mass_fraction_total = - parser_get_param_float(parameter_file, "EAGLEChemistry:InitMetallicity"); - - /* Read the individual mass fractions */ - for (int elem = 0; elem < chemistry_element_count; ++elem) { - char buffer[50]; - sprintf(buffer, "EAGLEChemistry:InitAbundance_%s", - chemistry_get_element_name((enum chemistry_element)elem)); - - data->initial_metal_mass_fraction[elem] = - parser_get_param_float(parameter_file, buffer); + data->initial_metal_mass_fraction_total = parser_get_opt_param_float( + parameter_file, "EAGLEChemistry:init_abundance_metal", -1); + + if (data->initial_metal_mass_fraction_total != -1) { + /* Read the individual mass fractions */ + for (int elem = 0; elem < chemistry_element_count; ++elem) { + char buffer[50]; + sprintf(buffer, "EAGLEChemistry:init_abundance_%s", + chemistry_get_element_name((enum chemistry_element)elem)); + + data->initial_metal_mass_fraction[elem] = + parser_get_param_float(parameter_file, buffer); + } } - - /* Read the constant ratios */ - data->calcium_over_silicon_ratio = parser_get_param_float( - parameter_file, "EAGLEChemistry:CalciumOverSilicon"); - data->sulphur_over_silicon_ratio = parser_get_param_float( - parameter_file, "EAGLEChemistry:SulphurOverSilicon"); } /** diff --git a/src/chemistry/EAGLE/chemistry_iact.h b/src/chemistry/EAGLE/chemistry_iact.h index bdbb8ac9bf7d260e29468b8bee0a84416b668d6a..573291637d66e39a973e662edec084d3a4687050 100644 --- a/src/chemistry/EAGLE/chemistry_iact.h +++ b/src/chemistry/EAGLE/chemistry_iact.h @@ -39,7 +39,49 @@ */ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( float r2, const float *dx, float hi, float hj, struct part *restrict pi, - struct part *restrict pj, float a, float H) {} + struct part *restrict pj, float a, float H) { + + struct chemistry_part_data *chi = &pi->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; + + float wi; + float wj; + + /* Get the masses. */ + const float mi = hydro_get_mass(pi); + const float mj = hydro_get_mass(pj); + + /* Get r */ + const float r = sqrtf(r2); + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_eval(ui, &wi); + + /* Compute the kernel function for pj */ + const float uj = r / hj; + kernel_eval(uj, &wj); + + /* Compute contribution to the smooth metallicity */ + for (int i = 0; i < chemistry_element_count; i++) { + chi->smoothed_metal_mass_fraction[i] += + mj * chj->metal_mass_fraction[i] * wi; + chj->smoothed_metal_mass_fraction[i] += + mi * chi->metal_mass_fraction[i] * wj; + } + + // Smooth metal mass fraction of all metals + chi->smoothed_metal_mass_fraction_total += + mj * chj->metal_mass_fraction_total * wi; + chj->smoothed_metal_mass_fraction_total += + mi * chi->metal_mass_fraction_total * wj; + + // Smooth iron mass fraction from SNIa + chi->smoothed_iron_mass_fraction_from_SNIa += + mj * chj->iron_mass_fraction_from_SNIa * wi; + chj->smoothed_iron_mass_fraction_from_SNIa += + mi * chi->iron_mass_fraction_from_SNIa * wj; +} /** * @brief do chemistry computation after the runner_iact_density (non symmetric @@ -56,6 +98,36 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry( */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( float r2, const float *dx, float hi, float hj, struct part *restrict pi, - const struct part *restrict pj, float a, float H) {} + const struct part *restrict pj, float a, float H) { + + struct chemistry_part_data *chi = &pi->chemistry_data; + const struct chemistry_part_data *chj = &pj->chemistry_data; + + float wi; + + /* Get the masses. */ + const float mj = hydro_get_mass(pj); + + /* Get r */ + const float r = sqrtf(r2); + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_eval(ui, &wi); + + /* Compute contribution to the smooth metallicity */ + for (int i = 0; i < chemistry_element_count; i++) { + chi->smoothed_metal_mass_fraction[i] += + mj * chj->metal_mass_fraction[i] * wi; + } + + // Smooth metal mass fraction of all metals + chi->smoothed_metal_mass_fraction_total += + mj * chj->metal_mass_fraction_total * wi; + + // Smooth iron mass fraction from SNIa + chi->smoothed_iron_mass_fraction_from_SNIa += + mj * chj->iron_mass_fraction_from_SNIa * wi; +} #endif /* SWIFT_EAGLE_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/EAGLE/chemistry_io.h b/src/chemistry/EAGLE/chemistry_io.h index d78a5f19a52e92426d5eb1f8575abe2b564e32ac..62de85d47329a588d658cc61db72510337988638 100644 --- a/src/chemistry/EAGLE/chemistry_io.h +++ b/src/chemistry/EAGLE/chemistry_io.h @@ -32,9 +32,18 @@ */ INLINE static int chemistry_read_particles(struct part* parts, struct io_props* list) { - - /* Nothing to read */ - return 0; + /* List what we want to read */ + list[0] = io_make_input_field( + "ElementAbundance", FLOAT, chemistry_element_count, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction); + list[1] = + io_make_input_field("Metallicity", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS, + parts, chemistry_data.metal_mass_fraction_total); + list[2] = io_make_input_field("IronMassFracFromSNIa", FLOAT, 1, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, + chemistry_data.iron_mass_fraction_from_SNIa); + + return 3; } /** @@ -97,6 +106,66 @@ INLINE static int chemistry_write_particles(const struct part* parts, return 12; } +/** + * @brief Specifies which star particle fields to write to a dataset + * + * @param sparts The star particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_sparticles(const struct spart* sparts, + struct io_props* list) { + + /* List what we want to write */ + list[0] = io_make_output_field("ElementAbundance", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, + sparts, chemistry_data.metal_mass_fraction); + + list[1] = io_make_output_field( + "SmoothedElementAbundance", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, sparts, chemistry_data.smoothed_metal_mass_fraction); + + list[2] = + io_make_output_field("Metallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, + chemistry_data.metal_mass_fraction_total); + + list[3] = io_make_output_field( + "SmoothedMetallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, + chemistry_data.smoothed_metal_mass_fraction_total); + + list[4] = io_make_output_field("TotalMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, + sparts, chemistry_data.mass_from_SNIa); + + list[5] = io_make_output_field("MetalMassFracFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, sparts, + chemistry_data.metal_mass_fraction_from_SNIa); + + list[6] = io_make_output_field("TotalMassFromAGB", FLOAT, 1, UNIT_CONV_MASS, + sparts, chemistry_data.mass_from_AGB); + + list[7] = + io_make_output_field("MetalMassFracFromAGB", FLOAT, 1, UNIT_CONV_NO_UNITS, + sparts, chemistry_data.metal_mass_fraction_from_AGB); + + list[8] = io_make_output_field("TotalMassFromSNII", FLOAT, 1, UNIT_CONV_MASS, + sparts, chemistry_data.mass_from_SNII); + + list[9] = io_make_output_field("MetalMassFracFromSNII", FLOAT, 1, + UNIT_CONV_NO_UNITS, sparts, + chemistry_data.metal_mass_fraction_from_SNII); + + list[10] = + io_make_output_field("IronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, + sparts, chemistry_data.iron_mass_fraction_from_SNIa); + + list[11] = io_make_output_field( + "SmoothedIronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, + chemistry_data.smoothed_iron_mass_fraction_from_SNIa); + + return 12; +} + #ifdef HAVE_HDF5 /** diff --git a/src/chemistry/EAGLE/chemistry_struct.h b/src/chemistry/EAGLE/chemistry_struct.h index 9093709e62d0af638ae485bd1154a4537791e84a..f5e47347f8b6a910640624ddfc0b5968242eedf6 100644 --- a/src/chemistry/EAGLE/chemistry_struct.h +++ b/src/chemistry/EAGLE/chemistry_struct.h @@ -45,12 +45,6 @@ struct chemistry_global_data { /*! Fraction of the particle mass in *all* metals at the start of the run */ float initial_metal_mass_fraction_total; - - /*! Constant ratio of Calcium over Silicium */ - float calcium_over_silicon_ratio; - - /*! Constant ratio of Calcium over Silicium */ - float sulphur_over_silicon_ratio; }; /** diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h index 2a0847bebfb8c1734f21bda2f6ad55b354a7aec9..b29f7db65d3ab7dce4b0dcafed06c97a9e621bfe 100644 --- a/src/chemistry/GEAR/chemistry_io.h +++ b/src/chemistry/GEAR/chemistry_io.h @@ -87,6 +87,32 @@ INLINE static int chemistry_write_particles(const struct part* parts, return 3; } +/** + * @brief Specifies which sparticle fields to write to a dataset + * + * @param sparts The sparticle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_sparticles(const struct spart* sparts, + struct io_props* list) { + + /* List what we want to write */ + list[0] = io_make_output_field( + "SmoothedElementAbundance", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, sparts, chemistry_data.smoothed_metal_mass_fraction); + + list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, sparts, + chemistry_data.Z); + + list[2] = io_make_output_field("ElementAbundance", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, + sparts, chemistry_data.metal_mass_fraction); + + return 3; +} + #ifdef HAVE_HDF5 /** diff --git a/src/chemistry/none/chemistry_io.h b/src/chemistry/none/chemistry_io.h index ef7e0d8d87dfeab5978f0e86bbf6279f7901d10a..c6e5b7b769cec667fee8c3fc674f3b4aa35929c1 100644 --- a/src/chemistry/none/chemistry_io.h +++ b/src/chemistry/none/chemistry_io.h @@ -55,10 +55,27 @@ INLINE static int chemistry_write_particles(const struct part* parts, return 0; } +/** + * @brief Specifies which sparticle fields to write to a dataset + * + * @param sparts The sparticle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +INLINE static int chemistry_write_sparticles(const struct spart* sparts, + struct io_props* list) { + + /* update list according to hydro_io */ + + /* Return the number of fields to write */ + return 0; +} + #ifdef HAVE_HDF5 /** - * @brief Writes the current model of SPH to the file + * @brief Writes the current model of chemistry to the file * @param h_grp The HDF5 group in which to write */ INLINE static void chemistry_write_flavour(hid_t h_grp) { diff --git a/src/collectgroup.c b/src/collectgroup.c index 0b7b419b565612149fd2b295116b37aa65aa01e9..ddf3e35d945fd8b07cc927d8ba383963c7558cd2 100644 --- a/src/collectgroup.c +++ b/src/collectgroup.c @@ -41,6 +41,9 @@ struct mpicollectgroup1 { integertime_t ti_hydro_end_min; integertime_t ti_gravity_end_min; int forcerebuild; + long long total_nr_cells; + long long total_nr_tasks; + float tasks_per_cell_max; }; /* Forward declarations. */ @@ -93,6 +96,9 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) { e->nr_inhibited_gparts = grp1->g_inhibited; e->nr_inhibited_sparts = grp1->s_inhibited; e->forcerebuild = grp1->forcerebuild; + e->total_nr_cells = grp1->total_nr_cells; + e->total_nr_tasks = grp1->total_nr_tasks; + e->tasks_per_cell_max = grp1->tasks_per_cell_max; } /** @@ -101,37 +107,39 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) { * @param grp1 The #collectgroup1 to initialise * @param updated the number of updated hydro particles on this node this step. * @param g_updated the number of updated gravity particles on this node this - * step. + * step. * @param s_updated the number of updated star particles on this node this step. * @param inhibited the number of inhibited hydro particles on this node this - * step. + * step. * @param g_inhibited the number of inhibited gravity particles on this node - * this step. + * this step. * @param s_inhibited the number of inhibited star particles on this node this - * step. + * step. * @param ti_hydro_end_min the minimum end time for next hydro time step after - * this step. + * this step. * @param ti_hydro_end_max the maximum end time for next hydro time step after - * this step. + * this step. * @param ti_hydro_beg_max the maximum begin time for next hydro time step after - * this step. + * this step. * @param ti_gravity_end_min the minimum end time for next gravity time step - * after this step. + * after this step. * @param ti_gravity_end_max the maximum end time for next gravity time step - * after this step. + * after this step. * @param ti_gravity_beg_max the maximum begin time for next gravity time step - * after this step. + * after this step. * @param forcerebuild whether a rebuild is required after this step. + * @param total_nr_cells total number of all cells on rank. + * @param total_nr_tasks total number of tasks on rank. + * @param tasks_per_cell the used number of tasks per cell. */ -void collectgroup1_init(struct collectgroup1 *grp1, size_t updated, - size_t g_updated, size_t s_updated, size_t inhibited, - size_t g_inhibited, size_t s_inhibited, - integertime_t ti_hydro_end_min, - integertime_t ti_hydro_end_max, - integertime_t ti_hydro_beg_max, - integertime_t ti_gravity_end_min, - integertime_t ti_gravity_end_max, - integertime_t ti_gravity_beg_max, int forcerebuild) { +void collectgroup1_init( + struct collectgroup1 *grp1, size_t updated, size_t g_updated, + size_t s_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, + integertime_t ti_hydro_end_min, integertime_t ti_hydro_end_max, + integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, + integertime_t ti_gravity_end_max, integertime_t ti_gravity_beg_max, + int forcerebuild, long long total_nr_cells, long long total_nr_tasks, + float tasks_per_cell) { grp1->updated = updated; grp1->g_updated = g_updated; @@ -146,6 +154,9 @@ void collectgroup1_init(struct collectgroup1 *grp1, size_t updated, grp1->ti_gravity_end_max = ti_gravity_end_max; grp1->ti_gravity_beg_max = ti_gravity_beg_max; grp1->forcerebuild = forcerebuild; + grp1->total_nr_cells = total_nr_cells; + grp1->total_nr_tasks = total_nr_tasks; + grp1->tasks_per_cell_max = tasks_per_cell; } /** @@ -171,6 +182,9 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { mpigrp11.ti_hydro_end_min = grp1->ti_hydro_end_min; mpigrp11.ti_gravity_end_min = grp1->ti_gravity_end_min; mpigrp11.forcerebuild = grp1->forcerebuild; + mpigrp11.total_nr_cells = grp1->total_nr_cells; + mpigrp11.total_nr_tasks = grp1->total_nr_tasks; + mpigrp11.tasks_per_cell_max = grp1->tasks_per_cell_max; struct mpicollectgroup1 mpigrp12; if (MPI_Allreduce(&mpigrp11, &mpigrp12, 1, mpicollectgroup1_type, @@ -187,6 +201,9 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { grp1->ti_hydro_end_min = mpigrp12.ti_hydro_end_min; grp1->ti_gravity_end_min = mpigrp12.ti_gravity_end_min; grp1->forcerebuild = mpigrp12.forcerebuild; + grp1->total_nr_cells = mpigrp12.total_nr_cells; + grp1->total_nr_tasks = mpigrp12.total_nr_tasks; + grp1->tasks_per_cell_max = mpigrp12.tasks_per_cell_max; #endif } @@ -221,6 +238,14 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, /* Everyone must agree to not rebuild. */ if (mpigrp11->forcerebuild || mpigrp12->forcerebuild) mpigrp11->forcerebuild = 1; + + /* Totals of all tasks and cells. */ + mpigrp11->total_nr_cells += mpigrp12->total_nr_cells; + mpigrp11->total_nr_tasks += mpigrp12->total_nr_tasks; + + /* Maximum value of tasks_per_cell. */ + mpigrp11->tasks_per_cell_max = + max(mpigrp11->tasks_per_cell_max, mpigrp12->tasks_per_cell_max); } /** diff --git a/src/collectgroup.h b/src/collectgroup.h index b6e8769ac993cc023ae402cdfc4b0169406f6181..3e430b58db05b563f96149d1ae21039444a03640 100644 --- a/src/collectgroup.h +++ b/src/collectgroup.h @@ -46,19 +46,25 @@ struct collectgroup1 { /* Force the engine to rebuild? */ int forcerebuild; + + /* Totals of cells and tasks. */ + long long total_nr_cells; + long long total_nr_tasks; + + /* Maximum value of actual tasks per cell across all ranks. */ + float tasks_per_cell_max; }; void collectgroup_init(void); void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e); -void collectgroup1_init(struct collectgroup1 *grp1, size_t updated, - size_t g_updated, size_t s_updated, size_t inhibited, - size_t g_inhibited, size_t s_inhibited, - integertime_t ti_hydro_end_min, - integertime_t ti_hydro_end_max, - integertime_t ti_hydro_beg_max, - integertime_t ti_gravity_end_min, - integertime_t ti_gravity_end_max, - integertime_t ti_gravity_beg_max, int forcerebuild); +void collectgroup1_init( + struct collectgroup1 *grp1, size_t updated, size_t g_updated, + size_t s_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, + integertime_t ti_hydro_end_min, integertime_t ti_hydro_end_max, + integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, + integertime_t ti_gravity_end_max, integertime_t ti_gravity_beg_max, + int forcerebuild, long long total_nr_cells, long long total_nr_tasks, + float tasks_per_cell); void collectgroup1_reduce(struct collectgroup1 *grp1); #endif /* SWIFT_COLLECTGROUP_H */ diff --git a/src/common_io.c b/src/common_io.c index 087697b489269d97a268966d341093dd666dd9c9..733cf1dacac5f0c73ea401a584e2aa40eadd4a23 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -142,7 +142,7 @@ void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, * Calls #error() if an error occurs. */ void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, - void* data, int num) { + const void* data, int num) { const hid_t h_space = H5Screate(H5S_SIMPLE); if (h_space < 0) @@ -387,6 +387,332 @@ void io_write_engine_policy(hid_t h_file, const struct engine* e) { H5Gclose(h_grp); } +void io_write_cell_offsets(hid_t h_grp, const int cdim[3], + const struct cell* cells_top, const int nr_cells, + const double width[3], const int nodeID, + const long long global_counts[swift_type_count], + const long long global_offsets[swift_type_count], + const struct unit_system* internal_units, + const struct unit_system* snapshot_units) { + + double cell_width[3] = {width[0], width[1], width[2]}; + + /* Temporary memory for the cell-by-cell information */ + double* centres = NULL; + centres = (double*)malloc(3 * nr_cells * sizeof(double)); + + /* Count of particles in each cell */ + long long *count_part = NULL, *count_gpart = NULL, *count_spart = NULL; + count_part = (long long*)malloc(nr_cells * sizeof(long long)); + count_gpart = (long long*)malloc(nr_cells * sizeof(long long)); + count_spart = (long long*)malloc(nr_cells * sizeof(long long)); + + /* Global offsets of particles in each cell */ + long long *offset_part = NULL, *offset_gpart = NULL, *offset_spart = NULL; + offset_part = (long long*)malloc(nr_cells * sizeof(long long)); + offset_gpart = (long long*)malloc(nr_cells * sizeof(long long)); + offset_spart = (long long*)malloc(nr_cells * sizeof(long long)); + + /* Offsets of the 0^th element */ + offset_part[0] = 0; + offset_gpart[0] = 0; + offset_spart[0] = 0; + + /* Collect the cell information of *local* cells */ + long long local_offset_part = 0; + long long local_offset_gpart = 0; + long long local_offset_spart = 0; + for (int i = 0; i < nr_cells; ++i) { + + if (cells_top[i].nodeID == nodeID) { + + /* Centre of each cell */ + centres[i * 3 + 0] = cells_top[i].loc[0] + cell_width[0] * 0.5; + centres[i * 3 + 1] = cells_top[i].loc[1] + cell_width[1] * 0.5; + centres[i * 3 + 2] = cells_top[i].loc[2] + cell_width[2] * 0.5; + + /* Count real particles that will be written */ + count_part[i] = cells_top[i].hydro.count - cells_top[i].hydro.inhibited; + count_gpart[i] = cells_top[i].grav.count - cells_top[i].grav.inhibited; + count_spart[i] = cells_top[i].stars.count - cells_top[i].stars.inhibited; + + /* Only count DM gpart (gpart without friends) */ + count_gpart[i] -= count_part[i]; + count_gpart[i] -= count_spart[i]; + + /* Offsets including the global offset of all particles on this MPI rank + */ + offset_part[i] = local_offset_part + global_offsets[swift_type_gas]; + offset_gpart[i] = + local_offset_gpart + global_offsets[swift_type_dark_matter]; + offset_spart[i] = local_offset_spart + global_offsets[swift_type_stars]; + + local_offset_part += count_part[i]; + local_offset_gpart += count_gpart[i]; + local_offset_spart += count_spart[i]; + + } else { + + /* Just zero everything for the foregin cells */ + + centres[i * 3 + 0] = 0.; + centres[i * 3 + 1] = 0.; + centres[i * 3 + 2] = 0.; + + count_part[i] = 0; + count_gpart[i] = 0; + count_spart[i] = 0; + + offset_part[i] = 0; + offset_gpart[i] = 0; + offset_spart[i] = 0; + } + } + +#ifdef WITH_MPI + /* Now, reduce all the arrays. Note that we use a bit-wise OR here. This + is safe as we made sure only local cells have non-zero values. */ + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, count_part, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(count_part, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, count_gpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(count_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, count_spart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(count_spart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, offset_part, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(offset_part, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, offset_gpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(offset_gpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, offset_spart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(offset_spart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + + /* For the centres we use a sum as MPI does not like bit-wise operations + on floating point numbers */ + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, centres, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, 0, + MPI_COMM_WORLD); + } else { + MPI_Reduce(centres, NULL, 3 * nr_cells, MPI_DOUBLE, MPI_SUM, 0, + MPI_COMM_WORLD); + } +#endif + + /* Only rank 0 actually writes */ + if (nodeID == 0) { + + /* Unit conversion if necessary */ + const double factor = units_conversion_factor( + internal_units, snapshot_units, UNIT_CONV_LENGTH); + if (factor != 1.) { + + /* Convert the cell centres */ + for (int i = 0; i < nr_cells; ++i) { + centres[i * 3 + 0] *= factor; + centres[i * 3 + 1] *= factor; + centres[i * 3 + 2] *= factor; + } + + /* Convert the cell widths */ + cell_width[0] *= factor; + cell_width[1] *= factor; + cell_width[2] *= factor; + } + + /* Write some meta-information first */ + hid_t h_subgrp = + H5Gcreate(h_grp, "Meta-data", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_subgrp < 0) error("Error while creating meta-data sub-group"); + io_write_attribute(h_subgrp, "nr_cells", INT, &nr_cells, 1); + io_write_attribute(h_subgrp, "size", DOUBLE, cell_width, 3); + io_write_attribute(h_subgrp, "dimension", INT, cdim, 3); + H5Gclose(h_subgrp); + + /* Write the centres to the group */ + hsize_t shape[2] = {nr_cells, 3}; + hid_t h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) error("Error while creating data space for cell centres"); + hid_t h_err = H5Sset_extent_simple(h_space, 2, shape, shape); + if (h_err < 0) + error("Error while changing shape of gas offsets data space."); + hid_t h_data = H5Dcreate(h_grp, "Centres", io_hdf5_type(DOUBLE), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for gas offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(DOUBLE), h_space, H5S_ALL, + H5P_DEFAULT, centres); + if (h_err < 0) error("Error while writing centres."); + H5Dclose(h_data); + H5Sclose(h_space); + + /* Group containing the offsets for each particle type */ + h_subgrp = + H5Gcreate(h_grp, "Offsets", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_subgrp < 0) error("Error while creating offsets sub-group"); + + if (global_counts[swift_type_gas] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) error("Error while creating data space for gas offsets"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of gas offsets data space."); + h_data = H5Dcreate(h_subgrp, "PartType0", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for gas offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, offset_part); + if (h_err < 0) error("Error while writing gas offsets."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + if (global_counts[swift_type_dark_matter] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) error("Error while creating data space for DM offsets"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of DM offsets data space."); + h_data = H5Dcreate(h_subgrp, "PartType1", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for DM offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, offset_gpart); + if (h_err < 0) error("Error while writing DM offsets."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + if (global_counts[swift_type_stars] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for stars offsets"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of stars offsets data space."); + h_data = H5Dcreate(h_subgrp, "PartType4", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for star offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, offset_spart); + if (h_err < 0) error("Error while writing star offsets."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + H5Gclose(h_subgrp); + + /* Group containing the counts for each particle type */ + h_subgrp = + H5Gcreate(h_grp, "Counts", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_subgrp < 0) error("Error while creating counts sub-group"); + + if (global_counts[swift_type_gas] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) error("Error while creating data space for gas counts"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of gas counts data space."); + h_data = H5Dcreate(h_subgrp, "PartType0", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for gas counts."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, count_part); + if (h_err < 0) error("Error while writing gas counts."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + if (global_counts[swift_type_dark_matter] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) error("Error while creating data space for DM counts"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of DM counts data space."); + h_data = H5Dcreate(h_subgrp, "PartType1", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for DM counts."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, count_gpart); + if (h_err < 0) error("Error while writing DM counts."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + if (global_counts[swift_type_stars] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for stars counts"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of stars counts data space."); + h_data = H5Dcreate(h_subgrp, "PartType4", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace for star counts."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, count_spart); + if (h_err < 0) error("Error while writing star counts."); + H5Dclose(h_data); + H5Sclose(h_space); + } + + H5Gclose(h_subgrp); + } + + /* Free everything we allocated */ + free(centres); + free(count_part); + free(count_gpart); + free(count_spart); + free(offset_part); + free(offset_gpart); + free(offset_spart); +} + #endif /* HAVE_HDF5 */ /** @@ -482,6 +808,28 @@ void io_convert_part_d_mapper(void* restrict temp, int N, &temp_d[i * dim]); } +/** + * @brief Mapper function to copy #part into a buffer of doubles using a + * conversion function. + */ +void io_convert_part_l_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct part* restrict parts = props.parts; + const struct xpart* restrict xparts = props.xparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + long long* restrict temp_l = (long long*)temp; + const ptrdiff_t delta = (temp_l - props.start_temp_l) / dim; + + for (int i = 0; i < N; i++) + props.convert_part_l(e, parts + delta + i, xparts + delta + i, + &temp_l[i * dim]); +} + /** * @brief Mapper function to copy #gpart into a buffer of floats using a * conversion function. @@ -522,6 +870,26 @@ void io_convert_gpart_d_mapper(void* restrict temp, int N, props.convert_gpart_d(e, gparts + delta + i, &temp_d[i * dim]); } +/** + * @brief Mapper function to copy #gpart into a buffer of doubles using a + * conversion function. + */ +void io_convert_gpart_l_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct gpart* restrict gparts = props.gparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + long long* restrict temp_l = (long long*)temp; + const ptrdiff_t delta = (temp_l - props.start_temp_l) / dim; + + for (int i = 0; i < N; i++) + props.convert_gpart_l(e, gparts + delta + i, &temp_l[i * dim]); +} + /** * @brief Mapper function to copy #spart into a buffer of floats using a * conversion function. @@ -562,6 +930,26 @@ void io_convert_spart_d_mapper(void* restrict temp, int N, props.convert_spart_d(e, sparts + delta + i, &temp_d[i * dim]); } +/** + * @brief Mapper function to copy #spart into a buffer of doubles using a + * conversion function. + */ +void io_convert_spart_l_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct spart* restrict sparts = props.sparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + long long* restrict temp_l = (long long*)temp; + const ptrdiff_t delta = (temp_l - props.start_temp_l) / dim; + + for (int i = 0; i < N; i++) + props.convert_spart_l(e, sparts + delta + i, &temp_l[i * dim]); +} + /** * @brief Copy the particle data into a temporary buffer ready for i/o. * @@ -619,6 +1007,18 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, io_convert_part_d_mapper, temp_d, N, copySize, 0, (void*)&props); + } else if (props.convert_part_l != NULL) { + + /* Prepare some parameters */ + long long* temp_l = (long long*)temp; + props.start_temp_l = (long long*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_part_l_mapper, temp_l, N, copySize, 0, + (void*)&props); + } else if (props.convert_gpart_f != NULL) { /* Prepare some parameters */ @@ -643,6 +1043,18 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, io_convert_gpart_d_mapper, temp_d, N, copySize, 0, (void*)&props); + } else if (props.convert_gpart_l != NULL) { + + /* Prepare some parameters */ + long long* temp_l = (long long*)temp; + props.start_temp_l = (long long*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_gpart_l_mapper, temp_l, N, copySize, 0, + (void*)&props); + } else if (props.convert_spart_f != NULL) { /* Prepare some parameters */ @@ -667,6 +1079,18 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, io_convert_spart_d_mapper, temp_d, N, copySize, 0, (void*)&props); + } else if (props.convert_spart_l != NULL) { + + /* Prepare some parameters */ + long long* temp_l = (long long*)temp; + props.start_temp_l = (long long*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_spart_l_mapper, temp_l, N, copySize, 0, + (void*)&props); + } else { error("Missing conversion function"); } @@ -875,7 +1299,8 @@ void io_collect_parts_to_write(const struct part* restrict parts, for (size_t i = 0; i < Nparts; ++i) { /* And collect the ones that have not been removed */ - if (parts[i].time_bin != time_bin_inhibited) { + if (parts[i].time_bin != time_bin_inhibited && + parts[i].time_bin != time_bin_not_created) { parts_written[count] = parts[i]; xparts_written[count] = xparts[i]; @@ -909,7 +1334,8 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts, for (size_t i = 0; i < Nsparts; ++i) { /* And collect the ones that have not been removed */ - if (sparts[i].time_bin != time_bin_inhibited) { + if (sparts[i].time_bin != time_bin_inhibited && + sparts[i].time_bin != time_bin_not_created) { sparts_written[count] = sparts[i]; count++; @@ -926,15 +1352,21 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts, * @brief Copy every non-inhibited DM #gpart into the gparts_written array. * * @param gparts The array of #gpart containing all particles. + * @param vr_data The array of gpart-related VELOCIraptor output. * @param gparts_written The array of #gpart to fill with particles we want to * write. + * @param vr_data_written The array of gpart-related VELOCIraptor with particles + * we want to write. * @param Ngparts The total number of #part. * @param Ngparts_written The total number of #part to write. + * @param with_stf Are we running with STF? i.e. do we want to collect vr data? */ -void io_collect_gparts_to_write(const struct gpart* restrict gparts, - struct gpart* restrict gparts_written, - const size_t Ngparts, - const size_t Ngparts_written) { +void io_collect_gparts_to_write( + const struct gpart* restrict gparts, + const struct velociraptor_gpart_data* restrict vr_data, + struct gpart* restrict gparts_written, + struct velociraptor_gpart_data* restrict vr_data_written, + const size_t Ngparts, const size_t Ngparts_written, const int with_stf) { size_t count = 0; @@ -943,8 +1375,11 @@ void io_collect_gparts_to_write(const struct gpart* restrict gparts, /* And collect the ones that have not been removed */ if ((gparts[i].time_bin != time_bin_inhibited) && + (gparts[i].time_bin != time_bin_not_created) && (gparts[i].type == swift_type_dark_matter)) { + if (with_stf) vr_data_written[count] = vr_data[i]; + gparts_written[count] = gparts[i]; count++; } @@ -952,7 +1387,7 @@ void io_collect_gparts_to_write(const struct gpart* restrict gparts, /* Check that everything is fine */ if (count != Ngparts_written) - error("Collected the wrong number of s-particles (%zu vs. %zu expected)", + error("Collected the wrong number of g-particles (%zu vs. %zu expected)", count, Ngparts_written); } diff --git a/src/common_io.h b/src/common_io.h index 016c5138e18ae8636834c35d659e07d8fcd46e36..eb1ee0a804f324d897842fb2a0ca33fc07e769d6 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -24,6 +24,7 @@ #include "../config.h" /* Local includes. */ +#include "part_type.h" #include "units.h" #define FIELD_BUFFER_SIZE 200 @@ -32,8 +33,10 @@ #define IO_BUFFER_ALIGNMENT 1024 /* Avoid cyclic inclusion problems */ +struct cell; struct part; struct gpart; +struct velociraptor_gpart_data; struct spart; struct xpart; struct io_props; @@ -65,7 +68,7 @@ void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, - void* data, int num); + const void* data, int num); void io_write_attribute_d(hid_t grp, const char* name, double data); void io_write_attribute_f(hid_t grp, const char* name, float data); @@ -76,6 +79,14 @@ void io_write_attribute_s(hid_t grp, const char* name, const char* str); void io_write_code_description(hid_t h_file); void io_write_engine_policy(hid_t h_file, const struct engine* e); +void io_write_cell_offsets(hid_t h_grp, const int cdim[3], + const struct cell* cells_top, const int nr_cells, + const double width[3], const int nodeID, + const long long global_counts[swift_type_count], + const long long global_offsets[swift_type_count], + const struct unit_system* internal_units, + const struct unit_system* snapshot_units); + void io_read_unit_system(hid_t h_file, struct unit_system* ic_units, const struct unit_system* internal_units, int mpi_rank); @@ -103,9 +114,11 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts, const size_t Nsparts, const size_t Nsparts_written); void io_collect_gparts_to_write(const struct gpart* restrict gparts, + const struct velociraptor_gpart_data* vr_data, struct gpart* restrict gparts_written, + struct velociraptor_gpart_data* vr_data_written, const size_t Ngparts, - const size_t Ngparts_written); + const size_t Ngparts_written, int with_stf); void io_prepare_dm_gparts(struct threadpool* tp, struct gpart* const gparts, size_t Ndm); void io_duplicate_hydro_gparts(struct threadpool* tp, struct part* const parts, diff --git a/src/const.h b/src/const.h index e417b8ca3827ef87396706c56df36bb9bd3aed75..613a48920e6f26c209faf6e354b82c2ed5be0bf1 100644 --- a/src/const.h +++ b/src/const.h @@ -33,6 +33,9 @@ /* Time integration constants. */ #define const_max_u_change 0.1f +/* Time-step limiter maximal difference in signal velocity */ +#define const_limiter_max_v_sig_ratio 4.1f + /* Type of gradients to use (GIZMO_SPH only) */ /* If no option is chosen, no gradients are used (first order scheme) */ //#define GRADIENTS_SPH diff --git a/src/cooling.c b/src/cooling.c index 154b859f74402d9e9a8adf1fb6c796b5195b8cd1..34205937bbd7ce144503b10ef047cf5b552f23cc 100644 --- a/src/cooling.c +++ b/src/cooling.c @@ -53,28 +53,3 @@ void cooling_print(const struct cooling_function_data* cooling) { cooling_print_backend(cooling); } - -/** - * @brief Write a cooling struct to the given FILE as a stream of bytes. - * - * @param cooling the struct - * @param stream the file stream - */ -void cooling_struct_dump(const struct cooling_function_data* cooling, - FILE* stream) { - restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, - stream, "cooling", "cooling function"); -} - -/** - * @brief Restore a hydro_props struct from the given FILE as a stream of - * bytes. - * - * @param cooling the struct - * @param stream the file stream - */ -void cooling_struct_restore(const struct cooling_function_data* cooling, - FILE* stream) { - restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, - stream, NULL, "cooling function"); -} diff --git a/src/cooling.h b/src/cooling.h index ae0d06a4733b31f8c0a2fe4db5329df1494f4aa4..875ef5054491f783d526e7c8e2caf3e005c8a5a0 100644 --- a/src/cooling.h +++ b/src/cooling.h @@ -27,6 +27,12 @@ /* Config parameters. */ #include "../config.h" +/* Local includes */ +#include "parser.h" +#include "physical_constants.h" +#include "restart.h" +#include "units.h" + /* Import the right cooling definition */ #if defined(COOLING_NONE) #include "./cooling/none/cooling.h" @@ -55,7 +61,7 @@ void cooling_print(const struct cooling_function_data* cooling); /* Dump/restore. */ void cooling_struct_dump(const struct cooling_function_data* cooling, FILE* stream); -void cooling_struct_restore(const struct cooling_function_data* cooling, - FILE* stream); +void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream, + const struct cosmology* cosmo); #endif /* SWIFT_COOLING_H */ diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h index da9225fe02fb6805f795938f86afd75ebdbef125..c796375c33c586e2c9a95515f4124062a41640eb 100644 --- a/src/cooling/Compton/cooling.h +++ b/src/cooling/Compton/cooling.h @@ -19,6 +19,14 @@ #ifndef SWIFT_COOLING_COMPTON_H #define SWIFT_COOLING_COMPTON_H +/** + * @file src/cooling/Compton/cooling.h + * @brief Routines related to the "Compton" cooling function. + * + * This model compute the cooling rate from the Compton interaction with + * the CMB photons. + */ + /* Config parameters. */ #include "../config.h" @@ -27,7 +35,7 @@ #include <math.h> /* Local includes. */ -#include "const.h" +#include "entropy_floor.h" #include "error.h" #include "hydro.h" #include "parser.h" @@ -36,60 +44,22 @@ #include "units.h" /** - * @brief Compute the mean molecular weight as a function of temperature for - * primordial gas. + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. * - * @param T The temperature of the gas [K]. - * @param H_mass_fraction The hydrogen mass fraction of the gas. - * @param T_transition The temperature of the transition from HII to HI [K]. - */ -__attribute__((always_inline, const)) INLINE static double -mean_molecular_weight(const double T, const double H_mass_fraction, - const double T_transition) { - - if (T > T_transition) - return 4. / (8. - 5. * (1. - H_mass_fraction)); - else - return 4. / (1. + 3. * H_mass_fraction); -} - -/** - * @brief Compute the temperature for a given internal energy per unit mass - * assuming primordial gas. - * - * @param u_cgs The internal energy per unit mass of the gas [erg * g^-1]. - * @param H_mass_fraction The hydrogen mass fraction of the gas. - * @param T_transition The temperature of the transition from HII to HI [K]. - * @param m_H_cgs The mass of the Hydorgen atom [g]. - * @param k_B_cgs The Boltzmann constant in cgs units [erg * K^-1] - * @return The temperature of the gas [K] + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. */ -__attribute__((always_inline, const)) INLINE static double -temperature_from_internal_energy(const double u_cgs, - const double H_mass_fraction, - const double T_transition, - const double m_H_cgs, const double k_B_cgs) { - - const double T_over_mu = hydro_gamma_minus_one * u_cgs * m_H_cgs / k_B_cgs; - - const double mu_high = - mean_molecular_weight(T_transition + 1., H_mass_fraction, T_transition); - const double mu_low = - mean_molecular_weight(T_transition - 1., H_mass_fraction, T_transition); - - if (T_over_mu > (T_transition + 1.) / mu_high) - return T_over_mu * mu_high; - else if (T_over_mu < (T_transition - 1.) / mu_low) - return T_over_mu * mu_low; - else - return T_transition; +INLINE static void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling) { + // Add content if required. } /** * @brief Calculates du/dt in CGS units for a particle. * - * * @param cosmo The current cosmological model. + * @param phys_const The physical constants in internal units. * @param hydro_props The properties of the hydro scheme. * @param cooling The #cooling_function_data used in the run. * @param z The current redshift. @@ -99,7 +69,8 @@ temperature_from_internal_energy(const double u_cgs, * in cgs units [erg * g^-1 * s^-1]. */ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs( - const struct cosmology* cosmo, const struct hydro_props* hydro_props, + const struct cosmology* cosmo, const struct phys_const* restrict phys_const, + const struct hydro_props* hydro_props, const struct cooling_function_data* cooling, const double z, const double u, const struct part* p) { @@ -115,15 +86,27 @@ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs( /* CMB temperature at this redshift */ const double T_CMB = cooling->const_T_CMB_0 * zp1; + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + /* Gas properties */ - const double H_mass_fraction = hydro_props->hydrogen_mass_fraction; const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; - /* Particle temperature */ - const double u_cgs = u * cooling->conv_factor_energy_to_cgs; - const double T = temperature_from_internal_energy(u_cgs, H_mass_fraction, - T_transition, 1., 1.); - // MATTHIEU: to do: get H mass in cgs and k_B in cgs. + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + double T; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + T = T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + T = T_over_mu * mu_neutral; + else + T = T_transition; /* Electron abundance */ double electron_abundance = 0.; // MATTHIEU: To do: compute X_e @@ -147,6 +130,7 @@ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs( * @param us The internal system of units. * @param cosmo The current cosmological model. * @param hydro_props The properties of the hydro scheme. + * @param floor_props Properties of the entropy floor. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the particle' extended data. @@ -158,6 +142,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, const float dt, const float dt_therm) { @@ -165,9 +150,6 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( /* Nothing to do here? */ if (dt == 0.) return; - /* Internal energy floor */ - const float u_floor = hydro_props->minimal_internal_energy; - /* Current energy */ const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); @@ -175,8 +157,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); /* Calculate cooling du_dt (in cgs units) */ - const double cooling_du_dt_cgs = - Compton_cooling_rate_cgs(cosmo, hydro_props, cooling, cosmo->z, u_old, p); + const double cooling_du_dt_cgs = Compton_cooling_rate_cgs( + cosmo, phys_const, hydro_props, cooling, cosmo->z, u_old, p); /* Convert to internal units */ float cooling_du_dt = @@ -189,11 +171,22 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( /* We now need to check that we are not going to go below any of the limits */ + /* Limit imposed by the entropy floor */ + const float A_floor = entropy_floor(p, cosmo, floor_props); + const float rho = hydro_get_physical_density(p, cosmo); + const float u_floor = gas_internal_energy_from_entropy(rho, A_floor); + + /* Absolute minimum */ + const float u_minimal = hydro_props->minimal_internal_energy; + + /* Largest of both limits */ + const float u_limit = max(u_minimal, u_floor); + /* First, check whether we may end up below the minimal energy after * this step 1/2 kick + another 1/2 kick that could potentially be for * a time-step twice as big. We hence check for 1.5 delta_t. */ - if (u_old + total_du_dt * 1.5 * dt_therm < u_floor) { - total_du_dt = (u_floor - u_old) / (1.5f * dt_therm); + if (u_old + total_du_dt * 1.5 * dt_therm < u_limit) { + total_du_dt = (u_limit - u_old) / (1.5f * dt_therm); } /* Second, check whether the energy used in the prediction could get negative. @@ -259,6 +252,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part( xp->cooling_data.radiated_energy = 0.f; } +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +INLINE static float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; +} + /** * @brief Returns the total radiated energy by this particle. * @@ -321,6 +357,16 @@ static INLINE void cooling_init_backend(struct swift_params* parameter_file, compton_coefficient_cgs * T_CMB_0 * T_CMB_0 * T_CMB_0 * T_CMB_0; } +/** + * @brief Restore cooling tables (if applicable) after + * restart + * + * @param cooling the cooling_function_data structure + * @param cosmo cosmology structure + */ +static INLINE void cooling_restore_tables(struct cooling_function_data* cooling, + const struct cosmology* cosmo) {} + /** * @brief Prints the properties of the cooling model to stdout. * @@ -332,4 +378,42 @@ static INLINE void cooling_print_backend( message("Cooling function is 'Compton cooling'."); } +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +static INLINE void cooling_clean(struct cooling_function_data* cooling) {} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +static INLINE void cooling_struct_dump( + const struct cooling_function_data* cooling, FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, + FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); +} + #endif /* SWIFT_COOLING_COMPTON_H */ diff --git a/src/cooling/Compton/cooling_io.h b/src/cooling/Compton/cooling_io.h index d020587c920f781450a5183954bc6c429e461512..8fa3944ff78e7592da3978ee9465451c96e1d533 100644 --- a/src/cooling/Compton/cooling_io.h +++ b/src/cooling/Compton/cooling_io.h @@ -23,6 +23,7 @@ #include "../config.h" /* Local includes */ +#include "cooling.h" #include "io_properties.h" #ifdef HAVE_HDF5 @@ -41,11 +42,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif +INLINE static void convert_part_T(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + /** * @brief Specifies which particle fields to write to a dataset * - * Nothing to write for this scheme. - * + * @param parts The particle array. * @param xparts The extended particle array. * @param list The list of i/o properties to write. * @param cooling The #cooling_function_data @@ -53,10 +61,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - return 0; + list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, parts, + xparts, convert_part_T); + + return 1; } #endif /* SWIFT_COOLING_IO_COMPTON_H */ diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c new file mode 100644 index 0000000000000000000000000000000000000000..87c12e73d970b4d1caa6ce33c20666556463e08c --- /dev/null +++ b/src/cooling/EAGLE/cooling.c @@ -0,0 +1,973 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@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/>. + * + ******************************************************************************/ +/** + * @file src/cooling/EAGLE/cooling.c + * @brief EAGLE cooling functions + */ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <hdf5.h> +#include <math.h> +#include <time.h> + +/* Local includes. */ +#include "chemistry.h" +#include "cooling.h" +#include "cooling_rates.h" +#include "cooling_struct.h" +#include "cooling_tables.h" +#include "entropy_floor.h" +#include "error.h" +#include "exp10.h" +#include "hydro.h" +#include "interpolate.h" +#include "io_properties.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/* Maximum number of iterations for newton + * and bisection integration schemes */ +static const int newton_max_iterations = 15; +static const int bisection_max_iterations = 150; + +/* Tolerances for termination criteria. */ +static const float explicit_tolerance = 0.05; +static const float newton_tolerance = 1.0e-4; +static const float bisection_tolerance = 1.0e-6; +static const float rounding_tolerance = 1.0e-4; +static const double bracket_factor = 1.5; /* sqrt(1.1) */ +static const double newton_log_u_guess_cgs = 12.30103; /* log10(2e12) */ + +/** + * @brief Find the index of the current redshift along the redshift dimension + * of the cooling tables. + * + * Since the redshift table is not evenly spaced, compare z with each + * table value in decreasing order starting with the previous redshift index + * + * The returned difference is expressed in units of the table separation. This + * means dx = (x - table[i]) / (table[i+1] - table[i]). It is always between + * 0 and 1. + * + * @param z Redshift we are searching for. + * @param z_index (return) Index of the redshift in the table. + * @param dz (return) Difference in redshift between z and table[z_index]. + * @param cooling #cooling_function_data structure containing redshift table. + */ +__attribute__((always_inline)) INLINE void get_redshift_index( + const float z, int *z_index, float *dz, + struct cooling_function_data *restrict cooling) { + + /* Before the earliest redshift or before hydrogen reionization, flag for + * collisional cooling */ + if (z > cooling->H_reion_z) { + *z_index = eagle_cooling_N_redshifts; + *dz = 0.0; + } + + /* From reionization use the cooling tables */ + else if (z > cooling->Redshifts[eagle_cooling_N_redshifts - 1] && + z <= cooling->H_reion_z) { + *z_index = eagle_cooling_N_redshifts + 1; + *dz = 0.0; + } + + /* At the end, just use the last value */ + else if (z <= cooling->Redshifts[0]) { + *z_index = 0; + *dz = 0.0; + } + + /* Normal case: search... */ + else { + + /* start at the previous index and search */ + for (int i = cooling->previous_z_index; i >= 0; i--) { + if (z > cooling->Redshifts[i]) { + + *z_index = i; + cooling->previous_z_index = i; + + *dz = (z - cooling->Redshifts[i]) / + (cooling->Redshifts[i + 1] - cooling->Redshifts[i]); + break; + } + } + } +} + +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. Predominantly used to read cooling tables + * above and below the current redshift, if not already read in. + * + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + */ +void cooling_update(const struct cosmology *cosmo, + struct cooling_function_data *cooling) { + + /* Current redshift */ + const float redshift = cosmo->z; + + /* What is the current table index along the redshift axis? */ + int z_index = -1; + float dz = 0.f; + get_redshift_index(redshift, &z_index, &dz, cooling); + cooling->dz = dz; + + /* Do we already have the correct tables loaded? */ + if (cooling->z_index == z_index) return; + + /* Which table should we load ? */ + if (z_index >= eagle_cooling_N_redshifts) { + + if (z_index == eagle_cooling_N_redshifts + 1) { + + /* Bewtween re-ionization and first table */ + get_redshift_invariant_table(cooling, /* photodis=*/0); + + } else { + + /* Above re-ionization */ + get_redshift_invariant_table(cooling, /* photodis=*/1); + } + + } else { + + /* Normal case: two tables bracketing the current z */ + const int low_z_index = z_index; + const int high_z_index = z_index + 1; + + get_cooling_table(cooling, low_z_index, high_z_index); + } + + /* Store the currently loaded index */ + cooling->z_index = z_index; +} + +/** + * @brief Newton Raphson integration scheme to calculate particle cooling over + * timestep. This replaces bisection scheme used in EAGLE to minimize the + * number of array accesses. Integration defaults to bisection scheme (see + * function bisection_iter) if this function does not converge within a + * specified number of steps + * + * @param logu_init Initial guess for log(internal energy) + * @param u_ini Internal energy at beginning of hydro step + * @param n_H_index Particle hydrogen number density index + * @param d_n_H Particle hydrogen number density offset + * @param He_index Particle helium fraction index + * @param d_He Particle helium fraction offset + * @param He_reion_heat Heating due to helium reionization + * (only depends on redshift, so passed as parameter) + * @param p #part structure + * @param cosmo #cosmology structure + * @param cooling #cooling_function_data structure + * @param phys_const #phys_const data structure + * @param abundance_ratio Array of ratios of metal abundance to solar + * @param dt timestep + * @param bisection_flag Flag to identify if scheme failed to converge + */ +INLINE static float newton_iter( + float logu_init, double u_ini, int n_H_index, float d_n_H, int He_index, + float d_He, float He_reion_heat, struct part *restrict p, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct phys_const *restrict phys_const, + const float abundance_ratio[chemistry_element_count + 2], float dt, + int *bisection_flag) { + + double logu, logu_old; + double dLambdaNet_du = 0.0, LambdaNet; + + /* table bounds */ + const float log_table_bound_high = + (cooling->Therm[eagle_cooling_N_temperature - 1] - 0.05) / M_LOG10E; + const float log_table_bound_low = (cooling->Therm[0] + 0.05) / M_LOG10E; + + /* convert Hydrogen mass fraction in Hydrogen number density */ + const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + const double n_H = + hydro_get_physical_density(p, cosmo) * XH / phys_const->const_proton_mass; + const double n_H_cgs = n_H * cooling->number_density_to_cgs; + + /* compute ratefact = n_H * n_H / rho; Might lead to round-off error: + * replaced by equivalent expression below */ + const double ratefact_cgs = n_H_cgs * XH * cooling->inv_proton_mass_cgs; + + logu_old = logu_init; + logu = logu_old; + int i = 0; + + float LambdaNet_old = 0; + LambdaNet = 0; + do /* iterate to convergence */ + { + logu_old = logu; + LambdaNet_old = LambdaNet; + LambdaNet = (He_reion_heat / (dt * ratefact_cgs)) + + eagle_cooling_rate(logu_old, cosmo->z, n_H_cgs, abundance_ratio, + n_H_index, d_n_H, He_index, d_He, cooling, + &dLambdaNet_du); + + /* Newton iteration. For details on how the cooling equation is integrated + * see documentation in theory/Cooling/ */ + logu = logu_old - (1.0 - u_ini * exp(-logu_old) - + LambdaNet * ratefact_cgs * dt * exp(-logu_old)) / + (1.0 - dLambdaNet_du * ratefact_cgs * dt); + /* Check if first step passes over equilibrium solution, if it does adjust + * next guess */ + if (i == 1 && LambdaNet_old * LambdaNet < 0) logu = newton_log_u_guess_cgs; + + /* check whether iterations go within about 10% of the table bounds, + * if they do default to bisection method */ + if (logu > log_table_bound_high) { + i = newton_max_iterations; + break; + } else if (logu < log_table_bound_low) { + i = newton_max_iterations; + break; + } + + i++; + } while (fabs(logu - logu_old) > newton_tolerance && + i < newton_max_iterations); + if (i >= newton_max_iterations) { + /* flag to trigger bisection scheme */ + *bisection_flag = 1; + } + + return logu; +} + +/** + * @brief Bisection integration scheme + * + * @param u_ini_cgs Internal energy at beginning of hydro step in CGS. + * @param n_H_cgs Hydrogen number density in CGS. + * @param redshift Current redshift. + * @param n_H_index Particle hydrogen number density index. + * @param d_n_H Particle hydrogen number density offset. + * @param He_index Particle helium fraction index. + * @param d_He Particle helium fraction offset. + * @param Lambda_He_reion_cgs Cooling rate coming from He reionization. + * @param ratefact_cgs Multiplication factor to get a cooling rate. + * @param cooling #cooling_function_data structure. + * @param abundance_ratio Array of ratios of metal abundance to solar. + * @param dt_cgs timestep in CGS. + * @param ID ID of the particle (for debugging). + */ +INLINE static double bisection_iter( + const double u_ini_cgs, const double n_H_cgs, const double redshift, + int n_H_index, float d_n_H, int He_index, float d_He, + double Lambda_He_reion_cgs, double ratefact_cgs, + const struct cooling_function_data *restrict cooling, + const float abundance_ratio[chemistry_element_count + 2], double dt_cgs, + long long ID) { + + /* Bracketing */ + double u_lower_cgs = u_ini_cgs; + double u_upper_cgs = u_ini_cgs; + + /*************************************/ + /* Let's get a first guess */ + /*************************************/ + + double LambdaNet_cgs = + Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_ini_cgs), redshift, n_H_cgs, abundance_ratio, + n_H_index, d_n_H, He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + + /*************************************/ + /* Let's try to bracket the solution */ + /*************************************/ + + if (LambdaNet_cgs < 0) { + + /* we're cooling! */ + u_lower_cgs /= bracket_factor; + u_upper_cgs *= bracket_factor; + + /* Compute a new rate */ + LambdaNet_cgs = + Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_lower_cgs), redshift, n_H_cgs, abundance_ratio, + n_H_index, d_n_H, He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + + int i = 0; + while (u_lower_cgs - u_ini_cgs - LambdaNet_cgs * ratefact_cgs * dt_cgs > + 0 && + i < bisection_max_iterations) { + + u_lower_cgs /= bracket_factor; + u_upper_cgs /= bracket_factor; + + /* Compute a new rate */ + LambdaNet_cgs = Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_lower_cgs), redshift, n_H_cgs, + abundance_ratio, n_H_index, d_n_H, + He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + i++; + } + + if (i >= bisection_max_iterations) { + error( + "particle %llu exceeded max iterations searching for bounds when " + "cooling", + ID); + } + } else { + + /* we are heating! */ + u_lower_cgs /= bracket_factor; + u_upper_cgs *= bracket_factor; + + /* Compute a new rate */ + LambdaNet_cgs = + Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_upper_cgs), redshift, n_H_cgs, abundance_ratio, + n_H_index, d_n_H, He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + + int i = 0; + while (u_upper_cgs - u_ini_cgs - LambdaNet_cgs * ratefact_cgs * dt_cgs < + 0 && + i < bisection_max_iterations) { + + u_lower_cgs *= bracket_factor; + u_upper_cgs *= bracket_factor; + + /* Compute a new rate */ + LambdaNet_cgs = Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_upper_cgs), redshift, n_H_cgs, + abundance_ratio, n_H_index, d_n_H, + He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + i++; + } + + if (i >= bisection_max_iterations) { + error( + "particle %llu exceeded max iterations searching for bounds when " + "heating", + ID); + } + } + + /********************************************/ + /* We now have an upper and lower bound. */ + /* Let's iterate by reducing the bracketing */ + /********************************************/ + + /* bisection iteration */ + int i = 0; + double u_next_cgs; + + do { + + /* New guess */ + u_next_cgs = 0.5 * (u_lower_cgs + u_upper_cgs); + + /* New rate */ + LambdaNet_cgs = + Lambda_He_reion_cgs + + eagle_cooling_rate(log(u_next_cgs), redshift, n_H_cgs, abundance_ratio, + n_H_index, d_n_H, He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + + /* Where do we go next? */ + if (u_next_cgs - u_ini_cgs - LambdaNet_cgs * ratefact_cgs * dt_cgs > 0.0) { + u_upper_cgs = u_next_cgs; + } else { + u_lower_cgs = u_next_cgs; + } + + i++; + } while (fabs(u_upper_cgs - u_lower_cgs) / u_next_cgs > bisection_tolerance && + i < bisection_max_iterations); + + if (i >= bisection_max_iterations) + error("Particle id %llu failed to converge", ID); + + return u_upper_cgs; +} + +/** + * @brief Apply the cooling function to a particle. + * + * We want to compute u_new such that u_new = u_old + dt * du/dt(u_new, X), + * where X stands for the metallicity, density and redshift. These are + * kept constant. + * + * We first compute du/dt(u_old). If dt * du/dt(u_old) is small enough, we + * use an explicit integration and use this as our solution. + * + * Otherwise, we try to find a solution to the implicit time-integration + * problem. This leads to the root-finding problem: + * + * f(u_new) = u_new - u_old - dt * du/dt(u_new, X) = 0 + * + * We first try a few Newton-Raphson iteration if it does not converge, we + * revert to a bisection scheme. + * + * This is done by first bracketing the solution and then iterating + * towards the solution by reducing the window down to a certain tolerance. + * Note there is always at least one solution since + * f(+inf) is < 0 and f(-inf) is > 0. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param hydro_properties the hydro_props struct + * @param floor_props Properties of the entropy floor. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param dt The cooling time-step of this particle. + * @param dt_therm The hydro time-step of this particle. + */ +void cooling_cool_part(const struct phys_const *phys_const, + const struct unit_system *us, + const struct cosmology *cosmo, + const struct hydro_props *hydro_properties, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *cooling, + struct part *restrict p, struct xpart *restrict xp, + const float dt, const float dt_therm) { + + /* No cooling happens over zero time */ + if (dt == 0.) return; + +#ifdef SWIFT_DEBUG_CHECKS + if (cooling->Redshifts == NULL) + error( + "Cooling function has not been initialised. Did you forget the " + "--cooling runtime flag?"); +#endif + + /* Get internal energy at the last kick step */ + const float u_start = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Get the change in internal energy due to hydro forces */ + const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); + + /* Get internal energy at the end of the next kick step (assuming dt does not + * increase) */ + double u_0 = (u_start + hydro_du_dt * dt_therm); + + /* Check for minimal energy */ + u_0 = max(u_0, hydro_properties->minimal_internal_energy); + + /* Convert to CGS units */ + const double u_start_cgs = u_start * cooling->internal_energy_to_cgs; + const double u_0_cgs = u_0 * cooling->internal_energy_to_cgs; + const double dt_cgs = dt * units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* Change in redshift over the course of this time-step + (See cosmology theory document for the derivation) */ + const double delta_redshift = -dt * cosmo->H * cosmo->a_inv; + + /* Get this particle's abundance ratios compared to solar + * Note that we need to add S and Ca that are in the tables but not tracked + * by the particles themselves. + * The order is [H, He, C, N, O, Ne, Mg, Si, S, Ca, Fe] */ + float abundance_ratio[chemistry_element_count + 2]; + abundance_ratio_to_solar(p, cooling, abundance_ratio); + + /* Get the Hydrogen and Helium mass fractions */ + const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + const float XHe = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + + /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a + * metal-free Helium mass fraction as per the Wiersma+08 definition */ + const float HeFrac = XHe / (XH + XHe); + + /* convert Hydrogen mass fraction into Hydrogen number density */ + const double n_H = + hydro_get_physical_density(p, cosmo) * XH / phys_const->const_proton_mass; + const double n_H_cgs = n_H * cooling->number_density_to_cgs; + + /* ratefact = n_H * n_H / rho; Might lead to round-off error: replaced by + * equivalent expression below */ + const double ratefact_cgs = n_H_cgs * (XH * cooling->inv_proton_mass_cgs); + + /* compute hydrogen number density and helium fraction table indices and + * offsets (These are fixed for any value of u, so no need to recompute them) + */ + int He_index, n_H_index; + float d_He, d_n_H; + get_index_1d(cooling->HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_index, + &d_He); + get_index_1d(cooling->nH, eagle_cooling_N_density, log10(n_H_cgs), &n_H_index, + &d_n_H); + + /* Start by computing the cooling (heating actually) rate from Helium + re-ionization as this needs to be added on no matter what */ + + /* Get helium and hydrogen reheating term */ + const double Helium_reion_heat_cgs = eagle_helium_reionization_extraheat( + cooling->z_index, delta_redshift, cooling); + + /* Convert this into a rate */ + const double Lambda_He_reion_cgs = + Helium_reion_heat_cgs / (dt_cgs * ratefact_cgs); + + /* Let's compute the internal energy at the end of the step */ + double u_final_cgs; + + /* First try an explicit integration (note we ignore the derivative) */ + const double LambdaNet_cgs = + Lambda_He_reion_cgs + eagle_cooling_rate(log(u_0_cgs), cosmo->z, n_H_cgs, + abundance_ratio, n_H_index, + d_n_H, He_index, d_He, cooling, + /*dLambdaNet_du=*/NULL); + + /* if cooling rate is small, take the explicit solution */ + if (fabs(ratefact_cgs * LambdaNet_cgs * dt_cgs) < + explicit_tolerance * u_0_cgs) { + + u_final_cgs = u_0_cgs + ratefact_cgs * LambdaNet_cgs * dt_cgs; + + } else { + + int bisection_flag = 1; + + // MATTHIEU: TO DO restore the Newton-Raphson scheme + if (0 && cooling->newton_flag) { + + /* Ok, try a Newton-Raphson scheme instead */ + double log_u_final_cgs = + newton_iter(log(u_0_cgs), u_0_cgs, n_H_index, d_n_H, He_index, d_He, + Lambda_He_reion_cgs, p, cosmo, cooling, phys_const, + abundance_ratio, dt_cgs, &bisection_flag); + + /* Check if newton scheme sent us to a higher energy despite being in + a cooling regime If it did try newton scheme with a better guess. + (Guess internal energy near equilibrium solution). */ + if (LambdaNet_cgs < 0 && log_u_final_cgs > log(u_0_cgs)) { + bisection_flag = 0; + log_u_final_cgs = + newton_iter(newton_log_u_guess_cgs, u_0_cgs, n_H_index, d_n_H, + He_index, d_He, Lambda_He_reion_cgs, p, cosmo, cooling, + phys_const, abundance_ratio, dt_cgs, &bisection_flag); + } + + u_final_cgs = exp(log_u_final_cgs); + } + + /* Alright, all else failed, let's bisect */ + if (bisection_flag || !(cooling->newton_flag)) { + u_final_cgs = + bisection_iter(u_0_cgs, n_H_cgs, cosmo->z, n_H_index, d_n_H, He_index, + d_He, Lambda_He_reion_cgs, ratefact_cgs, cooling, + abundance_ratio, dt_cgs, p->id); + } + } + + /* Expected change in energy over the next kick step + (assuming no change in dt) */ + const double delta_u_cgs = u_final_cgs - u_start_cgs; + + /* Convert back to internal units */ + double delta_u = delta_u_cgs * cooling->internal_energy_from_cgs; + + /* We now need to check that we are not going to go below any of the limits */ + + /* Limit imposed by the entropy floor */ + const double A_floor = entropy_floor(p, cosmo, floor_props); + const double rho = hydro_get_physical_density(p, cosmo); + const double u_floor = gas_internal_energy_from_entropy(rho, A_floor); + + /* Absolute minimum */ + const double u_minimal = hydro_properties->minimal_internal_energy; + + /* Largest of both limits */ + const double u_limit = max(u_minimal, u_floor); + + /* First, check whether we may end up below the minimal energy after + * this step 1/2 kick + another 1/2 kick that could potentially be for + * a time-step twice as big. We hence check for 1.5 delta_u. */ + if (u_start + 1.5 * delta_u < u_limit) { + delta_u = (u_limit - u_start) / 1.5; + } + + /* Second, check whether the energy used in the prediction could get negative. + * We need to check for the 1/2 dt kick followed by a full time-step drift + * that could potentially be for a time-step twice as big. We hence check + * for 2.5 delta_u but this time against 0 energy not the minimum. + * To avoid numerical rounding bringing us below 0., we add a tiny tolerance. + */ + if (u_start + 2.5 * delta_u < 0.) { + delta_u = -u_start / (2.5 + rounding_tolerance); + } + + /* Turn this into a rate of change (including cosmology term) */ + const float cooling_du_dt = delta_u / dt_therm; + + /* Update the internal energy time derivative */ + hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt); + + /* Store the radiated energy */ + xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cooling_du_dt * dt; +} + +/** + * @brief Computes the cooling time-step. + * + * The time-step is not set by the properties of cooling. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const #phys_const data struct. + * @param us The internal system of units. + * @param cosmo #cosmology struct. + * @param hydro_props the properties of the hydro scheme. + * @param p #part data. + * @param xp extended particle data. + */ +__attribute__((always_inline)) INLINE float cooling_timestep( + const struct cooling_function_data *restrict cooling, + const struct phys_const *restrict phys_const, + const struct cosmology *restrict cosmo, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, const struct part *restrict p, + const struct xpart *restrict xp) { + + return FLT_MAX; +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * @param phys_const #phys_const data structure. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +__attribute__((always_inline)) INLINE void cooling_first_init_part( + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp) { + + xp->cooling_data.radiated_energy = 0.f; +} + +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * We use the Temperature table of the Wiersma+08 set. This computes the + * equilibirum temperature of a gas for a given redshift, Hydrogen density, + * internal energy per unit mass and Helium fraction. + * + * The temperature returned is consistent with the cooling rates. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +float cooling_get_temperature( + const struct phys_const *restrict phys_const, + const struct hydro_props *restrict hydro_props, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, const struct xpart *restrict xp) { + +#ifdef SWIFT_DEBUG_CHECKS + if (cooling->Redshifts == NULL) + error( + "Cooling function has not been initialised. Did you forget the " + "--temperature runtime flag?"); +#endif + + /* Get physical internal energy */ + const float u = hydro_get_physical_internal_energy(p, xp, cosmo); + const double u_cgs = u * cooling->internal_energy_to_cgs; + + /* Get the Hydrogen and Helium mass fractions */ + const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + const float XHe = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + + /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a + * metal-free Helium mass fraction as per the Wiersma+08 definition */ + const float HeFrac = XHe / (XH + XHe); + + /* Convert Hydrogen mass fraction into Hydrogen number density */ + const float rho = hydro_get_physical_density(p, cosmo); + const double n_H = rho * XH / phys_const->const_proton_mass; + const double n_H_cgs = n_H * cooling->number_density_to_cgs; + + /* compute hydrogen number density and helium fraction table indices and + * offsets */ + int He_index, n_H_index; + float d_He, d_n_H; + get_index_1d(cooling->HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_index, + &d_He); + get_index_1d(cooling->nH, eagle_cooling_N_density, log10(n_H_cgs), &n_H_index, + &d_n_H); + + /* Compute the log10 of the temperature by interpolating the table */ + const double log_10_T = eagle_convert_u_to_temp( + log10(u_cgs), cosmo->z, /*compute_dT_du=*/0, /*dT_du=*/NULL, n_H_index, + He_index, d_n_H, d_He, cooling); + + /* Undo the log! */ + return exp10(log_10_T); +} + +/** + * @brief Returns the total radiated energy by this particle. + * + * @param xp #xpart data struct + */ +__attribute__((always_inline)) INLINE float cooling_get_radiated_energy( + const struct xpart *restrict xp) { + + return xp->cooling_data.radiated_energy; +} + +/** + * @brief Initialises properties stored in the cooling_function_data struct + * + * @param parameter_file The parsed parameter file + * @param us Internal system of units data structure + * @param phys_const #phys_const data structure + * @param cooling #cooling_function_data struct to initialize + */ +void cooling_init_backend(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + struct cooling_function_data *cooling) { + + /* read some parameters */ + parser_get_param_string(parameter_file, "EAGLECooling:dir_name", + cooling->cooling_table_path); + cooling->H_reion_z = + parser_get_param_float(parameter_file, "EAGLECooling:H_reion_z"); + cooling->He_reion_z_centre = + parser_get_param_float(parameter_file, "EAGLECooling:He_reion_z_centre"); + cooling->He_reion_z_sigma = + parser_get_param_float(parameter_file, "EAGLECooling:He_reion_z_sigma"); + cooling->He_reion_heat_cgs = + parser_get_param_float(parameter_file, "EAGLECooling:He_reion_eV_p_H"); + + /* Optional parameters to correct the abundances */ + cooling->Ca_over_Si_ratio_in_solar = parser_get_opt_param_float( + parameter_file, "EAGLECooling:Ca_over_Si_in_solar", 1.f); + cooling->S_over_Si_ratio_in_solar = parser_get_opt_param_float( + parameter_file, "EAGLECooling:S_over_Si_in_solar", 1.f); + + /* Convert to cgs (units used internally by the cooling routines) */ + cooling->He_reion_heat_cgs *= + phys_const->const_electron_volt * + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + + /* Read in the list of redshifts */ + get_cooling_redshifts(cooling); + + /* Read in cooling table header */ + char fname[eagle_table_path_name_length + 12]; + sprintf(fname, "%sz_0.000.hdf5", cooling->cooling_table_path); + read_cooling_header(fname, cooling); + + /* Allocate space for cooling tables */ + allocate_cooling_tables(cooling); + + /* Compute conversion factors */ + cooling->internal_energy_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + cooling->internal_energy_from_cgs = 1. / cooling->internal_energy_to_cgs; + cooling->number_density_to_cgs = + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + /* Store some constants in CGS units */ + const double proton_mass_cgs = + phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + cooling->inv_proton_mass_cgs = 1. / proton_mass_cgs; + cooling->T_CMB_0 = phys_const->const_T_CMB_0 * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Compute the coefficient at the front of the Compton cooling expression */ + const double radiation_constant = + 4. * phys_const->const_stefan_boltzmann / phys_const->const_speed_light_c; + const double compton_coefficient = + 4. * radiation_constant * phys_const->const_thomson_cross_section * + phys_const->const_boltzmann_k / + (phys_const->const_electron_mass * phys_const->const_speed_light_c); + const float dimension_coefficient[5] = {1, 2, -3, 0, -5}; + + /* This should be ~1.0178085e-37 g cm^2 s^-3 K^-5 */ + const double compton_coefficient_cgs = + compton_coefficient * + units_general_cgs_conversion_factor(us, dimension_coefficient); + +#ifdef SWIFT_DEBUG_CHECKS + const double expected_compton_coefficient_cgs = 1.0178085e-37; + if (fabs(compton_coefficient_cgs - expected_compton_coefficient_cgs) / + expected_compton_coefficient_cgs > + 0.01) + error("compton coefficient incorrect."); +#endif + + /* And now the Compton rate */ + cooling->compton_rate_cgs = compton_coefficient_cgs * cooling->T_CMB_0 * + cooling->T_CMB_0 * cooling->T_CMB_0 * + cooling->T_CMB_0; + + /* Set the redshift indices to invalid values */ + cooling->z_index = -10; + + /* set previous_z_index and to last value of redshift table*/ + cooling->previous_z_index = eagle_cooling_N_redshifts - 2; + + /* Check if we are running with the newton scheme */ + cooling->newton_flag = parser_get_opt_param_int( + parameter_file, "EAGLECooling:newton_integration", 0); +} + +/** + * @brief Restore cooling tables (if applicable) after + * restart + * + * @param cooling the #cooling_function_data structure + * @param cosmo #cosmology structure + */ +void cooling_restore_tables(struct cooling_function_data *cooling, + const struct cosmology *cosmo) { + + /* Read redshifts */ + get_cooling_redshifts(cooling); + + /* Read cooling header */ + char fname[eagle_table_path_name_length + 12]; + sprintf(fname, "%sz_0.000.hdf5", cooling->cooling_table_path); + read_cooling_header(fname, cooling); + + /* Allocate memory for the tables */ + allocate_cooling_tables(cooling); + + /* Force a re-read of the cooling tables */ + cooling->z_index = -10; + cooling->previous_z_index = eagle_cooling_N_redshifts - 2; + cooling_update(cosmo, cooling); +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling #cooling_function_data struct. + */ +void cooling_print_backend(const struct cooling_function_data *cooling) { + + message("Cooling function is 'EAGLE'."); +} + +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * We simply free all the arrays. + * + * @param cooling the cooling data structure. + */ +void cooling_clean(struct cooling_function_data *cooling) { + + /* Free the side arrays */ + free(cooling->Redshifts); + free(cooling->nH); + free(cooling->Temp); + free(cooling->HeFrac); + free(cooling->Therm); + free(cooling->SolarAbundances); + free(cooling->SolarAbundances_inv); + + /* Free the tables */ + free(cooling->table.metal_heating); + free(cooling->table.electron_abundance); + free(cooling->table.temperature); + free(cooling->table.H_plus_He_heating); + free(cooling->table.H_plus_He_electron_abundance); +} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * @param cooling the struct + * @param stream the file stream + */ +void cooling_struct_dump(const struct cooling_function_data *cooling, + FILE *stream) { + + /* To make sure everything is restored correctly, we zero all the pointers to + tables. If they are not restored correctly, we would crash after restart on + the first call to the cooling routines. Helps debugging. */ + struct cooling_function_data cooling_copy = *cooling; + cooling_copy.Redshifts = NULL; + cooling_copy.nH = NULL; + cooling_copy.Temp = NULL; + cooling_copy.Therm = NULL; + cooling_copy.SolarAbundances = NULL; + cooling_copy.SolarAbundances_inv = NULL; + cooling_copy.table.metal_heating = NULL; + cooling_copy.table.H_plus_He_heating = NULL; + cooling_copy.table.H_plus_He_electron_abundance = NULL; + cooling_copy.table.temperature = NULL; + cooling_copy.table.electron_abundance = NULL; + + restart_write_blocks((void *)&cooling_copy, + sizeof(struct cooling_function_data), 1, stream, + "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Read the structure from the stream and restore the cooling tables by + * re-reading them. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +void cooling_struct_restore(struct cooling_function_data *cooling, FILE *stream, + const struct cosmology *cosmo) { + restart_read_blocks((void *)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); + + cooling_restore_tables(cooling, cosmo); +} diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h index 8d74222c78deab0e6790a06dca847033556833a7..d95c75e58aecfd8fb4816f1e50c7a3f379a08e51 100644 --- a/src/cooling/EAGLE/cooling.h +++ b/src/cooling/EAGLE/cooling.h @@ -21,116 +21,65 @@ /** * @file src/cooling/EAGLE/cooling.h - * @brief EAGLE cooling function + * @brief EAGLE cooling function declarations */ -/* Config parameters. */ -#include "../config.h" +/* Local includes. */ +#include "cooling_struct.h" -/* Some standard headers. */ -#include <float.h> -#include <math.h> +struct part; +struct xpart; +struct cosmology; +struct hydro_props; +struct entropy_floor_properties; -/* Local includes. */ -#include "error.h" -#include "hydro.h" -#include "parser.h" -#include "part.h" -#include "physical_constants.h" -#include "units.h" +void cooling_update(const struct cosmology *cosmo, + struct cooling_function_data *cooling); -/** - * @brief Apply the cooling function to a particle. - * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cosmo The current cosmological model. - * @param cooling The #cooling_function_data used in the run. - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - * @param dt The time-step of this particle. - */ -__attribute__((always_inline)) INLINE static void cooling_cool_part( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct hydro_props* hydro_props, - const struct cooling_function_data* restrict cooling, - struct part* restrict p, struct xpart* restrict xp, const float dt, - const float dt_therm) {} +void cooling_cool_part(const struct phys_const *phys_const, + const struct unit_system *us, + const struct cosmology *cosmo, + const struct hydro_props *hydro_properties, + const struct entropy_floor_properties *floor_props, + const struct cooling_function_data *cooling, + struct part *restrict p, struct xpart *restrict xp, + const float dt, const float dt_therm); -/** - * @brief Computes the cooling time-step. - * - * @param cooling The #cooling_function_data used in the run. - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cosmo The current cosmological model. - * @param p Pointer to the particle data. - */ -__attribute__((always_inline)) INLINE static float cooling_timestep( - const struct cooling_function_data* restrict cooling, - const struct phys_const* restrict phys_const, - const struct cosmology* restrict cosmo, - const struct unit_system* restrict us, - const struct hydro_props* hydro_props, const struct part* restrict p, - const struct xpart* restrict xp) { +float cooling_timestep(const struct cooling_function_data *restrict cooling, + const struct phys_const *restrict phys_const, + const struct cosmology *restrict cosmo, + const struct unit_system *restrict us, + const struct hydro_props *hydro_props, + const struct part *restrict p, + const struct xpart *restrict xp); - return FLT_MAX; -} +void cooling_first_init_part( + const struct phys_const *restrict phys_const, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, struct xpart *restrict xp); -/** - * @brief Sets the cooling properties of the (x-)particles to a valid start - * state. - * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. - * @param cosmo The current cosmological model. - * @param cooling The properties of the cooling function. - * @param p Pointer to the particle data. - * @param xp Pointer to the extended particle data. - */ -__attribute__((always_inline)) INLINE static void cooling_first_init_part( - const struct phys_const* restrict phys_const, - const struct unit_system* restrict us, - const struct cosmology* restrict cosmo, - const struct cooling_function_data* restrict cooling, - const struct part* restrict p, struct xpart* restrict xp) {} +float cooling_get_temperature( + const struct phys_const *restrict phys_const, + const struct hydro_props *restrict hydro_props, + const struct unit_system *restrict us, + const struct cosmology *restrict cosmo, + const struct cooling_function_data *restrict cooling, + const struct part *restrict p, const struct xpart *restrict xp); -/** - * @brief Returns the total radiated energy by this particle. - * - * @param xp The extended particle data - */ -__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( - const struct xpart* restrict xp) { +float cooling_get_radiated_energy(const struct xpart *restrict xp); - return 0.f; -} +void cooling_init_backend(struct swift_params *parameter_file, + const struct unit_system *us, + const struct phys_const *phys_const, + struct cooling_function_data *cooling); -/** - * @brief Initialises the cooling properties. - * - * @param parameter_file The parsed parameter file. - * @param us The current internal system of units. - * @param phys_const The physical constants in internal units. - * @param cooling The cooling properties to initialize - */ -static INLINE void cooling_init_backend(struct swift_params* parameter_file, - const struct unit_system* us, - const struct phys_const* phys_const, - struct cooling_function_data* cooling) { -} +void cooling_restore_tables(struct cooling_function_data *cooling, + const struct cosmology *cosmo); -/** - * @brief Prints the properties of the cooling model to stdout. - * - * @param cooling The properties of the cooling function. - */ -static INLINE void cooling_print_backend( - const struct cooling_function_data* cooling) { +void cooling_print_backend(const struct cooling_function_data *cooling); - message("Cooling function is 'EAGLE'."); -} +void cooling_clean(struct cooling_function_data *data); #endif /* SWIFT_COOLING_EAGLE_H */ diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h index b8f89717b450719a90c13e26ec07a51738b35f49..5508153afc094d84383893f55ac0362a6d427b24 100644 --- a/src/cooling/EAGLE/cooling_io.h +++ b/src/cooling/EAGLE/cooling_io.h @@ -23,13 +23,16 @@ #include "../config.h" /* Local includes */ +#include "cooling.h" #include "io_properties.h" #ifdef HAVE_HDF5 /** - * @brief Writes the current model of cooling to the file + * @brief Writes the current model of cooling to the file. + * * @param h_grp The HDF5 group in which to write + * @param cooling The #cooling_function_data */ __attribute__((always_inline)) INLINE static void cooling_write_flavour( hid_t h_grp, const struct cooling_function_data* cooling) { @@ -38,9 +41,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif +INLINE static void convert_part_T(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + /** * @brief Specifies which particle fields to write to a dataset * + * @param parts The particle array. * @param xparts The extended data particle array. * @param list The list of i/o properties to write. * @param cooling The #cooling_function_data @@ -48,9 +60,13 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - return 0; + + list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, parts, + xparts, convert_part_T); + return 1; } #endif /* SWIFT_COOLING_EAGLE_IO_H */ diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h new file mode 100644 index 0000000000000000000000000000000000000000..d315a5ba339956828505c5f48165abdd2b2e0486 --- /dev/null +++ b/src/cooling/EAGLE/cooling_rates.h @@ -0,0 +1,764 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@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. */ +#ifndef SWIFT_EAGLE_COOLING_RATES_H +#define SWIFT_EAGLE_COOLING_RATES_H + +#include "../config.h" + +/* Local includes. */ +#include "cooling_tables.h" +#include "exp10.h" +#include "interpolate.h" + +/** + * @brief Compute ratio of mass fraction to solar mass fraction + * for each element carried by a given particle. + * + * The solar abundances are taken from the tables themselves. + * + * The EAGLE chemistry model does not track S and Ca. We assume + * that their abundance with respect to solar is the same as + * the ratio for Si. + * We optionally apply a correction if the user asked for a different + * ratio. + * + * We also re-order the elements such that they match the order of the + * tables. This is [H, He, C, N, O, Ne, Mg, Si, S, Ca, Fe]. + * + * The solar abundances table (from the cooling struct) is arranged as + * [H, He, C, N, O, Ne, Mg, Si, S, Ca, Fe]. + * + * @param p Pointer to #part struct. + * @param cooling #cooling_function_data struct. + * @param ratio_solar (return) Array of ratios to solar abundances. + */ +__attribute__((always_inline)) INLINE void abundance_ratio_to_solar( + const struct part *p, const struct cooling_function_data *cooling, + float ratio_solar[chemistry_element_count + 2]) { + + ratio_solar[0] = p->chemistry_data.metal_mass_fraction[chemistry_element_H] * + cooling->SolarAbundances_inv[0 /* H */]; + + ratio_solar[1] = p->chemistry_data.metal_mass_fraction[chemistry_element_He] * + cooling->SolarAbundances_inv[1 /* He */]; + + ratio_solar[2] = p->chemistry_data.metal_mass_fraction[chemistry_element_C] * + cooling->SolarAbundances_inv[2 /* C */]; + + ratio_solar[3] = p->chemistry_data.metal_mass_fraction[chemistry_element_N] * + cooling->SolarAbundances_inv[3 /* N */]; + + ratio_solar[4] = p->chemistry_data.metal_mass_fraction[chemistry_element_O] * + cooling->SolarAbundances_inv[4 /* O */]; + + ratio_solar[5] = p->chemistry_data.metal_mass_fraction[chemistry_element_Ne] * + cooling->SolarAbundances_inv[5 /* Ne */]; + + ratio_solar[6] = p->chemistry_data.metal_mass_fraction[chemistry_element_Mg] * + cooling->SolarAbundances_inv[6 /* Mg */]; + + ratio_solar[7] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */]; + + /* For S, we use the same ratio as Si */ + ratio_solar[8] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->S_over_Si_ratio_in_solar; + + /* For Ca, we use the same ratio as Si */ + ratio_solar[9] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->Ca_over_Si_ratio_in_solar; + + ratio_solar[10] = + p->chemistry_data.metal_mass_fraction[chemistry_element_Fe] * + cooling->SolarAbundances_inv[10 /* Fe */]; +} + +/** + * @brief Computes the extra heat from Helium reionisation at a given redshift. + * + * We follow the implementation of Wiersma et al. 2009, MNRAS, 399, 574-600, + * section. 2. The calculation returns energy in CGS. + * + * Note that delta_z is negative. + * + * @param z The current redshift. + * @param delta_z The change in redhsift over the course of this time-step. + * @param cooling The #cooling_function_data used in the run. + * @return Helium reionization energy in CGS units. + */ +__attribute__((always_inline)) INLINE double +eagle_helium_reionization_extraheat( + double z, double delta_z, const struct cooling_function_data *cooling) { + +#ifdef SWIFT_DEBUG_CHECKS + if (delta_z > 0.f) error("Invalid value for delta_z. Should be negative."); +#endif + + /* Recover the values we need */ + const double z_centre = cooling->He_reion_z_centre; + const double z_sigma = cooling->He_reion_z_sigma; + const double heat_cgs = cooling->He_reion_heat_cgs; + + double extra_heat = 0.; + + /* Integral of the Gaussian between z and z - delta_z */ + extra_heat += erf((z - delta_z - z_centre) / (M_SQRT2 * z_sigma)); + extra_heat -= erf((z - z_centre) / (M_SQRT2 * z_sigma)); + + /* Multiply by the normalisation factor */ + extra_heat *= heat_cgs * 0.5; + + return extra_heat; +} + +/** + * @brief Computes the log_10 of the temperature corresponding to a given + * internal energy, hydrogen number density, Helium fraction and redshift. + * + * Note that the redshift is implicitly passed in via the currently loaded + * tables in the #cooling_function_data. + * + * For the low-z case, we interpolate the flattened 4D table 'u_to_temp' that + * is arranged in the following way: + * - 1st dim: redshift, length = eagle_cooling_N_loaded_redshifts + * - 2nd dim: Hydrogen density, length = eagle_cooling_N_density + * - 3rd dim: Helium fraction, length = eagle_cooling_N_He_frac + * - 4th dim: Internal energy, length = eagle_cooling_N_temperature + * + * For the high-z case, we interpolate the flattened 3D table 'u_to_temp' that + * is arranged in the following way: + * - 1st dim: Hydrogen density, length = eagle_cooling_N_density + * - 2nd dim: Helium fraction, length = eagle_cooling_N_He_frac + * - 3rd dim: Internal energy, length = eagle_cooling_N_temperature + * + * @param log_10_u_cgs Log base 10 of internal energy in cgs. + * @param redshift Current redshift. + * @param n_H_index Index along the Hydrogen density dimension. + * @param He_index Index along the Helium fraction dimension. + * @param d_n_H Offset between Hydrogen density and table[n_H_index]. + * @param d_He Offset between helium fraction and table[He_index]. + * @param cooling #cooling_function_data structure. + * + * @param compute_dT_du Do we want to compute dT/du ? + * @param dT_du (return) The value of dT/du + * + * @return log_10 of the temperature. + */ +__attribute__((always_inline)) INLINE double eagle_convert_u_to_temp( + const double log_10_u_cgs, const float redshift, const int compute_dT_du, + float *dT_du, int n_H_index, int He_index, float d_n_H, float d_He, + const struct cooling_function_data *restrict cooling) { + + /* Get index of u along the internal energy axis */ + int u_index; + float d_u; + get_index_1d(cooling->Therm, eagle_cooling_N_temperature, log_10_u_cgs, + &u_index, &d_u); + + /* Interpolate temperature table to return temperature for current + * internal energy (use 3D interpolation for high redshift table, + * otherwise 4D) */ + float log_10_T; + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + log_10_T = interpolation_3d(cooling->table.temperature, /* */ + n_H_index, He_index, u_index, /* */ + d_n_H, d_He, d_u, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + } else { + + log_10_T = + interpolation_4d(cooling->table.temperature, /* */ + /*z_index=*/0, n_H_index, He_index, u_index, /* */ + cooling->dz, d_n_H, d_He, d_u, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + } + + if (compute_dT_du) { + + float log_10_T_high, log_10_T_low; + + /* Interpolate temperature table to return temperature for internal energy + * at grid point above current internal energy for computing dT_du used for + * calculation of dlambda_du in cooling.c (use 3D interpolation for high + * redshift table, otherwise 4D) */ + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + log_10_T_high = interpolation_3d(cooling->table.temperature, /* */ + n_H_index, He_index, u_index, /* */ + d_n_H, d_He, /*delta_u=*/1.f, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + + } else { + + log_10_T_high = + interpolation_4d(cooling->table.temperature, /* */ + /*z_index=*/0, n_H_index, He_index, u_index, /* */ + cooling->dz, d_n_H, d_He, /*delta_u=*/1.f, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + } + + /* Interpolate temperature table to return temperature for internal energy + * at grid point below current internal energy for computing dT_du used for + * calculation of dlambda_du in cooling.c (use 3D interpolation for high + * redshift table, otherwise 4D) */ + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + log_10_T_low = interpolation_3d(cooling->table.temperature, /* */ + n_H_index, He_index, u_index, /* */ + d_n_H, d_He, /*delta_u=*/0.f, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + + } else { + + log_10_T_low = + interpolation_4d(cooling->table.temperature, /* */ + /*z_index=*/0, n_H_index, He_index, u_index, /* */ + cooling->dz, d_n_H, d_He, /*delta_u=*/0.f, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + } + + /* Calculate dT/du */ + const float delta_u = exp(cooling->Therm[u_index + 1] * M_LN10) - + exp(cooling->Therm[u_index] * M_LN10); + *dT_du = + (exp(M_LN10 * log_10_T_high) - exp(M_LN10 * log_10_T_low)) / delta_u; + } + + /* Special case for temperatures below the start of the table */ + if (u_index == 0 && d_u == 0.f) { + + /* The temperature is multiplied by u / 10^T[0] + * where T[0] is the first entry in the table */ + log_10_T += log_10_u_cgs - cooling->Temp[0]; + } + + return log_10_T; +} + +/** + * @brief Compute the Compton cooling rate from the CMB at a given + * redshift, electron abundance, temperature and Hydrogen density. + * + * Uses an analytic formula. + * + * @param cooling The #cooling_function_data used in the run. + * @param redshift The current redshift. + * @param n_H_cgs The Hydrogen number density in CGS units. + * @param temperature The temperature. + * @param electron_abundance The electron abundance. + */ +__attribute__((always_inline)) INLINE double eagle_Compton_cooling_rate( + const struct cooling_function_data *cooling, const double redshift, + const double n_H_cgs, const double temperature, + const double electron_abundance) { + + const double zp1 = 1. + redshift; + const double zp1p2 = zp1 * zp1; + const double zp1p4 = zp1p2 * zp1p2; + + /* CMB temperature at this redshift */ + const double T_CMB = cooling->T_CMB_0 * zp1; + + /* Compton cooling rate */ + return cooling->compton_rate_cgs * (temperature - T_CMB) * zp1p4 * + electron_abundance / n_H_cgs; +} + +/** + * @brief Computes the cooling rate corresponding to a given internal energy, + * hydrogen number density, Helium fraction, redshift and metallicity from + * all the possible channels. + * + * 1) Metal-free cooling: + * We interpolate the flattened 4D table 'H_and_He_net_heating' that is + * arranged in the following way: + * - 1st dim: redshift, length = eagle_cooling_N_loaded_redshifts + * - 2nd dim: Hydrogen density, length = eagle_cooling_N_density + * - 3rd dim: Helium fraction, length = eagle_cooling_N_He_frac + * - 4th dim: Internal energy, length = eagle_cooling_N_temperature + * + * 2) Electron abundance + * We compute the electron abundance by interpolating the flattened 4d table + * 'H_and_He_electron_abundance' that is arranged in the following way: + * - 1st dim: redshift, length = eagle_cooling_N_loaded_redshifts + * - 2nd dim: Hydrogen density, length = eagle_cooling_N_density + * - 3rd dim: Helium fraction, length = eagle_cooling_N_He_frac + * - 4th dim: Internal energy, length = eagle_cooling_N_temperature + * + * 3) Compton cooling is applied via the analytic formula. + * + * 4) Solar electron abudance + * We compute the solar electron abundance by interpolating the flattened 3d + * table 'solar_electron_abundance' that is arranged in the following way: + * - 1st dim: redshift, length = eagle_cooling_N_loaded_redshifts + * - 2nd dim: Hydrogen density, length = eagle_cooling_N_density + * - 3rd dim: Internal energy, length = eagle_cooling_N_temperature + * + * 5) Metal-line cooling + * For each tracked element we interpolate the flattened 4D table + * 'table_metals_net_heating' that is arrange in the following way: + * - 1st dim: element, length = eagle_cooling_N_metal + * - 2nd dim: redshift, length = eagle_cooling_N_loaded_redshifts + * - 3rd dim: Hydrogen density, length = eagle_cooling_N_density + * - 4th dim: Internal energy, length = eagle_cooling_N_temperature + * + * Note that this is a fake 4D interpolation as we do not interpolate + * along the 1st dimension. We just do this once per element. + * + * Since only the temperature changes when cooling a given particle, + * the redshift, hydrogen number density and helium fraction indices + * and offsets passed in. + * + * If the arguement dlambda_du is non-NULL, the routine also + * calculates derivative of cooling rate with respect to internal + * energy. + * + * If the argument element_lambda is non-NULL, the routine also + * returns the cooling rate per element in the array. + * + * @param log10_u_cgs Log base 10 of internal energy per unit mass in CGS units. + * @param redshift The current redshift + * @param n_H_cgs The Hydrogen number density in CGS units. + * @param solar_ratio Array of ratios of particle metal abundances + * to solar metal abundances + * + * @param n_H_index Particle hydrogen number density index + * @param d_n_H Particle hydrogen number density offset + * @param He_index Particle helium fraction index + * @param d_He Particle helium fraction offset + * @param cooling Cooling data structure + * + * @param dlambda_du (return) Derivative of the cooling rate with respect to u. + * @param element_lambda (return) Cooling rate from each element + * + * @return The cooling rate + */ +INLINE static double eagle_metal_cooling_rate( + double log10_u_cgs, double redshift, double n_H_cgs, + const float solar_ratio[chemistry_element_count + 2], int n_H_index, + float d_n_H, int He_index, float d_He, + const struct cooling_function_data *restrict cooling, double *dlambda_du, + double *element_lambda) { + +#ifdef TO_BE_DONE + /* used for calculating dlambda_du */ + double temp_lambda_high = 0, temp_lambda_low = 0; + double h_plus_he_electron_abundance_high = 0; + double h_plus_he_electron_abundance_low = 0; + double solar_electron_abundance_high = 0; + double solar_electron_abundance_low = 0; + double elem_cool_low = 0, elem_cool_high = 0; +#endif + + /* We only need dT_du if dLambda_du is non-NULL */ + const int compute_dT_du = (dlambda_du != NULL) ? 1 : 0; + + /* Temperature */ + float dT_du = -1.f; + const double log_10_T = + eagle_convert_u_to_temp(log10_u_cgs, redshift, compute_dT_du, &dT_du, + n_H_index, He_index, d_n_H, d_He, cooling); + + /* Get index along temperature dimension of the tables */ + int T_index; + float d_T; + get_index_1d(cooling->Temp, eagle_cooling_N_temperature, log_10_T, &T_index, + &d_T); + +#ifdef TO_BE_DONE + /* Difference between entries on the temperature table around u */ + const float delta_T = exp(M_LN10 * cooling->Temp[T_index + 1]) - + exp(M_LN10 * cooling->Temp[T_index]); +#endif + + /**********************/ + /* Metal-free cooling */ + /**********************/ + + double Lambda_free; + + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + /* If we're using the high redshift tables then we don't interpolate + * in redshift */ + Lambda_free = interpolation_3d(cooling->table.H_plus_He_heating, /* */ + n_H_index, He_index, T_index, /* */ + d_n_H, d_He, d_T, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du. Pass in NULL pointer for + * dlambda_du in order to skip */ + if (dlambda_du != NULL) { + temp_lambda_high = interpolation_3d( + cooling->table.H_plus_He_heating, n_H_index, He_index, T_index, d_n_h, + d_He, 1.f, cooling->N_nH, cooling->N_He, cooling->N_Temp); + temp_lambda_low = interpolation_3d( + cooling->table.H_plus_He_heating, n_H_index, He_index, T_index, d_n_h, + d_He, 0.f, cooling->N_nH, cooling->N_He, cooling->N_Temp); + } +#endif + + } else { + + /* Using normal tables, have to interpolate in redshift */ + Lambda_free = + interpolation_4d(cooling->table.H_plus_He_heating, /* */ + /*z_index=*/0, n_H_index, He_index, T_index, /* */ + cooling->dz, d_n_H, d_He, d_T, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + if (dlambda_du != NULL) { + temp_lambda_high = + interpolation_4d(cooling->table.H_plus_He_heating, 0, n_H_index, + He_index, T_index, cooling->dz, d_n_h, d_He, 1.f, 2, + cooling->N_nH, cooling->N_He, cooling->N_Temp); + temp_lambda_low = + interpolation_4d(cooling->table.H_plus_He_heating, 0, n_H_index, + He_index, T_index, cooling->dz, d_n_h, d_He, 0.f, 2, + cooling->N_nH, cooling->N_He, cooling->N_Temp); + } +#endif + } + +#ifdef TO_BE_DONE + if (dlambda_du != NULL) { + *dlambda_du += (temp_lambda_high - temp_lambda_low) / delta_T * dT_du; + } +#endif + + /* If we're testing cooling rate contributions write to array */ + if (element_lambda != NULL) { + element_lambda[0] = Lambda_free; + } + + /**********************/ + /* Electron abundance */ + /**********************/ + + double H_plus_He_electron_abundance; + + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + H_plus_He_electron_abundance = + interpolation_3d(cooling->table.H_plus_He_electron_abundance, /* */ + n_H_index, He_index, T_index, /* */ + d_n_H, d_He, d_T, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du. Pass in NULL pointer for + * dlambda_du in order to skip */ + + h_plus_he_electron_abundance_high = + interpolation_3d(cooling->table.H_plus_He_electron_abundance, n_H_index, + He_index, T_index, d_n_h, d_He, 1.f, cooling->N_nH, + cooling->N_He, cooling->N_Temp); + h_plus_he_electron_abundance_low = + interpolation_3d(cooling->table.H_plus_He_electron_abundance, n_H_index, + He_index, T_index, d_n_h, d_He, 0.f, cooling->N_nH, + cooling->N_He, cooling->N_Temp); + +#endif + + } else { + + H_plus_He_electron_abundance = + interpolation_4d(cooling->table.H_plus_He_electron_abundance, /* */ + /*z_index=*/0, n_H_index, He_index, T_index, /* */ + cooling->dz, d_n_H, d_He, d_T, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_He_frac, /* */ + eagle_cooling_N_temperature); /* */ + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + h_plus_he_electron_abundance_high = + interpolation_4d(cooling->table.H_plus_He_electron_abundance, 0, + n_H_index, He_index, T_index, cooling->dz, d_n_h, d_He, + 1.f, 2, cooling->N_nH, cooling->N_He, cooling->N_Temp); + h_plus_he_electron_abundance_low = + interpolation_4d(cooling->table.H_plus_He_electron_abundance, 0, + n_H_index, He_index, T_index, cooling->dz, d_n_h, d_He, + 0.f, 2, cooling->N_nH, cooling->N_He, cooling->N_Temp); +#endif + } + + /**********************/ + /* Compton cooling */ + /**********************/ + + double Lambda_Compton = 0.; + + /* Do we need to add the inverse Compton cooling? */ + /* It is *not* stored in the tables before re-ionisation */ + if ((redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) || + (redshift > cooling->H_reion_z)) { + + const double T = exp10(log_10_T); + + /* Note the minus sign */ + Lambda_Compton -= eagle_Compton_cooling_rate(cooling, redshift, n_H_cgs, T, + H_plus_He_electron_abundance); + } + + /* If we're testing cooling rate contributions write to array */ + if (element_lambda != NULL) { + element_lambda[1] = Lambda_Compton; + } + + /*******************************/ + /* Solar electron abundance */ + /*******************************/ + + double solar_electron_abundance; + + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + /* If we're using the high redshift tables then we don't interpolate + * in redshift */ + solar_electron_abundance = + interpolation_2d(cooling->table.electron_abundance, /* */ + n_H_index, T_index, /* */ + d_n_H, d_T, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_temperature); /* */ + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + if (dlambda_du != NULL) { + solar_electron_abundance_high = + interpolation_2d(cooling->table.electron_abundance, n_H_index, + T_index, d_n_h, 1.f, cooling->N_nH, cooling->N_Temp); + solar_electron_abundance_low = + interpolation_2d(cooling->table.electron_abundance, n_H_index, + T_index, d_n_h, 0.f, cooling->N_nH, cooling->N_Temp); + } +#endif + + } else { + + /* Using normal tables, have to interpolate in redshift */ + solar_electron_abundance = + interpolation_3d(cooling->table.electron_abundance, /* */ + /*z_index=*/0, n_H_index, T_index, /* */ + cooling->dz, d_n_H, d_T, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_temperature); /* */ + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + if (dlambda_du != NULL) { + solar_electron_abundance_high = interpolation_3d( + cooling->table.electron_abundance, 0, n_H_index, T_index, cooling->dz, + d_n_h, 1.f, 2, cooling->N_nH, cooling->N_Temp); + solar_electron_abundance_low = interpolation_3d( + cooling->table.electron_abundance, 0, n_H_index, T_index, cooling->dz, + d_n_h, 0.f, 2, cooling->N_nH, cooling->N_Temp); + } +#endif + } + + const double electron_abundance_ratio = + H_plus_He_electron_abundance / solar_electron_abundance; + + /**********************/ + /* Metal-line cooling */ + /**********************/ + + /* for each element the cooling rate is multiplied by the ratio of H, He + * electron abundance to solar electron abundance then by the ratio of the + * particle metal abundance to solar metal abundance. */ + + double lambda_metal[eagle_cooling_N_metal + 2] = {0.}; + + if (redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) { + + /* Loop over the metals (ignore H and He) */ + for (int elem = 2; elem < eagle_cooling_N_metal + 2; elem++) { + + if (solar_ratio[elem] > 0.) { + + /* Note that we do not interpolate along the x-axis + * (element dimension) */ + lambda_metal[elem] = + interpolation_3d_no_x(cooling->table.metal_heating, /* */ + elem - 2, n_H_index, T_index, /* */ + /*delta_elem=*/0.f, d_n_H, d_T, /* */ + eagle_cooling_N_metal, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_temperature); /* */ + + lambda_metal[elem] *= electron_abundance_ratio; + lambda_metal[elem] *= solar_ratio[elem]; + } + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + if (dlambda_du != NULL) { + elem_cool_high = interpolation_3d_no_x( + cooling->table.metal_heating, elem, n_H_index, T_index, 0.f, d_n_h, + 1.f, cooling->N_Elements, cooling->N_nH, cooling->N_Temp); + + elem_cool_low = interpolation_3d_no_x( + cooling->table.metal_heating, elem, n_H_index, T_index, 0.f, d_n_h, + 0.f, cooling->N_nH, cooling->N_Temp, cooling->N_Elements); + + *dlambda_du += (elem_cool_high * h_plus_he_electron_abundance_high / + solar_electron_abundance_high - + elem_cool_low * h_plus_he_electron_abundance_low / + solar_electron_abundance_low) / + delta_T * dT_du * solar_ratio[elem + 2]; + } +#endif + } + + } else { + + /* Loop over the metals (ignore H and He) */ + for (int elem = 2; elem < eagle_cooling_N_metal + 2; elem++) { + + if (solar_ratio[elem] > 0.) { + + /* Note that we do not interpolate along the x-axis + * (element dimension) */ + lambda_metal[elem] = interpolation_4d_no_x( + cooling->table.metal_heating, /* */ + elem - 2, /*z_index=*/0, n_H_index, T_index, /* */ + /*delta_elem=*/0.f, cooling->dz, d_n_H, d_T, /* */ + eagle_cooling_N_metal, /* */ + eagle_cooling_N_loaded_redshifts, /* */ + eagle_cooling_N_density, /* */ + eagle_cooling_N_temperature); /* */ + + lambda_metal[elem] *= electron_abundance_ratio; + lambda_metal[elem] *= solar_ratio[elem]; + } + +#ifdef TO_BE_DONE + /* compute values at temperature gridpoints above and below input + * temperature for calculation of dlambda_du */ + if (dlambda_du != NULL) { + elem_cool_high = interpolation_4d_no_x( + cooling->table.metal_heating, elem, 0, n_H_index, T_index, 0., + cooling->dz, d_n_h, 1.f, cooling->N_Elements, 2, cooling->N_nH, + cooling->N_Temp); + + elem_cool_low = interpolation_4d_no_x( + cooling->table.metal_heating, elem, 0, n_H_index, T_index, 0., + cooling->dz, d_n_h, 0.f, cooling->N_Elements, 2, cooling->N_nH, + cooling->N_Temp); + + *dlambda_du += (elem_cool_high * h_plus_he_electron_abundance_high / + solar_electron_abundance_high - + elem_cool_low * h_plus_he_electron_abundance_low / + solar_electron_abundance_low) / + delta_T * dT_du * solar_ratio[elem + 2]; + } +#endif + } + } + + if (element_lambda != NULL) { + for (int elem = 2; elem < eagle_cooling_N_metal + 2; ++elem) { + element_lambda[elem] = lambda_metal[elem]; + } + } + + /* Sum up all the contributions */ + double Lambda_net = Lambda_free + Lambda_Compton; + for (int elem = 2; elem < eagle_cooling_N_metal + 2; ++elem) { + Lambda_net += lambda_metal[elem]; + } + + return Lambda_net; +} + +/** + * @brief Wrapper function used to calculate cooling rate and dLambda_du. + * Table indices and offsets for redshift, hydrogen number density and + * helium fraction are passed it so as to compute them only once per particle. + * + * @param log_u_cgs Natural log of internal energy per unit mass in CGS units. + * @param redshift The current redshift. + * @param n_H_cgs Hydrogen number density in CGS units. + * @param abundance_ratio Ratio of element abundance to solar. + * + * @param n_H_index Particle hydrogen number density index + * @param d_n_H Particle hydrogen number density offset + * @param He_index Particle helium fraction index + * @param d_He Particle helium fraction offset + * @param cooling #cooling_function_data structure + * + * @param dLambdaNet_du (return) Derivative of the cooling rate with respect to + * u. + * + * @return The cooling rate + */ +INLINE static double eagle_cooling_rate( + double log_u_cgs, double redshift, double n_H_cgs, + const float abundance_ratio[chemistry_element_count + 2], int n_H_index, + float d_n_H, int He_index, float d_He, + const struct cooling_function_data *restrict cooling, + double *dLambdaNet_du) { + + return eagle_metal_cooling_rate(log_u_cgs / M_LN10, redshift, n_H_cgs, + abundance_ratio, n_H_index, d_n_H, He_index, + d_He, cooling, dLambdaNet_du, + /*element_lambda=*/NULL); +} + +#endif /* SWIFT_EAGLE_COOLING_RATES_H */ diff --git a/src/cooling/EAGLE/cooling_struct.h b/src/cooling/EAGLE/cooling_struct.h index 24c8b2088bf5b54134fde7a4a76ab3d2ae61c6ba..0922bf74461c222bd6485bdc07cc35edc462ddba 100644 --- a/src/cooling/EAGLE/cooling_struct.h +++ b/src/cooling/EAGLE/cooling_struct.h @@ -19,14 +19,119 @@ #ifndef SWIFT_COOLING_STRUCT_EAGLE_H #define SWIFT_COOLING_STRUCT_EAGLE_H +#define eagle_table_path_name_length 500 + +/** + * @brief struct containing cooling tables + */ +struct cooling_tables { + + /* array of heating rates due to metals */ + float *metal_heating; + + /* array of heating rates due to hydrogen and helium */ + float *H_plus_He_heating; + + /* array of electron abundances due to hydrogen and helium */ + float *H_plus_He_electron_abundance; + + /* array of temperatures */ + float *temperature; + + /* array of electron abundances due to metals */ + float *electron_abundance; +}; + /** * @brief Properties of the cooling function. */ -struct cooling_function_data {}; +struct cooling_function_data { + + /*! Cooling tables */ + struct cooling_tables table; + + /*! Redshift bins */ + float *Redshifts; + + /*! Hydrogen number density bins */ + float *nH; + + /*! Temperature bins */ + float *Temp; + + /*! Helium fraction bins */ + float *HeFrac; + + /*! Internal energy bins */ + float *Therm; + + /*! Mass fractions of elements for solar abundances (from the tables) */ + float *SolarAbundances; + + /*! Inverse of the solar mass fractions */ + float *SolarAbundances_inv; + + /*! Filepath to the directory containing the HDF5 cooling tables */ + char cooling_table_path[eagle_table_path_name_length]; + + /*! Redshit of H reionization */ + float H_reion_z; + + /*! Ca over Si abundance divided by the solar ratio for these elements */ + float Ca_over_Si_ratio_in_solar; + + /*! S over Si abundance divided by the solar ratio for these elements */ + float S_over_Si_ratio_in_solar; + + /*! Redshift of He reionization */ + float He_reion_z_centre; + + /*! Spread of the He reionization */ + float He_reion_z_sigma; + + /*! He reionization energy in CGS units */ + float He_reion_heat_cgs; + + /*! Internal energy conversion from internal units to CGS (for quick access) + */ + double internal_energy_to_cgs; + + /*! Internal energy conversion from CGS to internal units (for quick access) + */ + double internal_energy_from_cgs; + + /*! Number density conversion from internal units to CGS (for quick access) */ + double number_density_to_cgs; + + /*! Inverse of proton mass in cgs (for quick access) */ + double inv_proton_mass_cgs; + + /*! Temperatur of the CMB at present day (for quick access) */ + double T_CMB_0; + + /*! Compton rate in cgs units */ + double compton_rate_cgs; + + /*! Index of the current redshift along the redshift index of the tables */ + int z_index; + + /*! Distance between the current redshift and table[z_index] */ + float dz; + + /*! Index of the previous tables along the redshift index of the tables */ + int previous_z_index; + + /*! Are we doing Newton-Raphson iterations? */ + int newton_flag; +}; /** * @brief Properties of the cooling stored in the extended particle data. */ -struct cooling_xpart_data {}; +struct cooling_xpart_data { + + /*! Cumulative energy radiated by the particle */ + float radiated_energy; +}; #endif /* SWIFT_COOLING_STRUCT_EAGLE_H */ diff --git a/src/cooling/EAGLE/cooling_tables.c b/src/cooling/EAGLE/cooling_tables.c new file mode 100644 index 0000000000000000000000000000000000000000..c66b7ebb8f8bea4aac557fe3b7f24f944014deda --- /dev/null +++ b/src/cooling/EAGLE/cooling_tables.c @@ -0,0 +1,757 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@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/>. + * + ******************************************************************************/ + +/** + * @file src/cooling/EAGLE/cooling_tables.c + * @brief Functions to read EAGLE tables + */ + +/* Config parameters. */ +#include "../config.h" + +#include <hdf5.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* Local includes. */ +#include "chemistry_struct.h" +#include "cooling_struct.h" +#include "cooling_tables.h" +#include "error.h" +#include "interpolate.h" + +/** + * @brief Names of the elements in the order they are stored in the files + */ +static const char *eagle_tables_element_names[eagle_cooling_N_metal] = { + "Carbon", "Nitrogen", "Oxygen", "Neon", "Magnesium", + "Silicon", "Sulphur", "Calcium", "Iron"}; + +/*! Number of elements in a z-slice of the H+He cooling rate tables */ +static const size_t num_elements_cooling_rate = + eagle_cooling_N_temperature * eagle_cooling_N_density; + +/*! Number of elements in a z-slice of the metal cooling rate tables */ +static const size_t num_elements_metal_heating = eagle_cooling_N_metal * + eagle_cooling_N_temperature * + eagle_cooling_N_density; + +/*! Number of elements in a z-slice of the metal electron abundance tables */ +static const size_t num_elements_electron_abundance = + eagle_cooling_N_temperature * eagle_cooling_N_density; + +/*! Number of elements in a z-slice of the temperature tables */ +static const size_t num_elements_temperature = eagle_cooling_N_He_frac * + eagle_cooling_N_temperature * + eagle_cooling_N_density; + +/*! Number of elements in a z-slice of the H+He cooling rate tables */ +static const size_t num_elements_HpHe_heating = eagle_cooling_N_He_frac * + eagle_cooling_N_temperature * + eagle_cooling_N_density; + +/*! Number of elements in a z-slice of the H+He electron abundance tables */ +static const size_t num_elements_HpHe_electron_abundance = + eagle_cooling_N_He_frac * eagle_cooling_N_temperature * + eagle_cooling_N_density; + +/** + * @brief Reads in EAGLE table of redshift values + * + * @param cooling #cooling_function_data structure + */ +void get_cooling_redshifts(struct cooling_function_data *cooling) { + + /* Read the list of table redshifts */ + char redshift_filename[eagle_table_path_name_length + 16]; + sprintf(redshift_filename, "%s/redshifts.dat", cooling->cooling_table_path); + + FILE *infile = fopen(redshift_filename, "r"); + if (infile == NULL) { + error("Cannot open the list of cooling table redshifts (%s)", + redshift_filename); + } + + int N_Redshifts = -1; + + /* Read the file */ + if (!feof(infile)) { + + char buffer[50]; + + /* Read the number of redshifts (1st line in the file) */ + if (fgets(buffer, 50, infile) != NULL) + N_Redshifts = atoi(buffer); + else + error("Impossible to read the number of redshifts"); + + /* Be verbose about it */ + message("Found cooling tables at %d redhsifts", N_Redshifts); + + /* Check value */ + if (N_Redshifts != eagle_cooling_N_redshifts) + error("Invalid redshift lenght array."); + + /* Allocate the list of redshifts */ + if (posix_memalign((void **)&cooling->Redshifts, SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_redshifts * sizeof(float)) != 0) + error("Failed to allocate redshift table"); + + /* Read all the redshift values */ + int count = 0; + while (!feof(infile)) { + if (fgets(buffer, 50, infile) != NULL) { + cooling->Redshifts[count] = atof(buffer); + count++; + } + } + + /* Verify that the file was self-consistent */ + if (count != N_Redshifts) { + error( + "Redshift file (%s) does not contain the correct number of redshifts " + "(%d vs. %d)", + redshift_filename, count, N_Redshifts); + } + } else { + error("Redshift file (%s) is empty!", redshift_filename); + } + + /* We are done with this file */ + fclose(infile); + + /* EAGLE cooling assumes cooling->Redshifts table is in increasing order. Test + * this. */ + for (int i = 0; i < N_Redshifts - 1; i++) { + if (cooling->Redshifts[i + 1] < cooling->Redshifts[i]) { + error("table should be in increasing order\n"); + } + } +} + +/** + * @brief Reads in EAGLE cooling table header. Consists of tables + * of values for temperature, hydrogen number density, helium fraction + * solar element abundances, and elements used to index the cooling tables. + * + * @param fname Filepath for cooling table from which to read header + * @param cooling Cooling data structure + */ +void read_cooling_header(const char *fname, + struct cooling_function_data *cooling) { + +#ifdef HAVE_HDF5 + + int N_Temp, N_nH, N_He, N_SolarAbundances, N_Elements; + + /* read sizes of array dimensions */ + hid_t tempfile_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (tempfile_id < 0) error("unable to open file %s\n", fname); + + /* read size of each table of values */ + hid_t dataset = + H5Dopen(tempfile_id, "/Header/Number_of_temperature_bins", H5P_DEFAULT); + herr_t status = + H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &N_Temp); + if (status < 0) error("error reading number of temperature bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Check value */ + if (N_Temp != eagle_cooling_N_temperature) + error("Invalid temperature array length."); + + dataset = H5Dopen(tempfile_id, "/Header/Number_of_density_bins", H5P_DEFAULT); + status = + H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &N_nH); + if (status < 0) error("error reading number of density bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Check value */ + if (N_nH != eagle_cooling_N_density) error("Invalid density array length."); + + dataset = + H5Dopen(tempfile_id, "/Header/Number_of_helium_fractions", H5P_DEFAULT); + status = + H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &N_He); + if (status < 0) error("error reading number of He fraction bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Check value */ + if (N_He != eagle_cooling_N_He_frac) + error("Invalid Helium fraction array length."); + + dataset = H5Dopen(tempfile_id, "/Header/Abundances/Number_of_abundances", + H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &N_SolarAbundances); + if (status < 0) error("error reading number of solar abundance bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Check value */ + if (N_SolarAbundances != eagle_cooling_N_abundances) + error("Invalid solar abundances array length."); + + /* Check value */ + if (N_SolarAbundances != chemistry_element_count + 2) + error("Number of abundances not compatible with the chemistry model."); + + dataset = H5Dopen(tempfile_id, "/Header/Number_of_metals", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &N_Elements); + if (status < 0) error("error reading number of metal bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Check value */ + if (N_Elements != eagle_cooling_N_metal) error("Invalid metal array length."); + + /* allocate arrays of values for each of the above quantities */ + if (posix_memalign((void **)&cooling->Temp, SWIFT_STRUCT_ALIGNMENT, + N_Temp * sizeof(float)) != 0) + error("Failed to allocate temperature table"); + if (posix_memalign((void **)&cooling->Therm, SWIFT_STRUCT_ALIGNMENT, + N_Temp * sizeof(float)) != 0) + error("Failed to allocate internal energy table"); + if (posix_memalign((void **)&cooling->nH, SWIFT_STRUCT_ALIGNMENT, + N_nH * sizeof(float)) != 0) + error("Failed to allocate nH table"); + if (posix_memalign((void **)&cooling->HeFrac, SWIFT_STRUCT_ALIGNMENT, + N_He * sizeof(float)) != 0) + error("Failed to allocate HeFrac table"); + if (posix_memalign((void **)&cooling->SolarAbundances, SWIFT_STRUCT_ALIGNMENT, + N_SolarAbundances * sizeof(float)) != 0) + error("Failed to allocate Solar abundances table"); + if (posix_memalign((void **)&cooling->SolarAbundances_inv, + SWIFT_STRUCT_ALIGNMENT, + N_SolarAbundances * sizeof(float)) != 0) + error("Failed to allocate Solar abundances inverses table"); + + /* read in values for each of the arrays */ + dataset = H5Dopen(tempfile_id, "/Solar/Temperature_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + cooling->Temp); + if (status < 0) error("error reading temperature bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + dataset = H5Dopen(tempfile_id, "/Solar/Hydrogen_density_bins", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + cooling->nH); + if (status < 0) error("error reading H density bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + dataset = H5Dopen(tempfile_id, "/Metal_free/Helium_mass_fraction_bins", + H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + cooling->HeFrac); + if (status < 0) error("error reading He fraction bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + dataset = H5Dopen(tempfile_id, "/Header/Abundances/Solar_mass_fractions", + H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + cooling->SolarAbundances); + if (status < 0) error("error reading solar mass fraction bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + dataset = H5Dopen(tempfile_id, "/Metal_free/Temperature/Energy_density_bins", + H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + cooling->Therm); + if (status < 0) error("error reading internal energy bins"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Convert to temperature, density and internal energy arrays to log10 */ + for (int i = 0; i < N_Temp; i++) { + cooling->Temp[i] = log10(cooling->Temp[i]); + cooling->Therm[i] = log10(cooling->Therm[i]); + } + for (int i = 0; i < N_nH; i++) { + cooling->nH[i] = log10(cooling->nH[i]); + } + /* Compute inverse of solar mass fractions */ + for (int i = 0; i < N_SolarAbundances; ++i) { + cooling->SolarAbundances_inv[i] = 1.f / cooling->SolarAbundances[i]; + } + +#else + error("Need HDF5 to read cooling tables"); +#endif +} + +/** + * @brief Allocate space for cooling tables. + * + * @param cooling #cooling_function_data structure + */ +void allocate_cooling_tables(struct cooling_function_data *restrict cooling) { + + /* Allocate arrays to store cooling tables. Arrays contain two tables of + * cooling rates with one table being for the redshift above current redshift + * and one below. */ + + if (posix_memalign((void **)&cooling->table.metal_heating, + SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_loaded_redshifts * + num_elements_metal_heating * sizeof(float)) != 0) + error("Failed to allocate metal_heating array"); + + if (posix_memalign((void **)&cooling->table.electron_abundance, + SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_loaded_redshifts * + num_elements_electron_abundance * sizeof(float)) != 0) + error("Failed to allocate electron_abundance array"); + + if (posix_memalign((void **)&cooling->table.temperature, + SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_loaded_redshifts * + num_elements_temperature * sizeof(float)) != 0) + error("Failed to allocate temperature array"); + + if (posix_memalign((void **)&cooling->table.H_plus_He_heating, + SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_loaded_redshifts * + num_elements_HpHe_heating * sizeof(float)) != 0) + error("Failed to allocate H_plus_He_heating array"); + + if (posix_memalign((void **)&cooling->table.H_plus_He_electron_abundance, + SWIFT_STRUCT_ALIGNMENT, + eagle_cooling_N_loaded_redshifts * + num_elements_HpHe_electron_abundance * + sizeof(float)) != 0) + error("Failed to allocate H_plus_He_electron_abundance array"); +} + +/** + * @brief Get the redshift invariant table of cooling rates (before reionization + * at redshift ~9) Reads in table of cooling rates and electron abundances due + * to metals (depending on temperature, hydrogen number density), cooling rates + * and electron abundances due to hydrogen and helium (depending on temperature, + * hydrogen number density and helium fraction), and temperatures (depending on + * internal energy, hydrogen number density and helium fraction; note: this is + * distinct from table of temperatures read in ReadCoolingHeader, as that table + * is used to index the cooling, electron abundance tables, whereas this one is + * used to obtain temperature of particle) + * + * @param cooling #cooling_function_data structure + * @param photodis Are we loading the photo-dissociation table? + */ +void get_redshift_invariant_table( + struct cooling_function_data *restrict cooling, const int photodis) { +#ifdef HAVE_HDF5 + + /* Temporary tables */ + float *net_cooling_rate = NULL; + float *electron_abundance = NULL; + float *temperature = NULL; + float *he_net_cooling_rate = NULL; + float *he_electron_abundance = NULL; + + /* Allocate arrays for reading in cooling tables. */ + if (posix_memalign((void **)&net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + num_elements_cooling_rate * sizeof(float)) != 0) + error("Failed to allocate net_cooling_rate array"); + if (posix_memalign((void **)&electron_abundance, SWIFT_STRUCT_ALIGNMENT, + num_elements_electron_abundance * sizeof(float)) != 0) + error("Failed to allocate electron_abundance array"); + if (posix_memalign((void **)&temperature, SWIFT_STRUCT_ALIGNMENT, + num_elements_temperature * sizeof(float)) != 0) + error("Failed to allocate temperature array"); + if (posix_memalign((void **)&he_net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + num_elements_HpHe_heating * sizeof(float)) != 0) + error("Failed to allocate he_net_cooling_rate array"); + if (posix_memalign((void **)&he_electron_abundance, SWIFT_STRUCT_ALIGNMENT, + num_elements_HpHe_electron_abundance * sizeof(float)) != 0) + error("Failed to allocate he_electron_abundance array"); + + /* Decide which high redshift table to read. Indices set in cooling_update */ + char filename[eagle_table_path_name_length + 21]; + if (photodis) { + sprintf(filename, "%sz_photodis.hdf5", cooling->cooling_table_path); + message("Reading cooling table 'z_photodis.hdf5'"); + } else { + sprintf(filename, "%sz_8.989nocompton.hdf5", cooling->cooling_table_path); + message("Reading cooling table 'z_8.989nocompton.hdf5'"); + } + + hid_t file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s\n", filename); + + char set_name[64]; + + /* read in cooling rates due to metals */ + for (int specs = 0; specs < eagle_cooling_N_metal; specs++) { + + /* Read in the cooling rate for this metal */ + sprintf(set_name, "/%s/Net_Cooling", eagle_tables_element_names[specs]); + hid_t dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + herr_t status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, net_cooling_rate); + if (status < 0) error("error reading metal cooling rate table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (temperature, nH) + * to (metal species, nH, temperature) where fastest + * varying index is on right. Tables contain cooling rates but we + * want rate of change of internal energy, hence minus sign. */ + for (int j = 0; j < eagle_cooling_N_temperature; j++) { + for (int k = 0; k < eagle_cooling_N_density; k++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_2d( + j, k, eagle_cooling_N_temperature, eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_3d( + specs, k, j, eagle_cooling_N_metal, eagle_cooling_N_density, + eagle_cooling_N_temperature); + + /* Change the sign and transpose */ + cooling->table.metal_heating[internal_index] = + -net_cooling_rate[hdf5_index]; + } + } + } + + /* read in cooling rates due to H + He */ + strcpy(set_name, "/Metal_free/Net_Cooling"); + hid_t dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + herr_t status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, he_net_cooling_rate); + if (status < 0) error("error reading metal free cooling rate table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* read in Temperatures */ + strcpy(set_name, "/Metal_free/Temperature/Temperature"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temperature); + if (status < 0) error("error reading temperature table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* read in H + He electron abundances */ + strcpy(set_name, "/Metal_free/Electron_density_over_n_h"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + he_electron_abundance); + if (status < 0) error("error reading electron density table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (helium fraction, temperature, + * nH) to (nH, helium fraction, temperature) where fastest + * varying index is on right. Tables contain cooling rates but we + * want rate of change of internal energy, hence minus sign. */ + for (int i = 0; i < eagle_cooling_N_He_frac; i++) { + for (int j = 0; j < eagle_cooling_N_temperature; j++) { + for (int k = 0; k < eagle_cooling_N_density; k++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_3d( + i, j, k, eagle_cooling_N_He_frac, eagle_cooling_N_temperature, + eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_3d( + k, i, j, eagle_cooling_N_density, eagle_cooling_N_He_frac, + eagle_cooling_N_temperature); + + /* Change the sign and transpose */ + cooling->table.H_plus_He_heating[internal_index] = + -he_net_cooling_rate[hdf5_index]; + + /* Convert to log T and transpose */ + cooling->table.temperature[internal_index] = + log10(temperature[hdf5_index]); + + /* Just transpose */ + cooling->table.H_plus_He_electron_abundance[internal_index] = + he_electron_abundance[hdf5_index]; + } + } + } + + /* read in electron densities due to metals */ + strcpy(set_name, "/Solar/Electron_density_over_n_h"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + electron_abundance); + if (status < 0) error("error reading solar electron density table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (temperature, nH) to + * (nH, temperature) where fastest varying index is on right. */ + for (int i = 0; i < eagle_cooling_N_temperature; i++) { + for (int j = 0; j < eagle_cooling_N_density; j++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_2d( + i, j, eagle_cooling_N_temperature, eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_2d( + j, i, eagle_cooling_N_density, eagle_cooling_N_temperature); + + /* Just transpose */ + cooling->table.electron_abundance[internal_index] = + electron_abundance[hdf5_index]; + } + } + + status = H5Fclose(file_id); + if (status < 0) error("error closing file"); + + free(net_cooling_rate); + free(electron_abundance); + free(temperature); + free(he_net_cooling_rate); + free(he_electron_abundance); + +#ifdef SWIFT_DEBUG_CHECKS + message("done reading in redshift invariant table"); +#endif + +#else + error("Need HDF5 to read cooling tables"); +#endif +} + +/** + * @brief Get redshift dependent table of cooling rates. + * Reads in table of cooling rates and electron abundances due to + * metals (depending on temperature, hydrogen number density), cooling rates and + * electron abundances due to hydrogen and helium (depending on temperature, + * hydrogen number density and helium fraction), and temperatures (depending on + * internal energy, hydrogen number density and helium fraction; note: this is + * distinct from table of temperatures read in ReadCoolingHeader, as that table + * is used to index the cooling, electron abundance tables, whereas this one is + * used to obtain temperature of particle) + * + * @param cooling #cooling_function_data structure + * @param low_z_index Index of the lowest redshift table to load. + * @param high_z_index Index of the highest redshift table to load. + */ +void get_cooling_table(struct cooling_function_data *restrict cooling, + const int low_z_index, const int high_z_index) { + +#ifdef HAVE_HDF5 + + /* Temporary tables */ + float *net_cooling_rate = NULL; + float *electron_abundance = NULL; + float *temperature = NULL; + float *he_net_cooling_rate = NULL; + float *he_electron_abundance = NULL; + + /* Allocate arrays for reading in cooling tables. */ + if (posix_memalign((void **)&net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + num_elements_cooling_rate * sizeof(float)) != 0) + error("Failed to allocate net_cooling_rate array"); + if (posix_memalign((void **)&electron_abundance, SWIFT_STRUCT_ALIGNMENT, + num_elements_electron_abundance * sizeof(float)) != 0) + error("Failed to allocate electron_abundance array"); + if (posix_memalign((void **)&temperature, SWIFT_STRUCT_ALIGNMENT, + num_elements_temperature * sizeof(float)) != 0) + error("Failed to allocate temperature array"); + if (posix_memalign((void **)&he_net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + num_elements_HpHe_heating * sizeof(float)) != 0) + error("Failed to allocate he_net_cooling_rate array"); + if (posix_memalign((void **)&he_electron_abundance, SWIFT_STRUCT_ALIGNMENT, + num_elements_HpHe_electron_abundance * sizeof(float)) != 0) + error("Failed to allocate he_electron_abundance array"); + + /* Read in tables, transpose so that values for indices which vary most are + * adjacent. Repeat for redshift above and redshift below current value. */ + for (int z_index = low_z_index; z_index <= high_z_index; z_index++) { + + /* Index along redhsift dimension for the subset of tables we read */ + const int local_z_index = z_index - low_z_index; + +#ifdef SWIFT_DEBUG_CHECKS + if (local_z_index >= eagle_cooling_N_loaded_redshifts) + error("Reading invalid number of tables along z axis."); +#endif + + /* Open table for this redshift index */ + char fname[eagle_table_path_name_length + 12]; + sprintf(fname, "%sz_%1.3f.hdf5", cooling->cooling_table_path, + cooling->Redshifts[z_index]); + message("Reading cooling table 'z_%1.3f.hdf5'", + cooling->Redshifts[z_index]); + + hid_t file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s", fname); + + char set_name[64]; + + /* read in cooling rates due to metals */ + for (int specs = 0; specs < eagle_cooling_N_metal; specs++) { + + sprintf(set_name, "/%s/Net_Cooling", eagle_tables_element_names[specs]); + hid_t dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + herr_t status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, net_cooling_rate); + if (status < 0) error("error reading metal cooling rate table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (temperature, nH) + * to (metal species, redshift, nH, temperature) where fastest + * varying index is on right. Tables contain cooling rates but we + * want rate of change of internal energy, hence minus sign. */ + for (int i = 0; i < eagle_cooling_N_density; i++) { + for (int j = 0; j < eagle_cooling_N_temperature; j++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_2d( + j, i, eagle_cooling_N_temperature, eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_4d( + specs, local_z_index, i, j, eagle_cooling_N_metal, + eagle_cooling_N_loaded_redshifts, eagle_cooling_N_density, + eagle_cooling_N_temperature); + + /* Change the sign and transpose */ + cooling->table.metal_heating[internal_index] = + -net_cooling_rate[hdf5_index]; + } + } + } + + /* read in cooling rates due to H + He */ + strcpy(set_name, "/Metal_free/Net_Cooling"); + hid_t dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + herr_t status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, he_net_cooling_rate); + if (status < 0) error("error reading metal free cooling rate table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* read in Temperature */ + strcpy(set_name, "/Metal_free/Temperature/Temperature"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temperature); + if (status < 0) error("error reading temperature table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Read in H + He electron abundance */ + strcpy(set_name, "/Metal_free/Electron_density_over_n_h"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + he_electron_abundance); + if (status < 0) error("error reading electron density table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (helium fraction, temperature, + * nH) to (redshift, nH, helium fraction, temperature) where fastest + * varying index is on right. */ + for (int i = 0; i < eagle_cooling_N_He_frac; i++) { + for (int j = 0; j < eagle_cooling_N_temperature; j++) { + for (int k = 0; k < eagle_cooling_N_density; k++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_3d( + i, j, k, eagle_cooling_N_He_frac, eagle_cooling_N_temperature, + eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_4d( + local_z_index, k, i, j, eagle_cooling_N_loaded_redshifts, + eagle_cooling_N_density, eagle_cooling_N_He_frac, + eagle_cooling_N_temperature); + + /* Change the sign and transpose */ + cooling->table.H_plus_He_heating[internal_index] = + -he_net_cooling_rate[hdf5_index]; + + /* Convert to log T and transpose */ + cooling->table.temperature[internal_index] = + log10(temperature[hdf5_index]); + + /* Just transpose */ + cooling->table.H_plus_He_electron_abundance[internal_index] = + he_electron_abundance[hdf5_index]; + } + } + } + + /* read in electron densities due to metals */ + strcpy(set_name, "/Solar/Electron_density_over_n_h"); + dataset = H5Dopen(file_id, set_name, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + electron_abundance); + if (status < 0) error("error reading solar electron density table"); + status = H5Dclose(dataset); + if (status < 0) error("error closing cooling dataset"); + + /* Transpose from order tables are stored in (temperature, nH) to + * (redshift, nH, temperature) where fastest varying index is on right. */ + for (int i = 0; i < eagle_cooling_N_temperature; i++) { + for (int j = 0; j < eagle_cooling_N_density; j++) { + + /* Index in the HDF5 table */ + const int hdf5_index = row_major_index_2d( + i, j, eagle_cooling_N_temperature, eagle_cooling_N_density); + + /* Index in the internal table */ + const int internal_index = row_major_index_3d( + local_z_index, j, i, eagle_cooling_N_loaded_redshifts, + eagle_cooling_N_density, eagle_cooling_N_temperature); + + /* Just transpose */ + cooling->table.electron_abundance[internal_index] = + electron_abundance[hdf5_index]; + } + } + + status = H5Fclose(file_id); + if (status < 0) error("error closing file"); + } + + free(net_cooling_rate); + free(electron_abundance); + free(temperature); + free(he_net_cooling_rate); + free(he_electron_abundance); + +#ifdef SWIFT_DEBUG_CHECKS + message("Done reading in general cooling table"); +#endif + +#else + error("Need HDF5 to read cooling tables"); +#endif +} diff --git a/src/cooling/EAGLE/cooling_tables.h b/src/cooling/EAGLE/cooling_tables.h new file mode 100644 index 0000000000000000000000000000000000000000..20abd6f423c9c5aadbb30b9bb8096e860b050234 --- /dev/null +++ b/src/cooling/EAGLE/cooling_tables.h @@ -0,0 +1,65 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@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 SWIFT_EAGLE_COOL_TABLES_H +#define SWIFT_EAGLE_COOL_TABLES_H + +/** + * @file src/cooling/EAGLE/cooling.h + * @brief EAGLE cooling function + */ + +/* Config parameters. */ +#include "../config.h" + +#include "cooling_struct.h" + +/*! Number of different bins along the redhsift axis of the tables */ +#define eagle_cooling_N_redshifts 49 + +/*! Number of redshift bins loaded at any given point int time */ +#define eagle_cooling_N_loaded_redshifts 2 + +/*! Number of different bins along the temperature axis of the tables */ +#define eagle_cooling_N_temperature 176 + +/*! Number of different bins along the density axis of the tables */ +#define eagle_cooling_N_density 41 + +/*! Number of different bins along the metal axis of the tables */ +#define eagle_cooling_N_metal 9 + +/*! Number of different bins along the metal axis of the tables */ +#define eagle_cooling_N_He_frac 7 + +/*! Number of different bins along the abundances axis of the tables */ +#define eagle_cooling_N_abundances 11 + +void get_cooling_redshifts(struct cooling_function_data *cooling); + +void read_cooling_header(const char *fname, + struct cooling_function_data *cooling); + +void allocate_cooling_tables(struct cooling_function_data *restrict cooling); + +void get_redshift_invariant_table( + struct cooling_function_data *restrict cooling, const int photodis); +void get_cooling_table(struct cooling_function_data *restrict cooling, + const int low_z_index, const int high_z_index); + +#endif diff --git a/src/cooling/EAGLE/interpolate.h b/src/cooling/EAGLE/interpolate.h new file mode 100644 index 0000000000000000000000000000000000000000..78955e7fd7409d3501d16ae50d4a2248cb1cff0b --- /dev/null +++ b/src/cooling/EAGLE/interpolate.h @@ -0,0 +1,499 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@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 SWIFT_INTERPOL_EAGLE_H +#define SWIFT_INTERPOL_EAGLE_H + +/** + * @file src/cooling/EAGLE/interpolate.h + * @brief Interpolation functions for EAGLE tables + */ + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "align.h" +#include "error.h" +#include "inline.h" + +/** + * @brief Returns the 1d index of element with 2d indices x,y + * from a flattened 2d array in row major order + * + * @param x, y Indices of element of interest + * @param Nx, Ny Sizes of array dimensions + */ +__attribute__((always_inline)) INLINE int row_major_index_2d(const int x, + const int y, + const int Nx, + const int Ny) { +#ifdef SWIFT_DEBUG_CHECKS + assert(x < Nx); + assert(y < Ny); +#endif + return x * Ny + y; +} + +/** + * @brief Returns the 1d index of element with 3d indices x,y,z + * from a flattened 3d array in row major order + * + * @param x, y, z Indices of element of interest + * @param Nx, Ny, Nz Sizes of array dimensions + */ +__attribute__((always_inline)) INLINE int row_major_index_3d( + const int x, const int y, const int z, const int Nx, const int Ny, + const int Nz) { +#ifdef SWIFT_DEBUG_CHECKS + assert(x < Nx); + assert(y < Ny); + assert(z < Nz); +#endif + return x * Ny * Nz + y * Nz + z; +} + +/** + * @brief Returns the 1d index of element with 4d indices x,y,z,w + * from a flattened 4d array in row major order + * + * @param x, y, z, w Indices of element of interest + * @param Nx, Ny, Nz, Nw Sizes of array dimensions + */ +__attribute__((always_inline)) INLINE int row_major_index_4d( + const int x, const int y, const int z, const int w, const int Nx, + const int Ny, const int Nz, const int Nw) { +#ifdef SWIFT_DEBUG_CHECKS + assert(x < Nx); + assert(y < Ny); + assert(z < Nz); + assert(w < Nw); +#endif + return x * Ny * Nz * Nw + y * Nz * Nw + z * Nw + w; +} + +/** + * @brief Finds the index of a value in a table and compute delta to nearest + * element. + * + * This function assumes the table is monotonically increasing with a constant + * difference between adjacent values. + * + * The returned difference is expressed in units of the table separation. This + * means dx = (x - table[i]) / (table[i+1] - table[i]). It is always between + * 0 and 1. + * + * We use a small epsilon of 1e-4 to avoid out-of-range accesses due to + * rounding errors. + * + * @param table The table to search in. + * @param size The number of elements in the table. + * @param x The value to search for. + * @param i (return) The index in the table of the element. + * @param *dx (return) The difference between x and table[i] + */ +__attribute__((always_inline)) INLINE void get_index_1d( + const float *restrict table, const int size, const float x, int *i, + float *restrict dx) { + + /* Small epsilon to avoid rounding issues leading to out-of-bound + * access when using the indices later to read data from the tables. */ + const float epsilon = 1e-4f; + + /* Indicate that the whole array is aligned on boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Distance between elements in the array */ + const float delta = (size - 1) / (table[size - 1] - table[0]); + + if (x < table[0] + epsilon) { + /* We are below the first element */ + *i = 0; + *dx = 0.f; + } else if (x < table[size - 1] - epsilon) { + /* Normal case */ + *i = (x - table[0]) * delta; + +#ifdef SWIFT_DEBUG_CHECKS + if (*i > size || *i < 0) { + error( + "trying to get index for value outside table range. Table size: %d, " + "calculated index: %d, value: %.5e, table[0]: %.5e, grid size: %.5e", + size, *i, x, table[0], delta); + } +#endif + + *dx = (x - table[*i]) * delta; + } else { + /* We are after the last element */ + *i = size - 2; + *dx = 1.f; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (*dx < -0.001f || *dx > 1.001f) error("Invalid distance found dx=%e", *dx); +#endif +} + +/** + * @brief Interpolate a flattened 2D table at a given position. + * + * This function uses linear interpolation along each axis. It also + * assumes that the table is aligned on SWIFT_STRUCT_ALIGNMENT. + * + * @param table The 2D table to interpolate. + * @param xi, yi Indices of element of interest. + * @param Nx, Ny Sizes of array dimensions. + * @param dx, dy Distance between the point and the index in units of + * the grid spacing. + */ +__attribute__((always_inline)) INLINE float interpolation_2d( + const float *table, const int xi, const int yi, const float dx, + const float dy, const int Nx, const int Ny) { + +#ifdef SWIFT_DEBUG_CHECKS + if (dx < -0.001f || dx > 1.001f) error("Invalid dx=%e", dx); + if (dy < -0.001f || dy > 1.001f) error("Invalid dy=%e", dy); +#endif + + const float tx = 1.f - dx; + const float ty = 1.f - dy; + + /* Indicate that the whole array is aligned on boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Linear interpolation along each axis. We read the table 2^2=4 times */ + float result = tx * ty * table[row_major_index_2d(xi + 0, yi + 0, Nx, Ny)]; + + result += tx * dy * table[row_major_index_2d(xi + 0, yi + 1, Nx, Ny)]; + result += dx * ty * table[row_major_index_2d(xi + 1, yi + 0, Nx, Ny)]; + + result += dx * dy * table[row_major_index_2d(xi + 1, yi + 1, Nx, Ny)]; + + return result; +} + +/** + * @brief Interpolate a flattened 3D table at a given position. + * + * This function uses linear interpolation along each axis. It also + * assumes that the table is aligned on SWIFT_STRUCT_ALIGNMENT. + * + * @param table The 3D table to interpolate. + * @param xi, yi, zi Indices of element of interest. + * @param Nx, Ny, Nz Sizes of array dimensions. + * @param dx, dy, dz Distance between the point and the index in units of + * the grid spacing. + */ +__attribute__((always_inline)) INLINE float interpolation_3d( + const float *table, const int xi, const int yi, const int zi, + const float dx, const float dy, const float dz, const int Nx, const int Ny, + const int Nz) { + +#ifdef SWIFT_DEBUG_CHECKS + if (dx < -0.001f || dx > 1.001f) error("Invalid dx=%e", dx); + if (dy < -0.001f || dy > 1.001f) error("Invalid dy=%e", dy); + if (dz < -0.001f || dz > 1.001f) error("Invalid dz=%e", dz); +#endif + + const float tx = 1.f - dx; + const float ty = 1.f - dy; + const float tz = 1.f - dz; + + /* Indicate that the whole array is aligned on page boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Linear interpolation along each axis. We read the table 2^3=8 times */ + float result = tx * ty * tz * + table[row_major_index_3d(xi + 0, yi + 0, zi + 0, Nx, Ny, Nz)]; + + result += tx * ty * dz * + table[row_major_index_3d(xi + 0, yi + 0, zi + 1, Nx, Ny, Nz)]; + result += tx * dy * tz * + table[row_major_index_3d(xi + 0, yi + 1, zi + 0, Nx, Ny, Nz)]; + result += dx * ty * tz * + table[row_major_index_3d(xi + 1, yi + 0, zi + 0, Nx, Ny, Nz)]; + + result += tx * dy * dz * + table[row_major_index_3d(xi + 0, yi + 1, zi + 1, Nx, Ny, Nz)]; + result += dx * ty * dz * + table[row_major_index_3d(xi + 1, yi + 0, zi + 1, Nx, Ny, Nz)]; + result += dx * dy * tz * + table[row_major_index_3d(xi + 1, yi + 1, zi + 0, Nx, Ny, Nz)]; + + result += dx * dy * dz * + table[row_major_index_3d(xi + 1, yi + 1, zi + 1, Nx, Ny, Nz)]; + + return result; +} + +/** + * @brief Interpolate a flattened 3D table at a given position but avoid the + * x-dimension. + * + * This function uses linear interpolation along each axis. + * We look at the xi coordoniate but do not interpolate around it. We just + * interpolate the remaining 2 dimensions. + * The function also assumes that the table is aligned on + * SWIFT_STRUCT_ALIGNMENT. + * + * @param table The 3D table to interpolate. + * @param xi, yi, zi Indices of element of interest. + * @param Nx, Ny, Nz Sizes of array dimensions. + * @param dx, dy, dz Distance between the point and the index in units of + * the grid spacing. + */ +__attribute__((always_inline)) INLINE float interpolation_3d_no_x( + const float *table, const int xi, const int yi, const int zi, + const float dx, const float dy, const float dz, const int Nx, const int Ny, + const int Nz) { + +#ifdef SWIFT_DEBUG_CHECKS + if (dx != 0.f) error("Attempting to interpolate along x!"); + if (dy < -0.001f || dy > 1.001f) error("Invalid dy=%e", dy); + if (dz < -0.001f || dz > 1.001f) error("Invalid dz=%e", dz); +#endif + + const float tx = 1.f; + const float ty = 1.f - dy; + const float tz = 1.f - dz; + + /* Indicate that the whole array is aligned on page boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Linear interpolation along each axis. We read the table 2^2=4 times */ + /* Note that we intentionally kept the table access along the axis where */ + /* we do not interpolate as comments in the code to allow readers to */ + /* understand what is going on. */ + float result = tx * ty * tz * + table[row_major_index_3d(xi + 0, yi + 0, zi + 0, Nx, Ny, Nz)]; + + result += tx * ty * dz * + table[row_major_index_3d(xi + 0, yi + 0, zi + 1, Nx, Ny, Nz)]; + result += tx * dy * tz * + table[row_major_index_3d(xi + 0, yi + 1, zi + 0, Nx, Ny, Nz)]; + /* result += dx * ty * tz * */ + /* table[row_major_index_3d(xi + 1, yi + 0, zi + 0, Nx, Ny, Nz)]; */ + + result += tx * dy * dz * + table[row_major_index_3d(xi + 0, yi + 1, zi + 1, Nx, Ny, Nz)]; + /* result += dx * ty * dz * */ + /* table[row_major_index_3d(xi + 1, yi + 0, zi + 1, Nx, Ny, Nz)]; */ + /* result += dx * dy * tz * */ + /* table[row_major_index_3d(xi + 1, yi + 1, zi + 0, Nx, Ny, Nz)]; */ + + /* result += dx * dy * dz * */ + /* table[row_major_index_3d(xi + 1, yi + 1, zi + 1, Nx, Ny, Nz)]; */ + + return result; +} + +/** + * @brief Interpolate a flattened 4D table at a given position. + * + * This function uses linear interpolation along each axis. It also + * assumes that the table is aligned on SWIFT_STRUCT_ALIGNMENT. + * + * @param table The 4D table to interpolate. + * @param xi, yi, zi, wi Indices of element of interest. + * @param Nx, Ny, Nz, Nw Sizes of array dimensions. + * @param dx, dy, dz, dw Distance between the point and the index in units of + * the grid spacing. + */ +__attribute__((always_inline)) INLINE float interpolation_4d( + const float *table, const int xi, const int yi, const int zi, const int wi, + const float dx, const float dy, const float dz, const float dw, + const int Nx, const int Ny, const int Nz, const int Nw) { + +#ifdef SWIFT_DEBUG_CHECKS + if (dx < -0.001f || dx > 1.001f) error("Invalid dx=%e", dx); + if (dy < -0.001f || dy > 1.001f) error("Invalid dy=%e", dy); + if (dz < -0.001f || dz > 1.001f) error("Invalid dz=%e", dz); + if (dw < -0.001f || dw > 1.001f) error("Invalid dw=%e", dw); +#endif + + const float tx = 1.f - dx; + const float ty = 1.f - dy; + const float tz = 1.f - dz; + const float tw = 1.f - dw; + + /* Indicate that the whole array is aligned on page boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Linear interpolation along each axis. We read the table 2^4=16 times */ + float result = + tx * ty * tz * tw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + + result += + tx * ty * tz * dw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * ty * dz * tw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + result += + tx * dy * tz * tw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + result += + dx * ty * tz * tw * + table[row_major_index_4d(xi + 1, yi + 0, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + + result += + tx * ty * dz * dw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * dy * tz * dw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + result += + dx * ty * tz * dw * + table[row_major_index_4d(xi + 1, yi + 0, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * dy * dz * tw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + result += + dx * ty * dz * tw * + table[row_major_index_4d(xi + 1, yi + 0, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + result += + dx * dy * tz * tw * + table[row_major_index_4d(xi + 1, yi + 1, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + + result += + dx * dy * dz * tw * + table[row_major_index_4d(xi + 1, yi + 1, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + result += + dx * dy * tz * dw * + table[row_major_index_4d(xi + 1, yi + 1, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + result += + dx * ty * dz * dw * + table[row_major_index_4d(xi + 1, yi + 0, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * dy * dz * dw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + + result += + dx * dy * dz * dw * + table[row_major_index_4d(xi + 1, yi + 1, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + + return result; +} + +/** + * @brief Interpolate a flattened 4D table at a given position but avoid the + * x-dimension. + * + * This function uses linear interpolation along each axis. + * We look at the xi coordoniate but do not interpolate around it. We just + * interpolate the remaining 3 dimensions. + * The function also assumes that the table is aligned on + * SWIFT_STRUCT_ALIGNMENT. + * + * @param table The 4D table to interpolate. + * @param xi, yi, zi, wi Indices of element of interest. + * @param Nx, Ny, Nz, Nw Sizes of array dimensions. + * @param dx, dy, dz, dw Distance between the point and the index in units of + * the grid spacing. + */ +__attribute__((always_inline)) INLINE float interpolation_4d_no_x( + const float *table, const int xi, const int yi, const int zi, const int wi, + const float dx, const float dy, const float dz, const float dw, + const int Nx, const int Ny, const int Nz, const int Nw) { + +#ifdef SWIFT_DEBUG_CHECKS + if (dx != 0.f) error("Attempting to interpolate along x!"); + if (dy < -0.001f || dy > 1.001f) error("Invalid dy=%e", dy); + if (dz < -0.001f || dz > 1.001f) error("Invalid dz=%e", dz); + if (dw < -0.001f || dw > 1.001f) error("Invalid dw=%e", dw); +#endif + + const float tx = 1.f; + const float ty = 1.f - dy; + const float tz = 1.f - dz; + const float tw = 1.f - dw; + + /* Indicate that the whole array is aligned on boundaries */ + swift_align_information(float, table, SWIFT_STRUCT_ALIGNMENT); + + /* Linear interpolation along each axis. We read the table 2^3=8 times */ + /* Note that we intentionally kept the table access along the axis where */ + /* we do not interpolate as comments in the code to allow readers to */ + /* understand what is going on. */ + float result = + tx * ty * tz * tw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + + result += + tx * ty * tz * dw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * ty * dz * tw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + result += + tx * dy * tz * tw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 0, wi + 0, Nx, Ny, Nz, Nw)]; + /* result += */ + /* dx * ty * tz * tw * */ + /* table[row_major_index_4d(xi + 1, yi + 0, zi + 0, wi + 0, Nx, Ny, Nz, + * Nw)]; */ + + result += + tx * ty * dz * dw * + table[row_major_index_4d(xi + 0, yi + 0, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + result += + tx * dy * tz * dw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 0, wi + 1, Nx, Ny, Nz, Nw)]; + /* result += */ + /* dx * ty * tz * dw * */ + /* table[row_major_index_4d(xi + 1, yi + 0, zi + 0, wi + 1, Nx, Ny, Nz, + * Nw)]; */ + result += + tx * dy * dz * tw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 1, wi + 0, Nx, Ny, Nz, Nw)]; + /* result += */ + /* dx * ty * dz * tw * */ + /* table[row_major_index_4d(xi + 1, yi + 0, zi + 1, wi + 0, Nx, Ny, Nz, + * Nw)]; */ + /* result += */ + /* dx * dy * tz * tw * */ + /* table[row_major_index_4d(xi + 1, yi + 1, zi + 0, wi + 0, Nx, Ny, Nz, */ + /* Nw)]; */ + + /* result += */ + /* dx * dy * dz * tw * */ + /* table[row_major_index_4d(xi + 1, yi + 1, zi + 1, wi + 0, Nx, Ny, Nz, */ + /* Nw)]; */ + /* result += */ + /* dx * dy * tz * dw * */ + /* table[row_major_index_4d(xi + 1, yi + 1, zi + 0, wi + 1, Nx, Ny, Nz, */ + /* Nw)]; */ + /* result += */ + /* dx * ty * dz * dw * */ + /* table[row_major_index_4d(xi + 1, yi + 0, zi + 1, wi + 1, Nx, Ny, Nz, + * Nw)]; */ + result += + tx * dy * dz * dw * + table[row_major_index_4d(xi + 0, yi + 1, zi + 1, wi + 1, Nx, Ny, Nz, Nw)]; + + /* result += */ + /* dx * dy * dz * dw * */ + /* table[row_major_index_4d(xi + 1, yi + 1, zi + 1, wi + 1, Nx, Ny, Nz, */ + /* Nw)]; */ + + return result; +} + +#endif diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h index 1c009886d2a08b62070162a80c4572aa43d35e3e..15eecc43093e9cc571d166aa49392e7dfea60c11 100644 --- a/src/cooling/const_du/cooling.h +++ b/src/cooling/const_du/cooling.h @@ -28,23 +28,38 @@ * This is the simplest possible cooling function. A constant cooling rate * (du/dt) with a minimal energy floor is applied. Should be used as a template * for more realistic functions. + * + * This cooling model does NOT include cosmological terms and will hence yield + * an incorrect answer when running in co-moving coordinates. */ /* Config parameters. */ #include "../config.h" /* Some standard headers. */ +#include <float.h> #include <math.h> /* Local includes. */ -#include "const.h" -#include "error.h" +#include "entropy_floor.h" #include "hydro.h" #include "parser.h" #include "part.h" #include "physical_constants.h" #include "units.h" +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. + * + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + */ +INLINE static void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling) { + // Add content if required. +} + /** * @brief Apply the cooling function to a particle. * @@ -55,6 +70,7 @@ * @param us The internal system of units. * @param cosmo The current cosmological model. * @param hydro_props The properties of the hydro scheme. + * @param floor_props Properties of the entropy floor. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. @@ -66,6 +82,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, const float dt, const float dt_therm) { @@ -150,6 +167,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part( xp->cooling_data.radiated_energy = 0.f; } +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +INLINE static float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; +} + /** * @brief Returns the total radiated energy by this particle. * @@ -188,6 +248,18 @@ static INLINE void cooling_init_backend(struct swift_params* parameter_file, parameter_file, "ConstCooling:cooling_tstep_mult"); } +/** + * @brief Restore cooling tables (if applicable) after + * restart + * + * Nothing to do here + * + * @param cooling the cooling_function_data structure + * @param cosmo cosmology structure + */ +static INLINE void cooling_restore_tables(struct cooling_function_data* cooling, + const struct cosmology* cosmo) {} + /** * @brief Prints the properties of the cooling model to stdout. * @@ -200,4 +272,42 @@ static INLINE void cooling_print_backend( cooling->cooling_rate, cooling->min_energy); } +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +static INLINE void cooling_clean(struct cooling_function_data* cooling) {} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +static INLINE void cooling_struct_dump( + const struct cooling_function_data* cooling, FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, + FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); +} + #endif /* SWIFT_COOLING_CONST_DU_H */ diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h index f4a327f14ec071bc62c4cf57bb118df71bab2b3e..a60aa5d282d0a244f206f74827f0c1979d3bcb75 100644 --- a/src/cooling/const_du/cooling_io.h +++ b/src/cooling/const_du/cooling_io.h @@ -34,6 +34,7 @@ #include "../config.h" /* Local includes */ +#include "cooling.h" #include "io_properties.h" #ifdef HAVE_HDF5 @@ -50,9 +51,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif +INLINE static void convert_part_T(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + /** * @brief Specifies which particle fields to write to a dataset * + * @param parts The particle array. * @param xparts The exended particle data array. * @param list The list of i/o properties to write. * @param cooling The #cooling_function_data @@ -60,9 +70,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - return 0; + + list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, parts, + xparts, convert_part_T); + + return 1; } #endif /* SWIFT_COOLING_CONST_DU_IO_H */ diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h index 83794edd0fd36c392cc64c14cad0c5e19b77f86a..974b055b1f8942f53b72e6ccf17389d8f85b666e 100644 --- a/src/cooling/const_lambda/cooling.h +++ b/src/cooling/const_lambda/cooling.h @@ -37,6 +37,7 @@ /* Local includes. */ #include "const.h" +#include "entropy_floor.h" #include "error.h" #include "hydro.h" #include "parser.h" @@ -44,6 +45,18 @@ #include "physical_constants.h" #include "units.h" +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. + * + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + */ +INLINE static void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling) { + // Add content if required. +} + /** * @brief Calculates du/dt in CGS units for a particle. * @@ -87,6 +100,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate_cgs( * @param us The internal system of units. * @param cosmo The current cosmological model. * @param hydro_props The properties of the hydro scheme. + * @param floor_props Properties of the entropy floor. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the particle' extended data. @@ -98,6 +112,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, const float dt, const float dt_therm) { @@ -105,9 +120,6 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( /* Nothing to do here? */ if (dt == 0.) return; - /* Internal energy floor */ - const float u_floor = hydro_props->minimal_internal_energy; - /* Current energy */ const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); @@ -129,11 +141,22 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( /* We now need to check that we are not going to go below any of the limits */ + /* Limit imposed by the entropy floor */ + const float A_floor = entropy_floor(p, cosmo, floor_props); + const float rho = hydro_get_physical_density(p, cosmo); + const float u_floor = gas_internal_energy_from_entropy(rho, A_floor); + + /* Absolute minimum */ + const float u_minimal = hydro_props->minimal_internal_energy; + + /* Largest of both limits */ + const float u_limit = max(u_minimal, u_floor); + /* First, check whether we may end up below the minimal energy after * this step 1/2 kick + another 1/2 kick that could potentially be for * a time-step twice as big. We hence check for 1.5 delta_t. */ - if (u_old + total_du_dt * 1.5 * dt_therm < u_floor) { - total_du_dt = (u_floor - u_old) / (1.5f * dt_therm); + if (u_old + total_du_dt * 1.5 * dt_therm < u_limit) { + total_du_dt = (u_limit - u_old) / (1.5f * dt_therm); } /* Second, check whether the energy used in the prediction could get negative. @@ -216,6 +239,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part( xp->cooling_data.radiated_energy = 0.f; } +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +INLINE static float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; +} + /** * @brief Returns the total radiated energy by this particle. * @@ -259,6 +325,18 @@ static INLINE void cooling_init_backend(struct swift_params* parameter_file, units_cgs_conversion_factor(us, UNIT_CONV_MASS)); } +/** + * @brief Restore cooling tables (if applicable) after + * restart + * + * Nothing to do here + * + * @param cooling the cooling_function_data structure + * @param cosmo cosmology structure + */ +static INLINE void cooling_restore_tables(struct cooling_function_data* cooling, + const struct cosmology* cosmo) {} + /** * @brief Prints the properties of the cooling model to stdout. * @@ -280,4 +358,42 @@ static INLINE void cooling_print_backend( cooling->cooling_tstep_mult); } +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +static INLINE void cooling_clean(struct cooling_function_data* cooling) {} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +static INLINE void cooling_struct_dump( + const struct cooling_function_data* cooling, FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, + FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); +} + #endif /* SWIFT_COOLING_CONST_LAMBDA_H */ diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h index 0dca5011ebe5bc6c2a4866387e9cf1ac0ba3447a..9437f0f94db41725d6715cf349843bf079137305 100644 --- a/src/cooling/const_lambda/cooling_io.h +++ b/src/cooling/const_lambda/cooling_io.h @@ -32,6 +32,7 @@ #include "../config.h" /* Local includes */ +#include "cooling.h" #include "io_properties.h" #ifdef HAVE_HDF5 @@ -49,11 +50,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif +INLINE static void convert_part_T(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + /** * @brief Specifies which particle fields to write to a dataset * - * Nothing to write for this scheme. - * + * @param parts The particle array. * @param xparts The extended particle array. * @param list The list of i/o properties to write. * @param cooling The #cooling_function_data @@ -61,10 +69,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - return 0; + list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, parts, + xparts, convert_part_T); + + return 1; } #endif /* SWIFT_COOLING_CONST_LAMBDA_IO_H */ diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index 479b1c8288cc12b12743c50684cb8b2af4d3a411..c5a165f4e1663394a0982879292acf91f43a5b68 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -37,6 +37,7 @@ /* Local includes. */ #include "chemistry.h" #include "cooling_io.h" +#include "entropy_floor.h" #include "error.h" #include "hydro.h" #include "parser.h" @@ -48,6 +49,18 @@ #define GRACKLE_NPART 1 #define GRACKLE_RANK 3 +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. + * + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + */ +INLINE static void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling) { + // Add content if required. +} + /* prototypes */ static gr_float cooling_time( const struct phys_const* restrict phys_const, @@ -637,12 +650,16 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time( * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param dt The time-step of this particle. + * @param hydro_properties the hydro_props struct, used for + * getting the minimal internal energy allowed in by SWIFT. + * Read from yml file into engine struct. */ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, double dt, double dt_therm) { @@ -668,6 +685,18 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( hydro_set_physical_internal_energy_dt(p, cosmo, hydro_du_dt + du_dt); } +static INLINE float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + error("This function needs implementing!!"); + return 0.; +} + /** * @brief Computes the cooling time-step. * @@ -798,4 +827,45 @@ __attribute__((always_inline)) INLINE static void cooling_init_backend( cooling_init_grackle(cooling); } +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +static INLINE void cooling_clean(struct cooling_function_data* cooling) { + + // MATTHIEU: To do: free stuff here +} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Nothing to do beyond writing the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + */ +static INLINE void cooling_struct_dump( + const struct cooling_function_data* cooling, FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Nothing to do beyond reading the structure from the stream. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, + FILE* stream, + const struct cosmology* cosmo) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); +} + #endif /* SWIFT_COOLING_GRACKLE_H */ diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h index 684ab347e19c8ea5f1897a54f21951256ef5f50b..88235a20a2f9d150b56f59ee32fa9ab91941e659 100644 --- a/src/cooling/grackle/cooling_io.h +++ b/src/cooling/grackle/cooling_io.h @@ -59,7 +59,7 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { int num = 0; diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h index c21af9c3f1b491256e9f2a65f2f924fa606f665d..3f90d357ad863da0525859f06e85a4cc492d3ae2 100644 --- a/src/cooling/none/cooling.h +++ b/src/cooling/none/cooling.h @@ -23,18 +23,33 @@ * @file src/cooling/none/cooling.h * @brief Empty infrastructure for the cases without cooling function */ +#include "../config.h" /* Some standard headers. */ #include <float.h> #include <math.h> /* Local includes. */ -#include "error.h" +#include "cooling_struct.h" +#include "cosmology.h" +#include "entropy_floor.h" #include "hydro.h" -#include "parser.h" +#include "hydro_properties.h" #include "part.h" -#include "physical_constants.h" -#include "units.h" + +/** + * @brief Common operations performed on the cooling function at a + * given time-step or redshift. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + */ +INLINE static void cooling_update(const struct cosmology* cosmo, + struct cooling_function_data* cooling) { + // Add content if required. +} /** * @brief Apply the cooling function to a particle. @@ -56,6 +71,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct unit_system* restrict us, const struct cosmology* restrict cosmo, const struct hydro_props* hydro_props, + const struct entropy_floor_properties* floor_props, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, const float dt, const float dt_therm) {} @@ -104,6 +120,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part( const struct cooling_function_data* data, const struct part* restrict p, struct xpart* restrict xp) {} +/** + * @brief Compute the temperature of a #part based on the cooling function. + * + * @param phys_const #phys_const data structure. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cosmo #cosmology data structure. + * @param cooling #cooling_function_data struct. + * @param p #part data. + * @param xp Pointer to the #xpart data. + */ +INLINE static float cooling_get_temperature( + const struct phys_const* restrict phys_const, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + const struct part* restrict p, const struct xpart* restrict xp) { + + /* Physical constants */ + const double m_H = phys_const->const_proton_mass; + const double k_B = phys_const->const_boltzmann_k; + + /* Gas properties */ + const double T_transition = hydro_props->hydrogen_ionization_temperature; + const double mu_neutral = hydro_props->mu_neutral; + const double mu_ionised = hydro_props->mu_ionised; + + /* Particle temperature */ + const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + + /* Temperature over mean molecular weight */ + const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; + + /* Are we above or below the HII -> HI transition? */ + if (T_over_mu > (T_transition + 1.) / mu_ionised) + return T_over_mu * mu_ionised; + else if (T_over_mu < (T_transition - 1.) / mu_neutral) + return T_over_mu * mu_neutral; + else + return T_transition; +} + /** * @brief Returns the total radiated energy by this particle. * @@ -144,4 +203,36 @@ static INLINE void cooling_print_backend( message("Cooling function is 'No cooling'."); } +/** + * @brief Clean-up the memory allocated for the cooling routines + * + * @param cooling the cooling data structure. + */ +static INLINE void cooling_clean(struct cooling_function_data* cooling) {} + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * Empty structure so nothing to do here. + * + * @param cooling the struct + * @param stream the file stream + */ +static INLINE void cooling_struct_dump( + const struct cooling_function_data* cooling, FILE* stream) {} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * Empty structure so nothing to do here. + * + * @param cooling the struct + * @param stream the file stream + * @param cosmo #cosmology structure + */ +static INLINE void cooling_struct_restore(struct cooling_function_data* cooling, + FILE* stream, + const struct cosmology* cosmo) {} + #endif /* SWIFT_COOLING_NONE_H */ diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h index 518c166480a0b81f6856c8a39e2a64d34369dc84..16b4b4ca29f8ebd325decc25420d7db617e1e4ef 100644 --- a/src/cooling/none/cooling_io.h +++ b/src/cooling/none/cooling_io.h @@ -23,6 +23,7 @@ #include "../config.h" /* Local includes */ +#include "cooling.h" #include "io_properties.h" #ifdef HAVE_HDF5 @@ -39,9 +40,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( } #endif +INLINE static void convert_part_T(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties, + e->internal_units, e->cosmology, + e->cooling_func, p, xp); +} + /** * @brief Specifies which particle fields to write to a dataset * + * @param parts The particle array. * @param xparts The extended particle array. * @param list The list of i/o properties to write. * @param cooling The #cooling_function_data @@ -49,9 +59,13 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour( * @return Returns the number of fields to write. */ __attribute__((always_inline)) INLINE static int cooling_write_particles( - const struct xpart* xparts, struct io_props* list, + const struct part* parts, const struct xpart* xparts, struct io_props* list, const struct cooling_function_data* cooling) { - return 0; + + list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, parts, + xparts, convert_part_T); + return 1; } #endif /* SWIFT_COOLING_NONE_IO_H */ diff --git a/src/cosmology.c b/src/cosmology.c index 4718ed5b316e514476e3ec38dd8771136f3a2f69..be23343d0d62584cd3a811e547b327120db744ef 100644 --- a/src/cosmology.c +++ b/src/cosmology.c @@ -576,6 +576,8 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->a_dot = 0.; c->time = 0.; c->universe_age_at_present_day = 0.; + c->Hubble_time = 0.; + c->lookback_time = 0.; /* Initialise the interpolation tables */ c->drift_fac_interp_table = NULL; diff --git a/src/cycle.h b/src/cycle.h index 65fe3bee17173dd1efdc98f5d90f8cc4fe51f007..0ba1277893fe7be7d8b62cdcb9a8873d5709eb60 100644 --- a/src/cycle.h +++ b/src/cycle.h @@ -543,7 +543,8 @@ INLINE_ELAPSED(inline) #define HAVE_TICK_COUNTER #endif -#if defined(__aarch64__) && defined(HAVE_ARMV8_PMCCNTR_EL0) +#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; diff --git a/src/debug.c b/src/debug.c index 809d7048c45888eb41bd277bdb4971d469a98dc3..2c1d81b676520c4823a27f3a7a8cc617585ca5f2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -60,6 +60,8 @@ #include "./hydro/Shadowswift/hydro_debug.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_debug.h" +#elif defined(ANARCHY_PU_SPH) +#include "./hydro/AnarchyPU/hydro_debug.h" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/dimension.h b/src/dimension.h index 0b2093d718a61c6ce850db1970412af3e2e462b9..2ae3a6d8ee9cac0b8bd3241d5e7b45f8f62dc92a 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -89,6 +89,34 @@ __attribute__((always_inline)) INLINE static float pow_dimension(float x) { #endif } +/** + * @brief Returns the argument to the power given by the inverse of the + * dimension + * + * Computes \f$x^{1/d}\f$. + */ +__attribute__((always_inline)) INLINE static float pow_inv_dimension(float x) { + +#if defined(HYDRO_DIMENSION_3D) + + return cbrtf(x); + +#elif defined(HYDRO_DIMENSION_2D) + + return sqrtf(x); + +#elif defined(HYDRO_DIMENSION_1D) + + return x; + +#else + + error("The dimension is not defined !"); + return 0.f; + +#endif +} + /** * @brief Returns the argument to the power given by the dimension plus one * @@ -288,7 +316,7 @@ __attribute__((always_inline)) INLINE static vector pow_dimension_vec( #else error("The dimension is not defined !"); - return vec_set(0.f); + return vec_set1(0.f); #endif } @@ -318,7 +346,7 @@ __attribute__((always_inline)) INLINE static vector pow_dimension_plus_one_vec( #else error("The dimension is not defined !"); - return vec_set(0.f); + return vec_set1(0.f); #endif } diff --git a/src/drift.h b/src/drift.h index 351b15381dde9a95af908003b1c8df01b56610fa..a4bdf9be74aade4fe0f1349544cf472363c81c99 100644 --- a/src/drift.h +++ b/src/drift.h @@ -142,6 +142,7 @@ __attribute__((always_inline)) INLINE static void drift_spart( for (int k = 0; k < 3; k++) { const float dx = sp->v[k] * dt_drift; sp->x_diff[k] -= dx; + sp->x_diff_sort[k] -= dx; } } diff --git a/src/engine.c b/src/engine.c index 5e0bfb764263024314185e5ce80fdff05a3e46f5..61ee1b2c07147bef892816fdc6bb8fef850a82eb 100644 --- a/src/engine.c +++ b/src/engine.c @@ -62,6 +62,7 @@ #include "cosmology.h" #include "cycle.h" #include "debug.h" +#include "entropy_floor.h" #include "equation_of_state.h" #include "error.h" #include "gravity.h" @@ -83,7 +84,7 @@ #include "serial_io.h" #include "single_io.h" #include "sort_part.h" -#include "sourceterms.h" +#include "star_formation.h" #include "stars_io.h" #include "statistics.h" #include "timers.h" @@ -109,13 +110,14 @@ const char *engine_policy_names[] = {"none", "cosmological integration", "drift everything", "reconstruct multi-poles", + "temperature", "cooling", - "sourceterms", "stars", "fof search", "structure finding", "star formation", - "feedback"}; + "feedback", + "time-step limiter"}; /** The rank of the engine as a global variable (for messages). */ int engine_rank; @@ -129,6 +131,7 @@ struct end_of_step_data { size_t inhibited, g_inhibited, s_inhibited; integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max; integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max; + integertime_t ti_stars_end_min; struct engine *e; }; @@ -146,7 +149,9 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) { /* Get the next free link. */ const size_t ind = atomic_inc(&e->nr_links); if (ind >= e->size_links) { - error("Link table overflow."); + error( + "Link table overflow. Increase the value of " + "`Scheduler:links_per_tasks`."); } struct link *res = &e->links[ind]; @@ -981,8 +986,7 @@ void engine_repartition(struct engine *e) { fflush(stdout); /* Check that all cells have been drifted to the current time */ - space_check_drift_point(e->s, e->ti_current, - e->policy & engine_policy_self_gravity); + space_check_drift_point(e->s, e->ti_current, /*check_multipoles=*/0); #endif /* Clear the repartition flag. */ @@ -992,6 +996,12 @@ void engine_repartition(struct engine *e) { * bug that doesn't handle this case well. */ if (e->nr_nodes == 1) return; + /* Generate the fixed costs include file. */ + if (e->step > 3 && e->reparttype->trigger <= 1.f) { + task_dump_stats("partition_fixed_costs.h", e, /* header = */ 1, + /* allranks = */ 1); + } + /* Do the repartitioning. */ partition_repartition(e->reparttype, e->nodeID, e->nr_nodes, e->s, e->sched.tasks, e->sched.nr_tasks); @@ -1047,32 +1057,41 @@ void engine_repartition_trigger(struct engine *e) { const ticks tic = getticks(); - /* Do nothing if there have not been enough steps since the last - * repartition, don't want to repeat this too often or immediately after - * a repartition step. Also nothing to do when requested. */ + /* Do nothing if there have not been enough steps since the last repartition + * as we don't want to repeat this too often or immediately after a + * repartition step. Also nothing to do when requested. */ if (e->step - e->last_repartition >= 2 && e->reparttype->type != REPART_NONE) { - /* Old style if trigger is >1 or this is the second step (want an early - * repartition following the initial repartition). */ - if (e->reparttype->trigger > 1 || e->step == 2) { + /* If we have fixed costs available and this is step 2 or we are forcing + * repartitioning then we do a fixed costs one now. */ + if (e->reparttype->trigger > 1 || + (e->step == 2 && e->reparttype->use_fixed_costs)) { + if (e->reparttype->trigger > 1) { if ((e->step % (int)e->reparttype->trigger) == 0) e->forcerepart = 1; } else { e->forcerepart = 1; } + e->reparttype->use_ticks = 0; } else { - /* Use cputimes from ranks to estimate the imbalance. */ - /* First check if we are going to skip this stage anyway, if so do that - * now. If is only worth checking the CPU loads when we have processed a - * significant number of all particles. */ + /* It is only worth checking the CPU loads when we have processed a + * significant number of all particles as we require all tasks to have + * timings. */ if ((e->updates > 1 && e->updates >= e->total_nr_parts * e->reparttype->minfrac) || (e->g_updates > 1 && e->g_updates >= e->total_nr_gparts * e->reparttype->minfrac)) { + /* Should we are use the task timings or fixed costs. */ + if (e->reparttype->use_fixed_costs > 1) { + e->reparttype->use_ticks = 0; + } else { + e->reparttype->use_ticks = 1; + } + /* Get CPU time used since the last call to this function. */ double elapsed_cputime = clocks_get_cputime_used() - e->cputime_last_step; @@ -1095,17 +1114,22 @@ void engine_repartition_trigger(struct engine *e) { double mean = sum / (double)e->nr_nodes; /* Are we out of balance? */ - if (((maxtime - mintime) / mean) > e->reparttype->trigger) { + double abs_trigger = fabs(e->reparttype->trigger); + if (((maxtime - mintime) / mean) > abs_trigger) { if (e->verbose) - message("trigger fraction %.3f exceeds %.3f will repartition", - (maxtime - mintime) / mintime, e->reparttype->trigger); + message("trigger fraction %.3f > %.3f will repartition", + (maxtime - mintime) / mean, abs_trigger); e->forcerepart = 1; + } else { + if (e->verbose) + message("trigger fraction %.3f =< %.3f will not repartition", + (maxtime - mintime) / mean, abs_trigger); } } - - /* All nodes do this together. */ - MPI_Bcast(&e->forcerepart, 1, MPI_INT, 0, MPI_COMM_WORLD); } + + /* All nodes do this together. */ + MPI_Bcast(&e->forcerepart, 1, MPI_INT, 0, MPI_COMM_WORLD); } /* Remember we did this. */ @@ -1131,84 +1155,12 @@ void engine_exchange_cells(struct engine *e) { #ifdef WITH_MPI - struct space *s = e->s; - const int nr_proxies = e->nr_proxies; const int with_gravity = e->policy & engine_policy_self_gravity; const ticks tic = getticks(); /* Exchange the cell structure with neighbouring ranks. */ proxy_cells_exchange(e->proxies, e->nr_proxies, e->s, with_gravity); - ticks tic2 = getticks(); - - /* Count the number of particles we need to import and re-allocate - the buffer if needed. */ - size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0; - for (int k = 0; k < nr_proxies; k++) - for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { - if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) - count_parts_in += e->proxies[k].cells_in[j]->hydro.count; - if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) - count_gparts_in += e->proxies[k].cells_in[j]->grav.count; - count_sparts_in += e->proxies[k].cells_in[j]->stars.count; - } - if (count_parts_in > s->size_parts_foreign) { - if (s->parts_foreign != NULL) free(s->parts_foreign); - s->size_parts_foreign = 1.1 * count_parts_in; - if (posix_memalign((void **)&s->parts_foreign, part_align, - sizeof(struct part) * s->size_parts_foreign) != 0) - error("Failed to allocate foreign part data."); - } - if (count_gparts_in > s->size_gparts_foreign) { - if (s->gparts_foreign != NULL) free(s->gparts_foreign); - s->size_gparts_foreign = 1.1 * count_gparts_in; - if (posix_memalign((void **)&s->gparts_foreign, gpart_align, - sizeof(struct gpart) * s->size_gparts_foreign) != 0) - error("Failed to allocate foreign gpart data."); - } - if (count_sparts_in > s->size_sparts_foreign) { - if (s->sparts_foreign != NULL) free(s->sparts_foreign); - s->size_sparts_foreign = 1.1 * count_sparts_in; - if (posix_memalign((void **)&s->sparts_foreign, spart_align, - sizeof(struct spart) * s->size_sparts_foreign) != 0) - error("Failed to allocate foreign spart data."); - } - - if (e->verbose) - message("Counting and allocating arrays took %.3f %s.", - clocks_from_ticks(getticks() - tic2), clocks_getunit()); - - tic2 = getticks(); - - /* Unpack the cells and link to the particle data. */ - struct part *parts = s->parts_foreign; - struct gpart *gparts = s->gparts_foreign; - struct spart *sparts = s->sparts_foreign; - for (int k = 0; k < nr_proxies; k++) { - for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { - - if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) { - cell_link_parts(e->proxies[k].cells_in[j], parts); - parts = &parts[e->proxies[k].cells_in[j]->hydro.count]; - } - - if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) { - cell_link_gparts(e->proxies[k].cells_in[j], gparts); - gparts = &gparts[e->proxies[k].cells_in[j]->grav.count]; - } - - cell_link_sparts(e->proxies[k].cells_in[j], sparts); - sparts = &sparts[e->proxies[k].cells_in[j]->stars.count]; - } - } - s->nr_parts_foreign = parts - s->parts_foreign; - s->nr_gparts_foreign = gparts - s->gparts_foreign; - s->nr_sparts_foreign = sparts - s->sparts_foreign; - - if (e->verbose) - message("Recursively linking arrays took %.3f %s.", - clocks_from_ticks(getticks() - tic2), clocks_getunit()); - if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), clocks_getunit()); @@ -1823,18 +1775,165 @@ void engine_exchange_proxy_multipoles(struct engine *e) { #endif } +/** + * @brief Allocate memory for the foreign particles. + * + * We look into the proxies for cells that have tasks and count + * the number of particles in these cells. We then allocate + * memory and link all the cells that have tasks and all cells + * deeper in the tree. + * + * @param e The #engine. + */ +void engine_allocate_foreign_particles(struct engine *e) { + +#ifdef WITH_MPI + + const int nr_proxies = e->nr_proxies; + struct space *s = e->s; + ticks tic = getticks(); + + /* Count the number of particles we need to import and re-allocate + the buffer if needed. */ + size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0; + for (int k = 0; k < nr_proxies; k++) { + for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { + + if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) { + count_parts_in += cell_count_parts_for_tasks(e->proxies[k].cells_in[j]); + } + + if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) { + count_gparts_in += + cell_count_gparts_for_tasks(e->proxies[k].cells_in[j]); + } + + /* For stars, we just use the numbers in the top-level cells */ + count_sparts_in += e->proxies[k].cells_in[j]->stars.count; + } + } + + if (e->verbose) + message("Counting number of foreign particles took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); + + /* Allocate space for the foreign particles we will receive */ + if (count_parts_in > s->size_parts_foreign) { + if (s->parts_foreign != NULL) free(s->parts_foreign); + s->size_parts_foreign = engine_foreign_alloc_margin * count_parts_in; + if (posix_memalign((void **)&s->parts_foreign, part_align, + sizeof(struct part) * s->size_parts_foreign) != 0) + error("Failed to allocate foreign part data."); + } + /* Allocate space for the foreign particles we will receive */ + if (count_gparts_in > s->size_gparts_foreign) { + if (s->gparts_foreign != NULL) free(s->gparts_foreign); + s->size_gparts_foreign = engine_foreign_alloc_margin * count_gparts_in; + if (posix_memalign((void **)&s->gparts_foreign, gpart_align, + sizeof(struct gpart) * s->size_gparts_foreign) != 0) + error("Failed to allocate foreign gpart data."); + } + /* Allocate space for the foreign particles we will receive */ + if (count_sparts_in > s->size_sparts_foreign) { + if (s->sparts_foreign != NULL) free(s->sparts_foreign); + s->size_sparts_foreign = engine_foreign_alloc_margin * count_sparts_in; + if (posix_memalign((void **)&s->sparts_foreign, spart_align, + sizeof(struct spart) * s->size_sparts_foreign) != 0) + error("Failed to allocate foreign spart data."); + } + + if (e->verbose) + message("Allocating %zd/%zd/%zd foreign part/gpart/spart (%zd/%zd/%zd MB)", + s->size_parts_foreign, s->size_gparts_foreign, + s->size_sparts_foreign, + s->size_parts_foreign * sizeof(struct part) / (1024 * 1024), + s->size_gparts_foreign * sizeof(struct gpart) / (1024 * 1024), + s->size_sparts_foreign * sizeof(struct spart) / (1024 * 1024)); + + /* Unpack the cells and link to the particle data. */ + struct part *parts = s->parts_foreign; + struct gpart *gparts = s->gparts_foreign; + struct spart *sparts = s->sparts_foreign; + for (int k = 0; k < nr_proxies; k++) { + for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { + + if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) { + + const size_t count_parts = + cell_link_foreign_parts(e->proxies[k].cells_in[j], parts); + parts = &parts[count_parts]; + } + + if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) { + + const size_t count_gparts = + cell_link_foreign_gparts(e->proxies[k].cells_in[j], gparts); + gparts = &gparts[count_gparts]; + } + + /* For stars, we just use the numbers in the top-level cells */ + cell_link_sparts(e->proxies[k].cells_in[j], sparts); + sparts = &sparts[e->proxies[k].cells_in[j]->stars.count]; + } + } + + /* Update the counters */ + s->nr_parts_foreign = parts - s->parts_foreign; + s->nr_gparts_foreign = gparts - s->gparts_foreign; + s->nr_sparts_foreign = sparts - s->sparts_foreign; + + if (e->verbose) + message("Recursively linking foreign arrays took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + +#else + error("SWIFT was not compiled with MPI support."); +#endif +} + /** * @brief Prints the number of tasks in the engine * * @param e The #engine. */ -void engine_print_task_counts(struct engine *e) { +void engine_print_task_counts(const struct engine *e) { const ticks tic = getticks(); - struct scheduler *const sched = &e->sched; + const struct scheduler *sched = &e->sched; const int nr_tasks = sched->nr_tasks; const struct task *const tasks = sched->tasks; + /* Global tasks and cells when using MPI. */ +#ifdef WITH_MPI + if (e->nodeID == 0 && e->total_nr_tasks > 0) + printf( + "[%04i] %s engine_print_task_counts: System total: %lld," + " no. cells: %lld\n", + e->nodeID, clocks_get_timesincestart(), e->total_nr_tasks, + e->total_nr_cells); + fflush(stdout); +#endif + + /* Report value that can be used to estimate the task_per_cells parameter. */ + float tasks_per_cell = (float)nr_tasks / (float)e->s->tot_cells; + +#ifdef WITH_MPI + message("Total = %d (per cell = %.2f)", nr_tasks, tasks_per_cell); + + /* And the system maximum on rank 0, only after first step, increase by our + * margin to allow for some variation in repartitioning. */ + if (e->nodeID == 0 && e->total_nr_tasks > 0) { + message("Total = %d (maximum per cell = %.2f)", nr_tasks, + e->tasks_per_cell_max * engine_tasks_per_cell_margin); + } + +#else + message("Total = %d (per cell = %.2f)", nr_tasks, tasks_per_cell); +#endif + fflush(stdout); + /* Count and print the number of each task type. */ int counts[task_type_count + 1]; for (int k = 0; k <= task_type_count; k++) counts[k] = 0; @@ -1844,8 +1943,7 @@ void engine_print_task_counts(struct engine *e) { else counts[(int)tasks[k].type] += 1; } - message("Total = %d (per cell = %d)", nr_tasks, - (int)ceil((double)nr_tasks / e->s->tot_cells)); + #ifdef WITH_MPI printf("[%04i] %s engine_print_task_counts: task counts are [ %s=%i", e->nodeID, clocks_get_timesincestart(), taskID_names[0], counts[0]); @@ -1870,7 +1968,7 @@ void engine_print_task_counts(struct engine *e) { * @brief if necessary, estimate the number of tasks required given * the current tasks in use and the numbers of cells. * - * If e->tasks_per_cell is set greater than 0 then that value is used + * If e->tasks_per_cell is set greater than 0.0 then that value is used * as the estimate of the average number of tasks per cell, * otherwise we attempt an estimate. * @@ -1878,20 +1976,28 @@ void engine_print_task_counts(struct engine *e) { * * @return the estimated total number of tasks */ -int engine_estimate_nr_tasks(struct engine *e) { +int engine_estimate_nr_tasks(const struct engine *e) { - int tasks_per_cell = e->tasks_per_cell; - if (tasks_per_cell > 0) return e->s->tot_cells * tasks_per_cell; + float tasks_per_cell = e->tasks_per_cell; + if (tasks_per_cell > 0.0f) { + if (e->verbose) + message("tasks per cell given as: %.2f, so maximum tasks: %d", + e->tasks_per_cell, (int)(e->s->tot_cells * tasks_per_cell)); + return (int)(e->s->tot_cells * tasks_per_cell); + } /* Our guess differs depending on the types of tasks we are using, but we * basically use a formula <n1>*ntopcells + <n2>*(totcells - ntopcells). * Where <n1> is the expected maximum tasks per top-level/super cell, and * <n2> the expected maximum tasks for all other cells. These should give - * a safe upper limit. - */ + * a safe upper limit. */ int n1 = 0; int n2 = 0; if (e->policy & engine_policy_hydro) { + /* 2 self (density, force), 1 sort, 26/2 density pairs + 26/2 force pairs, 1 drift, 3 ghosts, 2 kicks, 1 time-step, + 1 end_force, 2 extra space + */ n1 += 37; n2 += 2; #ifdef WITH_MPI @@ -1905,6 +2011,10 @@ int engine_estimate_nr_tasks(struct engine *e) { #endif #endif } + if (e->policy & engine_policy_limiter) { + n1 += 18; + n2 += 1; + } if (e->policy & engine_policy_self_gravity) { n1 += 125; n2 += 8; @@ -1919,18 +2029,28 @@ int engine_estimate_nr_tasks(struct engine *e) { n1 += 2; } if (e->policy & engine_policy_cooling) { + /* Cooling task + extra space */ n1 += 2; } - if (e->policy & engine_policy_sourceterms) { - n1 += 2; + if (e->policy & engine_policy_star_formation) { + n1 += 1; } if (e->policy & engine_policy_stars) { - n1 += 2; + /* 2 self (density, feedback), 1 sort, 26/2 density pairs + 26/2 feedback pairs, 1 drift, 3 ghosts, 2 kicks, 1 time-step, + 1 end_force, 2 extra space + */ + n1 += 37; + n2 += 2; +#ifdef WITH_MPI + n1 += 6; +#endif } if (e->policy & engine_policy_fof) { n1 += 2; } #if defined(WITH_LOGGER) + /* each cell logs its particles */ n1 += 1; #endif @@ -1972,15 +2092,15 @@ int engine_estimate_nr_tasks(struct engine *e) { int ncells = e->s->tot_cells; #endif - double ntasks = n1 * ntop + n2 * (ncells - ntop); + float ntasks = n1 * ntop + n2 * (ncells - ntop); if (ncells > 0) tasks_per_cell = ceil(ntasks / ncells); - if (tasks_per_cell < 1.0) tasks_per_cell = 1.0; + if (tasks_per_cell < 1.0f) tasks_per_cell = 1.0f; if (e->verbose) - message("tasks per cell estimated as: %d, maximum tasks: %d", - tasks_per_cell, ncells * tasks_per_cell); + message("tasks per cell estimated as: %.2f, maximum tasks: %d", + tasks_per_cell, (int)(ncells * tasks_per_cell)); - return ncells * tasks_per_cell; + return (int)(ncells * tasks_per_cell); } /** @@ -2003,12 +2123,30 @@ void engine_rebuild(struct engine *e, int repartitioned, /* Re-build the space. */ space_rebuild(e->s, repartitioned, e->verbose); + /* Report the number of cells and memory */ + if (e->verbose) + message( + "Nr. of top-level cells: %d Nr. of local cells: %d memory use: %zd MB.", + e->s->nr_cells, e->s->tot_cells, + (e->s->nr_cells + e->s->tot_cells) * sizeof(struct cell) / + (1024 * 1024)); + + /* Report the number of multipoles and memory */ + if (e->verbose && (e->policy & engine_policy_self_gravity)) + message( + "Nr. of top-level mpoles: %d Nr. of local mpoles: %d memory use: %zd " + "MB.", + e->s->nr_cells, e->s->tot_cells, + (e->s->nr_cells + e->s->tot_cells) * sizeof(struct gravity_tensors) / + (1024 * 1024)); + const ticks tic2 = getticks(); /* Update the global counters of particles */ - long long num_particles[3] = {(long long)e->s->nr_parts, - (long long)e->s->nr_gparts, - (long long)e->s->nr_sparts}; + long long num_particles[3] = { + (long long)(e->s->nr_parts - e->s->nr_extra_parts), + (long long)(e->s->nr_gparts - e->s->nr_extra_gparts), + (long long)(e->s->nr_sparts - e->s->nr_extra_sparts)}; #ifdef WITH_MPI MPI_Allreduce(MPI_IN_PLACE, num_particles, 3, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); @@ -2120,7 +2258,7 @@ void engine_prepare(struct engine *e) { /* Unskip active tasks and check for rebuild */ if (!e->forcerebuild && !e->forcerepart && !e->restarting) engine_unskip(e); - const ticks tic2 = getticks(); + const ticks tic3 = getticks(); #ifdef WITH_MPI MPI_Allreduce(MPI_IN_PLACE, &e->forcerebuild, 1, MPI_INT, MPI_MAX, @@ -2129,13 +2267,13 @@ void engine_prepare(struct engine *e) { if (e->verbose) message("Communicating rebuild flag took %.3f %s.", - clocks_from_ticks(getticks() - tic2), clocks_getunit()); + clocks_from_ticks(getticks() - tic3), clocks_getunit()); /* Do we need repartitioning ? */ if (e->forcerepart) { /* Let's start by drifting everybody to the current time */ - engine_drift_all(e); + engine_drift_all(e, /*drift_mpole=*/0); drifted_all = 1; /* And repartition */ @@ -2147,7 +2285,7 @@ void engine_prepare(struct engine *e) { if (e->forcerebuild) { /* Let's start by drifting everybody to the current time */ - if (!e->restarting && !drifted_all) engine_drift_all(e); + if (!e->restarting && !drifted_all) engine_drift_all(e, /*drift_mpole=*/0); /* And rebuild */ engine_rebuild(e, repartitioned, 0); @@ -2218,6 +2356,7 @@ void engine_collect_end_of_step_recurse(struct cell *c, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps; /* Collect the values from the progeny. */ for (int k = 0; k < 8; k++) { @@ -2237,6 +2376,8 @@ void engine_collect_end_of_step_recurse(struct cell *c, ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max); ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max); + ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); + updated += cp->hydro.updated; g_updated += cp->grav.updated; s_updated += cp->stars.updated; @@ -2259,6 +2400,7 @@ void engine_collect_end_of_step_recurse(struct cell *c, c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_end_max = ti_gravity_end_max; c->grav.ti_beg_max = ti_gravity_beg_max; + c->stars.ti_end_min = ti_stars_end_min; c->hydro.updated = updated; c->grav.updated = g_updated; c->stars.updated = s_updated; @@ -2293,6 +2435,7 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps; for (int ind = 0; ind < num_elements; ind++) { struct cell *c = &s->cells_top[local_cells[ind]]; @@ -2313,6 +2456,9 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, ti_gravity_end_max = max(ti_gravity_end_max, c->grav.ti_end_max); ti_gravity_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max); + if (c->stars.ti_end_min > e->ti_current) + ti_stars_end_min = min(ti_stars_end_min, c->stars.ti_end_min); + updated += c->hydro.updated; g_updated += c->grav.updated; s_updated += c->stars.updated; @@ -2351,6 +2497,9 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, max(ti_gravity_end_max, data->ti_gravity_end_max); data->ti_gravity_beg_max = max(ti_gravity_beg_max, data->ti_gravity_beg_max); + + if (ti_stars_end_min > e->ti_current) + data->ti_stars_end_min = min(ti_stars_end_min, data->ti_stars_end_min); } if (lock_unlock(&s->lock) != 0) error("Failed to unlock the space"); @@ -2401,7 +2550,9 @@ void engine_collect_end_of_step(struct engine *e, int apply) { &e->collect_group1, data.updated, data.g_updated, data.s_updated, data.inhibited, data.g_inhibited, data.s_inhibited, data.ti_hydro_end_min, data.ti_hydro_end_max, data.ti_hydro_beg_max, data.ti_gravity_end_min, - data.ti_gravity_end_max, data.ti_gravity_beg_max, e->forcerebuild); + data.ti_gravity_end_max, data.ti_gravity_beg_max, e->forcerebuild, + e->s->tot_cells, e->sched.nr_tasks, + (float)e->sched.nr_tasks / (float)e->s->tot_cells); /* Aggregate collective data from the different nodes for this step. */ #ifdef WITH_MPI @@ -2492,8 +2643,7 @@ void engine_print_stats(struct engine *e) { /* Check that all cells have been drifted to the current time. * That can include cells that have not * previously been active on this rank. */ - space_check_drift_point(e->s, e->ti_current, - e->policy & engine_policy_self_gravity); + space_check_drift_point(e->s, e->ti_current, /*chek_mpoles=*/0); /* Be verbose about this */ if (e->nodeID == 0) { @@ -2565,16 +2715,21 @@ void engine_skip_force_and_kick(struct engine *e) { /* Skip everything that updates the particles */ if (t->type == task_type_drift_part || t->type == task_type_drift_gpart || t->type == task_type_kick1 || t->type == task_type_kick2 || - t->type == task_type_timestep || t->subtype == task_subtype_force || - t->subtype == task_subtype_grav || t->type == task_type_end_force || + t->type == task_type_timestep || + t->type == task_type_timestep_limiter || + t->subtype == task_subtype_force || + t->subtype == task_subtype_limiter || t->subtype == task_subtype_grav || + t->type == task_type_end_force || t->type == task_type_grav_long_range || t->type == task_type_grav_mm || t->type == task_type_grav_down || t->type == task_type_cooling || - t->type == task_type_sourceterms) + t->type == task_type_star_formation || + t->type == task_type_extra_ghost || t->subtype == task_subtype_gradient) t->skip = 1; } /* Run through the cells and clear some flags. */ space_map_cells_pre(e->s, 1, cell_clear_drift_flags, NULL); + space_map_cells_pre(e->s, 1, cell_clear_limiter_flags, NULL); } /** @@ -2677,7 +2832,12 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, /* Update the softening lengths */ if (e->policy & engine_policy_self_gravity) - gravity_update(e->gravity_properties, e->cosmology); + gravity_props_update(e->gravity_properties, e->cosmology); + + /* Udpate the hydro properties */ + if (e->policy & engine_policy_hydro) + hydro_props_update(e->hydro_properties, e->gravity_properties, + e->cosmology); /* Start by setting the particles in a good state */ if (e->nodeID == 0) message("Setting particles to a valid state..."); @@ -2714,6 +2874,11 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, space_init_gparts(s, e->verbose); space_init_sparts(s, e->verbose); + /* Update the cooling function */ + if ((e->policy & engine_policy_cooling) || + (e->policy & engine_policy_temperature)) + cooling_update(e->cosmology, e->cooling_func); + #ifdef WITH_LOGGER /* Mark the first time step in the particle logger file. */ logger_log_timestamp(e->logger, e->ti_current, e->time, @@ -2784,7 +2949,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, gravity_exact_force_compute(e->s, e); #endif - if (e->nodeID == 0) scheduler_write_dependencies(&e->sched, e->verbose); + scheduler_write_dependencies(&e->sched, e->verbose); if (e->nodeID == 0) scheduler_write_task_level(&e->sched); /* Run the 0th time-step */ @@ -2798,6 +2963,11 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, gravity_exact_force_check(e->s, e, 1e-1); #endif +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure all woken-up particles have been processed */ + space_check_limiter(e->s); +#endif + /* Recover the (integer) end of the next time-step */ engine_collect_end_of_step(e, 1); @@ -2810,6 +2980,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, double *prev_x = s->parts[0].x; long long *prev_id = &s->parts[0].id; for (size_t k = 1; k < s->nr_parts; k++) { + + /* Ignore fake buffer particles for on-the-fly creation */ + if (s->parts[k].time_bin == time_bin_not_created) continue; + if (prev_x[0] == s->parts[k].x[0] && prev_x[1] == s->parts[k].x[1] && prev_x[2] == s->parts[k].x[2]) { if (e->verbose) @@ -2832,6 +3006,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, int failed = 0; double *prev_x = s->gparts[0].x; for (size_t k = 1; k < s->nr_gparts; k++) { + + /* Ignore fake buffer particles for on-the-fly creation */ + if (s->gparts[k].time_bin == time_bin_not_created) continue; + if (prev_x[0] == s->gparts[k].x[0] && prev_x[1] == s->gparts[k].x[1] && prev_x[2] == s->gparts[k].x[2]) { if (e->verbose) @@ -2909,9 +3087,7 @@ void engine_step(struct engine *e) { struct clocks_time time1, time2; clocks_gettime(&time1); -#ifdef SWIFT_DEBUG_TASKS e->tic_step = getticks(); -#endif if (e->nodeID == 0) { @@ -2922,7 +3098,9 @@ void engine_step(struct engine *e) { e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step, e->min_active_bin, e->max_active_bin, e->updates, e->g_updates, e->s_updates, e->wallclock_time, e->step_props); +#ifdef SWIFT_DEBUG_CHECKS fflush(stdout); +#endif if (!e->restarting) fprintf( @@ -2932,7 +3110,9 @@ void engine_step(struct engine *e) { e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step, e->min_active_bin, e->max_active_bin, e->updates, e->g_updates, e->s_updates, e->wallclock_time, e->step_props); +#ifdef SWIFT_DEBUG_CHECKS fflush(e->file_timesteps); +#endif } /* We need some cells to exist but not the whole task stuff. */ @@ -2947,7 +3127,7 @@ void engine_step(struct engine *e) { e->step_props = engine_step_prop_none; /* When restarting, move everyone to the current time. */ - if (e->restarting) engine_drift_all(e); + if (e->restarting) engine_drift_all(e, /*drift_mpole=*/1); /* Get the physical value of the time and time-step size */ if (e->policy & engine_policy_cosmology) { @@ -2961,13 +3141,23 @@ void engine_step(struct engine *e) { e->time_step = (e->ti_current - e->ti_old) * e->time_base; } + /* Update the cooling function */ + if ((e->policy & engine_policy_cooling) || + (e->policy & engine_policy_temperature)) + cooling_update(e->cosmology, e->cooling_func); + /*****************************************************/ /* OK, we now know what the next end of time-step is */ /*****************************************************/ /* Update the softening lengths */ if (e->policy & engine_policy_self_gravity) - gravity_update(e->gravity_properties, e->cosmology); + gravity_props_update(e->gravity_properties, e->cosmology); + + /* Udpate the hydro properties */ + if (e->policy & engine_policy_hydro) + hydro_props_update(e->hydro_properties, e->gravity_properties, + e->cosmology); /* Trigger a tree-rebuild if we passed the frequency threshold */ if ((e->policy & engine_policy_self_gravity) && @@ -2986,7 +3176,7 @@ void engine_step(struct engine *e) { /* Are we drifting everything (a la Gadget/GIZMO) ? */ if (e->policy & engine_policy_drift_all && !e->forcerebuild) - engine_drift_all(e); + engine_drift_all(e, /*drift_mpole=*/1); /* Are we reconstructing the multipoles or drifting them ?*/ if ((e->policy & engine_policy_self_gravity) && !e->forcerebuild) { @@ -3042,6 +3232,11 @@ void engine_step(struct engine *e) { gravity_exact_force_check(e->s, e, 1e-1); #endif +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure all woken-up particles have been processed */ + space_check_limiter(e->s); +#endif + /* Collect information about the next time-step */ engine_collect_end_of_step(e, 1); e->forcerebuild = e->collect_group1.forcerebuild; @@ -3068,10 +3263,8 @@ void engine_step(struct engine *e) { clocks_gettime(&time2); e->wallclock_time = (float)clocks_diff(&time1, &time2); -#ifdef SWIFT_DEBUG_TASKS /* Time in ticks at the end of this step. */ e->toc_step = getticks(); -#endif } /** @@ -3085,45 +3278,89 @@ void engine_step(struct engine *e) { */ void engine_check_for_dumps(struct engine *e) { + const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_stf = (e->policy & engine_policy_structure_finding); - const int stf_time_output = (e->stf_output_freq_format == io_stf_time); + + /* What kind of output are we getting? */ + enum output_type { + output_none, + output_snapshot, + output_statistics, + output_stf + }; + + /* What kind of output do we want? And at which time ? + * Find the earliest output (amongst all kinds) that takes place + * before the next time-step */ + enum output_type type = output_none; + integertime_t ti_output = max_nr_timesteps; /* Save some statistics ? */ - int save_stats = 0; - if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) save_stats = 1; + if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) { + if (e->ti_next_stats < ti_output) { + ti_output = e->ti_next_stats; + type = output_statistics; + } + } /* Do we want a snapshot? */ - int dump_snapshot = 0; - if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) - dump_snapshot = 1; + if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) { + if (e->ti_next_snapshot < ti_output) { + ti_output = e->ti_next_snapshot; + type = output_snapshot; + } + } /* Do we want to perform a FOF search? */ int run_fof = 0; - if ((e->policy & engine_policy_fof) && dump_snapshot) run_fof = 1; + if (e->policy & engine_policy_fof) run_fof = 1; /* Do we want to perform structure finding? */ - int run_stf = 0; - if (with_stf && stf_time_output) { - if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) run_stf = 1; - } - if (with_stf && !stf_time_output) { - if (e->step % e->delta_step_stf == 0) run_stf = 1; + if (with_stf) { + if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) { + if (e->ti_next_stf < ti_output) { + ti_output = e->ti_next_stf; + type = output_stf; + } + } } - /* Do we want to perform a FOF search? */ - if (e->policy & engine_policy_fof) e->run_fof = 1; /* Store information before attempting extra dump-related drifts */ - integertime_t ti_current = e->ti_current; - timebin_t max_active_bin = e->max_active_bin; - double time = e->time; + const integertime_t ti_current = e->ti_current; + const timebin_t max_active_bin = e->max_active_bin; + const double time = e->time; + + while (type != output_none) { + + /* Let's fake that we are at the dump time */ + e->ti_current = ti_output; + e->max_active_bin = 0; + if (with_cosmology) { + cosmology_update(e->cosmology, e->physical_constants, e->ti_current); + e->time = e->cosmology->time; + } else { + e->time = ti_output * e->time_base + e->time_begin; + } - while (save_stats || dump_snapshot || run_stf || run_fof) { + /* Drift everyone */ + engine_drift_all(e, /*drift_mpole=*/0); /* Write some form of output */ - if (dump_snapshot && save_stats) { + switch (type) { + case output_snapshot: + + /* Do we want a corresponding VELOCIraptor output? */ + if (with_stf && e->snapshot_invoke_stf) { - /* If both, need to figure out which one occurs first */ - if (e->ti_next_stats == e->ti_next_snapshot) { +#ifdef HAVE_VELOCIRAPTOR + velociraptor_invoke(e, /*linked_with_snap=*/1); + e->step_props |= engine_step_prop_stf; +#else + error( + "Asking for a VELOCIraptor output but SWIFT was compiled without " + "the interface!"); +#endif + } /* Perform a FOF search. */ if (e->run_fof) { @@ -3132,21 +3369,7 @@ void engine_check_for_dumps(struct engine *e) { e->run_fof = 0; } - /* Let's fake that we are at the common dump time */ - e->ti_current = e->ti_next_snapshot; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; - } - - /* Drift everyone */ - engine_drift_all(e); - - /* Dump everything */ - engine_print_stats(e); + /* Dump... */ #ifdef WITH_LOGGER /* Write a file containing the offsets in the particle logger. */ engine_dump_index(e); @@ -3154,158 +3377,46 @@ void engine_check_for_dumps(struct engine *e) { engine_dump_snapshot(e); #endif - } else if (e->ti_next_stats < e->ti_next_snapshot) { - - /* Let's fake that we are at the stats dump time */ - e->ti_current = e->ti_next_stats; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; - } - - /* Drift everyone */ - engine_drift_all(e); - - /* Dump stats */ - engine_print_stats(e); - - /* Let's fake that we are at the snapshot dump time */ - e->ti_current = e->ti_next_snapshot; - e->max_active_bin = 0; - if (!(e->policy & engine_policy_cosmology)) - e->time = e->ti_next_snapshot * e->time_base + e->time_begin; - - /* Drift everyone */ - engine_drift_all(e); - - /* Dump snapshot */ -#ifdef WITH_LOGGER - /* Write a file containing the offsets in the particle logger. */ - engine_dump_index(e); -#else - engine_dump_snapshot(e); + /* Free the memory allocated for VELOCIraptor i/o. */ + if (with_stf && e->snapshot_invoke_stf) { +#ifdef HAVE_VELOCIRAPTOR + free(e->s->gpart_group_data); + e->s->gpart_group_data = NULL; #endif - - } else if (e->ti_next_stats > e->ti_next_snapshot) { - - /* Let's fake that we are at the snapshot dump time */ - e->ti_current = e->ti_next_snapshot; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; } - /* Drift everyone */ - engine_drift_all(e); - - /* Dump snapshot */ -#ifdef WITH_LOGGER - /* Write a file containing the offsets in the particle logger. */ - engine_dump_index(e); -#else - engine_dump_snapshot(e); -#endif - - /* Let's fake that we are at the stats dump time */ - e->ti_current = e->ti_next_stats; - e->max_active_bin = 0; - if (!(e->policy & engine_policy_cosmology)) - e->time = e->ti_next_stats * e->time_base + e->time_begin; + /* ... and find the next output time */ + engine_compute_next_snapshot_time(e); + break; - /* Drift everyone */ - engine_drift_all(e); + case output_statistics: - /* Dump stats */ + /* Dump */ engine_print_stats(e); - } - - /* Let's compute the time of the next outputs */ - engine_compute_next_snapshot_time(e); - engine_compute_next_statistics_time(e); - - } else if (dump_snapshot) { - - /* Let's fake that we are at the snapshot dump time */ - e->ti_current = e->ti_next_snapshot; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; - } - - /* Drift everyone */ - engine_drift_all(e); - - /* Dump... */ -#ifdef WITH_LOGGER - /* Write a file containing the offsets in the particle logger. */ - engine_dump_index(e); -#else - engine_dump_snapshot(e); -#endif - - /* ... and find the next output time */ - engine_compute_next_snapshot_time(e); - - } else if (save_stats) { - - /* Let's fake that we are at the stats dump time */ - e->ti_current = e->ti_next_stats; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; - } - /* Drift everyone */ - engine_drift_all(e); + /* and move on */ + engine_compute_next_statistics_time(e); - /* Dump */ - engine_print_stats(e); - - /* and move on */ - engine_compute_next_statistics_time(e); - } + break; - /* Perform structure finding? */ - if (run_stf) { + case output_stf: #ifdef HAVE_VELOCIRAPTOR + /* Unleash the raptor! */ + velociraptor_invoke(e, /*linked_with_snap=*/0); + e->step_props |= engine_step_prop_stf; - // MATTHIEU: Check the order with the other i/o options. - if (!dump_snapshot && !save_stats) { - - /* Let's fake that we are at the stats dump time */ - e->ti_current = e->ti_next_stf; - e->max_active_bin = 0; - if ((e->policy & engine_policy_cosmology)) { - cosmology_update(e->cosmology, e->physical_constants, e->ti_current); - e->time = e->cosmology->time; - } else { - e->time = e->ti_next_stats * e->time_base + e->time_begin; - } - - /* Drift everyone */ - engine_drift_all(e); - } - - velociraptor_init(e); - velociraptor_invoke(e); - - /* ... and find the next output time */ - if (e->stf_output_freq_format == io_stf_time) + /* ... and find the next output time */ engine_compute_next_stf_time(e); +#else + error( + "Asking for a VELOCIraptor output but SWIFT was compiled without " + "the interface!"); #endif + break; + + default: + error("Invalid dump type"); } /* Perform a FOF search. */ @@ -3323,22 +3434,36 @@ void engine_check_for_dumps(struct engine *e) { /* We need to see whether whether we are in the pathological case * where there can be another dump before the next step. */ + type = output_none; + ti_output = max_nr_timesteps; + /* Save some statistics ? */ - save_stats = 0; - if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) - save_stats = 1; + if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) { + if (e->ti_next_stats < ti_output) { + ti_output = e->ti_next_stats; + type = output_statistics; + } + } /* Do we want a snapshot? */ - dump_snapshot = 0; - if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) - dump_snapshot = 1; + if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) { + if (e->ti_next_snapshot < ti_output) { + ti_output = e->ti_next_snapshot; + type = output_snapshot; + } + } /* Do we want to perform structure finding? */ - run_stf = 0; - if (with_stf && stf_time_output) { - if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) run_stf = 1; + if (with_stf) { + if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) { + if (e->ti_next_stf < ti_output) { + ti_output = e->ti_next_stf; + type = output_stf; + } + } } - } + + } /* While loop over output types */ /* Restore the information we stored */ e->ti_current = ti_current; @@ -3377,7 +3502,7 @@ void engine_dump_restarts(struct engine *e, int drifted_all, int force) { restart_remove_previous(e->restart_file); /* Drift all particles first (may have just been done). */ - if (!drifted_all) engine_drift_all(e); + if (!drifted_all) engine_drift_all(e, /*drift_mpole=*/1); restart_write(e, e->restart_file); if (e->verbose) @@ -3448,160 +3573,6 @@ void engine_unskip(struct engine *e) { clocks_getunit()); } -/** - * @brief Mapper function to drift *all* particle types and multipoles forward - * in time. - * - * @param map_data An array of #cell%s. - * @param num_elements Chunk size. - * @param extra_data Pointer to an #engine. - */ -void engine_do_drift_all_mapper(void *map_data, int num_elements, - void *extra_data) { - - const struct engine *e = (const struct engine *)extra_data; - const int restarting = e->restarting; - struct space *s = e->s; - struct cell *cells_top; - int *local_cells_with_tasks_top; - - if (restarting) { - - /* When restarting, we loop over all top-level cells */ - cells_top = (struct cell *)map_data; - local_cells_with_tasks_top = NULL; - - } else { - - /* In any other case, we use th list of local cells with tasks */ - cells_top = s->cells_top; - local_cells_with_tasks_top = (int *)map_data; - } - - for (int ind = 0; ind < num_elements; ind++) { - - struct cell *c; - - /* When restarting, the list of local cells with tasks does not - yet exist. We use the raw list of top-level cells instead */ - if (restarting) - c = &cells_top[ind]; - else - c = &cells_top[local_cells_with_tasks_top[ind]]; - - if (c->nodeID == e->nodeID) { - - /* Drift all the particles */ - cell_drift_part(c, e, 1); - - /* Drift all the g-particles */ - cell_drift_gpart(c, e, 1); - } - - /* Drift the multipoles */ - if (e->policy & engine_policy_self_gravity) { - cell_drift_all_multipoles(c, e); - } - } -} - -/** - * @brief Drift *all* particles and multipoles at all levels - * forward to the current time. - * - * @param e The #engine. - */ -void engine_drift_all(struct engine *e) { - - const ticks tic = getticks(); - -#ifdef SWIFT_DEBUG_CHECKS - if (e->nodeID == 0) { - if (e->policy & engine_policy_cosmology) - message("Drifting all to a=%e", - exp(e->ti_current * e->time_base) * e->cosmology->a_begin); - else - message("Drifting all to t=%e", - e->ti_current * e->time_base + e->time_begin); - } -#endif - - if (!e->restarting) { - - /* Normal case: We have a list of local cells with tasks to play with */ - threadpool_map(&e->threadpool, engine_do_drift_all_mapper, - e->s->local_cells_with_tasks_top, - e->s->nr_local_cells_with_tasks, sizeof(int), 0, e); - } else { - - /* When restarting, the list of local cells with tasks does not yet - exist. We use the raw list of top-level cells instead */ - threadpool_map(&e->threadpool, engine_do_drift_all_mapper, e->s->cells_top, - e->s->nr_cells, sizeof(struct cell), 0, e); - } - - /* Synchronize particle positions */ - space_synchronize_particle_positions(e->s); - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that all cells have been drifted to the current time. */ - space_check_drift_point(e->s, e->ti_current, - e->policy & engine_policy_self_gravity); - part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts, - e->s->nr_gparts, e->s->nr_sparts, e->verbose); -#endif - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - -/** - * @brief Mapper function to drift *all* top-level multipoles forward in - * time. - * - * @param map_data An array of #cell%s. - * @param num_elements Chunk size. - * @param extra_data Pointer to an #engine. - */ -void engine_do_drift_top_multipoles_mapper(void *map_data, int num_elements, - void *extra_data) { - - struct engine *e = (struct engine *)extra_data; - struct cell *cells = (struct cell *)map_data; - - for (int ind = 0; ind < num_elements; ind++) { - struct cell *c = &cells[ind]; - if (c != NULL) { - - /* Drift the multipole at this level only */ - if (c->grav.ti_old_multipole != e->ti_current) cell_drift_multipole(c, e); - } - } -} - -/** - * @brief Drift *all* top-level multipoles forward to the current time. - * - * @param e The #engine. - */ -void engine_drift_top_multipoles(struct engine *e) { - - const ticks tic = getticks(); - - threadpool_map(&e->threadpool, engine_do_drift_top_multipoles_mapper, - e->s->cells_top, e->s->nr_cells, sizeof(struct cell), 0, e); - -#ifdef SWIFT_DEBUG_CHECKS - /* Check that all cells have been drifted to the current time. */ - space_check_top_multipoles_drift_point(e->s, e->ti_current); -#endif - - if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - void engine_do_reconstruct_multipoles_mapper(void *map_data, int num_elements, void *extra_data) { @@ -3677,13 +3648,20 @@ void engine_makeproxies(struct engine *e) { const int with_gravity = (e->policy & engine_policy_self_gravity); const double theta_crit_inv = e->gravity_properties->theta_crit_inv; const double theta_crit2 = e->gravity_properties->theta_crit2; - const double max_distance = e->mesh->r_cut_max; + const double max_mesh_dist = e->mesh->r_cut_max; + const double max_mesh_dist2 = max_mesh_dist * max_mesh_dist; + + /* Distance between centre of the cell and corners */ + const double r_diag2 = cell_width[0] * cell_width[0] + + cell_width[1] * cell_width[1] + + cell_width[2] * cell_width[2]; + const double r_diag = 0.5 * sqrt(r_diag2); - /* Maximal distance between CoMs and any particle in the cell */ - const double r_max2 = cell_width[0] * cell_width[0] + - cell_width[1] * cell_width[1] + - cell_width[2] * cell_width[2]; - const double r_max = sqrt(r_max2); + /* Maximal distance from a shifted CoM to centre of cell */ + const double delta_CoM = engine_max_proxy_centre_frac * r_diag; + + /* Maximal distance from shifted CoM to any corner */ + const double r_max = r_diag + 2. * delta_CoM; /* Prepare the proxies and the proxy index. */ if (e->proxy_ind == NULL) @@ -3693,20 +3671,20 @@ void engine_makeproxies(struct engine *e) { e->nr_proxies = 0; /* Compute how many cells away we need to walk */ - int delta = 1; /*hydro case */ + int delta_cells = 1; /*hydro case */ /* Gravity needs to take the opening angle into account */ if (with_gravity) { const double distance = 2. * r_max * theta_crit_inv; - delta = (int)(distance / cells[0].dmin) + 1; + delta_cells = (int)(distance / cells[0].dmin) + 1; } /* Turn this into upper and lower bounds for loops */ - int delta_m = delta; - int delta_p = delta; + int delta_m = delta_cells; + int delta_p = delta_cells; /* Special case where every cell is in range of every other one */ - if (delta >= cdim[0] / 2) { + if (delta_cells >= cdim[0] / 2) { if (cdim[0] % 2 == 0) { delta_m = cdim[0] / 2; delta_p = cdim[0] / 2 - 1; @@ -3721,46 +3699,35 @@ void engine_makeproxies(struct engine *e) { message( "Looking for proxies up to %d top-level cells away (delta_m=%d " "delta_p=%d)", - delta, delta_m, delta_p); + delta_cells, delta_m, delta_p); /* Loop over each cell in the space. */ - int ind[3]; - for (ind[0] = 0; ind[0] < cdim[0]; ind[0]++) { - for (ind[1] = 0; ind[1] < cdim[1]; ind[1]++) { - for (ind[2] = 0; ind[2] < cdim[2]; ind[2]++) { + for (int i = 0; i < cdim[0]; i++) { + for (int j = 0; j < cdim[1]; j++) { + for (int k = 0; k < cdim[2]; k++) { /* Get the cell ID. */ - const int cid = cell_getid(cdim, ind[0], ind[1], ind[2]); - - /* and it's location */ - const double loc_i[3] = {cells[cid].loc[0], cells[cid].loc[1], - cells[cid].loc[2]}; - - /* Loop over all its neighbours (periodic). */ - for (int i = -delta_m; i <= delta_p; i++) { - int ii = ind[0] + i; - if (ii >= cdim[0]) - ii -= cdim[0]; - else if (ii < 0) - ii += cdim[0]; - for (int j = -delta_m; j <= delta_p; j++) { - int jj = ind[1] + j; - if (jj >= cdim[1]) - jj -= cdim[1]; - else if (jj < 0) - jj += cdim[1]; - for (int k = -delta_m; k <= delta_p; k++) { - int kk = ind[2] + k; - if (kk >= cdim[2]) - kk -= cdim[2]; - else if (kk < 0) - kk += cdim[2]; + const int cid = cell_getid(cdim, i, j, k); + + /* Loop over all its neighbours neighbours in range. */ + for (int ii = -delta_m; ii <= delta_p; ii++) { + int iii = i + ii; + if (!periodic && (iii < 0 || iii >= cdim[0])) continue; + iii = (iii + cdim[0]) % cdim[0]; + for (int jj = -delta_m; jj <= delta_p; jj++) { + int jjj = j + jj; + if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; + jjj = (jjj + cdim[1]) % cdim[1]; + for (int kk = -delta_m; kk <= delta_p; kk++) { + int kkk = k + kk; + if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; + kkk = (kkk + cdim[2]) % cdim[2]; /* Get the cell ID. */ - const int cjd = cell_getid(cdim, ii, jj, kk); + const int cjd = cell_getid(cdim, iii, jjj, kkk); - /* Early abort (same cell) */ - if (cid == cjd) continue; + /* Early abort */ + if (cid >= cjd) continue; /* Early abort (both same node) */ if (cells[cid].nodeID == nodeID && cells[cjd].nodeID == nodeID) @@ -3780,67 +3747,65 @@ void engine_makeproxies(struct engine *e) { /* This is super-ugly but checks for direct neighbours */ /* with periodic BC */ - if (((abs(ind[0] - ii) <= 1 || - abs(ind[0] - ii - cdim[0]) <= 1 || - abs(ind[0] - ii + cdim[0]) <= 1) && - (abs(ind[1] - jj) <= 1 || - abs(ind[1] - jj - cdim[1]) <= 1 || - abs(ind[1] - jj + cdim[1]) <= 1) && - (abs(ind[2] - kk) <= 1 || - abs(ind[2] - kk - cdim[2]) <= 1 || - abs(ind[2] - kk + cdim[2]) <= 1))) + if (((abs(i - iii) <= 1 || abs(i - iii - cdim[0]) <= 1 || + abs(i - iii + cdim[0]) <= 1) && + (abs(j - jjj) <= 1 || abs(j - jjj - cdim[1]) <= 1 || + abs(j - jjj + cdim[1]) <= 1) && + (abs(k - kkk) <= 1 || abs(k - kkk - cdim[2]) <= 1 || + abs(k - kkk + cdim[2]) <= 1))) proxy_type |= (int)proxy_cell_type_hydro; } /* In the gravity case, check distances using the MAC. */ if (with_gravity) { - /* We don't have multipoles yet (or there CoMs) so we will have - to cook up something based on cell locations only. We hence - need an upper limit on the distance that the CoMs in those - cells could have. We then can decide whether we are too close - for an M2L interaction and hence require a proxy as this pair - of cells cannot rely on just an M2L calculation. */ - - const double loc_j[3] = {cells[cjd].loc[0], cells[cjd].loc[1], - cells[cjd].loc[2]}; - - /* Start with the distance between the cell centres. */ - double dx = loc_i[0] - loc_j[0]; - double dy = loc_i[1] - loc_j[1]; - double dz = loc_i[2] - loc_j[2]; - - /* Apply BC */ - if (periodic) { - dx = nearest(dx, dim[0]); - dy = nearest(dy, dim[1]); - dz = nearest(dz, dim[2]); - } - - /* Add to it for the case where the future CoMs are in the - * corners */ - dx += cell_width[0]; - dy += cell_width[1]; - dz += cell_width[2]; - - /* This is a crazy upper-bound but the best we can do */ - const double r2 = dx * dx + dy * dy + dz * dz; - - /* Minimal distance between any pair of particles */ - const double min_radius = sqrt(r2) - 2. * r_max; - - /* Are we beyond the distance where the truncated forces are 0 - * but not too far such that M2L can be used? */ - if (periodic) { - - if ((min_radius < max_distance) && - (!gravity_M2L_accept(r_max, r_max, theta_crit2, r2))) - proxy_type |= (int)proxy_cell_type_gravity; + /* First just add the direct neighbours. Then look for + some further out if the opening angle demands it */ + /* This is super-ugly but checks for direct neighbours */ + /* with periodic BC */ + if (((abs(i - iii) <= 1 || abs(i - iii - cdim[0]) <= 1 || + abs(i - iii + cdim[0]) <= 1) && + (abs(j - jjj) <= 1 || abs(j - jjj - cdim[1]) <= 1 || + abs(j - jjj + cdim[1]) <= 1) && + (abs(k - kkk) <= 1 || abs(k - kkk - cdim[2]) <= 1 || + abs(k - kkk + cdim[2]) <= 1))) { + + proxy_type |= (int)proxy_cell_type_gravity; } else { - if (!gravity_M2L_accept(r_max, r_max, theta_crit2, r2)) - proxy_type |= (int)proxy_cell_type_gravity; + /* We don't have multipoles yet (or there CoMs) so we will + have to cook up something based on cell locations only. We + hence need an upper limit on the distance that the CoMs in + those cells could have. We then can decide whether we are + too close for an M2L interaction and hence require a proxy + as this pair of cells cannot rely on just an M2L + calculation. */ + + /* Minimal distance between any two points in the cells */ + const double min_dist_centres2 = cell_min_dist2_same_size( + &cells[cid], &cells[cjd], periodic, dim); + + /* Let's now assume the CoMs will shift a bit */ + const double min_dist_CoM = + sqrt(min_dist_centres2) - 2. * delta_CoM; + const double min_dist_CoM2 = min_dist_CoM * min_dist_CoM; + + /* Are we beyond the distance where the truncated forces are 0 + * but not too far such that M2L can be used? */ + if (periodic) { + + if ((min_dist_CoM2 < max_mesh_dist2) && + (!gravity_M2L_accept(r_max, r_max, theta_crit2, + min_dist_CoM2))) + proxy_type |= (int)proxy_cell_type_gravity; + + } else { + + if (!gravity_M2L_accept(r_max, r_max, theta_crit2, + min_dist_CoM2)) + proxy_type |= (int)proxy_cell_type_gravity; + } } } @@ -3851,8 +3816,8 @@ void engine_makeproxies(struct engine *e) { if (cells[cid].nodeID == nodeID && cells[cjd].nodeID != nodeID) { /* Do we already have a relationship with this node? */ - int pid = e->proxy_ind[cells[cjd].nodeID]; - if (pid < 0) { + int proxy_id = e->proxy_ind[cells[cjd].nodeID]; + if (proxy_id < 0) { if (e->nr_proxies == engine_maxproxies) error("Maximum number of proxies exceeded."); @@ -3862,24 +3827,31 @@ void engine_makeproxies(struct engine *e) { /* Store the information */ e->proxy_ind[cells[cjd].nodeID] = e->nr_proxies; - pid = e->nr_proxies; + proxy_id = e->nr_proxies; e->nr_proxies += 1; + + /* Check the maximal proxy limit */ + if ((size_t)proxy_id > 8 * sizeof(long long)) + error( + "Created more than %zd proxies. cell.mpi.sendto will " + "overflow.", + 8 * sizeof(long long)); } /* Add the cell to the proxy */ - proxy_addcell_in(&proxies[pid], &cells[cjd], proxy_type); - proxy_addcell_out(&proxies[pid], &cells[cid], proxy_type); + proxy_addcell_in(&proxies[proxy_id], &cells[cjd], proxy_type); + proxy_addcell_out(&proxies[proxy_id], &cells[cid], proxy_type); /* Store info about where to send the cell */ - cells[cid].mpi.sendto |= (1ULL << pid); + cells[cid].mpi.sendto |= (1ULL << proxy_id); } /* Same for the symmetric case? */ if (cells[cjd].nodeID == nodeID && cells[cid].nodeID != nodeID) { /* Do we already have a relationship with this node? */ - int pid = e->proxy_ind[cells[cid].nodeID]; - if (pid < 0) { + int proxy_id = e->proxy_ind[cells[cid].nodeID]; + if (proxy_id < 0) { if (e->nr_proxies == engine_maxproxies) error("Maximum number of proxies exceeded."); @@ -3889,16 +3861,23 @@ void engine_makeproxies(struct engine *e) { /* Store the information */ e->proxy_ind[cells[cid].nodeID] = e->nr_proxies; - pid = e->nr_proxies; + proxy_id = e->nr_proxies; e->nr_proxies += 1; + + /* Check the maximal proxy limit */ + if ((size_t)proxy_id > 8 * sizeof(long long)) + error( + "Created more than %zd proxies. cell.mpi.sendto will " + "overflow.", + 8 * sizeof(long long)); } /* Add the cell to the proxy */ - proxy_addcell_in(&proxies[pid], &cells[cid], proxy_type); - proxy_addcell_out(&proxies[pid], &cells[cjd], proxy_type); + proxy_addcell_in(&proxies[proxy_id], &cells[cid], proxy_type); + proxy_addcell_out(&proxies[proxy_id], &cells[cjd], proxy_type); /* Store info about where to send the cell */ - cells[cjd].mpi.sendto |= (1ULL << pid); + cells[cjd].mpi.sendto |= (1ULL << proxy_id); } } } @@ -3938,8 +3917,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { /* Re-allocate the local parts. */ if (e->verbose) message("Re-allocating parts array from %zu to %zu.", s->size_parts, - (size_t)(s->nr_parts * 1.2)); - s->size_parts = s->nr_parts * 1.2; + (size_t)(s->nr_parts * engine_redistribute_alloc_margin)); + s->size_parts = s->nr_parts * engine_redistribute_alloc_margin; struct part *parts_new = NULL; struct xpart *xparts_new = NULL; if (posix_memalign((void **)&parts_new, part_align, @@ -3963,8 +3942,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { /* Re-allocate the local sparts. */ if (e->verbose) message("Re-allocating sparts array from %zu to %zu.", s->size_sparts, - (size_t)(s->nr_sparts * 1.2)); - s->size_sparts = s->nr_sparts * 1.2; + (size_t)(s->nr_sparts * engine_redistribute_alloc_margin)); + s->size_sparts = s->nr_sparts * engine_redistribute_alloc_margin; struct spart *sparts_new = NULL; if (posix_memalign((void **)&sparts_new, spart_align, sizeof(struct spart) * s->size_sparts) != 0) @@ -3981,8 +3960,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { /* Re-allocate the local gparts. */ if (e->verbose) message("Re-allocating gparts array from %zu to %zu.", s->size_gparts, - (size_t)(s->nr_gparts * 1.2)); - s->size_gparts = s->nr_gparts * 1.2; + (size_t)(s->nr_gparts * engine_redistribute_alloc_margin)); + s->size_gparts = s->nr_gparts * engine_redistribute_alloc_margin; struct gpart *gparts_new = NULL; if (posix_memalign((void **)&gparts_new, gpart_align, sizeof(struct gpart) * s->size_gparts) != 0) @@ -4030,8 +4009,7 @@ void engine_dump_snapshot(struct engine *e) { /* Check that all cells have been drifted to the current time. * That can include cells that have not * previously been active on this rank. */ - space_check_drift_point(e->s, e->ti_current, - e->policy & engine_policy_self_gravity); + space_check_drift_point(e->s, e->ti_current, /* check_mpole=*/0); /* Be verbose about this */ if (e->nodeID == 0) { @@ -4191,26 +4169,28 @@ void engine_unpin(void) { * @param physical_constants The #phys_const used for this run. * @param cosmo The #cosmology used for this run. * @param hydro The #hydro_props used for this run. + * @param entropy_floor The #entropy_floor_properties for this run. * @param gravity The #gravity_props used for this run. * @param stars The #stars_props used for this run. * @param mesh The #pm_mesh used for the long-range periodic forces. * @param potential The properties of the external potential. * @param cooling_func The properties of the cooling function. + * @param starform The #star_formation model of this run. * @param chemistry The chemistry information. - * @param sourceterms The properties of the source terms function. */ void engine_init(struct engine *e, struct space *s, struct swift_params *params, long long Ngas, long long Ngparts, long long Nstars, int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, - struct cosmology *cosmo, const struct hydro_props *hydro, + struct cosmology *cosmo, struct hydro_props *hydro, + const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, struct pm_mesh *mesh, const struct external_potential *potential, - const struct cooling_function_data *cooling_func, - const struct chemistry_global_data *chemistry, - struct sourceterms *sourceterms) { + struct cooling_function_data *cooling_func, + const struct star_formation *starform, + const struct chemistry_global_data *chemistry) { /* Clean-up everything */ bzero(e, sizeof(struct engine)); @@ -4247,9 +4227,12 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, parser_get_opt_param_int(params, "Snapshots:compression", 0); e->snapshot_int_time_label_on = parser_get_opt_param_int(params, "Snapshots:int_time_label_on", 0); + e->snapshot_invoke_stf = + parser_get_opt_param_int(params, "Snapshots:invoke_stf", 0); e->snapshot_units = (struct unit_system *)malloc(sizeof(struct unit_system)); units_init_default(e->snapshot_units, params, "Snapshots", internal_units); e->snapshot_output_count = 0; + e->stf_output_count = 0; e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min"); e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max"); e->dt_max_RMS_displacement = FLT_MAX; @@ -4267,19 +4250,21 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->physical_constants = physical_constants; e->cosmology = cosmo; e->hydro_properties = hydro; + e->entropy_floor = entropy_floor; e->gravity_properties = gravity; e->stars_properties = stars; e->mesh = mesh; e->external_potential = potential; e->cooling_func = cooling_func; + e->star_formation = starform; e->chemistry = chemistry; - e->sourceterms = sourceterms; e->parameter_file = params; - e->cell_loc = NULL; #ifdef WITH_MPI e->cputime_last_step = 0; e->last_repartition = 0; #endif + e->total_nr_cells = 0; + e->total_nr_tasks = 0; #if defined(WITH_LOGGER) e->logger = (struct logger *)malloc(sizeof(struct logger)); @@ -4316,28 +4301,16 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, /* Initialise VELOCIraptor output. */ if (e->policy & engine_policy_structure_finding) { parser_get_param_string(params, "StructureFinding:basename", - e->stfBaseName); + e->stf_base_name); + parser_get_param_string(params, "StructureFinding:config_file_name", + e->stf_config_file_name); + e->time_first_stf_output = parser_get_opt_param_double(params, "StructureFinding:time_first", 0.); e->a_first_stf_output = parser_get_opt_param_double( params, "StructureFinding:scale_factor_first", 0.1); - e->stf_output_freq_format = (enum io_stf_output_format)parser_get_param_int( - params, "StructureFinding:output_time_format"); - - if (e->stf_output_freq_format == io_stf_steps) { - e->delta_step_stf = - parser_get_param_int(params, "StructureFinding:delta_step"); - } else if (e->stf_output_freq_format == io_stf_time) { - e->delta_time_stf = - parser_get_param_double(params, "StructureFinding:delta_time"); - } else { - error( - "Invalid flag (%d) set for output time format of structure finding.", - e->stf_output_freq_format); - } - - /* overwrite input if outputlist */ - if (e->output_list_stf) e->stf_output_freq_format = io_stf_time; + e->delta_time_stf = + parser_get_opt_param_double(params, "StructureFinding:delta_time", -1.); } engine_init_output_lists(e, params); @@ -4584,10 +4557,11 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, fprintf(e->file_timesteps, "# Step Properties: Rebuild=%d, Redistribute=%d, Repartition=%d, " - "Statistics=%d, Snapshot=%d, Restarts=%d\n", + "Statistics=%d, Snapshot=%d, Restarts=%d STF=%d, logger=%d\n", engine_step_prop_rebuild, engine_step_prop_redistribute, engine_step_prop_repartition, engine_step_prop_statistics, - engine_step_prop_snapshot, engine_step_prop_restarts); + engine_step_prop_snapshot, engine_step_prop_restarts, + engine_step_prop_stf, engine_step_prop_logger_index); fprintf(e->file_timesteps, "# %6s %14s %12s %12s %14s %9s %12s %12s %12s %16s [%s] %6s\n", @@ -4602,8 +4576,10 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, engine_print_policy(e); /* Print information about the hydro scheme */ - if (e->policy & engine_policy_hydro) + if (e->policy & engine_policy_hydro) { if (e->nodeID == 0) hydro_props_print(e->hydro_properties); + if (e->nodeID == 0) entropy_floor_print(e->entropy_floor); + } /* Print information about the gravity scheme */ if (e->policy & engine_policy_self_gravity) @@ -4678,17 +4654,18 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, "simulation start a=%e.", e->a_first_statistics, e->cosmology->a_begin); - if ((e->policy & engine_policy_structure_finding) && - (e->stf_output_freq_format == io_stf_time)) { + if (e->policy & engine_policy_structure_finding) { + + if (e->delta_time_stf == -1. && !e->snapshot_invoke_stf) + error("A value for `StructureFinding:delta_time` must be specified"); - if (e->delta_time_stf <= 1.) + if (e->delta_time_stf <= 1. && e->delta_time_stf != -1.) error("Time between STF (%e) must be > 1.", e->delta_time_stf); if (e->a_first_stf_output < e->cosmology->a_begin) error( "Scale-factor of first stf output (%e) must be after the " - "simulation " - "start a=%e.", + "simulation start a=%e.", e->a_first_stf_output, e->cosmology->a_begin); } } else { @@ -4714,10 +4691,12 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, "t=%e.", e->time_first_statistics, e->time_begin); - if ((e->policy & engine_policy_structure_finding) && - (e->stf_output_freq_format == io_stf_time)) { + if (e->policy & engine_policy_structure_finding) { + + if (e->delta_time_stf == -1. && !e->snapshot_invoke_stf) + error("A value for `StructureFinding:delta_time` must be specified"); - if (e->delta_time_stf <= 0.) + if (e->delta_time_stf <= 0. && e->delta_time_stf != -1.) error("Time between STF (%e) must be positive.", e->delta_time_stf); if (e->time_first_stf_output < e->time_begin) @@ -4726,12 +4705,6 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, } } - if (e->policy & engine_policy_structure_finding) { - /* Find the time of the first stf output */ - if (e->stf_output_freq_format == io_stf_time) - engine_compute_next_stf_time(e); - } - /* Get the total mass */ e->total_mass = 0.; for (size_t i = 0; i < e->s->nr_gparts; ++i) @@ -4756,6 +4729,19 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, /* Find the time of the first statistics output */ engine_compute_next_statistics_time(e); + /* Find the time of the first stf output */ + if (e->policy & engine_policy_structure_finding) { + engine_compute_next_stf_time(e); + } + + /* Check that we are invoking VELOCIraptor only if we have it */ + if (e->snapshot_invoke_stf && + !(e->policy & engine_policy_structure_finding)) { + error( + "Invoking VELOCIraptor after snapshots but structure finding wasn't " + "activated at runtime (Use --velociraptor)."); + } + /* Whether restarts are enabled. Yes by default. Can be changed on restart. */ e->restart_dump = parser_get_opt_param_int(params, "Restarts:enable", 1); @@ -4810,16 +4796,21 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, /* Expected average for tasks per cell. If set to zero we use a heuristic * guess based on the numbers of cells and how many tasks per cell we expect. * On restart this number cannot be estimated (no cells yet), so we recover - * from the end of the dumped run. Can be changed on restart. - */ + * from the end of the dumped run. Can be changed on restart. */ e->tasks_per_cell = - parser_get_opt_param_int(params, "Scheduler:tasks_per_cell", 0); - int maxtasks = 0; + parser_get_opt_param_float(params, "Scheduler:tasks_per_cell", 0.0); + e->tasks_per_cell_max = 0.0f; + + float maxtasks = 0; if (restart) maxtasks = e->restart_max_tasks; else maxtasks = engine_estimate_nr_tasks(e); + /* Estimated number of links per tasks */ + e->links_per_tasks = + parser_get_opt_param_int(params, "Scheduler:links_per_tasks", 10); + /* Init the scheduler. */ scheduler_init(&e->sched, e->s, maxtasks, nr_queues, (e->policy & scheduler_flag_steal), e->nodeID, &e->threadpool); @@ -4900,7 +4891,12 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, logger_write_file_header(e->logger, e); #endif - /* Free the affinity stuff */ + /* Initialise the structure finder */ +#ifdef HAVE_VELOCIRAPTOR + if (e->policy & engine_policy_structure_finding) velociraptor_init(e); +#endif + + /* Free the affinity stuff */ #if defined(HAVE_SETAFFINITY) if (with_aff) { free(cpuid); @@ -4944,6 +4940,7 @@ void engine_print_policy(struct engine *e) { * @param e The #engine. */ void engine_compute_next_snapshot_time(struct engine *e) { + /* Do outputlist file case */ if (e->output_list_snapshots) { output_list_read_next_time(e->output_list_snapshots, e, "snapshots", @@ -4964,6 +4961,8 @@ void engine_compute_next_snapshot_time(struct engine *e) { time = e->a_first_snapshot; else time = e->time_first_snapshot; + + int found_snapshot_time = 0; while (time < time_end) { /* Output time on the integer timeline */ @@ -4973,7 +4972,10 @@ void engine_compute_next_snapshot_time(struct engine *e) { e->ti_next_snapshot = (time - e->time_begin) / e->time_base; /* Found it? */ - if (e->ti_next_snapshot > e->ti_current) break; + if (e->ti_next_snapshot > e->ti_current) { + found_snapshot_time = 1; + break; + } if (e->policy & engine_policy_cosmology) time *= e->delta_time_snapshot; @@ -4982,7 +4984,7 @@ void engine_compute_next_snapshot_time(struct engine *e) { } /* Deal with last snapshot */ - if (e->ti_next_snapshot >= max_nr_timesteps) { + if (!found_snapshot_time) { e->ti_next_snapshot = -1; if (e->verbose) message("No further output time."); } else { @@ -5028,6 +5030,8 @@ void engine_compute_next_statistics_time(struct engine *e) { time = e->a_first_statistics; else time = e->time_first_statistics; + + int found_stats_time = 0; while (time < time_end) { /* Output time on the integer timeline */ @@ -5037,7 +5041,10 @@ void engine_compute_next_statistics_time(struct engine *e) { e->ti_next_stats = (time - e->time_begin) / e->time_base; /* Found it? */ - if (e->ti_next_stats > e->ti_current) break; + if (e->ti_next_stats > e->ti_current) { + found_stats_time = 1; + break; + } if (e->policy & engine_policy_cosmology) time *= e->delta_time_statistics; @@ -5046,7 +5053,7 @@ void engine_compute_next_statistics_time(struct engine *e) { } /* Deal with last statistics */ - if (e->ti_next_stats >= max_nr_timesteps) { + if (!found_stats_time) { e->ti_next_stats = -1; if (e->verbose) message("No further output time."); } else { @@ -5093,6 +5100,8 @@ void engine_compute_next_stf_time(struct engine *e) { time = e->a_first_stf_output; else time = e->time_first_stf_output; + + int found_stf_time = 0; while (time < time_end) { /* Output time on the integer timeline */ @@ -5102,7 +5111,10 @@ void engine_compute_next_stf_time(struct engine *e) { e->ti_next_stf = (time - e->time_begin) / e->time_base; /* Found it? */ - if (e->ti_next_stf > e->ti_current) break; + if (e->ti_next_stf > e->ti_current) { + found_stf_time = 1; + break; + } if (e->policy & engine_policy_cosmology) time *= e->delta_time_stf; @@ -5111,7 +5123,7 @@ void engine_compute_next_stf_time(struct engine *e) { } /* Deal with last snapshot */ - if (e->ti_next_stf >= max_nr_timesteps) { + if (!found_stf_time) { e->ti_next_stf = -1; if (e->verbose) message("No further output time."); } else { @@ -5206,9 +5218,11 @@ void engine_recompute_displacement_constraint(struct engine *e) { #ifdef SWIFT_DEBUG_CHECKS /* Check that the minimal mass collection worked */ float min_part_mass_check = FLT_MAX; - for (size_t i = 0; i < e->s->nr_parts; ++i) + for (size_t i = 0; i < e->s->nr_parts; ++i) { + if (e->s->parts[i].time_bin >= num_time_bins) continue; min_part_mass_check = min(min_part_mass_check, hydro_get_mass(&e->s->parts[i])); + } if (min_part_mass_check != min_mass[swift_type_gas]) error("Error collecting minimal mass of gas particles."); #endif @@ -5250,7 +5264,7 @@ void engine_recompute_displacement_constraint(struct engine *e) { /* Mesh forces smoothing scale */ float r_s; - if ((e->policy & engine_policy_self_gravity) && e->s->periodic == 1) + if ((e->policy & engine_policy_self_gravity) && e->s->periodic) r_s = e->mesh->r_s; else r_s = FLT_MAX; @@ -5324,7 +5338,6 @@ void engine_clean(struct engine *e) { output_list_clean(&e->output_list_stf); free(e->links); - free(e->cell_loc); #if defined(WITH_LOGGER) logger_clean(e->logger); free(e->logger); @@ -5362,13 +5375,14 @@ void engine_struct_dump(struct engine *e, FILE *stream) { phys_const_struct_dump(e->physical_constants, stream); hydro_props_struct_dump(e->hydro_properties, stream); + entropy_floor_struct_dump(e->entropy_floor, stream); gravity_props_struct_dump(e->gravity_properties, stream); stars_props_struct_dump(e->stars_properties, stream); pm_mesh_struct_dump(e->mesh, stream); potential_struct_dump(e->external_potential, stream); cooling_struct_dump(e->cooling_func, stream); + starformation_struct_dump(e->star_formation, stream); chemistry_struct_dump(e->chemistry, stream); - sourceterms_struct_dump(e->sourceterms, stream); parser_struct_dump(e->parameter_file, stream); if (e->output_list_snapshots) output_list_struct_dump(e->output_list_snapshots, stream); @@ -5434,6 +5448,12 @@ void engine_struct_restore(struct engine *e, FILE *stream) { hydro_props_struct_restore(hydro_properties, stream); e->hydro_properties = hydro_properties; + struct entropy_floor_properties *entropy_floor = + (struct entropy_floor_properties *)malloc( + sizeof(struct entropy_floor_properties)); + entropy_floor_struct_restore(entropy_floor, stream); + e->entropy_floor = entropy_floor; + struct gravity_props *gravity_properties = (struct gravity_props *)malloc(sizeof(struct gravity_props)); gravity_props_struct_restore(gravity_properties, stream); @@ -5456,20 +5476,20 @@ void engine_struct_restore(struct engine *e, FILE *stream) { struct cooling_function_data *cooling_func = (struct cooling_function_data *)malloc( sizeof(struct cooling_function_data)); - cooling_struct_restore(cooling_func, stream); + cooling_struct_restore(cooling_func, stream, e->cosmology); e->cooling_func = cooling_func; + struct star_formation *star_formation = + (struct star_formation *)malloc(sizeof(struct star_formation)); + starformation_struct_restore(star_formation, stream); + e->star_formation = star_formation; + struct chemistry_global_data *chemistry = (struct chemistry_global_data *)malloc( sizeof(struct chemistry_global_data)); chemistry_struct_restore(chemistry, stream); e->chemistry = chemistry; - struct sourceterms *sourceterms = - (struct sourceterms *)malloc(sizeof(struct sourceterms)); - sourceterms_struct_restore(sourceterms, stream); - e->sourceterms = sourceterms; - struct swift_params *parameter_file = (struct swift_params *)malloc(sizeof(struct swift_params)); parser_struct_restore(parameter_file, stream); diff --git a/src/engine.h b/src/engine.h index 4397fe8ba8639169869086f2e9cbd0a7ba071b47..c3f8694ab42a1428c3765b52ad155a6efe97b1ce 100644 --- a/src/engine.h +++ b/src/engine.h @@ -47,7 +47,6 @@ #include "potential.h" #include "runner.h" #include "scheduler.h" -#include "sourceterms_struct.h" #include "space.h" #include "task.h" #include "units.h" @@ -71,15 +70,16 @@ enum engine_policy { engine_policy_cosmology = (1 << 10), engine_policy_drift_all = (1 << 11), engine_policy_reconstruct_mpoles = (1 << 12), - engine_policy_cooling = (1 << 13), - engine_policy_sourceterms = (1 << 14), + engine_policy_temperature = (1 << 13), + engine_policy_cooling = (1 << 14), engine_policy_stars = (1 << 15), engine_policy_structure_finding = (1 << 16), engine_policy_star_formation = (1 << 17), engine_policy_feedback = (1 << 18), - engine_policy_fof = (1 << 19) + engine_policy_limiter = (1 << 19), + engine_policy_fof = (1 << 20) }; -#define engine_maxpolicy 20 +#define engine_maxpolicy 21 extern const char *engine_policy_names[engine_maxpolicy + 1]; /** @@ -93,18 +93,23 @@ enum engine_step_properties { engine_step_prop_statistics = (1 << 3), engine_step_prop_snapshot = (1 << 4), engine_step_prop_restarts = (1 << 5), - engine_step_prop_logger_index = (1 << 6) + engine_step_prop_stf = (1 << 6), + engine_step_prop_logger_index = (1 << 7) }; /* Some constants */ #define engine_maxproxies 64 #define engine_tasksreweight 1 #define engine_parts_size_grow 1.05 +#define engine_max_proxy_centre_frac 0.2 #define engine_redistribute_alloc_margin 1.2 +#define engine_rebuild_link_alloc_margin 1.2 +#define engine_foreign_alloc_margin 1.05 #define engine_default_energy_file_name "energy" #define engine_default_timesteps_file_name "timesteps" #define engine_max_parts_per_ghost 1000 #define engine_max_sparts_per_ghost 1000 +#define engine_tasks_per_cell_margin 1.2 /** * @brief The rank of the engine as a global variable (for messages). @@ -209,7 +214,13 @@ struct engine { /* Total numbers of particles in the system. */ long long total_nr_parts, total_nr_gparts, total_nr_sparts; - /* The total number of inhibted particles in the system. */ + /* Total numbers of cells (top-level and sub-cells) in the system. */ + long long total_nr_cells; + + /* Total numbers of tasks in the system. */ + long long total_nr_tasks; + + /* The total number of inhibited particles in the system. */ long long nr_inhibited_parts, nr_inhibited_gparts, nr_inhibited_sparts; #ifdef SWIFT_DEBUG_CHECKS @@ -224,9 +235,6 @@ struct engine { /* The internal system of units */ const struct unit_system *internal_units; - /* Top-level cell locations for VELOCIraptor. */ - struct cell_loc *cell_loc; - /* Snapshot information */ double a_first_snapshot; double time_first_snapshot; @@ -241,12 +249,11 @@ struct engine { char snapshot_base_name[PARSER_MAX_LINE_SIZE]; int snapshot_compression; int snapshot_int_time_label_on; + int snapshot_invoke_stf; struct unit_system *snapshot_units; int snapshot_output_count; /* Structure finding information */ - enum io_stf_output_format stf_output_freq_format; - int delta_step_stf; double a_first_stf_output; double time_first_stf_output; double delta_time_stf; @@ -257,7 +264,9 @@ struct engine { /* Integer time of the next stf output */ integertime_t ti_next_stf; - char stfBaseName[PARSER_MAX_LINE_SIZE]; + char stf_config_file_name[PARSER_MAX_LINE_SIZE]; + char stf_base_name[PARSER_MAX_LINE_SIZE]; + int stf_output_count; /* FOF information */ int run_fof; @@ -293,10 +302,8 @@ struct engine { struct proxy *proxies; int nr_proxies, *proxy_ind; -#ifdef SWIFT_DEBUG_TASKS /* Tic/toc at the start/end of a step. */ ticks tic_step, toc_step; -#endif #ifdef WITH_MPI /* CPU time of the last step. */ @@ -331,8 +338,13 @@ struct engine { size_t nr_links, size_links; /* Average number of tasks per cell. Used to estimate the sizes - * of the various task arrays. */ - size_t tasks_per_cell; + * of the various task arrays. Also the maximum from all ranks. */ + float tasks_per_cell; + float tasks_per_cell_max; + + /* Average number of links per tasks. This number is used before + the creation of communication tasks so needs to be large enough. */ + size_t links_per_tasks; /* Are we talkative ? */ int verbose; @@ -344,7 +356,10 @@ struct engine { struct cosmology *cosmology; /* Properties of the hydro scheme */ - const struct hydro_props *hydro_properties; + struct hydro_props *hydro_properties; + + /* Properties of the entropy floor */ + const struct entropy_floor_properties *entropy_floor; /* Properties of the star model */ const struct stars_props *stars_properties; @@ -359,14 +374,14 @@ struct engine { const struct external_potential *external_potential; /* Properties of the cooling scheme */ - const struct cooling_function_data *cooling_func; + struct cooling_function_data *cooling_func; + + /* Properties of the starformation law */ + const struct star_formation *star_formation; /* Properties of the chemistry model */ const struct chemistry_global_data *chemistry; - /* Properties of source terms */ - struct sourceterms *sourceterms; - /* The (parsed) parameter file */ struct swift_params *parameter_file; @@ -404,9 +419,10 @@ void engine_compute_next_stf_time(struct engine *e); void engine_compute_next_statistics_time(struct engine *e); void engine_recompute_displacement_constraint(struct engine *e); void engine_unskip(struct engine *e); -void engine_drift_all(struct engine *e); +void engine_drift_all(struct engine *e, const int drift_mpoles); void engine_drift_top_multipoles(struct engine *e); void engine_reconstruct_multipoles(struct engine *e); +void engine_allocate_foreign_particles(struct engine *e); void engine_print_stats(struct engine *e); void engine_check_for_dumps(struct engine *e); void engine_dump_snapshot(struct engine *e); @@ -416,13 +432,14 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, - struct cosmology *cosmo, const struct hydro_props *hydro, + struct cosmology *cosmo, struct hydro_props *hydro, + const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, struct pm_mesh *mesh, const struct external_potential *potential, - const struct cooling_function_data *cooling_func, - const struct chemistry_global_data *chemistry, - struct sourceterms *sourceterms); + struct cooling_function_data *cooling_func, + const struct star_formation *starform, + const struct chemistry_global_data *chemistry); void engine_config(int restart, struct engine *e, struct swift_params *params, int nr_nodes, int nodeID, int nr_threads, int with_aff, int verbose, const char *restart_file); @@ -448,7 +465,7 @@ int engine_is_done(struct engine *e); void engine_pin(void); void engine_unpin(void); void engine_clean(struct engine *e); -int engine_estimate_nr_tasks(struct engine *e); +int engine_estimate_nr_tasks(const struct engine *e); /* Function prototypes, engine_maketasks.c. */ void engine_maketasks(struct engine *e); diff --git a/src/engine_drift.c b/src/engine_drift.c new file mode 100644 index 0000000000000000000000000000000000000000..7a842068b57813575c33dd670172059abb1e8fc0 --- /dev/null +++ b/src/engine_drift.c @@ -0,0 +1,297 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2015 Peter W. Draper (p.w.draper@durham.ac.uk) + * Angus Lepper (angus.lepper@ed.ac.uk) + * 2016 John A. Regan (john.a.regan@durham.ac.uk) + * Tom Theuns (tom.theuns@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 "engine.h" + +/** + * @brief Mapper function to drift *all* the #part to the current time. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an #engine. + */ +void engine_do_drift_all_part_mapper(void *map_data, int num_elements, + void *extra_data) { + + const struct engine *e = (const struct engine *)extra_data; + const int restarting = e->restarting; + struct space *s = e->s; + struct cell *cells_top; + int *local_cells_top; + + if (restarting) { + + /* When restarting, we loop over all top-level cells */ + cells_top = (struct cell *)map_data; + local_cells_top = NULL; + + } else { + + /* In any other case, we use the list of local cells with tasks */ + cells_top = s->cells_top; + local_cells_top = (int *)map_data; + } + + for (int ind = 0; ind < num_elements; ind++) { + + struct cell *c; + + /* When restarting, the list of local cells does not + yet exist. We use the raw list of top-level cells instead */ + if (restarting) + c = &cells_top[ind]; + else + c = &cells_top[local_cells_top[ind]]; + + if (c->nodeID == e->nodeID) { + + /* Drift all the particles */ + cell_drift_part(c, e, /* force the drift=*/1); + } + } +} + +/** + * @brief Mapper function to drift *all* the #gpart to the current time. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an #engine. + */ +void engine_do_drift_all_gpart_mapper(void *map_data, int num_elements, + void *extra_data) { + + const struct engine *e = (const struct engine *)extra_data; + const int restarting = e->restarting; + struct space *s = e->s; + struct cell *cells_top; + int *local_cells_top; + + if (restarting) { + + /* When restarting, we loop over all top-level cells */ + cells_top = (struct cell *)map_data; + local_cells_top = NULL; + + } else { + + /* In any other case, we use the list of local cells with tasks */ + cells_top = s->cells_top; + local_cells_top = (int *)map_data; + } + + for (int ind = 0; ind < num_elements; ind++) { + + struct cell *c; + + /* When restarting, the list of local cells does not + yet exist. We use the raw list of top-level cells instead */ + if (restarting) + c = &cells_top[ind]; + else + c = &cells_top[local_cells_top[ind]]; + + if (c->nodeID == e->nodeID) { + + /* Drift all the particles */ + cell_drift_gpart(c, e, /* force the drift=*/1); + } + } +} + +/** + * @brief Mapper function to drift *all* the multipoles to the current time. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an #engine. + */ +void engine_do_drift_all_multipole_mapper(void *map_data, int num_elements, + void *extra_data) { + + const struct engine *e = (const struct engine *)extra_data; + const int restarting = e->restarting; + struct space *s = e->s; + struct cell *cells_top; + int *local_cells_with_tasks_top; + + if (restarting) { + + /* When restarting, we loop over all top-level cells */ + cells_top = (struct cell *)map_data; + local_cells_with_tasks_top = NULL; + + } else { + + /* In any other case, we use the list of local cells with tasks */ + cells_top = s->cells_top; + local_cells_with_tasks_top = (int *)map_data; + } + + for (int ind = 0; ind < num_elements; ind++) { + + struct cell *c; + + /* When restarting, the list of local cells does not + yet exist. We use the raw list of top-level cells instead */ + if (restarting) + c = &cells_top[ind]; + else + c = &cells_top[local_cells_with_tasks_top[ind]]; + + cell_drift_all_multipoles(c, e); + } +} + +/** + * @brief Drift *all* particles and multipoles at all levels + * forward to the current time. + * + * @param e The #engine. + * @param drift_mpoles Do we want to drift all the multipoles as well? + */ +void engine_drift_all(struct engine *e, const int drift_mpoles) { + + const ticks tic = getticks(); + +#ifdef SWIFT_DEBUG_CHECKS + if (e->nodeID == 0) { + if (e->policy & engine_policy_cosmology) + message("Drifting all to a=%e", + exp(e->ti_current * e->time_base) * e->cosmology->a_begin); + else + message("Drifting all to t=%e", + e->ti_current * e->time_base + e->time_begin); + } +#endif + + if (!e->restarting) { + + /* Normal case: We have a list of local cells with tasks to play with */ + + if (e->s->nr_parts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_part_mapper, + e->s->local_cells_top, e->s->nr_local_cells, sizeof(int), + /* default chunk */ 0, e); + } + if (e->s->nr_gparts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_gpart_mapper, + e->s->local_cells_top, e->s->nr_local_cells, sizeof(int), + /* default chunk */ 0, e); + } + if (drift_mpoles && (e->policy & engine_policy_self_gravity)) { + threadpool_map(&e->threadpool, engine_do_drift_all_multipole_mapper, + e->s->local_cells_with_tasks_top, + e->s->nr_local_cells_with_tasks, sizeof(int), + /* default chunk */ 0, e); + } + + } else { + + /* When restarting, the list of local cells with tasks does not yet + exist. We use the raw list of top-level cells instead */ + + if (e->s->nr_parts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_part_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), + /* default chunk */ 0, e); + } + if (e->s->nr_gparts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_gpart_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), + /* default chunk */ 0, e); + } + if (e->policy & engine_policy_self_gravity) { + threadpool_map(&e->threadpool, engine_do_drift_all_multipole_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), + /* default chunk */ 0, e); + } + } + + /* Synchronize particle positions */ + space_synchronize_particle_positions(e->s); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that all cells have been drifted to the current time. */ + space_check_drift_point( + e->s, e->ti_current, + drift_mpoles && (e->policy & engine_policy_self_gravity)); + part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts, + e->s->nr_gparts, e->s->nr_sparts, e->verbose); +#endif + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Mapper function to drift *all* top-level multipoles forward in + * time. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an #engine. + */ +void engine_do_drift_top_multipoles_mapper(void *map_data, int num_elements, + void *extra_data) { + + struct engine *e = (struct engine *)extra_data; + struct cell *cells = (struct cell *)map_data; + + for (int ind = 0; ind < num_elements; ind++) { + struct cell *c = &cells[ind]; + if (c != NULL) { + + /* Drift the multipole at this level only */ + if (c->grav.ti_old_multipole != e->ti_current) cell_drift_multipole(c, e); + } + } +} + +/** + * @brief Drift *all* top-level multipoles forward to the current time. + * + * @param e The #engine. + */ +void engine_drift_top_multipoles(struct engine *e) { + + const ticks tic = getticks(); + + threadpool_map(&e->threadpool, engine_do_drift_top_multipoles_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), 0, e); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that all cells have been drifted to the current time. */ + space_check_top_multipoles_drift_point(e->s, e->ti_current); +#endif + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index a833ee923e3351ee041a80d8b70b0bce324111e7..ba957b225c97faeea132b87a4bec9f41aebdf3aa 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -80,8 +80,8 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, /* Create the tasks and their dependencies? */ if (t_grav == NULL) { - /* Create a tag for this cell. */ - if (ci->mpi.tag < 0) cell_tag(ci); + /* Make sure this cell is tagged. */ + cell_ensure_tagged(ci); t_grav = scheduler_addtask(s, task_type_send, task_subtype_gpart, ci->mpi.tag, 0, ci, cj); @@ -139,8 +139,8 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, /* Create the tasks and their dependencies? */ if (t_xv == NULL) { - /* Create a tag for this cell. */ - if (ci->mpi.tag < 0) cell_tag(ci); + /* Make sure this cell is tagged. */ + cell_ensure_tagged(ci); t_xv = scheduler_addtask(s, task_type_send, task_subtype_xv, ci->mpi.tag, 0, ci, cj); @@ -210,9 +210,13 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, * @param ci The sending #cell. * @param cj Dummy cell containing the nodeID of the receiving node. * @param t_ti The send_ti #task, if it has already been created. + * @param t_limiter The send_limiter #task, if already created. + * @param with_limiter Are we running with the time-step limiter? */ void engine_addtasks_send_timestep(struct engine *e, struct cell *ci, - struct cell *cj, struct task *t_ti) { + struct cell *cj, struct task *t_ti, + struct task *t_limiter, + const int with_limiter) { #ifdef WITH_MPI struct link *l = NULL; @@ -238,25 +242,37 @@ void engine_addtasks_send_timestep(struct engine *e, struct cell *ci, /* Create the tasks and their dependencies? */ if (t_ti == NULL) { - /* Create a tag for this cell. */ - if (ci->mpi.tag < 0) cell_tag(ci); + /* Make sure this cell is tagged. */ + cell_ensure_tagged(ci); t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend, ci->mpi.tag, 0, ci, cj); + if (with_limiter) + t_limiter = scheduler_addtask(s, task_type_send, task_subtype_limiter, + ci->mpi.tag, 0, ci, cj); + /* The super-cell's timestep task should unlock the send_ti task. */ scheduler_addunlock(s, ci->super->timestep, t_ti); + if (with_limiter) scheduler_addunlock(s, t_limiter, ci->super->timestep); + if (with_limiter) + scheduler_addunlock(s, t_limiter, ci->super->timestep_limiter); + if (with_limiter) scheduler_addunlock(s, ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(s, ci->super->timestep_limiter, t_ti); } /* Add them to the local cell. */ engine_addlink(e, &ci->mpi.send_ti, t_ti); + if (with_limiter) engine_addlink(e, &ci->mpi.limiter.send, t_limiter); } /* Recurse? */ if (ci->split) for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) - engine_addtasks_send_timestep(e, ci->progeny[k], cj, t_ti); + engine_addtasks_send_timestep(e, ci->progeny[k], cj, t_ti, t_limiter, + with_limiter); #else error("SWIFT was not compiled with MPI support."); @@ -380,9 +396,12 @@ void engine_addtasks_recv_gravity(struct engine *e, struct cell *c, * @param e The #engine. * @param c The foreign #cell. * @param t_ti The recv_ti #task, if already been created. + * @param t_limiter The recv_limiter #task, if already created. + * @param with_limiter Are we running with the time-step limiter? */ void engine_addtasks_recv_timestep(struct engine *e, struct cell *c, - struct task *t_ti) { + struct task *t_ti, struct task *t_limiter, + const int with_limiter) { #ifdef WITH_MPI struct scheduler *s = &e->sched; @@ -397,21 +416,42 @@ void engine_addtasks_recv_timestep(struct engine *e, struct cell *c, t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, c->mpi.tag, 0, c, NULL); + + if (with_limiter) + t_limiter = scheduler_addtask(s, task_type_recv, task_subtype_limiter, + c->mpi.tag, 0, c, NULL); } c->mpi.recv_ti = t_ti; - for (struct link *l = c->grav.grav; l != NULL; l = l->next) + for (struct link *l = c->grav.grav; l != NULL; l = l->next) { scheduler_addunlock(s, l->t, t_ti); + } - for (struct link *l = c->hydro.force; l != NULL; l = l->next) - scheduler_addunlock(s, l->t, t_ti); + if (with_limiter) { + + for (struct link *l = c->hydro.force; l != NULL; l = l->next) { + scheduler_addunlock(s, l->t, t_limiter); + } + + for (struct link *l = c->hydro.limiter; l != NULL; l = l->next) { + scheduler_addunlock(s, t_limiter, l->t); + scheduler_addunlock(s, l->t, t_ti); + } + + } else { + + for (struct link *l = c->hydro.force; l != NULL; l = l->next) { + scheduler_addunlock(s, l->t, t_ti); + } + } /* Recurse? */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) - engine_addtasks_recv_timestep(e, c->progeny[k], t_ti); + engine_addtasks_recv_timestep(e, c->progeny[k], t_ti, t_limiter, + with_limiter); #else error("SWIFT was not compiled with MPI support."); @@ -435,6 +475,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { struct scheduler *s = &e->sched; const int is_with_cooling = (e->policy & engine_policy_cooling); const int is_with_star_formation = (e->policy & engine_policy_star_formation); + const int with_limiter = (e->policy & engine_policy_limiter); /* Are we in a super-cell ? */ if (c->super == c) { @@ -463,7 +504,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { task_subtype_none, 0, 0, c, NULL); /* Subgrid tasks */ - if (is_with_cooling) { + if (is_with_cooling && c->hydro.count_total > 0) { c->hydro.cooling = scheduler_addtask(s, task_type_cooling, task_subtype_none, 0, 0, c, NULL); @@ -475,7 +516,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->end_force, c->kick2); } - if (is_with_star_formation) { + if (is_with_star_formation && c->hydro.count_total > 0) { c->hydro.star_formation = scheduler_addtask( s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL); @@ -489,6 +530,16 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->timestep, c->kick1); + /* Time-step limiting */ + if (with_limiter) { + c->timestep_limiter = scheduler_addtask( + s, task_type_timestep_limiter, task_subtype_none, 0, 0, c, NULL); + + /* Make sure it is not run before kick2 */ + scheduler_addunlock(s, c->timestep, c->timestep_limiter); + scheduler_addunlock(s, c->timestep_limiter, c->kick1); + } + #if defined(WITH_LOGGER) scheduler_addunlock(s, c->kick1, c->logger); #endif @@ -610,8 +661,11 @@ void engine_add_stars_ghosts(struct engine *e, struct cell *c, struct task *stars_ghost_in, struct task *stars_ghost_out) { + /* Abort as there are no star particles here? */ + if (c->stars.count_total == 0) return; + /* If we have reached the leaf OR have to few particles to play with*/ - if (!c->split || c->stars.count < engine_max_sparts_per_ghost) { + if (!c->split || c->stars.count_total < engine_max_sparts_per_ghost) { /* Add the ghost task and its dependencies */ struct scheduler *s = &e->sched; @@ -634,8 +688,11 @@ void engine_add_stars_ghosts(struct engine *e, struct cell *c, void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in, struct task *ghost_out) { + /* Abort as there are no hydro particles here? */ + if (c->hydro.count_total == 0) return; + /* If we have reached the leaf OR have to few particles to play with*/ - if (!c->split || c->hydro.count < engine_max_parts_per_ghost) { + if (!c->split || c->hydro.count_total < engine_max_parts_per_ghost) { /* Add the ghost task and its dependencies */ struct scheduler *s = &e->sched; @@ -643,6 +700,7 @@ void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in, scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, c, NULL); scheduler_addunlock(s, ghost_in, c->hydro.ghost); scheduler_addunlock(s, c->hydro.ghost, ghost_out); + } else { /* Keep recursing */ for (int k = 0; k < 8; k++) @@ -666,7 +724,6 @@ void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in, void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { struct scheduler *s = &e->sched; - const int is_with_sourceterms = (e->policy & engine_policy_sourceterms); /* Are we in a super-cell ? */ if (c->hydro.super == c) { @@ -696,12 +753,6 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { c->hydro.extra_ghost = scheduler_addtask( s, task_type_extra_ghost, task_subtype_none, 0, 0, c, NULL); #endif - - /* add source terms */ - if (is_with_sourceterms) { - c->sourceterms = scheduler_addtask(s, task_type_sourceterms, - task_subtype_none, 0, 0, c, NULL); - } } } else { /* We are above the super-cell so need to go deeper */ @@ -733,6 +784,10 @@ void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) { /* Are we in a super-cell ? */ if (c->super == c) { + /* Add the sort task. */ + c->stars.sorts = scheduler_addtask(s, task_type_stars_sort, + task_subtype_none, 0, 0, c, NULL); + /* Local tasks only... */ if (c->nodeID == e->nodeID) { @@ -766,7 +821,7 @@ void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) { void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, void *extra_data) { - struct engine *e = ((struct engine **)extra_data)[0]; + struct engine *e = (struct engine *)extra_data; struct space *s = e->s; struct scheduler *sched = &e->sched; const int nodeID = e->nodeID; @@ -776,6 +831,7 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, struct cell *cells = s->cells_top; const double theta_crit = e->gravity_properties->theta_crit; const double max_distance = e->mesh->r_cut_max; + const double max_distance2 = max_distance * max_distance; /* Compute how many cells away we need to walk */ const double distance = 2.5 * cells[0].width[0] / theta_crit; @@ -811,91 +867,50 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, /* Skip cells without gravity particles */ if (ci->grav.count == 0) continue; - /* Is that cell local ? */ - if (ci->nodeID != nodeID) continue; - - /* If the cells is local build a self-interaction */ - scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci, NULL); - - /* Recover the multipole information */ - const struct gravity_tensors *const multi_i = ci->grav.multipole; - const double CoM_i[3] = {multi_i->CoM[0], multi_i->CoM[1], multi_i->CoM[2]}; - -#ifdef SWIFT_DEBUG_CHECKS - if (cell_getid(cdim, i, j, k) != cid) - error("Incorrect calculation of indices (i,j,k)=(%d,%d,%d) cid=%d", i, j, - k, cid); - - if (multi_i->r_max != multi_i->r_max_rebuild) - error( - "Multipole size not equal ot it's size after rebuild. But we just " - "rebuilt..."); -#endif + /* If the cell is local build a self-interaction */ + if (ci->nodeID == nodeID) { + scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci, + NULL); + } /* Loop over every other cell within (Manhattan) range delta */ - for (int x = -delta_m; x <= delta_p; x++) { - int ii = i + x; - if (ii >= cdim[0]) - ii -= cdim[0]; - else if (ii < 0) - ii += cdim[0]; - for (int y = -delta_m; y <= delta_p; y++) { - int jj = j + y; - if (jj >= cdim[1]) - jj -= cdim[1]; - else if (jj < 0) - jj += cdim[1]; - for (int z = -delta_m; z <= delta_p; z++) { - int kk = k + z; - if (kk >= cdim[2]) - kk -= cdim[2]; - else if (kk < 0) - kk += cdim[2]; + for (int ii = -delta_m; ii <= delta_p; ii++) { + int iii = i + ii; + if (!periodic && (iii < 0 || iii >= cdim[0])) continue; + iii = (iii + cdim[0]) % cdim[0]; + for (int jj = -delta_m; jj <= delta_p; jj++) { + int jjj = j + jj; + if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; + jjj = (jjj + cdim[1]) % cdim[1]; + for (int kk = -delta_m; kk <= delta_p; kk++) { + int kkk = k + kk; + if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; + kkk = (kkk + cdim[2]) % cdim[2]; /* Get the cell */ - const int cjd = cell_getid(cdim, ii, jj, kk); + const int cjd = cell_getid(cdim, iii, jjj, kkk); struct cell *cj = &cells[cjd]; -#ifdef SWIFT_DEBUG_CHECKS - const int iii = cjd / (cdim[1] * cdim[2]); - const int jjj = (cjd / cdim[2]) % cdim[1]; - const int kkk = cjd % cdim[2]; - - if (ii != iii || jj != jjj || kk != kkk) - error( - "Incorrect calculation of indices (iii,jjj,kkk)=(%d,%d,%d) " - "cjd=%d", - iii, jjj, kkk, cjd); -#endif - - /* Avoid duplicates of local pairs*/ - if (cid <= cjd && cj->nodeID == nodeID) continue; - - /* Skip cells without gravity particles */ - if (cj->grav.count == 0) continue; + /* Avoid duplicates, empty cells and completely foreign pairs */ + if (cid >= cjd || cj->grav.count == 0 || + (ci->nodeID != nodeID && cj->nodeID != nodeID)) + continue; /* Recover the multipole information */ - const struct gravity_tensors *const multi_j = cj->grav.multipole; - - /* Get the distance between the CoMs */ - double dx = CoM_i[0] - multi_j->CoM[0]; - double dy = CoM_i[1] - multi_j->CoM[1]; - double dz = CoM_i[2] - multi_j->CoM[2]; - - /* Apply BC */ - if (periodic) { - dx = nearest(dx, dim[0]); - dy = nearest(dy, dim[1]); - dz = nearest(dz, dim[2]); - } - const double r2 = dx * dx + dy * dy + dz * dz; + const struct gravity_tensors *multi_i = ci->grav.multipole; + const struct gravity_tensors *multi_j = cj->grav.multipole; + + if (multi_i == NULL && ci->nodeID != nodeID) + error("Multipole of ci was not exchanged properly via the proxies"); + if (multi_j == NULL && cj->nodeID != nodeID) + error("Multipole of cj was not exchanged properly via the proxies"); /* Minimal distance between any pair of particles */ - const double min_radius = - sqrt(r2) - (multi_i->r_max + multi_j->r_max); + const double min_radius2 = + cell_min_dist2_same_size(ci, cj, periodic, dim); /* Are we beyond the distance where the truncated forces are 0 ?*/ - if (periodic && min_radius > max_distance) continue; + if (periodic && min_radius2 > max_distance2) continue; /* Are the cells too close for a MM interaction ? */ if (!cell_can_use_pair_mm_rebuild(ci, cj, e, s)) { @@ -903,6 +918,54 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements, /* Ok, we need to add a direct pair calculation */ scheduler_addtask(sched, task_type_pair, task_subtype_grav, 0, 0, ci, cj); + +#ifdef SWIFT_DEBUG_CHECKS +#ifdef WITH_MPI + + /* Let's cross-check that we had a proxy for that cell */ + if (ci->nodeID == nodeID && cj->nodeID != engine_rank) { + + /* Find the proxy for this node */ + const int proxy_id = e->proxy_ind[cj->nodeID]; + if (proxy_id < 0) + error("No proxy exists for that foreign node %d!", cj->nodeID); + + const struct proxy *p = &e->proxies[proxy_id]; + + /* Check whether the cell exists in the proxy */ + int n = 0; + for (; n < p->nr_cells_in; n++) + if (p->cells_in[n] == cj) { + break; + } + if (n == p->nr_cells_in) + error( + "Cell %d not found in the proxy but trying to construct " + "grav task!", + cjd); + } else if (cj->nodeID == nodeID && ci->nodeID != engine_rank) { + + /* Find the proxy for this node */ + const int proxy_id = e->proxy_ind[ci->nodeID]; + if (proxy_id < 0) + error("No proxy exists for that foreign node %d!", ci->nodeID); + + const struct proxy *p = &e->proxies[proxy_id]; + + /* Check whether the cell exists in the proxy */ + int n = 0; + for (; n < p->nr_cells_in; n++) + if (p->cells_in[n] == ci) { + break; + } + if (n == p->nr_cells_in) + error( + "Cell %d not found in the proxy but trying to construct " + "grav task!", + cid); + } +#endif /* WITH_MPI */ +#endif /* SWIFT_DEBUG_CHECKS */ } } } @@ -932,26 +995,6 @@ void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements, } } -/** - * @brief Constructs the top-level tasks for the short-range gravity - * interactions (master function). - * - * - Create the FFT task and the array of gravity ghosts. - * - Call the mapper function to create the other tasks. - * - * @param e The #engine. - */ -void engine_make_self_gravity_tasks(struct engine *e) { - - struct space *s = e->s; - struct task **ghosts = NULL; - - /* Create the multipole self and pair tasks. */ - void *extra_data[2] = {e, ghosts}; - threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL, - s->nr_cells, 1, 0, extra_data); -} - /** * @brief Constructs the top-level tasks for the external gravity. * @@ -1010,6 +1053,14 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, scheduler_addunlock(sched, t, finger->hydro.sorts); } + /* Link stars sort tasks to all the higher sort task. */ + if (t_type == task_type_stars_sort) { + for (struct cell *finger = t->ci->parent; finger != NULL; + finger = finger->parent) + if (finger->stars.sorts != NULL) + scheduler_addunlock(sched, t, finger->stars.sorts); + } + /* Link self tasks to cells. */ else if (t_type == task_type_self) { atomic_inc(&ci->nr_tasks); @@ -1022,6 +1073,8 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->grav.grav, t); } else if (t->subtype == task_subtype_stars_density) { engine_addlink(e, &ci->stars.density, t); + } else if (t->subtype == task_subtype_stars_feedback) { + engine_addlink(e, &ci->stars.feedback, t); } /* Link pair tasks to cells. */ @@ -1038,6 +1091,9 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, } else if (t->subtype == task_subtype_stars_density) { engine_addlink(e, &ci->stars.density, t); engine_addlink(e, &cj->stars.density, t); + } else if (t->subtype == task_subtype_stars_feedback) { + engine_addlink(e, &ci->stars.feedback, t); + engine_addlink(e, &cj->stars.feedback, t); } #ifdef SWIFT_DEBUG_CHECKS else if (t_subtype == task_subtype_external_grav) { @@ -1057,6 +1113,8 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, engine_addlink(e, &ci->grav.grav, t); } else if (t->subtype == task_subtype_stars_density) { engine_addlink(e, &ci->stars.density, t); + } else if (t->subtype == task_subtype_stars_feedback) { + engine_addlink(e, &ci->stars.feedback, t); } /* Link sub-pair tasks to cells. */ @@ -1073,6 +1131,9 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements, } else if (t->subtype == task_subtype_stars_density) { engine_addlink(e, &ci->stars.density, t); engine_addlink(e, &cj->stars.density, t); + } else if (t->subtype == task_subtype_stars_feedback) { + engine_addlink(e, &ci->stars.feedback, t); + engine_addlink(e, &cj->stars.feedback, t); } #ifdef SWIFT_DEBUG_CHECKS else if (t_subtype == task_subtype_external_grav) { @@ -1115,9 +1176,19 @@ void engine_link_gravity_tasks(struct engine *e) { const enum task_types t_type = t->type; const enum task_subtypes t_subtype = t->subtype; - struct cell *ci_parent = (ci->parent != NULL) ? ci->parent : ci; - struct cell *cj_parent = - (cj != NULL && cj->parent != NULL) ? cj->parent : cj; + /* Pointers to the parent cells for tasks going up and down the tree + * In the case where we are at the super-level we don't + * want the parent as no tasks are defined above that level. */ + struct cell *ci_parent, *cj_parent; + if (ci->parent != NULL && ci->grav.super != ci) + ci_parent = ci->parent; + else + ci_parent = ci; + + if (cj != NULL && cj->parent != NULL && cj->grav.super != cj) + cj_parent = cj->parent; + else + cj_parent = cj; /* Node ID (if running with MPI) */ #ifdef WITH_MPI @@ -1256,12 +1327,15 @@ void engine_link_gravity_tasks(struct engine *e) { * @param density The density task to link. * @param gradient The gradient task to link. * @param force The force task to link. + * @param limiter The limiter task to link. * @param c The cell. * @param with_cooling Do we have a cooling task ? + * @param with_limiter Do we have a time-step limiter ? */ static inline void engine_make_hydro_loops_dependencies( struct scheduler *sched, struct task *density, struct task *gradient, - struct task *force, struct cell *c, int with_cooling) { + struct task *force, struct task *limiter, struct cell *c, int with_cooling, + int with_limiter) { /* density loop --> ghost --> gradient loop --> extra_ghost */ /* extra_ghost --> force loop */ @@ -1279,14 +1353,15 @@ static inline void engine_make_hydro_loops_dependencies( * @param sched The #scheduler. * @param density The density task to link. * @param force The force task to link. + * @param limiter The limiter task to link. * @param c The cell. - * @param with_cooling Are we running with cooling switched on ? + * @param with_cooling Are we running with cooling switched on? + * @param with_limiter Are we running with limiter switched on? */ -static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched, - struct task *density, - struct task *force, - struct cell *c, - int with_cooling) { +static inline void engine_make_hydro_loops_dependencies( + struct scheduler *sched, struct task *density, struct task *force, + struct task *limiter, struct cell *c, int with_cooling, int with_limiter) { + /* density loop --> ghost --> force loop */ scheduler_addunlock(sched, density, c->hydro.super->hydro.ghost_in); scheduler_addunlock(sched, c->hydro.super->hydro.ghost_out, force); @@ -1297,14 +1372,17 @@ static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched, * @brief Creates the dependency network for the stars tasks of a given cell. * * @param sched The #scheduler. - * @param density The density task to link. + * @param density The star density task to link. + * @param feedback The star feedback task to link. * @param c The cell. */ static inline void engine_make_stars_loops_dependencies(struct scheduler *sched, struct task *density, + struct task *feedback, struct cell *c) { - /* density loop --> ghost */ + /* density loop --> ghost --> feedback loop*/ scheduler_addunlock(sched, density, c->super->stars.ghost_in); + scheduler_addunlock(sched, c->super->stars.ghost_out, feedback); } /** @@ -1324,6 +1402,12 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, struct scheduler *sched = &e->sched; const int nodeID = e->nodeID; const int with_cooling = (e->policy & engine_policy_cooling); + const int with_limiter = (e->policy & engine_policy_limiter); +#ifdef EXTRA_HYDRO_LOOP + struct task *t_gradient = NULL; +#endif + struct task *t_force = NULL; + struct task *t_limiter = NULL; for (int ind = 0; ind < num_elements; ind++) { struct task *t = &((struct task *)map_data)[ind]; @@ -1341,31 +1425,53 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #ifdef EXTRA_HYDRO_LOOP /* Start by constructing the task for the second and third hydro loop. */ - struct task *t2 = scheduler_addtask( - sched, task_type_self, task_subtype_gradient, 0, 0, t->ci, NULL); - struct task *t3 = scheduler_addtask( - sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL); + t_gradient = scheduler_addtask(sched, task_type_self, + task_subtype_gradient, 0, 0, t->ci, NULL); + t_force = scheduler_addtask(sched, task_type_self, task_subtype_force, 0, + 0, t->ci, NULL); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = scheduler_addtask(sched, task_type_self, + task_subtype_limiter, 0, 0, t->ci, NULL); /* Add the link between the new loops and the cell */ - engine_addlink(e, &t->ci->hydro.gradient, t2); - engine_addlink(e, &t->ci->hydro.force, t3); + engine_addlink(e, &t->ci->hydro.gradient, t_gradient); + engine_addlink(e, &t->ci->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro */ - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci, - with_cooling); - scheduler_addunlock(sched, t3, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->ci, with_cooling, + with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); #else /* Start by constructing the task for the second hydro loop */ - struct task *t2 = scheduler_addtask( - sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL); + t_force = scheduler_addtask(sched, task_type_self, task_subtype_force, 0, + 0, t->ci, NULL); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = scheduler_addtask(sched, task_type_self, + task_subtype_limiter, 0, 0, t->ci, NULL); /* Add the link between the new loop and the cell */ - engine_addlink(e, &t->ci->hydro.force, t2); + engine_addlink(e, &t->ci->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro */ - engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling); - scheduler_addunlock(sched, t2, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, t->ci, + with_cooling, with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); #endif } @@ -1384,54 +1490,103 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #ifdef EXTRA_HYDRO_LOOP /* Start by constructing the task for the second and third hydro loop */ - struct task *t2 = scheduler_addtask( - sched, task_type_pair, task_subtype_gradient, 0, 0, t->ci, t->cj); - struct task *t3 = scheduler_addtask( - sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj); + t_gradient = scheduler_addtask(sched, task_type_pair, + task_subtype_gradient, 0, 0, t->ci, t->cj); + t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force, 0, + 0, t->ci, t->cj); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = scheduler_addtask(sched, task_type_pair, + task_subtype_limiter, 0, 0, t->ci, t->cj); /* Add the link between the new loop and both cells */ - engine_addlink(e, &t->ci->hydro.gradient, t2); - engine_addlink(e, &t->cj->hydro.gradient, t2); - engine_addlink(e, &t->ci->hydro.force, t3); - engine_addlink(e, &t->cj->hydro.force, t3); + engine_addlink(e, &t->ci->hydro.gradient, t_gradient); + engine_addlink(e, &t->cj->hydro.gradient, t_gradient); + engine_addlink(e, &t->ci->hydro.force, t_force); + engine_addlink(e, &t->cj->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); + if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci, - with_cooling); - scheduler_addunlock(sched, t3, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->ci, with_cooling, + with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } if (t->cj->nodeID == nodeID) { - if (t->ci->hydro.super != t->cj->hydro.super) - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj, - with_cooling); - if (t->ci->super != t->cj->super) - scheduler_addunlock(sched, t3, t->cj->super->end_force); + if (t->ci->hydro.super != t->cj->hydro.super) { + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->cj, with_cooling, + with_limiter); + } + + if (t->ci->super != t->cj->super) { + scheduler_addunlock(sched, t_force, t->cj->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->cj->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->cj->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, + t->cj->super->timestep_limiter); + } } #else /* Start by constructing the task for the second hydro loop */ - struct task *t2 = scheduler_addtask( - sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj); + t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force, 0, + 0, t->ci, t->cj); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = scheduler_addtask(sched, task_type_pair, + task_subtype_limiter, 0, 0, t->ci, t->cj); /* Add the link between the new loop and both cells */ - engine_addlink(e, &t->ci->hydro.force, t2); - engine_addlink(e, &t->cj->hydro.force, t2); + engine_addlink(e, &t->ci->hydro.force, t_force); + engine_addlink(e, &t->cj->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); + if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling); - scheduler_addunlock(sched, t2, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, + t->ci, with_cooling, with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } if (t->cj->nodeID == nodeID) { - if (t->ci->hydro.super != t->cj->hydro.super) - engine_make_hydro_loops_dependencies(sched, t, t2, t->cj, - with_cooling); - if (t->ci->super != t->cj->super) - scheduler_addunlock(sched, t2, t->cj->super->end_force); + if (t->ci->hydro.super != t->cj->hydro.super) { + engine_make_hydro_loops_dependencies( + sched, t, t_force, t_limiter, t->cj, with_cooling, with_limiter); + } + + if (t->ci->super != t->cj->super) { + scheduler_addunlock(sched, t_force, t->cj->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->cj->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->cj->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, + t->cj->super->timestep_limiter); + } } #endif @@ -1449,39 +1604,65 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #ifdef EXTRA_HYDRO_LOOP /* Start by constructing the task for the second and third hydro loop */ - struct task *t2 = + t_gradient = scheduler_addtask(sched, task_type_sub_self, task_subtype_gradient, - t->flags, 0, t->ci, t->cj); - struct task *t3 = - scheduler_addtask(sched, task_type_sub_self, task_subtype_force, - t->flags, 0, t->ci, t->cj); + t->flags, 0, t->ci, NULL); + t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force, + t->flags, 0, t->ci, NULL); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = + scheduler_addtask(sched, task_type_sub_self, task_subtype_limiter, + t->flags, 0, t->ci, NULL); /* Add the link between the new loop and the cell */ - engine_addlink(e, &t->ci->hydro.gradient, t2); - engine_addlink(e, &t->ci->hydro.force, t3); + engine_addlink(e, &t->ci->hydro.gradient, t_gradient); + engine_addlink(e, &t->ci->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci, - with_cooling); - scheduler_addunlock(sched, t3, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->ci, with_cooling, + with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } #else /* Start by constructing the task for the second hydro loop */ - struct task *t2 = - scheduler_addtask(sched, task_type_sub_self, task_subtype_force, - t->flags, 0, t->ci, t->cj); + t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force, + t->flags, 0, t->ci, NULL); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = + scheduler_addtask(sched, task_type_sub_self, task_subtype_limiter, + t->flags, 0, t->ci, NULL); /* Add the link between the new loop and the cell */ - engine_addlink(e, &t->ci->hydro.force, t2); + engine_addlink(e, &t->ci->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling); - scheduler_addunlock(sched, t2, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, + t->ci, with_cooling, with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } #endif } @@ -1503,56 +1684,106 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, #ifdef EXTRA_HYDRO_LOOP /* Start by constructing the task for the second and third hydro loop */ - struct task *t2 = + t_gradient = scheduler_addtask(sched, task_type_sub_pair, task_subtype_gradient, t->flags, 0, t->ci, t->cj); - struct task *t3 = - scheduler_addtask(sched, task_type_sub_pair, task_subtype_force, - t->flags, 0, t->ci, t->cj); + t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force, + t->flags, 0, t->ci, t->cj); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = + scheduler_addtask(sched, task_type_sub_pair, task_subtype_limiter, + t->flags, 0, t->ci, t->cj); /* Add the link between the new loop and both cells */ - engine_addlink(e, &t->ci->hydro.gradient, t2); - engine_addlink(e, &t->cj->hydro.gradient, t2); - engine_addlink(e, &t->ci->hydro.force, t3); - engine_addlink(e, &t->cj->hydro.force, t3); + engine_addlink(e, &t->ci->hydro.gradient, t_gradient); + engine_addlink(e, &t->cj->hydro.gradient, t_gradient); + engine_addlink(e, &t->ci->hydro.force, t_force); + engine_addlink(e, &t->cj->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); + if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci, - with_cooling); - scheduler_addunlock(sched, t3, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->ci, with_cooling, + with_limiter); + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } if (t->cj->nodeID == nodeID) { - if (t->ci->hydro.super != t->cj->hydro.super) - engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj, - with_cooling); - if (t->ci->super != t->cj->super) - scheduler_addunlock(sched, t3, t->cj->super->end_force); + if (t->ci->hydro.super != t->cj->hydro.super) { + engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force, + t_limiter, t->cj, with_cooling, + with_limiter); + } + + if (t->ci->super != t->cj->super) { + scheduler_addunlock(sched, t_force, t->cj->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->cj->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->cj->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, + t->cj->super->timestep_limiter); + } } #else /* Start by constructing the task for the second hydro loop */ - struct task *t2 = - scheduler_addtask(sched, task_type_sub_pair, task_subtype_force, - t->flags, 0, t->ci, t->cj); + t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force, + t->flags, 0, t->ci, t->cj); + + /* and the task for the time-step limiter */ + if (with_limiter) + t_limiter = + scheduler_addtask(sched, task_type_sub_pair, task_subtype_limiter, + t->flags, 0, t->ci, t->cj); /* Add the link between the new loop and both cells */ - engine_addlink(e, &t->ci->hydro.force, t2); - engine_addlink(e, &t->cj->hydro.force, t2); + engine_addlink(e, &t->ci->hydro.force, t_force); + engine_addlink(e, &t->cj->hydro.force, t_force); + if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter); + if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super_hydro-cells */ if (t->ci->nodeID == nodeID) { - engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling); - scheduler_addunlock(sched, t2, t->ci->super->end_force); + engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, + t->ci, with_cooling, with_limiter); + + scheduler_addunlock(sched, t_force, t->ci->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->ci->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter); } if (t->cj->nodeID == nodeID) { - if (t->ci->hydro.super != t->cj->hydro.super) - engine_make_hydro_loops_dependencies(sched, t, t2, t->cj, - with_cooling); - if (t->ci->super != t->cj->super) - scheduler_addunlock(sched, t2, t->cj->super->end_force); + if (t->ci->hydro.super != t->cj->hydro.super) { + engine_make_hydro_loops_dependencies( + sched, t, t_force, t_limiter, t->cj, with_cooling, with_limiter); + } + + if (t->ci->super != t->cj->super) { + scheduler_addunlock(sched, t_force, t->cj->super->end_force); + if (with_limiter) + scheduler_addunlock(sched, t->cj->super->kick2, t_limiter); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, t->cj->super->timestep); + if (with_limiter) + scheduler_addunlock(sched, t_limiter, + t->cj->super->timestep_limiter); + } } #endif } @@ -1560,14 +1791,17 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements, } /** - * @brief Creates all the task dependencies for the stars + * @brief Duplicates the first stars loop and construct all the + * dependencies for the stars part * - * @param map_data The tasks - * @param num_elements number of tasks - * @param extra_data The #engine + * This is done by looping over all the previously constructed tasks + * and adding another task involving the same cells but this time + * corresponding to the second stars loop over neighbours. + * With all the relevant tasks for a given cell available, we construct + * all the dependencies for that cell. */ -void engine_link_stars_tasks_mapper(void *map_data, int num_elements, - void *extra_data) { +void engine_make_extra_starsloop_tasks_mapper(void *map_data, int num_elements, + void *extra_data) { struct engine *e = (struct engine *)extra_data; struct scheduler *sched = &e->sched; @@ -1576,85 +1810,146 @@ void engine_link_stars_tasks_mapper(void *map_data, int num_elements, for (int ind = 0; ind < num_elements; ind++) { struct task *t = &((struct task *)map_data)[ind]; - /* Self-interaction? */ - if (t->type == task_type_self && t->subtype == task_subtype_stars_density) { + /* Sort tasks depend on the drift and gravity drift of the cell. */ + if (t->type == task_type_stars_sort && t->ci->nodeID == engine_rank) { + scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t); + scheduler_addunlock(sched, t->ci->super->grav.drift, t); + } - /* Make the self-density tasks depend on the drifts. */ - scheduler_addunlock(sched, t->ci->super->hydro.drift, t); + /* Self-interaction? */ + else if (t->type == task_type_self && + t->subtype == task_subtype_stars_density) { + /* Make the self-density tasks depend on the drift and gravity drift. */ + scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t); scheduler_addunlock(sched, t->ci->super->grav.drift, t); + /* Start by constructing the task for the second stars loop */ + struct task *t2 = + scheduler_addtask(sched, task_type_self, task_subtype_stars_feedback, + 0, 0, t->ci, NULL); + + /* Add the link between the new loop and the cell */ + engine_addlink(e, &t->ci->stars.feedback, t2); + /* Now, build all the dependencies for the stars */ - engine_make_stars_loops_dependencies(sched, t, t->ci); - if (t->ci == t->ci->super) - scheduler_addunlock(sched, t->ci->super->stars.ghost_out, - t->ci->super->end_force); + engine_make_stars_loops_dependencies(sched, t, t2, t->ci); + + /* end_force depends on feedback tasks */ + scheduler_addunlock(sched, t2, t->ci->super->end_force); } /* Otherwise, pair interaction? */ else if (t->type == task_type_pair && t->subtype == task_subtype_stars_density) { - /* Make all density tasks depend on the drift and the sorts. */ + /* Make all stars density tasks depend on the hydro drift and sorts, + * gravity drift and star sorts. */ if (t->ci->nodeID == engine_rank) scheduler_addunlock(sched, t->ci->super->hydro.drift, t); scheduler_addunlock(sched, t->ci->super->hydro.sorts, t); + if (t->cj->nodeID == engine_rank) + scheduler_addunlock(sched, t->cj->super->grav.drift, t); + scheduler_addunlock(sched, t->ci->super->stars.sorts, t); + if (t->ci->super != t->cj->super) { if (t->cj->nodeID == engine_rank) scheduler_addunlock(sched, t->cj->super->hydro.drift, t); scheduler_addunlock(sched, t->cj->super->hydro.sorts, t); + if (t->ci->nodeID == engine_rank) + scheduler_addunlock(sched, t->ci->super->grav.drift, t); + scheduler_addunlock(sched, t->cj->super->stars.sorts, t); } + /* Start by constructing the task for the second stars loop */ + struct task *t2 = + scheduler_addtask(sched, task_type_pair, task_subtype_stars_feedback, + 0, 0, t->ci, t->cj); + + /* Add the link between the new loop and both cells */ + engine_addlink(e, &t->ci->stars.feedback, t2); + engine_addlink(e, &t->cj->stars.feedback, t2); + /* Now, build all the dependencies for the stars for the cells */ - /* that are local and are not descendant of the same super-cells */ if (t->ci->nodeID == nodeID) { - engine_make_stars_loops_dependencies(sched, t, t->ci); + engine_make_stars_loops_dependencies(sched, t, t2, t->ci); + scheduler_addunlock(sched, t2, t->ci->super->end_force); } if (t->cj->nodeID == nodeID) { if (t->ci->super != t->cj->super) - engine_make_stars_loops_dependencies(sched, t, t->cj); + engine_make_stars_loops_dependencies(sched, t, t2, t->cj); + if (t->ci->super != t->cj->super) + scheduler_addunlock(sched, t2, t->cj->super->end_force); } - } /* Otherwise, sub-self interaction? */ else if (t->type == task_type_sub_self && t->subtype == task_subtype_stars_density) { - /* Make all density tasks depend on the drift and sorts. */ + /* Make all stars density tasks depend on the hydro drift and sorts, + * gravity drift and star sorts. */ scheduler_addunlock(sched, t->ci->super->hydro.drift, t); scheduler_addunlock(sched, t->ci->super->hydro.sorts, t); + scheduler_addunlock(sched, t->ci->super->grav.drift, t); + scheduler_addunlock(sched, t->ci->super->stars.sorts, t); + + /* Start by constructing the task for the second stars loop */ + struct task *t2 = scheduler_addtask(sched, task_type_sub_self, + task_subtype_stars_feedback, t->flags, + 0, t->ci, t->cj); + + /* Add the link between the new loop and the cell */ + engine_addlink(e, &t->ci->stars.feedback, t2); /* Now, build all the dependencies for the stars for the cells */ - /* that are local and are not descendant of the same super-cells */ if (t->ci->nodeID == nodeID) { - engine_make_stars_loops_dependencies(sched, t, t->ci); - } else - error("oo"); + engine_make_stars_loops_dependencies(sched, t, t2, t->ci); + scheduler_addunlock(sched, t2, t->ci->super->end_force); + } } /* Otherwise, sub-pair interaction? */ else if (t->type == task_type_sub_pair && t->subtype == task_subtype_stars_density) { - /* Make all density tasks depend on the drift. */ - if (t->ci->nodeID == engine_rank) - scheduler_addunlock(sched, t->ci->super->hydro.drift, t); - scheduler_addunlock(sched, t->ci->super->hydro.sorts, t); + /* Make all stars density tasks depend on the hydro drift and sorts, + * gravity drift and star sorts. */ + if (t->cj->nodeID == engine_rank) + scheduler_addunlock(sched, t->cj->super->hydro.drift, t); + scheduler_addunlock(sched, t->cj->super->hydro.sorts, t); + if (t->cj->nodeID == engine_rank) + scheduler_addunlock(sched, t->cj->super->grav.drift, t); + scheduler_addunlock(sched, t->ci->super->stars.sorts, t); + if (t->ci->super != t->cj->super) { - if (t->cj->nodeID == engine_rank) - scheduler_addunlock(sched, t->cj->super->hydro.drift, t); - scheduler_addunlock(sched, t->cj->super->hydro.sorts, t); + if (t->ci->nodeID == engine_rank) + scheduler_addunlock(sched, t->ci->super->hydro.drift, t); + scheduler_addunlock(sched, t->ci->super->hydro.sorts, t); + if (t->ci->nodeID == engine_rank) + scheduler_addunlock(sched, t->ci->super->grav.drift, t); + scheduler_addunlock(sched, t->cj->super->stars.sorts, t); } + /* Start by constructing the task for the second stars loop */ + struct task *t2 = scheduler_addtask(sched, task_type_sub_pair, + task_subtype_stars_feedback, t->flags, + 0, t->ci, t->cj); + + /* Add the link between the new loop and both cells */ + engine_addlink(e, &t->ci->stars.feedback, t2); + engine_addlink(e, &t->cj->stars.feedback, t2); + /* Now, build all the dependencies for the stars for the cells */ - /* that are local and are not descendant of the same super-cells */ if (t->ci->nodeID == nodeID) { - engine_make_stars_loops_dependencies(sched, t, t->ci); + engine_make_stars_loops_dependencies(sched, t, t2, t->ci); + scheduler_addunlock(sched, t2, t->ci->super->end_force); } if (t->cj->nodeID == nodeID) { if (t->ci->super != t->cj->super) - engine_make_stars_loops_dependencies(sched, t, t->cj); + engine_make_stars_loops_dependencies(sched, t, t2, t->cj); + if (t->ci->super != t->cj->super) + scheduler_addunlock(sched, t2, t->cj->super->end_force); } } } @@ -1678,6 +1973,7 @@ void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements, /* Extract the engine pointer. */ struct engine *e = (struct engine *)extra_data; + const int periodic = e->s->periodic; struct space *s = e->s; struct scheduler *sched = &e->sched; @@ -1697,26 +1993,27 @@ void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements, /* Get the cell */ struct cell *ci = &cells[cid]; - /* Skip cells without star particles */ - if (ci->stars.count == 0) continue; + /* Skip cells without particles */ + if (ci->stars.count == 0 && ci->hydro.count == 0) continue; /* If the cells is local build a self-interaction */ - if (ci->nodeID == nodeID) + if (ci->nodeID == nodeID) { scheduler_addtask(sched, task_type_self, task_subtype_stars_density, 0, 0, ci, NULL); + } /* Now loop over all the neighbours of this cell */ for (int ii = -1; ii < 2; ii++) { int iii = i + ii; - if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue; + if (!periodic && (iii < 0 || iii >= cdim[0])) continue; iii = (iii + cdim[0]) % cdim[0]; for (int jj = -1; jj < 2; jj++) { int jjj = j + jj; - if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue; + if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; jjj = (jjj + cdim[1]) % cdim[1]; for (int kk = -1; kk < 2; kk++) { int kkk = k + kk; - if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue; + if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; kkk = (kkk + cdim[2]) % cdim[2]; /* Get the neighbouring cell */ @@ -1724,7 +2021,7 @@ void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements, struct cell *cj = &cells[cjd]; /* Is that neighbour local and does it have particles ? */ - if (cid >= cjd || cj->hydro.count == 0 || + if (cid >= cjd || (cj->stars.count == 0 && cj->hydro.count == 0) || (ci->nodeID != nodeID && cj->nodeID != nodeID)) continue; @@ -1756,6 +2053,7 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, /* Extract the engine pointer. */ struct engine *e = (struct engine *)extra_data; + const int periodic = e->s->periodic; struct space *s = e->s; struct scheduler *sched = &e->sched; @@ -1768,6 +2066,8 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, /* Get the cell index. */ const int cid = (size_t)(map_data) + ind; + + /* Integer indices of the cell in the top-level grid */ const int i = cid / (cdim[1] * cdim[2]); const int j = (cid / cdim[2]) % cdim[1]; const int k = cid % cdim[2]; @@ -1778,23 +2078,24 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, /* Skip cells without hydro particles */ if (ci->hydro.count == 0) continue; - /* If the cells is local build a self-interaction */ - if (ci->nodeID == nodeID) + /* If the cell is local build a self-interaction */ + if (ci->nodeID == nodeID) { scheduler_addtask(sched, task_type_self, task_subtype_density, 0, 0, ci, NULL); + } /* Now loop over all the neighbours of this cell */ for (int ii = -1; ii < 2; ii++) { int iii = i + ii; - if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue; + if (!periodic && (iii < 0 || iii >= cdim[0])) continue; iii = (iii + cdim[0]) % cdim[0]; for (int jj = -1; jj < 2; jj++) { int jjj = j + jj; - if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue; + if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue; jjj = (jjj + cdim[1]) % cdim[1]; for (int kk = -1; kk < 2; kk++) { int kkk = k + kk; - if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue; + if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue; kkk = (kkk + cdim[2]) % cdim[2]; /* Get the neighbouring cell */ @@ -1810,12 +2111,115 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))]; scheduler_addtask(sched, task_type_pair, task_subtype_density, sid, 0, ci, cj); + +#ifdef SWIFT_DEBUG_CHECKS +#ifdef WITH_MPI + + /* Let's cross-check that we had a proxy for that cell */ + if (ci->nodeID == nodeID && cj->nodeID != engine_rank) { + + /* Find the proxy for this node */ + const int proxy_id = e->proxy_ind[cj->nodeID]; + if (proxy_id < 0) + error("No proxy exists for that foreign node %d!", cj->nodeID); + + const struct proxy *p = &e->proxies[proxy_id]; + + /* Check whether the cell exists in the proxy */ + int n = 0; + for (n = 0; n < p->nr_cells_in; n++) + if (p->cells_in[n] == cj) break; + if (n == p->nr_cells_in) + error( + "Cell %d not found in the proxy but trying to construct " + "hydro task!", + cjd); + } else if (cj->nodeID == nodeID && ci->nodeID != engine_rank) { + + /* Find the proxy for this node */ + const int proxy_id = e->proxy_ind[ci->nodeID]; + if (proxy_id < 0) + error("No proxy exists for that foreign node %d!", ci->nodeID); + + const struct proxy *p = &e->proxies[proxy_id]; + + /* Check whether the cell exists in the proxy */ + int n = 0; + for (n = 0; n < p->nr_cells_in; n++) + if (p->cells_in[n] == ci) break; + if (n == p->nr_cells_in) + error( + "Cell %d not found in the proxy but trying to construct " + "hydro task!", + cid); + } +#endif /* WITH_MPI */ +#endif /* SWIFT_DEBUG_CHECKS */ } } } } } +struct cell_type_pair { + struct cell *ci, *cj; + int type; +}; + +void engine_addtasks_send_mapper(void *map_data, int num_elements, + void *extra_data) { + struct engine *e = (struct engine *)extra_data; + const int with_limiter = (e->policy & engine_policy_limiter); + struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; + + for (int k = 0; k < num_elements; k++) { + struct cell *ci = cell_type_pairs[k].ci; + struct cell *cj = cell_type_pairs[k].cj; + const int type = cell_type_pairs[k].type; + + /* Add the send task for the particle timesteps. */ + engine_addtasks_send_timestep(e, ci, cj, NULL, NULL, with_limiter); + + /* Add the send tasks for the cells in the proxy that have a hydro + * connection. */ + if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) + engine_addtasks_send_hydro(e, ci, cj, /*t_xv=*/NULL, + /*t_rho=*/NULL, /*t_gradient=*/NULL); + + /* Add the send tasks for the cells in the proxy that have a gravity + * connection. */ + if ((e->policy & engine_policy_self_gravity) && + (type & proxy_cell_type_gravity)) + engine_addtasks_send_gravity(e, ci, cj, NULL); + } +} + +void engine_addtasks_recv_mapper(void *map_data, int num_elements, + void *extra_data) { + struct engine *e = (struct engine *)extra_data; + const int with_limiter = (e->policy & engine_policy_limiter); + struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; + + for (int k = 0; k < num_elements; k++) { + struct cell *ci = cell_type_pairs[k].ci; + const int type = cell_type_pairs[k].type; + + /* Add the recv task for the particle timesteps. */ + engine_addtasks_recv_timestep(e, ci, NULL, NULL, with_limiter); + + /* Add the recv tasks for the cells in the proxy that have a hydro + * connection. */ + if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) + engine_addtasks_recv_hydro(e, ci, NULL, NULL, NULL); + + /* Add the recv tasks for the cells in the proxy that have a gravity + * connection. */ + if ((e->policy & engine_policy_self_gravity) && + (type & proxy_cell_type_gravity)) + engine_addtasks_recv_gravity(e, ci, NULL); + } +} + /** * @brief Constructs the top-level self + pair tasks for the FOF loop over * neighbours. @@ -1934,8 +2338,17 @@ void engine_maketasks(struct engine *e) { s->nr_cells, 1, 0, e); } + if (e->verbose) + message("Making stellar feedback tasks took %.3f %s.", + clocks_from_ticks(getticks() - tic2), clocks_getunit()); + + tic2 = getticks(); + /* Add the self gravity tasks. */ - if (e->policy & engine_policy_self_gravity) engine_make_self_gravity_tasks(e); + if (e->policy & engine_policy_self_gravity) { + threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL, + s->nr_cells, 1, 0, e); + } if (e->verbose) message("Making gravity tasks took %.3f %s.", @@ -1948,39 +2361,6 @@ void engine_maketasks(struct engine *e) { if (e->sched.nr_tasks == 0 && (s->nr_gparts > 0 || s->nr_parts > 0)) error("We have particles but no hydro or gravity tasks were created."); - /* Free the old list of cell-task links. */ - if (e->links != NULL) free(e->links); - e->size_links = 0; - -/* The maximum number of links is the - * number of cells (s->tot_cells) times the number of neighbours (26) times - * the number of interaction types, so 26 * 2 (density, force) pairs - * and 2 (density, force) self. - */ -#ifdef EXTRA_HYDRO_LOOP - const size_t hydro_tasks_per_cell = 27 * 3; -#else - const size_t hydro_tasks_per_cell = 27 * 2; -#endif - const size_t self_grav_tasks_per_cell = 125; - const size_t ext_grav_tasks_per_cell = 1; - const size_t stars_tasks_per_cell = 15; - - if (e->policy & engine_policy_hydro) - e->size_links += s->tot_cells * hydro_tasks_per_cell; - if (e->policy & engine_policy_external_gravity) - e->size_links += s->tot_cells * ext_grav_tasks_per_cell; - if (e->policy & engine_policy_self_gravity) - e->size_links += s->tot_cells * self_grav_tasks_per_cell; - if (e->policy & engine_policy_stars) - e->size_links += s->tot_cells * stars_tasks_per_cell; - - /* Allocate the new link list */ - if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) == - NULL) - error("Failed to allocate cell-task links."); - e->nr_links = 0; - tic2 = getticks(); /* Split the tasks. */ @@ -1998,6 +2378,20 @@ void engine_maketasks(struct engine *e) { } #endif + /* Free the old list of cell-task links. */ + if (e->links != NULL) free(e->links); + e->size_links = e->sched.nr_tasks * e->links_per_tasks; + + /* Make sure that we have space for more links than last time. */ + if (e->size_links < e->nr_links * engine_rebuild_link_alloc_margin) + e->size_links = e->nr_links * engine_rebuild_link_alloc_margin; + + /* Allocate the new link list */ + if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) == + NULL) + error("Failed to allocate cell-task links."); + e->nr_links = 0; + tic2 = getticks(); /* Count the number of tasks associated with each cell and @@ -2046,24 +2440,29 @@ void engine_maketasks(struct engine *e) { tic2 = getticks(); - /* Add the dependencies for the gravity stuff */ - if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity)) - engine_link_gravity_tasks(e); + /* Run through the tasks and make stars feedback tasks for each stars density + task. Each stars feedback task depends on the stars ghosts and unlocks the + kick task of its super-cell. */ + if (e->policy & engine_policy_stars) + threadpool_map(&e->threadpool, engine_make_extra_starsloop_tasks_mapper, + sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e); if (e->verbose) - message("Linking gravity tasks took %.3f %s.", + message("Making extra starsloop tasks took %.3f %s.", clocks_from_ticks(getticks() - tic2), clocks_getunit()); tic2 = getticks(); - if (e->policy & engine_policy_stars) - threadpool_map(&e->threadpool, engine_link_stars_tasks_mapper, sched->tasks, - sched->nr_tasks, sizeof(struct task), 0, e); + /* Add the dependencies for the gravity stuff */ + if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity)) + engine_link_gravity_tasks(e); if (e->verbose) - message("Linking stars tasks took %.3f %s (including reweight).", + message("Linking gravity tasks took %.3f %s.", clocks_from_ticks(getticks() - tic2), clocks_getunit()); + tic2 = getticks(); + #ifdef WITH_MPI if (e->policy & engine_policy_feedback) error("Cannot run stellar feedback with MPI (yet)."); @@ -2075,32 +2474,34 @@ void engine_maketasks(struct engine *e) { /* Loop over the proxies and add the send tasks, which also generates the * cell tags for super-cells. */ + int max_num_send_cells = 0; + for (int pid = 0; pid < e->nr_proxies; pid++) + max_num_send_cells += e->proxies[pid].nr_cells_out; + struct cell_type_pair *send_cell_type_pairs = NULL; + if ((send_cell_type_pairs = (struct cell_type_pair *)malloc( + sizeof(struct cell_type_pair) * max_num_send_cells)) == NULL) + error("Failed to allocate temporary cell pointer list."); + int num_send_cells = 0; + for (int pid = 0; pid < e->nr_proxies; pid++) { /* Get a handle on the proxy. */ struct proxy *p = &e->proxies[pid]; - for (int k = 0; k < p->nr_cells_out; k++) - engine_addtasks_send_timestep(e, p->cells_out[k], p->cells_in[0], NULL); - - /* Loop through the proxy's outgoing cells and add the - send tasks for the cells in the proxy that have a hydro connection. */ - if (e->policy & engine_policy_hydro) - for (int k = 0; k < p->nr_cells_out; k++) - if (p->cells_out_type[k] & proxy_cell_type_hydro) - engine_addtasks_send_hydro(e, p->cells_out[k], p->cells_in[0], NULL, - NULL, NULL); - - /* Loop through the proxy's outgoing cells and add the - send tasks for the cells in the proxy that have a gravity connection. - */ - if (e->policy & engine_policy_self_gravity) - for (int k = 0; k < p->nr_cells_out; k++) - if (p->cells_out_type[k] & proxy_cell_type_gravity) - engine_addtasks_send_gravity(e, p->cells_out[k], p->cells_in[0], - NULL); + for (int k = 0; k < p->nr_cells_out; k++) { + send_cell_type_pairs[num_send_cells].ci = p->cells_out[k]; + send_cell_type_pairs[num_send_cells].cj = p->cells_in[0]; + send_cell_type_pairs[num_send_cells++].type = p->cells_out_type[k]; + } } + threadpool_map(&e->threadpool, engine_addtasks_send_mapper, + send_cell_type_pairs, num_send_cells, + sizeof(struct cell_type_pair), + /*chunk=*/0, e); + + free(send_cell_type_pairs); + if (e->verbose) message("Creating send tasks took %.3f %s.", clocks_from_ticks(getticks() - tic2), clocks_getunit()); @@ -2118,36 +2519,54 @@ void engine_maketasks(struct engine *e) { /* Loop over the proxies and add the recv tasks, which relies on having the * cell tags. */ + int max_num_recv_cells = 0; + for (int pid = 0; pid < e->nr_proxies; pid++) + max_num_recv_cells += e->proxies[pid].nr_cells_in; + struct cell_type_pair *recv_cell_type_pairs = NULL; + if ((recv_cell_type_pairs = (struct cell_type_pair *)malloc( + sizeof(struct cell_type_pair) * max_num_recv_cells)) == NULL) + error("Failed to allocate temporary cell pointer list."); + int num_recv_cells = 0; for (int pid = 0; pid < e->nr_proxies; pid++) { /* Get a handle on the proxy. */ struct proxy *p = &e->proxies[pid]; - - for (int k = 0; k < p->nr_cells_in; k++) - engine_addtasks_recv_timestep(e, p->cells_in[k], NULL); - - /* Loop through the proxy's incoming cells and add the - recv tasks for the cells in the proxy that have a hydro connection. */ - if (e->policy & engine_policy_hydro) - for (int k = 0; k < p->nr_cells_in; k++) - if (p->cells_in_type[k] & proxy_cell_type_hydro) - engine_addtasks_recv_hydro(e, p->cells_in[k], NULL, NULL, NULL); - - /* Loop through the proxy's incoming cells and add the - recv tasks for the cells in the proxy that have a gravity connection. - */ - if (e->policy & engine_policy_self_gravity) - for (int k = 0; k < p->nr_cells_in; k++) - if (p->cells_in_type[k] & proxy_cell_type_gravity) - engine_addtasks_recv_gravity(e, p->cells_in[k], NULL); + for (int k = 0; k < p->nr_cells_in; k++) { + recv_cell_type_pairs[num_recv_cells].ci = p->cells_in[k]; + recv_cell_type_pairs[num_recv_cells++].type = p->cells_in_type[k]; + } } + threadpool_map(&e->threadpool, engine_addtasks_recv_mapper, + recv_cell_type_pairs, num_recv_cells, + sizeof(struct cell_type_pair), + /*chunk=*/0, e); + free(recv_cell_type_pairs); if (e->verbose) message("Creating recv tasks took %.3f %s.", clocks_from_ticks(getticks() - tic2), clocks_getunit()); } + + /* Allocate memory for foreign particles */ + engine_allocate_foreign_particles(e); + #endif + /* Report the number of tasks we actually used */ + if (e->verbose) + message( + "Nr. of tasks: %d allocated tasks: %d ratio: %f memory use: %zd MB.", + e->sched.nr_tasks, e->sched.size, + (float)e->sched.nr_tasks / (float)e->sched.size, + e->sched.size * sizeof(struct task) / (1024 * 1024)); + + /* Report the number of links we actually used */ + if (e->verbose) + message( + "Nr. of links: %zd allocated links: %zd ratio: %f memory use: %zd MB.", + e->nr_links, e->size_links, (float)e->nr_links / (float)e->size_links, + e->size_links * sizeof(struct link) / (1024 * 1024)); + tic2 = getticks(); /* Set the unlocks per task. */ diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index 3d59665cea44957da653cc4c40594d68f0b1b543..a7f07829b573f5c99895320e1af1116659176a47 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -69,6 +69,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[2]); struct engine *e = (struct engine *)((size_t *)extra_data)[0]; const int nodeID = e->nodeID; + const int with_limiter = e->policy & engine_policy_limiter; for (int ind = 0; ind < num_elements; ind++) { @@ -90,6 +91,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_hydro(ci, e)) { scheduler_activate(s, t); cell_activate_drift_part(ci, s); + if (with_limiter) cell_activate_limiter(ci, s); } } @@ -99,6 +101,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_hydro(ci, e)) { scheduler_activate(s, t); cell_activate_subcell_hydro_tasks(ci, NULL, s); + if (with_limiter) cell_activate_limiter(ci, s); } } @@ -111,6 +114,16 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t); } + else if (t->type == task_type_self && + t->subtype == task_subtype_limiter) { + if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t); + } + + else if (t->type == task_type_sub_self && + t->subtype == task_subtype_limiter) { + if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t); + } + #ifdef EXTRA_HYDRO_LOOP else if (t_type == task_type_self && t_subtype == task_subtype_gradient) { if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t); @@ -128,7 +141,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cell_is_active_stars(ci, e)) { scheduler_activate(s, t); cell_activate_drift_part(ci, s); - cell_activate_drift_gpart(ci, s); + cell_activate_drift_spart(ci, s); } } @@ -141,6 +154,23 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } } + /* Activate the star feedback */ + else if (t_type == task_type_self && + t_subtype == task_subtype_stars_feedback) { + if (cell_is_active_stars(ci, e)) { + scheduler_activate(s, t); + } + } + + /* Store current values of dx_max and h_max. */ + else if (t_type == task_type_sub_self && + t_subtype == task_subtype_stars_feedback) { + if (cell_is_active_stars(ci, e)) { + scheduler_activate(s, t); + cell_activate_subcell_stars_tasks(ci, NULL, s); + } + } + /* Activate the gravity drift */ else if (t_type == task_type_self && t_subtype == task_subtype_grav) { if (cell_is_active_gravity(ci, e)) { @@ -180,14 +210,17 @@ void engine_marktasks_mapper(void *map_data, int num_elements, #endif const int ci_active_hydro = cell_is_active_hydro(ci, e); const int cj_active_hydro = cell_is_active_hydro(cj, e); + const int ci_active_gravity = cell_is_active_gravity(ci, e); const int cj_active_gravity = cell_is_active_gravity(cj, e); + const int ci_active_stars = cell_is_active_stars(ci, e); const int cj_active_stars = cell_is_active_stars(cj, e); /* Only activate tasks that involve a local active cell. */ if ((t_subtype == task_subtype_density || t_subtype == task_subtype_gradient || + t_subtype == task_subtype_limiter || t_subtype == task_subtype_force) && ((ci_active_hydro && ci_nodeID == nodeID) || (cj_active_hydro && cj_nodeID == nodeID))) { @@ -195,9 +228,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, scheduler_activate(s, t); /* Set the correct sorting flags */ - if (t_type == task_type_pair && - (t_subtype == task_subtype_density || - t_subtype == task_subtype_stars_density)) { + if (t_type == task_type_pair && t_subtype == task_subtype_density) { /* Store some values. */ atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); @@ -209,50 +240,72 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); + /* And the limiter */ + if (ci_nodeID == nodeID && with_limiter) cell_activate_limiter(ci, s); + if (cj_nodeID == nodeID && with_limiter) cell_activate_limiter(cj, s); + /* Check the sorts and activate them if needed. */ - cell_activate_sorts(ci, t->flags, s); - cell_activate_sorts(cj, t->flags, s); + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); } /* Store current values of dx_max and h_max. */ else if (t_type == task_type_sub_pair && - (t_subtype == task_subtype_density || - t_subtype == task_subtype_stars_density)) { + t_subtype == task_subtype_density) { cell_activate_subcell_hydro_tasks(t->ci, t->cj, s); } } - /* Stars */ + /* Stars density */ if (t_subtype == task_subtype_stars_density && ((ci_active_stars && ci->nodeID == engine_rank) || (cj_active_stars && cj->nodeID == engine_rank))) { + // MATTHIEU: The logic here can be improved. + // If ci is active for stars but not cj, then we can only drift the + // stars in ci and parts in cj. (and vice-versa). The same logic can be + // applied in cell_unskip_stars(). + scheduler_activate(s, t); /* Set the correct sorting flags */ if (t_type == task_type_pair) { + /* Do ci */ /* Store some values. */ - atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); atomic_or(&cj->hydro.requires_sorts, 1 << t->flags); - ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + atomic_or(&ci->stars.requires_sorts, 1 << t->flags); + cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort; + ci->stars.dx_max_sort_old = ci->stars.dx_max_sort; /* Activate the hydro drift tasks. */ - if (ci_nodeID == nodeID) { - cell_activate_drift_part(ci, s); - cell_activate_drift_gpart(ci, s); - } - if (cj_nodeID == nodeID) { - cell_activate_drift_part(cj, s); - cell_activate_drift_gpart(cj, s); - } + if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s); + + if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s); /* Check the sorts and activate them if needed. */ - cell_activate_sorts(ci, t->flags, s); - cell_activate_sorts(cj, t->flags, s); + cell_activate_hydro_sorts(cj, t->flags, s); + + cell_activate_stars_sorts(ci, t->flags, s); + + /* Do cj */ + /* Store some values. */ + atomic_or(&ci->hydro.requires_sorts, 1 << t->flags); + atomic_or(&cj->stars.requires_sorts, 1 << t->flags); + + ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort; + cj->stars.dx_max_sort_old = cj->stars.dx_max_sort; + + /* Activate the hydro drift tasks. */ + if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s); + + if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s); + /* Check the sorts and activate them if needed. */ + cell_activate_hydro_sorts(ci, t->flags, s); + cell_activate_stars_sorts(cj, t->flags, s); } /* Store current values of dx_max and h_max. */ @@ -261,6 +314,14 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } } + /* Stars feedback */ + if (t_subtype == task_subtype_stars_feedback && + ((ci_active_stars && ci->nodeID == engine_rank) || + (cj_active_stars && cj->nodeID == engine_rank))) { + + scheduler_activate(s, t); + } + /* Gravity */ if ((t_subtype == task_subtype_grav) && ((ci_active_gravity && ci_nodeID == nodeID) || @@ -285,7 +346,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (t_subtype == task_subtype_density) { /* Too much particle movement? */ - if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1; + if (cell_need_rebuild_for_hydro_pair(ci, cj)) *rebuild_space = 1; #ifdef WITH_MPI /* Activate the send/recv tasks. */ @@ -381,7 +442,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (t->subtype == task_subtype_stars_density) { /* Too much particle movement? */ - if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1; + if (cell_need_rebuild_for_stars_pair(ci, cj)) *rebuild_space = 1; // LOIC: Need implementing MPI case } diff --git a/src/entropy_floor.h b/src/entropy_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..9f2e97ccc815bee1087884d060492ff3715f1c6f --- /dev/null +++ b/src/entropy_floor.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_ENTROPY_FLOOR_H +#define SWIFT_ENTROPY_FLOOR_H + +/** + * @file src/entropy_floor.h + * @brief Branches between the different entropy floor models + */ + +/* Config parameters. */ +#include "../config.h" + +#include "common_io.h" +#include "error.h" +#include "inline.h" + +/* Import the right entropy floor definition */ +#if defined(ENTROPY_FLOOR_NONE) +#include "./entropy_floor/none/entropy_floor.h" +#elif defined(ENTROPY_FLOOR_EAGLE) +#include "./entropy_floor/EAGLE/entropy_floor.h" +#endif + +#endif /* SWIFT_ENTROPY_FLOOR_H */ diff --git a/src/entropy_floor/EAGLE/entropy_floor.h b/src/entropy_floor/EAGLE/entropy_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..41d35fa0484cc1ee491a3c6293893ad5d2b5583f --- /dev/null +++ b/src/entropy_floor/EAGLE/entropy_floor.h @@ -0,0 +1,281 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_ENTROPY_FLOOR_EAGLE_H +#define SWIFT_ENTROPY_FLOOR_EAGLE_H + +#include "adiabatic_index.h" +#include "cosmology.h" +#include "hydro.h" +#include "hydro_properties.h" +#include "parser.h" +#include "units.h" + +/** + * @file src/entropy_floor/EAGLE/entropy_floor.h + * @brief Entropy floor used in the EAGLE model + */ + +/** + * @brief Properties of the entropy floor in the EAGLE model. + */ +struct entropy_floor_properties { + + /*! Density threshold for the Jeans floor in Hydrogen atoms per cubic cm */ + float Jeans_density_threshold_H_p_cm3; + + /*! Density threshold for the Jeans floor in internal units */ + float Jeans_density_threshold; + + /*! Inverse of the density threshold for the Jeans floor in internal units */ + float Jeans_density_threshold_inv; + + /*! Over-density threshold for the Jeans floor */ + float Jeans_over_density_threshold; + + /*! Slope of the Jeans floor power-law */ + float Jeans_gamma_effective; + + /*! Temperature of the Jeans floor at the density threshold in Kelvin */ + float Jeans_temperature_norm_K; + + /*! Temperature of the Jeans floor at the density thresh. in internal units */ + float Jeans_temperature_norm; + + /*! Pressure of the Jeans floor at the density thresh. in internal units */ + float Jeans_pressure_norm; + + /*! Density threshold for the Cool floor in Hydrogen atoms per cubic cm */ + float Cool_density_threshold_H_p_cm3; + + /*! Density threshold for the Cool floor in internal units */ + float Cool_density_threshold; + + /*! Inverse of the density threshold for the Cool floor in internal units */ + float Cool_density_threshold_inv; + + /*! Over-density threshold for the Cool floor */ + float Cool_over_density_threshold; + + /*! Slope of the Cool floor power-law */ + float Cool_gamma_effective; + + /*! Temperature of the Cool floor at the density threshold in Kelvin */ + float Cool_temperature_norm_K; + + /*! Temperature of the Cool floor at the density thresh. in internal units */ + float Cool_temperature_norm; + + /*! Pressure of the Cool floor at the density thresh. in internal units */ + float Cool_pressure_norm; +}; + +/** + * @brief Compute the entropy floor of a given #part. + * + * Note that the particle is not updated!! + * + * @param p The #part. + * @param cosmo The cosmological model. + * @param props The properties of the entropy floor. + */ +static INLINE float entropy_floor( + const struct part *p, const struct cosmology *cosmo, + const struct entropy_floor_properties *props) { + + /* Physical density in internal units */ + const float rho = hydro_get_physical_density(p, cosmo); + + /* Critical density at this redshift. + * Recall that this is 0 in a non-cosmological run */ + const float rho_crit = cosmo->critical_density; + const float rho_crit_baryon = cosmo->Omega_b * rho_crit; + + /* Physical pressure */ + float pressure = 0.f; + + /* Are we in the regime of the Jeans equation of state? */ + if ((rho >= rho_crit_baryon * props->Jeans_over_density_threshold) && + (rho >= props->Jeans_density_threshold)) { + + const float pressure_Jeans = props->Jeans_pressure_norm * + powf(rho * props->Jeans_density_threshold_inv, + props->Jeans_gamma_effective); + + pressure = max(pressure, pressure_Jeans); + } + + /* Are we in the regime of the Cool equation of state? */ + if ((rho >= rho_crit_baryon * props->Cool_over_density_threshold) && + (rho >= props->Cool_density_threshold)) { + + const float pressure_Cool = props->Cool_pressure_norm * + powf(rho * props->Cool_density_threshold_inv, + props->Cool_gamma_effective); + + pressure = max(pressure, pressure_Cool); + } + + /* Convert to an entropy. + * (Recall that the entropy is the same in co-moving and phycial frames) */ + return gas_entropy_from_pressure(rho, pressure); +} + +/** + * @brief Initialise the entropy floor by reading the parameters and converting + * to internal units. + * + * @param params The YAML parameter file. + * @param us The system of units used internally. + * @param phys_const The physical constants. + * @param hydro_props The propoerties of the hydro scheme. + * @param props The entropy floor properties to fill. + */ +static INLINE void entropy_floor_init(struct entropy_floor_properties *props, + const struct phys_const *phys_const, + const struct unit_system *us, + const struct hydro_props *hydro_props, + struct swift_params *params) { + + /* Read the parameters in the units they are set */ + props->Jeans_density_threshold_H_p_cm3 = parser_get_param_float( + params, "EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"); + props->Jeans_over_density_threshold = parser_get_param_float( + params, "EAGLEEntropyFloor:Jeans_over_density_threshold"); + props->Jeans_temperature_norm_K = parser_get_param_float( + params, "EAGLEEntropyFloor:Jeans_temperature_norm_K"); + props->Jeans_gamma_effective = + parser_get_param_float(params, "EAGLEEntropyFloor:Jeans_gamma_effective"); + + props->Cool_density_threshold_H_p_cm3 = parser_get_param_float( + params, "EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"); + props->Cool_over_density_threshold = parser_get_param_float( + params, "EAGLEEntropyFloor:Cool_over_density_threshold"); + props->Cool_temperature_norm_K = parser_get_param_float( + params, "EAGLEEntropyFloor:Cool_temperature_norm_K"); + props->Cool_gamma_effective = + parser_get_param_float(params, "EAGLEEntropyFloor:Cool_gamma_effective"); + + /* Cross-check that the input makes sense */ + if (props->Cool_density_threshold_H_p_cm3 >= + props->Jeans_density_threshold_H_p_cm3) { + error( + "Invalid values for the entrop floor density thresholds. The 'Jeans' " + "threshold (%e cm^-3) should be at a higher density than the 'Cool' " + "threshold (%e cm^-3)", + props->Jeans_density_threshold_H_p_cm3, + props->Cool_density_threshold_H_p_cm3); + } + + /* Initial Hydrogen abundance (mass fraction) */ + const double X_H = hydro_props->hydrogen_mass_fraction; + + /* Now convert to internal units assuming primodial Hydrogen abundance */ + props->Jeans_temperature_norm = + props->Jeans_temperature_norm_K / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + props->Jeans_density_threshold = + props->Jeans_density_threshold_H_p_cm3 / + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY) * + phys_const->const_proton_mass / X_H; + + props->Cool_temperature_norm = + props->Cool_temperature_norm_K / + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + props->Cool_density_threshold = + props->Cool_density_threshold_H_p_cm3 / + units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY) * + phys_const->const_proton_mass / X_H; + + /* We assume neutral gas */ + const float mean_molecular_weight = hydro_props->mu_neutral; + + /* Get the common terms */ + props->Jeans_density_threshold_inv = 1.f / props->Jeans_density_threshold; + props->Cool_density_threshold_inv = 1.f / props->Cool_density_threshold; + + /* P_norm = (k_B * T) / (m_p * mu) * rho_threshold */ + props->Jeans_pressure_norm = + ((phys_const->const_boltzmann_k * props->Jeans_temperature_norm) / + (phys_const->const_proton_mass * mean_molecular_weight)) * + props->Jeans_density_threshold; + + props->Cool_pressure_norm = + ((phys_const->const_boltzmann_k * props->Cool_temperature_norm) / + (phys_const->const_proton_mass * mean_molecular_weight)) * + props->Cool_density_threshold; +} + +/** + * @brief Print the properties of the entropy floor to stdout. + * + * @param props The entropy floor properties. + */ +static INLINE void entropy_floor_print( + const struct entropy_floor_properties *props) { + + message("Entropy floor is 'EAGLE' with:"); + message("Jeans limiter with slope n=%.3f at rho=%e (%e H/cm^3) and T=%.1f K", + props->Jeans_gamma_effective, props->Jeans_density_threshold, + props->Jeans_density_threshold_H_p_cm3, + props->Jeans_temperature_norm); + message(" Cool limiter with slope n=%.3f at rho=%e (%e H/cm^3) and T=%.1f K", + props->Cool_gamma_effective, props->Cool_density_threshold, + props->Cool_density_threshold_H_p_cm3, props->Cool_temperature_norm); +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of entropy floor to the file + * @param h_grp The HDF5 group in which to write + */ +INLINE static void entropy_floor_write_flavour(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Entropy floor", "EAGLE"); +} +#endif + +/** + * @brief Write an entropy floor struct to the given FILE as a stream of bytes. + * + * @param props the struct + * @param stream the file stream + */ +static INLINE void entropy_floor_struct_dump( + const struct entropy_floor_properties *props, FILE *stream) { + + restart_write_blocks((void *)props, sizeof(struct entropy_floor_properties), + 1, stream, "entropy floor", "entropy floor properties"); +} + +/** + * @brief Restore a entropy floor struct from the given FILE as a stream of + * bytes. + * + * @param props the struct + * @param stream the file stream + */ +static INLINE void entropy_floor_struct_restore( + struct entropy_floor_properties *props, FILE *stream) { + + restart_read_blocks((void *)props, sizeof(struct entropy_floor_properties), 1, + stream, NULL, "entropy floor properties"); +} + +#endif /* SWIFT_ENTROPY_FLOOR_EAGLE_H */ diff --git a/src/entropy_floor/none/entropy_floor.h b/src/entropy_floor/none/entropy_floor.h new file mode 100644 index 0000000000000000000000000000000000000000..871ef8977e091841128e280184646e3be02957fd --- /dev/null +++ b/src/entropy_floor/none/entropy_floor.h @@ -0,0 +1,118 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_ENTROPY_FLOOR_NONE_H +#define SWIFT_ENTROPY_FLOOR_NONE_H + +/** + * @file src/entropy_floor/none/entropy_floor.h + * @brief Empty functions used for simulations without entropy + * floors. + */ + +struct cosmology; +struct hydro_props; +struct part; + +/** + * @brief Properties of the entropy floor. + * + * Nothing here. + */ +struct entropy_floor_properties {}; + +/** + * @brief Compute the entropy floor of a given #part. + * + * Simply return 0 (no floor). + * + * @param p The #part. + * @param cosmo The cosmological model. + * @param props The properties of the entropy floor. + */ +static INLINE float entropy_floor( + const struct part *p, const struct cosmology *cosmo, + const struct entropy_floor_properties *props) { + + return 0.f; +} + +/** + * @brief Initialise the entropy floor by reading the parameters and converting + * to internal units. + * + * Nothing to do here. + * + * @param params The YAML parameter file. + * @param us The system of units used internally. + * @param phys_cont The physical constants. + * @param props The entropy floor properties to fill. + */ +static INLINE void entropy_floor_init(struct entropy_floor_properties *props, + const struct phys_const *phys_const, + const struct unit_system *us, + const struct hydro_props *hydro_props, + struct swift_params *params) {} + +/** + * @brief Print the properties of the entropy floor to stdout. + * + * @param props The entropy floor properties. + */ +static INLINE void entropy_floor_print( + const struct entropy_floor_properties *props) { + + message("Entropy floor is 'no entropy floor'."); +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of entropy floor to the file + * @param h_grp The HDF5 group in which to write + */ +INLINE static void entropy_floor_write_flavour(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Entropy floor", "None"); +} +#endif + +/** + * @brief Write an entropy floor struct to the given FILE as a stream of bytes. + * + * Nothing to do here. + * + * @param props the struct + * @param stream the file stream + */ +static INLINE void entropy_floor_struct_dump( + const struct entropy_floor_properties *props, FILE *stream) {} + +/** + * @brief Restore a entropy floor struct from the given FILE as a stream of + * bytes. + * + * Nothing to do here. + * + * @param props the struct + * @param stream the file stream + */ +static INLINE void entropy_floor_struct_restore( + struct entropy_floor_properties *props, FILE *stream) {} + +#endif /* SWIFT_ENTROPY_FLOOR_NONE_H */ diff --git a/src/equation_of_state/planetary/tillotson.h b/src/equation_of_state/planetary/tillotson.h index 1a4210699380b3b0398506dde7fce6ca8055e4dc..609cc8dcb33e6533f68e13430f6654832af4d0a5 100644 --- a/src/equation_of_state/planetary/tillotson.h +++ b/src/equation_of_state/planetary/tillotson.h @@ -243,8 +243,8 @@ INLINE static float Til_soundspeed_from_internal_energy( P_c = (mat->a + mat->b * w_inv) * density * u + mat->A * mu + mat->B * mu * mu; } - c_sq_c = P_c * rho_inv * (1.f - mat->a - mat->b * w_inv) + - mat->b * (w - 1.f) * w_inv_sq * (2 * u + P_c * rho_inv) + + c_sq_c = P_c * rho_inv * (1.f + mat->a + mat->b * w_inv) + + mat->b * (w - 1.f) * w_inv_sq * (2.f * u - P_c * rho_inv) + rho_inv * (mat->A + mat->B * (eta_sq - 1.f)); c_sq_c = fmax(c_sq_c, mat->A * rho_0_inv); @@ -253,14 +253,15 @@ INLINE static float Til_soundspeed_from_internal_energy( P_e = mat->a * density * u + (mat->b * density * u * w_inv + mat->A * mu * exp_beta) * exp_alpha; - c_sq_e = P_e * rho_inv * (1.f - mat->a) + - (mat->b * density * u / (w * w * eta_sq) * - (rho_inv / mat->u_0 * (2 * u - P_e * rho_inv * eta_sq) + - 2.f * mat->alpha * nu * rho_0_inv) + - mat->A * rho_0_inv * - (1 + mu / eta_sq * (mat->beta + 2.f * mat->alpha * nu - eta)) * - exp_beta) * - exp_alpha; + c_sq_e = + P_e * rho_inv * (1.f + mat->a + mat->b * w_inv * exp_alpha) + + (mat->b * density * u * w_inv_sq / eta_sq * + (rho_inv / mat->u_0 * (2.f * u - P_e * rho_inv) + + 2.f * mat->alpha * nu * w * rho_0_inv) + + mat->A * rho_0_inv * + (1.f + mu / eta_sq * (mat->beta + 2.f * mat->alpha * nu - eta)) * + exp_beta) * + exp_alpha; // Condensed or cold state if ((1.f < eta) || (u < mat->u_iv)) { diff --git a/src/exp10.h b/src/exp10.h new file mode 100644 index 0000000000000000000000000000000000000000..b995bfdb3e1b6b1cb60bd4b60708413ea6c96f9f --- /dev/null +++ b/src/exp10.h @@ -0,0 +1,63 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_EXP10_H +#define SWIFT_EXP10_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <math.h> + +#ifndef __GNUC__ + +/* Local headers. */ +#include "inline.h" + +/** + * @brief Raises 10 to the power of the argument. + * + * This function is only used as a replacement for compilers that do + * not implement GNU extensions to the C language. + * + * @param x The input value. + */ +__attribute__((always_inline, const)) INLINE static double exp10( + const double x) { + + return exp(x * M_LN10); +} + +/** + * @brief Raises 10 to the power of the argument. + * + * This function is only used as a replacement for compilers that do + * not implement GNU extensions to the C language. + * + * @param x The input value. + */ +__attribute__((always_inline, const)) INLINE static float exp10f( + const float x) { + + return expf(x * (float)M_LN10); +} + +#endif /* __GNUC__ */ + +#endif /* SWIFT_EXP10_H */ diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index d446844e8ffc862fd3be0688302ebb3a2efab8fa..6d1270c952100f3a25202fcdb22be09f9acaa8d9 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -40,7 +40,7 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass( } /** - * @brief Returns the softening of a particle + * @brief Returns the current co-moving softening of a particle * * @param gp The particle of interest * @param grav_props The global gravity properties. diff --git a/src/gravity/Potential/gravity.h b/src/gravity/Potential/gravity.h index 10628dcea91786b0c7483134b8f7f844d6359e49..7d38e9126f1a313b169092b080ebc312c4bbe1bc 100644 --- a/src/gravity/Potential/gravity.h +++ b/src/gravity/Potential/gravity.h @@ -39,7 +39,7 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass( } /** - * @brief Returns the softening of a particle + * @brief Returns the current co-moving softening of a particle * * @param gp The particle of interest * @param grav_props The global gravity properties. diff --git a/src/gravity_properties.c b/src/gravity_properties.c index fffbf22ec187f179f0e80b7121beaa3a96de0260..60c0bd05ba8cb2234284154b4383bec07f30756d 100644 --- a/src/gravity_properties.c +++ b/src/gravity_properties.c @@ -99,10 +99,11 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params, } /* Set the softening to the current time */ - gravity_update(p, cosmo); + gravity_props_update(p, cosmo); } -void gravity_update(struct gravity_props *p, const struct cosmology *cosmo) { +void gravity_props_update(struct gravity_props *p, + const struct cosmology *cosmo) { /* Current softening lengths */ double softening; @@ -170,20 +171,22 @@ void gravity_props_print_snapshot(hid_t h_grpgrav, io_write_attribute_s(h_grpgrav, "Softening style", kernel_gravity_softening_name); io_write_attribute_f( - h_grpgrav, "Comoving softening length", + h_grpgrav, "Comoving softening length [internal units]", p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent); - io_write_attribute_f(h_grpgrav, - "Comoving Softening length (Plummer equivalent)", - p->epsilon_comoving); io_write_attribute_f( - h_grpgrav, "Maximal physical softening length", + h_grpgrav, + "Comoving Softening length (Plummer equivalent) [internal units]", + p->epsilon_comoving); + io_write_attribute_f( + h_grpgrav, "Maximal physical softening length [internal units]", p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent); io_write_attribute_f(h_grpgrav, - "Maximal physical softening length (Plummer equivalent)", + "Maximal physical softening length (Plummer equivalent) " + " [internal units]", p->epsilon_max_physical); io_write_attribute_f(h_grpgrav, "Opening angle", p->theta_crit); io_write_attribute_s(h_grpgrav, "Scheme", GRAVITY_IMPLEMENTATION); - io_write_attribute_d(h_grpgrav, "MM order", SELF_GRAVITY_MULTIPOLE_ORDER); + io_write_attribute_i(h_grpgrav, "MM order", SELF_GRAVITY_MULTIPOLE_ORDER); io_write_attribute_f(h_grpgrav, "Mesh a_smooth", p->a_smooth); io_write_attribute_f(h_grpgrav, "Mesh r_cut_max ratio", p->r_cut_max_ratio); io_write_attribute_f(h_grpgrav, "Mesh r_cut_min ratio", p->r_cut_min_ratio); diff --git a/src/gravity_properties.h b/src/gravity_properties.h index 0cabd9958efa2bb23524d03632f90fdd1f1c8306..09c8ef8ffa1d6cc4effa4895614106217a2861a9 100644 --- a/src/gravity_properties.h +++ b/src/gravity_properties.h @@ -73,7 +73,7 @@ struct gravity_props { /*! Maxium physical softening */ double epsilon_max_physical; - /*! Current sftening length */ + /*! Current softening length */ float epsilon_cur; /*! Square of current softening length */ @@ -90,7 +90,8 @@ void gravity_props_print(const struct gravity_props *p); void gravity_props_init(struct gravity_props *p, struct swift_params *params, const struct cosmology *cosmo, int with_cosmology, int periodic); -void gravity_update(struct gravity_props *p, const struct cosmology *cosmo); +void gravity_props_update(struct gravity_props *p, + const struct cosmology *cosmo); #if defined(HAVE_HDF5) void gravity_props_print_snapshot(hid_t h_grpsph, diff --git a/src/hydro.h b/src/hydro.h index 15c45c1dcfa1217d842904dfc1303acea607e3ab..3bf7d2228b528796a8717d6a5ab17fda6f569d25 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -72,6 +72,11 @@ #include "./hydro/Planetary/hydro.h" #include "./hydro/Planetary/hydro_iact.h" #define SPH_IMPLEMENTATION "Minimal version of SPH with multiple materials" +#elif defined(ANARCHY_PU_SPH) +#include "./hydro/AnarchyPU/hydro.h" +#include "./hydro/AnarchyPU/hydro_iact.h" +#define SPH_IMPLEMENTATION \ + "ANARCHY (Pressure-Energy) SPH (Dalla Vecchia+ in prep)" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/hydro/AnarchyPU/hydro.h b/src/hydro/AnarchyPU/hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..9bb53f290acb16b4f9efc44e430a78a6d1f5c5ff --- /dev/null +++ b/src/hydro/AnarchyPU/hydro.h @@ -0,0 +1,947 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Josh Borrow (joshua.borrow@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 SWIFT_ANARCHY_PU_HYDRO_H +#define SWIFT_ANARCHY_PU_HYDRO_H + +/** + * @file PressureEnergy/hydro.h + * @brief P-U conservative implementation of SPH (Non-neighbour loop + * equations) + * + * The thermal variable is the internal energy (u). A simple constant + * viscosity term with a Balsara switch is implemented. + * + * No thermal conduction term is implemented. + * + * This implementation corresponds to the one presented in the SWIFT + * documentation and in Hopkins, "A general class of Lagrangian smoothed + * particle hydrodynamics methods and implications for fluid mixing problems", + * MNRAS, 2013. + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "cosmology.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "hydro_space.h" +#include "kernel_hydro.h" +#include "minmax.h" + +#include <float.h> + +/** + * @brief Returns the comoving internal energy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable. + * + * @param p The particle of interest + * @param xp The extended data of the particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp) { + + return xp->u_full; +} + +/** + * @brief Returns the physical internal energy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp, + const struct cosmology *cosmo) { + + return xp->u_full * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving internal energy of a particle drifted to the + * current time. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) { + + return p->u; +} + +/** + * @brief Returns the physical internal energy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return p->u * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving pressure of a particle + * + * Computes the pressure based on the particle's properties. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( + const struct part *restrict p) { + + return p->pressure_bar; +} + +/** + * @brief Returns the physical pressure of a particle + * + * Computes the pressure based on the particle's properties and + * convert it to physical coordinates. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a_factor_pressure * p->pressure_bar; +} + +/** + * @brief Returns the comoving entropy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable. + * + * @param p The particle of interest + * @param xp The extended data of the particle of interest. + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( + const struct part *restrict p, const struct xpart *restrict xp) { + + return gas_entropy_from_internal_energy(p->rho, xp->u_full); +} + +/** + * @brief Returns the physical entropy of a particle at the last + * time the particle was kicked. + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param xp The extended data of the particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, xp->u_full); +} + +/** + * @brief Returns the comoving entropy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_comoving_entropy(const struct part *restrict p) { + + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the physical entropy of a particle drifted to the + * current time. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_drifted_physical_entropy(const struct part *restrict p, + const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { + + /* Compute the sound speed -- see theory section for justification */ + /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */ + const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho); + + return square_rooted; +} + +/** + * @brief Returns the physical sound speed of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( + const struct part *restrict p) { + + return p->rho; +} + +/** + * @brief Returns the comoving density of a particle. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a3_inv * p->rho; +} + +/** + * @brief Returns the mass of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_mass( + const struct part *restrict p) { + + return p->mass; +} + +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + +/** + * @brief Returns the velocities drifted to the current time of a particle. + * + * @param p The particle of interest + * @param xp The extended data of the particle. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. + * @param v (return) The velocities at the current time. + */ +__attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part *restrict p) { + + return p->u_dt; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + * @param cosmo Cosmology data structure + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part *restrict p, + const struct cosmology *cosmo) { + + return p->u_dt * cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { + + p->u_dt = du_dt; +} + +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_dt(struct part *restrict p, + const struct cosmology *cosmo, + float du_dt) { + + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); +} + +/** + * @brief Computes the hydro time-step of a given particle + * + * This function returns the time-step of a particle given its hydro-dynamical + * state. A typical time-step calculation would be the use of the CFL condition. + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * @param hydro_properties The SPH parameters + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_compute_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { + + const float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->viscosity.v_sig); + + const float dt_u_change = + (p->u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->u_dt) : FLT_MAX; + + return fminf(dt_cfl, dt_u_change); +} + +/** + * @brief Does some extra hydro operations once the actual physical time step + * for the particle is known. + * + * @param p The particle to act upon. + * @param dt Physical time step of the particle during the next step. + */ +__attribute__((always_inline)) INLINE static void hydro_timestep_extra( + struct part *p, float dt) {} + +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various density loop over neighbours. Typically, all fields of the + * density sub-structure of a particle get zeroed in here. + * + * @param p The particle to act upon + * @param hs #hydro_space containing hydro specific space information. + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p, const struct hydro_space *hs) { + + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->rho = 0.f; + p->density.rho_dh = 0.f; + p->pressure_bar = 0.f; + p->density.pressure_bar_dh = 0.f; + + p->density.rot_v[0] = 0.f; + p->density.rot_v[1] = 0.f; + p->density.rot_v[2] = 0.f; + + p->viscosity.div_v = 0.f; + p->diffusion.laplace_u = 0.f; +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * Additional quantities such as velocity gradients will also get the final + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. + * + * @param p The particle to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p, const struct cosmology *cosmo) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Final operation on the density (add self-contribution). */ + p->rho += p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->pressure_bar += p->mass * p->u * kernel_root; + p->density.pressure_bar_dh -= hydro_dimension * p->mass * p->u * kernel_root; + p->density.wcount += kernel_root; + p->density.wcount_dh -= hydro_dimension * kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->pressure_bar *= (h_inv_dim * hydro_gamma_minus_one); + p->density.pressure_bar_dh *= (h_inv_dim_plus_one * hydro_gamma_minus_one); + p->density.wcount *= h_inv_dim; + p->density.wcount_dh *= h_inv_dim_plus_one; + + const float rho_inv = 1.f / p->rho; + const float a_inv2 = cosmo->a2_inv; + + /* Finish calculation of the velocity curl components */ + p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + + /* Finish calculation of the velocity divergence */ + p->viscosity.div_v *= + h_inv_dim_plus_one * rho_inv * a_inv2 + cosmo->H * hydro_dimension; +} + +/** + * @brief Prepare a particle for the gradient calculation. + * + * This function is called after the density loop and before the gradient loop. + * + * We use it to set the physical timestep for the particle and to copy the + * actual velocities, which we need to boost our interfaces during the flux + * calculation. We also initialize the variables used for the time step + * calculation. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_gradient( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + const float fac_B = cosmo->a_factor_Balsara_eps; + + /* Compute the norm of the curl */ + const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + + p->density.rot_v[1] * p->density.rot_v[1] + + p->density.rot_v[2] * p->density.rot_v[2]); + + /* Compute the norm of div v */ + const float abs_div_v = fabsf(p->viscosity.div_v); + + /* Compute the sound speed -- see theory section for justification */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Compute the Balsara switch */ + const float balsara = + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_B / p->h); + + /* Compute the "grad h" term */ + const float common_factor = p->h / (hydro_dimension * p->density.wcount); + const float grad_h_term = (p->density.pressure_bar_dh * common_factor * + hydro_one_over_gamma_minus_one) / + (1.f + common_factor * p->density.wcount_dh); + + /* Update variables. */ + p->force.f = grad_h_term; + p->force.soundspeed = soundspeed; + p->force.balsara = balsara; +} + +/** + * @brief Resets the variables that are required for a gradient calculation. + * + * This function is called after hydro_prepare_gradient. + * + * @param p The particle to act upon. + * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_gradient( + struct part *restrict p) { + p->viscosity.v_sig = 2.f * p->force.soundspeed; +} + +/** + * @brief Finishes the gradient calculation. + * + * Just a wrapper around hydro_gradients_finalize, which can be an empty method, + * in which case no gradients are used. + * + * This method also initializes the force loop variables. + * + * @param p The particle to act upon. + */ +__attribute__((always_inline)) INLINE static void hydro_end_gradient( + struct part *p) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Include the extra factors in the del^2 u */ + + p->diffusion.laplace_u *= 2 * h_inv_dim_plus_one; +} + +/** + * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. + * + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + p->rho = p->mass * kernel_root * h_inv_dim; + p->viscosity.v_sig = 0.f; + p->pressure_bar = + p->mass * p->u * hydro_gamma_minus_one * kernel_root * h_inv_dim; + p->density.wcount = kernel_root * h_inv_dim; + p->density.rho_dh = 0.f; + p->density.wcount_dh = 0.f; + p->density.pressure_bar_dh = 0.f; + + p->density.rot_v[0] = 0.f; + p->density.rot_v[1] = 0.f; + p->density.rot_v[2] = 0.f; + + /* Probably not shocking, so this is safe to do */ + p->viscosity.div_v = 0.f; + p->diffusion.laplace_u = 0.f; +} + +/** + * @brief Prepare a particle for the force calculation. + * + * This function is called in the ghost task to convert some quantities coming + * from the density loop over neighbours into quantities ready to be used in the + * force loop over neighbours. Quantities are typically read from the density + * sub-structure and written to the force sub-structure. + * Examples of calculations done here include the calculation of viscosity term + * constants, thermal conduction terms, hydro conversions, etc. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. + * @param hydro_props Hydrodynamic properties. + * @param dt_alpha The time-step used to evolve non-cosmological quantities such + * as the artificial viscosity. + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const float dt_alpha) { + + /* Here we need to update the artificial viscosity */ + + /* Timescale for decay */ + const float tau = + p->h / (2.f * p->viscosity.v_sig * hydro_props->viscosity.length); + /* Construct time differential of div.v implicitly */ + const float div_v_dt = + dt_alpha == 0.f + ? 0.f + : (p->viscosity.div_v - p->viscosity.div_v_previous_step) / dt_alpha; + /* Construct the source term for the AV; if shock detected this is _positive_ + * as div_v_dt should be _negative_ before the shock hits */ + const float S = p->h * p->h * max(0.f, -1.f * div_v_dt); + const float v_sig_square = p->viscosity.v_sig * p->viscosity.v_sig; + /* Calculate the current appropriate value of the AV based on the above */ + const float alpha_loc = + hydro_props->viscosity.alpha_max * S / (v_sig_square + S); + + if (alpha_loc > p->viscosity.alpha) { + /* Reset the value of alpha to the appropriate value */ + p->viscosity.alpha = alpha_loc; + } else { + /* Integrate the alpha forward in time to decay back to alpha = 0 */ + const float alpha_dt = (alpha_loc - p->viscosity.alpha) / tau; + + /* Finally, we can update the actual value of the alpha */ + p->viscosity.alpha += alpha_dt * dt_alpha; + } + + if (p->viscosity.alpha < hydro_props->viscosity.alpha_min) { + p->viscosity.alpha = hydro_props->viscosity.alpha_min; + } + + /* Set our old div_v to the one for the next loop */ + p->viscosity.div_v_previous_step = p->viscosity.div_v; + + /* Now for the diffusive alpha */ + + const float sqrt_u = sqrtf(p->u); + /* Calculate initial value of alpha dt before bounding */ + /* alpha_diff_dt is cosmology-less */ + float alpha_diff_dt = + hydro_props->diffusion.beta * p->h * p->diffusion.laplace_u / sqrt_u; + + float new_diffusion_alpha = p->diffusion.alpha + alpha_diff_dt * dt_alpha; + + if (new_diffusion_alpha > hydro_props->diffusion.alpha_max) { + new_diffusion_alpha = hydro_props->diffusion.alpha_max; + } else if (new_diffusion_alpha < hydro_props->diffusion.alpha_min) { + new_diffusion_alpha = hydro_props->diffusion.alpha_min; + } + + p->diffusion.alpha = new_diffusion_alpha; +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the various force tasks. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( + struct part *restrict p) { + + /* Reset the acceleration. */ + p->a_hydro[0] = 0.0f; + p->a_hydro[1] = 0.0f; + p->a_hydro[2] = 0.0f; + + /* Reset the time derivatives. */ + p->u_dt = 0.0f; + p->force.h_dt = 0.0f; +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param p The particle. + * @param xp The extended data of this particle. + */ +__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( + struct part *restrict p, const struct xpart *restrict xp) { + + /* Re-set the predicted velocities */ + p->v[0] = xp->v_full[0]; + p->v[1] = xp->v_full[1]; + p->v[2] = xp->v_full[2]; + + /* Re-set the entropy */ + p->u = xp->u_full; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * Additional hydrodynamic quantites are drifted forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * + * @param p The particle. + * @param xp The extended data of the particle. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm) { + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt_drift; + if (fabsf(w1) < 0.2f) + p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ + else + p->h *= expf(w1); + + /* Predict density and weighted pressure */ + const float w2 = -hydro_dimension * w1; + if (fabsf(w2) < 0.2f) { + const float expf_approx = + approx_expf(w2); /* 4th order expansion of exp(w) */ + p->rho *= expf_approx; + p->pressure_bar *= expf_approx; + } else { + const float expf_exact = expf(w2); + p->rho *= expf_exact; + p->pressure_bar *= expf_exact; + } + + /* Predict the internal energy */ + p->u += p->u_dt * dt_therm; + + /* Compute the new sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + p->force.soundspeed = soundspeed; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the force and accelerations by the appropiate constants + * and add the self-contribution term. In most cases, there is little + * to do here. + * + * Cosmological terms are also added/multiplied here. + * + * @param p The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p, const struct cosmology *cosmo) { + + p->force.h_dt *= p->h * hydro_dimension_inv; +} + +/** + * @brief Kick the additional variables + * + * Additional hydrodynamic quantites are kicked forward in time here. These + * include thermal quantities (thermal energy or total energy or entropy, ...). + * + * @param p The particle to act upon. + * @param xp The particle extended data to act upon. + * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param dt_grav The time-step for this kick (for gravity quantities). + * @param dt_hydro The time-step for this kick (for hydro quantities). + * @param dt_kick_corr The time-step for this kick (for gravity corrections). + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + float dt_grav, float dt_hydro, float dt_kick_corr, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { + + /* Do not decrease the energy by more than a factor of 2*/ + if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) { + p->u_dt = -0.5f * xp->u_full / dt_therm; + } + xp->u_full += p->u_dt * dt_therm; + + /* Apply the minimal energy limit */ + const float min_energy = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; + if (xp->u_full < min_energy) { + xp->u_full = min_energy; + p->u_dt = 0.f; + } + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + p->force.soundspeed = soundspeed; +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * This function is called once at the end of the engine_init_particle() + * routine (at the start of a calculation) after the densities of + * particles have been computed. + * This can be used to convert internal energy into entropy for instance. + * + * @param p The particle to act upon + * @param xp The extended particle to act upon + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme. + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { + + /* Convert the physcial internal energy to the comoving one. */ + /* u' = a^(3(g-1)) u */ + const float factor = 1.f / cosmo->a_factor_internal_energy; + p->u *= factor; + xp->u_full = p->u; + + /* Apply the minimal energy limit */ + const float min_energy = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; + if (xp->u_full < min_energy) { + xp->u_full = min_energy; + p->u = min_energy; + p->u_dt = 0.f; + } + + /* Note that unlike Minimal the pressure and sound speed cannot be calculated + * here because they are smoothed properties in this scheme. */ + + /* Set the initial value of the artificial viscosity based on the non-variable + schemes for safety */ + + p->viscosity.alpha = hydro_props->viscosity.alpha; + /* Initialise this here to keep all the AV variables together */ + p->viscosity.div_v_previous_step = 0.f; + + /* Set the initial values for the thermal diffusion */ + p->diffusion.alpha = hydro_props->diffusion.alpha; +} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions or assignments between the particle + * and extended particle fields. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + + p->time_bin = 0; + p->wakeup = time_bin_not_awake; + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; + xp->u_full = p->u; + + hydro_reset_acceleration(p); + hydro_init_part(p, NULL); +} + +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->u = u_init; +} + +#endif /* SWIFT_ANARCHY_PU_HYDRO_H */ diff --git a/src/hydro/AnarchyPU/hydro_debug.h b/src/hydro/AnarchyPU/hydro_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..79ab5b96653a3f503c1baf255f4296f0ccc4aca9 --- /dev/null +++ b/src/hydro/AnarchyPU/hydro_debug.h @@ -0,0 +1,44 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Josh Borrow (joshua.borrow@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 SWIFT_ANARCHY_PU_HYDRO_DEBUG_H +#define SWIFT_ANARCHY_PU_HYDRO_DEBUG_H +/** + * @file PressureEnergy/hydro_debug.h + * @brief P-U conservative implementation of SPH (Debugging routines) + */ + +__attribute__((always_inline)) INLINE static void hydro_debug_particle( + const struct part* p, const struct xpart* xp) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e], " + "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" + "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, \n" + "p_dh=%.3e, p_bar=%.3e, alpha=%.3e \n" + "time_bin=%d\n", + p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], + xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], + p->u, p->u_dt, p->viscosity.v_sig, hydro_get_comoving_pressure(p), p->h, + p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, + p->density.pressure_bar_dh, p->pressure_bar, p->viscosity.alpha, + p->time_bin); +} + +#endif /* SWIFT_ANARCHY_PU_HYDRO_DEBUG_H */ diff --git a/src/hydro/AnarchyPU/hydro_iact.h b/src/hydro/AnarchyPU/hydro_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..c214db3b018e00b7f3881fb301b55d6cf49a1f43 --- /dev/null +++ b/src/hydro/AnarchyPU/hydro_iact.h @@ -0,0 +1,577 @@ +/******************************************************************************* + * This file is part* of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Josh Borrow (joshua.borrow@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 SWIFT_ANARCHY_PU_HYDRO_IACT_H +#define SWIFT_ANARCHY_PU_HYDRO_IACT_H + +/** + * @file PressureEnergy/hydro_iact.h + * @brief P-U implementation of SPH (Neighbour loop equations) + * + * The thermal variable is the internal energy (u). A simple constant + * viscosity term with a Balsara switch is implemented. + * + * No thermal conduction term is implemented. + * + * See PressureEnergy/hydro.h for references. + */ + +#include "adiabatic_index.h" +#include "minmax.h" + +/** + * @brief Density interaction between two part*icles. + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + float r2, const float* dx, float hi, float hj, struct part* pi, + struct part* pj, float a, float H) { + + float wi, wj, wi_dx, wj_dx; + float dv[3], curlvr[3]; + + const float r = sqrtf(r2); + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Compute density of pi. */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + pi->pressure_bar += mj * wi * pj->u; + pi->density.pressure_bar_dh -= + mj * pj->u * (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + /* Compute density of pj. */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + pj->pressure_bar += mi * wj * pi->u; + pj->density.pressure_bar_dh -= + mi * pi->u * (hydro_dimension * wj + uj * wj_dx); + pj->density.wcount += wj; + pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx); + + /* Now we need to compute the div terms */ + const float r_inv = 1.f / r; + const float faci = mj * wi_dx * r_inv; + const float facj = mi * wj_dx * r_inv; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->viscosity.div_v -= faci * dvdr; + pj->viscosity.div_v -= facj * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += faci * curlvr[0]; + pi->density.rot_v[1] += faci * curlvr[1]; + pi->density.rot_v[2] += faci * curlvr[2]; + + /* Negative because of the change in sign of dx & dv. */ + pj->density.rot_v[0] += facj * curlvr[0]; + pj->density.rot_v[1] += facj * curlvr[1]; + pj->density.rot_v[2] += facj * curlvr[2]; +} + +/** + * @brief Density interaction between two part*icles (non-symmetric). + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + float r2, const float* dx, float hi, float hj, struct part* pi, + const struct part* pj, float a, float H) { + + float wi, wi_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + + const float h_inv = 1.f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + pi->pressure_bar += mj * wi * pj->u; + + pi->density.pressure_bar_dh -= + mj * pj->u * (hydro_dimension * wi + ui * wi_dx); + pi->density.wcount += wi; + pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + + const float r_inv = 1.f / r; + const float faci = mj * wi_dx * r_inv; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->viscosity.div_v -= faci * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += faci * curlvr[0]; + pi->density.rot_v[1] += faci * curlvr[1]; + pi->density.rot_v[2] += faci * curlvr[2]; +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j + * + * This method wraps around hydro_gradients_collect, which can be an empty + * method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_gradient( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* We need to construct the maximal signal velocity between our particle + * and all of it's neighbours */ + + const float dv_dx = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + const float dv_dx_factor = min(0, const_viscosity_beta * dv_dx); + + const float new_v_sig = + pi->force.soundspeed + pj->force.soundspeed - dv_dx_factor; + + /* Update if we need to */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); + pj->viscosity.v_sig = max(pj->viscosity.v_sig, new_v_sig); + + /* Calculate Del^2 u for the thermal diffusion coefficient. */ + /* Need to get some kernel values F_ij = wi_dx */ + float wi, wi_dx, wj, wj_dx; + + const float r = sqrtf(r2); + const float ui = r / hi; + const float uj = r / hj; + + kernel_deval(ui, &wi, &wi_dx); + kernel_deval(uj, &wj, &wj_dx); + + const float delta_u_factor = (pi->u - pj->u) / r; + pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; + pj->diffusion.laplace_u -= pi->mass * delta_u_factor * wj_dx / pi->rho; +} + +/** + * @brief Calculate the gradient interaction between particle i and particle j: + * non-symmetric version + * + * This method wraps around hydro_gradients_nonsym_collect, which can be an + * empty method, in which case no gradients are used. + * + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi Particle i. + * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* We need to construct the maximal signal velocity between our particle + * and all of it's neighbours */ + + const float dv_dx = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + const float dv_dx_factor = min(0, const_viscosity_beta * dv_dx); + + const float new_v_sig = + pi->force.soundspeed + pj->force.soundspeed - dv_dx_factor; + + /* Update if we need to */ + pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); + + /* Calculate Del^2 u for the thermal diffusion coefficient. */ + /* Need to get some kernel values F_ij = wi_dx */ + float wi, wi_dx; + + const float r = sqrtf(r2); + const float ui = r / hi; + + kernel_deval(ui, &wi, &wi_dx); + + const float delta_u_factor = (pi->u - pj->u) / r; + pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; +} + +/** + * @brief Force interaction between two part*icles. + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + float r2, const float* dx, float hi, float hj, struct part* pi, + struct part* pj, float a, float H) { + + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Recover some data */ + const float mj = pj->mass; + const float mi = pi->mass; + + const float miui = mi * pi->u; + const float mjuj = mj * pj->u; + + const float rhoi = pi->rho; + const float rhoj = pj->rho; + /* Compute gradient terms */ + const float f_ij = 1.f - (pi->force.f / mjuj); + const float f_ji = 1.f - (pj->force.f / miui); + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the part*icles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Compute sound speeds and signal velocity */ + const float v_sig = 0.5 * (pi->viscosity.v_sig + pj->viscosity.v_sig); + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + -0.25f * alpha * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij; + + /* Convolve with the kernel */ + const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one * + ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) * + r_inv; + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + pj->a_hydro[0] += mi * acc * dx[0]; + pj->a_hydro[1] += mi * acc * dx[1]; + pj->a_hydro[2] += mi * acc * dx[2]; + + /* Get the time derivative for u. */ + const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one * + pj->u * pi->u * (f_ij / pi->pressure_bar) * + wi_dr * dvdr * r_inv; + const float sph_du_term_j = hydro_gamma_minus_one * hydro_gamma_minus_one * + pi->u * pj->u * (f_ji / pj->pressure_bar) * + wj_dr * dvdr * r_inv; + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; + + /* Diffusion term */ + const float v_diff = + max(pi->force.soundspeed + pj->force.soundspeed + dvdr_Hubble, 0.f); + const float alpha_diff = 0.5 * (pi->diffusion.alpha + pj->diffusion.alpha); + /* wi_dx + wj_dx / 2 is F_ij */ + const float diff_du_term = + alpha_diff * fac_mu * v_diff * (pi->u - pj->u) * (wi_dr + wj_dr) / rho_ij; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; + const float du_dt_j = sph_du_term_j + visc_du_term - diff_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + pj->u_dt += du_dt_j * mi; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; +} + +/** + * @brief Force interaction between two part*icles (non-symmetric). + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + float r2, const float* dx, float hi, float hj, struct part* pi, + const struct part* pj, float a, float H) { + + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Recover some data */ + // const float mi = pi->mass; + const float mj = pj->mass; + const float mi = pi->mass; + + const float miui = mi * pi->u; + const float mjuj = mj * pj->u; + + const float rhoi = pi->rho; + const float rhoj = pj->rho; + /* Compute gradient terms */ + const float f_ij = 1.f - (pi->force.f / mjuj); + const float f_ji = 1.f - (pj->force.f / miui); + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float xi = r * hi_inv; + float wi, wi_dx; + kernel_deval(xi, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + float wj, wj_dx; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Includes the hubble flow term; not used for du/dt */ + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + + /* Are the part*icles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Compute sound speeds and signal velocity */ + const float v_sig = 0.5 * (pi->viscosity.v_sig + pj->viscosity.v_sig); + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Construct the full viscosity term */ + const float rho_ij = rhoi + rhoj; + const float alpha = pi->viscosity.alpha + pj->viscosity.alpha; + const float visc = + -0.25f * alpha * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij; + + /* Convolve with the kernel */ + const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; + + /* SPH acceleration term */ + const float sph_acc_term = + pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one * + ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) * + r_inv; + + /* Assemble the acceleration */ + const float acc = sph_acc_term + visc_acc_term; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + /* Get the time derivative for u. */ + const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one * + pj->u * pi->u * (f_ij / pi->pressure_bar) * + wi_dr * dvdr * r_inv; + + /* Viscosity term */ + const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble; + + /* Diffusion term */ + const float v_diff = + max(pi->force.soundspeed + pj->force.soundspeed + dvdr_Hubble, 0.f); + const float alpha_diff = 0.5 * (pi->diffusion.alpha + pj->diffusion.alpha); + /* wi_dx + wj_dx / 2 is F_ij */ + const float diff_du_term = + alpha_diff * fac_mu * v_diff * (pi->u - pj->u) * (wi_dr + wj_dr) / rho_ij; + + /* Assemble the energy equation term */ + const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term; + + /* Internal energy time derivative */ + pi->u_dt += du_dt_i * mj; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; +} + +/** + * @brief Timestep limiter loop + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + * + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + * + * @param r2 Comoving square distance between the two part*icles. + * @param dx Comoving vector separating both part*icles (pi - pj). + * @param hi Comoving smoothing-length of part*icle i. + * @param hj Comoving smoothing-length of part*icle j. + * @param pi First part*icle. + * @param pj Second part*icle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + * + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->viscosity.v_sig > + const_limiter_max_v_sig_ratio * pj->viscosity.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + +#endif /* SWIFT_MINIMAL_HYDRO_IACT_H */ diff --git a/src/hydro/AnarchyPU/hydro_io.h b/src/hydro/AnarchyPU/hydro_io.h new file mode 100644 index 0000000000000000000000000000000000000000..99fea6a9feb2722da3c82482d4f5e79330e5c7e6 --- /dev/null +++ b/src/hydro/AnarchyPU/hydro_io.h @@ -0,0 +1,224 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Josh Borrow (joshua.borrow@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 SWIFT_ANARCHY_PU_HYDRO_IO_H +#define SWIFT_ANARCHY_PU_HYDRO_IO_H +/** + * @file PressureEnergy/hydro_io.h + * @brief P-U implementation of SPH (i/o routines) + * + * The thermal variable is the internal energy (u). A simple constant + * viscosity term with a Balsara switch is implemented. + * + * No thermal conduction term is implemented. + * + * See PressureEnergy/hydro.h for references. + */ + +#include "adiabatic_index.h" +#include "hydro.h" +#include "io_properties.h" +#include "kernel_hydro.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void hydro_read_particles(struct part* parts, + struct io_props* list, + int* num_fields) { + + *num_fields = 8; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, parts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, parts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + parts, mass); + list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY, + UNIT_CONV_LENGTH, parts, h); + list[4] = io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY, + UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); + list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, + UNIT_CONV_DENSITY, parts, rho); +} + +INLINE static void convert_S(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = hydro_get_comoving_entropy(p, xp); +} + +INLINE static void convert_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + ret[0] = hydro_get_comoving_pressure(p); +} + +INLINE static void convert_part_pos(const struct engine* e, + const struct part* p, + const struct xpart* xp, double* ret) { + + if (e->s->periodic) { + ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); + ret[1] = box_wrap(p->x[1], 0.0, e->s->dim[1]); + ret[2] = box_wrap(p->x[2], 0.0, e->s->dim[2]); + } else { + ret[0] = p->x[0]; + ret[1] = p->x[1]; + ret[2] = p->x[2]; + } +} + +INLINE static void convert_part_vel(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a_inv; + ret[1] *= cosmo->a_inv; + ret[2] *= cosmo->a_inv; +} + +INLINE static void convert_part_potential(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + +INLINE static void convert_viscosity(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = p->viscosity.alpha; +} + +INLINE static void convert_diffusion(const struct engine* e, + const struct part* p, + const struct xpart* xp, float* ret) { + ret[0] = p->diffusion.alpha; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +INLINE static void hydro_write_particles(const struct part* parts, + const struct xpart* xparts, + struct io_props* list, + int* num_fields) { + + *num_fields = 12; + + /* List what we want to write */ + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); + list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + parts, h); + list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); + list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = + io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[7] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, + parts, pressure_bar); + list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1, + UNIT_CONV_ENTROPY_PER_UNIT_MASS, + parts, xparts, convert_S); + list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); + list[10] = io_make_output_field_convert_part("Viscosity", FLOAT, 1, + UNIT_CONV_NO_UNITS, parts, + xparts, convert_viscosity); + list[11] = io_make_output_field_convert_part("Diffusion", FLOAT, 1, + UNIT_CONV_NO_UNITS, parts, + xparts, convert_diffusion); +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +INLINE static void hydro_write_flavour(hid_t h_grpsph) { + + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment"); + io_write_attribute_s(h_grpsph, "Viscosity Model", + "Minimal treatment as in Monaghan (1992)"); + + /* Time integration properties */ + io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt", + const_max_u_change); +} + +/** + * @brief Are we writing entropy in the internal energy field ? + * + * @return 1 if entropy is in 'internal energy', 0 otherwise. + */ +INLINE static int writeEntropyFlag(void) { return 0; } + +#endif /* SWIFT_ANARCHY_PU_HYDRO_IO_H */ diff --git a/src/hydro/AnarchyPU/hydro_part.h b/src/hydro/AnarchyPU/hydro_part.h new file mode 100644 index 0000000000000000000000000000000000000000..2c5022c262587c50f577970e1d1891a42b70491b --- /dev/null +++ b/src/hydro/AnarchyPU/hydro_part.h @@ -0,0 +1,218 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) & + * Josh Borrow (joshua.borrow@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 SWIFT_ANARCHY_PU_HYDRO_PART_H +#define SWIFT_ANARCHY_PU_HYDRO_PART_H +/** + * @file PressureEnergy/hydro_part.h + * @brief P-U implementation of SPH (Particle definition) + * + * The thermal variable is the internal energy (u). A simple constant + * viscosity term with a Balsara switch is implemented. + * + * No thermal conduction term is implemented. + * + * See PressureEnergy/hydro.h for references. + */ + +#include "chemistry_struct.h" +#include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" + +/** + * @brief Particle fields not needed during the SPH loops over neighbours. + * + * This structure contains the particle fields that are not used in the + * density or force loops. Quantities should be used in the kick, drift and + * potentially ghost tasks only. + */ +struct xpart { + + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Offset between the current position and position at the last sort. */ + float x_diff_sort[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Gravitational acceleration at the last full step. */ + float a_grav[3]; + + /*! Internal energy at the last full step. */ + float u_full; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the tracers */ + struct star_formation_xpart_data sf_data; + +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Particle fields for the SPH particles + * + * The density and force substructures are used to contain variables only used + * within the density and force loops over neighbours. All more permanent + * variables should be declared in the main part of the part structure, + */ +struct part { + + /*! Particle unique ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle mass. */ + float mass; + + /*! Particle smoothing length. */ + float h; + + /*! Particle internal energy. */ + float u; + + /*! Time derivative of the internal energy. */ + float u_dt; + + /*! Particle density. */ + float rho; + + /*! Particle pressure (weighted) */ + float pressure_bar; + + /* Store viscosity information in a separate struct. */ + struct { + + /*! Particle velocity divergence */ + float div_v; + + /*! Particle velocity divergence from previous step */ + float div_v_previous_step; + + /*! Artificial viscosity parameter */ + float alpha; + + /*! Signal velocity */ + float v_sig; + + } viscosity; + + /* Store thermal diffusion information in a separate struct. */ + struct { + + /*! del^2 u, a smoothed quantity */ + float laplace_u; + + /*! Thermal diffusion coefficient */ + float alpha; + + } diffusion; + + /* Store density/force specific stuff. */ + union { + + /** + * @brief Structure for the variables only used in the density loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the density + * loop over neighbours and the ghost task. + */ + struct { + + /*! Neighbour number count. */ + float wcount; + + /*! Derivative of the neighbour number with respect to h. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + /*! Derivative of the weighted pressure with respect to h */ + float pressure_bar_dh; + + /*! Particle velocity curl. */ + float rot_v[3]; + + } density; + + /** + * @brief Structure for the variables only used in the force loop over + * neighbours. + * + * Quantities in this sub-structure should only be accessed in the force + * loop over neighbours and the ghost, drift and kick tasks. + */ + struct { + + /*! "Grad h" term -- only partial in P-U */ + float f; + + /*! Particle soundspeed. */ + float soundspeed; + + /*! Time derivative of smoothing length */ + float h_dt; + + /*! Balsara switch */ + float balsara; + + } force; + }; + + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + + /*! Time-step length */ + timebin_t time_bin; + + /* Need waking up ? */ + timebin_t wakeup; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_ANARCHY_PU_HYDRO_PART_H */ diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index 4252f2787aefcec058b8fa956eaa0351b8f41d57..2b1d19bc916889a5cfdc40b1357f1e3dfe9388af 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -645,6 +645,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -674,4 +675,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->u = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + #endif /* SWIFT_DEFAULT_HYDRO_H */ diff --git a/src/hydro/Default/hydro_debug.h b/src/hydro/Default/hydro_debug.h index 3be9c9e1760591423edbd218d19b46ddf9aad01e..68367beaee97c285057cb055c1fbdbba5c370085 100644 --- a/src/hydro/Default/hydro_debug.h +++ b/src/hydro/Default/hydro_debug.h @@ -25,10 +25,11 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "x=[%.3e,%.3e,%.3e], " "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " "h=%.3e, " - "wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, time_bin=%d\n", + "wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, time_bin=%d wakeup=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], - p->h, (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->time_bin); + p->h, (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->time_bin, + p->wakeup); } #endif /* SWIFT_DEFAULT_HYDRO_DEBUG_H */ diff --git a/src/hydro/Default/hydro_iact.h b/src/hydro/Default/hydro_iact.h index 72808874c3fc6b58005d0e3ad450eafea8aa4b4d..85c586a4e921e38296453b71a2a2b9637971c28c 100644 --- a/src/hydro/Default/hydro_iact.h +++ b/src/hydro/Default/hydro_iact.h @@ -378,4 +378,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.v_sig = max(pi->force.v_sig, v_sig); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_DEFAULT_HYDRO_IACT_H */ diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h index 2a18e03cb533ca860f227a31152ef2058e0dd37d..21c0269f78c85b7d11ab5e838d45614161aee013 100644 --- a/src/hydro/Default/hydro_part.h +++ b/src/hydro/Default/hydro_part.h @@ -21,6 +21,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "tracers_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ struct xpart { @@ -40,6 +41,9 @@ struct xpart { /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + float u_full; /* Old density. */ @@ -132,6 +136,9 @@ struct part { /* Particle time-bin */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 9765ced22bb8e6291c2a189fc0e0f27419239c08..9994be7dc6051b033c052d32a4ebf3d7b59f1a84 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -306,10 +306,11 @@ hydro_get_physical_internal_energy_dt(const struct part *restrict p, * We assume a constant density for the conversion to entropy. * * @param p The particle of interest. - * @param du_dt The new time derivative of the internal energy. + * @param du_dt The new time derivative of the comoving internal energy. */ __attribute__((always_inline)) INLINE static void -hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { +hydro_set_comoving_internal_energy_dt(struct part *restrict p, + const float du_dt) { p->entropy_dt = gas_entropy_from_internal_energy(p->rho, du_dt); } @@ -321,15 +322,30 @@ hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { * * @param p The particle of interest. * @param cosmo Cosmology data structure - * @param du_dt The time derivative of the internal energy. + * @param du_dt The time derivative of the physical internal energy. */ __attribute__((always_inline)) INLINE static void hydro_set_physical_internal_energy_dt(struct part *restrict p, const struct cosmology *restrict cosmo, - float du_dt) { + const float du_dt) { p->entropy_dt = gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, du_dt); } +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + xp->entropy_full = entropy; +} /** * @brief Computes the hydro time-step of a given particle @@ -601,7 +617,14 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( else p->rho *= expf(w2); - /* Predict the entropy */ + /* Predict the entropy */ +#ifdef SWIFT_DEBUG_CHECKS + if (p->entropy + p->entropy_dt * dt_therm <= 0) + error( + "Negative entropy for particle id %llu old entropy %.5e d_entropy %.5e " + "entropy_dt %.5e dt therm %.5e", + p->id, p->entropy, p->entropy_dt * dt_therm, p->entropy_dt, dt_therm); +#endif p->entropy += p->entropy_dt * dt_therm; /* Re-compute the pressure */ @@ -743,6 +766,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -772,4 +796,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->entropy = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + #endif /* SWIFT_GADGET2_HYDRO_H */ diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h index d0642a03a4c4eecb2da80fdae473948e460c5e31..aeb43ee5d68930debfa867dc856465ac9d22902a 100644 --- a/src/hydro/Gadget2/hydro_debug.h +++ b/src/hydro/Gadget2/hydro_debug.h @@ -27,14 +27,14 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " "P=%.3e, P_over_rho2=%.3e, S=%.3e, dS/dt=%.3e, c=%.3e\n" "divV=%.3e, rotV=[%.3e,%.3e,%.3e], balsara=%.3e \n " - "v_sig=%e dh/dt=%.3e time_bin=%d\n", + "v_sig=%e dh/dt=%.3e time_bin=%d wakeup=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, p->rho, hydro_get_comoving_pressure(p), p->force.P_over_rho2, p->entropy, p->entropy_dt, p->force.soundspeed, p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->force.balsara, - p->force.v_sig, p->force.h_dt, p->time_bin); + p->force.v_sig, p->force.h_dt, p->time_bin, p->wakeup); } #endif /* SWIFT_GADGET2_HYDRO_DEBUG_H */ diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 746fd4778563aeaab43bafcc7904683ed5b6811c..1ded85acfb7486b1286ddfbbfa698da0f4344e7d 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -55,6 +55,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( float wj, wj_dx; float dv[3], curlvr[3]; +#ifdef SWIFT_DEBUG_CHECKS + if (pi->time_bin >= time_bin_inhibited) + error("Inhibited pi in interaction function!"); + if (pj->time_bin >= time_bin_inhibited) + error("Inhibited pj in interaction function!"); +#endif + /* Get the masses. */ const float mi = pi->mass; const float mj = pj->mass; @@ -145,6 +152,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( float wi, wi_dx; float dv[3], curlvr[3]; +#ifdef SWIFT_DEBUG_CHECKS + if (pi->time_bin >= time_bin_inhibited) + error("Inhibited pi in interaction function!"); + if (pj->time_bin >= time_bin_inhibited) + error("Inhibited pj in interaction function!"); +#endif + /* Get the masses. */ const float mj = pj->mass; @@ -279,7 +293,7 @@ runner_iact_nonsym_2_vec_density(float *R2, float *Dx, float *Dy, float *Dz, vector *wcountSum, vector *wcount_dhSum, vector *div_vSum, vector *curlvxSum, vector *curlvySum, vector *curlvzSum, - mask_t mask, mask_t mask2, short mask_cond) { + mask_t mask, mask_t mask2, int mask_cond) { vector r, ri, ui, wi, wi_dx; vector dvx, dvy, dvz; @@ -436,6 +450,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float wi, wj, wi_dx, wj_dx; +#ifdef SWIFT_DEBUG_CHECKS + if (pi->time_bin >= time_bin_inhibited) + error("Inhibited pi in interaction function!"); + if (pj->time_bin >= time_bin_inhibited) + error("Inhibited pj in interaction function!"); +#endif + /* Cosmological factors entering the EoMs */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); const float a2_Hubble = a * a * H; @@ -558,6 +579,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float wi, wj, wi_dx, wj_dx; +#ifdef SWIFT_DEBUG_CHECKS + if (pi->time_bin >= time_bin_inhibited) + error("Inhibited pi in interaction function!"); + if (pj->time_bin >= time_bin_inhibited) + error("Inhibited pj in interaction function!"); +#endif + /* Cosmological factors entering the EoMs */ const float fac_mu = pow_three_gamma_minus_five_over_two(a); const float a2_Hubble = a * a * H; @@ -1023,4 +1051,34 @@ runner_iact_nonsym_2_vec_force( #endif +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + + // MATTHIEU + // if (pj->wakeup == time_bin_not_awake) + // pj->wakeup = time_bin_awake; + // else if (pj->wakeup > 0) + // pj->wakeup = -pj->wakeup; + } +} + #endif /* SWIFT_GADGET2_HYDRO_IACT_H */ diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index bcb40243362dc57d47a3832c1d9330cb68d93fb8..3001700395b8584981c0087c8ff402a953461213 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -34,6 +34,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "logger.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ struct xpart { @@ -56,6 +58,12 @@ struct xpart { /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + #ifdef WITH_LOGGER /* Additional data for the particle logger */ struct logger_part_data logger_data; @@ -146,6 +154,9 @@ struct part { /* Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h index 8e466daabb59482a1c2ebbaf80af30c64c4abdfe..a4a54e7b551cc643bffedb8661f4fe269d348dc4 100644 --- a/src/hydro/GizmoMFM/hydro.h +++ b/src/hydro/GizmoMFM/hydro.h @@ -137,6 +137,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( p->conserved.momentum[2] * p->v[2]); #endif + p->time_bin = 0; + p->wakeup = time_bin_not_awake; + /* initialize the particle velocity based on the primitive fluid velocity */ xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; @@ -722,10 +725,12 @@ hydro_get_comoving_internal_energy(const struct part* restrict p) { * @brief Returns the physical internal energy of a particle * * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_internal_energy(const struct part* restrict p, + const struct xpart* restrict xp, const struct cosmology* cosmo) { return cosmo->a_factor_internal_energy * @@ -778,10 +783,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( * @brief Returns the physical internal energy of a particle * * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( - const struct part* restrict p, const struct cosmology* cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + const struct cosmology* cosmo) { /* Note: no cosmological conversion required here with our choice of * coordinates. */ @@ -906,6 +913,80 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( v[2] += xp->a_grav[2] * dt_kick_grav; } +/** + * @brief Returns the time derivative of co-moving internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part* restrict p) { + + error("Needs implementing"); + return 0.f; +} + +/** + * @brief Returns the time derivative of physical internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part* restrict p, + const struct cosmology* cosmo) { + error("Needs implementing"); + return 0.f; +} + +/** + * @brief Sets the time derivative of the co-moving internal energy of a + * particle + * + * We assume a constant density for the conversion to entropy. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the comoving internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part* restrict p, + const float du_dt) { + error("Needs implementing"); +} + +/** + * @brief Sets the time derivative of the physical internal energy of a particle + * + * We assume a constant density for the conversion to entropy. + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The time derivative of the physical internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_dt(struct part* restrict p, + const struct cosmology* restrict cosmo, + const float du_dt) { + error("Needs implementing"); +} +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part* p, struct xpart* xp, const struct cosmology* cosmo, + const float entropy) { + + error("Needs implementing"); +} + /** * @brief Returns the comoving density of a particle * @@ -1004,4 +1085,14 @@ hydro_set_init_internal_energy(struct part* p, float u_init) { p->P = hydro_gamma_minus_one * p->rho * u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part* p, const struct xpart* xp) {} + #endif /* SWIFT_GIZMO_MFM_HYDRO_H */ diff --git a/src/hydro/GizmoMFM/hydro_debug.h b/src/hydro/GizmoMFM/hydro_debug.h index e8b0914bd3cf6a99210399c6fc654e526319009f..e3c9f793aec92c7bfa2527143e6ad771c3897a09 100644 --- a/src/hydro/GizmoMFM/hydro_debug.h +++ b/src/hydro/GizmoMFM/hydro_debug.h @@ -27,6 +27,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "a=[%.3e,%.3e,%.3e], " "h=%.3e, " "time_bin=%d, " + "wakeup=%d, " "rho=%.3e, " "P=%.3e, " "gradients={" @@ -51,7 +52,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "wcount_dh=%.3e, " "wcount=%.3e}\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->rho, p->P, + p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->wakeup, p->rho, p->P, p->gradients.rho[0], p->gradients.rho[1], p->gradients.rho[2], p->gradients.v[0][0], p->gradients.v[0][1], p->gradients.v[0][2], p->gradients.v[1][0], p->gradients.v[1][1], p->gradients.v[1][2], diff --git a/src/hydro/GizmoMFM/hydro_iact.h b/src/hydro/GizmoMFM/hydro_iact.h index 38a97cbea39c1ed5c6926c911941e655e52362aa..09d4c7c70ee2bae8a31d10cb4a568c4627c7b3cd 100644 --- a/src/hydro/GizmoMFM/hydro_iact.h +++ b/src/hydro/GizmoMFM/hydro_iact.h @@ -486,4 +486,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->timestepvars.vmax > + const_limiter_max_v_sig_ratio * pj->timestepvars.vmax) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_GIZMO_MFM_HYDRO_IACT_H */ diff --git a/src/hydro/GizmoMFM/hydro_part.h b/src/hydro/GizmoMFM/hydro_part.h index 0055d7d86a35746a8ba90015b3a6986f8ddb5f9f..8097b1b2560f24f78636bbb855700054524fe0bb 100644 --- a/src/hydro/GizmoMFM/hydro_part.h +++ b/src/hydro/GizmoMFM/hydro_part.h @@ -21,6 +21,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /* Extra particle data not needed during the computation. */ struct xpart { @@ -40,6 +42,12 @@ struct xpart { /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ @@ -187,6 +195,9 @@ struct part { /* Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/GizmoMFV/hydro.h b/src/hydro/GizmoMFV/hydro.h index 98a70aefed098243bbf2dfe08e752ee48a838d3e..974f57ed68bbc409697111c52c40f36d4a5cb9b1 100644 --- a/src/hydro/GizmoMFV/hydro.h +++ b/src/hydro/GizmoMFV/hydro.h @@ -121,6 +121,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( const float mass = p->conserved.mass; + p->time_bin = 0; + p->wakeup = time_bin_not_awake; + p->primitives.v[0] = p->v[0]; p->primitives.v[1] = p->v[1]; p->primitives.v[2] = p->v[2]; @@ -808,10 +811,12 @@ hydro_get_comoving_internal_energy(const struct part* restrict p) { * @brief Returns the physical internal energy of a particle * * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_internal_energy(const struct part* restrict p, + const struct xpart* restrict xp, const struct cosmology* cosmo) { return cosmo->a_factor_internal_energy * @@ -828,7 +833,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_drifted_physical_internal_energy(const struct part* restrict p, const struct cosmology* cosmo) { - return hydro_get_physical_internal_energy(p, cosmo); + return hydro_get_physical_internal_energy(p, /*xp=*/NULL, cosmo); } /** @@ -850,10 +855,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( * @brief Returns the physical internal energy of a particle * * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( - const struct part* restrict p, const struct cosmology* cosmo) { + const struct part* restrict p, const struct xpart* restrict xp, + const struct cosmology* cosmo) { /* Note: no cosmological conversion required here with our choice of * coordinates. */ @@ -980,6 +987,80 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( v[2] += xp->a_grav[2] * dt_kick_grav; } +/** + * @brief Returns the time derivative of co-moving internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part* restrict p) { + + error("Needs implementing"); + return 0.f; +} + +/** + * @brief Returns the time derivative of physical internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part* restrict p, + const struct cosmology* cosmo) { + error("Needs implementing"); + return 0.f; +} + +/** + * @brief Sets the time derivative of the co-moving internal energy of a + * particle + * + * We assume a constant density for the conversion to entropy. + * + * @param p The particle of interest. + * @param du_dt The new time derivative of the comoving internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part* restrict p, + const float du_dt) { + error("Needs implementing"); +} + +/** + * @brief Sets the time derivative of the physical internal energy of a particle + * + * We assume a constant density for the conversion to entropy. + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The time derivative of the physical internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_dt(struct part* restrict p, + const struct cosmology* restrict cosmo, + const float du_dt) { + error("Needs implementing"); +} +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part* p, struct xpart* xp, const struct cosmology* cosmo, + const float entropy) { + + error("Needs implementing"); +} + /** * @brief Returns the comoving density of a particle * @@ -1078,4 +1159,14 @@ hydro_set_init_internal_energy(struct part* p, float u_init) { p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part* p, const struct xpart* xp) {} + #endif /* SWIFT_GIZMO_MFV_HYDRO_H */ diff --git a/src/hydro/GizmoMFV/hydro_debug.h b/src/hydro/GizmoMFV/hydro_debug.h index 8af3f824666529efad833c3bd520ace779718449..181bd6f82d547803c7303bd19be11cf66dc3a8a8 100644 --- a/src/hydro/GizmoMFV/hydro_debug.h +++ b/src/hydro/GizmoMFV/hydro_debug.h @@ -27,6 +27,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "a=[%.3e,%.3e,%.3e], " "h=%.3e, " "time_bin=%d, " + "wakeup=%d, " "primitives={" "v=[%.3e,%.3e,%.3e], " "rho=%.3e, " @@ -53,9 +54,9 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "wcount_dh=%.3e, " "wcount=%.3e}\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->primitives.v[0], - p->primitives.v[1], p->primitives.v[2], p->primitives.rho, - p->primitives.P, p->primitives.gradients.rho[0], + p->a_hydro[1], p->a_hydro[2], p->h, p->time_bin, p->wakeup, + p->primitives.v[0], p->primitives.v[1], p->primitives.v[2], + p->primitives.rho, p->primitives.P, p->primitives.gradients.rho[0], p->primitives.gradients.rho[1], p->primitives.gradients.rho[2], p->primitives.gradients.v[0][0], p->primitives.gradients.v[0][1], p->primitives.gradients.v[0][2], p->primitives.gradients.v[1][0], diff --git a/src/hydro/GizmoMFV/hydro_iact.h b/src/hydro/GizmoMFV/hydro_iact.h index 2f73e67ea2fdcecc527de8b1af0d15731f967b9b..d882549f8c55018419a2e1730d2ac099bbe1f5ee 100644 --- a/src/hydro/GizmoMFV/hydro_iact.h +++ b/src/hydro/GizmoMFV/hydro_iact.h @@ -501,4 +501,29 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->timestepvars.vmax > + const_limiter_max_v_sig_ratio * pj->timestepvars.vmax) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_GIZMO_MFV_HYDRO_IACT_H */ diff --git a/src/hydro/GizmoMFV/hydro_part.h b/src/hydro/GizmoMFV/hydro_part.h index 6248ddb11daf39a65be9a57fe51e40386ecda50b..af83dbd92590a30aa401e1cc5626554633058b1f 100644 --- a/src/hydro/GizmoMFV/hydro_part.h +++ b/src/hydro/GizmoMFV/hydro_part.h @@ -21,6 +21,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /* Extra particle data not needed during the computation. */ struct xpart { @@ -40,6 +42,12 @@ struct xpart { /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ @@ -198,6 +206,9 @@ struct part { /* Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index 93a2b3ec8ed4ecb6dcc73314233217d37141ba46..a5808468300da234a91a86feb897a9398e14db90 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -334,7 +334,24 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, const struct cosmology *cosmo, float du_dt) { - p->u_dt = du_dt * cosmo->a_factor_internal_energy; + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } /** @@ -740,6 +757,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -769,4 +787,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->u = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + #endif /* SWIFT_MINIMAL_HYDRO_H */ diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h index 73ffc26b8acf687a5445591ddccd72ea8e8fa8ae..3fadd05f9b93e53f1855c5daa7727d272ffe0fa5 100644 --- a/src/hydro/Minimal/hydro_debug.h +++ b/src/hydro/Minimal/hydro_debug.h @@ -41,12 +41,12 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v_full=[%.3g, %.3g, %.3g], a=[%.3g, %.3g, %.3g], \n " "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, \n " "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, \n " - "dh_drho=%.3g, time_bin=%d \n", + "dh_drho=%.3g, time_bin=%d wakeup=%d \n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->mass, p->u, p->u_dt, hydro_get_comoving_pressure(p), p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt, - p->density.wcount, p->rho, p->density.rho_dh, p->time_bin); + p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->wakeup); } #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */ diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h index e060cb3562f1b319c64d6f6523b18858662312e7..7fc7a3c67f6c832d70109319ad964e25df30ff4e 100644 --- a/src/hydro/Minimal/hydro_iact.h +++ b/src/hydro/Minimal/hydro_iact.h @@ -54,9 +54,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( float wi, wj, wi_dx, wj_dx; #ifdef SWIFT_DEBUG_CHECKS - if (pi->time_bin == time_bin_inhibited) + if (pi->time_bin >= time_bin_inhibited) error("Inhibited pi in interaction function!"); - if (pj->time_bin == time_bin_inhibited) + if (pj->time_bin >= time_bin_inhibited) error("Inhibited pj in interaction function!"); #endif @@ -135,9 +135,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( float wi, wi_dx; #ifdef SWIFT_DEBUG_CHECKS - if (pi->time_bin == time_bin_inhibited) + if (pi->time_bin >= time_bin_inhibited) error("Inhibited pi in interaction function!"); - if (pj->time_bin == time_bin_inhibited) + if (pj->time_bin >= time_bin_inhibited) error("Inhibited pj in interaction function!"); #endif @@ -196,9 +196,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( struct part *restrict pj, float a, float H) { #ifdef SWIFT_DEBUG_CHECKS - if (pi->time_bin == time_bin_inhibited) + if (pi->time_bin >= time_bin_inhibited) error("Inhibited pi in interaction function!"); - if (pj->time_bin == time_bin_inhibited) + if (pj->time_bin >= time_bin_inhibited) error("Inhibited pj in interaction function!"); #endif @@ -323,9 +323,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const struct part *restrict pj, float a, float H) { #ifdef SWIFT_DEBUG_CHECKS - if (pi->time_bin == time_bin_inhibited) + if (pi->time_bin >= time_bin_inhibited) error("Inhibited pi in interaction function!"); - if (pj->time_bin == time_bin_inhibited) + if (pj->time_bin >= time_bin_inhibited) error("Inhibited pj in interaction function!"); #endif @@ -424,4 +424,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.v_sig = max(pi->force.v_sig, v_sig); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_MINIMAL_HYDRO_IACT_H */ diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index 1d14a94f2d91bf259df54c875a32bf3072ad33b6..7697f36ca723875a8b77705eefcf2e2af4605583 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -34,6 +34,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /** * @brief Particle fields not needed during the SPH loops over neighbours. @@ -62,6 +64,12 @@ struct xpart { /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the tracers */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /** @@ -168,6 +176,9 @@ struct part { /*! Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h index dee65a15758043d2cf526ea889b993c694d5dab4..fbe0b6c8b9a8844cab3e38e485923fc7543ec528 100644 --- a/src/hydro/Planetary/hydro.h +++ b/src/hydro/Planetary/hydro.h @@ -37,6 +37,7 @@ #include "adiabatic_index.h" #include "approx_math.h" #include "cosmology.h" +#include "debug.h" #include "dimension.h" #include "equation_of_state.h" #include "hydro_properties.h" @@ -356,7 +357,25 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, const struct cosmology *cosmo, float du_dt) { - p->u_dt = du_dt * cosmo->a_factor_internal_energy; + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = + gas_internal_energy_from_entropy(p->rho, comoving_entropy, p->mat_id); } /** @@ -517,17 +536,15 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the "grad h" term */ const float rho_inv = 1.f / p->rho; - float grad_h_term; - const float grad_h_term_inv = - 1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv; - /* Avoid 1/0 from only having one neighbour right at the edge of the kernel */ - if (grad_h_term_inv != 0.f) { - grad_h_term = 1.f / grad_h_term_inv; - } else { - grad_h_term = 0.f; + float rho_dh = p->density.rho_dh; + /* Ignore changing-kernel effects when h is h_max */ + if (p->h == hydro_props->h_max) { + rho_dh = 0.f; } + const float grad_h_term = + 1.f / (1.f + hydro_dimension_inv * p->h * rho_dh * rho_inv); - /* Compute the Balsara switch */ + /* Compute the Balsara switch */ #ifdef PLANETARY_SPH_NO_BALSARA const float balsara = hydro_props->viscosity.alpha; #else @@ -736,6 +753,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -765,4 +783,17 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->u = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) { + + printParticle_single(p, xp); +} + #endif /* SWIFT_PLANETARY_HYDRO_H */ diff --git a/src/hydro/Planetary/hydro_debug.h b/src/hydro/Planetary/hydro_debug.h index 74261f3b49e2881af1c403013005560efa53a7f1..306f7526404599a051f83dc1b61886ed2aa5b69e 100644 --- a/src/hydro/Planetary/hydro_debug.h +++ b/src/hydro/Planetary/hydro_debug.h @@ -42,12 +42,13 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v_full=[%.3g, %.3g, %.3g], a=[%.3g, %.3g, %.3g], \n " "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, \n " "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, \n " - "dh_drho=%.3g, time_bin=%d, mat_id=%d \n", + "dh_drho=%.3g, time_bin=%d, wakeup=%d mat_id=%d \n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->mass, p->u, p->u_dt, hydro_get_comoving_pressure(p), p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt, - p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->mat_id); + p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->wakeup, + p->mat_id); } #endif /* SWIFT_PLANETARY_HYDRO_DEBUG_H */ diff --git a/src/hydro/Planetary/hydro_iact.h b/src/hydro/Planetary/hydro_iact.h index 19ee002b85c1b0bc8ed621a029059cd02c5e670f..afebb6a406bd310f38d51dcb32fc25da6b2674b5 100644 --- a/src/hydro/Planetary/hydro_iact.h +++ b/src/hydro/Planetary/hydro_iact.h @@ -346,4 +346,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.v_sig = max(pi->force.v_sig, v_sig); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_PLANETARY_HYDRO_IACT_H */ diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h index 4087cef62e873231a556f82869a7f6d848c8d72c..b2725ca1fceddb196a6b2be42b768eb3f88f1101 100644 --- a/src/hydro/Planetary/hydro_part.h +++ b/src/hydro/Planetary/hydro_part.h @@ -36,6 +36,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" #include "equation_of_state.h" // For enum material_id +#include "star_formation_struct.h" +#include "tracers_struct.h" /** * @brief Particle fields not needed during the SPH loops over neighbours. @@ -64,6 +66,12 @@ struct xpart { /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /*! Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /* Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /** @@ -173,6 +181,9 @@ struct part { /*! Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h index 4c3cc5c1c588e19de0d4833fc867ae9c0aed1209..4af00f7a657d61871dc0a82affb04d411e13e047 100644 --- a/src/hydro/PressureEnergy/hydro.h +++ b/src/hydro/PressureEnergy/hydro.h @@ -359,7 +359,24 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, const struct cosmology *cosmo, float du_dt) { - p->u_dt = du_dt * cosmo->a_factor_internal_energy; + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } /** @@ -535,7 +552,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const float dt_alpha) { - const float fac_mu = cosmo->a_factor_mu; + const float fac_B = cosmo->a_factor_Balsara_eps; /* Compute the norm of the curl */ const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + @@ -551,7 +568,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the Balsara switch */ const float balsara = hydro_props->viscosity.alpha * abs_div_v / - (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h); + (abs_div_v + curl_v + 0.0001f * soundspeed * fac_B / p->h); /* Compute the "grad h" term */ const float common_factor = p->h / (hydro_dimension * p->density.wcount); @@ -763,6 +780,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -792,4 +810,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->u = u_init; } -#endif /* SWIFT_MINIMAL_HYDRO_H */ +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + +#endif /* SWIFT_PRESSURE_ENERGY_HYDRO_H */ diff --git a/src/hydro/PressureEnergy/hydro_debug.h b/src/hydro/PressureEnergy/hydro_debug.h index 6324167f12726e155eeaa3359be9741aca3a1e42..7ffc370ed4d6abd273fc3d8d5b887f5ccf8e001c 100644 --- a/src/hydro/PressureEnergy/hydro_debug.h +++ b/src/hydro/PressureEnergy/hydro_debug.h @@ -32,12 +32,12 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, \n" "p_dh=%.3e, p_bar=%.3e \n" - "time_bin=%d\n", + "time_bin=%d wakeup=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h, p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, - p->density.pressure_bar_dh, p->pressure_bar, p->time_bin); + p->density.pressure_bar_dh, p->pressure_bar, p->time_bin, p->wakeup); } #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEnergy/hydro_iact.h b/src/hydro/PressureEnergy/hydro_iact.h index 4146e61a53dd7ece57e263cb90308e2579aa3930..ae154ea549a52cb24ed7c69453533b7d59b39a85 100644 --- a/src/hydro/PressureEnergy/hydro_iact.h +++ b/src/hydro/PressureEnergy/hydro_iact.h @@ -17,8 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_MINIMAL_HYDRO_IACT_H -#define SWIFT_MINIMAL_HYDRO_IACT_H +#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H +#define SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H /** * @file PressureEnergy/hydro_iact.h @@ -418,5 +418,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Update the signal velocity. */ pi->force.v_sig = max(pi->force.v_sig, v_sig); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} -#endif /* SWIFT_MINIMAL_HYDRO_IACT_H */ +#endif /* SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEnergy/hydro_io.h b/src/hydro/PressureEnergy/hydro_io.h index 06762c6124c2c726c4e687980455ab956a5fa79e..701c12283bf77acef4af77598f57705a2b364fa1 100644 --- a/src/hydro/PressureEnergy/hydro_io.h +++ b/src/hydro/PressureEnergy/hydro_io.h @@ -17,8 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H -#define SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H +#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_IO_H +#define SWIFT_PRESSURE_ENERGY_HYDRO_IO_H /** * @file PressureEnergy/hydro_io.h * @brief P-U implementation of SPH (i/o routines) diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h index bc7d14b612556dc722ecca67dd6ce823192e00f0..20c326da443e4acd1c3bdc0ebd01cce81bb6bad7 100644 --- a/src/hydro/PressureEnergy/hydro_part.h +++ b/src/hydro/PressureEnergy/hydro_part.h @@ -33,6 +33,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /** * @brief Particle fields not needed during the SPH loops over neighbours. @@ -61,6 +63,12 @@ struct xpart { /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /*! Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /*! Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /** @@ -168,6 +176,9 @@ struct part { /*! Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h index 060694a6afa850c4d4815899fde1450316da81f5..b50ca4e2543af94573ff34954c26a23200b78a1d 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h @@ -50,22 +50,26 @@ #include <float.h> /** - * @brief Returns the comoving internal energy of a particle + * @brief Returns the comoving internal energy of a particle at the last + * time the particle was kicked. * * For implementations where the main thermodynamic variable * is not internal energy, this function computes the internal * energy from the thermodynamic variable. * * @param p The particle of interest + * @param xp The extended data of the particle of interest. */ __attribute__((always_inline)) INLINE static float -hydro_get_comoving_internal_energy(const struct part *restrict p) { +hydro_get_comoving_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp) { - return p->u; + return xp->u_full; } /** - * @brief Returns the physical internal energy of a particle + * @brief Returns the physical internal energy of a particle at the last + * time the particle was kicked. * * For implementations where the main thermodynamic variable * is not internal energy, this function computes the internal @@ -73,13 +77,15 @@ hydro_get_comoving_internal_energy(const struct part *restrict p) { * physical coordinates. * * @param p The particle of interest. + * @param xp The extended data of the particle of interest. * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_internal_energy(const struct part *restrict p, + const struct xpart *restrict xp, const struct cosmology *cosmo) { - return p->u * cosmo->a_factor_internal_energy; + return xp->u_full * cosmo->a_factor_internal_energy; } /** @@ -137,11 +143,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( - const struct part *restrict p, const struct cosmology *cosmo) { + const struct part *restrict p, const struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Note: no cosmological conversion required here with our choice of * coordinates. */ - return gas_entropy_from_internal_energy(p->rho, p->u); + return gas_entropy_from_internal_energy(p->rho, xp->u_full); } /** @@ -300,12 +307,27 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy_dt(const struct part *restrict p) { return p->u_dt; } +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest + * @param cosmo Cosmology data structure + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy_dt(const struct part *restrict p, + const struct cosmology *cosmo) { + + return p->u_dt * cosmo->a_factor_internal_energy; +} + /** * @brief Sets the time derivative of internal energy of a particle * @@ -314,12 +336,46 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt( * @param p The particle of interest. * @param du_dt The new time derivative of the internal energy. */ -__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( - struct part *restrict p, float du_dt) { +__attribute__((always_inline)) INLINE static void +hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) { p->u_dt = du_dt; } +/** + * @brief Returns the time derivative of internal energy of a particle + * + * We assume a constant density. + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param du_dt The new time derivative of the internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy_dt(struct part *restrict p, + const struct cosmology *cosmo, + float du_dt) { + + p->u_dt = du_dt / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); +} + /** * @brief Computes the hydro time-step of a given particle * @@ -492,7 +548,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const struct cosmology *cosmo, const struct hydro_props *hydro_props, const float dt_alpha) { - const float fac_mu = cosmo->a_factor_mu; + const float fac_B = cosmo->a_factor_Balsara_eps; const float h_inv = 1.f / p->h; @@ -509,7 +565,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the Balsara switch */ const float balsara = - abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu * h_inv); + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_B * h_inv); /* Compute the "grad h" term */ const float common_factor = p->h / (hydro_dimension * p->density.wcount); @@ -734,6 +790,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; @@ -763,4 +820,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->u = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + #endif /* SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_H */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h index ead5fcc0c842d8018f784a1084941bdb9ebcb6ca..d0cd5367f94cd90f36cc2b738a63c7963adbd445 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_debug.h @@ -36,12 +36,13 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n" "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, \n" "p_dh=%.3e, p_bar=%.3e \n" - "time_bin=%d, alpha=%.3e\n", + "time_bin=%d, wakeup=%d alpha=%.3e\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h, p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, - p->density.pressure_bar_dh, p->pressure_bar, p->time_bin, p->alpha); + p->density.pressure_bar_dh, p->pressure_bar, p->time_bin, p->wakeup, + p->alpha); } #endif /* SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h index 747fca714ce20d9c2b018e14ac24a6492c51a75f..69da511c7544a71ef381a0889c8b56c80d5211f1 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_iact.h @@ -424,4 +424,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.v_sig = max(pi->force.v_sig, v_sig); } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float* dx, float hi, float hj, struct part* restrict pi, + struct part* restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h index 1600679bc2e840d0b3b958531c279f5f29293b48..71662f14c61c92d65bcf493b6f5a43b8172e3697 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_io.h @@ -69,12 +69,6 @@ INLINE static void hydro_read_particles(struct part* parts, UNIT_CONV_DENSITY, parts, rho); } -INLINE static void convert_u(const struct engine* e, const struct part* p, - const struct xpart* xp, float* ret) { - - ret[0] = hydro_get_comoving_internal_energy(p); -} - INLINE static void convert_S(const struct engine* e, const struct part* p, const struct xpart* xp, float* ret) { @@ -170,9 +164,8 @@ INLINE static void hydro_write_particles(const struct part* parts, io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, parts, h); - list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, - UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, xparts, convert_u); + list[4] = io_make_output_field("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); list[6] = diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h index da6391236811e2a907281c3db05462bb57602fe0..ecd20938456b04004ed2299fbe1de0c1b8bb50d6 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h @@ -34,6 +34,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /** * @brief Particle fields not needed during the SPH loops over neighbours. @@ -62,6 +64,12 @@ struct xpart { /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /*! Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /*! Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /** @@ -172,6 +180,9 @@ struct part { /*! Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index b16d24cfcee9407c8213b1e17465005884da6617..40b3f42eaed7cbff3c6503caa0fc8801d65ac8e3 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -327,6 +327,21 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, p->entropy_dt = gas_entropy_from_internal_energy(p->rho_bar * cosmo->a3_inv, du_dt); } +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + xp->entropy_full = entropy; +} /** * @brief Computes the hydro time-step of a given particle @@ -730,6 +745,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( struct part *restrict p, struct xpart *restrict xp) { p->time_bin = 0; + p->wakeup = time_bin_not_awake; p->rho_bar = 0.f; p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); xp->v_full[0] = p->v[0]; @@ -760,4 +776,14 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { p->entropy = u_init; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part *p, const struct xpart *xp) {} + #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */ diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h index 14d69bb650ff1bbd49394c0ca2f6256ad0cb188d..2163b70b94dde4e88f010d962358dccbde7960a3 100644 --- a/src/hydro/PressureEntropy/hydro_debug.h +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -36,14 +36,14 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " "rho_bar=%.3e, P=%.3e, dP_dh=%.3e, P_over_rho2=%.3e, S=%.3e, S^1/g=%.3e, " - "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e time_bin=%d\n", + "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e time_bin=%d wakeup=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, p->rho, p->rho_bar, hydro_get_comoving_pressure(p), p->density.pressure_dh, p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, p->entropy_dt, p->force.soundspeed, - p->force.v_sig, p->force.h_dt, p->time_bin); + p->force.v_sig, p->force.h_dt, p->time_bin, p->wakeup); } #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h index a018b39a99be5ed691485d93bd8dfd1735378bda..19279adec1f37117cf985e63a18a681ceee4f973 100644 --- a/src/hydro/PressureEntropy/hydro_iact.h +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -402,4 +402,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->entropy_dt += mj * visc_term * r_inv * dvdr; } +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { + + pj->wakeup = time_bin_awake; + } +} + #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h index fb8424d66196b7013866acef6bec6ec9889a3353..6cf5a88a167c529ad81737f1e206ba475f6bbc0e 100644 --- a/src/hydro/PressureEntropy/hydro_part.h +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -32,6 +32,8 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" +#include "tracers_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ struct xpart { @@ -54,6 +56,12 @@ struct xpart { /*! Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /*! Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + + /*! Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ @@ -148,6 +156,9 @@ struct part { /* Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h index 7e38aa6b57f383564e96d9fea24730926c0ac70b..b0f3207dfce69ca79899b1134740d035d47251d1 100644 --- a/src/hydro/Shadowswift/hydro.h +++ b/src/hydro/Shadowswift/hydro.h @@ -103,6 +103,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( const float mass = p->conserved.mass; + p->time_bin = 0; + p->wakeup = time_bin_not_awake; + p->primitives.v[0] = p->v[0]; p->primitives.v[1] = p->v[1]; p->primitives.v[2] = p->v[2]; @@ -847,4 +850,14 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_density( return cosmo->a3_inv * p->primitives.rho; } +/** + * @brief Operations performed when a particle gets removed from the + * simulation volume. + * + * @param p The particle. + * @param xp The extended particle data. + */ +__attribute__((always_inline)) INLINE static void hydro_remove_part( + const struct part* p, const struct xpart* xp) {} + #endif /* SWIFT_SHADOWSWIFT_HYDRO_H */ diff --git a/src/hydro/Shadowswift/hydro_debug.h b/src/hydro/Shadowswift/hydro_debug.h index 7cd7f89c8112ebcf1930c5ca52cb389139191975..8ff85d62fc7d58d53220b1f77a7afb44c00c33b0 100644 --- a/src/hydro/Shadowswift/hydro_debug.h +++ b/src/hydro/Shadowswift/hydro_debug.h @@ -23,6 +23,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "x=[%.16e,%.16e,%.16e], " "v=[%.3e,%.3e,%.3e], " "a=[%.3e,%.3e,%.3e], " + "time_bin=%d, " + "wakeup=%d, " "h=%.3e, " "primitives={" "v=[%.3e,%.3e,%.3e], " @@ -47,9 +49,9 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "wcount_dh=%.3e, " "wcount=%.3e}", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->h, p->primitives.v[0], - p->primitives.v[1], p->primitives.v[2], p->primitives.rho, - p->primitives.P, p->primitives.gradients.rho[0], + p->a_hydro[1], p->a_hydro[2], p->time_bin, p->wakeup, p->h, + p->primitives.v[0], p->primitives.v[1], p->primitives.v[2], + p->primitives.rho, p->primitives.P, p->primitives.gradients.rho[0], p->primitives.gradients.rho[1], p->primitives.gradients.rho[2], p->primitives.gradients.v[0][0], p->primitives.gradients.v[0][1], p->primitives.gradients.v[0][2], p->primitives.gradients.v[1][0], diff --git a/src/hydro/Shadowswift/hydro_iact.h b/src/hydro/Shadowswift/hydro_iact.h index eda8e3759d9e08dac8073ebed9fb36dd0c5b99f6..791e4c7924df9806fa9150d03c08a543771a7049 100644 --- a/src/hydro/Shadowswift/hydro_iact.h +++ b/src/hydro/Shadowswift/hydro_iact.h @@ -342,3 +342,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H); } + +/** + * @brief Timestep limiter loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Nothing to do here if both particles are active */ +} + +/** + * @brief Timestep limiter loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + /* Wake up the neighbour? */ + if (pi->timestepvars.vmax > + const_limiter_max_v_sig_ratio * pj->timestepvars.vmax) { + + pj->wakeup = time_bin_awake; + } +} diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h index a7cc9daf0839216f098ac05c2267adc60ea11fb0..d229b5c9d63ecfa855397f74f8a52a4117cefc03 100644 --- a/src/hydro/Shadowswift/hydro_part.h +++ b/src/hydro/Shadowswift/hydro_part.h @@ -21,6 +21,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "tracers_struct.h" #include "voronoi_cell.h" /* Extra particle data not needed during the computation. */ @@ -41,6 +42,9 @@ struct xpart { /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; + /* Additional data used by the tracers */ + struct tracers_xpart_data tracers_data; + } SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ @@ -179,6 +183,9 @@ struct part { /* Time-step length */ timebin_t time_bin; + /* Need waking-up ? */ + timebin_t wakeup; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/hydro_io.h b/src/hydro_io.h index 1a2d6319b7caf6c09b9af406cbdd323f27607791..60e5593cc0630ef4bc33ab407f6a669b7de1def1 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -43,6 +43,8 @@ #include "./hydro/Shadowswift/hydro_io.h" #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_io.h" +#elif defined(ANARCHY_PU_SPH) +#include "./hydro/AnarchyPU/hydro_io.h" #else #error "Invalid choice of SPH variant" #endif diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 2b1cd42055c66768e943241c75298e53e0bf75a8..f14c88bfb5128c1da17590f50698e5d038734b71 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -30,23 +30,45 @@ #include "dimension.h" #include "equation_of_state.h" #include "error.h" +#include "gravity_properties.h" #include "hydro.h" #include "kernel_hydro.h" +#include "parser.h" +#include "units.h" #define hydro_props_default_max_iterations 30 #define hydro_props_default_volume_change 1.4f #define hydro_props_default_h_max FLT_MAX +#define hydro_props_default_h_min_ratio 0.f #define hydro_props_default_h_tolerance 1e-4 #define hydro_props_default_init_temp 0.f #define hydro_props_default_min_temp 0.f #define hydro_props_default_H_ionization_temperature 1e4 #define hydro_props_default_viscosity_alpha 0.8f + +#ifdef ANARCHY_PU_SPH +/* This nasty #ifdef is only temporary until we separate the viscosity + * and hydro components. If it is not removed by July 2019, shout at JB. */ #define hydro_props_default_viscosity_alpha_min \ - 0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */ + 0.01f /* values taken from Schaller+ 2015 */ +#define hydro_props_default_viscosity_alpha_max \ + 2.0f /* values taken from Schaller+ 2015 */ +#define hydro_props_default_viscosity_length \ + 0.01f /* values taken from Schaller+ 2015 */ +#else +#define hydro_props_default_viscosity_alpha_min \ + 0.1f /* values taken from (price,2004), not used in legacy gadget mode */ #define hydro_props_default_viscosity_alpha_max \ - 2.0f /* Values taken from (Price,2004), not used in legacy gadget mode */ + 2.0f /* values taken from (price,2004), not used in legacy gadget mode */ #define hydro_props_default_viscosity_length \ 0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */ +#endif /* ANARCHY_PU_SPH */ + +/* Following values taken directly from the ANARCHY paper (Schaller+ 2015) */ +#define hydro_props_default_diffusion_alpha 0.0f +#define hydro_props_default_diffusion_beta 0.01f +#define hydro_props_default_diffusion_alpha_max 1.0f +#define hydro_props_default_diffusion_alpha_min 0.0f /** * @brief Initialize the global properties of the hydro scheme. @@ -86,10 +108,20 @@ void hydro_props_init(struct hydro_props *p, p->h_max = parser_get_opt_param_float(params, "SPH:h_max", hydro_props_default_h_max); + /* Minimal smoothing length ratio to softening */ + p->h_min_ratio = parser_get_opt_param_float(params, "SPH:h_min_ratio", + hydro_props_default_h_min_ratio); + + /* Temporarily set the minimal softening to 0. */ + p->h_min = 0.f; + /* Number of iterations to converge h */ p->max_smoothing_iterations = parser_get_opt_param_int( params, "SPH:max_ghost_iterations", hydro_props_default_max_iterations); + if (p->max_smoothing_iterations <= 10) + error("The number of smoothing length iterations should be > 10"); + /* Time integration properties */ p->CFL_condition = parser_get_param_float(params, "SPH:CFL_condition"); const float max_volume_change = parser_get_opt_param_float( @@ -119,6 +151,12 @@ void hydro_props_init(struct hydro_props *p, p->hydrogen_mass_fraction = parser_get_opt_param_double( params, "SPH:H_mass_fraction", default_H_fraction); + /* Mean molecular mass for neutral gas */ + p->mu_neutral = 4. / (1. + 3. * p->hydrogen_mass_fraction); + + /* Mean molecular mass for fully ionised gas */ + p->mu_ionised = 4. / (8. - 5. * (1. - p->hydrogen_mass_fraction)); + /* Read the artificial viscosity parameters from the file, if they exist */ p->viscosity.alpha = parser_get_opt_param_float( params, "SPH:viscosity_alpha", hydro_props_default_viscosity_alpha); @@ -134,6 +172,21 @@ void hydro_props_init(struct hydro_props *p, p->viscosity.length = parser_get_opt_param_float( params, "SPH:viscosity_length", hydro_props_default_viscosity_length); + /* Same for the thermal diffusion parameters */ + p->diffusion.alpha = parser_get_opt_param_float( + params, "SPH:diffusion_alpha", hydro_props_default_diffusion_alpha); + + p->diffusion.beta = parser_get_opt_param_float( + params, "SPH:diffusion_beta", hydro_props_default_diffusion_beta); + + p->diffusion.alpha_max = + parser_get_opt_param_float(params, "SPH:diffusion_alpha_max", + hydro_props_default_diffusion_alpha_max); + + p->diffusion.alpha_min = + parser_get_opt_param_float(params, "SPH:diffusion_alpha_min", + hydro_props_default_diffusion_alpha_min); + /* Compute the initial energy (Note the temp. read is in internal units) */ /* u_init = k_B T_init / (mu m_p (gamma - 1)) */ double u_init = phys_const->const_boltzmann_k / phys_const->const_proton_mass; @@ -233,7 +286,8 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { io_write_attribute_f(h_grpsph, "Kernel delta N_ngb", p->delta_neighbours); io_write_attribute_f(h_grpsph, "Kernel eta", p->eta_neighbours); io_write_attribute_f(h_grpsph, "Smoothing length tolerance", p->h_tolerance); - io_write_attribute_f(h_grpsph, "Maximal smoothing length", p->h_max); + io_write_attribute_f(h_grpsph, "Maximal smoothing length [internal units]", + p->h_max); io_write_attribute_f(h_grpsph, "CFL parameter", p->CFL_condition); io_write_attribute_f(h_grpsph, "Volume log(max(delta h))", p->log_max_h_change); @@ -242,8 +296,12 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { io_write_attribute_i(h_grpsph, "Max ghost iterations", p->max_smoothing_iterations); io_write_attribute_f(h_grpsph, "Minimal temperature", p->minimal_temperature); + io_write_attribute_f(h_grpsph, + "Minimal energy per unit mass [internal units]", + p->minimal_internal_energy); io_write_attribute_f(h_grpsph, "Initial temperature", p->initial_temperature); - io_write_attribute_f(h_grpsph, "Initial energy per unit mass", + io_write_attribute_f(h_grpsph, + "Initial energy per unit mass [internal units]", p->initial_internal_energy); io_write_attribute_f(h_grpsph, "Hydrogen mass fraction", p->hydrogen_mass_fraction); @@ -254,8 +312,11 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { p->viscosity.alpha_max); io_write_attribute_f(h_grpsph, "Alpha viscosity (min)", p->viscosity.alpha_min); - io_write_attribute_f(h_grpsph, "Viscosity decay length", p->viscosity.length); + io_write_attribute_f(h_grpsph, "Viscosity decay length [internal units]", + p->viscosity.length); io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); + io_write_attribute_f(h_grpsph, "Max v_sig ratio (limiter)", + const_limiter_max_v_sig_ratio); } #endif @@ -267,6 +328,7 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { * @param p the struct */ void hydro_props_init_no_hydro(struct hydro_props *p) { + p->eta_neighbours = 1.2348; p->h_tolerance = hydro_props_default_h_tolerance; p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm; @@ -275,6 +337,8 @@ void hydro_props_init_no_hydro(struct hydro_props *p) { (pow_dimension(delta_eta) - pow_dimension(p->eta_neighbours)) * kernel_norm; p->h_max = hydro_props_default_h_max; + p->h_min = 0.f; + p->h_min_ratio = hydro_props_default_h_min_ratio; p->max_smoothing_iterations = hydro_props_default_max_iterations; p->CFL_condition = 0.1; p->log_max_h_change = logf(powf(1.4, hydro_dimension_inv)); @@ -295,6 +359,25 @@ void hydro_props_init_no_hydro(struct hydro_props *p) { p->viscosity.alpha_max = hydro_props_default_viscosity_alpha_max; p->viscosity.alpha_min = hydro_props_default_viscosity_alpha_min; p->viscosity.length = hydro_props_default_viscosity_length; + + p->diffusion.alpha = hydro_props_default_diffusion_alpha; + p->diffusion.beta = hydro_props_default_diffusion_beta; + p->diffusion.alpha_max = hydro_props_default_diffusion_alpha_max; + p->diffusion.alpha_min = hydro_props_default_diffusion_alpha_min; +} + +/** + * @brief Update the global properties of the hydro scheme for that time-step. + * + * @param p The properties to update. + * @param gp The properties of the gravity scheme. + * @param cosmo The cosmological model. + */ +void hydro_props_update(struct hydro_props *p, const struct gravity_props *gp, + const struct cosmology *cosmo) { + + /* Update the minimal allowed smoothing length */ + p->h_min = p->h_min_ratio * gp->epsilon_cur; } /** diff --git a/src/hydro_properties.h b/src/hydro_properties.h index b45b93192e7db7b1bdca49557f8563322f09aae9..afc8a4b87aeb39f6bff1e61ae39edf391c856b1b 100644 --- a/src/hydro_properties.h +++ b/src/hydro_properties.h @@ -32,10 +32,14 @@ #endif /* Local includes. */ -#include "parser.h" -#include "physical_constants.h" #include "restart.h" -#include "units.h" + +/* Forward declarations */ +struct cosmology; +struct swift_params; +struct gravity_props; +struct phys_const; +struct unit_system; /** * @brief Contains all the constants and parameters of the hydro scheme @@ -57,6 +61,12 @@ struct hydro_props { /*! Maximal smoothing length */ float h_max; + /*! Minimal smoothing length expressed as ratio to softening length */ + float h_min_ratio; + + /*! Minimal smoothing length */ + float h_min; + /*! Maximal number of iterations to converge h */ int max_smoothing_iterations; @@ -84,6 +94,12 @@ struct hydro_props { /*! Temperature of the neutral to ionized transition of Hydrogen */ float hydrogen_ionization_temperature; + /*! Mean molecular weight below hydrogen ionization temperature */ + float mu_neutral; + + /*! Mean molecular weight above hydrogen ionization temperature */ + float mu_ionised; + /*! Artificial viscosity parameters */ struct { /*! For the fixed, simple case. Also used to set the initial AV @@ -99,6 +115,24 @@ struct hydro_props { /*! The decay length of the artificial viscosity (used in M&M, etc.) */ float length; } viscosity; + + /*! Thermal diffusion parameters */ + struct { + + /*! Initialisation value, or the case for constant thermal diffusion coeffs + */ + float alpha; + + /*! Tuning parameter for speed of ramp up/down */ + float beta; + + /*! Maximal value for alpha_diff */ + float alpha_max; + + /*! Minimal value for alpha_diff */ + float alpha_min; + + } diffusion; }; void hydro_props_print(const struct hydro_props *p); @@ -107,6 +141,9 @@ void hydro_props_init(struct hydro_props *p, const struct unit_system *us, struct swift_params *params); +void hydro_props_update(struct hydro_props *p, const struct gravity_props *gp, + const struct cosmology *cosmo); + #if defined(HAVE_HDF5) void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p); #endif diff --git a/src/io_properties.h b/src/io_properties.h index 9e948fc3991b0178d06fdd5d83fa900a98f84d2a..c45edb2641e374e2cfaec6c3251aff7d18f361d6 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -43,14 +43,23 @@ typedef void (*conversion_func_part_float)(const struct engine*, typedef void (*conversion_func_part_double)(const struct engine*, const struct part*, const struct xpart*, double*); +typedef void (*conversion_func_part_long_long)(const struct engine*, + const struct part*, + const struct xpart*, long long*); typedef void (*conversion_func_gpart_float)(const struct engine*, const struct gpart*, float*); typedef void (*conversion_func_gpart_double)(const struct engine*, const struct gpart*, double*); +typedef void (*conversion_func_gpart_long_long)(const struct engine*, + const struct gpart*, + long long*); typedef void (*conversion_func_spart_float)(const struct engine*, const struct spart*, float*); typedef void (*conversion_func_spart_double)(const struct engine*, const struct spart*, double*); +typedef void (*conversion_func_spart_long_long)(const struct engine*, + const struct spart*, + long long*); /** * @brief The properties of a given dataset for i/o @@ -79,6 +88,7 @@ struct io_props { char* start_temp_c; float* start_temp_f; double* start_temp_d; + long long* start_temp_l; /* Pointer to the engine */ const struct engine* e; @@ -98,14 +108,17 @@ struct io_props { /* Conversion function for part */ conversion_func_part_float convert_part_f; conversion_func_part_double convert_part_d; + conversion_func_part_long_long convert_part_l; /* Conversion function for gpart */ conversion_func_gpart_float convert_gpart_f; conversion_func_gpart_double convert_gpart_d; + conversion_func_gpart_long_long convert_gpart_l; /* Conversion function for spart */ conversion_func_spart_float convert_spart_f; conversion_func_spart_double convert_spart_d; + conversion_func_spart_long_long convert_spart_l; }; /** @@ -147,10 +160,13 @@ INLINE static struct io_props io_make_input_field_( r.conversion = 0; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -191,10 +207,13 @@ INLINE static struct io_props io_make_output_field_( r.conversion = 0; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -242,10 +261,13 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( r.conversion = 1; r.convert_part_f = functionPtr; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -285,10 +307,59 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = functionPtr; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; + + return r; +} + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param partSize The size in byte of the particle + * @param parts The particle array + * @param xparts The xparticle array + * @param functionPtr The function used to convert a particle to a double + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t partSize, + const struct part* parts, const struct xpart* xparts, + conversion_func_part_long_long functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = partSize; + r.parts = parts; + r.xparts = xparts; + r.gparts = NULL; + r.sparts = NULL; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = functionPtr; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -334,10 +405,13 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = functionPtr; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -375,10 +449,57 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = functionPtr; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; + + return r; +} + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param gpartSize The size in byte of the particle + * @param gparts The particle array + * @param functionPtr The function used to convert a g-particle to a double + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t gpartSize, + const struct gpart* gparts, conversion_func_gpart_long_long functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = gpartSize; + r.parts = NULL; + r.xparts = NULL; + r.gparts = gparts; + r.sparts = NULL; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = NULL; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = functionPtr; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -424,10 +545,13 @@ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = functionPtr; r.convert_spart_d = NULL; + r.convert_spart_l = NULL; return r; } @@ -465,10 +589,57 @@ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; + r.convert_part_l = NULL; r.convert_gpart_f = NULL; r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; r.convert_spart_f = NULL; r.convert_spart_d = functionPtr; + r.convert_spart_l = NULL; + + return r; +} + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param spartSize The size in byte of the particle + * @param sparts The particle array + * @param functionPtr The function used to convert a s-particle to a double + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t spartSize, + const struct spart* sparts, conversion_func_spart_long_long functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = spartSize; + r.parts = NULL; + r.xparts = NULL; + r.gparts = NULL; + r.sparts = sparts; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = NULL; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = functionPtr; return r; } diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h index aac06c19ce39c647ba7211f85ac0a849365d126f..3d5ec6ac84a77941739f5d3b57ed0340c831c061 100644 --- a/src/kernel_hydro.h +++ b/src/kernel_hydro.h @@ -124,20 +124,27 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)] #define kernel_name "Wendland C2" #define kernel_degree 5 /* Degree of the polynomial */ #define kernel_ivals 1 /* Number of branches */ +#if defined(HYDRO_DIMENSION_1D) +/* Wendland C* have different form in 1D than 2D/3D */ +#define kernel_gamma ((float)(1.620185)) +#define kernel_constant ((float)(5. / 4.)) +static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)] + __attribute__((aligned(16))) = { + 0.f, -3.f, 8.f, -6.f, 0.f, 1.f, /* 0 < u < 1 */ + 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; +#else #if defined(HYDRO_DIMENSION_3D) #define kernel_gamma ((float)(1.936492)) #define kernel_constant ((float)(21. * M_1_PI / 2.)) #elif defined(HYDRO_DIMENSION_2D) #define kernel_gamma ((float)(1.897367)) #define kernel_constant ((float)(7. * M_1_PI)) -#elif defined(HYDRO_DIMENSION_1D) -#error "Wendland C2 kernel not defined in 1D." #endif static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)] __attribute__((aligned(16))) = { 4.f, -15.f, 20.f, -10.f, 0.f, 1.f, /* 0 < u < 1 */ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; /* 1 < u */ - +#endif /* ------------------------------------------------------------------------- */ #elif defined(WENDLAND_C4_KERNEL) diff --git a/src/kick.h b/src/kick.h index e85c9de40d2084304bde108e6f5fa9c776fd3e8f..f2085bf1f427cf5f15ed0e8791ad1923f0b22bed 100644 --- a/src/kick.h +++ b/src/kick.h @@ -70,7 +70,8 @@ __attribute__((always_inline)) INLINE static void kick_gpart( * @param dt_kick_therm The kick time-step for changes in thermal state. * @param dt_kick_corr The kick time-step for the gizmo-mfv gravity correction. * @param cosmo The cosmological model. - * @param hydro_props The constants used in the scheme + * @param hydro_props The constants used in the scheme. + * @param entropy_floor_props Properties of the entropy floor. * @param ti_start The starting (integer) time of the kick (for debugging * checks). * @param ti_end The ending (integer) time of the kick (for debugging checks). @@ -79,14 +80,15 @@ __attribute__((always_inline)) INLINE static void kick_part( struct part *restrict p, struct xpart *restrict xp, double dt_kick_hydro, double dt_kick_grav, double dt_kick_therm, double dt_kick_corr, const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct entropy_floor_properties *entropy_floor_props, integertime_t ti_start, integertime_t ti_end) { #ifdef SWIFT_DEBUG_CHECKS if (p->ti_kick != ti_start) error( "particle has not been kicked to the current time p->ti_kick=%lld, " - "ti_start=%lld, ti_end=%lld id=%lld", - p->ti_kick, ti_start, ti_end, p->id); + "ti_start=%lld, ti_end=%lld id=%lld time_bin=%d wakeup=%d", + p->ti_kick, ti_start, ti_end, p->id, p->time_bin, p->wakeup); p->ti_kick = ti_end; #endif @@ -114,6 +116,13 @@ __attribute__((always_inline)) INLINE static void kick_part( hydro_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_hydro, dt_kick_corr, cosmo, hydro_props); if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav); + + /* Verify that the particle is not below the entropy floor */ + const float floor = entropy_floor(p, cosmo, entropy_floor_props); + if (hydro_get_physical_entropy(p, xp, cosmo) < floor) { + hydro_set_physical_entropy(p, xp, cosmo, floor); + hydro_set_physical_internal_energy_dt(p, cosmo, 0.f); + } } /** diff --git a/src/lock.h b/src/lock.h index b2dd2eac9d0ca5d7807907e31cf3fa31894f9aed..39601b0c52e414dad1a507b406c54640a254df30 100644 --- a/src/lock.h +++ b/src/lock.h @@ -34,6 +34,7 @@ #define lock_trylock(l) (pthread_spin_lock(l) != 0) #define lock_unlock(l) (pthread_spin_unlock(l) != 0) #define lock_unlock_blind(l) pthread_spin_unlock(l) +#define lock_static_initializer ((pthread_spinlock_t)0) #elif defined(PTHREAD_LOCK) #include <pthread.h> @@ -44,6 +45,7 @@ #define lock_trylock(l) (pthread_mutex_trylock(l) != 0) #define lock_unlock(l) (pthread_mutex_unlock(l) != 0) #define lock_unlock_blind(l) pthread_mutex_unlock(l) +#define lock_static_initializer PTHREAD_MUTEX_INITIALIZER #else #define swift_lock_type volatile int @@ -52,12 +54,12 @@ INLINE static int lock_lock(volatile int *l) { while (atomic_cas(l, 0, 1) != 0) ; - // while( *l ); return 0; } #define lock_trylock(l) ((*(l)) ? 1 : atomic_cas(l, 0, 1)) #define lock_unlock(l) (atomic_cas(l, 1, 0) != 1) #define lock_unlock_blind(l) atomic_cas(l, 1, 0) +#define lock_static_initializer 0 #endif #endif /* SWIFT_LOCK_H */ diff --git a/src/logger.c b/src/logger.c index 2f4b0593dac039db96375afcee258a25dd871549..8be521b27f949ea0d496a5207335f1ec68208489 100644 --- a/src/logger.c +++ b/src/logger.c @@ -25,6 +25,7 @@ /* Some standard headers. */ #include <hdf5.h> +#include <math.h> #include <stdint.h> #include <stdlib.h> #include <string.h> @@ -40,28 +41,49 @@ #include "part.h" #include "units.h" -/* header constants +/* * Thoses are definitions from the format and therefore should not be changed! - * Size in bytes */ -/* size of a mask */ +/* number of bytes for a mask */ +// TODO change this to number of bits #define logger_mask_size 1 -/* size of an offset */ -#define logger_offset_size 7 +/* number of bits for chunk header */ +#define logger_header_bytes 8 -/* size of the version information */ +/* number bytes for an offset */ +#define logger_offset_size logger_header_bytes - logger_mask_size + +/* number of bytes for the version information */ #define logger_version_size 20 -/* size of the size information */ -#define logger_header_number_size 2 +/* number of bytes for the labels in the header */ +#define logger_label_size 20 + +/* number of bytes for the number in the header */ +#define logger_number_size 4 char logger_version[logger_version_size] = "0.1"; -const unsigned int logger_datatype_size[logger_data_count] = { - sizeof(int), sizeof(float), sizeof(double), - sizeof(char), sizeof(long long), 1, -}; +const struct mask_data logger_mask_data[logger_count_mask] = { + /* Particle's position */ + {3 * sizeof(double), 1 << logger_x, "positions"}, + /* Particle's velocity */ + {3 * sizeof(float), 1 << logger_v, "velocities"}, + /* Particle's acceleration */ + {3 * sizeof(float), 1 << logger_a, "accelerations"}, + /* Particle's entropy */ + {sizeof(float), 1 << logger_u, "entropy"}, + /* Particle's smoothing length */ + {sizeof(float), 1 << logger_h, "smoothing length"}, + /* Particle's density */ + {sizeof(float), 1 << logger_rho, "density"}, + /* Particle's constants: mass (float) and ID (long long) */ + {sizeof(float) + sizeof(long long), 1 << logger_consts, "consts"}, + /* Simulation time stamp: integertime and double time (e.g. scale + factor or time) */ + {sizeof(integertime_t) + sizeof(double), 1 << logger_timestamp, + "timestamp"}}; /** * @brief Write the header of a chunk (offset + mask). @@ -104,39 +126,8 @@ void logger_write_data(struct dump *d, size_t *offset, size_t size, /* write data to the buffer */ memcpy(buff, p, size); -} - -/** - * @brief Write a parameter to the file - * - * TODO Make it thread safe or remove it. - * - * write data in the following order: name, data type, data. - * It should be used only for the file header. - * - * @param d #dump file - * @param params #logger_parameters file format informations - * @param offset (return) offset of the next chunk - * @param p pointer to the data - * @param name Label of the parameter (should be smaller than log->name) - * @param data_type #logger_datatype to write - */ -void logger_write_general_data(struct dump *d, - const struct logger_parameters *params, - size_t *offset, const void *p, char *name, - size_t data_type) { - /* write name */ - logger_write_data(d, offset, params->label_size, name); - - /* write data type */ - logger_write_data(d, offset, params->data_type_size, &data_type); - - /* write value */ - if (data_type >= logger_data_count) error("Not implemented"); - size_t size = logger_datatype_size[data_type]; - - logger_write_data(d, offset, size, p); + /* Update offset to end of chunk */ *offset += size; } @@ -153,40 +144,21 @@ int logger_compute_chunk_size(unsigned int mask) { int size = logger_mask_size + logger_offset_size; /* Is this a particle or a timestep? */ - if (mask & logger_mask_timestamp) { + if (mask & logger_mask_data[logger_timestamp].mask) { /* The timestamp should not contain any other bits. */ - if (mask != logger_mask_timestamp) + if (mask != logger_mask_data[logger_timestamp].mask) error("Timestamps should not include any other data."); /* A timestamp consists of an unsigned long long int. */ - size += sizeof(unsigned long long int); - size += sizeof(double); + size += logger_mask_data[logger_timestamp].size; } else { - /* Particle position as three doubles. */ - if (mask & logger_mask_x) size += 3 * sizeof(double); - - /* Particle velocity as three floats. */ - if (mask & logger_mask_v) size += 3 * sizeof(float); - - /* Particle accelleration as three floats. */ - if (mask & logger_mask_a) size += 3 * sizeof(float); - - /* Particle internal energy as a single float. */ - if (mask & logger_mask_u) size += sizeof(float); - - /* Particle smoothing length as a single float. */ - if (mask & logger_mask_h) size += sizeof(float); - - /* Particle density as a single float. */ - if (mask & logger_mask_rho) size += sizeof(float); - - /* Particle constants, which is a bit more complicated. */ - if (mask & logger_mask_rho) { - size += sizeof(float) + // mass - sizeof(long long); // id + for (int i = 0; i < logger_count_mask; i++) { + if (mask & logger_mask_data[i].mask) { + size += logger_mask_data[i].size; + } } } @@ -209,9 +181,11 @@ void logger_log_all(struct logger *log, const struct engine *e) { /* some constants */ const struct space *s = e->s; - const unsigned int mask = logger_mask_x | logger_mask_v | logger_mask_a | - logger_mask_u | logger_mask_h | logger_mask_rho | - logger_mask_consts; + const unsigned int mask = + logger_mask_data[logger_x].mask | logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | logger_mask_data[logger_u].mask | + logger_mask_data[logger_h].mask | logger_mask_data[logger_rho].mask | + logger_mask_data[logger_consts].mask; /* loop over all parts */ for (long long i = 0; i < e->total_nr_parts; i++) { @@ -240,7 +214,7 @@ void logger_log_part(struct logger *log, const struct part *p, unsigned int mask, size_t *offset) { /* Make sure we're not writing a timestamp. */ - if (mask & logger_mask_timestamp) + if (mask & logger_mask_data[logger_timestamp].mask) error("You should not log particles as timestamps."); /* Start by computing the size of the message. */ @@ -248,51 +222,52 @@ void logger_log_part(struct logger *log, const struct part *p, /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; - char *buff = (char *)dump_get(log->dump, size, &offset_new); + char *buff = (char *)dump_get(&log->dump, size, &offset_new); /* Write the header. */ buff = logger_write_chunk_header(buff, &mask, offset, offset_new); /* Particle position as three doubles. */ - if (mask & logger_mask_x) { - memcpy(buff, p->x, 3 * sizeof(double)); - buff += 3 * sizeof(double); + if (mask & logger_mask_data[logger_x].mask) { + memcpy(buff, p->x, logger_mask_data[logger_x].size); + buff += logger_mask_data[logger_x].size; } /* Particle velocity as three floats. */ - if (mask & logger_mask_v) { - memcpy(buff, p->v, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_v].mask) { + memcpy(buff, p->v, logger_mask_data[logger_v].size); + buff += logger_mask_data[logger_v].size; } /* Particle accelleration as three floats. */ - if (mask & logger_mask_a) { - memcpy(buff, p->a_hydro, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_a].mask) { + memcpy(buff, p->a_hydro, logger_mask_data[logger_a].size); + buff += logger_mask_data[logger_a].size; } #if defined(GADGET2_SPH) /* Particle internal energy as a single float. */ - if (mask & logger_mask_u) { - memcpy(buff, &p->entropy, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_u].mask) { + memcpy(buff, &p->entropy, logger_mask_data[logger_u].size); + buff += logger_mask_data[logger_u].size; } /* Particle smoothing length as a single float. */ - if (mask & logger_mask_h) { - memcpy(buff, &p->h, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_h].mask) { + memcpy(buff, &p->h, logger_mask_data[logger_h].size); + buff += logger_mask_data[logger_h].size; } /* Particle density as a single float. */ - if (mask & logger_mask_rho) { - memcpy(buff, &p->rho, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_rho].mask) { + memcpy(buff, &p->rho, logger_mask_data[logger_rho].size); + buff += logger_mask_data[logger_rho].size; } /* Particle constants, which is a bit more complicated. */ - if (mask & logger_mask_consts) { + if (mask & logger_mask_data[logger_consts].mask) { + // TODO make it dependent of logger_mask_data memcpy(buff, &p->mass, sizeof(float)); buff += sizeof(float); memcpy(buff, &p->id, sizeof(long long)); @@ -318,11 +293,12 @@ void logger_log_gpart(struct logger *log, const struct gpart *p, unsigned int mask, size_t *offset) { /* Make sure we're not writing a timestamp. */ - if (mask & logger_mask_timestamp) + if (mask & logger_mask_data[logger_timestamp].mask) error("You should not log particles as timestamps."); /* Make sure we're not looging fields not supported by gparts. */ - if (mask & (logger_mask_u | logger_mask_rho)) + if (mask & + (logger_mask_data[logger_u].mask | logger_mask_data[logger_rho].mask)) error("Can't log SPH quantities for gparts."); /* Start by computing the size of the message. */ @@ -330,31 +306,32 @@ void logger_log_gpart(struct logger *log, const struct gpart *p, /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; - char *buff = (char *)dump_get(log->dump, size, &offset_new); + char *buff = (char *)dump_get(&log->dump, size, &offset_new); /* Write the header. */ buff = logger_write_chunk_header(buff, &mask, offset, offset_new); /* Particle position as three doubles. */ - if (mask & logger_mask_x) { - memcpy(buff, p->x, 3 * sizeof(double)); - buff += 3 * sizeof(double); + if (mask & logger_mask_data[logger_x].mask) { + memcpy(buff, p->x, logger_mask_data[logger_x].size); + buff += logger_mask_data[logger_x].size; } /* Particle velocity as three floats. */ - if (mask & logger_mask_v) { - memcpy(buff, p->v_full, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_v].mask) { + memcpy(buff, p->v_full, logger_mask_data[logger_v].size); + buff += logger_mask_data[logger_v].size; } /* Particle accelleration as three floats. */ - if (mask & logger_mask_a) { - memcpy(buff, p->a_grav, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_a].mask) { + memcpy(buff, p->a_grav, logger_mask_data[logger_a].size); + buff += logger_mask_data[logger_a].size; } /* Particle constants, which is a bit more complicated. */ - if (mask & logger_mask_consts) { + if (mask & logger_mask_data[logger_consts].mask) { + // TODO make it dependent of logger_mask_data memcpy(buff, &p->mass, sizeof(float)); buff += sizeof(float); memcpy(buff, &p->id_or_neg_offset, sizeof(long long)); @@ -376,20 +353,22 @@ void logger_log_gpart(struct logger *log, const struct gpart *p, */ void logger_log_timestamp(struct logger *log, integertime_t timestamp, double time, size_t *offset) { - struct dump *dump = log->dump; + struct dump *dump = &log->dump; /* Start by computing the size of the message. */ - const int size = logger_compute_chunk_size(logger_mask_timestamp); + const int size = + logger_compute_chunk_size(logger_mask_data[logger_timestamp].mask); /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; char *buff = (char *)dump_get(dump, size, &offset_new); /* Write the header. */ - unsigned int mask = logger_mask_timestamp; + unsigned int mask = logger_mask_data[logger_timestamp].mask; buff = logger_write_chunk_header(buff, &mask, offset, offset_new); /* Store the timestamp. */ + // TODO make it dependent of logger_mask_data memcpy(buff, ×tamp, sizeof(integertime_t)); buff += sizeof(integertime_t); @@ -414,10 +393,8 @@ void logger_log_timestamp(struct logger *log, integertime_t timestamp, void logger_ensure_size(struct logger *log, size_t total_nr_parts, size_t total_nr_gparts, size_t total_nr_sparts) { - struct logger_parameters *log_params = log->params; - /* count part memory */ - size_t limit = log_params->total_size; + size_t limit = log->max_chunk_size; limit *= total_nr_parts; @@ -428,7 +405,7 @@ void logger_ensure_size(struct logger *log, size_t total_nr_parts, if (total_nr_sparts > 0) error("Not implemented"); /* ensure enough space in dump */ - dump_ensure(log->dump, limit, log->buffer_scale * limit); + dump_ensure(&log->dump, limit, log->buffer_scale * limit); } /** @@ -455,16 +432,17 @@ void logger_init(struct logger *log, struct swift_params *params) { strcpy(logger_name_file, log->base_name); strcat(logger_name_file, ".dump"); - /* init parameters */ - log->params = - (struct logger_parameters *)malloc(sizeof(struct logger_parameters)); - logger_parameters_init(log->params); + /* Compute max size for a particle chunk */ + int max_size = logger_offset_size + logger_mask_size; - /* init dump */ - log->dump = malloc(sizeof(struct dump)); - struct dump *dump_file = log->dump; + /* Loop over all fields except timestamp */ + for (int i = 0; i < logger_count_mask - 1; i++) { + max_size += logger_mask_data[i].size; + } + log->max_chunk_size = max_size; - dump_init(dump_file, logger_name_file, buffer_size); + /* init dump */ + dump_init(&log->dump, logger_name_file, buffer_size); } /** @@ -472,11 +450,7 @@ void logger_init(struct logger *log, struct swift_params *params) { * * @param log The #logger */ -void logger_clean(struct logger *log) { - dump_close(log->dump); - logger_parameters_clean(log->params); - free(log->params); -} +void logger_clean(struct logger *log) { dump_close(&log->dump); } /** * @brief Write a file header to a logger file @@ -488,8 +462,7 @@ void logger_clean(struct logger *log) { void logger_write_file_header(struct logger *log, const struct engine *e) { /* get required variables */ - const struct logger_parameters log_params = *log->params; - struct dump *dump = log->dump; + struct dump *dump = &log->dump; size_t file_offset = dump->file_offset; @@ -501,167 +474,35 @@ void logger_write_file_header(struct logger *log, const struct engine *e) { /* Write version information */ logger_write_data(dump, &file_offset, logger_version_size, &logger_version); - /* write number of bytes used for the offsets */ - logger_write_data(dump, &file_offset, logger_header_number_size, - &log_params.offset_size); - /* write offset direction */ - int reversed = 0; - logger_write_data(dump, &file_offset, logger_datatype_size[logger_data_bool], - &reversed); + const int reversed = 0; + logger_write_data(dump, &file_offset, logger_number_size, &reversed); /* placeholder to write the offset of the first log here */ - char *skip_header = dump_get(dump, log_params.offset_size, &file_offset); + char *skip_header = dump_get(dump, logger_offset_size, &file_offset); /* write number of bytes used for names */ - logger_write_data(dump, &file_offset, logger_header_number_size, - &log_params.label_size); - - /* write number of bytes used for numbers */ - logger_write_data(dump, &file_offset, logger_header_number_size, - &log_params.number_size); - - /* write number of bytes used for masks */ - logger_write_data(dump, &file_offset, logger_header_number_size, - &log_params.mask_size); + const int label_size = logger_label_size; + logger_write_data(dump, &file_offset, logger_number_size, &label_size); /* write number of masks */ - logger_write_data(dump, &file_offset, log_params.number_size, - &log_params.number_mask); + int count_mask = logger_count_mask; + logger_write_data(dump, &file_offset, logger_number_size, &count_mask); /* write masks */ // loop over all mask type - for (size_t i = 0; i < log_params.number_mask; i++) { + for (int i = 0; i < logger_count_mask; i++) { // mask name - size_t j = i * log_params.label_size; - logger_write_data(dump, &file_offset, log_params.label_size, - &log_params.masks_name[j]); - - // mask - logger_write_data(dump, &file_offset, log_params.mask_size, - &log_params.masks[i]); + logger_write_data(dump, &file_offset, logger_label_size, + &logger_mask_data[i].name); // mask size - logger_write_data(dump, &file_offset, log_params.number_size, - &log_params.masks_data_size[i]); + logger_write_data(dump, &file_offset, logger_number_size, + &logger_mask_data[i].size); } - /* write mask data */ - // TODO - /* loop over each mask and each data in this mask */ - /* write number of bytes for each field */ - /* write data type (float, double, ...) */ - /* write data name (mass, id, ...) */ - - /* Write data */ - char *name = malloc(sizeof(char) * log_params.label_size); - strcpy(name, "time_base"); - logger_write_general_data(dump, &log_params, &file_offset, &e->time_base, - name, logger_data_double); - /* last step: write first offset */ - memcpy(skip_header, &file_offset, log_params.offset_size); - - /* free memory */ - free(name); -} - -/** - * @brief initialize the #logger_parameters with the format informations - * - * @param log_params #logger_parameters to initialize - */ -void logger_parameters_init(struct logger_parameters *log_params) { - /* set parameters */ - log_params->label_size = 20; - log_params->offset_size = 7; - log_params->mask_size = 1; - log_params->number_size = 1; - log_params->data_type_size = 1; - - log_params->number_mask = 8; - - /* set masks array */ - log_params->masks = malloc(sizeof(size_t) * log_params->number_mask); - log_params->masks[0] = logger_mask_x; - log_params->masks[1] = logger_mask_v; - log_params->masks[2] = logger_mask_a; - log_params->masks[3] = logger_mask_u; - log_params->masks[4] = logger_mask_h; - log_params->masks[5] = logger_mask_rho; - log_params->masks[6] = logger_mask_consts; - log_params->masks[7] = logger_mask_timestamp; - - /* set the mask names */ - size_t block_size = log_params->label_size * log_params->number_mask; - log_params->masks_name = malloc(block_size); - char *cur_name = log_params->masks_name; - - char tmp[log_params->label_size]; - strcpy(tmp, "position"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "velocity"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "acceleration"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "entropy"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "cutoff radius"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "density"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "consts"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - strcpy(tmp, "timestamp"); - memcpy(cur_name, &tmp, log_params->label_size); - cur_name += log_params->label_size; - - /* set the data size */ - log_params->masks_data_size = - malloc(sizeof(size_t) * log_params->number_mask); - log_params->masks_data_size[0] = 3 * sizeof(double); - log_params->masks_data_size[1] = 3 * sizeof(float); - log_params->masks_data_size[2] = 3 * sizeof(float); - log_params->masks_data_size[3] = sizeof(float); - log_params->masks_data_size[4] = sizeof(float); - log_params->masks_data_size[5] = sizeof(float); - log_params->masks_data_size[6] = sizeof(float) + sizeof(long long); - log_params->masks_data_size[7] = sizeof(integertime_t) + sizeof(double); - - /* Compute the size of a chunk if all the mask are activated */ - log_params->total_size = logger_offset_size + logger_mask_size; - - for (size_t i = 0; i < log_params->number_mask; i++) { - if (log_params->masks[i] != logger_mask_timestamp) - log_params->total_size += log_params->masks_data_size[i]; - } - - // todo masks_type -} - -/** - * @brief Clean the #logger_parameters - * - * @param log_params The #logger_parameters - */ -void logger_parameters_clean(struct logger_parameters *log_params) { - free(log_params->masks); - free(log_params->masks_name); - free(log_params->masks_data_size); + memcpy(skip_header, &file_offset, logger_offset_size); } /** @@ -707,49 +548,50 @@ int logger_read_part(struct part *p, size_t *offset, const char *buff) { buff += logger_read_chunk_header(buff, &mask, offset, cur_offset); /* We are only interested in particle data. */ - if (mask & logger_mask_timestamp) + if (mask & logger_mask_data[logger_timestamp].mask) error("Trying to read timestamp as particle."); /* Particle position as three doubles. */ - if (mask & logger_mask_x) { - memcpy(p->x, buff, 3 * sizeof(double)); - buff += 3 * sizeof(double); + if (mask & logger_mask_data[logger_x].mask) { + memcpy(p->x, buff, logger_mask_data[logger_x].size); + buff += logger_mask_data[logger_x].size; } /* Particle velocity as three floats. */ - if (mask & logger_mask_v) { - memcpy(p->v, buff, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_v].mask) { + memcpy(p->v, buff, logger_mask_data[logger_v].size); + buff += logger_mask_data[logger_v].size; } /* Particle accelleration as three floats. */ - if (mask & logger_mask_a) { - memcpy(p->a_hydro, buff, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_a].mask) { + memcpy(p->a_hydro, buff, logger_mask_data[logger_a].size); + buff += logger_mask_data[logger_a].size; } #if defined(GADGET2_SPH) /* Particle internal energy as a single float. */ - if (mask & logger_mask_u) { - memcpy(&p->entropy, buff, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_u].mask) { + memcpy(&p->entropy, buff, logger_mask_data[logger_u].size); + buff += logger_mask_data[logger_u].size; } /* Particle smoothing length as a single float. */ - if (mask & logger_mask_h) { - memcpy(&p->h, buff, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_h].mask) { + memcpy(&p->h, buff, logger_mask_data[logger_h].size); + buff += logger_mask_data[logger_h].size; } /* Particle density as a single float. */ - if (mask & logger_mask_rho) { - memcpy(&p->rho, buff, sizeof(float)); - buff += sizeof(float); + if (mask & logger_mask_data[logger_rho].mask) { + memcpy(&p->rho, buff, logger_mask_data[logger_rho].size); + buff += logger_mask_data[logger_rho].size; } /* Particle constants, which is a bit more complicated. */ - if (mask & logger_mask_rho) { + if (mask & logger_mask_data[logger_rho].mask) { + // TODO make it dependent of logger_mask_data memcpy(&p->mass, buff, sizeof(float)); buff += sizeof(float); memcpy(&p->id, buff, sizeof(long long)); @@ -783,33 +625,35 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff) { buff += logger_read_chunk_header(buff, &mask, offset, cur_offset); /* We are only interested in particle data. */ - if (mask & logger_mask_timestamp) + if (mask & logger_mask_data[logger_timestamp].mask) error("Trying to read timestamp as particle."); /* We can't store all part fields in a gpart. */ - if (mask & (logger_mask_u | logger_mask_rho)) + if (mask & + (logger_mask_data[logger_u].mask | logger_mask_data[logger_rho].mask)) error("Trying to read SPH quantities into a gpart."); /* Particle position as three doubles. */ - if (mask & logger_mask_x) { - memcpy(p->x, buff, 3 * sizeof(double)); - buff += 3 * sizeof(double); + if (mask & logger_mask_data[logger_x].mask) { + memcpy(p->x, buff, logger_mask_data[logger_x].size); + buff += logger_mask_data[logger_x].size; } /* Particle velocity as three floats. */ - if (mask & logger_mask_v) { - memcpy(p->v_full, buff, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_v].mask) { + memcpy(p->v_full, buff, logger_mask_data[logger_v].size); + buff += logger_mask_data[logger_v].size; } /* Particle accelleration as three floats. */ - if (mask & logger_mask_a) { - memcpy(p->a_grav, buff, 3 * sizeof(float)); - buff += 3 * sizeof(float); + if (mask & logger_mask_data[logger_a].mask) { + memcpy(p->a_grav, buff, logger_mask_data[logger_a].size); + buff += logger_mask_data[logger_a].size; } /* Particle constants, which is a bit more complicated. */ - if (mask & logger_mask_rho) { + if (mask & logger_mask_data[logger_rho].mask) { + // TODO make it dependent of logger_mask_data memcpy(&p->mass, buff, sizeof(float)); buff += sizeof(float); memcpy(&p->id_or_neg_offset, buff, sizeof(long long)); @@ -842,14 +686,15 @@ int logger_read_timestamp(unsigned long long int *t, double *time, buff += logger_read_chunk_header(buff, &mask, offset, cur_offset); /* We are only interested in timestamps. */ - if (!(mask & logger_mask_timestamp)) + if (!(mask & logger_mask_data[logger_timestamp].mask)) error("Trying to read timestamp from a particle."); /* Make sure we don't have extra fields. */ - if (mask != logger_mask_timestamp) + if (mask != logger_mask_data[logger_timestamp].mask) error("Timestamp message contains extra fields."); /* Copy the timestamp value from the buffer. */ + // TODO make it dependent of logger_mask_data memcpy(t, buff, sizeof(unsigned long long int)); buff += sizeof(unsigned long long int); diff --git a/src/logger.h b/src/logger.h index 3ac5291eaab1f0e4fc05640cc23e1705a7178c9a..56e2c8ab94c66b24df1800877bb9cfb129c3e645 100644 --- a/src/logger.h +++ b/src/logger.h @@ -23,6 +23,7 @@ /* Includes. */ #include "common_io.h" +#include "dump.h" #include "inline.h" #include "timeline.h" #include "units.h" @@ -73,53 +74,32 @@ struct engine; */ /* Some constants. */ -enum logger_masks { - logger_mask_x = (1 << 0), - logger_mask_v = (1 << 1), - logger_mask_a = (1 << 2), - logger_mask_u = (1 << 3), - logger_mask_h = (1 << 4), - logger_mask_rho = (1 << 5), - logger_mask_consts = (1 << 6), - logger_mask_timestamp = (1 << 7), +enum logger_masks_number { + logger_x = 0, + logger_v = 1, + logger_a = 2, + logger_u = 3, + logger_h = 4, + logger_rho = 5, + logger_consts = 6, + logger_timestamp = 7, /* expect it to be before count */ + logger_count_mask = 8, /* Need to be the last */ +} __attribute__((packed)); + +struct mask_data { + /* Number of bytes for a mask */ + int size; + /* Mask value */ + unsigned int mask; + /* name of the mask */ + char name[100]; }; +extern const struct mask_data logger_mask_data[logger_count_mask]; + /* Size of the strings. */ #define logger_string_length 200 -/* parameters of the logger */ -struct logger_parameters { - /* size of a label in bytes */ - size_t label_size; - - /* size of an offset in bytes */ - size_t offset_size; - - /* size of a mask in bytes */ - size_t mask_size; - - /* size of a number in bytes */ - size_t number_size; - - /* size of a data type in bytes */ - size_t data_type_size; - - /* number of different mask */ - size_t number_mask; - - /* value of each masks */ - size_t *masks; - - /* data size of each mask */ - size_t *masks_data_size; - - /* label of each mask */ - char *masks_name; - - /* Size of a chunk if every mask are activated */ - size_t total_size; -}; - /* structure containing global data */ struct logger { /* Number of particle steps between dumping a chunk of data */ @@ -128,8 +108,8 @@ struct logger { /* Logger basename */ char base_name[logger_string_length]; - /* File name of the dump file */ - struct dump *dump; + /* Dump file */ + struct dump dump; /* timestamp offset for logger*/ size_t timestamp_offset; @@ -137,8 +117,8 @@ struct logger { /* scaling factor when buffer is too small */ float buffer_scale; - /* logger parameters */ - struct logger_parameters *params; + /* Size of a chunk if every mask are activated */ + int max_chunk_size; } SWIFT_STRUCT_ALIGN; @@ -151,18 +131,6 @@ struct logger_part_data { size_t last_offset; }; -enum logger_datatype { - logger_data_int, - logger_data_float, - logger_data_double, - logger_data_char, - logger_data_longlong, - logger_data_bool, - logger_data_count /* should be last */ -}; - -extern const unsigned int logger_datatype_size[]; - /* Function prototypes. */ int logger_compute_chunk_size(unsigned int mask); void logger_log_all(struct logger *log, const struct engine *e); @@ -183,9 +151,6 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff); int logger_read_timestamp(unsigned long long int *t, double *time, size_t *offset, const char *buff); -void logger_parameters_init(struct logger_parameters *log_params); -void logger_parameters_clean(struct logger_parameters *log_params); - /** * @brief Initialize the logger data for a particle. * @@ -193,7 +158,7 @@ void logger_parameters_clean(struct logger_parameters *log_params); */ INLINE static void logger_part_data_init(struct logger_part_data *logger) { logger->last_offset = 0; - logger->steps_since_last_output = SHRT_MAX; + logger->steps_since_last_output = INT_MAX; } /** diff --git a/src/logger_io.c b/src/logger_io.c index a0a5ba1db85aa4eb96ee140966a47393ba5a3b68..3cef3497b2912411cea6763f5418bc76a7f5ece0 100644 --- a/src/logger_io.c +++ b/src/logger_io.c @@ -78,7 +78,7 @@ void write_index_single(struct engine* e, const char* baseName, const size_t Ngas = e->s->nr_parts; const size_t Nstars = e->s->nr_sparts; const size_t Ntot = e->s->nr_gparts; - int periodic = e->s->periodic; + const int periodic = e->s->periodic; int numFiles = 1; struct part* parts = e->s->parts; struct xpart* xparts = e->s->xparts; diff --git a/src/memswap.h b/src/memswap.h index 2f7b9215ed48535fab9e8331303457c2f92859cd..330173100f41b80fcc65c9fce01838b5de8e778f 100644 --- a/src/memswap.h +++ b/src/memswap.h @@ -1,7 +1,7 @@ /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk) - * + * 2018 STFC (author email aidan.chalk@stfc.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 @@ -20,6 +20,7 @@ #define SWIFT_MEMSWAP_H /* Config parameters. */ +#include <stdint.h> #include "../config.h" #ifdef HAVE_IMMINTRIN_H @@ -33,7 +34,7 @@ #endif /* Macro for in-place swap of two values a and b of type t. a and b are - assumed to be of type char* so that the pointer arithmetic works. */ + assumed to be of type uint8_t* so that the pointer arithmetic works. */ #define swap_loop(type, a, b, count) \ while (count >= sizeof(type)) { \ register type temp = *(type *)a; \ @@ -60,9 +61,10 @@ * @param void_b Pointer to the second element. * @param bytes Size, in bytes, of the data pointed to by @c a and @c b. */ -__attribute__((always_inline)) inline void memswap(void *void_a, void *void_b, +__attribute__((always_inline)) inline void memswap(void *restrict void_a, + void *restrict void_b, size_t bytes) { - char *a = (char *)void_a, *b = (char *)void_b; + int8_t *restrict a = (int8_t *)void_a, *restrict b = (int8_t *)void_b; #if defined(__AVX512F__) && defined(__INTEL_COMPILER) swap_loop(__m512i, a, b, bytes); #endif @@ -75,10 +77,17 @@ __attribute__((always_inline)) inline void memswap(void *void_a, void *void_b, #ifdef __ALTIVEC__ swap_loop(vector int, a, b, bytes); #endif - swap_loop(size_t, a, b, bytes); - swap_loop(int, a, b, bytes); - swap_loop(short, a, b, bytes); - swap_loop(char, a, b, bytes); + swap_loop(int_least64_t, a, b, bytes); + swap_loop(int_least32_t, a, b, bytes); + swap_loop(int_least16_t, a, b, bytes); + swap_loop(int_least8_t, a, b, bytes); + + /* This is a known bug for the current version of clang on ARM. + * We add this synchronization as a temporary bug fix. + * See https://bugs.llvm.org/show_bug.cgi?id=40051 */ +#if defined(__clang__) && defined(__aarch64__) + __sync_synchronize(); +#endif } /** @@ -93,10 +102,9 @@ __attribute__((always_inline)) inline void memswap(void *void_a, void *void_b, * @param void_b Pointer to the second element. * @param bytes Size, in bytes, of the data pointed to by @c a and @c b. */ -__attribute__((always_inline)) inline void memswap_unaligned(void *void_a, - void *void_b, - size_t bytes) { - char *a = (char *)void_a, *b = (char *)void_b; +__attribute__((always_inline)) inline void memswap_unaligned( + void *restrict void_a, void *restrict void_b, size_t bytes) { + int8_t *restrict a = (int8_t *)void_a, *restrict b = (int8_t *)void_b; #ifdef __AVX512F__ while (bytes >= sizeof(__m512i)) { register __m512i temp; @@ -134,10 +142,17 @@ __attribute__((always_inline)) inline void memswap_unaligned(void *void_a, // Power8 supports unaligned load/stores, but not sure what it will do here. swap_loop(vector int, a, b, bytes); #endif - swap_loop(size_t, a, b, bytes); - swap_loop(int, a, b, bytes); - swap_loop(short, a, b, bytes); - swap_loop(char, a, b, bytes); + swap_loop(int_least64_t, a, b, bytes); + swap_loop(int_least32_t, a, b, bytes); + swap_loop(int_least16_t, a, b, bytes); + swap_loop(int_least8_t, a, b, bytes); + + /* This is a known bug for the current version of clang on ARM. + * We add this synchronization as a temporary bug fix. + * See https://bugs.llvm.org/show_bug.cgi?id=40051 */ +#if defined(__clang__) && defined(__aarch64__) + __sync_synchronize(); +#endif } #endif /* SWIFT_MEMSWAP_H */ diff --git a/src/multipole.h b/src/multipole.h index 8139dc0548bb94b108d6e32da4b19808998f48d3..e867dfd4e2cc5c9fcd06d7d95dcf76a97689c2b3 100644 --- a/src/multipole.h +++ b/src/multipole.h @@ -1049,13 +1049,6 @@ INLINE static void gravity_P2M(struct gravity_tensors *multi, vel[2] += gparts[k].v_full[2] * m; } -#ifdef PLANETARY_SPH - /* Prevent FPE from zero mass with the temporary outside-the-box particles */ - if (mass == 0.f) { - mass = FLT_MIN; - } -#endif // PLANETARY_SPH - /* Final operation on CoM */ const double imass = 1.0 / mass; com[0] *= imass; diff --git a/src/outputlist.c b/src/outputlist.c index 2ab904d4fd0b7008b324f3c37a5cab6c6b337520..cab4013bc3841698183b825c7985a75e7095b29c 100644 --- a/src/outputlist.c +++ b/src/outputlist.c @@ -235,7 +235,7 @@ void output_list_init(struct output_list **list, const struct engine *e, sprintf(param_name, "%s:output_list", name); parser_get_param_string(params, param_name, filename); - message("Reading %s output file.", name); + if (e->verbose) message("Reading %s output file.", name); output_list_read_file(*list, filename, cosmo); if ((*list)->size < 2) diff --git a/src/parallel_io.c b/src/parallel_io.c index 481a2d8a7b7ba8fbefe77434b0f57b43d421f21d..b598d944dfe7c90510b036e7f9d85619ea103c59 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -42,6 +42,7 @@ #include "cooling_io.h" #include "dimension.h" #include "engine.h" +#include "entropy_floor.h" #include "error.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -51,8 +52,11 @@ #include "kernel_hydro.h" #include "part.h" #include "part_type.h" +#include "star_formation_io.h" #include "stars_io.h" +#include "tracers_io.h" #include "units.h" +#include "velociraptor_io.h" #include "xmf.h" /* The current limit of ROMIO (the underlying MPI-IO layer) is 2GB */ @@ -708,6 +712,21 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, error("ICs dimensionality (%dD) does not match code dimensionality (%dD)", dimension, (int)hydro_dimension); + /* Check whether the number of files is specified (if the info exists) */ + const hid_t hid_files = H5Aexists(h_grp, "NumFilesPerSnapshot"); + int num_files = 1; + if (hid_files < 0) + error( + "Error while testing the existance of 'NumFilesPerSnapshot' attribute"); + if (hid_files > 0) + io_read_attribute(h_grp, "NumFilesPerSnapshot", INT, &num_files); + if (num_files != 1) + error( + "ICs are split over multiples files (%d). SWIFT cannot handle this " + "case. The script /tools/combine_ics.py is availalbe in the repository " + "to combine files into a valid input file.", + num_files); + /* Read the relevant information and print status */ int flag_entropy_temp[6]; io_read_attribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp); @@ -938,8 +957,17 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; struct swift_params* params = e->parameter_file; + const int with_cosmology = e->policy & engine_policy_cosmology; + const int with_cooling = e->policy & engine_policy_cooling; + const int with_temperature = e->policy & engine_policy_temperature; +#ifdef HAVE_VELOCIRAPTOR + const int with_stf = (e->policy & engine_policy_structure_finding) && + (e->s->gpart_group_data != NULL); +#else + const int with_stf = 0; +#endif + FILE* xmfFile = 0; - int periodic = e->s->periodic; int numFiles = 1; /* First time, we need to create the XMF file */ @@ -965,28 +993,26 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], * specific output */ xmf_write_outputheader(xmfFile, fileName, e->time); - /* Open header to write simulation properties */ - /* message("Writing runtime parameters..."); */ - hid_t h_grp = - H5Gcreate(h_file, "/RuntimePars", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if (h_grp < 0) error("Error while creating runtime parameters group\n"); - - /* Write the relevant information */ - io_write_attribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); - - /* Close runtime parameters */ - H5Gclose(h_grp); - /* Open header to write simulation properties */ /* message("Writing file header..."); */ - h_grp = H5Gcreate(h_file, "/Header", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + hid_t h_grp = + H5Gcreate(h_file, "/Header", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating file header\n"); + /* Convert basic output information to snapshot units */ + const double factor_time = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_TIME); + const double factor_length = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_LENGTH); + const double dblTime = e->time * factor_time; + const double dim[3] = {e->s->dim[0] * factor_length, + e->s->dim[1] * factor_length, + e->s->dim[2] * factor_length}; + /* Print the relevant information and print status */ - io_write_attribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); - double dblTime = e->time; + io_write_attribute(h_grp, "BoxSize", DOUBLE, dim, 3); io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); - int dimension = (int)hydro_dimension; + const int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); @@ -1039,8 +1065,10 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating subgrid group"); + entropy_floor_write_flavour(h_grp); cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); + tracers_write_flavour(h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ @@ -1121,14 +1149,36 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], case swift_type_gas: hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); + if (with_cooling || with_temperature) { + num_fields += cooling_write_particles( + parts, xparts, list + num_fields, e->cooling_func); + } + num_fields += tracers_write_particles(parts, xparts, list + num_fields, + with_cosmology); + num_fields += + star_formation_write_particles(parts, xparts, list + num_fields); + if (with_stf) { + num_fields += + velociraptor_write_parts(parts, xparts, list + num_fields); + } break; case swift_type_dark_matter: darkmatter_write_particles(gparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts(e->s->gpart_group_data, + list + num_fields); + } break; case swift_type_stars: stars_write_particles(sparts, list, &num_fields); + num_fields += chemistry_write_sparticles(sparts, list + num_fields); + num_fields += + tracers_write_sparticles(sparts, list + num_fields, with_cosmology); + if (with_stf) { + num_fields += velociraptor_write_sparts(sparts, list + num_fields); + } break; default: @@ -1195,6 +1245,15 @@ void write_output_parallel(struct engine* e, const char* baseName, const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; struct swift_params* params = e->parameter_file; + const int with_cosmology = e->policy & engine_policy_cosmology; + const int with_cooling = e->policy & engine_policy_cooling; + const int with_temperature = e->policy & engine_policy_temperature; +#ifdef HAVE_VELOCIRAPTOR + const int with_stf = (e->policy & engine_policy_structure_finding) && + (e->s->gpart_group_data != NULL); +#else + const int with_stf = 0; +#endif /* Number of particles currently in the arrays */ const size_t Ntot = e->s->nr_gparts; @@ -1204,9 +1263,12 @@ void write_output_parallel(struct engine* e, const char* baseName, // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; /* Number of particles that we will write */ - const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts; - const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts; - const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts; + const size_t Ntot_written = + e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; + const size_t Ngas_written = + e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; + const size_t Nstars_written = + e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; const size_t Nbaryons_written = Ngas_written + Nstars_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; @@ -1254,6 +1316,32 @@ void write_output_parallel(struct engine* e, const char* baseName, snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, e->snapshot_output_count); + /* Now write the top-level cell structure */ + hid_t h_file_cells = 0, h_grp_cells = 0; + if (mpi_rank == 0) { + + /* Open the snapshot on rank 0 */ + h_file_cells = H5Fopen(fileName, H5F_ACC_RDWR, H5P_DEFAULT); + if (h_file_cells < 0) + error("Error while opening file '%s' on rank %d.", fileName, mpi_rank); + + /* Create the group we want in the file */ + h_grp_cells = H5Gcreate(h_file_cells, "/Cells", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp_cells < 0) error("Error while creating cells group"); + } + + /* Write the location of the particles in the arrays */ + io_write_cell_offsets(h_grp_cells, e->s->cdim, e->s->cells_top, + e->s->nr_cells, e->s->width, mpi_rank, N_total, offset, + internal_units, snapshot_units); + + /* Close everything */ + if (mpi_rank == 0) { + H5Gclose(h_grp_cells); + H5Fclose(h_file_cells); + } + /* Prepare some file-access properties */ hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); @@ -1367,6 +1455,7 @@ void write_output_parallel(struct engine* e, const char* baseName, struct part* parts_written = NULL; struct xpart* xparts_written = NULL; struct gpart* gparts_written = NULL; + struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; /* Write particle fields from the particle structure */ @@ -1379,8 +1468,19 @@ void write_output_parallel(struct engine* e, const char* baseName, Nparticles = Ngas; hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); - num_fields += cooling_write_particles(xparts, list + num_fields, - e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += cooling_write_particles( + parts, xparts, list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += + velociraptor_write_parts(parts, xparts, list + num_fields); + } + num_fields += tracers_write_particles( + parts, xparts, list + num_fields, with_cosmology); + num_fields += + star_formation_write_particles(parts, xparts, list + num_fields); + } else { /* Ok, we need to fish out the particles we want */ @@ -1403,8 +1503,19 @@ void write_output_parallel(struct engine* e, const char* baseName, &num_fields); num_fields += chemistry_write_particles(parts_written, list + num_fields); - num_fields += cooling_write_particles( - xparts_written, list + num_fields, e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += + cooling_write_particles(parts_written, xparts_written, + list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += velociraptor_write_parts( + parts_written, xparts_written, list + num_fields); + } + num_fields += tracers_write_particles( + parts_written, xparts_written, list + num_fields, with_cosmology); + num_fields += star_formation_write_particles( + parts_written, xparts_written, list + num_fields); } } break; @@ -1414,6 +1525,10 @@ void write_output_parallel(struct engine* e, const char* baseName, /* This is a DM-only run without inhibited particles */ Nparticles = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts(e->s->gpart_group_data, + list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -1424,11 +1539,28 @@ void write_output_parallel(struct engine* e, const char* baseName, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); + if (with_stf) { + if (posix_memalign( + (void**)&gpart_group_data_written, gpart_align, + Ndm_written * sizeof(struct velociraptor_gpart_data)) != 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + /* Collect the non-inhibited DM particles from gpart */ - io_collect_gparts_to_write(gparts, gparts_written, Ntot, Ndm_written); + io_collect_gparts_to_write(gparts, e->s->gpart_group_data, + gparts_written, gpart_group_data_written, + Ntot, Ndm_written, with_stf); - /* Write DM particles */ + /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_stf) { +#ifdef HAVE_VELOCIRAPTOR + num_fields += velociraptor_write_gparts(gpart_group_data_written, + list + num_fields); +#endif + } } } break; @@ -1438,6 +1570,12 @@ void write_output_parallel(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ Nparticles = Nstars; stars_write_particles(sparts, list, &num_fields); + num_fields += chemistry_write_sparticles(sparts, list + num_fields); + num_fields += tracers_write_sparticles(sparts, list + num_fields, + with_cosmology); + if (with_stf) { + num_fields += velociraptor_write_sparts(sparts, list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -1454,6 +1592,13 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Select the fields to write */ stars_write_particles(sparts_written, list, &num_fields); + num_fields += chemistry_write_sparticles(sparts, list + num_fields); + num_fields += tracers_write_sparticles(sparts, list + num_fields, + with_cosmology); + if (with_stf) { + num_fields += + velociraptor_write_sparts(sparts_written, list + num_fields); + } } } break; @@ -1480,6 +1625,7 @@ void write_output_parallel(struct engine* e, const char* baseName, if (parts_written) free(parts_written); if (xparts_written) free(xparts_written); if (gparts_written) free(gparts_written); + if (gpart_group_data_written) free(gpart_group_data_written); if (sparts_written) free(sparts_written); #ifdef IO_SPEED_MEASUREMENT diff --git a/src/part.c b/src/part.c index 3a626e652cf28f0376cadc1d9a40ab85b752e6c1..ec3627d728f69f469cc7d75eb2beb9ae39ed107e 100644 --- a/src/part.c +++ b/src/part.c @@ -139,8 +139,9 @@ void part_verify_links(struct part *parts, struct gpart *gparts, for (size_t k = 0; k < nr_gparts; ++k) { - /* We have a DM particle */ - if (gparts[k].type == swift_type_dark_matter) { + /* We have a real DM particle */ + if (gparts[k].type == swift_type_dark_matter && + gparts[k].time_bin != time_bin_not_created) { /* Check that it's not linked */ if (gparts[k].id_or_neg_offset <= 0) diff --git a/src/part.h b/src/part.h index 6296756381a2835de734145508cc40fcef895b54..97aee8abe75fae60388b040933b51f3170471869 100644 --- a/src/part.h +++ b/src/part.h @@ -76,6 +76,10 @@ #elif defined(PLANETARY_SPH) #include "./hydro/Planetary/hydro_part.h" #define hydro_need_extra_init_loop 0 +#elif defined(ANARCHY_PU_SPH) +#include "./hydro/AnarchyPU/hydro_part.h" +#define hydro_need_extra_init_loop 0 +#define EXTRA_HYDRO_LOOP #else #error "Invalid choice of SPH variant" #endif @@ -90,7 +94,15 @@ #endif /* Import the right star particle definition */ +#if defined(STARS_NONE) #include "./stars/Default/stars_part.h" +#elif defined(STARS_EAGLE) +#include "./stars/EAGLE/stars_part.h" +#elif defined(STARS_GEAR) +#include "./stars/GEAR/stars_part.h" +#else +#error "Invalid choice of star particle" +#endif void part_relink_gparts_to_parts(struct part *parts, size_t N, ptrdiff_t offset); diff --git a/src/partition.c b/src/partition.c index ba29b645af4dc41369b2f01ccd48252af673a75b..1dde46dcd4ad2eb397bd26905fd8b57240088857 100644 --- a/src/partition.c +++ b/src/partition.c @@ -71,12 +71,23 @@ const char *initial_partition_name[] = { /* Simple descriptions of repartition types for reports. */ const char *repartition_name[] = { "none", "edge and vertex task cost weights", "task cost edge weights", - "task cost vertex weights", + "memory balanced, using particle vertex weights", "vertex task costs and edge delta timebin weights"}; /* Local functions, if needed. */ static int check_complete(struct space *s, int verbose, int nregions); +/* + * Repartition fixed costs per type/subtype. These are determined from the + * statistics output produced when running with task debugging enabled. + */ +#if defined(WITH_MPI) && (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) +static double repartition_costs[task_type_count][task_subtype_count]; +#endif +#if defined(WITH_MPI) +static int repart_init_fixed_costs(void); +#endif + /* Vectorisation support */ /* ===================== */ @@ -240,33 +251,127 @@ static void graph_init(struct space *s, idx_t *adjncy, idx_t *xadj) { #endif #if defined(WITH_MPI) && (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) +struct counts_mapper_data { + double *counts; + size_t size; + struct space *s; +}; + +/* Generic function for accumulating sized counts for TYPE parts. Note uses + * local memory to reduce contention, the amount of memory required is + * precalculated by an additional loop determining the range of cell IDs. */ +#define ACCUMULATE_SIZES_MAPPER(TYPE) \ + accumulate_sizes_mapper_##TYPE(void *map_data, int num_elements, \ + void *extra_data) { \ + struct TYPE *parts = (struct TYPE *)map_data; \ + struct counts_mapper_data *mydata = \ + (struct counts_mapper_data *)extra_data; \ + double size = mydata->size; \ + int *cdim = mydata->s->cdim; \ + double iwidth[3] = {mydata->s->iwidth[0], mydata->s->iwidth[1], \ + mydata->s->iwidth[2]}; \ + double dim[3] = {mydata->s->dim[0], mydata->s->dim[1], mydata->s->dim[2]}; \ + double *lcounts = NULL; \ + int lcid = mydata->s->nr_cells; \ + int ucid = 0; \ + for (int k = 0; k < num_elements; k++) { \ + for (int j = 0; j < 3; j++) { \ + if (parts[k].x[j] < 0.0) \ + parts[k].x[j] += dim[j]; \ + else if (parts[k].x[j] >= dim[j]) \ + parts[k].x[j] -= dim[j]; \ + } \ + const int cid = \ + cell_getid(cdim, parts[k].x[0] * iwidth[0], \ + parts[k].x[1] * iwidth[1], parts[k].x[2] * iwidth[2]); \ + if (cid > ucid) ucid = cid; \ + if (cid < lcid) lcid = cid; \ + } \ + int nused = ucid - lcid + 1; \ + if ((lcounts = (double *)calloc(sizeof(double), nused)) == NULL) \ + error("Failed to allocate counts thread-specific buffer"); \ + for (int k = 0; k < num_elements; k++) { \ + const int cid = \ + cell_getid(cdim, parts[k].x[0] * iwidth[0], \ + parts[k].x[1] * iwidth[1], parts[k].x[2] * iwidth[2]); \ + lcounts[cid - lcid] += size; \ + } \ + for (int k = 0; k < nused; k++) \ + atomic_add_d(&mydata->counts[k + lcid], lcounts[k]); \ + free(lcounts); \ + } + /** - * @brief Accumulate the counts of particles per cell. + * @brief Accumulate the sized counts of particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. * - * @param s the space containing the cells. - * @param counts the number of particles per cell. Should be - * allocated as size s->nr_parts. + * part version. + */ +static void ACCUMULATE_SIZES_MAPPER(part); + +/** + * @brief Accumulate the sized counts of particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * gpart version. */ -static void accumulate_counts(struct space *s, double *counts) { +static void ACCUMULATE_SIZES_MAPPER(gpart); - struct part *parts = s->parts; - int *cdim = s->cdim; - double iwidth[3] = {s->iwidth[0], s->iwidth[1], s->iwidth[2]}; - double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; +/** + * @brief Accumulate the sized counts of particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * spart version. + */ +static void ACCUMULATE_SIZES_MAPPER(spart); + +/** + * @brief Accumulate total memory size in particles per cell. + * + * @param s the space containing the cells. + * @param counts the number of bytes in particles per cell. Should be + * allocated as size s->nr_cells. + */ +static void accumulate_sizes(struct space *s, double *counts) { bzero(counts, sizeof(double) * s->nr_cells); - for (size_t k = 0; k < s->nr_parts; k++) { - for (int j = 0; j < 3; j++) { - if (parts[k].x[j] < 0.0) - parts[k].x[j] += dim[j]; - else if (parts[k].x[j] >= dim[j]) - parts[k].x[j] -= dim[j]; - } - const int cid = - cell_getid(cdim, parts[k].x[0] * iwidth[0], parts[k].x[1] * iwidth[1], - parts[k].x[2] * iwidth[2]); - counts[cid]++; + struct counts_mapper_data mapper_data; + mapper_data.counts = counts; + mapper_data.s = s; + + double hsize = (double)sizeof(struct part); + if (s->nr_parts > 0) { + mapper_data.size = hsize; + threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_part, s->parts, + s->nr_parts, sizeof(struct part), space_splitsize, + &mapper_data); + } + + double gsize = (double)sizeof(struct gpart); + if (s->nr_gparts > 0) { + mapper_data.size = gsize; + threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_gpart, s->gparts, + s->nr_gparts, sizeof(struct gpart), space_splitsize, + &mapper_data); + } + + double ssize = (double)sizeof(struct spart); + if (s->nr_sparts > 0) { + mapper_data.size = ssize; + threadpool_map(&s->e->threadpool, accumulate_sizes_mapper_spart, s->sparts, + s->nr_sparts, sizeof(struct spart), space_splitsize, + &mapper_data); + } + + /* Keep the sum of particles across all ranks in the range of IDX_MAX. */ + if ((s->e->total_nr_parts * hsize + s->e->total_nr_gparts * gsize + + s->e->total_nr_sparts * ssize) > (double)IDX_MAX) { + double vscale = + (double)(IDX_MAX - 1000) / + (double)(s->e->total_nr_parts * hsize + s->e->total_nr_gparts * gsize + + s->e->total_nr_sparts * ssize); + for (int k = 0; k < s->nr_cells; k++) counts[k] *= vscale; } } #endif @@ -284,7 +389,7 @@ static void split_metis(struct space *s, int nregions, int *celllist) { for (int i = 0; i < s->nr_cells; i++) s->cells_top[i].nodeID = celllist[i]; /* To check or visualise the partition dump all the cells. */ - /* dumpCellRanks("metis_partition", s->cells_top, s->nr_cells);*/ + /*dumpCellRanks("metis_partition", s->cells_top, s->nr_cells);*/ } #endif @@ -427,6 +532,7 @@ void permute_regions(int *newlist, int *oldlist, int nregions, int ncells, static void pick_parmetis(int nodeID, struct space *s, int nregions, double *vertexw, double *edgew, int refine, int adaptive, float itr, int *celllist) { + int res; MPI_Comm comm; MPI_Comm_dup(MPI_COMM_WORLD, &comm); @@ -609,14 +715,14 @@ static void pick_parmetis(int nodeID, struct space *s, int nregions, /* Dump graphs to disk files for testing. ParMETIS xadj isn't right for * a dump, so make a serial-like version. */ /*{ - idx_t *tmp_xadj = (idx_t *)malloc(sizeof(idx_t) * (ncells + nregions + - 1)); + idx_t *tmp_xadj = + (idx_t *)malloc(sizeof(idx_t) * (ncells + nregions + 1)); tmp_xadj[0] = 0; for (int k = 0; k < ncells; k++) tmp_xadj[k + 1] = tmp_xadj[k] + 26; - dumpParMETISGraph("parmetis_graph", ncells, 1, tmp_xadj, full_adjncy, - full_weights_v, NULL, full_weights_e); + dumpMETISGraph("parmetis_graph", ncells, 1, tmp_xadj, full_adjncy, + full_weights_v, NULL, full_weights_e); free(tmp_xadj); - }*/ + }*/ /* Send ranges to the other ranks and keep our own. */ for (int rank = 0, j1 = 0, j2 = 0, j3 = 0; rank < nregions; rank++) { @@ -1024,9 +1130,9 @@ static void pick_metis(int nodeID, struct space *s, int nregions, idx_t objval; /* Dump graph in METIS format */ - /*dumpMETISGraph("metis_graph", idx_ncells, one, xadj, adjncy, - * weights_v, NULL, weights_e); - */ + /*dumpMETISGraph("metis_graph", idx_ncells, one, xadj, adjncy, weights_v, + NULL, weights_e);*/ + if (METIS_PartGraphKway(&idx_ncells, &one, xadj, adjncy, weights_v, NULL, weights_e, &idx_nregions, NULL, NULL, options, &objval, regionid) != METIS_OK) @@ -1067,6 +1173,7 @@ struct weights_mapper_data { int timebins; int vweights; int nr_cells; + int use_ticks; struct cell *cells; }; @@ -1084,8 +1191,8 @@ static void check_weights(struct task *tasks, int nr_tasks, * @param num_elements the number of data elements to process. * @param extra_data additional data for the mapper context. */ -void partition_gather_weights(void *map_data, int num_elements, - void *extra_data) { +static void partition_gather_weights(void *map_data, int num_elements, + void *extra_data) { struct task *tasks = (struct task *)map_data; struct weights_mapper_data *mydata = (struct weights_mapper_data *)extra_data; @@ -1098,6 +1205,7 @@ void partition_gather_weights(void *map_data, int num_elements, int nr_cells = mydata->nr_cells; int timebins = mydata->timebins; int vweights = mydata->vweights; + int use_ticks = mydata->use_ticks; struct cell *cells = mydata->cells; @@ -1106,10 +1214,18 @@ void partition_gather_weights(void *map_data, int num_elements, struct task *t = &tasks[i]; /* Skip un-interesting tasks. */ - if (t->cost == 0.f) continue; - - /* Get the task weight based on costs. */ - double w = (double)t->cost; + if (t->type == task_type_send || t->type == task_type_recv || + t->type == task_type_logger || t->implicit || t->ci == NULL) + continue; + + /* Get weight for this task. Either based on fixed costs or task timings. */ + double w = 0.0; + if (use_ticks) { + w = (double)t->toc - (double)t->tic; + } else { + w = repartition_costs[t->type][t->subtype]; + } + if (w <= 0.0) continue; /* Get the top-level cells involved. */ struct cell *ci, *cj; @@ -1272,6 +1388,7 @@ static void repart_edge_metis(int vweights, int eweights, int timebins, weights_data.vweights = vweights; weights_data.weights_e = weights_e; weights_data.weights_v = weights_v; + weights_data.use_ticks = repartition->use_ticks; ticks tic = getticks(); @@ -1316,10 +1433,7 @@ static void repart_edge_metis(int vweights, int eweights, int timebins, } /* We need to rescale the sum of the weights so that the sums of the two - * types of weights are less than IDX_MAX, that is the range of idx_t. Also - * we would like to balance edges and vertices when the edge weights are - * timebins, as these have no reason to have equivalent scales, so we use an - * equipartition. */ + * types of weights are less than IDX_MAX, that is the range of idx_t. */ double vsum = 0.0; if (vweights) for (int k = 0; k < nr_cells; k++) vsum += weights_v[k]; @@ -1327,41 +1441,60 @@ static void repart_edge_metis(int vweights, int eweights, int timebins, if (eweights) for (int k = 0; k < 26 * nr_cells; k++) esum += weights_e[k]; + /* Do the scaling, if needed, keeping both weights in proportion. */ double vscale = 1.0; double escale = 1.0; - if (timebins && eweights) { - /* Make sums the same. */ + if (vweights && eweights) { if (vsum > esum) { - escale = vsum / esum; - esum = vsum; + if (vsum > (double)IDX_MAX) { + vscale = (double)(IDX_MAX - 1000) / vsum; + escale = vscale; + } } else { - vscale = esum / vsum; - vsum = esum; + if (esum > (double)IDX_MAX) { + escale = (double)(IDX_MAX - 1000) / esum; + vscale = escale; + } } - } - - /* Now make sure sum of weights are in the range of idx_t. */ - if (vweights) { + } else if (vweights) { if (vsum > (double)IDX_MAX) { vscale = (double)(IDX_MAX - 1000) / vsum; + } + } else if (eweights) { + if (esum > (double)IDX_MAX) { + escale = (double)(IDX_MAX - 1000) / esum; + } + } - if (!timebins && eweights) { - /* Keep edge weights in proportion. */ - esum = 0.0; - for (int k = 0; k < 26 * nr_cells; k++) { - weights_e[k] *= vscale; - esum += weights_e[k]; - } - } + if (vweights && vscale != 1.0) { + vsum = 0.0; + for (int k = 0; k < nr_cells; k++) { + weights_v[k] *= vscale; + vsum += weights_v[k]; } - if (vscale != 1.0) - for (int k = 0; k < nr_cells; k++) weights_v[k] *= vscale; + vscale = 1.0; + } + if (eweights && escale != 1.0) { + esum = 0.0; + for (int k = 0; k < 26 * nr_cells; k++) { + weights_e[k] *= escale; + esum += weights_e[k]; + } + escale = 1.0; } - if (eweights) { - if (esum > (double)IDX_MAX) escale = (double)(IDX_MAX - 1000) / esum; - if (escale != 1.0) + /* Balance edges and vertices when the edge weights are timebins, as these + * have no reason to have equivalent scales, we use an equipartition. */ + if (timebins && eweights) { + + /* Make sums the same. */ + if (vsum > esum) { + escale = vsum / esum; for (int k = 0; k < 26 * nr_cells; k++) weights_e[k] *= escale; + } else { + vscale = esum / vsum; + for (int k = 0; k < nr_cells; k++) weights_v[k] *= vscale; + } } /* And repartition/ partition, using both weights or not as requested. */ @@ -1416,8 +1549,106 @@ static void repart_edge_metis(int vweights, int eweights, int timebins, if (vweights) free(weights_v); if (eweights) free(weights_e); } + +/** + * @brief Repartition the cells amongst the nodes using weights based on + * the memory use of particles in the cells. + * + * @param repartition the partition struct of the local engine. + * @param nodeID our nodeID. + * @param nr_nodes the number of nodes. + * @param s the space of cells holding our local particles. + */ +static void repart_memory_metis(struct repartition *repartition, int nodeID, + int nr_nodes, struct space *s) { + + /* Space for counts of particle memory use per cell. */ + double *weights = NULL; + if ((weights = (double *)malloc(sizeof(double) * s->nr_cells)) == NULL) + error("Failed to allocate cell weights buffer."); + bzero(weights, sizeof(double) * s->nr_cells); + + /* Check each particle and accumulate the sizes per cell. */ + accumulate_sizes(s, weights); + + /* Get all the counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, weights, s->nr_cells, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce particle cell weights."); + + /* Allocate cell list for the partition. If not already done. */ +#ifdef HAVE_PARMETIS + int refine = 1; +#endif + if (repartition->ncelllist != s->nr_cells) { +#ifdef HAVE_PARMETIS + refine = 0; +#endif + free(repartition->celllist); + repartition->ncelllist = 0; + if ((repartition->celllist = (int *)malloc(sizeof(int) * s->nr_cells)) == + NULL) + error("Failed to allocate celllist"); + repartition->ncelllist = s->nr_cells; + } + + /* We need to rescale the sum of the weights so that the sum is + * less than IDX_MAX, that is the range of idx_t. */ + double sum = 0.0; + for (int k = 0; k < s->nr_cells; k++) sum += weights[k]; + if (sum > (double)IDX_MAX) { + double scale = (double)(IDX_MAX - 1000) / sum; + for (int k = 0; k < s->nr_cells; k++) weights[k] *= scale; + } + + /* And repartition. */ +#ifdef HAVE_PARMETIS + if (repartition->usemetis) { + pick_metis(nodeID, s, nr_nodes, weights, NULL, repartition->celllist); + } else { + pick_parmetis(nodeID, s, nr_nodes, weights, NULL, refine, + repartition->adaptive, repartition->itr, + repartition->celllist); + } +#else + pick_metis(nodeID, s, nr_nodes, weights, NULL, repartition->celllist); #endif + /* Check that all cells have good values. All nodes have same copy, so just + * check on one. */ + if (nodeID == 0) { + for (int k = 0; k < s->nr_cells; k++) + if (repartition->celllist[k] < 0 || repartition->celllist[k] >= nr_nodes) + error("Got bad nodeID %d for cell %i.", repartition->celllist[k], k); + } + + /* Check that the partition is complete and all nodes have some cells. */ + int present[nr_nodes]; + int failed = 0; + for (int i = 0; i < nr_nodes; i++) present[i] = 0; + for (int i = 0; i < s->nr_cells; i++) present[repartition->celllist[i]]++; + for (int i = 0; i < nr_nodes; i++) { + if (!present[i]) { + failed = 1; + if (nodeID == 0) message("Node %d is not present after repartition", i); + } + } + + /* If partition failed continue with the current one, but make this clear. */ + if (failed) { + if (nodeID == 0) + message( + "WARNING: repartition has failed, continuing with the current" + " partition, load balance will not be optimal"); + for (int k = 0; k < s->nr_cells; k++) + repartition->celllist[k] = s->cells_top[k].nodeID; + } + + /* And apply to our cells */ + split_metis(s, nr_nodes, repartition->celllist); +} +#endif /* WITH_MPI && (HAVE_METIS || HAVE_PARMETIS) */ + /** * @brief Repartition the space using the given repartition type. * @@ -1447,14 +1678,13 @@ void partition_repartition(struct repartition *reparttype, int nodeID, repart_edge_metis(0, 1, 0, reparttype, nodeID, nr_nodes, s, tasks, nr_tasks); - } else if (reparttype->type == REPART_METIS_VERTEX_COSTS) { - repart_edge_metis(1, 0, 0, reparttype, nodeID, nr_nodes, s, tasks, - nr_tasks); - } else if (reparttype->type == REPART_METIS_VERTEX_COSTS_TIMEBINS) { repart_edge_metis(1, 1, 1, reparttype, nodeID, nr_nodes, s, tasks, nr_tasks); + } else if (reparttype->type == REPART_METIS_VERTEX_COUNTS) { + repart_memory_metis(reparttype, nodeID, nr_nodes, s); + } else if (reparttype->type == REPART_NONE) { /* Doing nothing. */ @@ -1488,6 +1718,7 @@ void partition_repartition(struct repartition *reparttype, int nodeID, */ void partition_initial_partition(struct partition *initial_partition, int nodeID, int nr_nodes, struct space *s) { + ticks tic = getticks(); /* Geometric grid partitioning. */ if (initial_partition->type == INITPART_GRID) { @@ -1529,16 +1760,15 @@ void partition_initial_partition(struct partition *initial_partition, * inhomogeneous dist. */ - /* Space for particles per cell counts, which will be used as weights or - * not. */ + /* Space for particles sizes per cell, which will be used as weights. */ double *weights = NULL; if (initial_partition->type == INITPART_METIS_WEIGHT) { if ((weights = (double *)malloc(sizeof(double) * s->nr_cells)) == NULL) error("Failed to allocate weights buffer."); bzero(weights, sizeof(double) * s->nr_cells); - /* Check each particle and accumilate the counts per cell. */ - accumulate_counts(s, weights); + /* Check each particle and accumilate the sizes per cell. */ + accumulate_sizes(s, weights); /* Get all the counts from all the nodes. */ if (MPI_Allreduce(MPI_IN_PLACE, weights, s->nr_cells, MPI_DOUBLE, MPI_SUM, @@ -1603,11 +1833,15 @@ void partition_initial_partition(struct partition *initial_partition, error("SWIFT was not compiled with MPI support"); #endif } + + if (s->e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); } /** * @brief Initialises the partition and re-partition scheme from the parameter - * file + * file. * * @param partition The #partition scheme to initialise. * @param repartition The #repartition scheme to initialise. @@ -1622,10 +1856,10 @@ void partition_init(struct partition *partition, /* Defaults make use of METIS if available */ #if defined(HAVE_METIS) || defined(HAVE_PARMETIS) - const char *default_repart = "costs/costs"; + const char *default_repart = "fullcosts"; const char *default_part = "memory"; #else - const char *default_repart = "none/none"; + const char *default_repart = "none"; const char *default_part = "grid"; #endif @@ -1678,32 +1912,32 @@ void partition_init(struct partition *partition, parser_get_opt_param_string(params, "DomainDecomposition:repartition_type", part_type, default_repart); - if (strcmp("none/none", part_type) == 0) { + if (strcmp("none", part_type) == 0) { repartition->type = REPART_NONE; #if defined(HAVE_METIS) || defined(HAVE_PARMETIS) - } else if (strcmp("costs/costs", part_type) == 0) { + } else if (strcmp("fullcosts", part_type) == 0) { repartition->type = REPART_METIS_VERTEX_EDGE_COSTS; - } else if (strcmp("none/costs", part_type) == 0) { + } else if (strcmp("edgecosts", part_type) == 0) { repartition->type = REPART_METIS_EDGE_COSTS; - } else if (strcmp("costs/none", part_type) == 0) { - repartition->type = REPART_METIS_VERTEX_COSTS; + } else if (strcmp("memory", part_type) == 0) { + repartition->type = REPART_METIS_VERTEX_COUNTS; - } else if (strcmp("costs/time", part_type) == 0) { + } else if (strcmp("timecosts", part_type) == 0) { repartition->type = REPART_METIS_VERTEX_COSTS_TIMEBINS; } else { message("Invalid choice of re-partition type '%s'.", part_type); error( - "Permitted values are: 'none/none', 'costs/costs', 'none/costs' " - "'costs/none' or 'costs/time'"); + "Permitted values are: 'none', 'fullcosts', 'edgecosts' " + "'memory' or 'timecosts'"); #else } else { message("Invalid choice of re-partition type '%s'.", part_type); error( - "Permitted values are: 'none/none' when compiled without " + "Permitted values are: 'none' when compiled without " "METIS or ParMETIS."); #endif } @@ -1720,13 +1954,13 @@ void partition_init(struct partition *partition, " than 1"); /* Fraction of particles that should be updated before a repartition - * based on CPU time is considered. */ + * based on CPU time is considered, needs to be high. */ repartition->minfrac = - parser_get_opt_param_float(params, "DomainDecomposition:minfrac", 0.9f); - if (repartition->minfrac <= 0 || repartition->minfrac > 1) + parser_get_opt_param_float(params, "DomainDecomposition:minfrac", 0.95f); + if (repartition->minfrac <= 0.5 || repartition->minfrac > 1) error( - "Invalid DomainDecomposition:minfrac, must be greater than 0 and less " - "than equal to 1"); + "Invalid DomainDecomposition:minfrac, must be greater than 0.5 " + "and less than equal to 1"); /* Use METIS or ParMETIS when ParMETIS is also available. */ repartition->usemetis = @@ -1745,11 +1979,62 @@ void partition_init(struct partition *partition, repartition->ncelllist = 0; repartition->celllist = NULL; + /* Do we have fixed costs available? These can be used to force + * repartitioning at any time. Not required if not repartitioning.*/ + repartition->use_fixed_costs = parser_get_opt_param_int( + params, "DomainDecomposition:use_fixed_costs", 0); + if (repartition->type == REPART_NONE) repartition->use_fixed_costs = 0; + + /* Check if this is true or required and initialise them. */ + if (repartition->use_fixed_costs || repartition->trigger > 1) { + if (!repart_init_fixed_costs()) { + if (repartition->trigger <= 1) { + if (engine_rank == 0) + message( + "WARNING: fixed cost repartitioning was requested but is" + " not available."); + repartition->use_fixed_costs = 0; + } else { + error( + "Forced fixed cost repartitioning was requested but is" + " not available."); + } + } + } + #else error("SWIFT was not compiled with MPI support"); #endif } +#ifdef WITH_MPI +/** + * @brief Set the fixed costs for repartition using METIS. + * + * These are determined using a run with the -y flag on which produces + * a statistical analysis that is condensed into a .h file for inclusion. + * + * If the default include file is used then no fixed costs are set and this + * function will return 0. + */ +static int repart_init_fixed_costs(void) { + +#if defined(WITH_MPI) && (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) + /* Set the default fixed cost. */ + for (int j = 0; j < task_type_count; j++) { + for (int k = 0; k < task_subtype_count; k++) { + repartition_costs[j][k] = 1.0; + } + } + +#include <partition_fixed_costs.h> + return HAVE_FIXED_COSTS; +#endif + + return 0; +} +#endif /* WITH_MPI */ + /* General support */ /* =============== */ @@ -1793,11 +2078,11 @@ static int check_complete(struct space *s, int verbose, int nregions) { * @brief Check that the threadpool version of the weights construction is * correct by comparing to the old serial code. * - * @tasks the list of tasks - * @nr_tasks number of tasks - * @mydata additional values as passed to threadpool - * @ref_weights_v vertex weights to check - * @ref_weights_e edge weights to check + * @param tasks the list of tasks + * @param nr_tasks number of tasks + * @param mydata additional values as passed to threadpool + * @param ref_weights_v vertex weights to check + * @param ref_weights_e edge weights to check */ static void check_weights(struct task *tasks, int nr_tasks, struct weights_mapper_data *mydata, @@ -1809,6 +2094,7 @@ static void check_weights(struct task *tasks, int nr_tasks, int nr_cells = mydata->nr_cells; int timebins = mydata->timebins; int vweights = mydata->vweights; + int use_ticks = mydata->use_ticks; struct cell *cells = mydata->cells; @@ -1833,10 +2119,18 @@ static void check_weights(struct task *tasks, int nr_tasks, struct task *t = &tasks[j]; /* Skip un-interesting tasks. */ - if (t->cost == 0.f) continue; - - /* Get the task weight based on costs. */ - double w = (double)t->cost; + if (t->type == task_type_send || t->type == task_type_recv || + t->type == task_type_logger || t->implicit || t->ci == NULL) + continue; + + /* Get weight for this task. Either based on fixed costs or task timings. */ + double w = 0.0; + if (use_ticks) { + w = (double)t->toc - (double)t->tic; + } else { + w = repartition_costs[t->type][t->subtype]; + } + if (w <= 0.0) continue; /* Get the top-level cells involved. */ struct cell *ci, *cj; diff --git a/src/partition.h b/src/partition.h index 1202a1d19ff18f83ed26464bade088990ed51db6..de0d95a5e343f1aa85a03c2cda49019f2fd08037 100644 --- a/src/partition.h +++ b/src/partition.h @@ -46,7 +46,7 @@ enum repartition_type { REPART_NONE = 0, REPART_METIS_VERTEX_EDGE_COSTS, REPART_METIS_EDGE_COSTS, - REPART_METIS_VERTEX_COSTS, + REPART_METIS_VERTEX_COUNTS, REPART_METIS_VERTEX_COSTS_TIMEBINS }; @@ -59,6 +59,9 @@ struct repartition { int usemetis; int adaptive; + int use_fixed_costs; + int use_ticks; + /* The partition as a cell-list. */ int ncelllist; int *celllist; diff --git a/src/partition_fixed_costs.h b/src/partition_fixed_costs.h new file mode 100644 index 0000000000000000000000000000000000000000..e713684b28ce81e60b9fa98a6078d1c8c370f935 --- /dev/null +++ b/src/partition_fixed_costs.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Peter W. Draper (p.w.draper@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 SWIFT_PARTITION_FIXED_COSTS_H +#define SWIFT_PARTITION_FIXED_COSTS_H + +/* Default is no fixed costs. */ +#define HAVE_FIXED_COSTS 0 + +#endif /* SWIFT_PARTITION_FIXED_COSTS_H */ diff --git a/src/physical_constants.c b/src/physical_constants.c index ed25ff1b2be09a04400f98476c78a537e6d8e2bf..3e3c72812c552aba1204086353dc7d239a5c36f9 100644 --- a/src/physical_constants.c +++ b/src/physical_constants.c @@ -32,7 +32,8 @@ /** * @brief Converts physical constants to the internal unit system * - * Some constants can be overwritten by the YAML file values. + * Some constants can be overwritten by the YAML file values. If the + * param argument is NULL, no overwriting is done. * * @param us The current internal system of units. * @param params The parsed parameter file. @@ -48,8 +49,10 @@ void phys_const_init(const struct unit_system *us, struct swift_params *params, const_newton_G_cgs / units_general_cgs_conversion_factor(us, dimension_G); /* Overwrite G if present in the file */ - internal_const->const_newton_G = parser_get_opt_param_double( - params, "PhysicalConstants:G", internal_const->const_newton_G); + if (params != NULL) { + internal_const->const_newton_G = parser_get_opt_param_double( + params, "PhysicalConstants:G", internal_const->const_newton_G); + } const float dimension_c[5] = {0, 1, -1, 0, 0}; /* [cm s^-1] */ internal_const->const_speed_light_c = @@ -131,6 +134,11 @@ void phys_const_init(const struct unit_system *us, struct swift_params *params, internal_const->const_primordial_He_fraction = const_primordial_He_fraction_cgs / units_general_cgs_conversion_factor(us, dimension_Yp); + + const float dimension_reduced_hubble[5] = {0, 0, -1, 0, 0}; /* [s^-1] */ + internal_const->const_reduced_hubble = + const_reduced_hubble_cgs / + units_general_cgs_conversion_factor(us, dimension_reduced_hubble); } /** @@ -153,6 +161,7 @@ void phys_const_print(const struct phys_const *internal_const) { internal_const->const_astronomical_unit); message("%25s = %e", "Parsec", internal_const->const_parsec); message("%25s = %e", "Solar mass", internal_const->const_solar_mass); + message("%25s = %e", "km/s/Mpc", internal_const->const_reduced_hubble); } /** diff --git a/src/physical_constants.h b/src/physical_constants.h index a1b5e9c243c54bfd97179364170e8b3bbc36fe4f..97da4b322a8bca1f978b43a4cabda2ff1cc1e517 100644 --- a/src/physical_constants.h +++ b/src/physical_constants.h @@ -96,6 +96,9 @@ struct phys_const { /*! Primordial Helium fraction */ double const_primordial_He_fraction; + + /*! Reduced hubble constant units (i.e. H_0 / h) */ + double const_reduced_hubble; }; void phys_const_init(const struct unit_system* us, struct swift_params* params, diff --git a/src/physical_constants_cgs.h b/src/physical_constants_cgs.h index fecd1f894af5df5db54b37883ba07e470b956bdf..4d1a54f68ba557c74fb489a9343eaf3846c481f4 100644 --- a/src/physical_constants_cgs.h +++ b/src/physical_constants_cgs.h @@ -98,4 +98,8 @@ const double const_T_CMB_0_cgs = 2.7255; /*! Primordial Helium fraction [-] */ const double const_primordial_He_fraction_cgs = 0.245; +/*! Reduced Hubble constant units (i.e. H_0 / h == 100 km / s / Mpc in CGS) + * [s^-1] */ +const double const_reduced_hubble_cgs = 3.2407792894458e-18; + #endif /* SWIFT_PHYSICAL_CONSTANTS_CGS_H */ diff --git a/src/potential/hernquist/potential.h b/src/potential/hernquist/potential.h index d0ec339d91376d25e0e5a2106826d07deca7115b..b98f45ff7ab4aeffd94f47f4931d3dd6c80d5642 100644 --- a/src/potential/hernquist/potential.h +++ b/src/potential/hernquist/potential.h @@ -170,6 +170,12 @@ static INLINE void potential_init_backend( const struct unit_system* us, const struct space* s, struct external_potential* potential) { + /* Define the default value */ + static const int idealized_disk_default = 0; + static const double M200_default = 0.; + static const double V200_default = 0.; + static const double R200_default = 0.; + /* Read in the position of the centre of potential */ parser_get_param_double_array(parameter_file, "HernquistPotential:position", 3, potential->x); @@ -184,11 +190,91 @@ static INLINE void potential_init_backend( potential->x[2] += s->dim[2] / 2.; } - /* Read the other parameters of the model */ - potential->mass = - parser_get_param_double(parameter_file, "HernquistPotential:mass"); - potential->al = - parser_get_param_double(parameter_file, "HernquistPotential:scalelength"); + /* check whether we use the more advanced idealized disk setting */ + const int usedisk = parser_get_opt_param_int( + parameter_file, "HernquistPotential:idealizeddisk", + idealized_disk_default); + + if (!usedisk) { + /* Read the parameters of the model in the case of the simple + * potential form \f$ \Phi = - \frac{GM}{r+a} \f$ */ + potential->mass = + parser_get_param_double(parameter_file, "HernquistPotential:mass"); + potential->al = parser_get_param_double(parameter_file, + "HernquistPotential:scalelength"); + } else { + + /* Read the parameters in the case of a idealized disk + * There are 3 different possible input parameters M200, V200 and R200 + * First read in the mandatory parameters in this case */ + + const float G_newton = phys_const->const_newton_G; + const float kmoversoverMpc = phys_const->const_reduced_hubble; + + /* Initialize the variables */ + double M200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:M200", M200_default); + double V200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:V200", V200_default); + double R200 = parser_get_opt_param_double( + parameter_file, "HernquistPotential:R200", R200_default); + const double h = + parser_get_param_double(parameter_file, "HernquistPotential:h"); + + /* Hubble constant assumed for halo masses conversion */ + const double H0 = h * kmoversoverMpc; + + /* There are 3 legit runs possible with use disk, + * with a known M200, V200 or R200 */ + if (M200 != 0.0) { + /* Calculate V200 and R200 from M200 */ + V200 = cbrt(10. * M200 * G_newton * H0); + R200 = V200 / (10 * H0); + + } else if (V200 != 0.0) { + + /* Calculate M200 and R200 from V200 */ + M200 = V200 * V200 * V200 / (10. * G_newton * H0); + R200 = V200 / (10 * H0); + } else if (R200 != 0.0) { + + /* Calculate M200 and V200 from R200 */ + V200 = 10. * H0 * R200; + M200 = V200 * V200 * V200 / (10. * G_newton * H0); + } else { + error("Please specify one of the 3 variables M200, V200 or R200"); + } + + /* message("M200 = %g, R200 = %g, V200 = %g", M200, R200, V200); */ + /* message("H0 = %g", H0); */ + + /* get the concentration from the parameter file */ + const double concentration = parser_get_param_double( + parameter_file, "HernquistPotential:concentration"); + + /* Calculate the Scale radius using the NFW definition */ + const double RS = R200 / concentration; + + /* Calculate the Hernquist equivalent scale length */ + potential->al = RS * sqrt(1. * (log(1. + concentration) - + concentration / (1. + concentration))); + + /* Depending on the disk mass and and the bulge mass the halo + * gets a different mass, because of this we read the fractions + * from the parameter file and calculate the absolute mass*/ + const double diskfraction = parser_get_param_double( + parameter_file, "HernquistPotential:diskfraction"); + const double bulgefraction = parser_get_param_double( + parameter_file, "HernquistPotential:bulgefraction"); + /* Calculate the mass of the bulge and disk from the parameters */ + const double Mdisk = M200 * diskfraction; + const double Mbulge = M200 * bulgefraction; + + /* Store the mass of the DM halo */ + potential->mass = M200 - Mdisk - Mbulge; + } + + /* Retrieve the timestep and softening of the potential */ potential->timestep_mult = parser_get_param_float( parameter_file, "HernquistPotential:timestep_mult"); const float epsilon = @@ -199,7 +285,7 @@ static INLINE void potential_init_backend( /* This is the circular orbital time at the softened radius */ const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass); potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI * - (1 + epsilon / potential->al) / sqrtgm * + (1. + epsilon / potential->al) / sqrtgm * potential->timestep_mult; } diff --git a/src/proxy.c b/src/proxy.c index 325ed78644b07a497374e40bfc8518edcb018593..4a67b4b3584c43b2df63f17303eba9ec5e742cb0 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -269,6 +269,93 @@ void proxy_cells_exchange_second(struct proxy *p) { #endif } +#ifdef WITH_MPI + +void proxy_cells_count_mapper(void *map_data, int num_elements, + void *extra_data) { + struct cell *cells = (struct cell *)map_data; + + for (int k = 0; k < num_elements; k++) { + if (cells[k].mpi.sendto) cells[k].mpi.pcell_size = cell_getsize(&cells[k]); + } +} + +struct pack_mapper_data { + struct space *s; + int *offset; + struct pcell *pcells; + int with_gravity; +}; + +void proxy_cells_pack_mapper(void *map_data, int num_elements, + void *extra_data) { + struct cell *cells = (struct cell *)map_data; + struct pack_mapper_data *data = (struct pack_mapper_data *)extra_data; + + for (int k = 0; k < num_elements; k++) { + if (cells[k].mpi.sendto) { + ptrdiff_t ind = &cells[k] - data->s->cells_top; + cells[k].mpi.pcell = &data->pcells[data->offset[ind]]; + cell_pack(&cells[k], cells[k].mpi.pcell, data->with_gravity); + } + } +} + +void proxy_cells_exchange_first_mapper(void *map_data, int num_elements, + void *extra_data) { + struct proxy *proxies = (struct proxy *)map_data; + + for (int k = 0; k < num_elements; k++) { + proxy_cells_exchange_first(&proxies[k]); + } +} + +struct wait_and_unpack_mapper_data { + struct space *s; + int num_proxies; + MPI_Request *reqs_in; + struct proxy *proxies; + int with_gravity; + swift_lock_type lock; +}; + +void proxy_cells_wait_and_unpack_mapper(void *unused_map_data, int num_elements, + void *extra_data) { + + // MATTHIEU: This is currently unused. Scalar (non-threadpool) version is + // faster but we still need to explore why this happens. + + struct wait_and_unpack_mapper_data *data = + (struct wait_and_unpack_mapper_data *)extra_data; + + for (int k = 0; k < num_elements; k++) { + int pid = MPI_UNDEFINED; + MPI_Status status; + int res; + + /* We need a lock to prevent concurrent calls to MPI_Waitany on + the same array of requests since this is not supported in the MPI + standard (v3.1). This is not really a problem since the threads + would block inside MPI_Waitany anyway. */ + lock_lock(&data->lock); + if ((res = MPI_Waitany(data->num_proxies, data->reqs_in, &pid, &status)) != + MPI_SUCCESS || + pid == MPI_UNDEFINED) + mpi_error(res, "MPI_Waitany failed."); + if (lock_unlock(&data->lock) != 0) { + error("Failed to release lock."); + } + + // message( "cell data from proxy %i has arrived." , pid ); + for (int count = 0, j = 0; j < data->proxies[pid].nr_cells_in; j++) + count += cell_unpack(&data->proxies[pid].pcells_in[count], + data->proxies[pid].cells_in[j], data->s, + data->with_gravity); + } +} + +#endif // WITH_MPI + /** * @brief Exchange the cell structures with all proxies. * @@ -294,13 +381,14 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, /* Run through the cells and get the size of the ones that will be sent off. */ + threadpool_map(&s->e->threadpool, proxy_cells_count_mapper, s->cells_top, + s->nr_cells, sizeof(struct cell), /*chunk=*/0, + /*extra_data=*/NULL); int count_out = 0; int offset[s->nr_cells]; for (int k = 0; k < s->nr_cells; k++) { offset[k] = count_out; - if (s->cells_top[k].mpi.sendto) - count_out += - (s->cells_top[k].mpi.pcell_size = cell_getsize(&s->cells_top[k])); + if (s->cells_top[k].mpi.sendto) count_out += s->cells_top[k].mpi.pcell_size; } if (s->e->verbose) @@ -316,19 +404,19 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, tic2 = getticks(); /* Pack the cells. */ - for (int k = 0; k < s->nr_cells; k++) - if (s->cells_top[k].mpi.sendto) { - cell_pack(&s->cells_top[k], &pcells[offset[k]], with_gravity); - s->cells_top[k].mpi.pcell = &pcells[offset[k]]; - } + struct pack_mapper_data data = {s, offset, pcells, with_gravity}; + threadpool_map(&s->e->threadpool, proxy_cells_pack_mapper, s->cells_top, + s->nr_cells, sizeof(struct cell), /*chunk=*/0, &data); if (s->e->verbose) message("Packing cells took %.3f %s.", clocks_from_ticks(getticks() - tic2), clocks_getunit()); /* Launch the first part of the exchange. */ + threadpool_map(&s->e->threadpool, proxy_cells_exchange_first_mapper, proxies, + num_proxies, sizeof(struct proxy), /*chunk=*/0, + /*extra_data=*/NULL); for (int k = 0; k < num_proxies; k++) { - proxy_cells_exchange_first(&proxies[k]); reqs_in[k] = proxies[k].req_cells_count_in; reqs_out[k] = proxies[k].req_cells_count_out; } diff --git a/src/random.h b/src/random.h new file mode 100644 index 0000000000000000000000000000000000000000..4d665a2697076a139c6e4e614b223302b04ad7a6 --- /dev/null +++ b/src/random.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_RANDOM_H +#define SWIFT_RANDOM_H + +/* COde configuration */ +#include "../config.h" + +/* Standard header */ +#include <stdlib.h> + +/** + * @brief The categories of random number generated. + * + * The values of the fields are carefully chose prime + * numbers. Only change them if you know what you are + * doing! + */ +enum random_number_type { + random_number_star_formation = 7, + random_number_stellar_feedback = 53, + random_number_stellar_enrichment = 197, + random_number_BH_feedback = 491 +}; + +/** + * @brief Returns a pseudo-random number in the range [0, 1[. + * + * We generate numbers that are always reproducible for a given particle ID and + * simulation time (on the integer time-line). If more than one number per + * time-step per particle is needed, additional randomness can be obtained by + * using the type argument. + * + * @param id The ID of the particle for which to generate a number. + * @param ti_current The time (on the time-line) for which to generate a number. + * @param type The #random_number_type to generate. + * @return a random number in the interval [0, 1.[. + */ +INLINE static double random_unit_interval(const long long int id, + const integertime_t ti_current, + const enum random_number_type type) { + + /* Range used for the seeds. Best if prime */ + static const long long seed_range = RAND_MAX; + static const double RAND_MAX_inv = 1. / ((double)RAND_MAX); + + /* Calculate the seed */ + /* WARNING: Only change the math if you really know what you are doing! + The numbers are carefully chosen prime numbers that prevent correlation + with either the current integer time or the particle IDs. + The calculation overflows on purpose. */ + unsigned int seed = ((937LL * id + 1109LL) % 2147987LL + + (ti_current - 1LL) % 1514917LL + (long long)type) % + seed_range; + + /* Generate a random number between 0 and 1. */ + return rand_r(&seed) * RAND_MAX_inv; +} + +#endif /* SWIFT_RANDOM_H */ diff --git a/src/runner.c b/src/runner.c index 61537b7a667a2888e82aceab85d4aa7ad7707917..4401236fe7216b1c14417a5ea58d8492d144ca86 100644 --- a/src/runner.c +++ b/src/runner.c @@ -48,6 +48,7 @@ #include "debug.h" #include "drift.h" #include "engine.h" +#include "entropy_floor.h" #include "error.h" #include "gravity.h" #include "hydro.h" @@ -58,13 +59,15 @@ #include "runner_doiact_vec.h" #include "scheduler.h" #include "sort_part.h" -#include "sourceterms.h" #include "space.h" #include "space_getsid.h" +#include "star_formation.h" #include "stars.h" #include "task.h" #include "timers.h" #include "timestep.h" +#include "timestep_limiter.h" +#include "tracers.h" #define TASK_LOOP_DENSITY 0 #define TASK_LOOP_GRADIENT 1 @@ -94,47 +97,25 @@ #undef FUNCTION #undef FUNCTION_TASK_LOOP +/* Import the limiter loop functions. */ +#define FUNCTION limiter +#define FUNCTION_TASK_LOOP TASK_LOOP_LIMITER +#include "runner_doiact.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP + /* Import the gravity loop functions. */ #include "runner_doiact_grav.h" -/* Import the stars loop functions. */ +/* Import the stars density loop functions. */ +#define FUNCTION density #include "runner_doiact_stars.h" +#undef FUNCTION -/** - * @brief Perform source terms - * - * @param r runner task - * @param c cell - * @param timer 1 if the time is to be recorded. - */ -void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) { - const int count = c->hydro.count; - const double cell_min[3] = {c->loc[0], c->loc[1], c->loc[2]}; - const double cell_width[3] = {c->width[0], c->width[1], c->width[2]}; - struct sourceterms *sourceterms = r->e->sourceterms; - const int dimen = 3; - - TIMER_TIC; - - /* Recurse? */ - if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_sourceterms(r, c->progeny[k], 0); - } else { - - if (count > 0) { - - /* do sourceterms in this cell? */ - const int incell = - sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen); - if (incell == 1) { - sourceterms_apply(r, sourceterms, c); - } - } - } - - if (timer) TIMER_TOC(timer_dosource); -} +/* Import the stars feedback loop functions. */ +#define FUNCTION feedback +#include "runner_doiact_stars.h" +#undef FUNCTION /** * @brief Intermediate task after the density to check that the smoothing @@ -337,7 +318,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { free(sid); } - if (timer) TIMER_TOC(timer_do_stars_ghost); + if (timer) TIMER_TOC(timer_dostars_ghost); } /** @@ -435,6 +416,7 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { const struct phys_const *constants = e->physical_constants; const struct unit_system *us = e->internal_units; const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor_props = e->entropy_floor; const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; struct part *restrict parts = c->hydro.parts; @@ -478,8 +460,9 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { } /* Let's cool ! */ - cooling_cool_part(constants, us, cosmo, hydro_props, cooling_func, p, - xp, dt_cool, dt_therm); + cooling_cool_part(constants, us, cosmo, hydro_props, + entropy_floor_props, cooling_func, p, xp, dt_cool, + dt_therm); } } } @@ -492,11 +475,19 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { */ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { - const struct engine *e = r->e; + struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; + const struct star_formation *sf_props = e->star_formation; + const struct phys_const *phys_const = e->physical_constants; const int count = c->hydro.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct hydro_props *restrict hydro_props = e->hydro_properties; + const struct unit_system *restrict us = e->internal_units; + struct cooling_function_data *restrict cooling = e->cooling_func; + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; TIMER_TIC; @@ -516,18 +507,52 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; + /* Only work on active particles */ if (part_is_active(p, e)) { - const float rho = hydro_get_physical_density(p, cosmo); + /* Is this particle star forming? */ + if (star_formation_is_star_forming(p, xp, sf_props, phys_const, cosmo, + hydro_props, us, cooling)) { - // MATTHIEU: Temporary star-formation law - // Do not use this at home. - if (rho > 1.5e7 && e->step > 2) { - message("Removing particle id=%lld rho=%e", p->id, rho); - cell_convert_part_to_gpart(e, c, p, xp); - } - } - } + /* Time-step size for this particle */ + double dt_star; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_star = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + + } else { + dt_star = get_timestep(p->time_bin, time_base); + } + + /* Compute the SF rate of the particle */ + star_formation_compute_SFR(p, xp, sf_props, phys_const, cosmo, + dt_star); + + /* Are we forming a star particle from this SF rate? */ + if (star_formation_should_convert_to_star(p, xp, sf_props, e, + dt_star)) { + + /* Convert the gas particle to a star particle */ + struct spart *sp = cell_convert_part_to_spart(e, c, p, xp); + + /* Copy the properties of the gas particle to the star particle */ + star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, + with_cosmology); + } + + } else { /* Are we not star-forming? */ + + /* Update the particle to flag it as not star-forming */ + star_formation_update_part_not_SFR(p, xp, e, sf_props, + with_cosmology); + + } /* Not Star-forming? */ + } /* is active? */ + } /* Loop over particles */ } if (timer) TIMER_TOC(timer_do_star_formation); @@ -611,26 +636,30 @@ void runner_do_sort_ascending(struct entry *sort, int N) { } } +#ifdef SWIFT_DEBUG_CHECKS /** * @brief Recursively checks that the flags are consistent in a cell hierarchy. * - * Debugging function. - * - * @param c The #cell to check. - * @param flags The sorting flags to check. + * Debugging function. Exists in two flavours: hydro & stars. */ -void runner_check_sorts(struct cell *c, int flags) { - -#ifdef SWIFT_DEBUG_CHECKS - if (flags & ~c->hydro.sorted) error("Inconsistent sort flags (downward)!"); - if (c->split) - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) - runner_check_sorts(c->progeny[k], c->hydro.sorted); +#define RUNNER_CHECK_SORTS(TYPE) \ + void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ + \ + if (flags & ~c->TYPE.sorted) error("Inconsistent sort flags (downward)!"); \ + if (c->split) \ + for (int k = 0; k < 8; k++) \ + if (c->progeny[k] != NULL && c->progeny[k]->TYPE.count > 0) \ + runner_check_sorts_##TYPE(c->progeny[k], c->TYPE.sorted); \ + } #else - error("Calling debugging code without debugging flag activated."); +#define RUNNER_CHECK_SORTS(TYPE) \ + void runner_check_sorts_##TYPE(struct cell *c, int flags) { \ + error("Calling debugging code without debugging flag activated."); \ + } #endif -} + +RUNNER_CHECK_SORTS(hydro) +RUNNER_CHECK_SORTS(stars) /** * @brief Sort the particles in the given cell along all cardinal directions. @@ -643,8 +672,8 @@ void runner_check_sorts(struct cell *c, int flags) { * @param clock Flag indicating whether to record the timing or not, needed * for recursive calls. */ -void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup, - int clock) { +void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, + int cleanup, int clock) { struct entry *fingers[8]; const int count = c->hydro.count; @@ -669,7 +698,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup, #ifdef SWIFT_DEBUG_CHECKS /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts(c, c->hydro.sorted); + runner_check_sorts_hydro(c, c->hydro.sorted); /* Make sure the sort flags are consistent (upard). */ for (struct cell *finger = c->parent; finger != NULL; @@ -701,10 +730,10 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup, for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) { /* Only propagate cleanup if the progeny is stale. */ - runner_do_sort(r, c->progeny[k], flags, - cleanup && (c->progeny[k]->hydro.dx_max_sort > - space_maxreldx * c->progeny[k]->dmin), - 0); + runner_do_hydro_sort(r, c->progeny[k], flags, + cleanup && (c->progeny[k]->hydro.dx_max_sort_old > + space_maxreldx * c->progeny[k]->dmin), + 0); dx_max_sort = max(dx_max_sort, c->progeny[k]->hydro.dx_max_sort); dx_max_sort_old = max(dx_max_sort_old, c->progeny[k]->hydro.dx_max_sort_old); @@ -838,7 +867,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup, } /* Make sure the sort flags are consistent (downward). */ - runner_check_sorts(c, flags); + runner_check_sorts_hydro(c, flags); /* Make sure the sort flags are consistent (upward). */ for (struct cell *finger = c->parent; finger != NULL; @@ -856,6 +885,224 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup, if (clock) TIMER_TOC(timer_dosort); } +/** + * @brief Sort the stars particles in the given cell along all cardinal + * directions. + * + * @param r The #runner. + * @param c The #cell. + * @param flags Cell flag. + * @param cleanup If true, re-build the sorts for the selected flags instead + * of just adding them. + * @param clock Flag indicating whether to record the timing or not, needed + * for recursive calls. + */ +void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, + int cleanup, int clock) { + + struct entry *fingers[8]; + const int count = c->stars.count; + struct spart *sparts = c->stars.parts; + float buff[8]; + + TIMER_TIC; + + /* We need to do the local sorts plus whatever was requested further up. */ + flags |= c->stars.do_sort; + if (cleanup) { + c->stars.sorted = 0; + } else { + flags &= ~c->stars.sorted; + } + if (flags == 0 && !c->stars.do_sub_sort) return; + + /* Check that the particles have been moved to the current time */ + if (flags && !cell_are_spart_drifted(c, r->e)) + error("Sorting un-drifted cell c->nodeID=%d", c->nodeID); + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_stars(c, c->stars.sorted); + + /* Make sure the sort flags are consistent (upward). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->stars.sorted & ~c->stars.sorted) + error("Inconsistent sort flags (upward)."); + } + + /* Update the sort timer which represents the last time the sorts + were re-set. */ + if (c->stars.sorted == 0) c->stars.ti_sort = r->e->ti_current; +#endif + + /* start by allocating the entry arrays in the requested dimensions. */ + for (int j = 0; j < 13; j++) { + if ((flags & (1 << j)) && c->stars.sort[j] == NULL) { + if ((c->stars.sort[j] = (struct entry *)malloc(sizeof(struct entry) * + (count + 1))) == NULL) + error("Failed to allocate sort memory."); + } + } + + /* Does this cell have any progeny? */ + if (c->split) { + + /* Fill in the gaps within the progeny. */ + float dx_max_sort = 0.0f; + float dx_max_sort_old = 0.0f; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) { + /* Only propagate cleanup if the progeny is stale. */ + runner_do_stars_sort(r, c->progeny[k], flags, + cleanup && (c->progeny[k]->stars.dx_max_sort_old > + space_maxreldx * c->progeny[k]->dmin), + 0); + dx_max_sort = max(dx_max_sort, c->progeny[k]->stars.dx_max_sort); + dx_max_sort_old = + max(dx_max_sort_old, c->progeny[k]->stars.dx_max_sort_old); + } + } + c->stars.dx_max_sort = dx_max_sort; + c->stars.dx_max_sort_old = dx_max_sort_old; + + /* Loop over the 13 different sort arrays. */ + for (int j = 0; j < 13; j++) { + + /* Has this sort array been flagged? */ + if (!(flags & (1 << j))) continue; + + /* Init the particle index offsets. */ + int off[8]; + off[0] = 0; + for (int k = 1; k < 8; k++) + if (c->progeny[k - 1] != NULL) + off[k] = off[k - 1] + c->progeny[k - 1]->stars.count; + else + off[k] = off[k - 1]; + + /* Init the entries and indices. */ + int inds[8]; + for (int k = 0; k < 8; k++) { + inds[k] = k; + if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) { + fingers[k] = c->progeny[k]->stars.sort[j]; + buff[k] = fingers[k]->d; + off[k] = off[k]; + } else + buff[k] = FLT_MAX; + } + + /* Sort the buffer. */ + for (int i = 0; i < 7; i++) + for (int k = i + 1; k < 8; k++) + if (buff[inds[k]] < buff[inds[i]]) { + int temp_i = inds[i]; + inds[i] = inds[k]; + inds[k] = temp_i; + } + + /* For each entry in the new sort list. */ + struct entry *finger = c->stars.sort[j]; + for (int ind = 0; ind < count; ind++) { + + /* Copy the minimum into the new sort array. */ + finger[ind].d = buff[inds[0]]; + finger[ind].i = fingers[inds[0]]->i + off[inds[0]]; + + /* Update the buffer. */ + fingers[inds[0]] += 1; + buff[inds[0]] = fingers[inds[0]]->d; + + /* Find the smallest entry. */ + for (int k = 1; k < 8 && buff[inds[k]] < buff[inds[k - 1]]; k++) { + int temp_i = inds[k - 1]; + inds[k - 1] = inds[k]; + inds[k] = temp_i; + } + + } /* Merge. */ + + /* Add a sentinel. */ + c->stars.sort[j][count].d = FLT_MAX; + c->stars.sort[j][count].i = 0; + + /* Mark as sorted. */ + atomic_or(&c->stars.sorted, 1 << j); + + } /* loop over sort arrays. */ + + } /* progeny? */ + + /* Otherwise, just sort. */ + else { + + /* Reset the sort distance */ + if (c->stars.sorted == 0) { + + /* And the individual sort distances if we are a local cell */ + for (int k = 0; k < count; k++) { + sparts[k].x_diff_sort[0] = 0.0f; + sparts[k].x_diff_sort[1] = 0.0f; + sparts[k].x_diff_sort[2] = 0.0f; + } + c->stars.dx_max_sort_old = 0.f; + c->stars.dx_max_sort = 0.f; + } + + /* Fill the sort array. */ + for (int k = 0; k < count; k++) { + const double px[3] = {sparts[k].x[0], sparts[k].x[1], sparts[k].x[2]}; + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->stars.sort[j][k].i = k; + c->stars.sort[j][k].d = px[0] * runner_shift[j][0] + + px[1] * runner_shift[j][1] + + px[2] * runner_shift[j][2]; + } + } + + /* Add the sentinel and sort. */ + for (int j = 0; j < 13; j++) + if (flags & (1 << j)) { + c->stars.sort[j][count].d = FLT_MAX; + c->stars.sort[j][count].i = 0; + runner_do_sort_ascending(c->stars.sort[j], count); + atomic_or(&c->stars.sorted, 1 << j); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify the sorting. */ + for (int j = 0; j < 13; j++) { + if (!(flags & (1 << j))) continue; + struct entry *finger = c->stars.sort[j]; + for (int k = 1; k < count; k++) { + if (finger[k].d < finger[k - 1].d) + error("Sorting failed, ascending array."); + if (finger[k].i >= count) error("Sorting failed, indices borked."); + } + } + + /* Make sure the sort flags are consistent (downward). */ + runner_check_sorts_stars(c, flags); + + /* Make sure the sort flags are consistent (upward). */ + for (struct cell *finger = c->parent; finger != NULL; + finger = finger->parent) { + if (finger->stars.sorted & ~c->stars.sorted) + error("Inconsistent sort flags."); + } +#endif + + /* Clear the cell's sort flags. */ + c->stars.do_sort = 0; + c->stars.do_sub_sort = 0; + c->stars.requires_sorts = 0; + + if (clock) TIMER_TOC(timer_do_stars_sort); +} + /** * @brief Initialize the multipoles before the gravity calculation. * @@ -985,6 +1232,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { const struct cosmology *cosmo = e->cosmology; const struct chemistry_global_data *chemistry = e->chemistry; const float hydro_h_max = e->hydro_properties->h_max; + const float hydro_h_min = e->hydro_properties->h_min; const float eps = e->hydro_properties->h_tolerance; const float hydro_eta_dim = pow_dimension(e->hydro_properties->eta_neighbours); @@ -1002,13 +1250,26 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { if (c->progeny[k] != NULL) runner_do_ghost(r, c->progeny[k], 0); } else { - /* Init the list of active particles that have to be updated. */ + /* Init the list of active particles that have to be updated and their + * current smoothing lengths. */ int *pid = NULL; + float *h_0 = NULL; + float *left = NULL; + float *right = NULL; if ((pid = (int *)malloc(sizeof(int) * c->hydro.count)) == NULL) error("Can't allocate memory for pid."); + if ((h_0 = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for h_0."); + if ((left = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for left."); + if ((right = (float *)malloc(sizeof(float) * c->hydro.count)) == NULL) + error("Can't allocate memory for right."); for (int k = 0; k < c->hydro.count; k++) if (part_is_active(&parts[k], e)) { pid[count] = k; + h_0[count] = parts[k].h; + left[count] = 0.f; + right[count] = hydro_h_max; ++count; } @@ -1032,9 +1293,11 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { #endif /* Get some useful values */ + const float h_init = h_0[i]; const float h_old = p->h; const float h_old_dim = pow_dimension(h_old); const float h_old_dim_minus_one = pow_dimension_minus_one(h_old); + float h_new; int has_no_neighbours = 0; @@ -1060,12 +1323,24 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { p->density.wcount_dh * h_old_dim + hydro_dimension * p->density.wcount * h_old_dim_minus_one; + /* Improve the bisection bounds */ + if (n_sum < n_target) left[i] = max(left[i], h_old); + if (n_sum > n_target) right[i] = min(right[i], h_old); + +#ifdef SWIFT_DEBUG_CHECKS + /* Check the validity of the left and right bounds */ + if (left[i] > right[i]) + error("Invalid left (%e) and right (%e)", left[i], right[i]); +#endif + /* Skip if h is already h_max and we don't have enough neighbours */ - if ((p->h >= hydro_h_max) && (f < 0.f)) { + if (((p->h >= hydro_h_max) && (f < 0.f)) || + ((p->h <= hydro_h_min) && (f > 0.f))) { /* We have a particle whose smoothing length is already set (wants - * to be larger but has already hit the maximum). So, just tidy up - * as if the smoothing length had converged correctly */ + * to be larger but has already hit the maximum OR wants to be smaller + * but has already reached the minimum). So, just tidy up as if the + * smoothing length had converged correctly */ #ifdef EXTRA_HYDRO_LOOP @@ -1122,6 +1397,17 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Avoid floating point exception from f_prime = 0 */ h_new = h_old - f / (f_prime + FLT_MIN); + /* Be verbose about the particles that struggle to converge */ + if (num_reruns > max_smoothing_iter - 10) { + + message( + "Smoothing length convergence problem: iter=%d p->id=%lld " + "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f " + "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e", + num_reruns, p->id, h_init, h_old, h_new, f, f_prime, n_sum, + n_target, left[i], right[i]); + } + #ifdef SWIFT_DEBUG_CHECKS if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old)) error( @@ -1131,19 +1417,39 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Safety check: truncate to the range [ h_old/2 , 2h_old ]. */ h_new = min(h_new, 2.f * h_old); h_new = max(h_new, 0.5f * h_old); + + /* Verify that we are actually progrssing towards the answer */ + h_new = max(h_new, left[i]); + h_new = min(h_new, right[i]); } /* Check whether the particle has an inappropriate smoothing length */ if (fabsf(h_new - h_old) > eps * h_old) { /* Ok, correct then */ - p->h = h_new; - /* If below the absolute maximum, try again */ - if (p->h < hydro_h_max) { + /* Case where we have been oscillating around the solution */ + if ((h_new == left[i] && h_old == right[i]) || + (h_old == left[i] && h_new == right[i])) { + + /* Bissect the remaining interval */ + p->h = pow_inv_dimension( + 0.5f * (pow_dimension(left[i]) + pow_dimension(right[i]))); + + } else { + + /* Normal case */ + p->h = h_new; + } + + /* If within the allowed range, try again */ + if (p->h < hydro_h_max && p->h > hydro_h_min) { /* Flag for another round of fun */ pid[redo] = pid[i]; + h_0[redo] = h_0[i]; + left[redo] = left[i]; + right[redo] = right[i]; redo += 1; /* Re-initialise everything */ @@ -1153,7 +1459,12 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Off we go ! */ continue; - } else { + } else if (p->h <= hydro_h_min) { + + /* Ok, this particle is a lost cause... */ + p->h = hydro_h_min; + + } else if (p->h >= hydro_h_max) { /* Ok, this particle is a lost cause... */ p->h = hydro_h_max; @@ -1274,7 +1585,10 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { } /* Be clean */ + free(left); + free(right); free(pid); + free(h_0); } if (timer) TIMER_TOC(timer_do_ghost); @@ -1441,6 +1755,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; const int with_cosmology = (e->policy & engine_policy_cosmology); struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; @@ -1473,19 +1788,26 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { /* If particle needs to be kicked */ if (part_is_starting(p, e)) { +#ifdef SWIFT_DEBUG_CHECKS + if (p->wakeup == time_bin_awake) + error("Woken-up particle that has not been processed in kick1"); +#endif + + /* Skip particles that have been woken up and treated by the limiter. */ + if (p->wakeup != time_bin_not_awake) continue; + const integertime_t ti_step = get_integer_timestep(p->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, p->time_bin); #ifdef SWIFT_DEBUG_CHECKS - const integertime_t ti_end = - get_integer_time_end(ti_current + 1, p->time_bin); + const integertime_t ti_end = ti_begin + ti_step; if (ti_begin != ti_current) error( "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " - "ti_step=%lld time_bin=%d ti_current=%lld", - ti_end, ti_begin, ti_step, p->time_bin, ti_current); + "ti_step=%lld time_bin=%d wakeup=%d ti_current=%lld", + ti_end, ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); #endif /* Time interval for this half-kick */ @@ -1508,7 +1830,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { /* do the kick */ kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, - dt_kick_corr, cosmo, hydro_props, ti_begin, + dt_kick_corr, cosmo, hydro_props, entropy_floor, ti_begin, ti_begin + ti_step / 2); /* Update the accelerations to be used in the drift for hydro */ @@ -1615,6 +1937,7 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; const struct hydro_props *hydro_props = e->hydro_properties; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; const int with_cosmology = (e->policy & engine_policy_cosmology); const int count = c->hydro.count; const int gcount = c->grav.count; @@ -1647,39 +1970,60 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { /* If particle needs to be kicked */ if (part_is_active(p, e)) { - const integertime_t ti_step = get_integer_timestep(p->time_bin); - const integertime_t ti_begin = - get_integer_time_begin(ti_current, p->time_bin); + integertime_t ti_begin, ti_end, ti_step; + +#ifdef SWIFT_DEBUG_CHECKS + if (p->wakeup == time_bin_awake) + error("Woken-up particle that has not been processed in kick1"); +#endif + + if (p->wakeup == time_bin_not_awake) { + + /* Time-step from a regular kick */ + ti_step = get_integer_timestep(p->time_bin); + ti_begin = get_integer_time_begin(ti_current, p->time_bin); + ti_end = ti_begin + ti_step; + + } else { + + /* Time-step that follows a wake-up call */ + ti_begin = get_integer_time_begin(ti_current, p->wakeup); + ti_end = get_integer_time_end(ti_current, p->time_bin); + ti_step = ti_end - ti_begin; + + /* Reset the flag. Everything is back to normal from now on. */ + p->wakeup = time_bin_awake; + } #ifdef SWIFT_DEBUG_CHECKS if (ti_begin + ti_step != ti_current) error( "Particle in wrong time-bin, ti_begin=%lld, ti_step=%lld " - "time_bin=%d ti_current=%lld", - ti_begin, ti_step, p->time_bin, ti_current); + "time_bin=%d wakeup=%d ti_current=%lld", + ti_begin, ti_step, p->time_bin, p->wakeup, ti_current); #endif /* Time interval for this half-kick */ double dt_kick_grav, dt_kick_hydro, dt_kick_therm, dt_kick_corr; if (with_cosmology) { dt_kick_hydro = cosmology_get_hydro_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + cosmo, ti_begin + ti_step / 2, ti_end); dt_kick_grav = cosmology_get_grav_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + cosmo, ti_begin + ti_step / 2, ti_end); dt_kick_therm = cosmology_get_therm_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + cosmo, ti_begin + ti_step / 2, ti_end); dt_kick_corr = cosmology_get_corr_kick_factor( - cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + cosmo, ti_begin + ti_step / 2, ti_end); } else { - dt_kick_hydro = (ti_step / 2) * time_base; - dt_kick_grav = (ti_step / 2) * time_base; - dt_kick_therm = (ti_step / 2) * time_base; - dt_kick_corr = (ti_step / 2) * time_base; + dt_kick_hydro = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_grav = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_therm = (ti_end - (ti_begin + ti_step / 2)) * time_base; + dt_kick_corr = (ti_end - (ti_begin + ti_step / 2)) * time_base; } /* Finish the time-step with a second half-kick */ kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, - dt_kick_corr, cosmo, hydro_props, ti_begin + ti_step / 2, - ti_begin + ti_step); + dt_kick_corr, cosmo, hydro_props, entropy_floor, + ti_begin + ti_step / 2, ti_end); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ @@ -1790,6 +2134,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const integertime_t ti_current = e->ti_current; + const int with_cosmology = (e->policy & engine_policy_cosmology); const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; @@ -1814,6 +2159,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps; /* No children? */ if (!c->split) { @@ -1844,6 +2190,11 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { p->time_bin = get_time_bin(ti_new_step); if (p->gpart != NULL) p->gpart->time_bin = p->time_bin; + /* Update the tracers properties */ + tracers_after_timestep(p, xp, e->internal_units, e->physical_constants, + with_cosmology, e->cosmology, + e->hydro_properties, e->cooling_func, e->time); + /* Number of updated particles */ updated++; if (p->gpart != NULL) g_updated++; @@ -1990,6 +2341,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); + ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min); + /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); @@ -2006,6 +2359,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { ti_gravity_end_min = min(ti_end, ti_gravity_end_min); ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + ti_stars_end_min = min(ti_end, ti_stars_end_min); + const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, sp->time_bin); @@ -2036,6 +2391,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); + ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min); } } @@ -2052,6 +2408,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_end_max = ti_gravity_end_max; c->grav.ti_beg_max = ti_gravity_beg_max; + c->stars.ti_end_min = ti_stars_end_min; #ifdef SWIFT_DEBUG_CHECKS if (c->hydro.ti_end_min == e->ti_current && @@ -2060,11 +2417,152 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { if (c->grav.ti_end_min == e->ti_current && c->grav.ti_end_min < max_nr_timesteps) error("End of next gravity step is current time!"); + if (c->stars.ti_end_min == e->ti_current && + c->stars.ti_end_min < max_nr_timesteps) + error("End of next stars step is current time!"); #endif if (timer) TIMER_TOC(timer_timestep); } +/** + * @brief Apply the time-step limiter to all awaken particles in a cell + * hierarchy. + * + * @param r The task #runner. + * @param c The #cell. + * @param force Limit the particles irrespective of the #cell flags. + * @param timer Are we timing this ? + */ +void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer) { + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const int count = c->hydro.count; + struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; + + TIMER_TIC; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we only limit local cells. */ + if (c->nodeID != engine_rank) error("Limiting dt of a foreign cell is nope."); +#endif + + integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, + ti_hydro_beg_max = 0; + integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, + ti_gravity_beg_max = 0; + + /* Limit irrespective of cell flags? */ + force |= c->hydro.do_limiter; + + /* Early abort? */ + if (c->hydro.count == 0) { + + /* Clear the limiter flags. */ + c->hydro.do_limiter = 0; + c->hydro.do_sub_limiter = 0; + return; + } + + /* Loop over the progeny ? */ + if (c->split && (force || c->hydro.do_sub_limiter)) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *restrict cp = c->progeny[k]; + + /* Recurse */ + runner_do_limiter(r, cp, force, 0); + + /* And aggregate */ + ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); + ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); + ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); + ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); + ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); + ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); + } + } + + /* Store the updated values */ + c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); + c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); + c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); + c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); + c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); + c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); + + } else if (!c->split && force) { + + ti_hydro_end_min = c->hydro.ti_end_min; + ti_hydro_end_max = c->hydro.ti_end_max; + ti_hydro_beg_max = c->hydro.ti_beg_max; + ti_gravity_end_min = c->grav.ti_end_min; + ti_gravity_end_max = c->grav.ti_end_max; + ti_gravity_beg_max = c->grav.ti_beg_max; + + /* Loop over the gas particles in this cell. */ + for (int k = 0; k < count; k++) { + + /* Get a handle on the part. */ + struct part *restrict p = &parts[k]; + struct xpart *restrict xp = &xparts[k]; + + /* Avoid inhibited particles */ + if (part_is_inhibited(p, e)) continue; + + /* If the particle will be active no need to wake it up */ + if (part_is_active(p, e) && p->wakeup != time_bin_not_awake) + p->wakeup = time_bin_not_awake; + + /* Bip, bip, bip... wake-up time */ + if (p->wakeup == time_bin_awake) { + + /* Apply the limiter and get the new time-step size */ + const integertime_t ti_new_step = timestep_limit_part(p, xp, e); + + /* What is the next sync-point ? */ + ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); + ti_hydro_end_max = max(ti_current + ti_new_step, ti_hydro_end_max); + + /* What is the next starting point for this cell ? */ + ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); + + /* Also limit the gpart counter-part */ + if (p->gpart != NULL) { + + /* Register the time-bin */ + p->gpart->time_bin = p->time_bin; + + /* What is the next sync-point ? */ + ti_gravity_end_min = + min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = + max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + } + } + } + + /* Store the updated values */ + c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); + c->hydro.ti_end_max = max(c->hydro.ti_end_max, ti_hydro_end_max); + c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); + c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); + c->grav.ti_end_max = max(c->grav.ti_end_max, ti_gravity_end_max); + c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); + } + + /* Clear the limiter flags. */ + c->hydro.do_limiter = 0; + c->hydro.do_sub_limiter = 0; + + if (timer) TIMER_TOC(timer_do_limiter); +} + /** * @brief End the force calculation of all active particles in a cell * by multiplying the acccelerations by the relevant constants @@ -2517,12 +3015,16 @@ void *runner_main(void *data) { #endif else if (t->subtype == task_subtype_force) runner_doself2_branch_force(r, ci); + else if (t->subtype == task_subtype_limiter) + runner_doself2_branch_limiter(r, ci); else if (t->subtype == task_subtype_grav) runner_doself_recursive_grav(r, ci, 1); else if (t->subtype == task_subtype_external_grav) runner_do_grav_external(r, ci, 1); else if (t->subtype == task_subtype_stars_density) runner_doself_stars_density(r, ci, 1); + else if (t->subtype == task_subtype_stars_feedback) + runner_doself_stars_feedback(r, ci, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -2536,10 +3038,14 @@ void *runner_main(void *data) { #endif else if (t->subtype == task_subtype_force) runner_dopair2_branch_force(r, ci, cj); + else if (t->subtype == task_subtype_limiter) + runner_dopair2_branch_limiter(r, ci, cj); else if (t->subtype == task_subtype_grav) runner_dopair_recursive_grav(r, ci, cj, 1); else if (t->subtype == task_subtype_stars_density) runner_dopair_stars_density(r, ci, cj, 1); + else if (t->subtype == task_subtype_stars_feedback) + runner_dopair_stars_feedback(r, ci, cj, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -2553,8 +3059,12 @@ void *runner_main(void *data) { #endif else if (t->subtype == task_subtype_force) runner_dosub_self2_force(r, ci, 1); + else if (t->subtype == task_subtype_limiter) + runner_dosub_self2_limiter(r, ci, 1); else if (t->subtype == task_subtype_stars_density) runner_dosub_self_stars_density(r, ci, 1); + else if (t->subtype == task_subtype_stars_feedback) + runner_dosub_self_stars_feedback(r, ci, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; @@ -2568,17 +3078,29 @@ void *runner_main(void *data) { #endif else if (t->subtype == task_subtype_force) runner_dosub_pair2_force(r, ci, cj, t->flags, 1); + else if (t->subtype == task_subtype_limiter) + runner_dosub_pair2_limiter(r, ci, cj, t->flags, 1); else if (t->subtype == task_subtype_stars_density) runner_dosub_pair_stars_density(r, ci, cj, t->flags, 1); + else if (t->subtype == task_subtype_stars_feedback) + runner_dosub_pair_stars_feedback(r, ci, cj, t->flags, 1); else error("Unknown/invalid task subtype (%d).", t->subtype); break; case task_type_sort: /* Cleanup only if any of the indices went stale. */ - runner_do_sort(r, ci, t->flags, - ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, - 1); + runner_do_hydro_sort( + r, ci, t->flags, + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1); + /* Reset the sort flags as our work here is done. */ + t->flags = 0; + break; + case task_type_stars_sort: + /* Cleanup only if any of the indices went stale. */ + runner_do_stars_sort( + r, ci, t->flags, + ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin, 1); /* Reset the sort flags as our work here is done. */ t->flags = 0; break; @@ -2617,6 +3139,9 @@ void *runner_main(void *data) { case task_type_timestep: runner_do_timestep(r, ci, 1); break; + case task_type_timestep_limiter: + runner_do_limiter(r, ci, 0, 1); + break; #ifdef WITH_MPI case task_type_send: if (t->subtype == task_subtype_tend) { @@ -2633,6 +3158,8 @@ void *runner_main(void *data) { runner_do_recv_part(r, ci, 0, 1); } else if (t->subtype == task_subtype_gradient) { runner_do_recv_part(r, ci, 0, 1); + } else if (t->subtype == task_subtype_limiter) { + runner_do_recv_part(r, ci, 0, 1); } else if (t->subtype == task_subtype_gpart) { runner_do_recv_gpart(r, ci, 1); } else if (t->subtype == task_subtype_spart) { @@ -2663,9 +3190,6 @@ void *runner_main(void *data) { case task_type_star_formation: runner_do_star_formation(r, t->ci, 1); break; - case task_type_sourceterms: - runner_do_sourceterms(r, t->ci, 1); - break; case task_type_fof_self: runner_do_fof_self(r, t->ci, 1); break; @@ -2741,9 +3265,13 @@ void runner_do_logger(struct runner *r, struct cell *c, int timer) { /* Write particle */ /* Currently writing everything, should adapt it through time */ logger_log_part(e->logger, p, - logger_mask_x | logger_mask_v | logger_mask_a | - logger_mask_u | logger_mask_h | logger_mask_rho | - logger_mask_consts, + logger_mask_data[logger_x].mask | + logger_mask_data[logger_v].mask | + logger_mask_data[logger_a].mask | + logger_mask_data[logger_u].mask | + logger_mask_data[logger_h].mask | + logger_mask_data[logger_rho].mask | + logger_mask_data[logger_consts].mask, &xp->logger_data.last_offset); /* Set counter back to zero */ diff --git a/src/runner.h b/src/runner.h index 63c3d253a609046de3eeb9a3085ca5a7f0234446..9218deb15e30fa242b8ebc1f6be2d942d2267d74 100644 --- a/src/runner.h +++ b/src/runner.h @@ -69,8 +69,10 @@ struct runner { /* Function prototypes. */ void runner_do_ghost(struct runner *r, struct cell *c, int timer); void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer); -void runner_do_sort(struct runner *r, struct cell *c, int flag, int cleanup, - int clock); +void runner_do_hydro_sort(struct runner *r, struct cell *c, int flag, + int cleanup, int clock); +void runner_do_stars_sort(struct runner *r, struct cell *c, int flag, + int cleanup, int clock); void runner_do_drift_part(struct runner *r, struct cell *c, int timer); void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer); void runner_do_kick1(struct runner *r, struct cell *c, int timer); diff --git a/src/runner_doiact.h b/src/runner_doiact.h index 53cf51ed400f82d0e195e38dd08fcc5af16f1ad7..861798b70b8ba90b9267375253bd8570baec3e9a 100644 --- a/src/runner_doiact.h +++ b/src/runner_doiact.h @@ -168,8 +168,11 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts_i[pid]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const int pi_active = part_is_active(pi, e); - const int pi_inhibited = part_is_inhibited(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), @@ -181,10 +184,13 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; const int pj_active = part_is_active(pj, e); - const int pj_inhibited = part_is_inhibited(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), @@ -195,21 +201,21 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hig2 && pi_active && !pj_inhibited) { + if (r2 < hig2 && pi_active) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); #endif } - if (r2 < hjg2 && pj_active && !pi_inhibited) { + if (r2 < hjg2 && pj_active) { dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -270,8 +276,11 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts_i[pid]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const int pi_active = part_is_active(pi, e); - const int pi_inhibited = part_is_inhibited(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), @@ -283,8 +292,11 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const int pj_active = part_is_active(pj, e); - const int pj_inhibited = part_is_inhibited(pj, e); const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; @@ -297,28 +309,28 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pj_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pi_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ if (r2 < hig2 || r2 < hjg2) { - if (pi_active && pj_active && !pi_inhibited && !pj_inhibited) { + if (pi_active && pj_active) { IACT(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); #endif - } else if (pi_active && !pj_inhibited) { + } else if (pi_active) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); #endif - } else if (pj_active && !pi_inhibited) { + } else if (pj_active) { dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -366,8 +378,11 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const int pi_active = part_is_active(pi, e); - const int pi_inhibited = part_is_inhibited(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - c->loc[0]), @@ -379,10 +394,13 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; const int pj_active = part_is_active(pj, e); - const int pj_inhibited = part_is_inhibited(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), @@ -391,14 +409,14 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - const int doi = pi_active && (r2 < hig2) && !pj_inhibited; - const int doj = pj_active && (r2 < hjg2) && !pi_inhibited; + const int doi = pi_active && (r2 < hig2); + const int doj = pj_active && (r2 < hjg2); #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif @@ -462,8 +480,11 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts[pid]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const int pi_active = part_is_active(pi, e); - const int pi_inhibited = part_is_inhibited(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; const float pix[3] = {(float)(pi->x[0] - c->loc[0]), @@ -475,10 +496,13 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const float hjg2 = hj * hj * kernel_gamma2; const int pj_active = part_is_active(pj, e); - const int pj_inhibited = part_is_inhibited(pj, e); /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), @@ -487,16 +511,14 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; - const int doi = - pi_active && ((r2 < hig2) || (r2 < hjg2)) && !pj_inhibited; - const int doj = - pj_active && ((r2 < hig2) || (r2 < hjg2)) && !pi_inhibited; + const int doi = pi_active && ((r2 < hig2) || (r2 < hjg2)); + const int doj = pj_active && ((r2 < hig2) || (r2 < hjg2)); #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif @@ -581,7 +603,9 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; /* Compute the pairwise distance. */ float r2 = 0.0f; @@ -595,12 +619,12 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hig2 && !pj_inhibited) { + if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, pj->h, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -669,7 +693,10 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[sort_j[pjd].i]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const double pjx = pj->x[0]; const double pjy = pj->x[1]; @@ -684,12 +711,12 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hig2 && !pj_inhibited) { + if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -721,7 +748,10 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[sort_j[pjd].i]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const double pjx = pj->x[0]; const double pjy = pj->x[1]; @@ -736,12 +766,12 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hig2 && !pj_inhibited) { + if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -858,7 +888,10 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; /* Compute the pairwise distance. */ @@ -872,12 +905,12 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 > 0.f && r2 < hig2 && !pj_inhibited) { + if (r2 > 0.f && r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -992,7 +1025,10 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Recover pj */ struct part *pj = &parts_j[sort_j[pjd].i]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; const float pjx = pj->x[0] - cj->loc[0]; const float pjy = pj->x[1] - cj->loc[1]; @@ -1032,12 +1068,12 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hig2 && !pj_inhibited) { + if (r2 < hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -1076,7 +1112,10 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Recover pi */ struct part *pi = &parts_i[sort_i[pid].i]; - const int pi_inhibited = part_is_inhibited(pi, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const float hi = pi->h; const float pix = pi->x[0] - (cj->loc[0] + shift[0]); const float piy = pi->x[1] - (cj->loc[1] + shift[1]); @@ -1114,14 +1153,14 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, pjz, ci->width[2]); /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if (r2 < hjg2 && !pi_inhibited) { + if (r2 < hjg2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -1335,7 +1374,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Get a hold of the ith part in ci. */ struct part *pi = &parts_i[sort_i[pid].i]; - const int pi_inhibited = part_is_inhibited(pi, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const float hi = pi->h; /* Is there anything we need to interact with (for this specific hi) ? */ @@ -1397,7 +1439,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, pjz, ci->width[2]); /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); @@ -1405,7 +1447,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Hit or miss? (note that we will do the other condition in the reverse loop) */ - if (r2 < hig2 && !pi_inhibited) { + if (r2 < hig2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); @@ -1421,7 +1463,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Recover pj */ struct part *pj = &parts_j[sort_j[pjd].i]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; /* Get the position of pj in the right frame */ @@ -1461,14 +1506,14 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, pjz, ci->width[2]); /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? (note that we will do the other condition in the reverse loop) */ - if (r2 < hig2 && !pj_inhibited) { + if (r2 < hig2) { /* Does pj need to be updated too? */ if (part_is_active(pj, e)) { @@ -1496,7 +1541,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Get a hold of the jth part in cj. */ struct part *pj = &parts_j[sort_j[pjd].i]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; /* Is there anything we need to interact with (for this specific hj) ? */ @@ -1561,13 +1609,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? (note that we must avoid the r2 < hig2 cases we already processed) */ - if (r2 < hjg2 && r2 >= hig2 && !pj_inhibited) { + if (r2 < hjg2 && r2 >= hig2) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); @@ -1584,7 +1632,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Recover pi */ struct part *pi = &parts_i[sort_i[pid].i]; - const int pi_inhibited = part_is_inhibited(pi, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; @@ -1625,15 +1676,15 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, pjz, ci->width[2]); /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? (note that we must avoid the r2 < hig2 cases we already processed) */ - if (r2 < hjg2 && r2 >= hig2 && !pi_inhibited) { + if (r2 < hjg2 && r2 >= hig2) { /* Does pi need to be updated too? */ if (part_is_active(pi, e)) { @@ -1788,7 +1839,9 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; - const int pi_inhibited = part_is_inhibited(pi, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; /* Get the particle position and radius. */ double pix[3]; @@ -1808,7 +1861,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); @@ -1823,7 +1876,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { } /* Hit or miss? */ - if (r2 < hj * hj * kernel_gamma2 && !pi_inhibited) { + if (r2 < hj * hj * kernel_gamma2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -1844,7 +1897,10 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; /* Compute the pairwise distance. */ @@ -1861,9 +1917,9 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif @@ -1877,13 +1933,13 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); #endif - } else if (doi && !pj_inhibited) { + } else if (doi) { IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); #endif - } else if (doj && !pi_inhibited) { + } else if (doj) { dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -1972,7 +2028,9 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; - const int pi_inhibited = part_is_inhibited(pi, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; /* Get the particle position and radius. */ double pix[3]; @@ -2000,14 +2058,14 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if ((r2 < hig2 || r2 < hj * hj * kernel_gamma2) && !pi_inhibited) { + if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) @@ -2028,7 +2086,10 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; - const int pj_inhibited = part_is_inhibited(pj, e); + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + const float hj = pj->h; /* Compute the pairwise distance. */ @@ -2041,14 +2102,14 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ - if (pi->ti_drift != e->ti_current && !pi_inhibited) + if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); - if (pj->ti_drift != e->ti_current && !pj_inhibited) + if (pj->ti_drift != e->ti_current) error("Particle pj not drifted to current time"); #endif /* Hit or miss? */ - if ((r2 < hig2 || r2 < hj * hj * kernel_gamma2) && !pj_inhibited) { + if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { /* Does pj need to be updated too? */ if (part_is_active(pj, e)) { diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index 5bcd57d643911703bc0f4e19f17fdb63a11a5c12..c6885746a29fd7b6bd828496316f8dad01c1b7da 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -1714,7 +1714,7 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci, const int periodic = e->mesh->periodic; const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]}; const double theta_crit2 = e->gravity_properties->theta_crit2; - const double max_distance = e->mesh->r_cut_max; + const double max_distance2 = e->mesh->r_cut_max * e->mesh->r_cut_max; TIMER_TIC; @@ -1759,6 +1759,29 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci, /* Skip empty cells */ if (multi_j->m_pole.M_000 == 0.f) continue; + /* Can we escape early in the periodic BC case? */ + if (periodic) { + + /* Minimal distance between any pair of particles */ + const double min_radius2 = + cell_min_dist2_same_size(top, cj, periodic, dim); + + /* Are we beyond the distance where the truncated forces are 0 ?*/ + if (min_radius2 > max_distance2) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Need to account for the interactions we missed */ + multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; +#endif + + /* Record that this multipole received a contribution */ + multi_i->pot.interacted = 1; + + /* We are done here. */ + continue; + } + } + /* Get the distance between the CoMs at the last rebuild*/ double dx_r = CoM_rebuild_top[0] - multi_j->CoM_rebuild[0]; double dy_r = CoM_rebuild_top[1] - multi_j->CoM_rebuild[1]; @@ -1772,24 +1795,6 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci, } const double r2_rebuild = dx_r * dx_r + dy_r * dy_r + dz_r * dz_r; - const double max_radius = - sqrt(r2_rebuild) - (multi_top->r_max_rebuild + multi_j->r_max_rebuild); - - /* Are we beyond the distance where the truncated forces are 0 ?*/ - if (periodic && max_radius > max_distance) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Need to account for the interactions we missed */ - multi_i->pot.num_interacted += multi_j->m_pole.num_gpart; -#endif - - /* Record that this multipole received a contribution */ - multi_i->pot.interacted = 1; - - /* We are done here. */ - continue; - } - /* Are we in charge of this cell pair? */ if (gravity_M2L_accept(multi_top->r_max_rebuild, multi_j->r_max_rebuild, theta_crit2, r2_rebuild)) { diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h index e696e4fd10853536008d9d9fafc90e6475fd291a..e816d80399a0fef85645c914168dd4038f55988c 100644 --- a/src/runner_doiact_stars.h +++ b/src/runner_doiact_stars.h @@ -18,7 +18,53 @@ * ******************************************************************************/ -#include "swift.h" +/* Before including this file, define FUNCTION, which is the + name of the interaction function. This creates the interaction functions + runner_dopair_FUNCTION, runner_doself_FUNCTION and runner_dosub_FUNCTION + calling the pairwise interaction function runner_iact_FUNCTION. */ + +#define PASTE(x, y) x##_##y + +#define _DOSELF1_STARS(f) PASTE(runner_doself_stars, f) +#define DOSELF1_STARS _DOSELF1_STARS(FUNCTION) + +#define _DO_NONSYM_PAIR1_STARS(f) PASTE(runner_do_nonsym_pair_stars, f) +#define DO_NONSYM_PAIR1_STARS _DO_NONSYM_PAIR1_STARS(FUNCTION) + +#define _DOPAIR1_STARS(f) PASTE(runner_dopair_stars, f) +#define DOPAIR1_STARS _DOPAIR1_STARS(FUNCTION) + +#define _DOPAIR1_SUBSET_STARS(f) PASTE(runner_dopair_subset_stars, f) +#define DOPAIR1_SUBSET_STARS _DOPAIR1_SUBSET_STARS(FUNCTION) + +#define _DOSELF1_SUBSET_STARS(f) PASTE(runner_doself_subset_stars, f) +#define DOSELF1_SUBSET_STARS _DOSELF1_SUBSET_STARS(FUNCTION) + +#define _DOSELF1_SUBSET_BRANCH_STARS(f) \ + PASTE(runner_doself_subset_branch_stars, f) +#define DOSELF1_SUBSET_BRANCH_STARS _DOSELF1_SUBSET_BRANCH_STARS(FUNCTION) + +#define _DOPAIR1_SUBSET_BRANCH_STARS(f) \ + PASTE(runner_dopair_subset_branch_stars, f) +#define DOPAIR1_SUBSET_BRANCH_STARS _DOPAIR1_SUBSET_BRANCH_STARS(FUNCTION) + +#define _DOSUB_SUBSET_STARS(f) PASTE(runner_dosub_subset_stars, f) +#define DOSUB_SUBSET_STARS _DOSUB_SUBSET_STARS(FUNCTION) + +#define _DOSELF1_BRANCH_STARS(f) PASTE(runner_doself_branch_stars, f) +#define DOSELF1_BRANCH_STARS _DOSELF1_BRANCH_STARS(FUNCTION) + +#define _DOPAIR1_BRANCH_STARS(f) PASTE(runner_dopair_branch_stars, f) +#define DOPAIR1_BRANCH_STARS _DOPAIR1_BRANCH_STARS(FUNCTION) + +#define _DOSUB_PAIR1_STARS(f) PASTE(runner_dosub_pair_stars, f) +#define DOSUB_PAIR1_STARS _DOSUB_PAIR1_STARS(FUNCTION) + +#define _DOSUB_SELF1_STARS(f) PASTE(runner_dosub_self_stars, f) +#define DOSUB_SELF1_STARS _DOSUB_SELF1_STARS(FUNCTION) + +#define _IACT_STARS(f) PASTE(runner_iact_nonsym_stars, f) +#define IACT_STARS _IACT_STARS(FUNCTION) /** * @brief Calculate the number density of #part around the #spart @@ -27,14 +73,13 @@ * @param c cell * @param timer 1 if the time is to be recorded. */ -void runner_doself_stars_density(struct runner *r, struct cell *c, int timer) { +void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; - TIMER_TIC; - /* Anything to do here? */ if (!cell_is_active_stars(c, e)) return; + if (c->hydro.count == 0 && c->stars.count == 0) return; /* Cosmological terms */ const float a = cosmo->a; @@ -77,12 +122,10 @@ void runner_doself_stars_density(struct runner *r, struct cell *c, int timer) { #endif if (r2 > 0.f && r2 < hig2) { - runner_iact_nonsym_stars_density(r2, dx, hi, hj, si, pj, a, H); + IACT_STARS(r2, dx, hi, hj, si, pj, a, H); } } /* loop over the parts in ci. */ } /* loop over the sparts in ci. */ - - TIMER_TOC(timer_doself_stars_density); } /** @@ -92,14 +135,14 @@ void runner_doself_stars_density(struct runner *r, struct cell *c, int timer) { * @param ci The first #cell * @param cj The second #cell */ -void runner_dosubpair_stars_density(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj) { +void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; /* Anything to do here? */ - if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return; + if (!cell_is_active_stars(ci, e)) return; const int scount_i = ci->stars.count; const int count_j = cj->hydro.count; @@ -150,22 +193,19 @@ void runner_dosubpair_stars_density(struct runner *r, struct cell *restrict ci, error("Particle pj not drifted to current time"); #endif - if (r2 < hig2) - runner_iact_nonsym_stars_density(r2, dx, hi, hj, si, pj, a, H); + if (r2 < hig2) IACT_STARS(r2, dx, hi, hj, si, pj, a, H); } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ } -void runner_dopair_stars_density(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj, int timer) { - - TIMER_TIC; - - runner_dosubpair_stars_density(r, ci, cj); - runner_dosubpair_stars_density(r, cj, ci); +void DOPAIR1_STARS(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj, int timer) { - if (timer) TIMER_TOC(timer_dopair_stars_density); + if (ci->stars.count != 0 && cj->hydro.count != 0) + DO_NONSYM_PAIR1_STARS(r, ci, cj); + if (cj->stars.count != 0 && ci->hydro.count != 0) + DO_NONSYM_PAIR1_STARS(r, cj, ci); } /** @@ -182,18 +222,14 @@ void runner_dopair_stars_density(struct runner *r, struct cell *restrict ci, * @param cj The second #cell. * @param shift The shift vector to apply to the particles in ci. */ -void runner_dopair_subset_stars_density(struct runner *r, - struct cell *restrict ci, - struct spart *restrict sparts_i, - int *restrict ind, int scount, - struct cell *restrict cj, - const double *shift) { +void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, int *restrict ind, + int scount, struct cell *restrict cj, + const double *shift) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; - TIMER_TIC; - const int count_j = cj->hydro.count; struct part *restrict parts_j = cj->hydro.parts; @@ -237,12 +273,10 @@ void runner_dopair_subset_stars_density(struct runner *r, #endif /* Hit or miss? */ if (r2 < hig2) { - runner_iact_nonsym_stars_density(r2, dx, hi, pj->h, spi, pj, a, H); + IACT_STARS(r2, dx, hi, pj->h, spi, pj, a, H); } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ - - TIMER_TOC(timer_dopair_subset_naive); } /** @@ -255,16 +289,13 @@ void runner_dopair_subset_stars_density(struct runner *r, * @param ind The list of indices of particles in @c ci to interact with. * @param scount The number of particles in @c ind. */ -void runner_doself_subset_stars_density(struct runner *r, - struct cell *restrict ci, - struct spart *restrict sparts, - int *restrict ind, int scount) { +void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts, int *restrict ind, + int scount) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; - TIMER_TIC; - /* Cosmological terms */ const float a = cosmo->a; const float H = cosmo->H; @@ -310,36 +341,33 @@ void runner_doself_subset_stars_density(struct runner *r, /* Hit or miss? */ if (r2 > 0.f && r2 < hig2) { - runner_iact_nonsym_stars_density(r2, dx, hi, hj, spi, pj, a, H); + IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ - - TIMER_TOC(timer_doself_subset_stars_density); } /** -* @brief Determine which version of DOSELF_SUBSET needs to be called depending -* on the optimisation level. - -* @param r The #runner. -* @param ci The first #cell. -* @param sparts The #spart to interact. -* @param ind The list of indices of particles in @c ci to interact with. -* @param scount The number of particles in @c ind. -*/ -void runner_doself_subset_branch_stars_density(struct runner *r, - struct cell *restrict ci, - struct spart *restrict sparts, - int *restrict ind, int scount) { - - runner_doself_subset_stars_density(r, ci, sparts, ind, scount); + * @brief Determine which version of DOSELF1_SUBSET_STARS needs to be called + * depending on the optimisation level. + * + * @param r The #runner. + * @param ci The first #cell. + * @param sparts The #spart to interact. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + */ +void DOSELF1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts, + int *restrict ind, int scount) { + + DOSELF1_SUBSET_STARS(r, ci, sparts, ind, scount); } /** - * @brief Determine which version of DOPAIR_SUBSET needs to be called depending - * on the - * orientation of the cells or whether DOPAIR_SUBSET needs to be called at all. + * @brief Determine which version of DOPAIR1_SUBSET_STARS needs to be called + * depending on the orientation of the cells or whether DOPAIR1_SUBSET_STARS + * needs to be called at all. * * @param r The #runner. * @param ci The first #cell. @@ -348,11 +376,10 @@ void runner_doself_subset_branch_stars_density(struct runner *r, * @param scount The number of particles in @c ind. * @param cj The second #cell. */ -void runner_dopair_subset_branch_stars_density(struct runner *r, - struct cell *restrict ci, - struct spart *restrict sparts_i, - int *restrict ind, int scount, - struct cell *restrict cj) { +void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, + int *restrict ind, int scount, + struct cell *restrict cj) { const struct engine *e = r->e; @@ -365,24 +392,20 @@ void runner_dopair_subset_branch_stars_density(struct runner *r, shift[k] = -e->s->dim[k]; } - runner_dopair_subset_stars_density(r, ci, sparts_i, ind, scount, cj, shift); + DOPAIR1_SUBSET_STARS(r, ci, sparts_i, ind, scount, cj, shift); } -void runner_dosub_subset_stars_density(struct runner *r, struct cell *ci, - struct spart *sparts, int *ind, - int scount, struct cell *cj, int sid, - int gettimer) { +void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts, + int *ind, int scount, struct cell *cj, int sid, + int gettimer) { const struct engine *e = r->e; struct space *s = e->s; - TIMER_TIC; - /* Should we even bother? */ if (!cell_is_active_stars(ci, e) && (cj == NULL || !cell_is_active_stars(cj, e))) return; - if (ci->stars.count == 0 || (cj != NULL && cj->stars.count == 0)) return; /* Find out in which sub-cell of ci the parts are. */ struct cell *sub = NULL; @@ -406,18 +429,17 @@ void runner_dosub_subset_stars_density(struct runner *r, struct cell *ci, if (cell_can_recurse_in_self_stars_task(ci)) { /* Loop over all progeny. */ - runner_dosub_subset_stars_density(r, sub, sparts, ind, scount, NULL, -1, - 0); + DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, NULL, -1, 0); for (int j = 0; j < 8; j++) if (ci->progeny[j] != sub && ci->progeny[j] != NULL) - runner_dosub_subset_stars_density(r, sub, sparts, ind, scount, - ci->progeny[j], -1, 0); + DOSUB_SUBSET_STARS(r, sub, sparts, ind, scount, ci->progeny[j], -1, + 0); } /* Otherwise, compute self-interaction. */ else - runner_doself_subset_branch_stars_density(r, ci, sparts, ind, scount); + DOSELF1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount); } /* self-interaction. */ /* Otherwise, it's a pair interaction. */ @@ -437,497 +459,497 @@ void runner_dosub_subset_stars_density(struct runner *r, struct cell *ci, /* Regular sub-cell interactions of a single cell. */ case 0: /* ( 1 , 1 , 1 ) */ if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 1: /* ( 1 , 1 , 0 ) */ if (ci->progeny[6] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 2: /* ( 1 , 1 , -1 ) */ if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); break; case 3: /* ( 1 , 0 , 1 ) */ if (ci->progeny[5] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 4: /* ( 1 , 0 , 0 ) */ if (ci->progeny[4] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[4] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[4] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[4] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[5] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[6] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 5: /* ( 1 , 0 , -1 ) */ if (ci->progeny[4] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[4] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[6], -1, 0); break; case 6: /* ( 1 , -1 , 1 ) */ if (ci->progeny[5] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[5], -1, 0); break; case 7: /* ( 1 , -1 , 0 ) */ if (ci->progeny[4] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[4] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[4], -1, 0); if (ci->progeny[5] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[5], -1, 0); break; case 8: /* ( 1 , -1 , -1 ) */ if (ci->progeny[4] == sub && cj->progeny[3] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[4], sparts, ind, - scount, cj->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[4], sparts, ind, scount, + cj->progeny[3], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[3], sparts, ind, - scount, ci->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[3], sparts, ind, scount, + ci->progeny[4], -1, 0); break; case 9: /* ( 0 , 1 , 1 ) */ if (ci->progeny[3] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 10: /* ( 0 , 1 , 0 ) */ if (ci->progeny[2] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[2] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[2] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[2] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[3] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[6] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[7], -1, 0); break; case 11: /* ( 0 , 1 , -1 ) */ if (ci->progeny[2] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[2] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[2], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[2], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[2], -1, 0); if (ci->progeny[6] == sub && cj->progeny[1] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[1], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[1], sparts, ind, scount, + ci->progeny[6], -1, 0); if (ci->progeny[6] == sub && cj->progeny[5] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[6], sparts, ind, - scount, cj->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[6], sparts, ind, scount, + cj->progeny[5], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[5] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[5], sparts, ind, - scount, ci->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[5], sparts, ind, scount, + ci->progeny[6], -1, 0); break; case 12: /* ( 0 , 0 , 1 ) */ if (ci->progeny[1] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[1], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[1], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[1], -1, 0); if (ci->progeny[1] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[1], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[1], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[1], -1, 0); if (ci->progeny[1] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[1], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[1], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[1], -1, 0); if (ci->progeny[1] == sub && cj->progeny[6] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[1], sparts, ind, - scount, cj->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[1], sparts, ind, scount, + cj->progeny[6], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[6] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[6], sparts, ind, - scount, ci->progeny[1], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[6], sparts, ind, scount, + ci->progeny[1], -1, 0); if (ci->progeny[3] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[3] == sub && cj->progeny[6] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[3], sparts, ind, - scount, cj->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[3], sparts, ind, scount, + cj->progeny[6], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[6] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[6], sparts, ind, - scount, ci->progeny[3], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[6], sparts, ind, scount, + ci->progeny[3], -1, 0); if (ci->progeny[5] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[5] == sub && cj->progeny[6] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[5], sparts, ind, - scount, cj->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[5], sparts, ind, scount, + cj->progeny[6], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[6] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[6], sparts, ind, - scount, ci->progeny[5], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[6], sparts, ind, scount, + ci->progeny[5], -1, 0); if (ci->progeny[7] == sub && cj->progeny[0] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[0], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[0], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[0], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[2] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[2], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[2], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[2], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[4] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[4], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[4], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[4], sparts, ind, scount, + ci->progeny[7], -1, 0); if (ci->progeny[7] == sub && cj->progeny[6] != NULL) - runner_dosub_subset_stars_density(r, ci->progeny[7], sparts, ind, - scount, cj->progeny[6], -1, 0); + DOSUB_SUBSET_STARS(r, ci->progeny[7], sparts, ind, scount, + cj->progeny[6], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[6] == sub) - runner_dosub_subset_stars_density(r, cj->progeny[6], sparts, ind, - scount, ci->progeny[7], -1, 0); + DOSUB_SUBSET_STARS(r, cj->progeny[6], sparts, ind, scount, + ci->progeny[7], -1, 0); break; } @@ -939,23 +961,21 @@ void runner_dosub_subset_stars_density(struct runner *r, struct cell *ci, /* Do any of the cells need to be drifted first? */ if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!"); - runner_dopair_subset_branch_stars_density(r, ci, sparts, ind, scount, cj); + DOPAIR1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount, cj); } } /* otherwise, pair interaction. */ - - if (gettimer) TIMER_TOC(timer_dosub_subset); } /** - * @brief Determine which version of runner_doself needs to be called depending + * @brief Determine which version of DOSELF1_STARS needs to be called depending * on the optimisation level. * * @param r #runner * @param c #cell c * */ -void runner_doself_branch_stars_density(struct runner *r, struct cell *c) { +void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) { const struct engine *restrict e = r->e; @@ -966,84 +986,98 @@ void runner_doself_branch_stars_density(struct runner *r, struct cell *c) { if (c->stars.h_max_old * kernel_gamma > c->dmin) error("Cell smaller than smoothing length"); - runner_doself_stars_density(r, c, 1); + DOSELF1_STARS(r, c, 1); } +#define RUNNER_CHECK_SORT(TYPE, PART, cj, ci, sid) \ + ({ \ + const struct entry *restrict sort_j = cj->TYPE.sort[sid]; \ + \ + for (int pjd = 0; pjd < cj->TYPE.count; pjd++) { \ + const struct PART *p = &cj->TYPE.parts[sort_j[pjd].i]; \ + const float d = p->x[0] * runner_shift[sid][0] + \ + p->x[1] * runner_shift[sid][1] + \ + p->x[2] * runner_shift[sid][2]; \ + if ((fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ + 1.0e-4 * max(fabsf(d), cj->TYPE.dx_max_sort_old) && \ + (fabsf(d - sort_j[pjd].d) - cj->TYPE.dx_max_sort) > \ + cj->width[0] * 1.0e-10) \ + error( \ + "particle shift diff exceeds dx_max_sort in cell cj. " \ + "cj->nodeID=%d " \ + "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->" #TYPE \ + ".dx_max_sort=%e " \ + "cj->" #TYPE ".dx_max_sort_old=%e", \ + cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->TYPE.dx_max_sort, \ + cj->TYPE.dx_max_sort_old); \ + } \ + }) + /** - * @brief Determine which version of DOPAIR1 needs to be called depending on the - * orientation of the cells or whether DOPAIR1 needs to be called at all. + * @brief Determine which version of DOPAIR1_STARS needs to be called depending + * on the orientation of the cells or whether DOPAIR1_STARS needs to be called + * at all. * * @param r #runner * @param ci #cell ci * @param cj #cell cj * */ -void runner_dopair_branch_stars_density(struct runner *r, struct cell *ci, - struct cell *cj) { +void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { const struct engine *restrict e = r->e; + const int ci_active = cell_is_active_stars(ci, e); + const int cj_active = cell_is_active_stars(cj, e); + const int do_ci = (ci->stars.count != 0 && cj->hydro.count != 0 && ci_active); + const int do_cj = (cj->stars.count != 0 && ci->hydro.count != 0 && cj_active); /* Anything to do here? */ - if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return; - - /* Check that cells are drifted. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) - error("Interacting undrifted cells."); + if (!do_ci && !do_cj) return; /* Get the sort ID. */ double shift[3] = {0.0, 0.0, 0.0}; const int sid = space_getsid(e->s, &ci, &cj, shift); + /* Check that cells are drifted. */ + if (do_ci && + (!cell_are_spart_drifted(ci, e) || !cell_are_part_drifted(cj, e))) + error("Interacting undrifted cells."); + /* Have the cells been sorted? */ - if (!(ci->hydro.sorted & (1 << sid)) || - ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin) + if (do_ci && (!(ci->stars.sorted & (1 << sid)) || + ci->stars.dx_max_sort_old > space_maxreldx * ci->dmin)) error("Interacting unsorted cells."); - if (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin) + + if (do_ci && (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)) + error("Interacting unsorted cells."); + + if (do_cj && + (!cell_are_part_drifted(ci, e) || !cell_are_spart_drifted(cj, e))) + error("Interacting undrifted cells."); + + /* Have the cells been sorted? */ + if (do_cj && (!(ci->hydro.sorted & (1 << sid)) || + ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)) + error("Interacting unsorted cells."); + + if (do_cj && (!(cj->stars.sorted & (1 << sid)) || + cj->stars.dx_max_sort_old > space_maxreldx * cj->dmin)) error("Interacting unsorted cells."); #ifdef SWIFT_DEBUG_CHECKS - /* Pick-out the sorted lists. */ - const struct entry *restrict sort_i = ci->hydro.sort[sid]; - const struct entry *restrict sort_j = cj->hydro.sort[sid]; - - /* Check that the dx_max_sort values in the cell are indeed an upper - bound on particle movement. */ - for (int pid = 0; pid < ci->hydro.count; pid++) { - const struct part *p = &ci->hydro.parts[sort_i[pid].i]; - const float d = p->x[0] * runner_shift[sid][0] + - p->x[1] * runner_shift[sid][1] + - p->x[2] * runner_shift[sid][2]; - if (fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort > - 1.0e-4 * max(fabsf(d), ci->hydro.dx_max_sort_old) && - fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort > - ci->width[0] * 1.0e-10) - error( - "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d " - "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->hydro.dx_max_sort=%e " - "ci->hydro.dx_max_sort_old=%e", - ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->hydro.dx_max_sort, - ci->hydro.dx_max_sort_old); + if (do_ci) { + RUNNER_CHECK_SORT(hydro, part, cj, ci, sid); + RUNNER_CHECK_SORT(stars, spart, ci, cj, sid); } - for (int pjd = 0; pjd < cj->hydro.count; pjd++) { - const struct part *p = &cj->hydro.parts[sort_j[pjd].i]; - const float d = p->x[0] * runner_shift[sid][0] + - p->x[1] * runner_shift[sid][1] + - p->x[2] * runner_shift[sid][2]; - if ((fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) > - 1.0e-4 * max(fabsf(d), cj->hydro.dx_max_sort_old) && - (fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) > - cj->width[0] * 1.0e-10) - error( - "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d " - "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->hydro.dx_max_sort=%e " - "cj->hydro.dx_max_sort_old=%e", - cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->hydro.dx_max_sort, - cj->hydro.dx_max_sort_old); + + if (do_cj) { + RUNNER_CHECK_SORT(hydro, part, ci, cj, sid); + RUNNER_CHECK_SORT(stars, spart, cj, ci, sid); } #endif /* SWIFT_DEBUG_CHECKS */ - runner_dopair_stars_density(r, ci, cj, 1); + DOPAIR1_STARS(r, ci, cj, 1); } /** @@ -1058,17 +1092,18 @@ void runner_dopair_branch_stars_density(struct runner *r, struct cell *ci, * @todo Hard-code the sid on the recursive calls to avoid the * redundant computations to find the sid on-the-fly. */ -void runner_dosub_pair_stars_density(struct runner *r, struct cell *ci, - struct cell *cj, int sid, int gettimer) { +void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, + int sid, int gettimer) { struct space *s = r->e->s; const struct engine *e = r->e; - TIMER_TIC; - /* Should we even bother? */ - if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return; - if (ci->stars.count == 0 || cj->stars.count == 0) return; + int should_do = ci->stars.count != 0 && cj->hydro.count != 0 && + cell_is_active_stars(ci, e); + should_do |= cj->stars.count != 0 && ci->hydro.count != 0 && + cell_is_active_stars(cj, e); + if (!should_do) return; /* Get the type of pair if not specified explicitly. */ double shift[3]; @@ -1084,294 +1119,246 @@ void runner_dosub_pair_stars_density(struct runner *r, struct cell *ci, /* Regular sub-cell interactions of a single cell. */ case 0: /* ( 1 , 1 , 1 ) */ if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); break; case 1: /* ( 1 , 1 , 0 ) */ if (ci->progeny[6] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[1], -1, 0); break; case 2: /* ( 1 , 1 , -1 ) */ if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); break; case 3: /* ( 1 , 0 , 1 ) */ if (ci->progeny[5] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[2], -1, 0); break; case 4: /* ( 1 , 0 , 0 ) */ if (ci->progeny[4] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[0], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[1], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[2], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[3], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[1], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[3], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[2], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[3], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[3], -1, 0); break; case 5: /* ( 1 , 0 , -1 ) */ if (ci->progeny[4] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[1], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[3], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[3], -1, 0); break; case 6: /* ( 1 , -1 , 1 ) */ if (ci->progeny[5] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[2], -1, 0); break; case 7: /* ( 1 , -1 , 0 ) */ if (ci->progeny[4] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[2], -1, 0); if (ci->progeny[4] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[3], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[3], -1, 0); break; case 8: /* ( 1 , -1 , -1 ) */ if (ci->progeny[4] != NULL && cj->progeny[3] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[4], cj->progeny[3], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[4], cj->progeny[3], -1, 0); break; case 9: /* ( 0 , 1 , 1 ) */ if (ci->progeny[3] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[4], -1, 0); break; case 10: /* ( 0 , 1 , 0 ) */ if (ci->progeny[2] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[0], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[1], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[4], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[5], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[1], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[4], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[5], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[0], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[4], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[5], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[1], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[5], -1, 0); break; case 11: /* ( 0 , 1 , -1 ) */ if (ci->progeny[2] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[1], -1, 0); if (ci->progeny[2] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[2], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[2], cj->progeny[5], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[1] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[1], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[1], -1, 0); if (ci->progeny[6] != NULL && cj->progeny[5] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[6], cj->progeny[5], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[6], cj->progeny[5], -1, 0); break; case 12: /* ( 0 , 0 , 1 ) */ if (ci->progeny[1] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[1], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[1], cj->progeny[0], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[1], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[1], cj->progeny[2], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[1], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[1], cj->progeny[4], -1, 0); if (ci->progeny[1] != NULL && cj->progeny[6] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[1], cj->progeny[6], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[1], cj->progeny[6], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[0], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[2], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[4], -1, 0); if (ci->progeny[3] != NULL && cj->progeny[6] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[3], cj->progeny[6], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[3], cj->progeny[6], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[0], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[2], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[4], -1, 0); if (ci->progeny[5] != NULL && cj->progeny[6] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[5], cj->progeny[6], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[5], cj->progeny[6], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[0] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[0], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[0], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[2] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[2], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[2], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[4] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[4], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[4], -1, 0); if (ci->progeny[7] != NULL && cj->progeny[6] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[7], cj->progeny[6], -1, - 0); + DOSUB_PAIR1_STARS(r, ci->progeny[7], cj->progeny[6], -1, 0); break; } } /* Otherwise, compute the pair directly. */ - else if (cell_is_active_stars(ci, e) || cell_is_active_stars(cj, e)) { - - /* Make sure both cells are drifted to the current timestep. */ - if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e)) - error("Interacting undrifted cells."); - - /* Do any of the cells need to be sorted first? */ - if (!(ci->hydro.sorted & (1 << sid)) || - ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx) - error("Interacting unsorted cell."); - if (!(cj->hydro.sorted & (1 << sid)) || - cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx) - error("Interacting unsorted cell."); - - /* Compute the interactions. */ - runner_dopair_branch_stars_density(r, ci, cj); - } + else { + + const int do_ci = ci->stars.count != 0 && cj->hydro.count != 0 && + cell_is_active_stars(ci, e); + const int do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && + cell_is_active_stars(cj, e); - if (gettimer) TIMER_TOC(TIMER_DOSUB_PAIR); + if (do_ci) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_spart_drifted(ci, e)) + error("Interacting undrifted cells (sparts)."); + + if (!cell_are_part_drifted(cj, e)) + error("Interacting undrifted cells (parts)."); + + /* Do any of the cells need to be sorted first? */ + if (!(ci->stars.sorted & (1 << sid)) || + ci->stars.dx_max_sort_old > ci->dmin * space_maxreldx) + error("Interacting unsorted cell (sparts)."); + + if (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx) + error("Interacting unsorted cell (parts)."); + } + + if (do_cj) { + + /* Make sure both cells are drifted to the current timestep. */ + if (!cell_are_part_drifted(ci, e)) + error("Interacting undrifted cells (parts)."); + + if (!cell_are_spart_drifted(cj, e)) + error("Interacting undrifted cells (sparts)."); + + /* Do any of the cells need to be sorted first? */ + if (!(ci->hydro.sorted & (1 << sid)) || + ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx) + error("Interacting unsorted cell (parts)."); + + if (!(cj->stars.sorted & (1 << sid)) || + cj->stars.dx_max_sort_old > cj->dmin * space_maxreldx) + error("Interacting unsorted cell (sparts)."); + } + + if (do_ci || do_cj) DOPAIR1_BRANCH_STARS(r, ci, cj); + } } /** @@ -1381,13 +1368,12 @@ void runner_dosub_pair_stars_density(struct runner *r, struct cell *ci, * @param ci The first #cell. * @param gettimer Do we have a timer ? */ -void runner_dosub_self_stars_density(struct runner *r, struct cell *ci, - int gettimer) { - - TIMER_TIC; +void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer) { /* Should we even bother? */ - if (ci->stars.count == 0 || !cell_is_active_stars(ci, r->e)) return; + if (ci->hydro.count == 0 || ci->stars.count == 0 || + !cell_is_active_stars(ci, r->e)) + return; /* Recurse? */ if (cell_can_recurse_in_self_stars_task(ci)) { @@ -1395,19 +1381,19 @@ void runner_dosub_self_stars_density(struct runner *r, struct cell *ci, /* Loop over all progeny. */ for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) { - runner_dosub_self_stars_density(r, ci->progeny[k], 0); + DOSUB_SELF1_STARS(r, ci->progeny[k], 0); for (int j = k + 1; j < 8; j++) if (ci->progeny[j] != NULL) - runner_dosub_pair_stars_density(r, ci->progeny[k], ci->progeny[j], - -1, 0); + DOSUB_PAIR1_STARS(r, ci->progeny[k], ci->progeny[j], -1, 0); } } /* Otherwise, compute self-interaction. */ else { - runner_doself_branch_stars_density(r, ci); - } + /* Drift the cell to the current timestep if needed. */ + if (!cell_are_spart_drifted(ci, r->e)) error("Interacting undrifted cell."); - if (gettimer) TIMER_TOC(timer_dosub_self_stars_density); + DOSELF1_BRANCH_STARS(r, ci); + } } diff --git a/src/runner_doiact_vec.c b/src/runner_doiact_vec.c index c74fa7c8f53576f2e80578488fdf3378c59c0400..182e81e99c442cf5e27405ea71321e22e7f374e3 100644 --- a/src/runner_doiact_vec.c +++ b/src/runner_doiact_vec.c @@ -23,9 +23,6 @@ /* This object's header. */ #include "runner_doiact_vec.h" -/* Local headers. */ -#include "active.h" - #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) static const vector kernel_gamma2_vec = FILL_VEC(kernel_gamma2); @@ -68,8 +65,6 @@ __attribute__((always_inline)) INLINE static void calcRemInteractions( vector *v_curlvzSum, vector v_hi_inv, vector v_vix, vector v_viy, vector v_viz, int *icount_align) { - mask_t int_mask, int_mask2; - /* Work out the number of remainder interactions and pad secondary cache. */ *icount_align = icount; int rem = icount % (NUM_VEC_PROC * VEC_SIZE); @@ -78,6 +73,7 @@ __attribute__((always_inline)) INLINE static void calcRemInteractions( *icount_align += pad; /* Initialise masks to true. */ + mask_t int_mask, int_mask2; vec_init_mask_true(int_mask); vec_init_mask_true(int_mask2); @@ -654,7 +650,6 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { /* Get some local variables */ const struct engine *e = r->e; - const timebin_t max_active_bin = e->max_active_bin; struct part *restrict parts = c->hydro.parts; const int count = c->hydro.count; @@ -663,12 +658,13 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { /* Anything to do here? */ if (!cell_is_active_hydro(c, e)) return; + /* Check that everybody was drifted here */ if (!cell_are_part_drifted(c, e)) error("Interacting undrifted cell."); #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < count; i++) { /* Check that particles have been drifted to the current time */ - if (parts[i].ti_drift != e->ti_current) + if (parts[i].ti_drift != e->ti_current && !part_is_inhibited(&parts[i], e)) error("Particle pi not drifted to current time"); } #endif @@ -679,7 +675,7 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { if (cell_cache->count < count) cache_init(cell_cache, count); /* Read the particles from the cell and store them locally in the cache. */ - cache_read_particles(c, cell_cache); + const int count_align = cache_read_particles(c, cell_cache); /* Create secondary cache to store particle interactions. */ struct c2_cache int_cache; @@ -690,25 +686,23 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; - /* Is the ith particle active? */ - if (!part_is_active_no_debug(pi, max_active_bin)) continue; - - const float hi = cell_cache->h[pid]; + /* Is the i^th particle active? */ + if (!part_is_active(pi, e)) continue; /* Fill particle pi vectors. */ const vector v_pix = vector_set1(cell_cache->x[pid]); const vector v_piy = vector_set1(cell_cache->y[pid]); const vector v_piz = vector_set1(cell_cache->z[pid]); - const vector v_hi = vector_set1(hi); + const vector v_hi = vector_set1(cell_cache->h[pid]); const vector v_vix = vector_set1(cell_cache->vx[pid]); const vector v_viy = vector_set1(cell_cache->vy[pid]); const vector v_viz = vector_set1(cell_cache->vz[pid]); + /* Some useful mulitples of h */ + const float hi = cell_cache->h[pid]; const float hig2 = hi * hi * kernel_gamma2; const vector v_hig2 = vector_set1(hig2); - - /* Get the inverse of hi. */ - vector v_hi_inv = vec_reciprocal(v_hi); + const vector v_hi_inv = vec_reciprocal(v_hi); /* Reset cumulative sums of update vectors. */ vector v_rhoSum = vector_setzero(); @@ -720,21 +714,6 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { vector v_curlvySum = vector_setzero(); vector v_curlvzSum = vector_setzero(); - /* Pad cache if there is a serial remainder. */ - int count_align = count; - const int rem = count % (NUM_VEC_PROC * VEC_SIZE); - if (rem != 0) { - count_align += (NUM_VEC_PROC * VEC_SIZE) - rem; - - /* Set positions to the same as particle pi so when the r2 > 0 mask is - * applied these extra contributions are masked out.*/ - for (int i = count; i < count_align; i++) { - cell_cache->x[i] = v_pix.f[0]; - cell_cache->y[i] = v_piy.f[0]; - cell_cache->z[i] = v_piz.f[0]; - } - } - /* The number of interactions for pi and the padded version of it to * make it a multiple of VEC_SIZE. */ int icount = 0, icount_align = 0; @@ -771,8 +750,8 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { v_r2_2.v = vec_fma(v_dz_2.v, v_dz_2.v, v_r2_2.v); /* Form a mask from r2 < hig2 and r2 > 0.*/ - mask_t v_doi_mask, v_doi_mask_self_check, v_doi_mask2, - v_doi_mask2_self_check; + mask_t v_doi_mask, v_doi_mask2; + mask_t v_doi_mask_self_check, v_doi_mask2_self_check; /* Form r2 > 0 mask and r2 < hig2 mask. */ vec_create_mask(v_doi_mask_self_check, vec_cmp_gt(v_r2.v, vec_setzero())); @@ -789,6 +768,25 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { const int doi_mask2 = vec_is_mask_true(v_doi_mask2) & vec_is_mask_true(v_doi_mask2_self_check); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (doi_mask & (1 << bit_index)) { + if (parts[pjd + bit_index].time_bin >= time_bin_inhibited) { + error("Inhibited particle in interaction cache!"); + } + } + } + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (doi_mask2 & (1 << bit_index)) { + if (parts[pjd + VEC_SIZE + bit_index].time_bin >= + time_bin_inhibited) { + error("Inhibited particle in interaction cache2!"); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (doi_mask & (1 << bit_index)) { @@ -837,7 +835,7 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { vec_init_mask_true(int_mask); vec_init_mask_true(int_mask2); - /* Perform interaction with 2 vectors. */ + /* Perform interaction with NUM_VEC_PROC vectors. */ for (int pjd = 0; pjd < icount_align; pjd += (NUM_VEC_PROC * VEC_SIZE)) { runner_iact_nonsym_2_vec_density( &int_cache.r2q[pjd], &int_cache.dxq[pjd], &int_cache.dyq[pjd], @@ -848,8 +846,7 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) { &v_curlvzSum, int_mask, int_mask2, 0); } - /* Perform horizontal adds on vector sums and store result in particle pi. - */ + /* Perform horizontal adds on vector sums and store result in pi. */ VEC_HADD(v_rhoSum, pi->rho); VEC_HADD(v_rho_dhSum, pi->density.rho_dh); VEC_HADD(v_wcountSum, pi->density.wcount); @@ -899,7 +896,7 @@ void runner_doself_subset_density_vec(struct runner *r, struct cell *restrict c, if (cell_cache->count < count) cache_init(cell_cache, count); /* Read the particles from the cell and store them locally in the cache. */ - cache_read_particles(c, cell_cache); + const int count_align = cache_read_particles_subset_self(c, cell_cache); /* Create secondary cache to store particle interactions. */ struct c2_cache int_cache; @@ -942,23 +939,6 @@ void runner_doself_subset_density_vec(struct runner *r, struct cell *restrict c, vector v_curlvySum = vector_setzero(); vector v_curlvzSum = vector_setzero(); - /* Pad cache if there is a serial remainder. */ - int count_align = count; - const int rem = count % (NUM_VEC_PROC * VEC_SIZE); - if (rem != 0) { - const int pad = (NUM_VEC_PROC * VEC_SIZE) - rem; - - count_align += pad; - - /* Set positions to the same as particle pi so when the r2 > 0 mask is - * applied these extra contributions are masked out.*/ - for (int i = count; i < count_align; i++) { - cell_cache->x[i] = v_pix.f[0]; - cell_cache->y[i] = v_piy.f[0]; - cell_cache->z[i] = v_piz.f[0]; - } - } - /* The number of interactions for pi and the padded version of it to * make it a multiple of VEC_SIZE. */ int icount = 0, icount_align = 0; @@ -1015,9 +995,33 @@ void runner_doself_subset_density_vec(struct runner *r, struct cell *restrict c, const int doi_mask2 = vec_is_mask_true(v_doi_mask2) & vec_is_mask_true(v_doi_mask2_self_check); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_i = c->hydro.parts; + + if (doi_mask & (1 << bit_index)) { + if (parts_i[pjd + bit_index].time_bin >= time_bin_inhibited) { + error("Inhibited particle in interaction cache!"); + } + } + } + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_i = c->hydro.parts; + + if (doi_mask2 & (1 << bit_index)) { + if (parts_i[pjd + VEC_SIZE + bit_index].time_bin >= + time_bin_inhibited) { + error("Inhibited particle in interaction cache2!"); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH - struct part *restrict parts_i = c->hydro.parts; for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_i = c->hydro.parts; + if (doi_mask & (1 << bit_index)) { if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS) pi->ids_ngbs_density[pi->num_ngb_density] = @@ -1112,7 +1116,6 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { const struct engine *e = r->e; const struct cosmology *restrict cosmo = e->cosmology; - const timebin_t max_active_bin = e->max_active_bin; struct part *restrict parts = c->hydro.parts; const int count = c->hydro.count; @@ -1126,7 +1129,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { #ifdef SWIFT_DEBUG_CHECKS for (int i = 0; i < count; i++) { /* Check that particles have been drifted to the current time */ - if (parts[i].ti_drift != e->ti_current) + if (parts[i].ti_drift != e->ti_current && !part_is_inhibited(&parts[i], e)) error("Particle pi not drifted to current time"); } #endif @@ -1138,7 +1141,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { if (cell_cache->count < count) cache_init(cell_cache, count); /* Read the particles from the cell and store them locally in the cache. */ - cache_read_force_particles(c, cell_cache); + const int count_align = cache_read_force_particles(c, cell_cache); /* Cosmological terms */ const float a = cosmo->a; @@ -1150,16 +1153,14 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { /* Get a pointer to the ith particle. */ struct part *restrict pi = &parts[pid]; - /* Is the ith particle active? */ - if (!part_is_active_no_debug(pi, max_active_bin)) continue; - - const float hi = cell_cache->h[pid]; + /* Is the i^th particle active? */ + if (!part_is_active(pi, e)) continue; /* Fill particle pi vectors. */ const vector v_pix = vector_set1(cell_cache->x[pid]); const vector v_piy = vector_set1(cell_cache->y[pid]); const vector v_piz = vector_set1(cell_cache->z[pid]); - const vector v_hi = vector_set1(hi); + const vector v_hi = vector_set1(cell_cache->h[pid]); const vector v_vix = vector_set1(cell_cache->vx[pid]); const vector v_viy = vector_set1(cell_cache->vy[pid]); const vector v_viz = vector_set1(cell_cache->vz[pid]); @@ -1170,11 +1171,11 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { const vector v_balsara_i = vector_set1(cell_cache->balsara[pid]); const vector v_ci = vector_set1(cell_cache->soundspeed[pid]); + /* Some useful powers of h */ + const float hi = cell_cache->h[pid]; const float hig2 = hi * hi * kernel_gamma2; const vector v_hig2 = vector_set1(hig2); - - /* Get the inverse of hi. */ - vector v_hi_inv = vec_reciprocal(v_hi); + const vector v_hi_inv = vec_reciprocal(v_hi); /* Reset cumulative sums of update vectors. */ vector v_a_hydro_xSum = vector_setzero(); @@ -1184,39 +1185,18 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { vector v_sigSum = vector_set1(pi->force.v_sig); vector v_entropy_dtSum = vector_setzero(); - /* Pad cache if there is a serial remainder. */ - int count_align = count; - int rem = count % VEC_SIZE; - if (rem != 0) { - int pad = VEC_SIZE - rem; - - count_align += pad; - - /* Set positions to the same as particle pi so when the r2 > 0 mask is - * applied these extra contributions are masked out.*/ - for (int i = count; i < count_align; i++) { - cell_cache->x[i] = v_pix.f[0]; - cell_cache->y[i] = v_piy.f[0]; - cell_cache->z[i] = v_piz.f[0]; - cell_cache->h[i] = 1.f; - cell_cache->rho[i] = 1.f; - cell_cache->grad_h[i] = 1.f; - cell_cache->pOrho2[i] = 1.f; - cell_cache->balsara[i] = 1.f; - cell_cache->soundspeed[i] = 1.f; - } - } - /* Find all of particle pi's interacions and store needed values in the * secondary cache.*/ for (int pjd = 0; pjd < count_align; pjd += VEC_SIZE) { /* Load 1 set of vectors from the particle cache. */ - vector hjg2; const vector v_pjx = vector_load(&cell_cache->x[pjd]); const vector v_pjy = vector_load(&cell_cache->y[pjd]); const vector v_pjz = vector_load(&cell_cache->z[pjd]); const vector hj = vector_load(&cell_cache->h[pjd]); + + /* (hj * gamma)^2 */ + vector hjg2; hjg2.v = vec_mul(vec_mul(hj.v, hj.v), kernel_gamma2_vec.v); /* Compute the pairwise distance. */ @@ -1229,20 +1209,33 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { v_r2.v = vec_fma(v_dy.v, v_dy.v, v_r2.v); v_r2.v = vec_fma(v_dz.v, v_dz.v, v_r2.v); - /* Form r2 > 0 mask, r2 < hig2 mask and r2 < hjg2 mask. */ - mask_t v_doi_mask, v_doi_mask_self_check; - - /* Form r2 > 0 mask.*/ + /* Form r2 > 0 mask. + * This is used to avoid self-interctions */ + mask_t v_doi_mask_self_check; vec_create_mask(v_doi_mask_self_check, vec_cmp_gt(v_r2.v, vec_setzero())); - /* Form a mask from r2 < hig2 mask and r2 < hjg2 mask. */ - vector v_h2; - v_h2.v = vec_fmax(v_hig2.v, hjg2.v); - vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_h2.v)); + /* Form a mask from r2 < hig2 mask and r2 < hjg2 mask. + * This is writen as r2 < max(hig2, hjg2) */ + mask_t v_doi_mask; + vec_create_mask(v_doi_mask, + vec_cmp_lt(v_r2.v, vec_fmax(v_hig2.v, hjg2.v))); - /* Combine all 3 masks. */ + /* Combine both masks. */ vec_combine_masks(v_doi_mask, v_doi_mask_self_check); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { + if ((pjd + bit_index < count) && + (parts[pjd + bit_index].time_bin >= time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts[pjd + bit_index].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { @@ -1255,10 +1248,14 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { /* If there are any interactions perform them. */ if (vec_is_mask_true(v_doi_mask)) { - vector v_hj_inv = vec_reciprocal(hj); - /* To stop floating point exceptions for when particle separations are - * 0. */ + /* 1 / hj */ + const vector v_hj_inv = vec_reciprocal(hj); + + /* To stop floating point exceptions when particle separations are 0. + * Note that the results for r2==0 are masked out but may still raise + * an FPE as only the final operaion is masked, not the whole math + * operations sequence. */ v_r2.v = vec_add(v_r2.v, vec_set1(FLT_MIN)); runner_iact_nonsym_1_vec_force( @@ -1278,9 +1275,10 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { VEC_HADD(v_a_hydro_ySum, pi->a_hydro[1]); VEC_HADD(v_a_hydro_zSum, pi->a_hydro[2]); VEC_HADD(v_h_dtSum, pi->force.h_dt); - VEC_HMAX(v_sigSum, pi->force.v_sig); VEC_HADD(v_entropy_dtSum, pi->entropy_dt); + VEC_HMAX(v_sigSum, pi->force.v_sig); + } /* loop over all particles. */ TIMER_TOC(timer_doself_force); @@ -1341,10 +1339,12 @@ void runner_dopair1_density_vec(struct runner *r, struct cell *ci, #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ for (int pid = 0; pid < count_i; pid++) - if (parts_i[pid].ti_drift != e->ti_current) + if (parts_i[pid].ti_drift != e->ti_current && + !part_is_inhibited(&parts_i[pid], e)) error("Particle pi not drifted to current time"); for (int pjd = 0; pjd < count_j; pjd++) - if (parts_j[pjd].ti_drift != e->ti_current) + if (parts_j[pjd].ti_drift != e->ti_current && + !part_is_inhibited(&parts_j[pjd], e)) error("Particle pj not drifted to current time"); #endif @@ -1497,6 +1497,21 @@ void runner_dopair1_density_vec(struct runner *r, struct cell *ci, /* Form r2 < hig2 mask. */ vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_hig2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { + if ((pjd + bit_index < count_j) && + (parts_j[sort_j[pjd + bit_index].i].time_bin >= + time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_j[sort_j[pjd + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { @@ -1623,6 +1638,21 @@ void runner_dopair1_density_vec(struct runner *r, struct cell *ci, /* Form r2 < hig2 mask. */ vec_create_mask(v_doj_mask, vec_cmp_lt(v_r2.v, v_hjg2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (vec_is_mask_true(v_doj_mask) & (1 << bit_index)) { + if ((ci_cache_idx + first_pi + bit_index < count_i) && + (parts_i[sort_i[ci_cache_idx + first_pi + bit_index].i] + .time_bin >= time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_i[sort_i[ci_cache_idx + first_pi + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (vec_is_mask_true(v_doj_mask) & (1 << bit_index)) { @@ -1733,7 +1763,8 @@ void runner_dopair_subset_density_vec(struct runner *r, runner_shift_x, runner_shift_y, runner_shift_z, sort_j, max_index_i, 0); /* Read the particles from the cell and store them locally in the cache. */ - cache_read_particles_subset(cj, cj_cache, sort_j, 0, &last_pj, ci->loc, 0); + cache_read_particles_subset_pair(cj, cj_cache, sort_j, 0, &last_pj, ci->loc, + 0); const double dj_min = sort_j[0].d; @@ -1805,9 +1836,27 @@ void runner_dopair_subset_density_vec(struct runner *r, mask_t v_doi_mask; vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_hig2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_j = cj->hydro.parts; + + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { + if ((pjd + bit_index < count_j) && + (parts_j[sort_j[pjd + bit_index].i].time_bin >= + time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_j[sort_j[pjd + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH - struct part *restrict parts_j = cj->hydro.parts; for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_j = cj->hydro.parts; + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS) { pi->ids_ngbs_density[pi->num_ngb_density] = @@ -1851,7 +1900,8 @@ void runner_dopair_subset_density_vec(struct runner *r, runner_shift_x, runner_shift_y, runner_shift_z, sort_j, max_index_i, 1); /* Read the particles from the cell and store them locally in the cache. */ - cache_read_particles_subset(cj, cj_cache, sort_j, &first_pj, 0, ci->loc, 1); + cache_read_particles_subset_pair(cj, cj_cache, sort_j, &first_pj, 0, + ci->loc, 1); /* Get the number of particles read into the ci cache. */ const int cj_cache_count = count_j - first_pj; @@ -1934,9 +1984,27 @@ void runner_dopair_subset_density_vec(struct runner *r, mask_t v_doi_mask; vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_hig2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_j = cj->hydro.parts; + + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { + if ((cj_cache_idx + bit_index < count_j) && + (parts_j[sort_j[cj_cache_idx + first_pj + bit_index].i] + .time_bin >= time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_j[sort_j[cj_cache_idx + first_pj + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH - struct part *restrict parts_j = cj->hydro.parts; for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + struct part *restrict parts_j = cj->hydro.parts; + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS) { pi->ids_ngbs_density[pi->num_ngb_density] = @@ -2032,10 +2100,12 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ for (int pid = 0; pid < count_i; pid++) - if (parts_i[pid].ti_drift != e->ti_current) + if (parts_i[pid].ti_drift != e->ti_current && + !part_is_inhibited(&parts_i[pid], e)) error("Particle pi not drifted to current time"); for (int pjd = 0; pjd < count_j; pjd++) - if (parts_j[pjd].ti_drift != e->ti_current) + if (parts_j[pjd].ti_drift != e->ti_current && + !part_is_inhibited(&parts_j[pjd], e)) error("Particle pj not drifted to current time"); #endif @@ -2200,6 +2270,21 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, v_h2.v = vec_fmax(v_hig2.v, v_hjg2.v); vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_h2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { + if ((pjd + bit_index < count_j) && + (parts_j[sort_j[pjd + bit_index].i].time_bin >= + time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_j[sort_j[pjd + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) { @@ -2336,6 +2421,21 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, v_h2.v = vec_fmax(v_hjg2.v, v_hig2.v); vec_create_mask(v_doj_mask, vec_cmp_lt(v_r2.v, v_h2.v)); +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that we have no inhibited particles in the interaction cache + */ + for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { + if (vec_is_mask_true(v_doj_mask) & (1 << bit_index)) { + if ((ci_cache_idx + first_pi + bit_index < count_i) && + (parts_i[sort_i[ci_cache_idx + first_pi + bit_index].i] + .time_bin >= time_bin_inhibited)) { + error("Inhibited particle in interaction cache! id=%lld", + parts_i[sort_i[ci_cache_idx + first_pi + bit_index].i].id); + } + } + } +#endif + #ifdef DEBUG_INTERACTIONS_SPH for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) { if (vec_is_mask_true(v_doj_mask) & (1 << bit_index)) { diff --git a/src/scheduler.c b/src/scheduler.c index 0621036dd0e056adfe8e08ef46198537132cdaec..4014e9acf22b13cf53e4c254137dee9e535db4b3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -29,6 +29,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> /* MPI headers. */ #ifdef WITH_MPI @@ -58,12 +59,43 @@ */ void scheduler_clear_active(struct scheduler *s) { s->active_count = 0; } +/** + * @brief Increase the space available for unlocks. Only call when + * current index == s->size_unlock; + */ +static void scheduler_extend_unlocks(struct scheduler *s) { + + /* Allocate the new buffer. */ + const int size_unlocks_new = s->size_unlocks * 2; + struct task **unlocks_new = + (struct task **)malloc(sizeof(struct task *) * size_unlocks_new); + int *unlock_ind_new = (int *)malloc(sizeof(int) * size_unlocks_new); + if (unlocks_new == NULL || unlock_ind_new == NULL) + error("Failed to re-allocate unlocks."); + + /* Wait for all writes to the old buffer to complete. */ + while (s->completed_unlock_writes < s->size_unlocks) + ; + + /* Copy the buffers. */ + memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * s->size_unlocks); + memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * s->size_unlocks); + free(s->unlocks); + free(s->unlock_ind); + s->unlocks = unlocks_new; + s->unlock_ind = unlock_ind_new; + + /* Publish the new buffer size. */ + s->size_unlocks = size_unlocks_new; +} + /** * @brief Add an unlock_task to the given task. * * @param s The #scheduler. * @param ta The unlocking #task. * @param tb The #task that will be unlocked. + */ void scheduler_addunlock(struct scheduler *s, struct task *ta, struct task *tb) { @@ -76,37 +108,21 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta, const int ind = atomic_inc(&s->nr_unlocks); /* Does the buffer need to be grown? */ - if (ind == s->size_unlocks) { - /* Allocate the new buffer. */ - struct task **unlocks_new; - int *unlock_ind_new; - const int size_unlocks_new = s->size_unlocks * 2; - if ((unlocks_new = (struct task **)malloc(sizeof(struct task *) * - size_unlocks_new)) == NULL || - (unlock_ind_new = (int *)malloc(sizeof(int) * size_unlocks_new)) == - NULL) - error("Failed to re-allocate unlocks."); - - /* Wait for all writes to the old buffer to complete. */ - while (s->completed_unlock_writes < ind) - ; - - /* Copy the buffers. */ - memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * ind); - memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * ind); - free(s->unlocks); - free(s->unlock_ind); - s->unlocks = unlocks_new; - s->unlock_ind = unlock_ind_new; - - /* Publish the new buffer size. */ - s->size_unlocks = size_unlocks_new; - } + if (ind == s->size_unlocks) scheduler_extend_unlocks(s); + +#ifdef SWIFT_DEBUG_CHECKS + if (ind > s->size_unlocks * 2) + message("unlocks guard enabled: %d / %d", ind, s->size_unlocks); +#endif /* Wait for there to actually be space at my index. */ while (ind > s->size_unlocks) ; + /* Guard against case when more than (old) s->size_unlocks unlocks + * are now pending. */ + if (ind == s->size_unlocks) scheduler_extend_unlocks(s); + /* Write the unlock to the scheduler. */ s->unlocks[ind] = tb; s->unlock_ind[ind] = ta - s->tasks; @@ -114,31 +130,218 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta, } /** - * @brief generate the dependency name for the tasks + * @brief compute the number of similar dependencies + * + * @param s The #scheduler + * @param ta The #task + * @param tb The dependent #task + * + * @return Number of dependencies + */ +int scheduler_get_number_relation(const struct scheduler *s, + const struct task *ta, + const struct task *tb) { + + int count = 0; + + /* loop over all tasks */ + for (int i = 0; i < s->nr_tasks; i++) { + const struct task *ta_tmp = &s->tasks[i]; + + /* and their dependencies */ + for (int j = 0; j < ta->nr_unlock_tasks; j++) { + const struct task *tb_tmp = ta->unlock_tasks[j]; + + if (ta->type == ta_tmp->type && ta->subtype == ta_tmp->subtype && + tb->type == tb_tmp->type && tb->subtype == tb_tmp->subtype) { + count += 1; + } + } + } + return count; +} + +/* Conservative number of dependencies per task type */ +#define MAX_NUMBER_DEP 128 + +/** + * @brief Informations about all the task dependencies of + * a single task. + */ +struct task_dependency { + /* Main task */ + /* ID of the task */ + int type_in; + + /* ID of the subtask */ + int subtype_in; + + /* Is the task implicit */ + int implicit_in; + + /* Dependent task */ + /* ID of the dependent task */ + int type_out[MAX_NUMBER_DEP]; + + /* ID of the dependent subtask */ + int subtype_out[MAX_NUMBER_DEP]; + + /* Is the dependent task implicit */ + int implicit_out[MAX_NUMBER_DEP]; + + /* Statistics */ + /* number of link between the two task type */ + int number_link[MAX_NUMBER_DEP]; + + /* number of ranks having this relation */ + int number_rank[MAX_NUMBER_DEP]; +}; + +#ifdef WITH_MPI + +/** + * @brief Define the #task_dependency for MPI * - * @param ta_type The #task type. - * @param ta_subtype The #task type. - * @param ta_name (return) The formatted string + * @param tstype The MPI_Datatype to initialize */ -void scheduler_task_dependency_name(int ta_type, int ta_subtype, - char *ta_name) { - - /* Check input */ - if ((ta_type < 0) || (ta_type >= task_type_count)) - error("Unknown task type %i", ta_type); - - if ((ta_subtype < 0) || (ta_subtype >= task_subtype_count)) - error("Unknown task subtype %i with type %s", ta_subtype, - taskID_names[ta_type]); - - /* construct line */ - if (ta_subtype == task_subtype_none) - sprintf(ta_name, "%s", taskID_names[ta_type]); - else - sprintf(ta_name, "\"%s %s\"", taskID_names[ta_type], - subtaskID_names[ta_subtype]); +void task_dependency_define(MPI_Datatype *tstype) { + + /* Define the variables */ + const int count = 8; + int blocklens[count]; + MPI_Datatype types[count]; + MPI_Aint disps[count]; + + /* all the type are int */ + for (int i = 0; i < count; i++) { + types[i] = MPI_INT; + } + + /* Task in */ + disps[0] = offsetof(struct task_dependency, type_in); + blocklens[0] = 1; + disps[1] = offsetof(struct task_dependency, subtype_in); + blocklens[1] = 1; + disps[2] = offsetof(struct task_dependency, implicit_in); + blocklens[2] = 1; + + /* Task out */ + disps[3] = offsetof(struct task_dependency, type_out); + blocklens[3] = MAX_NUMBER_DEP; + disps[4] = offsetof(struct task_dependency, subtype_out); + blocklens[4] = MAX_NUMBER_DEP; + disps[5] = offsetof(struct task_dependency, implicit_out); + blocklens[5] = MAX_NUMBER_DEP; + + /* statistics */ + disps[6] = offsetof(struct task_dependency, number_link); + blocklens[6] = MAX_NUMBER_DEP; + disps[7] = offsetof(struct task_dependency, number_rank); + blocklens[7] = MAX_NUMBER_DEP; + + /* define it for MPI */ + MPI_Type_create_struct(count, blocklens, disps, types, tstype); + MPI_Type_commit(tstype); } +/** + * @brief Sum operator of #task_dependency for MPI + * + * @param in_p The #task_dependency to add + * @param out_p The #task_dependency where in_p is added + * @param len The length of the arrays + * @param type The MPI datatype + */ +void task_dependency_sum(void *in_p, void *out_p, int *len, + MPI_Datatype *type) { + + /* change pointer type */ + struct task_dependency *in = (struct task_dependency *)in_p; + struct task_dependency *out = (struct task_dependency *)out_p; + + /* Loop over all the current objects */ + for (int i = 0; i < *len; i++) { + + /* loop over all the object set in invals */ + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + + /* Have we reached the end of the links? */ + if (in[i].number_link[j] == -1) { + break; + } + + /* get a few variables */ + int tb_type = in[i].type_out[j]; + int tb_subtype = in[i].subtype_out[j]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check tasks */ + if (tb_type >= task_type_count) { + error("Unknown task type %i", tb_type); + } + + if (tb_subtype >= task_subtype_count) { + error("Unknown subtask type %i", tb_subtype); + } +#endif + + /* find the corresponding id */ + int k = 0; + while (k < MAX_NUMBER_DEP) { + /* have we reached the end of the links? */ + if (out[i].number_link[k] == -1) { + /* reset the counter in order to be safe */ + out[i].number_link[k] = 0; + out[i].number_rank[k] = 0; + + /* set the relation */ + out[i].type_in = in[i].type_in; + out[i].subtype_in = in[i].subtype_in; + out[i].implicit_in = in[i].implicit_in; + + out[i].type_out[k] = in[i].type_out[j]; + out[i].subtype_out[k] = in[i].subtype_out[j]; + out[i].implicit_out[k] = in[i].implicit_out[j]; + break; + } + + /* do we have the same relation? */ + if (out[i].type_out[k] == tb_type && + out[i].subtype_out[k] == tb_subtype) { + break; + } + + k++; + } + + /* Check if we are still in the memory */ + if (k == MAX_NUMBER_DEP) { + error("Not enough memory, please increase MAX_NUMBER_DEP"); + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Check if correct relation */ + if (out[i].type_in != in[i].type_in || + out[i].subtype_in != in[i].subtype_in || + out[i].implicit_in != in[i].implicit_in || + out[i].type_out[k] != in[i].type_out[j] || + out[i].subtype_out[k] != in[i].subtype_out[j] || + out[i].implicit_out[k] != in[i].implicit_out[j]) { + error("Tasks do not correspond"); + } +#endif + + /* sum the contributions */ + out[i].number_link[k] += in[i].number_link[j]; + out[i].number_rank[k] += in[i].number_rank[j]; + } + } + + return; +} + +#endif // WITH_MPI + /** * @brief Write a dot file with the task dependencies. * @@ -152,241 +355,178 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) { const ticks tic = getticks(); - /* Conservative number of dependencies per task type */ - const int max_nber_dep = 128; - /* Number of possible relations between tasks */ - const int nber_relation = - 2 * task_type_count * task_subtype_count * max_nber_dep; + const int nber_tasks = task_type_count * task_subtype_count; - /* To get the table of max_nber_dep for a task: - * ind = (ta * task_subtype_count + sa) * max_nber_dep * 2 + /* To get the table for a task: + * ind = (ta * task_subtype_count + sa) * where ta is the value of task_type and sa is the value of * task_subtype */ - int *table = (int *)malloc(nber_relation * sizeof(int)); - if (table == NULL) - error("Error allocating memory for task-dependency graph (table)."); + struct task_dependency *task_dep = (struct task_dependency *)malloc( + nber_tasks * sizeof(struct task_dependency)); - int *count_rel = (int *)malloc(nber_relation * sizeof(int) / 2); - if (count_rel == NULL) - error("Error allocating memory for task-dependency graph (count_rel)."); - - /* Reset everything */ - for (int i = 0; i < nber_relation; i++) table[i] = -1; - for (int i = 0; i < nber_relation / 2; i++) count_rel[i] = 0; - - /* Create file */ - char filename[200] = "dependency_graph.dot"; - FILE *f = fopen(filename, "w"); - if (f == NULL) error("Error opening dependency graph file."); + if (task_dep == NULL) + error("Error allocating memory for task-dependency graph (table)."); - /* Write header */ - fprintf(f, "digraph task_dep {\n"); - fprintf(f, "label=\"Task dependencies for SWIFT %s\";\n", git_revision()); - fprintf(f, "\t compound=true;\n"); - fprintf(f, "\t ratio=0.66;\n"); - fprintf(f, "\t node[nodesep=0.15];\n"); + /* Reset counter */ + for (int i = 0; i < nber_tasks; i++) { + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Use number_link as indicator of the existance of a relation */ + task_dep[i].number_link[j] = -1; + } + } /* loop over all tasks */ for (int i = 0; i < s->nr_tasks; i++) { const struct task *ta = &s->tasks[i]; + /* Current index */ + const int ind = ta->type * task_subtype_count + ta->subtype; + + struct task_dependency *cur = &task_dep[ind]; + + /* Set ta */ + cur->type_in = ta->type; + cur->subtype_in = ta->subtype; + cur->implicit_in = ta->implicit; + /* and their dependencies */ for (int j = 0; j < ta->nr_unlock_tasks; j++) { const struct task *tb = ta->unlock_tasks[j]; - /* check if dependency already written */ - int written = 0; - - /* Current index */ - int ind = ta->type * task_subtype_count + ta->subtype; - ind *= 2 * max_nber_dep; - int k = 0; - int *cur = &table[ind]; - while (k < max_nber_dep) { + while (k < MAX_NUMBER_DEP) { /* not written yet */ - if (cur[0] == -1) { - cur[0] = tb->type; - cur[1] = tb->subtype; + if (cur->number_link[k] == -1) { + /* set tb */ + cur->type_out[k] = tb->type; + cur->subtype_out[k] = tb->subtype; + cur->implicit_out[k] = tb->implicit; + + /* statistics */ + const int count = scheduler_get_number_relation(s, ta, tb); + cur->number_link[k] = count; + cur->number_rank[k] = 1; + break; } /* already written */ - if (cur[0] == tb->type && cur[1] == tb->subtype) { - written = 1; + if (cur->type_out[k] == tb->type && + cur->subtype_out[k] == tb->subtype) { break; } k += 1; - cur = &cur[2]; } - /* max_nber_dep is too small */ - if (k == max_nber_dep) - error("Not enough memory, please increase max_nber_dep"); + /* MAX_NUMBER_DEP is too small */ + if (k == MAX_NUMBER_DEP) + error("Not enough memory, please increase MAX_NUMBER_DEP"); + } + } - /* Increase counter of relation */ - count_rel[ind / 2 + k] += 1; +#ifdef WITH_MPI + /* create MPI operator */ + MPI_Datatype data_type; + task_dependency_define(&data_type); - /* Not written yet => write it */ - if (!written) { + MPI_Op sum; + MPI_Op_create(task_dependency_sum, /* commute */ 1, &sum); - /* text to write */ - char ta_name[200]; - char tb_name[200]; + /* create recv buffer */ + struct task_dependency *recv = NULL; - /* construct line */ - scheduler_task_dependency_name(ta->type, ta->subtype, ta_name); - scheduler_task_dependency_name(tb->type, tb->subtype, tb_name); - - /* Change colour of implicit tasks */ - if (ta->implicit) - fprintf(f, "\t %s [style = filled];\n\t %s [color = lightgrey];\n", - ta_name, ta_name); - if (tb->implicit) - fprintf(f, "\t %s [style = filled];\n\t %s [color = lightgrey];\n", - tb_name, tb_name); - - /* Change shape of MPI communications */ - if (ta->type == task_type_send || ta->type == task_type_recv) - fprintf(f, "\t \"%s %s\" [shape = diamond];\n", - taskID_names[ta->type], subtaskID_names[ta->subtype]); - if (tb->type == task_type_send || tb->type == task_type_recv) - fprintf(f, "\t \"%s %s\" [shape = diamond];\n", - taskID_names[tb->type], subtaskID_names[tb->subtype]); + if (s->nodeID == 0) { + recv = (struct task_dependency *)malloc(nber_tasks * + sizeof(struct task_dependency)); + + /* reset counter */ + for (int i = 0; i < nber_tasks; i++) { + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Use number_link as indicator of the existance of a relation */ + recv[i].number_link[j] = -1; } } } - int density_cluster[4] = {0}; - int gradient_cluster[4] = {0}; - int force_cluster[4] = {0}; - int gravity_cluster[5] = {0}; - int stars_density_cluster[4] = {0}; - - /* Check whether we need to construct a group of tasks */ - for (int type = 0; type < task_type_count; ++type) { - - for (int subtype = 0; subtype < task_subtype_count; ++subtype) { - - const int ind = 2 * (type * task_subtype_count + subtype) * max_nber_dep; - - /* Does this task/sub-task exist? */ - if (table[ind] != -1) { - - for (int k = 0; k < 4; ++k) { - if (type == task_type_self + k && subtype == task_subtype_density) - density_cluster[k] = 1; - if (type == task_type_self + k && subtype == task_subtype_gradient) - gradient_cluster[k] = 1; - if (type == task_type_self + k && subtype == task_subtype_force) - force_cluster[k] = 1; - if (type == task_type_self + k && subtype == task_subtype_grav) - gravity_cluster[k] = 1; - if (type == task_type_self + k && - subtype == task_subtype_stars_density) - stars_density_cluster[k] = 1; - } - if (type == task_type_grav_mesh) gravity_cluster[2] = 1; - if (type == task_type_grav_long_range) gravity_cluster[3] = 1; - if (type == task_type_grav_mm) gravity_cluster[4] = 1; - } - } + /* Do the reduction */ + int test = + MPI_Reduce(task_dep, recv, nber_tasks, data_type, sum, 0, MPI_COMM_WORLD); + if (test != MPI_SUCCESS) error("MPI reduce failed"); + + /* free some memory */ + if (s->nodeID == 0) { + free(task_dep); + task_dep = recv; } +#endif + + if (s->nodeID == 0) { + /* Create file */ + char *filename = "dependency_graph.csv"; + FILE *f = fopen(filename, "w"); + if (f == NULL) error("Error opening dependency graph file."); + + /* Write header */ + fprintf(f, "# %s\n", git_revision()); + fprintf( + f, + "task_in,task_out,implicit_in,implicit_out,mpi_in,mpi_out,cluster_in," + "cluster_out,number_link,number_rank\n"); + + for (int i = 0; i < nber_tasks; i++) { + for (int j = 0; j < MAX_NUMBER_DEP; j++) { + /* Does this link exists */ + if (task_dep[i].number_link[j] == -1) { + continue; + } + + /* Define a few variables */ + const int ta_type = task_dep[i].type_in; + const int ta_subtype = task_dep[i].subtype_in; + const int ta_implicit = task_dep[i].implicit_in; + + const int tb_type = task_dep[i].type_out[j]; + const int tb_subtype = task_dep[i].subtype_out[j]; + const int tb_implicit = task_dep[i].implicit_out[j]; - /* Make a cluster for the density tasks */ - fprintf(f, "\t subgraph cluster0{\n"); - fprintf(f, "\t\t label=\"\";\n"); - for (int k = 0; k < 4; ++k) - if (density_cluster[k]) - fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k], - subtaskID_names[task_subtype_density]); - fprintf(f, "\t};\n"); - - /* Make a cluster for the force tasks */ - fprintf(f, "\t subgraph cluster1{\n"); - fprintf(f, "\t\t label=\"\";\n"); - for (int k = 0; k < 4; ++k) - if (force_cluster[k]) - fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k], - subtaskID_names[task_subtype_force]); - fprintf(f, "\t};\n"); - - /* Make a cluster for the gradient tasks */ - fprintf(f, "\t subgraph cluster2{\n"); - fprintf(f, "\t\t label=\"\";\n"); - for (int k = 0; k < 4; ++k) - if (gradient_cluster[k]) - fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k], - subtaskID_names[task_subtype_gradient]); - fprintf(f, "\t};\n"); - - /* Make a cluster for the gravity tasks */ - fprintf(f, "\t subgraph cluster3{\n"); - fprintf(f, "\t\t label=\"\";\n"); - for (int k = 0; k < 2; ++k) - if (gravity_cluster[k]) - fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k], - subtaskID_names[task_subtype_grav]); - if (gravity_cluster[2]) - fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_mesh]); - if (gravity_cluster[3]) - fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_long_range]); - if (gravity_cluster[4]) - fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_mm]); - fprintf(f, "\t};\n"); - - /* Make a cluster for the density tasks */ - fprintf(f, "\t subgraph cluster4{\n"); - fprintf(f, "\t\t label=\"\";\n"); - for (int k = 0; k < 4; ++k) - if (stars_density_cluster[k]) - fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k], - subtaskID_names[task_subtype_stars_density]); - fprintf(f, "\t};\n"); - - /* Write down the number of relation */ - for (int ta_type = 0; ta_type < task_type_count; ta_type++) { - - for (int ta_subtype = 0; ta_subtype < task_subtype_count; ta_subtype++) { - - /* Get task indice */ - const int ind = - (ta_type * task_subtype_count + ta_subtype) * max_nber_dep; - - /* Loop over dependencies */ - for (int k = 0; k < max_nber_dep; k++) { - - if (count_rel[ind + k] == 0) continue; - - /* Get task type */ - const int i = 2 * (ind + k); - int tb_type = table[i]; - int tb_subtype = table[i + 1]; - - /* Get names */ + const int count = task_dep[i].number_link[j]; + const int number_rank = task_dep[i].number_rank[j]; + + /* text to write */ char ta_name[200]; char tb_name[200]; - scheduler_task_dependency_name(ta_type, ta_subtype, ta_name); - scheduler_task_dependency_name(tb_type, tb_subtype, tb_name); + /* construct line */ + task_get_full_name(ta_type, ta_subtype, ta_name); + task_get_full_name(tb_type, tb_subtype, tb_name); + + /* Check if MPI */ + int ta_mpi = 0; + if (ta_type == task_type_send || ta_type == task_type_recv) ta_mpi = 1; + + int tb_mpi = 0; + if (tb_type == task_type_send || tb_type == task_type_recv) tb_mpi = 1; - /* Write to the fle */ - fprintf(f, "\t %s->%s[label=%i];\n", ta_name, tb_name, - count_rel[ind + k]); + /* Get group name */ + char ta_cluster[20]; + char tb_cluster[20]; + task_get_group_name(ta_type, ta_subtype, ta_cluster); + task_get_group_name(tb_type, tb_subtype, tb_cluster); + + fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d\n", ta_name, tb_name, + ta_implicit, tb_implicit, ta_mpi, tb_mpi, ta_cluster, + tb_cluster, count, number_rank); } } + /* Close the file */ + fclose(f); } - /* Close the file */ - fprintf(f, "}"); - fclose(f); - /* Be clean */ - free(table); - free(count_rel); + free(task_dep); if (verbose) message("Printing task graph took %.3f %s.", @@ -408,7 +548,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Reset the redo flag. */ redo = 0; - /* Non-splittable task? */ + /* Empty task? */ if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) || t->ci->hydro.count == 0 || (t->cj != NULL && t->cj->hydro.count == 0)) { t->type = task_type_none; @@ -489,6 +629,12 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { double shift[3]; const int sid = space_getsid(s->space, &ci, &cj, shift); +#ifdef SWIFT_DEBUG_CHECKS + if (sid != t->flags) + error("Got pair task with incorrect flags: sid=%d flags=%lld", sid, + t->flags); +#endif + /* Should this task be split-up? */ if (cell_can_split_pair_hydro_task(ci) && cell_can_split_pair_hydro_task(cj)) { @@ -883,7 +1029,7 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) { /* Reset the redo flag. */ redo = 0; - /* Non-splittable task? */ + /* Empty task? */ if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) || t->ci->stars.count == 0 || (t->cj != NULL && t->cj->stars.count == 0)) { t->type = task_type_none; @@ -1661,9 +1807,9 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type, t->nr_unlock_tasks = 0; #ifdef SWIFT_DEBUG_TASKS t->rid = -1; +#endif t->tic = 0; t->toc = 0; -#endif /* Add an index for it. */ // lock_lock( &s->lock ); @@ -1882,17 +2028,12 @@ void scheduler_reweight(struct scheduler *s, int verbose) { /* Run through the tasks backwards and set their weights. */ for (int k = nr_tasks - 1; k >= 0; k--) { struct task *t = &tasks[tid[k]]; + float cost = 0.f; t->weight = 0.f; -#if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS)) - t->cost = 0.f; -#endif + for (int j = 0; j < t->nr_unlock_tasks; j++) if (t->unlock_tasks[j]->weight > t->weight) t->weight = t->unlock_tasks[j]->weight; - float cost = 0.f; -#if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS)) - int partcost = 1; -#endif const float count_i = (t->ci != NULL) ? t->ci->hydro.count : 0.f; const float count_j = (t->cj != NULL) ? t->cj->hydro.count : 0.f; @@ -1907,10 +2048,15 @@ void scheduler_reweight(struct scheduler *s, int verbose) { (sizeof(int) * 8 - intrinsics_clz(t->ci->hydro.count)); break; + case task_type_stars_sort: + cost = wscale * intrinsics_popcount(t->flags) * scount_i * + (sizeof(int) * 8 - intrinsics_clz(t->ci->stars.count)); + break; + case task_type_self: - if (t->subtype == task_subtype_grav) + if (t->subtype == task_subtype_grav) { cost = 1.f * (wscale * gcount_i) * gcount_i; - else if (t->subtype == task_subtype_external_grav) + } else if (t->subtype == task_subtype_external_grav) cost = 1.f * wscale * gcount_i; else if (t->subtype == task_subtype_stars_density) cost = 1.f * wscale * scount_i * count_i; @@ -2010,18 +2156,12 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = wscale * count_i + wscale * gcount_i; break; case task_type_send: -#if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS)) - partcost = 0; -#endif if (count_i < 1e5) cost = 10.f * (wscale * count_i) * count_i; else cost = 2e9; break; case task_type_recv: -#if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS)) - partcost = 0; -#endif if (count_i < 1e5) cost = 5.f * (wscale * count_i) * count_i; else @@ -2031,10 +2171,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = 0; break; } - -#if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS)) - if (partcost) t->cost = cost; -#endif t->weight += cost; } @@ -2107,14 +2243,14 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements, */ void scheduler_start(struct scheduler *s) { -/* Reset all task debugging timers */ -#ifdef SWIFT_DEBUG_TASKS + /* Reset all task timers. */ for (int i = 0; i < s->nr_tasks; ++i) { s->tasks[i].tic = 0; s->tasks[i].toc = 0; +#ifdef SWIFT_DEBUG_TASKS s->tasks[i].rid = -1; - } #endif + } /* Re-wait the tasks. */ if (s->active_count > 1000) { @@ -2196,6 +2332,7 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { case task_type_kick2: case task_type_stars_ghost: case task_type_logger: + case task_type_stars_sort: case task_type_timestep: qid = t->ci->super->owner; break; @@ -2365,9 +2502,7 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) { /* Task definitely done, signal any sleeping runners. */ if (!t->implicit) { -#ifdef SWIFT_DEBUG_TASKS t->toc = getticks(); -#endif pthread_mutex_lock(&s->sleep_mutex); atomic_dec(&s->waiting); pthread_cond_broadcast(&s->sleep_cond); @@ -2408,9 +2543,7 @@ struct task *scheduler_unlock(struct scheduler *s, struct task *t) { /* Task definitely done. */ if (!t->implicit) { -#ifdef SWIFT_DEBUG_TASKS t->toc = getticks(); -#endif pthread_mutex_lock(&s->sleep_mutex); atomic_dec(&s->waiting); pthread_cond_broadcast(&s->sleep_cond); @@ -2494,13 +2627,13 @@ struct task *scheduler_gettask(struct scheduler *s, int qid, } } -#ifdef SWIFT_DEBUG_TASKS /* Start the timer on this task, if we got one. */ if (res != NULL) { res->tic = getticks(); +#ifdef SWIFT_DEBUG_TASKS res->rid = qid; - } #endif + } /* No milk today. */ return res; diff --git a/src/serial_io.c b/src/serial_io.c index 79edcd769a1fbfd81089098a69d6732519ebd621..b55f4780814fe45544a5680e95e67f55194fc5e8 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -42,6 +42,7 @@ #include "cooling_io.h" #include "dimension.h" #include "engine.h" +#include "entropy_floor.h" #include "error.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -51,8 +52,11 @@ #include "kernel_hydro.h" #include "part.h" #include "part_type.h" +#include "star_formation_io.h" #include "stars_io.h" +#include "tracers_io.h" #include "units.h" +#include "velociraptor_io.h" #include "xmf.h" /** @@ -500,6 +504,23 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, error("ICs dimensionality (%dD) does not match code dimensionality (%dD)", dimension, (int)hydro_dimension); + /* Check whether the number of files is specified (if the info exists) */ + const hid_t hid_files = H5Aexists(h_grp, "NumFilesPerSnapshot"); + int num_files = 1; + if (hid_files < 0) + error( + "Error while testing the existance of 'NumFilesPerSnapshot' " + "attribute"); + if (hid_files > 0) + io_read_attribute(h_grp, "NumFilesPerSnapshot", INT, &num_files); + if (num_files != 1) + error( + "ICs are split over multiples files (%d). SWIFT cannot handle this " + "case. The script /tools/combine_ics.py is availalbe in the " + "repository " + "to combine files into a valid input file.", + num_files); + /* Read the relevant information and print status */ int flag_entropy_temp[6]; io_read_attribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp); @@ -758,13 +779,22 @@ void write_output_serial(struct engine* e, const char* baseName, int mpi_size, MPI_Comm comm, MPI_Info info) { hid_t h_file = 0, h_grp = 0; - int periodic = e->s->periodic; int numFiles = 1; const struct part* parts = e->s->parts; const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; struct swift_params* params = e->parameter_file; + const int with_cosmology = e->policy & engine_policy_cosmology; + const int with_cooling = e->policy & engine_policy_cooling; + const int with_temperature = e->policy & engine_policy_temperature; +#ifdef HAVE_VELOCIRAPTOR + const int with_stf = (e->policy & engine_policy_structure_finding) && + (e->s->gpart_group_data != NULL); +#else + const int with_stf = 0; +#endif + FILE* xmfFile = 0; /* Number of particles currently in the arrays */ @@ -775,9 +805,12 @@ void write_output_serial(struct engine* e, const char* baseName, // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; /* Number of particles that we will write */ - const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts; - const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts; - const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts; + const size_t Ntot_written = + e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; + const size_t Ngas_written = + e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; + const size_t Nstars_written = + e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; const size_t Nbaryons_written = Ngas_written + Nstars_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; @@ -823,28 +856,25 @@ void write_output_serial(struct engine* e, const char* baseName, h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); if (h_file < 0) error("Error while opening file '%s'.", fileName); - /* Open header to write simulation properties */ - /* message("Writing runtime parameters..."); */ - h_grp = H5Gcreate(h_file, "/RuntimePars", H5P_DEFAULT, H5P_DEFAULT, - H5P_DEFAULT); - if (h_grp < 0) error("Error while creating runtime parameters group\n"); - - /* Write the relevant information */ - io_write_attribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); - - /* Close runtime parameters */ - H5Gclose(h_grp); - /* Open header to write simulation properties */ /* message("Writing file header..."); */ h_grp = H5Gcreate(h_file, "/Header", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating file header\n"); + /* Convert basic output information to snapshot units */ + const double factor_time = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_TIME); + const double factor_length = units_conversion_factor( + internal_units, snapshot_units, UNIT_CONV_LENGTH); + const double dblTime = e->time * factor_time; + const double dim[3] = {e->s->dim[0] * factor_length, + e->s->dim[1] * factor_length, + e->s->dim[2] * factor_length}; + /* Print the relevant information and print status */ - io_write_attribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); - double dblTime = e->time; + io_write_attribute(h_grp, "BoxSize", DOUBLE, dim, 3); io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); - int dimension = (int)hydro_dimension; + const int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); @@ -897,8 +927,10 @@ void write_output_serial(struct engine* e, const char* baseName, h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating subgrid group"); + entropy_floor_write_flavour(h_grp); cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); + tracers_write_flavour(h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ @@ -1005,6 +1037,32 @@ void write_output_serial(struct engine* e, const char* baseName, H5Fclose(h_file); } + /* Now write the top-level cell structure */ + hid_t h_file_cells = 0, h_grp_cells = 0; + if (mpi_rank == 0) { + + /* Open the snapshot on rank 0 */ + h_file_cells = H5Fopen(fileName, H5F_ACC_RDWR, H5P_DEFAULT); + if (h_file_cells < 0) + error("Error while opening file '%s' on rank %d.", fileName, mpi_rank); + + /* Create the group we want in the file */ + h_grp_cells = H5Gcreate(h_file_cells, "/Cells", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp_cells < 0) error("Error while creating cells group"); + } + + /* Write the location of the particles in the arrays */ + io_write_cell_offsets(h_grp_cells, e->s->cdim, e->s->cells_top, + e->s->nr_cells, e->s->width, mpi_rank, N_total, offset, + internal_units, snapshot_units); + + /* Close everything */ + if (mpi_rank == 0) { + H5Gclose(h_grp_cells); + H5Fclose(h_file_cells); + } + /* Now loop over ranks and write the data */ for (int rank = 0; rank < mpi_size; ++rank) { @@ -1042,6 +1100,7 @@ void write_output_serial(struct engine* e, const char* baseName, struct part* parts_written = NULL; struct xpart* xparts_written = NULL; struct gpart* gparts_written = NULL; + struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; /* Write particle fields from the particle structure */ @@ -1054,8 +1113,19 @@ void write_output_serial(struct engine* e, const char* baseName, Nparticles = Ngas; hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); - num_fields += cooling_write_particles(xparts, list + num_fields, - e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += cooling_write_particles( + parts, xparts, list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += + velociraptor_write_parts(parts, xparts, list + num_fields); + } + num_fields += tracers_write_particles( + parts, xparts, list + num_fields, with_cosmology); + num_fields += star_formation_write_particles(parts, xparts, + list + num_fields); + } else { /* Ok, we need to fish out the particles we want */ @@ -1078,8 +1148,20 @@ void write_output_serial(struct engine* e, const char* baseName, &num_fields); num_fields += chemistry_write_particles(parts_written, list + num_fields); - num_fields += cooling_write_particles( - xparts_written, list + num_fields, e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += + cooling_write_particles(parts_written, xparts_written, + list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += velociraptor_write_parts( + parts_written, xparts_written, list + num_fields); + } + num_fields += + tracers_write_particles(parts_written, xparts_written, + list + num_fields, with_cosmology); + num_fields += star_formation_write_particles( + parts_written, xparts_written, list + num_fields); } } break; @@ -1089,6 +1171,10 @@ void write_output_serial(struct engine* e, const char* baseName, /* This is a DM-only run without inhibited particles */ Nparticles = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts(e->s->gpart_group_data, + list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -1099,12 +1185,27 @@ void write_output_serial(struct engine* e, const char* baseName, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); + if (with_stf) { + if (posix_memalign( + (void**)&gpart_group_data_written, gpart_align, + Ndm_written * sizeof(struct velociraptor_gpart_data)) != + 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + /* Collect the non-inhibited DM particles from gpart */ - io_collect_gparts_to_write(gparts, gparts_written, Ntot, - Ndm_written); + io_collect_gparts_to_write( + gparts, e->s->gpart_group_data, gparts_written, + gpart_group_data_written, Ntot, Ndm_written, with_stf); - /* Write DM particles */ + /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts( + gpart_group_data_written, list + num_fields); + } } } break; @@ -1114,6 +1215,14 @@ void write_output_serial(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ Nparticles = Nstars; stars_write_particles(sparts, list, &num_fields); + num_fields += + chemistry_write_sparticles(sparts, list + num_fields); + num_fields += tracers_write_sparticles(sparts, list + num_fields, + with_cosmology); + if (with_stf) { + num_fields += + velociraptor_write_sparts(sparts, list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -1130,6 +1239,14 @@ void write_output_serial(struct engine* e, const char* baseName, /* Select the fields to write */ stars_write_particles(sparts_written, list, &num_fields); + num_fields += + chemistry_write_sparticles(sparts, list + num_fields); + num_fields += tracers_write_sparticles(sparts, list + num_fields, + with_cosmology); + if (with_stf) { + num_fields += velociraptor_write_sparts(sparts_written, + list + num_fields); + } } } break; @@ -1156,6 +1273,7 @@ void write_output_serial(struct engine* e, const char* baseName, if (parts_written) free(parts_written); if (xparts_written) free(xparts_written); if (gparts_written) free(gparts_written); + if (gpart_group_data_written) free(gpart_group_data_written); if (sparts_written) free(sparts_written); /* Close particle group */ diff --git a/src/single_io.c b/src/single_io.c index 95cdb31028566eb8e7886313b64f5a0e730aec7d..4b23310ee02f3b485eff1a0358de850f497a3478 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -41,6 +41,7 @@ #include "cooling_io.h" #include "dimension.h" #include "engine.h" +#include "entropy_floor.h" #include "error.h" #include "gravity_io.h" #include "gravity_properties.h" @@ -50,8 +51,11 @@ #include "kernel_hydro.h" #include "part.h" #include "part_type.h" +#include "star_formation_io.h" #include "stars_io.h" +#include "tracers_io.h" #include "units.h" +#include "velociraptor_io.h" #include "xmf.h" /** @@ -404,6 +408,21 @@ void read_ic_single(const char* fileName, error("ICs dimensionality (%dD) does not match code dimensionality (%dD)", dimension, (int)hydro_dimension); + /* Check whether the number of files is specified (if the info exists) */ + const hid_t hid_files = H5Aexists(h_grp, "NumFilesPerSnapshot"); + int num_files = 1; + if (hid_files < 0) + error( + "Error while testing the existance of 'NumFilesPerSnapshot' attribute"); + if (hid_files > 0) + io_read_attribute(h_grp, "NumFilesPerSnapshot", INT, &num_files); + if (num_files != 1) + error( + "ICs are split over multiples files (%d). SWIFT cannot handle this " + "case. The script /tools/combine_ics.py is availalbe in the repository " + "to combine files into a valid input file.", + num_files); + /* Read the relevant information and print status */ int flag_entropy_temp[6]; io_read_attribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp); @@ -623,13 +642,21 @@ void write_output_single(struct engine* e, const char* baseName, const struct unit_system* snapshot_units) { hid_t h_file = 0, h_grp = 0; - int periodic = e->s->periodic; int numFiles = 1; const struct part* parts = e->s->parts; const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; struct swift_params* params = e->parameter_file; + const int with_cosmology = e->policy & engine_policy_cosmology; + const int with_cooling = e->policy & engine_policy_cooling; + const int with_temperature = e->policy & engine_policy_temperature; +#ifdef HAVE_VELOCIRAPTOR + const int with_stf = (e->policy & engine_policy_structure_finding) && + (e->s->gpart_group_data != NULL); +#else + const int with_stf = 0; +#endif /* Number of particles currently in the arrays */ const size_t Ntot = e->s->nr_gparts; @@ -639,9 +666,12 @@ void write_output_single(struct engine* e, const char* baseName, // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; /* Number of particles that we will write */ - const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts; - const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts; - const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts; + const size_t Ntot_written = + e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts; + const size_t Ngas_written = + e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; + const size_t Nstars_written = + e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; const size_t Nbaryons_written = Ngas_written + Nstars_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; @@ -678,28 +708,25 @@ void write_output_single(struct engine* e, const char* baseName, h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); if (h_file < 0) error("Error while opening file '%s'.", fileName); - /* Open header to write simulation properties */ - /* message("Writing runtime parameters..."); */ - h_grp = - H5Gcreate(h_file, "/RuntimePars", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if (h_grp < 0) error("Error while creating runtime parameters group\n"); - - /* Write the relevant information */ - io_write_attribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); - - /* Close runtime parameters */ - H5Gclose(h_grp); - /* Open header to write simulation properties */ /* message("Writing file header..."); */ h_grp = H5Gcreate(h_file, "/Header", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating file header\n"); + /* Convert basic output information to snapshot units */ + const double factor_time = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_TIME); + const double factor_length = + units_conversion_factor(internal_units, snapshot_units, UNIT_CONV_LENGTH); + const double dblTime = e->time * factor_time; + const double dim[3] = {e->s->dim[0] * factor_length, + e->s->dim[1] * factor_length, + e->s->dim[2] * factor_length}; + /* Print the relevant information and print status */ - io_write_attribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); - double dblTime = e->time; + io_write_attribute(h_grp, "BoxSize", DOUBLE, dim, 3); io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); - int dimension = (int)hydro_dimension; + const int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); @@ -752,8 +779,10 @@ void write_output_single(struct engine* e, const char* baseName, h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating subgrid group"); + entropy_floor_write_flavour(h_grp); cooling_write_flavour(h_grp, e->cooling_func); chemistry_write_flavour(h_grp); + tracers_write_flavour(h_grp); H5Gclose(h_grp); /* Print the gravity parameters */ @@ -805,6 +834,17 @@ void write_output_single(struct engine* e, const char* baseName, /* Print the system of Units used internally */ io_write_unit_system(h_file, internal_units, "InternalCodeUnits"); + /* Now write the top-level cell structure */ + long long global_offsets[swift_type_count] = {0}; + h_grp = H5Gcreate(h_file, "/Cells", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_grp < 0) error("Error while creating cells group"); + + /* Write the location of the particles in the arrays */ + io_write_cell_offsets(h_grp, e->s->cdim, e->s->cells_top, e->s->nr_cells, + e->s->width, e->nodeID, N_total, global_offsets, + internal_units, snapshot_units); + H5Gclose(h_grp); + /* Tell the user if a conversion will be needed */ if (e->verbose) { if (units_are_equal(snapshot_units, internal_units)) { @@ -863,6 +903,7 @@ void write_output_single(struct engine* e, const char* baseName, struct part* parts_written = NULL; struct xpart* xparts_written = NULL; struct gpart* gparts_written = NULL; + struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; /* Write particle fields from the particle structure */ @@ -875,8 +916,19 @@ void write_output_single(struct engine* e, const char* baseName, N = Ngas; hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); - num_fields += cooling_write_particles(xparts, list + num_fields, - e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += cooling_write_particles( + parts, xparts, list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += + velociraptor_write_parts(parts, xparts, list + num_fields); + } + num_fields += tracers_write_particles( + parts, xparts, list + num_fields, with_cosmology); + num_fields += + star_formation_write_particles(parts, xparts, list + num_fields); + } else { /* Ok, we need to fish out the particles we want */ @@ -899,8 +951,19 @@ void write_output_single(struct engine* e, const char* baseName, &num_fields); num_fields += chemistry_write_particles(parts_written, list + num_fields); - num_fields += cooling_write_particles( - xparts_written, list + num_fields, e->cooling_func); + if (with_cooling || with_temperature) { + num_fields += + cooling_write_particles(parts_written, xparts_written, + list + num_fields, e->cooling_func); + } + if (with_stf) { + num_fields += velociraptor_write_parts( + parts_written, xparts_written, list + num_fields); + } + num_fields += tracers_write_particles( + parts_written, xparts_written, list + num_fields, with_cosmology); + num_fields += star_formation_write_particles( + parts_written, xparts_written, list + num_fields); } } break; @@ -910,6 +973,10 @@ void write_output_single(struct engine* e, const char* baseName, /* This is a DM-only run without inhibited particles */ N = Ntot; darkmatter_write_particles(gparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts(e->s->gpart_group_data, + list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -920,11 +987,26 @@ void write_output_single(struct engine* e, const char* baseName, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); + if (with_stf) { + if (posix_memalign( + (void**)&gpart_group_data_written, gpart_align, + Ndm_written * sizeof(struct velociraptor_gpart_data)) != 0) + error( + "Error while allocating temporart memory for gparts STF " + "data"); + } + /* Collect the non-inhibited DM particles from gpart */ - io_collect_gparts_to_write(gparts, gparts_written, Ntot, Ndm_written); + io_collect_gparts_to_write(gparts, e->s->gpart_group_data, + gparts_written, gpart_group_data_written, + Ntot, Ndm_written, with_stf); - /* Write DM particles */ + /* Select the fields to write */ darkmatter_write_particles(gparts_written, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_gparts(gpart_group_data_written, + list + num_fields); + } } } break; @@ -934,6 +1016,12 @@ void write_output_single(struct engine* e, const char* baseName, /* No inhibted particles: easy case */ N = Nstars; stars_write_particles(sparts, list, &num_fields); + num_fields += chemistry_write_sparticles(sparts, list + num_fields); + num_fields += tracers_write_sparticles(sparts, list + num_fields, + with_cosmology); + if (with_stf) { + num_fields += velociraptor_write_sparts(sparts, list + num_fields); + } } else { /* Ok, we need to fish out the particles we want */ @@ -950,6 +1038,14 @@ void write_output_single(struct engine* e, const char* baseName, /* Select the fields to write */ stars_write_particles(sparts_written, list, &num_fields); + num_fields += + chemistry_write_sparticles(sparts_written, list + num_fields); + num_fields += tracers_write_sparticles( + sparts_written, list + num_fields, with_cosmology); + if (with_stf) { + num_fields += + velociraptor_write_sparts(sparts_written, list + num_fields); + } } } break; @@ -975,6 +1071,7 @@ void write_output_single(struct engine* e, const char* baseName, if (parts_written) free(parts_written); if (xparts_written) free(xparts_written); if (gparts_written) free(gparts_written); + if (gpart_group_data_written) free(gpart_group_data_written); if (sparts_written) free(sparts_written); /* Close particle group */ diff --git a/src/sourceterms.c b/src/sourceterms.c deleted file mode 100644 index 993045e61503e4e78b855816921bc057706b76d1..0000000000000000000000000000000000000000 --- a/src/sourceterms.c +++ /dev/null @@ -1,85 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@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" - -/* Local includes. */ -#include "const.h" -#include "hydro.h" -#include "parser.h" -#include "units.h" - -/* This object's header. */ -#include "sourceterms.h" - -/** - * @brief Initialises the sourceterms - * - * @param parameter_file The parsed parameter file - * @param us The current internal system of units - * @param source the structure that has all the source term properties - */ -void sourceterms_init(struct swift_params *parameter_file, - struct unit_system *us, struct sourceterms *source) { -#ifdef SOURCETERMS_SN_FEEDBACK - supernova_init(parameter_file, us, source); -#endif /* SOURCETERMS_SN_FEEDBACK */ -}; - -/** - * @brief Prints the properties of the source terms to stdout - * @param source the structure that has all the source term properties - */ -void sourceterms_print(struct sourceterms *source) { -#ifdef SOURCETERMS_NONE - error(" no sourceterms defined yet you ran with -F"); -#ifdef SOURCETERMS_SN_FEEDBACK -#error "can't have sourceterms when defined SOURCETERMS_NONE" -#endif -#endif -#ifdef SOURCETERMS_SN_FEEDBACK - supernova_print(source); -#endif /* SOURCETERMS_SN_FEEDBACK */ -}; - -/** - * @brief Write a sourceterms struct to the given FILE as a stream of bytes. - * - * @param sourceterms the struct - * @param stream the file stream - */ -void sourceterms_struct_dump(const struct sourceterms *sourceterms, - FILE *stream) { - restart_write_blocks((void *)sourceterms, sizeof(struct sourceterms), 1, - stream, "sourceterms", "sourceterms"); -} - -/** - * @brief Restore a sourceterms struct from the given FILE as a stream of - * bytes. - * - * @param sourceterms the struct - * @param stream the file stream - */ -void sourceterms_struct_restore(const struct sourceterms *sourceterms, - FILE *stream) { - restart_read_blocks((void *)sourceterms, sizeof(struct sourceterms), 1, - stream, NULL, "sourceterms"); -} diff --git a/src/sourceterms.h b/src/sourceterms.h deleted file mode 100644 index 407d2f19362531a3fd3537889593c484319919b5..0000000000000000000000000000000000000000 --- a/src/sourceterms.h +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@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 SWIFT_SOURCETERMS_H -#define SWIFT_SOURCETERMS_H - -/** - * @file src/sourceterms.h - * @brief Branches between the different sourceterms functions. - */ - -#include "./const.h" -#include "runner.h" - -#ifdef SOURCETERMS_SN_FEEDBACK -#include "sourceterms/sn_feedback/sn_feedback_struct.h" -#endif - -/* So far only one model here */ -struct sourceterms { -#ifdef SOURCETERMS_SN_FEEDBACK - struct supernova_struct supernova; -#endif -}; -#ifdef SOURCETERMS_SN_FEEDBACK -#include "sourceterms/sn_feedback/sn_feedback.h" -#endif - -void sourceterms_init(struct swift_params* parameter_file, - struct unit_system* us, struct sourceterms* source); -void sourceterms_print(struct sourceterms* source); - -/* Dump/restore. */ -void sourceterms_struct_dump(const struct sourceterms* source, FILE* stream); -void sourceterms_struct_restore(const struct sourceterms* source, FILE* stream); - -/** - * @brief Routines related to source terms - * @param cell_min: corner of cell to test - * @param cell_width: width of cell to test - * @param sourceterms: properties of source terms to test - * @param dimen: dimensionality of the problem - * - * This routine tests whether a source term should be applied to this cell - * return: 1 if yes, return: 0 if no - */ - -__attribute__((always_inline)) INLINE static int sourceterms_test_cell( - const double cell_min[], const double cell_width[], - struct sourceterms* sourceterms, const int dimen) { -#ifdef SOURCETERMS_SN_FEEDBACK - return supernova_feedback_test_cell(cell_min, cell_width, sourceterms, dimen); -#endif - return 0; -}; - -__attribute__((always_inline)) INLINE static void sourceterms_apply( - struct runner* r, struct sourceterms* sourceterms, struct cell* c) { -#ifdef SOURCETERMS_SN_FEEDBACK - supernova_feedback_apply(r, sourceterms, c); -#endif -}; -#endif /* SWIFT_SOURCETERMS_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback.h b/src/sourceterms/sn_feedback/sn_feedback.h deleted file mode 100644 index 411673c37e82ff89d906425d1cadaa135c46a38d..0000000000000000000000000000000000000000 --- a/src/sourceterms/sn_feedback/sn_feedback.h +++ /dev/null @@ -1,192 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@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 SWIFT_SN_FEEDBACK_H -#define SWIFT_SN_FEEDBACK_H -#include <float.h> -/* Config parameters. */ -#include "../config.h" - -#include "engine.h" -#include "equation_of_state.h" -#include "hydro.h" -#include "runner.h" -#include "timestep.h" - -/** - * @file src/sourceterms/sn_feedback.h - * - * @brief Routines related to sourceterms (supernova feedback): determine if - * feedback occurs in this cell - * - * @param cell_min: corner of cell to test - * @param cell_width: width of cell to test - * @param sourceterms: properties of source terms to test - * @param dimen: dimensionality of the problem - * - * This routine tests whether a source term should be applied to this cell - * return: 1 if yes, return: 0 if no - */ -__attribute__((always_inline)) INLINE static int supernova_feedback_test_cell( - const double cell_min[], const double cell_width[], - struct sourceterms* sourceterms, const int dimen) { - if (sourceterms->supernova.status == supernova_is_done) return 0; - - const double location[3] = {sourceterms->supernova.x, - sourceterms->supernova.y, - sourceterms->supernova.z}; - for (int i = 0; i < dimen; i++) { - if (cell_min[i] > location[i]) return 0; - if ((cell_min[i] + cell_width[i]) <= location[i]) return 0; - }; - return 1; -}; - -/** - * @file src/sourceterms/sn_feedback.h - * - * @brief Routines related to source terms (supernova feedback): perform - * feedback in this cell - * @param r: the runner - * @param sourceterms the structure describing the source terms properties - * @param c the cell to apply feedback to - * - * This routine heats an individual particle (p), increasing its thermal energy - * per unit mass - * by supernova energy / particle mass. - */ -__attribute__((always_inline)) INLINE static void supernova_feedback_apply( - struct runner* restrict r, struct sourceterms* restrict sourceterms, - struct cell* restrict c) { - - const int count = c->count; - struct part* restrict parts = c->parts; - struct xpart* restrict xparts = c->xparts; - const double timeBase = r->e->timeBase; - const int ti_current = r->e->ti_current; - - /* inject SN energy into the particle with highest id in this cell if it is - * active */ - int imax = 0; - struct part* restrict p_sn = NULL; - struct xpart* restrict xp_sn = NULL; - - for (int i = 0; i < count; i++) { - - /* Get a direct pointer on the part. */ - struct part* restrict p = &parts[i]; - if (p->id > imax) { - imax = p->id; - p_sn = p; - xp_sn = &xparts[i]; - } - } - - /* Is this part within the time step? */ - if (p_sn->ti_begin == ti_current) { - - /* Does this time step straddle the feedback injection time? */ - const float t_begin = p_sn->ti_begin * timeBase; - const float t_end = p_sn->ti_end * timeBase; - if (t_begin <= sourceterms->supernova.time && - t_end > sourceterms->supernova.time) { - - /* store old time step */ - const int dti_old = p_sn->ti_end - p_sn->ti_begin; - - /* add supernova feedback */ - const float u_old = hydro_get_internal_energy(p_sn, 0); - const float ent_old = hydro_get_entropy(p_sn, 0.0); - const float u_new = - u_old + sourceterms->supernova.energy / hydro_get_mass(p_sn); - hydro_set_internal_energy(p_sn, u_new); - const float u_set = hydro_get_internal_energy(p_sn, 0.0); - const float ent_set = hydro_get_entropy(p_sn, 0.0); - message( - " applied super nova, time = %e, location= %e %e %e velocity= %e %e " - "%e", - ti_current * timeBase, p_sn->x[0], p_sn->x[1], p_sn->x[2], p_sn->v[0], - p_sn->v[1], p_sn->v[2]); - message( - " injected SN energy in particle = %lld, increased energy from %e to " - "%e and is notw %e, entropy from %e to %e", - p_sn->id, u_old, u_new, u_set, ent_old, ent_set); - - /* label supernova as done */ - sourceterms->supernova.status = supernova_is_done; - - /* update timestep if new time step shorter than old time step */ - const int dti = get_part_timestep(p_sn, xp_sn, r->e); - if (dti < dti_old) { - p_sn->ti_end = p_sn->ti_begin + dti; - message(" changed timestep from %d to %d", dti_old, dti); - - /* apply simple time-step limiter on all particles in same cell: - */ - int i_limit = 0; - for (int i = 0; i < count; i++) { - struct part* restrict p = &parts[i]; - const int dti_old = p->ti_end - p->ti_begin; - if (dti_old > 2 * dti) { - i_limit++; - const int dti_new = 2 * dti; - p->ti_end = p->ti_begin + dti_new; - message(" old step = %d new step = %d", dti_old, dti_new); - } else - message(" old step = %d", dti_old); - } - message(" count= %d limited timestep of %d particles ", count, i_limit); - } /* end of limiter */ - error("end"); - } - } -}; - -/** - * @file src/sourceterms/sn_feedback.h - * - * @brief Routine to initialise supernova feedback - * @param parameterfile: the parse parmeter file - * @param us: the unit system in use - * @param sourceterms the structure describing the source terms properties - * - * This routine heats an individual particle (p), increasing its thermal energy - * per unit mass - * by supernova energy / particle mass. - */ - -__attribute__((always_inline)) INLINE static void supernova_init( - struct swift_params* parameter_file, struct unit_system* us, - struct sourceterms* source) { - source->supernova.time = parser_get_param_double(parameter_file, "SN:time"); - source->supernova.energy = - parser_get_param_double(parameter_file, "SN:energy"); - source->supernova.x = parser_get_param_double(parameter_file, "SN:x"); - source->supernova.y = parser_get_param_double(parameter_file, "SN:y"); - source->supernova.z = parser_get_param_double(parameter_file, "SN:z"); - source->supernova.status = supernova_is_not_done; -} -__attribute__((always_inline)) INLINE static void supernova_print( - struct sourceterms* source) { - message( - " Single SNe of energy= %e will explode at time= %e at location " - "(%e,%e,%e)", - source->supernova.energy, source->supernova.time, source->supernova.x, - source->supernova.y, source->supernova.z); -} -#endif /* SWIFT_SN_FEEDBACK_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback_struct.h b/src/sourceterms/sn_feedback/sn_feedback_struct.h deleted file mode 100644 index dd1842a6717c6c5a20352324cbe6b018c73e7b3e..0000000000000000000000000000000000000000 --- a/src/sourceterms/sn_feedback/sn_feedback_struct.h +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@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/>. - * - ******************************************************************************/ -/** - * @file src/sourceterms/sn_feedback_struct.h - * @brief Routines related to source terms (feedback) - * - * enumeration type that sets if supernova explosion is done (is_done) or still - * needs doing (is_not_done) - */ -#ifndef SWIFT_SN_FEEDBACK_STRUCT_H -#define SWIFT_SN_FEEDBACK_STRUCT_H -enum supernova_status { supernova_is_done, supernova_is_not_done }; - -/** - * @file src/sourceterms/sn_feedback_struct.h - * @brief Routines related to source terms (feedback) - * - * The structure that describes the source term (supernova feedback) - * It specifies the time, energy and location of the desired supernova - * explosion, and a status (supernova_is_done/supernova_is_not_done) - * that records the status of the supernova - */ -struct supernova_struct { - double time; - double energy; - double x, y, z; - enum supernova_status status; -}; -#endif /* SWIFT_SN_FEEDBACK_STRUCT_H */ diff --git a/src/space.c b/src/space.c index 361b1265c94fe9daf092b4646f3fb1fceac62b53..a05a1882d40f5a8feeb6000e8fe2fbc45f54f523 100644 --- a/src/space.c +++ b/src/space.c @@ -59,6 +59,7 @@ #include "stars.h" #include "threadpool.h" #include "tools.h" +#include "tracers.h" /* Split size. */ int space_splitsize = space_splitsize_default; @@ -70,6 +71,18 @@ int space_subsize_pair_stars = space_subsize_pair_stars_default; int space_subsize_self_stars = space_subsize_self_stars_default; int space_subdepth_diff_grav = space_subdepth_diff_grav_default; int space_maxsize = space_maxsize_default; + +/*! Number of extra #part we allocate memory for per top-level cell */ +int space_extra_parts = space_extra_parts_default; + +/*! Number of extra #spart we allocate memory for per top-level cell */ +int space_extra_sparts = space_extra_sparts_default; + +/*! Number of extra #gpart we allocate memory for per top-level cell */ +int space_extra_gparts = space_extra_gparts_default; + +/*! Expected maximal number of strays received at a rebuild */ +int space_expected_max_nr_strays = space_expected_max_nr_strays_default; #ifdef SWIFT_DEBUG_CHECKS int last_cell_id; #endif @@ -104,9 +117,12 @@ struct index_data { struct space *s; int *ind; int *cell_counts; - int count_inhibited_part; - int count_inhibited_gpart; - int count_inhibited_spart; + size_t count_inhibited_part; + size_t count_inhibited_gpart; + size_t count_inhibited_spart; + size_t count_extra_part; + size_t count_extra_gpart; + size_t count_extra_spart; }; /** @@ -136,13 +152,13 @@ void space_rebuild_recycle_rec(struct space *s, struct cell *c, c->progeny[k]->next = *cell_rec_begin; *cell_rec_begin = c->progeny[k]; - if (s->gravity) { + if (s->with_self_gravity) { c->progeny[k]->grav.multipole->next = *multipole_rec_begin; *multipole_rec_begin = c->progeny[k]->grav.multipole; } if (*cell_rec_end == NULL) *cell_rec_end = *cell_rec_begin; - if (s->gravity && *multipole_rec_end == NULL) + if (s->with_self_gravity && *multipole_rec_end == NULL) *multipole_rec_end = *multipole_rec_begin; c->progeny[k]->grav.multipole = NULL; @@ -167,24 +183,31 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, space_recycle_list(s, cell_rec_begin, cell_rec_end, multipole_rec_begin, multipole_rec_end); c->hydro.sorts = NULL; + c->stars.sorts = NULL; c->nr_tasks = 0; c->grav.nr_mm_tasks = 0; c->hydro.density = NULL; c->hydro.gradient = NULL; c->hydro.force = NULL; + c->hydro.limiter = NULL; c->grav.grav = NULL; c->grav.mm = NULL; c->hydro.dx_max_part = 0.0f; c->hydro.dx_max_sort = 0.0f; c->stars.dx_max_part = 0.f; + c->stars.dx_max_sort = 0.f; c->hydro.sorted = 0; + c->stars.sorted = 0; c->hydro.count = 0; + c->hydro.count_total = 0; c->hydro.updated = 0; c->hydro.inhibited = 0; c->grav.count = 0; + c->grav.count_total = 0; c->grav.updated = 0; c->grav.inhibited = 0; c->stars.count = 0; + c->stars.count_total = 0; c->stars.updated = 0; c->stars.inhibited = 0; c->grav.init = NULL; @@ -197,14 +220,16 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.ghost_out = NULL; c->stars.ghost = NULL; c->stars.density = NULL; + c->stars.feedback = NULL; c->kick1 = NULL; c->kick2 = NULL; c->timestep = NULL; + c->timestep_limiter = NULL; c->end_force = NULL; c->hydro.drift = NULL; c->grav.drift = NULL; + c->grav.drift_out = NULL; c->hydro.cooling = NULL; - c->sourceterms = NULL; c->grav.long_range = NULL; c->grav.down_in = NULL; c->grav.down = NULL; @@ -217,21 +242,31 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->grav.parts = NULL; c->stars.parts = NULL; c->hydro.do_sub_sort = 0; + c->stars.do_sub_sort = 0; c->grav.do_sub_drift = 0; c->hydro.do_sub_drift = 0; + c->hydro.do_sub_limiter = 0; + c->hydro.do_limiter = 0; c->hydro.ti_end_min = -1; c->hydro.ti_end_max = -1; c->grav.ti_end_min = -1; c->grav.ti_end_max = -1; + c->stars.ti_end_min = -1; #ifdef SWIFT_DEBUG_CHECKS c->cellID = 0; #endif - if (s->gravity) bzero(c->grav.multipole, sizeof(struct gravity_tensors)); - for (int i = 0; i < 13; i++) + if (s->with_self_gravity) + bzero(c->grav.multipole, sizeof(struct gravity_tensors)); + for (int i = 0; i < 13; i++) { if (c->hydro.sort[i] != NULL) { free(c->hydro.sort[i]); c->hydro.sort[i] = NULL; } + if (c->stars.sort[i] != NULL) { + free(c->stars.sort[i]); + c->stars.sort[i] = NULL; + } + } #if WITH_MPI c->mpi.tag = -1; @@ -240,12 +275,14 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->mpi.hydro.recv_gradient = NULL; c->mpi.grav.recv = NULL; c->mpi.recv_ti = NULL; + c->mpi.limiter.recv = NULL; c->mpi.hydro.send_xv = NULL; c->mpi.hydro.send_rho = NULL; c->mpi.hydro.send_gradient = NULL; c->mpi.grav.send = NULL; c->mpi.send_ti = NULL; + c->mpi.limiter.send = NULL; #endif } } @@ -434,7 +471,7 @@ void space_regrid(struct space *s, int verbose) { bzero(s->cells_top, s->nr_cells * sizeof(struct cell)); /* Allocate the multipoles for the top-level cells. */ - if (s->gravity) { + if (s->with_self_gravity) { if (posix_memalign((void **)&s->multipoles_top, multipole_align, s->nr_cells * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate top-level multipoles."); @@ -530,7 +567,7 @@ void space_regrid(struct space *s, int verbose) { c->mpi.grav.recv = NULL; c->mpi.grav.send = NULL; #endif // WITH_MPI - if (s->gravity) c->grav.multipole = &s->multipoles_top[cid]; + if (s->with_self_gravity) c->grav.multipole = &s->multipoles_top[cid]; #ifdef SWIFT_DEBUG_CHECKS c->cellID = -last_cell_id; last_cell_id++; @@ -607,6 +644,341 @@ void space_regrid(struct space *s, int verbose) { clocks_getunit()); } +/** + * @brief Allocate memory for the extra particles used for on-the-fly creation. + * + * This rarely actually allocates memory. Most of the time, we convert + * pre-allocated memory inot extra particles. + * + * This function also sets the extra particles' location to their top-level + * cells. They can then be sorted into their correct memory position later on. + * + * @param s The current #space. + * @param verbose Are we talkative? + */ +void space_allocate_extras(struct space *s, int verbose) { + + const int local_nodeID = s->e->nodeID; + + /* Anything to do here? (Abort if we don't want extras)*/ + if (space_extra_parts == 0 && space_extra_gparts == 0 && + space_extra_sparts == 0) + return; + + /* The top-level cells */ + const struct cell *cells = s->cells_top; + const double half_cell_width[3] = {0.5 * cells[0].width[0], + 0.5 * cells[0].width[1], + 0.5 * cells[0].width[2]}; + + /* The current number of particles (including spare ones) */ + size_t nr_parts = s->nr_parts; + size_t nr_gparts = s->nr_gparts; + size_t nr_sparts = s->nr_sparts; + + /* The current number of actual particles */ + size_t nr_actual_parts = nr_parts - s->nr_extra_parts; + size_t nr_actual_gparts = nr_gparts - s->nr_extra_gparts; + size_t nr_actual_sparts = nr_sparts - s->nr_extra_sparts; + + /* The number of particles we allocated memory for (MPI overhead) */ + size_t size_parts = s->size_parts; + size_t size_gparts = s->size_gparts; + size_t size_sparts = s->size_sparts; + + int local_cells = 0; + for (int i = 0; i < s->nr_cells; ++i) + if (s->cells_top[i].nodeID == local_nodeID) local_cells++; + + /* Number of extra particles we want for each type */ + const size_t expected_num_extra_parts = local_cells * space_extra_parts; + const size_t expected_num_extra_gparts = local_cells * space_extra_gparts; + const size_t expected_num_extra_sparts = local_cells * space_extra_sparts; + + if (verbose) { + message("Currently have %zd/%zd/%zd real particles.", nr_actual_parts, + nr_actual_gparts, nr_actual_sparts); + message("Currently have %zd/%zd/%zd spaces for extra particles.", + s->nr_extra_parts, s->nr_extra_gparts, s->nr_extra_sparts); + message("Requesting space for future %zd/%zd/%zd part/gpart/sparts.", + expected_num_extra_parts, expected_num_extra_gparts, + expected_num_extra_sparts); + } + + if (expected_num_extra_parts < s->nr_extra_parts) + error("Reduction in top-level cells number not handled."); + if (expected_num_extra_gparts < s->nr_extra_gparts) + error("Reduction in top-level cells number not handled."); + if (expected_num_extra_sparts < s->nr_extra_sparts) + error("Reduction in top-level cells number not handled."); + + /* Do we have enough space for the extra gparts (i.e. we haven't used up any) + * ? */ + if (nr_gparts + expected_num_extra_gparts > size_gparts) { + + /* Ok... need to put some more in the game */ + + /* Do we need to reallocate? */ + if (nr_actual_gparts + expected_num_extra_gparts > size_gparts) { + + size_gparts = (nr_actual_gparts + expected_num_extra_gparts) * + engine_redistribute_alloc_margin; + + if (verbose) + message("Re-allocating gparts array from %zd to %zd", s->size_gparts, + size_gparts); + + /* Create more space for parts */ + struct gpart *gparts_new = NULL; + if (posix_memalign((void **)&gparts_new, gpart_align, + sizeof(struct gpart) * size_gparts) != 0) + error("Failed to allocate new gpart data"); + const ptrdiff_t delta = gparts_new - s->gparts; + memcpy(gparts_new, s->gparts, sizeof(struct gpart) * s->size_gparts); + free(s->gparts); + s->gparts = gparts_new; + + /* Update the counter */ + s->size_gparts = size_gparts; + + /* We now need to reset all the part and spart pointers */ + for (size_t i = 0; i < nr_parts; ++i) { + if (s->parts[i].time_bin != time_bin_not_created) + s->parts[i].gpart += delta; + } + for (size_t i = 0; i < nr_sparts; ++i) { + if (s->sparts[i].time_bin != time_bin_not_created) + s->sparts[i].gpart += delta; + } + } + + /* Turn some of the allocated spares into particles we can use */ + for (size_t i = nr_gparts; i < nr_actual_gparts + expected_num_extra_gparts; + ++i) { + bzero(&s->gparts[i], sizeof(struct gpart)); + s->gparts[i].time_bin = time_bin_not_created; + s->gparts[i].type = swift_type_dark_matter; + s->gparts[i].id_or_neg_offset = -1; + } + + /* Put the spare particles in their correct cell */ +#ifdef WITH_MPI + error("Need to do this correctly over MPI for only the local cells."); +#endif + int count_in_cell = 0, current_cell = 0; + size_t count_extra_gparts = 0; + for (size_t i = 0; i < nr_actual_gparts + expected_num_extra_gparts; ++i) { + +#ifdef SWIFT_DEBUG_CHECKS + if (current_cell == s->nr_cells) + error("Cell counter beyond the maximal nr. cells."); +#endif + + if (s->gparts[i].time_bin == time_bin_not_created) { + + /* We want the extra particles to be at the centre of their cell */ + s->gparts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0]; + s->gparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; + s->gparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; + ++count_in_cell; + count_extra_gparts++; + } + + /* Once we have reached the number of extra gpart per cell, we move to the + * next */ + if (count_in_cell == space_extra_gparts) { + ++current_cell; + count_in_cell = 0; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (count_extra_gparts != expected_num_extra_gparts) + error("Constructed the wrong number of extra gparts (%zd vs. %zd)", + count_extra_gparts, expected_num_extra_gparts); +#endif + + /* Update the counters */ + s->nr_gparts = nr_actual_gparts + expected_num_extra_gparts; + s->nr_extra_gparts = expected_num_extra_gparts; + } + + /* Do we have enough space for the extra parts (i.e. we haven't used up any) ? + */ + if (expected_num_extra_parts > s->nr_extra_parts) { + + /* Ok... need to put some more in the game */ + + /* Do we need to reallocate? */ + if (nr_actual_parts + expected_num_extra_parts > size_parts) { + + size_parts = (nr_actual_parts + expected_num_extra_parts) * + engine_redistribute_alloc_margin; + + if (verbose) + message("Re-allocating parts array from %zd to %zd", s->size_parts, + size_parts); + + /* Create more space for parts */ + struct part *parts_new = NULL; + if (posix_memalign((void **)&parts_new, part_align, + sizeof(struct part) * size_parts) != 0) + error("Failed to allocate new part data"); + memcpy(parts_new, s->parts, sizeof(struct part) * s->size_parts); + free(s->parts); + s->parts = parts_new; + + /* Same for xparts */ + struct xpart *xparts_new = NULL; + if (posix_memalign((void **)&xparts_new, xpart_align, + sizeof(struct xpart) * size_parts) != 0) + error("Failed to allocate new xpart data"); + memcpy(xparts_new, s->xparts, sizeof(struct xpart) * s->size_parts); + free(s->xparts); + s->xparts = xparts_new; + + /* Update the counter */ + s->size_parts = size_parts; + } + + /* Turn some of the allocated spares into particles we can use */ + for (size_t i = nr_parts; i < nr_actual_parts + expected_num_extra_parts; + ++i) { + bzero(&s->parts[i], sizeof(struct part)); + bzero(&s->xparts[i], sizeof(struct xpart)); + s->parts[i].time_bin = time_bin_not_created; + s->parts[i].id = -1; + } + + /* Put the spare particles in their correct cell */ +#ifdef WITH_MPI + error("Need to do this correctly over MPI for only the local cells."); +#endif + int count_in_cell = 0, current_cell = 0; + size_t count_extra_parts = 0; + for (size_t i = 0; i < nr_actual_parts + expected_num_extra_parts; ++i) { + +#ifdef SWIFT_DEBUG_CHECKS + if (current_cell == s->nr_cells) + error("Cell counter beyond the maximal nr. cells."); +#endif + + if (s->parts[i].time_bin == time_bin_not_created) { + + /* We want the extra particles to be at the centre of their cell */ + s->parts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0]; + s->parts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; + s->parts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; + ++count_in_cell; + count_extra_parts++; + } + + /* Once we have reached the number of extra part per cell, we move to the + * next */ + if (count_in_cell == space_extra_parts) { + ++current_cell; + count_in_cell = 0; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (count_extra_parts != expected_num_extra_parts) + error("Constructed the wrong number of extra parts (%zd vs. %zd)", + count_extra_parts, expected_num_extra_parts); +#endif + + /* Update the counters */ + s->nr_parts = nr_actual_parts + expected_num_extra_parts; + s->nr_extra_parts = expected_num_extra_parts; + } + + /* Do we have enough space for the extra sparts (i.e. we haven't used up any) + * ? */ + if (nr_actual_sparts + expected_num_extra_sparts > nr_sparts) { + + /* Ok... need to put some more in the game */ + + /* Do we need to reallocate? */ + if (nr_actual_sparts + expected_num_extra_sparts > size_sparts) { + + size_sparts = (nr_actual_sparts + expected_num_extra_sparts) * + engine_redistribute_alloc_margin; + + if (verbose) + message("Re-allocating sparts array from %zd to %zd", s->size_sparts, + size_sparts); + + /* Create more space for parts */ + struct spart *sparts_new = NULL; + if (posix_memalign((void **)&sparts_new, spart_align, + sizeof(struct spart) * size_sparts) != 0) + error("Failed to allocate new spart data"); + memcpy(sparts_new, s->sparts, sizeof(struct spart) * s->size_sparts); + free(s->sparts); + s->sparts = sparts_new; + + /* Update the counter */ + s->size_sparts = size_sparts; + } + + /* Turn some of the allocated spares into particles we can use */ + for (size_t i = nr_sparts; i < nr_actual_sparts + expected_num_extra_sparts; + ++i) { + bzero(&s->sparts[i], sizeof(struct spart)); + s->sparts[i].time_bin = time_bin_not_created; + s->sparts[i].id = -42; + } + + /* Put the spare particles in their correct cell */ +#ifdef WITH_MPI + error("Need to do this correctly over MPI for only the local cells."); +#endif + int count_in_cell = 0, current_cell = 0; + size_t count_extra_sparts = 0; + for (size_t i = 0; i < nr_actual_sparts + expected_num_extra_sparts; ++i) { + +#ifdef SWIFT_DEBUG_CHECKS + if (current_cell == s->nr_cells) + error("Cell counter beyond the maximal nr. cells."); +#endif + + if (s->sparts[i].time_bin == time_bin_not_created) { + + /* We want the extra particles to be at the centre of their cell */ + s->sparts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0]; + s->sparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1]; + s->sparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2]; + ++count_in_cell; + count_extra_sparts++; + } + + /* Once we have reached the number of extra spart per cell, we move to the + * next */ + if (count_in_cell == space_extra_sparts) { + ++current_cell; + count_in_cell = 0; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (count_extra_sparts != expected_num_extra_sparts) + error("Constructed the wrong number of extra sparts (%zd vs. %zd)", + count_extra_sparts, expected_num_extra_sparts); +#endif + + /* Update the counters */ + s->nr_sparts = nr_actual_sparts + expected_num_extra_sparts; + s->nr_extra_sparts = expected_num_extra_sparts; + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the links are correct */ + if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0)) + part_verify_links(s->parts, s->gparts, s->sparts, nr_parts, nr_gparts, + nr_sparts, verbose); +#endif +} + /** * @brief Re-build the cells as well as the tasks. * @@ -627,67 +999,105 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Re-grid if necessary, or just re-set the cell data. */ space_regrid(s, verbose); + /* Allocate extra space for particles that will be created */ + if (s->with_star_formation) space_allocate_extras(s, verbose); + + struct cell *cells_top = s->cells_top; + const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0; + const int local_nodeID = s->e->nodeID; + + /* The current number of particles */ size_t nr_parts = s->nr_parts; size_t nr_gparts = s->nr_gparts; size_t nr_sparts = s->nr_sparts; - int count_inhibited_parts = 0; - int count_inhibited_gparts = 0; - int count_inhibited_sparts = 0; - struct cell *restrict cells_top = s->cells_top; - const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0; - /* Run through the particles and get their cell index. Allocates - an index that is larger than the number of particles to avoid - re-allocating after shuffling. */ - const size_t ind_size = s->size_parts + 100; - int *ind = (int *)malloc(sizeof(int) * ind_size); - if (ind == NULL) error("Failed to allocate temporary particle indices."); - int *cell_part_counts = (int *)calloc(sizeof(int), s->nr_cells); - if (cell_part_counts == NULL) - error("Failed to allocate cell part count buffer."); - if (s->size_parts > 0) - space_parts_get_cell_index(s, ind, cell_part_counts, &count_inhibited_parts, - verbose); + /* The number of particles we allocated memory for */ + size_t size_parts = s->size_parts; + size_t size_gparts = s->size_gparts; + size_t size_sparts = s->size_sparts; + + /* Counter for the number of inhibited particles found on the node */ + size_t count_inhibited_parts = 0; + size_t count_inhibited_gparts = 0; + size_t count_inhibited_sparts = 0; + + /* Counter for the number of extra particles found on the node */ + size_t count_extra_parts = 0; + size_t count_extra_gparts = 0; + size_t count_extra_sparts = 0; + + /* Number of particles we expect to have after strays exchange */ + const size_t h_index_size = size_parts + space_expected_max_nr_strays; + const size_t g_index_size = size_gparts + space_expected_max_nr_strays; + const size_t s_index_size = size_sparts + space_expected_max_nr_strays; + + /* Allocate arrays to store the indices of the cells where particles + belong. We allocate extra space to allow for particles we may + receive from other nodes */ + int *h_index = (int *)malloc(sizeof(int) * h_index_size); + int *g_index = (int *)malloc(sizeof(int) * g_index_size); + int *s_index = (int *)malloc(sizeof(int) * s_index_size); + if (h_index == NULL || g_index == NULL || s_index == NULL) + error("Failed to allocate temporary particle indices."); + + /* Allocate counters of particles that will land in each cell */ + int *cell_part_counts = (int *)malloc(sizeof(int) * s->nr_cells); + int *cell_gpart_counts = (int *)malloc(sizeof(int) * s->nr_cells); + int *cell_spart_counts = (int *)malloc(sizeof(int) * s->nr_cells); + if (cell_part_counts == NULL || cell_gpart_counts == NULL || + cell_spart_counts == NULL) + error("Failed to allocate cell particle count buffer."); + + /* Initialise the counters, including buffer space for future particles */ + for (int i = 0; i < s->nr_cells; ++i) { + cell_part_counts[i] = 0; + cell_gpart_counts[i] = 0; + cell_spart_counts[i] = 0; + } - /* Run through the gravity particles and get their cell index. */ - const size_t gind_size = s->size_gparts + 100; - int *gind = (int *)malloc(sizeof(int) * gind_size); - if (gind == NULL) error("Failed to allocate temporary g-particle indices."); - int *cell_gpart_counts = (int *)calloc(sizeof(int), s->nr_cells); - if (cell_gpart_counts == NULL) - error("Failed to allocate cell gpart count buffer."); - if (s->size_gparts > 0) - space_gparts_get_cell_index(s, gind, cell_gpart_counts, - &count_inhibited_gparts, verbose); - - /* Run through the star particles and get their cell index. */ - const size_t sind_size = s->size_sparts + 100; - int *sind = (int *)malloc(sizeof(int) * sind_size); - if (sind == NULL) error("Failed to allocate temporary s-particle indices."); - int *cell_spart_counts = (int *)calloc(sizeof(int), s->nr_cells); - if (cell_spart_counts == NULL) - error("Failed to allocate cell gpart count buffer."); - if (s->size_sparts > 0) - space_sparts_get_cell_index(s, sind, cell_spart_counts, - &count_inhibited_sparts, verbose); + /* Run through the particles and get their cell index. */ + if (nr_parts > 0) + space_parts_get_cell_index(s, h_index, cell_part_counts, + &count_inhibited_parts, &count_extra_parts, + verbose); + if (nr_gparts > 0) + space_gparts_get_cell_index(s, g_index, cell_gpart_counts, + &count_inhibited_gparts, &count_extra_gparts, + verbose); + if (nr_sparts > 0) + space_sparts_get_cell_index(s, s_index, cell_spart_counts, + &count_inhibited_sparts, &count_extra_sparts, + verbose); #ifdef SWIFT_DEBUG_CHECKS + /* Some safety checks */ if (repartitioned && count_inhibited_parts) error("We just repartitioned but still found inhibited parts."); if (repartitioned && count_inhibited_sparts) error("We just repartitioned but still found inhibited sparts."); if (repartitioned && count_inhibited_gparts) error("We just repartitioned but still found inhibited gparts."); -#endif - const int local_nodeID = s->e->nodeID; + if (count_extra_parts != s->nr_extra_parts) + error( + "Number of extra parts in the part array not matching the space " + "counter."); + if (count_extra_gparts != s->nr_extra_gparts) + error( + "Number of extra gparts in the gpart array not matching the space " + "counter."); + if (count_extra_sparts != s->nr_extra_sparts) + error( + "Number of extra sparts in the spart array not matching the space " + "counter."); +#endif /* Move non-local parts and inhibited parts to the end of the list. */ if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_parts > 0)) { for (size_t k = 0; k < nr_parts; /* void */) { /* Inhibited particle or foreign particle */ - if (ind[k] == -1 || cells_top[ind[k]].nodeID != local_nodeID) { + if (h_index[k] == -1 || cells_top[h_index[k]].nodeID != local_nodeID) { /* One fewer particle */ nr_parts -= 1; @@ -706,7 +1116,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Swap the xpart */ memswap(&s->xparts[k], &s->xparts[nr_parts], sizeof(struct xpart)); /* Swap the index */ - memswap(&ind[k], &ind[nr_parts], sizeof(int)); + memswap(&h_index[k], &h_index[nr_parts], sizeof(int)); } else { /* Increment when not exchanging otherwise we need to retest "k".*/ @@ -717,17 +1127,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Check that all parts are in the correct places. */ - int check_count_inhibited_part = 0; + size_t check_count_inhibited_part = 0; for (size_t k = 0; k < nr_parts; k++) { - if (ind[k] == -1 || cells_top[ind[k]].nodeID != local_nodeID) { + if (h_index[k] == -1 || cells_top[h_index[k]].nodeID != local_nodeID) { error("Failed to move all non-local parts to send list"); } } for (size_t k = nr_parts; k < s->nr_parts; k++) { - if (ind[k] != -1 && cells_top[ind[k]].nodeID == local_nodeID) { + if (h_index[k] != -1 && cells_top[h_index[k]].nodeID == local_nodeID) { error("Failed to remove local parts from send list"); } - if (ind[k] == -1) ++check_count_inhibited_part; + if (h_index[k] == -1) ++check_count_inhibited_part; } if (check_count_inhibited_part != count_inhibited_parts) error("Counts of inhibited particles do not match!"); @@ -738,7 +1148,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { for (size_t k = 0; k < nr_sparts; /* void */) { /* Inhibited particle or foreign particle */ - if (sind[k] == -1 || cells_top[sind[k]].nodeID != local_nodeID) { + if (s_index[k] == -1 || cells_top[s_index[k]].nodeID != local_nodeID) { /* One fewer particle */ nr_sparts -= 1; @@ -755,7 +1165,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } /* Swap the index */ - memswap(&sind[k], &sind[nr_sparts], sizeof(int)); + memswap(&s_index[k], &s_index[nr_sparts], sizeof(int)); } else { /* Increment when not exchanging otherwise we need to retest "k".*/ @@ -766,17 +1176,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Check that all sparts are in the correct place. */ - int check_count_inhibited_spart = 0; + size_t check_count_inhibited_spart = 0; for (size_t k = 0; k < nr_sparts; k++) { - if (sind[k] == -1 || cells_top[sind[k]].nodeID != local_nodeID) { + if (s_index[k] == -1 || cells_top[s_index[k]].nodeID != local_nodeID) { error("Failed to move all non-local sparts to send list"); } } for (size_t k = nr_sparts; k < s->nr_sparts; k++) { - if (sind[k] != -1 && cells_top[sind[k]].nodeID == local_nodeID) { + if (s_index[k] != -1 && cells_top[s_index[k]].nodeID == local_nodeID) { error("Failed to remove local sparts from send list"); } - if (sind[k] == -1) ++check_count_inhibited_spart; + if (s_index[k] == -1) ++check_count_inhibited_spart; } if (check_count_inhibited_spart != count_inhibited_sparts) error("Counts of inhibited s-particles do not match!"); @@ -787,7 +1197,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { for (size_t k = 0; k < nr_gparts; /* void */) { /* Inhibited particle or foreign particle */ - if (gind[k] == -1 || cells_top[gind[k]].nodeID != local_nodeID) { + if (g_index[k] == -1 || cells_top[g_index[k]].nodeID != local_nodeID) { /* One fewer particle */ nr_gparts -= 1; @@ -810,7 +1220,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } /* Swap the index */ - memswap(&gind[k], &gind[nr_gparts], sizeof(int)); + memswap(&g_index[k], &g_index[nr_gparts], sizeof(int)); } else { /* Increment when not exchanging otherwise we need to retest "k".*/ k++; @@ -820,17 +1230,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Check that all gparts are in the correct place. */ - int check_count_inhibited_gpart = 0; + size_t check_count_inhibited_gpart = 0; for (size_t k = 0; k < nr_gparts; k++) { - if (gind[k] == -1 || cells_top[gind[k]].nodeID != local_nodeID) { + if (g_index[k] == -1 || cells_top[g_index[k]].nodeID != local_nodeID) { error("Failed to move all non-local gparts to send list"); } } for (size_t k = nr_gparts; k < s->nr_gparts; k++) { - if (gind[k] != -1 && cells_top[gind[k]].nodeID == local_nodeID) { + if (g_index[k] != -1 && cells_top[g_index[k]].nodeID == local_nodeID) { error("Failed to remove local gparts from send list"); } - if (gind[k] == -1) ++check_count_inhibited_gpart; + if (g_index[k] == -1) ++check_count_inhibited_gpart; } if (check_count_inhibited_gpart != count_inhibited_gparts) error("Counts of inhibited g-particles do not match!"); @@ -839,21 +1249,23 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { #ifdef WITH_MPI /* Exchange the strays, note that this potentially re-allocates - the parts arrays. This can be skipped if we just repartitioned aspace - there should be no strays */ + the parts arrays. This can be skipped if we just repartitioned space + as there should be no strays in that case */ if (!repartitioned) { size_t nr_parts_exchanged = s->nr_parts - nr_parts; size_t nr_gparts_exchanged = s->nr_gparts - nr_gparts; size_t nr_sparts_exchanged = s->nr_sparts - nr_sparts; - engine_exchange_strays(s->e, nr_parts, &ind[nr_parts], &nr_parts_exchanged, - nr_gparts, &gind[nr_gparts], &nr_gparts_exchanged, - nr_sparts, &sind[nr_sparts], &nr_sparts_exchanged); + engine_exchange_strays(s->e, nr_parts, &h_index[nr_parts], + &nr_parts_exchanged, nr_gparts, &g_index[nr_gparts], + &nr_gparts_exchanged, nr_sparts, &s_index[nr_sparts], + &nr_sparts_exchanged); /* Set the new particle counts. */ s->nr_parts = nr_parts + nr_parts_exchanged; s->nr_gparts = nr_gparts + nr_gparts_exchanged; s->nr_sparts = nr_sparts + nr_sparts_exchanged; + } else { #ifdef SWIFT_DEBUG_CHECKS if (s->nr_parts != nr_parts) @@ -875,23 +1287,23 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } /* Re-allocate the index array for the parts if needed.. */ - if (s->nr_parts + 1 > ind_size) { + if (s->nr_parts + 1 > h_index_size) { int *ind_new; if ((ind_new = (int *)malloc(sizeof(int) * (s->nr_parts + 1))) == NULL) error("Failed to allocate temporary particle indices."); - memcpy(ind_new, ind, sizeof(int) * nr_parts); - free(ind); - ind = ind_new; + memcpy(ind_new, h_index, sizeof(int) * nr_parts); + free(h_index); + h_index = ind_new; } /* Re-allocate the index array for the sparts if needed.. */ - if (s->nr_sparts + 1 > sind_size) { + if (s->nr_sparts + 1 > s_index_size) { int *sind_new; if ((sind_new = (int *)malloc(sizeof(int) * (s->nr_sparts + 1))) == NULL) error("Failed to allocate temporary s-particle indices."); - memcpy(sind_new, sind, sizeof(int) * nr_sparts); - free(sind); - sind = sind_new; + memcpy(sind_new, s_index, sizeof(int) * nr_sparts); + free(s_index); + s_index = sind_new; } const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; @@ -900,13 +1312,13 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Assign each received part to its cell. */ for (size_t k = nr_parts; k < s->nr_parts; k++) { const struct part *const p = &s->parts[k]; - ind[k] = + h_index[k] = cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]); - cell_part_counts[ind[k]]++; + cell_part_counts[h_index[k]]++; #ifdef SWIFT_DEBUG_CHECKS - if (cells_top[ind[k]].nodeID != local_nodeID) + if (cells_top[h_index[k]].nodeID != local_nodeID) error("Received part that does not belong to me (nodeID=%i).", - cells_top[ind[k]].nodeID); + cells_top[h_index[k]].nodeID); #endif } nr_parts = s->nr_parts; @@ -914,13 +1326,13 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Assign each received spart to its cell. */ for (size_t k = nr_sparts; k < s->nr_sparts; k++) { const struct spart *const sp = &s->sparts[k]; - sind[k] = + s_index[k] = cell_getid(cdim, sp->x[0] * ih[0], sp->x[1] * ih[1], sp->x[2] * ih[2]); - cell_spart_counts[sind[k]]++; + cell_spart_counts[s_index[k]]++; #ifdef SWIFT_DEBUG_CHECKS - if (cells_top[sind[k]].nodeID != local_nodeID) + if (cells_top[s_index[k]].nodeID != local_nodeID) error("Received s-part that does not belong to me (nodeID=%i).", - cells_top[sind[k]].nodeID); + cells_top[s_index[k]].nodeID); #endif } nr_sparts = s->nr_sparts; @@ -935,8 +1347,8 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Sort the parts according to their cells. */ if (nr_parts > 0) - space_parts_sort(s->parts, s->xparts, ind, cell_part_counts, s->nr_cells, - 0); + space_parts_sort(s->parts, s->xparts, h_index, cell_part_counts, + s->nr_cells, 0); #ifdef SWIFT_DEBUG_CHECKS /* Verify that the part have been sorted correctly. */ @@ -954,7 +1366,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* New cell of this part */ const struct cell *c = &s->cells_top[new_ind]; - if (ind[k] != new_ind) + if (h_index[k] != new_ind) error("part's new cell index not matching sorted index."); if (p->x[0] < c->loc[0] || p->x[0] > c->loc[0] + c->width[0] || @@ -966,7 +1378,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Sort the sparts according to their cells. */ if (nr_sparts > 0) - space_sparts_sort(s->sparts, sind, cell_spart_counts, s->nr_cells, 0); + space_sparts_sort(s->sparts, s_index, cell_spart_counts, s->nr_cells, 0); #ifdef SWIFT_DEBUG_CHECKS /* Verify that the spart have been sorted correctly. */ @@ -984,7 +1396,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* New cell of this spart */ const struct cell *c = &s->cells_top[new_sind]; - if (sind[k] != new_sind) + if (s_index[k] != new_sind) error("spart's new cell index not matching sorted index."); if (sp->x[0] < c->loc[0] || sp->x[0] > c->loc[0] + c->width[0] || @@ -994,54 +1406,58 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } #endif /* SWIFT_DEBUG_CHECKS */ - /* Extract the cell counts from the sorted indices. */ + /* Extract the cell counts from the sorted indices. Deduct the extra + * particles. */ size_t last_index = 0; - ind[nr_parts] = s->nr_cells; // sentinel. + h_index[nr_parts] = s->nr_cells; // sentinel. for (size_t k = 0; k < nr_parts; k++) { - if (ind[k] < ind[k + 1]) { - cells_top[ind[k]].hydro.count = k - last_index + 1; + if (h_index[k] < h_index[k + 1]) { + cells_top[h_index[k]].hydro.count = + k - last_index + 1 - space_extra_parts; last_index = k + 1; } } - /* Extract the cell counts from the sorted indices. */ + /* Extract the cell counts from the sorted indices. Deduct the extra + * particles. */ size_t last_sindex = 0; - sind[nr_sparts] = s->nr_cells; // sentinel. + s_index[nr_sparts] = s->nr_cells; // sentinel. for (size_t k = 0; k < nr_sparts; k++) { - if (sind[k] < sind[k + 1]) { - cells_top[sind[k]].stars.count = k - last_sindex + 1; + if (s_index[k] < s_index[k + 1]) { + cells_top[s_index[k]].stars.count = + k - last_sindex + 1 - space_extra_sparts; last_sindex = k + 1; } } /* We no longer need the indices as of here. */ - free(ind); + free(h_index); free(cell_part_counts); - free(sind); + free(s_index); free(cell_spart_counts); #ifdef WITH_MPI /* Re-allocate the index array for the gparts if needed.. */ - if (s->nr_gparts + 1 > gind_size) { + if (s->nr_gparts + 1 > g_index_size) { int *gind_new; if ((gind_new = (int *)malloc(sizeof(int) * (s->nr_gparts + 1))) == NULL) error("Failed to allocate temporary g-particle indices."); - memcpy(gind_new, gind, sizeof(int) * nr_gparts); - free(gind); - gind = gind_new; + memcpy(gind_new, g_index, sizeof(int) * nr_gparts); + free(g_index); + g_index = gind_new; } /* Assign each received gpart to its cell. */ for (size_t k = nr_gparts; k < s->nr_gparts; k++) { const struct gpart *const p = &s->gparts[k]; - gind[k] = + g_index[k] = cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]); - cell_gpart_counts[gind[k]]++; + cell_gpart_counts[g_index[k]]++; #ifdef SWIFT_DEBUG_CHECKS - if (cells_top[gind[k]].nodeID != s->e->nodeID) + if (cells_top[g_index[k]].nodeID != s->e->nodeID) error("Received g-part that does not belong to me (nodeID=%i).", - cells_top[gind[k]].nodeID); + cells_top[g_index[k]].nodeID); #endif } nr_gparts = s->nr_gparts; @@ -1060,8 +1476,8 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* Sort the gparts according to their cells. */ if (nr_gparts > 0) - space_gparts_sort(s->gparts, s->parts, s->sparts, gind, cell_gpart_counts, - s->nr_cells); + space_gparts_sort(s->gparts, s->parts, s->sparts, g_index, + cell_gpart_counts, s->nr_cells); #ifdef SWIFT_DEBUG_CHECKS /* Verify that the gpart have been sorted correctly. */ @@ -1079,7 +1495,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { /* New cell of this gpart */ const struct cell *c = &s->cells_top[new_gind]; - if (gind[k] != new_gind) + if (g_index[k] != new_gind) error("gpart's new cell index not matching sorted index."); if (gp->x[0] < c->loc[0] || gp->x[0] > c->loc[0] + c->width[0] || @@ -1089,18 +1505,20 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } #endif /* SWIFT_DEBUG_CHECKS */ - /* Extract the cell counts from the sorted indices. */ + /* Extract the cell counts from the sorted indices. Deduct the extra + * particles. */ size_t last_gindex = 0; - gind[nr_gparts] = s->nr_cells; + g_index[nr_gparts] = s->nr_cells; for (size_t k = 0; k < nr_gparts; k++) { - if (gind[k] < gind[k + 1]) { - cells_top[gind[k]].grav.count = k - last_gindex + 1; + if (g_index[k] < g_index[k + 1]) { + cells_top[g_index[k]].grav.count = + k - last_gindex + 1 - space_extra_gparts; last_gindex = k + 1; } } /* We no longer need the indices as of here. */ - free(gind); + free(g_index); free(cell_gpart_counts); #ifdef SWIFT_DEBUG_CHECKS @@ -1139,10 +1557,15 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { c->hydro.xparts = xfinger; c->grav.parts = gfinger; c->stars.parts = sfinger; - finger = &finger[c->hydro.count]; - xfinger = &xfinger[c->hydro.count]; - gfinger = &gfinger[c->grav.count]; - sfinger = &sfinger[c->stars.count]; + + c->hydro.count_total = c->hydro.count + space_extra_parts; + c->grav.count_total = c->grav.count + space_extra_gparts; + c->stars.count_total = c->stars.count + space_extra_sparts; + + finger = &finger[c->hydro.count_total]; + xfinger = &xfinger[c->hydro.count_total]; + gfinger = &gfinger[c->grav.count_total]; + sfinger = &sfinger[c->stars.count_total]; /* Add this cell to the list of local cells */ s->local_cells_top[s->nr_local_cells] = k; @@ -1165,13 +1588,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { clocks_from_ticks(getticks() - tic2), clocks_getunit()); } - /* At this point, we have the upper-level cells, old or new. Now make - sure that the parts in each cell are ok. */ + /* Re-order the extra particles such that they are at the end of their cell's + memory pool. */ + if (s->with_star_formation) space_reorder_extras(s, verbose); + + /* At this point, we have the upper-level cells. Now recursively split each + cell to get the full AMR grid. */ space_split(s, verbose); #ifdef SWIFT_DEBUG_CHECKS /* Check that the multipole construction went OK */ - if (s->gravity) + if (s->with_self_gravity) for (int k = 0; k < s->nr_cells; k++) cell_check_multipole(&s->cells_top[k]); #endif @@ -1206,6 +1633,75 @@ void space_split(struct space *s, int verbose) { clocks_getunit()); } +void space_reorder_extra_parts_mapper(void *map_data, int num_cells, + void *extra_data) { + + struct cell *cells_top = (struct cell *)map_data; + struct space *s = (struct space *)extra_data; + + for (int ind = 0; ind < num_cells; ind++) { + struct cell *c = &cells_top[ind]; + cell_reorder_extra_parts(c, c->hydro.parts - s->parts); + } +} + +void space_reorder_extra_gparts_mapper(void *map_data, int num_cells, + void *extra_data) { + + struct cell *cells_top = (struct cell *)map_data; + struct space *s = (struct space *)extra_data; + + for (int ind = 0; ind < num_cells; ind++) { + struct cell *c = &cells_top[ind]; + cell_reorder_extra_gparts(c, s->parts, s->sparts); + } +} + +void space_reorder_extra_sparts_mapper(void *map_data, int num_cells, + void *extra_data) { + + struct cell *cells_top = (struct cell *)map_data; + struct space *s = (struct space *)extra_data; + + for (int ind = 0; ind < num_cells; ind++) { + struct cell *c = &cells_top[ind]; + cell_reorder_extra_sparts(c, c->stars.parts - s->sparts); + } +} + +/** + * @brief Re-orders the particles in each cell such that the extra particles + * for on-the-fly creation are located at the end of their respective cells. + * + * This assumes that all the particles (real and extra) have already been sorted + * in their correct top-level cell. + * + * @param s The #space to act upon. + * @param verbose Are we talkative? + */ +void space_reorder_extras(struct space *s, int verbose) { + +#ifdef WITH_MPI + if (space_extra_parts || space_extra_gparts || space_extra_sparts) + error("Need an MPI-proof version of this."); +#endif + + /* Re-order the gas particles */ + if (space_extra_parts) + threadpool_map(&s->e->threadpool, space_reorder_extra_parts_mapper, + s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); + + /* Re-order the gravity particles */ + if (space_extra_gparts) + threadpool_map(&s->e->threadpool, space_reorder_extra_gparts_mapper, + s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); + + /* Re-order the star particles */ + if (space_extra_sparts) + threadpool_map(&s->e->threadpool, space_reorder_extra_sparts_mapper, + s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); +} + /** * @brief #threadpool mapper function to sanitize the cells * @@ -1269,7 +1765,8 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, /* Init the local collectors */ float min_mass = FLT_MAX; float sum_vel_norm = 0.f; - int count_inhibited_part = 0; + size_t count_inhibited_part = 0; + size_t count_extra_part = 0; /* Loop over the parts. */ for (int k = 0; k < nr_parts; k++) { @@ -1281,6 +1778,17 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, const double old_pos_y = p->x[1]; const double old_pos_z = p->x[2]; +#ifdef SWIFT_DEBUG_CHECKS + if (!s->periodic) { + if (old_pos_x < 0. || old_pos_x > dim_x) + error("Particle outside of volume along X."); + if (old_pos_y < 0. || old_pos_y > dim_y) + error("Particle outside of volume along Y."); + if (old_pos_z < 0. || old_pos_z > dim_z) + error("Particle outside of volume along Z."); + } +#endif + /* Put it back into the simulation volume */ const double pos_x = box_wrap(old_pos_x, 0.0, dim_x); const double pos_y = box_wrap(old_pos_y, 0.0, dim_y); @@ -1301,26 +1809,31 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, pos_z); #endif - /* Is this particle to be removed? */ if (p->time_bin == time_bin_inhibited) { + /* Is this particle to be removed? */ ind[k] = -1; ++count_inhibited_part; + } else if (p->time_bin == time_bin_not_created) { + /* Is this a place-holder for on-the-fly creation? */ + ind[k] = index; + cell_counts[index]++; + ++count_extra_part; } else { - /* List its top-level cell index */ + /* Normal case: list its top-level cell index */ ind[k] = index; cell_counts[index]++; - } - /* Compute minimal mass */ - min_mass = min(min_mass, hydro_get_mass(p)); + /* Compute minimal mass */ + min_mass = min(min_mass, hydro_get_mass(p)); - /* Compute sum of velocity norm */ - sum_vel_norm += p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2]; + /* Compute sum of velocity norm */ + sum_vel_norm += p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2]; - /* Update the position */ - p->x[0] = pos_x; - p->x[1] = pos_y; - p->x[2] = pos_z; + /* Update the position */ + p->x[0] = pos_x; + p->x[1] = pos_y; + p->x[2] = pos_z; + } } /* Write the counts back to the global array. */ @@ -1328,8 +1841,10 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]); free(cell_counts); - /* Write the count of inhibited parts */ - atomic_add(&data->count_inhibited_part, count_inhibited_part); + /* Write the count of inhibited and extra parts */ + if (count_inhibited_part) + atomic_add(&data->count_inhibited_part, count_inhibited_part); + if (count_extra_part) atomic_add(&data->count_extra_part, count_extra_part); /* Write back the minimal part mass and velocity sum */ atomic_min_f(&s->min_part_mass, min_mass); @@ -1369,7 +1884,8 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, /* Init the local collectors */ float min_mass = FLT_MAX; float sum_vel_norm = 0.f; - int count_inhibited_gpart = 0; + size_t count_inhibited_gpart = 0; + size_t count_extra_gpart = 0; for (int k = 0; k < nr_gparts; k++) { @@ -1380,6 +1896,17 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, const double old_pos_y = gp->x[1]; const double old_pos_z = gp->x[2]; +#ifdef SWIFT_DEBUG_CHECKS + if (!s->periodic) { + if (old_pos_x < 0. || old_pos_x > dim_x) + error("Particle outside of volume along X."); + if (old_pos_y < 0. || old_pos_y > dim_y) + error("Particle outside of volume along Y."); + if (old_pos_z < 0. || old_pos_z > dim_z) + error("Particle outside of volume along Z."); + } +#endif + /* Put it back into the simulation volume */ const double pos_x = box_wrap(old_pos_x, 0.0, dim_x); const double pos_y = box_wrap(old_pos_y, 0.0, dim_y); @@ -1400,31 +1927,36 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, pos_z); #endif - /* Is this particle to be removed? */ if (gp->time_bin == time_bin_inhibited) { + /* Is this particle to be removed? */ ind[k] = -1; ++count_inhibited_gpart; + } else if (gp->time_bin == time_bin_not_created) { + /* Is this a place-holder for on-the-fly creation? */ + ind[k] = index; + cell_counts[index]++; + ++count_extra_gpart; } else { /* List its top-level cell index */ ind[k] = index; cell_counts[index]++; - } - if (gp->type == swift_type_dark_matter) { + if (gp->type == swift_type_dark_matter) { - /* Compute minimal mass */ - min_mass = min(min_mass, gp->mass); + /* Compute minimal mass */ + min_mass = min(min_mass, gp->mass); - /* Compute sum of velocity norm */ - sum_vel_norm += gp->v_full[0] * gp->v_full[0] + - gp->v_full[1] * gp->v_full[1] + - gp->v_full[2] * gp->v_full[2]; - } + /* Compute sum of velocity norm */ + sum_vel_norm += gp->v_full[0] * gp->v_full[0] + + gp->v_full[1] * gp->v_full[1] + + gp->v_full[2] * gp->v_full[2]; + } - /* Update the position */ - gp->x[0] = pos_x; - gp->x[1] = pos_y; - gp->x[2] = pos_z; + /* Update the position */ + gp->x[0] = pos_x; + gp->x[1] = pos_y; + gp->x[2] = pos_z; + } } /* Write the counts back to the global array. */ @@ -1432,8 +1964,11 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]); free(cell_counts); - /* Write the count of inhibited gparts */ - atomic_add(&data->count_inhibited_gpart, count_inhibited_gpart); + /* Write the count of inhibited and extra gparts */ + if (count_inhibited_gpart) + atomic_add(&data->count_inhibited_gpart, count_inhibited_gpart); + if (count_extra_gpart) + atomic_add(&data->count_extra_gpart, count_extra_gpart); /* Write back the minimal part mass and velocity sum */ atomic_min_f(&s->min_gpart_mass, min_mass); @@ -1473,7 +2008,8 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, /* Init the local collectors */ float min_mass = FLT_MAX; float sum_vel_norm = 0.f; - int count_inhibited_spart = 0; + size_t count_inhibited_spart = 0; + size_t count_extra_spart = 0; for (int k = 0; k < nr_sparts; k++) { @@ -1484,6 +2020,17 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, const double old_pos_y = sp->x[1]; const double old_pos_z = sp->x[2]; +#ifdef SWIFT_DEBUG_CHECKS + if (!s->periodic) { + if (old_pos_x < 0. || old_pos_x > dim_x) + error("Particle outside of volume along X."); + if (old_pos_y < 0. || old_pos_y > dim_y) + error("Particle outside of volume along Y."); + if (old_pos_z < 0. || old_pos_z > dim_z) + error("Particle outside of volume along Z."); + } +#endif + /* Put it back into the simulation volume */ const double pos_x = box_wrap(old_pos_x, 0.0, dim_x); const double pos_y = box_wrap(old_pos_y, 0.0, dim_y); @@ -1508,23 +2055,28 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, if (sp->time_bin == time_bin_inhibited) { ind[k] = -1; ++count_inhibited_spart; + } else if (sp->time_bin == time_bin_not_created) { + /* Is this a place-holder for on-the-fly creation? */ + ind[k] = index; + cell_counts[index]++; + ++count_extra_spart; } else { /* List its top-level cell index */ ind[k] = index; cell_counts[index]++; - } - /* Compute minimal mass */ - min_mass = min(min_mass, sp->mass); + /* Compute minimal mass */ + min_mass = min(min_mass, sp->mass); - /* Compute sum of velocity norm */ - sum_vel_norm += - sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2]; + /* Compute sum of velocity norm */ + sum_vel_norm += + sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2]; - /* Update the position */ - sp->x[0] = pos_x; - sp->x[1] = pos_y; - sp->x[2] = pos_z; + /* Update the position */ + sp->x[0] = pos_x; + sp->x[1] = pos_y; + sp->x[2] = pos_z; + } } /* Write the counts back to the global array. */ @@ -1532,8 +2084,11 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]); free(cell_counts); - /* Write the count of inhibited parts */ - atomic_add(&data->count_inhibited_spart, count_inhibited_spart); + /* Write the count of inhibited and extra sparts */ + if (count_inhibited_spart) + atomic_add(&data->count_inhibited_spart, count_inhibited_spart); + if (count_extra_spart) + atomic_add(&data->count_extra_spart, count_extra_spart); /* Write back the minimal part mass and velocity sum */ atomic_min_f(&s->min_spart_mass, min_mass); @@ -1549,10 +2104,13 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, * @param ind The array of indices to fill. * @param cell_counts The cell counters to update. * @param count_inhibited_parts (return) The number of #part to remove. + * @param count_extra_parts (return) The number of #part for on-the-fly + * creation. * @param verbose Are we talkative ? */ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts, - int *count_inhibited_parts, int verbose) { + size_t *count_inhibited_parts, + size_t *count_extra_parts, int verbose) { const ticks tic = getticks(); @@ -1568,11 +2126,15 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_extra_part = 0; + data.count_extra_gpart = 0; + data.count_extra_spart = 0; threadpool_map(&s->e->threadpool, space_parts_get_cell_index_mapper, s->parts, s->nr_parts, sizeof(struct part), 0, &data); *count_inhibited_parts = data.count_inhibited_part; + *count_extra_parts = data.count_extra_part; if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -1588,10 +2150,13 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts, * @param gind The array of indices to fill. * @param cell_counts The cell counters to update. * @param count_inhibited_gparts (return) The number of #gpart to remove. + * @param count_extra_gparts (return) The number of #gpart for on-the-fly + * creation. * @param verbose Are we talkative ? */ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, - int *count_inhibited_gparts, int verbose) { + size_t *count_inhibited_gparts, + size_t *count_extra_gparts, int verbose) { const ticks tic = getticks(); @@ -1607,11 +2172,15 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_extra_part = 0; + data.count_extra_gpart = 0; + data.count_extra_spart = 0; threadpool_map(&s->e->threadpool, space_gparts_get_cell_index_mapper, s->gparts, s->nr_gparts, sizeof(struct gpart), 0, &data); *count_inhibited_gparts = data.count_inhibited_gpart; + *count_extra_gparts = data.count_extra_gpart; if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -1627,10 +2196,13 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, * @param sind The array of indices to fill. * @param cell_counts The cell counters to update. * @param count_inhibited_sparts (return) The number of #spart to remove. + * @param count_extra_sparts (return) The number of #spart for on-the-fly + * creation. * @param verbose Are we talkative ? */ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, - int *count_inhibited_sparts, int verbose) { + size_t *count_inhibited_sparts, + size_t *count_extra_sparts, int verbose) { const ticks tic = getticks(); @@ -1646,11 +2218,15 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_extra_part = 0; + data.count_extra_gpart = 0; + data.count_extra_spart = 0; threadpool_map(&s->e->threadpool, space_sparts_get_cell_index_mapper, s->sparts, s->nr_sparts, sizeof(struct spart), 0, &data); *count_inhibited_sparts = data.count_inhibited_spart; + *count_extra_sparts = data.count_extra_spart; if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -1852,11 +2428,16 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts, */ void space_map_clearsort(struct cell *c, void *data) { - for (int i = 0; i < 13; i++) + for (int i = 0; i < 13; i++) { if (c->hydro.sort[i] != NULL) { free(c->hydro.sort[i]); c->hydro.sort[i] = NULL; } + if (c->stars.sort[i] != NULL) { + free(c->stars.sort[i]); + c->stars.sort[i] = NULL; + } + } } /** @@ -2018,7 +2599,7 @@ void space_split_recursive(struct space *s, struct cell *c, const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; - const int with_gravity = s->gravity; + const int with_self_gravity = s->with_self_gravity; const int depth = c->depth; int maxdepth = 0; float h_max = 0.0f; @@ -2027,6 +2608,7 @@ void space_split_recursive(struct space *s, struct cell *c, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; + integertime_t ti_stars_end_min = max_nr_timesteps; struct part *parts = c->hydro.parts; struct gpart *gparts = c->grav.parts; struct spart *sparts = c->stars.parts; @@ -2045,6 +2627,8 @@ void space_split_recursive(struct space *s, struct cell *c, #ifdef SWIFT_DEBUG_CHECKS if (parts[k].time_bin == time_bin_inhibited) error("Inhibited particle present in space_split()"); + if (parts[k].time_bin == time_bin_not_created) + error("Extra particle present in space_split()"); #endif buff[k].x[0] = parts[k].x[0]; buff[k].x[1] = parts[k].x[1]; @@ -2059,6 +2643,8 @@ void space_split_recursive(struct space *s, struct cell *c, #ifdef SWIFT_DEBUG_CHECKS if (gparts[k].time_bin == time_bin_inhibited) error("Inhibited particle present in space_split()"); + if (gparts[k].time_bin == time_bin_not_created) + error("Extra particle present in space_split()"); #endif gbuff[k].x[0] = gparts[k].x[0]; gbuff[k].x[1] = gparts[k].x[1]; @@ -2073,6 +2659,8 @@ void space_split_recursive(struct space *s, struct cell *c, #ifdef SWIFT_DEBUG_CHECKS if (sparts[k].time_bin == time_bin_inhibited) error("Inhibited particle present in space_split()"); + if (sparts[k].time_bin == time_bin_not_created) + error("Extra particle present in space_split()"); #endif sbuff[k].x[0] = sparts[k].x[0]; sbuff[k].x[1] = sparts[k].x[1]; @@ -2093,8 +2681,8 @@ void space_split_recursive(struct space *s, struct cell *c, } /* Split or let it be? */ - if ((with_gravity && gcount > space_splitsize) || - (!with_gravity && + if ((with_self_gravity && gcount > space_splitsize) || + (!with_self_gravity && (count > space_splitsize || scount > space_splitsize))) { /* No longer just a leaf. */ @@ -2107,6 +2695,9 @@ void space_split_recursive(struct space *s, struct cell *c, cp->hydro.count = 0; cp->grav.count = 0; cp->stars.count = 0; + cp->hydro.count_total = 0; + cp->grav.count_total = 0; + cp->stars.count_total = 0; cp->hydro.ti_old_part = c->hydro.ti_old_part; cp->grav.ti_old_part = c->grav.ti_old_part; cp->grav.ti_old_multipole = c->grav.ti_old_multipole; @@ -2127,14 +2718,18 @@ void space_split_recursive(struct space *s, struct cell *c, cp->hydro.dx_max_sort = 0.f; cp->stars.h_max = 0.f; cp->stars.dx_max_part = 0.f; + cp->stars.dx_max_sort = 0.f; cp->nodeID = c->nodeID; cp->parent = c; cp->super = NULL; cp->hydro.super = NULL; cp->grav.super = NULL; cp->hydro.do_sub_sort = 0; + cp->stars.do_sub_sort = 0; cp->grav.do_sub_drift = 0; cp->hydro.do_sub_drift = 0; + cp->hydro.do_sub_limiter = 0; + cp->hydro.do_limiter = 0; #ifdef WITH_MPI cp->mpi.tag = -1; #endif // WITH_MPI @@ -2175,13 +2770,14 @@ void space_split_recursive(struct space *s, struct cell *c, /* Update the cell-wide properties */ h_max = max(h_max, cp->hydro.h_max); - stars_h_max = max(h_max, cp->stars.h_max); + stars_h_max = max(stars_h_max, cp->stars.h_max); ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_end_max = max(ti_hydro_end_max, cp->hydro.ti_end_max); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min); ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max); ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max); + ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); /* Increase the depth */ if (cp->maxdepth > maxdepth) maxdepth = cp->maxdepth; @@ -2189,7 +2785,7 @@ void space_split_recursive(struct space *s, struct cell *c, } /* Deal with the multipole */ - if (s->gravity) { + if (s->with_self_gravity) { /* Reset everything */ gravity_reset(c->grav.multipole); @@ -2308,10 +2904,13 @@ void space_split_recursive(struct space *s, struct cell *c, timebin_t hydro_time_bin_min = num_time_bins, hydro_time_bin_max = 0; timebin_t gravity_time_bin_min = num_time_bins, gravity_time_bin_max = 0; + timebin_t stars_time_bin_min = num_time_bins; /* parts: Get dt_min/dt_max and h_max. */ for (int k = 0; k < count; k++) { #ifdef SWIFT_DEBUG_CHECKS + if (parts[k].time_bin == time_bin_not_created) + error("Extra particle present in space_split()"); if (parts[k].time_bin == time_bin_inhibited) error("Inhibited particle present in space_split()"); #endif @@ -2330,6 +2929,8 @@ void space_split_recursive(struct space *s, struct cell *c, /* gparts: Get dt_min/dt_max. */ for (int k = 0; k < gcount; k++) { #ifdef SWIFT_DEBUG_CHECKS + if (gparts[k].time_bin == time_bin_not_created) + error("Extra g-particle present in space_split()"); if (gparts[k].time_bin == time_bin_inhibited) error("Inhibited g-particle present in space_split()"); #endif @@ -2340,11 +2941,15 @@ void space_split_recursive(struct space *s, struct cell *c, /* sparts: Get dt_min/dt_max */ for (int k = 0; k < scount; k++) { #ifdef SWIFT_DEBUG_CHECKS + if (sparts[k].time_bin == time_bin_not_created) + error("Extra s-particle present in space_split()"); if (sparts[k].time_bin == time_bin_inhibited) error("Inhibited s-particle present in space_split()"); #endif gravity_time_bin_min = min(gravity_time_bin_min, sparts[k].time_bin); gravity_time_bin_max = max(gravity_time_bin_max, sparts[k].time_bin); + stars_time_bin_min = min(stars_time_bin_min, sparts[k].time_bin); + stars_h_max = max(stars_h_max, sparts[k].h); /* Reset x_diff */ @@ -2362,9 +2967,10 @@ void space_split_recursive(struct space *s, struct cell *c, ti_gravity_end_max = get_integer_time_end(ti_current, gravity_time_bin_max); ti_gravity_beg_max = get_integer_time_begin(ti_current + 1, gravity_time_bin_max); + ti_stars_end_min = get_integer_time_end(ti_current, stars_time_bin_min); /* Construct the multipole and the centre of mass*/ - if (s->gravity) { + if (s->with_self_gravity) { if (gcount > 0) { gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count); @@ -2399,6 +3005,7 @@ void space_split_recursive(struct space *s, struct cell *c, c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_end_max = ti_gravity_end_max; c->grav.ti_beg_max = ti_gravity_beg_max; + c->stars.ti_end_min = ti_stars_end_min; c->stars.h_max = stars_h_max; c->maxdepth = maxdepth; @@ -2471,7 +3078,7 @@ void space_recycle(struct space *s, struct cell *c) { lock_lock(&s->lock); /* Hook the multipole back in the buffer */ - if (s->gravity) { + if (s->with_self_gravity) { c->grav.multipole->next = s->multipoles_sub; s->multipoles_sub = c->grav.multipole; } @@ -2529,7 +3136,7 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, s->tot_cells -= count; /* Hook the multipoles into the buffer. */ - if (s->gravity) { + if (s->with_self_gravity) { multipole_list_end->next = s->multipoles_sub; s->multipoles_sub = multipole_list_begin; } @@ -2573,7 +3180,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { } /* Is the multipole buffer empty? */ - if (s->gravity && s->multipoles_sub == NULL) { + if (s->with_self_gravity && s->multipoles_sub == NULL) { if (posix_memalign( (void **)&s->multipoles_sub, multipole_align, space_cellallocchunk * sizeof(struct gravity_tensors)) != 0) @@ -2591,7 +3198,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { s->tot_cells += 1; /* Hook the multipole */ - if (s->gravity) { + if (s->with_self_gravity) { cells[j]->grav.multipole = s->multipoles_sub; s->multipoles_sub = cells[j]->grav.multipole->next; } @@ -2602,8 +3209,10 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Init some things in the cell we just got. */ for (int j = 0; j < nr_cells; j++) { - for (int k = 0; k < 13; k++) + for (int k = 0; k < 13; k++) { if (cells[j]->hydro.sort[k] != NULL) free(cells[j]->hydro.sort[k]); + if (cells[j]->stars.sort[k] != NULL) free(cells[j]->stars.sort[k]); + } struct gravity_tensors *temp = cells[j]->grav.multipole; bzero(cells[j], sizeof(struct cell)); cells[j]->grav.multipole = temp; @@ -2624,11 +3233,16 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { void space_free_buff_sort_indices(struct space *s) { for (struct cell *finger = s->cells_sub; finger != NULL; finger = finger->next) { - for (int k = 0; k < 13; k++) + for (int k = 0; k < 13; k++) { if (finger->hydro.sort[k] != NULL) { free(finger->hydro.sort[k]); finger->hydro.sort[k] = NULL; } + if (finger->stars.sort[k] != NULL) { + free(finger->stars.sort[k]); + finger->stars.sort[k] = NULL; + } + } } } @@ -2753,10 +3367,27 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, const struct hydro_props *hydro_props = s->e->hydro_properties; const float u_init = hydro_props->initial_internal_energy; + const float hydro_h_min_ratio = e->hydro_properties->h_min_ratio; + + const struct gravity_props *grav_props = s->e->gravity_properties; + const int with_gravity = e->policy & engine_policy_self_gravity; const struct chemistry_global_data *chemistry = e->chemistry; const struct cooling_function_data *cool_func = e->cooling_func; + /* Check that the smoothing lengths are non-zero */ + for (int k = 0; k < count; k++) { + if (p[k].h <= 0.) + error("Invalid value of smoothing length for part %lld h=%e", p[k].id, + p[k].h); + + if (with_gravity) { + const struct gpart *gp = p[k].gpart; + const float softening = gravity_get_softening(gp, grav_props); + p->h = max(p->h, softening * hydro_h_min_ratio); + } + } + /* Convert velocities to internal units */ for (int k = 0; k < count; k++) { p[k].v[0] *= a_factor_vel; @@ -2791,6 +3422,10 @@ void space_first_init_parts_mapper(void *restrict map_data, int count, /* And the cooling */ cooling_first_init_part(phys_const, us, cosmo, cool_func, &p[k], &xp[k]); + /* And the tracers */ + tracers_first_init_xpart(&p[k], &xp[k], us, phys_const, cosmo, hydro_props, + cool_func); + #ifdef SWIFT_DEBUG_CHECKS /* Check part->gpart->part linkeage. */ if (p[k].gpart && p[k].gpart->id_or_neg_offset != -(k + delta)) @@ -2884,12 +3519,15 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, struct spart *restrict sp = (struct spart *)map_data; const struct space *restrict s = (struct space *)extra_data; + const struct engine *e = s->e; #ifdef SWIFT_DEBUG_CHECKS const ptrdiff_t delta = sp - s->sparts; #endif - const struct cosmology *cosmo = s->e->cosmology; + const int with_feedback = (e->policy & engine_policy_feedback); + + const struct cosmology *cosmo = e->cosmology; const float a_factor_vel = cosmo->a; /* Convert velocities to internal units */ @@ -2910,6 +3548,13 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, #endif } + /* Check that the smoothing lengths are non-zero */ + for (int k = 0; k < count; k++) { + if (with_feedback && sp[k].h <= 0.) + error("Invalid value of smoothing length for spart %lld h=%e", sp[k].id, + sp[k].h); + } + /* Initialise the rest */ for (int k = 0; k < count; k++) { @@ -3029,8 +3674,11 @@ void space_convert_quantities_mapper(void *restrict map_data, int count, const ptrdiff_t index = parts - s->parts; struct xpart *restrict xparts = s->xparts + index; + /* Loop over all the particles ignoring the extra buffer ones for on-the-fly + * creation */ for (int k = 0; k < count; k++) - hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props); + if (parts[k].time_bin <= num_time_bins) + hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props); } /** @@ -3071,6 +3719,7 @@ void space_convert_quantities(struct space *s, int verbose) { * @param generate_gas_in_ics Are we generating gas particles from the gparts? * @param hydro flag whether we are doing hydro or not? * @param self_gravity flag whether we are doing gravity or not? + * @param star_formation flag whether we are doing star formation or not? * @param verbose Print messages to stdout or not. * @param dry_run If 1, just initialise stuff, don't do anything with the parts. * @@ -3084,7 +3733,8 @@ void space_init(struct space *s, struct swift_params *params, struct part *parts, struct gpart *gparts, struct spart *sparts, size_t Npart, size_t Ngpart, size_t Nspart, int periodic, int replicate, int generate_gas_in_ics, int hydro, - int self_gravity, int verbose, int dry_run) { + int self_gravity, int star_formation, int verbose, + int dry_run) { /* Clean-up everything */ bzero(s, sizeof(struct space)); @@ -3094,16 +3744,23 @@ void space_init(struct space *s, struct swift_params *params, s->dim[1] = dim[1]; s->dim[2] = dim[2]; s->periodic = periodic; - s->gravity = self_gravity; - s->hydro = hydro; + s->with_self_gravity = self_gravity; + s->with_hydro = hydro; + s->with_star_formation = star_formation; s->nr_parts = Npart; - s->size_parts = Npart; - s->parts = parts; s->nr_gparts = Ngpart; - s->size_gparts = Ngpart; - s->gparts = gparts; s->nr_sparts = Nspart; + s->size_parts = Npart; + s->size_gparts = Ngpart; s->size_sparts = Nspart; + s->nr_inhibited_parts = 0; + s->nr_inhibited_gparts = 0; + s->nr_inhibited_sparts = 0; + s->nr_extra_parts = 0; + s->nr_extra_gparts = 0; + s->nr_extra_sparts = 0; + s->parts = parts; + s->gparts = gparts; s->sparts = sparts; s->min_part_mass = FLT_MAX; s->min_gpart_mass = FLT_MAX; @@ -3186,6 +3843,12 @@ void space_init(struct space *s, struct swift_params *params, space_subdepth_diff_grav = parser_get_opt_param_int(params, "Scheduler:cell_subdepth_diff_grav", space_subdepth_diff_grav_default); + space_extra_parts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_parts", space_extra_parts_default); + space_extra_sparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_sparts", space_extra_sparts_default); + space_extra_gparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_gparts", space_extra_gparts_default); if (verbose) { message("max_size set to %d split_size set to %d", space_maxsize, @@ -3292,6 +3955,9 @@ void space_init(struct space *s, struct swift_params *params, last_cell_id = 1; #endif + /* Do we want any spare particles for on the fly cration? */ + if (!star_formation) space_extra_sparts = 0; + /* Build the cells recursively. */ if (!dry_run) space_regrid(s, verbose); } @@ -3437,7 +4103,7 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo, int periodic, const double dim[3], int verbose) { /* Check that this is a sensible ting to do */ - if (!s->hydro) + if (!s->with_hydro) error( "Cannot generate gas from ICs if we are running without " "hydrodynamics. Need to run with -s and the corresponding " @@ -3668,6 +4334,49 @@ void space_check_timesteps(struct space *s) { #endif } +/** + * @brief #threadpool mapper function for the limiter debugging check + */ +void space_check_limiter_mapper(void *map_data, int nr_parts, + void *extra_data) { +#ifdef SWIFT_DEBUG_CHECKS + /* Unpack the data */ + struct part *restrict parts = (struct part *)map_data; + + /* Verify that all limited particles have been treated */ + for (int k = 0; k < nr_parts; k++) { + + if (parts[k].time_bin == time_bin_inhibited) continue; + + if (parts[k].wakeup == time_bin_awake) + error("Particle still woken up! id=%lld", parts[k].id); + + if (parts[k].gpart != NULL) + if (parts[k].time_bin != parts[k].gpart->time_bin) + error("Gpart not on the same time-bin as part"); + } +#else + error("Calling debugging code without debugging flag activated."); +#endif +} + +/** + * @brief Checks that all particles have their wakeup flag in a correct state. + * + * Should only be used for debugging purposes. + * + * @param s The #space to check. + */ +void space_check_limiter(struct space *s) { +#ifdef SWIFT_DEBUG_CHECKS + + threadpool_map(&s->e->threadpool, space_check_limiter_mapper, s->parts, + s->nr_parts, sizeof(struct part), 1000, NULL); +#else + error("Calling debugging code without debugging flag activated."); +#endif +} + /** * @brief Resets all the individual cell task counters to 0. * @@ -3755,7 +4464,6 @@ void space_struct_restore(struct space *s, FILE *stream) { s->local_cells_with_tasks_top = NULL; s->cells_with_particles_top = NULL; s->local_cells_with_particles_top = NULL; - s->grav_top_level = NULL; s->nr_local_cells_with_tasks = 0; s->nr_cells_with_particles = 0; #ifdef WITH_MPI diff --git a/src/space.h b/src/space.h index d11dc58ec960c00d9ed7d8dd3f487cd2b9f65e11..f4d2196de74b6bf90712aa1dfbbaca9d1adea731 100644 --- a/src/space.h +++ b/src/space.h @@ -35,6 +35,7 @@ #include "lock.h" #include "parser.h" #include "part.h" +#include "velociraptor_struct.h" /* Avoid cyclic inclusions */ struct cell; @@ -44,6 +45,10 @@ struct cosmology; #define space_cellallocchunk 1000 #define space_splitsize_default 400 #define space_maxsize_default 8000000 +#define space_extra_parts_default 0 +#define space_extra_gparts_default 0 +#define space_extra_sparts_default 100 +#define space_expected_max_nr_strays_default 100 #define space_subsize_pair_hydro_default 256000000 #define space_subsize_self_hydro_default 32000 #define space_subsize_pair_grav_default 256000000 @@ -68,6 +73,9 @@ extern int space_subsize_self_grav; extern int space_subsize_pair_stars; extern int space_subsize_self_stars; extern int space_subdepth_diff_grav; +extern int space_extra_parts; +extern int space_extra_gparts; +extern int space_extra_sparts; /** * @brief The space in which the cells and particles reside. @@ -84,10 +92,13 @@ struct space { struct hydro_space hs; /*! Are we doing hydrodynamics? */ - int hydro; + int with_hydro; /*! Are we doing gravity? */ - int gravity; + int with_self_gravity; + + /*! Are we doing star formation? */ + int with_star_formation; /*! Width of the top-level cells. */ double width[3]; @@ -149,14 +160,23 @@ struct space { /*! The indices of the top-level cells that have >0 particles (of any kind) */ int *local_cells_with_particles_top; - /*! The total number of parts in the space. */ - size_t nr_parts, size_parts; + /*! The total number of #part in the space. */ + size_t nr_parts; + + /*! The total number of #gpart in the space. */ + size_t nr_gparts; + + /*! The total number of #spart in the space. */ + size_t nr_sparts; - /*! The total number of g-parts in the space. */ - size_t nr_gparts, size_gparts; + /*! The total number of #part we allocated memory for */ + size_t size_parts; - /*! The total number of g-parts in the space. */ - size_t nr_sparts, size_sparts; + /*! The total number of #gpart we allocated memory for */ + size_t size_gparts; + + /*! The total number of #spart we allocated memory for */ + size_t size_sparts; /*! Number of inhibted gas particles in the space */ size_t nr_inhibited_parts; @@ -167,6 +187,15 @@ struct space { /*! Number of inhibted star particles in the space */ size_t nr_inhibited_sparts; + /*! Number of extra #part we allocated (for on-the-fly creation) */ + size_t nr_extra_parts; + + /*! Number of extra #gpart we allocated (for on-the-fly creation) */ + size_t nr_extra_gparts; + + /*! Number of extra #spart we allocated (for on-the-fly creation) */ + size_t nr_extra_sparts; + /*! The particle data (cells have pointers to this). */ struct part *parts; @@ -179,9 +208,6 @@ struct space { /*! The s-particle data (cells have pointers to this). */ struct spart *sparts; - /*! The top-level FFT task */ - struct task *grav_top_level; - /*! Minimal mass of all the #part */ float min_part_mass; @@ -218,6 +244,9 @@ struct space { /*! List of cell indices. */ int *cell_index; + /*! The group information returned by VELOCIraptor for each #gpart. */ + struct velociraptor_gpart_data *gpart_group_data; + #ifdef WITH_MPI /*! Buffers for parts that we will receive from foreign cells. */ @@ -250,7 +279,7 @@ void space_init(struct space *s, struct swift_params *params, struct part *parts, struct gpart *gparts, struct spart *sparts, size_t Npart, size_t Ngpart, size_t Nspart, int periodic, int replicate, int generate_gas_in_ics, int hydro, int gravity, - int verbose, int dry_run); + int star_formation, int verbose, int dry_run); void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data); @@ -269,14 +298,18 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, struct gravity_tensors *multipole_list_begin, struct gravity_tensors *multipole_list_end); void space_split(struct space *s, int verbose); +void space_reorder_extras(struct space *s, int verbose); void space_split_mapper(void *map_data, int num_elements, void *extra_data); void space_list_useful_top_level_cells(struct space *s); void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts, - int *count_inibibited_parts, int verbose); + size_t *count_inhibited_parts, + size_t *count_extra_parts, int verbose); void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, - int *count_inibibited_gparts, int verbose); + size_t *count_inhibited_gparts, + size_t *count_extra_gparts, int verbose); void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, - int *count_inibibited_sparts, int verbose); + size_t *count_inhibited_sparts, + size_t *count_extra_sparts, int verbose); void space_synchronize_particle_positions(struct space *s); void space_do_parts_sort(void); void space_do_gparts_sort(void); @@ -294,6 +327,7 @@ void space_check_drift_point(struct space *s, integertime_t ti_drift, void space_check_top_multipoles_drift_point(struct space *s, integertime_t ti_drift); void space_check_timesteps(struct space *s); +void space_check_limiter(struct space *s); void space_replicate(struct space *s, int replicate, int verbose); void space_generate_gas(struct space *s, const struct cosmology *cosmo, int periodic, const double dim[3], int verbose); diff --git a/src/star_formation.c b/src/star_formation.c new file mode 100644 index 0000000000000000000000000000000000000000..698a64cc636dd79f00feac3f6cc88bf519fe09c1 --- /dev/null +++ b/src/star_formation.c @@ -0,0 +1,83 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 "part.h" +#include "restart.h" +#include "star_formation.h" +#include "units.h" + +/** + * @brief Initialises the star formation law properties in the internal + * unit system. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us the current internal system of units + * @param hydro_props The propoerties of the hydro scheme. + * @param starform the properties of the star formation law + */ +void starformation_init(struct swift_params* parameter_file, + const struct phys_const* phys_const, + const struct unit_system* us, + const struct hydro_props* hydro_props, + struct star_formation* starform) { + + starformation_init_backend(parameter_file, phys_const, us, hydro_props, + starform); +} + +/** + * @brief Print the properties of the star fromation law + * + * @param starform the star formation properties. + */ +void starformation_print(const struct star_formation* starform) { + + starformation_print_backend(starform); +} + +/** + * @brief Write an star_formation struct to the given FILE as a stream of + * bytes. + * + * @param starform the star formation struct + * @param stream the file stream + */ +void starformation_struct_dump(const struct star_formation* starform, + FILE* stream) { + restart_write_blocks((void*)starform, sizeof(struct star_formation), 1, + stream, "starformation", "star formation"); +} + +/** + * @brief Restore a star_formation struct from the given FILE as a stream of + * bytes. + * + * @param starform the star formation struct + * @param stream the file stream + */ +void starformation_struct_restore(const struct star_formation* starform, + FILE* stream) { + restart_read_blocks((void*)starform, sizeof(struct star_formation), 1, stream, + NULL, "star formation"); +} diff --git a/src/star_formation.h b/src/star_formation.h new file mode 100644 index 0000000000000000000000000000000000000000..78b9e018bf5f6cabc8a2b34dc5c4915f12806f68 --- /dev/null +++ b/src/star_formation.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_STAR_FORMATION_H +#define SWIFT_STAR_FORMATION_H + +/** + * @file src/star_formation.h + * @brief Branches between the different star formation recipies. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right star formation law definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation.h" +#else +#error "Invalid choice of star formation law" +#endif + +/* General functions defined in the source file */ +void starformation_init(struct swift_params* parameter_file, + const struct phys_const* phys_const, + const struct unit_system* us, + const struct hydro_props* hydro_props, + struct star_formation* starform); + +void starformation_print(const struct star_formation* starform); + +/* Dump store */ +void starformation_struct_dump(const struct star_formation* starform, + FILE* stream); + +void starformation_struct_restore(const struct star_formation* starform, + FILE* stream); + +#endif /* SWIFT_STAR_FORMATION_H */ diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h new file mode 100644 index 0000000000000000000000000000000000000000..e4753731b31df6634da9d3112369506fe4857ff5 --- /dev/null +++ b/src/star_formation/EAGLE/star_formation.h @@ -0,0 +1,629 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_EAGLE_STAR_FORMATION_H +#define SWIFT_EAGLE_STAR_FORMATION_H + +/* Local includes */ +#include "adiabatic_index.h" +#include "cooling.h" +#include "cosmology.h" +#include "engine.h" +#include "equation_of_state.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "random.h" +#include "stars.h" +#include "units.h" + +/** + * @file src/star_formation/EAGLE/star_formation.h + * @brief Star formation model used in the EAGLE model + */ + +/** + * @brief Properties of the EAGLE star formation model. + */ +struct star_formation { + + /*! Normalization of the KS star formation law (internal units) */ + double KS_normalization; + + /*! Normalization of the KS star formation law (Msun / kpc^2 / yr) */ + double KS_normalization_MSUNpYRpKPC2; + + /*! Slope of the KS law */ + double KS_power_law; + + /*! Slope of the high density KS law */ + double KS_high_den_power_law; + + /*! KS law High density threshold (internal units) */ + double KS_high_den_thresh; + + /*! KS high density normalization (internal units) */ + double KS_high_den_normalization; + + /*! KS high density normalization (H atoms per cm^3) */ + double KS_high_den_thresh_HpCM3; + + /*! Critical overdensity */ + double min_over_den; + + /*! Dalla Vecchia & Schaye temperature criteria */ + double temperature_margin_threshold_dex; + + /*! 10^Tdex of Dalla Vecchia & SChaye temperature criteria */ + double ten_to_temperature_margin_threshold_dex; + + /*! gas fraction */ + double fgas; + + /*! Star formation law slope */ + double SF_power_law; + + /*! star formation normalization (internal units) */ + double SF_normalization; + + /*! star formation high density slope */ + double SF_high_den_power_law; + + /*! Star formation high density normalization (internal units) */ + double SF_high_den_normalization; + + /*! Density threshold to form stars (internal units) */ + double density_threshold; + + /*! Density threshold to form stars in user units */ + double density_threshold_HpCM3; + + /*! Maximum density threshold to form stars (internal units) */ + double density_threshold_max; + + /*! Maximum density threshold to form stars (H atoms per cm^3) */ + double density_threshold_max_HpCM3; + + /*! Reference metallicity for metal-dependant threshold */ + double Z0; + + /*! Inverse of reference metallicity */ + double Z0_inv; + + /*! critical density Metallicity power law (internal units) */ + double n_Z0; + + /*! Polytropic index */ + double EOS_polytropic_index; + + /*! EOS density norm (H atoms per cm^3) */ + double EOS_density_norm_HpCM3; + + /*! EOS Temperature norm (Kelvin) */ + double EOS_temperature_norm_K; + + /*! EOS pressure norm, eq. 13 of Schaye & Dalla Vecchia 2008 (internal units) + */ + double EOS_pressure_c; + + /*! EOS Temperarure norm, eq. 13 of Schaye & Dalla Vecchia 2008 (internal + * units) */ + double EOS_temperature_c; + + /*! EOS density norm, eq. 13 of Schaye & Dalla Vecchia 2008 (internal units) + */ + double EOS_density_c; + + /*! Inverse of EOS density norm (internal units) */ + double EOS_density_c_inv; + + /*! Max physical density (H atoms per cm^3)*/ + double max_gas_density_HpCM3; + + /*! Max physical density (internal units) */ + double max_gas_density; +}; + +/** + * @brief Computes the density threshold for star-formation fo a given total + * metallicity. + * + * Follows Schaye (2004) eq. 19 and 24 (see also Schaye et al. 2015, eq. 2). + * + * @param Z The metallicity (metal mass fraction). + * @param starform The properties of the star formation model. + * @param phys_const The physical constants. + * @return The physical density threshold for star formation in internal units. + */ +INLINE static double star_formation_threshold( + const double Z, const struct star_formation* starform, + const struct phys_const* phys_const) { + + double density_threshold; + + /* Schaye (2004), eq. 19 and 24 */ + if (Z > 0.) { + density_threshold = starform->density_threshold * + powf(Z * starform->Z0_inv, starform->n_Z0); + density_threshold = min(density_threshold, starform->density_threshold_max); + } else { + density_threshold = starform->density_threshold_max; + } + + /* Convert to mass density */ + return density_threshold * phys_const->const_proton_mass; +} + +/** + * @brief Compute the pressure on the polytropic equation of state for a given + * Hydrogen number density. + * + * Schaye & Dalla Vecchia 2008, eq. 13. + * + * @param n_H The Hydrogen number density in internal units. + * @param starform The properties of the star formation model. + * @return The pressure on the equation of state in internal units. + */ +INLINE static double EOS_pressure(const double n_H, + const struct star_formation* starform) { + + return starform->EOS_pressure_c * + pow(n_H * starform->EOS_density_c_inv, starform->EOS_polytropic_index); +} + +/** + * @brief Compute the temperarue on the polytropic equation of state for a given + * Hydrogen number density. + * + * Schaye & Dalla Vecchia 2008, eq. 13 rewritten for temperature + * + * @param n_H The Hydrogen number density in internal units. + * @param starform The properties of the star formation model. + * @return The temperature on the equation of state in internal units. + */ +INLINE static double EOS_temperature(const double n_H, + const struct star_formation* starform) { + + return starform->EOS_temperature_c * + pow(n_H, starform->EOS_polytropic_index - 1.); +} + +/** + * @brief Calculate if the gas has the potential of becoming + * a star. + * + * @param starform the star formation law properties to use. + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. + * + */ +INLINE static int star_formation_is_star_forming( + const struct part* restrict p, const struct xpart* restrict xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cooling_function_data* restrict cooling) { + + /* Minimal density (converted from critical density) for star formation */ + const double rho_crit_times_min_over_den = + cosmo->critical_density * starform->min_over_den; + + /* Physical density of the particle */ + const double physical_density = hydro_get_physical_density(p, cosmo); + + /* Deside whether we should form stars or not, + * first we deterime if we have the correct over density + * if that is true we calculate if either the maximum density + * threshold is reached or if the metallicity dependent + * threshold is reached, after this we calculate if the + * temperature is appropriate */ + if (physical_density < rho_crit_times_min_over_den) return 0; + + /* In this case there are actually multiple possibilities + * because we also need to check if the physical density exceeded + * the appropriate limit */ + + const double Z = p->chemistry_data.smoothed_metal_mass_fraction_total; + const double X_H = p->chemistry_data.smoothed_metal_mass_fraction[0]; + const double n_H = physical_density * X_H; + + /* Get the density threshold */ + const double density_threshold = + star_formation_threshold(Z, starform, phys_const); + + /* Check if it exceeded the minimum density */ + if (n_H < density_threshold) return 0; + + /* Calculate the temperature */ + const double temperature = cooling_get_temperature(phys_const, hydro_props, + us, cosmo, cooling, p, xp); + + /* Temperature on the equation of state */ + const double temperature_eos = EOS_temperature(n_H, starform); + + /* Check the Scahye & Dalla Vecchia 2012 EOS-based temperature critrion */ + return (temperature < + temperature_eos * starform->ten_to_temperature_margin_threshold_dex); +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #xpart. + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR( + const struct part* restrict p, struct xpart* restrict xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, const double dt_star) { + + /* Abort early if time-step size is 0 */ + if (dt_star == 0.) { + + xp->sf_data.SFR = 0.f; + return; + } + + /* Hydrogen number density of this particle */ + const double physical_density = hydro_get_physical_density(p, cosmo); + const double X_H = p->chemistry_data.smoothed_metal_mass_fraction[0]; + const double n_H = physical_density * X_H / phys_const->const_proton_mass; + + /* Are we above the threshold for automatic star formation? */ + if (physical_density > + starform->max_gas_density * phys_const->const_proton_mass) { + + xp->sf_data.SFR = hydro_get_mass(p) / dt_star; + return; + } + + /* Pressure on the effective EOS for this particle */ + const double pressure = EOS_pressure(n_H, starform); + + /* Calculate the specific star formation rate */ + double SFRpergasmass; + if (hydro_get_physical_density(p, cosmo) < + starform->KS_high_den_thresh * phys_const->const_proton_mass) { + + SFRpergasmass = + starform->SF_normalization * pow(pressure, starform->SF_power_law); + + } else { + + SFRpergasmass = starform->SF_high_den_normalization * + pow(pressure, starform->SF_high_den_power_law); + } + + /* Store the SFR */ + xp->sf_data.SFR = SFRpergasmass * hydro_get_mass(p); +} + +/** + * @brief Decides whether a particle should be converted into a + * star or not. + * + * Equation 21 of Schaye & Dalla Vecchia 2008. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * @param e The #engine (for random numbers). + * @param dt_star The time-step of this particle + * @return 1 if a conversion should be done, 0 otherwise. + */ +INLINE static int star_formation_should_convert_to_star( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform, const struct engine* e, + const double dt_star) { + + /* Calculate the propability of forming a star */ + const double prob = xp->sf_data.SFR * dt_star / hydro_get_mass(p); + + /* Get a unique random number between 0 and 1 for star formation */ + const double random_number = + random_unit_interval(p->id, e->ti_current, random_number_star_formation); + + /* Have we been lucky and need to form a star? */ + return (prob > random_number); +} + +/** + * @brief Update the SF properties of a particle that is not star forming. + * + * @param p The #part. + * @param xp The #xpart. + * @param e The #engine. + * @param starform The properties of the star formation model. + * @param with_cosmology Are we running with cosmology switched on? + */ +INLINE static void star_formation_update_part_not_SFR( + struct part* p, struct xpart* xp, const struct engine* e, + const struct star_formation* starform, const int with_cosmology) { + + /* Check if it is the first time steps after star formation */ + if (xp->sf_data.SFR > 0.f) { + + /* Record the current time as an indicator of when this particle was last + star-forming. */ + if (with_cosmology) { + xp->sf_data.SFR = -e->cosmology->a; + } else { + xp->sf_data.SFR = -e->time; + } + } +} + +/** + * @brief Copies the properties of the gas particle over to the + * star particle + * + * @param e The #engine + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param sp the new created star particle with its properties. + * @param starform the star formation law properties to use. + * @param cosmo the cosmological parameters and properties. + * @param with_cosmology if we run with cosmology. + */ +INLINE static void star_formation_copy_properties( + const struct part* p, const struct xpart* xp, struct spart* sp, + const struct engine* e, const struct star_formation* starform, + const struct cosmology* cosmo, const int with_cosmology) { + + /* Store the current mass */ + sp->mass = hydro_get_mass(p); + + /* Store the current mass as the initial mass */ + sp->mass_init = hydro_get_mass(p); + + /* Store either the birth_scale_factor or birth_time depending */ + if (with_cosmology) { + sp->birth_scale_factor = cosmo->a; + } else { + sp->birth_time = e->time; + } + + /* Store the chemistry struct in the star particle */ + sp->chemistry_data = p->chemistry_data; + + /* Store the tracers data */ + sp->tracers_data = xp->tracers_data; + + /* Store the birth density in the star particle */ + sp->birth_density = hydro_get_physical_density(p, cosmo); +} + +/** + * @brief initialization of the star formation law + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units. + * @param hydro_props The propertis of the hydro model. + * @param starform the star formation law properties to initialize + */ +INLINE static void starformation_init_backend( + struct swift_params* parameter_file, const struct phys_const* phys_const, + const struct unit_system* us, const struct hydro_props* hydro_props, + struct star_formation* starform) { + + /* Get the Gravitational constant */ + const double G_newton = phys_const->const_newton_G; + + /* Initial Hydrogen abundance (mass fraction) */ + const double X_H = hydro_props->hydrogen_mass_fraction; + + /* Mean molecular weight assuming neutral gas */ + const double mean_molecular_weight = hydro_props->mu_neutral; + + /* Get the surface density unit Msun / pc^2 in internal units */ + const double Msun_per_pc2 = + phys_const->const_solar_mass / + (phys_const->const_parsec * phys_const->const_parsec); + + /* Get the SF surface density unit Msun / kpc^2 / yr in internal units */ + const double kpc = 1000. * phys_const->const_parsec; + const double Msun_per_kpc2_per_year = + phys_const->const_solar_mass / (kpc * kpc) / phys_const->const_year; + + /* Conversion of number density from cgs */ + const double number_density_from_cgs = + 1. / units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + /* Quantities that have to do with the Normal Kennicutt- + * Schmidt law will be read in this part of the code*/ + + /* Load the equation of state for this model */ + starform->EOS_polytropic_index = parser_get_param_double( + parameter_file, "EAGLEStarFormation:EOS_gamma_effective"); + starform->EOS_temperature_norm_K = parser_get_param_double( + parameter_file, "EAGLEStarFormation:EOS_temperature_norm_K"); + starform->EOS_density_norm_HpCM3 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:EOS_density_norm_H_p_cm3"); + starform->EOS_density_c = + starform->EOS_density_norm_HpCM3 * number_density_from_cgs; + starform->EOS_density_c_inv = 1. / starform->EOS_density_c; + + /* Calculate the EOS pressure normalization */ + starform->EOS_pressure_c = + starform->EOS_density_c * starform->EOS_temperature_norm_K * + phys_const->const_boltzmann_k / mean_molecular_weight / X_H; + + /* Normalisation of the temperature in the EOS calculatio */ + starform->EOS_temperature_c = + starform->EOS_pressure_c / phys_const->const_boltzmann_k; + starform->EOS_temperature_c *= + pow(starform->EOS_density_c, starform->EOS_polytropic_index); + + /* Read the critical density contrast from the parameter file*/ + starform->min_over_den = parser_get_param_double( + parameter_file, "EAGLEStarFormation:KS_min_over_density"); + + /* Read the gas fraction from the file */ + starform->fgas = parser_get_opt_param_double( + parameter_file, "EAGLEStarFormation:gas_fraction", 1.); + + /* Read the Kennicutt-Schmidt power law exponent */ + starform->KS_power_law = + parser_get_param_double(parameter_file, "EAGLEStarFormation:KS_exponent"); + + /* Calculate the power law of the corresponding star formation Schmidt law */ + starform->SF_power_law = (starform->KS_power_law - 1.) / 2.; + + /* Read the normalization of the KS law in KS law units */ + starform->KS_normalization_MSUNpYRpKPC2 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:KS_normalisation"); + + /* Convert to internal units */ + starform->KS_normalization = + starform->KS_normalization_MSUNpYRpKPC2 * Msun_per_kpc2_per_year; + + /* Calculate the starformation pre-factor (eq. 12 of Schaye & Dalla Vecchia + * 2008) */ + starform->SF_normalization = + starform->KS_normalization * pow(Msun_per_pc2, -starform->KS_power_law) * + pow(hydro_gamma * starform->fgas / G_newton, starform->SF_power_law); + + /* Read the high density Kennicutt-Schmidt power law exponent */ + starform->KS_high_den_power_law = parser_get_param_double( + parameter_file, "EAGLEStarFormation:KS_high_density_exponent"); + + /* Calculate the SF high density power law */ + starform->SF_high_den_power_law = (starform->KS_high_den_power_law - 1.) / 2.; + + /* Read the high density criteria for the KS law in number density per cm^3 */ + starform->KS_high_den_thresh_HpCM3 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"); + + /* Transform the KS high density criteria to simulation units */ + starform->KS_high_den_thresh = + starform->KS_high_den_thresh_HpCM3 * number_density_from_cgs; + + /* Pressure at the high-density threshold */ + const double EOS_high_den_pressure = + EOS_pressure(starform->KS_high_den_thresh, starform); + + /* Calculate the KS high density normalization + * We want the SF law to be continous so the normalisation of the second + * power-law is the value of the first power-law at the high-density threshold + */ + starform->KS_high_den_normalization = + starform->KS_normalization * + pow(Msun_per_pc2, + starform->KS_high_den_power_law - starform->KS_power_law) * + pow(hydro_gamma * EOS_high_den_pressure * starform->fgas / G_newton, + (starform->KS_power_law - starform->KS_high_den_power_law) * 0.5f); + + /* Calculate the SF high density normalization */ + starform->SF_high_den_normalization = + starform->KS_high_den_normalization * + pow(Msun_per_pc2, -starform->KS_high_den_power_law) * + pow(hydro_gamma * starform->fgas / G_newton, + starform->SF_high_den_power_law); + + /* Get the maximum physical density for SF */ + starform->max_gas_density_HpCM3 = parser_get_opt_param_double( + parameter_file, "EAGLEStarFormation:KS_max_density_threshold_H_p_cm3", + FLT_MAX); + + /* Convert the maximum physical density to internal units */ + starform->max_gas_density = + starform->max_gas_density_HpCM3 * number_density_from_cgs; + + starform->temperature_margin_threshold_dex = parser_get_opt_param_double( + parameter_file, "EAGLEStarFormation:KS_temperature_margin_dex", FLT_MAX); + + starform->ten_to_temperature_margin_threshold_dex = + exp10(starform->temperature_margin_threshold_dex); + + /* Read the normalization of the metallicity dependent critical + * density*/ + starform->density_threshold_HpCM3 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:threshold_norm_H_p_cm3"); + + /* Convert to internal units */ + starform->density_threshold = + starform->density_threshold_HpCM3 * number_density_from_cgs; + + /* Read the scale metallicity Z0 */ + starform->Z0 = parser_get_param_double(parameter_file, + "EAGLEStarFormation:threshold_Z0"); + starform->Z0_inv = 1. / starform->Z0; + + /* Read the power law of the critical density scaling */ + starform->n_Z0 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:threshold_slope"); + + /* Read the maximum allowed density for star formation */ + starform->density_threshold_max_HpCM3 = parser_get_param_double( + parameter_file, "EAGLEStarFormation:threshold_max_density_H_p_cm3"); + + /* Convert to internal units */ + starform->density_threshold_max = + starform->density_threshold_max_HpCM3 * number_density_from_cgs; +} + +/** + * @brief Prints the used parameters of the star formation law + * + * @param starform the star formation law properties. + * */ +INLINE static void starformation_print_backend( + const struct star_formation* starform) { + + message("Star formation law is EAGLE (Schaye & Dalla Vecchia 2008)"); + message( + "With properties: normalization = %e Msun/kpc^2/yr, slope of the" + "Kennicutt-Schmidt law = %e and gas fraction = %e ", + starform->KS_normalization_MSUNpYRpKPC2, starform->KS_power_law, + starform->fgas); + message("At densities of %e H/cm^3 the slope changes to %e.", + starform->KS_high_den_thresh_HpCM3, starform->KS_high_den_power_law); + message( + "The effective equation of state is given by: polytropic " + "index = %e , normalization density = %e #/cm^3 and normalization " + "temperature = %e K", + starform->EOS_polytropic_index, starform->EOS_density_norm_HpCM3, + starform->EOS_temperature_norm_K); + message("Density threshold follows Schaye (2004)"); + message( + "the normalization of the density threshold is given by" + " %e #/cm^3, with metallicity slope of %e, and metallicity normalization" + " of %e, the maximum density threshold is given by %e #/cm^3", + starform->density_threshold_HpCM3, starform->n_Z0, starform->Z0, + starform->density_threshold_max_HpCM3); + message("Temperature threshold is given by Dalla Vecchia and Schaye (2012)"); + message("The temperature threshold offset from the EOS is given by: %e dex", + starform->temperature_margin_threshold_dex); + message("Running with a maximum gas density given by: %e #/cm^3", + starform->max_gas_density_HpCM3); +} + +#endif /* SWIFT_EAGLE_STAR_FORMATION_H */ diff --git a/src/star_formation/EAGLE/star_formation_io.h b/src/star_formation/EAGLE/star_formation_io.h new file mode 100644 index 0000000000000000000000000000000000000000..cee96326e458d0581af6e62e452ac433dcf407bd --- /dev/null +++ b/src/star_formation/EAGLE/star_formation_io.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_STAR_FORMATION_EAGLE_IO_H +#define SWIFT_STAR_FORMATION_EAGLE_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended data particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int star_formation_write_particles( + const struct part* parts, const struct xpart* xparts, + struct io_props* list) { + + list[0] = + io_make_output_field("SFR", FLOAT, 1, UNIT_CONV_SFR, xparts, sf_data.SFR); + + return 1; +} + +#endif /* SWIFT_STAR_FORMATION_EAGLE_IO_H */ diff --git a/src/star_formation/EAGLE/star_formation_struct.h b/src/star_formation/EAGLE/star_formation_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..41247e160a3eddbc9184c59b67cfa2a1d7259a05 --- /dev/null +++ b/src/star_formation/EAGLE/star_formation_struct.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_EAGLE_STAR_FORMATION_STRUCT_H +#define SWIFT_EAGLE_STAR_FORMATION_STRUCT_H + +/** + * @brief Star-formation-related properties stored in the extended particle + * data. + */ +struct star_formation_xpart_data { + + /*! Star formation rate */ + float SFR; +}; + +#endif /* SWIFT_EAGLE_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h new file mode 100644 index 0000000000000000000000000000000000000000..093c4118601ec79c4f55646975f0505b8357afa6 --- /dev/null +++ b/src/star_formation/none/star_formation.h @@ -0,0 +1,160 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_NONE_STAR_FORMATION_H +#define SWIFT_NONE_STAR_FORMATION_H + +/* Local includes */ +#include "cosmology.h" +#include "error.h" +#include "hydro_properties.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/* Starformation struct */ +struct star_formation {}; + +/** + * @brief Calculate if the gas has the potential of becoming + * a star. + * + * No star formation should occur, so return 0. + * + * @param starform the star formation law properties to use. + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param hydro_props The properties of the hydro scheme. + * @param us The internal system of units. + * @param cooling The cooling data struct. + * + */ +INLINE static int star_formation_is_star_forming( + const struct part* restrict p, const struct xpart* restrict xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, + const struct hydro_props* restrict hydro_props, + const struct unit_system* restrict us, + const struct cooling_function_data* restrict cooling) { + + return 0; +} + +/** + * @brief Compute the star-formation rate of a given particle and store + * it into the #xpart. + * + * Nothing to do here. + * + * @param p #part. + * @param xp the #xpart. + * @param starform the star formation law properties to use + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param dt_star The time-step of this particle. + */ +INLINE static void star_formation_compute_SFR( + const struct part* restrict p, struct xpart* restrict xp, + const struct star_formation* starform, const struct phys_const* phys_const, + const struct cosmology* cosmo, const double dt_star) {} + +/** + * @brief Decides whether a particle should be converted into a + * star or not. + * + * No SF should occur, so return 0. + * + * @param p The #part. + * @param xp The #xpart. + * @param starform The properties of the star formation model. + * @param e The #engine (for random numbers). + * @param dt_star The time-step of this particle + * @return 1 if a conversion should be done, 0 otherwise. + */ +INLINE static int star_formation_should_convert_to_star( + const struct part* p, const struct xpart* xp, + const struct star_formation* starform, const struct engine* e, + const double dt_star) { + + return 0; +} + +/** + * @brief Update the SF properties of a particle that is not star forming. + * + * Nothing to do here. + * + * @param p The #part. + * @param xp The #xpart. + * @param e The #engine. + * @param starform The properties of the star formation model. + * @param with_cosmology Are we running with cosmology switched on? + */ +INLINE static void star_formation_update_part_not_SFR( + struct part* p, struct xpart* xp, const struct engine* e, + const struct star_formation* starform, const int with_cosmology) {} + +/** + * @brief Copies the properties of the gas particle over to the + * star particle. + * + * Nothing to do here. + * + * @param e The #engine + * @param p the gas particles. + * @param xp the additional properties of the gas particles. + * @param sp the new created star particle with its properties. + * @param starform the star formation law properties to use. + * @param phys_const the physical constants in internal units. + * @param cosmo the cosmological parameters and properties. + * @param with_cosmology if we run with cosmology. + */ +INLINE static void star_formation_copy_properties( + const struct part* p, const struct xpart* xp, struct spart* sp, + const struct engine* e, const struct star_formation* starform, + const struct cosmology* cosmo, const int with_cosmology) {} + +/** + * @brief initialization of the star formation law + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param starform the star formation law properties to initialize + * + */ +INLINE static void starformation_init_backend( + struct swift_params* parameter_file, const struct phys_const* phys_const, + const struct unit_system* us, const struct hydro_props* hydro_props, + const struct star_formation* starform) {} + +/** + * @brief Prints the used parameters of the star formation law + * + * @param starform the star formation law properties. + */ +INLINE static void starformation_print_backend( + const struct star_formation* starform) { + + message("Star formation law is 'No Star Formation'"); +} + +#endif /* SWIFT_NONE_STAR_FORMATION_H */ diff --git a/src/star_formation/none/star_formation_io.h b/src/star_formation/none/star_formation_io.h new file mode 100644 index 0000000000000000000000000000000000000000..b271926f915279b681aac8348a0e375083901deb --- /dev/null +++ b/src/star_formation/none/star_formation_io.h @@ -0,0 +1,44 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_STAR_FORMATION_NONE_IO_H +#define SWIFT_STAR_FORMATION_NONE_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended data particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int star_formation_write_particles( + const struct part* parts, const struct xpart* xparts, + struct io_props* list) { + + return 0; +} + +#endif /* SWIFT_STAR_FORMATION_NONE_IO_H */ diff --git a/src/star_formation/none/star_formation_struct.h b/src/star_formation/none/star_formation_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..27a2adaf83d0a02a0d08e7eef8b45bea630689e4 --- /dev/null +++ b/src/star_formation/none/star_formation_struct.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_NONE_STAR_FORMATION_STRUCT_H +#define SWIFT_NONE_STAR_FORMATION_STRUCT_H + +/** + * @brief Star-formation-related properties stored in the extended particle + * data. + */ +struct star_formation_xpart_data {}; + +#endif /* SWIFT_NONE_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation_io.h b/src/star_formation_io.h new file mode 100644 index 0000000000000000000000000000000000000000..248866c513e405b80f974d4932f02d3c707785ee --- /dev/null +++ b/src/star_formation_io.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_STAR_FORMATION_IO_H +#define SWIFT_STAR_FORMATION_IO_H + +/** + * @file src/star_formation_io.h + * @brief Branches between the i/o routines for the SF code. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation_io.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation_io.h" +#else +#error "Invalid choice of star formation model." +#endif + +#endif /* SWIFT_STAR_FORMATION_IO_H */ diff --git a/src/star_formation_struct.h b/src/star_formation_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..3e9859cfb650ddd370c6acce282084b7eba6bf82 --- /dev/null +++ b/src/star_formation_struct.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_STAR_FORMATION_STRUCT_H +#define SWIFT_STAR_FORMATION_STRUCT_H + +/** + * @file src/star_formation_struct.h + * @brief Branches between the different particle data SF tracers + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation_struct.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation_struct.h" +#else +#error "Invalid choice of star formation structure." +#endif + +#endif /* SWIFT_STAR_FORMATION_STRUCT_H */ diff --git a/src/stars.h b/src/stars.h index 3e921239a29d862aba998c138623eb1cb81a37b9..fc7ee74d3a2cae91ee209c4008eee4d5dd0f375e 100644 --- a/src/stars.h +++ b/src/stars.h @@ -22,9 +22,18 @@ /* Config parameters. */ #include "../config.h" -/* So far only one model here */ -/* Straight-forward import */ +/* Select the correct star model */ +#if defined(STARS_NONE) #include "./stars/Default/stars.h" #include "./stars/Default/stars_iact.h" +#elif defined(STARS_EAGLE) +#include "./stars/EAGLE/stars.h" +#include "./stars/EAGLE/stars_iact.h" +#elif defined(STARS_GEAR) +#include "./stars/GEAR/stars.h" +#include "./stars/GEAR/stars_iact.h" +#else +#error "Invalid choice of star model" +#endif #endif diff --git a/src/stars/Default/stars_iact.h b/src/stars/Default/stars_iact.h index 3b066686060bcee7583fe65c032da5c12f637081..9e27f86028245a230cfd777dfc46da7b7d2f3915 100644 --- a/src/stars/Default/stars_iact.h +++ b/src/stars/Default/stars_iact.h @@ -38,3 +38,20 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, ++si->num_ngb_density; #endif } + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj, + struct spart *restrict si, + struct part *restrict pj, float a, float H) {} diff --git a/src/stars/Default/stars_part.h b/src/stars/Default/stars_part.h index 1922cc0e260a3c0f2052649a87252019514e637f..eb253fd3c9f0a44efeafbfdd2b002f4b2694c4e3 100644 --- a/src/stars/Default/stars_part.h +++ b/src/stars/Default/stars_part.h @@ -22,6 +22,10 @@ /* Some standard headers. */ #include <stdlib.h> +/* Read chemistry */ +#include "chemistry_struct.h" +#include "tracers_struct.h" + /** * @brief Particle fields for the star particles. * @@ -41,6 +45,9 @@ struct spart { /* Offset between current position and position at last tree rebuild. */ float x_diff[3]; + /* Offset between current position and position at last tree rebuild. */ + float x_diff_sort[3]; + /*! Particle velocity. */ float v[3]; @@ -62,6 +69,12 @@ struct spart { } density; + /*! Tracer structure */ + struct tracers_xpart_data tracers_data; + + /*! Chemistry structure */ + struct chemistry_part_data chemistry_data; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h new file mode 100644 index 0000000000000000000000000000000000000000..4acfef360cedf8993702d27a748a364288052410 --- /dev/null +++ b/src/stars/EAGLE/stars.h @@ -0,0 +1,151 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@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 SWIFT_EAGLE_STARS_H +#define SWIFT_EAGLE_STARS_H + +#include <float.h> +#include "minmax.h" + +/** + * @brief Computes the gravity time-step of a given star particle. + * + * @param sp Pointer to the s-particle data. + */ +__attribute__((always_inline)) INLINE static float stars_compute_timestep( + const struct spart* const sp) { + + return FLT_MAX; +} + +/** + * @brief Initialises the s-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_first_init_spart( + struct spart* sp) { + + sp->time_bin = 0; + sp->birth_density = -1.f; +} + +/** + * @brief Prepares a s-particle for its interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_init_spart( + struct spart* sp) { + +#ifdef DEBUG_INTERACTIONS_STARS + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + sp->ids_ngbs_density[i] = -1; + sp->num_ngb_density = 0; +#endif + + sp->density.wcount = 0.f; + sp->density.wcount_dh = 0.f; +} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param sp The particle. + */ +__attribute__((always_inline)) INLINE static void stars_reset_predicted_values( + struct spart* restrict sp) {} + +/** + * @brief Finishes the calculation of (non-gravity) forces acting on stars + * + * Multiplies the forces and accelerations by the appropiate constants + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void stars_end_force( + struct spart* sp) {} + +/** + * @brief Kick the additional variables + * + * @param sp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void stars_kick_extra( + struct spart* sp, float dt) {} + +/** + * @brief Finishes the calculation of density on stars + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_end_density( + struct spart* sp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = sp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Finish the calculation by inserting the missing h-factors */ + sp->density.wcount *= h_inv_dim; + sp->density.wcount_dh *= h_inv_dim_plus_one; +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( + struct spart* restrict sp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = sp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + sp->density.wcount = kernel_root * h_inv_dim; + sp->density.wcount_dh = 0.f; +} + +/** + * @brief Evolve the stellar properties of a #spart. + * + * This function allows for example to compute the SN rate before sending + * this information to a different MPI rank. + * + * @param sp The particle to act upon + * @param cosmo The current cosmological model. + * @param stars_properties The #stars_props + */ +__attribute__((always_inline)) INLINE static void stars_evolve_spart( + struct spart* restrict sp, const struct stars_props* stars_properties, + const struct cosmology* cosmo) {} + +#endif /* SWIFT_EAGLE_STARS_H */ diff --git a/src/stars/EAGLE/stars_debug.h b/src/stars/EAGLE/stars_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..6bdba45e3b090ccd2eb207bf92a374ce531ee3e0 --- /dev/null +++ b/src/stars/EAGLE/stars_debug.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@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 SWIFT_EAGLE_STARS_DEBUG_H +#define SWIFT_EAGLE_STARS_DEBUG_H + +__attribute__((always_inline)) INLINE static void stars_debug_particle( + const struct spart* p) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v_full=[%.3e,%.3e,%.3e] p->mass=%.3e \n t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v_full[0], p->v_full[1], p->v_full[2], + p->mass, p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_EAGLE_STARS_DEBUG_H */ diff --git a/src/stars/EAGLE/stars_iact.h b/src/stars/EAGLE/stars_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..9e27f86028245a230cfd777dfc46da7b7d2f3915 --- /dev/null +++ b/src/stars/EAGLE/stars_iact.h @@ -0,0 +1,57 @@ +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, + struct spart *restrict si, + const struct part *restrict pj, float a, + float H) { + + float wi, wi_dx; + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the number of neighbours */ + si->density.wcount += wi; + si->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); + +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS) + si->ids_ngbs_density[si->num_ngb_density] = pj->id; + ++si->num_ngb_density; +#endif +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj, + struct spart *restrict si, + struct part *restrict pj, float a, float H) {} diff --git a/src/stars/EAGLE/stars_io.h b/src/stars/EAGLE/stars_io.h new file mode 100644 index 0000000000000000000000000000000000000000..64fde1a779966cc072a750fab89c74c5329bc03b --- /dev/null +++ b/src/stars/EAGLE/stars_io.h @@ -0,0 +1,209 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_EAGLE_STARS_IO_H +#define SWIFT_EAGLE_STARS_IO_H + +#include "io_properties.h" +#include "stars_part.h" + +/** + * @brief Specifies which s-particle fields to read from a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void stars_read_particles(struct spart *sparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to read */ + *num_fields = 5; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, sparts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, sparts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + sparts, mass); + list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, sparts, id); + list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, + UNIT_CONV_LENGTH, sparts, h); +} + +/** + * @brief Specifies which s-particle fields to write to a dataset + * + * @param sparts The s-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +INLINE static void stars_write_particles(const struct spart *sparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to write */ + *num_fields = 8; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + sparts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, sparts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, sparts, mass); + list[3] = io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, + sparts, id); + list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + sparts, h); + list[5] = io_make_output_field("BirthDensity", FLOAT, 1, UNIT_CONV_DENSITY, + sparts, birth_density); + list[6] = io_make_output_field("Initial_Masses", FLOAT, 1, UNIT_CONV_MASS, + sparts, mass_init); + list[7] = io_make_output_field("Birth_time", FLOAT, 1, UNIT_CONV_TIME, sparts, + birth_time); +} + +/** + * @brief Initialize the global properties of the stars scheme. + * + * By default, takes the values provided by the hydro. + * + * @param sp The #stars_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param p The already read-in properties of the hydro scheme. + */ +INLINE static void stars_props_init(struct stars_props *sp, + const struct phys_const *phys_const, + const struct unit_system *us, + struct swift_params *params, + const struct hydro_props *p) { + + /* Kernel properties */ + sp->eta_neighbours = parser_get_opt_param_float( + params, "Stars:resolution_eta", p->eta_neighbours); + + /* Tolerance for the smoothing length Newton-Raphson scheme */ + sp->h_tolerance = + parser_get_opt_param_float(params, "Stars:h_tolerance", p->h_tolerance); + + /* Get derived properties */ + sp->target_neighbours = pow_dimension(sp->eta_neighbours) * kernel_norm; + const float delta_eta = sp->eta_neighbours * (1.f + sp->h_tolerance); + sp->delta_neighbours = + (pow_dimension(delta_eta) - pow_dimension(sp->eta_neighbours)) * + kernel_norm; + + /* Maximal smoothing length */ + sp->h_max = parser_get_opt_param_float(params, "Stars:h_max", p->h_max); + + /* Number of iterations to converge h */ + sp->max_smoothing_iterations = parser_get_opt_param_int( + params, "Stars:max_ghost_iterations", p->max_smoothing_iterations); + + /* Initialize with solar abundance */ + // sp->chemistry_data.smoothed_metal_mass_fraction_total = + + /* Time integration properties */ + const float max_volume_change = + parser_get_opt_param_float(params, "Stars:max_volume_change", -1); + if (max_volume_change == -1) + sp->log_max_h_change = p->log_max_h_change; + else + sp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); +} + +/** + * @brief Print the global properties of the stars scheme. + * + * @param sp The #stars_props. + */ +INLINE static void stars_props_print(const struct stars_props *sp) { + + /* Now stars */ + message("Stars kernel: %s with eta=%f (%.2f neighbours).", kernel_name, + sp->eta_neighbours, sp->target_neighbours); + + message("Stars relative tolerance in h: %.5f (+/- %.4f neighbours).", + sp->h_tolerance, sp->delta_neighbours); + + message( + "Stars integration: Max change of volume: %.2f " + "(max|dlog(h)/dt|=%f).", + pow_dimension(expf(sp->log_max_h_change)), sp->log_max_h_change); + + if (sp->h_max != FLT_MAX) + message("Maximal smoothing length allowed: %.4f", sp->h_max); + + message("Maximal iterations in ghost task set to %d", + sp->max_smoothing_iterations); +} + +#if defined(HAVE_HDF5) +INLINE static void stars_props_print_snapshot(hid_t h_grpstars, + const struct stars_props *sp) { + + io_write_attribute_s(h_grpstars, "Kernel function", kernel_name); + io_write_attribute_f(h_grpstars, "Kernel target N_ngb", + sp->target_neighbours); + io_write_attribute_f(h_grpstars, "Kernel delta N_ngb", sp->delta_neighbours); + io_write_attribute_f(h_grpstars, "Kernel eta", sp->eta_neighbours); + io_write_attribute_f(h_grpstars, "Smoothing length tolerance", + sp->h_tolerance); + io_write_attribute_f(h_grpstars, "Maximal smoothing length", sp->h_max); + io_write_attribute_f(h_grpstars, "Volume log(max(delta h))", + sp->log_max_h_change); + io_write_attribute_f(h_grpstars, "Volume max change time-step", + pow_dimension(expf(sp->log_max_h_change))); + io_write_attribute_i(h_grpstars, "Max ghost iterations", + sp->max_smoothing_iterations); +} +#endif + +/** + * @brief Write a #stars_props struct to the given FILE as a stream of bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_dump(const struct stars_props *p, + FILE *stream) { + restart_write_blocks((void *)p, sizeof(struct stars_props), 1, stream, + "starsprops", "stars props"); +} + +/** + * @brief Restore a stars_props struct from the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +INLINE static void stars_props_struct_restore(const struct stars_props *p, + FILE *stream) { + restart_read_blocks((void *)p, sizeof(struct stars_props), 1, stream, NULL, + "stars props"); +} + +#endif /* SWIFT_EAGLE_STAR_IO_H */ diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h new file mode 100644 index 0000000000000000000000000000000000000000..a18a55e959078201ada3c7589db92e5b4946ad25 --- /dev/null +++ b/src/stars/EAGLE/stars_part.h @@ -0,0 +1,141 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * 2018 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * 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 SWIFT_EAGLE_STAR_PART_H +#define SWIFT_EAGLE_STAR_PART_H + +/* Some standard headers. */ +#include <stdlib.h> + +/* Read chemistry */ +#include "chemistry_struct.h" +#include "tracers_struct.h" + +/** + * @brief Particle fields for the star particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct spart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff_sort[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Star mass */ + float mass; + + /*! Initial star mass */ + float mass_init; + + /* Particle cutoff radius. */ + float h; + + /*! Particle time bin */ + timebin_t time_bin; + + struct { + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + + /*! Union for the birth time and birht scale factor */ + union { + /*! Birth time */ + float birth_time; + + /*! Birth scale factor */ + float birth_scale_factor; + }; + + /*! Birth density */ + float birth_density; + + /*! Tracer structure */ + struct tracers_xpart_data tracers_data; + + /*! Chemistry structure */ + struct chemistry_part_data chemistry_data; + +#ifdef SWIFT_DEBUG_CHECKS + + /* Time of the last drift */ + integertime_t ti_drift; + + /* Time of the last kick */ + integertime_t ti_kick; + +#endif + +#ifdef DEBUG_INTERACTIONS_STARS + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; + + /*! Number of interactions in the density SELF and PAIR */ + int num_ngb_density; +#endif + +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Contains all the constants and parameters of the stars scheme + */ +struct stars_props { + + /*! Resolution parameter */ + float eta_neighbours; + + /*! Target weightd number of neighbours (for info only)*/ + float target_neighbours; + + /*! Smoothing length tolerance */ + float h_tolerance; + + /*! Tolerance on neighbour number (for info only)*/ + float delta_neighbours; + + /*! Maximal smoothing length */ + float h_max; + + /*! Maximal number of iterations to converge h */ + int max_smoothing_iterations; + + /*! Maximal change of h over one time-step */ + float log_max_h_change; +}; + +#endif /* SWIFT_EAGLE_STAR_PART_H */ diff --git a/src/stars_io.h b/src/stars_io.h index 046e90ee7570430ea25632539bc2cd642d4b52c0..c2a095c47c8d491fe6cf97d22c367d1e9d4a7fd6 100644 --- a/src/stars_io.h +++ b/src/stars_io.h @@ -19,8 +19,18 @@ #ifndef SWIFT_STARS_IO_H #define SWIFT_STARS_IO_H +#include "../config.h" #include "./const.h" +/* Load the correct star type */ +#if defined(STARS_NONE) #include "./stars/Default/stars_io.h" +#elif defined(STARS_EAGLE) +#include "./stars/EAGLE/stars_io.h" +#elif defined(STARS_GEAR) +#include "./stars/GEAR/stars_io.h" +#else +#error "Invalid choice of star model" +#endif #endif /* SWIFT_STARS_IO_H */ diff --git a/src/swift.h b/src/swift.h index 153c4ae0d4440d083f1b0c9850e1f2649c0df6fb..e166dde5dd3baed07fb5c081c64ce941d6c6ce6d 100644 --- a/src/swift.h +++ b/src/swift.h @@ -38,6 +38,7 @@ #include "debug.h" #include "dump.h" #include "engine.h" +#include "entropy_floor.h" #include "error.h" #include "gravity.h" #include "gravity_derivatives.h" @@ -60,13 +61,14 @@ #include "potential.h" #include "profiler.h" #include "queue.h" +#include "random.h" #include "restart.h" #include "runner.h" #include "scheduler.h" #include "serial_io.h" #include "single_io.h" -#include "sourceterms.h" #include "space.h" +#include "star_formation.h" #include "stars.h" #include "stars_io.h" #include "task.h" diff --git a/src/swift_velociraptor_part.h b/src/swift_velociraptor_part.h index adae884c2f930c44edf4d48f47f168475bc65885..700842ac5a13e5bee4af15cc0d8726fc668ce421 100644 --- a/src/swift_velociraptor_part.h +++ b/src/swift_velociraptor_part.h @@ -21,7 +21,13 @@ #include "part_type.h" -/* SWIFT/VELOCIraptor particle. */ +/** + * @brief SWIFT/VELOCIraptor particle. + * + * This should match the structure Swift::swift_vel_part + * defined in the file NBodylib/src/NBody/SwiftParticle.h + * of the VELOCIraptor code. + */ struct swift_vel_part { /*! Particle ID. */ @@ -42,8 +48,18 @@ struct swift_vel_part { /*! Internal energy of gas particle */ float u; + /*! Temperature of a gas particle */ + float T; + /*! Type of the #gpart (DM, gas, star, ...) */ enum part_type type; + + /*! MPI rank on which this #gpart lives on the SWIFT side. */ + int task; + + /*! Index of this #gpart in the global array of this rank on the SWIFT + side. */ + int index; }; #endif /* SWIFT_VELOCIRAPTOR_PART_H */ diff --git a/src/task.c b/src/task.c index e1d7a4e4dc832b47b4100e70f6af87bf6927d484..d619eac96cd9913b6cccde421c273022d7bec6f1 100644 --- a/src/task.c +++ b/src/task.c @@ -42,6 +42,7 @@ /* Local headers. */ #include "atomic.h" +#include "engine.h" #include "error.h" #include "inline.h" #include "lock.h" @@ -66,6 +67,7 @@ const char *taskID_names[task_type_count] = {"none", "kick1", "kick2", "timestep", + "timestep_limiter", "send", "recv", "grav_long_range", @@ -75,19 +77,20 @@ const char *taskID_names[task_type_count] = {"none", "grav_mesh", "cooling", "star_formation", - "sourceterms", "logger", "stars_ghost_in", "stars_ghost", "stars_ghost_out", + "stars_sort", "fof_self", "fof_pair"}; /* Sub-task type names. */ const char *subtaskID_names[task_subtype_count] = { - "none", "density", "gradient", "force", "grav", - "external_grav", "tend", "xv", "rho", "gpart", - "multipole", "spart", "stars_density"}; + "none", "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend", + "xv", "rho", "gpart", "multipole", + "spart", "stars_density", "stars_feedback"}; #ifdef WITH_MPI /* MPI communicators for the subtypes. */ @@ -141,8 +144,8 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_sort: case task_type_ghost: case task_type_extra_ghost: + case task_type_timestep_limiter: case task_type_cooling: - case task_type_sourceterms: return task_action_part; break; @@ -150,6 +153,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( return task_action_all; case task_type_stars_ghost: + case task_type_stars_sort: return task_action_spart; break; @@ -162,10 +166,12 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_subtype_density: case task_subtype_gradient: case task_subtype_force: + case task_subtype_limiter: return task_action_part; break; case task_subtype_stars_density: + case task_subtype_stars_feedback: return task_action_all; break; @@ -339,6 +345,8 @@ void task_unlock(struct task *t) { case task_type_drift_part: case task_type_sort: + case task_type_ghost: + case task_type_timestep_limiter: cell_unlocktree(ci); break; @@ -347,11 +355,20 @@ void task_unlock(struct task *t) { cell_gunlocktree(ci); break; + case task_type_stars_sort: + cell_sunlocktree(ci); + break; + case task_type_self: case task_type_sub_self: if (subtype == task_subtype_grav) { cell_gunlocktree(ci); cell_munlocktree(ci); + } else if (subtype == task_subtype_stars_density) { + cell_sunlocktree(ci); + } else if (subtype == task_subtype_stars_feedback) { + cell_sunlocktree(ci); + cell_unlocktree(ci); } else { cell_unlocktree(ci); } @@ -364,6 +381,14 @@ void task_unlock(struct task *t) { cell_gunlocktree(cj); cell_munlocktree(ci); cell_munlocktree(cj); + } else if (subtype == task_subtype_stars_density) { + cell_sunlocktree(ci); + cell_sunlocktree(cj); + } else if (subtype == task_subtype_stars_feedback) { + cell_sunlocktree(ci); + cell_sunlocktree(cj); + cell_unlocktree(ci); + cell_unlocktree(cj); } else { cell_unlocktree(ci); cell_unlocktree(cj); @@ -384,6 +409,12 @@ void task_unlock(struct task *t) { cell_munlocktree(cj); break; + case task_type_star_formation: + cell_unlocktree(ci); + cell_sunlocktree(ci); + cell_gunlocktree(ci); + break; + default: break; } @@ -441,10 +472,17 @@ int task_lock(struct task *t) { case task_type_drift_part: case task_type_sort: + case task_type_ghost: + case task_type_timestep_limiter: if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; break; + case task_type_stars_sort: + if (ci->stars.hold) return 0; + if (cell_slocktree(ci) != 0) return 0; + break; + case task_type_drift_gpart: case task_type_grav_mesh: if (ci->grav.phold) return 0; @@ -462,7 +500,19 @@ int task_lock(struct task *t) { cell_gunlocktree(ci); return 0; } - } else { + } else if (subtype == task_subtype_stars_density) { + if (ci->stars.hold) return 0; + if (cell_slocktree(ci) != 0) return 0; + } else if (subtype == task_subtype_stars_feedback) { + if (ci->stars.hold) return 0; + if (ci->hydro.hold) return 0; + if (cell_slocktree(ci) != 0) return 0; + if (cell_locktree(ci) != 0) { + cell_sunlocktree(ci); + return 0; + } + } else { /* subtype == hydro */ + if (ci->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; } break; @@ -486,7 +536,34 @@ int task_lock(struct task *t) { cell_munlocktree(ci); return 0; } - } else { + } else if (subtype == task_subtype_stars_density) { + if (ci->stars.hold || cj->stars.hold) return 0; + if (cell_slocktree(ci) != 0) return 0; + if (cell_slocktree(cj) != 0) { + cell_sunlocktree(ci); + return 0; + } + } else if (subtype == task_subtype_stars_feedback) { + /* Lock the stars and the gas particles in both cells */ + if (ci->stars.hold || cj->stars.hold) return 0; + if (ci->hydro.hold || cj->hydro.hold) return 0; + if (cell_slocktree(ci) != 0) return 0; + if (cell_slocktree(cj) != 0) { + cell_sunlocktree(ci); + return 0; + } + if (cell_locktree(ci) != 0) { + cell_sunlocktree(ci); + cell_sunlocktree(cj); + return 0; + } + if (cell_locktree(cj) != 0) { + cell_sunlocktree(ci); + cell_sunlocktree(cj); + cell_unlocktree(ci); + return 0; + } + } else { /* subtype == hydro */ /* Lock the parts in both cells */ if (ci->hydro.hold || cj->hydro.hold) return 0; if (cell_locktree(ci) != 0) return 0; @@ -522,6 +599,21 @@ int task_lock(struct task *t) { cell_munlocktree(ci); return 0; } + break; + + case task_type_star_formation: + /* Lock the gas, gravity and star particles */ + if (ci->hydro.hold || ci->stars.hold || ci->grav.phold) return 0; + if (cell_locktree(ci) != 0) return 0; + if (cell_slocktree(ci) != 0) { + cell_unlocktree(ci); + return 0; + } + if (cell_glocktree(ci) != 0) { + cell_unlocktree(ci); + cell_sunlocktree(ci); + return 0; + } default: break; @@ -543,6 +635,74 @@ void task_print(const struct task *t) { t->nr_unlock_tasks, t->skip); } +/** + * @brief Get the group name of a task. + * + * This is used to group tasks with similar actions in the task dependency + * graph. + * + * @param type The #task type. + * @param subtype The #task subtype. + * @param cluster (return) The group name (should be allocated) + */ +void task_get_group_name(int type, int subtype, char *cluster) { + + if (type == task_type_grav_long_range || type == task_type_grav_mm || + type == task_type_grav_mesh) { + + strcpy(cluster, "Gravity"); + return; + } + + switch (subtype) { + case task_subtype_density: + strcpy(cluster, "Density"); + break; + case task_subtype_gradient: + strcpy(cluster, "Gradient"); + break; + case task_subtype_force: + strcpy(cluster, "Force"); + break; + case task_subtype_grav: + strcpy(cluster, "Gravity"); + break; + case task_subtype_limiter: + strcpy(cluster, "Timestep_limiter"); + break; + case task_subtype_stars_density: + strcpy(cluster, "Stars"); + break; + default: + strcpy(cluster, "None"); + break; + } +} + +/** + * @brief Generate the full name of a #task. + * + * @param type The #task type. + * @param subtype The #task type. + * @param name (return) The formatted string + */ +void task_get_full_name(int type, int subtype, char *name) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Check input */ + if (type >= task_type_count) error("Unknown task type %i", type); + + if (subtype >= task_subtype_count) + error("Unknown task subtype %i with type %s", subtype, taskID_names[type]); +#endif + + /* Full task name */ + if (subtype == task_subtype_none) + sprintf(name, "%s", taskID_names[type]); + else + sprintf(name, "%s_%s", taskID_names[type], subtaskID_names[subtype]); +} + #ifdef WITH_MPI /** * @brief Create global communicators for each of the subtasks. @@ -553,3 +713,243 @@ void task_create_mpi_comms(void) { } } #endif + +/** + * @brief dump all the tasks of all the known engines into a file for + * postprocessing. + * + * Dumps the information to a file "thread_info-stepn.dat" where n is the + * given step value, or "thread_info_MPI-stepn.dat", if we are running + * under MPI. Note if running under MPIU all the ranks are dumped into this + * one file, which has an additional field to identify the rank. + * + * @param e the #engine + * @param step the current step. + */ +void task_dump_all(struct engine *e, int step) { + +#ifdef SWIFT_DEBUG_TASKS + + /* Need this to convert ticks to seconds. */ + unsigned long long cpufreq = clocks_get_cpufreq(); + +#ifdef WITH_MPI + /* Make sure output file is empty, only on one rank. */ + char dumpfile[35]; + snprintf(dumpfile, sizeof(dumpfile), "thread_info_MPI-step%d.dat", step); + FILE *file_thread; + if (engine_rank == 0) { + file_thread = fopen(dumpfile, "w"); + fclose(file_thread); + } + MPI_Barrier(MPI_COMM_WORLD); + + for (int i = 0; i < e->nr_nodes; i++) { + + /* Rank 0 decides the index of the writing node, this happens + * one-by-one. */ + int kk = i; + MPI_Bcast(&kk, 1, MPI_INT, 0, MPI_COMM_WORLD); + + if (i == engine_rank) { + + /* Open file and position at end. */ + file_thread = fopen(dumpfile, "a"); + + /* Add some information to help with the plots and conversion of ticks to + * seconds. */ + fprintf(file_thread, " %03d 0 0 0 0 %lld %lld %lld %lld %lld 0 0 %lld\n", + engine_rank, (long long int)e->tic_step, + (long long int)e->toc_step, e->updates, e->g_updates, + e->s_updates, cpufreq); + int count = 0; + for (int l = 0; l < e->sched.nr_tasks; l++) { + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0) { + fprintf( + file_thread, " %03i %i %i %i %i %lli %lli %i %i %i %i %lli %i\n", + engine_rank, e->sched.tasks[l].rid, e->sched.tasks[l].type, + e->sched.tasks[l].subtype, (e->sched.tasks[l].cj == NULL), + (long long int)e->sched.tasks[l].tic, + (long long int)e->sched.tasks[l].toc, + (e->sched.tasks[l].ci != NULL) ? e->sched.tasks[l].ci->hydro.count + : 0, + (e->sched.tasks[l].cj != NULL) ? e->sched.tasks[l].cj->hydro.count + : 0, + (e->sched.tasks[l].ci != NULL) ? e->sched.tasks[l].ci->grav.count + : 0, + (e->sched.tasks[l].cj != NULL) ? e->sched.tasks[l].cj->grav.count + : 0, + e->sched.tasks[l].flags, e->sched.tasks[l].sid); + } + count++; + } + fclose(file_thread); + } + + /* And we wait for all to synchronize. */ + MPI_Barrier(MPI_COMM_WORLD); + } + +#else + /* Non-MPI, so just a single engine's worth of tasks to dump. */ + char dumpfile[32]; + snprintf(dumpfile, sizeof(dumpfile), "thread_info-step%d.dat", step); + FILE *file_thread; + file_thread = fopen(dumpfile, "w"); + + /* Add some information to help with the plots and conversion of ticks to + * seconds. */ + fprintf(file_thread, " %d %d %d %d %lld %lld %lld %lld %lld %d %lld\n", -2, + -1, -1, 1, (unsigned long long)e->tic_step, + (unsigned long long)e->toc_step, e->updates, e->g_updates, + e->s_updates, 0, cpufreq); + for (int l = 0; l < e->sched.nr_tasks; l++) { + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0) { + fprintf( + file_thread, " %i %i %i %i %lli %lli %i %i %i %i %i\n", + e->sched.tasks[l].rid, e->sched.tasks[l].type, + e->sched.tasks[l].subtype, (e->sched.tasks[l].cj == NULL), + (unsigned long long)e->sched.tasks[l].tic, + (unsigned long long)e->sched.tasks[l].toc, + (e->sched.tasks[l].ci == NULL) ? 0 + : e->sched.tasks[l].ci->hydro.count, + (e->sched.tasks[l].cj == NULL) ? 0 + : e->sched.tasks[l].cj->hydro.count, + (e->sched.tasks[l].ci == NULL) ? 0 : e->sched.tasks[l].ci->grav.count, + (e->sched.tasks[l].cj == NULL) ? 0 : e->sched.tasks[l].cj->grav.count, + e->sched.tasks[l].sid); + } + } + fclose(file_thread); +#endif // WITH_MPI +#endif // SWIFT_DEBUG_TASKS +} + +/** + * @brief Generate simple statistics about the times used by the tasks of + * all the engines and write these into two format, a human readable + * version for debugging and one intented for inclusion as the fixed + * costs for repartitioning. + * + * Note that when running under MPI all the tasks can be summed into this single + * file. In the fuller, human readable file, the statistics included are the + * number of task of each type/subtype followed by the minimum, maximum, mean + * and total time, in millisec and then the fixed costs value. + * + * If header is set, only the fixed costs value is written into the output + * file in a format that is suitable for inclusion in SWIFT (as + * partition_fixed_costs.h). + * + * @param dumpfile name of the file for the output. + * @param e the #engine + * @param header whether to write a header include file. + * @param allranks do the statistics over all ranks, if not just the current + * one, only used if header is false. + */ +void task_dump_stats(const char *dumpfile, struct engine *e, int header, + int allranks) { + + /* Need arrays for sum, min and max across all types and subtypes. */ + double sum[task_type_count][task_subtype_count]; + double min[task_type_count][task_subtype_count]; + double max[task_type_count][task_subtype_count]; + int count[task_type_count][task_subtype_count]; + + for (int j = 0; j < task_type_count; j++) { + for (int k = 0; k < task_subtype_count; k++) { + sum[j][k] = 0.0; + count[j][k] = 0; + min[j][k] = DBL_MAX; + max[j][k] = 0.0; + } + } + + double total[1] = {0.0}; + for (int l = 0; l < e->sched.nr_tasks; l++) { + int type = e->sched.tasks[l].type; + + /* Skip implicit tasks, tasks that didn't run and MPI send/recv as these + * are not interesting (or meaningfully measured). */ + if (!e->sched.tasks[l].implicit && e->sched.tasks[l].toc != 0 && + type != task_type_send && type != task_type_recv) { + int subtype = e->sched.tasks[l].subtype; + + double dt = e->sched.tasks[l].toc - e->sched.tasks[l].tic; + sum[type][subtype] += dt; + count[type][subtype] += 1; + if (dt < min[type][subtype]) { + min[type][subtype] = dt; + } + if (dt > max[type][subtype]) { + max[type][subtype] = dt; + } + total[0] += dt; + } + } + +#ifdef WITH_MPI + if (allranks || header) { + /* Get these from all ranks for output from rank 0. Could wrap these into a + * single operation. */ + size_t size = task_type_count * task_subtype_count; + int res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : sum), sum, size, + MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task sums"); + + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : count), count, size, + MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task counts"); + + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : min), min, size, + MPI_DOUBLE, MPI_MIN, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task minima"); + + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : max), max, size, + MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task maxima"); + + res = MPI_Reduce((engine_rank == 0 ? MPI_IN_PLACE : total), total, 1, + MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + if (res != MPI_SUCCESS) mpi_error(res, "Failed to reduce task total time"); + } + + if (!allranks || (engine_rank == 0 && (allranks || header))) { +#endif + + FILE *dfile = fopen(dumpfile, "w"); + if (header) { + fprintf(dfile, "/* use as src/partition_fixed_costs.h */\n"); + fprintf(dfile, "#define HAVE_FIXED_COSTS 1\n"); + } else { + fprintf(dfile, "# task ntasks min max sum mean percent fixed_cost\n"); + } + + for (int j = 0; j < task_type_count; j++) { + const char *taskID = taskID_names[j]; + for (int k = 0; k < task_subtype_count; k++) { + if (sum[j][k] > 0.0) { + double mean = sum[j][k] / (double)count[j][k]; + double perc = 100.0 * sum[j][k] / total[0]; + + /* Fixed cost is in .1ns as we want to compare between runs in + * some absolute units. */ + int fixed_cost = (int)(clocks_from_ticks(mean) * 10000.f); + if (header) { + fprintf(dfile, "repartition_costs[%d][%d] = %10d; /* %s/%s */\n", j, + k, fixed_cost, taskID, subtaskID_names[k]); + } else { + fprintf(dfile, + "%15s/%-10s %10d %14.4f %14.4f %14.4f %14.4f %14.4f %10d\n", + taskID, subtaskID_names[k], count[j][k], + clocks_from_ticks(min[j][k]), clocks_from_ticks(max[j][k]), + clocks_from_ticks(sum[j][k]), clocks_from_ticks(mean), perc, + fixed_cost); + } + } + } + } + fclose(dfile); +#ifdef WITH_MPI + } +#endif +} diff --git a/src/task.h b/src/task.h index 932b6c10882108e60693e6ee4ddef05809d7fd09..c75db7e0d139d1c3d28b2d78af898ce5fd6f008d 100644 --- a/src/task.h +++ b/src/task.h @@ -58,6 +58,7 @@ enum task_types { task_type_kick1, task_type_kick2, task_type_timestep, + task_type_timestep_limiter, task_type_send, task_type_recv, task_type_grav_long_range, @@ -67,11 +68,11 @@ enum task_types { task_type_grav_mesh, task_type_cooling, task_type_star_formation, - task_type_sourceterms, task_type_logger, task_type_stars_ghost_in, task_type_stars_ghost, task_type_stars_ghost_out, + task_type_stars_sort, task_type_fof_self, task_type_fof_pair, task_type_count @@ -85,6 +86,7 @@ enum task_subtypes { task_subtype_density, task_subtype_gradient, task_subtype_force, + task_subtype_limiter, task_subtype_grav, task_subtype_external_grav, task_subtype_tend, @@ -94,6 +96,7 @@ enum task_subtypes { task_subtype_multipole, task_subtype_spart, task_subtype_stars_density, + task_subtype_stars_feedback, task_subtype_count } __attribute__((packed)); @@ -157,11 +160,6 @@ struct task { /*! Weight of the task */ float weight; -#if defined(WITH_MPI) && (defined(HAVE_METIS) || defined(HAVE_PARMETIS)) - /*! Individual cost estimate for this task. */ - float cost; -#endif - /*! Number of tasks unlocked by this one */ short int nr_unlock_tasks; @@ -186,10 +184,10 @@ struct task { /*! Information about the direction of the pair task */ short int sid; +#endif /*! Start and end time of this task */ ticks tic, toc; -#endif #ifdef SWIFT_DEBUG_CHECKS /* When was this task last run? */ @@ -204,6 +202,12 @@ float task_overlap(const struct task *ta, const struct task *tb); int task_lock(struct task *t); void task_do_rewait(struct task *t); void task_print(const struct task *t); +void task_dump_all(struct engine *e, int step); +void task_dump_stats(const char *dumpfile, struct engine *e, int header, + int allranks); +void task_get_full_name(int type, int subtype, char *name); +void task_get_group_name(int type, int subtype, char *cluster); + #ifdef WITH_MPI void task_create_mpi_comms(void); #endif diff --git a/src/timeline.h b/src/timeline.h index 38727def50b5a81c073ab23375f0c548ca096b66..a2bb8da6e8c5c92288541c206b453af141bf094e 100644 --- a/src/timeline.h +++ b/src/timeline.h @@ -27,9 +27,10 @@ #include "intrinsics.h" #include <math.h> +#include <stdint.h> typedef long long integertime_t; -typedef char timebin_t; +typedef int8_t timebin_t; /*! The number of time bins */ #define num_time_bins 56 @@ -40,6 +41,9 @@ typedef char timebin_t; /*! Fictious time-bin to hold inhibited particles */ #define time_bin_inhibited (num_time_bins + 2) +/*! Fictious time-bin to hold particles not yet created */ +#define time_bin_not_created (num_time_bins + 3) + /*! Fictitious time-bin for particles not awaken */ #define time_bin_not_awake (0) diff --git a/src/timers.c b/src/timers.c index 9c1f03239b7714af5affe9c77cc111e173490abd..73a47b88947c9731b779416436cddaeb9828caac 100644 --- a/src/timers.c +++ b/src/timers.c @@ -61,7 +61,6 @@ const char* timers_names[timer_count] = { "dograv_mesh", "dograv_top_level", "dograv_long_range", - "dosource", "dosub_self_density", "dosub_self_gradient", "dosub_self_force", @@ -87,14 +86,9 @@ const char* timers_names[timer_count] = { "locktree", "runners", "step", - "doself_stars_density", - "dopair_stars_density", "do_stars_ghost", - "doself_subset_stars_density", - "dopair_subset_stars_density", - "dosubpair_stars_density", - "dosub_self_stars_density", "logger", + "do_stars_sort", "fof_self", "fof_pair", }; diff --git a/src/timers.h b/src/timers.h index e1da3f9004d5ebb1cd28822df6a7fb0b5dd5c8a2..068f68d115746e9d5c3f6823231f48ea90334480 100644 --- a/src/timers.h +++ b/src/timers.h @@ -62,7 +62,6 @@ enum { timer_dograv_mesh, timer_dograv_top_level, timer_dograv_long_range, - timer_dosource, timer_dosub_self_density, timer_dosub_self_gradient, timer_dosub_self_force, @@ -88,14 +87,9 @@ enum { timer_locktree, timer_runners, timer_step, - timer_doself_stars_density, - timer_dopair_stars_density, timer_dostars_ghost, - timer_doself_subset_stars_density, - timer_dopair_subset_stars_density, - timer_dosubpair_stars_density, - timer_dosub_self_stars_density, timer_logger, + timer_do_stars_sort, timer_fof_self, timer_fof_pair, timer_count, diff --git a/src/timestep_limiter.h b/src/timestep_limiter.h new file mode 100644 index 0000000000000000000000000000000000000000..db5e044132370c273f66aefd8e2d116642b28a73 --- /dev/null +++ b/src/timestep_limiter.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@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 SWIFT_TIMESTEP_LIMITER_H +#define SWIFT_TIMESTEP_LIMITER_H + +/* Config parameters. */ +#include "../config.h" + +/** + * @brief Wakes up a particle by rewinding it's kick1 back in time and applying + * a new one such that the particle becomes active again in the next time-step. + * + * @param p The #part to update. + * @param xp Its #xpart companion. + * @param e The #engine (to extract time-line information). + */ +__attribute__((always_inline)) INLINE static integertime_t timestep_limit_part( + struct part *restrict p, struct xpart *restrict xp, + const struct engine *e) { + + const struct cosmology *cosmo = e->cosmology; + const int with_cosmology = e->policy & engine_policy_cosmology; + const double time_base = e->time_base; + + integertime_t old_ti_beg, old_ti_end; + timebin_t old_time_bin; + + /* Let's see when this particle started and used to end */ + if (p->wakeup == time_bin_awake) { + + /* Normal case */ + old_ti_beg = get_integer_time_begin(e->ti_current, p->time_bin); + old_ti_end = get_integer_time_end(e->ti_current, p->time_bin); + old_time_bin = p->time_bin; + } else { + + /* Particle that was limited in the previous step already */ + old_ti_beg = get_integer_time_begin(e->ti_current, -p->wakeup); + old_ti_end = get_integer_time_end(e->ti_current, p->time_bin); + old_time_bin = -p->wakeup; + } + + const integertime_t old_dti = old_ti_end - old_ti_beg; + + /* The new fake time-step the particle will be on */ + const integertime_t new_fake_ti_step = + get_integer_timestep(e->min_active_bin); + + /* The actual time-step size this particle will use */ + const integertime_t new_ti_beg = old_ti_beg; + const integertime_t new_ti_end = e->ti_current + new_fake_ti_step; + const integertime_t new_dti = new_ti_end - new_ti_beg; + +#ifdef SWIFT_DEBUG_CHECKS + /* Some basic safety checks */ + if (old_ti_beg >= e->ti_current) + error( + "Incorrect value for old time-step beginning ti_current=%lld, " + "old_ti_beg=%lld", + e->ti_current, old_ti_beg); + + if (old_ti_end <= e->ti_current) + error( + "Incorrect value for old time-step end ti_current=%lld, " + "old_ti_end=%lld", + e->ti_current, old_ti_end); + + if (new_ti_end > old_ti_end) error("New end of time-step after the old one"); + + if (new_dti > old_dti) error("New time-step larger than old one"); + + if (new_fake_ti_step == 0) error("Wakeup call too early"); +#endif + + double dt_kick_grav = 0., dt_kick_hydro = 0., dt_kick_therm = 0., + dt_kick_corr = 0.; + + /* Now we need to reverse the kick1... (the dt are negative here) */ + if (with_cosmology) { + dt_kick_hydro = -cosmology_get_hydro_kick_factor(cosmo, old_ti_beg, + old_ti_beg + old_dti / 2); + dt_kick_grav = -cosmology_get_grav_kick_factor(cosmo, old_ti_beg, + old_ti_beg + old_dti / 2); + dt_kick_therm = -cosmology_get_therm_kick_factor(cosmo, old_ti_beg, + old_ti_beg + old_dti / 2); + dt_kick_corr = -cosmology_get_corr_kick_factor(cosmo, old_ti_beg, + old_ti_beg + old_dti / 2); + } else { + dt_kick_hydro = -(old_dti / 2) * time_base; + dt_kick_grav = -(old_dti / 2) * time_base; + dt_kick_therm = -(old_dti / 2) * time_base; + dt_kick_corr = -(old_dti / 2) * time_base; + } + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, dt_kick_corr, + e->cosmology, e->hydro_properties, e->entropy_floor, + old_ti_beg + old_dti / 2, old_ti_beg); + + /* ...and apply the new one (dt is positiive) */ + if (with_cosmology) { + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, new_ti_beg, + new_ti_beg + new_dti / 2); + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, new_ti_beg, + new_ti_beg + new_dti / 2); + dt_kick_therm = cosmology_get_therm_kick_factor(cosmo, new_ti_beg, + new_ti_beg + new_dti / 2); + dt_kick_corr = cosmology_get_corr_kick_factor(cosmo, new_ti_beg, + new_ti_beg + new_dti / 2); + } else { + dt_kick_hydro = (new_dti / 2) * time_base; + dt_kick_grav = (new_dti / 2) * time_base; + dt_kick_therm = (new_dti / 2) * time_base; + dt_kick_corr = (new_dti / 2) * time_base; + } + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, dt_kick_corr, + e->cosmology, e->hydro_properties, e->entropy_floor, new_ti_beg, + new_ti_beg + new_dti / 2); + + /* Remember the old time-bin */ + p->wakeup = old_time_bin; + + /* Update the time bin of this particle */ + p->time_bin = e->min_active_bin; + + return new_fake_ti_step; +} + +#endif /* SWIFT_TIMESTEP_LIMITER_H */ diff --git a/src/tools.c b/src/tools.c index ff33afbca3fdc97ede61ff09998d8dc27bf0154b..45b04e35bb4713a5b73158839e642d0796aa94a7 100644 --- a/src/tools.c +++ b/src/tools.c @@ -217,7 +217,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { } /* Hit or miss? */ - if (r2 < hig2) { + if (r2 < hig2 && !part_is_inhibited(pj, e)) { /* Interact */ runner_iact_nonsym_density(r2, dx, hi, pj->h, pi, pj, a, H); @@ -249,7 +249,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { } /* Hit or miss? */ - if (r2 < hjg2) { + if (r2 < hjg2 && !part_is_inhibited(pi, e)) { /* Interact */ runner_iact_nonsym_density(r2, dx, hj, pi->h, pj, pi, a, H); @@ -259,6 +259,85 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { } } +#ifdef EXTRA_HYDRO_LOOP +void pairs_all_gradient(struct runner *r, struct cell *ci, struct cell *cj) { + + float r2, hi, hj, hig2, hjg2, dx[3]; + struct part *pi, *pj; + const double dim[3] = {r->e->s->dim[0], r->e->s->dim[1], r->e->s->dim[2]}; + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const float a = cosmo->a; + const float H = cosmo->H; + + /* Implements a double-for loop and checks every interaction */ + for (int i = 0; i < ci->hydro.count; ++i) { + + pi = &ci->hydro.parts[i]; + hi = pi->h; + hig2 = hi * hi * kernel_gamma2; + + /* Skip inactive particles. */ + if (!part_is_active(pi, e)) continue; + + for (int j = 0; j < cj->hydro.count; ++j) { + + pj = &cj->hydro.parts[j]; + hj = pj->h; + hjg2 = hj * hj * kernel_gamma2; + + /* Pairwise distance */ + r2 = 0.0f; + for (int k = 0; k < 3; k++) { + dx[k] = ci->hydro.parts[i].x[k] - cj->hydro.parts[j].x[k]; + dx[k] = nearest(dx[k], dim[k]); + r2 += dx[k] * dx[k]; + } + + /* Hit or miss? */ + if (r2 < hig2 && !part_is_inhibited(pj, e)) { + + /* Interact */ + runner_iact_nonsym_gradient(r2, dx, hi, hj, pi, pj, a, H); + } + } + } + + /* Reverse double-for loop and checks every interaction */ + for (int j = 0; j < cj->hydro.count; ++j) { + + pj = &cj->hydro.parts[j]; + hj = pj->h; + hjg2 = hj * hj * kernel_gamma2; + + /* Skip inactive particles. */ + if (!part_is_active(pj, e)) continue; + + for (int i = 0; i < ci->hydro.count; ++i) { + + pi = &ci->hydro.parts[i]; + hi = pi->h; + hig2 = hi * hi * kernel_gamma2; + + /* Pairwise distance */ + r2 = 0.0f; + for (int k = 0; k < 3; k++) { + dx[k] = cj->hydro.parts[j].x[k] - ci->hydro.parts[i].x[k]; + dx[k] = nearest(dx[k], dim[k]); + r2 += dx[k] * dx[k]; + } + + /* Hit or miss? */ + if (r2 < hjg2 && !part_is_inhibited(pi, e)) { + + /* Interact */ + runner_iact_nonsym_gradient(r2, dx, hj, pi->h, pj, pi, a, H); + } + } + } +} +#endif /* EXTRA_HDYRO_LOOP */ + void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) { float r2, hi, hj, hig2, hjg2, dx[3]; @@ -438,7 +517,7 @@ void self_all_density(struct runner *r, struct cell *ci) { } /* Hit or miss? */ - if (r2 < hig2 && part_is_active(pi, e)) { + if (r2 < hig2 && part_is_active(pi, e) && !part_is_inhibited(pj, e)) { /* Interact */ runner_iact_nonsym_density(r2, dxi, hi, hj, pi, pj, a, H); @@ -446,7 +525,7 @@ void self_all_density(struct runner *r, struct cell *ci) { } /* Hit or miss? */ - if (r2 < hjg2 && part_is_active(pj, e)) { + if (r2 < hjg2 && part_is_active(pj, e) && !part_is_inhibited(pi, e)) { dxi[0] = -dxi[0]; dxi[1] = -dxi[1]; @@ -460,6 +539,59 @@ void self_all_density(struct runner *r, struct cell *ci) { } } +#ifdef EXTRA_HYDRO_LOOP +void self_all_gradient(struct runner *r, struct cell *ci) { + float r2, hi, hj, hig2, hjg2, dxi[3]; //, dxj[3]; + struct part *pi, *pj; + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const float a = cosmo->a; + const float H = cosmo->H; + + /* Implements a double-for loop and checks every interaction */ + for (int i = 0; i < ci->hydro.count; ++i) { + + pi = &ci->hydro.parts[i]; + hi = pi->h; + hig2 = hi * hi * kernel_gamma2; + + for (int j = i + 1; j < ci->hydro.count; ++j) { + + pj = &ci->hydro.parts[j]; + hj = pj->h; + hjg2 = hj * hj * kernel_gamma2; + + if (pi == pj) continue; + + /* Pairwise distance */ + r2 = 0.0f; + for (int k = 0; k < 3; k++) { + dxi[k] = ci->hydro.parts[i].x[k] - ci->hydro.parts[j].x[k]; + r2 += dxi[k] * dxi[k]; + } + + /* Hit or miss? */ + if (r2 < hig2 && part_is_active(pi, e) && !part_is_inhibited(pj, e)) { + + /* Interact */ + runner_iact_nonsym_gradient(r2, dxi, hi, hj, pi, pj, a, H); + } + + /* Hit or miss? */ + if (r2 < hjg2 && part_is_active(pj, e) && !part_is_inhibited(pi, e)) { + + dxi[0] = -dxi[0]; + dxi[1] = -dxi[1]; + dxi[2] = -dxi[2]; + + /* Interact */ + runner_iact_nonsym_gradient(r2, dxi, hj, hi, pj, pi, a, H); + } + } + } +} +#endif /* EXTRA_HYDRO_LOOP */ + void self_all_force(struct runner *r, struct cell *ci) { float r2, hi, hj, hig2, hjg2, dxi[3]; //, dxj[3]; struct part *pi, *pj; @@ -714,7 +846,7 @@ int compare_values(double a, double b, double threshold, double *absDiff, * * @return 1 if difference found, 0 otherwise */ -int compare_particles(struct part a, struct part b, double threshold) { +int compare_particles(struct part *a, struct part *b, double threshold) { #ifdef GADGET2_SPH @@ -722,117 +854,117 @@ int compare_particles(struct part a, struct part b, double threshold) { double absDiff = 0.0, absSum = 0.0, relDiff = 0.0; for (int k = 0; k < 3; k++) { - if (compare_values(a.x[k], b.x[k], threshold, &absDiff, &absSum, + if (compare_values(a->x[k], b->x[k], threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for x[%d] of " "particle %lld.", - relDiff, threshold, k, a.id); - message("a = %e, b = %e", a.x[k], b.x[k]); + relDiff, threshold, k, a->id); + message("a = %e, b = %e", a->x[k], b->x[k]); result = 1; } } for (int k = 0; k < 3; k++) { - if (compare_values(a.v[k], b.v[k], threshold, &absDiff, &absSum, + if (compare_values(a->v[k], b->v[k], threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for v[%d] of " "particle %lld.", - relDiff, threshold, k, a.id); - message("a = %e, b = %e", a.v[k], b.v[k]); + relDiff, threshold, k, a->id); + message("a = %e, b = %e", a->v[k], b->v[k]); result = 1; } } for (int k = 0; k < 3; k++) { - if (compare_values(a.a_hydro[k], b.a_hydro[k], threshold, &absDiff, &absSum, - &relDiff)) { + if (compare_values(a->a_hydro[k], b->a_hydro[k], threshold, &absDiff, + &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for a_hydro[%d] " "of particle %lld.", - relDiff, threshold, k, a.id); - message("a = %e, b = %e", a.a_hydro[k], b.a_hydro[k]); + relDiff, threshold, k, a->id); + message("a = %e, b = %e", a->a_hydro[k], b->a_hydro[k]); result = 1; } } - if (compare_values(a.rho, b.rho, threshold, &absDiff, &absSum, &relDiff)) { + if (compare_values(a->rho, b->rho, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for rho of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.rho, b.rho); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->rho, b->rho); result = 1; } - if (compare_values(a.density.rho_dh, b.density.rho_dh, threshold, &absDiff, + if (compare_values(a->density.rho_dh, b->density.rho_dh, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for rho_dh of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.density.rho_dh, b.density.rho_dh); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->density.rho_dh, b->density.rho_dh); result = 1; } - if (compare_values(a.density.wcount, b.density.wcount, threshold, &absDiff, + if (compare_values(a->density.wcount, b->density.wcount, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for wcount of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.density.wcount, b.density.wcount); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->density.wcount, b->density.wcount); result = 1; } - if (compare_values(a.density.wcount_dh, b.density.wcount_dh, threshold, + if (compare_values(a->density.wcount_dh, b->density.wcount_dh, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for wcount_dh of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.density.wcount_dh, b.density.wcount_dh); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->density.wcount_dh, b->density.wcount_dh); result = 1; } - if (compare_values(a.force.h_dt, b.force.h_dt, threshold, &absDiff, &absSum, + if (compare_values(a->force.h_dt, b->force.h_dt, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for h_dt of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.force.h_dt, b.force.h_dt); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->force.h_dt, b->force.h_dt); result = 1; } - if (compare_values(a.force.v_sig, b.force.v_sig, threshold, &absDiff, &absSum, - &relDiff)) { + if (compare_values(a->force.v_sig, b->force.v_sig, threshold, &absDiff, + &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for v_sig of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.force.v_sig, b.force.v_sig); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->force.v_sig, b->force.v_sig); result = 1; } - if (compare_values(a.entropy_dt, b.entropy_dt, threshold, &absDiff, &absSum, + if (compare_values(a->entropy_dt, b->entropy_dt, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for entropy_dt of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.entropy_dt, b.entropy_dt); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->entropy_dt, b->entropy_dt); result = 1; } - if (compare_values(a.density.div_v, b.density.div_v, threshold, &absDiff, + if (compare_values(a->density.div_v, b->density.div_v, threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for div_v of " "particle %lld.", - relDiff, threshold, a.id); - message("a = %e, b = %e", a.density.div_v, b.density.div_v); + relDiff, threshold, a->id); + message("a = %e, b = %e", a->density.div_v, b->density.div_v); result = 1; } for (int k = 0; k < 3; k++) { - if (compare_values(a.density.rot_v[k], b.density.rot_v[k], threshold, + if (compare_values(a->density.rot_v[k], b->density.rot_v[k], threshold, &absDiff, &absSum, &relDiff)) { message( "Relative difference (%e) larger than tolerance (%e) for rot_v[%d] " "of particle %lld.", - relDiff, threshold, k, a.id); - message("a = %e, b = %e", a.density.rot_v[k], b.density.rot_v[k]); + relDiff, threshold, k, a->id); + message("a = %e, b = %e", a->density.rot_v[k], b->density.rot_v[k]); result = 1; } } diff --git a/src/tools.h b/src/tools.h index 5ec2ca42810e1cb733e1aa674bd1c94ac5c569bd..a34904bcbb7af1ce15408a376f957f0f72cd327c 100644 --- a/src/tools.h +++ b/src/tools.h @@ -38,6 +38,8 @@ void pairs_single_density(double *dim, long long int pid, void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj); void self_all_density(struct runner *r, struct cell *ci); +void pairs_all_gradient(struct runner *r, struct cell *ci, struct cell *cj); +void self_all_gradient(struct runner *r, struct cell *ci); void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj); void self_all_force(struct runner *r, struct cell *ci); void pairs_all_stars_density(struct runner *r, struct cell *ci, @@ -54,7 +56,7 @@ void gravity_n2(struct gpart *gparts, const int gcount, const struct gravity_props *gravity_properties, float rlr); int compare_values(double a, double b, double threshold, double *absDiff, double *absSum, double *relDiff); -int compare_particles(struct part a, struct part b, double threshold); +int compare_particles(struct part *a, struct part *b, double threshold); long get_maxrss(void); diff --git a/src/tracers.h b/src/tracers.h new file mode 100644 index 0000000000000000000000000000000000000000..888d30af1172e1b8b639f8826b68067874a4f63a --- /dev/null +++ b/src/tracers.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_H +#define SWIFT_TRACERS_H + +/** + * @file src/tracers.h + * @brief Branches between the different particle data tracers + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(TRACERS_NONE) +#include "./tracers/none/tracers.h" +#elif defined(TRACERS_EAGLE) +#include "./tracers/EAGLE/tracers.h" +#else +#error "Invalid choice of tracers." +#endif + +#endif /* SWIFT_TRACERS_H */ diff --git a/src/tracers/EAGLE/tracers.h b/src/tracers/EAGLE/tracers.h new file mode 100644 index 0000000000000000000000000000000000000000..706a8ad9916bec287ad7aad4853b063d44cc37da --- /dev/null +++ b/src/tracers/EAGLE/tracers.h @@ -0,0 +1,140 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_EAGLE_H +#define SWIFT_TRACERS_EAGLE_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "cooling.h" +#include "part.h" +#include "tracers_struct.h" + +/** + * @brief Update the particle tracers just after it has been initialised at the + * start of a step. + * + * Nothing to do here in the EAGLE model. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param time The current time. + */ +static INLINE void tracers_after_init( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) {} + +/** + * @brief Update the particle tracers just after it has been drifted. + * + * Nothing to do here in the EAGLE model. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param time The current time. + */ +static INLINE void tracers_after_drift( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) {} + +/** + * @brief Update the particle tracers just after its time-step has been + * computed. + * + * In EAGLE we record the highest temperature reached. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param with_cosmology Are we running a cosmological simulation? + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param time The current time. + */ +static INLINE void tracers_after_timestep( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) { + + /* Current temperature */ + const float temperature = cooling_get_temperature(phys_const, hydro_props, us, + cosmo, cooling, p, xp); + + /* New record? */ + if (temperature > xp->tracers_data.maximum_temperature) { + + xp->tracers_data.maximum_temperature = temperature; + + if (with_cosmology) { + xp->tracers_data.maximum_temperature_scale_factor = cosmo->a; + } else { + xp->tracers_data.maximum_temperature_time = time; + } + } +} + +/** + * @brief Update the particle tracers just after its time-step has been + * computed. + * + * Set the maximal temperature to a valid initial state + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + */ +static INLINE void tracers_first_init_xpart( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling) { + + xp->tracers_data.maximum_temperature = -1.f; + xp->tracers_data.maximum_temperature_time = -1.f; +} + +#endif /* SWIFT_TRACERS_EAGLE_H */ diff --git a/src/tracers/EAGLE/tracers_io.h b/src/tracers/EAGLE/tracers_io.h new file mode 100644 index 0000000000000000000000000000000000000000..0b0e372ffc35ec5729affd4f6a86e358c914ca6d --- /dev/null +++ b/src/tracers/EAGLE/tracers_io.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_EAGLE_IO_H +#define SWIFT_TRACERS_EAGLE_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "io_properties.h" +#include "tracers.h" + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of tracers to the file. + * + * @param h_grp The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void tracers_write_flavour( + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Tracers", "EAGLE"); +} +#endif + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended data particle array. + * @param list The list of i/o properties to write. + * @param with_cosmology Are we running with cosmology switched on? + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int tracers_write_particles( + const struct part* parts, const struct xpart* xparts, struct io_props* list, + const int with_cosmology) { + + list[0] = io_make_output_field("Maximal Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, xparts, + tracers_data.maximum_temperature); + + if (with_cosmology) { + list[1] = io_make_output_field( + "Maximal Temperature scale-factor", FLOAT, 1, UNIT_CONV_NO_UNITS, + xparts, tracers_data.maximum_temperature_scale_factor); + + } else { + + list[1] = io_make_output_field("Maximal Temperature time", FLOAT, 1, + UNIT_CONV_NO_UNITS, xparts, + tracers_data.maximum_temperature_time); + } + + return 2; +} + +__attribute__((always_inline)) INLINE static int tracers_write_sparticles( + const struct spart* sparts, struct io_props* list, + const int with_cosmology) { + + list[0] = io_make_output_field("Maximal Temperature", FLOAT, 1, + UNIT_CONV_TEMPERATURE, sparts, + tracers_data.maximum_temperature); + + if (with_cosmology) { + list[1] = io_make_output_field( + "Maximal Temperature scale-factor", FLOAT, 1, UNIT_CONV_NO_UNITS, + sparts, tracers_data.maximum_temperature_scale_factor); + + } else { + + list[1] = io_make_output_field("MaxTemperature time", FLOAT, 1, + UNIT_CONV_NO_UNITS, sparts, + tracers_data.maximum_temperature_time); + } + + return 2; +} +#endif /* SWIFT_TRACERS_EAGLE_IO_H */ diff --git a/src/tracers/EAGLE/tracers_struct.h b/src/tracers/EAGLE/tracers_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..d893c85bcb65b625743f3cec603560d65efa472d --- /dev/null +++ b/src/tracers/EAGLE/tracers_struct.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_STRUCT_EAGLE_H +#define SWIFT_TRACERS_STRUCT_EAGLE_H + +/** + * @brief Properties of the tracers stored in the extended particle data. + */ +struct tracers_xpart_data { + + /*! Maximum temperature achieved by this particle */ + float maximum_temperature; + + /*! Anonymous union for the cosmological non-cosmological runs distinction */ + union { + + /*! Scale-factor at which the maximal temperature was reached */ + float maximum_temperature_scale_factor; + + /*! Time at which the maximal temperature was reached */ + float maximum_temperature_time; + }; +}; + +#endif /* SWIFT_TRACERS_STRUCT_EAGLE_H */ diff --git a/src/tracers/none/tracers.h b/src/tracers/none/tracers.h new file mode 100644 index 0000000000000000000000000000000000000000..4cf2fb4ad4380139a392d5d76a0f78162aa6eac9 --- /dev/null +++ b/src/tracers/none/tracers.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_NONE_H +#define SWIFT_TRACERS_NONE_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "cooling.h" +#include "part.h" +#include "tracers_struct.h" + +/** + * @brief Update the particle tracers just after it has been initialised at the + * start of a step. + * + * Nothing to do here in the EAGLE model. + * + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + */ +static INLINE void tracers_after_init( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) {} + +/** + * @brief Update the particle tracers just after it has been drifted. + * + * Nothing to do here in the EAGLE model. + * + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + */ +static INLINE void tracers_after_drift( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) {} + +/** + * @brief Update the particle tracers just after its time-step has been + * computed. + * + * Nothing to do here in the EAGLE model. + * + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + */ +static INLINE void tracers_after_timestep( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const int with_cosmology, + const struct cosmology *cosmo, const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling, const double time) {} + +/** + * @brief Update the particle tracers just after its time-step has been + * computed. + * + * Nothing to do here in the EAGLE model. + * + * @param us The internal system of units. + * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. + * @param hydro_props the hydro_props struct + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data (containing the tracers + * struct). + */ +static INLINE void tracers_first_init_xpart( + const struct part *p, struct xpart *xp, const struct unit_system *us, + const struct phys_const *phys_const, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, + const struct cooling_function_data *cooling) {} + +#endif /* SWIFT_TRACERS_NONE_H */ diff --git a/src/tracers/none/tracers_io.h b/src/tracers/none/tracers_io.h new file mode 100644 index 0000000000000000000000000000000000000000..b58e5f74aea211214b45c07668c03b461c088a99 --- /dev/null +++ b/src/tracers/none/tracers_io.h @@ -0,0 +1,66 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_NONE_IO_H +#define SWIFT_TRACERS_NONE_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "io_properties.h" +#include "tracers.h" + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of tracers to the file. + * + * @param h_grp The HDF5 group in which to write + * @param tracers The #tracers_function_data + */ +__attribute__((always_inline)) INLINE static void tracers_write_flavour( + hid_t h_grp) { + + io_write_attribute_s(h_grp, "Tracers", "none"); +} +#endif + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param xparts The extended data particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +__attribute__((always_inline)) INLINE static int tracers_write_particles( + const struct part* parts, const struct xpart* xparts, struct io_props* list, + const int with_cosmology) { + + return 0; +} + +__attribute__((always_inline)) INLINE static int tracers_write_sparticles( + const struct spart* sparts, struct io_props* list, + const int with_cosmology) { + + return 0; +} +#endif /* SWIFT_TRACERS_NONE_IO_H */ diff --git a/src/sourceterms_struct.h b/src/tracers/none/tracers_struct.h similarity index 74% rename from src/sourceterms_struct.h rename to src/tracers/none/tracers_struct.h index b3c38986db52d72df825fda97b36c985dff922b6..b13539917eef888ade3f9056e245ebb7f963f4d1 100644 --- a/src/sourceterms_struct.h +++ b/src/tracers/none/tracers_struct.h @@ -1,6 +1,6 @@ /******************************************************************************* * This file is part of SWIFT. - * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 @@ -16,11 +16,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ -#ifndef SWIFT_SOURCETERMS_STRUCT_H -#define SWIFT_SOURCETERMS_STRUCT_H -#include "./const.h" -#ifdef SOURCETERMS_SN_FEEDBACK -#include "sourceterms/sn_feedback/sn_feedback_struct.h" -#endif +#ifndef SWIFT_TRACERS_STRUCT_NONE_H +#define SWIFT_TRACERS_STRUCT_NONE_H -#endif /* SWIFT_SOURCETERMS_STRUCT_H */ +/** + * @brief Properties of the tracers stored in the extended particle data. + */ +struct tracers_xpart_data {}; + +#endif /* SWIFT_TRACERS_STRUCT_NONE_H */ diff --git a/src/tracers_io.h b/src/tracers_io.h new file mode 100644 index 0000000000000000000000000000000000000000..87ca8bb1ffa22017aa825c2760fe42eca4c2888b --- /dev/null +++ b/src/tracers_io.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_IO_H +#define SWIFT_TRACERS_IO_H + +/** + * @file src/tracers_io.h + * @brief Branches between the different particle data tracers + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(TRACERS_NONE) +#include "./tracers/none/tracers_io.h" +#elif defined(TRACERS_EAGLE) +#include "./tracers/EAGLE/tracers_io.h" +#else +#error "Invalid choice of tracers." +#endif + +#endif /* SWIFT_TRACERS_IO_H */ diff --git a/src/tracers_struct.h b/src/tracers_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..48af73c2e577f7df3b0fed291b1517692f6437bd --- /dev/null +++ b/src/tracers_struct.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 SWIFT_TRACERS_STRUCT_H +#define SWIFT_TRACERS_STRUCT_H + +/** + * @file src/tracers_struct.h + * @brief Branches between the different particle data tracers + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(TRACERS_NONE) +#include "./tracers/none/tracers_struct.h" +#elif defined(TRACERS_EAGLE) +#include "./tracers/EAGLE/tracers_struct.h" +#else +#error "Invalid choice of tracers." +#endif + +#endif /* SWIFT_TRACERS_STRUCT_H */ diff --git a/src/units.c b/src/units.c index 6a308a9a9203d25d348e24b1d877a79346fb0a06..1194735aafd80d51897e4c4cb3e4b0976478145a 100644 --- a/src/units.c +++ b/src/units.c @@ -244,6 +244,7 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], break; case UNIT_CONV_FREQUENCY: + case UNIT_CONV_SSFR: baseUnitsExp[UNIT_TIME] = -1.f; break; @@ -253,6 +254,7 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], break; case UNIT_CONV_SPEED: + case UNIT_CONV_VELOCITY: baseUnitsExp[UNIT_LENGTH] = 1.f; baseUnitsExp[UNIT_TIME] = -1.f; break; @@ -370,9 +372,15 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], break; case UNIT_CONV_INV_VOLUME: + case UNIT_CONV_NUMBER_DENSITY: baseUnitsExp[UNIT_LENGTH] = -3.f; break; + case UNIT_CONV_SFR: + baseUnitsExp[UNIT_MASS] = 1.f; + baseUnitsExp[UNIT_TIME] = -1.f; + break; + default: error("Invalid choice of pre-defined units"); break; diff --git a/src/units.h b/src/units.h index 08b738c5303db8b40dfbe51799d67da8df3936ce..62669425e52c4e39800330a4150259856d8fc0bb 100644 --- a/src/units.h +++ b/src/units.h @@ -71,7 +71,9 @@ enum unit_conversion_factor { UNIT_CONV_LENGTH, UNIT_CONV_TIME, UNIT_CONV_DENSITY, + UNIT_CONV_NUMBER_DENSITY, UNIT_CONV_SPEED, + UNIT_CONV_VELOCITY, UNIT_CONV_ACCELERATION, UNIT_CONV_POTENTIAL, UNIT_CONV_FORCE, @@ -92,7 +94,9 @@ enum unit_conversion_factor { UNIT_CONV_MAGNETIC_INDUCTANCE, UNIT_CONV_TEMPERATURE, UNIT_CONV_VOLUME, - UNIT_CONV_INV_VOLUME + UNIT_CONV_INV_VOLUME, + UNIT_CONV_SFR, + UNIT_CONV_SSFR }; void units_init_cgs(struct unit_system*); diff --git a/src/velociraptor_dummy.c b/src/velociraptor_dummy.c index 8f14a3230d341993122f09f2bccf3d8232550fd9..36cb65bfbe6931464f33d7e4b641f8882fdf65d0 100644 --- a/src/velociraptor_dummy.c +++ b/src/velociraptor_dummy.c @@ -20,9 +20,6 @@ /* Config parameters. */ #include "../config.h" -/* Some standard headers. */ -#include <stddef.h> - /* Local includes. */ #include "error.h" #include "swift_velociraptor_part.h" @@ -36,19 +33,41 @@ struct unitinfo {}; struct cell_loc {}; struct siminfo {}; +/* int InitVelociraptor(char *config_name, char *output_name, struct cosmoinfo cosmo_info, struct unitinfo unit_info, - struct siminfo sim_info) { + struct siminfo sim_info, const int numthreads) { error("This is only a dummy. Call the real one!"); return 0; } + int InvokeVelociraptor(const size_t num_gravity_parts, - const size_t num_hydro_parts, + const size_t num_hydro_parts, const int snapnum, struct swift_vel_part *swift_parts, - const int *cell_node_ids, char *output_name) { + const int *cell_node_ids, char *output_name, + const int numthreads) { + + error("This is only a dummy. Call the real one!"); + return 0; +} +*/ +int InitVelociraptor(char *config_name, struct unitinfo unit_info, + struct siminfo sim_info, const int numthreads) { error("This is only a dummy. Call the real one!"); return 0; } + +struct groupinfo *InvokeVelociraptor( + const int snapnum, char *output_name, struct cosmoinfo cosmo_info, + struct siminfo sim_info, const size_t num_gravity_parts, + const size_t num_hydro_parts, const size_t num_star_parts, + struct swift_vel_part *swift_parts, const int *cell_node_ids, + const int numthreads, const int return_group_flags, + int *const num_in_groups) { + error("This is only a dummy. Call the real one!"); + return 0; +} + #endif /* HAVE_DUMMY_VELOCIRAPTOR */ diff --git a/src/velociraptor_interface.c b/src/velociraptor_interface.c index 7756fe4b937986c108d223c56183f7d31cdfaa98..47c107d047e3abfcac32571f780cf52649a9c38d 100644 --- a/src/velociraptor_interface.c +++ b/src/velociraptor_interface.c @@ -21,21 +21,23 @@ #include "../config.h" /* Some standard headers. */ -#include <errno.h> #include <unistd.h> /* This object's header. */ #include "velociraptor_interface.h" /* Local includes. */ -#include "common_io.h" +#include "cooling.h" #include "engine.h" #include "hydro.h" #include "swift_velociraptor_part.h" +#include "velociraptor_struct.h" #ifdef HAVE_VELOCIRAPTOR -/* Structure for passing cosmological information to VELOCIraptor. */ +/** + * @brief Structure for passing cosmological information to VELOCIraptor. + */ struct cosmoinfo { /*! Current expansion factor of the Universe. (cosmology.a) */ @@ -47,6 +49,15 @@ struct cosmoinfo { /*! Matter density parameter (cosmology.Omega_m) */ double Omega_m; + /*! Radiation density parameter (cosmology.Omega_r) */ + double Omega_r; + + /*! Neutrino density parameter (0 in SWIFT) */ + double Omega_nu; + + /*! Neutrino density parameter (cosmology.Omega_k) */ + double Omega_k; + /*! Baryon density parameter (cosmology.Omega_b) */ double Omega_b; @@ -60,19 +71,21 @@ struct cosmoinfo { double w_de; }; -/* Structure for passing unit information to VELOCIraptor. */ +/** + * @brief Structure for passing unit information to VELOCIraptor. + */ struct unitinfo { - /* Length conversion factor to kpc. */ + /*! Length conversion factor to kpc. */ double lengthtokpc; - /* Velocity conversion factor to km/s. */ + /*! Velocity conversion factor to km/s. */ double velocitytokms; - /* Mass conversion factor to solar masses. */ + /*! Mass conversion factor to solar masses. */ double masstosolarmass; - /* Potential conversion factor. */ + /*! Potential conversion factor to (km/s)^2. */ double energyperunitmass; /*! Newton's gravitationl constant (phys_const.const_newton_G)*/ @@ -82,18 +95,34 @@ struct unitinfo { double hubbleunit; }; -/* Structure to hold the location of a top-level cell. */ +/** + * @brief Structure to hold the location of a top-level cell. + */ struct cell_loc { - /* Coordinates x,y,z */ + /*! Coordinates x,y,z */ double loc[3]; }; -/* Structure for passing simulation information to VELOCIraptor. */ +/** + * @brief Structure for passing simulation information to VELOCIraptor for a + * given call. + */ struct siminfo { - double period, zoomhigresolutionmass, interparticlespacing, spacedimension[3]; - /* Number of top-cells. */ + /*! Size of periodic replications */ + double period; + + /*! Mass of the high-resolution DM particles in a zoom-in run. */ + double zoomhigresolutionmass; + + /*! Mean inter-particle separation of the DM particles */ + double interparticlespacing; + + /*! Spacial extent of the simulation volume */ + double spacedimension[3]; + + /*! Number of top-level cells. */ int numcells; /*! Locations of top-level cells. */ @@ -105,142 +134,240 @@ struct siminfo { /*! Inverse of the top-level cell width. */ double icellwidth[3]; + /*! Holds the node ID of each top-level cell. */ + int *cellnodeids; + + /*! Is this a cosmological simulation? */ int icosmologicalsim; + + /*! Is this a zoom-in simulation? */ + int izoomsim; + + /*! Do we have DM particles? */ + int idarkmatter; + + /*! Do we have gas particles? */ + int igas; + + /*! Do we have star particles? */ + int istar; + + /*! Do we have BH particles? */ + int ibh; + + /*! Do we have other particles? */ + int iother; +}; + +/** + * @brief Structure for group information back to swift + */ +struct groupinfo { + + /*! Index of a #gpart in the global array on this MPI rank */ + int index; + + /*! Group number of the #gpart. */ + long long groupID; }; -/* VELOCIraptor interface. */ -int InitVelociraptor(char *config_name, char *output_name, - struct cosmoinfo cosmo_info, struct unitinfo unit_info, - struct siminfo sim_info); -int InvokeVelociraptor(const size_t num_gravity_parts, - const size_t num_hydro_parts, - struct swift_vel_part *swift_parts, - const int *cell_node_ids, char *output_name); +int InitVelociraptor(char *config_name, struct unitinfo unit_info, + struct siminfo sim_info, const int numthreads); + +struct groupinfo *InvokeVelociraptor( + const int snapnum, char *output_name, struct cosmoinfo cosmo_info, + struct siminfo sim_info, const size_t num_gravity_parts, + const size_t num_hydro_parts, const size_t num_star_parts, + struct swift_vel_part *swift_parts, const int *cell_node_ids, + const int numthreads, const int return_group_flags, + int *const num_in_groups); #endif /* HAVE_VELOCIRAPTOR */ /** - * @brief Initialise VELOCIraptor with input and output file names along with - * cosmological info needed to run. + * @brief Temporary structure used for the data copy mapper. + */ +struct velociraptor_copy_data { + const struct engine *e; + struct swift_vel_part *swift_parts; +}; + +/** + * @brief Mapper function to conver the #gpart into VELOCIraptor Particles. * - * @param e The #engine. + * @param map_data The array of #gpart. + * @param nr_gparts The number of #gpart. + * @param extra_data Pointer to the #engine and to the array to fill. + */ +void velociraptor_convert_particles_mapper(void *map_data, int nr_gparts, + void *extra_data) { + + /* Unpack the data */ + struct gpart *restrict gparts = (struct gpart *)map_data; + struct velociraptor_copy_data *data = + (struct velociraptor_copy_data *)extra_data; + const struct engine *e = data->e; + const struct space *s = e->s; + struct swift_vel_part *swift_parts = + data->swift_parts + (ptrdiff_t)(gparts - s->gparts); + + /* Handle on the other particle types */ + const struct part *parts = s->parts; + const struct xpart *xparts = s->xparts; + const struct spart *sparts = s->sparts; + + /* Handle on the physics modules */ + const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; + const struct unit_system *us = e->internal_units; + const struct phys_const *phys_const = e->physical_constants; + const struct cooling_function_data *cool_func = e->cooling_func; + + const float a_inv = e->cosmology->a_inv; + + /* Convert particle properties into VELOCIraptor units. + * VELOCIraptor wants: + * - Co-moving positions, + * - Peculiar velocities, + * - Co-moving potential, + * - Physical internal energy (for the gas), + * - Temperatures (for the gas). + */ + for (int i = 0; i < nr_gparts; i++) { + + swift_parts[i].x[0] = gparts[i].x[0]; + swift_parts[i].x[1] = gparts[i].x[1]; + swift_parts[i].x[2] = gparts[i].x[2]; + + swift_parts[i].v[0] = gparts[i].v_full[0] * a_inv; + swift_parts[i].v[1] = gparts[i].v_full[1] * a_inv; + swift_parts[i].v[2] = gparts[i].v_full[2] * a_inv; + + swift_parts[i].mass = gravity_get_mass(&gparts[i]); + swift_parts[i].potential = gravity_get_comoving_potential(&gparts[i]); + + swift_parts[i].type = gparts[i].type; + + swift_parts[i].index = i; +#ifdef WITH_MPI + swift_parts[i].task = e->nodeID; +#else + swift_parts[i].task = 0; +#endif + + /* Set gas particle IDs from their hydro counterparts and set internal + * energies. */ + switch (gparts[i].type) { + + case swift_type_gas: { + const struct part *p = &parts[-gparts[i].id_or_neg_offset]; + const struct xpart *xp = &xparts[-gparts[i].id_or_neg_offset]; + + swift_parts[i].id = parts[-gparts[i].id_or_neg_offset].id; + swift_parts[i].u = hydro_get_drifted_physical_internal_energy(p, cosmo); + swift_parts[i].T = cooling_get_temperature(phys_const, hydro_props, us, + cosmo, cool_func, p, xp); + } break; + + case swift_type_stars: + + swift_parts[i].id = sparts[-gparts[i].id_or_neg_offset].id; + swift_parts[i].u = 0.f; + swift_parts[i].T = 0.f; + break; + + case swift_type_dark_matter: + + swift_parts[i].id = gparts[i].id_or_neg_offset; + swift_parts[i].u = 0.f; + swift_parts[i].T = 0.f; + break; + + default: + error("Particle type not handled by VELOCIraptor."); + } + } +} + +/** + * @brief Initialise VELOCIraptor with configuration, units, + * simulation info needed to run. * + * @param e The #engine. */ void velociraptor_init(struct engine *e) { #ifdef HAVE_VELOCIRAPTOR - struct space *s = e->s; - struct cosmoinfo cosmo_info; - struct unitinfo unit_info; - struct siminfo sim_info; + const ticks tic = getticks(); - /* Set cosmological constants. */ - cosmo_info.atime = e->cosmology->a; - cosmo_info.littleh = e->cosmology->h; - cosmo_info.Omega_m = e->cosmology->Omega_m; - cosmo_info.Omega_b = e->cosmology->Omega_b; - cosmo_info.Omega_Lambda = e->cosmology->Omega_lambda; - cosmo_info.Omega_cdm = e->cosmology->Omega_m - e->cosmology->Omega_b; - cosmo_info.w_de = e->cosmology->w; - - message("Scale factor: %e", cosmo_info.atime); - message("Little h: %e", cosmo_info.littleh); - message("Omega_m: %e", cosmo_info.Omega_m); - message("Omega_b: %e", cosmo_info.Omega_b); - message("Omega_Lambda: %e", cosmo_info.Omega_Lambda); - message("Omega_cdm: %e", cosmo_info.Omega_cdm); - message("w_de: %e", cosmo_info.w_de); + /* Internal SWIFT units */ + const struct unit_system *swift_us = e->internal_units; - if (e->cosmology->w != -1.) - error("w_de is not 1. It is: %lf", e->cosmology->w); + /* CGS units and physical constants in CGS */ + struct unit_system cgs_us; + units_init_cgs(&cgs_us); + struct phys_const cgs_pc; + phys_const_init(&cgs_us, /*params=*/NULL, &cgs_pc); /* Set unit conversions. */ - unit_info.lengthtokpc = 1.0; - unit_info.velocitytokms = 1.0; - unit_info.masstosolarmass = 1.0; - unit_info.energyperunitmass = 1.0; + struct unitinfo unit_info; + unit_info.lengthtokpc = + units_cgs_conversion_factor(swift_us, UNIT_CONV_LENGTH) / + (1000. * cgs_pc.const_parsec); + unit_info.velocitytokms = + units_cgs_conversion_factor(swift_us, UNIT_CONV_VELOCITY) / 1.0e5; + unit_info.masstosolarmass = + units_cgs_conversion_factor(swift_us, UNIT_CONV_MASS) / + cgs_pc.const_solar_mass; + unit_info.energyperunitmass = + units_cgs_conversion_factor(swift_us, UNIT_CONV_ENERGY_PER_UNIT_MASS) / + (1.0e10); unit_info.gravity = e->physical_constants->const_newton_G; unit_info.hubbleunit = e->cosmology->H0 / e->cosmology->h; - message("Length conversion factor: %e", unit_info.lengthtokpc); - message("Velocity conversion factor: %e", unit_info.velocitytokms); - message("Mass conversion factor: %e", unit_info.masstosolarmass); - message("Potential conversion factor: %e", unit_info.energyperunitmass); - message("G: %e", unit_info.gravity); - message("H: %e", unit_info.hubbleunit); - - /* TODO: Find the total number of DM particles when running with star - * particles and BHs. */ - const int total_nr_dmparts = e->total_nr_gparts - e->total_nr_parts; + /* Gather some information about the simulation */ + struct siminfo sim_info; - /* Set simulation information. */ - if (e->s->periodic) { - sim_info.period = - unit_info.lengthtokpc * - s->dim[0]; /* Physical size of box in VELOCIraptor units (kpc). */ - } else - sim_info.period = 0.0; - sim_info.zoomhigresolutionmass = -1.0; /* Placeholder. */ - sim_info.interparticlespacing = sim_info.period / cbrt(total_nr_dmparts); - if (e->policy & engine_policy_cosmology) + /* Are we running with cosmology? */ + if (e->policy & engine_policy_cosmology) { sim_info.icosmologicalsim = 1; - else + } else { sim_info.icosmologicalsim = 0; - sim_info.spacedimension[0] = unit_info.lengthtokpc * s->dim[0]; - sim_info.spacedimension[1] = unit_info.lengthtokpc * s->dim[1]; - sim_info.spacedimension[2] = unit_info.lengthtokpc * s->dim[2]; - sim_info.numcells = s->nr_cells; - - sim_info.cellwidth[0] = unit_info.lengthtokpc * s->cells_top[0].width[0]; - sim_info.cellwidth[1] = unit_info.lengthtokpc * s->cells_top[0].width[1]; - sim_info.cellwidth[2] = unit_info.lengthtokpc * s->cells_top[0].width[2]; - - sim_info.icellwidth[0] = s->iwidth[0] / unit_info.lengthtokpc; - sim_info.icellwidth[1] = s->iwidth[1] / unit_info.lengthtokpc; - sim_info.icellwidth[2] = s->iwidth[2] / unit_info.lengthtokpc; - - /* Only allocate cell location array on first call to velociraptor_init(). */ - if (e->cell_loc == NULL) { - /* Allocate and populate top-level cell locations. */ - if (posix_memalign((void **)&(e->cell_loc), 32, - s->nr_cells * sizeof(struct cell_loc)) != 0) - error("Failed to allocate top-level cell locations for VELOCIraptor."); - - for (int i = 0; i < s->nr_cells; i++) { - e->cell_loc[i].loc[0] = unit_info.lengthtokpc * s->cells_top[i].loc[0]; - e->cell_loc[i].loc[1] = unit_info.lengthtokpc * s->cells_top[i].loc[1]; - e->cell_loc[i].loc[2] = unit_info.lengthtokpc * s->cells_top[i].loc[2]; - } } - - sim_info.cell_loc = e->cell_loc; - - char configfilename[PARSER_MAX_LINE_SIZE], - outputFileName[PARSER_MAX_LINE_SIZE + 128]; - parser_get_param_string(e->parameter_file, - "StructureFinding:config_file_name", configfilename); - snprintf(outputFileName, PARSER_MAX_LINE_SIZE + 128, "%s.VELOCIraptor", - e->stfBaseName); - - message("Config file name: %s", configfilename); - message("Period: %e", sim_info.period); - message("Zoom high res mass: %e", sim_info.zoomhigresolutionmass); - message("Inter-particle spacing: %e", sim_info.interparticlespacing); - message("Cosmological: %d", sim_info.icosmologicalsim); - message("Space dimensions: (%e,%e,%e)", sim_info.spacedimension[0], - sim_info.spacedimension[1], sim_info.spacedimension[2]); - message("No. of top-level cells: %d", sim_info.numcells); - message("Top-level cell locations range: (%e,%e,%e) -> (%e,%e,%e)", - sim_info.cell_loc[0].loc[0], sim_info.cell_loc[0].loc[1], - sim_info.cell_loc[0].loc[2], - sim_info.cell_loc[sim_info.numcells - 1].loc[0], - sim_info.cell_loc[sim_info.numcells - 1].loc[1], - sim_info.cell_loc[sim_info.numcells - 1].loc[2]); + sim_info.izoomsim = 0; + + /* Tell VELOCIraptor what we have in the simulation */ + sim_info.idarkmatter = (e->total_nr_gparts - e->total_nr_parts > 0); + sim_info.igas = (e->policy & engine_policy_hydro); + sim_info.istar = (e->policy & engine_policy_stars); + sim_info.ibh = 0; // sim_info.ibh = (e->policy&engine_policy_bh); + sim_info.iother = 0; + + /* Be nice, talk! */ + if (e->verbose) { + message("VELOCIraptor conf: Length conversion factor: %e", + unit_info.lengthtokpc); + message("VELOCIraptor conf: Velocity conversion factor: %e", + unit_info.velocitytokms); + message("VELOCIraptor conf: Mass conversion factor: %e", + unit_info.masstosolarmass); + message("VELOCIraptor conf: Internal energy conversion factor: %e", + unit_info.energyperunitmass); + message("VELOCIraptor conf: G: %e", unit_info.gravity); + message("VELOCIraptor conf: H0/h: %e", unit_info.hubbleunit); + message("VELOCIraptor conf: Config file name: %s", e->stf_config_file_name); + message("VELOCIraptor conf: Cosmological Simulation: %d", + sim_info.icosmologicalsim); + } /* Initialise VELOCIraptor. */ - if (!InitVelociraptor(configfilename, outputFileName, cosmo_info, unit_info, - sim_info)) - error("Exiting. VELOCIraptor initialisation failed."); + if (InitVelociraptor(e->stf_config_file_name, unit_info, sim_info, + e->nr_threads) != 1) + error("VELOCIraptor initialisation failed."); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); #else error("SWIFT not configure to run with VELOCIraptor."); #endif /* HAVE_VELOCIRAPTOR */ @@ -250,118 +377,241 @@ void velociraptor_init(struct engine *e) { * @brief Run VELOCIraptor with current particle data. * * @param e The #engine. - * + * @param linked_with_snap Are we running at the same time as a snapshot dump? */ -void velociraptor_invoke(struct engine *e) { +void velociraptor_invoke(struct engine *e, const int linked_with_snap) { #ifdef HAVE_VELOCIRAPTOR - struct space *s = e->s; - struct gpart *gparts = s->gparts; - struct part *parts = s->parts; - struct xpart *xparts = s->xparts; + + /* Handle on the particles */ + const struct space *s = e->s; const size_t nr_gparts = s->nr_gparts; - const size_t nr_hydro_parts = s->nr_parts; + const size_t nr_parts = s->nr_parts; + const size_t nr_sparts = s->nr_sparts; const int nr_cells = s->nr_cells; - int *cell_node_ids = NULL; - static int stf_output_count = 0; + const struct cell *cells_top = s->cells_top; /* Allow thread to run on any core for the duration of the call to - * VELOCIraptor so that - * when OpenMP threads are spawned they can run on any core on the processor. - */ + * VELOCIraptor so that when OpenMP threads are spawned + * they can run on any core on the processor. */ const int nr_cores = sysconf(_SC_NPROCESSORS_ONLN); - cpu_set_t cpuset; pthread_t thread = pthread_self(); /* Set affinity mask to include all cores on the CPU for VELOCIraptor. */ + cpu_set_t cpuset; CPU_ZERO(&cpuset); for (int j = 0; j < nr_cores; j++) CPU_SET(j, &cpuset); - pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); + /* Set cosmology information for this point in time */ + struct cosmoinfo cosmo_info; + cosmo_info.atime = e->cosmology->a; + cosmo_info.littleh = e->cosmology->h; + cosmo_info.Omega_m = e->cosmology->Omega_m; + cosmo_info.Omega_b = e->cosmology->Omega_b; + cosmo_info.Omega_r = e->cosmology->Omega_r; + cosmo_info.Omega_k = e->cosmology->Omega_k; + cosmo_info.Omega_nu = 0.; + cosmo_info.Omega_Lambda = e->cosmology->Omega_lambda; + cosmo_info.Omega_cdm = e->cosmology->Omega_m - e->cosmology->Omega_b; + cosmo_info.w_de = e->cosmology->w; + + /* Report the cosmo info we use */ + if (e->verbose) { + message("VELOCIraptor conf: Scale factor: %e", cosmo_info.atime); + message("VELOCIraptor conf: Little h: %e", cosmo_info.littleh); + message("VELOCIraptor conf: Omega_m: %e", cosmo_info.Omega_m); + message("VELOCIraptor conf: Omega_b: %e", cosmo_info.Omega_b); + message("VELOCIraptor conf: Omega_Lambda: %e", cosmo_info.Omega_Lambda); + message("VELOCIraptor conf: Omega_cdm: %e", cosmo_info.Omega_cdm); + message("VELOCIraptor conf: w_de: %e", cosmo_info.w_de); + } + + /* Update the simulation information */ + struct siminfo sim_info; + + /* Period of the box (Note we assume a cubic box!) */ + if (e->s->periodic) { + sim_info.period = s->dim[0]; + } else { + sim_info.period = 0.0; + } + + /* Tell VELOCIraptor this is not a zoom-in simulation */ + sim_info.zoomhigresolutionmass = -1.0; + + /* Are we running with cosmology? */ + if (e->policy & engine_policy_cosmology) { + sim_info.icosmologicalsim = 1; + sim_info.izoomsim = 0; + const size_t total_nr_baryons = e->total_nr_parts + e->total_nr_sparts; + const size_t total_nr_dmparts = e->total_nr_gparts - total_nr_baryons; + sim_info.interparticlespacing = sim_info.period / cbrt(total_nr_dmparts); + } else { + sim_info.icosmologicalsim = 0; + sim_info.izoomsim = 0; + sim_info.interparticlespacing = -1; + } + + /* Set the spatial extent of the simulation volume */ + sim_info.spacedimension[0] = s->dim[0]; + sim_info.spacedimension[1] = s->dim[1]; + sim_info.spacedimension[2] = s->dim[2]; + + /* Store number of top-level cells */ + sim_info.numcells = s->nr_cells; + + /* Size and inverse size of the top-level cells in VELOCIraptor units */ + sim_info.cellwidth[0] = s->cells_top[0].width[0]; + sim_info.cellwidth[1] = s->cells_top[0].width[1]; + sim_info.cellwidth[2] = s->cells_top[0].width[2]; + sim_info.icellwidth[0] = s->iwidth[0]; + sim_info.icellwidth[1] = s->iwidth[1]; + sim_info.icellwidth[2] = s->iwidth[2]; + ticks tic = getticks(); - /* Allocate and populate array of cell node IDs. */ - if (posix_memalign((void **)&cell_node_ids, 32, nr_cells * sizeof(int)) != 0) + /* Allocate and populate array of cell node IDs and positions. */ + int *cell_node_ids = NULL; + if (posix_memalign((void **)&sim_info.cell_loc, SWIFT_STRUCT_ALIGNMENT, + s->nr_cells * sizeof(struct cell_loc)) != 0) + error("Failed to allocate top-level cell locations for VELOCIraptor."); + if (posix_memalign((void **)&cell_node_ids, SWIFT_STRUCT_ALIGNMENT, + nr_cells * sizeof(int)) != 0) error("Failed to allocate list of cells node IDs for VELOCIraptor."); - for (int i = 0; i < nr_cells; i++) cell_node_ids[i] = s->cells_top[i].nodeID; + for (int i = 0; i < s->nr_cells; i++) { + cell_node_ids[i] = cells_top[i].nodeID; + + sim_info.cell_loc[i].loc[0] = cells_top[i].loc[0]; + sim_info.cell_loc[i].loc[1] = cells_top[i].loc[1]; + sim_info.cell_loc[i].loc[2] = cells_top[i].loc[2]; + } + + if (e->verbose) { + message("VELOCIraptor conf: Space dimensions: (%e,%e,%e)", + sim_info.spacedimension[0], sim_info.spacedimension[1], + sim_info.spacedimension[2]); + message("VELOCIraptor conf: No. of top-level cells: %d", sim_info.numcells); + message( + "VELOCIraptor conf: Top-level cell locations range: (%e,%e,%e) -> " + "(%e,%e,%e)", + sim_info.cell_loc[0].loc[0], sim_info.cell_loc[0].loc[1], + sim_info.cell_loc[0].loc[2], + sim_info.cell_loc[sim_info.numcells - 1].loc[0], + sim_info.cell_loc[sim_info.numcells - 1].loc[1], + sim_info.cell_loc[sim_info.numcells - 1].loc[2]); + } + + /* Report timing */ + if (e->verbose) + message("VR Collecting top-level cell info took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); - message("MPI rank %d sending %zu gparts to VELOCIraptor.", engine_rank, - nr_gparts); + /* Mention the number of particles being sent */ + if (e->verbose) + message( + "VELOCIraptor conf: MPI rank %d sending %zu gparts to VELOCIraptor.", + engine_rank, nr_gparts); - /* Append base name with either the step number or time depending on what - * format is specified in the parameter file. */ + /* Append base name with the current output number */ char outputFileName[PARSER_MAX_LINE_SIZE + 128]; - if (e->stf_output_freq_format == io_stf_steps) { - snprintf(outputFileName, PARSER_MAX_LINE_SIZE + 128, "%s_%04i.VELOCIraptor", - e->stfBaseName, e->step); - } else if (e->stf_output_freq_format == io_stf_time) { + + /* What should the filename be? */ + if (linked_with_snap) { + snprintf(outputFileName, PARSER_MAX_LINE_SIZE + 128, + "stf_%s_%04i.VELOCIraptor", e->snapshot_base_name, + e->snapshot_output_count); + } else { snprintf(outputFileName, PARSER_MAX_LINE_SIZE + 128, "%s_%04i.VELOCIraptor", - e->stfBaseName, stf_output_count); + e->stf_base_name, e->stf_output_count); + } + + /* What is the snapshot number? */ + int snapnum; + if (linked_with_snap) { + snapnum = e->snapshot_output_count; + } else { + snapnum = e->stf_output_count; } + tic = getticks(); + /* Allocate and populate an array of swift_vel_parts to be passed to * VELOCIraptor. */ struct swift_vel_part *swift_parts = NULL; - if (posix_memalign((void **)&swift_parts, part_align, nr_gparts * sizeof(struct swift_vel_part)) != 0) error("Failed to allocate array of particles for VELOCIraptor."); - bzero(swift_parts, nr_gparts * sizeof(struct swift_vel_part)); + struct velociraptor_copy_data copy_data = {e, swift_parts}; + threadpool_map(&e->threadpool, velociraptor_convert_particles_mapper, + s->gparts, nr_gparts, sizeof(struct gpart), 0, ©_data); - const float energy_scale = 1.0; - const float a = e->cosmology->a; + /* Report timing */ + if (e->verbose) + message("VR Collecting particle info took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); - message("Energy scaling factor: %f", energy_scale); - message("a: %f", a); + tic = getticks(); - /* Convert particle properties into VELOCIraptor units */ - for (size_t i = 0; i < nr_gparts; i++) { - swift_parts[i].x[0] = gparts[i].x[0]; - swift_parts[i].x[1] = gparts[i].x[1]; - swift_parts[i].x[2] = gparts[i].x[2]; - swift_parts[i].v[0] = gparts[i].v_full[0] / a; - swift_parts[i].v[1] = gparts[i].v_full[1] / a; - swift_parts[i].v[2] = gparts[i].v_full[2] / a; - swift_parts[i].mass = gravity_get_mass(&gparts[i]); - swift_parts[i].potential = gravity_get_comoving_potential(&gparts[i]); - swift_parts[i].type = gparts[i].type; - - /* Set gas particle IDs from their hydro counterparts and set internal - * energies. */ - if (gparts[i].type == swift_type_gas) { - swift_parts[i].id = parts[-gparts[i].id_or_neg_offset].id; - swift_parts[i].u = - hydro_get_physical_internal_energy( - &parts[-gparts[i].id_or_neg_offset], - &xparts[-gparts[i].id_or_neg_offset], e->cosmology) * - energy_scale; - } else if (gparts[i].type == swift_type_dark_matter) { - swift_parts[i].id = gparts[i].id_or_neg_offset; - swift_parts[i].u = 0.f; - } else { - error("Particle type not handled by velociraptor (yet?) !"); - } - } + /* Values returned by VELOCIRaptor */ + int num_gparts_in_groups = -1; + struct groupinfo *group_info = NULL; /* Call VELOCIraptor. */ - if (!InvokeVelociraptor(nr_gparts, nr_hydro_parts, swift_parts, cell_node_ids, - outputFileName)) + group_info = (struct groupinfo *)InvokeVelociraptor( + snapnum, outputFileName, cosmo_info, sim_info, nr_gparts, nr_parts, + nr_sparts, swift_parts, cell_node_ids, e->nr_threads, linked_with_snap, + &num_gparts_in_groups); + + /* Check that the ouput is valid */ + if (linked_with_snap && group_info == NULL && num_gparts_in_groups < 0) { error("Exiting. Call to VELOCIraptor failed on rank: %d.", e->nodeID); + } + if (!linked_with_snap && group_info != NULL) { + error("VELOCIraptor returned an array whilst it should not have."); + } + + /* Report timing */ + if (e->verbose) + message("VR Invokation of velociraptor took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); + + /* Assign the group IDs back to the gparts */ + if (linked_with_snap) { + + if (posix_memalign((void **)&s->gpart_group_data, part_align, + nr_gparts * sizeof(struct velociraptor_gpart_data)) != 0) + error("Failed to allocate array of gpart data for VELOCIraptor i/o."); + + struct velociraptor_gpart_data *data = s->gpart_group_data; + + /* Zero the array (gparts not in groups have an ID of 0) */ + bzero(data, nr_gparts * sizeof(struct velociraptor_gpart_data)); + + /* Copy the data at the right place */ + for (int i = 0; i < num_gparts_in_groups; i++) { + data[group_info[i].index].groupID = group_info[i].groupID; + } + + /* Report timing */ + if (e->verbose) + message("VR Copying group information back took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + /* Free the array returned by VELOCIraptor */ + free(group_info); + } /* Reset the pthread affinity mask after VELOCIraptor returns. */ pthread_setaffinity_np(thread, sizeof(cpu_set_t), engine_entry_affinity()); - /* Free cell node ids after VELOCIraptor has copied them. */ - free(cell_node_ids); - free(swift_parts); - - stf_output_count++; + /* Increase output counter (if not linked with snapshots) */ + if (!linked_with_snap) e->stf_output_count++; - message("VELOCIraptor took %.3f %s on rank %d.", - clocks_from_ticks(getticks() - tic), clocks_getunit(), engine_rank); #else error("SWIFT not configure to run with VELOCIraptor."); #endif /* HAVE_VELOCIRAPTOR */ diff --git a/src/velociraptor_interface.h b/src/velociraptor_interface.h index 1f29be11c9dd8e267c87201b0a438979fec3775b..2547fa56c1677e93b1c59a1435e9a6ab92c1f308 100644 --- a/src/velociraptor_interface.h +++ b/src/velociraptor_interface.h @@ -22,19 +22,11 @@ /* Config parameters. */ #include "../config.h" -/** - * @brief The different formats for when to run structure finding. - */ -enum io_stf_output_format { - io_stf_steps = 0, /*!< Output every N steps */ - io_stf_time /*!< Output at fixed time intervals */ -}; - /* Forward declaration */ struct engine; /* VELOCIraptor wrapper functions. */ void velociraptor_init(struct engine *e); -void velociraptor_invoke(struct engine *e); +void velociraptor_invoke(struct engine *e, const int linked_with_snap); #endif /* SWIFT_VELOCIRAPTOR_INTERFACE_H */ diff --git a/src/velociraptor_io.h b/src/velociraptor_io.h new file mode 100644 index 0000000000000000000000000000000000000000..f18398219bfbc5cd6bb58a37b103f29527fa5589 --- /dev/null +++ b/src/velociraptor_io.h @@ -0,0 +1,78 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_VELOCIRAPTOR_IO_H +#define SWIFT_VELOCIRAPTOR_IO_H + +/* Config parameters. */ +#include "../config.h" + +INLINE static void velociraptor_convert_part_groupID(const struct engine* e, + const struct part* p, + const struct xpart* xp, + long long* ret) { + if (p->gpart == NULL) + ret[0] = 0.f; + else { + const ptrdiff_t offset = p->gpart - e->s->gparts; + *ret = (e->s->gpart_group_data + offset)->groupID; + } +} + +INLINE static void velociraptor_convert_spart_groupID(const struct engine* e, + const struct spart* sp, + long long* ret) { + if (sp->gpart == NULL) + ret[0] = 0.f; + else { + const ptrdiff_t offset = sp->gpart - e->s->gparts; + *ret = (e->s->gpart_group_data + offset)->groupID; + } +} + +__attribute__((always_inline)) INLINE static int velociraptor_write_parts( + const struct part* parts, const struct xpart* xparts, + struct io_props* list) { + + list[0] = io_make_output_field_convert_part( + "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, parts, xparts, + velociraptor_convert_part_groupID); + + return 1; +} + +__attribute__((always_inline)) INLINE static int velociraptor_write_gparts( + const struct velociraptor_gpart_data* group_data, struct io_props* list) { + + list[0] = io_make_output_field("GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, + group_data, groupID); + + return 1; +} + +__attribute__((always_inline)) INLINE static int velociraptor_write_sparts( + const struct spart* sparts, struct io_props* list) { + + list[0] = io_make_output_field_convert_spart( + "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, sparts, + velociraptor_convert_spart_groupID); + + return 1; +} + +#endif /* SWIFT_VELOCIRAPTOR_IO_H */ diff --git a/src/velociraptor_struct.h b/src/velociraptor_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..b998263a6ba2fe0aaa6552f274cb8f4ee85d3b1c --- /dev/null +++ b/src/velociraptor_struct.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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 SWIFT_VELOCIRAPTOR_STRUCT_H +#define SWIFT_VELOCIRAPTOR_STRUCT_H + +/* Config parameters. */ +#include "../config.h" + +/** + * @brief Data returned by VELOCIraptor for each #gpart. + */ +struct velociraptor_gpart_data { + + /*! Group ID of that #gpart. */ + long long groupID; +}; + +#endif /* SWIFT_VELOCIRAPTOR_STRUCT_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 9698e66d1a7a80566a9347bb629e9670eafbb98c..f18b6c44c63f6e394bc2b47616f86da5dbcd54e2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,15 +15,15 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Add the source directory and the non-standard paths to the included library headers to CFLAGS -AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) -AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) +AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS) # List of programs and scripts to run in the test suite TESTS = testGreetings testMaths testReading.sh testSingle testKernel \ testActivePair.sh test27cells.sh test27cellsPerturbed.sh \ testParser.sh testSPHStep test125cells.sh test125cellsPerturbed.sh testFFT \ - testAdiabaticIndex \ + testAdiabaticIndex testRandom \ testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \ testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \ testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ @@ -34,14 +34,14 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel \ # List of test programs to compile check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ testSPHStep testActivePair test27cells test27cells_subset test125cells testParser \ - testKernel testFFT testInteractions testMaths \ + testKernel testFFT testInteractions testMaths testRandom \ testSymmetry testThreadpool \ testAdiabaticIndex testRiemannExact testRiemannTRRS \ testRiemannHLLC testMatrixInversion testDump testLogger \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \ testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \ - test27cellsStars_subset + test27cellsStars_subset testCooling # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -51,6 +51,8 @@ testGreetings_SOURCES = testGreetings.c testMaths_SOURCES = testMaths.c +testRandom_SOURCES = testRandom.c + testReading_SOURCES = testReading.c testSelectOutput_SOURCES = testSelectOutput.c @@ -128,6 +130,8 @@ testEOS_SOURCES = testEOS.c testUtilities_SOURCES = testUtilities.c +testCooling_SOURCES = testCooling.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ diff --git a/tests/test125cells.c b/tests/test125cells.c index cfe7e18b07b30c309531642a769241841de3a593..3b49e71163d05afdc0e33a33306121dc48a22283 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -112,7 +112,7 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, #elif defined(DEFAULT_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ - defined(HOPKINS_PU_SPH_MONAGHAN) + defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(PLANETARY_SPH) part->u = pressure / (hydro_gamma_minus_one * density); @@ -401,10 +401,13 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1], main_cell->hydro.parts[pid].v[2], main_cell->hydro.parts[pid].h, hydro_get_comoving_density(&main_cell->hydro.parts[pid]), -#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ - defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) || \ - defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) +#if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) || \ + defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) || \ + defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ + defined(ANARCHY_PU_SPH) 0.f, +#elif defined(ANARCHY_PU_SPH) + main_cell->hydro.parts[pid].viscosity.div_v, #else main_cell->hydro.parts[pid].density.div_v, #endif @@ -427,6 +430,9 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, defined(HOPKINS_PU_SPH_MONAGHAN) main_cell->hydro.parts[pid].force.v_sig, 0.f, main_cell->hydro.parts[pid].u_dt +#elif defined(ANARCHY_PU_SPH) + main_cell->hydro.parts[pid].viscosity.v_sig, 0.f, + main_cell->hydro.parts[pid].u_dt #else 0.f, 0.f, 0.f #endif @@ -459,9 +465,15 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, /* Just a forward declaration... */ void runner_dopair1_branch_density(struct runner *r, struct cell *ci, struct cell *cj); -void runner_doself1_density(struct runner *r, struct cell *ci); +void runner_doself1_branch_density(struct runner *r, struct cell *ci); +#ifdef EXTRA_HYDRO_LOOP +void runner_dopair1_branch_gradient(struct runner *r, struct cell *ci, + struct cell *cj); +void runner_doself1_branch_gradient(struct runner *r, struct cell *ci); +#endif /* EXTRA_HYDRO LOOP */ void runner_dopair2_branch_force(struct runner *r, struct cell *ci, struct cell *cj); +void runner_doself2_branch_force(struct runner *r, struct cell *ci); void runner_doself2_force(struct runner *r, struct cell *ci); void runner_doself2_force_vec(struct runner *r, struct cell *ci); @@ -589,7 +601,9 @@ int main(int argc, char *argv[]) { hp.eta_neighbours = h; hp.h_tolerance = 1e0; hp.h_max = FLT_MAX; - hp.max_smoothing_iterations = 1; + hp.h_min = 0.f; + hp.h_min_ratio = 0.f; + hp.max_smoothing_iterations = 10; hp.CFL_condition = 0.1; struct engine engine; @@ -663,7 +677,7 @@ int main(int argc, char *argv[]) { /* First, sort stuff */ for (int j = 0; j < 125; ++j) - runner_do_sort(&runner, cells[j], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0); /* Do the density calculation */ @@ -675,7 +689,7 @@ int main(int argc, char *argv[]) { cache_init(&runner.cj_cache, 512); #endif - /* Run all the pairs (only once !)*/ + /* Run all the (only once !)*/ for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { for (int k = 0; k < 5; k++) { @@ -707,11 +721,56 @@ int main(int argc, char *argv[]) { /* And now the self-interaction for the central cells*/ for (int j = 0; j < 27; ++j) - runner_doself1_density(&runner, inner_cells[j]); + runner_doself1_branch_density(&runner, inner_cells[j]); /* Ghost to finish everything on the central cells */ for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0); +#ifdef EXTRA_HYDRO_LOOP + /* We need to do the gradient loop and the extra ghost! */ + message( + "Extra hydro loop detected, running gradient loop in test125cells."); + + /* Run all the pairs (only once !)*/ + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + for (int k = 0; k < 5; k++) { + + struct cell *ci = cells[i * 25 + j * 5 + k]; + + for (int ii = -1; ii < 2; ii++) { + int iii = i + ii; + if (iii < 0 || iii >= 5) continue; + iii = (iii + 5) % 5; + for (int jj = -1; jj < 2; jj++) { + int jjj = j + jj; + if (jjj < 0 || jjj >= 5) continue; + jjj = (jjj + 5) % 5; + for (int kk = -1; kk < 2; kk++) { + int kkk = k + kk; + if (kkk < 0 || kkk >= 5) continue; + kkk = (kkk + 5) % 5; + + struct cell *cj = cells[iii * 25 + jjj * 5 + kkk]; + + if (cj > ci) runner_dopair1_branch_gradient(&runner, ci, cj); + } + } + } + } + } + } + + /* And now the self-interaction for the central cells */ + for (int j = 0; j < 27; ++j) + runner_doself1_branch_gradient(&runner, inner_cells[j]); + + /* Extra ghost to finish everything on the central cells */ + for (int j = 0; j < 27; ++j) + runner_do_extra_ghost(&runner, inner_cells[j], 0); + +#endif /* EXTRA_HYDRO_LOOP */ + /* Do the force calculation */ #ifdef WITH_VECTORIZATION @@ -745,7 +804,7 @@ int main(int argc, char *argv[]) { ticks self_tic = getticks(); /* And now the self-interaction for the main cell */ - runner_doself2_force(&runner, main_cell); + runner_doself2_branch_force(&runner, main_cell); timings[26] += getticks() - self_tic; @@ -780,11 +839,11 @@ int main(int argc, char *argv[]) { ticks self_time = timings[26]; - message("Corner calculations took : %15lli ticks.", corner_time / runs); - message("Edge calculations took : %15lli ticks.", edge_time / runs); - message("Face calculations took : %15lli ticks.", face_time / runs); - message("Self calculations took : %15lli ticks.", self_time / runs); - message("SWIFT calculation took : %15lli ticks.", time / runs); + message("Corner calculations took: %15lli ticks.", corner_time / runs); + message("Edge calculations took: %15lli ticks.", edge_time / runs); + message("Face calculations took: %15lli ticks.", face_time / runs); + message("Self calculations took: %15lli ticks.", self_time / runs); + message("SWIFT calculation took: %15lli ticks.", time / runs); for (int j = 0; j < 125; ++j) reset_particles(cells[j], &space.hs, vel, press, size, rho); @@ -841,6 +900,48 @@ int main(int argc, char *argv[]) { /* Ghost to finish everything on the central cells */ for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0); +#ifdef EXTRA_HYDRO_LOOP + /* We need to do the gradient loop and the extra ghost! */ + + /* Run all the pairs (only once !)*/ + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + for (int k = 0; k < 5; k++) { + + struct cell *ci = cells[i * 25 + j * 5 + k]; + + for (int ii = -1; ii < 2; ii++) { + int iii = i + ii; + if (iii < 0 || iii >= 5) continue; + iii = (iii + 5) % 5; + for (int jj = -1; jj < 2; jj++) { + int jjj = j + jj; + if (jjj < 0 || jjj >= 5) continue; + jjj = (jjj + 5) % 5; + for (int kk = -1; kk < 2; kk++) { + int kkk = k + kk; + if (kkk < 0 || kkk >= 5) continue; + kkk = (kkk + 5) % 5; + + struct cell *cj = cells[iii * 25 + jjj * 5 + kkk]; + + if (cj > ci) pairs_all_gradient(&runner, ci, cj); + } + } + } + } + } + } + + /* And now the self-interaction for the central cells */ + for (int j = 0; j < 27; ++j) self_all_gradient(&runner, inner_cells[j]); + + /* Extra ghost to finish everything on the central cells */ + for (int j = 0; j < 27; ++j) + runner_do_extra_ghost(&runner, inner_cells[j], 0); + +#endif /* EXTRA_HYDRO_LOOP */ + /* Do the force calculation */ /* Do the pairs (for the central 27 cells) */ @@ -865,7 +966,7 @@ int main(int argc, char *argv[]) { const ticks toc = getticks(); /* Output timing */ - message("Brute force calculation took : %15lli ticks.", toc - tic); + message("Brute force calculation took: %15lli ticks.", toc - tic); sprintf(outputFileName, "brute_force_125_%.150s.dat", outputFileNameExtension); diff --git a/tests/test27cells.c b/tests/test27cells.c index 6930638efe28aafab1c614e8ce71b353eb0c5b8f..cc34f503304feb56799a2d31baa3416b940202d3 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -275,7 +275,8 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, hydro_get_comoving_density(&main_cell->hydro.parts[pid]), #if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) 0.f, -#elif defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) +#elif defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ + defined(ANARCHY_PU_SPH) main_cell->hydro.parts[pid].density.pressure_bar_dh, #else main_cell->hydro.parts[pid].density.rho_dh, @@ -288,6 +289,12 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->hydro.parts[pid].density.rot_v[0], main_cell->hydro.parts[pid].density.rot_v[1], main_cell->hydro.parts[pid].density.rot_v[2] +#elif defined(ANARCHY_PU_SPH) + /* this is required because of the variable AV scheme */ + main_cell->hydro.parts[pid].viscosity.div_v, + main_cell->hydro.parts[pid].density.rot_v[0], + main_cell->hydro.parts[pid].density.rot_v[1], + main_cell->hydro.parts[pid].density.rot_v[2] #else 0., 0., 0., 0. #endif @@ -327,6 +334,12 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, cj->hydro.parts[pjd].density.rot_v[0], cj->hydro.parts[pjd].density.rot_v[1], cj->hydro.parts[pjd].density.rot_v[2] +#elif defined(ANARCHY_PU_SPH) + /* this is required because of the variable AV scheme */ + cj->hydro.parts[pjd].viscosity.div_v, + cj->hydro.parts[pjd].density.rot_v[0], + cj->hydro.parts[pjd].density.rot_v[1], + cj->hydro.parts[pjd].density.rot_v[2] #else 0., 0., 0., 0. #endif @@ -492,7 +505,7 @@ int main(int argc, char *argv[]) { runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0); - runner_do_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); } } } diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c index 3875bf75b1bb315bf48acae13b9553c689416a18..0377fc49edfedc8b1d9ce0630821622117187c9b 100644 --- a/tests/test27cellsStars.c +++ b/tests/test27cellsStars.c @@ -164,6 +164,7 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, cell->stars.count = scount; cell->hydro.dx_max_part = 0.; cell->hydro.dx_max_sort = 0.; + cell->stars.dx_max_sort = 0.; cell->width[0] = size; cell->width[1] = size; cell->width[2] = size; @@ -174,8 +175,10 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, cell->hydro.ti_old_part = 8; cell->hydro.ti_end_min = 8; cell->hydro.ti_end_max = 8; + cell->grav.ti_old_part = 8; cell->grav.ti_end_min = 8; cell->grav.ti_end_max = 8; + cell->stars.ti_end_min = 8; cell->nodeID = NODE_ID; shuffle_particles(cell->hydro.parts, cell->hydro.count); @@ -414,7 +417,8 @@ int main(int argc, char *argv[]) { runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0); - runner_do_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); + runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); + runner_do_stars_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0); } } } diff --git a/tests/testActivePair.c b/tests/testActivePair.c index a4eb04330fba7c984333fc7c705235223810ec4d..54a3189b89d9de757bf340bf759db5b40f947174 100644 --- a/tests/testActivePair.c +++ b/tests/testActivePair.c @@ -112,7 +112,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, #if defined(GADGET2_SPH) part->entropy = 1.f; #elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ - defined(HOPKINS_PU_SPH_MONAGHAN) + defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) part->u = 1.f; #elif defined(HOPKINS_PE_SPH) part->entropy = 1.f; @@ -212,7 +212,8 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo, p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h)); p->density.wcount_dh = 0.f; #endif /* PRESSURE-ENTROPY */ -#if defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) +#if defined(HOPKINS_PU_SPH) || defined(HOPKINS_PU_SPH_MONAGHAN) || \ + defined(ANARCHY_PU_SPH) p->rho = 1.f; p->pressure_bar = 0.6666666; p->density.rho_dh = 0.f; @@ -220,6 +221,13 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo, p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h)); p->density.wcount_dh = 0.f; #endif /* PRESSURE-ENERGY */ +#if defined(ANARCHY_PU_SPH) + /* Initialise viscosity variables */ + p->viscosity.alpha = 0.8; + p->viscosity.div_v = 0.f; + p->viscosity.div_v_previous_step = 0.f; + p->viscosity.v_sig = hydro_get_comoving_soundspeed(p); +#endif /* ANARCHY_PU_SPH viscosity variables */ /* And prepare for a round of force tasks. */ hydro_prepare_force(p, xp, cosmo, hydro_props, 0.); @@ -298,8 +306,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci, interaction_func vec_interaction, init_func init, finalise_func finalise) { - runner_do_sort(runner, *ci, 0x1FFF, 0, 0); - runner_do_sort(runner, *cj, 0x1FFF, 0, 0); + runner_do_hydro_sort(runner, *ci, 0x1FFF, 0, 0); + runner_do_hydro_sort(runner, *cj, 0x1FFF, 0, 0); /* Zero the fields */ init(*ci, runner->e->cosmology, runner->e->hydro_properties); diff --git a/tests/testCbrt.c b/tests/testCbrt.c index 3663e0e19ad2a5ad35d67703e00f5c0309a3eb00..bba379902b2bbc16bd49a5bbba0917100b4d60a7 100644 --- a/tests/testCbrt.c +++ b/tests/testCbrt.c @@ -38,7 +38,9 @@ int main(int argc, char *argv[]) { clocks_set_cpufreq(cpufreq); /* Choke on FP-exceptions */ +#ifdef HAVE_FE_ENABLE_EXCEPT feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif /* Some constants for this test. */ const int num_vals = 200000000; @@ -59,8 +61,8 @@ int main(int argc, char *argv[]) { for (int k = 0; k < num_vals; k++) { const double exact = cbrt(data[k]); // computed in double just to be sure. const float ours = 1.0f / icbrtf(data[k]); - const float err_abs = fabsf(exact - ours); - const float err_rel = 0.5f * fabsf(exact - ours) / (exact + ours); + const float err_abs = fabs(exact - ours); + const float err_rel = 0.5f * fabs(exact - ours) / (exact + ours); if (err_rel > err_rel_tol && data[k] != 0.f) error( diff --git a/tests/testCooling.c b/tests/testCooling.c new file mode 100644 index 0000000000000000000000000000000000000000..727a9638b09b871e866fe787438a5707fd43ec6b --- /dev/null +++ b/tests/testCooling.c @@ -0,0 +1,204 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@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/>. + * + ******************************************************************************/ +#include "../config.h" + +/* Local headers. */ +#include "swift.h" + +#if 0 + +/* + * @brief Assign particle density and entropy corresponding to the + * hydrogen number density and internal energy specified. + * + * @param p Particle data structure + * @param xp extra particle structure + * @param us unit system struct + * @param cooling Cooling function data structure + * @param cosmo Cosmology data structure + * @param phys_const Physical constants data structure + * @param nh_cgs Hydrogen number density (cgs units) + * @param u Internal energy (cgs units) + * @param ti_current integertime to set cosmo quantities + */ +void set_quantities(struct part *restrict p, struct xpart *restrict xp, + const struct unit_system *restrict us, + const struct cooling_function_data *restrict cooling, + struct cosmology *restrict cosmo, + const struct phys_const *restrict phys_const, float nh_cgs, + double u, integertime_t ti_current) { + + /* Update cosmology quantities */ + cosmology_update(cosmo, phys_const, ti_current); + + /* calculate density */ + double hydrogen_number_density = nh_cgs / cooling->number_density_scale; + p->rho = hydrogen_number_density * phys_const->const_proton_mass / + p->chemistry_data.metal_mass_fraction[chemistry_element_H] * + (cosmo->a * cosmo->a * cosmo->a); + + /* update entropy based on internal energy */ + float pressure = (u * cosmo->a * cosmo->a) / cooling->internal_energy_scale * + p->rho * (hydro_gamma_minus_one); + p->entropy = pressure * (pow(p->rho, -hydro_gamma)); + xp->entropy_full = p->entropy; +} + +/* + * @brief Produces contributions to cooling rates for different + * hydrogen number densities, from different metals, + * tests 1d and 4d table interpolations produce + * same results for cooling rate, dlambda/du and temperature. + */ +int main(int argc, char **argv) { + // Declare relevant structs + struct swift_params *params = malloc(sizeof(struct swift_params)); + struct unit_system us; + struct chemistry_global_data chem_data; + struct part p; + struct xpart xp; + struct phys_const phys_const; + struct cooling_function_data cooling; + struct cosmology cosmo; + char *parametersFileName = "./testCooling.yml"; + + float nh; // hydrogen number density + double u; // internal energy + + /* Number of values to test for in redshift, + * hydrogen number density and internal energy */ + const int n_z = 50; + const int n_nh = 50; + const int n_u = 50; + + /* Number of subcycles and tolerance used to compare + * subcycled and implicit solution. Note, high value + * of tolerance due to mismatch between explicit and + * implicit solution for large timesteps */ + const int n_subcycle = 1000; + const float integration_tolerance = 0.2; + + /* Set dt */ + const float dt_cool = 1.0e-5; + const float dt_therm = 1.0e-5; + + /* Read the parameter file */ + if (params == NULL) error("Error allocating memory for the parameter file."); + message("Reading runtime parameters from file '%s'", parametersFileName); + parser_read_file(parametersFileName, params); + + /* Init units */ + units_init_from_params(&us, params, "InternalUnitSystem"); + phys_const_init(&us, params, &phys_const); + + /* Init chemistry */ + chemistry_init(params, &us, &phys_const, &chem_data); + chemistry_first_init_part(&phys_const, &us, &cosmo, &chem_data, &p, &xp); + chemistry_print(&chem_data); + + /* Init cosmology */ + cosmology_init(params, &us, &phys_const, &cosmo); + cosmology_print(&cosmo); + + /* Init cooling */ + cooling_init(params, &us, &phys_const, &cooling); + cooling_print(&cooling); + cooling_update(&cosmo, &cooling, 0); + + /* Calculate abundance ratios */ + float *abundance_ratio; + abundance_ratio = malloc((chemistry_element_count + 2) * sizeof(float)); + abundance_ratio_to_solar(&p, &cooling, abundance_ratio); + + /* extract mass fractions, calculate table indices and offsets */ + float XH = p.chemistry_data.metal_mass_fraction[chemistry_element_H]; + float HeFrac = + p.chemistry_data.metal_mass_fraction[chemistry_element_He] / + (XH + p.chemistry_data.metal_mass_fraction[chemistry_element_He]); + int He_i; + float d_He; + get_index_1d(cooling.HeFrac, cooling.N_He, HeFrac, &He_i, &d_He); + + /* Cooling function needs to know the minimal energy. Set it to the lowest + * internal energy in the cooling table. */ + struct hydro_props hydro_properties; + hydro_properties.minimal_internal_energy = + exp(M_LN10 * cooling.Therm[0]) / cooling.internal_energy_scale; + + /* calculate spacing in nh and u */ + const float delta_nh = (cooling.nH[cooling.N_nH - 1] - cooling.nH[0]) / n_nh; + const float delta_u = + (cooling.Therm[cooling.N_Temp - 1] - cooling.Therm[0]) / n_u; + + for (int z_i = 0; z_i < n_z; z_i++) { + integertime_t ti_current = max_nr_timesteps / n_z * z_i; + for (int nh_i = 0; nh_i < n_nh; nh_i++) { + nh = exp(M_LN10 * cooling.nH[0] + delta_nh * nh_i); + for (int u_i = 0; u_i < n_u; u_i++) { + u = exp(M_LN10 * cooling.Therm[0] + delta_u * u_i); + + /* update nh, u, z */ + set_quantities(&p, &xp, &us, &cooling, &cosmo, &phys_const, nh, u, + ti_current); + + /* calculate subcycled solution */ + for (int t_subcycle = 0; t_subcycle < n_subcycle; t_subcycle++) { + p.entropy_dt = 0; + cooling_cool_part(&phys_const, &us, &cosmo, &hydro_properties, + &cooling, &p, &xp, dt_cool / n_subcycle, + dt_therm / n_subcycle); + xp.entropy_full += p.entropy_dt * dt_therm / n_subcycle; + } + double u_subcycled = + hydro_get_physical_internal_energy(&p, &xp, &cosmo) * + cooling.internal_energy_scale; + + /* reset quantities to nh, u, and z that we want to test */ + set_quantities(&p, &xp, &us, &cooling, &cosmo, &phys_const, nh, u, + ti_current); + + /* compute implicit solution */ + cooling_cool_part(&phys_const, &us, &cosmo, &hydro_properties, &cooling, + &p, &xp, dt_cool, dt_therm); + double u_implicit = + hydro_get_physical_internal_energy(&p, &xp, &cosmo) * + cooling.internal_energy_scale; + + /* check if the two solutions are consistent */ + if (fabs((u_implicit - u_subcycled) / u_subcycled) > + integration_tolerance) + message( + "implicit and subcycled solutions do not match. z_i %d nh_i %d " + "u_i %d implicit %.5e subcycled %.5e error %.5e", + z_i, nh_i, u_i, u_implicit, u_subcycled, + fabs((u_implicit - u_subcycled) / u_subcycled)); + } + } + } + message("done test"); + + free(params); + return 0; +} + +#else + +int main(int argc, char **argv) { return 0; } + +#endif diff --git a/tests/testCooling.yml b/tests/testCooling.yml new file mode 100644 index 0000000000000000000000000000000000000000..faec32cdfec20b48af7341889c79b60bd2f6bb5b --- /dev/null +++ b/tests/testCooling.yml @@ -0,0 +1,107 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.989e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.085678e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.1 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1e-2 # The end time of the simulation (in internal units). + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-7 # The maximal time-step size of the simulation (in internal units). + +Scheduler: + max_top_level_cells: 15 + +# Parameters governing the snapshots +Snapshots: + basename: coolingBox # Common part of the name of output files + scale_factor_first: 0.142857142857 # Scale-factor of the first snaphot (cosmological run) + time_first: 0.01 # Time of the first output (non-cosmological run) (in internal units) + delta_time: 1.00002 # Time difference between consecutive outputs (in internal units) + compression: 1 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.142857142857 # Scale-factor of the first stat dump (cosmological run) + time_first: 0.01 # Time of the first stat dump (non-cosmological run) (in internal units) + delta_time: 1.00002 # Time between statistics output + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100. # Kelvin + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./coolingBox.hdf5 # The file to read + periodic: 1 + +# Dimensionless pre-factor for the time-step condition +LambdaCooling: + lambda_nH2_cgs: 1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) + cooling_tstep_mult: 1.0 # Dimensionless pre-factor for the time-step condition + +# Dimensionless constant cooling (AB 13/02/18) +ConstCooling: + cooling_rate: 10000.0 + min_energy: 0.0 + cooling_tstep_mult: 1.0 + +# Cooling with Grackle 2.0 +GrackleCooling: + CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository) + WithUVbackground: 0 # Enable or not the UV background + Redshift: 0 # Redshift to use (-1 means time based redshift) + WithMetalCooling: 1 # Enable or not the metal cooling + ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates + ProvideSpecificHeatingRates: 0 # User provide specific heating rates + SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method + OutputMode: 1 # Write in output corresponding primordial chemistry mode + MaxSteps: 1000 + ConvergenceLimit: 1e-2 + +EagleCooling: + filename: /cosma5/data/Eagle/BG_Tables/CoolingTables/ + reionisation_redshift: 8.989 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_ev_pH: 2.0 + +EAGLEChemistry: + InitMetallicity: 0.014 + InitAbundance_Hydrogen: 0.70649785 + InitAbundance_Helium: 0.28055534 + InitAbundance_Carbon: 2.0665436e-3 + InitAbundance_Nitrogen: 8.3562563e-4 + InitAbundance_Oxygen: 5.4926244e-3 + InitAbundance_Neon: 1.4144605e-3 + InitAbundance_Magnesium: 5.907064e-4 + InitAbundance_Silicon: 6.825874e-4 + InitAbundance_Iron: 1.1032152e-3 + CalciumOverSilicon: 0.0941736 + SulphurOverSilicon: 0.6054160 + +GearChemistry: + InitialMetallicity: 0.01295 + diff --git a/tests/testCosmology.c b/tests/testCosmology.c index bafad55471453f7308d1498daa15dbae3a76a6bc..05dc69b2a925b0e19a9bb4a20ac1003b4b30704b 100644 --- a/tests/testCosmology.c +++ b/tests/testCosmology.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include <stdlib.h> +#include "../config.h" /* Includes. */ #include "swift.h" diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 0241a46634bdd12fb8a89bc3912c3b160198c389..e14fddd640764c7e22a217fb483791494ba4fae0 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -111,9 +111,10 @@ struct part *make_particles(size_t count, double *offset, double spacing, */ void prepare_force(struct part *parts, size_t count) { -#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH) && \ - !defined(MINIMAL_SPH) && !defined(PLANETARY_SPH) && \ - !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) +#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH) && \ + !defined(MINIMAL_SPH) && !defined(PLANETARY_SPH) && \ + !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) && \ + !defined(ANARCHY_PU_SPH) struct part *p; for (size_t i = 0; i < count; ++i) { p = &parts[i]; @@ -154,7 +155,7 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { #elif defined(DEFAULT_SPH) p->force.v_sig, 0.f, p->force.u_dt #elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH) || \ - defined(HOPKINS_PU_SPH_MONAGHAN) + defined(HOPKINS_PU_SPH_MONAGHAN) || defined(ANARCHY_PU_SPH) p->force.v_sig, 0.f, p->u_dt #else 0.f, 0.f, 0.f @@ -197,10 +198,10 @@ int check_results(struct part serial_test_part, struct part *serial_parts, struct part vec_test_part, struct part *vec_parts, int count) { int result = 0; - result += compare_particles(serial_test_part, vec_test_part, ACC_THRESHOLD); + result += compare_particles(&serial_test_part, &vec_test_part, ACC_THRESHOLD); for (int i = 0; i < count; i++) - result += compare_particles(serial_parts[i], vec_parts[i], ACC_THRESHOLD); + result += compare_particles(&serial_parts[i], &vec_parts[i], ACC_THRESHOLD); return result; } @@ -558,7 +559,8 @@ void test_force_interactions(struct part test_part, struct part *parts, vizq[i] = pi_vec.v[2]; rhoiq[i] = pi_vec.rho; grad_hiq[i] = pi_vec.force.f; -#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) +#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) && \ + !defined(ANARCHY_PU_SPH) pOrhoi2q[i] = pi_vec.force.P_over_rho2; #endif balsaraiq[i] = pi_vec.force.balsara; @@ -571,7 +573,8 @@ void test_force_interactions(struct part test_part, struct part *parts, vjzq[i] = pj_vec[i].v[2]; rhojq[i] = pj_vec[i].rho; grad_hjq[i] = pj_vec[i].force.f; -#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) +#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) && \ + !defined(ANARCHY_PU_SPH) pOrhoj2q[i] = pj_vec[i].force.P_over_rho2; #endif balsarajq[i] = pj_vec[i].force.balsara; @@ -653,7 +656,8 @@ void test_force_interactions(struct part test_part, struct part *parts, VEC_HADD(a_hydro_zSum, piq[0]->a_hydro[2]); VEC_HADD(h_dtSum, piq[0]->force.h_dt); VEC_HMAX(v_sigSum, piq[0]->force.v_sig); -#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) +#if !defined(HOPKINS_PU_SPH) && !defined(HOPKINS_PU_SPH_MONAGHAN) && \ + !defined(ANARCHY_PU_SPH) VEC_HADD(entropy_dtSum, piq[0]->entropy_dt); #endif diff --git a/tests/testOutputList.c b/tests/testOutputList.c index dbc257aafdee85429d05dc517a86f496443ac0ed..fd69ef91389758adf87aa48ab983a6cfbd6a89a9 100644 --- a/tests/testOutputList.c +++ b/tests/testOutputList.c @@ -17,10 +17,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#include "../config.h" -#include <math.h> -#include <stdio.h> -#include <string.h> +/* Includes. */ #include "swift.h" #define Ntest 3 diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c index e4a5a85845f92bfbecae85d9dc9453b38b9b7646..be83f20a58b17f9a5fdcf967cda9a678aab5b8a9 100644 --- a/tests/testPeriodicBC.c +++ b/tests/testPeriodicBC.c @@ -273,7 +273,7 @@ int check_results(struct part *serial_parts, struct part *vec_parts, int count, int result = 0; for (int i = 0; i < count; i++) - result += compare_particles(serial_parts[i], vec_parts[i], threshold); + result += compare_particles(&serial_parts[i], &vec_parts[i], threshold); return result; } @@ -505,8 +505,8 @@ int main(int argc, char *argv[]) { runner_do_drift_part(&runner, cells[i * (dim * dim) + j * dim + k], 0); - runner_do_sort(&runner, cells[i * (dim * dim) + j * dim + k], 0x1FFF, 0, - 0); + runner_do_hydro_sort(&runner, cells[i * (dim * dim) + j * dim + k], + 0x1FFF, 0, 0); } } } diff --git a/tests/testRandom.c b/tests/testRandom.c new file mode 100644 index 0000000000000000000000000000000000000000..4ac3230705f7c119ce2a4868d2d131375ff20858 --- /dev/null +++ b/tests/testRandom.c @@ -0,0 +1,88 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * 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" + +#include <fenv.h> + +/* Local headers. */ +#include "swift.h" + +int main(int argc, char* argv[]) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FPEs */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + + /* Time-step size */ + const int time_bin = 29; + + /* Try a few different values for the ID */ + for (int i = 0; i < 20; ++i) { + + const long long id = rand() * (1LL << 31) + rand(); + const integertime_t increment = (1LL << time_bin); + + message("Testing id=%lld time_bin=%d", id, time_bin); + + double total = 0., total2 = 0.; + int count = 0; + + /* Check that the numbers are uniform over the full-range of useful + * time-steps */ + for (integertime_t ti_current = 0LL; ti_current < max_nr_timesteps; + ti_current += increment) { + + ti_current += increment; + + const double r = + random_unit_interval(id, ti_current, random_number_star_formation); + + total += r; + total2 += r * r; + count++; + } + + const double mean = total / (double)count; + const double var = total2 / (double)count - mean * mean; + + /* Verify that the mean and variance match the expected values for a uniform + * distribution */ + if ((fabs(mean - 0.5) / 0.5 > 1e-4) || + (fabs(var - 1. / 12.) / (1. / 12.) > 1e-4)) { + message("Test failed!"); + message("Result: count=%d mean=%f var=%f", count, mean, var); + message("Expected: count=%d mean=%f var=%f", count, 0.5f, 1. / 12.); + return 1; + } + } + + return 0; +} diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c index 01c80ce8f15f2be7d264ceecdb397950b822de35..5f3db8a4598d2dd0adab086a08c1f6208bd9b130 100644 --- a/tests/testSelectOutput.c +++ b/tests/testSelectOutput.c @@ -18,7 +18,7 @@ ******************************************************************************/ /* Some standard headers. */ -#include <stdlib.h> +#include "../config.h" /* Includes. */ #include "swift.h" diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index ce1e2e9354c4d59a6e58619d43b743864ed38585..1f0849bb9093948fa68d88984c285c44b403ba79 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -177,6 +177,38 @@ void test(void) { error("Particles 'pj' do not match after density (byte = %d)", j_not_ok); } + /* --- Test the gradient loop --- */ +#ifdef EXTRA_HYDRO_LOOP + + /* Call the symmetric version */ + runner_iact_gradient(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + + /* Call the non-symmetric version */ + runner_iact_nonsym_gradient(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); + dx[0] = -dx[0]; + dx[1] = -dx[1]; + dx[2] = -dx[2]; + runner_iact_nonsym_gradient(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); + + i_not_ok = memcmp((char *)&pi, (char *)&pi2, sizeof(struct part)); + j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part)); + + if (i_not_ok) { + printParticle_single(&pi, &xpi); + printParticle_single(&pi2, &xpi); + print_bytes(&pi, sizeof(struct part)); + print_bytes(&pi2, sizeof(struct part)); + error("Particles 'pi' do not match after gradient (byte = %d)", i_not_ok); + } + if (j_not_ok) { + printParticle_single(&pj, &xpj); + printParticle_single(&pj2, &xpj); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pj' do not match after gradient (byte = %d)", j_not_ok); + } +#endif + /* --- Test the force loop --- */ /* Call the symmetric version */ diff --git a/tests/tolerance_125_perturbed.dat b/tests/tolerance_125_perturbed.dat index 9987f8a0703a6106f41b73c1a16b4cea8af3bc1e..95f5f78246a82b7c326c87f9b4edbac4f51c65e9 100644 --- a/tests/tolerance_125_perturbed.dat +++ b/tests/tolerance_125_perturbed.dat @@ -1,4 +1,4 @@ # ID pos_x pos_y pos_z v_x v_y v_z h rho div_v S u P c a_x a_y a_z h_dt v_sig dS/dt du/dt 0 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 - 0 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 2.3e-3 2e-3 2e-3 1e-4 1e-4 1e-4 1e-4 + 0 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 1e-4 3.6e-3 2e-3 2e-3 1e-4 1e-4 1e-4 1e-4 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-4 2e-4 2e-4 1e-6 1e-6 1e-6 1e-6 diff --git a/theory/Cooling/bibliography.bib b/theory/Cooling/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..c0277fed06c19dbc428978517afc395d7e57d474 --- /dev/null +++ b/theory/Cooling/bibliography.bib @@ -0,0 +1,15 @@ +@ARTICLE{Wiersma2009, + author = {{Wiersma}, R.~P.~C. and {Schaye}, J. and {Smith}, B.~D.}, + title = "{The effect of photoionization on the cooling rates of enriched, astrophysical plasmas}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {0807.3748}, + keywords = {atomic processes , plasmas , cooling flows , galaxies: formation , intergalactic medium}, + year = 2009, + month = feb, + volume = 393, + pages = {99-107}, + doi = {10.1111/j.1365-2966.2008.14191.x}, + adsurl = {http://adsabs.harvard.edu/abs/2009MNRAS.393...99W}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/theory/Cooling/eagle_cooling.tex b/theory/Cooling/eagle_cooling.tex new file mode 100644 index 0000000000000000000000000000000000000000..db01f65f4cd4a48a66c62e640b9c0165626f4bdf --- /dev/null +++ b/theory/Cooling/eagle_cooling.tex @@ -0,0 +1,358 @@ +\documentclass[fleqn, usenatbib, useAMS, a4paper]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} +\usepackage{comment} +\usepackage[super]{nth} + +\newcommand{\todo}[1]{{\textcolor{red}{TODO: #1}\\}} +\newcommand{\swift}{{\sc Swift}\xspace} + +\newcommand{\D}[2]{\frac{d#1}{d#2}} +\newcommand{\LL}{\left(} +\newcommand{\RR}{\right)} + +\title{Integration scheme for cooling} +\author{Alexei Borissov, Matthieu Schaller} + +\begin{document} + +\maketitle + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Basic principles} + +\subsection{Isochoric cooling} + +\todo{MATTHIEU: Discuss the fact that we want to do cooling at constant + density.} + +\subsection{Time integration} + +We want to compute the change in internal energy of a given particle +due to the interaction of the gas with the background radiation. More +specifically we want to integrate the following equation: +\begin{equation} + u_{\rm final} \equiv u(t+\Delta t) = u(t) + \left(\frac{\partial u}{\partial t}\bigg|_{\rm + hydro} + \frac{\partial u}{\partial t}\bigg|_{\rm cooling}\right) + \times \Delta t. +\end{equation} +The first derivative is given by the SPH scheme, the second one is +what we are after here. We start by computing the internal energy the +particle would have in the absence of cooling: +\begin{equation} + u_0 \equiv u(t) + \frac{\partial u}{\partial t}\bigg|_{\rm + hydro} \times \Delta t. +\end{equation} +We then proceed to solve the implicit equation +\begin{equation}\label{implicit-eq} + u_{\rm final} = u_0 + \lambda(u_{\rm final}) \Delta t, +\end{equation} +where $\lambda$ is the cooling rate\footnote{Note this is not the + physical cooling rate $\Lambda/n_{\rm H}^2$ that is commonly + used. This is the change in energy over time $\frac{\partial + u}{\partial t}\big|_{\rm cool}$ from all the channels + including all the multiplicative factors coming in front of the + physical $\Lambda$.}, which for a given particle varies +only with respect to $u$ throughout the duration of the timestep. The +other dependencies of $\lambda$ (density, metallicity and redshift) +are kept constant over the course of $\Delta t$. Crucially, we want to +evaluate $\lambda$ at the end of the time-step. Once a solution to this +implicit problem has been found, we get the total cooling rate: +\begin{equation} + \frac{\partial u}{\partial t}\bigg|_{\rm total} \equiv \frac{u_{\rm final} - + u(t)}{\Delta t}, +\end{equation} +leading to the following total equation of motion for internal energy: +\begin{equation} + u(t+\Delta t) = u(t) + \frac{\partial u}{\partial t}\bigg|_{\rm + total} \times \Delta t. +\end{equation} +The time integration is then performed in the regular time integration +section of the code. Note that, as expected, if $\lambda=0$ the whole +processes reduces to a normal hydro-dynamics only time integration of +the internal energy. + +Finally, for schemes evolving entropy $A$ instead of internal energy +$u$ (or for that matter any other thermodynamic quantity), we convert +the entropy derivative coming from the hydro scheme to an internal +energy derivative, solve the implicit cooling problem using internal +energies and convert the total time derivative back to an entropy +derivative. Since we already assume that cooling is performed at +constant density, there is no loss in accuracy happening via this +conversion procedure. + +\subsubsection{Energy floor and prediction step} + +In most applications, the cooling is not allowed to bring the internal +energy below a certain value $u_{\rm min}$, usually expressed in the +form of a minimal temperature. Additionally, and even in the absence +of such a temperature floor, we must ensure that the energy does not +become negative. + +Since the time-step size is not chosen in a way to fulfil these +criteria, we have to limit the total rate of change of energy such +that the limits are not reached. In practice this means modifying +$\frac{\partial u}{\partial t}\big|_{\rm total}$ such that +\begin{equation} + u(t) + \frac{\partial u}{\partial t}\bigg|_{\rm total} \times \Delta t \geq + u_{\rm min} +\end{equation} +is true. In the vast majority of cases, there is no need to modify the +energy derivative but this may be necessary for some rapidly cooling +particles. + +The time integration uses a leapfrog algorithm in its +``Kick-Drift-Kick'' form. In the cases, where the time-step is +constant, the condition as written above would be sufficient, however +with variable $\Delta t$ this needs modifying. If the next time-step +of a particle decreases in size, then the condition above will remain +true. However, if the time-step size increases then we may violate the +condition and integrate the energy to a value below $u_{\rm min}$. The +time-step size is chosen in-between the application of the two kick +operators\footnote{Effectively creating the chain + ``Kick-Drift-Kick-Timestep'', where the last operation fixes the + time-step size for the next kick-drift-kick cycle.}. We hence have +to ensure that the integration of one half of the current step (the +second kick) and one half of the next step (the first kick) does not +lead to a value below the allowed minimum. In \swift, we do not allow +the time-step to increase by more than a factor of $2$. This implies +that we will at most integrate the internal energy forward in time for +$1.5\Delta t$, where $\Delta t$ is the current value of the time-step +size we used in all the equations thus far. An additional subtlety +does, however, enter the scheme. The internal energy is not just used +in the Kick operator. Because of the needs of the SPH scheme, the +particles have to carry an estimate of the entropy at various points +in time inside the step of this particle. This is especially important +for inactive particles that act as sources for neighbouring active +particles. We must hence not only protect for the next half-kick, we +must also ensure that the value we estimated will be valid over the +next drift step as well. This means completing the current half-kick +and the next full drift, which could in principle double in size; this +implies checking the limits over the next $2.5\Delta t$. However, for +that variable, since it is an extrapolation and not the actual +variable we integrate forward in time, we do not need to enforce the +$u_{\rm min}$ limit. We must only ensure that the energy remains +positive. Combining those two conditions, we conclude that we must +enforce two limits: +\begin{equation} + \left\lbrace + \begin{array}{ll} + \displaystyle\frac{\partial u}{\partial t}\bigg|_{\rm total} \geq + -\displaystyle\frac{u(t) - u_{\rm min} }{1.5 \Delta t}, \Bigg. \\ + \displaystyle\frac{\partial u}{\partial t}\bigg|_{\rm total} \geq + -\displaystyle\frac{u(t) - u_{\rm min} }{(2.5 + \epsilon) \Delta t}, + \end{array} + \right. +\end{equation} +where in the second equation we added a small value $\epsilon$ to +ensure that we will not get negative values because of rounding errors. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Solution to the implicit cooling problem} + +In this section we describe the integration scheme used to compute the +cooling rate. It consists of an explicit solver for cases where the +cooling rate is small, a solver based on the Newton-Raphson method, and +one based on the bisection method. + +\subsection{Explicit solver} + +For many particles the cooling occuring over a timestep will be small +(for example, if a particle is at the equilibrium temperature and was not +heated by other means such as shock heating). In these cases $\lambda(u_0) +\simeq \lambda(u_{final})$, so an explicit solution to compute $u_{final}$ +may be used as a faster alternative to a more complicated implicit scheme. +More specifically, if $\lambda(u_0) dt < \varepsilon u_0$ we set +\begin{equation} +u_{final} = u_0 + \lambda(u_0) dt, +\end{equation} +where $\varepsilon$ is a small constant, set to $0.05$ to be consistent +with the EAGLE simulations. + +In cases where $\lambda(u_0) dt > \varepsilon u_0$ one of two implicit +methods are used, either the Newton-Raphson method, which benefits from +faster convergence, however is not guaranteed to converge, or the +bisection method, which is slower but always converges. + +\subsection{Newton-Raphson method} + +Equation \ref{implicit-eq} may be rearranged so that we are trying to +find the root of +\begin{equation}\label{fu-eq} +f(u_{final}) = u_{final} - u_0 - \lambda(u_{final}) dt = 0. +\end{equation} +This may be done iteratively using the Newton-Raphson method obtaining +consecutive approximations to $u_{final}$ by +\begin{equation} +u_{n+1} = u_n - \frac{f(u_n)}{df(u_n)/du}. +\end{equation} +In some cases a negative value of $u_{n+1}$ may be calculated. To +prevent the occurance of negative internal energies during the +calculation we introduce $x = \log (u_{final})$, so that we solve +\begin{equation}\label{fx-eq} +f(x) = e^x - u_0 - \lambda(e^x) dt = 0 +\end{equation} +instead of \ref{fu-eq}. Thus we obtain consecutive approximations of +the root of $f$ by the formula $x_{n+1} = x_n - f(x_n)/f'(x_n)$. This +leads to +\begin{equation} +x_{n+1} = x_n - \frac{1 - u_0 e^{-x_n} -\lambda(e^{x_n})e^{-x_n}dt}{1 + - \frac{d\lambda}{du}(e^{x_n}) dt}. +\end{equation} + +The tables used for EAGLE cooling in fact depend on temperature rather +than internal energy and include a separate table to convert from +internal energy to temperature. Hence, to obtain the gradient we use +\begin{align*} + \D \lambda u &= \D \lambda T \D T u \\ + &= \frac{\lambda(T_{high,n}) + - \lambda(T_{low,n})}{T_{high,n} - T_{low,n}} + \frac{T(u_{high,n}) + - T(u_{low,n})}{u_{high,n} - u_{low,n}}, +\end{align*} +where $T_{\rm high,n}, u_{\rm high,n}$ and $T_{\rm low,n}, u_{\rm low,n}$ +are values of the temperature and internal energy grid bracketing the current +temperature and internal energy for the iteration in Newton's method +(e.g. $u_{high,n} \ge u_n \ge u_{low,n}$). + +The initial guess for the Newton-Raphson method is taken to be $x_0 = \log(u_0)$. +If in the first iteration the sign of the $\lambda$ changes the next +guess to correspond to the equilibrium temperature (i.e. $10^4$K). + +A particle is considered to have converged if the relative error in +the internal energy is sufficiently small. This can be formulated as +\begin{align*} +\frac{u_{n+1} - u_n}{u_{n+1}} &< C \\ +u_{n+1} - u_n &< Cu_{n+1} \\ +\LL 1-C\RR u_{n+1} &< u_n \\ +\frac{u_{n+1}}{u_n} &< \frac{1}{1-C} \\ +x_{n+1} - x_n = \log\frac{u_{n+1}}{u_n} &< -\log\LL 1-C \RR \simeq C. +\end{align*} +Since the grid spacing in the internal energy of the Eagle tables is +0.045 in $\log_{10}u$ we take $C = 10^{-2}$. + +In cases when the Newton-Raphson method doesn't converge within a specified +number of iterations we revert to the bisection method. In order to use +the Newton-Raphson method a parameter (EagleCooling:newton\_integration) in +the yaml file needs to be set to 1. + +\subsection{Bisection method} + +In order to guarantee convergence the bisection method is used to solve +equation \ref{fu-eq} The implementation is the same as in the EAGLE +simulations, but is described here for convenience. + +First a small interval is used to bracket the solution. The interval bounds +are defined as $u_{upper} = \kappa u_0$ and $u_{lower} = \kappa^{-1} u_0$, +with $\kappa = \sqrt{1.1}$ as specified in EAGLE. If the particle is cooling +($\lambda(u_0) < 0$) $u_{upper}$ and $u_{lower}$ are iteratively decreased +by factors of $\kappa$ until $f(u_{lower}) < 0$. Alternatively, if the +particle is initially heating ($\lambda(u_0) > 0$) the bounds are iteratively +increased by factors of $\kappa$ until $f(u_{upper}) > 0$. Once the bounds +are obtained, the bisection scheme is performed as normal. + +\section{EAGLE cooling tables} + +We use the same cooling tables as used in EAGLE, specifically those found in +\cite{Wiersma2009} and may be found at http://www.strw.leidenuniv.nl/WSS08/. +These tables contain pre-computed values of the cooling rate for a given +redshift, metallicity, hydrogen number density and temperature produced using +the package CLOUDY. When calculating the cooling rate for particles at +redshifts higher than the redshift of reionisation the tables used do not +depend on redshift, but only on metallicity, hydrogen number density and +temperature. These tables are linearly interpolated based on the particle +based on the particle properties. + +Since these tables specify the cooling rate in terms of temperature, the internal +energy of a particle needs to be converted to a temperature in a way which takes +into account the ionisation state of the gas. This is done by interpolating a +pre-computed table of values of temperature depending on redshift, hydrogen number +density, helium fraction and internal energy (again, for redshifts higher than the +redshift of reionisation this table does not depend on redshift). + +Inverse Compton cooling is not accounted for in the high redshift tables, so prior +to reionisation it is taken care of by an analytical formula, +\begin{equation} +\frac{\Lambda_{compton}}{n_h^2} = -\Lambda_{0,compton} \left( T - T_{CMB}(1+z) +\right) (1+z)^4 \frac{n_e}{n_h}, +\end{equation} +which is added to the cooling rate interpolated from the tables. Here $n_h$ is the +hydrogen number density, $T$ the temperature of the particle, $T_{CMB} = 2.7255$K +the temperature of the CMB, $z$ the redshift, $n_e$ the hydrogen and helium electron +number density, and $\Lambda_{0,compton} = 1.0178085 \times 10^{-37} g \cdot cm^2 +\cdot s^{-3} \cdot K^{-5}$. + +\section{Co-moving time integration} + +In the case of cosmological simulations, the equations need to be +slightly modified to take into account the expansion of the +Universe. The code uses the comoving internal energy $u' = +a(t)^{3(\gamma-1)}u$ or comoving entropy $A'=A$ as thermodynamic +variable. The equation of motion for the variable are then modified +and take the following form: +\begin{equation} + \frac{\partial u'_i}{\partial t} = \frac{\partial u'_i}{\partial + t}\bigg|_{\rm hydro} = \frac{1}{a(t)^2} Y'_i(t)\big|_{\rm + hydro}, +\end{equation} +where $Y_i$ is computed from the particle itself and its neighbours +and corresponds to the change in internal energy due to hydrodynamic +forces. We then integrate the internal energy forward in time using +\begin{equation} + u'_i(t+\Delta t) = u'_i(t) + Y'_i(t)\big|_{\rm hydro} \times \underbrace{\int_t^{t+\Delta t} + \frac{1}{a(t)^2} dt}_{\Delta t_{\rm therm}}. +\end{equation} +The exact same equations apply in the case of a hydrodynamics scheme +evolving entropy (see cosmology document). We note that this is +different from the choice made in Gadget where there is no $a^{-2}$ +term as it is absorbed in the definition in $Y'_i$ itself. As a +consequence $\Delta t_{\rm therm}$ is just $\Delta t$. + +In order to compute the +cooling rate of a particle, we convert quantities to physcial +coordinates. Given the appearence of scale-factors in some of these +equations, we have to be careful to remain consistent throughout. We +start by constructing the co-moving internal energy at the end of the +time-step in the absence of cooling: +\begin{equation} + u'_0 \equiv u'(t) + Y'_i(t)\big|_{\rm hydro} \times \Delta t_{\rm therm}, +\end{equation} +which we then convert into a physical internal energy alongside the +thermal energy at the current time: +\begin{align} + u(t) &= a^{3(1-\gamma)}u'(t),\\ + u_0 &= a^{3(1-\gamma)}u'_0. +\end{align} +We can then solve the implicit cooling problem in the same way as in +the non-comoving case and obtain +\begin{equation} + u_{\rm final} = u_0 + \lambda(u_{\rm final}) \Delta t. +\end{equation} +We note that the $\Delta t$ here is the actual time between the start +and end of the step; unlike $\Delta t_{\rm therm}$ there are no +scale-factors entering that term. The solution to the implicit problem +in physical coordinates yields the definition of the total time +derivative of internal energy: +\begin{equation} + \frac{\partial u}{\partial t}\bigg|_{\rm total} \equiv \frac{u_{\rm final} - + u(t)}{\Delta t}. +\end{equation} +This allows us to construct the total eveolution of co-moving energy: +\begin{equation} + Y'_i(t)\big|_{\rm total} = a^{3(\gamma-1)} \times \frac{\Delta t}{\Delta + t_{\rm therm}} \times + \frac{\partial u}{\partial t}\bigg|_{\rm total}, +\end{equation} +where the first term is is the conversion from physical to co-moving +internal energy and the second term is required by our definition of +our time integration opertator. The time integration routine then performs the +same calculation as in the non-cooling case: +\begin{equation} + u'_i(t+\Delta t) = u'_i(t) + Y'_i(t)\big|_{\rm total} \times {\Delta t_{\rm therm}}. +\end{equation} + +\bibliographystyle{mnras} +\bibliography{./bibliography.bib} + +\end{document} diff --git a/theory/Cooling/run.sh b/theory/Cooling/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..17ae407ba66b36b5f192f2b97f7d216a17af26a0 --- /dev/null +++ b/theory/Cooling/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +echo "Generating PDF..." +pdflatex -jobname=eagle_cooling eagle_cooling.tex +bibtex eagle_cooling.aux +pdflatex -jobname=eagle_cooling eagle_cooling.tex +pdflatex -jobname=eagle_cooling eagle_cooling.tex diff --git a/theory/Cosmology/bibliography.bib b/theory/Cosmology/bibliography.bib index 94ac688f7043e1e911561e460b1fc607ce1e1421..84cec263d2e8195bc831e672184b41d61479fcc2 100644 --- a/theory/Cosmology/bibliography.bib +++ b/theory/Cosmology/bibliography.bib @@ -138,7 +138,6 @@ doi = "https://doi.org/10.1006/jcph.1997.5732", url = "http://www.sciencedirect.com/science/article/pii/S0021999197957326", author = "J.J. Monaghan" } - @article{Cullen2010, author = {Cullen, Lee and Dehnen, Walter}, title = {{Inviscid smoothed particle hydrodynamics}}, @@ -162,4 +161,18 @@ pages = {41--50}, month = sep } - +@ARTICLE{Springel2010, + author = {{Springel}, V.}, + title = "{E pur si muove: Galilean-invariant cosmological hydrodynamical simulations on a moving mesh}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {0901.4107}, + keywords = {methods: numerical, galaxies: interactions, cosmology: dark matter}, + year = 2010, + month = jan, + volume = 401, + pages = {791-851}, + doi = {10.1111/j.1365-2966.2009.15715.x}, + adsurl = {http://adsabs.harvard.edu/abs/2010MNRAS.401..791S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/theory/Cosmology/cosmology_standalone.tex b/theory/Cosmology/cosmology_standalone.tex index 56f31297e55cef298b20204db0ee35867ad1789b..5b5fa228fe4cd1c5cfbd64a5ddb7a7ec466fa7a7 100644 --- a/theory/Cosmology/cosmology_standalone.tex +++ b/theory/Cosmology/cosmology_standalone.tex @@ -3,6 +3,7 @@ \usepackage{amsmath,paralist,xcolor,xspace,amssymb} \usepackage{times} \usepackage{comment} +\usepackage{bbold} \usepackage[super]{nth} \newcommand{\todo}[1]{{\textcolor{red}{#1}}} @@ -44,6 +45,8 @@ Making cosmology great again. \input{artificialvisc} +\input{gizmo} + \bibliographystyle{mnras} \bibliography{./bibliography.bib} diff --git a/theory/Cosmology/gizmo.tex b/theory/Cosmology/gizmo.tex new file mode 100644 index 0000000000000000000000000000000000000000..785cb673bb0a4e60a843ae02d26d8a7ecbee9f74 --- /dev/null +++ b/theory/Cosmology/gizmo.tex @@ -0,0 +1,194 @@ +\subsection{Derviation of the GIZMO equations} + +For GIZMO and other finite volume/mass schemes, the equations of motion can no +longer be derived from a Lagrangian. Instead, we have to explicitly transform +the Euler equations to comoving coordinates and variables. In a physical +coordinate frame, these equations are +\begin{align} +\frac{\partial{}\rho{}}{\partial{}t} + +\nabla{} \cdot \left(\rho{} \mathbf{v}_{\rm tot} \right) +&= 0, \\ +\frac{\partial{} \rho{} \mathbf{v}_{\rm tot} }{\partial{} t} + +\nabla{} \cdot \left( \rho{} \mathbf{v}_{\rm tot} \mathbf{v}_{\rm tot} + +P \mathbb{1} \right) &= 0. +\end{align} +\begin{multline} +\frac{\partial{}}{\partial{} t} \left( \rho{} u + \frac{1}{2} \rho{} +\mathbf{v}_{\rm{} tot}\cdot\mathbf{v}_{\rm{} tot} \right) +\\ +\nabla{} \cdot \left( \rho{} u \mathbf{v}_{\rm{} tot} + +\frac{1}{2} \rho{} \left(\mathbf{v}_{\rm{} tot} \cdot \mathbf{v}_{\rm{} tot}\right) +\mathbf{v}_{\rm{} tot} + P \mathbf{v}_{\rm{} tot} \right) = 0, +\end{multline} +where $\mathbb{1}$ is the unit tensor. +For simplicity, we will rewrite the last two equations in terms of the quantities +$\mathbf{v}_{\rm{} tot}$ and $u$: +\begin{align} +\frac{\partial{} \mathbf{v}_{\rm{} tot}}{\partial{} t} + +\left(\mathbf{v}_{\rm{} tot} \cdot \nabla{} \right) \mathbf{v}_{\rm{} tot} + +\frac{1}{\rho{}}\nabla{} P &= 0,\\ +\frac{\partial{} u}{\partial{} t} + +\left(\mathbf{v}_{\rm{} tot} \cdot \nabla{} \right) u + +\frac{P}{\rho{}} \nabla{} \cdot \mathbf{v}_{\rm{} tot} &= 0. +\end{align} +To convert to comoving coordinates, we need to take into account the +appropriate operator transformations: +\begin{align} +\nabla{} &\rightarrow{} \frac{1}{a} \nabla{}', \\ +\frac{\partial{}}{\partial{} t} &\rightarrow{} +\frac{\partial{}}{\partial{} t} - \frac{\dot{a}}{a} \mathbf{r}' \cdot \nabla{}', +\end{align} +the latter of which follows from the explicit time dependence of the +comoving coordinate $\mathbf{r}'$. Substituting the definitions of the +comoving variables and operators into the first Euler equation, we +obtain +\begin{equation} +\frac{\partial{} \rho{}'}{\partial{} t} + \frac{1}{a^2} \nabla{}' \cdot \left( +\rho{}' \mathbf{v}' \right) = 0, +\end{equation} +which is the same as the original continuity equation but now with an extra +$1/a^2$ for the second term (the same correction factor that appears +for the ``drift'' in the SPH case). For the velocity equation, we find +\begin{multline} +\frac{\partial{} \mathbf{v}'}{\partial{} t} + \frac{1}{a^2} \left( \mathbf{v}' \cdot +\nabla{}' \right) \mathbf{v}' + \frac{1}{a^{3(\gamma{} - 1)}\rho{}'} +\nabla{}' P' = \\ +- \nabla{}' \left( \frac{1}{2} a \ddot{a} \mathbf{r}'\cdot\mathbf{r}' +\right). +\end{multline} +The right hand side of this equation is simply the extra term we absorb in the +potential through the gauge transformation; the $1/a^2$ dependence in the +second term on the left hand side again corresponds to the SPH ``drift'' factor, +while the more complicated correction factor for the third term corresponds +to the SPH ``kick'' factor in the equation of motion for the +velocity. Finally, the thermal energy equation reduces to +\begin{equation} +\frac{\partial{} u'}{\partial{} t} + \frac{1}{a^2} \left( \mathbf{v}' . +\nabla{}' \right) u' + \frac{1}{a^2} \frac{P'}{\rho{}'} \nabla{}' \cdot \mathbf{v}' += 0. +\end{equation} +Again, this gives us the same correction factors as used by the SPH energy +equation.\\ + +Unfortunately, the system of equations above is no longer consistent with the +original equations. For the continuity equation, we can transform to a new +time coordinate $t'$ defined by +\begin{equation} +\dot{t}' = \frac{1}{a^2}, \quad{} t'(t) = \int \frac{dt}{a^2(t)} + {\rm{}const,} +\end{equation} +and end up with the original continuity equation in comoving variables. The +same transformation brings the thermal energy equation into its original form +in comoving variables. Unfortunately, the same does not work for the velocity +equation due to the $a^{-3(\gamma{}-1)}$ factor in the third term (note that +for the specific choice $\gamma{}=5/3$ this procedure would work). +To get around this issue, we will rewrite the velocity equation as +\begin{multline} +\frac{\partial{} \mathbf{v}'}{\partial{} t} + \frac{1}{a^2} \left( +\mathbf{v}' \cdot +\nabla{}' \right) \mathbf{v}' + \frac{1}{a^2\rho{}'} +\nabla{}' P' = \\ +- \nabla{}' \left( \frac{1}{2} a \ddot{a} \mathbf{r}'\cdot\mathbf{r}' +\right) - \left(\frac{1}{a^{3(\gamma{} - 1)}} - \frac{1}{a^2} \right) +\nabla{}'P' +\end{multline} +and treat the extra correction term on the right hand side as a source term, +like we do for the gravitational acceleration. This means we end up with +a fully consistent set of comoving Euler equations, so that we can solve the +Riemann problem in the comoving frame. + +If we now convert the primitive Euler equations back to their conservative form, +we end up with the following set of equations: +\begin{equation} +\frac{\partial{} \rho{}'}{\partial{} t} + \frac{1}{a^2} \nabla{}' \cdot \left( +\rho{}' \mathbf{v}' \right) = 0, +\end{equation} +\begin{multline} +\frac{\partial{} \rho{}' \mathbf{v}'}{\partial{} t} + \frac{1}{a^2} +\nabla{} \cdot \left( \rho{}' \mathbf{v}'\mathbf{v}' + P' \right) = \\ +- \rho{}' \nabla{}' \left( \frac{1}{2} a \ddot{a} \mathbf{r}'\cdot\mathbf{r}' +\right) - \left(\frac{1}{a^{3(\gamma{} - 1)}} - \frac{1}{a^2} \right) +\nabla{}'P', +\end{multline} +\begin{multline} +\frac{\partial{}}{\partial{} t} \left( \rho{}' u' + \frac{1}{2} \rho{}' +\mathbf{v}'\cdot\mathbf{v}' \right) +\\ +\frac{1}{a^2}\nabla{} \cdot \left( \rho{}' u' \mathbf{v}' + +\frac{1}{2} \rho{}' \left(\mathbf{v}'\cdot \mathbf{v}'\right) +\mathbf{v}' + P' \mathbf{v}' \right) =\\ +- \rho{}' \mathbf{v}'\cdot\nabla{}' \left( \frac{1}{2} a \ddot{a} \mathbf{r}'\cdot +\mathbf{r}' \right) - +\left(\frac{1}{a^{3(\gamma{} - 1)}} - \frac{1}{a^2} \right) +\mathbf{v}'\cdot\nabla{}'P'. +\end{multline} +These equations tell us that the mass, comoving momentum and comoving total +energy are conserved in the case of adiabatic expansion ($\gamma{} = 5/3$). +For a more general $\gamma{}$, we however end up with extra source terms that +depend on $\nabla{}'P'$. Since we already use this quantity for the gradient +reconstruction step, adding this term is straightforward. The additional time +step required to integrate the source term is +\begin{multline} +\Delta{} t_{\rm{} kick,c} \equiv \int_{a_n}^{a_{n+1}} +\left(\frac{1}{a^{3(\gamma{} - 1)}} - \frac{1}{a^2} \right) dt \\ = +\Delta{} t_{\rm{} kick,h} - \Delta{} t_{\rm{} drift}. +\end{multline} + +The last issue we need to address is the appropriate scale factor for the +gravitational correction term that is used by the finite volume flavour of +GIZMO. Remember that in GIZMO we evolve the comoving conserved quantities. The +evolution equations for the conserved quantities of particle $i$ are then +simply given by integrating over the +comoving ``volume'' of the particle and adding the appropriate correction terms +(we ignore the comoving correction terms for this derivation): +\begin{align} +\frac{d m_i'}{dt} &= -\frac{1}{a^2} \sum_j +\mathbf{F}_{m,ij}'\left(\rho{}'\mathbf{v}'\right),\\ +\frac{d \mathbf{p}_i'}{dt} &= -\frac{1}{a^2} \sum_j +\mathbf{F}_{p,ij}'\left(\rho{}'\mathbf{v}'\mathbf{v}' + +P\mathbb{1}\right) - \frac{1}{a}\nabla{}'\phi{}_i', +\end{align} +\begin{multline} +\frac{d E_i'}{dt} = -\frac{1}{a^2} \sum_j +\mathbf{F}_{E,ij}'\left( \rho{}' u' \mathbf{v}' + +\frac{1}{2} \rho{}' \left(\mathbf{v}'\cdot \mathbf{v}'\right) +\mathbf{v}' + P' \mathbf{v}' \right) \\ +- \frac{1}{a} \mathbf{p}_i'\cdot{}\nabla{}'\phi{}_i', +\end{multline} +where $\mathbf{F}_{X,ij}'(Y)$ represents the appropriately geometrically evaluated +flux $Y$ for conserved quantity $X$ between particle $i$ and particle $j$. +In finite volume GIZMO, the particle +velocity $\mathbf{v}_i' = \mathbf{w}_i' + \mathbf{v}_{i,{\rm{}rel}}'$ consists of the +actual particle movement $\mathbf{w}_i'$ and the relative movement of the fluid +w.r.t. the particle movement, $\mathbf{v}_{i,{\rm{}rel}}'$. +We can therefore replace the gravitational contribution +to the energy evolution with \citep{Springel2010} +\begin{equation} +\mathbf{p}_i'\cdot{}\nabla{}'\phi{}_i' \rightarrow{} m_i'\mathbf{w}_i' \cdot{} +\nabla{}'\phi{}_i' + \int{} \rho{}'\left(\mathbf{v}' - +\mathbf{w}_i' \right)\cdot{} +\nabla{}'\phi{}' dV +\end{equation} +to get a more accurate update of the total energy. If we make the following +approximation +\begin{equation} +\rho{}'\left(\mathbf{v}' - \mathbf{w}_i' \right) \approx{} +\left(\mathbf{r}' - \mathbf{r}_i'\right) \nabla{}' \cdot{} +\left( \rho{}' \left( \mathbf{v}' - \mathbf{w}_i' \right) \right) +\end{equation} +and assume that the force is constant over the ``volume'' of the particle, then +the second term in the gravity contribution reduces to +\begin{multline} +\int{} \rho{}'\left(\mathbf{v}' - +\mathbf{w}_i' \right)\cdot{} +\nabla{}'\phi{}' dV \approx{} \\\sum_j \frac{1}{2} +\left(\mathbf{r}_j' - \mathbf{r}_i'\right) +a^2 \mathbf{F}_{m,ij}'\left(\rho{}'\mathbf{v}'\right) \cdot{} +\nabla{}'\phi{}'_i. +\end{multline} +This means that the gravitational correction term will have a total scale factor +dependence $a$ instead of the $1/a$ for the normal gravitational contribution +and the $1/a^2$ for the hydrodynamical flux. We hence need an additional time +step +\begin{equation} +\Delta{}t_{\rm{}kick,corr} = \int_{a_n}^{a_n+1} adt = \frac{1}{H_0} +\int_{a_n}^{a_n+1} \frac{da}{E(a)} +\end{equation} +that needs to be precomputed. diff --git a/theory/Cosmology/operators.tex b/theory/Cosmology/operators.tex index 89aa32bae554dceba8f1525cb209728a17154f5b..4ec4ea4b5fa49082295675420f562fa9e45e3e18 100644 --- a/theory/Cosmology/operators.tex +++ b/theory/Cosmology/operators.tex @@ -37,19 +37,21 @@ time using $\Delta t_{\rm kick,A} = \Delta t_{\rm operator. They then use $\int H dt$ as the operator, which integrates out trivially. This slight inconsistency with the rest of the time-integration operators is unlikely to lead to any practical - difference.}, whilst the change in energy due to the expansion of -the Universe (first term in eq.~\ref{eq:cosmo_eom_u}) can be computed -using -\begin{equation} - \int_{a_n}^{a_{n+1}} H dt = \int_{a_n}^{a_{n+1}} \frac{da}{a} = - \log{a_{n+1}} - \log{a_n}. -\end{equation} + difference.}. We additionally compute a few other terms +appearing in some viscosity terms and subgrid models. There are the +difference in cosmic time between the start and the end of the step +and the corresponding change in redshift: +\begin{align} + \Delta t_{\rm cosmic} &= \int_{a_n}^{a_{n+1}} dt = \frac{1}{H_0} + \int_{a_n}^{a_{n+1}} \frac{da}{a E(a)},\\ + \Delta z &= \frac{1}{a_n} - \frac{1}{a_{n+1}} \approx -\frac{H}{a} \Delta t_{\rm cosmic}. +\end{align} Following the same method as for the age of the Universe -(sec. \ref{ssec:flrw}), the three non-trivial integrals are evaluated -numerically at the start of the simulation for a series $10^4$ values -of $a$ placed at regular intervals between $\log a_{\rm begin}$ and -$\log a_{\rm end}$. The values for a specific pair of scale-factors -$a_n$ and $a_{n+1}$ are then obtained by interpolating that table -linearly. +(sec. \ref{ssec:flrw}), these three non-trivial integrals are +evaluated numerically at the start of the simulation for a series +$10^4$ values of $a$ placed at regular intervals between $\log a_{\rm + begin}$ and $\log a_{\rm end}$. The values for a specific pair of +scale-factors $a_n$ and $a_{n+1}$ are then obtained by interpolating +that table linearly. diff --git a/theory/Cosmology/timesteps.tex b/theory/Cosmology/timesteps.tex index 4a1c2ef534d32c667f1b5b655e9b93ae618b8c99..a9856d03a2d04cc0c917176a828fc135a83fb745 100644 --- a/theory/Cosmology/timesteps.tex +++ b/theory/Cosmology/timesteps.tex @@ -40,3 +40,7 @@ dominates the overall time-step size calculation. \subsubsection{Conversion from time to integer time-line} +\begin{equation} + \int_{a_n}^{a_{n+1}} H dt = \int_{a_n}^{a_{n+1}} \frac{da}{a} = + \log{a_{n+1}} - \log{a_n}. +\end{equation} diff --git a/theory/SPH/Flavours/anarchy.tex b/theory/SPH/Flavours/anarchy.tex new file mode 100644 index 0000000000000000000000000000000000000000..5924f9438f9b553298b0d45a8e4d7ddae9167270 --- /dev/null +++ b/theory/SPH/Flavours/anarchy.tex @@ -0,0 +1,123 @@ +\section{ANARCHY-SPH} +\label{sec:sph:anarchy} + +This section is loosely based on Dalla Vecchia (\textit{in prep.}), also described in section 2.2.2 of +\cite{Schaller2015}.\\ + +The version of ANARCHY that is currently implemented in \swift{} is based on the Pressure-Energy +(P-U) SPH scheme, rather than the original Pressure-Entropy. This was chosen to make the +implementation of sub-grid physics easier, as well as injection of energy more accurate as +no iteration of the entropy-energy relation is required. + +ANARCH SPH comprises of: +\begin{itemize} + \item Pressure-Energy SPH + \item \citet[][, henceforth C\&D]{cullen2010} variable artificial viscosity + \item A basic thermal diffusion term + \item The time-step limiter from \citet{durier2012}. +\end{itemize} + +\subsection{Equations of Motion} + +The following smoothed quantities are required, and are calculated in the density loop: +\begin{itemize} + \item $\rho_i = [h_i^{-n_d}]\sum_j m_j w_{i}$ + \item $(d\rho/dh)_i = - [h_i^{-n_d - 1}]\sum_j m_j ( n_d * w_i + x_i \nabla_i w_i)$ + \item $\bar{P}_i = [(\gamma - 1)h_i^{-n_d}]\sum_j m_j u_j w_{i}$ + \item $(d\bar{P}/dh)_i = - [(\gamma - 1)h_i^{-n_d - 1}]\sum_j m_j u_j ( n_d * w_i + x_i \nabla_i w_i)$ + \item $n_i = [h_i^{-n_d}]\sum_j w_{i}$ + \item $(dn/dh)_i = - [h_i^{-n_d - 1}]\sum_j ( n_d * w_i + x_i \nabla_i w_i)$ + \item $(\nabla \cdot \mathbf{v})_i = - [a^{-2} \rho_i^{-1} h^{-n_d - 1}]\sum_j m_j \mathbf{v}_{ij} \cdot \tilde{\mathbf{x}}_{ij} \nabla_i w_i$ + % Think the cosmo factor entering here is wrong... + \item $(\nabla \times \mathbf{v})_i = - [a^{-2} \rho_i^{-1} h^{-n_d - 1} + Hn_d]\sum_j m_j \mathbf{v}_{ij} \times \tilde{\mathbf{x}}_{ij} \nabla_i w_i$ +\end{itemize} +with quantities in square brackets added in {\tt hydro\_end\_density} and: +\begin{itemize} + \item $h_i$ the smoothing length of particle $i$ + \item $n_d$ the number of hydro dimensions + \item $m_j$ the mass of particle $j$ + \item $w_{i}$ the dimensionless kernel evalulated at $x_i = r / h_i$ + \item $r$ the interparticle separation of particles $i$ and $j$ + \item $u_i$ the internal energy of the particle + \item $\gamma$ the ratio of specific heats + \item $\mathbf{v}_{ij}$ the difference between the velocities of particle $i$ and $j$ + \item $\tilde{\mathbf{x}}_{ij}$ the unit vector connecting particles $i$ and $j$ + \item $a$ the current scale factor + \item $H$ the current Hubble constant +\end{itemize} + +The ANARCHY scheme requires a gradient loop, as intermediate smoothed quantities are required +for the artificial viscosity and diffusion schemes. The following quatntities are calculated: +\begin{itemize} + \item $v_{{\rm sig}, i} = \rm{max}(v_{\rm sig}, c_i + c_j - 3\mathbf{v}_{ij} \cdot \tilde{\mathbf{x}}_{ij})$ + \item $\nabla^2 u_i = [2]\sum_j m_j \frac{u_i - u_j}{\rho_j} \frac{\nabla_i W_i}{r_{ij}}$ +\end{itemize} +with quantities in square brackets added in {\tt hydro\_end\_gradient} and: +\begin{itemize} + \item $v_{\rm sig}$ the siginal velocity + \item $c_i$ the sound speed of particle $i$ +\end{itemize} + +In {\tt hydro\_prepare\_force}, the differential equations for the viscosity and +diffusion schemes are integrated as follows. This includes some logic, so it is +split into viscosity: +\begin{itemize} + \item $\tau_i = h_i / (2 v_{{\rm sig}, i} \ell$ + \item $\dot{\nabla \cdot \mathbf{v}_i} = + \left({\nabla \cdot \mathbf{v}_i}(t+dt) - {\nabla \cdot \mathbf{v}_i}(t)\right) + / dt$ + \item $S_i = h_i^2 {\rm max}(0, -\dot{\nabla \cdot \mathbf{v}_i})$ + \item $\alpha_{{\rm loc}, i} = \alpha_{\rm max} S_i / (S_i + v_{{\rm sig}, i}^2)$. +\end{itemize} +and diffusion: +\begin{itemize} + \item $\dot{\tilde{\alpha}}_i = \beta h_i \frac{\nabla^2 u_i}{\sqrt{u_i}}$ +\end{itemize} +where: +\begin{itemize} + \item $\alpha_i$ is the viscosity coefficient + \item $\tilde{\alpha}_i$ is the diffusion coefficient + \item $\tau_i$ is the timescale for decay + \item $\ell$ is the viscosity length coefficient + \item $\beta$ is the diffusion length coefficient +\end{itemize} +The equations are then integrated as follows for viscosity: +\begin{enumerate} + \item If $\alpha_{\rm loc} > \alpha_i$, update $\alpha_i$ to $\alpha_{\rm loc}$ + immediately. + \item Otherwise, decay the viscosity, with $\dot{\alpha}_i = (\alpha_{\rm loc} - \alpha_i) / \tau_i$. + This equation is integrated with the same time-step as the velocity divergence derivative + uses, and is the same time-step used for the cooling. + \item Finally, if $\alpha_i < \alpha_{\rm min}$, $\alpha_i$ is reset to that minimal + value. +\end{enumerate} +and for diffusion: +\begin{enumerate} + \item First, find the new diffusion coefficient, $\tilde{\alpha}_i(t+dt) = + \tilde{\alpha}_i(t) + \dot{\tilde{\alpha}}_i \cdot dt$, using the + same time-step as for the viscosity. + \item If this is outside of the bounds set for the coefficient, set it + to the respective upper or lower bound. +\end{enumerate} +The final force loop calculates the equations of motion for the particles ready for +their time-integration. The following quantities are calculated: +\begin{itemize} + \item $\mathbf{a}_{\rm hydro} = -\sum_j m_j u_i u_j (\gamma - 1)^2 \left( + \frac{f_{ij}}{\bar{P}_i} \nabla_i W_i + \frac{f_{ji}}{\bar{P}_j} \nabla_j W_j\right)$ + \item $\mathbf{a}_{\rm visc} = - \frac{1}{8}\sum_j (\alpha_i + \alpha_j) v_{{\rm sig}, i} + \mu_{ij} (b_i + b_j) (\nabla_i W_i + \nabla_j W_j)/ (\rho_i + \rho_j)$ + \item $\dot{u}_{ij, {\rm hydro}} = \sum_j m_j u_i u_j (\gamma - 1)^2 + \frac{f_{ij}}{\bar{P}_i} \nabla_i W_i$ + \item $\dot{u}_{ij, {\rm visc}} = \frac{1}{2} \a_{\rm visc} (\mathbf{v}_{ij} \cdot \tilde{\mathbf{x}}_{ij} + r^2a^2 H)$ + \item $v_{{\rm diff}, i} = {\rm max}(0, c_i + c_j + \mathbf{v}_{ij} \cdot \tilde{\mathbf{x}}_{ij} + r^2a^2 H)$ + \item $\dot{u}_{ij, {\rm diff}} = \frac{1}{2}(\tilde{\alpha}_i + \tilde{\alpha}_j) a^{(3\gamma - 5)/2)} + v_{{\rm diff}, i} (u_i - u_j) (\nabla_i W_i + \nabla_j W_j)/ (\rho_i + \rho_j) $ + \item $\dot{u}_i = \sum_j \dot{u}_{ij, {\rm hydro}} + \dot{u}_{ij, {\rm visc}} + \dot{u}_{ij, {\rm diff}}$ +\end{itemize} +where: +\begin{itemize} + \item $f_{ij}$ are the variable smoothing length correction factors + \item $b_i$ is the Balsara switch for particle $i$ + \item $\mu_{ij} = a^{(3\gamma - 5)/2) {\rm min}(\mathbf{v}_{ij} \cdot \tilde{\mathbf{x}}_{ij} + r^2a^2 H, 0)$ +\end{itemize} + diff --git a/theory/SPH/Flavours/sph_flavours.tex b/theory/SPH/Flavours/sph_flavours.tex index 81ac2153ed23f29f14345e0377774548420c84c9..d84bdcc0b42129e9d5008051dd6e0e212e4e9463 100644 --- a/theory/SPH/Flavours/sph_flavours.tex +++ b/theory/SPH/Flavours/sph_flavours.tex @@ -626,8 +626,3 @@ The rest of the artificial viscosity implementation, including the \citet{Balsara1995} switch, is the same - just with $\alpha \rightarrow \alpha_{ij}$. -\subsection{Anarchy SPH} -Dalla Vecchia (\textit{in prep.}), also described in section 2.2.2 of -\cite{Schaller2015}.\\ -\label{sec:sph:anarchy} -\tbd diff --git a/theory/SPH/Flavours/sph_flavours_standalone.tex b/theory/SPH/Flavours/sph_flavours_standalone.tex index 20c9f1451c2499d661bfbb1022bfd34c02fde4dd..7cc92fdb438ea09916dfbd12bfb554ddb9fc2ecd 100644 --- a/theory/SPH/Flavours/sph_flavours_standalone.tex +++ b/theory/SPH/Flavours/sph_flavours_standalone.tex @@ -24,6 +24,7 @@ \maketitle \input{sph_flavours} +\input{anarchy} \bibliographystyle{mnras} \bibliography{./bibliography} diff --git a/theory/SPH/swift_sph.tex b/theory/SPH/swift_sph.tex index e9c185c3cd0b845bff75be2092846bffbdcfd1a9..51ab6c3e49ae6b74d8b63590caeadd962cd6e4d5 100644 --- a/theory/SPH/swift_sph.tex +++ b/theory/SPH/swift_sph.tex @@ -34,6 +34,7 @@ \section{SPH flavours} \input{Flavours/sph_flavours} +\input{Flavours/anarchy} \bibliographystyle{mnras} \bibliography{./bibliography} diff --git a/theory/Star_Formation/bibliography.bib b/theory/Star_Formation/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..95a3678d868a3229075bb98db1f1f6db3d9b05c3 --- /dev/null +++ b/theory/Star_Formation/bibliography.bib @@ -0,0 +1,86 @@ +@ARTICLE{schaye2008, + author = {{Schaye}, J. and {Dalla Vecchia}, C.}, + title = "{On the relation between the Schmidt and Kennicutt-Schmidt star formation laws and its implications for numerical simulations}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {0709.0292}, + keywords = {stars: formation , galaxies: evolution , galaxies: formation , galaxies: ISM}, + year = 2008, + month = jan, + volume = 383, + pages = {1210-1222}, + doi = {10.1111/j.1365-2966.2007.12639.x}, + adsurl = {http://adsabs.harvard.edu/abs/2008MNRAS.383.1210S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{schaye2004, + author = {{Schaye}, J.}, + title = "{Star Formation Thresholds and Galaxy Edges: Why and Where}", + journal = {\apj}, + eprint = {astro-ph/0205125}, + keywords = {Galaxies: Evolution, Galaxies: Formation, Galaxies: ISM, ISM: Clouds, Stars: Formation}, + year = 2004, + month = jul, + volume = 609, + pages = {667-682}, + doi = {10.1086/421232}, + adsurl = {http://adsabs.harvard.edu/abs/2004ApJ...609..667S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{kennicutt1998, + author = {{Kennicutt}, Jr., R.~C.}, + title = "{The Global Schmidt Law in Star-forming Galaxies}", + journal = {\apj}, + eprint = {astro-ph/9712213}, + keywords = {GALAXIES: EVOLUTION, GALAXIES: ISM, GALAXIES: SPIRAL, GALAXIES: STELLAR CONTENT, GALAXIES: STARBURST, STARS: FORMATION, Galaxies: Evolution, Galaxies: ISM, Galaxies: Spiral, Galaxies: Starburst, Galaxies: Stellar Content, Stars: Formation}, + year = 1998, + month = may, + volume = 498, + pages = {541-552}, + doi = {10.1086/305588}, + adsurl = {http://adsabs.harvard.edu/abs/1998ApJ...498..541K}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{dallavecchia2012, + author = {{Dalla Vecchia}, C. and {Schaye}, J.}, + title = "{Simulating galactic outflows with thermal supernova feedback}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1203.5667}, + keywords = {methods: numerical, ISM: bubbles, ISM: jets and outflows, galaxies: evolution, galaxies: formation, galaxies: ISM }, + year = 2012, + month = oct, + volume = 426, + pages = {140-158}, + doi = {10.1111/j.1365-2966.2012.21704.x}, + adsurl = {http://adsabs.harvard.edu/abs/2012MNRAS.426..140D}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{schaye2015, + author = {{Schaye}, J. and {Crain}, R.~A. and {Bower}, R.~G. and {Furlong}, M. and + {Schaller}, M. and {Theuns}, T. and {Dalla Vecchia}, C. and + {Frenk}, C.~S. and {McCarthy}, I.~G. and {Helly}, J.~C. and + {Jenkins}, A. and {Rosas-Guevara}, Y.~M. and {White}, S.~D.~M. and + {Baes}, M. and {Booth}, C.~M. and {Camps}, P. and {Navarro}, J.~F. and + {Qu}, Y. and {Rahmati}, A. and {Sawala}, T. and {Thomas}, P.~A. and + {Trayford}, J.}, + title = "{The EAGLE project: simulating the evolution and assembly of galaxies and their environments}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1407.7040}, + keywords = {methods: numerical, galaxies: evolution, galaxies: formation, cosmology: theory}, + year = 2015, + month = jan, + volume = 446, + pages = {521-554}, + doi = {10.1093/mnras/stu2058}, + adsurl = {http://adsabs.harvard.edu/abs/2015MNRAS.446..521S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + diff --git a/theory/Star_Formation/run.sh b/theory/Star_Formation/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..7608ef7cb7e5ecc07a38a773e550cb42beec6fb7 --- /dev/null +++ b/theory/Star_Formation/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +echo "Generating PDF..." +pdflatex -jobname=starform starformation_standalone.tex +bibtex starform.aux +pdflatex -jobname=starform starformation_standalone.tex +pdflatex -jobname=starform starformation_standalone.tex diff --git a/theory/Star_Formation/starformation.tex b/theory/Star_Formation/starformation.tex new file mode 100644 index 0000000000000000000000000000000000000000..c1b8d511ce2f3cded2850a9b30a696dd76b8b47f --- /dev/null +++ b/theory/Star_Formation/starformation.tex @@ -0,0 +1,122 @@ +\section{Star Formation in EAGLE} + +In this section we will shortly explain how the star formation in EAGLE works. +The implemented star formation is based on the \citet{schaye2008}, instead of +the constant density threshold used by \citet{schaye2008}, a metallicity +dependent density threshold is used, following \citet{schaye2004}. An important +property of the implemented star formation law is the explicit reproducability +of the Kennicutt-Schmidt star formation law \citep{kennicutt1998}: +\begin{align} + \dot{\Sigma}_\star &= A \left( \frac{\Sigma}{1 ~\text{M}_\odot ~\text{pc}^{-2}} \right) +\end{align} + +\noindent In which $A$ is the normalization of the Kennicutt-Schmidt, $\dot{\Sigma}_\star$ +is the surface density of newly formed stars, $\Sigma$ is the gas surface +density and $n$ is the power law index. In the case of the star formation +implementation of \citet{schaye2008}, the star formation law is given by +a pressure law: + +\begin{align} +\dot{m}_\star &= m_g A ( 1~\text{M}_\odot~\text{pc}^{-2})^{-n} \left( +\frac{\gamma}{G} f_g P \right)^{(n-1)/2}. +\end{align} + +\noindent In which $m_g$ is the gas particle mass, $\gamma$ is the ratio of specific heats, +$G$ is the gravitational constant, $f_g$ is the mass fraction of gas (unity in +EAGLE), and $P$ is the total pressure of the gas particle. In this equation +$A$ and $n$ are directly constrainted from the observations of the Kennicutt- +Schmidt law so both variables do not require tuning. Further there are +two constrains on the over density which should be $\Delta > 57.7$ (why this +specific number? \citet{schaye2008} says $\Delta \approx 60$), and the +temperature of the gas should be atleast $T_\text{crit}<10^5 ~\text{K}$ + +Besides this it is required that there is an effective equation of state. +Specifically we could take this to be equal to: +\begin{align} + P &= P_\text{eos} (\rho) = P_\text{tot,c}\left( \frac{\rho_\text{g}}{\rho_\text{g,c}} \right)^{\gamma_\text{eff}}. +\end{align} +\noindent In which $\gamma_\text{eff}$ is the polytropic index. But the EAGLE +code just uses the EOS of the gas? + +\noindent Using this it is possible to calculate the propability that a gas particle is +converted to a star particle: +\begin{align} + \text{Prob.} = \text{min} \left( \frac{\dot{m}_\star \Delta t}{m_g}, 1 \right) + = \text{min} \left( A \left( 1 ~\text{M}_\odot ~\text{pc}^{-2} \right)^{-n} \left( \frac{\gamma}{G} f_g P_\text{tot} \right)^{(n-1)/2}, 1 \right). +\end{align} + +\noindent In general we use $A=1.515 \cdot 10^{-4}~\text{M}_\odot ~\text{yr}^{-1} ~\text{kpc}^{-2}$ +and $n=1.4$. In the case of high densities ($n_\text{H,thresh} > 10^3 ~\text{cm}^{-3}$), +the power law will be steaper and have a value of $n=2$ \citep{schaye2015}. This will also adjust +the normalization of the star formation law, both need to be equal at the +pressure with a corresponding density. This means we have: +\begin{align} +\begin{split} + A \left( 1 ~\text{M}_\odot ~\text{pc}^{-2} \right)^{-n} \left( \frac{\gamma}{G} f_g P_\text{tot} \right)^{(n-1)/2} \\ + = A_\text{high} \left( 1 ~\text{M}_\odot ~\text{pc}^{-2} \right)^{-n_\text{high}} \left( \frac{\gamma}{G} f_g P_\text{tot} \right)^{(n_\text{high}-1)/2}. +\end{split} +\end{align} +\begin{align} +A_\text{high} = A \left( 1 ~\text{M}_\odot ~\text{pc}^{-2} \right)^{n_\text{high}-n} \left( \frac{\gamma}{G} f_g P_\text{tot}(\rho_{hd}) \right)^{(n-n_\text{high})/2}. +\end{align} +In which $\rho_{hd}$ is the density at which both laws are equal. + +This is differently from the EAGLE code ($f_g=1$) which uses: +\begin{align} +A_\text{high} = A \left( \frac{\gamma}{G} P_\text{tot} (\rho_{hd}) \right)^{(n-n_\text{high})/2} . +\end{align} + +Besides this we also use the metallicity dependent density threshold given by \citep{schaye2004}: +\begin{align} +n^*_\text{H} (Z) &= n_\text{H,norm} \left( \frac{Z}{Z_0} \right)^{n_z}. +\end{align} +In which $n_\text{H,norm}$ is the normalization of the metallicity dependent +star formation law, $Z$ the metallicity, $Z_0$ the normalization metallicity, +and $n_Z$ the power law of the metallicity dependence on density. standard +values we take for the EAGLE are $n_\text{H,norm} = 0.1 ~\text{cm}^{-3}$, +$n_Z=-0.64$ and $Z_0 = 0.002$. Also we impose that the density threshold cannot +exceed the maximum value of $n_\text{H,max,norm}$ \citep{schaye2015}. + +For the initial pressure determination the EAGLE code uses (Explanation needed): +\begin{align} + P_\text{cgs} &= (\gamma -1) \frac{n_\text{EOS, norm} \cdot m_H}{X} T_{EOS,jeans} \cdot \frac{k_B}{1.22 \cdot (\gamma -1) m_H } \left( \frac{n_\text{highden}}{n_\text{norm,EOS}} \right)^{\gamma_\text{eff}}. +\end{align} + +To determine the pressure for the star formation law the EAGLE code uses the +physical pressure? Is this the effective EOS or the real EOS of the gas? + +Compared to the EAGLE code we can calculate a fraction of the calculations already +in the struct which are not depending on time, this may save some calculations. + +Besides this we also use the more extended temperature criteria proposed by +\citet{dallavecchia2012} that uses a temperature floor given by: +\begin{align} + \log_{10} T < \log_{10} T_\text{eos} + 0.5. +\end{align} + +\begin{table} +\begin{tabular}{l|l|l|l} +Variable & Parameter file name & Default value & unit \\ \hline +$A$ & SchmidtLawCoeff\_MSUNpYRpKPC2 & $1.515\cdot10^{-4}$ & $M_\odot ~yr^{-1} ~kpc^{-2}$ \\ +$n$ & SchmidtLawExponent & $1.4$ & none \\ +$\gamma$ & gamma & $\frac{5}{3}$ & none \\ +$G$ & No, in constants & - & - \\ +$f_g$ & fg & $1.$ & none \\ +$n_{high}$ & SchmidtLawHighDensExponent & $2.0$ & none \\ +$n_{H,thresh}$ & SchmidtLawHighDens\_thresh\_HpCM3 & $10^3$ & $cm^{-3}$ \\ +$n_{H,norm}$ & thresh\_norm\_HpCM3 & $.1$ & $cm^{-3}$ \\ +$Z_0$ & MetDep\_Z0 & $0.002$ & none \\ +$n_Z$ & MetDep\_SFthresh\_Slope & $-0.64$ & none \\ +$\Delta$ & thresh\_MinOverDens & $57.7$ & none \\ +$T_{crit}$ & thresh\_temp & $10^5$ & $K$ \\ +$n_{H,max,norm}$ & thresh\_max\_norm\_HpCM3 & 10.0 & $cm^{-3}$ +\end{tabular} +\end{table} + +Questions: +Is rho in part mass density or number density?? +Why is the cooling\_get\_temperature() depending on so many variables? +I would expect $P_\text{tot,c} = n k_B T$, but which $n$ and $T$? +Is Seed in the function or declared outside of function? +Correct Unit conversion? + diff --git a/theory/Star_Formation/starformation_standalone.tex b/theory/Star_Formation/starformation_standalone.tex new file mode 100644 index 0000000000000000000000000000000000000000..518179a393f7c35ce56091e6b74eaccb133c65de --- /dev/null +++ b/theory/Star_Formation/starformation_standalone.tex @@ -0,0 +1,43 @@ +\documentclass[fleqn, usenatbib, useAMS, a4paper]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} +\usepackage{comment} +\usepackage[super]{nth} + +\newcommand{\todo}[1]{{\textcolor{red}{#1}}} +\newcommand{\gadget}{{\sc Gadget}\xspace} +\newcommand{\swift}{{\sc Swift}\xspace} +\newcommand{\nbody}{$N$-body\xspace} +\newcommand{\Lag}{\mathcal{L}} + +%opening +\title{Star formation equations in SWIFT} +\author{Folkert Nobels} +\begin{document} + +\date{\today} + +\pagerange{\pageref{firstpage}--\pageref{lastpage}} \pubyear{2018} + +\maketitle + +\label{firstpage} + +\begin{abstract} +Making stars all over again. +\end{abstract} + +\begin{keywords} +\end{keywords} + +\input{starformation} + + + +\bibliographystyle{mnras} +\bibliography{./bibliography.bib} + +\label{lastpage} + +\end{document} diff --git a/tools/analyse_runtime.py b/tools/analyse_runtime.py index 5cf2bbce7f44d5ddca6c6f6c2f372ead835bd569..fda47afc946405c475b3d3809a2cfd1a2d28d656 100755 --- a/tools/analyse_runtime.py +++ b/tools/analyse_runtime.py @@ -53,90 +53,55 @@ threshold = 0.008 num_files = len(sys.argv) - 1 labels = [ - "Gpart assignment", - "Mesh comunication", - "Forward Fourier transform", - "Green function", - "Backwards Fourier transform", - "engine_recompute_displacement_constraint:", - "engine_exchange_top_multipoles:", - "updating particle counts", - "Making gravity tasks", - "Making hydro tasks", - "Splitting tasks", - "Counting and linking tasks", - "Setting super-pointers", - "Making extra hydroloop tasks", - "Linking gravity tasks", - "Creating send tasks", - "Exchanging cell tags", - "Creating recv tasks", - "Setting unlocks", - "Ranking the tasks", - "scheduler_reweight:", - "space_list_useful_top_level_cells:", - "space_rebuild:", - "engine_drift_all:", - "engine_unskip:", - "engine_collect_end_of_step:", - "engine_launch:", - "writing particle properties", - "engine_repartition:", - "engine_exchange_cells:", - "Dumping restart files", - "engine_print_stats:", - "engine_marktasks:", - "Reading initial conditions", - "engine_print_task_counts:", - "engine_drift_top_multipoles:", - "Communicating rebuild flag", - "engine_split:", - "space_init", - "engine_init", - "engine_repartition_trigger" -] -is_rebuild = [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 + ["Gpart assignment", 1], + ["Mesh comunication", 1], + ["Forward Fourier transform", 1], + ["Green function", 1], + ["Backwards Fourier transform", 1], + ["engine_recompute_displacement_constraint:", 1], + ["engine_exchange_top_multipoles:", 1], + ["updating particle counts", 1], + ["engine_estimate_nr_tasks:", 1], + ["Making gravity tasks", 1], + ["Making hydro tasks", 1], + ["Splitting tasks", 1], + ["Counting and linking tasks", 1], + ["Setting super-pointers", 1], + ["Making extra hydroloop tasks", 1], + ["Making extra starsloop tasks", 1], + ["Linking gravity tasks", 1], + ["Creating send tasks", 1], + ["Exchanging cell tags", 1], + ["Creating recv tasks", 1], + ["Counting number of foreign particles", 1], + ["Recursively linking foreign arrays", 1], + ["Setting unlocks", 1], + ["Ranking the tasks", 1], + ["scheduler_reweight:", 1], + ["space_list_useful_top_level_cells:", 1], + ["space_rebuild:", 1], + ["engine_drift_all:", 0], + ["engine_unskip:", 0], + ["engine_collect_end_of_step:", 0], + ["engine_launch:", 0], + ["writing particle properties", 0], + ["engine_repartition:", 0], + ["engine_exchange_cells:", 1], + ["Dumping restart files", 0], + ["engine_print_stats:", 0], + ["engine_marktasks:", 1], + ["Reading initial conditions", 0], + ["engine_print_task_counts:", 0], + ["engine_drift_top_multipoles:", 0], + ["Communicating rebuild flag", 0], + ["engine_split:", 0], + ["space_init", 0], + ["engine_init", 0], + ["engine_repartition_trigger:", 0], + ["VR Collecting top-level cell info", 0], + ["VR Collecting particle info", 0], + ["VR Invokation of velociraptor", 0], + ["VR Copying group information back", 0] ] times = np.zeros(len(labels)) counts = np.zeros(len(labels)) @@ -176,20 +141,20 @@ for i in range(num_files): for i in range(len(labels)): # Extract the different blocks - if re.search("%s took" % labels[i], line): + if re.search("%s took" % labels[i][0], line): counts[i] += 1.0 times[i] += float( re.findall(r"[+-]?((\d+\.?\d*)|(\.\d+))", line)[-1][0] ) - # Find the last line with meaningful output (avoid crash report, batch system stuf....) + # Find the last line with meaningful output (avoid crash report, batch system stuff....) if re.findall(r"\[[0-9]{4}\][ ]\[*", line) or re.findall( r"^\[[0-9]*[.][0-9]+\][ ]", line ): lastline = line # Total run time - total_time += float(re.findall(r"[+-]?([0-9]*[.])?[0-9]+", lastline)[1]) + total_time += float(re.findall(r"[+-]?(\[[0-9]\])?(\[[0-9]*[.][0-9]*\])+", lastline)[0][1][1:-1]) # Conver to seconds times /= 1000.0 @@ -205,35 +170,33 @@ time_ratios = times / total_time # Better looking labels for i in range(len(labels)): - labels[i] = labels[i].replace("_", " ") - labels[i] = labels[i].replace(":", "") - labels[i] = labels[i].title() + labels[i][0] = labels[i][0].replace("_", " ") + labels[i][0] = labels[i][0].replace(":", "") + labels[i][0] = labels[i][0].title() times = np.array(times) time_ratios = np.array(time_ratios) -is_rebuild = np.array(is_rebuild) # Sort in order of importance order = np.argsort(-times) times = times[order] counts = counts[order] time_ratios = time_ratios[order] -is_rebuild = is_rebuild[order] -labels = np.take(labels, order) +labels = [labels[i] for i in order] # Keep only the important components important_times = [0.0] important_ratios = [0.0] -important_labels = ["Others (all below %.1f\%%)" % (threshold * 100)] important_is_rebuild = [0] +important_labels = ["Others (all below %.1f\%%)" % (threshold * 100)] need_print = True print("Time spent in the different code sections:") for i in range(len(labels)): if time_ratios[i] > threshold: important_times.append(times[i]) important_ratios.append(time_ratios[i]) - important_labels.append(labels[i]) - important_is_rebuild.append(is_rebuild[i]) + important_is_rebuild.append(labels[i][1]) + important_labels.append(labels[i][0]) else: if need_print: print("Elements in 'Other' category (<%.1f%%):" % (threshold * 100)) @@ -241,7 +204,7 @@ for i in range(len(labels)): important_times[0] += times[i] important_ratios[0] += time_ratios[i] - print(" - '%-40s' (%5d calls): %.4f%%" % (labels[i], counts[i], time_ratios[i] * 100)) + print(" - '%-40s' (%5d calls, time: %.4fs): %.4f%%" % (labels[i][0], counts[i], times[i], time_ratios[i] * 100)) # Anything unaccounted for? print( @@ -252,8 +215,8 @@ print( important_ratios = np.array(important_ratios) important_is_rebuild = np.array(important_is_rebuild) -figure() +figure() def func(pct): return "$%4.2f\\%%$" % pct diff --git a/tools/check_interactions.sh b/tools/check_interactions.sh index 24a534b154313927ee4b2a108d3da7ea5f4d1f31..d688e69bb36b628905668183989d08204604c631 100755 --- a/tools/check_interactions.sh +++ b/tools/check_interactions.sh @@ -20,7 +20,7 @@ cd examples/SedovBlast_3D/ ./getGlass.sh python makeIC.py -../swift -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_naive.hdf5 @@ -29,7 +29,7 @@ cd ../EAGLE_12/ # Link to ICs ln -s /gpfs/data/Swift/web-storage/ICs/EAGLE_ICs_12.hdf5 EAGLE_ICs_12.hdf5 -../swift -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_naive.hdf5 @@ -45,13 +45,13 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ -../swift -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_serial.hdf5 cd ../EAGLE_12/ -../swift -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_serial.hdf5 @@ -67,7 +67,7 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ -../swift -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_vec.hdf5 @@ -98,7 +98,7 @@ fi cd ../EAGLE_12/ -../swift -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +../swift --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_vec.hdf5 @@ -145,13 +145,13 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_naive.hdf5 cd ../EAGLE_12/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_naive.hdf5 @@ -167,13 +167,13 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_serial.hdf5 cd ../EAGLE_12/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_serial.hdf5 @@ -189,7 +189,7 @@ make clean; make -j 6 cd examples/SedovBlast_3D/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 sedov.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv sedov_0000.hdf5 sedov_vec.hdf5 @@ -220,7 +220,7 @@ fi cd ../EAGLE_12/ -mpirun -np 4 ../swift_mpi -s -t 16 -n 5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 +mpirun -np 4 ../swift_mpi --hydro --threads=16 --steps=5 eagle_12.yml -P SPH:h_tolerance:10 -P Snapshots:compression:7 mv eagle_0000.hdf5 eagle_12_vec.hdf5 diff --git a/tools/combine_ics.py b/tools/combine_ics.py index 447af1cc3d6d9c09e6322648a0dc5035382e857c..64f255a61934bc3667fdb5934f74a206013e4872 100755 --- a/tools/combine_ics.py +++ b/tools/combine_ics.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ Usage: - combine_ics.py input_file.0.hdf5 merged_file.hdf5 + combine_ics.py input_file.0.hdf5 merged_file.hdf5 gzip_level This file combines Gadget-2 type 2 (i.e. hdf5) initial condition files into a single file that can be digested by SWIFT. @@ -11,6 +11,9 @@ the DM particles is handled. No unit conversions are applied nor are any scale-factors or h-factors changed. The script applies some compression and checksum filters to the output to save disk space. +The last argument `gzip_level` is used to specify the level of compression +to apply to all the fields in the file. Use 0 to cancel all coompression. +The default value is `4`. This file is part of SWIFT. Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) @@ -35,6 +38,11 @@ import sys import h5py as h5 import numpy as np +# Store the compression level +gzip_level = 4 +if len(sys.argv) > 3: + gzip_level = sys.argv[3] + # First, we need to collect some information from the master file main_file_name = str(sys.argv[1])[:-7] print("Merging snapshots files with name", main_file_name) @@ -45,19 +53,19 @@ grp_header = master_file["/Header"] num_files = grp_header.attrs["NumFilesPerSnapshot"] tot_num_parts = grp_header.attrs["NumPart_Total"] -tot_num_parts_high_word = grp_header.attrs["NumPart_Total"] +tot_num_parts_high_word = grp_header.attrs["NumPart_Total_HighWord"] entropy_flag = grp_header.attrs["Flag_Entropy_ICs"] box_size = grp_header.attrs["BoxSize"] time = grp_header.attrs["Time"] # Combine the low- and high-words +tot_num_parts = tot_num_parts.astype(np.int64) for i in range(6): - tot_num_parts[i] += np.int64(tot_num_parts_high_word[i]) << 32 + tot_num_parts[i] += (np.int64(tot_num_parts_high_word[i]) << 32) # Some basic information print("Reading", tot_num_parts, "particles from", num_files, "files.") - # Check whether there is a mass table DM_mass = 0.0 mtable = grp_header.attrs.get("MassTable") @@ -105,7 +113,7 @@ def create_set(grp, name, size, dim, dtype): dtype=dtype, chunks=True, compression="gzip", - compression_opts=4, + compression_opts=gzip_level, shuffle=True, fletcher32=True, maxshape=(size,), @@ -117,7 +125,7 @@ def create_set(grp, name, size, dim, dtype): dtype=dtype, chunks=True, compression="gzip", - compression_opts=4, + compression_opts=gzip_level, shuffle=True, fletcher32=True, maxshape=(size, dim), diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py new file mode 100644 index 0000000000000000000000000000000000000000..fd7c8f8f7c4165f38947828f64a7f3fd26f41ee5 --- /dev/null +++ b/tools/plot_task_dependencies.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python3 +""" +This file generates a graphviz file that represents the SWIFT tasks + dependencies. + +Example: ./plot_task_dependencies.py dependency_graph_*.csv +""" +from pandas import read_csv +import numpy as np +from subprocess import call +from optparse import OptionParser + + +def parseOption(): + parser = OptionParser() + + parser.add_option( + "-c", "--with-calls", dest="with_calls", + help="Add the function calls in the graph", + action="store_true") + + opt, files = parser.parse_args() + if len(files) != 1: + raise Exception("You need to provide one file") + + return opt, files + + +def getGitVersion(f, git): + """ + Read the git version from the file + + Parameters + ---------- + + f: str + Filename + + git: str + Git version of previous file + + Returns + ------- + + new_git: str + Git version of current file + """ + # read comment in csv file + with open(f, "r") as f: + line = f.readline() + + # check if really a comment + if line[0] != "#": + return None + + # remove trailing characters + new_git = line[2:].rstrip() + + # check if previous and current are the same + if git is not None and git != new_git: + raise Exception("Files were not produced by the same version") + + return new_git + + +def appendSingleData(data0, datai): + """ + Append two DataFrame together + + Parameters + ---------- + + data0: DataFrame + One of the dataframe + + datai: DataFrame + The second dataframe + + Returns + ------- + + data0: DataFrame + The updated dataframe + """ + + # loop over all rows in datai + for i, row in datai.iterrows(): + # get data + ta = datai["task_in"][i] + tb = datai["task_out"][i] + ind = np.logical_and(data0["task_in"] == ta, + data0["task_out"] == tb) + + # check number of ta->tb + N = np.sum(ind) + if N > 1: + raise Exception("Same dependency written multiple times %s->%s" % + (ta, tb)) + # if not present in data0 + if N == 0: + data0.append(row) + else: + # otherwise just update the number of link + ind = ind[ind].index[0] + tmp = data0["number_link"][ind] + datai["number_link"][i] + data0.at[ind, "number_link"] = tmp + + return data0 + + +def appendData(data): + """ + Append all the dataframe together + + Parameters + ---------- + + data: list + List containing all the dataframe to append together + + Returns + ------- + + data: DataFrame + The complete dataframe + """ + N = len(data) + if N == 1: + return data[0] + + # add number link to data[0] + for i in range(N-1): + i += 1 + data[0] = appendSingleData(data[0], data[i]) + + return data[0] + + +def taskIsStars(name): + """ + Does the task concern stars? + + Parameters + ---------- + + name: str + Task name + """ + if "stars" in name or "spart" in name: + return True + return False + + +def taskIsHydro(name): + """ + Does the task concern the hydro? + + Parameters + ---------- + + name: str + Task name + """ + if "_part" in name: + return True + if "density" in name and "stars" not in name: + return True + if "rho" in name: + return True + if "force" in name: + return True + if "xv" in name: + return True + + task_name = [ + "sort", + "ghost_in", + "ghost", + "ghost_out", + ] + if name in task_name: + return True + return False + + +def taskIsGravity(name): + """ + Does the task concern the gravity? + + Parameters + ---------- + + name: str + Task name + """ + if "gpart" in name: + return True + if "grav" in name: + return True + return False + + +def getFunctionCalls(name): + txt = None + if name == "ghost": + txt = """hydro_end_density, chemistry_end_density,<br/> + hydro_prepare_gradient, hydro_reset_gradient,<br/> + hydro_prepare_force, hydro_reset_acceleration,<br/> + hydro_init_part, chemistry_init_part,<br/> + hydro_has_no_neighbours, chemistry_part_has_no_neighbours + """ + + elif name == "cooling": + txt = "cooling_cool_part" + + elif name == "timestep": + txt = "tracers_after_timestep" + + elif name == "drift_part": + txt = """drift_part, tracers_after_drift,<br/> + hydro_init_part, chemistry_init_part,<br/> + tracers_after_init + """ + + elif name == "kick1": + txt = "kick_part, kick_gpart, kick_spart" + + elif name == "kick2": + txt = """kick_part, kick_gpart, kick_spart,<br/> + hydro_reset_predicted_values, + gravity_reset_predicted_Values,<br/> + stars_reset_predicted_values, + """ + + elif name == "end_force": + txt = """hydro_end_force, gravity_end_force,<br/> + stars_end_force""" + + elif name == "drift_gpart": + txt = """drift_gpart, gravity_init_gpart,<br/> + drift_spart + """ + + if "density" in name and "stars" not in name: + txt = """runner_iact_nonsym_chemistry, runner_iact_chemistry,<br/> + runner_iact_nonsym_density, runner_iact_density""" + + if "force" in name and "end" not in name: + txt = "runner_iact_nonsym_density, runner_iact_density" + + if txt is None: + return None + else: + pre = "<" + name + "<BR/> <Font POINT-SIZE='10'>Calls: " + app = "</Font>>" + return pre + txt + app + + +def writeTask(f, name, implicit, mpi, with_calls): + """ + Write the special task (e.g. implicit and mpi) + + Parameters + ---------- + + f: File + File where to write the data + + name: str + Task name + + implicit: int + Is the task implicit + + mpi: int + Is the task MPI related + + with_calls: bool + if true, write down the function calls + """ + # generate text + txt = "\t " + name + "[" + + if implicit: + txt += "style=filled,fillcolor=lightgrey," + if mpi: + txt += "shape=diamond," + + if taskIsStars(name): + txt += "color=darkorange1," + + if taskIsHydro(name): + txt += "color=blue3," + + if taskIsGravity(name): + txt += "color=red3," + + if with_calls: + func = getFunctionCalls(name) + if func is not None: + txt += "label=" + func + "," + + # remove extra ',' + if txt[-1] == ",": + txt = txt[:-1] + txt += "];\n" + + # write it + f.write(txt) + + +def writeHeader(f, data, git, opt): + """ + Write the header and the special tasks + + Parameters + ---------- + + f: File + File where to write the data + + data: DataFrame + The dataframe to write + + git: str + The git version + + opt: object + The options provided to this script + """ + # write header + f.write("digraph task_dep {\n") + f.write("\t # Header\n") + f.write('\t label="Task dependencies for SWIFT %s";\n' % git) + f.write("\t compound=true;\n") + f.write("\t ratio=0.66;\n") + f.write("\t node[nodesep=0.15];\n") + + f.write("\n") + + # write the special task + f.write("\t # Special tasks\n") + N = len(data) + written = [] + # do task in + for i in range(N): + ta = data["task_in"][i] + if ta in written: + continue + + written.append(ta) + writeTask(f, ta, data["implicit_in"][i], data["mpi_in"][i], + opt.with_calls) + + # do task out + for i in range(N): + tb = data["task_out"][i] + if tb in written: + continue + + written.append(tb) + writeTask(f, tb, data["implicit_out"][i], data["mpi_out"][i], + opt.with_calls) + + f.write("\n") + + +def writeCluster(f, tasks, cluster): + """ + Write a single cluster + + Parameters + ---------- + + f: File + File where to write the data + + tasks: list + List of all tasks in the cluster + + cluster: str + Cluster name + """ + f.write("\t subgraph cluster%s {\n" % cluster) + f.write('\t\t label="";\n') + for t in tasks: + f.write("\t\t %s;\n" % t) + f.write("\t };\n\n") + + +def writeClusters(f, data): + """ + Write all the clusters + + Parameters + ---------- + + f: File + File where to write the data + + data: DataFrame + The dataframe to write + """ + f.write("\t # Clusters\n") + # get list of all the clusters + clusters = data[["cluster_in", "cluster_out"]] + clusters = np.unique(clusters) + + cluster_in = data["cluster_in"] + cluster_out = data["cluster_out"] + # loop over all clusters + for cluster in clusters: + # is it a cluster? + if cluster == "None": + continue + + # get all the task in current cluster + ta = data["task_in"][cluster_in == cluster] + tb = data["task_out"][cluster_out == cluster] + + # make them unique + tasks = np.append(ta, tb) + tasks = np.unique(tasks) + + # write current cluster + writeCluster(f, tasks, cluster) + + f.write("\n") + + +def writeDependencies(f, data): + """ + Write all the dependencies between tasks + + Parameters + ---------- + + f: File + File where to write the data + + data: DataFrame + The dataframe to write + + """ + f.write("\t # Dependencies\n") + N = len(data) + written = [] + max_rank = data["number_rank"].max() + for i in range(N): + # get data + ta = data["task_in"][i] + tb = data["task_out"][i] + number_link = data["number_link"][i] + + # check if already done + name = "%s_%s" % (ta, tb) + if name in written: + raise Exception("Found two same task dependencies") + + written.append(name) + + # write relation + arrow = "" + if data["number_rank"][i] != max_rank: + arrow = ",style=dashed" + f.write("\t %s->%s[label=%i%s]\n" % + (ta, tb, number_link, arrow)) + + +def writeFooter(f): + """ + Write the footer + + Parameters + ---------- + + f: File + File where to write the data + """ + f.write("}") + + +if __name__ == "__main__": + + opt, files = parseOption() + + # output + dot_output = "dependency_graph.dot" + png_output = "dependency_graph.png" + + # read files + data = [] + git = None + for f in files: + tmp = read_csv(f, delimiter=",", comment="#") + git = getGitVersion(f, git) + data.append(tmp) + + data = appendData(data) + + # write output + with open(dot_output, "w") as f: + writeHeader(f, data, git, opt) + + writeClusters(f, data) + + writeDependencies(f, data) + + writeFooter(f) + + call(["dot", "-Tpng", dot_output, "-o", png_output]) + + print("You will find the graph in %s" % png_output) + + if opt.with_calls: + print("We recommand to use the python package xdot available on pypi:") + print(" python -m xdot %s" % dot_output) diff --git a/tools/plot_task_dependencies.sh b/tools/plot_task_dependencies.sh deleted file mode 100755 index 77784d8a9cdd3720621c9ad35c4cfbdaf0167ff1..0000000000000000000000000000000000000000 --- a/tools/plot_task_dependencies.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Creates a graphic from the task graph file dependency_graph.dot. -# Requires the graphviz command "dot". - -if [ ! -e dependency_graph.dot ]; then - echo "Missing task-graph output 'dependency_graph.dot'! Cannot generate figure." -else - dot -Tpng dependency_graph.dot -o task_graph.png - echo "Output written to task_graph.png" -fi - -exit diff --git a/tools/task_plots/analyse_tasks.py b/tools/task_plots/analyse_tasks.py index 19860167668c481f006c52d9122d16fa17fa7fb9..d5f33bd9fe915ccb7b329c4feca1292d5d582004 100755 --- a/tools/task_plots/analyse_tasks.py +++ b/tools/task_plots/analyse_tasks.py @@ -82,6 +82,7 @@ TASKTYPES = [ "kick1", "kick2", "timestep", + "timestep_limiter", "send", "recv", "grav_long_range", @@ -91,11 +92,11 @@ TASKTYPES = [ "grav_mesh", "cooling", "star_formation", - "sourceterms", "logger", "stars_ghost_in", "stars_ghost", "stars_ghost_out", + "stars_sort", "fof_self", "fof_pair", "count", @@ -106,6 +107,7 @@ SUBTYPES = [ "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend", @@ -115,6 +117,7 @@ SUBTYPES = [ "multipole", "spart", "stars_density", + "stars_feedback", "count", ] diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py index c19ecf5ce638604e8f701f0dd69178b925dca6ec..51ad46d0c054aada6c050732470d5bc8f1fcd453 100755 --- a/tools/task_plots/plot_tasks.py +++ b/tools/task_plots/plot_tasks.py @@ -167,6 +167,7 @@ TASKTYPES = [ "kick1", "kick2", "timestep", + "timestep_limiter", "send", "recv", "grav_long_range", @@ -176,11 +177,11 @@ TASKTYPES = [ "grav_mesh", "cooling", "star_formation", - "sourceterms", "logger", "stars_ghost_in", "stars_ghost", "stars_ghost_out", + "stars_sort", "fof_self", "fof_pair", "count", @@ -191,6 +192,7 @@ SUBTYPES = [ "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend", @@ -200,20 +202,29 @@ SUBTYPES = [ "multipole", "spart", "stars_density", + "stars_feedback", "count", ] # Task/subtypes of interest. FULLTYPES = [ + "self/limiter", "self/force", + "self/gradient", "self/density", "self/grav", + "sub_self/limiter", "sub_self/force", + "sub_self/gradient", "sub_self/density", + "pair/limiter", "pair/force", + "pair/gradient", "pair/density", "pair/grav", + "sub_pair/limiter", "sub_pair/force", + "sub_pair/gradient", "sub_pair/density", "recv/xv", "send/xv", @@ -227,6 +238,10 @@ FULLTYPES = [ "pair/stars_density", "sub_self/stars_density", "sub_pair/stars_density", + "self/stars_feedback", + "pair/stars_feedback", + "sub_self/stars_feedback", + "sub_pair/stars_feedback", ] # A number of colours for the various types. Recycled when there are