Script for Stellar Model Generation and Evolution Simulations
This Python script automates the process of generating and evolving stellar models for a given range of stellar masses using pre-main sequence models, high temperature opacity tables, and chemical abundance files. It incorporates parallel processing for efficiency and allows for customization through command-line arguments.
from pysep.dm.filetypes import premsf_file from pysep.dm.filetypes import opacf_file from pysep.io.nml.control.defaults import solar, controlLow from pysep.io.nml.physics.defaults import phys1, phys1Low from pysep.opac.tops import open_and_parse from pysep.opac.opal.defaults import GS98hz from pysep.dsep import stellarModel as sm from pysep.api.parallel import pStellarModel import argparse from typing import Generator import re import shutil import os def error_check(opac, prems, abun): A = (opac[1] == prems[1]) and (prems[1] == abun[1]) B = (opac[2] == prems[2]) and (prems[2] == abun[2]) return A and B def mass_generator(mlow: float, mhigh: float) -> Generator[float, None, None]: """ Generator to return mass with a varying incriment between masses in different ranges. Below 0.6 msolar masses incriment by 0.025, above that they incriment by 0.05. Parameters ---------- mlow : float low end of mass range, used inclusivly mhigh : float high end of mass range, used inclusivly Yields ------ rmass : float mass to investigate """ nDigits = 6 # rounds are to quash floating point errors rmass = round(mlow, nDigits) while rmass <= mhigh: yield rmass if rmass < 0.6: rmass = round(rmass + 0.025, nDigits) else: rmass = round(rmass + 0.05, nDigits) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Generate dsep output for all NGC_2808 abundance tables") parser.add_argument("rootPath", help="root path to store output", type=str) parser.add_argument("opacPath", help="path to high temperature opacity tables", type=str) parser.add_argument("premsPath", help="path to pre main sequence models", type=str) parser.add_argument("abunPath", help="path to chemical abundance files", type=str) parser.add_argument("-op", "--opacPattern", help='regular expression opac pattern', type=str, default=r".+_opac") parser.add_argument("-pp", "--premsPattern", help='regular expression prems pattern', type=str, default=r"^m.+\.prems") parser.add_argument("-ap", "--abunPattern", help='regular expression abun pattern', type=str, default=r'.+Y[+|-]\d\.\d+\.dat') parser.add_argument("--opal", help="use default opal opacity GS98hz", action="store_true", default=False) parser.add_argument("--kttau", type=int, default=5, help="KTTAU flag to pick atmosphere model") parser.add_argument("--outdir", type=str, help="output directory", default="data") args = parser.parse_args() massRange = (0.3, 0.9) masses = mass_generator(massRange[0], massRange[1]) opacDirCont = os.listdir(args.opacPath) premsDirCont = os.listdir(args.premsPath) abunDirCont = os.listdir(args.abunPath) opacRegx = args.opacPattern premsRegx = args.premsPattern abunRegx = args.abunPattern opacFiles = filter(lambda x: re.match(opacRegx, x), opacDirCont) opacPaths = map(lambda x: os.path.join(os.path.abspath(args.opacPath), x), opacFiles) opacPathsM = map(lambda x: (x, float(re.search('(?<=Y[+|-])\d+\.\d+', x).group(0)), re.search("(?<=pop).", x).group(0)), opacPaths) opacPathsSorted = sorted(opacPathsM, key=lambda x: (x[2], x[1])) abunFiles = filter(lambda x: re.match(abunRegx, x), abunDirCont) abunPaths = map(lambda x: os.path.join(os.path.abspath(args.abunPath), x), abunFiles) abunPathsM = map(lambda x: (x, float(re.search('(?<=Y[+|-])\d+\.\d+', x).group(0)), re.search("(?<=pop).", x).group(0)), abunPaths) abunPathsSorted = sorted(abunPathsM, key=lambda x: (x[2], x[1])) models = list() for targetMass in [int(x*1000) for x in masses]: if 'm' in premsRegx: premsRegxS = premsRegx.replace('m', f"m{targetMass:0>4d}", 1) premsFiles = filter(lambda x: re.match(premsRegxS, x), premsDirCont) premsPaths = map(lambda x: os.path.join(os.path.abspath(args.premsPath), x), premsFiles) premsPathsM = map(lambda x: (x, float(re.search('(?<=Y[+|-])\d+\.\d+', x).group(0)), re.search("(?<=pop).", x).group(0)), premsPaths) premsPathsSorted = sorted(premsPathsM, key=lambda x: (x[2], x[1])) for opac, prems, abun in zip(opacPathsSorted, premsPathsSorted, abunPathsSorted): error_check(opac, prems, abun) pop = re.findall("(?:pop)(A|E)", abun[0])[0] parsed = open_and_parse(abun[0]) X = parsed['AbundanceRatio']['X'] Z = parsed['AbundanceRatio']['Z'] Y = 1-(X+Z) if targetMass < 0.75: usePHYS = phys1Low.copy() useCTL = controlLow.copy() else: usePHYS = phys1.copy() useCTL = solar.copy() useCTL[0]['rsclx'] = X useCTL[0]['rsclz'] = Z useCTL[0]['descrip'] = f"NGC 2808 Pop{pop} Y+{Y:0.2f}, {targetMass/1000} solar masses" useCTL[1]['descrip'] = f"NGC 2808 Pop{pop} Y+{Y:0.2f}, {targetMass/1000} solar masses, evolve" useCTL[1]['endage'] = 12e9 usePHYS['kttau'] = args.kttau outputName = f"{args.outdir}/DSEPRun_ngc2808_pop{opac[2]}_Y+{opac[1]:<0.2f}_m{targetMass:0>04d}" opacf = opacf_file(opac[0]) premsf = premsf_file(prems[0]) if not os.path.exists(outputName): os.mkdir(outputName) else: shutil.rmtree(outputName) os.mkdir(outputName) if args.opal: opac = GS98hz else: opac = opacf model = sm(outputName, useCTL, usePHYS, opac, premsf) models.append(model) print(f"Model {outputName} Defined") pModels = pStellarModel(models) pModels.pEvolve() pModels.pStash()