/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
* 2019 Joel Pfeffer (joel.pfeffer@uwa.edu.au)
*
* 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_MOSAICS_STARS_H
#define SWIFT_MOSAICS_STARS_H
/* Standard includes */
#include
/* Local includes */
#include "cooling.h"
#include "mosaics.h"
/**
* @brief Computes the time-step length of a given star particle from stars
* physics
*
* @param sp Pointer to the s-particle data.
* @param stars_properties Properties of the stars model.
* @param with_cosmology Are we running with cosmological time integration.
* @param cosmo The current cosmological model (used if running with
* cosmology).
* @param time The current time (used if running without cosmology).
*/
__attribute__((always_inline)) INLINE static float stars_compute_timestep(
const struct spart* const sp, const struct stars_props* stars_properties,
const int with_cosmology, const struct cosmology* cosmo,
const double time) {
/* Background star particles have no time-step limits */
if (sp->birth_time == -1.) {
return FLT_MAX;
}
/* Star age (in internal units) */
double star_age;
if (with_cosmology) {
/* Deal with rounding issues */
if (sp->birth_scale_factor >= cosmo->a) {
star_age = 0.;
} else {
star_age = cosmology_get_delta_time_from_scale_factors(
cosmo, sp->birth_scale_factor, cosmo->a);
}
} else {
star_age = time - sp->birth_time;
}
/* What age category are we in? */
if (star_age > stars_properties->age_threshold_unlimited) {
return FLT_MAX;
} else if (star_age > stars_properties->age_threshold) {
return stars_properties->max_time_step_old;
} else {
return stars_properties->max_time_step_young;
}
}
/**
* @brief Returns the age of a star in internal units
*
* @param sp The star particle.
* @param cosmo The cosmological model.
* @param time The current time (in internal units).
* @param with_cosmology Are we running with cosmological integration?
*/
__attribute__((always_inline)) INLINE static double stars_compute_age(
const struct spart* sp, const struct cosmology* cosmo, double time,
const int with_cosmology) {
if (with_cosmology) {
return cosmology_get_delta_time_from_scale_factors(
cosmo, (double)sp->birth_scale_factor, cosmo->a);
} else {
return time - (double)sp->birth_time;
}
}
/**
* @brief Prepares a s-particle for its interactions
*
* @param sp The particle to act upon
*/
__attribute__((always_inline)) INLINE static void stars_init_spart(
struct spart* sp) {
#ifdef DEBUG_INTERACTIONS_STARS
for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
sp->ids_ngbs_density[i] = -1;
sp->num_ngb_density = 0;
#endif
sp->density.wcount = 0.f;
sp->density.wcount_dh = 0.f;
}
/**
* @brief Initialises the s-particles for the first time
*
* This function is called only once just after the ICs have been
* read in to do some conversions.
*
* @param sp The particle to act upon.
* @param stars_properties Properties of the stars model.
* @param with_cosmology Are we running with cosmological time integration.
* @param scale_factor The current scale-factor (used if running with
* cosmology).
* @param time The current time (used if running without cosmology).
*/
__attribute__((always_inline)) INLINE static void stars_first_init_spart(
struct spart* sp, const struct stars_props* stars_properties,
const int with_cosmology, const double scale_factor, const double time) {
sp->time_bin = 0;
sp->SNII_f_E = -1.f;
if (stars_properties->overwrite_birth_time)
sp->birth_time = stars_properties->spart_first_init_birth_time;
if (stars_properties->overwrite_birth_density)
sp->birth_density = stars_properties->spart_first_init_birth_density;
if (stars_properties->overwrite_birth_temperature)
sp->birth_temperature =
stars_properties->spart_first_init_birth_temperature;
if (with_cosmology)
sp->birth_scale_factor = min(sp->birth_scale_factor, (float)scale_factor);
else
sp->birth_scale_factor = min(sp->birth_time, (float)time);
if (with_cosmology)
sp->last_enrichment_time = scale_factor;
else
sp->last_enrichment_time = time;
sp->HIIregion_last_rebuild = -1.f;
sp->HIIregion_mass_to_ionize = 0.f;
sp->HIIregion_mass_in_kernel = -1.f;
sp->star_timestep = 0.f;
/* Only MOSAICS task for this particle is stellar evolution for 'field' */
for (int iMP = 0; iMP < MOSAICS_MULTIPHYS; iMP++) {
sp->num_clusters[iMP] = 0;
sp->initial_num_clusters[iMP] = 0;
sp->initial_num_clusters_evo[iMP] = 0;
sp->field_mass[iMP] = sp->mass;
}
sp->mass_prev_timestep = sp->mass;
stars_init_spart(sp);
}
/**
* @brief Predict additional particle fields forward in time when drifting
*
* @param sp The particle
* @param dt_drift The drift time-step for positions.
*/
__attribute__((always_inline)) INLINE static void stars_predict_extra(
struct spart* restrict sp, float dt_drift) {}
/**
* @brief Sets the values to be predicted in the drifts to their values at a
* kick time
*
* @param sp The particle.
*/
__attribute__((always_inline)) INLINE static void stars_reset_predicted_values(
struct spart* restrict sp) {}
/**
* @brief Finishes the calculation of (non-gravity) forces acting on stars
*
* Multiplies the forces and accelerations by the appropiate constants
*
* @param sp The particle to act upon
*/
__attribute__((always_inline)) INLINE static void stars_end_feedback(
struct spart* sp) {}
/**
* @brief Kick the additional variables
*
* @param sp The particle to act upon
* @param dt The time-step for this kick
*/
__attribute__((always_inline)) INLINE static void stars_kick_extra(
struct spart* sp, float dt) {}
/**
* @brief Finishes the calculation of density on stars
*
* @param sp The particle to act upon
* @param cosmo The current cosmological model.
*/
__attribute__((always_inline)) INLINE static void stars_end_density(
struct spart* sp, const struct cosmology* cosmo) {
/* Some smoothing length multiples. */
const float h = sp->h;
const float h_inv = 1.0f / h; /* 1/h */
const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
/* Finish the calculation by inserting the missing h-factors */
sp->density.wcount *= h_inv_dim;
sp->density.wcount_dh *= h_inv_dim_plus_one;
}
/**
* @brief Sets all particle fields to sensible values when the #spart has 0
* ngbs.
*
* @param sp The particle to act upon
* @param cosmo The current cosmological model.
*/
__attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours(
struct spart* restrict sp, const struct cosmology* cosmo) {
/* Re-set problematic values */
sp->density.wcount = 0.f;
sp->density.wcount_dh = 0.f;
}
/**
* @brief Reset acceleration fields of a particle
*
* This is the equivalent of hydro_reset_acceleration.
* We do not compute the acceleration on star, therefore no need to use it.
*
* @param p The particle to act upon
*/
__attribute__((always_inline)) INLINE static void stars_reset_acceleration(
struct spart* restrict p) {
#ifdef DEBUG_INTERACTIONS_STARS
p->num_ngb_force = 0;
#endif
}
/**
* @brief Reset acceleration fields of a particle
*
* This is the equivalent of hydro_reset_acceleration.
* We do not compute the acceleration on star, therefore no need to use it.
*
* @param p The particle to act upon
*/
__attribute__((always_inline)) INLINE static void stars_reset_feedback(
struct spart* restrict p) {
#ifdef DEBUG_INTERACTIONS_STARS
for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
p->ids_ngbs_force[i] = -1;
p->num_ngb_force = 0;
#endif
}
/**
* @brief Compute the luminosities of a particles in different bands.
*
* @param sp The particle.
* @param with_cosmology Are we running a cosmological simulation?
* @param cosmo The #cosmology object.
* @param time The current physical time (internal units).
* @param phys_const The physical constants in internal units.
* @param props The #stars_props of that run.
* @param luminosities (return) The luminosity in each band.
*/
INLINE static void stars_get_luminosities(
const struct spart* sp, const int with_cosmology,
const struct cosmology* cosmo, const double time,
const struct phys_const* phys_const, const struct stars_props* props,
float luminosities[luminosity_bands_count]) {
const int count_Z = eagle_stars_lum_tables_N_Z;
const int count_ages = eagle_stars_lum_tables_N_ages;
/* Get star properties (all in internal units */
const float Z =
chemistry_get_star_total_metal_mass_fraction_for_luminosity(sp);
const float mass = sp->mass_init;
float age;
if (with_cosmology)
age = cosmology_get_delta_time_from_scale_factors(
cosmo, sp->birth_scale_factor, cosmo->a);
else
age = time - sp->birth_time;
/* Convert to the units of the tables */
const float mass_Msun = mass / phys_const->const_solar_mass;
const float age_Gyr = age / phys_const->const_year / 1e9;
for (int i = 0; i < (int)luminosity_bands_count; ++i) {
/* Log things */
float log10_Z = log10(Z + FLT_MIN);
float log10_age_Gyr = log10(age_Gyr + FLT_MIN);
/* Clip the input */
log10_Z = max(log10_Z, props->lum_tables_Z[i][0]);
log10_Z = min(log10_Z, props->lum_tables_Z[i][count_Z - 1]);
log10_age_Gyr = max(log10_age_Gyr, props->lum_tables_ages[i][0]);
log10_age_Gyr =
min(log10_age_Gyr, props->lum_tables_ages[i][count_ages - 1]);
/* Get index along the interpolation axis */
int Z_index = 0, age_index = 0;
for (int j = 0; j < count_Z - 1; ++j) {
if (log10_Z >= props->lum_tables_Z[i][j]) ++Z_index;
}
for (int j = 0; j < count_ages - 1; ++j) {
if (log10_age_Gyr >= props->lum_tables_ages[i][j]) ++age_index;
}
#ifdef SWIFT_DEBUG_CHECKS
if (age_index == 0) error("Invalid age index!");
if (Z_index == 0) error("Invalid Z index!");
#endif
const float* array = props->lum_tables_luminosities[i];
/* 2D interpolation */
const float f_11 = array[(Z_index - 1) * count_ages + (age_index - 1)];
const float f_12 = array[(Z_index - 0) * count_ages + (age_index - 1)];
const float f_21 = array[(Z_index - 1) * count_ages + (age_index - 0)];
const float f_22 = array[(Z_index - 0) * count_ages + (age_index - 0)];
const float x_diff1 = props->lum_tables_ages[i][age_index] - log10_age_Gyr;
const float x_diff2 =
log10_age_Gyr - props->lum_tables_ages[i][age_index - 1];
const float x_diff3 = props->lum_tables_ages[i][age_index] -
props->lum_tables_ages[i][age_index - 1];
const float y_diff1 = props->lum_tables_Z[i][Z_index] - log10_Z;
const float y_diff2 = log10_Z - props->lum_tables_Z[i][Z_index - 1];
const float y_diff3 =
props->lum_tables_Z[i][Z_index] - props->lum_tables_Z[i][Z_index - 1];
const float f_1 = (f_11 * x_diff1 + f_21 * x_diff2) / x_diff3;
const float f_2 = (f_12 * x_diff1 + f_22 * x_diff2) / x_diff3;
const float log10_f = (y_diff1 * f_1 + y_diff2 * f_2) / y_diff3;
/* Final conversion */
luminosities[i] =
powf(10.f, log10_f) * mass_Msun * props->lum_tables_factor;
}
}
/**
* @brief Do the mosaics subgrid star cluster formation model
*
* @param sp The particle to act upon
* @param e The #engine.
* @param with_cosmology Are we running with cosmological time integration.
* @param cell_gas_fraction Gas fraction in local cell
* @param cell_with Width of cell when gas fraction was calculated
*/
__attribute__((always_inline)) INLINE static void stars_do_mosaics_formation(
struct spart* restrict sp, const struct engine* e,
const struct cosmology* cosmo, float cell_gas_fraction, float cell_width) {
/* Retrieve tidal tensors from the gpart */
sp->tidal_tensor[2][0] = sp->gpart->T_xx;
sp->tidal_tensor[2][1] = sp->gpart->T_yy;
sp->tidal_tensor[2][2] = sp->gpart->T_zz;
sp->tidal_tensor[2][3] = sp->gpart->T_xy;
sp->tidal_tensor[2][4] = sp->gpart->T_xz;
sp->tidal_tensor[2][5] = sp->gpart->T_yz;
/* Cell gas fraction for surface density calculation */
sp->cell_gas_fraction = cell_gas_fraction;
sp->cell_width = cell_width;
#ifdef SWIFT_DEBUG_CHECKS
if (cell_gas_fraction <= 0.f) {
error("[mosaics] cell_gas_fraction = %g !!", cell_gas_fraction);
}
#endif
/* Go make clusters */
mosaics_clform(sp, e, cosmo);
/* We're done with cluster formation */
sp->new_star = 0;
}
/**
* @brief Do the mosaics subgrid star cluster evolution model
*
* @param sp The particle to act upon
* @param e The #engine.
* @param with_cosmology Are we running with cosmological time integration.
* @param cell_gas_fraction Gas fraction in local cell
* @param cell_with Width of cell when gas fraction was calculated
*/
__attribute__((always_inline)) INLINE static void stars_do_mosaics_evolution(
struct spart* restrict sp, const struct engine* e,
const struct cosmology* cosmo, const int with_cosmology) {
/* Shift the old tensors along */
for (int i = 0; i < 2; i++) {
sp->tidal_tensor[i][0] = sp->tidal_tensor[i + 1][0];
sp->tidal_tensor[i][1] = sp->tidal_tensor[i + 1][1];
sp->tidal_tensor[i][2] = sp->tidal_tensor[i + 1][2];
sp->tidal_tensor[i][3] = sp->tidal_tensor[i + 1][3];
sp->tidal_tensor[i][4] = sp->tidal_tensor[i + 1][4];
sp->tidal_tensor[i][5] = sp->tidal_tensor[i + 1][5];
}
/* Now retrieve the new ones from the gpart */
sp->tidal_tensor[2][0] = sp->gpart->T_xx;
sp->tidal_tensor[2][1] = sp->gpart->T_yy;
sp->tidal_tensor[2][2] = sp->gpart->T_zz;
sp->tidal_tensor[2][3] = sp->gpart->T_xy;
sp->tidal_tensor[2][4] = sp->gpart->T_xz;
sp->tidal_tensor[2][5] = sp->gpart->T_yz;
/* Do cluster evolution, and apply stellar evo. to field props */
mosaics_clevo(sp, e, cosmo, with_cosmology);
}
/**
* @brief Gather some extra props needed at star formation time for mosaics
*
* @param p The gas particle
* @param xp The additional properties of the gas particles.
* @param sp The star particle
* @param cosmo the cosmological parameters and properties.
* @param phys_const The physical constants in internal units.
* @param hydro_props The properties of the hydro scheme.
* @param us The internal system of units.
* @param cooling The cooling data struct.
* cosmology).
*/
__attribute__((always_inline)) INLINE static void
stars_mosaics_copy_extra_properties(
const struct part* p, const struct xpart* xp, struct spart* restrict sp,
const struct cosmology* cosmo, const struct phys_const* phys_const,
const struct hydro_props* restrict hydro_props,
const struct unit_system* restrict us,
const struct cooling_function_data* restrict cooling) {
#if !defined(STAR_FORMATION_NONE)
/* Flag star for cluster formation */
sp->new_star = 1;
/* For applying stellar evolutionary mass loss */
sp->mass_prev_timestep = sp->mass;
/* Hydro pressure */
sp->birth_pressure = hydro_get_physical_pressure(p, cosmo);
/* Calculate the hydro sound speed */
sp->sound_speed_subgrid = hydro_get_physical_soundspeed(p, cosmo);
#if defined(COOLING_COLIBRE) || defined(COOLING_COLIBRE_SPLIT) || \
defined(COOLING_CHIMES)
#if defined(STAR_FORMATION_COLIBRE)
/* Correction for subgrid ISM */
sp->sound_speed_subgrid *=
sqrt(sp->sf_data.birth_subgrid_temperature / sp->birth_temperature);
#endif /* star formation model */
#endif /* cooling model */
#endif /* !defined(STAR_FORMATION_NONE) */
}
#endif /* SWIFT_MOSAICS_STARS_H */