From e74788d55f5871765db2ac88c905058114cf34a7 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Tue, 10 Dec 2024 11:49:44 +0100 Subject: [PATCH 1/5] fix handling of AssumeException --- assume_cli/cli.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/assume_cli/cli.py b/assume_cli/cli.py index 5f7a9c7c..7a10c381 100644 --- a/assume_cli/cli.py +++ b/assume_cli/cli.py @@ -126,13 +126,14 @@ def cli(args=None): # add these two weird hacks for now warnings.filterwarnings("ignore", "coroutine.*?was never awaited.*") logging.getLogger("asyncio").setLevel("FATAL") + + # import package after argcomplete.autocomplete + # to improve autocompletion speed + from assume import World + from assume.common.exceptions import AssumeException + from assume.scenario.loader_csv import load_scenario_folder, run_learning try: - # import package after argcomplete.autocomplete - # to improve autocompletion speed - from assume import World - from assume.scenario.loader_csv import load_scenario_folder, run_learning - os.makedirs("./examples/local_db", exist_ok=True) if args.parallel: @@ -169,6 +170,8 @@ def cli(args=None): except KeyboardInterrupt: pass + except AssumeException as e: + logging.error(f"Stopping: {e}") except Exception: logging.exception("Simulation aborted") From 06eabbfaf61c3b33ba97831a852e631442760615 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Tue, 10 Dec 2024 11:52:01 +0100 Subject: [PATCH 2/5] run delete_similar_runs not before on_ready the database engine is not created before this part --- assume/common/outputs.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/assume/common/outputs.py b/assume/common/outputs.py index 8e683b98..872cd056 100644 --- a/assume/common/outputs.py +++ b/assume/common/outputs.py @@ -86,17 +86,6 @@ def __init__( self.learning_mode = learning_mode self.perform_evaluation = perform_evaluation - # get episode number if in learning or evaluation mode - self.episode = None - if self.learning_mode or self.perform_evaluation: - episode = self.simulation_id.split("_")[-1] - if episode.isdigit(): - self.episode = int(episode) - - # check if episode=0 and delete all similar runs - if self.episode == 0: - self.delete_similar_runs() - # construct all timeframe under which hourly values are written to excel and db self.start = start self.end = end @@ -217,6 +206,17 @@ def on_ready(self): # this should not wait for the task to finish to block the simulation ) + # get episode number if in learning or evaluation mode + self.episode = None + if self.learning_mode or self.perform_evaluation: + episode = self.simulation_id.split("_")[-1] + if episode.isdigit(): + self.episode = int(episode) + + # check if episode=0 and delete all similar runs + if self.episode == 0: + self.delete_similar_runs() + def handle_output_message(self, content: dict, meta: MetaDict): """ Handles the incoming messages and performs corresponding actions. From 14713b38d47f14ae624d3e33fbd076413a088fb9 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Tue, 10 Dec 2024 11:52:29 +0100 Subject: [PATCH 3/5] set episode to 0 Mentioned in https://github.com/assume-framework/assume/issues/512#issuecomment-2531064464 --- assume/scenario/loader_csv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assume/scenario/loader_csv.py b/assume/scenario/loader_csv.py index c934058b..bc6dba75 100644 --- a/assume/scenario/loader_csv.py +++ b/assume/scenario/loader_csv.py @@ -728,7 +728,7 @@ def load_scenario_folder( study_case: str, perform_evaluation: bool = False, terminate_learning: bool = False, - episode: int = 1, + episode: int = 0, eval_episode: int = 1, ): """ From 48cca0c26446b1bdf930e199bfe88613a82eecd7 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Tue, 10 Dec 2024 11:52:55 +0100 Subject: [PATCH 4/5] run small_learning_1 instead of default examples --- examples/examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples.py b/examples/examples.py index 321b3fd8..71901c2f 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -107,7 +107,7 @@ data_format = "local_db" # "local_db" or "timescale" # select the example to run from the available examples above - example = "small" + example = "small_learning_1" if data_format == "local_db": db_uri = "sqlite:///./examples/local_db/assume_db.db" From c543a841d54130e1fc9c511a15149d0fc8cbb920 Mon Sep 17 00:00:00 2001 From: Nick Harder Date: Thu, 12 Dec 2024 10:42:48 +0100 Subject: [PATCH 5/5] -move all episodes and eval_episodes to start from 1 -remove unrequired call for delete_similar_runs in run_learning function -move episode initilization back into init of output role -delete_similar_runs on start in output role when episode is 1 -clean up load_scenario_folder function as it doesn't require episode and other info any more -remove not setting up the world when episode==1 since this error no longer occurs -save last policies when exiting the loop to avoid confusion with numbering of last episode -adjust tutorial 04 as well --- assume/common/outputs.py | 25 ++++---- assume/scenario/loader_csv.py | 59 +++++-------------- assume_cli/cli.py | 2 +- examples/examples.py | 2 +- ...forcement_learning_algorithm_example.ipynb | 28 ++++----- .../notebooks/09_example_Sim_and_xRL.ipynb | 14 ++--- 6 files changed, 49 insertions(+), 81 deletions(-) diff --git a/assume/common/outputs.py b/assume/common/outputs.py index 872cd056..b7effb47 100644 --- a/assume/common/outputs.py +++ b/assume/common/outputs.py @@ -86,6 +86,13 @@ def __init__( self.learning_mode = learning_mode self.perform_evaluation = perform_evaluation + # get episode number if in learning or evaluation mode + self.episode = None + if self.learning_mode or self.perform_evaluation: + episode = self.simulation_id.split("_")[-1] + if episode.isdigit(): + self.episode = int(episode) + # construct all timeframe under which hourly values are written to excel and db self.start = start self.end = end @@ -191,6 +198,11 @@ def on_ready(self): self.db = create_engine(self.db_uri) if self.db is not None: self.delete_db_scenario(self.simulation_id) + + # check if episode equals 1 and delete all similar runs + if self.episode == 1: + self.delete_similar_runs() + if self.save_frequency_hours is not None: recurrency_task = rr.rrule( freq=rr.HOURLY, @@ -206,17 +218,6 @@ def on_ready(self): # this should not wait for the task to finish to block the simulation ) - # get episode number if in learning or evaluation mode - self.episode = None - if self.learning_mode or self.perform_evaluation: - episode = self.simulation_id.split("_")[-1] - if episode.isdigit(): - self.episode = int(episode) - - # check if episode=0 and delete all similar runs - if self.episode == 0: - self.delete_similar_runs() - def handle_output_message(self, content: dict, meta: MetaDict): """ Handles the incoming messages and performs corresponding actions. @@ -687,7 +688,7 @@ def get_sum_reward(self): ) if self.db is None: return [] - + with self.db.begin() as db: rewards_by_unit = db.execute(query).fetchall() diff --git a/assume/scenario/loader_csv.py b/assume/scenario/loader_csv.py index bc6dba75..4e445aec 100644 --- a/assume/scenario/loader_csv.py +++ b/assume/scenario/loader_csv.py @@ -536,8 +536,8 @@ def setup_world( study_case: str, perform_evaluation: bool = False, terminate_learning: bool = False, - episode: int = 0, - eval_episode: int = 0, + episode: int = 1, + eval_episode: int = 1, ) -> None: """ Load a scenario from a given path. @@ -550,8 +550,8 @@ def setup_world( study_case (str): The specific study case within the scenario to be loaded. perform_evaluation (bool, optional): A flag indicating whether evaluation should be performed. Defaults to False. terminate_learning (bool, optional): An automatically set flag indicating that we terminated the learning process now, either because we reach the end of the episode iteration or because we triggered an early stopping. - episode (int, optional): The episode number for learning. Defaults to 0. - eval_episode (int, optional): The episode number for evaluation. Defaults to 0. + episode (int, optional): The episode number for learning. Defaults to 1. + eval_episode (int, optional): The episode number for evaluation. Defaults to 1. Raises: ValueError: If the specified scenario or study case is not found in the provided inputs. @@ -726,10 +726,6 @@ def load_scenario_folder( inputs_path: str, scenario: str, study_case: str, - perform_evaluation: bool = False, - terminate_learning: bool = False, - episode: int = 0, - eval_episode: int = 1, ): """ Load a scenario from a given path. @@ -741,29 +737,12 @@ def load_scenario_folder( inputs_path (str): The path to the folder containing input files necessary for the scenario. scenario (str): The name of the scenario to be loaded. study_case (str): The specific study case within the scenario to be loaded. - perform_evaluation (bool, optional): A flag indicating whether evaluation should be performed. Defaults to False. - terminate_learning (bool, optional): An automatically set flag indicating that we terminated the learning process now, either because we reach the end of the episode iteration or because we triggered an early stopping. - episode (int, optional): The episode number for learning. Defaults to 0. - eval_episode (int, optional): The episode number for evaluation. Defaults to 0. Raises: ValueError: If the specified scenario or study case is not found in the provided inputs. - Example: - >>> load_scenario_folder( - world=world, - inputs_path="/path/to/inputs", - scenario="scenario_name", - study_case="study_case_name", - perform_evaluation=False, - episode=1, - eval_episode=1, - trained_policies_save_path="", - ) - Notes: - The function sets up the world environment based on the provided inputs and configuration files. - - If `perform_evaluation` is set to True, the function performs evaluation using the specified evaluation episode number. - The function utilizes the specified inputs to configure the simulation environment, including market parameters, unit operators, and forecasting data. - After calling this function, the world environment is prepared for further simulation and analysis. @@ -776,10 +755,6 @@ def load_scenario_folder( world=world, scenario_data=scenario_data, study_case=study_case, - perform_evaluation=perform_evaluation, - terminate_learning=terminate_learning, - episode=episode, - eval_episode=eval_episode, ) @@ -878,7 +853,6 @@ def run_learning( # initialize policies already here to set the obs_dim and act_dim in the learning role actors_and_critics = None world.learning_role.initialize_policy(actors_and_critics=actors_and_critics) - world.output_role.delete_similar_runs() # check if we already stored policies for this simulation save_path = world.learning_config["trained_policies_save_path"] @@ -928,14 +902,12 @@ def run_learning( range(1, world.learning_role.training_episodes + 1), desc="Training Episodes", ): - # TODO normally, loading twice should not create issues, somehow a scheduling issue is raised currently - if episode != 1: - setup_world( - world=world, - scenario_data=scenario_data, - study_case=study_case, - episode=episode, - ) + setup_world( + world=world, + scenario_data=scenario_data, + study_case=study_case, + episode=episode, + ) # ----------------------------------------- # Give the newly initialized learning role the needed information across episodes @@ -993,13 +965,12 @@ def run_learning( world.reset() - # if at end of simulation save last policies - if episode == (world.learning_role.training_episodes): - world.learning_role.rl_algorithm.save_params( - directory=f"{world.learning_role.trained_policies_save_path}/last_policies" - ) + # save the last policies at the end of the training + world.learning_role.rl_algorithm.save_params( + directory=f"{world.learning_role.trained_policies_save_path}/last_policies" + ) - # container shutdown implicitly with new initialisation + # container shutdown implicitly with new initialisation logger.info("################") logger.info("Training finished, Start evaluation run") world.export_csv_path = temp_csv_path diff --git a/assume_cli/cli.py b/assume_cli/cli.py index 7a10c381..14d45df0 100644 --- a/assume_cli/cli.py +++ b/assume_cli/cli.py @@ -126,7 +126,7 @@ def cli(args=None): # add these two weird hacks for now warnings.filterwarnings("ignore", "coroutine.*?was never awaited.*") logging.getLogger("asyncio").setLevel("FATAL") - + # import package after argcomplete.autocomplete # to improve autocompletion speed from assume import World diff --git a/examples/examples.py b/examples/examples.py index 71901c2f..321b3fd8 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -107,7 +107,7 @@ data_format = "local_db" # "local_db" or "timescale" # select the example to run from the available examples above - example = "small_learning_1" + example = "small" if data_format == "local_db": db_uri = "sqlite:///./examples/local_db/assume_db.db" diff --git a/examples/notebooks/04_reinforcement_learning_algorithm_example.ipynb b/examples/notebooks/04_reinforcement_learning_algorithm_example.ipynb index a44fcd7e..3e8c882a 100644 --- a/examples/notebooks/04_reinforcement_learning_algorithm_example.ipynb +++ b/examples/notebooks/04_reinforcement_learning_algorithm_example.ipynb @@ -311,13 +311,12 @@ " range(1, world.learning_role.training_episodes + 1),\n", " desc=\"Training Episodes\",\n", " ):\n", - " if episode != 1:\n", - " setup_world(\n", - " world=world,\n", - " scenario_data=scenario_data,\n", - " study_case=study_case,\n", - " episode=episode,\n", - " )\n", + " setup_world(\n", + " world=world,\n", + " scenario_data=scenario_data,\n", + " study_case=study_case,\n", + " episode=episode,\n", + " )\n", "\n", " # Give the newly initialized learning role the needed information across episodes\n", " world.learning_role.load_inter_episodic_data(inter_episodic_data)\n", @@ -370,16 +369,15 @@ "\n", " world.reset()\n", "\n", - " # -----------------------------------------\n", - " # 4 - Terminate Learning and Save policies\n", + " # -----------------------------------------\n", + " # 4 - Terminate Learning and Save policies\n", "\n", - " # if at end of simulation save last policies\n", - " if episode == (world.learning_role.training_episodes):\n", - " world.learning_role.rl_algorithm.save_params(\n", - " directory=f\"{world.learning_role.trained_policies_save_path}/last_policies\"\n", - " )\n", + " # if at end of simulation save last policies\n", + " world.learning_role.rl_algorithm.save_params(\n", + " directory=f\"{world.learning_role.trained_policies_save_path}/last_policies\"\n", + " )\n", "\n", - " # container shutdown implicitly with new initialisation\n", + " # container shutdown implicitly with new initialisation\n", " logger.info(\"################\")\n", " logger.info(\"Training finished, Start evaluation run\")\n", " world.export_csv_path = temp_csv_path\n", diff --git a/examples/notebooks/09_example_Sim_and_xRL.ipynb b/examples/notebooks/09_example_Sim_and_xRL.ipynb index 05c3e087..67b44952 100644 --- a/examples/notebooks/09_example_Sim_and_xRL.ipynb +++ b/examples/notebooks/09_example_Sim_and_xRL.ipynb @@ -578,14 +578,12 @@ " range(1, world.learning_role.training_episodes + 1),\n", " desc=\"Training Episodes\",\n", " ):\n", - " # TODO normally, loading twice should not create issues, somehow a scheduling issue is raised currently\n", - " if episode != 1:\n", - " setup_world(\n", - " world=world,\n", - " scenario_data=scenario_data,\n", - " study_case=study_case,\n", - " episode=episode,\n", - " )\n", + " setup_world(\n", + " world=world,\n", + " scenario_data=scenario_data,\n", + " study_case=study_case,\n", + " episode=episode,\n", + " )\n", "\n", " # -----------------------------------------\n", " # Give the newly initialized learning role the needed information across episodes\n",