main.c 39.3 KB
Newer Older
1
/*******************************************************************************
2
 * This file is part of SWIFT.
3
 * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
4
 *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
5
 *               2015 Peter W. Draper (p.w.draper@durham.ac.uk)
6
7
8
 *                    Angus Lepper (angus.lepper@ed.ac.uk)
 *               2016 John A. Regan (john.a.regan@durham.ac.uk)
 *                    Tom Theuns (tom.theuns@durham.ac.uk)
9
 *
10
11
12
13
 * 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.
14
 *
15
16
17
18
 * 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.
19
 *
20
21
 * 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/>.
22
 *
23
24
 ******************************************************************************/

Pedro Gonnet's avatar
Pedro Gonnet committed
25
26
/* Config parameters. */
#include "../config.h"
Pedro Gonnet's avatar
Pedro Gonnet committed
27
28

/* Some standard headers. */
29
#include <errno.h>
30
#include <fenv.h>
31
#include <libgen.h>
Pedro Gonnet's avatar
Pedro Gonnet committed
32
33
34
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
35
#include <sys/stat.h>
36
#include <unistd.h>
Pedro Gonnet's avatar
Pedro Gonnet committed
37

38
39
/* MPI headers. */
#ifdef WITH_MPI
40
#include <mpi.h>
41
42
#endif

Pedro Gonnet's avatar
Pedro Gonnet committed
43
/* Local headers. */
44
#include "argparse.h"
45
#include "swift.h"
Pedro Gonnet's avatar
Pedro Gonnet committed
46

47
48
/* Engine policy flags. */
#ifndef ENGINE_POLICY
49
#define ENGINE_POLICY engine_policy_none
50
51
#endif

James Willis's avatar
James Willis committed
52
53
54
/* Global profiler. */
struct profiler prof;

55
56
//  Usage string.
static const char *const swift_usage[] = {
Peter W. Draper's avatar
Peter W. Draper committed
57
58
59
60
61
    "swift [options] [[--] param-file]",
    "swift [options] param-file",
    "swift_mpi [options] [[--] param-file]",
    "swift_mpi [options] param-file",
    NULL,
62
63
};

64
65
66
67
68
69
70
71
72
73
74
75
// 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;
76
77
}

Pedro Gonnet's avatar
Pedro Gonnet committed
78
79
80
81
/**
 * @brief Main routine that loads a few particles and generates some output.
 *
 */
82
83
int main(int argc, char *argv[]) {

84
  struct clocks_time tic, toc;
85
  struct engine e;
86

87
88
  /* Structs used by the engine. Declare now to make sure these are always in
   * scope.  */
lhausamm's avatar
lhausamm committed
89
  struct chemistry_global_data chemistry;
90
  struct cooling_function_data cooling_func;
91
  struct cosmology cosmo;
92
  struct external_potential potential;
93
  struct pm_mesh mesh;
94
95
96
  struct gpart *gparts = NULL;
  struct gravity_props gravity_properties;
  struct hydro_props hydro_properties;
Loic Hausammann's avatar
Loic Hausammann committed
97
  struct stars_props stars_properties;
98
99
100
101
102
103
  struct part *parts = NULL;
  struct phys_const prog_const;
  struct sourceterms sourceterms;
  struct space s;
  struct spart *sparts = NULL;
  struct unit_system us;
104

105
  int nr_nodes = 1, myrank = 0;
106

107
#ifdef WITH_MPI
108
  /* Start by initializing MPI. */
109
  int res = 0, prov = 0;
110
111
  if ((res = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &prov)) !=
      MPI_SUCCESS)
112
113
    error("Call to MPI_Init failed with error %i.", res);
  if (prov != MPI_THREAD_MULTIPLE)
114
    error(
115
116
        "MPI does not provide the level of threading"
        " required (MPI_THREAD_MULTIPLE).");
117
  if ((res = MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes)) != MPI_SUCCESS)
