main.c 20.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 <fenv.h>
Pedro Gonnet's avatar
Pedro Gonnet committed
30
31
32
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
33
#include <unistd.h>
Pedro Gonnet's avatar
Pedro Gonnet committed
34

35
36
/* MPI headers. */
#ifdef WITH_MPI
37
#include <mpi.h>
38
39
#endif

Pedro Gonnet's avatar
Pedro Gonnet committed
40
/* Local headers. */
41
#include "swift.h"
Pedro Gonnet's avatar
Pedro Gonnet committed
42

43
44
/* Engine policy flags. */
#ifndef ENGINE_POLICY
45
#define ENGINE_POLICY engine_policy_none
46
47
#endif

48
49
50
/**
 * @brief Help messages for the command line parameters.
 */
51
52
void print_help_message() {

Matthieu Schaller's avatar
Matthieu Schaller committed
53
54
55
56
  printf("\nUsage: swift [OPTION]... PARAMFILE\n");
  printf("       swift_mpi [OPTION]... PARAMFILE\n");
  printf("       swift_fixdt [OPTION]... PARAMFILE\n");
  printf("       swift_fixdt_mpi [OPTION]... PARAMFILE\n\n");
57

58
  printf("Valid options are:\n");
59
  printf("  %2s %8s %s\n", "-a", "", "Pin runners using processor affinity");
60
  printf("  %2s %8s %s\n", "-c", "", "Run with cosmological time integration");
61
62
63
64
65
66
67
68
69
  printf(
      "  %2s %8s %s\n", "-d", "",
      "Dry run. Read the parameter file, allocate memory but does not read ");
  printf(
      "  %2s %8s %s\n", "", "",
      "the particles from ICs and exit before the start of time integration.");
  printf("  %2s %8s %s\n", "", "",
         "Allows user to check validy of parameter and IC files as well as "
         "memory limits.");
70
71
  printf("  %2s %8s %s\n", "-e", "",
         "Enable floating-point exceptions (debugging mode)");
72
73
  printf("  %2s %8s %s\n", "-f", "{int}",
         "Overwrite the CPU frequency (Hz) to be used for time measurements");
74
75
76
77
  printf("  %2s %8s %s\n", "-g", "",
         "Run with an external gravitational potential");
  printf("  %2s %8s %s\n", "-G", "", "Run with self-gravity");
  printf("  %2s %8s %s\n", "-s", "", "Run with SPH");
78
79
80
  printf("  %2s %8s %s\n", "-t", "{int}",
         "The number of threads to use on each MPI rank. Defaults to 1 if not "
         "specified.");
81
  printf("  %2s %8s %s\n", "-v", "[12]",
82
83
         "Increase the level of verbosity 1: MPI-rank 0 writes ");
  printf("  %2s %8s %s\n", "", "", "2: All MPI-ranks write");
84
  printf("  %2s %8s %s\n", "-y", "{int}",
85
         "Time-step frequency at which task graphs are dumped");
86
  printf("  %2s %8s %s\n", "-h", "", "Print this help message and exit");
87
88
89
  printf(
      "\nSee the file parameter_example.yml for an example of "
      "parameter file.\n");
90
91
}

Pedro Gonnet's avatar
Pedro Gonnet committed
92
93
94
95
/**
 * @brief Main routine that loads a few particles and generates some output.
 *
 */
96
97
int main(int argc, char *argv[]) {

98
  struct clocks_time tic, toc;
99

100
  int nr_nodes = 1, myrank = 0;
101

102
#ifdef WITH_MPI
103
  /* Start by initializing MPI. */
104
  int res = 0, prov = 0;
105
106
  if ((res = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &prov)) !=
      MPI_SUCCESS)
107
108
    error("Call to MPI_Init failed with error %i.", res);
  if (prov != MPI_THREAD_MULTIPLE)
109
110
111
    error(
        "MPI does not provide the level of threading required "
        "(MPI_THREAD_MULTIPLE).");
112
  if ((res = MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes)) != MPI_SUCCESS)
113
114
115
116
117
118
    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);
  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);
119
  if (myrank == 0)
120
121
    printf("[0000][00000.0] MPI is up and running with %i node(s).\n",
           nr_nodes);
122
123
124
125
  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.");
  }
126
  fflush(stdout);
127
#endif
128

129
/* Let's pin the main thread */
130
#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE)
131
  if (((ENGINE_POLICY)&engine_policy_setaffinity) == engine_policy_setaffinity)
