Differences
This shows you the differences between two versions of the page.
bolometricallycorrectevolutionarytracks [2024/02/19 12:40] – created tboudreaux | bolometricallycorrectevolutionarytracks [2024/02/19 12:43] (current) – added script tboudreaux | ||
---|---|---|---|
Line 2: | Line 2: | ||
This script provides utility to scan a directory, build a queryable in memory datastore | This script provides utility to scan a directory, build a queryable in memory datastore | ||
for that directory, then bolometrically correct those tracks | for that directory, then bolometrically correct those tracks | ||
+ | |||
+ | |||
+ | ---- | ||
+ | < | ||
+ | from pysep.dsep import load_model | ||
+ | from pysep.io.trk import read_trk | ||
+ | import os | ||
+ | import pickle | ||
+ | | ||
+ | import matplotlib.pyplot as plt | ||
+ | import pandas as pd | ||
+ | import numpy as np | ||
+ | | ||
+ | from scipy.interpolate import interp1d | ||
+ | | ||
+ | from fidanka.bolometric.bctab import BolometricCorrector | ||
+ | | ||
+ | def scan_simulation_data(root_dir): | ||
+ | sim_data = SimulationData() | ||
+ | for population_dir in os.listdir(root_dir): | ||
+ | population_path = os.path.join(root_dir, | ||
+ | if not os.path.isdir(population_path): | ||
+ | continue | ||
+ | for mixing_length_dir in os.listdir(population_path): | ||
+ | mixing_length_path = os.path.join(population_path, | ||
+ | if not os.path.isdir(mixing_length_path): | ||
+ | continue | ||
+ | for mass_dir in os.listdir(mixing_length_path): | ||
+ | mass_path = os.path.join(mixing_length_path, | ||
+ | if not os.path.isdir(mass_path): | ||
+ | continue | ||
+ | model_path = os.path.join(mass_path, | ||
+ | if os.path.exists(model_path): | ||
+ | if ' | ||
+ | elif ' | ||
+ | mixing_length = float(mixing_length_dir[cut: | ||
+ | mass = float(mass_dir[2: | ||
+ | sim_data.add_entry(population_dir, | ||
+ | return sim_data | ||
+ | | ||
+ | # mixing length comes up a lot in this class as I wrote it to scan through a grid | ||
+ | # which was over mixing length. Here I use it for FeH. So in the class definition | ||
+ | # where mixing length is refered to that should really be FeH. Note that this does | ||
+ | # not actually change the internal logic of this class as it is really just a way | ||
+ | # to query data. | ||
+ | class SimulationData: | ||
+ | def __init__(self): | ||
+ | self.data = {} # Dictionary to hold the simulation data structure | ||
+ | | ||
+ | def add_entry(self, | ||
+ | """ | ||
+ | if population not in self.data: | ||
+ | self.data[population] = {} | ||
+ | if mixing_length not in self.data[population]: | ||
+ | self.data[population][mixing_length] = {} | ||
+ | self.data[population][mixing_length][mass] = path | ||
+ | | ||
+ | def get_path(self, | ||
+ | """ | ||
+ | return self.data.get(population, | ||
+ | | ||
+ | def save_to_file(self, | ||
+ | """ | ||
+ | with open(filename, | ||
+ | pickle.dump(self.data, | ||
+ | | ||
+ | def load_from_file(self, | ||
+ | """ | ||
+ | with open(filename, | ||
+ | self.data = pickle.load(f) | ||
+ | | ||
+ | def query(self, population, mixing_length, | ||
+ | """ | ||
+ | # Check if the population exists; if not, return None or an error message | ||
+ | if population not in self.data: | ||
+ | return None, " | ||
+ | | ||
+ | mixing_length = float(mixing_length) | ||
+ | mass = float(mass) | ||
+ | | ||
+ | if population in self.data: | ||
+ | closest_mixing_length = self._find_closest(mixing_length, | ||
+ | closest_mass = self._find_closest(mass, | ||
+ | path = self.data[population][closest_mixing_length][closest_mass] | ||
+ | return path, (population, | ||
+ | else: | ||
+ | return None, " | ||
+ | | ||
+ | def query_and_load(self, | ||
+ | queryResults = self.query(population, | ||
+ | return load_model(queryResults[0]), | ||
+ | | ||
+ | def query_and_load_trk(self, | ||
+ | queryResults = self.query(population, | ||
+ | newPath = os.path.dirname(queryResults[0]) | ||
+ | trkFile = filter(lambda x: x.endswith(" | ||
+ | trkFilePath = os.path.join(newPath, | ||
+ | return read_trk(trkFilePath), | ||
+ | | ||
+ | def _find_closest(self, | ||
+ | """ | ||
+ | options = [float(option) for option in options] | ||
+ | closest_value = min(options, | ||
+ | return closest_value | ||
+ | | ||
+ | def find_bump_lum(l, | ||
+ | dlda = np.gradient(l, | ||
+ | bumpAge = age[dlda == dlda.min()] | ||
+ | lf = interp1d(age, | ||
+ | bumpLum = lf(bumpAge) | ||
+ | return bumpLum[0] | ||
+ | | ||
+ | if __name__ == " | ||
+ | if not os.path.exists(" | ||
+ | models = scan_simulation_data(" | ||
+ | models.save_to_file(" | ||
+ | else: | ||
+ | models = SimulationData() | ||
+ | models.load_from_file(" | ||
+ | |||
+ | # Masses calibrated to place the start of the red giant branch at about 9.1 Gyr for all models | ||
+ | # the range is between 9.1 and 9.4 Gyr due to the spacing of the mass grid | ||
+ | # This may be able to be improved with mass interpolation between the tracks | ||
+ | FeHMasses = { | ||
+ | -2.4: 0.86, | ||
+ | -2.2: 0.86, | ||
+ | -2.0: 0.86, | ||
+ | -1.8: 0.86, | ||
+ | -1.6: 0.86, | ||
+ | -1.4: 0.88, | ||
+ | -1.2: 0.9, | ||
+ | -1.0: 0.92, | ||
+ | -0.8: 0.96 | ||
+ | } | ||
+ | trks = dict() | ||
+ | for FeH, mass in FeHMasses.items(): | ||
+ | model, queryResults = models.query_and_load(" | ||
+ | trks[FeH] = (model.trk(), | ||
+ | |||
+ | bumpLoc = list() | ||
+ | for FeH, trk in trks.items(): | ||
+ | logL = trk[0][' | ||
+ | Teff = 10**trk[0][' | ||
+ | age = trk[0][' | ||
+ | cond = age > 4 | ||
+ | bumpL = find_bump_lum(logL[cond], | ||
+ | bumpLoc.append((FeH, | ||
+ | |||
+ | # the RGB bump location in Log(L) | ||
+ | bumpLoc = np.array(bumpLoc) | ||
+ | |||
+ | # Apply bolometric corrections to find it in Mv | ||
+ | bcs = {feh: BolometricCorrector(" | ||
+ | apparentMags = {feh: | ||
+ | bcs[feh].apparent_mags( | ||
+ | 10**trk[0][' | ||
+ | trk[0][' | ||
+ | trk[0][' | ||
+ | ) | ||
+ | for feh, trk in trks.items() | ||
+ | } | ||
+ | dfs = {feh: | ||
+ | pd.DataFrame( | ||
+ | { | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | }) | ||
+ | for feh, trk in trks.items() | ||
+ | } | ||
+ | with open(" | ||
+ | pickle.dump(dfs, | ||
+ | |||
+ | print(dfs) | ||
+ | </ | ||