/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
* Matthieu Schaller (schaller@strw.leidenuniv.nl)
* 2015 Peter W. Draper (p.w.draper@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/* Config parameters. */
#include
/* MPI headers. */
#ifdef WITH_MPI
#include
#endif
/* This object's header. */
#include "engine.h"
/* Local headers. */
#include "active.h"
#include "csds_io.h"
#include "distributed_io.h"
#include "kick.h"
#include "lightcone/lightcone.h"
#include "lightcone/lightcone_array.h"
#include "line_of_sight.h"
#include "parallel_io.h"
#include "power_spectrum.h"
#include "serial_io.h"
#include "single_io.h"
#include "tracers.h"
/* Standard includes */
#include
/**
* @brief Finalize the quantities recorded via the snapshot triggers
*
* This adds the small amount of time between the particles' last start
* of step and the current (snapshot) time.
*
* @param e The #engine to act on.
*/
void engine_finalize_trigger_recordings(struct engine *e) {
const int with_cosmology = (e->policy & engine_policy_cosmology);
struct space *s = e->s;
/* Finish the recording period for part triggers */
if (num_snapshot_triggers_part) {
for (size_t k = 0; k < s->nr_parts; ++k) {
/* Get a handle on the part. */
struct part *p = &s->parts[k];
struct xpart *xp = &s->xparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, p->time_bin);
/* Escape inhibited particles */
if (part_is_inhibited(p, e)) continue;
/* We need to escape the special case of a particle that
* actually ended its time-step on this very step */
if (e->ti_current - ti_begin == get_integer_timestep(p->time_bin))
continue;
/* Time from the start of the particle's step to the snapshot (aka.
* current time) */
double missing_time;
if (with_cosmology) {
missing_time =
cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current);
} else {
missing_time = (e->ti_current - ti_begin) * e->time_base;
}
tracers_after_timestep_part(
p, xp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, e->hydro_properties, e->cooling_func, e->time,
missing_time, e->snapshot_recording_triggers_started_part);
}
}
/* Finish the recording period for spart triggers */
if (num_snapshot_triggers_spart) {
for (size_t k = 0; k < s->nr_sparts; ++k) {
/* Get a handle on the part. */
struct spart *sp = &s->sparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, sp->time_bin);
/* Escape inhibited particles */
if (spart_is_inhibited(sp, e)) continue;
/* We need to escape the special case of a particle that
* actually ended its time-step on this very step */
if (e->ti_current - ti_begin == get_integer_timestep(sp->time_bin))
continue;
/* Time from the start of the particle's step to the snapshot (aka.
* current time) */
double missing_time;
if (with_cosmology) {
missing_time =
cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current);
} else {
missing_time = (e->ti_current - ti_begin) * e->time_base;
}
tracers_after_timestep_spart(
sp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, missing_time,
e->snapshot_recording_triggers_started_spart);
}
}
/* Finish the recording period for spart triggers */
if (num_snapshot_triggers_bpart) {
for (size_t k = 0; k < s->nr_bparts; ++k) {
/* Get a handle on the part. */
struct bpart *bp = &s->bparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, bp->time_bin);
/* Escape inhibited particles */
if (bpart_is_inhibited(bp, e)) continue;
/* We need to escape the special case of a particle that
* actually ended its time-step on this very step */
if (e->ti_current - ti_begin == get_integer_timestep(bp->time_bin))
continue;
/* Time from the start of the particle's step to the snapshot (aka.
* current time) */
double missing_time;
if (with_cosmology) {
missing_time =
cosmology_get_delta_time(e->cosmology, ti_begin, e->ti_current);
} else {
missing_time = (e->ti_current - ti_begin) * e->time_base;
}
tracers_after_timestep_bpart(
bp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, missing_time,
e->snapshot_recording_triggers_started_bpart);
}
}
}
/**
* @brief dump restart files if it is time to do so and dumps are enabled.
*
* @param e the engine.
* @param drifted_all true if a drift_all has just been performed.
* @param force force a dump, if dumping is enabled.
* @return Do we want to stop the run altogether?
*/
int engine_dump_restarts(struct engine *e, const int drifted_all,
const int force) {
/* Are any of the conditions to fully stop a run met? */
const int end_run_time = e->runtime > e->restart_max_hours_runtime;
const int stop_file = (e->step % e->restart_stop_steps == 0 &&
restart_stop_now(e->restart_dir, 0));
/* Exit run when told to */
const int exit_run = (end_run_time || stop_file);
if (e->restart_dump) {
ticks tic = getticks();
const int check_point_time = tic > e->restart_next;
/* Dump when the time has arrived, or we are told to. */
int dump = (check_point_time || end_run_time || force || stop_file);
#ifdef WITH_MPI
/* Synchronize this action from rank 0 (ticks may differ between
* machines). */
MPI_Bcast(&dump, 1, MPI_INT, 0, MPI_COMM_WORLD);
#endif
if (dump) {
if (e->nodeID == 0) {
/* Flush the time-step file to avoid gaps in case of crashes
* before the next automated flush */
fflush(e->file_timesteps);
message("Writing restart files");
}
/* Clean out the previous saved files, if found. Do this now as we are
* MPI synchronized. */
restart_remove_previous(e->restart_file);
/* Drift all particles first (may have just been done). */
if (!drifted_all) engine_drift_all(e, /*drift_mpole=*/1);
/* Free the foreign particles to get more breathing space. */
#ifdef WITH_MPI
if (e->free_foreign_when_dumping_restart)
space_free_foreign_parts(e->s, /*clear_cell_pointers=*/1);
#endif
#ifdef WITH_LIGHTCONE
/* Flush lightcone buffers before dumping restarts */
lightcone_array_flush(e->lightcone_array_properties, &(e->threadpool),
e->cosmology, e->internal_units, e->snapshot_units,
/*flush_map_updates=*/1, /*flush_particles=*/1,
/*end_file=*/1, /*dump_all_shells=*/0);
#ifdef WITH_MPI
MPI_Barrier(MPI_COMM_WORLD);
#endif
#endif
restart_write(e, e->restart_file);
#ifdef WITH_MPI
/* Make sure all ranks finished writing to avoid having incomplete
* sets of restart files should the code crash before all the ranks
* are done */
MPI_Barrier(MPI_COMM_WORLD);
/* Reallocate freed memory */
if (e->free_foreign_when_dumping_restart)
engine_allocate_foreign_particles(e, /*fof=*/0);
#endif
if (e->verbose)
message("Dumping restart files took %.3f %s",
clocks_from_ticks(getticks() - tic), clocks_getunit());
/* Time after which next dump will occur. */
e->restart_next += e->restart_dt;
/* Flag that we dumped the restarts */
e->step_props |= engine_step_prop_restarts;
}
}
/* If we stopped by reaching the time limit, flag that we need to
* run the resubmission command */
if (end_run_time && e->resubmit_after_max_hours) e->resubmit = 1;
return exit_run;
}
/**
* @brief Writes a snapshot with the current state of the engine
*
* @param e The #engine.
* @param fof Is this a stand-alone FOF call?
*/
void engine_dump_snapshot(struct engine *e, const int fof) {
struct clocks_time time1, time2;
clocks_gettime(&time1);
#ifdef SWIFT_DEBUG_CHECKS
/* Check that all cells have been drifted to the current time.
* That can include cells that have not
* previously been active on this rank. */
space_check_drift_point(e->s, e->ti_current, /* check_mpole=*/0);
/* Be verbose about this */
if (e->nodeID == 0) {
if (e->policy & engine_policy_cosmology)
message("Dumping snapshot at a=%e",
exp(e->ti_current * e->time_base) * e->cosmology->a_begin);
else
message("Dumping snapshot at t=%e",
e->ti_current * e->time_base + e->time_begin);
}
#else
if (e->verbose) {
if (e->policy & engine_policy_cosmology)
message("Dumping snapshot at a=%e",
exp(e->ti_current * e->time_base) * e->cosmology->a_begin);
else
message("Dumping snapshot at t=%e",
e->ti_current * e->time_base + e->time_begin);
}
#endif
#ifdef DEBUG_INTERACTIONS_STARS
engine_collect_stars_counter(e);
#endif
/* Finalize the recording periods */
engine_finalize_trigger_recordings(e);
/* Get time-step since the last mesh kick */
if ((e->policy & engine_policy_self_gravity) && e->s->periodic) {
const int with_cosmology = (e->policy & engine_policy_cosmology);
e->dt_kick_grav_mesh_for_io =
kick_get_grav_kick_dt(e->mesh->ti_beg_mesh_next, e->ti_current,
e->time_base, with_cosmology, e->cosmology) -
kick_get_grav_kick_dt(
e->mesh->ti_beg_mesh_next,
(e->mesh->ti_beg_mesh_next + e->mesh->ti_end_mesh_next) / 2,
e->time_base, with_cosmology, e->cosmology);
}
/* Dump (depending on the chosen strategy) ... */
#if defined(HAVE_HDF5)
#if defined(WITH_MPI)
MPI_Info info;
MPI_Info_create(&info);
if (e->snapshot_distributed) {
write_output_distributed(e, e->internal_units, e->snapshot_units, fof,
e->nodeID, e->nr_nodes, MPI_COMM_WORLD, info);
} else {
#if defined(HAVE_PARALLEL_HDF5)
write_output_parallel(e, e->internal_units, e->snapshot_units, fof,
e->nodeID, e->nr_nodes, MPI_COMM_WORLD, info);
#else
write_output_serial(e, e->internal_units, e->snapshot_units, fof, e->nodeID,
e->nr_nodes, MPI_COMM_WORLD, info);
#endif
}
MPI_Info_free(&info);
#else
write_output_single(e, e->internal_units, e->snapshot_units, fof);
#endif /* WITH_MPI */
#endif /* WITH_HDF5 */
/* Cancel any triggers that are switched on */
if (num_snapshot_triggers_part > 0 || num_snapshot_triggers_spart > 0 ||
num_snapshot_triggers_bpart > 0) {
/* Reset the trigger flags */
for (int i = 0; i < num_snapshot_triggers_part; ++i)
e->snapshot_recording_triggers_started_part[i] = 0;
for (int i = 0; i < num_snapshot_triggers_spart; ++i)
e->snapshot_recording_triggers_started_spart[i] = 0;
for (int i = 0; i < num_snapshot_triggers_bpart; ++i)
e->snapshot_recording_triggers_started_bpart[i] = 0;
/* Reser the tracers themselves */
space_after_snap_tracer(e->s, e->verbose);
}
/* Flag that we dumped a snapshot */
e->step_props |= engine_step_prop_snapshot;
clocks_gettime(&time2);
if (e->verbose)
message("writing particle properties took %.3f %s.",
(float)clocks_diff(&time1, &time2), clocks_getunit());
/* Run the post-dump command if required */
if (e->nodeID == 0) {
engine_run_on_dump(e);
}
}
/**
* @brief Runs the snapshot_dump_command if relevant. Note that we
* perform no error checking on this command, and assume
* it works fine.
*
* @param e The #engine.
*/
void engine_run_on_dump(struct engine *e) {
if (e->snapshot_run_on_dump) {
/* Generate a string containing (optionally) the snapshot number.
* Note that -1 is used because snapshot_output_count was just
* increased when the write_output_* functions are called. */
const int buf_size = PARSER_MAX_LINE_SIZE * 3;
char dump_command_buf[buf_size];
snprintf(dump_command_buf, buf_size, "%s %s %04d", e->snapshot_dump_command,
e->snapshot_base_name, e->snapshot_output_count - 1);
/* Let's trust the user's command... */
const int result = system(dump_command_buf);
if (result != 0) {
message("Snapshot dump command returned error code %d", result);
}
}
}
/**
* @brief Check whether any kind of i/o has to be performed during this
* step.
*
* This includes snapshots, stats and halo finder. We also handle the case
* of multiple outputs between two steps.
*
* @param e The #engine.
*/
void engine_io(struct engine *e) {
const int with_cosmology = (e->policy & engine_policy_cosmology);
const int with_stf = (e->policy & engine_policy_structure_finding);
const int with_los = (e->policy & engine_policy_line_of_sight);
const int with_fof = (e->policy & engine_policy_fof);
const int with_power = (e->policy & engine_policy_power_spectra);
/* What kind of output are we getting? */
enum output_type {
output_none,
output_snapshot,
output_statistics,
output_ps,
output_stf,
output_los,
};
/* What kind of output do we want? And at which time ?
* Find the earliest output (amongst all kinds) that takes place
* before the next time-step */
enum output_type type = output_none;
integertime_t ti_output = max_nr_timesteps;
e->stf_this_timestep = 0;
/* Save some statistics ? */
if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) {
if (e->ti_next_stats < ti_output) {
ti_output = e->ti_next_stats;
type = output_statistics;
}
}
/* Do we want a snapshot? */
if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) {
if (e->ti_next_snapshot < ti_output) {
ti_output = e->ti_next_snapshot;
type = output_snapshot;
}
}
/* Do we want a power-spectrum? */
if (e->ti_end_min > e->ti_next_ps && e->ti_next_ps > 0) {
if (e->ti_next_ps < ti_output) {
ti_output = e->ti_next_ps;
type = output_ps;
}
}
/* Do we want to perform structure finding? */
if (with_stf) {
if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) {
if (e->ti_next_stf < ti_output) {
ti_output = e->ti_next_stf;
type = output_stf;
}
}
}
/* Do we want to write a line of sight file? */
if (with_los) {
if (e->ti_end_min > e->ti_next_los && e->ti_next_los > 0) {
if (e->ti_next_los < ti_output) {
ti_output = e->ti_next_los;
type = output_los;
}
}
}
/* Store information before attempting extra dump-related drifts */
const integertime_t ti_current = e->ti_current;
const timebin_t max_active_bin = e->max_active_bin;
const double time = e->time;
while (type != output_none) {
/* Let's fake that we are at the dump time */
e->ti_current = ti_output;
e->max_active_bin = 0;
if (with_cosmology) {
cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
e->time = e->cosmology->time;
} else {
e->time = ti_output * e->time_base + e->time_begin;
}
/* Drift everyone */
engine_drift_all(e, /*drift_mpole=*/0);
/* Write some form of output */
switch (type) {
case output_snapshot:
#ifdef SWIFT_GRAVITY_FORCE_CHECKS
/* Indicate we are allowed to do a brute force calculation now */
e->force_checks_snapshot_flag = 1;
#endif
/* Free the mesh memory to get some breathing space */
if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
pm_mesh_free(e->mesh);
/* Free the foreign particles to get more breathing space.
* If called, the FOF code itself will reallocate what it needs. */
#ifdef WITH_MPI
space_free_foreign_parts(e->s, /*clear_cell_pointers=*/1);
#endif
/* Do we want FoF group IDs in the snapshot? */
if (with_fof && e->snapshot_invoke_fof) {
engine_fof(e, /*dump_results=*/1, /*dump_debug=*/0,
/*seed_black_holes=*/0, /*buffers allocated=*/0);
}
/* Do we want a corresponding VELOCIraptor output? */
if (with_stf && e->snapshot_invoke_stf && !e->stf_this_timestep) {
#ifdef HAVE_VELOCIRAPTOR
velociraptor_invoke(e, /*linked_with_snap=*/1);
e->step_props |= engine_step_prop_stf;
#else
error(
"Asking for a VELOCIraptor output but SWIFT was compiled without "
"the interface!");
#endif
}
/* Do we want power spectrum outputs? */
if (with_power && e->snapshot_invoke_ps) {
calc_all_power_spectra(e->power_data, e->s, &e->threadpool,
e->verbose);
}
/* Dump... */
engine_dump_snapshot(e, /*fof=*/0);
/* Free the memory allocated for VELOCIraptor i/o. */
if (with_stf && e->snapshot_invoke_stf && e->s->gpart_group_data) {
#ifdef HAVE_VELOCIRAPTOR
swift_free("gpart_group_data", e->s->gpart_group_data);
e->s->gpart_group_data = NULL;
#endif
}
/* Reallocate freed memory */
if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
pm_mesh_allocate(e->mesh);
#ifdef WITH_MPI
engine_allocate_foreign_particles(e, /*fof=*/0);
#endif
/* ... and find the next output time */
engine_compute_next_snapshot_time(e, /*restart=*/0);
break;
case output_statistics:
/* Dump */
engine_print_stats(e);
/* and move on */
engine_compute_next_statistics_time(e);
break;
case output_stf:
/* Free the mesh memory to get some breathing space */
if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
pm_mesh_free(e->mesh);
#ifdef WITH_MPI
space_free_foreign_parts(e->s, /*clear_cell_pointers=*/1);
#endif
#ifdef HAVE_VELOCIRAPTOR
/* Unleash the raptor! */
if (!e->stf_this_timestep) {
velociraptor_invoke(e, /*linked_with_snap=*/0);
e->step_props |= engine_step_prop_stf;
}
/* Reallocate freed memory */
if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
pm_mesh_allocate(e->mesh);
#ifdef WITH_MPI
engine_allocate_foreign_particles(e, /*fof=*/0);
#endif
/* ... and find the next output time */
engine_compute_next_stf_time(e);
#else
error(
"Asking for a VELOCIraptor output but SWIFT was compiled without "
"the interface!");
#endif
break;
case output_los:
/* Compute the LoS */
do_line_of_sight(e);
/* Move on */
engine_compute_next_los_time(e);
break;
case output_ps:
/* Compute the PS */
calc_all_power_spectra(e->power_data, e->s, &e->threadpool, e->verbose);
/* Move on */
engine_compute_next_ps_time(e);
break;
default:
error("Invalid dump type");
}
/* We need to see whether whether we are in the pathological case
* where there can be another dump before the next step. */
type = output_none;
ti_output = max_nr_timesteps;
/* Save some statistics ? */
if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) {
if (e->ti_next_stats < ti_output) {
ti_output = e->ti_next_stats;
type = output_statistics;
}
}
/* Do we want a snapshot? */
if (e->ti_end_min > e->ti_next_snapshot && e->ti_next_snapshot > 0) {
if (e->ti_next_snapshot < ti_output) {
ti_output = e->ti_next_snapshot;
type = output_snapshot;
}
}
/* Do we want a power-spectrum? */
if (e->ti_end_min > e->ti_next_ps && e->ti_next_ps > 0) {
if (e->ti_next_ps < ti_output) {
ti_output = e->ti_next_ps;
type = output_ps;
}
}
/* Do we want to perform structure finding? */
if (with_stf) {
if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) {
if (e->ti_next_stf < ti_output) {
ti_output = e->ti_next_stf;
type = output_stf;
}
}
}
/* Do line of sight ? */
if (with_los) {
if (e->ti_end_min > e->ti_next_los && e->ti_next_los > 0) {
if (e->ti_next_los < ti_output) {
ti_output = e->ti_next_los;
type = output_los;
}
}
}
} /* While loop over output types */
/* Restore the information we stored */
e->ti_current = ti_current;
if (e->policy & engine_policy_cosmology)
cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
e->max_active_bin = max_active_bin;
e->time = time;
}
/**
* @brief Set the value of the recording trigger windows based
* on the user's desires and the time to the next snapshot.
*
* @param e The #engine.
*/
void engine_set_and_verify_snapshot_triggers(struct engine *e) {
integertime_t ti_next_snap = e->ti_next_snapshot;
if (ti_next_snap == -1) ti_next_snap = max_nr_timesteps;
/* Time until the next snapshot */
double time_to_next_snap;
if (e->policy & engine_policy_cosmology) {
time_to_next_snap =
cosmology_get_delta_time(e->cosmology, e->ti_current, ti_next_snap);
} else {
time_to_next_snap = (ti_next_snap - e->ti_current) * e->time_base;
}
/* Do we need to reduce any of the recording trigger times?
* Or can we set them with the user's desired range? */
for (int k = 0; k < num_snapshot_triggers_part; ++k) {
if (e->snapshot_recording_triggers_desired_part[k] > 0) {
if (e->snapshot_recording_triggers_desired_part[k] > time_to_next_snap) {
e->snapshot_recording_triggers_part[k] = time_to_next_snap;
} else {
e->snapshot_recording_triggers_part[k] =
e->snapshot_recording_triggers_desired_part[k];
}
}
}
for (int k = 0; k < num_snapshot_triggers_spart; ++k) {
if (e->snapshot_recording_triggers_desired_spart[k] > 0) {
if (e->snapshot_recording_triggers_desired_spart[k] > time_to_next_snap) {
e->snapshot_recording_triggers_spart[k] = time_to_next_snap;
} else {
e->snapshot_recording_triggers_spart[k] =
e->snapshot_recording_triggers_desired_spart[k];
}
}
}
for (int k = 0; k < num_snapshot_triggers_bpart; ++k) {
if (e->snapshot_recording_triggers_desired_bpart[k] > 0) {
if (e->snapshot_recording_triggers_desired_bpart[k] > time_to_next_snap) {
e->snapshot_recording_triggers_bpart[k] = time_to_next_snap;
} else {
e->snapshot_recording_triggers_bpart[k] =
e->snapshot_recording_triggers_desired_bpart[k];
}
}
}
}
/**
* @brief Computes the next time (on the time line) for a dump
*
* @param e The #engine.
* @param restart Are we calling this upon a restart event?
*/
void engine_compute_next_snapshot_time(struct engine *e, const int restart) {
/* Do output_list file case */
if (e->output_list_snapshots) {
output_list_read_next_time(e->output_list_snapshots, e, "snapshots",
&e->ti_next_snapshot);
/* Unless we are restarting, check the allowed recording trigger time */
if (!restart) engine_set_and_verify_snapshot_triggers(e);
/* All done in the list case */
return;
}
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_snapshot;
else
time_end = e->time_end + e->delta_time_snapshot;
/* Find next snasphot above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_snapshot;
else
time = e->time_first_snapshot;
int found_snapshot_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_snapshot = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_snapshot = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_snapshot > e->ti_current) {
found_snapshot_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_snapshot;
else
time += e->delta_time_snapshot;
}
/* Deal with last snapshot */
if (!found_snapshot_time) {
e->ti_next_snapshot = -1;
if (e->verbose) message("No further output time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const double next_snapshot_time =
exp(e->ti_next_snapshot * e->time_base) * e->cosmology->a_begin;
if (e->verbose)
message("Next snapshot time set to a=%e.", next_snapshot_time);
} else {
const double next_snapshot_time =
e->ti_next_snapshot * e->time_base + e->time_begin;
if (e->verbose)
message("Next snapshot time set to t=%e.", next_snapshot_time);
}
/* Unless we are restarting, set the recording triggers accordingly for the
* next output */
if (!restart) engine_set_and_verify_snapshot_triggers(e);
}
}
/**
* @brief Computes the next time (on the time line) for a statistics dump
*
* @param e The #engine.
*/
void engine_compute_next_statistics_time(struct engine *e) {
/* Do output_list file case */
if (e->output_list_stats) {
output_list_read_next_time(e->output_list_stats, e, "stats",
&e->ti_next_stats);
return;
}
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_statistics;
else
time_end = e->time_end + e->delta_time_statistics;
/* Find next snasphot above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_statistics;
else
time = e->time_first_statistics;
int found_stats_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_stats = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_stats = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_stats > e->ti_current) {
found_stats_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_statistics;
else
time += e->delta_time_statistics;
}
/* Deal with last statistics */
if (!found_stats_time) {
e->ti_next_stats = -1;
if (e->verbose) message("No further output time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const double next_statistics_time =
exp(e->ti_next_stats * e->time_base) * e->cosmology->a_begin;
if (e->verbose)
message("Next output time for stats set to a=%e.",
next_statistics_time);
} else {
const double next_statistics_time =
e->ti_next_stats * e->time_base + e->time_begin;
if (e->verbose)
message("Next output time for stats set to t=%e.",
next_statistics_time);
}
}
}
/**
* @brief Computes the next time (on the time line) for a line of sight dump
*
* @param e The #engine.
*/
void engine_compute_next_los_time(struct engine *e) {
/* Do output_list file case */
if (e->output_list_los) {
output_list_read_next_time(e->output_list_los, e, "line of sights",
&e->ti_next_los);
return;
}
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_los;
else
time_end = e->time_end + e->delta_time_los;
/* Find next los above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_los;
else
time = e->time_first_los;
int found_los_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_los = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_los = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_los > e->ti_current) {
found_los_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_los;
else
time += e->delta_time_los;
}
/* Deal with last line of sight */
if (!found_los_time) {
e->ti_next_los = -1;
if (e->verbose) message("No further LOS output time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const double next_los_time =
exp(e->ti_next_los * e->time_base) * e->cosmology->a_begin;
if (e->verbose)
message("Next output time for line of sight set to a=%e.",
next_los_time);
} else {
const double next_los_time =
e->ti_next_los * e->time_base + e->time_begin;
if (e->verbose)
message("Next output time for line of sight set to t=%e.",
next_los_time);
}
}
}
/**
* @brief Computes the next time (on the time line) for structure finding
*
* @param e The #engine.
*/
void engine_compute_next_stf_time(struct engine *e) {
/* Do output_list file case */
if (e->output_list_stf) {
output_list_read_next_time(e->output_list_stf, e, "stf", &e->ti_next_stf);
return;
}
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_stf;
else
time_end = e->time_end + e->delta_time_stf;
/* Find next snasphot above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_stf_output;
else
time = e->time_first_stf_output;
int found_stf_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_stf = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_stf = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_stf > e->ti_current) {
found_stf_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_stf;
else
time += e->delta_time_stf;
}
/* Deal with last snapshot */
if (!found_stf_time) {
e->ti_next_stf = -1;
if (e->verbose) message("No further output time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const float next_stf_time =
exp(e->ti_next_stf * e->time_base) * e->cosmology->a_begin;
if (e->verbose)
message("Next VELOCIraptor time set to a=%e.", next_stf_time);
} else {
const float next_stf_time = e->ti_next_stf * e->time_base + e->time_begin;
if (e->verbose)
message("Next VELOCIraptor time set to t=%e.", next_stf_time);
}
}
}
/**
* @brief Computes the next time (on the time line) for FoF black holes seeding
*
* @param e The #engine.
*/
void engine_compute_next_fof_time(struct engine *e) {
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_fof;
else
time_end = e->time_end + e->delta_time_fof;
/* Find next snasphot above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_fof_call;
else
time = e->time_first_fof_call;
int found_fof_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_fof = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_fof = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_fof > e->ti_current) {
found_fof_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_fof;
else
time += e->delta_time_fof;
}
/* Deal with last snapshot */
if (!found_fof_time) {
e->ti_next_fof = -1;
if (e->verbose) message("No further FoF time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const float next_fof_time =
exp(e->ti_next_fof * e->time_base) * e->cosmology->a_begin;
if (e->verbose) message("Next FoF time set to a=%e.", next_fof_time);
} else {
const float next_fof_time = e->ti_next_fof * e->time_base + e->time_begin;
if (e->verbose) message("Next FoF time set to t=%e.", next_fof_time);
}
}
}
/**
* @brief Computes the next time (on the time line) for a power-spectrum dump
*
* @param e The #engine.
*/
void engine_compute_next_ps_time(struct engine *e) {
/* Do output_list file case */
if (e->output_list_ps) {
output_list_read_next_time(e->output_list_ps, e, "power spectrum",
&e->ti_next_ps);
return;
}
/* Find upper-bound on last output */
double time_end;
if (e->policy & engine_policy_cosmology)
time_end = e->cosmology->a_end * e->delta_time_ps;
else
time_end = e->time_end + e->delta_time_ps;
/* Find next ps above current time */
double time;
if (e->policy & engine_policy_cosmology)
time = e->a_first_ps_output;
else
time = e->time_first_ps_output;
int found_ps_time = 0;
while (time < time_end) {
/* Output time on the integer timeline */
if (e->policy & engine_policy_cosmology)
e->ti_next_ps = log(time / e->cosmology->a_begin) / e->time_base;
else
e->ti_next_ps = (time - e->time_begin) / e->time_base;
/* Found it? */
if (e->ti_next_ps > e->ti_current) {
found_ps_time = 1;
break;
}
if (e->policy & engine_policy_cosmology)
time *= e->delta_time_ps;
else
time += e->delta_time_ps;
}
/* Deal with last line of sight */
if (!found_ps_time) {
e->ti_next_ps = -1;
if (e->verbose) message("No further PS output time.");
} else {
/* Be nice, talk... */
if (e->policy & engine_policy_cosmology) {
const double next_ps_time =
exp(e->ti_next_ps * e->time_base) * e->cosmology->a_begin;
if (e->verbose)
message("Next output time for power spectrum set to a=%e.",
next_ps_time);
} else {
const double next_ps_time = e->ti_next_ps * e->time_base + e->time_begin;
if (e->verbose)
message("Next output time for power spectrum set to t=%e.",
next_ps_time);
}
}
}
/**
* @brief Initialize all the output_list required by the engine
*
* @param e The #engine.
* @param params The #swift_params.
*/
void engine_init_output_lists(struct engine *e, struct swift_params *params,
const struct output_options *output_options) {
/* Deal with snapshots */
e->output_list_snapshots = NULL;
output_list_init(&e->output_list_snapshots, e, "Snapshots",
&e->delta_time_snapshot);
if (e->output_list_snapshots) {
/* If we are using a different output selection for the
* various entries, verify that the user did not specify
* invalid selections. */
if (e->output_list_snapshots->select_output_on)
output_list_check_selection(e->output_list_snapshots, output_options);
engine_compute_next_snapshot_time(e, /*restart=*/0);
if (e->policy & engine_policy_cosmology)
e->a_first_snapshot =
exp(e->ti_next_snapshot * e->time_base) * e->cosmology->a_begin;
else
e->time_first_snapshot =
e->ti_next_snapshot * e->time_base + e->time_begin;
}
/* Deal with stats */
e->output_list_stats = NULL;
output_list_init(&e->output_list_stats, e, "Statistics",
&e->delta_time_statistics);
if (e->output_list_stats) {
engine_compute_next_statistics_time(e);
if (e->policy & engine_policy_cosmology)
e->a_first_statistics =
exp(e->ti_next_stats * e->time_base) * e->cosmology->a_begin;
else
e->time_first_statistics =
e->ti_next_stats * e->time_base + e->time_begin;
}
/* Deal with stf */
if (e->policy & engine_policy_structure_finding) {
e->output_list_stf = NULL;
output_list_init(&e->output_list_stf, e, "StructureFinding",
&e->delta_time_stf);
if (e->output_list_stf) {
engine_compute_next_stf_time(e);
if (e->policy & engine_policy_cosmology)
e->a_first_stf_output =
exp(e->ti_next_stf * e->time_base) * e->cosmology->a_begin;
else
e->time_first_stf_output =
e->ti_next_stf * e->time_base + e->time_begin;
}
}
/* Deal with line of sight */
if (e->policy & engine_policy_line_of_sight) {
e->output_list_los = NULL;
output_list_init(&e->output_list_los, e, "LineOfSight", &e->delta_time_los);
if (e->output_list_los) {
engine_compute_next_los_time(e);
if (e->policy & engine_policy_cosmology)
e->a_first_los =
exp(e->ti_next_los * e->time_base) * e->cosmology->a_begin;
else
e->time_first_los = e->ti_next_los * e->time_base + e->time_begin;
}
}
/* Deal with power-spectra */
if (e->policy & engine_policy_power_spectra) {
e->output_list_ps = NULL;
output_list_init(&e->output_list_ps, e, "PowerSpectrum", &e->delta_time_ps);
if (e->output_list_ps) {
engine_compute_next_ps_time(e);
if (e->policy & engine_policy_cosmology)
e->a_first_ps_output =
exp(e->ti_next_ps * e->time_base) * e->cosmology->a_begin;
else
e->time_first_ps_output = e->ti_next_ps * e->time_base + e->time_begin;
}
}
}
/**
* @brief Checks whether we passed a certain delta time before the next
* snapshot and need to trigger a recording.
*
* If a recording has to start, we also loop over the particles to correct
* for the time between the particles' end of time-step and the actual start
* of trigger.
*
* @param e The #engine.
*/
void engine_io_check_snapshot_triggers(struct engine *e) {
struct space *s = e->s;
const int with_cosmology = (e->policy & engine_policy_cosmology);
/* Time until the next snapshot */
integertime_t ti_next_snap = e->ti_next_snapshot;
if (ti_next_snap == -1) ti_next_snap = max_nr_timesteps;
double time_to_next_snap;
if (e->policy & engine_policy_cosmology) {
time_to_next_snap =
cosmology_get_delta_time(e->cosmology, e->ti_current, ti_next_snap);
} else {
time_to_next_snap = (ti_next_snap - e->ti_current) * e->time_base;
}
/* Should any not yet switched on trigger be activated? (part version) */
for (int i = 0; i < num_snapshot_triggers_part; ++i) {
if (time_to_next_snap <= e->snapshot_recording_triggers_part[i] &&
e->snapshot_recording_triggers_part[i] > 0. &&
!e->snapshot_recording_triggers_started_part[i]) {
e->snapshot_recording_triggers_started_part[i] = 1;
/* Be vocal about this */
if (e->verbose)
message(
"Snapshot will be dumped in %e U_t. Recording trigger for part "
"activated.",
e->snapshot_recording_triggers_part[i]);
/* We now need to loop over the particles to preemptively deduct the
* extra time logged between the particles' start of step and the
* actual start of the trigger */
for (size_t k = 0; k < s->nr_parts; ++k) {
/* Get a handle on the part. */
struct part *p = &s->parts[k];
struct xpart *xp = &s->xparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, p->time_bin);
/* Escape inhibited particles */
if (part_is_inhibited(p, e)) continue;
/* Time from the start of the particle's step to the snapshot */
double total_time;
if (with_cosmology) {
total_time =
cosmology_get_delta_time(e->cosmology, ti_begin, ti_next_snap);
} else {
total_time = (ti_next_snap - ti_begin) * e->time_base;
}
/* Time to deduct = time since the start of the step - trigger time */
const double time_to_remove =
total_time - e->snapshot_recording_triggers_part[i];
#ifdef SWIFT_DEBUG_CHECKS
if (time_to_remove < 0.)
error("Invalid time to deduct! %e", time_to_remove);
#endif
/* Note that we need to use a separate array (not the raw
* e->snapshot_recording_triggers_part) as we only want to
* update one entry */
int my_temp_array[num_snapshot_triggers_part];
memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_part);
my_temp_array[i] = 1;
tracers_after_timestep_part(
p, xp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, e->hydro_properties, e->cooling_func, e->time,
-time_to_remove, my_temp_array);
}
}
}
/* Should any not yet switched on trigger be activated? (spart version) */
for (int i = 0; i < num_snapshot_triggers_spart; ++i) {
if (time_to_next_snap <= e->snapshot_recording_triggers_spart[i] &&
e->snapshot_recording_triggers_spart[i] > 0. &&
!e->snapshot_recording_triggers_started_spart[i]) {
e->snapshot_recording_triggers_started_spart[i] = 1;
/* Be vocal about this */
if (e->verbose)
message(
"Snapshot will be dumped in %e U_t. Recording trigger for spart "
"activated.",
e->snapshot_recording_triggers_spart[i]);
/* We now need to loop over the particles to preemptively deduct the
* extra time logged between the particles' start of step and the
* actual start of the trigger */
for (size_t k = 0; k < s->nr_sparts; ++k) {
/* Get a handle on the spart. */
struct spart *sp = &s->sparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, sp->time_bin);
/* Escape inhibited particles */
if (spart_is_inhibited(sp, e)) continue;
/* Time from the start of the particle's step to the snapshot */
double total_time;
if (with_cosmology) {
total_time =
cosmology_get_delta_time(e->cosmology, ti_begin, ti_next_snap);
} else {
total_time = (ti_next_snap - ti_begin) * e->time_base;
}
/* Time to deduct = time since the start of the step - trigger time */
const double time_to_remove =
total_time - e->snapshot_recording_triggers_part[i];
#ifdef SWIFT_DEBUG_CHECKS
if (time_to_remove < 0.)
error("Invalid time to deduct! %e", time_to_remove);
#endif
/* Note that we need to use a separate array (not the raw
* e->snapshot_recording_triggers_part) as we only want to
* update one entry */
int my_temp_array[num_snapshot_triggers_spart];
memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_spart);
my_temp_array[i] = 1;
tracers_after_timestep_spart(
sp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, -time_to_remove, my_temp_array);
}
}
}
/* Should any not yet switched on trigger be activated? (bpart version) */
for (int i = 0; i < num_snapshot_triggers_bpart; ++i) {
if (time_to_next_snap <= e->snapshot_recording_triggers_bpart[i] &&
e->snapshot_recording_triggers_bpart[i] > 0. &&
!e->snapshot_recording_triggers_started_bpart[i]) {
e->snapshot_recording_triggers_started_bpart[i] = 1;
/* Be vocal about this */
if (e->verbose)
message(
"Snapshot will be dumped in %e U_t. Recording trigger for bpart "
"activated.",
e->snapshot_recording_triggers_bpart[i]);
/* We now need to loop over the particles to preemptively deduct the
* extra time logged between the particles' start of step and the
* actual start of the trigger */
for (size_t k = 0; k < s->nr_bparts; ++k) {
/* Get a handle on the bpart. */
struct bpart *bp = &s->bparts[k];
const integertime_t ti_begin =
get_integer_time_begin(e->ti_current, bp->time_bin);
/* Escape inhibited particles */
if (bpart_is_inhibited(bp, e)) continue;
/* Time from the start of the particle's step to the snapshot */
double total_time;
if (with_cosmology) {
total_time =
cosmology_get_delta_time(e->cosmology, ti_begin, ti_next_snap);
} else {
total_time = (ti_next_snap - ti_begin) * e->time_base;
}
/* Time to deduct = time since the start of the step - trigger time */
const double time_to_remove =
total_time - e->snapshot_recording_triggers_part[i];
#ifdef SWIFT_DEBUG_CHECKS
if (time_to_remove < 0.)
error("Invalid time to deduct! %e", time_to_remove);
#endif
/* Note that we need to use a separate array (not the raw
* e->snapshot_recording_triggers_part) as we only want to
* update one entry */
int my_temp_array[num_snapshot_triggers_bpart];
memset(my_temp_array, 0, sizeof(int) * num_snapshot_triggers_bpart);
my_temp_array[i] = 1;
tracers_after_timestep_bpart(
bp, e->internal_units, e->physical_constants, with_cosmology,
e->cosmology, -time_to_remove, my_temp_array);
}
}
}
}