mesaport.ProjectOps.project_ops
1import os 2import shutil 3import psutil 4import subprocess 5import glob 6from itertools import repeat 7import traceback 8 9from rich import print, progress, prompt, status 10progress_columns = (progress.SpinnerColumn(spinner_name="moon"), 11 progress.MofNCompleteColumn(), 12 *progress.Progress.get_default_columns(), 13 progress.TimeElapsedColumn()) 14import multiprocessing as mp 15 16from ..Access import MesaAccess, GyreAccess, MesaEnvironmentHandler 17from . import ops_helper 18from . import istarmap 19 20class ProjectOps: 21 """This class handles MESA project operations. 22 """ 23 def __init__(self, name='work', astero=False, binary=False): 24 """Constructor for ProjectOps class. 25 26 Args: 27 name (str, optional): Name of the project. Defaults to 'work'. 28 binary (bool, optional): True for a binary star system. Defaults to False. 29 """ 30 self.projName = name 31 self.binary = binary 32 self.astero = astero 33 self.envObject = MesaEnvironmentHandler() 34 if self.binary: 35 self.defaultWork = os.path.join(self.envObject.mesaDir, 'binary/work') 36 elif self.astero: 37 self.defaultWork = os.path.join(self.envObject.mesaDir, 'astero/work') 38 else: 39 self.defaultWork = os.path.join(self.envObject.mesaDir, 'star/work') 40 41 if os.path.exists(self.projName): 42 self.exists = True ## Proj already present flag 43 if os.path.isabs(self.projName): 44 self.work_dir = self.projName 45 else: 46 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 47 else: 48 self.exists = False 49 50 51 52 def create(self, overwrite=None, clean=None): 53 """Creates a new MESA project. 54 55 Args: 56 overwrite (bool, optional): Overwrite the existing project. Defaults to None. 57 clean (bool, optional): Clean the existing project. Defaults to None. 58 """ 59 60 def useExisting(): 61 """A helper function to use the existing project.""" 62 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 63 raise ValueError("Aborting!!! No project specified.") 64 65 def cleanCheck(): 66 """A helper function to check if the user wants to clean the existing project.""" 67 if clean is None: 68 if prompt.Confirm.ask(f"Clean the existing '{self.projName}' project for re-use?", default=False): 69 self.clean() 70 else: 71 useExisting() 72 elif clean is True: 73 self.clean() 74 elif clean is False: 75 print(f"Using the already existing '{self.projName}' project as it is.") 76 else: 77 raise ValueError("Invalid input for argument 'clean'.") 78 79 def writeover(): 80 """A helper function to overwrite the existing project.""" 81 try: 82 shutil.rmtree(self.projName) 83 shutil.copytree(self.defaultWork, self.projName) 84 85 except shutil.Error: 86 raise Exception(f"Could not overwrite the existing '{self.projName}' project!") 87 88 if self.exists is True: 89 if os.path.isabs(self.projName): 90 self.work_dir = self.projName 91 else: 92 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 93 if overwrite is True: 94 writeover() 95 elif overwrite is False: 96 cleanCheck() 97 elif overwrite is None: 98 print(f"Mesa project named '{self.projName}' already exists!") 99 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 100 if prompt.Confirm.ask("Do you wish to overwrite?", default=False): 101 writeover() 102 else: 103 cleanCheck() 104 else: 105 raise ValueError("Invalid input for argument 'overwrite'.") 106 else: 107 try: 108 shutil.copytree(self.defaultWork, self.projName) 109 if os.path.isabs(self.projName): 110 self.work_dir = self.projName 111 else: 112 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 113 self.exists = True 114 except shutil.Error: 115 raise Exception(f"Could not create the project '{self.projName}'!") 116 117 def delete(self): 118 """Deletes the project. 119 """ 120 if self.exists is True: 121 shutil.rmtree(self.work_dir) 122 print(f"Deleted project '{self.projName}'.") 123 else: 124 print(f"Project '{self.projName}' does not exist.") 125 126 def clean(self): 127 """Cleans the project. 128 129 Raises: 130 Exception: If the clean fails. 131 """ 132 ops_helper.check_exists(self.exists, self.projName) 133 ## clean files are missing a shebang (#!/bin/bash) and hence need to be run with bash 134 res = subprocess.call('/bin/bash ./clean', cwd=self.work_dir, shell=True, stderr=subprocess.STDOUT) 135 runlog = os.path.join(self.work_dir, "runlog") 136 if os.path.exists(runlog): 137 os.remove(runlog) 138 if res!=0: 139 raise Exception(f"Clean failed! Returned non-zero exit code ({res})") 140 else: 141 print("Clean successful.\n") 142 143 144 def make(self, silent=False): 145 """Makes the project. 146 147 Args: 148 silent (bool, optional): Run the command silently. Defaults to False. 149 150 Raises: 151 Exception: If the make fails. 152 """ 153 ops_helper.check_exists(self.exists, self.projName) 154 if silent: 155 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 156 else: 157 with status.Status("[b i cyan3]Making...", spinner="moon"): 158 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 159 if res!=0: 160 raise Exception(f"Make failed! Returned non-zero exit code ({res})") 161 else: 162 print("Make successful.\n") 163 164 165 166 def run(self, silent=True, logging=True, parallel=False, trace=None, env=os.environ.copy()): 167 """ 168 Runs the project. 169 Args: 170 silent (bool, optional): Run the command silently. Defaults to True. 171 logging (bool, optional): Log the run. Defaults to True. 172 parallel (bool, optional): Run in parallel. Defaults to False. 173 trace (list of str, optional): Trace specific history variables. Defaults to None. 174 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 175 176 Raises: 177 Exception: If the project is not made yet. 178 ValueError: If the input for argument 'silent' is invalid. 179 Exception: If the run fails. 180 181 Returns: (If run is successful) 182 termination_code (str): Termination code. 183 age (float): Age of the star in years. 184 """ 185 if trace is not None: 186 ops_helper.setup_trace(trace, self.work_dir) 187 ops_helper.check_exists(self.exists, self.projName) 188 if logging: 189 runlog = os.path.join(self.work_dir, "run.log") 190 else: 191 runlog = os.devnull 192 if not os.path.exists(os.path.join(self.work_dir, "star")) and \ 193 not os.path.exists(os.path.join(self.work_dir, "binary")): 194 raise Exception("Aborting! Run 'make()' first.") 195 else: 196 if silent not in [True, False]: 197 raise ValueError("Invalid input for argument 'silent'") 198 else: 199 if parallel: 200 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 201 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 202 else: 203 with status.Status("[b i cyan3]Running...", spinner="moon") as status_: 204 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 205 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 206 if res == False: 207 raise Exception("Run failed! Check runlog.") 208 else: 209 termination_code, age = res 210 if age is not None: 211 print("Run successful.\n") 212 return termination_code, age 213 else: 214 print("Run unsuccessful.\n") 215 return termination_code, None 216 217 218 219 def resume(self, photo=None, silent=True, target=None, logging=True, parallel=False, trace=None, env=os.environ.copy()): 220 """Resumes the run from a given photo. 221 222 Args: 223 photo (str, optional): Photo name from which the run is to be resumed. 224 If None, the last photo is used. Defaults to None. 225 silent (bool, optional): Run the command silently. Defaults to True. 226 target (str, optional): Target photo name. Defaults to None. 227 logging (bool, optional): Log the run. Defaults to True. 228 parallel (bool, optional): Run in parallel. Defaults to False. 229 trace (list of str, optional): Trace specific history variables. Defaults to None. 230 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 231 232 Raises: 233 FileNotFoundError: If the photo does not exist. 234 ValueError: If the input for argument 'silent' is invalid. 235 236 Returns: (If run is successful) 237 termination_code (str): Termination code. 238 age (float): Age of the star in years. 239 """ 240 if trace is not None: 241 ops_helper.setup_trace(trace, self.work_dir) 242 ops_helper.check_exists(self.exists, self.projName) 243 if logging: 244 runlog = os.path.join(self.work_dir, "run.log") 245 else: 246 runlog = os.devnull 247 if photo == None: 248 if parallel: 249 res = ops_helper.run_subprocess(commands='./re', wdir=self.work_dir, 250 silent=silent, runlog=runlog, parallel=True, trace=trace) 251 else: 252 # print(f"[b i cyan3]Resuming run from the most recent photo.") 253 with status.Status("[b i cyan3]Resuming run from the most recent photo.\nRunning...", spinner="moon") as status_: 254 res = ops_helper.run_subprocess(commands=f'./re', wdir=self.work_dir, 255 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 256 else: 257 if self.binary: 258 if target == 'primary': 259 photo_path = os.path.join(self.work_dir, "photos1", photo) 260 elif target == 'secondary': 261 photo_path = os.path.join(self.work_dir, "photos2", photo) 262 else: 263 raise ValueError('''Invalid input for argument 'target'. 264 Please use 'primary' or 'secondary' ''') 265 else: 266 photo_path = os.path.join(self.work_dir, "photos", photo) 267 268 if not os.path.isfile(photo_path): 269 raise FileNotFoundError(f"Photo '{photo}' could not be exists.") 270 else: 271 if silent not in [True, False]: 272 raise ValueError("Invalid input for argument 'silent'.") 273 else: 274 if parallel: 275 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 276 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 277 else: 278 with status.Status(f"[b i cyan3]Resuming run from photo {photo}.\nRunning...", spinner="moon") as status_: 279 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 280 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 281 if res is False: 282 raise Exception("Resume from photo failed! Check runlog.") 283 else: 284 termination_code, age = res 285 if age is not None: 286 print("Run successful.\n") 287 return termination_code, age 288 else: 289 print("Run unsuccessful.\n") 290 return termination_code, None 291 292 293 def runGyre(self, gyre_in, files='all', wdir=None, data_format="GYRE", silent=True, target=None, logging=True, logfile="gyre.log", 294 parallel=False, n_cores=None, gyre_input_params=None, env=os.environ.copy()): 295 """ 296 Runs GYRE. 297 298 Arguments: 299 gyre_in (str): GYRE input file. 300 files (str or list of strings, optional): Profile files in the LOGS directory 301 to be processed by GYRE. Defaults to 'all'. 302 wdir (str, optional): Working directory. Defaults to None and uses the project directory. 303 silent (bool, optional): Run the command silently. Defaults to True. 304 target (str, optional): Target star. Defaults to None. 305 logging (bool, optional): Log the output. Defaults to True. 306 logdir (str, optional): Log file name. Defaults to "run.log". 307 parallel (bool, optional): Run GYRE in parallel. Defaults to False. 308 n_cores (int, optional): Number of cores to use. Defaults to None. 309 gyre_input_params (dict or list of dicts, optional): Dictionary of GYRE input parameters. 310 {parameter: value}. Parameter must be a string. Value 311 can be a string, int, float, or bool. 312 List of dictionaries for multiple profiles. 313 list of dicts must be in the same order as the 314 list of profile files. len(list of dicts) must be 315 equal to len(list of profile files). Defaults to None. 316 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 317 Raises: 318 FileNotFoundError: If the GYRE input file does not exist. 319 ValueError: If the input for argument 'silent' is invalid. 320 ValueError: If the input for argument 'files' is invalid. 321 """ 322 if wdir is not None: 323 wdir = os.path.abspath(wdir) 324 gyre_in = os.path.abspath(gyre_in) 325 326 if 'GYRE_DIR' in os.environ: 327 gyre_ex = os.path.join(os.environ['GYRE_DIR'], "bin", "gyre") 328 else: 329 raise FileNotFoundError("GYRE_DIR is not set in your enviroment. Be sure to set it properly!!") 330 if self.binary: 331 if target == 'primary': 332 LOGS_dir = os.path.join(self.work_dir, "LOGS1") if wdir is None else wdir 333 elif target == 'secondary': 334 LOGS_dir = os.path.join(self.work_dir, "LOGS2") if wdir is None else wdir 335 else: 336 raise ValueError("""Invalid input for argument 'star'. 337 Please use primary or secondary""") 338 else: 339 LOGS_dir = os.path.join(self.work_dir, "LOGS") if wdir is None else wdir 340 341 if wdir is None: 342 wdir = self.work_dir 343 344 if logging: 345 runlog = os.path.join(wdir, logfile) 346 else: 347 runlog = os.devnull 348 349 350 if not silent in [True, False]: 351 raise ValueError("Invalid input for argument 'silent'") 352 353 if files == 'all' or isinstance(files, list) or isinstance(files, str): 354 ## ALL FILES 355 if files == 'all': 356 files = [] 357 try: 358 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}")), 359 key=lambda x: int(os.path.basename(x).split('.')[0].split('profile')[1])) 360 except ValueError: 361 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}"))) 362 files = [file.split('/')[-1] for file in files] 363 if len(files) == 0: 364 raise ValueError(f"No {data_format} files found in LOGS directory.") 365 366 ## SPECIFIC FILES 367 elif type(files) == list or type(files) == str: 368 if type(files) == str: 369 files = [files] 370 gyre_input_params = [gyre_input_params] 371 if len(files) == 0: 372 raise ValueError("No files provided.") 373 # else: 374 # for file in files: 375 # if not os.path.isfile(os.path.join(LOGS_dir, file)) and not os.path.isfile(file): 376 # raise FileNotFoundError(f"File '{file}' does not exist.") 377 378 with open(f'{wdir}/gyre.log', 'a+') as f: 379 f.write(f"Total {len(files)} profiles to be processed by GYRE.\n\n") 380 if parallel: 381 gyre_input_params = gyre_input_params if gyre_input_params is not None else repeat(None) 382 os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' 383 ## copy gyre.in to gyre1.in, gyre2.in, etc. for parallel runs 384 for i, file in enumerate(files): 385 num = file.split(".")[0] 386 new_gyre_in = os.path.join(wdir, f"gyre{num}.in") 387 shutil.copyfile(gyre_in, new_gyre_in) 388 # commands, wdir, 389 # silent=True, runlog='', 390 # status=None, filename="", 391 # data_format="FGONG", parallel=False, gyre_in=None, 392 # gyre_input_params=None, trace=None, env=None 393 args = (repeat(f'{gyre_ex} gyre.in'), repeat(LOGS_dir), 394 repeat(silent), repeat(runlog), 395 repeat(None), files, repeat(data_format), 396 repeat(True), repeat(gyre_in), 397 gyre_input_params, repeat(None), repeat(os.environ.copy())) 398 if n_cores is None: 399 n_cores = psutil.cpu_count(logical=True) 400 Pool = mp.Pool 401 with progress.Progress(*progress_columns) as progressbar: 402 task = progressbar.add_task("[b i cyan3]Running GYRE...", total=len(files)) 403 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 404 with Pool(n_processes) as pool: 405 gyre_in = os.path.abspath(gyre_in) 406 try: 407 for _ in pool.istarmap(ops_helper.run_subprocess, zip(*args)): 408 progressbar.advance(task) 409 except Exception as e: 410 print(traceback.format_exc()) 411 print(f"Error: {e}") 412 pool.terminate() 413 else: 414 try: 415 from concurrent.futures import ThreadPoolExecutor 416 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 417 with ThreadPoolExecutor(max_workers=n_processes) as executor: 418 gyre_in = os.path.abspath(gyre_in) 419 try: 420 executor.map(ops_helper.run_subprocess, *args) 421 except Exception as e: 422 print(traceback.format_exc()) 423 print(f"Error: {e}") 424 executor.shutdown(wait=False) 425 except Exception as e: 426 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 427 with open(runlog, 'a+') as outfile: 428 for fname in filenames: 429 with open(fname) as infile: 430 for line in infile: 431 outfile.write(line) 432 os.remove(fname) 433 print(traceback.format_exc()) 434 print(f"Error: {e}") 435 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 436 with open(runlog, 'a+') as outfile: 437 for fname in filenames: 438 with open(fname) as infile: 439 for line in infile: 440 outfile.write(line) 441 os.remove(fname) 442 res = True 443 else: 444 for i, file in enumerate(files): 445 gyre_input_params_i = gyre_input_params[i] if gyre_input_params is not None else None 446 res = ops_helper.run_subprocess(f'{gyre_ex} gyre.in', wdir=LOGS_dir, filename=file, 447 silent=silent, runlog=runlog, status=None, gyre_in=gyre_in, 448 data_format=data_format, gyre_input_params=gyre_input_params_i) 449 else: 450 raise ValueError("Invalid input for argument 'files'") 451 if res is False: 452 print("GYRE run failed! Check runlog.") 453 else: 454 print("GYRE run complete!\n") 455 return res
progress_columns =
(<rich.progress.SpinnerColumn object>, <rich.progress.MofNCompleteColumn object>, <rich.progress.TextColumn object>, <rich.progress.BarColumn object>, <rich.progress.TaskProgressColumn object>, <rich.progress.TimeRemainingColumn object>, <rich.progress.TimeElapsedColumn object>)
class
ProjectOps:
21class ProjectOps: 22 """This class handles MESA project operations. 23 """ 24 def __init__(self, name='work', astero=False, binary=False): 25 """Constructor for ProjectOps class. 26 27 Args: 28 name (str, optional): Name of the project. Defaults to 'work'. 29 binary (bool, optional): True for a binary star system. Defaults to False. 30 """ 31 self.projName = name 32 self.binary = binary 33 self.astero = astero 34 self.envObject = MesaEnvironmentHandler() 35 if self.binary: 36 self.defaultWork = os.path.join(self.envObject.mesaDir, 'binary/work') 37 elif self.astero: 38 self.defaultWork = os.path.join(self.envObject.mesaDir, 'astero/work') 39 else: 40 self.defaultWork = os.path.join(self.envObject.mesaDir, 'star/work') 41 42 if os.path.exists(self.projName): 43 self.exists = True ## Proj already present flag 44 if os.path.isabs(self.projName): 45 self.work_dir = self.projName 46 else: 47 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 48 else: 49 self.exists = False 50 51 52 53 def create(self, overwrite=None, clean=None): 54 """Creates a new MESA project. 55 56 Args: 57 overwrite (bool, optional): Overwrite the existing project. Defaults to None. 58 clean (bool, optional): Clean the existing project. Defaults to None. 59 """ 60 61 def useExisting(): 62 """A helper function to use the existing project.""" 63 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 64 raise ValueError("Aborting!!! No project specified.") 65 66 def cleanCheck(): 67 """A helper function to check if the user wants to clean the existing project.""" 68 if clean is None: 69 if prompt.Confirm.ask(f"Clean the existing '{self.projName}' project for re-use?", default=False): 70 self.clean() 71 else: 72 useExisting() 73 elif clean is True: 74 self.clean() 75 elif clean is False: 76 print(f"Using the already existing '{self.projName}' project as it is.") 77 else: 78 raise ValueError("Invalid input for argument 'clean'.") 79 80 def writeover(): 81 """A helper function to overwrite the existing project.""" 82 try: 83 shutil.rmtree(self.projName) 84 shutil.copytree(self.defaultWork, self.projName) 85 86 except shutil.Error: 87 raise Exception(f"Could not overwrite the existing '{self.projName}' project!") 88 89 if self.exists is True: 90 if os.path.isabs(self.projName): 91 self.work_dir = self.projName 92 else: 93 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 94 if overwrite is True: 95 writeover() 96 elif overwrite is False: 97 cleanCheck() 98 elif overwrite is None: 99 print(f"Mesa project named '{self.projName}' already exists!") 100 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 101 if prompt.Confirm.ask("Do you wish to overwrite?", default=False): 102 writeover() 103 else: 104 cleanCheck() 105 else: 106 raise ValueError("Invalid input for argument 'overwrite'.") 107 else: 108 try: 109 shutil.copytree(self.defaultWork, self.projName) 110 if os.path.isabs(self.projName): 111 self.work_dir = self.projName 112 else: 113 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 114 self.exists = True 115 except shutil.Error: 116 raise Exception(f"Could not create the project '{self.projName}'!") 117 118 def delete(self): 119 """Deletes the project. 120 """ 121 if self.exists is True: 122 shutil.rmtree(self.work_dir) 123 print(f"Deleted project '{self.projName}'.") 124 else: 125 print(f"Project '{self.projName}' does not exist.") 126 127 def clean(self): 128 """Cleans the project. 129 130 Raises: 131 Exception: If the clean fails. 132 """ 133 ops_helper.check_exists(self.exists, self.projName) 134 ## clean files are missing a shebang (#!/bin/bash) and hence need to be run with bash 135 res = subprocess.call('/bin/bash ./clean', cwd=self.work_dir, shell=True, stderr=subprocess.STDOUT) 136 runlog = os.path.join(self.work_dir, "runlog") 137 if os.path.exists(runlog): 138 os.remove(runlog) 139 if res!=0: 140 raise Exception(f"Clean failed! Returned non-zero exit code ({res})") 141 else: 142 print("Clean successful.\n") 143 144 145 def make(self, silent=False): 146 """Makes the project. 147 148 Args: 149 silent (bool, optional): Run the command silently. Defaults to False. 150 151 Raises: 152 Exception: If the make fails. 153 """ 154 ops_helper.check_exists(self.exists, self.projName) 155 if silent: 156 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 157 else: 158 with status.Status("[b i cyan3]Making...", spinner="moon"): 159 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 160 if res!=0: 161 raise Exception(f"Make failed! Returned non-zero exit code ({res})") 162 else: 163 print("Make successful.\n") 164 165 166 167 def run(self, silent=True, logging=True, parallel=False, trace=None, env=os.environ.copy()): 168 """ 169 Runs the project. 170 Args: 171 silent (bool, optional): Run the command silently. Defaults to True. 172 logging (bool, optional): Log the run. Defaults to True. 173 parallel (bool, optional): Run in parallel. Defaults to False. 174 trace (list of str, optional): Trace specific history variables. Defaults to None. 175 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 176 177 Raises: 178 Exception: If the project is not made yet. 179 ValueError: If the input for argument 'silent' is invalid. 180 Exception: If the run fails. 181 182 Returns: (If run is successful) 183 termination_code (str): Termination code. 184 age (float): Age of the star in years. 185 """ 186 if trace is not None: 187 ops_helper.setup_trace(trace, self.work_dir) 188 ops_helper.check_exists(self.exists, self.projName) 189 if logging: 190 runlog = os.path.join(self.work_dir, "run.log") 191 else: 192 runlog = os.devnull 193 if not os.path.exists(os.path.join(self.work_dir, "star")) and \ 194 not os.path.exists(os.path.join(self.work_dir, "binary")): 195 raise Exception("Aborting! Run 'make()' first.") 196 else: 197 if silent not in [True, False]: 198 raise ValueError("Invalid input for argument 'silent'") 199 else: 200 if parallel: 201 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 202 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 203 else: 204 with status.Status("[b i cyan3]Running...", spinner="moon") as status_: 205 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 206 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 207 if res == False: 208 raise Exception("Run failed! Check runlog.") 209 else: 210 termination_code, age = res 211 if age is not None: 212 print("Run successful.\n") 213 return termination_code, age 214 else: 215 print("Run unsuccessful.\n") 216 return termination_code, None 217 218 219 220 def resume(self, photo=None, silent=True, target=None, logging=True, parallel=False, trace=None, env=os.environ.copy()): 221 """Resumes the run from a given photo. 222 223 Args: 224 photo (str, optional): Photo name from which the run is to be resumed. 225 If None, the last photo is used. Defaults to None. 226 silent (bool, optional): Run the command silently. Defaults to True. 227 target (str, optional): Target photo name. Defaults to None. 228 logging (bool, optional): Log the run. Defaults to True. 229 parallel (bool, optional): Run in parallel. Defaults to False. 230 trace (list of str, optional): Trace specific history variables. Defaults to None. 231 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 232 233 Raises: 234 FileNotFoundError: If the photo does not exist. 235 ValueError: If the input for argument 'silent' is invalid. 236 237 Returns: (If run is successful) 238 termination_code (str): Termination code. 239 age (float): Age of the star in years. 240 """ 241 if trace is not None: 242 ops_helper.setup_trace(trace, self.work_dir) 243 ops_helper.check_exists(self.exists, self.projName) 244 if logging: 245 runlog = os.path.join(self.work_dir, "run.log") 246 else: 247 runlog = os.devnull 248 if photo == None: 249 if parallel: 250 res = ops_helper.run_subprocess(commands='./re', wdir=self.work_dir, 251 silent=silent, runlog=runlog, parallel=True, trace=trace) 252 else: 253 # print(f"[b i cyan3]Resuming run from the most recent photo.") 254 with status.Status("[b i cyan3]Resuming run from the most recent photo.\nRunning...", spinner="moon") as status_: 255 res = ops_helper.run_subprocess(commands=f'./re', wdir=self.work_dir, 256 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 257 else: 258 if self.binary: 259 if target == 'primary': 260 photo_path = os.path.join(self.work_dir, "photos1", photo) 261 elif target == 'secondary': 262 photo_path = os.path.join(self.work_dir, "photos2", photo) 263 else: 264 raise ValueError('''Invalid input for argument 'target'. 265 Please use 'primary' or 'secondary' ''') 266 else: 267 photo_path = os.path.join(self.work_dir, "photos", photo) 268 269 if not os.path.isfile(photo_path): 270 raise FileNotFoundError(f"Photo '{photo}' could not be exists.") 271 else: 272 if silent not in [True, False]: 273 raise ValueError("Invalid input for argument 'silent'.") 274 else: 275 if parallel: 276 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 277 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 278 else: 279 with status.Status(f"[b i cyan3]Resuming run from photo {photo}.\nRunning...", spinner="moon") as status_: 280 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 281 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 282 if res is False: 283 raise Exception("Resume from photo failed! Check runlog.") 284 else: 285 termination_code, age = res 286 if age is not None: 287 print("Run successful.\n") 288 return termination_code, age 289 else: 290 print("Run unsuccessful.\n") 291 return termination_code, None 292 293 294 def runGyre(self, gyre_in, files='all', wdir=None, data_format="GYRE", silent=True, target=None, logging=True, logfile="gyre.log", 295 parallel=False, n_cores=None, gyre_input_params=None, env=os.environ.copy()): 296 """ 297 Runs GYRE. 298 299 Arguments: 300 gyre_in (str): GYRE input file. 301 files (str or list of strings, optional): Profile files in the LOGS directory 302 to be processed by GYRE. Defaults to 'all'. 303 wdir (str, optional): Working directory. Defaults to None and uses the project directory. 304 silent (bool, optional): Run the command silently. Defaults to True. 305 target (str, optional): Target star. Defaults to None. 306 logging (bool, optional): Log the output. Defaults to True. 307 logdir (str, optional): Log file name. Defaults to "run.log". 308 parallel (bool, optional): Run GYRE in parallel. Defaults to False. 309 n_cores (int, optional): Number of cores to use. Defaults to None. 310 gyre_input_params (dict or list of dicts, optional): Dictionary of GYRE input parameters. 311 {parameter: value}. Parameter must be a string. Value 312 can be a string, int, float, or bool. 313 List of dictionaries for multiple profiles. 314 list of dicts must be in the same order as the 315 list of profile files. len(list of dicts) must be 316 equal to len(list of profile files). Defaults to None. 317 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 318 Raises: 319 FileNotFoundError: If the GYRE input file does not exist. 320 ValueError: If the input for argument 'silent' is invalid. 321 ValueError: If the input for argument 'files' is invalid. 322 """ 323 if wdir is not None: 324 wdir = os.path.abspath(wdir) 325 gyre_in = os.path.abspath(gyre_in) 326 327 if 'GYRE_DIR' in os.environ: 328 gyre_ex = os.path.join(os.environ['GYRE_DIR'], "bin", "gyre") 329 else: 330 raise FileNotFoundError("GYRE_DIR is not set in your enviroment. Be sure to set it properly!!") 331 if self.binary: 332 if target == 'primary': 333 LOGS_dir = os.path.join(self.work_dir, "LOGS1") if wdir is None else wdir 334 elif target == 'secondary': 335 LOGS_dir = os.path.join(self.work_dir, "LOGS2") if wdir is None else wdir 336 else: 337 raise ValueError("""Invalid input for argument 'star'. 338 Please use primary or secondary""") 339 else: 340 LOGS_dir = os.path.join(self.work_dir, "LOGS") if wdir is None else wdir 341 342 if wdir is None: 343 wdir = self.work_dir 344 345 if logging: 346 runlog = os.path.join(wdir, logfile) 347 else: 348 runlog = os.devnull 349 350 351 if not silent in [True, False]: 352 raise ValueError("Invalid input for argument 'silent'") 353 354 if files == 'all' or isinstance(files, list) or isinstance(files, str): 355 ## ALL FILES 356 if files == 'all': 357 files = [] 358 try: 359 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}")), 360 key=lambda x: int(os.path.basename(x).split('.')[0].split('profile')[1])) 361 except ValueError: 362 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}"))) 363 files = [file.split('/')[-1] for file in files] 364 if len(files) == 0: 365 raise ValueError(f"No {data_format} files found in LOGS directory.") 366 367 ## SPECIFIC FILES 368 elif type(files) == list or type(files) == str: 369 if type(files) == str: 370 files = [files] 371 gyre_input_params = [gyre_input_params] 372 if len(files) == 0: 373 raise ValueError("No files provided.") 374 # else: 375 # for file in files: 376 # if not os.path.isfile(os.path.join(LOGS_dir, file)) and not os.path.isfile(file): 377 # raise FileNotFoundError(f"File '{file}' does not exist.") 378 379 with open(f'{wdir}/gyre.log', 'a+') as f: 380 f.write(f"Total {len(files)} profiles to be processed by GYRE.\n\n") 381 if parallel: 382 gyre_input_params = gyre_input_params if gyre_input_params is not None else repeat(None) 383 os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' 384 ## copy gyre.in to gyre1.in, gyre2.in, etc. for parallel runs 385 for i, file in enumerate(files): 386 num = file.split(".")[0] 387 new_gyre_in = os.path.join(wdir, f"gyre{num}.in") 388 shutil.copyfile(gyre_in, new_gyre_in) 389 # commands, wdir, 390 # silent=True, runlog='', 391 # status=None, filename="", 392 # data_format="FGONG", parallel=False, gyre_in=None, 393 # gyre_input_params=None, trace=None, env=None 394 args = (repeat(f'{gyre_ex} gyre.in'), repeat(LOGS_dir), 395 repeat(silent), repeat(runlog), 396 repeat(None), files, repeat(data_format), 397 repeat(True), repeat(gyre_in), 398 gyre_input_params, repeat(None), repeat(os.environ.copy())) 399 if n_cores is None: 400 n_cores = psutil.cpu_count(logical=True) 401 Pool = mp.Pool 402 with progress.Progress(*progress_columns) as progressbar: 403 task = progressbar.add_task("[b i cyan3]Running GYRE...", total=len(files)) 404 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 405 with Pool(n_processes) as pool: 406 gyre_in = os.path.abspath(gyre_in) 407 try: 408 for _ in pool.istarmap(ops_helper.run_subprocess, zip(*args)): 409 progressbar.advance(task) 410 except Exception as e: 411 print(traceback.format_exc()) 412 print(f"Error: {e}") 413 pool.terminate() 414 else: 415 try: 416 from concurrent.futures import ThreadPoolExecutor 417 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 418 with ThreadPoolExecutor(max_workers=n_processes) as executor: 419 gyre_in = os.path.abspath(gyre_in) 420 try: 421 executor.map(ops_helper.run_subprocess, *args) 422 except Exception as e: 423 print(traceback.format_exc()) 424 print(f"Error: {e}") 425 executor.shutdown(wait=False) 426 except Exception as e: 427 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 428 with open(runlog, 'a+') as outfile: 429 for fname in filenames: 430 with open(fname) as infile: 431 for line in infile: 432 outfile.write(line) 433 os.remove(fname) 434 print(traceback.format_exc()) 435 print(f"Error: {e}") 436 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 437 with open(runlog, 'a+') as outfile: 438 for fname in filenames: 439 with open(fname) as infile: 440 for line in infile: 441 outfile.write(line) 442 os.remove(fname) 443 res = True 444 else: 445 for i, file in enumerate(files): 446 gyre_input_params_i = gyre_input_params[i] if gyre_input_params is not None else None 447 res = ops_helper.run_subprocess(f'{gyre_ex} gyre.in', wdir=LOGS_dir, filename=file, 448 silent=silent, runlog=runlog, status=None, gyre_in=gyre_in, 449 data_format=data_format, gyre_input_params=gyre_input_params_i) 450 else: 451 raise ValueError("Invalid input for argument 'files'") 452 if res is False: 453 print("GYRE run failed! Check runlog.") 454 else: 455 print("GYRE run complete!\n") 456 return res
This class handles MESA project operations.
ProjectOps(name='work', astero=False, binary=False)
24 def __init__(self, name='work', astero=False, binary=False): 25 """Constructor for ProjectOps class. 26 27 Args: 28 name (str, optional): Name of the project. Defaults to 'work'. 29 binary (bool, optional): True for a binary star system. Defaults to False. 30 """ 31 self.projName = name 32 self.binary = binary 33 self.astero = astero 34 self.envObject = MesaEnvironmentHandler() 35 if self.binary: 36 self.defaultWork = os.path.join(self.envObject.mesaDir, 'binary/work') 37 elif self.astero: 38 self.defaultWork = os.path.join(self.envObject.mesaDir, 'astero/work') 39 else: 40 self.defaultWork = os.path.join(self.envObject.mesaDir, 'star/work') 41 42 if os.path.exists(self.projName): 43 self.exists = True ## Proj already present flag 44 if os.path.isabs(self.projName): 45 self.work_dir = self.projName 46 else: 47 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 48 else: 49 self.exists = False
Constructor for ProjectOps class.
Arguments:
- name (str, optional): Name of the project. Defaults to 'work'.
- binary (bool, optional): True for a binary star system. Defaults to False.
def
create(self, overwrite=None, clean=None):
53 def create(self, overwrite=None, clean=None): 54 """Creates a new MESA project. 55 56 Args: 57 overwrite (bool, optional): Overwrite the existing project. Defaults to None. 58 clean (bool, optional): Clean the existing project. Defaults to None. 59 """ 60 61 def useExisting(): 62 """A helper function to use the existing project.""" 63 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 64 raise ValueError("Aborting!!! No project specified.") 65 66 def cleanCheck(): 67 """A helper function to check if the user wants to clean the existing project.""" 68 if clean is None: 69 if prompt.Confirm.ask(f"Clean the existing '{self.projName}' project for re-use?", default=False): 70 self.clean() 71 else: 72 useExisting() 73 elif clean is True: 74 self.clean() 75 elif clean is False: 76 print(f"Using the already existing '{self.projName}' project as it is.") 77 else: 78 raise ValueError("Invalid input for argument 'clean'.") 79 80 def writeover(): 81 """A helper function to overwrite the existing project.""" 82 try: 83 shutil.rmtree(self.projName) 84 shutil.copytree(self.defaultWork, self.projName) 85 86 except shutil.Error: 87 raise Exception(f"Could not overwrite the existing '{self.projName}' project!") 88 89 if self.exists is True: 90 if os.path.isabs(self.projName): 91 self.work_dir = self.projName 92 else: 93 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 94 if overwrite is True: 95 writeover() 96 elif overwrite is False: 97 cleanCheck() 98 elif overwrite is None: 99 print(f"Mesa project named '{self.projName}' already exists!") 100 if not prompt.Confirm.ask(f"Use the already existing '{self.projName}' project as it is?", default=False): 101 if prompt.Confirm.ask("Do you wish to overwrite?", default=False): 102 writeover() 103 else: 104 cleanCheck() 105 else: 106 raise ValueError("Invalid input for argument 'overwrite'.") 107 else: 108 try: 109 shutil.copytree(self.defaultWork, self.projName) 110 if os.path.isabs(self.projName): 111 self.work_dir = self.projName 112 else: 113 self.work_dir = os.path.abspath(os.path.join(os.getcwd(), self.projName)) 114 self.exists = True 115 except shutil.Error: 116 raise Exception(f"Could not create the project '{self.projName}'!")
Creates a new MESA project.
Arguments:
- overwrite (bool, optional): Overwrite the existing project. Defaults to None.
- clean (bool, optional): Clean the existing project. Defaults to None.
def
delete(self):
118 def delete(self): 119 """Deletes the project. 120 """ 121 if self.exists is True: 122 shutil.rmtree(self.work_dir) 123 print(f"Deleted project '{self.projName}'.") 124 else: 125 print(f"Project '{self.projName}' does not exist.")
Deletes the project.
def
clean(self):
127 def clean(self): 128 """Cleans the project. 129 130 Raises: 131 Exception: If the clean fails. 132 """ 133 ops_helper.check_exists(self.exists, self.projName) 134 ## clean files are missing a shebang (#!/bin/bash) and hence need to be run with bash 135 res = subprocess.call('/bin/bash ./clean', cwd=self.work_dir, shell=True, stderr=subprocess.STDOUT) 136 runlog = os.path.join(self.work_dir, "runlog") 137 if os.path.exists(runlog): 138 os.remove(runlog) 139 if res!=0: 140 raise Exception(f"Clean failed! Returned non-zero exit code ({res})") 141 else: 142 print("Clean successful.\n")
Cleans the project.
Raises:
- Exception: If the clean fails.
def
make(self, silent=False):
145 def make(self, silent=False): 146 """Makes the project. 147 148 Args: 149 silent (bool, optional): Run the command silently. Defaults to False. 150 151 Raises: 152 Exception: If the make fails. 153 """ 154 ops_helper.check_exists(self.exists, self.projName) 155 if silent: 156 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 157 else: 158 with status.Status("[b i cyan3]Making...", spinner="moon"): 159 res = subprocess.call('./mk', cwd=self.work_dir, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) 160 if res!=0: 161 raise Exception(f"Make failed! Returned non-zero exit code ({res})") 162 else: 163 print("Make successful.\n")
Makes the project.
Arguments:
- silent (bool, optional): Run the command silently. Defaults to False.
Raises:
- Exception: If the make fails.
def
run( self, silent=True, logging=True, parallel=False, trace=None, env={'SELENIUM_JAR_PATH': '/usr/share/java/selenium-server.jar', 'CONDA': '/usr/share/miniconda', 'GITHUB_WORKSPACE': '/home/runner/work/MESA-PORT/MESA-PORT', 'JAVA_HOME_11_X64': '/usr/lib/jvm/temurin-11-jdk-amd64', 'PKG_CONFIG_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib/pkgconfig', 'GITHUB_PATH': '/home/runner/work/_temp/_runner_file_commands/add_path_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_ACTION': '__run_2', 'JAVA_HOME': '/usr/lib/jvm/temurin-11-jdk-amd64', 'GITHUB_RUN_NUMBER': '52', 'RUNNER_NAME': 'GitHub Actions 32', 'GRADLE_HOME': '/usr/share/gradle-8.10.2', 'GITHUB_REPOSITORY_OWNER_ID': '45739450', 'ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE': '/opt/actionarchivecache', 'XDG_CONFIG_HOME': '/home/runner/.config', 'Python_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE': '1', 'ANT_HOME': '/usr/share/ant', 'JAVA_HOME_8_X64': '/usr/lib/jvm/temurin-8-jdk-amd64', 'GITHUB_TRIGGERING_ACTOR': 'gautam-404', 'pythonLocation': '/opt/hostedtoolcache/Python/3.11.10/x64', 'GITHUB_REF_TYPE': 'branch', 'HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS': '3650', 'ANDROID_NDK': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'BOOTSTRAP_HASKELL_NONINTERACTIVE': '1', 'PWD': '/home/runner/work/MESA-PORT/MESA-PORT', 'PIPX_BIN_DIR': '/opt/pipx_bin', 'STATS_TRP': 'true', 'GITHUB_REPOSITORY_ID': '589065195', 'DEPLOYMENT_BASEPATH': '/opt/runner', 'GITHUB_ACTIONS': 'true', 'STATS_VMD': 'true', 'ANDROID_NDK_LATEST_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'SYSTEMD_EXEC_PID': '601', 'GITHUB_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_WORKFLOW_REF': 'gautam-404/MESA-PORT/.github/workflows/docs.yml@refs/heads/dev', 'POWERSHELL_DISTRIBUTION_CHANNEL': 'GitHub-Actions-ubuntu22', 'RUNNER_ENVIRONMENT': 'github-hosted', 'STATS_EXTP': 'https://provjobdsettingscdn.blob.core.windows.net/settings/provjobdsettings-latest/provjobd.data', 'DOTNET_MULTILEVEL_LOOKUP': '0', 'STATS_TIS': 'mining', 'GITHUB_REF': 'refs/heads/dev', 'RUNNER_OS': 'Linux', 'GITHUB_REF_PROTECTED': 'false', 'HOME': '/home/runner', 'GITHUB_API_URL': 'https://api.github.com', 'LANG': 'C.UTF-8', 'RUNNER_TRACKING_ID': 'github_98fdf4a2-5290-4396-880c-3efb836dc53a', 'RUNNER_ARCH': 'X64', 'GOROOT_1_21_X64': '/opt/hostedtoolcache/go/1.21.13/x64', 'RUNNER_TEMP': '/home/runner/work/_temp', 'GITHUB_STATE': '/home/runner/work/_temp/_runner_file_commands/save_state_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'EDGEWEBDRIVER': '/usr/local/share/edge_driver', 'JAVA_HOME_21_X64': '/usr/lib/jvm/temurin-21-jdk-amd64', 'GITHUB_ENV': '/home/runner/work/_temp/_runner_file_commands/set_env_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_EVENT_PATH': '/home/runner/work/_temp/_github_workflow/event.json', 'INVOCATION_ID': 'bb8c957a548a4a8ea780ebb4717f67f6', 'STATS_D': 'false', 'GITHUB_EVENT_NAME': 'push', 'GITHUB_RUN_ID': '11432368347', 'JAVA_HOME_17_X64': '/usr/lib/jvm/temurin-17-jdk-amd64', 'ANDROID_NDK_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_STEP_SUMMARY': '/home/runner/work/_temp/_runner_file_commands/step_summary_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'HOMEBREW_NO_AUTO_UPDATE': '1', 'GITHUB_ACTOR': 'gautam-404', 'NVM_DIR': '/home/runner/.nvm', 'SGX_AESM_ADDR': '1', 'GITHUB_RUN_ATTEMPT': '1', 'STATS_RDCL': 'true', 'ANDROID_HOME': '/usr/local/lib/android/sdk', 'GITHUB_GRAPHQL_URL': 'https://api.github.com/graphql', 'ACCEPT_EULA': 'Y', 'RUNNER_USER': 'runner', 'STATS_UE': 'true', 'USER': 'runner', 'GITHUB_SERVER_URL': 'https://github.com', 'STATS_V3PS': 'true', 'PIPX_HOME': '/opt/pipx', 'GECKOWEBDRIVER': '/usr/local/share/gecko_driver', 'STATS_EXT': 'true', 'CHROMEWEBDRIVER': '/usr/local/share/chromedriver-linux64', 'SHLVL': '1', 'ANDROID_SDK_ROOT': '/usr/local/lib/android/sdk', 'VCPKG_INSTALLATION_ROOT': '/usr/local/share/vcpkg', 'GITHUB_ACTOR_ID': '45739450', 'RUNNER_TOOL_CACHE': '/opt/hostedtoolcache', 'ImageVersion': '20241015.1.0', 'Python3_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_NOLOGO': '1', 'GOROOT_1_23_X64': '/opt/hostedtoolcache/go/1.23.2/x64', 'GITHUB_WORKFLOW_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_REF_NAME': 'dev', 'GITHUB_JOB': 'build', 'LD_LIBRARY_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib', 'XDG_RUNTIME_DIR': '/run/user/1001', 'AZURE_EXTENSION_DIR': '/opt/az/azcliextensions', 'PERFLOG_LOCATION_SETTING': 'RUNNER_PERFLOG', 'STATS_VMFE': 'true', 'GITHUB_REPOSITORY': 'gautam-404/MESA-PORT', 'Python2_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'CHROME_BIN': '/usr/bin/google-chrome', 'GOROOT_1_22_X64': '/opt/hostedtoolcache/go/1.22.8/x64', 'ANDROID_NDK_ROOT': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_RETENTION_DAYS': '90', 'JOURNAL_STREAM': '8:19320', 'RUNNER_WORKSPACE': '/home/runner/work/MESA-PORT', 'LEIN_HOME': '/usr/local/lib/lein', 'LEIN_JAR': '/usr/local/lib/lein/self-installs/leiningen-2.11.2-standalone.jar', 'GITHUB_ACTION_REPOSITORY': '', 'PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/bin:/opt/hostedtoolcache/Python/3.11.10/x64:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin', 'RUNNER_PERFLOG': '/home/runner/perflog', 'GITHUB_BASE_REF': '', 'GHCUP_INSTALL_BASE_PREFIX': '/usr/local', 'CI': 'true', 'SWIFT_PATH': '/usr/share/swift/usr/bin', 'ImageOS': 'ubuntu22', 'STATS_D_D': 'false', 'GITHUB_REPOSITORY_OWNER': 'gautam-404', 'GITHUB_HEAD_REF': '', 'GITHUB_ACTION_REF': '', 'GITHUB_WORKFLOW': 'website', 'DEBIAN_FRONTEND': 'noninteractive', 'GITHUB_OUTPUT': '/home/runner/work/_temp/_runner_file_commands/set_output_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'AGENT_TOOLSDIRECTORY': '/opt/hostedtoolcache', '_': '/opt/hostedtoolcache/Python/3.11.10/x64/bin/pdoc'}):
167 def run(self, silent=True, logging=True, parallel=False, trace=None, env=os.environ.copy()): 168 """ 169 Runs the project. 170 Args: 171 silent (bool, optional): Run the command silently. Defaults to True. 172 logging (bool, optional): Log the run. Defaults to True. 173 parallel (bool, optional): Run in parallel. Defaults to False. 174 trace (list of str, optional): Trace specific history variables. Defaults to None. 175 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 176 177 Raises: 178 Exception: If the project is not made yet. 179 ValueError: If the input for argument 'silent' is invalid. 180 Exception: If the run fails. 181 182 Returns: (If run is successful) 183 termination_code (str): Termination code. 184 age (float): Age of the star in years. 185 """ 186 if trace is not None: 187 ops_helper.setup_trace(trace, self.work_dir) 188 ops_helper.check_exists(self.exists, self.projName) 189 if logging: 190 runlog = os.path.join(self.work_dir, "run.log") 191 else: 192 runlog = os.devnull 193 if not os.path.exists(os.path.join(self.work_dir, "star")) and \ 194 not os.path.exists(os.path.join(self.work_dir, "binary")): 195 raise Exception("Aborting! Run 'make()' first.") 196 else: 197 if silent not in [True, False]: 198 raise ValueError("Invalid input for argument 'silent'") 199 else: 200 if parallel: 201 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 202 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 203 else: 204 with status.Status("[b i cyan3]Running...", spinner="moon") as status_: 205 res = ops_helper.run_subprocess(commands='./rn', wdir=self.work_dir, 206 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 207 if res == False: 208 raise Exception("Run failed! Check runlog.") 209 else: 210 termination_code, age = res 211 if age is not None: 212 print("Run successful.\n") 213 return termination_code, age 214 else: 215 print("Run unsuccessful.\n") 216 return termination_code, None
Runs the project.
Arguments:
- silent (bool, optional): Run the command silently. Defaults to True.
- logging (bool, optional): Log the run. Defaults to True.
- parallel (bool, optional): Run in parallel. Defaults to False.
- trace (list of str, optional): Trace specific history variables. Defaults to None.
- env (dict, optional): Environment variables. Defaults to os.environ.copy().
Raises:
- Exception: If the project is not made yet.
- ValueError: If the input for argument 'silent' is invalid.
- Exception: If the run fails.
Returns: (If run is successful) termination_code (str): Termination code. age (float): Age of the star in years.
def
resume( self, photo=None, silent=True, target=None, logging=True, parallel=False, trace=None, env={'SELENIUM_JAR_PATH': '/usr/share/java/selenium-server.jar', 'CONDA': '/usr/share/miniconda', 'GITHUB_WORKSPACE': '/home/runner/work/MESA-PORT/MESA-PORT', 'JAVA_HOME_11_X64': '/usr/lib/jvm/temurin-11-jdk-amd64', 'PKG_CONFIG_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib/pkgconfig', 'GITHUB_PATH': '/home/runner/work/_temp/_runner_file_commands/add_path_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_ACTION': '__run_2', 'JAVA_HOME': '/usr/lib/jvm/temurin-11-jdk-amd64', 'GITHUB_RUN_NUMBER': '52', 'RUNNER_NAME': 'GitHub Actions 32', 'GRADLE_HOME': '/usr/share/gradle-8.10.2', 'GITHUB_REPOSITORY_OWNER_ID': '45739450', 'ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE': '/opt/actionarchivecache', 'XDG_CONFIG_HOME': '/home/runner/.config', 'Python_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE': '1', 'ANT_HOME': '/usr/share/ant', 'JAVA_HOME_8_X64': '/usr/lib/jvm/temurin-8-jdk-amd64', 'GITHUB_TRIGGERING_ACTOR': 'gautam-404', 'pythonLocation': '/opt/hostedtoolcache/Python/3.11.10/x64', 'GITHUB_REF_TYPE': 'branch', 'HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS': '3650', 'ANDROID_NDK': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'BOOTSTRAP_HASKELL_NONINTERACTIVE': '1', 'PWD': '/home/runner/work/MESA-PORT/MESA-PORT', 'PIPX_BIN_DIR': '/opt/pipx_bin', 'STATS_TRP': 'true', 'GITHUB_REPOSITORY_ID': '589065195', 'DEPLOYMENT_BASEPATH': '/opt/runner', 'GITHUB_ACTIONS': 'true', 'STATS_VMD': 'true', 'ANDROID_NDK_LATEST_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'SYSTEMD_EXEC_PID': '601', 'GITHUB_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_WORKFLOW_REF': 'gautam-404/MESA-PORT/.github/workflows/docs.yml@refs/heads/dev', 'POWERSHELL_DISTRIBUTION_CHANNEL': 'GitHub-Actions-ubuntu22', 'RUNNER_ENVIRONMENT': 'github-hosted', 'STATS_EXTP': 'https://provjobdsettingscdn.blob.core.windows.net/settings/provjobdsettings-latest/provjobd.data', 'DOTNET_MULTILEVEL_LOOKUP': '0', 'STATS_TIS': 'mining', 'GITHUB_REF': 'refs/heads/dev', 'RUNNER_OS': 'Linux', 'GITHUB_REF_PROTECTED': 'false', 'HOME': '/home/runner', 'GITHUB_API_URL': 'https://api.github.com', 'LANG': 'C.UTF-8', 'RUNNER_TRACKING_ID': 'github_98fdf4a2-5290-4396-880c-3efb836dc53a', 'RUNNER_ARCH': 'X64', 'GOROOT_1_21_X64': '/opt/hostedtoolcache/go/1.21.13/x64', 'RUNNER_TEMP': '/home/runner/work/_temp', 'GITHUB_STATE': '/home/runner/work/_temp/_runner_file_commands/save_state_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'EDGEWEBDRIVER': '/usr/local/share/edge_driver', 'JAVA_HOME_21_X64': '/usr/lib/jvm/temurin-21-jdk-amd64', 'GITHUB_ENV': '/home/runner/work/_temp/_runner_file_commands/set_env_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_EVENT_PATH': '/home/runner/work/_temp/_github_workflow/event.json', 'INVOCATION_ID': 'bb8c957a548a4a8ea780ebb4717f67f6', 'STATS_D': 'false', 'GITHUB_EVENT_NAME': 'push', 'GITHUB_RUN_ID': '11432368347', 'JAVA_HOME_17_X64': '/usr/lib/jvm/temurin-17-jdk-amd64', 'ANDROID_NDK_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_STEP_SUMMARY': '/home/runner/work/_temp/_runner_file_commands/step_summary_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'HOMEBREW_NO_AUTO_UPDATE': '1', 'GITHUB_ACTOR': 'gautam-404', 'NVM_DIR': '/home/runner/.nvm', 'SGX_AESM_ADDR': '1', 'GITHUB_RUN_ATTEMPT': '1', 'STATS_RDCL': 'true', 'ANDROID_HOME': '/usr/local/lib/android/sdk', 'GITHUB_GRAPHQL_URL': 'https://api.github.com/graphql', 'ACCEPT_EULA': 'Y', 'RUNNER_USER': 'runner', 'STATS_UE': 'true', 'USER': 'runner', 'GITHUB_SERVER_URL': 'https://github.com', 'STATS_V3PS': 'true', 'PIPX_HOME': '/opt/pipx', 'GECKOWEBDRIVER': '/usr/local/share/gecko_driver', 'STATS_EXT': 'true', 'CHROMEWEBDRIVER': '/usr/local/share/chromedriver-linux64', 'SHLVL': '1', 'ANDROID_SDK_ROOT': '/usr/local/lib/android/sdk', 'VCPKG_INSTALLATION_ROOT': '/usr/local/share/vcpkg', 'GITHUB_ACTOR_ID': '45739450', 'RUNNER_TOOL_CACHE': '/opt/hostedtoolcache', 'ImageVersion': '20241015.1.0', 'Python3_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_NOLOGO': '1', 'GOROOT_1_23_X64': '/opt/hostedtoolcache/go/1.23.2/x64', 'GITHUB_WORKFLOW_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_REF_NAME': 'dev', 'GITHUB_JOB': 'build', 'LD_LIBRARY_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib', 'XDG_RUNTIME_DIR': '/run/user/1001', 'AZURE_EXTENSION_DIR': '/opt/az/azcliextensions', 'PERFLOG_LOCATION_SETTING': 'RUNNER_PERFLOG', 'STATS_VMFE': 'true', 'GITHUB_REPOSITORY': 'gautam-404/MESA-PORT', 'Python2_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'CHROME_BIN': '/usr/bin/google-chrome', 'GOROOT_1_22_X64': '/opt/hostedtoolcache/go/1.22.8/x64', 'ANDROID_NDK_ROOT': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_RETENTION_DAYS': '90', 'JOURNAL_STREAM': '8:19320', 'RUNNER_WORKSPACE': '/home/runner/work/MESA-PORT', 'LEIN_HOME': '/usr/local/lib/lein', 'LEIN_JAR': '/usr/local/lib/lein/self-installs/leiningen-2.11.2-standalone.jar', 'GITHUB_ACTION_REPOSITORY': '', 'PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/bin:/opt/hostedtoolcache/Python/3.11.10/x64:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin', 'RUNNER_PERFLOG': '/home/runner/perflog', 'GITHUB_BASE_REF': '', 'GHCUP_INSTALL_BASE_PREFIX': '/usr/local', 'CI': 'true', 'SWIFT_PATH': '/usr/share/swift/usr/bin', 'ImageOS': 'ubuntu22', 'STATS_D_D': 'false', 'GITHUB_REPOSITORY_OWNER': 'gautam-404', 'GITHUB_HEAD_REF': '', 'GITHUB_ACTION_REF': '', 'GITHUB_WORKFLOW': 'website', 'DEBIAN_FRONTEND': 'noninteractive', 'GITHUB_OUTPUT': '/home/runner/work/_temp/_runner_file_commands/set_output_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'AGENT_TOOLSDIRECTORY': '/opt/hostedtoolcache', '_': '/opt/hostedtoolcache/Python/3.11.10/x64/bin/pdoc'}):
220 def resume(self, photo=None, silent=True, target=None, logging=True, parallel=False, trace=None, env=os.environ.copy()): 221 """Resumes the run from a given photo. 222 223 Args: 224 photo (str, optional): Photo name from which the run is to be resumed. 225 If None, the last photo is used. Defaults to None. 226 silent (bool, optional): Run the command silently. Defaults to True. 227 target (str, optional): Target photo name. Defaults to None. 228 logging (bool, optional): Log the run. Defaults to True. 229 parallel (bool, optional): Run in parallel. Defaults to False. 230 trace (list of str, optional): Trace specific history variables. Defaults to None. 231 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 232 233 Raises: 234 FileNotFoundError: If the photo does not exist. 235 ValueError: If the input for argument 'silent' is invalid. 236 237 Returns: (If run is successful) 238 termination_code (str): Termination code. 239 age (float): Age of the star in years. 240 """ 241 if trace is not None: 242 ops_helper.setup_trace(trace, self.work_dir) 243 ops_helper.check_exists(self.exists, self.projName) 244 if logging: 245 runlog = os.path.join(self.work_dir, "run.log") 246 else: 247 runlog = os.devnull 248 if photo == None: 249 if parallel: 250 res = ops_helper.run_subprocess(commands='./re', wdir=self.work_dir, 251 silent=silent, runlog=runlog, parallel=True, trace=trace) 252 else: 253 # print(f"[b i cyan3]Resuming run from the most recent photo.") 254 with status.Status("[b i cyan3]Resuming run from the most recent photo.\nRunning...", spinner="moon") as status_: 255 res = ops_helper.run_subprocess(commands=f'./re', wdir=self.work_dir, 256 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 257 else: 258 if self.binary: 259 if target == 'primary': 260 photo_path = os.path.join(self.work_dir, "photos1", photo) 261 elif target == 'secondary': 262 photo_path = os.path.join(self.work_dir, "photos2", photo) 263 else: 264 raise ValueError('''Invalid input for argument 'target'. 265 Please use 'primary' or 'secondary' ''') 266 else: 267 photo_path = os.path.join(self.work_dir, "photos", photo) 268 269 if not os.path.isfile(photo_path): 270 raise FileNotFoundError(f"Photo '{photo}' could not be exists.") 271 else: 272 if silent not in [True, False]: 273 raise ValueError("Invalid input for argument 'silent'.") 274 else: 275 if parallel: 276 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 277 silent=silent, runlog=runlog, parallel=True, trace=trace, env=env) 278 else: 279 with status.Status(f"[b i cyan3]Resuming run from photo {photo}.\nRunning...", spinner="moon") as status_: 280 res = ops_helper.run_subprocess(commands=f'./re {photo}', wdir=self.work_dir, 281 silent=silent, runlog=runlog, status=status_, trace=trace, env=env) 282 if res is False: 283 raise Exception("Resume from photo failed! Check runlog.") 284 else: 285 termination_code, age = res 286 if age is not None: 287 print("Run successful.\n") 288 return termination_code, age 289 else: 290 print("Run unsuccessful.\n") 291 return termination_code, None
Resumes the run from a given photo.
Arguments:
- photo (str, optional): Photo name from which the run is to be resumed. If None, the last photo is used. Defaults to None.
- silent (bool, optional): Run the command silently. Defaults to True.
- target (str, optional): Target photo name. Defaults to None.
- logging (bool, optional): Log the run. Defaults to True.
- parallel (bool, optional): Run in parallel. Defaults to False.
- trace (list of str, optional): Trace specific history variables. Defaults to None.
- env (dict, optional): Environment variables. Defaults to os.environ.copy().
Raises:
- FileNotFoundError: If the photo does not exist.
- ValueError: If the input for argument 'silent' is invalid.
Returns: (If run is successful) termination_code (str): Termination code. age (float): Age of the star in years.
def
runGyre( self, gyre_in, files='all', wdir=None, data_format='GYRE', silent=True, target=None, logging=True, logfile='gyre.log', parallel=False, n_cores=None, gyre_input_params=None, env={'SELENIUM_JAR_PATH': '/usr/share/java/selenium-server.jar', 'CONDA': '/usr/share/miniconda', 'GITHUB_WORKSPACE': '/home/runner/work/MESA-PORT/MESA-PORT', 'JAVA_HOME_11_X64': '/usr/lib/jvm/temurin-11-jdk-amd64', 'PKG_CONFIG_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib/pkgconfig', 'GITHUB_PATH': '/home/runner/work/_temp/_runner_file_commands/add_path_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_ACTION': '__run_2', 'JAVA_HOME': '/usr/lib/jvm/temurin-11-jdk-amd64', 'GITHUB_RUN_NUMBER': '52', 'RUNNER_NAME': 'GitHub Actions 32', 'GRADLE_HOME': '/usr/share/gradle-8.10.2', 'GITHUB_REPOSITORY_OWNER_ID': '45739450', 'ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE': '/opt/actionarchivecache', 'XDG_CONFIG_HOME': '/home/runner/.config', 'Python_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE': '1', 'ANT_HOME': '/usr/share/ant', 'JAVA_HOME_8_X64': '/usr/lib/jvm/temurin-8-jdk-amd64', 'GITHUB_TRIGGERING_ACTOR': 'gautam-404', 'pythonLocation': '/opt/hostedtoolcache/Python/3.11.10/x64', 'GITHUB_REF_TYPE': 'branch', 'HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS': '3650', 'ANDROID_NDK': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'BOOTSTRAP_HASKELL_NONINTERACTIVE': '1', 'PWD': '/home/runner/work/MESA-PORT/MESA-PORT', 'PIPX_BIN_DIR': '/opt/pipx_bin', 'STATS_TRP': 'true', 'GITHUB_REPOSITORY_ID': '589065195', 'DEPLOYMENT_BASEPATH': '/opt/runner', 'GITHUB_ACTIONS': 'true', 'STATS_VMD': 'true', 'ANDROID_NDK_LATEST_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'SYSTEMD_EXEC_PID': '601', 'GITHUB_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_WORKFLOW_REF': 'gautam-404/MESA-PORT/.github/workflows/docs.yml@refs/heads/dev', 'POWERSHELL_DISTRIBUTION_CHANNEL': 'GitHub-Actions-ubuntu22', 'RUNNER_ENVIRONMENT': 'github-hosted', 'STATS_EXTP': 'https://provjobdsettingscdn.blob.core.windows.net/settings/provjobdsettings-latest/provjobd.data', 'DOTNET_MULTILEVEL_LOOKUP': '0', 'STATS_TIS': 'mining', 'GITHUB_REF': 'refs/heads/dev', 'RUNNER_OS': 'Linux', 'GITHUB_REF_PROTECTED': 'false', 'HOME': '/home/runner', 'GITHUB_API_URL': 'https://api.github.com', 'LANG': 'C.UTF-8', 'RUNNER_TRACKING_ID': 'github_98fdf4a2-5290-4396-880c-3efb836dc53a', 'RUNNER_ARCH': 'X64', 'GOROOT_1_21_X64': '/opt/hostedtoolcache/go/1.21.13/x64', 'RUNNER_TEMP': '/home/runner/work/_temp', 'GITHUB_STATE': '/home/runner/work/_temp/_runner_file_commands/save_state_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'EDGEWEBDRIVER': '/usr/local/share/edge_driver', 'JAVA_HOME_21_X64': '/usr/lib/jvm/temurin-21-jdk-amd64', 'GITHUB_ENV': '/home/runner/work/_temp/_runner_file_commands/set_env_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'GITHUB_EVENT_PATH': '/home/runner/work/_temp/_github_workflow/event.json', 'INVOCATION_ID': 'bb8c957a548a4a8ea780ebb4717f67f6', 'STATS_D': 'false', 'GITHUB_EVENT_NAME': 'push', 'GITHUB_RUN_ID': '11432368347', 'JAVA_HOME_17_X64': '/usr/lib/jvm/temurin-17-jdk-amd64', 'ANDROID_NDK_HOME': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_STEP_SUMMARY': '/home/runner/work/_temp/_runner_file_commands/step_summary_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'HOMEBREW_NO_AUTO_UPDATE': '1', 'GITHUB_ACTOR': 'gautam-404', 'NVM_DIR': '/home/runner/.nvm', 'SGX_AESM_ADDR': '1', 'GITHUB_RUN_ATTEMPT': '1', 'STATS_RDCL': 'true', 'ANDROID_HOME': '/usr/local/lib/android/sdk', 'GITHUB_GRAPHQL_URL': 'https://api.github.com/graphql', 'ACCEPT_EULA': 'Y', 'RUNNER_USER': 'runner', 'STATS_UE': 'true', 'USER': 'runner', 'GITHUB_SERVER_URL': 'https://github.com', 'STATS_V3PS': 'true', 'PIPX_HOME': '/opt/pipx', 'GECKOWEBDRIVER': '/usr/local/share/gecko_driver', 'STATS_EXT': 'true', 'CHROMEWEBDRIVER': '/usr/local/share/chromedriver-linux64', 'SHLVL': '1', 'ANDROID_SDK_ROOT': '/usr/local/lib/android/sdk', 'VCPKG_INSTALLATION_ROOT': '/usr/local/share/vcpkg', 'GITHUB_ACTOR_ID': '45739450', 'RUNNER_TOOL_CACHE': '/opt/hostedtoolcache', 'ImageVersion': '20241015.1.0', 'Python3_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'DOTNET_NOLOGO': '1', 'GOROOT_1_23_X64': '/opt/hostedtoolcache/go/1.23.2/x64', 'GITHUB_WORKFLOW_SHA': '1ff77688171833851f128b1251813b0b252251ff', 'GITHUB_REF_NAME': 'dev', 'GITHUB_JOB': 'build', 'LD_LIBRARY_PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/lib', 'XDG_RUNTIME_DIR': '/run/user/1001', 'AZURE_EXTENSION_DIR': '/opt/az/azcliextensions', 'PERFLOG_LOCATION_SETTING': 'RUNNER_PERFLOG', 'STATS_VMFE': 'true', 'GITHUB_REPOSITORY': 'gautam-404/MESA-PORT', 'Python2_ROOT_DIR': '/opt/hostedtoolcache/Python/3.11.10/x64', 'CHROME_BIN': '/usr/bin/google-chrome', 'GOROOT_1_22_X64': '/opt/hostedtoolcache/go/1.22.8/x64', 'ANDROID_NDK_ROOT': '/usr/local/lib/android/sdk/ndk/27.1.12297006', 'GITHUB_RETENTION_DAYS': '90', 'JOURNAL_STREAM': '8:19320', 'RUNNER_WORKSPACE': '/home/runner/work/MESA-PORT', 'LEIN_HOME': '/usr/local/lib/lein', 'LEIN_JAR': '/usr/local/lib/lein/self-installs/leiningen-2.11.2-standalone.jar', 'GITHUB_ACTION_REPOSITORY': '', 'PATH': '/opt/hostedtoolcache/Python/3.11.10/x64/bin:/opt/hostedtoolcache/Python/3.11.10/x64:/snap/bin:/home/runner/.local/bin:/opt/pipx_bin:/home/runner/.cargo/bin:/home/runner/.config/composer/vendor/bin:/usr/local/.ghcup/bin:/home/runner/.dotnet/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin', 'RUNNER_PERFLOG': '/home/runner/perflog', 'GITHUB_BASE_REF': '', 'GHCUP_INSTALL_BASE_PREFIX': '/usr/local', 'CI': 'true', 'SWIFT_PATH': '/usr/share/swift/usr/bin', 'ImageOS': 'ubuntu22', 'STATS_D_D': 'false', 'GITHUB_REPOSITORY_OWNER': 'gautam-404', 'GITHUB_HEAD_REF': '', 'GITHUB_ACTION_REF': '', 'GITHUB_WORKFLOW': 'website', 'DEBIAN_FRONTEND': 'noninteractive', 'GITHUB_OUTPUT': '/home/runner/work/_temp/_runner_file_commands/set_output_f559b2da-a6f8-428e-af25-68f762b4aa2c', 'AGENT_TOOLSDIRECTORY': '/opt/hostedtoolcache', '_': '/opt/hostedtoolcache/Python/3.11.10/x64/bin/pdoc'}):
294 def runGyre(self, gyre_in, files='all', wdir=None, data_format="GYRE", silent=True, target=None, logging=True, logfile="gyre.log", 295 parallel=False, n_cores=None, gyre_input_params=None, env=os.environ.copy()): 296 """ 297 Runs GYRE. 298 299 Arguments: 300 gyre_in (str): GYRE input file. 301 files (str or list of strings, optional): Profile files in the LOGS directory 302 to be processed by GYRE. Defaults to 'all'. 303 wdir (str, optional): Working directory. Defaults to None and uses the project directory. 304 silent (bool, optional): Run the command silently. Defaults to True. 305 target (str, optional): Target star. Defaults to None. 306 logging (bool, optional): Log the output. Defaults to True. 307 logdir (str, optional): Log file name. Defaults to "run.log". 308 parallel (bool, optional): Run GYRE in parallel. Defaults to False. 309 n_cores (int, optional): Number of cores to use. Defaults to None. 310 gyre_input_params (dict or list of dicts, optional): Dictionary of GYRE input parameters. 311 {parameter: value}. Parameter must be a string. Value 312 can be a string, int, float, or bool. 313 List of dictionaries for multiple profiles. 314 list of dicts must be in the same order as the 315 list of profile files. len(list of dicts) must be 316 equal to len(list of profile files). Defaults to None. 317 env (dict, optional): Environment variables. Defaults to os.environ.copy(). 318 Raises: 319 FileNotFoundError: If the GYRE input file does not exist. 320 ValueError: If the input for argument 'silent' is invalid. 321 ValueError: If the input for argument 'files' is invalid. 322 """ 323 if wdir is not None: 324 wdir = os.path.abspath(wdir) 325 gyre_in = os.path.abspath(gyre_in) 326 327 if 'GYRE_DIR' in os.environ: 328 gyre_ex = os.path.join(os.environ['GYRE_DIR'], "bin", "gyre") 329 else: 330 raise FileNotFoundError("GYRE_DIR is not set in your enviroment. Be sure to set it properly!!") 331 if self.binary: 332 if target == 'primary': 333 LOGS_dir = os.path.join(self.work_dir, "LOGS1") if wdir is None else wdir 334 elif target == 'secondary': 335 LOGS_dir = os.path.join(self.work_dir, "LOGS2") if wdir is None else wdir 336 else: 337 raise ValueError("""Invalid input for argument 'star'. 338 Please use primary or secondary""") 339 else: 340 LOGS_dir = os.path.join(self.work_dir, "LOGS") if wdir is None else wdir 341 342 if wdir is None: 343 wdir = self.work_dir 344 345 if logging: 346 runlog = os.path.join(wdir, logfile) 347 else: 348 runlog = os.devnull 349 350 351 if not silent in [True, False]: 352 raise ValueError("Invalid input for argument 'silent'") 353 354 if files == 'all' or isinstance(files, list) or isinstance(files, str): 355 ## ALL FILES 356 if files == 'all': 357 files = [] 358 try: 359 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}")), 360 key=lambda x: int(os.path.basename(x).split('.')[0].split('profile')[1])) 361 except ValueError: 362 files = sorted(glob.glob(os.path.join(LOGS_dir, f"*.{data_format}"))) 363 files = [file.split('/')[-1] for file in files] 364 if len(files) == 0: 365 raise ValueError(f"No {data_format} files found in LOGS directory.") 366 367 ## SPECIFIC FILES 368 elif type(files) == list or type(files) == str: 369 if type(files) == str: 370 files = [files] 371 gyre_input_params = [gyre_input_params] 372 if len(files) == 0: 373 raise ValueError("No files provided.") 374 # else: 375 # for file in files: 376 # if not os.path.isfile(os.path.join(LOGS_dir, file)) and not os.path.isfile(file): 377 # raise FileNotFoundError(f"File '{file}' does not exist.") 378 379 with open(f'{wdir}/gyre.log', 'a+') as f: 380 f.write(f"Total {len(files)} profiles to be processed by GYRE.\n\n") 381 if parallel: 382 gyre_input_params = gyre_input_params if gyre_input_params is not None else repeat(None) 383 os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' 384 ## copy gyre.in to gyre1.in, gyre2.in, etc. for parallel runs 385 for i, file in enumerate(files): 386 num = file.split(".")[0] 387 new_gyre_in = os.path.join(wdir, f"gyre{num}.in") 388 shutil.copyfile(gyre_in, new_gyre_in) 389 # commands, wdir, 390 # silent=True, runlog='', 391 # status=None, filename="", 392 # data_format="FGONG", parallel=False, gyre_in=None, 393 # gyre_input_params=None, trace=None, env=None 394 args = (repeat(f'{gyre_ex} gyre.in'), repeat(LOGS_dir), 395 repeat(silent), repeat(runlog), 396 repeat(None), files, repeat(data_format), 397 repeat(True), repeat(gyre_in), 398 gyre_input_params, repeat(None), repeat(os.environ.copy())) 399 if n_cores is None: 400 n_cores = psutil.cpu_count(logical=True) 401 Pool = mp.Pool 402 with progress.Progress(*progress_columns) as progressbar: 403 task = progressbar.add_task("[b i cyan3]Running GYRE...", total=len(files)) 404 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 405 with Pool(n_processes) as pool: 406 gyre_in = os.path.abspath(gyre_in) 407 try: 408 for _ in pool.istarmap(ops_helper.run_subprocess, zip(*args)): 409 progressbar.advance(task) 410 except Exception as e: 411 print(traceback.format_exc()) 412 print(f"Error: {e}") 413 pool.terminate() 414 else: 415 try: 416 from concurrent.futures import ThreadPoolExecutor 417 n_processes = (n_cores//int(os.environ['OMP_NUM_THREADS'])) 418 with ThreadPoolExecutor(max_workers=n_processes) as executor: 419 gyre_in = os.path.abspath(gyre_in) 420 try: 421 executor.map(ops_helper.run_subprocess, *args) 422 except Exception as e: 423 print(traceback.format_exc()) 424 print(f"Error: {e}") 425 executor.shutdown(wait=False) 426 except Exception as e: 427 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 428 with open(runlog, 'a+') as outfile: 429 for fname in filenames: 430 with open(fname) as infile: 431 for line in infile: 432 outfile.write(line) 433 os.remove(fname) 434 print(traceback.format_exc()) 435 print(f"Error: {e}") 436 filenames = glob.glob(os.path.join(LOGS_dir, f"gyreprofile*.log")) 437 with open(runlog, 'a+') as outfile: 438 for fname in filenames: 439 with open(fname) as infile: 440 for line in infile: 441 outfile.write(line) 442 os.remove(fname) 443 res = True 444 else: 445 for i, file in enumerate(files): 446 gyre_input_params_i = gyre_input_params[i] if gyre_input_params is not None else None 447 res = ops_helper.run_subprocess(f'{gyre_ex} gyre.in', wdir=LOGS_dir, filename=file, 448 silent=silent, runlog=runlog, status=None, gyre_in=gyre_in, 449 data_format=data_format, gyre_input_params=gyre_input_params_i) 450 else: 451 raise ValueError("Invalid input for argument 'files'") 452 if res is False: 453 print("GYRE run failed! Check runlog.") 454 else: 455 print("GYRE run complete!\n") 456 return res
Runs GYRE.
Arguments:
- gyre_in (str): GYRE input file.
- files (str or list of strings, optional): Profile files in the LOGS directory to be processed by GYRE. Defaults to 'all'.
- wdir (str, optional): Working directory. Defaults to None and uses the project directory.
- silent (bool, optional): Run the command silently. Defaults to True.
- target (str, optional): Target star. Defaults to None.
- logging (bool, optional): Log the output. Defaults to True.
- logdir (str, optional): Log file name. Defaults to "run.log".
- parallel (bool, optional): Run GYRE in parallel. Defaults to False.
- n_cores (int, optional): Number of cores to use. Defaults to None.
- gyre_input_params (dict or list of dicts, optional): Dictionary of GYRE input parameters. {parameter: value}. Parameter must be a string. Value can be a string, int, float, or bool. List of dictionaries for multiple profiles. list of dicts must be in the same order as the list of profile files. len(list of dicts) must be equal to len(list of profile files). Defaults to None.
- env (dict, optional): Environment variables. Defaults to os.environ.copy().
Raises:
- FileNotFoundError: If the GYRE input file does not exist.
- ValueError: If the input for argument 'silent' is invalid.
- ValueError: If the input for argument 'files' is invalid.