118
119
120
    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);
121
122
123
124

  /* Make sure messages are stamped with the correct rank. */
  engine_rank = myrank;

125
126
127
  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);
128
129
  if (myrank == 0)
    printf("[0000] [00000.0] main: MPI is up and running with %i node(s).\n\n",
Peter W. Draper's avatar
Peter W. Draper committed
130
           nr_nodes);
131
132
133
134
  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.");
  }
135
  fflush(stdout);
136

137
#endif
138

139
140
  /* Welcome to SWIFT, you made the right choice */
  if (myrank == 0) greetings();
141

142
  int with_aff = 0;
143
  int dry_run = 0;
144
  int dump_tasks = 0;
145
  int dump_threadpool = 0;
146
  int nsteps = -2;
147
  int restart = 0;
148
149
  int with_cosmology = 0;
  int with_external_gravity = 0;
Tom Theuns's avatar
Tom Theuns committed
150
  int with_sourceterms = 0;
151
  int with_cooling = 0;
152
153
  int with_self_gravity = 0;
  int with_hydro = 0;
154
  int with_stars = 0;
Loic Hausammann's avatar
Loic Hausammann committed
155
  int with_feedback = 0;
156
  int with_fp_exceptions = 0;
157
  int with_drift_all = 0;
158
  int with_mpole_reconstruction = 0;
159
  int with_structure_finding = 0;
160
  int verbose = 0;
161
  int nr_threads = 1;
162
  int with_verbose_timers = 0;
163
  char *output_parameters_filename = NULL;
164
  char *cpufreqarg = NULL;
165
  char *param_filename = NULL;
166
  char restart_file[200] = "";
167
  unsigned long long cpufreq = 0;
168
169
170
171
  struct cmdparams cmdps;
  cmdps.nparam = 0;
  cmdps.param[0] = NULL;
  char *buffer = NULL;
172

173
174
175
  /* Parse the command-line parameters. */
  struct argparse_option options[] = {
      OPT_HELP(),
176

Peter W. Draper's avatar
Peter W. Draper committed
177
      OPT_GROUP("  Simulation options:"),
178
179
180
181
      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),
Peter W. Draper's avatar
Peter W. Draper committed
182
183
      OPT_BOOLEAN('C', "cooling", &with_cooling, "Run with cooling", NULL, 0,
                  0),
184
185
186
187
188
      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),
189
      OPT_BOOLEAN('F', "sourceterms", &with_sourceterms, "", NULL, 0, 0),
190
191
192
193
194
195
      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),
196
197
      OPT_BOOLEAN('s', "hydro", &with_hydro, "Run with hydrodynamics.", NULL, 0,
                  0),
