Reading MIST Isochrone Files

Provides functions to read and parse MIST formatted isochrone files, extracting either detailed data tables as pandas DataFrames organized by age or essential metadata, including version details and initial conditions.

import re
import pandas as pd
from io import StringIO
from itertools import islice
 
from typing import Any
 
 
def read_iso(filename: str) -> dict[float, pd.DataFrame]:
    """
    Read in a MIST formated isochrone to a list of DataFrame
 
    Parameters
    ----------
        filename : str
            filepath pointing to the isochrone file
 
    Returns
    -------
        isos : dict[float, pd.DataFrame]
            Dictionary of isochrones loaded and indexed by their ages in
            Gyr
    """
    with open(filename) as f:
        contents = f.read()
 
    lines = contents.split("\n")
 
    tabSep = r"((?:# number of EEPs, cols =\s+)(\d+)(?:\s+)(\d+))"
    tables = map(lambda x: (x[0], re.search(tabSep, x[1])), enumerate(lines))
    fTables = list(filter(lambda x: x[1], tables))
 
    sepTables = map(lambda x: lines[x[0][0] : x[1][0]], zip(fTables[:-1], fTables[1:]))
 
    isos = dict()
    for table in sepTables:
        header = re.findall(r"[^\s\\#]+", table[2])
        okayTab = filter(lambda x: x != "" and x.lstrip()[0] != "#", table)
        strTab = "\n".join(okayTab)
        df = pd.read_csv(StringIO(strTab), sep=r"\s+", names=header)
 
        if df.shape[0] > 0 and isinstance(df, pd.DataFrame):
            age = 10 ** df["log10_isochrone_age_yr"].iloc[0]
            df["age"] = 10 ** df["log10_isochrone_age_yr"]
            isos[age] = df
    return isos
 
 
def read_iso_metadata(filename: str) -> dict[str, Any]:
    """
    Efficiently read in isochrone metadata without opening entire file
 
    Parameters
    ----------
        filename : str
            filepath pointing to the isochrone file.
 
    Returns
    -------
        metadata : dict[str, Any]
            Dictionary of metadata relating to isochrone pulled from the
            first 7 rows of the isochrone file.
    """
    with open(filename) as f:
        rawHeader = list(islice(f, 9))
    photometry = (
        True
        if any(["photometric system" in headerLine for headerLine in rawHeader])
        else False
    )
    MESAVersion = rawHeader[0].split("=")[1].rstrip().lstrip()
    MESARevision = rawHeader[1].split("=")[1].rstrip().lstrip()
    if photometry:
        photometricSystem = rawHeader[2].split("=")[1].rstrip().lstrip()
        numRowID = 5
    else:
        photometricSystem = None
        numRowID = 4
    numRow = [float(x) for x in rawHeader[numRowID][1:].rstrip().lstrip().split()]
 
    metadata = {
        "MESAVersion": MESAVersion,
        "MESARevision": MESARevision,
        "Yinit": numRow[0],
        "Zinit": numRow[1],
        "[Fe/H]": numRow[2],
        "[a/Fe]": numRow[3],
        "v/vcrit": numRow[4],
        "photometricSystem": photometricSystem,
    }
    return metadata