diff --git a/.gitignore b/.gitignore index 9fb1fa18de02b1394d1789b88dd33624f1df193e..0edad37768b7f4020ae63a3eed0826750ff7adfe 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ doc/Doxyfile examples/swift examples/swift_mpi +examples/fof +examples/fof_mpi examples/*/*.xmf examples/*/*.h5 examples/*/*.png diff --git a/examples/Makefile.am b/examples/Makefile.am index 227b164d538e7c2d68d7d58d5f83b6770d9a2f13..dae300933aa1c8a479d4c44b604c5ad11b5a0217 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -33,9 +33,12 @@ MPI_FLAGS = -DWITH_MPI $(PARMETIS_INCS) $(METIS_INCS) # Programs. bin_PROGRAMS = swift +bin_PROGRAMS += fof + # Build MPI versions as well? if HAVEMPI bin_PROGRAMS += swift_mpi +bin_PROGRAMS += fof_mpi endif # engine_policy_setaffinity is available? @@ -55,6 +58,16 @@ 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) +# Sources for fof +fof_SOURCES = main_fof.c +fof_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +fof_LDADD = ../src/.libs/libswiftsim.a $(EXTRA_LIBS) + +# Sources for fof_mpi, do we need an affinity policy for MPI? +fof_mpi_SOURCES = main_fof.c +fof_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" +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 \ diff --git a/examples/main_fof.c b/examples/main_fof.c new file mode 100644 index 0000000000000000000000000000000000000000..fce6ad74be5df49883d20a4f5396a48cf05c8234 --- /dev/null +++ b/examples/main_fof.c @@ -0,0 +1,1023 @@ +/******************************************************************************* + * 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" + +/* Some standard headers. */ +#include <errno.h> +#include <fenv.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +/* MPI headers. */ +#ifdef WITH_MPI +#include <mpi.h> +#endif + +/* Local headers. */ +#include "swift.h" + +/* Engine policy flags. */ +#ifndef ENGINE_POLICY +#define ENGINE_POLICY engine_policy_none +#endif + +/* 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"); +} + +/** + * @brief Main routine that loads a few particles and generates some output. + * + */ +int main(int argc, char *argv[]) { + + struct clocks_time tic, toc; + struct engine e; + + /* Structs used by the engine. Declare now to make sure these are always in + * scope. */ + struct chemistry_global_data chemistry; + struct cooling_function_data cooling_func; + struct cosmology cosmo; + struct external_potential potential; + struct pm_mesh mesh; + struct gpart *gparts = NULL; + struct gravity_props gravity_properties; + struct hydro_props hydro_properties; + struct stars_props stars_properties; + struct part *parts = NULL; + struct phys_const prog_const; + struct sourceterms sourceterms; + struct space s; + struct spart *sparts = NULL; + struct unit_system us; + + int nr_nodes = 1, myrank = 0; + +#ifdef WITH_MPI + /* Start by initializing MPI. */ + int res = 0, prov = 0; + if ((res = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &prov)) != + MPI_SUCCESS) + error("Call to MPI_Init failed with error %i.", res); + if (prov != MPI_THREAD_MULTIPLE) + error( + "MPI does not provide the level of threading" + " required (MPI_THREAD_MULTIPLE)."); + if ((res = MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes)) != MPI_SUCCESS) + error("MPI_Comm_size failed with error %i.", res); + if ((res = MPI_Comm_rank(MPI_COMM_WORLD, &myrank)) != MPI_SUCCESS) + error("Call to MPI_Comm_rank failed with error %i.", res); + + /* Make sure messages are stamped with the correct rank. */ + engine_rank = myrank; + + if ((res = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN)) != + MPI_SUCCESS) + error("Call to MPI_Comm_set_errhandler failed with error %i.", res); + if (myrank == 0) + printf("[0000] [00000.0] main: MPI is up and running with %i node(s).\n\n", + nr_nodes); + if (nr_nodes == 1) { + message("WARNING: you are running with one MPI rank."); + message("WARNING: you should use the non-MPI version of this program."); + } + fflush(stdout); + +#endif + + /* Welcome to SWIFT, you made the right choice */ + if (myrank == 0) greetings(); + + int with_aff = 0; + int dry_run = 0; + int dump_tasks = 0; + int dump_threadpool = 0; + int nsteps = -2; + int restart = 0; + int with_cosmology = 0; + int with_external_gravity = 0; + int with_sourceterms = 0; + int with_cooling = 0; + int with_self_gravity = 0; + int with_hydro = 0; + int with_stars = 0; + int with_fof = 0; + int with_feedback = 0; + int with_fp_exceptions = 0; + int with_drift_all = 0; + int with_mpole_reconstruction = 0; + int with_structure_finding = 0; + 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 restart_file[200] = ""; + unsigned long long cpufreq = 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"); +#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"); +#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."); +#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."); + } +#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."); + } +#endif + break; + case '?': + if (myrank == 0) print_help_message(); + return 1; + break; + } + + /* Write output parameter file */ + if (myrank == 0 && strcmp(output_parameters_filename, "") != 0) { + 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(); + return 1; + } + if (with_stars && !with_external_gravity && !with_self_gravity) { + if (myrank == 0) + printf( + "Error: Cannot process stars without gravity, -g or -G must be " + "chosen.\n"); + if (myrank == 0) print_help_message(); + return 1; + } + if (with_fof && !with_external_gravity && !with_self_gravity) { + if (myrank == 0) + 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) + printf( + "Error: Cannot process feedback without stars, -S must be " + "chosen.\n"); + if (myrank == 0) print_help_message(); + return 1; + } + +/* Let's pin the main thread, now we know if affinity will be used. */ +#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE) + if (with_aff && + ((ENGINE_POLICY)&engine_policy_setaffinity) == engine_policy_setaffinity) + engine_pin(); +#endif + + /* Genesis 1.1: And then, there was time ! */ + clocks_set_cpufreq(cpufreq); + + /* How vocal are we ? */ + const int talking = (verbose == 1 && myrank == 0) || (verbose == 2); + + if (myrank == 0 && dry_run) + message( + "Executing a dry run. No i/o or time integration will be performed."); + + /* Report CPU frequency.*/ + cpufreq = clocks_get_cpufreq(); + if (myrank == 0) { + message("CPU frequency used for tick conversion: %llu Hz", cpufreq); + } + +/* Report host name(s). */ +#ifdef WITH_MPI + if (talking) { + message("Rank %d running on: %s", myrank, hostname()); + } +#else + message("Running on: %s", hostname()); +#endif + +/* Do we have debugging checks ? */ +#ifdef SWIFT_DEBUG_CHECKS + if (myrank == 0) + message("WARNING: Debugging checks activated. Code will be slower !"); +#endif + +/* Do we have debugging checks ? */ +#ifdef SWIFT_USE_NAIVE_INTERACTIONS + if (myrank == 0) + message( + "WARNING: Naive cell interactions activated. Code will be slower !"); +#endif + +/* Do we have gravity accuracy checks ? */ +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + if (myrank == 0) + message( + "WARNING: Checking 1/%d of all gpart for gravity accuracy. Code will " + "be slower !", + SWIFT_GRAVITY_FORCE_CHECKS); +#endif + + /* Do we choke on FP-exceptions ? */ + if (with_fp_exceptions) { +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + if (myrank == 0) + message("WARNING: Floating point exceptions will be reported."); + } + +/* Do we have slow barriers? */ +#ifndef HAVE_PTHREAD_BARRIERS + if (myrank == 0) + message("WARNING: Non-optimal thread barriers are being used."); +#endif + + /* How large are the parts? */ + if (myrank == 0) { + message("sizeof(part) is %4zi bytes.", sizeof(struct part)); + message("sizeof(xpart) is %4zi bytes.", sizeof(struct xpart)); + message("sizeof(spart) is %4zi bytes.", sizeof(struct spart)); + message("sizeof(gpart) is %4zi bytes.", sizeof(struct gpart)); + message("sizeof(multipole) is %4zi bytes.", sizeof(struct multipole)); + message("sizeof(grav_tensor) is %4zi bytes.", sizeof(struct grav_tensor)); + message("sizeof(task) is %4zi bytes.", sizeof(struct task)); + message("sizeof(cell) is %4zi bytes.", sizeof(struct cell)); + } + + /* Read the parameter file */ + struct swift_params *params = + (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); + + /* Handle any command-line overrides. */ + if (nparams > 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]); + } + } +#ifdef WITH_MPI + /* Broadcast the parameter file */ + MPI_Bcast(params, sizeof(struct swift_params), MPI_BYTE, 0, MPI_COMM_WORLD); +#endif + + /* Temporary early aborts for modes not supported over MPI. */ +#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)."); +#endif + +#if defined(WITH_MPI) && defined(HAVE_VELOCIRAPTOR) + if (with_structure_finding && nr_nodes > 1) + error("VEOCIraptor not yet enabled over MPI."); +#endif + + /* Check that we can write the snapshots by testing if the output + * directory exists and is searchable and writable. */ + char basename[PARSER_MAX_LINE_SIZE]; + parser_get_param_string(params, "Snapshots:basename", basename); + const char *dirp = dirname(basename); + if (access(dirp, W_OK | X_OK) != 0) { + error("Cannot write snapshots in directory %s (%s)", dirp, strerror(errno)); + } + + /* Check that we can write the structure finding catalogues by testing if the + * 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); + const char *stfdirp = dirname(stfbasename); + if (access(stfdirp, W_OK | X_OK) != 0) { + error("Cannot write stf catalogues in directory %s (%s)", stfdirp, + strerror(errno)); + } + } + + /* Prepare the domain decomposition scheme */ + struct repartition reparttype; +#ifdef WITH_MPI + struct partition initial_partition; + partition_init(&initial_partition, &reparttype, params, nr_nodes); + + /* Let's report what we did */ + if (myrank == 0) { +#if defined(HAVE_PARMETIS) + if (reparttype.usemetis) + message("Using METIS serial partitioning:"); + else + message("Using ParMETIS partitioning:"); +#else + message("Using METIS serial partitioning:"); +#endif + message(" initial partitioning: %s", + initial_partition_name[initial_partition.type]); + if (initial_partition.type == INITPART_GRID) + message(" grid set to [ %i %i %i ].", initial_partition.grid[0], + initial_partition.grid[1], initial_partition.grid[2]); + message(" repartitioning: %s", repartition_name[reparttype.type]); + } +#endif + + /* Common variables for restart and IC sections. */ + int clean_smoothing_length_values = 0; + int flag_entropy_ICs = 0; + + /* Work out where we will read and write restart files. */ + char restart_dir[PARSER_MAX_LINE_SIZE]; + parser_get_opt_param_string(params, "Restarts:subdir", restart_dir, + "restart"); + + /* The directory must exist. */ + if (myrank == 0) { + if (access(restart_dir, W_OK | X_OK) != 0) { + if (restart) { + error("Cannot restart as no restart subdirectory: %s (%s)", restart_dir, + strerror(errno)); + } else { + if (mkdir(restart_dir, 0777) != 0) + error("Failed to create restart directory: %s (%s)", restart_dir, + strerror(errno)); + } + } + } + + /* Basename for any restart files. */ + char restart_name[PARSER_MAX_LINE_SIZE]; + parser_get_opt_param_string(params, "Restarts:basename", restart_name, + "swift"); + + /* If restarting, look for the restart files. */ + if (restart) { + + /* Attempting a restart. */ + char **restart_files = NULL; + int restart_nfiles = 0; + + if (myrank == 0) { + message("Restarting SWIFT"); + + /* Locate the restart files. */ + restart_files = + restart_locate(restart_dir, restart_name, &restart_nfiles); + if (restart_nfiles == 0) + error("Failed to locate any restart files in %s", restart_dir); + + /* We need one file per rank. */ + if (restart_nfiles != nr_nodes) + error("Incorrect number of restart files, expected %d found %d", + nr_nodes, restart_nfiles); + + if (verbose > 0) + for (int i = 0; i < restart_nfiles; i++) + message("found restart file: %s", restart_files[i]); + } + +#ifdef WITH_MPI + /* Distribute the restart files, need one for each rank. */ + if (myrank == 0) { + + for (int i = 1; i < nr_nodes; i++) { + strcpy(restart_file, restart_files[i]); + MPI_Send(restart_file, 200, MPI_BYTE, i, 0, MPI_COMM_WORLD); + } + + /* Keep local file. */ + strcpy(restart_file, restart_files[0]); + + /* Finished with the list. */ + restart_locate_free(restart_nfiles, restart_files); + + } else { + MPI_Recv(restart_file, 200, MPI_BYTE, 0, 0, MPI_COMM_WORLD, + MPI_STATUS_IGNORE); + } + if (verbose > 1) message("local restart file = %s", restart_file); +#else + + /* Just one restart file. */ + strcpy(restart_file, restart_files[0]); +#endif + + /* Now read it. */ + restart_read(&e, restart_file); + + /* And initialize the engine with the space and policies. */ + if (myrank == 0) clocks_gettime(&tic); + engine_config(1, &e, params, nr_nodes, myrank, nr_threads, with_aff, + talking, restart_file); + if (myrank == 0) { + clocks_gettime(&toc); + message("engine_config took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } + + /* Check if we are already done when given steps on the command-line. */ + if (e.step >= nsteps && nsteps > 0) + error("Not restarting, already completed %d steps", e.step); + + } else { + + /* Not restarting so look for the ICs. */ + /* Initialize unit system and constants */ + units_init_from_params(&us, params, "InternalUnitSystem"); + phys_const_init(&us, params, &prog_const); + if (myrank == 0) { + message("Internal unit system: U_M = %e g.", us.UnitMass_in_cgs); + message("Internal unit system: U_L = %e cm.", us.UnitLength_in_cgs); + message("Internal unit system: U_t = %e s.", us.UnitTime_in_cgs); + message("Internal unit system: U_I = %e A.", us.UnitCurrent_in_cgs); + message("Internal unit system: U_T = %e K.", us.UnitTemperature_in_cgs); + phys_const_print(&prog_const); + } + + /* Initialise the cosmology */ + if (with_cosmology) + cosmology_init(params, &us, &prog_const, &cosmo); + else + cosmology_init_no_cosmo(&cosmo); + if (myrank == 0 && with_cosmology) cosmology_print(&cosmo); + + /* Initialise the hydro properties */ + if (with_hydro) + hydro_props_init(&hydro_properties, &prog_const, &us, params); + else + bzero(&hydro_properties, sizeof(struct hydro_props)); + + /* Initialise the equation of state */ + if (with_hydro) + eos_init(&eos, &prog_const, &us, params); + else + bzero(&eos, sizeof(struct eos_parameters)); + + /* Initialise the stars properties */ + if (with_stars) + stars_props_init(&stars_properties, &prog_const, &us, params, + &hydro_properties); + else + bzero(&stars_properties, sizeof(struct stars_props)); + + /* Initialise the gravity properties */ + if (with_self_gravity) + gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology); + else + bzero(&gravity_properties, sizeof(struct gravity_props)); + + /* Read particles and space information from (GADGET) ICs */ + char ICfileName[200] = ""; + parser_get_param_string(params, "InitialConditions:file_name", ICfileName); + const int replicate = + parser_get_opt_param_int(params, "InitialConditions:replicate", 1); + clean_smoothing_length_values = parser_get_opt_param_int( + params, "InitialConditions:cleanup_smoothing_lengths", 0); + const int cleanup_h = parser_get_opt_param_int( + params, "InitialConditions:cleanup_h_factors", 0); + const int cleanup_sqrt_a = parser_get_opt_param_int( + params, "InitialConditions:cleanup_velocity_factors", 0); + const int generate_gas_in_ics = parser_get_opt_param_int( + params, "InitialConditions:generate_gas_in_ics", 0); + if (generate_gas_in_ics && flag_entropy_ICs) + error("Can't generate gas if the entropy flag is set in the ICs."); + if (generate_gas_in_ics && !with_cosmology) + error("Can't generate gas if the run is not cosmological."); + if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); + if (myrank == 0 && cleanup_h) + message("Cleaning up h-factors (h=%f)", cosmo.h); + if (myrank == 0 && cleanup_sqrt_a) + message("Cleaning up a-factors from velocity (a=%f)", cosmo.a); + fflush(stdout); + + /* Get ready to read particles of all kinds */ + size_t Ngas = 0, Ngpart = 0, Nspart = 0; + double dim[3] = {0., 0., 0.}; + int periodic = 0; + if (myrank == 0) clocks_gettime(&tic); +#if defined(HAVE_HDF5) +#if defined(WITH_MPI) +#if defined(HAVE_PARALLEL_HDF5) + read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, + &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, + (with_external_gravity || with_self_gravity), with_stars, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, + nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, + dry_run); +#else + read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, + &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, + (with_external_gravity || with_self_gravity), with_stars, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, + nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, + dry_run); +#endif +#else + read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, + &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, + (with_external_gravity || with_self_gravity), with_stars, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, + dry_run); +#endif +#endif + if (myrank == 0) { + clocks_gettime(&toc); + message("Reading initial conditions took %.3f %s.", + clocks_diff(&tic, &toc), clocks_getunit()); + fflush(stdout); + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Check once and for all that we don't have unwanted links */ + if (!with_stars && !dry_run) { + for (size_t k = 0; k < Ngpart; ++k) + if (gparts[k].type == swift_type_stars) error("Linking problem"); + } + if (!with_hydro && !dry_run) { + for (size_t k = 0; k < Ngpart; ++k) + if (gparts[k].type == swift_type_gas) error("Linking problem"); + } + + /* Check that the other links are correctly set */ + if (!dry_run) + part_verify_links(parts, gparts, sparts, Ngas, Ngpart, Nspart, 1); +#endif + + /* Get the total number of particles across all nodes. */ + long long N_total[3] = {0, 0, 0}; +#if defined(WITH_MPI) + long long N_long[3] = {Ngas, Ngpart, Nspart}; + MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD); +#else + N_total[0] = Ngas; + N_total[1] = Ngpart; + N_total[2] = Nspart; +#endif + + if (myrank == 0) + message( + "Read %lld gas particles, %lld stars particles and %lld gparts from " + "the ICs.", + N_total[0], N_total[2], N_total[1]); + + /* Verify that the fields to dump actually exist */ + if (myrank == 0) io_check_output_fields(params, N_total); + + /* Initialize the space with these data. */ + 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); + + if (myrank == 0) { + clocks_gettime(&toc); + message("space_init took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } + + /* Initialise the long-range gravity mesh */ + if (with_self_gravity && periodic) { +#ifdef HAVE_FFTW + pm_mesh_init(&mesh, &gravity_properties, s.dim, nr_threads); +#else + /* Need the FFTW library if periodic and self gravity. */ + error( + "No FFTW library found. Cannot compute periodic long-range forces."); +#endif + } else { + pm_mesh_init_no_mesh(&mesh, s.dim); + } + + /* Check that the matter content matches the cosmology given in the + * parameter file. */ + if (with_cosmology && with_self_gravity && !dry_run) + space_check_cosmology(&s, &cosmo, myrank); + +/* Also update the total counts (in case of changes due to replication) */ +#if defined(WITH_MPI) + N_long[0] = s.nr_parts; + N_long[1] = s.nr_gparts; + N_long[2] = s.nr_sparts; + MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD); +#else + N_total[0] = s.nr_parts; + N_total[1] = s.nr_gparts; + N_total[2] = s.nr_sparts; +#endif + + /* Say a few nice things about the space we just created. */ + if (myrank == 0) { + message("space dimensions are [ %.3f %.3f %.3f ].", s.dim[0], s.dim[1], + s.dim[2]); + message("space %s periodic.", s.periodic ? "is" : "isn't"); + message("highest-level cell dimensions are [ %i %i %i ].", s.cdim[0], + s.cdim[1], s.cdim[2]); + message("%zi parts in %i cells.", s.nr_parts, s.tot_cells); + message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells); + message("%zi sparts in %i cells.", s.nr_sparts, s.tot_cells); + message("maximum depth is %d.", s.maxdepth); + fflush(stdout); + } + + /* Verify that each particle is in it's proper cell. */ + if (talking && !dry_run) { + int icount = 0; + space_map_cells_pre(&s, 0, &map_cellcheck, &icount); + message("map_cellcheck picked up %i parts.", icount); + } + + /* Verify the maximal depth of cells. */ + if (talking && !dry_run) { + int data[2] = {s.maxdepth, 0}; + space_map_cells_pre(&s, 0, &map_maxdepth, data); + message("nr of cells at depth %i is %i.", data[0], data[1]); + } + + /* Initialise the external potential properties */ + 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); + if (myrank == 0) cooling_print(&cooling_func); + + /* 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; + if (with_mpole_reconstruction) + engine_policies |= engine_policy_reconstruct_mpoles; + if (with_hydro) engine_policies |= engine_policy_hydro; + if (with_self_gravity) engine_policies |= engine_policy_self_gravity; + if (with_external_gravity) + 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; + if (with_structure_finding) + engine_policies |= engine_policy_structure_finding; + if (with_fof) engine_policies |= engine_policy_fof; + + /* 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); + engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, + talking, restart_file); + + /* Initialise the FOF parameters. */ + fof_init(&s, N_total[0], N_total[1]); + + if (myrank == 0) { + clocks_gettime(&toc); + message("engine_init took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } + + /* Get some info to the user. */ + if (myrank == 0) { + long long N_DM = N_total[1] - N_total[2] - N_total[0]; + message( + "Running on %lld gas particles, %lld stars particles and %lld DM " + "particles (%lld gravity particles)", + N_total[0], N_total[2], N_total[1] > 0 ? N_DM : 0, N_total[1]); + message( + "from t=%.3e until t=%.3e with %d ranks, %d threads / rank and %d " + "task queues / rank (dt_min=%.3e, dt_max=%.3e)...", + e.time_begin, e.time_end, nr_nodes, e.nr_threads, e.sched.nr_queues, + e.dt_min, e.dt_max); + fflush(stdout); + } + } + + /* Time to say good-bye if this was not a serious run. */ + if (dry_run) { +#ifdef WITH_MPI + if ((res = MPI_Finalize()) != MPI_SUCCESS) + error("call to MPI_Finalize failed with error %i.", res); +#endif + if (myrank == 0) + message("Time integration ready to start. End of dry-run."); + engine_clean(&e); + free(params); + return 0; + } + +/* Initialise the table of Ewald corrections for the gravity checks */ +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + if (s.periodic) gravity_exact_force_ewald_init(e.s->dim[0]); +#endif + +/* Init the runner history. */ +#ifdef HIST + for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0; +#endif + + if (!restart) { + +#ifdef WITH_MPI + /* Split the space. */ + engine_split(&e, &initial_partition); + engine_redistribute(&e); +#endif + + /* Initialise the particles */ + engine_init_particles(&e, flag_entropy_ICs, clean_smoothing_length_values, 0); + + /* Perform first FOF search after the first snapshot dump. */ + fof_search_tree(&s); + +#ifdef WITH_MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + } + + /* used parameters */ + parser_write_params_to_file(params, "used_parameters.yml", 1); + /* unused parameters */ + parser_write_params_to_file(params, "unused_parameters.yml", 0); + + /* Clean everything */ + if (with_verbose_timers) timers_close_file(); + if (with_cosmology) cosmology_clean(&cosmo); + if (with_self_gravity) pm_mesh_clean(&mesh); + engine_clean(&e); + free(params); + + /* Say goodbye. */ + if (myrank == 0) message("done. Bye."); + + /* All is calm, all is bright. */ + return 0; +}