/******************************************************************************* * 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_H #define SWIFT_TIMESTEP_H /* Config parameters. */ #include /* Local headers. */ #include "cooling.h" #include "debug.h" #include "forcing.h" #include "potential.h" #include "rt.h" #include "timeline.h" /** * @brief Compute a valid integer time-step form a given time-step * * We consider the minimal time-bin of any neighbours and prevent particles * to differ from it by a fixed constant `time_bin_neighbour_max_delta_bin`. * * If min_ngb_bin is set to `num_time_bins`, then no limit from the neighbours * is imposed. * * @param new_dt The time-step to convert. * @param old_bin The old time bin. * @param min_ngb_bin Minimal time-bin of any neighbour of this particle. * @param ti_current The current time on the integer time-line. * @param time_base_inv The inverse of the system's minimal time-step. */ __attribute__((always_inline, const)) INLINE static integertime_t make_integer_timestep(const float new_dt, const timebin_t old_bin, const timebin_t min_ngb_bin, const integertime_t ti_current, const double time_base_inv) { /* Convert to integer time */ integertime_t new_dti = (integertime_t)(new_dt * time_base_inv); /* Are we allowed to use this bin given the neighbours? */ timebin_t new_bin = get_time_bin(new_dti); new_bin = min(new_bin, min_ngb_bin + time_bin_neighbour_max_delta_bin); new_dti = get_integer_timestep(new_bin); /* Current time-step */ const integertime_t current_dti = get_integer_timestep(old_bin); const integertime_t ti_end = get_integer_time_end(ti_current, old_bin); /* Limit timestep increase */ if (old_bin > 0) new_dti = min(new_dti, 2 * current_dti); /* Put this timestep on the time line */ integertime_t dti_timeline = max_nr_timesteps; while (new_dti < dti_timeline) dti_timeline /= ((integertime_t)2); new_dti = dti_timeline; /* Make sure we are allowed to increase the timestep size */ if (new_dti > current_dti) { if ((max_nr_timesteps - ti_end) % new_dti > 0) new_dti = current_dti; } #ifdef SWIFT_DEBUG_CHECKS if (new_dti == 0) error("Computed an integer time-step of size 0"); #endif return new_dti; } /** * @brief Compute the new (integer) time-step of a given #gpart * * @param gp The #gpart. * @param e The #engine (used to get some constants). */ __attribute__((always_inline)) INLINE static integertime_t get_gpart_timestep( const struct gpart *restrict gp, const struct engine *restrict e) { #ifdef SWIFT_DEBUG_CHECKS if (gp->time_bin == time_bin_not_created) { error("Trying to compute time step for an extra particle."); } #endif float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) new_dt_ext = external_gravity_timestep(e->time, e->external_potential, e->physical_constants, gp); const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) new_dt_self = gravity_compute_timestep_self( gp, a_hydro, e->gravity_properties, e->cosmology); /* Take the minimum of all */ float new_dt = min(new_dt_self, new_dt_ext); /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); if (new_dt < e->dt_min) error("gpart (id=%lld) wants a time-step (%e) below dt_min (%e)", gp->id_or_neg_offset, new_dt, e->dt_min); /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( new_dt, gp->time_bin, num_time_bins, e->ti_current, e->time_base_inv); return new_dti; } /** * @brief Compute the new (integer) time-step of a given #part * * @param p The #part. * @param xp The #xpart partner of p. * @param e The #engine (used to get some constants). * @param new_dti_rt The new radiation integer time step. */ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( const struct part *restrict p, const struct xpart *restrict xp, const struct engine *restrict e, const integertime_t new_dti_rt) { /* Compute the next timestep (hydro condition) */ const float new_dt_hydro = hydro_compute_timestep(p, xp, e->hydro_properties, e->cosmology); /* Compute the next timestep (MHD condition) */ const float new_dt_mhd = mhd_compute_timestep(p, xp, e->hydro_properties, e->cosmology); /* Compute the next timestep (cooling condition) */ float new_dt_cooling = FLT_MAX; if (e->policy & engine_policy_cooling) new_dt_cooling = cooling_timestep(e->cooling_func, e->physical_constants, e->cosmology, e->internal_units, e->hydro_properties, p, xp); /* Compute the next timestep (gravity condition) */ float new_dt_grav = FLT_MAX, new_dt_self_grav = FLT_MAX, new_dt_ext_grav = FLT_MAX; if (p->gpart != NULL) { if (e->policy & engine_policy_external_gravity) new_dt_ext_grav = external_gravity_timestep( e->time, e->external_potential, e->physical_constants, p->gpart); if (e->policy & engine_policy_self_gravity) new_dt_self_grav = gravity_compute_timestep_self( p->gpart, p->a_hydro, e->gravity_properties, e->cosmology); new_dt_grav = min(new_dt_self_grav, new_dt_ext_grav); } /* Compute the next timestep (forcing terms condition) */ const float new_dt_forcing = forcing_terms_timestep( e->time, e->forcing_terms, e->physical_constants, p, xp); /* Compute the next timestep (chemistry condition, e.g. diffusion) */ const float new_dt_chemistry = chemistry_timestep(e->physical_constants, e->cosmology, e->internal_units, e->hydro_properties, e->chemistry, p); /* Take the minimum of all */ float new_dt = min3(new_dt_hydro, new_dt_cooling, new_dt_grav); new_dt = min4(new_dt, new_dt_mhd, new_dt_chemistry, new_dt_forcing); /* Limit change in smoothing length */ const float dt_h_change = (p->force.h_dt != 0.0f) ? fabsf(e->hydro_properties->log_max_h_change * p->h / p->force.h_dt) : FLT_MAX; new_dt = min(new_dt, dt_h_change); /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); if (new_dt < e->dt_min) error("part (id=%lld) wants a time-step (%e) below dt_min (%e)", p->id, new_dt, e->dt_min); /* Convert to integer time */ integertime_t new_dti = make_integer_timestep( new_dt, p->time_bin, p->limiter_data.min_ngb_time_bin, e->ti_current, e->time_base_inv); if (e->policy & engine_policy_rt) { if (new_dti_rt <= new_dti) { /* enforce dt_hydro <= nsubcycles * dt_rt. The rare case where * new_dti_rt > new_dti will be handled in the parent function * that calls this one. */ integertime_t max_subcycles = max(e->max_nr_rt_subcycles, 1); if (max_nr_timesteps / max_subcycles < new_dti_rt) { /* multiplication new_dti_rt * max_subcycles would overflow. This can * happen in rare cases, especially if the total physical time the * simulation should cover is small. So limit max_subcycles to a * reasonable value. * First find an integer guess for the maximal permissible number * of sub-cycles. Then find highest power-of-two below that guess. * Divide the guess by a factor of 2 to simplify the subsequent while * loop. The max() is there to prevent bad things happening. */ const integertime_t max_subcycles_guess = max(1LL, max_nr_timesteps / (new_dti_rt * 2LL)); max_subcycles = 1LL; while (max_subcycles_guess > max_subcycles) max_subcycles *= 2LL; } new_dti = min(new_dti, new_dti_rt * max_subcycles); } } return new_dti; } /** * @brief Compute the new (integer) time-step of a given #part * * @param p The #part. * @param xp The #xpart partner of p. * @param e The #engine (used to get some constants). */ __attribute__((always_inline)) INLINE static integertime_t get_part_rt_timestep( const struct part *restrict p, const struct xpart *restrict xp, const struct engine *restrict e) { if (!(e->policy & engine_policy_rt)) return get_integer_timestep(num_time_bins); float new_dt = rt_compute_timestep(p, xp, e->rt_props, e->cosmology, e->hydro_properties, e->physical_constants, e->internal_units); if ((e->policy & engine_policy_cosmology)) /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); #ifdef SWIFT_RT_DEBUG_CHECKS /* Proper error will be caught in get_part_timestep(), so keep this as * debugging check only. */ const float f = (float)max(e->max_nr_rt_subcycles, 1); if (new_dt < e->dt_min / f) error( "part (id=%lld) wants an RT time-step (%e) below dt_min/nr_subcycles " "(%e)", p->id, new_dt, e->dt_min / f); #endif const integertime_t new_dti = make_integer_timestep( new_dt, p->rt_time_data.time_bin, p->rt_time_data.min_ngb_time_bin, e->ti_current, e->time_base_inv); return new_dti; } /** * @brief Compute the new (integer) time-step of a given #spart * * @param sp The #spart. * @param e The #engine (used to get some constants). */ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( const struct spart *restrict sp, const struct engine *restrict e) { /* Stellar time-step */ float new_dt_stars = stars_compute_timestep( sp, e->stars_properties, (e->policy & engine_policy_cosmology), e->cosmology, e->time); /* Gravity time-step */ float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) new_dt_ext = external_gravity_timestep(e->time, e->external_potential, e->physical_constants, sp->gpart); const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) new_dt_self = gravity_compute_timestep_self( sp->gpart, a_hydro, e->gravity_properties, e->cosmology); float new_dt_rt = FLT_MAX; if (e->policy & engine_policy_rt) new_dt_rt = rt_compute_spart_timestep(sp, e->rt_props, e->cosmology); /* Take the minimum of all */ float new_dt = min4(new_dt_stars, new_dt_self, new_dt_ext, new_dt_rt); /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); if (new_dt < e->dt_min) { error("spart (id=%lld) wants a time-step (%e) below dt_min (%e)", sp->id, new_dt, e->dt_min); } /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( new_dt, sp->time_bin, num_time_bins, e->ti_current, e->time_base_inv); return new_dti; } /** * @brief Compute the new (integer) time-step of a given #bpart * * @param bp The #bpart. * @param e The #engine (used to get some constants). */ __attribute__((always_inline)) INLINE static integertime_t get_bpart_timestep( const struct bpart *restrict bp, const struct engine *restrict e) { /* Black hole internal time-step */ float new_dt_black_holes = black_holes_compute_timestep( bp, e->black_holes_properties, e->physical_constants, e->cosmology); /* Gravity time-step */ float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) new_dt_ext = external_gravity_timestep(e->time, e->external_potential, e->physical_constants, bp->gpart); const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) new_dt_self = gravity_compute_timestep_self( bp->gpart, a_hydro, e->gravity_properties, e->cosmology); /* Take the minimum of all */ float new_dt = min3(new_dt_black_holes, new_dt_self, new_dt_ext); /* Apply the maximal dibslacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); if (new_dt < e->dt_min) { error("bpart (id=%lld) wants a time-step (%e) below dt_min (%e)", bp->id, new_dt, e->dt_min); } /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( new_dt, bp->time_bin, num_time_bins, e->ti_current, e->time_base_inv); return new_dti; } /** * @brief Compute the new (integer) time-step of a given #sink. * * @param sink The #sink. * @param e The #engine (used to get some constants). */ __attribute__((always_inline)) INLINE static integertime_t get_sink_timestep( const struct sink *restrict sink, const struct engine *restrict e) { /* Sink time-step */ float new_dt_sink = sink_compute_timestep( sink, e->sink_properties, (e->policy & engine_policy_cosmology), e->cosmology, e->gravity_properties, e->time, e->time_base); /* Gravity time-step */ float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) new_dt_ext = external_gravity_timestep(e->time, e->external_potential, e->physical_constants, sink->gpart); const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) new_dt_self = gravity_compute_timestep_self( sink->gpart, a_hydro, e->gravity_properties, e->cosmology); /* Take the minimum of all */ float new_dt = min3(new_dt_sink, new_dt_self, new_dt_ext); /* Apply the maximal dibslacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); /* Apply cosmology correction (This is 1 if non-cosmological) */ new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); if (new_dt < e->dt_min) { error("sink (id=%lld) wants a time-step (%e) below dt_min (%e)", sink->id, new_dt, e->dt_min); } /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( new_dt, sink->time_bin, num_time_bins, e->ti_current, e->time_base_inv); return new_dti; } #endif /* SWIFT_TIMESTEP_H */