/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
*
* 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 .
*
******************************************************************************/
/* Include header */
#include "stellar_evolution.h"
/* Include local headers */
#include "hdf5_functions.h"
#include "initial_mass_function.h"
#include "lifetime.h"
#include "random.h"
#include "stellar_evolution_struct.h"
#include "supernovae_ia.h"
#include "supernovae_ii.h"
#include
#include
/**
* @brief Print the stellar model.
*
* @param sm The #stellar_model.
*/
void stellar_model_print(const struct stellar_model* sm) {
/* Only the master print */
if (engine_rank != 0) {
return;
}
/* Print the type of yields */
message("Discrete yields? %i", sm->discrete_yields);
/* Print the sub properties */
initial_mass_function_print(&sm->imf);
lifetime_print(&sm->lifetime);
supernovae_ia_print(&sm->snia);
supernovae_ii_print(&sm->snii);
}
/**
* @brief Compute the integer number of supernovae from the floating number.
*
* @param sp The particle to act upon
* @param number_supernovae_f Floating number of supernovae during this step.
* @param ti_begin The #integertime_t at the begining of the step.
* @param random_type The categorie of random.
*
* @return The integer number of supernovae.
*/
int stellar_evolution_compute_integer_number_supernovae(
struct spart* restrict sp, float number_supernovae_f,
const integertime_t ti_begin, enum random_number_type random_type) {
const int number_supernovae_i = floor(number_supernovae_f);
/* Get the random number for the decimal part */
const float rand_sn = random_unit_interval(sp->id, ti_begin, random_type);
/* Get the fraction part */
const float frac_sn = number_supernovae_f - number_supernovae_i;
/* Get the integer number of SN */
return number_supernovae_i + (rand_sn < frac_sn);
}
/**
* @brief Compute the feedback properties.
*
* @param sp The particle to act upon
* @param sm The #stellar_model structure.
* @param phys_const The physical constants in the internal unit system.
* @param log_m_beg_step Mass of a star ending its life at the begining of the
* step (log10(solMass))
* @param log_m_end_step Mass of a star ending its life at the end of the step
* (log10(solMass))
* @param m_beg_step Mass of a star ending its life at the begining of the step
* (solMass)
* @param m_end_step Mass of a star ending its life at the end of the step
* (solMass)
* @param m_init Birth mass of the stellar particle (solMass).
* @param number_snia_f (Floating) Number of SNIa produced by the stellar
* particle.
* @param number_snii_f (Floating) Number of SNII produced by the stellar
* particle.
*
*/
void stellar_evolution_compute_continuous_feedback_properties(
struct spart* restrict sp, const struct stellar_model* sm,
const struct phys_const* phys_const, const float log_m_beg_step,
const float log_m_end_step, const float m_beg_step, const float m_end_step,
const float m_init, const float number_snia_f, const float number_snii_f) {
/* Compute the mass ejected */
/* SNIa */
const float mass_snia =
supernovae_ia_get_ejected_mass_processed(&sm->snia) * number_snia_f;
/* SNII */
const float mass_frac_snii =
supernovae_ii_get_ejected_mass_fraction_processed_from_integral(
&sm->snii, log_m_end_step, log_m_beg_step);
/* Sum the contributions from SNIa and SNII */
sp->feedback_data.mass_ejected = mass_frac_snii * sp->sf_data.birth_mass +
mass_snia * phys_const->const_solar_mass;
if (sp->mass <= sp->feedback_data.mass_ejected) {
error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g",
sp->mass, sp->feedback_data.mass_ejected, sp->sf_data.birth_mass);
}
/* Update the mass */
sp->mass -= sp->feedback_data.mass_ejected;
/* Now deal with the metals */
/* Get the SNIa yields */
const float* snia_yields = supernovae_ia_get_yields(&sm->snia);
/* Compute the SNII yields */
float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
supernovae_ii_get_yields_from_integral(&sm->snii, log_m_end_step,
log_m_beg_step, snii_yields);
/* Compute the mass fraction of non processed elements */
const float non_processed =
supernovae_ii_get_ejected_mass_fraction_non_processed_from_integral(
&sm->snii, log_m_end_step, log_m_beg_step);
/* Set the yields */
for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
/* Compute the mass fraction of metals */
sp->feedback_data.metal_mass_ejected[i] =
/* Supernovae II yields */
snii_yields[i] +
/* Gas contained in stars initial metallicity */
chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed;
/* Convert it to total mass */
sp->feedback_data.metal_mass_ejected[i] *= sp->sf_data.birth_mass;
/* Add the Supernovae Ia */
sp->feedback_data.metal_mass_ejected[i] +=
snia_yields[i] * number_snia_f * phys_const->const_solar_mass;
}
}
/**
* @brief Compute the feedback properties.
*
* @param sp The particle to act upon
* @param sm The #stellar_model structure.
* @param phys_const The physical constants in the internal unit system.
* @param log_m_beg_step Mass of a star ending its life at the begining of the
* step (log10(solMass))
* @param log_m_end_step Mass of a star ending its life at the end of the step
* (log10(solMass))
* @param m_beg_step Mass of a star ending its life at the begining of the step
* (solMass)
* @param m_end_step Mass of a star ending its life at the end of the step
* (solMass)
* @param m_init Birth mass in solMass.
* @param number_snia Number of SNIa produced by the stellar particle.
* @param number_snii Number of SNII produced by the stellar particle.
*
*/
void stellar_evolution_compute_discrete_feedback_properties(
struct spart* restrict sp, const struct stellar_model* sm,
const struct phys_const* phys_const, const float log_m_beg_step,
const float log_m_end_step, const float m_beg_step, const float m_end_step,
const float m_init, const int number_snia, const int number_snii) {
/* Limit the mass within the imf limits */
const float m_beg_lim = min(m_beg_step, sm->imf.mass_max);
const float m_end_lim = max(m_end_step, sm->imf.mass_min);
/* Compute the average mass */
const float m_avg = 0.5 * (m_beg_lim + m_end_lim);
const float log_m_avg = log10(m_avg);
/* Compute the mass ejected */
/* SNIa */
const float mass_snia =
supernovae_ia_get_ejected_mass_processed(&sm->snia) * number_snia;
/* SNII */
const float mass_snii =
supernovae_ii_get_ejected_mass_fraction_processed_from_raw(&sm->snii,
log_m_avg) *
m_avg * number_snii;
sp->feedback_data.mass_ejected = mass_snia + mass_snii;
/* Transform into internal units */
sp->feedback_data.mass_ejected *= phys_const->const_solar_mass;
if (sp->mass <= sp->feedback_data.mass_ejected) {
error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g",
sp->mass, sp->feedback_data.mass_ejected, sp->sf_data.birth_mass);
}
/* Update the mass */
sp->mass -= sp->feedback_data.mass_ejected;
/* Get the SNIa yields */
const float* snia_yields = supernovae_ia_get_yields(&sm->snia);
/* Compute the SNII yields */
float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
supernovae_ii_get_yields_from_raw(&sm->snii, log_m_avg, snii_yields);
/* Compute the mass fraction of non processed elements */
const float non_processed =
supernovae_ii_get_ejected_mass_fraction_non_processed_from_raw(&sm->snii,
log_m_avg);
/* Set the yields */
for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
/* Compute the mass fraction of metals */
sp->feedback_data.metal_mass_ejected[i] =
/* Supernovae II yields */
snii_yields[i] +
/* Gas contained in stars initial metallicity */
chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed;
/* Convert it to total mass */
sp->feedback_data.metal_mass_ejected[i] *= m_avg * number_snii;
/* Supernovae Ia yields */
sp->feedback_data.metal_mass_ejected[i] += snia_yields[i] * number_snia;
/* Convert everything in code units */
sp->feedback_data.metal_mass_ejected[i] *= phys_const->const_solar_mass;
}
}
/**
* @brief Evolve the stellar properties of a #spart.
*
* This function compute the SN rate and yields before sending
* this information to a different MPI rank.
*
* Here I am using Myr-solar mass units internally in order to
* avoid numerical errors.
*
* @param sp The particle to act upon
* @param sm The #stellar_model structure.
* @param cosmo The current cosmological model.
* @param us The unit system.
* @param phys_const The physical constants in the internal unit system.
* @param ti_begin The #integertime_t at the begining of the step.
* @param star_age_beg_step The age of the star at the star of the time-step in
* internal units.
* @param dt The time-step size of this star in internal units.
*/
void stellar_evolution_evolve_spart(
struct spart* restrict sp, const struct stellar_model* sm,
const struct cosmology* cosmo, const struct unit_system* us,
const struct phys_const* phys_const, const integertime_t ti_begin,
const double star_age_beg_step, const double dt) {
/* Convert the inputs */
const double conversion_to_myr = phys_const->const_year * 1e6;
const double star_age_beg_step_myr = star_age_beg_step / conversion_to_myr;
const double dt_myr = dt / conversion_to_myr;
/* Get the metallicity */
const float metallicity =
chemistry_get_total_metal_mass_fraction_for_feedback(sp);
/* Compute masses range */
const float log_m_beg_step =
star_age_beg_step == 0.
? FLT_MAX
: lifetime_get_log_mass_from_lifetime(
&sm->lifetime, log10(star_age_beg_step_myr), metallicity);
const float log_m_end_step = lifetime_get_log_mass_from_lifetime(
&sm->lifetime, log10(star_age_beg_step_myr + dt_myr), metallicity);
const float m_beg_step =
star_age_beg_step == 0. ? FLT_MAX : exp10(log_m_beg_step);
const float m_end_step = exp10(log_m_end_step);
/* Check if the star can produce a supernovae */
const int can_produce_snia =
supernovae_ia_can_explode(&sm->snia, m_end_step, m_beg_step);
const int can_produce_snii =
supernovae_ii_can_explode(&sm->snii, m_end_step, m_beg_step);
/* Is it possible to generate a supernovae? */
if (!can_produce_snia && !can_produce_snii) return;
/* Compute the initial mass */
const float m_init = sp->sf_data.birth_mass / phys_const->const_solar_mass;
/* Compute number of SNIa */
float number_snia_f = 0;
if (can_produce_snia) {
number_snia_f = supernovae_ia_get_number_per_unit_mass(
&sm->snia, m_end_step, m_beg_step) *
m_init;
}
/* Compute number of SNII */
float number_snii_f = 0;
if (can_produce_snii) {
number_snii_f = supernovae_ii_get_number_per_unit_mass(
&sm->snii, m_end_step, m_beg_step) *
m_init;
}
/* Does this star produce a supernovae? */
if (number_snia_f == 0 && number_snii_f == 0) return;
/* Compute the properties of the feedback (e.g. yields) */
if (sm->discrete_yields) {
/* Get the integer number of supernovae */
const int number_snia = stellar_evolution_compute_integer_number_supernovae(
sp, number_snia_f, ti_begin, random_number_stellar_feedback_1);
/* Get the integer number of supernovae */
const int number_snii = stellar_evolution_compute_integer_number_supernovae(
sp, number_snii_f, ti_begin, random_number_stellar_feedback_2);
/* Do we have a supernovae? */
if (number_snia == 0 && number_snii == 0) return;
/* Save the number of supernovae */
sp->feedback_data.number_sn = number_snia + number_snii;
/* Compute the yields */
stellar_evolution_compute_discrete_feedback_properties(
sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step,
m_end_step, m_init, number_snia, number_snii);
} else {
/* Save the number of supernovae */
sp->feedback_data.number_sn = number_snia_f + number_snii_f;
/* Compute the yields */
stellar_evolution_compute_continuous_feedback_properties(
sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step,
m_end_step, m_init, number_snia_f, number_snii_f);
}
}
/**
* @brief Get the name of the element i.
*
* @param sm The #stellar_model.
* @param i The element indice.
*/
const char* stellar_evolution_get_element_name(const struct stellar_model* sm,
int i) {
return sm->elements_name + i * GEAR_LABELS_SIZE;
}
/**
* @brief Read the name of all the elements present in the tables.
*
* @param sm The #stellar_model.
* @param params The #swift_params.
*/
void stellar_evolution_read_elements(struct stellar_model* sm,
struct swift_params* params) {
/* Read the elements from the parameter file. */
int nval = -1;
char** elements;
parser_get_param_string_array(params, "GEARFeedback:elements", &nval,
&elements);
/* Check that we have the correct number of elements. */
if (nval != GEAR_CHEMISTRY_ELEMENT_COUNT - 1) {
error(
"You need to provide %i elements but found %i. "
"If you wish to provide a different number of elements, "
"you need to compile with --with-chemistry=GEAR_N where N "
"is the number of elements + 1.",
GEAR_CHEMISTRY_ELEMENT_COUNT, nval);
}
/* Copy the elements into the stellar model. */
for (int i = 0; i < nval; i++) {
if (strlen(elements[i]) >= GEAR_LABELS_SIZE) {
error("Element name '%s' too long", elements[i]);
}
strcpy(sm->elements_name + i * GEAR_LABELS_SIZE, elements[i]);
}
/* Cleanup. */
parser_free_param_string_array(nval, elements);
/* Add the metals to the end. */
strcpy(
sm->elements_name + (GEAR_CHEMISTRY_ELEMENT_COUNT - 1) * GEAR_LABELS_SIZE,
"Metals");
}
/**
* @brief Initialize the global properties of the stellar evolution scheme.
*
* @param sm The #stellar_model.
* @param phys_const The physical constants in the internal unit system.
* @param us The internal unit system.
* @param params The parsed parameters.
* @param cosmo The cosmological model.
*/
void stellar_evolution_props_init(struct stellar_model* sm,
const struct phys_const* phys_const,
const struct unit_system* us,
struct swift_params* params,
const struct cosmology* cosmo) {
/* Read the list of elements */
stellar_evolution_read_elements(sm, params);
/* Use the discrete yields approach? */
sm->discrete_yields =
parser_get_param_int(params, "GEARFeedback:discrete_yields");
/* Initialize the initial mass function */
initial_mass_function_init(&sm->imf, phys_const, us, params,
sm->yields_table);
/* Initialize the lifetime model */
lifetime_init(&sm->lifetime, phys_const, us, params, sm->yields_table);
/* Initialize the supernovae Ia model */
supernovae_ia_init(&sm->snia, phys_const, us, params, sm);
/* Initialize the supernovae II model */
supernovae_ii_init(&sm->snii, params, sm);
}
/**
* @brief Write a stellar_evolution struct to the given FILE as a stream of
* bytes.
*
* Here we are only writing the arrays, everything has been copied in the
* feedback.
*
* @param sm the struct
* @param stream the file stream
*/
void stellar_evolution_dump(const struct stellar_model* sm, FILE* stream) {
/* Dump the initial mass function */
initial_mass_function_dump(&sm->imf, stream, sm);
/* Dump the lifetime model */
lifetime_dump(&sm->lifetime, stream, sm);
/* Dump the supernovae Ia model */
supernovae_ia_dump(&sm->snia, stream, sm);
/* Dump the supernovae II model */
supernovae_ii_dump(&sm->snii, stream, sm);
}
/**
* @brief Restore a stellar_evolution struct from the given FILE as a stream of
* bytes.
*
* Here we are only writing the arrays, everything has been copied in the
* feedback.
*
* @param sm the struct
* @param stream the file stream
*/
void stellar_evolution_restore(struct stellar_model* sm, FILE* stream) {
/* Restore the initial mass function */
initial_mass_function_restore(&sm->imf, stream, sm);
/* Restore the lifetime model */
lifetime_restore(&sm->lifetime, stream, sm);
/* Restore the supernovae Ia model */
supernovae_ia_restore(&sm->snia, stream, sm);
/* Restore the supernovae II model */
supernovae_ii_restore(&sm->snii, stream, sm);
}
/**
* @brief Clean the allocated memory.
*
* @param sm the #stellar_model.
*/
void stellar_evolution_clean(struct stellar_model* sm) {
initial_mass_function_clean(&sm->imf);
lifetime_clean(&sm->lifetime);
supernovae_ia_clean(&sm->snia);
supernovae_ii_clean(&sm->snii);
}