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) | ||
| + | </ | ||