/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@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 .
*
******************************************************************************/
#ifndef SWIFT_TRACERS_COLIBRE_H
#define SWIFT_TRACERS_COLIBRE_H
/* Config parameters. */
#include
/* Local includes */
#include "black_holes.h"
#include "cooling.h"
#include "part.h"
#include "star_formation.h"
#include "tracers_struct.h"
/**
* @brief Update the particle tracers just after it has been initialised at the
* start of a step.
*
* Nothing to do here in the COLIBRE model.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The current cosmological model.
* @param hydro_props the hydro_props struct
* @param cooling The #cooling_function_data used in the run.
* @param time The current time.
*/
static INLINE void tracers_after_init(
const struct part *p, struct xpart *xp, const struct unit_system *us,
const struct phys_const *phys_const, const int with_cosmology,
const struct cosmology *cosmo, const struct hydro_props *hydro_props,
const struct cooling_function_data *cooling, const double time) {}
/**
* @brief Update the particle tracers just after it has been drifted.
*
* Nothing to do here in the COLIBRE model.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The current cosmological model.
* @param hydro_props the hydro_props struct
* @param cooling The #cooling_function_data used in the run.
* @param time The current time.
*/
static INLINE void tracers_after_drift(
const struct part *p, struct xpart *xp, const struct unit_system *us,
const struct phys_const *phys_const, const int with_cosmology,
const struct cosmology *cosmo, const struct hydro_props *hydro_props,
const struct cooling_function_data *cooling, const double time) {}
/**
* @brief Update the particle tracers just after its time-step has been
* computed.
*
* In COLIBRE we record the highest temperature reached.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The current cosmological model.
* @param hydro_props the hydro_props struct
* @param cooling The #cooling_function_data used in the run.
* @param time The current time.
*/
static INLINE void tracers_after_timestep_part(
const struct part *p, struct xpart *xp, const struct unit_system *us,
const struct phys_const *phys_const, const int with_cosmology,
const struct cosmology *cosmo, const struct hydro_props *hydro_props,
const struct cooling_function_data *cooling,
const struct entropy_floor_properties *floor_props, const double time,
const double time_step_length, const int *const tracers_triggers_started) {
/* Current temperature */
const float temperature = cooling_get_temperature(phys_const, hydro_props, us,
cosmo, cooling, p, xp);
/* New record? */
if (temperature > xp->tracers_data.maximum_temperature) {
/* Throw an error if there is a particle(s) with a too high T */
const double T_extreme_cgs = 1e12;
const double T_extreme = T_extreme_cgs / us->UnitTemperature_in_cgs;
if (temperature > T_extreme)
error("High temperature! [T=%.5e, pj->id=%lld, pj->h=%.5e, pj->rho=%.5e]",
temperature, p->id, p->h, p->rho);
xp->tracers_data.maximum_temperature = temperature;
if (with_cosmology) {
xp->tracers_data.maximum_temperature_scale_factor = cosmo->a;
} else {
xp->tracers_data.maximum_temperature_time = time;
}
}
/* New h record? */
if (p->h < xp->tracers_data.min_h) {
xp->tracers_data.min_h = p->h;
if (with_cosmology) {
xp->tracers_data.min_h_scale_factor = cosmo->a;
} else {
xp->tracers_data.min_h_time = time;
}
}
/* Check for last time in ISM
* Gas must be above a set over-density AND
* (be an HII region OR have a atomic + molecular fraction < 0.5) */
if (p->rho > 100.f * cosmo->mean_density_Omega_m) {
const float fHI = cooling_get_particle_subgrid_HI_fraction(
us, phys_const, cosmo, hydro_props, floor_props, cooling, p, xp);
const float fH2 = cooling_get_particle_subgrid_H2_fraction(
us, phys_const, cosmo, hydro_props, floor_props, cooling, p, xp);
const int is_HII_region = xp->tracers_data.HIIregion_timer_gas > 0.;
if (fHI + 2.f * fH2 > 0.5f || is_HII_region) {
if (with_cosmology) {
xp->tracers_data.last_ISM_scale_factor = cosmo->a;
} else {
xp->tracers_data.last_ISM_time = time;
}
}
}
/* Accumulate average SFR */
for (int i = 0; i < num_snapshot_triggers_part; ++i) {
if (tracers_triggers_started[i])
xp->tracers_data.averaged_SFR[i] +=
star_formation_get_SFR(p, xp) * time_step_length;
}
/* Current particle volume, convert to physical frame */
const float volume = (p->mass / p->rho) * cosmo->a * cosmo->a * cosmo->a;
/* Rate at which particle is converting its kinetic energy to internal
* energy, on account of shocks (artificial viscosity). Also convert to
* total across particle (not per unit mass) and in physical frame. */
const float shocking_rate = hydro_get_comoving_viscous_internal_energy_dt(p) *
p->mass * cosmo->a2_inv;
/* CMB energy density (in physical frame and in internal units) */
const float CMB_energy_density =
(4.f / phys_const->const_speed_light_c) *
phys_const->const_stefan_boltzmann * phys_const->const_T_CMB_0 *
phys_const->const_T_CMB_0 * phys_const->const_T_CMB_0 *
phys_const->const_T_CMB_0;
/* Slope of the distribution of number of electrons versus Lorentz factor.
* This is assuming that shocks where they are injected are relativistic
* and strong (Mach number > 10) */
const float electron_slope = 2.2f;
/* Adiabatic indeces of the relativistic electrons and (tangled) magnetic
* fields */
const float electron_adiabatic_index = 4.f / 3.f;
const float magnetic_adiabatic_index = 4.f / 3.f;
/* Combination of above numbers that will appear often */
const float xi = (1.f - electron_slope) * (electron_adiabatic_index - 1.f);
/* Increment all quantities we need for radio modeling. These are only
* incremented if 'last_jet_kick_initial_volume' is > 0 because this will
* only be true if this particle has been kicked before. The other condition
* is that the shocking rate is larger than 0; otherwise no injection
* occurs. */
const float initial_volume =
xp->tracers_data.jet_radio_emission.last_jet_kick_initial_volume;
if (initial_volume > 0.f && shocking_rate > 0.f) {
/* Ratio of volumes the particle has now and had when it was kicked. */
const float volume_ratio = volume / initial_volume;
/* Various integrals over shocking rate (modulo volume ratio factors raised
* to different powers) */
xp->tracers_data.jet_radio_emission.shocking_integrals[0] +=
shocking_rate * powf(volume_ratio, -xi) * time_step_length;
xp->tracers_data.jet_radio_emission.shocking_integrals[1] +=
shocking_rate * powf(volume_ratio, -(magnetic_adiabatic_index - 1.f)) *
time_step_length;
xp->tracers_data.jet_radio_emission.shocking_integrals[2] +=
shocking_rate *
powf(volume_ratio, -xi - (electron_adiabatic_index - 1.f)) *
time_step_length;
/* Integral used to compute when most of the shocking ocurred */
xp->tracers_data.jet_radio_emission.weighted_injection_scale_factor +=
shocking_rate * cosmo->a *
powf(volume_ratio, -xi - (electron_adiabatic_index - 1.f)) *
time_step_length;
/* Integrals used to compute the minimum Lorentz factors gamma of the
electron distribution */
xp->tracers_data.jet_radio_emission.gamma_integrals[0] +=
(xp->tracers_data.jet_radio_emission.shocking_integrals[1] / volume) *
time_step_length;
xp->tracers_data.jet_radio_emission.gamma_integrals[1] +=
powf(volume_ratio, (electron_adiabatic_index - 1.f)) *
CMB_energy_density * time_step_length;
/* Integrals used to compute the effective minimum Lorentz factors gamma
of the electron distribution */
xp->tracers_data.jet_radio_emission.gamma_effective_integrals[0] +=
shocking_rate * xp->tracers_data.jet_radio_emission.gamma_integrals[0] *
powf(volume_ratio, -xi) * time_step_length;
xp->tracers_data.jet_radio_emission.gamma_effective_integrals[1] +=
shocking_rate * xp->tracers_data.jet_radio_emission.gamma_integrals[1] *
powf(volume_ratio, -xi) * time_step_length;
}
}
/**
* @brief Update the star particle tracers just after its time-step has been
* computed.
*
* In EAGLE, nothing to do.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The current cosmological model.
* @param time_step_length The length of the step that just finished
* @param tracers_triggers_started Which triggers have started? (array of size
* num_snapshot_triggers_spart)
*/
static INLINE void tracers_after_timestep_spart(
struct spart *sp, const struct unit_system *us,
const struct phys_const *phys_const, const int with_cosmology,
const struct cosmology *cosmo, const double time_step_length,
const int *const tracers_triggers_started) {}
/**
* @brief Update the black hole particle tracers just after its time-step has
* been computed.
*
* In EAGLE, we record the average accr. rate.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The current cosmological model.
* @param time_step_length The length of the step that just finished
* @param tracers_triggers_started Which triggers have started? (array of size
* num_snapshot_triggers_bpart)
*/
static INLINE void tracers_after_timestep_bpart(
struct bpart *bp, const struct unit_system *us,
const struct phys_const *phys_const, const int with_cosmology,
const struct cosmology *cosmo, const double time_step_length,
const int *const tracers_triggers_started) {
const float accr_rate = black_holes_get_accretion_rate(bp);
/* Accumulate average accretion rate */
for (int i = 0; i < num_snapshot_triggers_part; ++i) {
if (tracers_triggers_started[i])
bp->tracers_data.averaged_accretion_rate[i] +=
accr_rate * time_step_length;
}
if (bp->time_bin < bp->tracers_data.min_time_bin) {
bp->tracers_data.min_time_bin = bp->time_bin;
if (with_cosmology) {
bp->tracers_data.min_time_bin_scale_factor = cosmo->a;
} else {
bp->tracers_data.min_time_bin_time = cosmo->time;
}
}
}
/**
* @brief Update the particle tracers just after its time-step has been
* computed.
*
* Set the maximal temperature to a valid initial state
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param cosmo The current cosmological model.
* @param hydro_props the hydro_props struct
* @param cooling The #cooling_function_data used in the run.
*/
static INLINE void tracers_first_init_xpart(
const struct part *p, struct xpart *xp, const struct unit_system *us,
const struct phys_const *phys_const, const struct cosmology *cosmo,
const struct hydro_props *hydro_props,
const struct cooling_function_data *cooling) {
xp->tracers_data.maximum_temperature = -1.f;
xp->tracers_data.maximum_temperature_scale_factor = -1.f;
xp->tracers_data.stellar_wind_momentum_received = 0.f;
xp->tracers_data.HIIregion_timer_gas = -1.f;
xp->tracers_data.HIIregion_starid = -1;
xp->tracers_data.last_stellar_wind_kick_scale_factor = -1.f;
xp->tracers_data.last_SNII_thermal_injection_scale_factor = -1.f;
xp->tracers_data.last_SNII_kick_scale_factor = -1.f;
xp->tracers_data.last_SNIa_thermal_injection_scale_factor = -1.f;
xp->tracers_data.last_AGN_injection_scale_factor = -1.f;
xp->tracers_data.last_SNII_kick_velocity = -1.f;
xp->tracers_data.max_SNII_kick_velocity = -1.f;
xp->tracers_data.density_at_last_SN_thermal_feedback_event = -1.f;
xp->tracers_data.density_at_last_AGN_feedback_event = -1.f;
xp->tracers_data.AGN_feedback_energy = 0.f;
xp->tracers_data.last_AGN_feedback_energy = 0.f;
xp->tracers_data.hit_by_jet_feedback = 0;
xp->tracers_data.jet_feedback_energy = 0.f;
xp->tracers_data.last_AGN_jet_feedback_scale_factor = 0.f;
xp->tracers_data.last_AGN_jet_feedback_time = 0.f;
xp->tracers_data.last_jet_kick_velocity = 0.f;
xp->tracers_data.last_jet_kick_accretion_mode = BH_thick_disc;
xp->tracers_data.last_jet_kick_BH_id = 0;
xp->tracers_data.jet_radio_emission.last_jet_kick_initial_volume = 0.f;
for (int i = 0; i < 3; ++i)
xp->tracers_data.jet_radio_emission.shocking_integrals[i] = 0.f;
for (int i = 0; i < 2; ++i)
xp->tracers_data.jet_radio_emission.gamma_integrals[i] = 0.f;
for (int i = 0; i < 2; ++i)
xp->tracers_data.jet_radio_emission.gamma_effective_integrals[i] = 0.f;
xp->tracers_data.jet_radio_emission.weighted_injection_scale_factor = 0.f;
xp->tracers_data.delta_T_at_last_SN_thermal_feedback_event = -1.f;
xp->tracers_data.delta_T_at_last_AGN_feedback_event = -1.f;
xp->tracers_data.last_ISM_scale_factor = -1.f;
xp->tracers_data.min_h = FLT_MAX;
xp->tracers_data.min_h_scale_factor = -1.f;
xp->tracers_data.last_halo_mass = -1.f;
xp->tracers_data.last_in_halo_scale_factor = -1.f;
}
/**
* @brief Initialise the star tracer data at the start of a calculation.
*
* In EAGLE, nothing to do.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param cosmo The current cosmological model.
*/
static INLINE void tracers_first_init_spart(struct spart *sp,
const struct unit_system *us,
const struct phys_const *phys_const,
const struct cosmology *cosmo) {}
/**
* @brief Initialise the black hole tracer data at the start of a calculation.
*
* @param p Pointer to the particle data.
* @param xp Pointer to the extended particle data (containing the tracers
* struct).
* @param us The internal system of units.
* @param phys_const The physical constants in internal units.
* @param cosmo The current cosmological model.
*/
static INLINE void tracers_first_init_bpart(struct bpart *bp,
const struct unit_system *us,
const struct phys_const *phys_const,
const struct cosmology *cosmo) {
for (int i = 0; i < num_snapshot_triggers_bpart; ++i)
bp->tracers_data.averaged_accretion_rate[i] = 0.f;
bp->tracers_data.min_time_bin = num_time_bins;
bp->tracers_data.min_time_bin_scale_factor = -1.f;
}
static INLINE void tracers_after_SNII_thermal_feedback(
const struct part *p, struct xpart *xp, const int with_cosmology,
const float scale_factor, const double time, const float delta_T) {
if (with_cosmology)
xp->tracers_data.last_SNII_thermal_injection_scale_factor = scale_factor;
else
xp->tracers_data.last_SNII_thermal_injection_time = time;
xp->tracers_data.density_at_last_SN_thermal_feedback_event =
hydro_get_comoving_density(p) /
(scale_factor * scale_factor * scale_factor);
xp->tracers_data.delta_T_at_last_SN_thermal_feedback_event = delta_T;
}
static INLINE void tracers_after_SNII_kinetic_feedback(struct xpart *xp,
const int with_cosmology,
const float scale_factor,
const double time,
const float v_kick) {
if (with_cosmology)
xp->tracers_data.last_SNII_kick_scale_factor = scale_factor;
else
xp->tracers_data.last_SNII_kick_time = time;
xp->tracers_data.last_SNII_kick_velocity = v_kick;
if (v_kick > xp->tracers_data.max_SNII_kick_velocity) {
xp->tracers_data.max_SNII_kick_velocity = v_kick;
}
}
static INLINE void tracers_after_SNIa_thermal_feedback(
const struct part *p, struct xpart *xp, const int with_cosmology,
const float scale_factor, const double time, const float delta_T) {
if (with_cosmology)
xp->tracers_data.last_SNIa_thermal_injection_scale_factor = scale_factor;
else
xp->tracers_data.last_SNIa_thermal_injection_time = time;
xp->tracers_data.density_at_last_SN_thermal_feedback_event =
hydro_get_comoving_density(p) /
(scale_factor * scale_factor * scale_factor);
xp->tracers_data.delta_T_at_last_SN_thermal_feedback_event = delta_T;
}
static INLINE void tracers_after_black_holes_feedback(
const struct part *p, struct xpart *xp, const int with_cosmology,
const float scale_factor, const double time, const double delta_energy,
const float delta_T) {
if (with_cosmology)
xp->tracers_data.last_AGN_injection_scale_factor = scale_factor;
else
xp->tracers_data.last_AGN_injection_time = time;
xp->tracers_data.density_at_last_AGN_feedback_event =
hydro_get_comoving_density(p) /
(scale_factor * scale_factor * scale_factor);
xp->tracers_data.AGN_feedback_energy += delta_energy;
xp->tracers_data.last_AGN_feedback_energy = delta_energy;
xp->tracers_data.delta_T_at_last_AGN_feedback_event = delta_T;
}
static INLINE void tracers_after_jet_feedback(
const struct part *p, struct xpart *xp, const int with_cosmology,
const float scale_factor, const double time, const double delta_energy,
const double vel_kick, const enum BH_accretion_modes accretion_mode,
const long long id) {
if (with_cosmology)
xp->tracers_data.last_AGN_jet_feedback_scale_factor = scale_factor;
else
xp->tracers_data.last_AGN_jet_feedback_time = time;
xp->tracers_data.hit_by_jet_feedback++;
xp->tracers_data.jet_feedback_energy += delta_energy;
xp->tracers_data.last_jet_kick_velocity = vel_kick;
xp->tracers_data.last_jet_kick_accretion_mode = accretion_mode;
xp->tracers_data.last_jet_kick_BH_id = id;
/* Volume of this particle at time of kick (physical frame) */
xp->tracers_data.jet_radio_emission.last_jet_kick_initial_volume =
(p->mass / p->rho) * scale_factor * scale_factor * scale_factor;
}
static INLINE void tracers_after_stellar_wind_feedback(struct xpart *xp,
const int with_cosmology,
const float scale_factor,
const double time) {
if (with_cosmology)
xp->tracers_data.last_stellar_wind_kick_scale_factor = scale_factor;
else
xp->tracers_data.last_stellar_wind_kick_time = time;
}
/**
* @brief Tracer event called after a snapshot was written.
*
* @param p the #part.
* @param xp the #xpart.
*/
static INLINE void tracers_after_snapshot_part(const struct part *p,
struct xpart *xp) {
for (int i = 0; i < num_snapshot_triggers_part; ++i)
xp->tracers_data.averaged_SFR[i] = 0.f;
}
/**
* @brief Tracer event called after a snapshot was written.
*
* @param sp the #spart.
*/
static INLINE void tracers_after_snapshot_spart(struct spart *sp) {
for (int i = 0; i < num_snapshot_triggers_part; ++i)
sp->tracers_data.averaged_SFR[i] = 0.f;
}
/**
* @brief Tracer event called after a snapshot was written.
*
* @param bp the #bpart.
*/
static INLINE void tracers_after_snapshot_bpart(struct bpart *bp) {
for (int i = 0; i < num_snapshot_triggers_bpart; ++i)
bp->tracers_data.averaged_accretion_rate[i] = 0.f;
}
/**
* @brief Tracer event called after a fof call.
*
* @param xp the #xpart.
* @param fof_mass the mass of the FOF group this particle belongs to.
* @param with_cosmology Are we running with comoving integration?
* @param scale_factore The current scale-factor.
* @param time The current time.
*/
static INLINE void tracers_after_fof_part(struct xpart *xp,
const float fof_mass,
const int with_cosmology,
const float scale_factor,
const double time) {
xp->tracers_data.last_halo_mass = fof_mass;
if (with_cosmology)
xp->tracers_data.last_in_halo_scale_factor = scale_factor;
else
xp->tracers_data.last_in_halo_time = time;
}
/**
* @brief Split the tracer content of a particle into n pieces
*
* @param p The #part.
* @param xp The #xpart.
* @param n The number of pieces to split into.
*/
__attribute__((always_inline)) INLINE static void tracers_split_part(
struct part *p, struct xpart *xp, const double n) {
xp->tracers_data.AGN_feedback_energy /= n;
xp->tracers_data.stellar_wind_momentum_received /= n;
}
#endif /* SWIFT_TRACERS_COLIBRE_H */