Isochrone Interpolation and CMD Loading in Astronomy

This Python script is designed for astronomers and astrophysicists who work with stellar isochrones and color-magnitude diagrams (CMDs). It includes functions to load isochrone data files, extract metadata, and perform interpolations for specific ages and metallicities based on the isochrone models. The script utilizes libraries such as NumPy for numerical operations, SciPy for interpolation, and tqdm for progress bars. Key functionalities include the ability to read isochrone files and metadata, construct and interpolate within CMDs at specific magnitudes, and interpolate isochrone data for a target stellar age.

from fidanka.isochrone.MIST import read_iso, read_iso_metadata
import numpy as np
import pickle as pkl
import pathlib
import re
 
from tqdm import tqdm
 
from scipy.interpolate import interp1d
 
 
def load_ISO_CMDs(root):
    CMDs = list(map(lambda x: str(x), pathlib.Path(root).rglob("isochrones.txt")))
    extract = list(map(lambda x: re.findall(r"Pop(A|E)\+(\d\.\d+)\/alpha-(\d\.\d+)\/", x)[0], CMDs))
    pops = set(map(lambda x: x[0], extract))
    Ys = set(map(lambda x: x[1], extract))
    alphas = set(map(lambda x: x[2], extract))
 
    lookup = dict()
    FeHLookup = dict()
    for pop in tqdm(pops, leave=False):
        lookup[pop] = dict()
        FeHLookup[pop] = dict()
        for Y in tqdm(Ys, leave=False):
            lookup[pop][float(Y)] = dict()
            FeHLookup[pop][float(Y)] = dict()
            for alpha in tqdm(alphas, leave=False):
                if checkTup := (pop, Y, alpha) in extract:
                    extractID = extract.index((pop, Y, alpha))
                    iso = read_iso(CMDs[extractID])
                    metadata = read_iso_metadata(CMDs[extractID])
                    FeH = metadata['[Fe/H]']
                    lookup[pop][float(Y)][float(alpha)] = iso
                    FeHLookup[pop][float(Y)][float(alpha)] = FeH
    return lookup, FeHLookup
 
def interCMDatMag(color, mag, targetMag):
    f = interp1d(mag, color)
    return f(targetMag)
 
def interp_isochrone_age(iso, targetAge):
    logTargetAgeYr= np.log10(targetAge*1e9)
    ageKeys = list(iso.keys())
    distance = [(x-logTargetAgeYr, x) for x in ageKeys]
    below = sorted(filter(lambda x: x[0] <=0, distance), key=lambda x: abs(x[0]))
    above = sorted(filter(lambda x: x[0] > 0, distance), key=lambda x: x[0])
    isoBelow = iso[below[0][1]]
    isoAbove = iso[above[0][1]]
    age1 = isoBelow['log10_isochrone_age_yr'].iloc[0]
    age2 = isoAbove['log10_isochrone_age_yr'].iloc[0]
 
    def linearinterpolate(x, other, age1, age2):
        newIso = ((other[x.name] - x)/(age2-age1)) * (logTargetAgeYr - age1) + x
        return newIso
 
    interpolated = isoBelow.apply(lambda x: linearinterpolate(x, isoAbove, age1, age2))
    return interpolated