/******************************************************************************* * 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 */