198
      OPT_BOOLEAN('S', "stars", &with_stars, "Run with stars", NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
199
      OPT_BOOLEAN('x', "velociraptor", &with_structure_finding,
200
201
                  "Run with structure finding", NULL, 0, 0),

Peter W. Draper's avatar
Peter W. Draper committed
202
      OPT_GROUP("  Control options:"),
203
      OPT_BOOLEAN('a', "pin", &with_aff,
204
                  "Pin runners using processor affinity.", NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
205
206
207
208
209
      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.",
210
                  NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
211
212
213
214
215
216
217
218
      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,
219
220
                  "Execute a fixed number of time steps. When unset use the "
                  "time_end parameter to stop.",
221
                  NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
222
      OPT_STRING('o', "output-params", &output_parameters_filename,
223
                 "Generate a default output parameter file.", NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
224
      OPT_STRING('P', "param", &buffer,
225
226
                 "Set parameter value, overiding the value read from the "
                 "parameter file. Can be used more than once {sec:par:value}.",
227
228
229
                 handle_cmdparam, (intptr_t)&cmdps, 0),
      OPT_BOOLEAN('r', "restart", &restart, "Continue using restart files.",
                  NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
230
      OPT_INTEGER('t', "threads", &nr_threads,
231
232
                  "The number of threads to use on each MPI rank. Defaults to "
                  "1 if not specified.",
233
234
                  NULL, 0, 0),
      OPT_INTEGER('T', "timers", &with_verbose_timers,
Peter W. Draper's avatar
Peter W. Draper committed
235
236
237
                  "Print timers every time-step.", NULL, 0, 0),
      OPT_INTEGER('v', "verbose", &verbose,
                  "Run in verbose mode, in MPI mode 2 outputs from all ranks.",
238
                  NULL, 0, 0),
Peter W. Draper's avatar
Peter W. Draper committed
239
240
241
242
      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,
243
244
                  "Time-step frequency at which threadpool tasks are dumped.",
                  NULL, 0, 0),
245
      OPT_END(),
246
247
  };
  struct argparse argparse;
248
  argparse_init(&argparse, options, swift_usage, 0);
Peter W. Draper's avatar
Peter W. Draper committed
249
250
251
  argparse_describe(&argparse, "\nParameters:",
                    "\nSee the file examples/parameter_example.yml for an "
                    "example of parameter file.");
252
253
254
255
  int nargs = argparse_parse(&argparse, argc, (const char **)argv);

  /* Need a parameter file. */
  if (nargs != 1) {
Peter W. Draper's avatar
Peter W. Draper committed
256
257
258
    if (myrank == 0) argparse_usage(&argparse);
    printf("\nError: no parameter file was supplied.\n");
    return 1;
259
260
  }
  param_filename = argv[0];
261

262
  /* Checks of options. */
Peter W. Draper's avatar
Peter W. Draper committed
263
#if !defined(HAVE_SETAFFINITY) || !defined(HAVE_LIBNUMA)
264
  if (with_aff) {
Peter W. Draper's avatar
Peter W. Draper committed
265
266
    printf("Error: no NUMA support for thread affinity\n");
    return 1;
267
  }
268
#endif
269
270

#ifndef HAVE_FE_ENABLE_EXCEPT
271
  if (with_fp_exceptions) {
Peter W. Draper's avatar
Peter W. Draper committed
272
273
    printf("Error: no support for floating point exceptions\n");
    return 1;
274
  }
275
#endif
276
277

#ifndef HAVE_VELOCIRAPTOR
278
  if (with_structure_finding) {
Peter W. Draper's avatar
Peter W. Draper committed
279
280
    printf("Error: VELOCIraptor is not available\n");
    return 1;
281
  }
282
#endif
283

284
#ifndef SWIFT_DEBUG_TASKS
285
  if (dump_tasks) {
286
287
288
289
290
    if (myrank == 0) {
      message("WARNING: complete task dumps are only created when "
              "configured with --enable-task-debugging.");
      message("         Basic task statistics will be output.");
    }
291
  }
292
#endif
293

294
#ifndef SWIFT_DEBUG_THREADPOOL
295
  if (dump_threadpool) {
Peter W. Draper's avatar
Peter W. Draper committed
296
297
298
299
    printf(
        "Error: threadpool dumping is only possible if SWIFT was "
        "configured with the --enable-threadpool-debugging option.\n");
    return 1;
300
  }
301
#endif
302

303
304
305
  /* The CPU frequency is a long long, so we need to parse that ourselves. */
  if (cpufreqarg != NULL) {
    if (sscanf(cpufreqarg, "%llu", &cpufreq) != 1) {
Peter W. Draper's avatar
Peter W. Draper committed
306
307
      if (myrank == 0)
        printf("Error parsing CPU frequency (%s).\n", cpufreqarg);
308
      return 1;
309
    }
310
  }
311
312

  /* Write output parameter file */
313
  if (myrank == 0 && output_parameters_filename != NULL) {
314
    io_write_output_field_parameter(output_parameters_filename);
315
    printf("End of run.\n");
316
317
318
    return 0;
  }

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
319
  if (!with_self_gravity && !with_hydro && !with_external_gravity) {
Peter W. Draper's avatar
Peter W. Draper committed
320
321
322
323
    if (myrank == 0) {
      argparse_usage(&argparse);
      printf("\nError: At least one of -s, -g or -G must be chosen.\n");
    }
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
324
325
    return 1;
  }
326
  if (with_stars && !with_external_gravity && !with_self_gravity) {
Peter W. Draper's avatar
Peter W. Draper committed
327
328
    if (myrank == 0) {
      argparse_usage(&argparse);
329
      printf(
Peter W. Draper's avatar
Peter W. Draper committed
330
331
332
          "\nError: Cannot process stars without gravity, -g or -G "
          "must be chosen.\n");
    }
333
334
    return 1;
  }
335

Loic Hausammann's avatar
Loic Hausammann committed
336
  if (!with_stars && with_feedback) {
Peter W. Draper's avatar
Peter W. Draper committed
337
338
    if (myrank == 0) {
      argparse_usage(&argparse);
Loic Hausammann's avatar
Loic Hausammann committed
339
      printf(
Peter W. Draper's avatar
Peter W. Draper committed
340
          "\nError: Cannot process feedback without stars, -S must be "
Loic Hausammann's avatar
Loic Hausammann committed
341
          "chosen.\n");
Peter W. Draper's avatar
Peter W. Draper committed
342
    }
Loic Hausammann's avatar
Loic Hausammann committed
343
344
345
    return 1;
  }

346
347
/* Let's pin the main thread, now we know if affinity will be used. */
#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE)
348
349
  if (with_aff &&
      ((ENGINE_POLICY)&engine_policy_setaffinity) == engine_policy_setaffinity)
350
351
352
    engine_pin();
#endif

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
353
354
  /* Genesis 1.1: And then, there was time ! */
  clocks_set_cpufreq(cpufreq);
355

356
357
358
  /* How vocal are we ? */
  const int talking = (verbose == 1 && myrank == 0) || (verbose == 2);

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
359
360
361
  if (myrank == 0 && dry_run)
    message(
        "Executing a dry run. No i/o or time integration will be performed.");
362

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
363
364
365
366
367
  /* Report CPU frequency.*/
  cpufreq = clocks_get_cpufreq();
  if (myrank == 0) {
    message("CPU frequency used for tick conversion: %llu Hz", cpufreq);
  }
368

Matthieu Schaller's avatar
Matthieu Schaller committed
369
/* Report host name(s). */
370
#ifdef WITH_MPI
371
  if (talking) {
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
372
373
    message("Rank %d running on: %s", myrank, hostname());
  }
374
#else
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
375
  message("Running on: %s", hostname());
376
377
#endif

378
379
/* Do we have debugging checks ? */
#ifdef SWIFT_DEBUG_CHECKS
380
381
  if (myrank == 0)
    message("WARNING: Debugging checks activated. Code will be slower !");
382
383
#endif

384
385
386
387
388
389
390
/* Do we have debugging checks ? */
#ifdef SWIFT_USE_NAIVE_INTERACTIONS
  if (myrank == 0)
    message(
        "WARNING: Naive cell interactions activated. Code will be slower !");
#endif

391
392
393
394
395
396
397
398
399
/* 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

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
400
401
  /* Do we choke on FP-exceptions ? */
  if (with_fp_exceptions) {
402
#ifdef HAVE_FE_ENABLE_EXCEPT
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
403
    feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
404
#endif
405
406
    if (myrank == 0)
      message("WARNING: Floating point exceptions will be reported.");
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
407
  }
408

409
/* Do we have slow barriers? */
410
#ifndef HAVE_PTHREAD_BARRIERS
411
  if (myrank == 0)
412
    message("WARNING: Non-optimal thread barriers are being used.");
413
414
#endif

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
415
416
  /* How large are the parts? */
  if (myrank == 0) {
417
418
419
420
421
422
423
424
    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));
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
425
  }