132
    engine_pin();
Angus Lepper's avatar
Angus Lepper committed
133
134
#endif

135
136
  /* Welcome to SWIFT, you made the right choice */
  if (myrank == 0) greetings();
137

138
  int with_aff = 0;
139
  int dry_run = 0;
140
141
142
143
144
  int dump_tasks = 0;
  int with_cosmology = 0;
  int with_external_gravity = 0;
  int with_self_gravity = 0;
  int with_hydro = 0;
145
  int with_fp_exceptions = 0;
146
  int verbose = 0;
147
  int nr_threads = 1;
148
149
150
151
  char paramFileName[200] = "";
  unsigned long long cpufreq = 0;

  /* Parse the parameters */
152
  int c;
153
  while ((c = getopt(argc, argv, "acdef:gGhst:v:y:")) != -1) switch (c) {
154
      case 'a':
155
        with_aff = 1;
156
        break;
157
      case 'c':
158
        with_cosmology = 1;
159
160
        break;
      case 'd':
161
        dry_run = 1;
162
        break;
163
      case 'e':
164
        with_fp_exceptions = 1;
165
        break;
166
      case 'f':
167
168
169
        if (sscanf(optarg, "%llu", &cpufreq) != 1) {
          if (myrank == 0) printf("Error parsing CPU frequency (-f).\n");
          if (myrank == 0) print_help_message();
170
          return 1;
171
        }
172
        break;
173
      case 'g':
174
        with_external_gravity = 1;
175
        break;
176
177
      case 'G':
        with_self_gravity = 1;
178
        break;
179
      case 'h':
180
        if (myrank == 0) print_help_message();
181
        return 0;
182
      case 's':
183
        with_hydro = 1;
184
        break;
185
186
187
188
189
190
191
192
      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;
193
      case 'v':
194
195
196
        if (sscanf(optarg, "%d", &verbose) != 1) {
          if (myrank == 0) printf("Error parsing verbosity level (-v).\n");
          if (myrank == 0) print_help_message();
197
          return 1;
198
        }
199
        break;
200
      case 'y':
201
202
203
        if (sscanf(optarg, "%d", &dump_tasks) != 1) {
          if (myrank == 0) printf("Error parsing dump_tasks (-y). \n");
          if (myrank == 0) print_help_message();
204
          return 1;
205
        }
206
207
        break;
      case '?':
208
        if (myrank == 0) print_help_message();
209
        return 1;
210
211
        break;
    }
212
213
214
215
216
217
  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();
218
    return 1;
219
220
221
  } else {
    if (myrank == 0) printf("Error: Too many parameters given\n");
    if (myrank == 0) print_help_message();
222
223
224
225
226
227
228
    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;
229
  }
230

231
  /* Genesis 1.1: And then, there was time ! */
232
  clocks_set_cpufreq(cpufreq);
233

234
235
236
237
  if (myrank == 0 && dry_run)
    message(
        "Executing a dry run. No i/o or time integration will be performed.");

238
  /* Report CPU frequency. */
239
  cpufreq = clocks_get_cpufreq();
240
  if (myrank == 0) {
241
    message("CPU frequency used for tick conversion: %llu Hz", cpufreq);
242
243
  }

244
245
246
247
  /* Do we choke on FP-exceptions ? */
  if (with_fp_exceptions) {
    feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
    if (myrank == 0) message("Floating point exceptions will be reported.");
248
  }
249

250
251
  /* How large are the parts? */
  if (myrank == 0) {
252
253
254
    message("sizeof(struct part)  is %4zi bytes.", sizeof(struct part));
    message("sizeof(struct xpart) is %4zi bytes.", sizeof(struct xpart));
    message("sizeof(struct gpart) is %4zi bytes.", sizeof(struct gpart));
255
256
    message("sizeof(struct task)  is %4zi bytes.", sizeof(struct task));
    message("sizeof(struct cell)  is %4zi bytes.", sizeof(struct cell));
257
  }
258

259
260
261
  /* How vocal are we ? */
  const int talking = (verbose == 1 && myrank == 0) || (verbose == 2);

262
  /* Read the parameter file */
263
264
  struct swift_params *params = malloc(sizeof(struct swift_params));
  if (params == NULL) error("Error allocating memory for the parameter file.");
265
266
  if (myrank == 0) {
    message("Reading parameters from file '%s'", paramFileName);
267
    parser_read_file(paramFileName, params);
268
    // parser_print_params(&params);
269
    parser_write_params_to_file(params, "used_parameters.yml");
270
  }
