/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2018 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_EAGLE_BLACK_HOLES_PROPERTIES_H
#define SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H
/* Config parameters. */
#include
/* Local includes. */
#include "chemistry.h"
#include "exp10.h"
#include "hydro_properties.h"
/* Includes. */
#include
/**
* @brief Modes of energy injection for AGN feedback
*/
enum AGN_feedback_models {
AGN_random_ngb_model, /*< Random neighbour model for AGN feedback */
AGN_isotropic_model, /*< Isotropic model of AGN feedback */
AGN_minimum_distance_model, /*< Minimum-distance model of AGN feedback */
AGN_minimum_density_model /*< Minimum-density model of AGN feedback */
};
enum BH_merger_thresholds {
BH_mergers_circular_velocity, /*< v_circ at separation, as in EAGLE */
BH_mergers_escape_velocity, /*< v_esc at separation */
BH_mergers_dynamical_escape_velocity /*< combined v_esc_dyn at separation */
};
/**
* @brief Properties of black holes and AGN feedback in the EAGEL model.
*/
struct black_holes_props {
/* ----- Basic neighbour search properties ------ */
/*! Resolution parameter */
float eta_neighbours;
/*! Target weightd number of neighbours (for info only)*/
float target_neighbours;
/*! Smoothing length tolerance */
float h_tolerance;
/*! Tolerance on neighbour number (for info only)*/
float delta_neighbours;
/*! Maximal number of iterations to converge h */
int max_smoothing_iterations;
/*! Maximal change of h over one time-step */
float log_max_h_change;
/* ----- Initialisation properties ------ */
/*! Mass of a BH seed at creation time */
float subgrid_seed_mass;
/*! Should we use the subgrid mass specified in ICs? */
int use_subgrid_mass_from_ics;
/*! Should we enforce positive subgrid masses initially? */
int with_subgrid_mass_check;
/* ----- Properties of the accretion model ------ */
/*! Calculate Bondi accretion rate for individual neighbours? */
int use_multi_phase_bondi;
/*! Are we using the subgrid gas properties in the Bondi model? */
int use_subgrid_bondi;
/*! Are we applying the angular-momentum-based multiplicative term from
* Rosas-Guevara et al. (2015)? */
int with_angmom_limiter;
/*! Normalisation of the viscuous angular momentum accretion reduction */
float alpha_visc;
/*! Radiative efficiency of the black holes. */
float epsilon_r;
/*! Maximal fraction of the Eddington rate allowed. */
float f_Edd;
/*! Eddington fraction threshold for recording */
float f_Edd_recording;
/*! Switch for the Booth & Schaye 2009 model */
int with_boost_factor;
/*! Use constant-alpha version of Booth & Schaye (2009) model? */
int boost_alpha_only;
/*! Lowest value of the boost of the Booth & Schaye 2009 model */
float boost_alpha;
/*! Power law slope for the boost of the Booth & Schaye 2009 model */
float boost_beta;
/*! Normalisation density (internal units) for the boost of the Booth & Schaye
* 2009 model */
double boost_n_h_star;
/*! Switch for nibbling mode */
int use_nibbling;
/*! Minimum gas particle mass in nibbling mode */
float min_gas_mass_for_nibbling;
/*! Switch to calculate the sound speed with a fixed T near the EoS */
int with_fixed_T_near_EoS;
/*! Factor above EoS below which fixed T applies for sound speed */
float fixed_T_above_EoS_factor;
/*! Fixed T (expressed as internal energy) for sound speed near EoS */
float fixed_u_for_soundspeed;
/* ---- Properties of the feedback model ------- */
/*! AGN feedback model: random, isotropic or minimum distance */
enum AGN_feedback_models feedback_model;
/*! Is the AGN feedback model deterministic or stochastic? */
int AGN_deterministic;
/*! Feedback coupling efficiency of the black holes. */
float epsilon_f;
/*! (Constant) temperature increase induced by AGN feedback [Kelvin], if we
* use a model with a variable temperature increase than we use this value
* to initialize a BH that just has formed */
float AGN_delta_T_desired;
/*! Switch on adaptive heating temperature scheme? */
int use_variable_delta_T;
/*! If we use variable delta_T, should we scale with local gas properties
* in addition to BH mass? */
int AGN_with_locally_adaptive_delta_T;
/*! Normalisation for dT scaling with BH mass */
float AGN_delta_T_mass_norm;
/*! Reference BH mass for dT scaling [M_Sun] */
float AGN_delta_T_mass_reference;
/*! Exponent for dT scaling with BH mass */
float AGN_delta_T_mass_exponent;
/*! Buffer factor for numerical efficiency temperature */
float AGN_delta_T_crit_factor;
/*! Buffer factor for background temperature */
float AGN_delta_T_background_factor;
/*! Max/min temperature increase induced by AGN feedback [Kelvin] */
float AGN_delta_T_max;
float AGN_delta_T_min;
/*! Vary the energy reservoir according to the BH accretion rate? */
int use_adaptive_energy_reservoir_threshold;
/*! Normalisation for energy reservoir threshold, at upper end */
float nheat_alpha;
/*! Reference max accretion rate for energy reservoir variation */
float nheat_maccr_normalisation;
/*! Hard limit to the energy reservoir threshold */
float nheat_limit;
/*! Number of gas neighbours to heat in a feedback event */
float num_ngbs_to_heat;
/*! Switch to make nheat use the constant dT as basis, not actual dT */
int AGN_use_nheat_with_fixed_dT;
/* ---- Properties of the repositioning model --- */
/*! Maximal mass of BH to reposition */
float max_reposition_mass;
/*! Maximal distance to reposition, in units of softening length */
float max_reposition_distance_ratio;
/*! Switch to enable a relative velocity limit for particles to which the
* black holes can reposition */
int with_reposition_velocity_threshold;
/*! Maximal velocity offset of particles to which the black hole can
* reposition, in units of the ambient sound speed of the black hole */
float max_reposition_velocity_ratio;
/*! Minimum value of the velocity repositioning threshold */
float min_reposition_velocity_threshold;
/*! Switch to enable repositioning at fixed (maximum) speed */
int set_reposition_speed;
/*! Normalisation factor for repositioning velocity */
float reposition_coefficient_upsilon;
/*! Reference black hole mass for repositioning scaling */
float reposition_reference_mass;
/*! Repositioning velocity scaling with black hole mass */
float reposition_exponent_mass;
/*! Reference gas density for repositioning scaling */
float reposition_reference_n_H;
/*! Repositioning velocity scaling with gas density */
float reposition_exponent_n_H;
/*! Correct potential of BH? */
int correct_bh_potential_for_repositioning;
/* ---- Properties of the merger model ---------- */
/*! Mass ratio above which a merger is considered 'minor' */
float minor_merger_threshold;
/*! Mass ratio above which a merger is considered 'major' */
float major_merger_threshold;
/*! Type of merger threshold */
enum BH_merger_thresholds merger_threshold_type;
/*! Maximal distance over which BHs merge, in units of softening length */
float max_merging_distance_ratio;
/* ---- Black hole time-step properties ---------- */
/*! Minimum allowed time-step of BH (internal units) */
float time_step_min;
/* ---- Common conversion factors --------------- */
/*! Conversion factor from temperature to internal energy */
float temp_to_u_factor;
/*! Conversion factor from physical density to n_H [cgs] */
float rho_to_n_cgs;
/*! Conversion factor from internal mass to solar masses */
float mass_to_solar_mass;
};
/**
* @brief Initialise the black hole properties from the parameter file.
*
* For the basic black holes neighbour finding properties we use the
* defaults from the hydro scheme if the users did not provide specific
* values.
*
* @param bp The #black_holes_props.
* @param phys_const The physical constants in the internal unit system.
* @param us The internal unit system.
* @param params The parsed parameters.
* @param hydro_props The already read-in properties of the hydro scheme.
* @param cosmo The cosmological model.
*/
INLINE static void black_holes_props_init(struct black_holes_props *bp,
const struct phys_const *phys_const,
const struct unit_system *us,
struct swift_params *params,
const struct hydro_props *hydro_props,
const struct cosmology *cosmo) {
/* Calculate temperature to internal energy conversion factor (all internal
* units) */
const double k_B = phys_const->const_boltzmann_k;
const double m_p = phys_const->const_proton_mass;
const double mu = hydro_props->mu_ionised;
bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p);
/* Read in the basic neighbour search properties or default to the hydro
ones if the user did not provide any different values */
/* Kernel properties */
bp->eta_neighbours = parser_get_opt_param_float(
params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours);
/* Tolerance for the smoothing length Newton-Raphson scheme */
bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance",
hydro_props->h_tolerance);
/* Get derived properties */
bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm;
const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance);
bp->delta_neighbours =
(pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) *
kernel_norm;
/* Number of iterations to converge h */
bp->max_smoothing_iterations =
parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations",
hydro_props->max_smoothing_iterations);
/* Time integration properties */
const float max_volume_change =
parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1);
if (max_volume_change == -1)
bp->log_max_h_change = hydro_props->log_max_h_change;
else
bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv));
/* Initialisation properties ---------------------------- */
bp->subgrid_seed_mass =
parser_get_param_float(params, "EAGLEAGN:subgrid_seed_mass_Msun");
/* Convert to internal units */
bp->subgrid_seed_mass *= phys_const->const_solar_mass;
bp->use_subgrid_mass_from_ics =
parser_get_opt_param_int(params, "EAGLEAGN:use_subgrid_mass_from_ics", 1);
if (bp->use_subgrid_mass_from_ics)
bp->with_subgrid_mass_check =
parser_get_opt_param_int(params, "EAGLEAGN:with_subgrid_mass_check", 1);
/* Accretion parameters ---------------------------------- */
bp->use_multi_phase_bondi =
parser_get_param_int(params, "EAGLEAGN:use_multi_phase_bondi");
bp->use_subgrid_bondi =
parser_get_param_int(params, "EAGLEAGN:use_subgrid_bondi");
if (bp->use_multi_phase_bondi && bp->use_subgrid_bondi)
error(
"Cannot run with both the multi-phase Bondi and subgrid Bondi models "
"at the same time!");
/* Rosas-Guevara et al. (2015) model */
bp->with_angmom_limiter =
parser_get_param_int(params, "EAGLEAGN:with_angmom_limiter");
if (bp->with_angmom_limiter)
bp->alpha_visc = parser_get_param_float(params, "EAGLEAGN:viscous_alpha");
bp->epsilon_r =
parser_get_param_float(params, "EAGLEAGN:radiative_efficiency");
if (bp->epsilon_r > 1.f)
error("EAGLEAGN:radiative_efficiency must be <= 1, not %f.", bp->epsilon_r);
bp->f_Edd = parser_get_param_float(params, "EAGLEAGN:max_eddington_fraction");
bp->f_Edd_recording = parser_get_param_float(
params, "EAGLEAGN:eddington_fraction_for_recording");
/* Booth & Schaye (2009) Parameters */
bp->with_boost_factor =
parser_get_param_int(params, "EAGLEAGN:with_boost_factor");
if (bp->with_boost_factor) {
bp->boost_alpha_only =
parser_get_param_int(params, "EAGLEAGN:boost_alpha_only");
bp->boost_alpha = parser_get_param_float(params, "EAGLEAGN:boost_alpha");
if (!bp->boost_alpha_only) {
bp->boost_beta = parser_get_param_float(params, "EAGLEAGN:boost_beta");
/* Load the density in cgs and convert to internal units */
bp->boost_n_h_star =
parser_get_param_float(params, "EAGLEAGN:boost_n_h_star_H_p_cm3") /
units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY);
}
}
bp->use_nibbling = parser_get_param_int(params, "EAGLEAGN:use_nibbling");
if (bp->use_nibbling) {
bp->min_gas_mass_for_nibbling = parser_get_param_float(
params, "EAGLEAGN:min_gas_mass_for_nibbling_Msun");
bp->min_gas_mass_for_nibbling *= phys_const->const_solar_mass;
if ((bp->min_gas_mass_for_nibbling < 1e-5 * bp->subgrid_seed_mass) ||
(bp->min_gas_mass_for_nibbling > 1e5 * bp->subgrid_seed_mass)) {
error(
"The BH seeding mass and minimal gas mass for nibbling differ by "
"more "
"than 10^5. That is probably indicating a typo in the parameter "
"file.");
}
}
bp->with_fixed_T_near_EoS =
parser_get_param_int(params, "EAGLEAGN:with_fixed_T_near_EoS");
if (bp->with_fixed_T_near_EoS) {
bp->fixed_T_above_EoS_factor =
exp10(parser_get_param_float(params, "EAGLEAGN:fixed_T_above_EoS_dex"));
bp->fixed_u_for_soundspeed =
parser_get_param_float(params, "EAGLEAGN:fixed_T_near_EoS_K") /
units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
bp->fixed_u_for_soundspeed *= bp->temp_to_u_factor;
}
/* Feedback parameters ---------------------------------- */
char temp[40];
parser_get_param_string(params, "EAGLEAGN:AGN_feedback_model", temp);
if (strcmp(temp, "Random") == 0)
bp->feedback_model = AGN_random_ngb_model;
else if (strcmp(temp, "Isotropic") == 0)
bp->feedback_model = AGN_isotropic_model;
else if (strcmp(temp, "MinimumDistance") == 0)
bp->feedback_model = AGN_minimum_distance_model;
else if (strcmp(temp, "MinimumDensity") == 0)
bp->feedback_model = AGN_minimum_density_model;
else
error(
"The AGN feedback model must be either 'Random', 'MinimumDistance', "
"'MinimumDensity' or 'Isotropic', not %s",
temp);
bp->AGN_deterministic =
parser_get_param_int(params, "EAGLEAGN:AGN_use_deterministic_feedback");
bp->epsilon_f =
parser_get_param_float(params, "EAGLEAGN:coupling_efficiency");
const double T_K_to_int =
1. / units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
/* Read the constant AGN heating temperature or the the initial value
* for the IC or new BH that formed from gas */
bp->AGN_delta_T_desired =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_K");
/* Read the properties of the variable heating temperature model */
bp->use_variable_delta_T =
parser_get_param_int(params, "EAGLEAGN:use_variable_delta_T");
if (bp->use_variable_delta_T) {
bp->AGN_delta_T_mass_norm =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_mass_norm") *
T_K_to_int;
bp->AGN_delta_T_mass_reference =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_mass_reference") *
phys_const->const_solar_mass;
bp->AGN_delta_T_mass_exponent =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_mass_exponent");
bp->AGN_with_locally_adaptive_delta_T = parser_get_param_int(
params, "EAGLEAGN:AGN_with_locally_adaptive_delta_T");
if (bp->AGN_with_locally_adaptive_delta_T) {
bp->AGN_delta_T_crit_factor =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_crit_factor");
bp->AGN_delta_T_background_factor = parser_get_param_float(
params, "EAGLEAGN:AGN_delta_T_background_factor");
}
bp->AGN_delta_T_max =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_max") * T_K_to_int;
bp->AGN_delta_T_min =
parser_get_param_float(params, "EAGLEAGN:AGN_delta_T_min") * T_K_to_int;
bp->AGN_use_nheat_with_fixed_dT =
parser_get_param_int(params, "EAGLEAGN:AGN_use_nheat_with_fixed_dT");
}
bp->use_adaptive_energy_reservoir_threshold = parser_get_param_int(
params, "EAGLEAGN:AGN_use_adaptive_energy_reservoir_threshold");
if (bp->use_adaptive_energy_reservoir_threshold) {
bp->nheat_alpha =
parser_get_param_float(params, "EAGLEAGN:AGN_nheat_alpha");
bp->nheat_maccr_normalisation =
parser_get_param_float(params,
"EAGLEAGN:AGN_nheat_maccr_normalisation") *
phys_const->const_solar_mass / phys_const->const_year;
bp->nheat_limit =
parser_get_param_float(params, "EAGLEAGN:AGN_nheat_limit");
}
/* We must always read a default value to initialize BHs to */
bp->num_ngbs_to_heat =
parser_get_param_float(params, "EAGLEAGN:AGN_num_ngb_to_heat");
/* Reposition parameters --------------------------------- */
bp->max_reposition_mass =
parser_get_param_float(params, "EAGLEAGN:max_reposition_mass") *
phys_const->const_solar_mass;
bp->max_reposition_distance_ratio =
parser_get_param_float(params, "EAGLEAGN:max_reposition_distance_ratio");
bp->with_reposition_velocity_threshold = parser_get_param_int(
params, "EAGLEAGN:with_reposition_velocity_threshold");
if (bp->with_reposition_velocity_threshold) {
bp->max_reposition_velocity_ratio = parser_get_param_float(
params, "EAGLEAGN:max_reposition_velocity_ratio");
/* Prevent nonsensical input */
if (bp->max_reposition_velocity_ratio <= 0)
error("max_reposition_velocity_ratio must be positive, not %f.",
bp->max_reposition_velocity_ratio);
bp->min_reposition_velocity_threshold = parser_get_param_float(
params, "EAGLEAGN:min_reposition_velocity_threshold");
/* Convert from km/s to internal units */
bp->min_reposition_velocity_threshold *=
(1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs));
}
bp->set_reposition_speed =
parser_get_param_int(params, "EAGLEAGN:set_reposition_speed");
if (bp->set_reposition_speed) {
bp->reposition_coefficient_upsilon = parser_get_param_float(
params, "EAGLEAGN:reposition_coefficient_upsilon");
/* Prevent the user from making silly wishes */
if (bp->reposition_coefficient_upsilon <= 0)
error(
"reposition_coefficient_upsilon must be positive, not %f "
"km/s/M_sun.",
bp->reposition_coefficient_upsilon);
/* Convert from km/s to internal units */
bp->reposition_coefficient_upsilon *=
(1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs));
/* Scaling parameters with BH mass and gas density */
bp->reposition_reference_mass =
parser_get_param_float(params, "EAGLEAGN:reposition_reference_mass") *
phys_const->const_solar_mass;
bp->reposition_exponent_mass = parser_get_opt_param_float(
params, "EAGLEAGN:reposition_exponent_mass", 2.0);
bp->reposition_reference_n_H =
parser_get_param_float(params, "EAGLEAGN:reposition_reference_n_H");
bp->reposition_exponent_n_H = parser_get_opt_param_float(
params, "EAGLEAGN:reposition_exponent_n_H", 1.0);
}
bp->correct_bh_potential_for_repositioning =
parser_get_param_int(params, "EAGLEAGN:with_potential_correction");
/* Merger parameters ------------------------------------- */
bp->minor_merger_threshold =
parser_get_param_float(params, "EAGLEAGN:threshold_minor_merger");
bp->major_merger_threshold =
parser_get_param_float(params, "EAGLEAGN:threshold_major_merger");
char temp2[40];
parser_get_param_string(params, "EAGLEAGN:merger_threshold_type", temp2);
if (strcmp(temp2, "CircularVelocity") == 0)
bp->merger_threshold_type = BH_mergers_circular_velocity;
else if (strcmp(temp2, "EscapeVelocity") == 0)
bp->merger_threshold_type = BH_mergers_escape_velocity;
else if (strcmp(temp2, "DynamicalEscapeVelocity") == 0)
bp->merger_threshold_type = BH_mergers_dynamical_escape_velocity;
else
error(
"The BH merger model must be either CircularVelocity, EscapeVelocity, "
"or DynamicalEscapeVelocity, not %s",
temp2);
bp->max_merging_distance_ratio =
parser_get_param_float(params, "EAGLEAGN:merger_max_distance_ratio");
/* ---- Black hole time-step properties ------------------ */
const double Myr_in_cgs = 1e6 * 365.25 * 24. * 60. * 60.;
const double time_step_min_Myr = parser_get_opt_param_float(
params, "EAGLEAGN:minimum_timestep_Myr", FLT_MAX);
bp->time_step_min = time_step_min_Myr * Myr_in_cgs /
units_cgs_conversion_factor(us, UNIT_CONV_TIME);
/* Common conversion factors ----------------------------- */
/* Calculate conversion factor from rho to n_H.
* Note this assumes primoridal abundance */
const double X_H = hydro_props->hydrogen_mass_fraction;
bp->rho_to_n_cgs =
(X_H / m_p) * units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY);
/* Conversion factor for internal mass to M_solar */
bp->mass_to_solar_mass = 1. / phys_const->const_solar_mass;
}
/**
* @brief Write a black_holes_props struct to the given FILE as a stream of
* bytes.
*
* @param props the black hole properties struct
* @param stream the file stream
*/
INLINE static void black_holes_struct_dump(
const struct black_holes_props *props, FILE *stream) {
restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1,
stream, "black_holes props", "black holes props");
}
/**
* @brief Restore a black_holes_props struct from the given FILE as a stream of
* bytes.
*
* @param props the black hole properties struct
* @param stream the file stream
*/
INLINE static void black_holes_struct_restore(
const struct black_holes_props *props, FILE *stream) {
restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1,
stream, NULL, "black holes props");
}
#endif /* SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H */