426

Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
427
  /* Read the parameter file */
Matthieu Schaller's avatar
Matthieu Schaller committed
428
429
  struct swift_params *params =
      (struct swift_params *)malloc(sizeof(struct swift_params));
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
430
431
  if (params == NULL) error("Error allocating memory for the parameter file.");
  if (myrank == 0) {
432
433
    message("Reading runtime parameters from file '%s'", param_filename);
    parser_read_file(param_filename, params);
434
435

    /* Handle any command-line overrides. */
436
    if (cmdps.nparam > 0) {
437
438
439
      message(
          "Overwriting values read from the YAML file with command-line "
          "values.");
Peter W. Draper's avatar
Peter W. Draper committed
440
441
      for (int k = 0; k < cmdps.nparam; k++)
        parser_set_param(params, cmdps.param[k]);
442
    }
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
443
  }
444
#ifdef WITH_MPI
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
445
446
  /* Broadcast the parameter file */
  MPI_Bcast(params, sizeof(struct swift_params), MPI_BYTE, 0, MPI_COMM_WORLD);
447
#endif
448

449
450
  /* Temporary early aborts for modes not supported over MPI. */
#ifdef WITH_MPI
Matthieu Schaller's avatar
Matthieu Schaller committed
451
452
  if (with_mpole_reconstruction && nr_nodes > 1)
    error("Cannot reconstruct m-poles every step over MPI (yet).");
