/******************************************************************************* * 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 "cell.h" /* Local headers. */ #include "active.h" #include "adaptive_softening.h" #include "drift.h" #include "feedback.h" #include "gravity.h" #include "lightcone/lightcone.h" #include "lightcone/lightcone_array.h" #include "multipole.h" #include "neutrino.h" #include "rt.h" #include "sink.h" #include "star_formation.h" #include "tracers.h" #ifdef WITH_LIGHTCONE /** * @brief Compute refined lightcone replication list for a cell * * Returns a pointer to the new list, which must be freed later. * * @param e The #engine * @param c The #cell * @param replication_list_in The input replication_list struct */ static struct replication_list *refine_replications( const struct engine *e, const struct cell *c, struct replication_list *replication_list_in) { struct replication_list *replication_list; if (e->lightcone_array_properties->nr_lightcones > 0) { if (replication_list_in) { /* We're not at the top of the hierarchy, so use the replication lists * passed in */ replication_list = replication_list_in; } else { /* Current call is top of the recursive hierarchy, so compute refined * replication lists */ replication_list = lightcone_array_refine_replications(e->lightcone_array_properties, c); } } else { replication_list = NULL; } return replication_list; } #endif /** * @brief Recursively set the hydro's ti_old_part to the current time. * * @param c The cell to update. * @param ti The current integer time. */ void cell_set_ti_old_part(struct cell *c, const integertime_t ti) { c->hydro.ti_old_part = ti; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) cell_set_ti_old_part(c->progeny[k], ti); } } } /** * @brief Recursively set the gravity's ti_old_part to the current time. * * @param c The cell to update. * @param ti The current integer time. */ void cell_set_ti_old_gpart(struct cell *c, const integertime_t ti) { c->grav.ti_old_part = ti; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) cell_set_ti_old_gpart(c->progeny[k], ti); } } } /** * @brief Recursively set the stars' ti_old_part to the current time. * * @param c The cell to update. * @param ti The current integer time. */ void cell_set_ti_old_spart(struct cell *c, const integertime_t ti) { c->stars.ti_old_part = ti; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) cell_set_ti_old_spart(c->progeny[k], ti); } } } /** * @brief Recursively set the black holes' ti_old_part to the current time. * * @param c The cell to update. * @param ti The current integer time. */ void cell_set_ti_old_bpart(struct cell *c, const integertime_t ti) { c->black_holes.ti_old_part = ti; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) cell_set_ti_old_bpart(c->progeny[k], ti); } } } /** * @brief Recursively set the sinks' ti_old_part to the current time. * * @param c The cell to update. * @param ti The current integer time. */ void cell_set_ti_old_sink(struct cell *c, const integertime_t ti) { c->sinks.ti_old_part = ti; if (c->split) { for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) cell_set_ti_old_sink(c->progeny[k], ti); } } } /** * @brief Recursively drifts the #part in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ void cell_drift_part(struct cell *c, const struct engine *e, int force, struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); const float hydro_h_max = e->hydro_properties->h_max; const float hydro_h_min = e->hydro_properties->h_min; const integertime_t ti_old_part = c->hydro.ti_old_part; const integertime_t ti_current = e->ti_current; struct part *const parts = c->hydro.parts; struct xpart *const xparts = c->hydro.xparts; float dx_max = 0.f, dx2_max = 0.f; float dx_max_sort = 0.0f, dx2_max_sort = 0.f; float cell_h_max = 0.f; float cell_h_max_active = 0.f; /* Drift irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_hydro_drift)); #ifdef SWIFT_DEBUG_CHECKS /* Check that we only drift local cells. */ if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); /* Check that we are actually going to move forward. */ if (ti_current < ti_old_part) error("Attempt to drift to the past"); #endif /* Early abort? */ if (c->hydro.count == 0) { /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_hydro_drift | cell_flag_do_hydro_sub_drift); /* Update the time of the last drift */ cell_set_ti_old_part(c, ti_current); return; } /* Ok, we have some particles somewhere in the hierarchy to drift IMPORTANT: after this point we must not return without freeing the replication lists if we allocated them. */ struct replication_list *replication_list = NULL; #ifdef WITH_LIGHTCONE replication_list = refine_replications(e, c, replication_list_in); #endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_hydro_sub_drift))) { /* Loop over the progeny and collect their data. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; /* Collect */ cell_drift_part(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->hydro.dx_max_part); dx_max_sort = max(dx_max_sort, cp->hydro.dx_max_sort); cell_h_max = max(cell_h_max, cp->hydro.h_max); cell_h_max_active = max(cell_h_max_active, cp->hydro.h_max_active); } } /* Store the values */ c->hydro.h_max = cell_h_max; c->hydro.h_max_active = cell_h_max_active; c->hydro.dx_max_part = dx_max; c->hydro.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->hydro.ti_old_part = ti_current; } else if (!c->split && force && ti_current > ti_old_part) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift, dt_kick_grav, dt_kick_hydro, dt_therm; if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_part, ti_current); dt_kick_grav = cosmology_get_grav_kick_factor(e->cosmology, ti_old_part, ti_current); dt_kick_hydro = cosmology_get_hydro_kick_factor(e->cosmology, ti_old_part, ti_current); dt_therm = cosmology_get_therm_kick_factor(e->cosmology, ti_old_part, ti_current); } else { dt_drift = (ti_current - ti_old_part) * e->time_base; dt_kick_grav = (ti_current - ti_old_part) * e->time_base; dt_kick_hydro = (ti_current - ti_old_part) * e->time_base; dt_therm = (ti_current - ti_old_part) * e->time_base; } /* Loop over all the gas particles in the cell */ const size_t nr_parts = c->hydro.count; for (size_t k = 0; k < nr_parts; k++) { /* Get a handle on the part. */ struct part *const p = &parts[k]; struct xpart *const xp = &xparts[k]; /* Ignore inhibited particles */ if (part_is_inhibited(p, e)) continue; /* Apply the effects of feedback on this particle * (Note: Only used in schemes that have a delayed feedback mechanism * otherwise just an empty function) */ feedback_update_part(p, xp, e); /* Drift... */ drift_part(p, xp, dt_drift, dt_kick_hydro, dt_kick_grav, dt_therm, ti_old_part, ti_current, e, replication_list, c->loc); /* Update the tracers properties */ tracers_after_drift(p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(xp->v_full[0] * dt_drift) > e->s->dim[0] || fabs(xp->v_full[1] * dt_drift) > e->s->dim[1] || fabs(xp->v_full[2] * dt_drift) > e->s->dim[2]) { error( "Particle drifts by more than a box length! id %llu xp->v_full " "%.5e %.5e %.5e p->v %.5e %.5e %.5e", p->id, xp->v_full[0], xp->v_full[1], xp->v_full[2], p->v[0], p->v[1], p->v[2]); } #endif /* In non-periodic BC runs, remove particles that crossed the border */ if (!periodic) { /* Did the particle leave the box? */ if ((p->x[0] > dim[0]) || (p->x[0] < 0.) || // x (p->x[1] > dim[1]) || (p->x[1] < 0.) || // y (p->x[2] > dim[2]) || (p->x[2] < 0.)) { // z lock_lock(&e->s->lock); /* Re-check that the particle has not been removed * by another thread before we do the deed. */ if (!part_is_inhibited(p, e)) { #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { /* Log the particle one last time. */ csds_log_part(e->csds, p, xp, e, /* log_all */ 1, csds_flag_delete, /* data */ 0); } #endif /* One last action before death? */ hydro_remove_part(p, xp, e->time); /* Remove the particle entirely */ cell_remove_part(e, c, p, xp); } if (lock_unlock(&e->s->lock) != 0) error("Failed to unlock the space!"); continue; } } /* Limit h to within the allowed range */ p->h = min(p->h, hydro_h_max); p->h = max(p->h, hydro_h_min); /* Set the appropriate depth level for this particle */ cell_set_part_h_depth(p, c); /* Compute (square of) motion since last cell construction */ const float dx2 = xp->x_diff[0] * xp->x_diff[0] + xp->x_diff[1] * xp->x_diff[1] + xp->x_diff[2] * xp->x_diff[2]; dx2_max = max(dx2_max, dx2); const float dx2_sort = xp->x_diff_sort[0] * xp->x_diff_sort[0] + xp->x_diff_sort[1] * xp->x_diff_sort[1] + xp->x_diff_sort[2] * xp->x_diff_sort[2]; dx2_max_sort = max(dx2_max_sort, dx2_sort); /* Update the maximal smoothing length in the cell */ cell_h_max = max(cell_h_max, p->h); /* Mark the particle has not being swallowed */ black_holes_mark_part_as_not_swallowed(&p->black_holes_data); /* Mark the particle has not being swallowed by a sink */ sink_mark_part_as_not_swallowed(&p->sink_data); /* Reset the gas particle-carried feedback fields */ feedback_reset_part(p, xp); /* Get ready for a density calculation */ if (part_is_active(p, e)) { hydro_init_part(p, &e->s->hs); adaptive_softening_init_part(p); mhd_init_part(p); black_holes_init_potential(&p->black_holes_data); chemistry_init_part(p, e->chemistry); star_formation_init_part(p, e->star_formation); tracers_after_init(p, xp, e->internal_units, e->physical_constants, with_cosmology, e->cosmology, e->hydro_properties, e->cooling_func, e->time); sink_init_part(p, e->sink_properties); rt_init_part(p); /* Update the maximal active smoothing length in the cell */ cell_h_max_active = max(cell_h_max_active, p->h); } #ifdef SWIFT_HYDRO_DENSITY_CHECKS p->limiter_data.n_limiter = 0.f; p->limiter_data.N_limiter = 0; #endif } /* Now, get the maximal particle motion from its square */ dx_max = sqrtf(dx2_max); dx_max_sort = sqrtf(dx2_max_sort); /* Store the values */ c->hydro.h_max = cell_h_max; c->hydro.h_max_active = cell_h_max_active; c->hydro.dx_max_part = dx_max; c->hydro.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->hydro.ti_old_part = ti_current; } #ifdef WITH_LIGHTCONE /* If we're at the top of the recursive hierarchy, clean up the refined * replication lists */ if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) lightcone_array_free_replications(e->lightcone_array_properties, replication_list); #endif /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_hydro_drift | cell_flag_do_hydro_sub_drift); } /** * @brief Recursively drifts the #gpart in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ void cell_drift_gpart(struct cell *c, const struct engine *e, int force, struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); const integertime_t ti_old_gpart = c->grav.ti_old_part; const integertime_t ti_current = e->ti_current; struct gpart *const gparts = c->grav.parts; const struct gravity_props *grav_props = e->gravity_properties; const double a = e->cosmology->a; const double c_vel = e->physical_constants->const_speed_light_c; const int with_neutrinos = e->s->with_neutrinos; /* Drift irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_grav_drift)); #ifdef SWIFT_DEBUG_CHECKS /* Check that we only drift local cells. */ if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); /* Check that we are actually going to move forward. */ if (ti_current < ti_old_gpart) error("Attempt to drift to the past"); #endif /* Early abort? */ if (c->grav.count == 0) { /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_grav_drift | cell_flag_do_grav_sub_drift); /* Update the time of the last drift */ cell_set_ti_old_gpart(c, ti_current); return; } /* Ok, we have some particles somewhere in the hierarchy to drift. If making lightcones, get the refined replication list for this cell. IMPORTANT: after this point we must not return without freeing the replication lists if we allocated them. */ struct replication_list *replication_list = NULL; #ifdef WITH_LIGHTCONE replication_list = refine_replications(e, c, replication_list_in); #endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_grav_sub_drift))) { /* Loop over the progeny and collect their data. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; /* Recurse */ cell_drift_gpart(cp, e, force, replication_list); } } /* Update the time of the last drift */ c->grav.ti_old_part = ti_current; } else if (!c->split && force && ti_current > ti_old_gpart) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_gpart, ti_current); } else { dt_drift = (ti_current - ti_old_gpart) * e->time_base; } /* Loop over all the g-particles in the cell */ const size_t nr_gparts = c->grav.count; for (size_t k = 0; k < nr_gparts; k++) { /* Get a handle on the gpart. */ struct gpart *const gp = &gparts[k]; /* Ignore inhibited particles */ if (gpart_is_inhibited(gp, e)) continue; /* Relativistic drift correction for neutrinos */ double dt_drift_k = dt_drift; if (with_neutrinos && gp->type == swift_type_neutrino) { dt_drift_k *= relativistic_drift_factor(gp->v_full, a, c_vel); } /* Drift... */ drift_gpart(gp, dt_drift_k, ti_old_gpart, ti_current, grav_props, e, replication_list, c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(gp->v_full[0] * dt_drift_k) > e->s->dim[0] || fabs(gp->v_full[1] * dt_drift_k) > e->s->dim[1] || fabs(gp->v_full[2] * dt_drift_k) > e->s->dim[2]) { error( "Particle drifts by more than a box length! gp->v_full %.5e %.5e " "%.5e", gp->v_full[0], gp->v_full[1], gp->v_full[2]); } #endif /* In non-periodic BC runs, remove particles that crossed the border */ if (!periodic) { /* Did the particle leave the box? */ if ((gp->x[0] > dim[0]) || (gp->x[0] < 0.) || // x (gp->x[1] > dim[1]) || (gp->x[1] < 0.) || // y (gp->x[2] > dim[2]) || (gp->x[2] < 0.)) { // z lock_lock(&e->s->lock); /* Re-check that the particle has not been removed * by another thread before we do the deed. */ if (!gpart_is_inhibited(gp, e)) { /* Remove the particle entirely */ if (gp->type == swift_type_dark_matter) { #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { /* Log the particle one last time. */ csds_log_gpart(e->csds, gp, e, /* log_all */ 1, csds_flag_delete, /* data */ 0); } #endif /* Remove the particle */ cell_remove_gpart(e, c, gp); } } if (lock_unlock(&e->s->lock) != 0) error("Failed to unlock the space!"); continue; } } /* Init gravity force fields. */ if (gpart_is_active(gp, e)) { gravity_init_gpart(gp); } } /* Update the time of the last drift */ c->grav.ti_old_part = ti_current; } #ifdef WITH_LIGHTCONE /* If we're at the top of the recursive hierarchy, clean up the refined * replication lists */ if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) lightcone_array_free_replications(e->lightcone_array_properties, replication_list); #endif /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_grav_drift | cell_flag_do_grav_sub_drift); } /** * @brief Recursively drifts the #spart in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ void cell_drift_spart(struct cell *c, const struct engine *e, int force, struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); const float stars_h_max = e->hydro_properties->h_max; const float stars_h_min = e->hydro_properties->h_min; const integertime_t ti_old_spart = c->stars.ti_old_part; const integertime_t ti_current = e->ti_current; struct spart *const sparts = c->stars.parts; float dx_max = 0.f, dx2_max = 0.f; float dx_max_sort = 0.0f, dx2_max_sort = 0.f; float cell_h_max = 0.f; float cell_h_max_active = 0.f; /* Drift irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_stars_drift)); #ifdef SWIFT_DEBUG_CHECKS /* Check that we only drift local cells. */ if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); /* Check that we are actually going to move forward. */ if (ti_current < ti_old_spart) error("Attempt to drift to the past"); #endif /* Early abort? */ if (c->stars.count == 0) { /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_stars_drift | cell_flag_do_stars_sub_drift); /* Update the time of the last drift */ cell_set_ti_old_spart(c, ti_current); return; } /* Ok, we have some particles somewhere in the hierarchy to drift IMPORTANT: after this point we must not return without freeing the replication lists if we allocated them. */ struct replication_list *replication_list = NULL; #ifdef WITH_LIGHTCONE replication_list = refine_replications(e, c, replication_list_in); #endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_stars_sub_drift))) { /* Loop over the progeny and collect their data. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; /* Recurse */ cell_drift_spart(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->stars.dx_max_part); dx_max_sort = max(dx_max_sort, cp->stars.dx_max_sort); cell_h_max = max(cell_h_max, cp->stars.h_max); cell_h_max_active = max(cell_h_max_active, cp->stars.h_max_active); } } /* Store the values */ c->stars.h_max = cell_h_max; c->stars.h_max_active = cell_h_max_active; c->stars.dx_max_part = dx_max; c->stars.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->stars.ti_old_part = ti_current; } else if (!c->split && force && ti_current > ti_old_spart) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_spart, ti_current); } else { dt_drift = (ti_current - ti_old_spart) * e->time_base; } /* Loop over all the star particles in the cell */ const size_t nr_sparts = c->stars.count; for (size_t k = 0; k < nr_sparts; k++) { /* Get a handle on the spart. */ struct spart *const sp = &sparts[k]; /* Ignore inhibited particles */ if (spart_is_inhibited(sp, e)) continue; /* Drift... */ drift_spart(sp, dt_drift, ti_old_spart, ti_current, e, replication_list, c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(sp->v[0] * dt_drift) > e->s->dim[0] || fabs(sp->v[1] * dt_drift) > e->s->dim[1] || fabs(sp->v[2] * dt_drift) > e->s->dim[2]) { error("Particle drifts by more than a box length!"); } #endif /* In non-periodic BC runs, remove particles that crossed the border */ if (!periodic) { /* Did the particle leave the box? */ if ((sp->x[0] > dim[0]) || (sp->x[0] < 0.) || // x (sp->x[1] > dim[1]) || (sp->x[1] < 0.) || // y (sp->x[2] > dim[2]) || (sp->x[2] < 0.)) { // z lock_lock(&e->s->lock); /* Re-check that the particle has not been removed * by another thread before we do the deed. */ if (!spart_is_inhibited(sp, e)) { #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { /* Log the particle one last time. */ csds_log_spart(e->csds, sp, e, /* log_all */ 1, csds_flag_delete, /* data */ 0); } #endif /* Remove the particle entirely */ cell_remove_spart(e, c, sp); } if (lock_unlock(&e->s->lock) != 0) error("Failed to unlock the space!"); continue; } } /* Limit h to within the allowed range */ sp->h = min(sp->h, stars_h_max); sp->h = max(sp->h, stars_h_min); /* Set the appropriate depth level for this particle */ cell_set_spart_h_depth(sp, c); /* Compute (square of) motion since last cell construction */ const float dx2 = sp->x_diff[0] * sp->x_diff[0] + sp->x_diff[1] * sp->x_diff[1] + sp->x_diff[2] * sp->x_diff[2]; dx2_max = max(dx2_max, dx2); const float dx2_sort = sp->x_diff_sort[0] * sp->x_diff_sort[0] + sp->x_diff_sort[1] * sp->x_diff_sort[1] + sp->x_diff_sort[2] * sp->x_diff_sort[2]; dx2_max_sort = max(dx2_max_sort, dx2_sort); /* Maximal smoothing length */ cell_h_max = max(cell_h_max, sp->h); /* Get ready for a density calculation */ if (spart_is_active(sp, e)) { stars_init_spart(sp); feedback_init_spart(sp); rt_init_spart(sp); /* Update the maximal active smoothing length in the cell */ cell_h_max_active = max(cell_h_max_active, sp->h); } } /* Now, get the maximal particle motion from its square */ dx_max = sqrtf(dx2_max); dx_max_sort = sqrtf(dx2_max_sort); /* Store the values */ c->stars.h_max = cell_h_max; c->stars.h_max_active = cell_h_max_active; c->stars.dx_max_part = dx_max; c->stars.dx_max_sort = dx_max_sort; /* Update the time of the last drift */ c->stars.ti_old_part = ti_current; } #ifdef WITH_LIGHTCONE /* If we're at the top of the recursive hierarchy, clean up the refined * replication lists */ if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) lightcone_array_free_replications(e->lightcone_array_properties, replication_list); #endif /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_stars_drift | cell_flag_do_stars_sub_drift); } /** * @brief Recursively drifts the #bpart in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ void cell_drift_bpart(struct cell *c, const struct engine *e, int force, struct replication_list *replication_list_in) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); const float black_holes_h_max = e->hydro_properties->h_max; const float black_holes_h_min = e->hydro_properties->h_min; const integertime_t ti_old_bpart = c->black_holes.ti_old_part; const integertime_t ti_current = e->ti_current; struct bpart *const bparts = c->black_holes.parts; float dx_max = 0.f, dx2_max = 0.f; float cell_h_max = 0.f; float cell_h_max_active = 0.f; /* Drift irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_bh_drift)); #ifdef SWIFT_DEBUG_CHECKS /* Check that we only drift local cells. */ if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); /* Check that we are actually going to move forward. */ if (ti_current < ti_old_bpart) error("Attempt to drift to the past"); #endif /* Early abort? */ if (c->black_holes.count == 0) { /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_bh_drift | cell_flag_do_bh_sub_drift); /* Update the time of the last drift */ cell_set_ti_old_bpart(c, ti_current); return; } /* Ok, we have some particles somewhere in the hierarchy to drift IMPORTANT: after this point we must not return without freeing the replication lists if we allocated them. */ struct replication_list *replication_list = NULL; #ifdef WITH_LIGHTCONE replication_list = refine_replications(e, c, replication_list_in); #endif /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_bh_sub_drift))) { /* Loop over the progeny and collect their data. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; /* Recurse */ cell_drift_bpart(cp, e, force, replication_list); /* Update */ dx_max = max(dx_max, cp->black_holes.dx_max_part); cell_h_max = max(cell_h_max, cp->black_holes.h_max); cell_h_max_active = max(cell_h_max_active, cp->black_holes.h_max_active); } } /* Store the values */ c->black_holes.h_max = cell_h_max; c->black_holes.h_max_active = cell_h_max_active; c->black_holes.dx_max_part = dx_max; /* Update the time of the last drift */ c->black_holes.ti_old_part = ti_current; } else if (!c->split && force && ti_current > ti_old_bpart) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_bpart, ti_current); } else { dt_drift = (ti_current - ti_old_bpart) * e->time_base; } /* Loop over all the black hole particles in the cell */ const size_t nr_bparts = c->black_holes.count; for (size_t k = 0; k < nr_bparts; k++) { /* Get a handle on the bpart. */ struct bpart *const bp = &bparts[k]; /* Ignore inhibited particles */ if (bpart_is_inhibited(bp, e)) continue; /* Drift... */ drift_bpart(bp, dt_drift, ti_old_bpart, ti_current, e, replication_list, c->loc); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(bp->v[0] * dt_drift) > e->s->dim[0] || fabs(bp->v[1] * dt_drift) > e->s->dim[1] || fabs(bp->v[2] * dt_drift) > e->s->dim[2]) { error("Particle drifts by more than a box length!"); } #endif /* In non-periodic BC runs, remove particles that crossed the border */ if (!periodic) { /* Did the particle leave the box? */ if ((bp->x[0] > dim[0]) || (bp->x[0] < 0.) || // x (bp->x[1] > dim[1]) || (bp->x[1] < 0.) || // y (bp->x[2] > dim[2]) || (bp->x[2] < 0.)) { // z lock_lock(&e->s->lock); /* Re-check that the particle has not been removed * by another thread before we do the deed. */ if (!bpart_is_inhibited(bp, e)) { #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { error("Logging of black hole particles is not yet implemented."); } #endif /* Remove the particle entirely */ cell_remove_bpart(e, c, bp); } if (lock_unlock(&e->s->lock) != 0) error("Failed to unlock the space!"); continue; } } /* Limit h to within the allowed range */ bp->h = min(bp->h, black_holes_h_max); bp->h = max(bp->h, black_holes_h_min); /* Set the appropriate depth level for this particle */ cell_set_bpart_h_depth(bp, c); /* Compute (square of) motion since last cell construction */ const float dx2 = bp->x_diff[0] * bp->x_diff[0] + bp->x_diff[1] * bp->x_diff[1] + bp->x_diff[2] * bp->x_diff[2]; dx2_max = max(dx2_max, dx2); /* Maximal smoothing length */ cell_h_max = max(cell_h_max, bp->h); /* Mark the particle has not being swallowed */ black_holes_mark_bpart_as_not_swallowed(&bp->merger_data); /* Get ready for a density calculation */ if (bpart_is_active(bp, e)) { black_holes_init_bpart(bp); /* Update the maximal active smoothing length in the cell */ cell_h_max_active = max(cell_h_max_active, bp->h); } } /* Now, get the maximal particle motion from its square */ dx_max = sqrtf(dx2_max); /* Store the values */ c->black_holes.h_max = cell_h_max; c->black_holes.h_max_active = cell_h_max_active; c->black_holes.dx_max_part = dx_max; /* Update the time of the last drift */ c->black_holes.ti_old_part = ti_current; } #ifdef WITH_LIGHTCONE /* If we're at the top of the recursive hierarchy, clean up the refined * replication lists */ if (e->lightcone_array_properties->nr_lightcones > 0 && !replication_list_in) lightcone_array_free_replications(e->lightcone_array_properties, replication_list); #endif /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_bh_drift | cell_flag_do_bh_sub_drift); } /** * @brief Recursively drifts the #sink's in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). * @param force Drift the particles irrespective of the #cell flags. */ void cell_drift_sink(struct cell *c, const struct engine *e, int force) { const int periodic = e->s->periodic; const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]}; const int with_cosmology = (e->policy & engine_policy_cosmology); const float sinks_h_max = e->hydro_properties->h_max; const float sinks_h_min = e->hydro_properties->h_min; const integertime_t ti_old_sink = c->sinks.ti_old_part; const integertime_t ti_current = e->ti_current; struct sink *const sinks = c->sinks.parts; float dx_max = 0.f, dx2_max = 0.f; float cell_h_max = 0.f; float cell_h_max_active = 0.f; /* Drift irrespective of cell flags? */ force = (force || cell_get_flag(c, cell_flag_do_sink_drift)); #ifdef SWIFT_DEBUG_CHECKS /* Check that we only drift local cells. */ if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); /* Check that we are actually going to move forward. */ if (ti_current < ti_old_sink) error("Attempt to drift to the past"); #endif /* Early abort? */ if (c->sinks.count == 0) { /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_sink_drift | cell_flag_do_sink_sub_drift); /* Update the time of the last drift */ cell_set_ti_old_sink(c, ti_current); return; } /* Ok, we have some particles somewhere in the hierarchy to drift */ /* Are we not in a leaf ? */ if (c->split && (force || cell_get_flag(c, cell_flag_do_sink_sub_drift))) { /* Loop over the progeny and collect their data. */ for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; /* Recurse */ cell_drift_sink(cp, e, force); /* Update */ dx_max = max(dx_max, cp->sinks.dx_max_part); cell_h_max = max(cell_h_max, cp->sinks.h_max); cell_h_max_active = max(cell_h_max_active, cp->sinks.h_max_active); } } /* Store the values */ c->sinks.h_max = cell_h_max; c->sinks.h_max_active = cell_h_max_active; c->sinks.dx_max_part = dx_max; /* Update the time of the last drift */ c->sinks.ti_old_part = ti_current; } else if (!c->split && force && ti_current > ti_old_sink) { /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (with_cosmology) { dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_sink, ti_current); } else { dt_drift = (ti_current - ti_old_sink) * e->time_base; } /* Loop over all the sink particles in the cell */ const size_t nr_sinks = c->sinks.count; for (size_t k = 0; k < nr_sinks; k++) { /* Get a handle on the sink. */ struct sink *const sink = &sinks[k]; /* Ignore inhibited particles */ if (sink_is_inhibited(sink, e)) continue; /* Drift... */ drift_sink(sink, dt_drift, ti_old_sink, ti_current); #ifdef SWIFT_DEBUG_CHECKS /* Make sure the particle does not drift by more than a box length. */ if (fabs(sink->v[0] * dt_drift) > e->s->dim[0] || fabs(sink->v[1] * dt_drift) > e->s->dim[1] || fabs(sink->v[2] * dt_drift) > e->s->dim[2]) { error("Particle drifts by more than a box length!"); } #endif /* In non-periodic BC runs, remove particles that crossed the border */ if (!periodic) { /* Did the particle leave the box? */ if ((sink->x[0] > dim[0]) || (sink->x[0] < 0.) || // x (sink->x[1] > dim[1]) || (sink->x[1] < 0.) || // y (sink->x[2] > dim[2]) || (sink->x[2] < 0.)) { // z lock_lock(&e->s->lock); /* Re-check that the particle has not been removed * by another thread before we do the deed. */ if (!sink_is_inhibited(sink, e)) { #ifdef WITH_CSDS if (e->policy & engine_policy_csds) { error("Logging of sink particles is not yet implemented."); } #endif /* Remove the particle entirely */ cell_remove_sink(e, c, sink); } if (lock_unlock(&e->s->lock) != 0) error("Failed to unlock the space!"); continue; } } /* Limit h to within the allowed range */ sink->h = min(sink->h, sinks_h_max); sink->h = max(sink->h, sinks_h_min); /* Set the appropriate depth level for this particle */ cell_set_sink_h_depth(sink, c); /* Compute (square of) motion since last cell construction */ const float dx2 = sink->x_diff[0] * sink->x_diff[0] + sink->x_diff[1] * sink->x_diff[1] + sink->x_diff[2] * sink->x_diff[2]; dx2_max = max(dx2_max, dx2); /* Maximal smoothing length */ cell_h_max = max(cell_h_max, sink->h); /* Mark the particle has not being swallowed */ sink_mark_sink_as_not_swallowed(&sink->merger_data); /* Get ready for a density calculation */ if (sink_is_active(sink, e)) { sink_init_sink(sink); cell_h_max_active = max(cell_h_max_active, sink->h); } } /* Now, get the maximal particle motion from its square */ dx_max = sqrtf(dx2_max); /* Store the values */ c->sinks.h_max = cell_h_max; c->sinks.h_max_active = cell_h_max_active; c->sinks.dx_max_part = dx_max; /* Update the time of the last drift */ c->sinks.ti_old_part = ti_current; } /* Clear the drift flags. */ cell_clear_flag(c, cell_flag_do_sink_drift | cell_flag_do_sink_sub_drift); } /** * @brief Recursively drifts all multipoles in a cell hierarchy. * * @param c The #cell. * @param e The #engine (to get ti_current). */ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) { const integertime_t ti_old_multipole = c->grav.ti_old_multipole; const integertime_t ti_current = e->ti_current; #ifdef SWIFT_DEBUG_CHECKS /* Check that we are actually going to move forward. */ if (ti_current < ti_old_multipole) error("Attempt to drift to the past"); #endif /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (e->policy & engine_policy_cosmology) dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_multipole, ti_current); else dt_drift = (ti_current - ti_old_multipole) * e->time_base; /* Drift the multipole */ if (ti_current > ti_old_multipole) gravity_drift(c->grav.multipole, dt_drift); /* Are we not in a leaf ? */ if (c->split) { /* Loop over the progeny and recurse. */ for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) cell_drift_all_multipoles(c->progeny[k], e); } /* Update the time of the last drift */ c->grav.ti_old_multipole = ti_current; } /** * @brief Drifts the multipole of a cell to the current time. * * Only drifts the multipole at this level. Multipoles deeper in the * tree are not updated. * * @param c The #cell. * @param e The #engine (to get ti_current). */ void cell_drift_multipole(struct cell *c, const struct engine *e) { const integertime_t ti_old_multipole = c->grav.ti_old_multipole; const integertime_t ti_current = e->ti_current; #ifdef SWIFT_DEBUG_CHECKS /* Check that we are actually going to move forward. */ if (ti_current < ti_old_multipole) error("Attempt to drift to the past"); #endif /* Drift from the last time the cell was drifted to the current time */ double dt_drift; if (e->policy & engine_policy_cosmology) dt_drift = cosmology_get_drift_factor(e->cosmology, ti_old_multipole, ti_current); else dt_drift = (ti_current - ti_old_multipole) * e->time_base; if (ti_current > ti_old_multipole) gravity_drift(c->grav.multipole, dt_drift); /* Update the time of the last drift */ c->grav.ti_old_multipole = ti_current; }