Source code for posydon_setup_grid

#!/usr/bin/env python
##############################################################################
#  IMPORT ALL NECESSARY PYTHON PACKAGES
##############################################################################
import argparse
import os
import shutil
import pandas
import subprocess
import glob

from posydon.utils import configfile
from posydon.utils import gridutils as utils
from posydon.grids.psygrid import PSyGrid
from posydon.active_learning.psy_cris.utils import parse_inifile

###############################################################################
# DEFINE COMMANDLINE ARGUMENTS
###############################################################################
[docs] def parse_commandline(): """Parse the arguments given on the command-line. """ parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--inifile", help="Name of ini file of params", required=True) parser.add_argument("--grid-type", help="Either you are supplying a grid " "of points to run MESA on (fixed) or you are supplying a pre computed MESA " "grid and want to sample new points to run MESA on (dynamic).", required=True) parser.add_argument("--run-directory", help="Path where executable will be made and MESA " "simulation output will be placed", default=os.getcwd()) parser.add_argument("--submission-type", help="Options include creating a shell script or a slurm script", default='shell') parser.add_argument("-n", "--nproc", help="number of processors", type=int, default=1) parser.add_argument("--verbose", action="store_true", default=False, help="Run in Verbose Mode") args = parser.parse_args() if args.grid_type not in ['fixed', 'dynamic']: raise parser.error("--grid-type must be either fixed or dynamic") if args.submission_type not in ['slurm', 'shell']: raise parser.error('--submission-type must be either slurm of shell') return args
[docs] def find_inlist_from_scenario(source, gitcommit, system_type): """Dynamically find the inlists the user wants to from the supplied info Parameters ---------- source: gitcommit: system_type: """ # note the directory we are in now where_am_i_now = os.getcwd() print("We are going to dynamically fetch the posydon inlists based on your scenario") if source == 'posydon': print("You have selected posydon as your source") print("checking if we have already cloned POSYDON-MESA-INLISTS for you") if not os.path.isdir('{0}/.posydon_mesa_inlists'.format(os.environ['HOME'])): print("We are clonining the repo for you") # Determine location of executables proc = subprocess.Popen(['git', 'clone', 'https://github.com/POSYDON-code/POSYDON-MESA-INLISTS.git', '{0}/.posydon_mesa_inlists'.format(os.environ['HOME'])], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) (clone, err) = proc.communicate() else: print("git repository is already there, using that") inlists_dir = '{0}/.posydon_mesa_inlists'.format(os.environ['HOME']) branch = gitcommit.split('-')[0] githash = gitcommit.split('-')[1] elif source == 'user': print("You have selected user as your source " "checking if we have already cloned USER-MESA-INLISTS for you " "Validating the name of the git hash you want to use..." "must be of format 'branch-githash'") if len(gitcommit.split('-')) != 2: raise ValueError("You have supplied an invalid user gitcommit format, must be of format 'branch-githash'") branch = gitcommit.split('-')[0] githash = gitcommit.split('-')[1] if not os.path.isdir('{0}/.user_mesa_inlists'.format(os.environ['HOME'])): print("We are clonining the repo for you") # Determine location of executables proc = subprocess.Popen(['git', 'clone', 'https://github.com/POSYDON-code/USER-MESA-INLISTS.git', '{0}/.user_mesa_inlists'.format(os.environ['HOME'])], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) (clone, err) = proc.communicate() else: print("git repository is already there, using that") inlists_dir = '{0}/.user_mesa_inlists'.format(os.environ['HOME']) branch = gitcommit.split('-')[0] githash = gitcommit.split('-')[1] else: raise ValueError("supplied source is not valid/understood. Valid sources are user and posydon") os.chdir(inlists_dir) print("checking out branch: {0}".format(branch)) proc = subprocess.Popen(['git', 'checkout', '{0}'.format(branch)], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) print("For posterity we are pulling (specifically needed if you already have the repo clone)") proc = subprocess.call(['git', 'pull'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) print("checking out commit/tag: {0}".format(githash)) proc = subprocess.Popen(['git', 'checkout', '{0}'.format(githash)], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) # if this is looking at posydon defaults, all posydon defaults build from default common inlists if source == 'posydon': inlists_location_common = '{0}/{1}/{2}/'.format(inlists_dir, 'r11701', "default_common_inlists") print("Based on system_type {0} " "We are populating the posydon inlists in the following directory: " "{1}".format(system_type, inlists_location_common)) inlist1 = os.path.join(inlists_location_common, 'binary', 'inlist1') inlist2 = os.path.join(inlists_location_common, 'binary', 'inlist2') if os.path.isfile(inlist1): with open(inlist1) as f: # check if we also need to find the location of the zams.data file for line in f.readlines(): if 'zams_filename' in line: print("ZAMS_FILENAME detected, setting mesa_inlists['zams_filename']") zams_filename = os.path.split(line.split("'")[1])[1] zams_file_path = os.path.join(inlists_dir, 'r11701', "ZAMS_models", zams_filename) if os.path.isfile(zams_file_path): print("Verified locations of ZAMS data file, {0}".format(zams_file_path)) mesa_inlists['zams_filename'] = "{0}".format(zams_file_path) print("Running Single Grid: Setting mesa_star1_extras to {0}/binary/src/run_star_extras.f".format(inlists_location_common)) print("Updating inifile values") # binary inlists mesa_inlists['star1_controls_posydon_defaults'] = '{0}/binary/inlist1'.format(inlists_location_common) mesa_inlists['star1_job_posydon_defaults'] = '{0}/binary/inlist1'.format(inlists_location_common) mesa_inlists['star2_controls_posydon_defaults'] = '{0}/binary/inlist2'.format(inlists_location_common) mesa_inlists['star2_job_posydon_defaults'] = '{0}/binary/inlist2'.format(inlists_location_common) mesa_inlists['binary_controls_posydon_defaults'] = '{0}/binary/inlist_project'.format(inlists_location_common) mesa_inlists['binary_job_posydon_defaults'] = '{0}/binary/inlist_project'.format(inlists_location_common) # columns mesa_inlists['star_history_columns'] = '{0}/history_columns.list'.format(inlists_location_common) mesa_inlists['binary_history_columns'] = '{0}/binary_history_columns.list'.format(inlists_location_common) mesa_inlists['profile_columns'] = '{0}/profile_columns.list'.format(inlists_location_common) # executables mesa_extras['posydon_binary_extras'] = '{0}/binary/src/run_binary_extras.f'.format(inlists_location_common) mesa_extras['posydon_star_binary_extras'] = '{0}/binary/src/run_star_extras.f'.format(inlists_location_common) mesa_extras["mesa_star1_extras"] = '{0}/binary/src/run_star_extras.f'.format(inlists_location_common) # so this is sufficient for system type HMS-HMS but not for others, for others we stack the above inlists on further inlists # we also need to see if we are looking at a folder for binaries or singles if system_type != "HMS-HMS" and not mesa_inlists['single_star_grid']: inlists_location = '{0}/{1}/{2}/'.format(inlists_dir, 'r11701', system_type) print("Based on system_type {0} " "We are populating the user inlists in the following directory: " "{1}".format(system_type, inlists_location)) # determine where the binary inlist(s) are if os.path.isfile(os.path.join(inlists_location, "binary", "inlist_project")): mesa_inlists['binary_controls_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist_project")) mesa_inlists['binary_job_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist_project")) if os.path.isfile(os.path.join(inlists_location, "binary", "inlist1")): mesa_inlists['star1_controls_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist1")) mesa_inlists['star1_job_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist1")) if os.path.isfile(os.path.join(inlists_location, "binary", "inlist2")): mesa_inlists['star2_controls_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist2")) mesa_inlists['star2_job_user'] = '{0}'.format(os.path.join(inlists_location, "binary", "inlist2")) if os.path.isfile(os.path.join(inlists_location, "history_columns.list")): mesa_inlists['star_history_columns'] = os.path.join(inlists_location, "history_columns.list") if os.path.isfile(os.path.join(inlists_location, "binary_history_columns.list")): mesa_inlists['binary_history_columns'] = os.path.join(inlists_location, "binary_history_columns.list") if os.path.isfile(os.path.join(inlists_location, "profile_columns.list")): mesa_inlists['profile_columns'] = os.path.join(inlists_location, "profile_columns.list") if os.path.isfile(os.path.join(inlists_location, "src", "run_binary_extras.f")): mesa_extras['user_binary_extras'] = '{0}'.format(os.path.join(inlists_location, "src", "run_binary_extras.f")) if os.path.isfile(os.path.join(inlists_location, "src", "run_star_extras.f")): mesa_extras['user_star_binary_extras'] = '{0}'.format(os.path.join(inlists_location, "src", "run_star_extras.f")) # check for star formation parameters if os.path.isdir(os.path.join(inlists_location, "star1_formation")): # We are making star1 so we can unset the zams file we were going to use for star1 print("We are making star1 so we can unset the zams file we were going to use for star1") mesa_inlists['zams_filename'] = None # Figure out how many user star1 formation steps there are and layer the posydon default inlists on all of them star1_formation_scenario = sorted(glob.glob(os.path.join(inlists_location, "star1_formation", "*step*"))) print("These are the user we are using to make star1: {0}".format(star1_formation_scenario)) print("We are going to add a layer of posydon default common inlists to these user steps: {0}".format('{0}/binary/inlist1'.format(inlists_location_common))) mesa_inlists['star1_formation_controls_posydon_defaults'] = [] mesa_inlists['star1_formation_job_posydon_defaults'] = [] for i in range(len(star1_formation_scenario)): mesa_inlists['star1_formation_controls_posydon_defaults'].append('{0}/binary/inlist1'.format(inlists_location_common)) mesa_inlists['star1_formation_job_posydon_defaults'].append('{0}/binary/inlist1'.format(inlists_location_common)) mesa_inlists['star1_formation_controls_user'] = star1_formation_scenario mesa_inlists['star1_formation_job_user'] = star1_formation_scenario if os.path.isdir(os.path.join(inlists_location, "star2_formation")): # We are making star2 so we can unset the zams file we were going to use for star2 print("We are making star2 so we can unset the zams file we were going to use for star2") mesa_inlists['zams_filename'] = None # Figure out how many user star2 formation steps there are and layer the posydon default inlists on all of them star2_formation_scenario = sorted(glob.glob(os.path.join(inlists_location, "star2_formation", "*step*"))) print("These are the user we are using to make star2: {0}".format(star2_formation_scenario)) print("We are going to add a layer of posydon default common inlists to these user steps: {0}".format('{0}/binary/inlist1'.format(inlists_location_common))) mesa_inlists['star2_formation_controls_posydon_defaults'] = [] mesa_inlists['star2_formation_job_posydon_defaults'] = [] for i in range(len(star2_formation_scenario)): mesa_inlists['star2_formation_controls_posydon_defaults'].append('{0}/binary/inlist1'.format(inlists_location_common)) mesa_inlists['star2_formation_job_posydon_defaults'].append('{0}/binary/inlist1'.format(inlists_location_common)) mesa_inlists['star2_formation_controls_user'] = star2_formation_scenario mesa_inlists['star2_formation_job_user'] = star2_formation_scenario if system_type == "HMS-HMS" and mesa_inlists['single_star_grid']: print("You want a single star HMS grid, this means that we need to make a user inlist on the fly with a single line " "x_logical_ctrl(1)=.true.") # write star1 formation step to file special_single_star_user_inlist = os.path.join(os.getcwd(), "special_single_star_user_inlist") with open(special_single_star_user_inlist, 'wb') as f: f.write(b'&controls\n\n') f.write('\t{0} = {1}\n'.format("x_logical_ctrl(1)", ".true.").encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star1_controls namelist """) mesa_inlists['star1_controls_user'] = special_single_star_user_inlist elif system_type == "CO-He_star" and mesa_inlists['single_star_grid']: inlists_location = '{0}/{1}/{2}/'.format(inlists_dir, 'r11701', system_type) print("Based on system_type {0} " "We are populating the user inlists in the following directory: " "{1}".format(system_type, inlists_location)) # We are making star2 so we can unset the zams file we were going to use for star2 print("We are making star2 so we can unset the zams file we were going to use for star2") mesa_inlists['zams_filename'] = None # Find the user single star controls single_star_scenario = sorted(glob.glob(os.path.join(inlists_location, "star1_formation", "*step*"))) print("These are the user inlists used in the single star grid: {0}".format(single_star_scenario)) mesa_inlists['star1_controls_user'] = single_star_scenario mesa_inlists['star1_job_user'] = single_star_scenario print("You want a single star He grid, " "this means that we need to make the inlist that will be used to evolve the system " "and make sure we layer on the line " "x_logical_ctrl(1)=.true.") # write star1 formation step to file special_single_star_user_inlist = os.path.join(os.getcwd(), "special_single_star_user_inlist") with open(special_single_star_user_inlist, 'wb') as f: f.write(b'&controls\n\n') f.write('\t{0} = {1}\n'.format("x_logical_ctrl(1)", ".true.").encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star1_controls namelist """) f.write(b'&star_job\n\n') f.write(b""" / / ! end of star_job namelist """) mesa_inlists['star1_controls_user'].append(special_single_star_user_inlist) # change back to where I was os.chdir(where_am_i_now) return
[docs] def construct_static_inlist(mesa_inlists, grid_parameters, working_directory=os.getcwd()): """Based on all the inlists that were passed construc the MESA project dir Parameters mesa_inlists: All of the values from the mesa_inlists section of the inifile (`dict`) grid_parameters: A list of the parameters from the csv file so we can determine all of the inlist parameters for binary, star1 and star2 that will be changing with this grid Returns: inlists """ if 'zams_filename' not in mesa_inlists.keys(): mesa_inlists['zams_filename'] = None if 'single_star_grid' not in mesa_inlists.keys(): mesa_inlists['single_star_grid'] = False ######################################## ### CONSTRUCT BINARY INLIST PARAMS ### ######################################## # inlist project controls binary_job and binary_controls inlist_binary_project = os.path.join(working_directory, 'binary', 'inlist_project') # inlist1 (controls star_job1 and star_controls1 inlist_star1_binary = os.path.join(working_directory, 'binary', 'inlist1') # inlist1 (controls star_job2 and star_controls2 inlist_star2_binary = os.path.join(working_directory, 'binary', 'inlist2') # Initialize some stuff final_binary_controls = {} final_binary_job = {} final_star1_binary_job = {} final_star2_binary_job = {} final_star1_binary_controls = {} final_star2_binary_controls = {} if not mesa_inlists['single_star_grid']: for k, v in mesa_inlists.items(): if v is not None: if 'binary_controls' in k: section = '&binary_controls' controls_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in controls_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_binary_controls[k1] = v1 elif 'binary_job' in k: section = '&binary_job' job_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in job_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_binary_job[k1] = v1 elif 'star1_job' in k: section = '&star_job' star_job1_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in star_job1_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_star1_binary_job[k1] = v1 elif 'star2_job' in k: section = '&star_job' star_job2_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in star_job2_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_star2_binary_job[k1] = v1 elif 'star1_controls' in k: section = '&controls' star_control1_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in star_control1_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue if 'num_x_ctrls' in k1: # This is a special default that the default value in the .defaults # file in MESA does not work because it is a placeholder final_star1_binary_controls[k1.replace('num_x_ctrls','1')] = v1 else: final_star1_binary_controls[k1] = v1 elif 'star2_controls' in k: section = '&controls' star_control2_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in star_control2_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue if 'num_x_ctrls' in k1: # This is a special default that the default value in the .defaults # file in MESA does not work because it is a placeholder final_star2_binary_controls[k1.replace('num_x_ctrls','1')] = v1 else: final_star2_binary_controls[k1] = v1 # detemine which is any of the parameters are binary_controls or binary_job params grid_params_binary_controls = [param for param in grid_parameters if param in final_binary_controls.keys()] print("Grid parameters that effect binary_controls: {0}".format(','.join(grid_params_binary_controls))) grid_params_binary_job = [param for param in grid_parameters if param in final_binary_job.keys()] print("Grid parameters that effect binary_job: {0}".format(','.join(grid_params_binary_job))) grid_params_star1_binary_controls = [param for param in grid_parameters if param in final_star1_binary_controls.keys()] print("Grid parameters that effect star1_binary_controls: {0}".format(','.join(grid_params_star1_binary_controls))) grid_params_star1_binary_job = [param for param in grid_parameters if param in final_star1_binary_job.keys()] print("Grid parameters that effect star1_binary_job: {0}".format(','.join(grid_params_star1_binary_job))) grid_params_star2_binary_controls = [param for param in grid_parameters if param in final_star2_binary_controls.keys()] print("Grid parameters that effect star2_binary_controls: {0}".format(','.join(grid_params_star2_binary_controls))) grid_params_star2_binary_job = [param for param in grid_parameters if param in final_star2_binary_job.keys()] print("Grid parameters that effect star2_binary_job: {0}".format(','.join(grid_params_star2_binary_job))) # depending on if there are any grid parameters that effect star1 or star2 we need to actually # do a read star extras step if grid_params_star1_binary_controls: final_star1_binary_controls['read_extra_controls_inlist1'] = '.true.' final_star1_binary_controls['extra_controls_inlist1_name'] = "'inlist_grid_star1_binary_controls'" if grid_params_star2_binary_controls: final_star2_binary_controls['read_extra_controls_inlist1'] = '.true.' final_star2_binary_controls['extra_controls_inlist1_name'] = "'inlist_grid_star2_binary_controls'" if grid_params_star1_binary_job: final_star1_binary_job['read_extra_star_job_inlist1'] = '.true.' final_star1_binary_job['extra_star_job_inlist1_name'] = "'inlist_grid_star1_binary_job'" if grid_params_star2_binary_job: final_star2_binary_job['read_extra_star_job_inlist1'] = '.true.' final_star2_binary_job['extra_star_job_inlist1_name'] = "'inlist_grid_star2_binary_job'" # We want to point the binary_job section to the star1 and star2 inlist we just made final_binary_job['inlist_names(1)'] = "'{0}'".format(inlist_star1_binary) final_binary_job['inlist_names(2)'] = "'{0}'".format(inlist_star2_binary) ######################## ### STAR 1 FORMATION ### ######################## # Check the number of inlists provided to the star1 formation sections # of the inifile star1_formation = {} # if we have provided a pre-computed zams model, it does not matter if we wanted to form star1 and star2 for # the binary step, we have supceded this with the zams_filename if (mesa_inlists['zams_filename'] is not None) and (not mesa_inlists['single_star_grid']): star1_formation_dictionary = {} elif mesa_inlists['single_star_grid']: star1_formation_dictionary = dict(filter(lambda elem: (('star1_job' in elem[0]) or ('star1_controls' in elem[0])) and elem[1] is not None, mesa_inlists.items())) else: # create dictionary of only these sections star1_formation_dictionary = dict(filter(lambda elem: 'star1_formation' in elem[0] and elem[1] is not None, mesa_inlists.items())) # See if the user even supplied inlists for doing star1_formation if star1_formation_dictionary: # initialize the string argument for star1 formation that will be passed to posydon-run-grid inlist_star1_formation = '' # check the number of inlists in each star1 formation parameter. We will take calculate the max number and treat that as # the number of star1 formation steps desired before making the final star1 model that will be fed into the binary exectuable number_of_star1_formation_steps = 1 for k, v in star1_formation_dictionary.items(): if type(v) == list: number_of_star1_formation_steps = max(number_of_star1_formation_steps, len(v)) for step in range(number_of_star1_formation_steps): star1_formation['step{0}'.format(step)] = {} star1_formation['step{0}'.format(step)]['inlist_file'] = os.path.join(working_directory, 'star1', 'inlist_step{0}'.format(step)) for k, v in star1_formation_dictionary.items(): star1_formation['step{0}'.format(step)][k] = v[step] if type(v) == list else v # Now we loop over each star1 formation step and construct the final star1 formation inlist for each step for step, step_inlists in enumerate(star1_formation.values()): # there is a new one of these final star1 formation inlists per step final_star1_formation_controls = {} final_star1_formation_job = {} for k, v in step_inlists.items(): if ('star1_formation_controls' in k) or ('star1_controls' in k): section = '&controls' controls_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in controls_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue if 'num_x_ctrls' in k1: # This is a special default that the default value in the .defaults # file in MESA does not work because it is a placeholder final_star1_formation_controls[k1.replace('num_x_ctrls','1')] = v1 else: final_star1_formation_controls[k1] = v1 if ('star1_formation_job' in k) or ('star1_job' in k): section = '&star_job' controls_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in controls_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_star1_formation_job[k1] = v1 # The user supplied a way to form star1 and we need to update dictionary of parameters and their values correctly # We want to make sure that the binary inlists load up the properly saved models from star1 formation final_star1_binary_job['create_pre_main_sequence_model'] = ".false." final_star1_binary_job['load_saved_model'] = ".true." final_star1_binary_job['saved_model_name'] = "'initial_star1_step{0}.mod'".format(step) # if this is step0 then we simply overwrite the save_model_when_terminate and # save_model_filename parts of the inlists. However, for all steps higher than # step 0 we need to have that step load in the model from the step # below the current step if step == 0: final_star1_formation_job['save_model_when_terminate'] = '.true.' final_star1_formation_job['save_model_filename'] = "'initial_star1_step{0}.mod'".format(step) else: final_star1_formation_job['create_pre_main_sequence_model'] = ".false." final_star1_formation_job['load_saved_model'] = ".true." final_star1_formation_job['saved_model_name'] = "'initial_star1_step{0}.mod'".format(step-1) final_star1_formation_job['save_model_when_terminate'] = '.true.' final_star1_formation_job['save_model_filename'] = "'initial_star1_step{0}.mod'".format(step) if (mesa_inlists['zams_filename'] is not None) and (mesa_inlists['single_star_grid']): final_star1_formation_controls['zams_filename'] = "'{0}'".format(mesa_inlists['zams_filename']) elif (mesa_inlists['zams_filename'] is None) and (mesa_inlists['single_star_grid']) and ('zams_filename' in final_star1_formation_controls.keys()): final_star1_formation_controls.pop("zams_filename", None) # write star1 formation step to file with open(step_inlists['inlist_file'], 'wb') as f: f.write(b'&controls\n\n') for k,v in final_star1_formation_controls.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star1_controls namelist """) f.write(b'&star_job\n\n') for k,v in final_star1_formation_job.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b""" / ! end of star1_job namelist """) # Construct star1 formation argument string to be passed to posydon-run-grid inlist_star1_formation += ' {0}'.format(step_inlists['inlist_file']) else: inlist_star1_formation = None ######################## ### STAR 2 FORMATION ### ######################## # Check the number of inlists provided to the star2 formation sections # of the inifile star2_formation = {} # if we have provided a pre-computed zams model, it does not matter if we wanted to form star1 and star2 for # the binary step, we have supceded this with the zams_filename if mesa_inlists['zams_filename'] is not None: star2_formation_dictionary = {} else: # create dictionary of only these sections star2_formation_dictionary = dict(filter(lambda elem: 'star2_formation' in elem[0] and elem[1] is not None, mesa_inlists.items())) # See if the user even supplied inlists for doing star2_formation if star2_formation_dictionary: # initialize the string argument for star2 formation that will be passed to posydon-run-grid inlist_star2_formation = '' # check the number of inlists in each star2 formation parameter. We will take calculate the max number and treat that as # the number of star2 formation steps desired before making the final star2 model that will be fed into the binary exectuable number_of_star2_formation_steps = 1 for k, v in star2_formation_dictionary.items(): if type(v) == list: number_of_star2_formation_steps = max(number_of_star2_formation_steps, len(v)) for step in range(number_of_star2_formation_steps): star2_formation['step{0}'.format(step)] = {} star2_formation['step{0}'.format(step)]['inlist_file'] = os.path.join(working_directory, 'star2', 'inlist_step{0}'.format(step)) for k, v in star2_formation_dictionary.items(): star2_formation['step{0}'.format(step)][k] = v[step] if type(v) == list else v # Now we loop over each star2 formation step and construct the final star2 formation inlist for each step for step, step_inlists in enumerate(star2_formation.values()): final_star2_formation_controls = {} final_star2_formation_job = {} for k, v in step_inlists.items(): if 'star2_formation_controls' in k: section = '&controls' controls_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in controls_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue if 'num_x_ctrls' in k1: # This is a special default that the default value in the .defaults # file in MESA does not work because it is a placeholder final_star2_formation_controls[k1.replace('num_x_ctrls','1')] = v1 else: final_star2_formation_controls[k1] = v1 if 'star2_formation_job' in k: section = '&star_job' controls_dict = utils.clean_inlist_file(v, section=section) for k1,v1 in controls_dict[section].items(): # remove any hidden inlists extras since that is not how we want to do things if ('read_extra' in k1) or ('inlist' in k1): continue final_star2_formation_job[k1] = v1 # then the user supplied a star2 formation and we need to update dictionary of parameters and their values correctly # We want to make sure that the binary inlists load up the properly saved models from star2 formation final_star2_binary_job['create_pre_main_sequence_model'] = ".false." final_star2_binary_job['load_saved_model'] = ".true." final_star2_binary_job['saved_model_name'] = "'initial_star2_step{0}.mod'".format(step) # if this is step0 then we simply overwrite the save_model_when_terminate and # save_model_filename parts of the inlists. However, for all steps higher than # step 0 we need to have that step load in the model from the step # below the current step if step == 0: final_star2_formation_job['save_model_when_terminate'] = '.true.' final_star2_formation_job['save_model_filename'] = "'initial_star2_step{0}.mod'".format(step) else: final_star2_formation_job['create_pre_main_sequence_model'] = ".false." final_star2_formation_job['load_saved_model'] = ".true." final_star2_formation_job['saved_model_name'] = "'initial_star2_step{0}.mod'".format(step-1) final_star2_formation_job['save_model_when_terminate'] = '.true.' final_star2_formation_job['save_model_filename'] = "'initial_star2_step{0}.mod'".format(step) with open(step_inlists['inlist_file'], 'wb') as f: f.write(b'&controls\n\n') for k,v in final_star2_formation_controls.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star2_controls namelist """) f.write(b'&star_job\n\n') for k,v in final_star2_formation_job.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b""" / ! end of star2_job namelist """) # Construct star2 formation argument string to be passed to posydon-run-grid inlist_star2_formation += ' {0}'.format(step_inlists['inlist_file']) else: inlist_star2_formation = None ########################################## ###### MESA BINARY OUTPUT CONTROLS ####### ########################################## if mesa_inlists['final_profile_star1']: final_star1_binary_job['write_profile_when_terminate'] = ".true." final_star1_binary_job['filename_for_profile_when_terminate'] = "'final_profile_star1.data'" else: final_star1_binary_job['write_profile_when_terminate'] = ".false." if mesa_inlists['final_profile_star2']: final_star2_binary_job['write_profile_when_terminate'] = ".true." final_star2_binary_job['filename_for_profile_when_terminate'] = "'final_profile_star2.data'" else: final_star2_binary_job['write_profile_when_terminate'] = ".false." if mesa_inlists['final_model_star1']: final_star1_binary_job['save_model_when_terminate'] = ".true." final_star1_binary_job['save_model_filename'] = "'final_star1.mod'" else: final_star1_binary_job['save_model_when_terminate'] = ".false." if mesa_inlists['final_model_star2']: final_star2_binary_job['save_model_when_terminate'] = ".true." final_star2_binary_job['save_model_filename'] = "'final_star2.mod'" else: final_star2_binary_job['save_model_when_terminate'] = ".false." if mesa_inlists['history_star1']: final_star1_binary_controls['do_history_file'] = ".true." else: final_star1_binary_controls['do_history_file'] = ".false." if mesa_inlists['history_star2']: final_star2_binary_controls['do_history_file'] = ".true." else: final_star2_binary_controls['do_history_file'] = ".false." final_binary_controls['history_interval'] = mesa_inlists['history_interval'] final_star1_binary_controls['history_interval'] = mesa_inlists['history_interval'] final_star2_binary_controls['history_interval'] = mesa_inlists['history_interval'] if not mesa_inlists['binary_history']: final_binary_controls['history_interval'] = "-1" # update the controls for star1 star2 for the binary with the precomputed zams model if mesa_inlists['zams_filename'] is not None: final_star1_binary_controls['zams_filename'] = "'{0}'".format(mesa_inlists['zams_filename']) final_star2_binary_controls['zams_filename'] = "'{0}'".format(mesa_inlists['zams_filename']) ########################################## ###### WRITE MESA BINARY INLISTS ####### ########################################## # now that we have all the parameters and their correct values # we now write our own inlist_project, inlist1 and inlist2 for the binary with open(inlist_binary_project, 'wb') as f: f.write(b'&binary_controls\n\n') for k,v in final_binary_controls.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b'\n/ ! end of binary_controls namelist') f.write(b'\n\n') f.write(b'&binary_job\n\n') for k,v in final_binary_job.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b""" / ! end of binary_job namelist """) with open(inlist_star1_binary, 'wb') as f: f.write(b'&controls\n\n') for k,v in final_star1_binary_controls.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star1_controls namelist """) f.write(b'&star_job\n\n') for k,v in final_star1_binary_job.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b""" / ! end of star1_job namelist """) with open(inlist_star2_binary, 'wb') as f: f.write(b'&controls\n\n') for k,v in final_star2_binary_controls.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b'\n\n') f.write(b""" / ! end of star2_controls namelist """) f.write(b'&star_job\n\n') for k,v in final_star2_binary_job.items(): f.write('\t{0} = {1}\n'.format(k,v).encode('utf-8')) f.write(b""" / ! end of star2_job namelist """) return inlist_star1_formation, inlist_star2_formation, inlist_binary_project, inlist_star1_binary, inlist_star2_binary
[docs] def make_executables(mesa_extras, working_directory=os.getcwd()): """Pass mesa extra function and compile binary executable on the fly """ # First, make individual star executables star1_src_folder = os.path.join(working_directory, 'star1', 'src') if os.path.exists(star1_src_folder): shutil.rmtree(star1_src_folder) os.makedirs(star1_src_folder) star1_make_folder = os.path.join(working_directory, 'star1', 'make') if os.path.exists(star1_make_folder): shutil.rmtree(star1_make_folder) os.makedirs(star1_make_folder) star2_src_folder = os.path.join(working_directory, 'star2', 'src') if os.path.exists(star2_src_folder): shutil.rmtree(star2_src_folder) os.makedirs(star2_src_folder) star2_make_folder = os.path.join(working_directory, 'star2', 'make') if os.path.exists(star2_make_folder): shutil.rmtree(star2_make_folder) os.makedirs(star2_make_folder) # Now make the binary folder binary_src_folder = os.path.join(working_directory, 'binary', 'src') if os.path.exists(binary_src_folder): shutil.rmtree(binary_src_folder) os.makedirs(binary_src_folder) binary_make_folder = os.path.join(working_directory, 'binary', 'make') if os.path.exists(binary_make_folder): shutil.rmtree(binary_make_folder) os.makedirs(binary_make_folder) with open('mk', "w") as f: # first we need to cd into the make folder for k, v in mesa_extras.items(): if v is not None: if ('binary_extras' in k) or ('binary_run' in k): shutil.copy(v, binary_src_folder) elif ('star_run' in k): shutil.copy(v, star1_src_folder) shutil.copy(v, star2_src_folder) elif ('star1_extras' in k): shutil.copy(v, star1_src_folder) elif ('star2_extras' in k): shutil.copy(v, star2_src_folder) elif 'makefile_binary' in k: shutil.copy(v, os.path.join(binary_make_folder, k)) f.write('cd {0}\n'.format(binary_make_folder)) f.write('make -f {0}\n'.format(k)) elif 'makefile_star' in k: shutil.copy(v, os.path.join(star1_make_folder, k)) f.write('cd {0}\n'.format(star1_make_folder)) f.write('make -f {0}\n'.format(k)) shutil.copy(v, os.path.join(star2_make_folder, k)) f.write('cd {0}\n'.format(star2_make_folder)) f.write('make -f {0}\n'.format(k)) elif 'mesa_dir' == k: continue else: shutil.copy(v, working_directory) os.system("chmod 755 mk") os.system('./mk') return os.path.join(working_directory,'binary','binary'), \ os.path.join(working_directory,'star1','star'), \ os.path.join(working_directory,'star2','star'), \
[docs] def construct_command_line(number_of_mpi_processes, path_to_grid, binary_exe, star1_exe, star2_exe, inlist_binary_project, inlist_star1_binary, inlist_star2_binary, inlist_star1_formation, inlist_star2_formation, star_history_columns, binary_history_columns, profile_columns, run_directory, grid_type, path_to_run_grid_exec, psycris_inifile=None, keep_profiles=False, keep_photos=False): """Based on the inifile construct the command line call to posydon-run-grid """ if grid_type == "fixed": command_line = 'python {15} --mesa-grid {1} --mesa-binary-executable {2} ' elif grid_type == "dynamic": command_line = 'mpirun --bind-to none -np {0} python -m mpi4py {15} --mesa-grid {1} --mesa-binary-executable {2} ' else: raise ValueError("grid_type can either be fixed or dynamic not anything else") command_line += '--mesa-star1-executable {3} --mesa-star2-executable {4} --mesa-binary-inlist-project {5} ' command_line += '--mesa-binary-inlist1 {6} --mesa-binary-inlist2 {7} --mesa-star1-inlist-project {8} ' command_line += '--mesa-star2-inlist-project {9} --mesa-star-history-columns {10} ' command_line += '--mesa-binary-history-columns {11} --mesa-profile-columns {12} ' command_line += '--output-directory {13} --grid-type {14} ' command_line += '--psycris-inifile {16}' if keep_profiles: command_line += ' --keep_profiles' if keep_photos: command_line += ' --keep_photos' command_line = command_line.format(number_of_mpi_processes, path_to_grid, binary_exe, star1_exe, star2_exe, inlist_binary_project, inlist_star1_binary, inlist_star2_binary, inlist_star1_formation, inlist_star2_formation, star_history_columns, binary_history_columns, profile_columns, run_directory, grid_type, path_to_run_grid_exec, psycris_inifile) return command_line
############################################################################### # BEGIN MAIN FUNCTION ############################################################################### if __name__ == '__main__': # READ COMMANDLINE ARGUMENTS ########################################################################### args = parse_commandline() try: os.environ['MESA_DIR'] except: raise ValueError("MESA_DIR must be defined in your environment " "before you can run a grid os MESA runs") # Determine location of executables proc = subprocess.Popen(['which', 'posydon-run-grid'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) (path_to_run_grid_exec, err) = proc.communicate() if not path_to_run_grid_exec: raise ValueError('Cannot locate posydon-run-grid executable in your path') else: path_to_run_grid_exec = path_to_run_grid_exec.decode('utf-8').strip('\n') run_parameters, slurm, mesa_inlists, mesa_extras = configfile.parse_inifile(args.inifile) if 'scenario' not in mesa_inlists.keys(): mesa_inlists['scenario'] = None if 'keep_profiles' not in run_parameters.keys(): run_parameters['keep_profiles'] = False if 'keep_photos' not in run_parameters.keys(): run_parameters['keep_photos'] = False if ((not os.path.isfile(run_parameters['grid'])) and (not os.path.isdir(run_parameters['grid']))): raise ValueError("Supplied grid does not exist, please check your path and try again") if ('psycris_inifile' not in run_parameters.keys()) and (args.grid_type == 'dynamic'): raise ValueError("Please add psycris inifile to the [run_parameters] section of the inifile.") if mesa_inlists['scenario'] is not None: find_inlist_from_scenario(source=mesa_inlists['scenario'][0], gitcommit=mesa_inlists['scenario'][1], system_type=mesa_inlists['scenario'][2]) # read grid if '.csv' in run_parameters['grid']: grid_df = pandas.read_csv(run_parameters['grid']) fixgrid_file_name = run_parameters['grid'] elif '.h5' in run_parameters['grid']: psy_grid = PSyGrid() psy_grid.load(run_parameters['grid']) grid_df = psy_grid.get_pandas_initial_final() psy_grid.close() fixgrid_file_name = run_parameters['grid'] elif os.path.isdir(run_parameters['grid']): mygrid = PSyGrid().create(run_parameters['grid'], "./fixed_grid_results.h5", slim=True) psy_grid = PSyGrid() psy_grid.load("./fixed_grid_results.h5") grid_df = psy_grid.get_pandas_initial_final() psy_grid.close() fixgrid_file_name = os.path.join(os.getcwd(), "fixed_grid_results.h5") else: raise ValueError('Grid format not recognized, please feed in an acceptable format: csv') # validate mesa_extras dictionary # check if user has supplied multiple run_star run_binary extras files and enforce mesa, then posydon, then user order extras_files_types = sorted(set([k.split('_')[0] for k in mesa_extras.keys() if ('binary_extras' in k)])) print("WE ARE USING THE EXTRA FILE FROM TYPE {0}".format(extras_files_types[-1])) for k in mesa_extras.keys(): if ('binary_extras' in k) and (extras_files_types[-1] not in k): print("Section mesa_extras value {0} is being set to None".format(k)) mesa_extras[k] = None binary_exe, star1_exe, star2_exe = make_executables(mesa_extras=mesa_extras, working_directory=args.run_directory) if args.grid_type == "dynamic": dynamic_grid_params = parse_inifile(run_parameters["psycris_inifile"]) mesa_params_to_run_grid_over = dynamic_grid_params["posydon_dynamic_sampling_kwargs"]["mesa_column_names"] inlist_star1_formation, inlist_star2_formation, inlist_binary_project, inlist_star1_binary, \ inlist_star2_binary = construct_static_inlist(mesa_inlists, grid_parameters=mesa_params_to_run_grid_over, working_directory=args.run_directory) else: inlist_star1_formation, inlist_star2_formation, inlist_binary_project, inlist_star1_binary, \ inlist_star2_binary = construct_static_inlist(mesa_inlists, grid_parameters=grid_df.columns, working_directory=args.run_directory) # now we can write the mpi command line if slurm['job_array']: command_line = construct_command_line(1, run_parameters['grid'], binary_exe, star1_exe, star2_exe, inlist_binary_project, inlist_star1_binary, inlist_star2_binary, inlist_star1_formation, inlist_star2_formation, mesa_inlists['star_history_columns'], mesa_inlists['binary_history_columns'], mesa_inlists['profile_columns'], args.run_directory, 'fixed', path_to_run_grid_exec, keep_profiles=run_parameters['keep_profiles'], keep_photos=run_parameters['keep_photos']) command_line += ' --grid-point-index $SLURM_ARRAY_TASK_ID' else: command_line = construct_command_line(slurm['number_of_mpi_tasks']*slurm['number_of_nodes'], fixgrid_file_name, binary_exe, star1_exe, star2_exe, inlist_binary_project, inlist_star1_binary, inlist_star2_binary, inlist_star1_formation, inlist_star2_formation, mesa_inlists['star_history_columns'], mesa_inlists['binary_history_columns'], mesa_inlists['profile_columns'], args.run_directory, args.grid_type, path_to_run_grid_exec, psycris_inifile = run_parameters["psycris_inifile"], keep_profiles=run_parameters['keep_profiles'], keep_photos=run_parameters['keep_photos']) if args.submission_type == 'slurm': command_line += ' --job_start $SLURM_JOB_START_TIME --job_end $SLURM_JOB_END_TIME' if 'work_dir' in slurm.keys() and not(slurm['work_dir'] == ''): command_line += ' --temporary-directory '+slurm['work_dir'] # now we need to know how this person plans to run the above created # command. As a shell script? As a SLURM submission? In some other way? if args.submission_type == 'shell': with open('grid_command.sh', 'w') as f: f.write('#!/bin/bash\n\n') f.write('export OMP_NUM_THREADS={0}\n\n'.format(slurm['number_of_cpus_per_task'])) f.write('export MESASDK_ROOT={0}\n'.format(os.environ['MESASDK_ROOT'])) f.write('source $MESASDK_ROOT/bin/mesasdk_init.sh\n') f.write('export MESA_DIR={0}\n\n\n'.format(os.environ['MESA_DIR'])) f.write(command_line) elif args.submission_type == 'slurm': # if slurm will we submit as job array or MPI if slurm['job_array']: if '.csv' in run_parameters['grid']: grid = pandas.read_csv(run_parameters['grid']) else: raise ValueError('Grid format not recognized, please feed in an acceptable format: csv') with open('slurm_job_array_grid_submit.sh', 'w') as f: f.write('#!/bin/bash\n') f.write('#SBATCH --account={0}\n'.format(slurm['account'])) f.write('#SBATCH --partition={0}\n'.format(slurm['partition'])) f.write('#SBATCH -N 1\n') f.write('#SBATCH --array=0-{0}\n'.format(len(grid)-1)) f.write('#SBATCH --cpus-per-task {0}\n'.format(slurm['number_of_cpus_per_task'])) f.write('#SBATCH --ntasks-per-node 1\n') f.write('#SBATCH --time={0}\n'.format(slurm['walltime'])) f.write('#SBATCH --job-name=\"mesa_grid_\${SLURM_ARRAY_TASK_ID}\"\n') f.write('#SBATCH --output=mesa_grid.%A_%a.out\n') f.write('#SBATCH --mail-type=ALL\n') f.write('#SBATCH --mail-user={0}\n'.format(slurm['email'])) f.write('#SBATCH --mem-per-cpu=4G\n\n') f.write('export OMP_NUM_THREADS={0}\n\n'.format(slurm['number_of_cpus_per_task'])) f.write('export MESASDK_ROOT={0}\n'.format(os.environ['MESASDK_ROOT'])) f.write('source $MESASDK_ROOT/bin/mesasdk_init.sh\n') f.write('export MESA_DIR={0}\n\n\n'.format(os.environ['MESA_DIR'])) f.write(command_line) else: with open('slurm_mpi_grid_submit.sh', 'w') as f: f.write('#!/bin/bash\n') f.write('#SBATCH --account={0}\n'.format(slurm['account'])) f.write('#SBATCH --partition={0}\n'.format(slurm['partition'])) f.write('#SBATCH -N {0}\n'.format(slurm['number_of_nodes'])) f.write('#SBATCH --cpus-per-task {0}\n'.format(slurm['number_of_cpus_per_task'])) f.write('#SBATCH --ntasks-per-node {0}\n'.format(slurm['number_of_mpi_tasks'])) f.write('#SBATCH --time={0}\n'.format(slurm['walltime'])) f.write('#SBATCH --output=\"mesa_grid.out\"\n') f.write('#SBATCH --mail-type=ALL\n') f.write('#SBATCH --mail-user={0}\n'.format(slurm['email'])) f.write('#SBATCH --mem-per-cpu=4G\n\n') f.write('export OMP_NUM_THREADS={0}\n\n'.format(slurm['number_of_cpus_per_task'])) f.write('export MESASDK_ROOT={0}\n'.format(os.environ['MESASDK_ROOT'])) f.write('source $MESASDK_ROOT/bin/mesasdk_init.sh\n') f.write('export MESA_DIR={0}\n\n\n'.format(os.environ['MESA_DIR'])) f.write(command_line)