453
454
#endif

455
456
457
458
#ifdef WITH_MPI
  if (with_feedback) error("Can't run with feedback over MPI (yet).");
#endif

459
#if defined(WITH_MPI) && defined(HAVE_VELOCIRAPTOR)
Matthieu Schaller's avatar
Matthieu Schaller committed
460
461
  if (with_structure_finding && nr_nodes > 1)
    error("VEOCIraptor not yet enabled over MPI.");
462
463
#endif

464
  /* Check that we can write the snapshots by testing if the output
465
   * directory exists and is searchable and writable. */
466
467
468
  char basename[PARSER_MAX_LINE_SIZE];
  parser_get_param_string(params, "Snapshots:basename", basename);
  const char *dirp = dirname(basename);
469
470
  if (access(dirp, W_OK | X_OK) != 0) {
    error("Cannot write snapshots in directory %s (%s)", dirp, strerror(errno));
471
  }
Matthieu Schaller's avatar
Matthieu Schaller committed
472
473
474

  /* Check that we can write the structure finding catalogues by testing if the
   * output
475
   * directory exists and is searchable and writable. */
Matthieu Schaller's avatar
Matthieu Schaller committed
476
  if (with_structure_finding) {
477
    char stfbasename[PARSER_MAX_LINE_SIZE];
478
    parser_get_param_string(params, "StructureFinding:basename", stfbasename);
479
480
    const char *stfdirp = dirname(stfbasename);
    if (access(stfdirp, W_OK | X_OK) != 0) {
Matthieu Schaller's avatar
Matthieu Schaller committed
481
482
      error("Cannot write stf catalogues in directory %s (%s)", stfdirp,
            strerror(errno));
483
484
    }
  }
485

486
  /* Prepare the domain decomposition scheme */
487
  struct repartition reparttype;
488
#ifdef WITH_MPI
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
489
490
491
492
493
  struct partition initial_partition;
  partition_init(&initial_partition, &reparttype, params, nr_nodes);

  /* Let's report what we did */
  if (myrank == 0) {
494
495
#if defined(HAVE_PARMETIS)
    if (reparttype.usemetis)
Matthieu Schaller's avatar
Matthieu Schaller committed
496
      message("Using METIS serial partitioning:");
497
    else
Matthieu Schaller's avatar
Matthieu Schaller committed
498
      message("Using ParMETIS partitioning:");
499
500
501
502
#else
    message("Using METIS serial partitioning:");
#endif
    message("  initial partitioning: %s",
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
503
504
            initial_partition_name[initial_partition.type]);
    if (initial_partition.type == INITPART_GRID)
505
      message("    grid set to [ %i %i %i ].", initial_partition.grid[0],
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
506
              initial_partition.grid[1], initial_partition.grid[2]);
507
    message("  repartitioning: %s", repartition_name[reparttype.type]);
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
508
  }
