Star Formation History Analysis with Custom Filtering and Optimization
This Python file performs a complex analysis of star formation histories by loading results from previously executed fits, defining conditions for valid combinations of parameters, and applying a series of functions to filter, optimize, and calculate statistics like mean chi-squared values. It includes importing external libraries, defining custom lambda functions for filtering based on stellar age, distance modulus, and color excess, and employs a bolometric correction to calculate apparent magnitudes. The script concludes by saving filtered and optimized results considering predefined conditions into a serialized (pickled) file, incorporating a datetime format to label the output file distinctly.
from itertools import combinations import numpy as np import pickle as pkl from fidanka.isochrone.isochrone import interp_isochrone_age from tqdm import tqdm with open("./fitResults.denseAlpha.fixedLowmass.pkl", 'rb') as f: fitResults = pkl.load(f) bfr = fitResults['r'] # Function to calculate mean chi2 def mean_chi2(optimization_results): return np.mean([opt.fun for opt in optimization_results]) # Function to check if A and E satisfy conditions def satisfy_conditions(A, E, cond): if not cond['age'](A['opt'].x[0], E['opt'].x[0]): return False if not cond['mu'](A['opt'].x[1], E['opt'].x[1]): return False if not cond['Av'](A['opt'].x[2], E['opt'].x[2]): return False conditions = { 'age': lambda a, e: a < e, 'mu': lambda a_mu, e_mu: 0.95 * e_mu <= a_mu <= 1.05 * e_mu, 'Av': lambda a_av, e_av: 0.95 * e_av <= a_av <= 1.05 * e_av } # conditions = { # # Age condition: A is younger than E and within 1 percent of E's age # 'age': lambda a_age, e_age: a_age < e_age and (e_age * 0.99) <= a_age, # # Distance modulus condition: Agree within 0.1 percent # 'mu': lambda a_mu, e_mu: (e_mu * 0.999) <= a_mu <= (e_mu * 1.001), # # Color excess condition: Agree within 0.1 percent # 'Av': lambda a_av, e_av: (e_av * 0.999) <= a_av <= (e_av * 1.001) # } # Function to apply bolometric corrector and get apparent mags def apply_bc(iso, bc, dm, excess): return bc.apparent_mags(iso[:, 1], iso[:, 2], iso[:, 3], mu=dm, Av=excess) bfr_list = [ (population, Y, alphaML, fitResults) for population, popFitResults in bfr.items() for Y, YFitResults in popFitResults.items() for alphaML, fitResults in YFitResults.items() ] # Generate combinations combos = combinations(bfr_list, 2) # Filter to keep only valid combinations and calculate mean chi2 valid_combos = [(A, E) for A, E in combos if satisfy_conditions(A[3], E[3], conditions)] # Create the result list result_list = [] for combo in tqdm(valid_combos): (popA, A_y, A_a, A), (popE, E_y, E_a, E) = combo A_age = A['opt'].x[0] E_age = E['opt'].x[0] A_dm = A['opt'].x[1] E_dm = E['opt'].x[1] A_excess = A['opt'].x[2] E_excess = E['opt'].x[2] A_iso = interp_isochrone_age(A['iso'], A_age) E_iso = interp_isochrone_age(E['iso'], E_age) A_apparent_mags = apply_bc(A_iso, A['bc'], A_dm, A_excess) E_apparent_mags = apply_bc(E_iso, E['bc'], E_dm, E_excess) mean_chi2_val = mean_chi2([A['opt'], E['opt']]) result_list.append((mean_chi2_val, [(popA, A_y, A_a, A['opt']), (popE, E_y, E_a, E['opt'])], [A_apparent_mags, E_apparent_mags])) # Filter out those with mean goodness of fit greater than 1 filtered_chi2_results = [result for result in result_list if result[0] <= 1] # Sort the combos from best fit to worst filtered_chi2_results.sort(key=lambda x: x[0]) print(filtered_chi2_results[0]) import datetime currentDate = datetime.datetime.now().strftime("%m-%d-%Y") with open(f"ReducedResults.denseAlpha.fixedLowmass_{currentDate}.pkl", 'wb') as f: pkl.dump(filtered_chi2_results, f)