diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fbaeba529ff1208399d417e7e00419057632cc9b..542d9df5512176037d5602266d177978c37f59d6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,8 @@ image: ubuntu:latest before_script: - - apt-get update -qq && apt-get install -y -qq build-essential libhdf5-serial-dev gfortran csh git libtool-bin autoconf + - export DEBIAN_FRONTEND=noninteractive + - apt-get update -qq && apt-get install -y -qq build-essential libhdf5-serial-dev gfortran csh git libtool-bin autoconf openmpi-bin openmpi-common libopenmpi-dev python3 python3-setuptools python3-numpy python3-scipy python3-matplotlib python3-dev python3-h5py - export LHOME=`pwd` # Compile grackle - mkdir grackle @@ -27,7 +28,7 @@ before_script: - cd .. # compile pyswiftsim - - python setup.py install --user + - python3 setup.py install --with-swift $LHOME/swiftsim --with-grackle $GRACKLE_ROOT --user # compile the docs - cd docs diff --git a/docs/RST/examples/stellar.rst b/docs/RST/examples/stellar.rst index 94e1ac8ee917b03ae0ee1fd50d3886c07f312707..031a8849b978958ec5b183d29be70ae421c16cd0 100644 --- a/docs/RST/examples/stellar.rst +++ b/docs/RST/examples/stellar.rst @@ -21,5 +21,14 @@ The lifetime in GEAR is a quadratic in metallicity and mass (see equation 3.32 i .. image:: https://obswww.unige.ch/lastro/projects/Clastro/PySWIFTsim/examples/lifetime.png +Supernovae Rate +~~~~~~~~~~~~~~~ + +This example is generated in ``examples/supernovae_rate`` with GEAR's stellar model. +The supernovae rate is given by the contribution of SNIa (in blue) and SNII (in orange). +The two peaks in SNIa correspond to the two different type of companion (red giant and main sequence). + +.. image:: https://obswww.unige.ch/lastro/projects/Clastro/PySWIFTsim/examples/supernovae_rate.png + .. [Kroupa2001] `On the variation of the Initial Mass Function <https://arxiv.org/abs/astro-ph/0009005>`_ .. [Poirier2004] `Étude de l'évolution chimique et dynamique d'objets proto-galactiques: Application à l'évolution des galaxies spirales <https://obswww.unige.ch/~revaz/PyChem/_downloads/ff03ba21204a3f36fa4ec885abd545ae/ThesePoirier.pdf>`_ diff --git a/examples/cooling_box/plot_cooling.py b/examples/cooling_box/plot_cooling.py index 18bbb26903a25e87667887dc5beeacc8f3feb976..0a3f2ee4c5262c4cdeb3dc22058ae7953af98bd1 100644 --- a/examples/cooling_box/plot_cooling.py +++ b/examples/cooling_box/plot_cooling.py @@ -131,7 +131,7 @@ if __name__ == "__main__": overwrite = { "GrackleCooling": { - "WithMetalCooling": with_metals, + "with_metal_cooling": with_metals, } } with pyswiftsim.ParameterFile(overwrite) as filename: diff --git a/examples/cooling_rate/plot_cooling.py b/examples/cooling_rate/plot_cooling.py index 884f0592da44acc0444e14e41ea63dcba67bf4eb..0fe4d8b42a5c7f99645b9957120f738a36817e32 100644 --- a/examples/cooling_rate/plot_cooling.py +++ b/examples/cooling_rate/plot_cooling.py @@ -20,6 +20,8 @@ from pyswiftsim.libcooling import coolingRate import pyswiftsim +import matplotlib + from copy import deepcopy import numpy as np import matplotlib.pyplot as plt @@ -27,8 +29,20 @@ from astropy import units, constants from time import strftime, gmtime import sys +SMALL_SIZE = 13 +MEDIUM_SIZE = 15 +BIGGER_SIZE = 18 + +plt.rc('font', size=SMALL_SIZE) # controls default text sizes +plt.rc('axes', titlesize=SMALL_SIZE) # fontsize of the axes title +plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels +plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels +plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels +plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize +plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title plt.rc('text', usetex=True) + pyswiftsim.downloadCoolingTable() # PARAMETERS @@ -40,6 +54,12 @@ with_cosmo = False # include metals? with_metals = 1 +metallicity = 0.02041 + +# cooling table +cooling_tables = [ + "./CloudyData_UVB=HM2012.h5", +] # hydrogen mass fraction h_mass_frac = 0.76 @@ -50,6 +70,7 @@ if N_rho == 1: density = np.array([1.]) else: density = np.logspace(-6, 4, N_rho) + plot_cooling_length = False # temperature in K N_T = 1000 @@ -113,7 +134,7 @@ def generate_initial_condition(us): return d -def plot_solution(rate, data, us): +def plot_solution(rates, data, us): print("Plotting solution") energy = data["energy"] rho = data["density"] @@ -131,55 +152,82 @@ def plot_solution(rate, data, us): energy *= u_v**2 - rate *= u_v**2 / u_t + rates = [rate * u_v**2 / u_t for rate in rates] # lambda cooling - lam = rate * rho + lambdas = [rate * rho for rate in rates] # do plot if N_rho == 1: # plot Lambda vs T plt.figure() - plt.loglog(T, np.abs(lam), - label="SWIFT") + for i, lam in enumerate(lambdas): + plt.loglog(T, np.abs(lam), + label="Table %i" % i) plt.xlabel("Temperature [K]") plt.ylabel("$\\Lambda$ [erg s$^{-1}$ cm$^{3}$]") + plt.legend() else: m_p = constants.m_p.to("g") / units.g rho /= m_p - - shape = [N_rho, N_T] - cooling_time = energy / rate - cooling_length = np.sqrt(gamma * (gamma-1.) * energy) * cooling_time - - cooling_length = np.log10(np.abs(cooling_length) / units.kpc.to('cm')) - - # reshape - rho = rho.reshape(shape) - T = T.reshape(shape) - energy = energy.reshape(shape) - cooling_length = cooling_length.reshape(shape) - - _min = -7 - _max = 7 - N_levels = 100 - levels = np.linspace(_min, _max, N_levels) - plt.figure() - plt.contourf(rho, T, cooling_length, levels, - cmap=plt.get_cmap(colormap)) - plt.xlabel("Density [atom/cm3]") - plt.ylabel("Temperature [K]") - - ax = plt.gca() - ax.set_xscale("log") - ax.set_yscale("log") - - cbar = plt.colorbar() - tc = np.arange(_min, _max, 1.5) - cbar.set_ticks(tc) - cbar.set_ticklabels(tc) - cbar.ax.set_ylabel("Log( Cooling Length / kpc )") + r = [] + for rate in rates: + shape = [N_rho, N_T] + energy = np.reshape(energy, shape) + rate = np.reshape(rate, shape) + + cooling_time = energy / rate + cooling_length = np.sqrt(gamma * (gamma-1.) * energy) + cooling_length *= cooling_time + + cooling_length = np.log10( + np.abs(cooling_length) / units.kpc.to('cm')) + + # reshape + rho = rho.reshape(shape) + T = T.reshape(shape) + energy = energy.reshape(shape) + cooling_length = cooling_length.reshape(shape) + + levels = 500 + if plot_cooling_length: + _min = -7 + _max = 7 + levels = np.linspace(_min, _max, levels) + z = cooling_length + else: + z = np.log10(np.abs(rate)) + + plt.figure() + r.append(rate) + plt.contourf(np.log10(rho), np.log10(T), z, + levels, + cmap=plt.get_cmap(colormap)) + plt.xlabel("Log( Density [atom/cm3] )") + plt.ylabel("Log( Temperature [K] )") + + cbar = plt.colorbar() + if plot_cooling_length: + tc = np.arange(_min, _max, 1.5) + cbar.set_ticks(tc) + cbar.set_ticklabels(tc) + cbar.ax.set_ylabel("Log( Cooling Length [kpc] )") + else: + cbar.ax.set_ylabel("Log( Rate [s / erg] )") + + if len(r) == 2: + plt.figure() + plt.contourf(rho, T, np.log10(np.abs((r[1] - r[0]) / r[1])), 500, + cmap=plt.get_cmap(colormap)) + plt.xlabel("Density [atom/cm3]") + plt.ylabel("Temperature [K]") + + ax = plt.gca() + ax.set_xscale("log") + ax.set_yscale("log") + + cbar = plt.colorbar() date = strftime("%d %b %Y", gmtime()) plt.title("Date: {}".format(date)) @@ -188,24 +236,35 @@ def plot_solution(rate, data, us): if __name__ == "__main__": - overwrite = { - "GrackleCooling": { - "WithMetalCooling": with_metals, + rates = [] + + for cooling_table in cooling_tables: + overwrite = { + "GrackleCooling": { + "with_metal_cooling": with_metals, + "redshift": 0, + "cloudy_table": cooling_table, + }, + "GEARChemistry": { + "initial_metallicity": metallicity, + }, } - } - with pyswiftsim.ParameterFile(overwrite) as filename: - parser = pyswiftsim.parseYamlFile(filename) - us = parser["InternalUnitSystem"] + with pyswiftsim.ParameterFile(overwrite) as filename: + + parser = pyswiftsim.parseYamlFile(filename) + us = parser["InternalUnitSystem"] + + d = generate_initial_condition(us) - d = generate_initial_condition(us) + # du / dt + print("Computing cooling...") - # du / dt - print("Computing cooling...") + rate = coolingRate(filename, + d["density"].astype(np.float32), + d["energy"].astype(np.float32), + with_cosmo, d["time_step"]) - rate = coolingRate(filename, - d["density"].astype(np.float32), - d["energy"].astype(np.float32), - with_cosmo, d["time_step"]) + rates.append(rate) - plot_solution(rate, d, us) + plot_solution(rates, d, us) diff --git a/examples/cooling_rate/run.sh b/examples/cooling_rate/run.sh index ce3d510430474c3986b463878c3edff2ba02d212..c140dca3d370df398e77fa31d871e97e9652c7a6 100644 --- a/examples/cooling_rate/run.sh +++ b/examples/cooling_rate/run.sh @@ -1,4 +1,4 @@ #!/bin/bash ./plot_cooling.py 1 -./plot_cooling.py 100 +# ./plot_cooling.py 100 diff --git a/examples/initial_mass_function/plot_initial_mass_function.py b/examples/initial_mass_function/plot_initial_mass_function.py index bc653edc70a36756b7f65f206fd4e5e02cd56129..873aa4bb6bedaa64f0971476bcbbd2ff5e9fd681 100644 --- a/examples/initial_mass_function/plot_initial_mass_function.py +++ b/examples/initial_mass_function/plot_initial_mass_function.py @@ -22,32 +22,45 @@ import pyswiftsim import numpy as np import matplotlib.pyplot as plt +from time import strftime, gmtime plt.rc('text', usetex=True) N = 100 +solMass_in_cgs = 1.98848e33 +mass_min = 0.05 # in solar mass +mass_max = 50 # in solar mass +pyswiftsim.downloadYieldsTable() if __name__ == "__main__": - with pyswiftsim.ParameterFile() as filename: parser = pyswiftsim.parseYamlFile(filename) - imf_parser = parser["GEARInitialMassFunction"] - mass_limits = list(imf_parser["mass_limits"]) - mass_min = np.log10(mass_limits[0]) - mass_max = np.log10(mass_limits[-1]) + # get parser + units = parser["InternalUnitSystem"] + unit_mass = float(units["UnitMass_in_cgs"]) + solMass_in_internal = solMass_in_cgs / unit_mass + + mass_min = np.log10(mass_min * solMass_in_internal) + mass_max = np.log10(mass_max * solMass_in_internal) + + mass = np.logspace(mass_min, mass_max, N, dtype=np.float32, + endpoint=False) - # du / dt print("Computing initial mass function...") - mass = np.logspace(mass_min, mass_max, N, dtype=np.float32) imf = libstellar.initialMassFunction(filename, mass) + imf *= solMass_in_internal # plot IMF + plt.figure() + mass /= solMass_in_internal plt.loglog(mass, imf) - plt.xlabel("Mass [Msun]") - plt.ylabel("Mass fraction") + date = strftime("%d %b %Y", gmtime()) + plt.title("Date: {}".format(date)) + plt.xlabel("Mass [$M_\mathrm{sun}$]") + plt.ylabel("Initial Mass Function [$M_\mathrm{sun}^{-1}$]") plt.show() diff --git a/examples/lifetime/plot_lifetime.py b/examples/lifetime/plot_lifetime.py index 7696311e3cfd048d2dd000bebbbfc4389d448125..3cfcdec817a030b4c977a2f7760e2012db2dd854 100644 --- a/examples/lifetime/plot_lifetime.py +++ b/examples/lifetime/plot_lifetime.py @@ -22,25 +22,38 @@ import pyswiftsim import numpy as np import matplotlib.pyplot as plt +from time import strftime, gmtime plt.rc('text', usetex=True) N = 1000 solar_metallicity = 0.02041 +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 +mass_min = 0.05 +mass_max = 50 +pyswiftsim.downloadYieldsTable() if __name__ == "__main__": with pyswiftsim.ParameterFile() as filename: parser = pyswiftsim.parseYamlFile(filename) # Get masses - imf = parser["GEARInitialMassFunction"] - mass_limits = imf["mass_limits"] - mass_min = np.log10(mass_limits[0]) - mass_max = np.log10(mass_limits[-1]) + mass_min = np.log10(mass_min) + mass_max = np.log10(mass_max) mass = np.logspace(mass_min, mass_max, N, dtype=np.float32) + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + + mass *= solMass_in_cgs / unit_mass + # get metallicity Z = solar_metallicity * np.ones(N, dtype=np.float32) @@ -49,8 +62,13 @@ if __name__ == "__main__": lifetime = libstellar.lifetimeFromMass(filename, mass, Z) mass_from_lifetime = libstellar.massFromLifetime(filename, lifetime, Z) - lifetime /= 1e6 + mass /= solMass_in_cgs / unit_mass + mass_from_lifetime /= solMass_in_cgs / unit_mass + lifetime /= 1e6 * year_in_cgs / unit_time + plt.figure() + date = strftime("%d %b %Y", gmtime()) + plt.title("Date: {}".format(date)) plt.loglog(mass, lifetime, label="Lifetime from mass") plt.loglog(mass_from_lifetime, lifetime, "--", label="Mass from lifetime") diff --git a/examples/supernovae_rate/plot_rates.py b/examples/supernovae_rate/plot_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..ac40330d786144d1b0c0379ed9f779b3c3f1f5a7 --- /dev/null +++ b/examples/supernovae_rate/plot_rates.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +from time import strftime, gmtime +plt.rc('text', usetex=True) + + +N = 1000 +solar_metallicity = 0.02041 +t_min = 1e6 # in yr +t_max = 3e10 +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 + +pyswiftsim.downloadYieldsTable() + +if __name__ == "__main__": + with pyswiftsim.ParameterFile() as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get times + times = np.logspace(np.log10(t_min), np.log10(t_max), N + 1, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + + times *= year_in_cgs / unit_time + + t1 = times[:-1] + t2 = times[1:] + + # get metallicity + Z = solar_metallicity * np.ones(N, dtype=np.float32) + + print("Computing supernovae rate...") + + rate_snia = libstellar.supernovaeIaRate(filename, t1, t2, Z) + rate_snii = libstellar.supernovaeIIRate(filename, t1, t2, Z) + + t1 /= 1e6 * year_in_cgs / unit_time + + unit_rate = year_in_cgs / unit_time + unit_rate *= solMass_in_cgs / unit_mass + + rate_snia *= 1e9 * unit_rate + rate_snii *= 1e9 * unit_rate + + plt.figure() + date = strftime("%d %b %Y", gmtime()) + plt.title("Date: {}".format(date)) + plt.loglog(t1, rate_snia, label="SNIa") + plt.loglog(t1, rate_snii, label="SNII") + + plt.xlabel("Time [Myr]") + plt.ylabel("Supernovae rate [$M_\odot^{-1} \mathrm{Gyr}^{-1}$]") + plt.legend() + + plt.show() diff --git a/examples/supernovae_rate/run.sh b/examples/supernovae_rate/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..4264cedc3634a1af6e88d23fc110956587a0932b --- /dev/null +++ b/examples/supernovae_rate/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python plot_rates.py diff --git a/examples/supernovae_yields/plot_yields.py b/examples/supernovae_yields/plot_yields.py new file mode 100644 index 0000000000000000000000000000000000000000..d8f1c94eacdf95735db86ee098bdf86f53093e70 --- /dev/null +++ b/examples/supernovae_yields/plot_yields.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +from time import strftime, gmtime +plt.rc('text', usetex=True) + + +N = 1000 +m_min = 0.08 # in solar mass +m_max = 50 +m_wd = 1.4 # white dwarf mass +solMass_in_cgs = 1.989e33 + +pyswiftsim.downloadYieldsTable() + + +if __name__ == "__main__": + with pyswiftsim.ParameterFile() as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get masses + mass = np.logspace(np.log10(m_min), np.log10(m_max), N, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + + mass *= solMass_in_cgs / unit_mass + + m1 = np.ones(N, dtype=np.float32) * 1e-20 # avoid log(0) + + print("Computing supernovae yields...") + + yields_snii = libstellar.supernovaeIIYields(filename, m1, mass) + mass_ejected_snii = libstellar.supernovaeIIMassEjected( + filename, m1, mass) + mass_ejected_np_snii = libstellar.supernovaeIIMassEjectedNonProcessed( + filename, m1, mass) + + yields_snia = libstellar.supernovaeIaYields(filename) + mass_ejected_snia = libstellar.supernovaeIaMassEjected(filename) + + elements = libstellar.getElements(filename) + + # units + mass /= solMass_in_cgs / unit_mass + + plt.figure() + date = strftime("%d %b %Y", gmtime()) + plt.title("Date: {}".format(date)) + for i in range(len(elements)): + p = plt.loglog(mass, yields_snii[:, i], label=elements[i]) + plt.loglog(m_wd, yields_snia[i], "o", color=p[0].get_color()) + + p = plt.loglog(mass, mass_ejected_snii, label="Mass Ejected") + plt.loglog(m_wd, mass_ejected_snia, "o", color=p[0].get_color()) + + plt.loglog(mass, mass_ejected_np_snii, + label="Non Processed Mass Ejected") + + plt.xlabel("$Mass [M_\odot]$") + plt.ylabel("Mass fraction") + plt.legend() + + plt.show() diff --git a/examples/supernovae_yields/run.sh b/examples/supernovae_yields/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..358d7c530922339afb1903ef7521946d5f6ebeb2 --- /dev/null +++ b/examples/supernovae_yields/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python plot_yields.py diff --git a/pyswiftsim/__init__.py b/pyswiftsim/__init__.py index 7852bc713c33d950db8883ca686b71f12e998b4f..de585175385c46080183b649f8d07a63798abf10 100644 --- a/pyswiftsim/__init__.py +++ b/pyswiftsim/__init__.py @@ -22,6 +22,32 @@ from tempfile import NamedTemporaryFile import yaml +def download(url, filename): + """ + Download a file from a given url. + + Parameters + ========== + + url: str + The url to download. + + filename: str + The filename to use for saving the file. + """ + progress = [0, 0] + + def report(count, size, total): + progress[0] = 100. * count * size / total + if progress[0] - progress[1] > 10: + progress[1] = progress[0] + print("Downloaded {}% ...".format(int(progress[1]))) + + if not os.path.isfile(filename): + # Download the file from `url` and save it locally under `file_name`: + urllib.request.urlretrieve(url, filename, reporthook=report) + + def downloadCoolingTable(): """ Download the cooling table if it does not already exist in the current @@ -30,10 +56,7 @@ def downloadCoolingTable(): url = "http://virgodb.cosma.dur.ac.uk/" url += "swift-webstorage/CoolingTables/CloudyData_UVB=HM2012.h5" filename = "CloudyData_UVB=HM2012.h5" - - if not os.path.isfile(filename): - # Download the file from `url` and save it locally under `file_name`: - urllib.request.urlretrieve(url, filename) + download(url, filename) def downloadYieldsTable(): @@ -41,13 +64,10 @@ def downloadYieldsTable(): Download the yields table if it does not already exist in the current repository. """ - url = "https://obswww.unige.ch/" - url += "lastro/projects/Clastro/PySWIFTsim/" + url = "https://obswww.unige.ch/lastro/projects/" + url += "Clastro/PySWIFTsim/chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5" filename = "chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5" - - if not os.path.isfile(filename): - # Download the file from `url` and save it locally under `file_name`: - urllib.request.urlretrieve(url, filename) + download(url, filename) def parseYamlFile(filename): @@ -78,7 +98,7 @@ def parseYamlFile(filename): >>> unit_mass = float(us["UnitMass_in_cgs"]) """ with open(filename, "r") as stream: - stream = yaml.load(stream) + stream = yaml.load(stream, Loader=yaml.SafeLoader) return stream @@ -139,39 +159,28 @@ class ParameterFile: }, "GrackleCooling": { - "CloudyTable": "CloudyData_UVB=HM2012.h5", - "WithUVbackground": 1, - "Redshift": 0, - "WithMetalCooling": 1, - "ProvideVolumetricHeatingRates": 0, - "ProvideSpecificHeatingRates": 0, - "SelfShieldingMethod": 0, - "ConvergenceLimit": 1e-2, - "MaxSteps": 20000, - "Omega": 0.8 + "cloudy_table": "CloudyData_UVB=HM2012.h5", + "with_UV_background": 1, + "redshift": 0, + "with_metal_cooling": 1, + "provide_volumetric_heating_rates": 0, + "provide_specific_heating_rates": 0, + "self_shielding_method": 0, + "convergence_limit": 1e-2, + "max_steps": 20000, + "omega": 0.8, + "thermal_time_myr": 5, }, - "GearChemistry": { - "InitialMetallicity": 0.01295, - }, - - "GEARInitialMassFunction": { - "number_function_part": 4, - "exponents": [0.7, -0.8, -1.7, -1.3], - "mass_limits": [0.05, 0.08, 0.5, 1.0, 50.] + "GEARChemistry": { + "initial_metallicity": 0.01295, }, "GEARFeedback": { - "SupernovaeEnergy_erg": 1e51, - "ThermalTime_Myr": 5, - "ChemistryTable": "chemistry.hdf5", + "supernovae_energy_erg": 1e51, + "yields_table": "chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5", + "discrete_yields": 0 }, - - "GEARLifetime": { - "quadratic": [-40.110, 5.509, 0.7824], - "linear": [141.929, -15.889, -3.2557], - "constant": [-261.365, 17.073, 9.8661], - } } """ Dictionnary containing all the default parameters to write in the diff --git a/scripts/pyswift_cooling_rate b/scripts/pyswift_cooling_rate index 262629c6debc640b90821d828f9190ef5b67d872..7c905944b6bda576c1048fcc11828912810c8f9e 100644 --- a/scripts/pyswift_cooling_rate +++ b/scripts/pyswift_cooling_rate @@ -25,7 +25,6 @@ import numpy as np import matplotlib.pyplot as plt from astropy import units, constants import yaml -from time import strftime, gmtime from argparse import ArgumentParser import os @@ -244,8 +243,6 @@ def plot_solution(rate, data, us, opt): cbar.set_ticklabels(tc) cbar.ax.set_ylabel("Log( Cooling Length / kpc )") - date = strftime("%d %b %Y", gmtime()) - plt.title("Date: {}".format(date)) plt.show() diff --git a/scripts/pyswift_initial_mass_function b/scripts/pyswift_initial_mass_function index 803e520495fc9d453963975de2e4100d20374bc5..d62cad5d5dd780f808b43c2f3c6bae9e35c3973e 100644 --- a/scripts/pyswift_initial_mass_function +++ b/scripts/pyswift_initial_mass_function @@ -26,6 +26,7 @@ from argparse import ArgumentParser import os plt.rc('text', usetex=True) +solMass_in_cgs = 1.98848e33 def parseArguments(): @@ -48,12 +49,12 @@ def parseArguments(): parser.add_argument( "--mass_min", dest="mass_min", - type=float, default=None, + type=float, default=0.05, help="Minimal mass to plot") parser.add_argument( "--mass_max", dest="mass_max", - type=float, default=None, + type=float, default=50, help="Maximal mass to plot") args = parser.parse_args() @@ -65,7 +66,7 @@ if __name__ == "__main__": opt = parseArguments() if opt.table: - pyswiftsim.downloadChemistryTable() + pyswiftsim.downloadYieldsTable() if opt.params is not None and not os.path.isfile(opt.params): raise Exception( @@ -74,24 +75,22 @@ if __name__ == "__main__": with pyswiftsim.ParameterFile(existing=opt.params) as filename: parser = pyswiftsim.parseYamlFile(filename) - imf_parser = parser["GEARInitialMassFunction"] + units = parser["InternalUnitSystem"] + unit_mass = float(units["UnitMass_in_cgs"]) - mass_limits = list(imf_parser["mass_limits"]) - if opt.mass_min is not None: - mass_min = np.log10(opt.mass_min) - else: - mass_min = np.log10(mass_limits[0]) - - if opt.mass_max is not None: - mass_max = np.log10(opt.mass_max) - else: - mass_max = np.log10(mass_limits[-1]) + mass_min = np.log10(opt.mass_min) + mass_max = np.log10(opt.mass_max) print("Computing initial mass function...") mass = np.logspace(mass_min, mass_max, opt.N, dtype=np.float32) + mass *= solMass_in_cgs / unit_mass + imf = libstellar.initialMassFunction(filename, mass) + imf *= solMass_in_cgs / unit_mass + mass /= solMass_in_cgs / unit_mass + # plot IMF plt.loglog(mass, imf) diff --git a/scripts/pyswift_lifetime b/scripts/pyswift_lifetime index 0b14c53f589d9a26c21841b4393536a000dc1eec..da0a88dfd0fae15762f91f070ab0639813e767d2 100644 --- a/scripts/pyswift_lifetime +++ b/scripts/pyswift_lifetime @@ -26,9 +26,15 @@ from argparse import ArgumentParser import os plt.rc('text', usetex=True) +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 + +mass_min = 0.05 +mass_max = 50 + def parseArguments(): - parser = ArgumentParser(description="Plot the initial mass function") + parser = ArgumentParser(description="Plot the lifetime of a star") parser.add_argument( "--download_table", dest="table", @@ -47,12 +53,12 @@ def parseArguments(): parser.add_argument( "--mass_min", dest="mass_min", - type=float, default=None, + type=float, default=0.05, help="Minimal mass to plot") parser.add_argument( "--mass_max", dest="mass_max", - type=float, default=None, + type=float, default=50, help="Maximal mass to plot") parser.add_argument( @@ -69,7 +75,7 @@ if __name__ == "__main__": opt = parseArguments() if opt.table: - pyswiftsim.downloadChemistryTable() + pyswiftsim.downloadYieldsTable() if opt.params is not None and not os.path.isfile(opt.params): raise Exception( @@ -78,28 +84,30 @@ if __name__ == "__main__": with pyswiftsim.ParameterFile(existing=opt.params) as filename: parser = pyswiftsim.parseYamlFile(filename) - imf_parser = parser["GEARInitialMassFunction"] - - mass_limits = list(imf_parser["mass_limits"]) - if opt.mass_min is not None: - mass_min = np.log10(opt.mass_min) - else: - mass_min = np.log10(mass_limits[0]) - if opt.mass_max is not None: - mass_max = np.log10(opt.mass_max) - else: - mass_max = np.log10(mass_limits[-1]) + mass_min = np.log10(opt.mass_min) + mass_max = np.log10(opt.mass_max) mass = np.logspace(mass_min, mass_max, opt.N, dtype=np.float32) + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = float(units["UnitMass_in_cgs"]) + unit_vel = float(units["UnitVelocity_in_cgs"]) + unit_length = float(units["UnitLength_in_cgs"]) + unit_time = unit_length / unit_vel + + mass *= solMass_in_cgs / unit_mass + # get metallicity Z = opt.metallicity * np.ones(opt.N, dtype=np.float32) print("Computing lifetime...") lifetime = libstellar.lifetimeFromMass(filename, mass, Z) - lifetime /= 1e6 + + mass /= solMass_in_cgs / unit_mass + lifetime /= 1e6 * year_in_cgs / unit_time plt.loglog(mass, lifetime) diff --git a/scripts/pyswift_plot_yields b/scripts/pyswift_plot_yields new file mode 100644 index 0000000000000000000000000000000000000000..469cd26f132089b2d8be50a389a2d2808e92c7d8 --- /dev/null +++ b/scripts/pyswift_plot_yields @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +from time import strftime, gmtime +from argparse import ArgumentParser +import os +plt.rc('text', usetex=True) + + +N = 1000 +m_min = 0.08 # in solar mass +m_max = 50 +m_wd = 1.4 # white dwarf mass +solMass_in_cgs = 1.989e33 + + +def parseArguments(): + parser = ArgumentParser(description="Plot the supernovae rate") + + parser.add_argument( + "--download_table", dest="table", + action="store_true", + help="Download the default chemistry table") + + parser.add_argument( + "--params", dest="params", + type=str, default=None, + help="SWIFT parameters file") + + parser.add_argument( + "--N", dest="N", + type=int, default=1000, + help="Number of points") + + parser.add_argument( + "--m_min", dest="m_min", + type=float, default=0.08, + help="Minimal mass to plot in solar mass") + + parser.add_argument( + "--m_max", dest="m_max", + type=float, default=50, + help="Maximal mass to plot in solar mass") + + parser.add_argument( + "--disable-snia", dest="disable_snia", + action="store_true", + help="Disable the SNIa") + + parser.add_argument( + "--disable-snii", dest="disable_snii", + action="store_true", + help="Disable the SNII") + + args = parser.parse_args() + + return args + + +if __name__ == "__main__": + opt = parseArguments() + + if opt.table: + pyswiftsim.downloadYieldsTable() + + if opt.params is not None and not os.path.isfile(opt.params): + raise Exception( + "The parameter file '{}' does not exist".format(opt.params)) + + with pyswiftsim.ParameterFile(existing=opt.params) as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get masses + mass = np.logspace(np.log10(opt.m_min), np.log10(opt.m_max), N, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = float(units["UnitMass_in_cgs"]) + unit_vel = float(units["UnitVelocity_in_cgs"]) + unit_length = float(units["UnitLength_in_cgs"]) + unit_time = unit_length / unit_vel + + mass *= solMass_in_cgs / unit_mass + + m1 = np.ones(N, dtype=np.float32) * 1e-20 # avoid log(0) + + print("Computing supernovae yields...") + + yields_snii = libstellar.supernovaeIIYields(filename, m1, mass) + mass_ejected_snii = libstellar.supernovaeIIMassEjected( + filename, m1, mass) + mass_ejected_np_snii = libstellar.supernovaeIIMassEjectedNonProcessed( + filename, m1, mass) + + yields_snia = libstellar.supernovaeIaYields(filename) + mass_ejected_snia = libstellar.supernovaeIaMassEjected(filename) + + elements = libstellar.getElements(filename) + + # units + mass /= solMass_in_cgs / unit_mass + + plt.figure() + date = strftime("%d %b %Y", gmtime()) + plt.title("Date: {}".format(date)) + for i in range(len(elements)): + p = plt.loglog(mass, yields_snii[:, i], label=elements[i]) + plt.loglog(m_wd, yields_snia[i], "o", color=p[0].get_color()) + + p = plt.loglog(mass, mass_ejected_snii, label="Mass Ejected") + plt.loglog(m_wd, mass_ejected_snia, "o", color=p[0].get_color()) + + plt.loglog(mass, mass_ejected_np_snii, + label="Non Processed Mass Ejected") + + plt.xlabel("$Mass [M_\odot]$") + plt.ylabel("Mass fraction") + plt.legend() + + plt.show() diff --git a/scripts/pyswift_supernovae_rate b/scripts/pyswift_supernovae_rate new file mode 100644 index 0000000000000000000000000000000000000000..93e461f8b4d6d112839968412acd470401e800a2 --- /dev/null +++ b/scripts/pyswift_supernovae_rate @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +from argparse import ArgumentParser +import os +plt.rc('text', usetex=True) + +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 + + +def parseArguments(): + parser = ArgumentParser(description="Plot the supernovae rate") + + parser.add_argument( + "--download_table", dest="table", + action="store_true", + help="Download the default chemistry table") + + parser.add_argument( + "--params", dest="params", + type=str, default=None, + help="SWIFT parameters file") + + parser.add_argument( + "--N", dest="N", + type=int, default=1000, + help="Number of points") + + parser.add_argument( + "--time_min", dest="time_min", + type=float, default=1., + help="Minimal time to plot in Myr") + + parser.add_argument( + "--time_max", dest="time_max", + type=float, default=3e4, + help="Maximal time to plot in Myr") + + parser.add_argument( + "--metallicity", dest="metallicity", + type=float, default=0.02041, + help="Metallicity of the stars") + + parser.add_argument( + "--disable-snia", dest="disable_snia", + action="store_true", + help="Disable the SNIa") + + parser.add_argument( + "--disable-snii", dest="disable_snii", + action="store_true", + help="Disable the SNII") + + args = parser.parse_args() + + return args + + +if __name__ == "__main__": + opt = parseArguments() + + if opt.table: + pyswiftsim.downloadYieldsTable() + + if opt.params is not None and not os.path.isfile(opt.params): + raise Exception( + "The parameter file '{}' does not exist".format(opt.params)) + + with pyswiftsim.ParameterFile(existing=opt.params) as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get times in yr + opt.time_min *= 1e6 + opt.time_max *= 1e6 + + times = np.logspace( + np.log10(opt.time_min), np.log10(opt.time_max), opt.N + 1, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = float(units["UnitMass_in_cgs"]) + unit_vel = float(units["UnitVelocity_in_cgs"]) + unit_length = float(units["UnitLength_in_cgs"]) + unit_time = unit_length / unit_vel + + times *= year_in_cgs / unit_time + + t1 = times[:-1] + t2 = times[1:] + + # get metallicity + Z = opt.metallicity * np.ones(opt.N, dtype=np.float32) + + print("Computing supernovae rate...") + + if not opt.disable_snia: + rate_snia = libstellar.supernovaeIaRate(filename, t1, t2, Z) + if not opt.disable_snii: + rate_snii = libstellar.supernovaeIIRate(filename, t1, t2, Z) + + t1 /= 1e6 * year_in_cgs / unit_time + + unit_rate = year_in_cgs / unit_time + unit_rate *= solMass_in_cgs / unit_mass + + if not opt.disable_snia: + rate_snia *= 1e9 * unit_rate + plt.loglog(t1, rate_snia, label="SNIa") + if not opt.disable_snii: + rate_snii *= 1e9 * unit_rate + plt.loglog(t1, rate_snii, label="SNII") + + plt.xlabel("Time [Myr]") + plt.ylabel("Supernovae rate [$M_\odot^{-1} Gyr^{-1}$]") + plt.legend() + + plt.show() diff --git a/setup.py b/setup.py index 62a38d6006d0bd59d22d2e1efdd3254612664d7c..c8c1ee94860e164a356784a8dd1a3fbc6e02f3ca 100644 --- a/setup.py +++ b/setup.py @@ -80,9 +80,11 @@ if swift_path: ]) # hdf5 + if "/usr/bin" in hdf5_root: + include.append("/usr/include/hdf5/serial") + if "bin" in hdf5_root: hdf5_root = hdf5_root.split("bin")[0] - include.append(hdf5_root + "/include") # grackle if parseCmdLine("--with-grackle", store=True): @@ -101,6 +103,8 @@ lib_dir = [] if swift_path: lib_dir.append(swift_path + "/src/.libs") lib_dir.append(hdf5_root + "/lib") + # fix for default install of hdf5 + lib_dir.append(hdf5_root + "/lib/x86_64-linux-gnu/hdf5") # src files c_src = [] diff --git a/src/cooling_wrapper.c b/src/cooling_wrapper.c index 4fa9731fb44d1a66db652c95c553ab590ddc8dd9..dbe718c9f17cf5c7f5ef01f313b5d694bd60ca0c 100644 --- a/src/cooling_wrapper.c +++ b/src/cooling_wrapper.c @@ -19,11 +19,6 @@ #include "cooling_wrapper.h" #include "pyswiftsim_tools.h" -/* TODO Remove this */ -struct cooling_function_data cooling; -int initialized; - - /** * @brief Set the cooling element fractions * @@ -79,7 +74,6 @@ void pycooling_set_fractions(struct xpart *xp, PyArrayObject* frac, const int id */ PyObject* pycooling_rate(PyObject* self, PyObject* args) { import_array(); - char *filename; @@ -125,7 +119,6 @@ PyObject* pycooling_rate(PyObject* self, PyObject* args) { struct chemistry_global_data chemistry; chemistry_init_backend(¶ms, &us, &pconst, &chemistry); - /* Init entropy floor */ struct entropy_floor_properties floor_props; entropy_floor_init(&floor_props, &pconst, &us, @@ -135,7 +128,7 @@ PyObject* pycooling_rate(PyObject* self, PyObject* args) { struct xpart xp; hydro_first_init_part(&p, &xp); - + /* return object */ PyArrayObject *rate = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(energy), PyArray_DIMS(energy), NPY_FLOAT); @@ -150,20 +143,24 @@ PyObject* pycooling_rate(PyObject* self, PyObject* args) { chemistry_first_init_part(&pconst, &us, &cosmo, &chemistry, &p, &xp); if (fractions != NULL) - pycooling_set_fractions(&xp, fractions, i); + pycooling_set_fractions(&xp, fractions, i); else { - /* Need to set the mass in order to estimate the element fractions */ - hydro_set_mass(&p, p.rho); - p.h = 1; + /* Need to set the mass in order to estimate the element fractions */ + hydro_set_mass(&p, p.rho); + p.h = 1; - cooling_first_init_part(&pconst, &us, &cosmo, &cooling, &p, &xp); + cooling_first_init_part(&pconst, &us, &hydro_props, &cosmo, &cooling, &p, &xp); } hydro_set_physical_internal_energy_dt(&p, &cosmo, 0); + for(int j = 0; j < GEAR_CHEMISTRY_ELEMENT_COUNT; j++) { + p.chemistry_data.smoothed_metal_mass_fraction[j] = p.chemistry_data.metal_mass_fraction[j]; + } + /* compute cooling rate */ cooling_cool_part(&pconst, &us, &cosmo, &hydro_props, - &floor_props, &cooling, &p, &xp, dt, dt); + &floor_props, &cooling, &p, &xp, /* Time */0, dt, dt); float *tmp = PyArray_GETPTR1(rate, i); *tmp = hydro_get_physical_internal_energy_dt(&p, &cosmo); @@ -171,6 +168,7 @@ PyObject* pycooling_rate(PyObject* self, PyObject* args) { /* Cleanup */ cosmology_clean(&cosmo); + cooling_clean(&cooling); return (PyObject *) rate; diff --git a/src/cooling_wrapper.h b/src/cooling_wrapper.h index e0b97a9c9f70d9033dfaac54ae75bb061f8dfb35..8491ff58ae39a044d0fa2e0f979f56ccb3a96481 100644 --- a/src/cooling_wrapper.h +++ b/src/cooling_wrapper.h @@ -21,26 +21,12 @@ #include "pyswiftsim_includes.h" -/* A few global variables */ -extern struct cooling_function_data cooling; -extern int initialized; - - PyObject* pycooling_rate(PyObject* self, PyObject* args); -#define PYSWIFTSIM_INIT_COOLING() \ - \ - /* init cooling */ \ - if (initialized == 0) { \ - /* struct cooling_function_data cooling; */ \ - cooling_init_backend(¶ms, &us, &pconst, &cooling); \ - initialized = 1; \ - } \ - else if (initialized == 1) { \ - message("WARNING: not reinitializing the cooling, need to be fixed"); \ - initialized = 2; \ - } \ - \ +#define PYSWIFTSIM_INIT_COOLING() \ + struct cooling_function_data cooling; \ + cooling_init_backend(¶ms, &us, &pconst, &hydro_props, \ + &cooling); \ #endif // __PYSWIFTSIM_COOLING_H__ diff --git a/src/libstellar.c b/src/libstellar.c index fcd1cb304d5207fd409eb9461337e0bac03ac60e..412bd143057329c9e71ea02ec5db430e5e32234c 100644 --- a/src/libstellar.c +++ b/src/libstellar.c @@ -96,7 +96,181 @@ static PyMethodDef libstellar_methods[] = { ">>> mass = libstellar.massFromLifetime(filename, lifetime, Z)\n" }, + {"supernovaeIaRate", pysupernovae_ia_rate, METH_VARARGS, + "Compute the supernovae Ia rate from the time and the stellar metallicity.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "time1: array\n" + "\t Beginning of the time interval for the SNIa in internal units.\n" + "time2: array\n" + "\t End of the time interval for the SNIa in internal units.\n" + "metallicity: array\n" + "\t Metallicity of the stars.\n" + "\n" + "Returns\n" + "-------\n" + "rate: array\n" + "\t The rate of SNIa.\n" + "\n" + "Examples\n" + "--------\n" + ">>> time1 = np.array([1e7, 1e9])\n" + ">>> time2 = np.array([2e7, 2e9])\n" + ">>> Z = np.array([0.02, 0.01])\n" + ">>> filename = 'params.yml'\n" + ">>> mass = libstellar.supernovaeIaRate(filename, time1, time2, Z)\n" + }, + + {"supernovaeIIRate", pysupernovae_ii_rate, METH_VARARGS, + "Compute the supernovae II rate from the time and the stellar metallicity.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "time1: array\n" + "\t Beginning of the time interval for the SNII in internal units.\n" + "time2: array\n" + "\t End of the time interval for the SNII in internal units.\n" + "metallicity: array\n" + "\t Metallicity of the stars.\n" + "\n" + "Returns\n" + "-------\n" + "rate: array\n" + "\t The rate of SNII.\n" + "\n" + "Examples\n" + "--------\n" + ">>> time1 = np.array([1e7, 1e9])\n" + ">>> time2 = np.array([2e7, 2e9])\n" + ">>> Z = np.array([0.02, 0.01])\n" + ">>> filename = 'params.yml'\n" + ">>> mass = libstellar.supernovaeIIRate(filename, time1, time2, Z)\n" + }, + + {"supernovaeIIYields", pysupernovae_ii_yields, METH_VARARGS, + "Compute the supernovae II yields from the masses.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "mass1: array\n" + "\t Beginning of the mass interval for the SNII in internal units.\n" + "mass2: array\n" + "\t End of the time interval for the SNII in internal units.\n" + "\n" + "Returns\n" + "-------\n" + "yields: array\n" + "\t The yields (mass fraction) of SNII.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass1 = np.array([1.1, 1.3])\n" + ">>> mass2 = np.array([1.2, 1.4])\n" + ">>> mass = libstellar.supernovaeIIYields('params.yml', mass1, mass2)\n" + }, + {"supernovaeIaYields", pysupernovae_ia_yields, METH_VARARGS, + "Compute the supernovae Ia yields from the masses.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "\n" + "Returns\n" + "-------\n" + "yields: array\n" + "\t The yields (mass fraction) of SNIa.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass = libstellar.supernovaeIaYields('params.yml')\n" + }, + + {"getElements", pystellar_get_elements, METH_VARARGS, + "Read the element names..\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "\n" + "Returns\n" + "-------\n" + "element_names: array\n" + "\t The name of all the elements.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass = libstellar.getElements('params.yml')\n" + "[Fe, Mg, O, S, Zn, Sr, Y, Ba, Eu, Metals]" + }, + + {"supernovaeIIMassEjected", pysupernovae_ii_mass_ejected, METH_VARARGS, + "Compute the mass ejected by supernovae II from the masses.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "mass1: array\n" + "\t Beginning of the mass interval for the SNII in internal units.\n" + "mass2: array\n" + "\t End of the time interval for the SNII in internal units.\n" + "\n" + "Returns\n" + "-------\n" + "mass_ejected: array\n" + "\t The mass ejected by the supernovae SNII.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass1 = np.array([1.1, 1.3])\n" + ">>> mass2 = np.array([1.2, 1.4])\n" + ">>> mass = libstellar.supernovaeIIMassEjected('params.yml', mass1, mass2)\n" + }, + + {"supernovaeIIMassEjectedNonProcessed", pysupernovae_ii_mass_ejected_non_processed, METH_VARARGS, + "Compute the (non processed) mass ejected by supernovae II from the masses.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "mass1: array\n" + "\t Beginning of the mass interval for the SNII in internal units.\n" + "mass2: array\n" + "\t End of the time interval for the SNII in internal units.\n" + "\n" + "Returns\n" + "-------\n" + "mass_ejected: array\n" + "\t The non processed mass ejected by the supernovae SNII.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass1 = np.array([1.1, 1.3])\n" + ">>> mass2 = np.array([1.2, 1.4])\n" + ">>> mass = libstellar.supernovaeIIMassEjectedNonProcessed('params.yml', mass1, mass2)\n" + }, + + {"supernovaeIaMassEjected", pysupernovae_ia_mass_ejected, METH_VARARGS, + "Compute the mass ejected by supernovae Ia.\n\n" + "Parameters\n" + "----------\n\n" + "filename: str\n" + "\t Parameter file\n" + "\n" + "Returns\n" + "-------\n" + "mass_ejected: float\n" + "\t The mass ejected by the supernovae SNIa.\n" + "\n" + "Examples\n" + "--------\n" + ">>> mass = libstellar.supernovaeIaMassEjected('params.yml')\n" + }, + {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/src/stellar_evolution_wrapper.c b/src/stellar_evolution_wrapper.c index a990b753d2c05ddc620f2115bce366a75fc4ce09..3a23c8d0fd80f6fbff27cc02c2b06e8fc267cf28 100644 --- a/src/stellar_evolution_wrapper.c +++ b/src/stellar_evolution_wrapper.c @@ -63,8 +63,18 @@ PyObject* pyinitial_mass_function(PyObject* self, PyObject* args) { 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; - *imf_i = stellar_evolution_get_imf(&fp.stellar_model.imf, m); + 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; @@ -75,11 +85,13 @@ PyObject* pyinitial_mass_function(PyObject* self, PyObject* args) { * @brief Compute the lifetime of a star. * * args is expecting: - * TODO + * - 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 cooling rate + * @return the lifetime of the stars */ PyObject* pylifetime_from_mass(PyObject* self, PyObject* args) { import_array(); @@ -122,9 +134,12 @@ PyObject* pylifetime_from_mass(PyObject* self, PyObject* args) { 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 = stellar_evolution_get_log_lifetime_from_mass(&fp.stellar_model.lifetime, log10(m), z); - *tau = pow(10, *tau); + *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; @@ -135,11 +150,13 @@ PyObject* pylifetime_from_mass(PyObject* self, PyObject* args) { * @brief Compute the mass of a star with a given lifetime. * * args is expecting: - * TODO + * - 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 cooling rate + * @return The mass of the stars. */ PyObject* pymass_from_lifetime(PyObject* self, PyObject* args) { import_array(); @@ -183,10 +200,513 @@ PyObject* pymass_from_lifetime(PyObject* self, PyObject* args) { float *m = (float*) PyArray_GETPTR1(mass, i); - *m = stellar_evolution_get_log_mass_from_lifetime(&fp.stellar_model.lifetime, log10(tau), z); - *m = pow(10, *m); + /* 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(&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(&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(&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; + +} diff --git a/src/stellar_evolution_wrapper.h b/src/stellar_evolution_wrapper.h index a76b04d39b1733a8667cc9435859cfeb9fbfbeea..2c3725eeab2837c8d152c016d787e3cd49f26512 100644 --- a/src/stellar_evolution_wrapper.h +++ b/src/stellar_evolution_wrapper.h @@ -24,6 +24,15 @@ PyObject* pyinitial_mass_function(PyObject* self, PyObject* args); PyObject* pylifetime_from_mass(PyObject* self, PyObject* args); PyObject* pymass_from_lifetime(PyObject* self, PyObject* args); +PyObject* pysupernovae_ia_rate(PyObject* self, PyObject* args); +PyObject* pysupernovae_ii_rate(PyObject* self, PyObject* args); +PyObject* pysupernovae_ia_yields(PyObject* self, PyObject* args); +PyObject* pysupernovae_ii_yields(PyObject* self, PyObject* args); +PyObject* pysupernovae_ia_mass_ejected(PyObject* self, PyObject* args); +PyObject* pysupernovae_ii_mass_ejected(PyObject* self, PyObject* args); +PyObject* pysupernovae_ii_mass_ejected_non_processed(PyObject* self, PyObject* args); +PyObject* pystellar_get_elements(PyObject* self, PyObject* args); + #endif // __PYSWIFTSIM_STELLAR_H__ diff --git a/tests/test_initial_mass_function.py b/tests/test_initial_mass_function.py index ca80c505bb39844035953934cdb84bf221591068..d77b0e24b2b21f831cc5d05b8ec58ee8fe831d11 100644 --- a/tests/test_initial_mass_function.py +++ b/tests/test_initial_mass_function.py @@ -26,22 +26,29 @@ plt.rc('text', usetex=True) N = 100 +solMass_in_cgs = 1.989e33 +mass_min = 0.5 +mass_max = 45 +pyswiftsim.downloadYieldsTable() if __name__ == "__main__": with pyswiftsim.ParameterFile() as filename: parser = pyswiftsim.parseYamlFile(filename) - imf = parser["GEARInitialMassFunction"] - mass_limits = imf["mass_limits"] - mass_min = float(mass_limits[0]) - mass_max = float(mass_limits[1]) mass = np.linspace(mass_min, mass_max, N, dtype=np.float32) + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + # change the units + mass *= solMass_in_cgs / unit_mass + print("Computing initial mass function...") imf = libstellar.initialMassFunction(filename, mass) + imf *= solMass_in_cgs / unit_mass + print("IMF obtained: {}".format(imf)) diff --git a/tests/test_lifetime.py b/tests/test_lifetime.py index 9a1816e22438b5a857a9945e571961af280cccbc..cc1f75ef9f0ae453ba8065f3d6b20d548fc1b9c7 100644 --- a/tests/test_lifetime.py +++ b/tests/test_lifetime.py @@ -27,20 +27,26 @@ plt.rc('text', usetex=True) N = 100 solar_metallicity = 0.02041 - +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 +mass_min = 0.05 # in solar mass +mass_max = 50 if __name__ == "__main__": with pyswiftsim.ParameterFile() as filename: parser = pyswiftsim.parseYamlFile(filename) - # Get masses - imf = parser["GEARInitialMassFunction"] - mass_limits = imf["mass_limits"] - mass_min = float(mass_limits[0]) - mass_max = float(mass_limits[1]) mass = np.linspace(mass_min, mass_max, N, dtype=np.float32) + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + # change the units + mass *= solMass_in_cgs / unit_mass + # get metallicity Z = solar_metallicity * np.ones(N, dtype=np.float32) @@ -49,6 +55,10 @@ if __name__ == "__main__": lifetime = libstellar.lifetimeFromMass(filename, mass, Z) mass_from_lifetime = libstellar.massFromLifetime(filename, lifetime, Z) + mass /= solMass_in_cgs / unit_mass + mass_from_lifetime /= solMass_in_cgs / unit_mass + lifetime /= year_in_cgs / unit_time + print("Mass used: {}".format(mass)) print("Lifetime obtained: {}".format(lifetime)) diff --git a/tests/test_rates.py b/tests/test_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..6d879d62712ced21ba6a98a9d83e5d616ab946f1 --- /dev/null +++ b/tests/test_rates.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +plt.rc('text', usetex=True) + + +N = 100 +solar_metallicity = 0.02041 +t_min = 1e6 # in yr +t_max = 1e10 +solMass_in_cgs = 1.989e33 +year_in_cgs = 3.15569252e7 + +pyswiftsim.downloadYieldsTable() + + +if __name__ == "__main__": + with pyswiftsim.ParameterFile() as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get times + times = np.logspace(np.log10(t_min), np.log10(t_max), N + 1, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + + times *= year_in_cgs / unit_time + + t1 = times[:-1] + t2 = times[1:] + + # get metallicity + Z = solar_metallicity * np.ones(N, dtype=np.float32) + + print("Computing supernovae rate...") + + rate = libstellar.supernovaeIaRate(filename, t1, t2, Z) + rate += libstellar.supernovaeIIRate(filename, t1, t2, Z) + + rate *= year_in_cgs / unit_time + rate *= solMass_in_cgs / unit_mass + + t1 /= year_in_cgs / unit_time + + print("Time used: {}".format(t1)) + + print("Rate obtained: {}".format(rate)) diff --git a/tests/test_yields.py b/tests/test_yields.py new file mode 100644 index 0000000000000000000000000000000000000000..0753da7b998257e7c81145a7b69d5e8f741177e8 --- /dev/null +++ b/tests/test_yields.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# ######################################################################## +# This file is part of PYSWIFT. +# 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 <http://www.gnu.org/licenses/>. +# ######################################################################## + +from pyswiftsim import libstellar +import pyswiftsim + +import numpy as np +import matplotlib.pyplot as plt +plt.rc('text', usetex=True) + + +N = 10 +m_min = 1 # in solar mass +m_max = 50 +solMass_in_cgs = 1.989e33 + +pyswiftsim.downloadYieldsTable() + + +if __name__ == "__main__": + with pyswiftsim.ParameterFile() as filename: + parser = pyswiftsim.parseYamlFile(filename) + + # Get masses + mass = np.logspace(np.log10(m_min), np.log10(m_max), N + 1, + dtype=np.float32) + + # transform into internal units + units = parser["InternalUnitSystem"] + unit_mass = units["UnitMass_in_cgs"] + unit_vel = units["UnitVelocity_in_cgs"] + unit_length = units["UnitLength_in_cgs"] + unit_time = unit_length / unit_vel + + mass *= solMass_in_cgs / unit_mass + + m1 = mass[:-1] + m2 = mass[1:] + + print("Computing supernovae yields...") + + yields_snii = libstellar.supernovaeIIYields(filename, m1, m2) + mass_ejected_snii = libstellar.supernovaeIIMassEjected( + filename, m1, m2) + mass_ejected_np_snii = libstellar.supernovaeIIMassEjectedNonProcessed( + filename, m1, m2) + + m1 /= solMass_in_cgs / unit_mass + + yields_snia = libstellar.supernovaeIaYields(filename) + mass_ejected_snia = libstellar.supernovaeIaMassEjected(filename) + + print("Mass used: {}".format(m1)) + + print("SNII yields obtained: {}".format(yields_snii)) + + print("SNIa yields obtained: {}".format(yields_snia)) + + print("SNII mass ejected: {}".format(mass_ejected_snii)) + + print("SNIa mass ejected: {}".format(mass_ejected_snia)) + + print("SNII non processed mass ejected: {}".format( + mass_ejected_np_snii))