From 4c68cfb0cdc3fda98793af277e782f971559fa21 Mon Sep 17 00:00:00 2001 From: Ana Mileva Date: Wed, 1 Apr 2026 16:10:47 -0700 Subject: [PATCH 1/2] Some enhancements to the import/export rules functionality --- gridpath/common_functions.py | 6 +++ gridpath/import_scenario_results.py | 57 ++++++++++++++++++----- gridpath/run_scenario.py | 72 ++++++++++++++--------------- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/gridpath/common_functions.py b/gridpath/common_functions.py index e260f54e2..55a948efe 100644 --- a/gridpath/common_functions.py +++ b/gridpath/common_functions.py @@ -305,6 +305,12 @@ def get_import_results_parser(): "--results_import_rule", help="The name of the rule to use to decide whether to import results.", ) + parser.add_argument( + "--ignore_incomplete", default=False, action="store_true", + help="Ignore problems with no results. Can be used to import results " + "for subproblems before all other subproblems have been solved. " + "Proceed with caution." + ) return parser diff --git a/gridpath/import_scenario_results.py b/gridpath/import_scenario_results.py index 4d92ae9cb..8572fdeb4 100644 --- a/gridpath/import_scenario_results.py +++ b/gridpath/import_scenario_results.py @@ -21,6 +21,7 @@ *gridpath_import_results* command when GridPath is installed. """ +import warnings from argparse import ArgumentParser import os.path import pandas as pd @@ -63,6 +64,7 @@ def import_scenario_results_into_database( scenario_structure, db, scenario_directory, + ignore_incomplete, quiet, ): """ @@ -72,6 +74,7 @@ def import_scenario_results_into_database( :param scenario_structure: :param db: :param scenario_directory: + :param ignore_incomplete: boolean :param quiet: boolean :return: @@ -148,13 +151,27 @@ def import_scenario_results_into_database( # Import termination condition data c = db.cursor() - with open( - os.path.join( - results_directory, "termination_condition.txt" - ), - "r", - ) as f: - termination_condition = f.read() + try: + with open( + os.path.join( + results_directory, "termination_condition.txt" + ), + "r", + ) as f: + termination_condition = f.read() + except FileNotFoundError: + if ignore_incomplete: + warnings.warn("GridPath Warning: termination " + "condition file not found.") + termination_condition = ( + "termination condition file not found" + ) + else: + tc_fname = os.path.join( + results_directory, "termination_condition.txt" + ) + raise FileNotFoundError(f"{tc_fname} not " + f"found.") termination_condition_sql = """ INSERT INTO results_scenario @@ -180,10 +197,26 @@ def import_scenario_results_into_database( many=False, ) - with open( - os.path.join(results_directory, "solver_status.txt"), "r" - ) as status_f: - solver_status = status_f.read() + try: + with open( + os.path.join(results_directory, "solver_status.txt"), + "r", + ) as status_f: + solver_status = status_f.read() + except FileNotFoundError: + if ignore_incomplete: + warnings.warn("GridPath Warning: solver status " + "file not found.") + termination_condition = ( + "solver status file not found" + ) + else: + ss_fname = os.path.join( + results_directory, + "solver_status.txt" + ) + raise FileNotFoundError(f"{ss_fname} not found.") + # Only import other results if solver status was "ok" # When the problem is infeasible, the solver status is "warning" @@ -353,6 +386,7 @@ def main(args=None): scenario_location = parsed_arguments.scenario_location quiet = parsed_arguments.quiet import_rule = parsed_arguments.results_import_rule + ignore_incomplete = parsed_arguments.ignore_incomplete conn = connect_to_database(db_path=db_path) c = conn.cursor() @@ -404,6 +438,7 @@ def main(args=None): scenario_structure=scenario_structure, db=conn, scenario_directory=scenario_directory, + ignore_incomplete=ignore_incomplete, quiet=quiet, ) diff --git a/gridpath/run_scenario.py b/gridpath/run_scenario.py index ee16317ce..ed2103eb2 100644 --- a/gridpath/run_scenario.py +++ b/gridpath/run_scenario.py @@ -855,21 +855,21 @@ def save_results( parsed_arguments.results_export_summary_rule ]["export_summary"](instance=instance, quiet=parsed_arguments.quiet) - if not parsed_arguments.quiet: - print("...exporting summary CSV results") - export_summary_results( - scenario_directory=scenario_directory, - weather_iteration=weather_iteration, - hydro_iteration=hydro_iteration, - availability_iteration=availability_iteration, - subproblem=subproblem, - stage=stage, - multi_stage=multi_stage, - instance=instance, - dynamic_components=dynamic_components, - export_summary_results_rule=export_summary_rule, - verbose=parsed_arguments.verbose, - ) + if export_summary_rule: + if not parsed_arguments.quiet: + print("...exporting summary CSV results") + export_summary_results( + scenario_directory=scenario_directory, + weather_iteration=weather_iteration, + hydro_iteration=hydro_iteration, + availability_iteration=availability_iteration, + subproblem=subproblem, + stage=stage, + multi_stage=multi_stage, + instance=instance, + dynamic_components=dynamic_components, + verbose=parsed_arguments.verbose, + ) export_pass_through_inputs( scenario_directory=scenario_directory, @@ -1264,7 +1264,6 @@ def export_summary_results( multi_stage, instance, dynamic_components, - export_summary_results_rule, verbose, ): """ @@ -1280,29 +1279,28 @@ def export_summary_results( Export results for each loaded module (if applicable) """ - if export_summary_results_rule: - # Determine/load modules and dynamic components - modules_to_use, loaded_modules = set_up_gridpath_modules( - scenario_directory=scenario_directory, multi_stage=multi_stage - ) + # Determine/load modules and dynamic components + modules_to_use, loaded_modules = set_up_gridpath_modules( + scenario_directory=scenario_directory, multi_stage=multi_stage + ) - n = 0 - for m in loaded_modules: - if hasattr(m, "export_summary_results"): - if verbose: - print(f"... {modules_to_use[n]}") - m.export_summary_results( - scenario_directory, - weather_iteration, - hydro_iteration, - availability_iteration, - subproblem, - stage, - instance, - dynamic_components, - ) + n = 0 + for m in loaded_modules: + if hasattr(m, "export_summary_results"): + if verbose: + print(f"... {modules_to_use[n]}") + m.export_summary_results( + scenario_directory, + weather_iteration, + hydro_iteration, + availability_iteration, + subproblem, + stage, + instance, + dynamic_components, + ) - n += 1 + n += 1 def export_pass_through_inputs( From 910296fda6b1d4f34bb1e9f020bd5188681568ac Mon Sep 17 00:00:00 2001 From: Ana Mileva Date: Wed, 1 Apr 2026 16:38:47 -0700 Subject: [PATCH 2/2] lint --- gridpath/common_functions.py | 8 +++++--- gridpath/import_scenario_results.py | 20 +++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gridpath/common_functions.py b/gridpath/common_functions.py index 55a948efe..4652f7b25 100644 --- a/gridpath/common_functions.py +++ b/gridpath/common_functions.py @@ -306,10 +306,12 @@ def get_import_results_parser(): help="The name of the rule to use to decide whether to import results.", ) parser.add_argument( - "--ignore_incomplete", default=False, action="store_true", + "--ignore_incomplete", + default=False, + action="store_true", help="Ignore problems with no results. Can be used to import results " - "for subproblems before all other subproblems have been solved. " - "Proceed with caution." + "for subproblems before all other subproblems have been solved. " + "Proceed with caution.", ) return parser diff --git a/gridpath/import_scenario_results.py b/gridpath/import_scenario_results.py index 8572fdeb4..4dd5e6298 100644 --- a/gridpath/import_scenario_results.py +++ b/gridpath/import_scenario_results.py @@ -161,8 +161,10 @@ def import_scenario_results_into_database( termination_condition = f.read() except FileNotFoundError: if ignore_incomplete: - warnings.warn("GridPath Warning: termination " - "condition file not found.") + warnings.warn( + "GridPath Warning: termination " + "condition file not found." + ) termination_condition = ( "termination condition file not found" ) @@ -170,8 +172,7 @@ def import_scenario_results_into_database( tc_fname = os.path.join( results_directory, "termination_condition.txt" ) - raise FileNotFoundError(f"{tc_fname} not " - f"found.") + raise FileNotFoundError(f"{tc_fname} not " f"found.") termination_condition_sql = """ INSERT INTO results_scenario @@ -205,19 +206,16 @@ def import_scenario_results_into_database( solver_status = status_f.read() except FileNotFoundError: if ignore_incomplete: - warnings.warn("GridPath Warning: solver status " - "file not found.") - termination_condition = ( - "solver status file not found" + warnings.warn( + "GridPath Warning: solver status " "file not found." ) + termination_condition = "solver status file not found" else: ss_fname = os.path.join( - results_directory, - "solver_status.txt" + results_directory, "solver_status.txt" ) raise FileNotFoundError(f"{ss_fname} not found.") - # Only import other results if solver status was "ok" # When the problem is infeasible, the solver status is "warning" # If there's no solution, variables remain uninitialized,