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/pyswiftsim/__init__.py b/pyswiftsim/__init__.py index 32b6d181e0160b2239e508181044d860bc81f4b6..de585175385c46080183b649f8d07a63798abf10 100644 --- a/pyswiftsim/__init__.py +++ b/pyswiftsim/__init__.py @@ -159,26 +159,27 @@ 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, - "ThermalTime_Myr": 5, + "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, + "GEARChemistry": { + "initial_metallicity": 0.01295, }, "GEARFeedback": { - "SupernovaeEnergy_erg": 1e51, - "YieldsTable": "chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5", + "supernovae_energy_erg": 1e51, + "yields_table": "chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5", + "discrete_yields": 0 }, } """ diff --git a/scripts/pyswift_lifetime b/scripts/pyswift_lifetime index b576e86d91b2dd1dc1643ac89af9dd368a2951ad..da0a88dfd0fae15762f91f070ab0639813e767d2 100644 --- a/scripts/pyswift_lifetime +++ b/scripts/pyswift_lifetime @@ -92,9 +92,9 @@ if __name__ == "__main__": # 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_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 diff --git a/src/cooling_wrapper.c b/src/cooling_wrapper.c index 116d94159e7a4fae312a72a3403a374b9bf394e5..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,16 +143,20 @@ 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, @@ -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/stellar_evolution_wrapper.c b/src/stellar_evolution_wrapper.c index 007e369a8a5550e95d3d15cdfbb0ab2fb882d22d..3a23c8d0fd80f6fbff27cc02c2b06e8fc267cf28 100644 --- a/src/stellar_evolution_wrapper.c +++ b/src/stellar_evolution_wrapper.c @@ -72,6 +72,9 @@ PyObject* pyinitial_mass_function(PyObject* self, PyObject* args) { } *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; @@ -280,7 +283,7 @@ PyObject* pysupernovae_ia_rate(PyObject* self, PyObject* args) { m2 = pow(10, m2); /* Need to reverse 1 and 2 due to the lifetime being a decreasing function */ - *r = supernovae_ia_get_number(&fp.stellar_model.snia, m2, m1) / (t2 - t1); + *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; @@ -360,7 +363,7 @@ PyObject* pysupernovae_ii_rate(PyObject* self, PyObject* args) { m2 = pow(10, m2); /* Need to reverse 1 and 2 due to the lifetime being a decreasing function */ - *r = supernovae_ii_get_number(&fp.stellar_model.snii, m2, m1) / (t2 - t1); + *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; @@ -421,11 +424,11 @@ PyObject* pysupernovae_ii_yields(PyObject* self, PyObject* args) { /* return object */ const int ndims = 2; - npy_intp dims[2] = {N, CHEMISTRY_ELEMENT_COUNT}; + 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[CHEMISTRY_ELEMENT_COUNT] = {0.}; + 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; @@ -434,7 +437,7 @@ PyObject* pysupernovae_ii_yields(PyObject* self, PyObject* args) { /* Compute the yields */ supernovae_ii_get_yields(&fp.stellar_model.snii, log_m1, log_m2, yields); - for(int j = 0; j < CHEMISTRY_ELEMENT_COUNT; j++) { + for(int j = 0; j < GEAR_CHEMISTRY_ELEMENT_COUNT; j++) { float *y = (float*) PyArray_GETPTR2(yields_out, i, j); *y = yields[j]; } @@ -608,12 +611,12 @@ PyObject* pysupernovae_ia_yields(PyObject* self, PyObject* args) { /* return object */ const int ndims = 1; - npy_intp dims[1] = {CHEMISTRY_ELEMENT_COUNT}; + 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 < CHEMISTRY_ELEMENT_COUNT; i++) { + 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; @@ -658,7 +661,7 @@ PyObject* pysupernovae_ia_mass_ejected(PyObject* self, PyObject* args) { float *y = (float*) PyArray_GETPTR1(mass_ejected, 0); - *y = supernovae_ia_get_ejected_mass_fraction_processed(&fp.stellar_model.snia) / fp.stellar_model.snia.mass_white_dwarf; + *y = supernovae_ia_get_ejected_mass_processed(&fp.stellar_model.snia) / fp.stellar_model.snia.mass_white_dwarf; return (PyObject *) mass_ejected; @@ -693,9 +696,9 @@ PyObject* pystellar_get_elements(PyObject* self, PyObject* args) { feedback_props_init(&fp, &pconst, &us, ¶ms, &hydro_props, &cosmo); /* Save the variables */ - PyObject *t = PyList_New(CHEMISTRY_ELEMENT_COUNT); + PyObject *t = PyList_New(GEAR_CHEMISTRY_ELEMENT_COUNT); - for(int i = 0; i < CHEMISTRY_ELEMENT_COUNT; i++) { + 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);