509
#endif
510

511
  /* Common variables for restart and IC sections. */
512
  int clean_smoothing_length_values = 0;
513
514
515
  int flag_entropy_ICs = 0;

  /* Work out where we will read and write restart files. */
516
  char restart_dir[PARSER_MAX_LINE_SIZE];
Peter W. Draper's avatar
Peter W. Draper committed
517
518
  parser_get_opt_param_string(params, "Restarts:subdir", restart_dir,
                              "restart");
519
520

  /* The directory must exist. */
521
  if (myrank == 0) {
522
    if (access(restart_dir, W_OK | X_OK) != 0) {
523
      if (restart) {
524
        error("Cannot restart as no restart subdirectory: %s (%s)", restart_dir,
525
              strerror(errno));
526
      } else {
Peter W. Draper's avatar
Peter W. Draper committed
527
528
529
        if (mkdir(restart_dir, 0777) != 0)
          error("Failed to create restart directory: %s (%s)", restart_dir,
                strerror(errno));
530
      }
531
    }
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
532
533
  }

534
  /* Basename for any restart files. */
535
536
  char restart_name[PARSER_MAX_LINE_SIZE];
  parser_get_opt_param_string(params, "Restarts:basename", restart_name,
537
                              "swift");
538

539
540
  /* How often to check for the stop file and dump restarts and exit the
   * application. */
541
  const int restart_stop_steps =
Peter W. Draper's avatar
Peter W. Draper committed
542
      parser_get_opt_param_int(params, "Restarts:stop_steps", 100);
543

544
545
546
547
548
549
550
551
552
553
554
  /* Get the maximal wall-clock time of this run */
  const float restart_max_hours_runtime =
      parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX);

  /* Do we want to resubmit when we hit the limit? */
  const int resubmit_after_max_hours =
      parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0);

  /* What command should we run to resubmit at the end? */
  char resubmit_command[PARSER_MAX_LINE_SIZE];
  if (resubmit_after_max_hours)
555
556
    parser_get_param_string(params, "Restarts:resubmit_command",
                            resubmit_command);
557

558
  /* If restarting, look for the restart files. */