271
272
#ifdef WITH_MPI
  /* Broadcast the parameter file */
273
  MPI_Bcast(params, sizeof(struct swift_params), MPI_BYTE, 0, MPI_COMM_WORLD);
274
#endif
275

276
/* Prepare the domain decomposition scheme */
277
#ifdef WITH_MPI
278
279
  struct partition initial_partition;
  enum repartition_type reparttype;
280
  partition_init(&initial_partition, &reparttype, params, nr_nodes);
281
282

  /* Let's report what we did */
283
  if (myrank == 0) {
284
285
286
287
288
289
    message("Using initial partition %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("Using %s repartitioning", repartition_name[reparttype]);
290
  }
291
#endif
292

Matthieu Schaller's avatar
Matthieu Schaller committed
293
  /* Initialize unit system and constants */
294
  struct UnitSystem us;
295
  struct phys_const prog_const;
296
  units_init(&us, params, "InternalUnitSystem");
297
298
  phys_const_init(&us, &prog_const);
  if (myrank == 0 && verbose > 0) {
299
300
301
302
303
    message("Unit system: U_M = %e g.", us.UnitMass_in_cgs);
    message("Unit system: U_L = %e cm.", us.UnitLength_in_cgs);
    message("Unit system: U_t = %e s.", us.UnitTime_in_cgs);
    message("Unit system: U_I = %e A.", us.UnitCurrent_in_cgs);
    message("Unit system: U_T = %e K.", us.UnitTemperature_in_cgs);
304
    phys_const_print(&prog_const);
305
  }
306

307
308
309
310
  /* Initialise the hydro properties */
  struct hydro_props hydro_properties;
  hydro_props_init(&hydro_properties, params);

Matthieu Schaller's avatar
Matthieu Schaller committed
311
312
  /* Initialise the external potential properties */
  struct external_potential potential;
313
314
  if (with_external_gravity) potential_init(params, &us, &potential);
  if (with_external_gravity && myrank == 0) potential_print(&potential);
315

316
  /* Read particles and space information from (GADGET) ICs */
317
  char ICfileName[200] = "";
318
  parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
319
  if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
320
321
  struct part *parts = NULL;
  struct gpart *gparts = NULL;
322
323
324
  size_t Ngas = 0, Ngpart = 0;
  double dim[3] = {0., 0., 0.};
  int periodic = 0;
325
  if (myrank == 0) clocks_gettime(&tic);
326
327
#if defined(WITH_MPI)
#if defined(HAVE_PARALLEL_HDF5)
328
  read_ic_parallel(ICfileName, dim, &parts, &gparts, &Ngas, &Ngpart, &periodic,
329
                   myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, dry_run);
330
#else
Matthieu Schaller's avatar
Matthieu Schaller committed
331
  read_ic_serial(ICfileName, dim, &parts, &gparts, &Ngas, &Ngpart, &periodic,
332
                 myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, dry_run);
333
334
#endif
#else
335
336
  read_ic_single(ICfileName, dim, &parts, &gparts, &Ngas, &Ngpart, &periodic,
                 dry_run);
337
#endif
338
339
  if (myrank == 0) {
    clocks_gettime(&toc);
340
341
    message("Reading initial conditions took %.3f %s.", clocks_diff(&tic, &toc),
            clocks_getunit());
342
343
    fflush(stdout);
  }
344

345
346
  /* Discard gparts if we don't have gravity
   * (Better implementation of i/o will come)*/
347
  if (!with_external_gravity && !with_self_gravity) {
348
    free(gparts);
349
    gparts = NULL;
Matthieu Schaller's avatar
Matthieu Schaller committed
350
    for (size_t k = 0; k < Ngas; ++k) parts[k].gpart = NULL;
351
    Ngpart = 0;
352
  }
353
354
355
  if (!with_hydro) {
    free(parts);
    parts = NULL;
356
357
    for (size_t k = 0; k < Ngpart; ++k)
      if (gparts[k].id > 0) error("Linking problem");
358
359
    Ngas = 0;
  }
360
361
362

  /* Get the total number of particles across all nodes. */
  long long N_total[2] = {0, 0};
363
#if defined(WITH_MPI)
364
365
  long long N_long[2] = {Ngas, Ngpart};
  MPI_Reduce(&N_long, &N_total, 2, MPI_LONG_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
366
#else
367
  N_total[0] = Ngas;
368
  N_total[1] = Ngpart;
369
#endif
370
371
372
  if (myrank == 0)
    message("Read %lld gas particles and %lld gparts from the ICs.", N_total[0],
            N_total[1]);
373

374
  /* Initialize the space with these data. */
375
  if (myrank == 0) clocks_gettime(&tic);
376
  struct space s;
377
  space_init(&s, params, dim, parts, gparts, Ngas, Ngpart, periodic, talking,
378
             dry_run);
379
  if (myrank == 0) {
380
    clocks_gettime(&toc);
381
382
    message("space_init took %.3f %s.", clocks_diff(&tic, &toc),
            clocks_getunit());
383
384
    fflush(stdout);
  }
385
386
387
388
389
390
391
392

  /* 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]);
Peter W. Draper's avatar
Peter W. Draper committed
393
    message("%zi parts in %i cells.", s.nr_parts, s.tot_cells);
394
    message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells);
Matthieu Schaller's avatar
Matthieu Schaller committed
395
396
    message("maximum depth is %d.", s.maxdepth);
    fflush(stdout);
397
398
  }

399
  /* Verify that each particle is in it's proper cell. */
400
  if (talking && !dry_run) {
401
    int icount = 0;
402
403
404
405
    space_map_cells_pre(&s, 0, &map_cellcheck, &icount);
    message("map_cellcheck picked up %i parts.", icount);
  }

406
  /* Verify the maximal depth of cells. */
407
  if (talking && !dry_run) {
408
    int data[2] = {s.maxdepth, 0};
409
410
411
412
    space_map_cells_pre(&s, 0, &map_maxdepth, data);
    message("nr of cells at depth %i is %i.", data[0], data[1]);
  }

413
  /* Construct the engine policy */
414
  int engine_policies = ENGINE_POLICY | engine_policy_steal;
415
  if (with_hydro) engine_policies |= engine_policy_hydro;
416
  if (with_self_gravity) engine_policies |= engine_policy_self_gravity;
417
418
  if (with_external_gravity) engine_policies |= engine_policy_external_gravity;
  if (with_cosmology) engine_policies |= engine_policy_cosmology;
419

420
  /* Initialize the engine with the space and policies. */
421
  if (myrank == 0) clocks_gettime(&tic);
422
  struct engine e;
423
424
425
  engine_init(&e, &s, params, nr_nodes, myrank, nr_threads, with_aff,
              engine_policies, talking, &prog_const, &hydro_properties,
              &potential);
426
  if (myrank == 0) {
427
    clocks_gettime(&toc);
428
429
    message("engine_init took %.3f %s.", clocks_diff(&tic, &toc),
            clocks_getunit());
430
431
    fflush(stdout);
  }
432

433
  /* Write the state of the system before starting time integration. */
434
  if (!dry_run) engine_dump_snapshot(&e);
435
436
437
438
439
440

/* Init the runner history. */
#ifdef HIST
  for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0;
#endif

441
  /* Get some info to the user. */
442
  if (myrank == 0) {
443
    message(
444
445
446
447
        "Running on %lld gas particles and %lld DM particles from t=%.3e until "
        "t=%.3e with %d threads and %d queues (dt_min=%.3e, dt_max=%.3e)...",
        N_total[0], N_total[1], e.timeBegin, e.timeEnd, e.nr_threads,
        e.sched.nr_queues, e.dt_min, e.dt_max);
448
449
    fflush(stdout);
  }
450

451
452
  /* Time to say good-bye if this was not a serious run. */
  if (dry_run) {
453
454
455
456
#ifdef WITH_MPI
    if ((res = MPI_Finalize()) != MPI_SUCCESS)
      error("call to MPI_Finalize failed with error %i.", res);
#endif
457
    if (myrank == 0)
458
      message("Time integration ready to start. End of dry-run.");
459
460
461
462
463
464
465
466
467
    return 0;
  }

#ifdef WITH_MPI
  /* Split the space. */
  engine_split(&e, &initial_partition);
  engine_redistribute(&e);
#endif

468
469
  /* Initialise the particles */
  engine_init_particles(&e);
470

471
472
  /* Legend */
  if (myrank == 0)
473
474
    printf("# %6s %14s %14s %10s %10s %16s [%s]\n", "Step", "Time", "Time-step",
           "Updates", "g-Updates", "Wall-clock time", clocks_getunit());
475

476
  /* Main simulation loop */
Matthieu Schaller's avatar
Matthieu Schaller committed
477
  for (int j = 0; !engine_is_done(&e); j++) {
478
479

/* Repartition the space amongst the nodes? */
480
#ifdef WITH_MPI
481
    if (j % 100 == 2) e.forcerepart = reparttype;
482
483
#endif

484
    /* Reset timers */
485
486
487
488
489
    timers_reset(timers_mask_all);

    /* Take a step. */
    engine_step(&e);

490
    /* Dump the task data using the given frequency. */
491
492
493
    if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) {
#ifdef WITH_MPI

494
      /* Make sure output file is empty, only on one rank. */
495
      char dumpfile[30];
496
      snprintf(dumpfile, 30, "thread_info_MPI-step%d.dat", j);
497
      FILE *file_thread;
498
      if (myrank == 0) {
499
500
        file_thread = fopen(dumpfile, "w");
        fclose(file_thread);
501
502
      }
      MPI_Barrier(MPI_COMM_WORLD);
503
504
505
506
507
508
509
510
511
512
513
514

      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");

515
516
          fprintf(file_thread, " %03i 0 0 0 0 %lli %lli 0 0 0 0 %lli\n", myrank,
                  e.tic_step, e.toc_step, cpufreq);
517
518
519
          int count = 0;
          for (int l = 0; l < e.sched.nr_tasks; l++)
            if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit) {
520
521
522
523
524
525
526
527
528
529
530
531
532
533
              fprintf(
                  file_thread, " %03i %i %i %i %i %lli %lli %i %i %i %i %i\n",
                  myrank, e.sched.tasks[l].last_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->count
                                                : 0,
                  (e.sched.tasks[l].cj != NULL) ? e.sched.tasks[l].cj->count
                                                : 0,
                  (e.sched.tasks[l].ci != NULL) ? e.sched.tasks[l].ci->gcount
                                                : 0,
                  (e.sched.tasks[l].cj != NULL) ? e.sched.tasks[l].cj->gcount
                                                : 0,
                  e.sched.tasks[l].flags);
534
535
536
537
538
539
540
541
542
543
544
545
546
              fflush(stdout);
              count++;
            }
          message("rank %d counted %d tasks", myrank, count);

          fclose(file_thread);
        }

        /* And we wait for all to synchronize. */
        MPI_Barrier(MPI_COMM_WORLD);
      }

