/******************************************************************************* * 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 /* This object's header. */ #include "runner.h" /* Local headers. */ #include "active.h" #include "black_holes.h" #include "cell.h" #include "engine.h" #include "feedback.h" #include "kick.h" #include "multipole.h" #include "timers.h" #include "timestep.h" #include "timestep_limiter.h" #include "timestep_sync.h" #include "tracers.h" /** * @brief Initialize the multipoles before the gravity calculation. * * @param r The runner thread. * @param c The cell. * @param timer 1 if the time is to be recorded. */ void runner_do_init_grav(struct runner *r, struct cell *c, const int timer) { const struct engine *e = r->e; TIMER_TIC; #ifdef SWIFT_DEBUG_CHECKS if (!(e->policy & engine_policy_self_gravity)) error("Grav-init task called outside of self-gravity calculation"); #endif /* Anything to do here? */ if (!cell_is_active_gravity(c, e)) return; /* Does the multipole need drifting? */ if (c->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(c, e); /* Reset the gravity acceleration tensors */ gravity_field_tensors_init(&c->grav.multipole->pot, e->ti_current); /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) runner_do_init_grav(r, c->progeny[k], /*timer=*/0); } } if (timer) TIMER_TOC(timer_init_grav); } /** * @brief Perform the first half-kick on all the active particles in a cell. * * @param r The runner thread. * @param c The cell. * @param timer Are we timing this ? */ void runner_do_kick1(struct runner *r, struct cell *c, const int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; const struct hydro_props *hydro_props = e->hydro_properties; const struct entropy_floor_properties *entropy_floor = e->entropy_floor; const int periodic = e->s->periodic; const int with_cosmology = (e->policy & engine_policy_cosmology); struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; struct gpart *restrict gparts = c->grav.parts; struct spart *restrict sparts = c->stars.parts; struct sink *restrict sinks = c->sinks.parts; struct bpart *restrict bparts = c->black_holes.parts; const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; const int sink_count = c->sinks.count; const int bcount = c->black_holes.count; const integertime_t ti_current = e->ti_current; const double time_base = e->time_base; TIMER_TIC; /* Anything to do here? */ if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e) && !cell_is_starting_stars(c, e) && !cell_is_starting_sinks(c, e) && !cell_is_starting_black_holes(c, e)) return; /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) runner_do_kick1(r, c->progeny[k], /*timer=*/0); } else { integertime_t ti_begin_mesh = -1; integertime_t ti_end_mesh = 0; double dt_kick_mesh_grav = 0.; /* Are we at a step where we do mesh-gravity time-integration? */ if (periodic && e->mesh->ti_beg_mesh_next == e->ti_current) { const integertime_t ti_step = e->mesh->ti_end_mesh_next - e->mesh->ti_beg_mesh_next; ti_begin_mesh = e->mesh->ti_beg_mesh_next; ti_end_mesh = e->mesh->ti_beg_mesh_next + ti_step / 2; /* Time interval for this mesh gravity half-kick */ dt_kick_mesh_grav = kick_get_grav_kick_dt( ti_begin_mesh, ti_end_mesh, time_base, with_cosmology, cosmo); } /* Loop over the parts in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; /* If particle needs to be kicked */ if (part_is_starting(p, e)) { #ifdef SWIFT_DEBUG_CHECKS if (p->limiter_data.wakeup != time_bin_not_awake) error("Woken-up particle that has not been processed in kick1"); #endif const integertime_t ti_step = get_integer_timestep(p->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, p->time_bin); const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_begin != ti_current) error( "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " "ti_step=%lld time_bin=%d wakeup=%d ti_current=%lld", ti_end, ti_begin, ti_step, p->time_bin, p->limiter_data.wakeup, ti_current); #endif /* Time intervals for this half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_hydro = kick_get_hydro_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_therm = kick_get_therm_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_corr = kick_get_corr_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Do the kick */ kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_mesh_grav, dt_kick_therm, dt_kick_corr, cosmo, hydro_props, entropy_floor, ti_begin, ti_end, ti_begin_mesh, ti_end_mesh); /* Update the accelerations to be used in the drift for hydro */ if (p->gpart != NULL) { xp->a_grav[0] = p->gpart->a_grav[0] + p->gpart->a_grav_mesh[0]; xp->a_grav[1] = p->gpart->a_grav[1] + p->gpart->a_grav_mesh[1]; xp->a_grav[2] = p->gpart->a_grav[2] + p->gpart->a_grav_mesh[2]; } } } /* Loop over the gparts in this cell. */ for (int k = 0; k < gcount; k++) { /* Get a handle on the part. */ struct gpart *restrict gp = &gparts[k]; #ifdef SWIFT_DEBUG_CHECKS if (ti_begin_mesh != -1 && !gpart_is_starting(gp, e) && !gpart_is_inhibited(gp, e)) { error( "Particle on a time-step longer than the mesh synchronization " "step!"); } #endif /* If the g-particle has no counterpart and needs to be kicked */ if ((gp->type == swift_type_dark_matter || gp->type == swift_type_dark_matter_background || gp->type == swift_type_neutrino) && gpart_is_starting(gp, e)) { const integertime_t ti_step = get_integer_timestep(gp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, gp->time_bin); const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_end_check = get_integer_time_end(ti_current + 1, gp->time_bin); if (ti_begin != ti_current) error( "G-particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " "ti_step=%lld time_bin=%d ti_current=%lld", ti_end_check, ti_begin, ti_step, gp->time_bin, ti_current); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Do the kick */ kick_gpart(gp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); } } /* Loop over the stars particles in this cell. */ for (int k = 0; k < scount; k++) { /* Get a handle on the s-part. */ struct spart *restrict sp = &sparts[k]; /* If particle needs to be kicked */ if (spart_is_starting(sp, e)) { const integertime_t ti_step = get_integer_timestep(sp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, sp->time_bin); const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_end_check = get_integer_time_end(ti_current + 1, sp->time_bin); if (ti_begin != ti_current) error( "S-particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " "ti_step=%lld time_bin=%d ti_current=%lld", ti_end_check, ti_begin, ti_step, sp->time_bin, ti_current); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Do the kick */ kick_spart(sp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); } } /* Loop over the sink particles in this cell. */ for (int k = 0; k < sink_count; k++) { /* Get a handle on the s-part. */ struct sink *restrict sink = &sinks[k]; /* If particle needs to be kicked */ if (sink_is_starting(sink, e)) { const integertime_t ti_step = get_integer_timestep(sink->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, sink->time_bin); const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_end_check = get_integer_time_end(ti_current + 1, sink->time_bin); if (ti_begin != ti_current) error( "Sink-particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " "ti_step=%lld time_bin=%d ti_current=%lld", ti_end_check, ti_begin, ti_step, sink->time_bin, ti_current); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Do the kick */ kick_sink(sink, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); } } /* Loop over the black hole particles in this cell. */ for (int k = 0; k < bcount; k++) { /* Get a handle on the s-part. */ struct bpart *restrict bp = &bparts[k]; /* If particle needs to be kicked */ if (bpart_is_starting(bp, e)) { const integertime_t ti_step = get_integer_timestep(bp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current + 1, bp->time_bin); const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS const integertime_t ti_end_check = get_integer_time_end(ti_current + 1, bp->time_bin); if (ti_begin != ti_current) error( "B-particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, " "ti_step=%lld time_bin=%d ti_current=%lld", ti_end_check, ti_begin, ti_step, bp->time_bin, ti_current); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Do the kick */ kick_bpart(bp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); } } } if (timer) TIMER_TOC(timer_kick1); } /** * @brief Perform the second half-kick on all the active particles in a cell. * * Also prepares particles to be drifted. * * @param r The runner thread. * @param c The cell. * @param timer Are we timing this ? */ void runner_do_kick2(struct runner *r, struct cell *c, const int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; const struct hydro_props *hydro_props = e->hydro_properties; const struct pressure_floor_props *pressure_floor = e->pressure_floor_props; const struct entropy_floor_properties *entropy_floor = e->entropy_floor; const int with_cosmology = (e->policy & engine_policy_cosmology); const int periodic = e->s->periodic; const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; const int sink_count = c->sinks.count; const int bcount = c->black_holes.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; struct gpart *restrict gparts = c->grav.parts; struct spart *restrict sparts = c->stars.parts; struct sink *restrict sinks = c->sinks.parts; struct bpart *restrict bparts = c->black_holes.parts; const integertime_t ti_current = e->ti_current; const double time_base = e->time_base; TIMER_TIC; /* Anything to do here? */ if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && !cell_is_active_stars(c, e) && !cell_is_active_sinks(c, e) && !cell_is_active_black_holes(c, e)) return; /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) runner_do_kick2(r, c->progeny[k], /*timer=*/0); } else { integertime_t ti_begin_mesh = -1; integertime_t ti_end_mesh = 0; double dt_kick_mesh_grav = 0.; /* Are we at a step where we do mesh-gravity time-integration? */ if (periodic && e->mesh->ti_end_mesh_last == e->ti_current) { const integertime_t ti_step = e->mesh->ti_end_mesh_last - e->mesh->ti_beg_mesh_last; ti_begin_mesh = e->mesh->ti_beg_mesh_last + ti_step / 2; ti_end_mesh = e->mesh->ti_end_mesh_last; /* Time interval for this mesh gravity half-kick */ dt_kick_mesh_grav = kick_get_grav_kick_dt( ti_begin_mesh, ti_end_mesh, time_base, with_cosmology, cosmo); } /* Loop over the particles in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; /* If particle needs to be kicked */ if (part_is_active(p, e)) { #ifdef SWIFT_DEBUG_CHECKS if (p->limiter_data.wakeup != time_bin_not_awake) error("Woken-up particle that has not been processed in kick1"); #endif /* Time-step length on the integer timeline */ const integertime_t ti_step = get_integer_timestep(p->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current, p->time_bin) + ti_step / 2; const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_end != ti_current) error( "Particle in wrong time-bin, ti_begin=%lld, ti_step=%lld " "time_bin=%d wakeup=%d ti_current=%lld", ti_begin, ti_step, p->time_bin, p->limiter_data.wakeup, ti_current); #endif /* Time intervals for this half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_hydro = kick_get_hydro_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_therm = kick_get_therm_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); const double dt_kick_corr = kick_get_corr_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Finish the time-step with a second half-kick */ kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_mesh_grav, dt_kick_therm, dt_kick_corr, cosmo, hydro_props, entropy_floor, ti_begin, ti_end, ti_begin_mesh, ti_end_mesh); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ if (p->ti_drift != p->ti_kick) error("Error integrating part in time."); #endif /* Prepare the values to be drifted */ hydro_reset_predicted_values(p, xp, cosmo, pressure_floor); } } /* Loop over the g-particles in this cell. */ for (int k = 0; k < gcount; k++) { /* Get a handle on the part. */ struct gpart *restrict gp = &gparts[k]; #ifdef SWIFT_DEBUG_CHECKS if (ti_begin_mesh != -1 && !gpart_is_active(gp, e)) { error( "Particle on a time-step longer than the mesh synchronization " "step!"); } #endif /* If the g-particle has no counterpart and needs to be kicked */ if ((gp->type == swift_type_dark_matter || gp->type == swift_type_dark_matter_background || gp->type == swift_type_neutrino) && gpart_is_active(gp, e)) { const integertime_t ti_step = get_integer_timestep(gp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current, gp->time_bin) + ti_step / 2; const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_end != ti_current) error("Particle in wrong time-bin"); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Finish the time-step with a second half-kick */ kick_gpart(gp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ if (gp->ti_drift != gp->ti_kick) error("Error integrating g-part in time."); #endif /* Prepare the values to be drifted */ gravity_reset_predicted_values(gp); } } /* Loop over the particles in this cell. */ for (int k = 0; k < scount; k++) { /* Get a handle on the part. */ struct spart *restrict sp = &sparts[k]; /* If particle needs to be kicked */ if (spart_is_active(sp, e)) { const integertime_t ti_step = get_integer_timestep(sp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current, sp->time_bin) + ti_step / 2; const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_end != ti_current) error("Particle in wrong time-bin"); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Finish the time-step with a second half-kick */ kick_spart(sp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ if (sp->ti_drift != sp->ti_kick) error("Error integrating s-part in time."); #endif /* Prepare the values to be drifted */ stars_reset_predicted_values(sp); } } /* Loop over the sink particles in this cell. */ for (int k = 0; k < sink_count; k++) { /* Get a handle on the part. */ struct sink *restrict sink = &sinks[k]; /* If particle needs to be kicked */ if (sink_is_active(sink, e)) { const integertime_t ti_step = get_integer_timestep(sink->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current, sink->time_bin) + ti_step / 2; const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_end != ti_current) error("Particle in wrong time-bin"); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Finish the time-step with a second half-kick */ kick_sink(sink, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ if (sink->ti_drift != sink->ti_kick) error("Error integrating sink-part in time."); #endif /* Prepare the values to be drifted */ sink_reset_predicted_values(sink); } } /* Loop over the particles in this cell. */ for (int k = 0; k < bcount; k++) { /* Get a handle on the part. */ struct bpart *restrict bp = &bparts[k]; /* If particle needs to be kicked */ if (bpart_is_active(bp, e)) { const integertime_t ti_step = get_integer_timestep(bp->time_bin); const integertime_t ti_begin = get_integer_time_begin(ti_current, bp->time_bin) + ti_step / 2; const integertime_t ti_end = ti_begin + ti_step / 2; #ifdef SWIFT_DEBUG_CHECKS if (ti_end != ti_current) error("Particle in wrong time-bin"); #endif /* Time interval for this gravity half-kick */ const double dt_kick_grav = kick_get_grav_kick_dt( ti_begin, ti_end, time_base, with_cosmology, cosmo); /* Finish the time-step with a second half-kick */ kick_bpart(bp, dt_kick_grav, ti_begin, ti_end, dt_kick_mesh_grav, ti_begin_mesh, ti_end_mesh); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ if (bp->ti_drift != bp->ti_kick) error("Error integrating b-part in time."); #endif /* Prepare the values to be drifted */ black_holes_reset_predicted_values(bp); } } } if (timer) TIMER_TOC(timer_kick2); } /** * @brief Computes the next time-step of all active particles in this cell * and update the cell's statistics. * * @param r The runner thread. * @param c The cell. * @param timer Are we timing this ? */ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) { const struct engine *e = r->e; const integertime_t ti_current = e->ti_current; const integertime_t ti_current_subcycle = e->ti_current_subcycle; const struct cosmology *cosmo = e->cosmology; const struct feedback_props *feedback_props = e->feedback_props; const struct unit_system *us = e->internal_units; const struct phys_const *phys_const = e->physical_constants; const int with_cosmology = (e->policy & engine_policy_cosmology); const int with_feedback = (e->policy & engine_policy_feedback); const int with_rt = (e->policy & engine_policy_rt); const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; const int sink_count = c->sinks.count; const int bcount = c->black_holes.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; struct gpart *restrict gparts = c->grav.parts; struct spart *restrict sparts = c->stars.parts; struct sink *restrict sinks = c->sinks.parts; struct bpart *restrict bparts = c->black_holes.parts; TIMER_TIC; /* Anything to do here? */ if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && !cell_is_active_stars(c, e) && !cell_is_active_sinks(c, e) && !cell_is_active_black_holes(c, e)) { /* Note: cell_is_rt_active is deliberately skipped. We only change * the RT subcycling time steps when particles are hydro active. */ c->hydro.updated = 0; c->grav.updated = 0; c->stars.updated = 0; c->sinks.updated = 0; c->black_holes.updated = 0; c->rt.updated = 0; return; } int updated = 0, g_updated = 0, s_updated = 0, sink_updated = 0, b_updated = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; integertime_t ti_rt_min_step_size = max_nr_timesteps; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0; integertime_t ti_sinks_end_min = max_nr_timesteps, ti_sinks_beg_max = 0; integertime_t ti_black_holes_end_min = max_nr_timesteps, ti_black_holes_beg_max = 0; /* No children? */ if (!c->split) { /* Loop over the particles in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; /* If particle needs updating */ if (part_is_active(p, e)) { #ifdef SWIFT_DEBUG_CHECKS /* Current end of time-step */ const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); if (ti_end != ti_current) error("Computing time-step of rogue particle."); if (with_rt) { const integertime_t ti_rt_end = get_integer_time_end( ti_current_subcycle, p->rt_time_data.time_bin); if (ti_rt_end != ti_current_subcycle) error("Computing RT time-step of rogue particle"); } #endif /* Old time-step length in physical units */ const integertime_t ti_old_step = get_integer_timestep(p->time_bin); double old_time_step_length; if (with_cosmology) { old_time_step_length = cosmology_get_delta_time( e->cosmology, e->ti_current - ti_old_step, e->ti_current); } else { old_time_step_length = get_timestep(p->time_bin, e->time_base); } /* Get new time-step */ integertime_t ti_rt_new_step = get_part_rt_timestep(p, xp, e); const integertime_t ti_new_step = get_part_timestep(p, xp, e, ti_rt_new_step); /* Enforce RT time-step size <= hydro step size. */ ti_rt_new_step = min(ti_new_step, ti_rt_new_step); #ifdef SWIFT_RT_DEBUG_CHECKS /* For the DEBUG RT scheme, this sets the RT time step to be * (dt_hydro / max_nr_sub_cycles). For others, this does a proper * debugging/consistency check. */ rt_debugging_check_timestep(p, &ti_rt_new_step, &ti_new_step, e->max_nr_rt_subcycles, e->time_base); if (ti_rt_new_step <= 0LL) error("Got integer time step <= 0? %lld %lld", get_part_rt_timestep(p, xp, e), ti_new_step); #endif /* Update particle */ p->time_bin = get_time_bin(ti_new_step); if (p->gpart != NULL) p->gpart->time_bin = p->time_bin; /* Update the tracers properties */ tracers_after_timestep_part( p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time, old_time_step_length, e->snapshot_recording_triggers_started_part); /* Number of updated particles */ updated++; if (p->gpart != NULL) g_updated++; /* What is the next sync-point ? */ ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); /* What is the next starting point for this cell ? */ ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); if (p->gpart != NULL) { /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); } /* Same for RT */ if (with_rt) { p->rt_time_data.time_bin = get_time_bin(ti_rt_new_step); ti_rt_end_min = min(ti_current_subcycle + ti_rt_new_step, ti_rt_end_min); ti_rt_beg_max = max(ti_current_subcycle + ti_rt_new_step, ti_rt_beg_max); ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_new_step); } } else { /* part is inactive */ if (!part_is_inhibited(p, e)) { const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, p->time_bin); /* What is the next sync-point ? */ ti_hydro_end_min = min(ti_end, ti_hydro_end_min); /* What is the next starting point for this cell ? */ ti_hydro_beg_max = max(ti_beg, ti_hydro_beg_max); /* Same for RT. */ if (with_rt) { /* Here we assume that the particle is inactive, which is true for * hydro, but not necessarily for a RT subcycle. RT time steps are * only changed while the particle is hydro active. This allows to * end up with results ti_rt_end == ti_current_subcyle, so we need * to pretend we're past ti_current_subcycle already. */ integertime_t ti_rt_end = get_integer_time_end( ti_current_subcycle + 1, p->rt_time_data.time_bin); const integertime_t ti_rt_beg = get_integer_time_begin( ti_current_subcycle + 1, p->rt_time_data.time_bin); ti_rt_end_min = min(ti_rt_end, ti_rt_end_min); ti_rt_beg_max = max(ti_rt_beg, ti_rt_beg_max); integertime_t ti_rt_step = get_integer_timestep(p->rt_time_data.time_bin); ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_step); } if (p->gpart != NULL) { /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_end, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } } } /* Loop over the g-particles in this cell. */ for (int k = 0; k < gcount; k++) { /* Get a handle on the part. */ struct gpart *restrict gp = &gparts[k]; /* If the g-particle has no counterpart */ if (gp->type == swift_type_dark_matter || gp->type == swift_type_dark_matter_background || gp->type == swift_type_neutrino) { /* need to be updated ? */ if (gpart_is_active(gp, e)) { #ifdef SWIFT_DEBUG_CHECKS /* Current end of time-step */ const integertime_t ti_end = get_integer_time_end(ti_current, gp->time_bin); if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif /* Get new time-step */ const integertime_t ti_new_step = get_gpart_timestep(gp, e); /* Update particle */ gp->time_bin = get_time_bin(ti_new_step); /* Number of updated g-particles */ g_updated++; /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); } else { /* gpart is inactive */ if (!gpart_is_inhibited(gp, e)) { const integertime_t ti_end = get_integer_time_end(ti_current, gp->time_bin); /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_end, ti_gravity_end_min); const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, gp->time_bin); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } } } /* Loop over the star particles in this cell. */ for (int k = 0; k < scount; k++) { /* Get a handle on the part. */ struct spart *restrict sp = &sparts[k]; /* need to be updated ? */ if (spart_is_active(sp, e)) { #ifdef SWIFT_DEBUG_CHECKS /* Current end of time-step */ const integertime_t ti_end = get_integer_time_end(ti_current, sp->time_bin); if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif /* Old time-step length in physical units */ const integertime_t ti_old_step = get_integer_timestep(sp->time_bin); double old_time_step_length; if (with_cosmology) { old_time_step_length = cosmology_get_delta_time( e->cosmology, e->ti_current - ti_old_step, e->ti_current); } else { old_time_step_length = get_timestep(sp->time_bin, e->time_base); } /* Get new time-step */ const integertime_t ti_new_step = get_spart_timestep(sp, e); /* Update particle */ sp->time_bin = get_time_bin(ti_new_step); sp->gpart->time_bin = get_time_bin(ti_new_step); /* Update the tracers properties */ tracers_after_timestep_spart( sp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, old_time_step_length, e->snapshot_recording_triggers_started_spart); /* Update feedback related counters */ if (with_feedback) { feedback_will_do_feedback(sp, feedback_props, with_cosmology, cosmo, e->time, us, phys_const, e->ti_current, e->time_base); } /* Number of updated s-particles */ s_updated++; g_updated++; ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min); ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_stars_beg_max = max(ti_current, ti_stars_beg_max); ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); /* star particle is inactive but not inhibited */ } else { if (!spart_is_inhibited(sp, e)) { const integertime_t ti_end = get_integer_time_end(ti_current, sp->time_bin); const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, sp->time_bin); ti_stars_end_min = min(ti_end, ti_stars_end_min); ti_gravity_end_min = min(ti_end, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_stars_beg_max = max(ti_beg, ti_stars_beg_max); ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } } /* Loop over the sink particles in this cell. */ for (int k = 0; k < sink_count; k++) { /* Get a handle on the part. */ struct sink *restrict sink = &sinks[k]; /* need to be updated ? */ if (sink_is_active(sink, e)) { #ifdef SWIFT_DEBUG_CHECKS /* Current end of time-step */ const integertime_t ti_end = get_integer_time_end(ti_current, sink->time_bin); if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif /* Get new time-step */ const integertime_t ti_new_step = get_sink_timestep(sink, e); /* Update particle */ sink->time_bin = get_time_bin(ti_new_step); sink->gpart->time_bin = get_time_bin(ti_new_step); /* Number of updated sink-particles */ sink_updated++; g_updated++; ti_sinks_end_min = min(ti_current + ti_new_step, ti_sinks_end_min); ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_sinks_beg_max = max(ti_current, ti_sinks_beg_max); ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); /* sink particle is inactive but not inhibited */ } else { if (!sink_is_inhibited(sink, e)) { const integertime_t ti_end = get_integer_time_end(ti_current, sink->time_bin); const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, sink->time_bin); ti_sinks_end_min = min(ti_end, ti_sinks_end_min); ti_gravity_end_min = min(ti_end, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_sinks_beg_max = max(ti_beg, ti_sinks_beg_max); ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } } /* Loop over the black hole particles in this cell. */ for (int k = 0; k < bcount; k++) { /* Get a handle on the part. */ struct bpart *restrict bp = &bparts[k]; /* need to be updated ? */ if (bpart_is_active(bp, e)) { #ifdef SWIFT_DEBUG_CHECKS /* Current end of time-step */ const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); if (ti_end != ti_current) error("Computing time-step of rogue particle."); #endif /* Old time-step length in physical units */ const integertime_t ti_old_step = get_integer_timestep(bp->time_bin); double old_time_step_length; if (with_cosmology) { old_time_step_length = cosmology_get_delta_time( e->cosmology, e->ti_current - ti_old_step, e->ti_current); } else { old_time_step_length = get_timestep(bp->time_bin, e->time_base); } /* Get new time-step */ const integertime_t ti_new_step = get_bpart_timestep(bp, e); /* Update particle */ bp->time_bin = get_time_bin(ti_new_step); bp->gpart->time_bin = get_time_bin(ti_new_step); /* Update the tracers properties */ tracers_after_timestep_bpart( bp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, old_time_step_length, e->snapshot_recording_triggers_started_bpart); /* Number of updated s-particles */ b_updated++; g_updated++; ti_black_holes_end_min = min(ti_current + ti_new_step, ti_black_holes_end_min); ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_black_holes_beg_max = max(ti_current, ti_black_holes_beg_max); ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); /* star particle is inactive but not inhibited */ } else { if (!bpart_is_inhibited(bp, e)) { const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); const integertime_t ti_beg = get_integer_time_begin(ti_current + 1, bp->time_bin); ti_black_holes_end_min = min(ti_end, ti_black_holes_end_min); ti_gravity_end_min = min(ti_end, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_black_holes_beg_max = max(ti_beg, ti_black_holes_beg_max); ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } } } else { /* Loop over the progeny. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *restrict cp = c->progeny[k]; /* Recurse */ runner_do_timestep(r, cp, /*timer=*/0); /* And aggregate */ updated += cp->hydro.updated; g_updated += cp->grav.updated; sink_updated += cp->sinks.updated; s_updated += cp->stars.updated; b_updated += cp->black_holes.updated; ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); ti_rt_min_step_size = min(cp->rt.ti_rt_min_step_size, ti_rt_min_step_size); ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min); ti_stars_beg_max = max(cp->stars.ti_beg_max, ti_stars_beg_max); ti_sinks_end_min = min(cp->sinks.ti_end_min, ti_sinks_end_min); ti_sinks_beg_max = max(cp->sinks.ti_beg_max, ti_sinks_beg_max); ti_black_holes_end_min = min(cp->black_holes.ti_end_min, ti_black_holes_end_min); ti_black_holes_beg_max = max(cp->grav.ti_beg_max, ti_black_holes_beg_max); } } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the values. */ c->hydro.updated = updated; c->grav.updated = g_updated; c->stars.updated = s_updated; c->sinks.updated = sink_updated; c->black_holes.updated = b_updated; /* We don't count the RT updates here because the * timestep tasks aren't active during sub-cycles. * We do that in rt_advanced_cell_time instead. */ c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_beg_max = ti_hydro_beg_max; c->rt.ti_rt_end_min = ti_rt_end_min; c->rt.ti_rt_beg_max = ti_rt_beg_max; if (cell_is_starting_hydro(c, e)) { /* We only change the RT time steps when the cell is also hydro active. * Without this check here, ti_rt_min_step_size = max_nr_steps... */ c->rt.ti_rt_min_step_size = ti_rt_min_step_size; } c->grav.ti_end_min = ti_gravity_end_min; c->grav.ti_beg_max = ti_gravity_beg_max; c->stars.ti_end_min = ti_stars_end_min; c->stars.ti_beg_max = ti_stars_beg_max; c->sinks.ti_end_min = ti_sinks_end_min; c->sinks.ti_beg_max = ti_sinks_beg_max; c->black_holes.ti_end_min = ti_black_holes_end_min; c->black_holes.ti_beg_max = ti_black_holes_beg_max; #ifdef SWIFT_DEBUG_CHECKS if (c->hydro.ti_end_min == e->ti_current && c->hydro.ti_end_min < max_nr_timesteps) error("End of next hydro step is current time!"); if (c->grav.ti_end_min == e->ti_current && c->grav.ti_end_min < max_nr_timesteps) error("End of next gravity step is current time!"); if (c->stars.ti_end_min == e->ti_current && c->stars.ti_end_min < max_nr_timesteps) error("End of next stars step is current time!"); if (c->sinks.ti_end_min == e->ti_current && c->sinks.ti_end_min < max_nr_timesteps) error("End of next sinks step is current time!"); if (c->black_holes.ti_end_min == e->ti_current && c->black_holes.ti_end_min < max_nr_timesteps) error("End of next black holes step is current time!"); /* Contrary to sinks, stars, bhs etc, we may have "rt particles" * without running with RT. So additional if (with_rt) check is * needed here. */ if (with_rt && (c->rt.ti_rt_end_min == e->ti_current && c->rt.ti_rt_end_min < max_nr_timesteps)) error("Cell %lld End of next RT step is current time!", c->cellID); #endif if (timer) TIMER_TOC(timer_timestep); } /** * @brief Recursively collect the end-of-timestep information from the top-level * to the super level. * * @param r The runner thread. * @param c The cell. * @param timer Are we timing this ? */ void runner_do_timestep_collect(struct runner *r, struct cell *c, const int timer) { /* Early stop if we are at the super level. * The time-step task would have set things at this level already */ if (c->super == c) return; /* Counters for the different quantities. */ size_t h_updated = 0; size_t g_updated = 0; size_t s_updated = 0; size_t b_updated = 0; size_t si_updated = 0; size_t rt_updated = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0; integertime_t ti_black_holes_end_min = max_nr_timesteps, ti_black_holes_beg_max = 0; integertime_t ti_sinks_end_min = max_nr_timesteps, ti_sinks_beg_max = 0; /* Collect the values from the progeny. */ for (int k = 0; k < 8; k++) { struct cell *cp = c->progeny[k]; if (cp != NULL) { /* Recurse */ runner_do_timestep_collect(r, cp, 0); /* And update */ ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min); ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max); ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); ti_stars_beg_max = max(ti_stars_beg_max, cp->stars.ti_beg_max); ti_black_holes_end_min = min(ti_black_holes_end_min, cp->black_holes.ti_end_min); ti_black_holes_beg_max = max(ti_black_holes_beg_max, cp->black_holes.ti_beg_max); ti_sinks_end_min = min(ti_sinks_end_min, cp->sinks.ti_end_min); ti_sinks_beg_max = max(ti_sinks_beg_max, cp->sinks.ti_beg_max); h_updated += cp->hydro.updated; g_updated += cp->grav.updated; s_updated += cp->stars.updated; b_updated += cp->black_holes.updated; si_updated += cp->sinks.updated; rt_updated += cp->rt.updated; /* Collected, so clear for next time. */ cp->hydro.updated = 0; cp->grav.updated = 0; cp->stars.updated = 0; cp->black_holes.updated = 0; cp->sinks.updated = 0; cp->rt.updated = 0; } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the collected values in the cell. */ c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_beg_max = ti_hydro_beg_max; c->rt.ti_rt_end_min = ti_rt_end_min; c->rt.ti_rt_beg_max = ti_rt_beg_max; c->grav.ti_end_min = ti_grav_end_min; c->grav.ti_beg_max = ti_grav_beg_max; c->stars.ti_end_min = ti_stars_end_min; c->stars.ti_beg_max = ti_stars_beg_max; c->black_holes.ti_end_min = ti_black_holes_end_min; c->black_holes.ti_beg_max = ti_black_holes_beg_max; c->sinks.ti_end_min = ti_sinks_end_min; c->sinks.ti_beg_max = ti_sinks_beg_max; c->hydro.updated = h_updated; c->grav.updated = g_updated; c->stars.updated = s_updated; c->black_holes.updated = b_updated; c->sinks.updated = si_updated; c->rt.updated = rt_updated; } /** * @brief Apply the time-step limiter to all awaken particles in a cell * hierarchy. * * @param r The task #runner. * @param c The #cell. * @param force Limit the particles irrespective of the #cell flags. * @param timer Are we timing this ? */ void runner_do_limiter(struct runner *r, struct cell *c, int force, const int timer) { const struct engine *e = r->e; const int count = c->hydro.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; TIMER_TIC; #ifdef SWIFT_DEBUG_CHECKS /* Check that we only limit local cells. */ if (c->nodeID != engine_rank) error("Limiting dt of a foreign cell is nope."); #endif integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_beg_max = 0; /* Limit irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_hydro_limiter)); /* Early abort? */ if (c->hydro.count == 0) { /* Clear the limiter flags. */ cell_clear_flag( c, cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); return; } /* Loop over the progeny ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_limiter))) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *restrict cp = c->progeny[k]; /* Recurse */ runner_do_limiter(r, cp, force, /*timer=*/0); /* And aggregate */ ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the updated values */ c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); } else if (!c->split && force) { ti_hydro_end_min = c->hydro.ti_end_min; ti_hydro_beg_max = c->hydro.ti_beg_max; ti_gravity_end_min = c->grav.ti_end_min; ti_gravity_beg_max = c->grav.ti_beg_max; /* Loop over the gas particles in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; #ifdef SWIFT_HYDRO_DENSITY_CHECKS /* Finish the limiter loop by adding a (fake) self-contribution */ p->limiter_data.N_limiter++; const float h_inv_dim = pow_dimension(1. / p->h); /* 1/h^d */ p->limiter_data.n_limiter += kernel_root; p->limiter_data.n_limiter *= h_inv_dim; #endif /* Avoid inhibited particles */ if (part_is_inhibited(p, e)) continue; /* Bip, bip, bip... wake-up time */ if (p->limiter_data.wakeup != time_bin_not_awake) { if (!part_is_active(p, e) && p->limiter_data.to_be_synchronized) { warning( "Not limiting particle with id %lld because it needs to be " "synced.", p->id); continue; } // message("Limiting particle %lld in cell %lld", p->id, c->cellID); /* Apply the limiter and get the new end of time-step */ const integertime_t ti_end_new = timestep_limit_part(p, xp, e); const timebin_t new_bin = p->time_bin; const integertime_t ti_beg_new = ti_end_new - get_integer_timestep(new_bin); /* Mark this particle has not needing synchronization */ p->limiter_data.to_be_synchronized = 0; #ifdef SWIFT_HYDRO_DENSITY_CHECKS p->limited_part = 1; #endif /* What is the next sync-point ? */ ti_hydro_end_min = min(ti_end_new, ti_hydro_end_min); /* What is the next starting point for this cell ? */ ti_hydro_beg_max = max(ti_beg_new, ti_hydro_beg_max); /* Also limit the gpart counter-part */ if (p->gpart != NULL) { /* Register the time-bin */ p->gpart->time_bin = p->time_bin; /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_end_new, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_beg_new, ti_gravity_beg_max); } } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the updated values */ c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); } /* Clear the limiter flags. */ cell_clear_flag(c, cell_flag_do_hydro_limiter | cell_flag_do_hydro_sub_limiter); if (timer) TIMER_TOC(timer_do_limiter); } /** * @brief Apply the time-step synchronization proceduere to all flagged * particles in a cell hierarchy. * * @param r The task #runner. * @param c The #cell. * @param force Limit the particles irrespective of the #cell flags. * @param timer Are we timing this ? */ void runner_do_sync(struct runner *r, struct cell *c, int force, const int timer) { const struct engine *e = r->e; const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; const int with_cosmology = (e->policy & engine_policy_cosmology); const int count = c->hydro.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; TIMER_TIC; #ifdef SWIFT_DEBUG_CHECKS /* Check that we only sync local cells. */ if (c->nodeID != engine_rank) error("Syncing of a foreign cell is nope."); #endif integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_beg_max = 0; /* Limit irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_hydro_sync)); /* Early abort? */ if (c->hydro.count == 0) { /* Clear the sync flags. */ cell_clear_flag(c, cell_flag_do_hydro_sync | cell_flag_do_hydro_sub_sync); return; } /* Loop over the progeny ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_sync))) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *restrict cp = c->progeny[k]; /* Recurse */ runner_do_sync(r, cp, force, /*timer=*/0); /* And aggregate */ ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the updated values */ c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); } else if (!c->split && force) { ti_hydro_end_min = c->hydro.ti_end_min; ti_hydro_beg_max = c->hydro.ti_beg_max; ti_gravity_end_min = c->grav.ti_end_min; ti_gravity_beg_max = c->grav.ti_beg_max; /* Loop over the gas particles in this cell. */ for (int k = 0; k < count; k++) { /* Get a handle on the part. */ struct part *restrict p = &parts[k]; struct xpart *restrict xp = &xparts[k]; /* Avoid inhibited particles */ if (part_is_inhibited(p, e)) continue; /* If the particle is active no need to sync it */ if (part_is_active(p, e) && p->limiter_data.to_be_synchronized) { p->limiter_data.to_be_synchronized = 0; } if (p->limiter_data.to_be_synchronized) { /* Finish this particle's time-step */ timestep_process_sync_part(p, xp, e, cosmo); /* Note that at this moment the new RT time step is only used to * limit the hydro time step here. */ integertime_t ti_rt_new_step = get_part_rt_timestep(p, xp, e); /* Get new time-step */ integertime_t ti_new_step = get_part_timestep(p, xp, e, ti_rt_new_step); timebin_t new_time_bin = get_time_bin(ti_new_step); /* Enforce RT time-step size <= hydro step size. */ /* On the commented out line below: We should be doing this once we * correctly add RT to this part of the code. */ /* ti_rt_new_step = min(ti_new_step, ti_rt_new_step); */ /* Apply the limiter if necessary */ if (p->limiter_data.wakeup != time_bin_not_awake) { new_time_bin = min(new_time_bin, -p->limiter_data.wakeup + 2); p->limiter_data.wakeup = time_bin_not_awake; } /* Limit the time-bin to what is allowed in this step */ new_time_bin = min(new_time_bin, e->max_active_bin); ti_new_step = get_integer_timestep(new_time_bin); /* Time-step length in physical units */ // MATTHIEU: TODO: think about this one! double time_step_length; if (with_cosmology) { time_step_length = cosmology_get_delta_time( e->cosmology, e->ti_current, e->ti_current + ti_new_step); } else { time_step_length = get_timestep(new_time_bin, e->time_base); } /* Update particle */ p->time_bin = new_time_bin; if (p->gpart != NULL) p->gpart->time_bin = new_time_bin; /* Update the tracers properties */ tracers_after_timestep_part( p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time, 0 * time_step_length, e->snapshot_recording_triggers_started_part); #ifdef SWIFT_HYDRO_DENSITY_CHECKS p->limited_part = 1; #endif /* What is the next sync-point ? */ ti_hydro_end_min = min(ti_current + ti_new_step, ti_hydro_end_min); /* What is the next starting point for this cell ? */ ti_hydro_beg_max = max(ti_current, ti_hydro_beg_max); /* Also limit the gpart counter-part */ if (p->gpart != NULL) { /* Register the time-bin */ p->gpart->time_bin = p->time_bin; /* What is the next sync-point ? */ ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); /* What is the next starting point for this cell ? */ ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); } } } /* Flag something may have changed */ if (c->top == c) space_mark_cell_as_updated(r->e->s, c); /* Store the updated values */ c->hydro.ti_end_min = min(c->hydro.ti_end_min, ti_hydro_end_min); c->hydro.ti_beg_max = max(c->hydro.ti_beg_max, ti_hydro_beg_max); c->grav.ti_end_min = min(c->grav.ti_end_min, ti_gravity_end_min); c->grav.ti_beg_max = max(c->grav.ti_beg_max, ti_gravity_beg_max); } /* Clear the sync flags. */ cell_clear_flag(c, cell_flag_do_hydro_sync | cell_flag_do_hydro_sub_sync); if (timer) TIMER_TOC(timer_do_sync); } /** * @brief Update the cell's t_rt_end_min so that the sub-cycling can proceed * with correct cell times. (During sub-cycles, the regular timestep and * timestep_collect tasks do not run. This replaces the collection of cell * times of timestep tasks during sub-cycles. ) * * @param r The #runner thread. * @param c The #cell. * @param timer Are we timing this ? */ void runner_do_rt_advance_cell_time(struct runner *r, struct cell *c, int timer) { struct engine *e = r->e; const int count = c->hydro.count; /* Reset update count regardless whether cell is active or not */ c->rt.updated = 0; #ifdef SWIFT_RT_DEBUG_CHECKS if (c->super == c) c->rt.advanced_time = 1; #endif /* Anything to do here? */ if (count == 0) return; if (!cell_is_rt_active(c, e)) return; TIMER_TIC; int rt_updated = 0; if (c->split) { for (int k = 0; k < 8; k++) { struct cell *cp = c->progeny[k]; if (cp != NULL) { runner_do_rt_advance_cell_time(r, cp, 0); rt_updated += cp->rt.updated; } } } else { /* Do some debugging stuff on active particles before setting the cell time. * This test is not reliable on foreign cells. After a rebuild, we may end * up with an active foreign cell which was not updated in this step because * it had no active neighbouring cells, and its particle data may be random * junk. */ if (c->nodeID == engine_rank) { struct part *restrict parts = c->hydro.parts; /* Loop over the gas particles in this cell. */ for (int i = 0; i < count; i++) { /* Get a handle on the part. */ struct part *restrict p = &parts[i]; /* Skip inhibited parts */ if (part_is_inhibited(p, e)) continue; /* Skip inactive parts */ if (!part_is_rt_active(p, e)) continue; #ifdef SWIFT_RT_DEBUG_CHECKS /* Run checks. */ rt_debug_sequence_check(p, 5, __func__); /* Mark that the subcycling has happened */ rt_debugging_count_subcycle(p); #endif rt_updated++; } } } c->rt.updated = rt_updated; /* Note: c->rt.ti_rt_min_step_size may be greater than * c->super->rt.ti_rt_min_step_size. This is expected behaviour. * We only update the cell's own time after it's been active. */ c->rt.ti_rt_end_min += c->rt.ti_rt_min_step_size; if (timer) TIMER_TOC(timer_do_rt_advance_cell_time); } /** * @brief Recursively collect the end-of-timestep information from the top-level * to the super level for the RT sub-cycling. (During sub-cycles, the regular * timestep and timestep_collect tasks do not run. This replaces the * timestep_collect task.) * * @param r The runner thread. * @param c The cell. * @param timer Are we timing this ? */ void runner_do_collect_rt_times(struct runner *r, struct cell *c, const int timer) { TIMER_TIC; const struct engine *e = r->e; size_t rt_updated = 0; if (e->ti_current == e->ti_current_subcycle) error("called collect_rt_times during a main step"); /* Early stop if we are at the super level. * The time-step/rt_advance_cell_time tasks would have set things at * this level already. */ if (c->super == c) { #ifdef SWIFT_RT_DEBUG_CHECKS /* Do a check before the early exit. * rt_advanced_cell_time should be called exactly once before * collect times. Except on the first subcycle, because the * collect_rt_times task shouldn't be called in the main steps. * In that case, it should be exactly 2. * This is only valid if the cell has been active this step. * Otherwise, rt_advance_cell_time will not have run, yet the * rt_collect_cell_times call may still be executed if the top * level cell is above the super level cell. */ if (!cell_is_rt_active(c, e)) return; if (e->ti_current_subcycle - c->rt.ti_rt_end_min == e->ti_current) { /* This is the first subcycle */ if (c->rt.advanced_time != 2) error("Called cell with wrong advanced_time counter. Expect=2, got=%d", c->rt.advanced_time); } else { if (c->rt.advanced_time != 1) error("Called cell with wrong advanced_time counter. Expect=1, got=%d", c->rt.advanced_time); } c->rt.advanced_time = 0; #endif return; } integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0; /* Collect the values from the progeny. */ for (int k = 0; k < 8; k++) { struct cell *cp = c->progeny[k]; if (cp != NULL) { /* Recurse */ runner_do_collect_rt_times(r, cp, 0); /* And update */ ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min); ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max); /* Collected, so clear for next time. */ rt_updated += cp->rt.updated; cp->rt.updated = 0; } } /* Store the collected values in the cell. */ c->rt.ti_rt_end_min = ti_rt_end_min; c->rt.ti_rt_beg_max = ti_rt_beg_max; c->rt.updated = rt_updated; if (timer) TIMER_TOC(timer_do_rt_collect_times); }