/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl) * * 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 . * ******************************************************************************/ /** * @file src/cooling/QLA/cooling_tables.c * @brief Functions to read QLA tables */ /* Config parameters. */ #include /* This file's header */ #include "cooling_tables.h" /* Standard includes */ #include #include #include #include /* Local includes. */ #include "chemistry_struct.h" #include "cooling_properties.h" #include "error.h" #include "exp10.h" #include "interpolate.h" /** * @brief Reads in PS2020 cooling table header. Consists of tables * of values for temperature, hydrogen number density, metallicity, * abundance ratios, and elements used to index the cooling tables. * * @param cooling Cooling data structure */ void read_cooling_header(struct cooling_function_data *cooling) { #ifdef HAVE_HDF5 hid_t dataset; herr_t status; /* read sizes of array dimensions */ hid_t tempfile_id = H5Fopen(cooling->cooling_table_path, H5F_ACC_RDONLY, H5P_DEFAULT); if (tempfile_id < 0) error("unable to open file %s\n", cooling->cooling_table_path); /* allocate arrays of bins */ if (posix_memalign((void **)&cooling->Temp, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_temperature * sizeof(float)) != 0) error("Failed to allocate temperature table\n"); if (posix_memalign((void **)&cooling->Redshifts, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * sizeof(float)) != 0) error("Failed to allocate redshift table\n"); if (posix_memalign((void **)&cooling->nH, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate density table\n"); if (posix_memalign((void **)&cooling->Metallicity, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * sizeof(float)) != 0) error("Failed to allocate metallicity table\n"); if (posix_memalign((void **)&cooling->Therm, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_internalenergy * sizeof(float)) != 0) error("Failed to allocate internal energy table\n"); if (posix_memalign((void **)&cooling->LogAbundances, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate abundance array\n"); if (posix_memalign((void **)&cooling->Abundances, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate abundance array\n"); if (posix_memalign((void **)&cooling->Abundances_inv, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate abundance array\n"); if (posix_memalign((void **)&cooling->atomicmass, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate atomic masses array\n"); if (posix_memalign((void **)&cooling->atomicmass_inv, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate inverse atomic masses array\n"); if (posix_memalign((void **)&cooling->Zsol, SWIFT_STRUCT_ALIGNMENT, 1 * sizeof(float)) != 0) error("Failed to allocate solar metallicity array\n"); if (posix_memalign((void **)&cooling->Zsol_inv, SWIFT_STRUCT_ALIGNMENT, 1 * sizeof(float)) != 0) error("Failed to allocate inverse solar metallicity array\n"); if (posix_memalign((void **)&cooling->LogMassFractions, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate log mass fraction array\n"); if (posix_memalign((void **)&cooling->MassFractions, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_metallicity * qla_cooling_N_elementtypes * sizeof(float)) != 0) error("Failed to allocate mass fraction array\n"); /* read in bins and misc information */ dataset = H5Dopen(tempfile_id, "/TableBins/TemperatureBins", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->Temp); if (status < 0) error("error reading temperature bins\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TableBins/RedshiftBins", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->Redshifts); if (status < 0) error("error reading redshift bins\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TableBins/DensityBins", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->nH); if (status < 0) error("error reading density bins\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TableBins/MetallicityBins", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->Metallicity); if (status < 0) error("error reading metallicity bins\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TableBins/InternalEnergyBins", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->Therm); if (status < 0) error("error reading internal energy bins\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TotalAbundances", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->LogAbundances); if (status < 0) error("error reading total abundances\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/TotalMassFractions", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->LogMassFractions); if (status < 0) error("error reading total mass fractions\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/ElementMasses", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->atomicmass); if (status < 0) error("error reading element masses\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); dataset = H5Dopen(tempfile_id, "/SolarMetallicity", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->Zsol); if (status < 0) error("error reading solar metallicity \n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Close the file */ H5Fclose(tempfile_id); cooling->Zsol_inv[0] = 1.f / cooling->Zsol[0]; /* find the metallicity bin that refers to solar metallicity */ const float tol = 1.e-3; for (int i = 0; i < qla_cooling_N_metallicity; i++) { if (fabsf(cooling->Metallicity[i]) < tol) { cooling->indxZsol = i; } } #if defined(__ICC) #pragma novector #endif for (int i = 0; i < qla_cooling_N_elementtypes; i++) { cooling->atomicmass_inv[i] = 1.f / cooling->atomicmass[i]; } /* set some additional useful abundance arrays */ for (int i = 0; i < qla_cooling_N_metallicity; i++) { #if defined(__ICC) #pragma novector #endif for (int j = 0; j < qla_cooling_N_elementtypes; j++) { const int indx1d = row_major_index_2d(i, j, qla_cooling_N_metallicity, qla_cooling_N_elementtypes); cooling->Abundances[indx1d] = exp10f(cooling->LogAbundances[indx1d]); cooling->Abundances_inv[indx1d] = 1.f / cooling->Abundances[indx1d]; cooling->MassFractions[indx1d] = exp10f(cooling->LogMassFractions[indx1d]); } } #else error("Need HDF5 to read cooling tables"); #endif } /** * @brief Allocate space for cooling tables and read them * * @param cooling #cooling_function_data structure */ void read_cooling_tables(struct cooling_function_data *restrict cooling) { #ifdef HAVE_HDF5 hid_t dataset; herr_t status; /* open hdf5 file */ hid_t tempfile_id = H5Fopen(cooling->cooling_table_path, H5F_ACC_RDONLY, H5P_DEFAULT); if (tempfile_id < 0) error("unable to open file %s\n", cooling->cooling_table_path); /* Allocate and read arrays to store cooling tables. */ /* Mean particle mass (temperature) */ if (swift_memalign("cooling_table.Tmu", (void **)&cooling->table.Tmu, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate Tmu array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/MeanParticleMass", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Tmu); if (status < 0) error("error reading Tmu\n"); status = H5Dclose(dataset); if (status < 0) error("error closing mean particle mass dataset"); /* Mean particle mass (internal energy) */ if (swift_memalign("cooling_table.Umu", (void **)&cooling->table.Umu, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_internalenergy * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate Umu array\n"); dataset = H5Dopen(tempfile_id, "/Udep/MeanParticleMass", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Umu); if (status < 0) error("error reading Umu\n"); status = H5Dclose(dataset); if (status < 0) error("error closing mean particle mass dataset"); /* Cooling (temperature) */ if (swift_memalign("cooling_table.Tcooling", (void **)&cooling->table.Tcooling, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_cooltypes * sizeof(float)) != 0) error("Failed to allocate Tcooling array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/Cooling", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Tcooling); if (status < 0) error("error reading Tcooling\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Cooling (internal energy) */ if (swift_memalign("cooling_table.Ucooling", (void **)&cooling->table.Ucooling, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_internalenergy * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_cooltypes * sizeof(float)) != 0) error("Failed to allocate Ucooling array\n"); dataset = H5Dopen(tempfile_id, "/Udep/Cooling", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Ucooling); if (status < 0) error("error reading Ucooling\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Heating (temperature) */ if (swift_memalign("cooling_table.Theating", (void **)&cooling->table.Theating, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_heattypes * sizeof(float)) != 0) error("Failed to allocate Theating array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/Heating", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Theating); if (status < 0) error("error reading Theating\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Heating (internal energy) */ if (swift_memalign("cooling_table.Uheating", (void **)&cooling->table.Uheating, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_internalenergy * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_heattypes * sizeof(float)) != 0) error("Failed to allocate Uheating array\n"); dataset = H5Dopen(tempfile_id, "/Udep/Heating", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Uheating); if (status < 0) error("error reading Uheating\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Electron fraction (temperature) */ if (swift_memalign("cooling_table.Tefrac", (void **)&cooling->table.Telectron_fraction, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_electrontypes * sizeof(float)) != 0) error("Failed to allocate Telectron_fraction array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/ElectronFractionsVol", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Telectron_fraction); if (status < 0) error("error reading electron_fraction (temperature)\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Electron fraction (internal energy) */ if (swift_memalign("cooling_table.Uefrac", (void **)&cooling->table.Uelectron_fraction, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_internalenergy * qla_cooling_N_metallicity * qla_cooling_N_density * qla_cooling_N_electrontypes * sizeof(float)) != 0) error("Failed to allocate Uelectron_fraction array\n"); dataset = H5Dopen(tempfile_id, "/Udep/ElectronFractionsVol", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.Uelectron_fraction); if (status < 0) error("error reading electron_fraction (internal energy)\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Internal energy from temperature */ if (swift_memalign("cooling_table.UfromT", (void **)&cooling->table.U_from_T, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate U_from_T array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/U_from_T", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.U_from_T); if (status < 0) error("error reading U_from_T array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Temperature from interal energy */ if (swift_memalign("cooling_table.TfromU", (void **)&cooling->table.T_from_U, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_internalenergy * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate T_from_U array\n"); dataset = H5Dopen(tempfile_id, "/Udep/T_from_U", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.T_from_U); if (status < 0) error("error reading T_from_U array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing cooling dataset"); /* Thermal equilibrium temperature */ if (swift_memalign("cooling_table.Teq", (void **)&cooling->table.logTeq, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate logTeq array\n"); dataset = H5Dopen(tempfile_id, "/ThermEq/Temperature", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.logTeq); if (status < 0) error("error reading Teq array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing logTeq dataset"); /* Mean particle mass at thermal equilibrium temperature */ if (swift_memalign("cooling_table.mueq", (void **)&cooling->table.meanpartmass_Teq, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate mu array\n"); dataset = H5Dopen(tempfile_id, "/ThermEq/MeanParticleMass", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.meanpartmass_Teq); if (status < 0) error("error reading mu array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing mu dataset"); /* Hydrogen fractions at thermal equilibirum temperature */ if (swift_memalign("cooling_table.Hfracs", (void **)&cooling->table.logHfracs_Teq, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_metallicity * qla_cooling_N_density * 3 * sizeof(float)) != 0) error("Failed to allocate hydrogen fractions array\n"); dataset = H5Dopen(tempfile_id, "/ThermEq/HydrogenFractionsVol", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.logHfracs_Teq); if (status < 0) error("error reading hydrogen fractions array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing hydrogen fractions dataset"); /* All hydrogen fractions */ if (swift_memalign("cooling_table.Hfracs", (void **)&cooling->table.logHfracs_all, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_temperature * qla_cooling_N_metallicity * qla_cooling_N_density * 3 * sizeof(float)) != 0) error("Failed to allocate big hydrogen fractions array\n"); dataset = H5Dopen(tempfile_id, "/Tdep/HydrogenFractionsVol", H5P_DEFAULT); status = H5Dread(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, cooling->table.logHfracs_all); if (status < 0) error("error reading big hydrogen fractions array\n"); status = H5Dclose(dataset); if (status < 0) error("error closing big hydrogen fractions dataset"); /* Close the file */ H5Fclose(tempfile_id); /* Pressure at thermal equilibrium temperature */ if (swift_memalign("cooling_table.Peq", (void **)&cooling->table.logPeq, SWIFT_STRUCT_ALIGNMENT, qla_cooling_N_redshifts * qla_cooling_N_metallicity * qla_cooling_N_density * sizeof(float)) != 0) error("Failed to allocate logPeq array\n"); const float log10_kB_cgs = cooling->log10_kB_cgs; /* Compute the pressures at thermal eq. */ for (int ired = 0; ired < qla_cooling_N_redshifts; ired++) { for (int imet = 0; imet < qla_cooling_N_metallicity; imet++) { const int index_XH = row_major_index_2d( imet, 0, qla_cooling_N_metallicity, qla_cooling_N_elementtypes); const float log10_XH = cooling->LogMassFractions[index_XH]; for (int iden = 0; iden < qla_cooling_N_density; iden++) { const int index_Peq = row_major_index_3d( ired, imet, iden, qla_cooling_N_redshifts, qla_cooling_N_metallicity, qla_cooling_N_density); cooling->table.logPeq[index_Peq] = cooling->nH[iden] + cooling->table.logTeq[index_Peq] - log10_XH - log10(cooling->table.meanpartmass_Teq[index_Peq]) + log10_kB_cgs; } } } #ifdef SWIFT_DEBUG_CHECKS message("Done reading in general cooling table"); #endif #else error("Need HDF5 to read cooling tables"); #endif }