559
  if (restart) {
560

561
    /* Attempting a restart. */
562
563
    char **restart_files = NULL;
    int restart_nfiles = 0;
564
565

    if (myrank == 0) {
566
      message("Restarting SWIFT");
567

568
      /* Locate the restart files. */
Peter W. Draper's avatar
Peter W. Draper committed
569
570
      restart_files =
          restart_locate(restart_dir, restart_name, &restart_nfiles);
571
572
      if (restart_nfiles == 0)
        error("Failed to locate any restart files in %s", restart_dir);
573
574

      /* We need one file per rank. */
575
      if (restart_nfiles != nr_nodes)
576
        error("Incorrect number of restart files, expected %d found %d",
577
              nr_nodes, restart_nfiles);
578
579

      if (verbose > 0)
580
581
        for (int i = 0; i < restart_nfiles; i++)
          message("found restart file: %s", restart_files[i]);
582
583
584
    }

#ifdef WITH_MPI
585
    /* Distribute the restart files, need one for each rank. */
586
587
588
    if (myrank == 0) {

      for (int i = 1; i < nr_nodes; i++) {
589
590
        strcpy(restart_file, restart_files[i]);
        MPI_Send(restart_file, 200, MPI_BYTE, i, 0, MPI_COMM_WORLD);
591
592
593
      }

      /* Keep local file. */
594
      strcpy(restart_file, restart_files[0]);
595
596

      /* Finished with the list. */
597
      restart_locate_free(restart_nfiles, restart_files);
598
599

    } else {
600
      MPI_Recv(restart_file, 200, MPI_BYTE, 0, 0, MPI_COMM_WORLD,
601
602
               MPI_STATUS_IGNORE);
    }
603
    if (verbose > 1) message("local restart file = %s", restart_file);
604
#else
605

606
    /* Just one restart file. */
607
    strcpy(restart_file, restart_files[0]);
608
609
#endif

610
    /* Now read it. */
611
    restart_read(&e, restart_file);
612
613
614

    /* And initialize the engine with the space and policies. */
    if (myrank == 0) clocks_gettime(&tic);
Peter W. Draper's avatar
Peter W. Draper committed
615
616
    engine_config(1, &e, params, nr_nodes, myrank, nr_threads, with_aff,
                  talking, restart_file);
617
618
    if (myrank == 0) {
      clocks_gettime(&toc);
619
      message("engine_config took %.3f %s.", clocks_diff(&tic, &toc),
620
621
622
623
              clocks_getunit());
      fflush(stdout);
    }

624
625
    /* Check if we are already done when given steps on the command-line. */
    if (e.step >= nsteps && nsteps > 0)
626
      error("Not restarting, already completed %d steps", e.step);
627

628
  } else {
629

630
    /* Not restarting so look for the ICs. */
631
    /* Initialize unit system and constants */
632
    units_init_from_params(&us, params, "InternalUnitSystem");
633
    phys_const_init(&us, params, &prog_const);
634
    if (myrank == 0) {
635
636
637
638
639
640
641
      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);
    }
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
642

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    /* Read particles and space information from ICs */
    char ICfileName[200] = "";
    parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
    const int periodic =
        parser_get_param_int(params, "InitialConditions:periodic");
    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);

    /* Some checks that we are not doing something stupid */
    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.");

665
    /* Initialise the cosmology */
666
667
668
    if (with_cosmology)
      cosmology_init(params, &us, &prog_const, &cosmo);
    else
669
      cosmology_init_no_cosmo(&cosmo);
670
    if (myrank == 0 && with_cosmology) cosmology_print(&cosmo);
671

672
    /* Initialise the hydro properties */
673
674
    if (with_hydro)
      hydro_props_init(&hydro_properties, &prog_const, &us, params);
675
676
677
678
679
680
681
682
    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));
683

Loic Hausammann's avatar
Loic Hausammann committed
684
685
    /* Initialise the stars properties */
    if (with_stars)
686
687
      stars_props_init(&stars_properties, &prog_const, &us, params,
                       &hydro_properties);
Loic Hausammann's avatar
Loic Hausammann committed
688
689
690
    else
      bzero(&stars_properties, sizeof(struct stars_props));

691
    /* Initialise the gravity properties */
692
    if (with_self_gravity)
693
694
      gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology,
                         periodic);
695
696
    else
      bzero(&gravity_properties, sizeof(struct gravity_props));
697

698
    /* Be verbose about what happens next */
699
    if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
700
701
    if (myrank == 0 && cleanup_h)
      message("Cleaning up h-factors (h=%f)", cosmo.h);
702
703
    if (myrank == 0 && cleanup_sqrt_a)
      message("Cleaning up a-factors from velocity (a=%f)", cosmo.a);
704
    fflush(stdout);
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
705

706
707
708
709
    /* Get ready to read particles of all kinds */
    size_t Ngas = 0, Ngpart = 0, Nspart = 0;
    double dim[3] = {0., 0., 0.};
    if (myrank == 0) clocks_gettime(&tic);
710
#if defined(HAVE_HDF5)
711
712
#if defined(WITH_MPI)
#if defined(HAVE_PARALLEL_HDF5)
713
    read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
714
                     &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
715
                     (with_external_gravity || with_self_gravity), with_stars,
716
717
718
                     cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
                     nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
                     dry_run);
