/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 Matthieu Schaller (schaller@strw.leidenuniv.nl)
*
* 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 .
*
******************************************************************************/
#ifndef SWIFT_TIMESTEP_LIMITER_H
#define SWIFT_TIMESTEP_LIMITER_H
/* Config parameters. */
#include
/* Local headers. */
#include "kick.h"
/**
* @brief Prepare the limiter-quantities for a hydro force calculation.
*
* @param p The #part.
* @param xp The #xpart.
*/
__attribute__((always_inline)) INLINE static void
timestep_limiter_prepare_force(struct part *restrict p,
struct xpart *restrict xp) {
p->limiter_data.min_ngb_time_bin = num_time_bins + 1;
}
/**
* @brief Terminate the limiter-quantities after a hydro force calculation.
*
* @param p The #part.
*/
__attribute__((always_inline)) INLINE static void timestep_limiter_end_force(
struct part *restrict p) {
#ifdef SWIFT_DEBUG_CHECKS
if (p->limiter_data.min_ngb_time_bin == 0)
error("Minimal time-bin of neighbours is 0");
#endif
}
/**
* @brief Wakes up a particle by rewinding it's kick1 back in time and applying
* a new one such that the particle becomes active again in the next time-step.
*
* @param p The #part to update.
* @param xp Its #xpart companion.
* @param e The #engine (to extract time-line information).
*
* @return The updated integer end-of-step of the particle.
*/
__attribute__((always_inline)) INLINE static integertime_t timestep_limit_part(
struct part *restrict p, struct xpart *restrict xp,
const struct engine *e) {
const struct cosmology *cosmo = e->cosmology;
const int with_cosmology = e->policy & engine_policy_cosmology;
const double time_base = e->time_base;
const integertime_t ti_current = e->ti_current;
if (part_is_active(p, e)) {
/* First case, the particle was active so we only need to update the length
of its next time-step */
/* New time-bin of this particle */
p->time_bin = -p->limiter_data.wakeup + 2;
/* Mark the particle as being rady to be time integrated */
p->limiter_data.wakeup = time_bin_not_awake;
/* Return the new end-of-step for this particle */
return ti_current + get_integer_timestep(p->time_bin);
} else {
/* Second case, the particle was inactive so we need to interrupt its
time-step, undo the "kick" operator and assign a new time-step size */
/* The timebins to play with */
const timebin_t old_bin = p->time_bin;
const timebin_t new_bin = -p->limiter_data.wakeup + 2;
/* Current start and end time of this particle */
const integertime_t ti_beg_old =
get_integer_time_begin(ti_current, old_bin);
const integertime_t ti_end_old = get_integer_time_end(ti_current, old_bin);
/* Length of the old and new time-step */
const integertime_t dti_old = ti_end_old - ti_beg_old;
const integertime_t dti_new = get_integer_timestep(new_bin);
/* Let's now search for the starting point of the new step */
int k = 0;
while (ti_beg_old + k * dti_new <= ti_current) k++;
const integertime_t ti_beg_new = ti_beg_old + (k - 1) * dti_new;
/* End of the old half step */
const integertime_t ti_end_half_old = ti_beg_old + dti_old / 2;
/* End of the new half step */
const integertime_t ti_end_half_new = ti_beg_new + dti_new / 2;
#ifdef SWIFT_DEBUG_CHECKS
/* Some basic safety checks */
if (ti_beg_old >= e->ti_current)
error(
"Incorrect value for old time-step beginning ti_current=%lld, "
"ti_beg_old=%lld",
e->ti_current, ti_beg_old);
if (ti_end_old <= e->ti_current)
error(
"Incorrect value for old time-step end ti_current=%lld, "
"ti_end_old=%lld",
e->ti_current, ti_end_old);
if (ti_beg_new < ti_beg_old)
error("New beg of time-step before the old one");
if (dti_new > dti_old) error("New time-step larger than old one");
#endif
double dt_kick_grav = 0., dt_kick_hydro = 0., dt_kick_therm = 0.,
dt_kick_corr = 0.;
/* Now we need to reverse the kick1...
* Note the minus sign! (the dt are negative here) */
dt_kick_hydro = -kick_get_hydro_kick_dt(ti_beg_old, ti_end_half_old,
time_base, with_cosmology, cosmo);
dt_kick_grav = -kick_get_grav_kick_dt(ti_beg_old, ti_end_half_old,
time_base, with_cosmology, cosmo);
dt_kick_therm = -kick_get_therm_kick_dt(ti_beg_old, ti_end_half_old,
time_base, with_cosmology, cosmo);
dt_kick_corr = -kick_get_corr_kick_dt(ti_beg_old, ti_end_half_old,
time_base, with_cosmology, cosmo);
/* Note that there is no need to change the mesh integration as we
* can't go back more than one global step */
kick_part(p, xp, dt_kick_hydro, dt_kick_grav, /*dt_kick_mesh_grav=*/0.,
dt_kick_therm, dt_kick_corr, e->cosmology, e->hydro_properties,
e->entropy_floor, ti_end_half_old, ti_beg_old,
/*ti_start_mesh=*/-1, /*ti_end_mesh=*/-1);
/* ...and apply the new one (dt is positiive).
* This brings us to the current time. */
dt_kick_hydro = kick_get_hydro_kick_dt(ti_beg_old, ti_beg_new, time_base,
with_cosmology, cosmo);
dt_kick_grav = kick_get_grav_kick_dt(ti_beg_old, ti_beg_new, time_base,
with_cosmology, cosmo);
dt_kick_therm = kick_get_therm_kick_dt(ti_beg_old, ti_beg_new, time_base,
with_cosmology, cosmo);
dt_kick_corr = kick_get_corr_kick_dt(ti_beg_old, ti_beg_new, time_base,
with_cosmology, cosmo);
/* Note that there is no need to change the mesh integration as we
* can't go back more than one global step */
kick_part(p, xp, dt_kick_hydro, dt_kick_grav, /*dt_kick_mesh_grav=*/0.,
dt_kick_therm, dt_kick_corr, e->cosmology, e->hydro_properties,
e->entropy_floor, ti_beg_old, ti_beg_new, /*ti_start_mesh=*/-1,
/*ti_end_mesh=*/-1);
/* The particle has now been kicked to the current time */
/* New time-bin of this particle */
p->time_bin = new_bin;
/* Mark the particle as being ready to be time integrated */
p->limiter_data.wakeup = time_bin_not_awake;
/* Do we need to apply the mising kick1 or is this bin active and
it will be done in the kick task? */
if (new_bin > e->max_active_bin) {
/* Apply the missing kick1 */
dt_kick_hydro = kick_get_hydro_kick_dt(ti_beg_new, ti_end_half_new,
time_base, with_cosmology, cosmo);
dt_kick_grav = kick_get_grav_kick_dt(ti_beg_new, ti_end_half_new,
time_base, with_cosmology, cosmo);
dt_kick_therm = kick_get_therm_kick_dt(ti_beg_new, ti_end_half_new,
time_base, with_cosmology, cosmo);
dt_kick_corr = kick_get_corr_kick_dt(ti_beg_new, ti_end_half_new,
time_base, with_cosmology, cosmo);
/* Note that there is no need to change the mesh integration as we
* can't go back more than one global step */
kick_part(p, xp, dt_kick_hydro, dt_kick_grav, /*dt_kick_mesh_grav=*/0.,
dt_kick_therm, dt_kick_corr, e->cosmology, e->hydro_properties,
e->entropy_floor, ti_beg_new, ti_end_half_new,
/*ti_start_mesh=*/-1, /*ti_end_mesh=*/-1);
/* Return the new end-of-step for this particle */
return ti_beg_new + dti_new;
} else {
/* No kick to do here */
/* Return the new end-of-step for this particle */
return ti_current + get_integer_timestep(p->time_bin);
}
}
}
#endif /* SWIFT_TIMESTEP_LIMITER_H */