-
Loic Hausammann authoredLoic Hausammann authored
stellar_evolution_wrapper.c 19.68 KiB
/*******************************************************************************
* This file is part of PYSWIFTSIM.
* Copyright (c) 2018 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 <http://www.gnu.org/licenses/>.
*
******************************************************************************/
#include "stellar_evolution_wrapper.h"
#include "pyswiftsim_tools.h"
/**
* @brief Compute the initial mass function.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the masses.
*
* @param self calling object
* @param args arguments
* @return The initial mass function
*/
PyObject* pyinitial_mass_function(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *mass;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sO", &filename, &mass))
return NULL;
/* check numpy array */
if (pytools_check_array(mass, 1, NPY_FLOAT, "Mass") != SWIFT_SUCCESS)
return NULL;
size_t N = PyArray_DIM(mass, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *imf = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(mass), PyArray_DIMS(mass), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float m = *(float*) PyArray_GETPTR1(mass, i);
float *imf_i = (float*) PyArray_GETPTR1(imf, i);
/* change units internal units -> solar mass */
m /= pconst.const_solar_mass;
if (m > fp.stellar_model.imf.mass_max) {
*imf_i = 0.;
continue;
}
*imf_i = initial_mass_function_get_imf(&fp.stellar_model.imf, m);
/* change from internal units to solar mass */
*imf_i /= pconst.const_solar_mass;
}
return (PyObject *) imf;
}
/**
* @brief Compute the lifetime of a star.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the masses.
* - a numpy array containing the metallicity.
*
* @param self calling object
* @param args arguments
* @return the lifetime of the stars
*/
PyObject* pylifetime_from_mass(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *mass;
PyArrayObject *metallicity;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOO", &filename, &mass, &metallicity))
return NULL;
/* check numpy array */
if (pytools_check_array(mass, 1, NPY_FLOAT, "Mass") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(metallicity, 1, NPY_FLOAT, "Metallicity") != SWIFT_SUCCESS)
return NULL;
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(mass, 0))
error("Mass and metallicity should have the same dimension");
size_t N = PyArray_DIM(mass, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *lifetime = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(mass), PyArray_DIMS(mass), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float m = *(float*) PyArray_GETPTR1(mass, i);
float z = *(float*) PyArray_GETPTR1(metallicity, i);
float *tau = (float*) PyArray_GETPTR1(lifetime, i);
/* change units internal units -> solar mass */
m /= pconst.const_solar_mass;
*tau = lifetime_get_log_lifetime_from_mass(&fp.stellar_model.lifetime, log10(m), z);
*tau = pow(10, *tau) * pconst.const_year * 1e6;
}
return (PyObject *) lifetime;
}
/**
* @brief Compute the mass of a star with a given lifetime.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the time.
* - a numpy array containing the metallicity.
*
* @param self calling object
* @param args arguments
* @return The mass of the stars.
*/
PyObject* pymass_from_lifetime(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *time;
PyArrayObject *metallicity;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOO", &filename, &time, &metallicity))
return NULL;
/* check numpy array */
if (pytools_check_array(time, 1, NPY_FLOAT, "Lifetime") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(metallicity, 1, NPY_FLOAT, "Metallicity") != SWIFT_SUCCESS)
return NULL;
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(time, 0))
error("Lifetime and metallicity should have the same dimension");
size_t N = PyArray_DIM(time, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *mass = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(time), PyArray_DIMS(time), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float tau = *(float*) PyArray_GETPTR1(time, i);
float z = *(float*) PyArray_GETPTR1(metallicity, i);
float *m = (float*) PyArray_GETPTR1(mass, i);
/* change units internal units to Myr */
tau /= pconst.const_year * 1e6;
*m = lifetime_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(tau), z);
*m = pow(10, *m) * pconst.const_solar_mass;
}
return (PyObject *) mass;
}
/**
* @brief Compute the supernovae Ia rate.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the lower limit for the time.
* - a numpy array containing the upper limit for the time.
* - a numpy array containing the metallicity.
*
* @param self calling object
* @param args arguments
* @return Rate of supernovae Ia
*/
PyObject* pysupernovae_ia_rate(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *time1;
PyArrayObject *time2;
PyArrayObject *metallicity;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOOO", &filename, &time1, &time2, &metallicity))
return NULL;
/* check numpy array */
if (pytools_check_array(time1, 1, NPY_FLOAT, "Time1") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(time2, 1, NPY_FLOAT, "Time2") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(metallicity, 1, NPY_FLOAT, "Metallicity") != SWIFT_SUCCESS)
return NULL;
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(time1, 0))
error("Time1 and metallicity should have the same dimension");
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(time2, 0))
error("Time2 and metallicity should have the same dimension");
size_t N = PyArray_DIM(time1, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *rate = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(time1), PyArray_DIMS(time1), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float t1 = *(float*) PyArray_GETPTR1(time1, i);
float t2 = *(float*) PyArray_GETPTR1(time2, i);
float z = *(float*) PyArray_GETPTR1(metallicity, i);
float *r = (float*) PyArray_GETPTR1(rate, i);
/* change units internal units -> Myr */
t1 /= pconst.const_year * 1e6;
t2 /= pconst.const_year * 1e6;
float m1 = lifetime_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(t1), z);
m1 = pow(10, m1);
float m2 = lifetime_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(t2), z);
m2 = pow(10, m2);
/* Need to reverse 1 and 2 due to the lifetime being a decreasing function */
*r = supernovae_ia_get_number_per_unit_mass(&fp.stellar_model.snia, m2, m1) / (t2 - t1);
/* change units 1 / (solMass Myr) -> internal units */
*r /= pconst.const_solar_mass * pconst.const_year * 1e6;
}
return (PyObject *) rate;
}
/**
* @brief Compute the supernovae II rate.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the lower limit for the time.
* - a numpy array containing the upper limit for the time.
* - a numpy array containing the metallicity.
*
* @param self calling object
* @param args arguments
* @return Rate of supernovae II
*/
PyObject* pysupernovae_ii_rate(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *time1;
PyArrayObject *time2;
PyArrayObject *metallicity;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOOO", &filename, &time1, &time2, &metallicity))
return NULL;
/* check numpy array */
if (pytools_check_array(time1, 1, NPY_FLOAT, "Time1") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(time2, 1, NPY_FLOAT, "Time2") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(metallicity, 1, NPY_FLOAT, "Metallicity") != SWIFT_SUCCESS)
return NULL;
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(time1, 0))
error("Time1 and metallicity should have the same dimension");
if (PyArray_DIM(metallicity, 0) != PyArray_DIM(time2, 0))
error("Time2 and metallicity should have the same dimension");
size_t N = PyArray_DIM(time1, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *rate = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(time1), PyArray_DIMS(time1), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float t1 = *(float*) PyArray_GETPTR1(time1, i);
float t2 = *(float*) PyArray_GETPTR1(time2, i);
float z = *(float*) PyArray_GETPTR1(metallicity, i);
float *r = (float*) PyArray_GETPTR1(rate, i);
/* change units internal units -> Myr */
t1 /= pconst.const_year * 1e6;
t2 /= pconst.const_year * 1e6;
float m1 = lifetime_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(t1), z);
m1 = pow(10, m1);
float m2 = lifetime_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(t2), z);
m2 = pow(10, m2);
/* Need to reverse 1 and 2 due to the lifetime being a decreasing function */
*r = supernovae_ii_get_number_per_unit_mass(&fp.stellar_model.snii, m2, m1) / (t2 - t1);
/* change units 1 / (solMass Myr) -> internal units */
*r /= pconst.const_solar_mass * pconst.const_year * 1e6;
}
return (PyObject *) rate;
}
/**
* @brief Compute the supernovae II yields.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the lower limit for the mass.
* - a numpy array containing the upper limit for the mass.
*
* @param self calling object
* @param args arguments
* @return mass fraction of ejected metals
*/
PyObject* pysupernovae_ii_yields(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *mass1;
PyArrayObject *mass2;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOO", &filename, &mass1, &mass2))
return NULL;
/* check numpy array */
if (pytools_check_array(mass1, 1, NPY_FLOAT, "Mass1") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(mass2, 1, NPY_FLOAT, "Mass2") != SWIFT_SUCCESS)
return NULL;
if (PyArray_NDIM(mass1) != 1 || PyArray_NDIM(mass2) != 1)
error("The inputs should be 1-D arrays");
if (PyArray_DIM(mass1, 0) != PyArray_DIM(mass2, 0))
error("Mass1 and Mass2 should have the same dimension");
size_t N = PyArray_DIM(mass1, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
const int ndims = 2;
npy_intp dims[2] = {N, GEAR_CHEMISTRY_ELEMENT_COUNT};
PyArrayObject *yields_out = (PyArrayObject *)PyArray_SimpleNew(ndims, dims, NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
float yields[GEAR_CHEMISTRY_ELEMENT_COUNT] = {0.};
const float m1 = *(float*) PyArray_GETPTR1(mass1, i) / pconst.const_solar_mass;
const float log_m1 = log10(m1);
const float m2 = *(float*) PyArray_GETPTR1(mass2, i) / pconst.const_solar_mass;
const float log_m2 = log10(m2);
/* Compute the yields */
supernovae_ii_get_yields_from_integral(&fp.stellar_model.snii, log_m1, log_m2, yields);
for(int j = 0; j < GEAR_CHEMISTRY_ELEMENT_COUNT; j++) {
float *y = (float*) PyArray_GETPTR2(yields_out, i, j);
*y = yields[j];
}
}
return (PyObject *) yields_out;
}
/**
* @brief Compute the mass ejected by a supernovae II.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the lower limit for the mass.
* - a numpy array containing the upper limit for the mass.
*
* @param self calling object
* @param args arguments
* @return mass fraction of ejected mass.
*/
PyObject* pysupernovae_ii_mass_ejected(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *mass1;
PyArrayObject *mass2;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOO", &filename, &mass1, &mass2))
return NULL;
/* check numpy array */
if (pytools_check_array(mass1, 1, NPY_FLOAT, "Mass1") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(mass2, 1, NPY_FLOAT, "Mass2") != SWIFT_SUCCESS)
return NULL;
if (PyArray_NDIM(mass1) != 1 || PyArray_NDIM(mass2) != 1)
error("The inputs should be 1-D arrays");
if (PyArray_DIM(mass1, 0) != PyArray_DIM(mass2, 0))
error("Mass1 and Mass2 should have the same dimension");
size_t N = PyArray_DIM(mass1, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *mass_ejected = (PyArrayObject *)PyArray_SimpleNew(
PyArray_NDIM(mass1), PyArray_DIMS(mass1), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
const float m1 = *(float*) PyArray_GETPTR1(mass1, i) / pconst.const_solar_mass;
const float log_m1 = log10(m1);
const float m2 = *(float*) PyArray_GETPTR1(mass2, i) / pconst.const_solar_mass;
const float log_m2 = log10(m2);
float *y = (float*) PyArray_GETPTR1(mass_ejected, i);
*y = supernovae_ii_get_ejected_mass_fraction_processed_from_integral(&fp.stellar_model.snii, log_m1, log_m2);
}
return (PyObject *) mass_ejected;
}
/**
* @brief Compute the non processed mass ejected by a supernovae II.
*
* args is expecting:
* - the name of the parameter file
* - a numpy array containing the lower limit for the mass.
* - a numpy array containing the upper limit for the mass.
*
* @param self calling object
* @param args arguments
* @return mass ratio of (non processed) mass ejected
*/
PyObject* pysupernovae_ii_mass_ejected_non_processed(PyObject* self, PyObject* args) {
import_array();
char *filename;
PyArrayObject *mass1;
PyArrayObject *mass2;
/* parse argument */
if (!PyArg_ParseTuple(
args, "sOO", &filename, &mass1, &mass2))
return NULL;
/* check numpy array */
if (pytools_check_array(mass1, 1, NPY_FLOAT, "Mass1") != SWIFT_SUCCESS)
return NULL;
if (pytools_check_array(mass2, 1, NPY_FLOAT, "Mass2") != SWIFT_SUCCESS)
return NULL;
if (PyArray_NDIM(mass1) != 1 || PyArray_NDIM(mass2) != 1)
error("The inputs should be 1-D arrays");
if (PyArray_DIM(mass1, 0) != PyArray_DIM(mass2, 0))
error("Mass1 and Mass2 should have the same dimension");
size_t N = PyArray_DIM(mass1, 0);
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
PyArrayObject *mass_ejected = (PyArrayObject *)PyArray_SimpleNew(
PyArray_NDIM(mass1), PyArray_DIMS(mass1), NPY_FLOAT);
for(size_t i = 0; i < N; i++) {
const float m1 = *(float*) PyArray_GETPTR1(mass1, i) / pconst.const_solar_mass;
const float log_m1 = log10(m1);
const float m2 = *(float*) PyArray_GETPTR1(mass2, i) / pconst.const_solar_mass;
const float log_m2 = log10(m2);
float *y = (float*) PyArray_GETPTR1(mass_ejected, i);
*y = supernovae_ii_get_ejected_mass_fraction_non_processed_from_integral(&fp.stellar_model.snii, log_m1, log_m2);
}
return (PyObject *) mass_ejected;
}
/**
* @brief Compute the supernovae Ia yields.
*
* args is expecting:
* - the name of the parameter file
*
* @param self calling object
* @param args arguments
* @return Mass fraction of the supernovae Ia yields.
*/
PyObject* pysupernovae_ia_yields(PyObject* self, PyObject* args) {
import_array();
char *filename;
/* parse argument */
if (!PyArg_ParseTuple(
args, "s", &filename))
return NULL;
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
const int ndims = 1;
npy_intp dims[1] = {GEAR_CHEMISTRY_ELEMENT_COUNT};
PyArrayObject *yields_out = (PyArrayObject *)PyArray_SimpleNew(ndims, dims, NPY_FLOAT);
const float *yields = supernovae_ia_get_yields(&fp.stellar_model.snia);
for(size_t i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
float *y = (float*) PyArray_GETPTR1(yields_out, i);
*y = yields[i] / fp.stellar_model.snia.mass_white_dwarf;
}
return (PyObject *) yields_out;
}
/**
* @brief Compute the mass ejected by a supernovae Ia.
*
* args is expecting:
* - the name of the parameter file
*
* @param self calling object
* @param args arguments
* @return mass fraction ejected.
*/
PyObject* pysupernovae_ia_mass_ejected(PyObject* self, PyObject* args) {
import_array();
char *filename;
/* parse argument */
if (!PyArg_ParseTuple(
args, "s", &filename))
return NULL;
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* return object */
const int ndims = 1;
npy_intp dims[1] = {1};
PyArrayObject *mass_ejected = (PyArrayObject *)PyArray_SimpleNew(ndims, dims, NPY_FLOAT);
float *y = (float*) PyArray_GETPTR1(mass_ejected, 0);
*y = supernovae_ia_get_ejected_mass_processed(&fp.stellar_model.snia) / fp.stellar_model.snia.mass_white_dwarf;
return (PyObject *) mass_ejected;
}
/**
* @brief Read the elements names.
*
* args is expecting:
* - the name of the parameter file
*
* @param self calling object
* @param args arguments
* @return cooling rate
*/
PyObject* pystellar_get_elements(PyObject* self, PyObject* args) {
import_array();
char *filename;
/* parse argument */
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
int with_cosmo = 0;
PYSWIFTSIM_INIT_STRUCTS();
/* Init stellar physics */
struct feedback_props fp;
feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo);
/* Save the variables */
PyObject *t = PyList_New(GEAR_CHEMISTRY_ELEMENT_COUNT);
for(int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
const char *name = stellar_evolution_get_element_name(&fp.stellar_model, i);
PyObject *string = PyUnicode_FromString(name);
int test = PyList_SetItem(t, i, string);
if (test != 0) {
error("Failed to add an element");
}
}
return t;
}