|
1 | | -"""authors: Mukesh Kumar Ramancha, Maitreya Manoj Kurumbhati, Prof. J.P. Conte, Aakash Bangalore Satish* |
2 | | -affiliation: University of California, San Diego, *SimCenter, University of California, Berkeley |
3 | | -
|
4 | | -""" # noqa: INP001, D205, D400 |
5 | | - |
6 | | -# ====================================================================================================================== |
| 1 | +""" |
| 2 | +module: mainscript.py. |
| 3 | +
|
| 4 | +This script executes an uncertainty quantification (UQ) analysis based on an input JSON file. |
| 5 | +It dynamically selects the appropriate UQ method in the UCSD_UQ engine and executes the corresponding script. |
| 6 | +
|
| 7 | +### Functionality: |
| 8 | +- Adds the `common` directory to `sys.path` to allow importing necessary modules. |
| 9 | +- Parses command-line arguments for necessary paths and parameters. |
| 10 | +- Reads and validates the JSON input file. |
| 11 | +- Determines the appropriate UQ script to run based on `uqType` and `useApproximation` flags. |
| 12 | +- Constructs the command and executes the selected script. |
| 13 | +- Captures errors and logs them to `UCSD_UQ.err`. |
| 14 | +
|
| 15 | +### Usage: |
| 16 | +```sh |
| 17 | +python script.py /path/to/working /path/to/template runningLocal driver input.json |
| 18 | +``` |
| 19 | +
|
| 20 | +### Arguments: |
| 21 | +1. `working_dir` (Path): Path to the working directory where computations are performed. |
| 22 | +2. `template_dir` (Path): Path to the template directory containing the input JSON file. |
| 23 | +3. `run_type` (str): Execution mode (`runningLocal` or `runningRemote`). |
| 24 | +4. `driver_file` (str): Name of the driver file. |
| 25 | +5. `input_file` (Path): Name of the JSON input file. |
| 26 | +
|
| 27 | +### Error Handling: |
| 28 | +- If an invalid JSON file is provided, the script exits with an error. |
| 29 | +- If an unknown UQ type is encountered, the script exits with an error. |
| 30 | +- If an exception occurs during execution, the error is logged in `UCSD_UQ.err`. |
| 31 | +
|
| 32 | +""" |
| 33 | + |
| 34 | +import argparse |
7 | 35 | import json |
8 | | -import shlex |
9 | 36 | import sys |
10 | 37 | import traceback |
11 | 38 | from pathlib import Path |
12 | 39 |
|
13 | | -path_to_common_uq = Path(__file__).parent.parent / 'common' |
14 | | -sys.path.append(str(path_to_common_uq)) |
| 40 | +# Dynamically add 'common' directory to sys.path |
| 41 | +path_to_common_uq = Path(__file__).resolve().parent.parent / 'common' |
| 42 | +if path_to_common_uq not in sys.path: |
| 43 | + sys.path.insert(0, str(path_to_common_uq)) |
15 | 44 |
|
16 | 45 |
|
17 | | -# ====================================================================================================================== |
18 | | -def main(input_args): # noqa: D103 |
19 | | - # # Initialize analysis |
20 | | - # path_to_UCSD_UQ_directory = Path(input_args[2]).resolve().parent |
21 | | - # path_to_working_directory = Path(input_args[3]).resolve() |
22 | | - # path_to_template_directory = Path(input_args[4]).resolve() |
23 | | - # run_type = input_args[5] # either "runningLocal" or "runningRemote" |
24 | | - # driver_file_name = input_args[6] |
25 | | - # input_file_name = input_args[7] |
| 46 | +def parse_args(): |
| 47 | + """Parse command-line arguments.""" |
| 48 | + parser = argparse.ArgumentParser(description='Run UQ analysis.') |
| 49 | + parser.add_argument('working_dir', type=Path, help='Path to working directory') |
| 50 | + parser.add_argument('template_dir', type=Path, help='Path to template directory') |
| 51 | + parser.add_argument( |
| 52 | + 'run_type', choices=['runningLocal', 'runningRemote'], help='Run type' |
| 53 | + ) |
| 54 | + parser.add_argument('driver_file', help='Driver file name') |
| 55 | + parser.add_argument('input_file', type=Path, help='Input JSON file') |
| 56 | + return parser.parse_args() |
| 57 | + |
26 | 58 |
|
27 | | - # Initialize analysis |
28 | | - path_to_UCSD_UQ_directory = Path(input_args[0]).resolve().parent # noqa: N806, F841 |
29 | | - path_to_working_directory = Path(input_args[1]).resolve() |
30 | | - path_to_template_directory = Path(input_args[2]).resolve() |
31 | | - run_type = input_args[3] # either "runningLocal" or "runningRemote" |
32 | | - driver_file_name = input_args[4] |
33 | | - input_file_name = input_args[5] |
| 59 | +def load_json(file_path): |
| 60 | + """Load and validate JSON input.""" |
| 61 | + try: |
| 62 | + with file_path.open(encoding='utf-8') as f: |
| 63 | + data = json.load(f) |
| 64 | + except json.JSONDecodeError as e: |
| 65 | + sys.stderr.write(f'ERROR: Invalid JSON format in {file_path}:\n{e}\n') |
| 66 | + sys.exit(1) |
34 | 67 |
|
35 | | - Path('dakotaTab.out').unlink(missing_ok=True) |
36 | | - Path('dakotaTabPrior.out').unlink(missing_ok=True) |
| 68 | + if 'UQ' not in data: |
| 69 | + sys.stderr.write(f"ERROR: Missing 'UQ' key in input JSON: {file_path}\n") |
| 70 | + sys.exit(1) |
37 | 71 |
|
38 | | - input_file_full_path = path_to_template_directory / input_file_name |
| 72 | + return data |
39 | 73 |
|
40 | | - with open(input_file_full_path, encoding='utf-8') as f: # noqa: PTH123 |
41 | | - inputs = json.load(f) |
42 | 74 |
|
43 | | - uq_inputs = inputs['UQ'] |
44 | | - if uq_inputs['uqType'] == 'Metropolis Within Gibbs Sampler': |
45 | | - import mainscript_hierarchical_bayesian |
| 75 | +def select_uq_script(uq_inputs): |
| 76 | + """Determine which UQ script to run.""" |
| 77 | + uq_methods = { |
| 78 | + 'Metropolis Within Gibbs Sampler': 'mainscript_hierarchical_bayesian', |
| 79 | + } |
46 | 80 |
|
47 | | - main_function = mainscript_hierarchical_bayesian.main |
48 | | - else: |
49 | | - import mainscript_tmcmc |
| 81 | + module_name = uq_methods.get(uq_inputs['uqType'], 'mainscript_tmcmc') |
| 82 | + if uq_inputs.get('useApproximation'): |
| 83 | + module_name = 'gp_ab_algorithm' |
50 | 84 |
|
51 | | - main_function = mainscript_tmcmc.main |
| 85 | + try: |
| 86 | + module = __import__(module_name) |
| 87 | + except ImportError: |
| 88 | + sys.stderr.write( |
| 89 | + f'ERROR: Could not import {module_name}. Ensure the script exists.\n' |
| 90 | + ) |
| 91 | + sys.exit(1) |
52 | 92 |
|
53 | | - command = ( |
54 | | - f'"{path_to_working_directory}" "{path_to_template_directory}" ' |
55 | | - f'{run_type} "{driver_file_name}" "{input_file_full_path}"' |
56 | | - ) |
57 | | - command_list = shlex.split(command) |
| 93 | + return module.main |
58 | 94 |
|
59 | | - # Check if 'UCSD_UQ.err' exists, and create it if not |
60 | | - err_file = path_to_working_directory / 'UCSD_UQ.err' |
61 | 95 |
|
62 | | - if not err_file.exists(): |
63 | | - err_file.touch() # Create the file |
| 96 | +def main(): |
| 97 | + """Run UQ analysis.""" |
| 98 | + args = parse_args() |
| 99 | + |
| 100 | + # Ensure output error file exists |
| 101 | + err_file = args.working_dir / 'UCSD_UQ.err' |
| 102 | + err_file.touch(exist_ok=True) |
| 103 | + |
| 104 | + # Remove any previous output files |
| 105 | + (args.working_dir / 'dakotaTab.out').unlink(missing_ok=True) |
| 106 | + (args.working_dir / 'dakotaTabPrior.out').unlink(missing_ok=True) |
| 107 | + |
| 108 | + # Load JSON input file |
| 109 | + inputs = load_json(args.template_dir / args.input_file) |
| 110 | + uq_inputs = inputs['UQ'] |
| 111 | + |
| 112 | + # Select the appropriate UQ script |
| 113 | + main_function = select_uq_script(uq_inputs) |
| 114 | + |
| 115 | + # Construct the command |
| 116 | + command = [ |
| 117 | + str(args.working_dir), |
| 118 | + str(args.template_dir), |
| 119 | + args.run_type, |
| 120 | + args.driver_file, |
| 121 | + str(args.template_dir / args.input_file), |
| 122 | + ] |
64 | 123 |
|
65 | | - # Try running the main_function and catch any exceptions |
66 | 124 | try: |
67 | | - main_function(command_list) |
68 | | - except Exception: # noqa: BLE001 |
69 | | - # Write the exception message to the .err file |
| 125 | + main_function(command) |
| 126 | + except Exception: |
| 127 | + err_msg = f'ERROR: An exception occurred:\n{traceback.format_exc()}\n' |
70 | 128 | with err_file.open('a') as f: |
71 | | - f.write('ERROR: An exception occurred:\n') |
72 | | - f.write(f'{traceback.format_exc()}\n') |
| 129 | + f.write(err_msg) |
| 130 | + sys.stderr.write(err_msg) |
73 | 131 |
|
| 132 | + if args.run_type == 'runningRemote': |
| 133 | + from mpi4py import MPI |
74 | 134 |
|
75 | | -# ====================================================================================================================== |
| 135 | + MPI.COMM_WORLD.Abort(1) |
76 | 136 |
|
77 | | -if __name__ == '__main__': |
78 | | - input_args = sys.argv |
79 | | - main(input_args) |
80 | 137 |
|
81 | | -# ====================================================================================================================== |
| 138 | +if __name__ == '__main__': |
| 139 | + main() |
0 commit comments