Isochrone Fitting Script for Astronomical Data Analysis

This Python script is part of a larger project for fitting isochrones to color-magnitude diagrams (CMDs) of star clusters. It imports functionality from a custom package fidanka, handles astronomical data processing with numpy and file operations, uses pickle for serialization, and relies on argparse for command line argument parsing. The script supports selecting different fitting methods, applies bolometric corrections, loads photometric and isochrone data, performs optimization to match observed and model CMDs, and finally saves the results in a Pickle file.

from fidanka.isofit.fit import optimize
from fidanka.isochrone.isochrone import load_ISO_CMDs
from fidanka.isofit.fit import order_best_fit_result
from fidanka.misc.logging import LoggerManager
 
import pickle as pkl
import numpy as np
import os
import shutil
from tqdm import tqdm
import re
 
PHOT_ROOT = "../../photometry/HUGS/ngc2808"
ISO_ROOT = "../../outputs.denseAlpha.fixedLowmass"
# ISO_ROOT = "../../outputs.smallTest"
 
 
def format_iso_nd_array(isochrones):
    outDict = dict()
    for key, iso in isochrones.items():
        Teff = 10**iso["log_Teff"].values
        logg = iso["log_g"].values
        logL = iso["log_L"].values
        EEPs = iso["EEP"].values
        outDict[key] = np.array([EEPs, Teff, logg, logL]).T
    return outDict
 
 
if __name__ == "__main__":
    import argparse
 
    parser = argparse.ArgumentParser(description="Fit an isochrones to a CMD")
    method = parser.add_mutually_exclusive_group(required=True)
    method.add_argument("--meth1", help="Use HUGS Method 1", action='store_true')
    method.add_argument("--meth2", help="Use HUGS Method 2", action='store_true')
    method.add_argument("--meth3", help="Use HUGS Method 3", action='store_true')
 
    parser.add_argument("--fLines", help="Fiducial piclke file", default="Fiducial.pkl")
 
    parser.add_argument("--filter1", help="Magnitude filter", default="F606W")
    parser.add_argument("--filter2", help="Second Color Filter", default="F814W")
    parser.add_argument("--rFilterOrder", action="store_true", help="flip which filter in the color is the magnitude axis")
 
    parser.add_argument("--verbosePlotting", action="store_true", help="Write out a lot of plots")
    parser.add_argument("-o", "--output", type=str, default="OptResult.pkl", help="Where to save Pickle File")
    parser.add_argument("--bTabs", type=str, help="Root paths to the bolometric  correction tables")
 
    args = parser.parse_args()
    LoggerManager.config_logger("isoFit_fidanka.log")
 
    if args.rFilterOrder:
        f3 = args.filter2
    else:
        f3 = args.filter1
    f1 = args.filter1
    f2 = args.filter2
 
    assert os.path.exists(args.bTabs), "Bolometric Correction Table Path does not exist"
    bolFilenames = list(filter(lambda x: re.search(r"feh[mp]\d+", x), os.listdir(args.bTabs)))
    bolPaths = list(map(lambda x: os.path.join(args.bTabs, x), bolFilenames))
 
    with open(os.path.join(PHOT_ROOT, "photometry.pkl"), 'rb') as f:
        photometry = pkl.load(f)
 
    if args.meth1:
        methID = 1
    elif args.meth2:
        methID = 2
    elif args.meth3:
        methID = 3
    else:
        raise RuntimeError("Something Horrid has gone wrong! You should re-evalute the choices which lead you here...")
    cleanedPhot = photometry[methID]
 
 
    print("Loading Fiducial Sequences....")
    with open(args.fLines, 'rb') as f:
        fiducialSequences = pkl.load(f)
    fiducialSequences[0].name = "A"
    fiducialSequences[1].name = "E"
    fiducialLookup = dict()
    for i, fid in enumerate(fiducialSequences):
        fiducialLookup[fid.name] = i
    print("Fiducial Sequences Loaded")
 
    print("Loading Isochrones....")
    ISOs, FeHs = load_ISO_CMDs(ISO_ROOT)
    print("Isochrones Loaded")
    results = dict()
    popFid = None
 
    print("Starting Optimization...")
    for population, popISO in tqdm(ISOs.items(), total=len(ISOs), leave=False):
        results[population] = dict()
        assert population in fiducialLookup.keys()
        if population == "A":
            popFid = fiducialSequences[fiducialLookup['A']]
        if population == "E":
            popFid = fiducialSequences[fiducialLookup['E']]
        for Y, YISO in tqdm(popISO.items(), total=len(popISO), leave=False):
            results[population][Y] = dict()
            for a, alphaISO in tqdm(YISO.items(), total=len(YISO), leave=False):
                FeH = FeHs[population][Y][a]
                iso = format_iso_nd_array(alphaISO)
                results[population][Y][a] = optimize(
                        popFid.mean,
                        iso,
                        'WFC3',
                        FeH,
                        filters=(f1,f2,f3)
                        )
 
    print("Optimization Complete")
    print("Saving Results...")
    orderedOptimizationResults = order_best_fit_result(results)
    with open(args.output, 'wb') as f:
        pkl.dump({'bf': orderedOptimizationResults, 'r': results, 'phot': cleanedPhot}, f)
    print("Results Saved")