/*******************************************************************************
* 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 "timers.h"
/**
* @brief Process all the gas particles in a cell that have been flagged for
* swallowing by a black hole.
*
* This is done by recursing down to the leaf-level and skipping the sub-cells
* that have not been drifted as they would not have any particles with
* swallowing flag. We then loop over the particles with a flag and look into
* the space-wide list of black holes for the particle with the corresponding
* ID. If found, the BH swallows the gas particle and the gas particle is
* removed. If the cell is local, we may be looking for a foreign BH, in which
* case, we do not update the BH (that will be done on its node) but just remove
* the gas particle.
*
* @param r The thread #runner.
* @param c The #cell.
* @param timer Are we timing this?
*/
void runner_do_gas_swallow(struct runner *r, struct cell *c, int timer) {
struct engine *e = r->e;
struct space *s = e->s;
const struct black_holes_props *props = e->black_holes_properties;
const int use_nibbling = props->use_nibbling;
struct bpart *bparts = s->bparts;
const size_t nr_bpart = s->nr_bparts;
#ifdef WITH_MPI
struct bpart *bparts_foreign = s->bparts_foreign;
const size_t nr_bparts_foreign = s->nr_bparts_foreign;
#endif
struct part *parts = c->hydro.parts;
struct xpart *xparts = c->hydro.xparts;
/* Nothing to do here if the cell is foreign and we are nibbling */
if (c->nodeID != e->nodeID && use_nibbling) {
return;
}
/* Early abort?
* (We only want cells for which we drifted the gas as these are
* the only ones that could have gas particles that have been flagged
* for swallowing) */
if (c->hydro.count == 0 || c->hydro.ti_old_part != e->ti_current) {
return;
}
/* Loop over the progeny ? */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *restrict cp = c->progeny[k];
runner_do_gas_swallow(r, cp, 0);
}
}
} else {
/* Loop over all the gas particles in the cell
* Note that the cell (and hence the parts) may be local or foreign. */
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 (they have already been removed!) */
if (part_is_inhibited(p, e)) continue;
/* Update mass of associated gpart, to reflect potential changes from
* nibbling. In this case, we are already done. */
if (use_nibbling) {
p->gpart->mass = hydro_get_mass(p);
continue;
}
/* Get the ID of the black holes that will swallow this part */
const long long swallow_id =
black_holes_get_part_swallow_id(&p->black_holes_data);
/* Has this particle been flagged for swallowing? */
if (swallow_id >= 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (p->ti_drift != e->ti_current)
error("Trying to swallow an un-drifted particle.");
#endif
/* ID of the BH swallowing this particle */
const long long BH_id = swallow_id;
/* Have we found this particle's BH already? */
int found = 0;
/* Let's look for the hungry black hole in the local list */
for (size_t i = 0; i < nr_bpart; ++i) {
/* Get a handle on the bpart. */
struct bpart *bp = &bparts[i];
if (bp->id == BH_id) {
/* Lock the space as we are going to work directly on the bpart list
*/
lock_lock(&s->lock);
/* Swallow the gas particle (i.e. update the BH properties) */
black_holes_swallow_part(bp, p, xp, e->cosmology);
/* Release the space as we are done updating the bpart */
if (lock_unlock(&s->lock) != 0)
error("Failed to unlock the space.");
/* If the gas particle is local, remove it */
if (c->nodeID == e->nodeID) {
message("BH %lld removing gas particle %lld", bp->id, p->id);
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)) {
/* Finally, remove the gas particle from the system
* Recall that the gpart associated with it is also removed
* at the same time. */
cell_remove_part(e, c, p, xp);
}
if (lock_unlock(&e->s->lock) != 0)
error("Failed to unlock the space!");
}
/* In any case, prevent the particle from being re-swallowed */
black_holes_mark_part_as_swallowed(&p->black_holes_data);
found = 1;
break;
}
} /* Loop over local BHs */
#ifdef WITH_MPI
/* We could also be in the case of a local gas particle being
* swallowed by a foreign BH. In this case, we won't update the
* BH but just remove the particle from the local list. */
if (c->nodeID == e->nodeID && !found) {
/* Let's look for the foreign hungry black hole */
for (size_t i = 0; i < nr_bparts_foreign; ++i) {
/* Get a handle on the bpart. */
struct bpart *bp = &bparts_foreign[i];
if (bp->id == BH_id) {
message("BH %lld removing gas particle %lld (foreign BH case)",
bp->id, p->id);
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)) {
/* Finally, remove the gas particle from the system */
cell_remove_part(e, c, p, xp);
}
if (lock_unlock(&e->s->lock) != 0)
error("Failed to unlock the space!");
found = 1;
break;
}
} /* Loop over foreign BHs */
} /* Is the cell local? */
#endif
/* If we have a local particle, we must have found the BH in one
* of our list of black holes. */
if (c->nodeID == e->nodeID && !found) {
error("Gas particle %lld could not find BH %lld to be swallowed",
p->id, swallow_id);
}
} /* Part was flagged for swallowing */
} /* Loop over the parts */
} /* Cell is not split */
}
/**
* @brief Processing of gas particles to swallow - self task case.
*
* @param r The thread #runner.
* @param c The #cell.
* @param timer Are we timing this?
*/
void runner_do_gas_swallow_self(struct runner *r, struct cell *c, int timer) {
#ifdef SWIFT_DEBUG_CHECKS
if (c->nodeID != r->e->nodeID) error("Running self task on foreign node");
if (!cell_is_active_black_holes(c, r->e))
error("Running self task on inactive cell");
#endif
runner_do_gas_swallow(r, c, timer);
}
/**
* @brief Processing of gas particles to swallow - pair task case.
*
* @param r The thread #runner.
* @param ci First #cell.
* @param cj Second #cell.
* @param timer Are we timing this?
*/
void runner_do_gas_swallow_pair(struct runner *r, struct cell *ci,
struct cell *cj, int timer) {
const struct engine *e = r->e;
#ifdef SWIFT_DEBUG_CHECKS
if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID)
error("Running pair task on foreign node");
#endif
/* Run the swallowing loop only in the cell that is the neighbour of the
* active BH */
if (cell_is_active_black_holes(cj, e)) runner_do_gas_swallow(r, ci, timer);
if (cell_is_active_black_holes(ci, e)) runner_do_gas_swallow(r, cj, timer);
}
/**
* @brief Process all the BH particles in a cell that have been flagged for
* swallowing by a black hole.
*
* This is done by recursing down to the leaf-level and skipping the sub-cells
* that have not been drifted as they would not have any particles with
* swallowing flag. We then loop over the particles with a flag and look into
* the space-wide list of black holes for the particle with the corresponding
* ID. If found, the BH swallows the BH particle and the BH particle is
* removed. If the cell is local, we may be looking for a foreign BH, in which
* case, we do not update the BH (that will be done on its node) but just remove
* the BH particle.
*
* @param r The thread #runner.
* @param c The #cell.
* @param timer Are we timing this?
*/
void runner_do_bh_swallow(struct runner *r, struct cell *c, int timer) {
struct engine *e = r->e;
struct space *s = e->s;
const int with_cosmology = (e->policy & engine_policy_cosmology);
const struct black_holes_props *props = e->black_holes_properties;
const int use_nibbling = props->use_nibbling;
struct bpart *bparts = s->bparts;
const size_t nr_bpart = s->nr_bparts;
#ifdef WITH_MPI
struct bpart *bparts_foreign = s->bparts_foreign;
const size_t nr_bparts_foreign = s->nr_bparts_foreign;
#endif
struct bpart *cell_bparts = c->black_holes.parts;
/* Early abort?
* (We only want cells for which we drifted the BH as these are
* the only ones that could have BH particles that have been flagged
* for swallowing) */
if (c->black_holes.count == 0 ||
c->black_holes.ti_old_part != e->ti_current) {
return;
}
/* Loop over the progeny ? */
if (c->split) {
for (int k = 0; k < 8; k++) {
if (c->progeny[k] != NULL) {
struct cell *restrict cp = c->progeny[k];
runner_do_bh_swallow(r, cp, 0);
}
}
} else {
/* Loop over all the BH particles in the cell
* Note that the cell (and hence the bparts) may be local or foreign. */
const size_t nr_cell_bparts = c->black_holes.count;
for (size_t k = 0; k < nr_cell_bparts; k++) {
/* Get a handle on the part. */
struct bpart *const cell_bp = &cell_bparts[k];
/* Ignore inhibited particles (they have already been removed!) */
if (bpart_is_inhibited(cell_bp, e)) continue;
/* Update mass of associated gpart, to reflect potential changes from
* nibbling. */
if (use_nibbling && c->nodeID == e->nodeID) {
cell_bp->gpart->mass = cell_bp->mass;
cell_bp->gpart->v_full[0] = cell_bp->v[0];
cell_bp->gpart->v_full[1] = cell_bp->v[1];
cell_bp->gpart->v_full[2] = cell_bp->v[2];
}
/* Get the ID of the black holes that will swallow this bpart */
const long long swallow_id =
black_holes_get_bpart_swallow_id(&cell_bp->merger_data);
/* Has this particle been flagged for swallowing? */
if (swallow_id >= 0) {
#ifdef SWIFT_DEBUG_CHECKS
if (cell_bp->ti_drift != e->ti_current)
error("Trying to swallow an un-drifted particle.");
#endif
/* ID of the BH swallowing this particle */
const long long BH_id = swallow_id;
/* Have we found this particle's BH already? */
int found = 0;
/* Let's look for the hungry black hole in the local list */
for (size_t i = 0; i < nr_bpart; ++i) {
/* Get a handle on the bpart. */
struct bpart *bp = &bparts[i];
if (bp->id == BH_id) {
/* Is the swallowing BH itself flagged for swallowing by
another BH? */
if (black_holes_get_bpart_swallow_id(&bp->merger_data) != -1) {
/* Pretend it was found and abort */
black_holes_mark_bpart_as_not_swallowed(&cell_bp->merger_data);
found = 1;
break;
}
/* Lock the space as we are going to work directly on the
* space's bpart list */
lock_lock(&s->lock);
/* Swallow the BH particle (i.e. update the swallowing BH
* properties with the properties of cell_bp) */
black_holes_swallow_bpart(bp, cell_bp, e->cosmology, e->time,
with_cosmology, props,
e->physical_constants);
/* Release the space as we are done updating the bpart */
if (lock_unlock(&s->lock) != 0)
error("Failed to unlock the space.");
message("BH %lld swallowing BH particle %lld", bp->id, cell_bp->id);
/* If the BH particle is local, remove it */
if (c->nodeID == e->nodeID) {
message("BH %lld removing BH particle %lld", bp->id, cell_bp->id);
/* Finally, remove the BH particle from the system
* Recall that the gpart associated with it is also removed
* at the same time. */
cell_remove_bpart(e, c, cell_bp);
}
/* In any case, prevent the particle from being re-swallowed */
black_holes_mark_bpart_as_merged(&cell_bp->merger_data);
found = 1;
break;
}
} /* Loop over local BHs */
#ifdef WITH_MPI
/* We could also be in the case of a local BH particle being
* swallowed by a foreign BH. In this case, we won't update the
* foreign BH but just remove the particle from the local list. */
if (c->nodeID == e->nodeID && !found) {
/* Let's look for the foreign hungry black hole */
for (size_t i = 0; i < nr_bparts_foreign; ++i) {
/* Get a handle on the bpart. */
struct bpart *bp = &bparts_foreign[i];
if (bp->id == BH_id) {
/* Is the swallowing BH itself flagged for swallowing by
another BH? */
if (black_holes_get_bpart_swallow_id(&bp->merger_data) != -1) {
/* Pretend it was found and abort */
black_holes_mark_bpart_as_not_swallowed(&cell_bp->merger_data);
found = 1;
break;
}
message("BH %lld removing BH particle %lld (foreign BH case)",
bp->id, cell_bp->id);
/* Finally, remove the gas particle from the system */
cell_remove_bpart(e, c, cell_bp);
found = 1;
break;
}
} /* Loop over foreign BHs */
} /* Is the cell local? */
#endif
/* If we have a local particle, we must have found the BH in one
* of our list of black holes. */
if (c->nodeID == e->nodeID && !found) {
error("BH particle %lld could not find BH %lld to be swallowed",
cell_bp->id, swallow_id);
}
} /* Part was flagged for swallowing */
} /* Loop over the parts */
} /* Cell is not split */
}
/**
* @brief Processing of bh particles to swallow - self task case.
*
* @param r The thread #runner.
* @param c The #cell.
* @param timer Are we timing this?
*/
void runner_do_bh_swallow_self(struct runner *r, struct cell *c, int timer) {
#ifdef SWIFT_DEBUG_CHECKS
if (c->nodeID != r->e->nodeID) error("Running self task on foreign node");
if (!cell_is_active_black_holes(c, r->e))
error("Running self task on inactive cell");
#endif
runner_do_bh_swallow(r, c, timer);
}
/**
* @brief Processing of bh particles to swallow - pair task case.
*
* @param r The thread #runner.
* @param ci First #cell.
* @param cj Second #cell.
* @param timer Are we timing this?
*/
void runner_do_bh_swallow_pair(struct runner *r, struct cell *ci,
struct cell *cj, int timer) {
const struct engine *e = r->e;
#ifdef SWIFT_DEBUG_CHECKS
if (ci->nodeID != e->nodeID && cj->nodeID != e->nodeID)
error("Running pair task on foreign node");
#endif
/* Run the swallowing loop only in the cell that is the neighbour of the
* active BH */
if (cell_is_active_black_holes(cj, e)) runner_do_bh_swallow(r, ci, timer);
if (cell_is_active_black_holes(ci, e)) runner_do_bh_swallow(r, cj, timer);
}