#else
547
      char dumpfile[30];
548
      snprintf(dumpfile, 30, "thread_info-step%d.dat", j);
549
      FILE *file_thread;
550
      file_thread = fopen(dumpfile, "w");
551
      /* Add some information to help with the plots */
552
553
      fprintf(file_thread, " %i %i %i %i %lli %lli %i %i %i %lli\n", -2, -1, -1,
              1, e.tic_step, e.toc_step, 0, 0, 0, cpufreq);
554
555
      for (int l = 0; l < e.sched.nr_tasks; l++)
        if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit)
556
          fprintf(
557
558
              file_thread, " %i %i %i %i %lli %lli %i %i %i %i\n",
              e.sched.tasks[l].last_rid, e.sched.tasks[l].type,
559
560
561
              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->count,
562
563
564
              (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->count,
              (e.sched.tasks[l].ci == NULL) ? 0 : e.sched.tasks[l].ci->gcount,
              (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->gcount);
565
566
567
      fclose(file_thread);
#endif
    }
568
569
570
  }

/* Print the values of the runner histogram. */
571
#ifdef HIST
572
573
574
575
576
577
578
  printf("main: runner histogram data:\n");
  for (k = 0; k < runner_hist_N; k++)
    printf(" %e %e %e\n",
           runner_hist_a + k * (runner_hist_b - runner_hist_a) / runner_hist_N,
           runner_hist_a +
               (k + 1) * (runner_hist_b - runner_hist_a) / runner_hist_N,
           (double)runner_hist_bins[k]);
579
#endif
Pedro Gonnet's avatar
Pedro Gonnet committed
580

581
  /* Write final output. */
582
  engine_dump_snapshot(&e);
583

584
#ifdef WITH_MPI
585
  if ((res = MPI_Finalize()) != MPI_SUCCESS)
586
    error("call to MPI_Finalize failed with error %i.", res);
587
#endif
588
589

  /* Say goodbye. */
Matthieu Schaller's avatar
Matthieu Schaller committed
590
  if (myrank == 0) message("done. Bye.");
591
592
593

  /* All is calm, all is bright. */
  return 0;
594
}