719
720
#else
    read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
721
                   &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
Matthieu Schaller's avatar
Matthieu Schaller committed
722
                   (with_external_gravity || with_self_gravity), with_stars,
723
724
725
                   cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
                   nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
                   dry_run);
726
727
#endif
#else
728
    read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
729
                   &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
730
                   (with_external_gravity || with_self_gravity), with_stars,
731
732
                   cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads,
                   dry_run);
733
#endif
734
#endif
735
736
737
738
739
740
    if (myrank == 0) {
      clocks_gettime(&toc);
      message("Reading initial conditions took %.3f %s.",
              clocks_diff(&tic, &toc), clocks_getunit());
      fflush(stdout);
    }
741

742
#ifdef SWIFT_DEBUG_CHECKS
743
    /* Check once and for all that we don't have unwanted links */
744
    if (!with_stars && !dry_run) {
745
      for (size_t k = 0; k < Ngpart; ++k)
746
        if (gparts[k].type == swift_type_stars) error("Linking problem");
747
    }
748
    if (!with_hydro && !dry_run) {
749
750
751
      for (size_t k = 0; k < Ngpart; ++k)
        if (gparts[k].type == swift_type_gas) error("Linking problem");
    }
752
753

    /* Check that the other links are correctly set */
754
755
    if (!dry_run)
      part_verify_links(parts, gparts, sparts, Ngas, Ngpart, Nspart, 1);
756
#endif
757

758
759
    /* Get the total number of particles across all nodes. */
    long long N_total[3] = {0, 0, 0};
760
#if defined(WITH_MPI)
761
762
763
    long long N_long[3] = {Ngas, Ngpart, Nspart};
    MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM,
                  MPI_COMM_WORLD);
764
#else
765
766
767
    N_total[0] = Ngas;
    N_total[1] = Ngpart;
    N_total[2] = Nspart;
768
#endif
Pedro Gonnet's avatar
oops.    
Pedro Gonnet committed
769

770
771
    if (myrank == 0)
      message(
772
          "Read %lld gas particles, %lld stars particles and %lld gparts from "
Loic Hausammann's avatar
Loic Hausammann committed
773
          "the ICs.",
774
          N_total[0], N_total[2], N_total[1]);
775

776
777
778
    /* Verify that the fields to dump actually exist */
    if (myrank == 0) io_check_output_fields(params, N_total);

779
780
    /* Initialize the space with these data. */
    if (myrank == 0) clocks_gettime(&tic);
781
    space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart,
782
               Nspart, periodic, replicate, generate_gas_in_ics, with_hydro,
783
               with_self_gravity, talking, dry_run);
784
785
786
787
788
789
790
791

    if (myrank == 0) {
      clocks_gettime(&toc);
      message("space_init took %.3f %s.", clocks_diff(&tic, &toc),
              clocks_getunit());
      fflush(stdout);
    }

792
793
794
    /* Initialise the long-range gravity mesh */
    if (with_self_gravity && periodic) {
#ifdef HAVE_FFTW
795
      pm_mesh_init(&mesh, &gravity_properties, s.dim, nr_threads);
796
797
798
799
800
801
802
803
804
#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);
    }

805
806
    /* Check that the matter content matches the cosmology given in the
     * parameter file. */
807
    if (with_cosmology && with_self_gravity && !dry_run)
808
      space_check_cosmology(&s, &cosmo, myrank);
809

810
/* Also update the total counts (in case of changes due to replication) */
811
#if defined(WITH_MPI)
812
813
814
815
816
    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);
817
#else
818
819
820
    N_total[0] = s.nr_parts;
    N_total[1] = s.nr_gparts;
    N_total[2] = s.nr_sparts;
821
822
#endif

823
824
825
826
827
828
829
830
831
832
833
834
835
    /* 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);
    }
836

837
838
839
840
841
842
    /* 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);
    }
843

844
845
846
847
848
849
    /* 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]);
    }