Skip to content

Commit

Permalink
run hisim simus on cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
k-rieck committed Sep 18, 2024
1 parent 9891095 commit 59e4607
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 13 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,6 @@ dmypy.json
/building_sizer/bs_request*
/building_sizer/default_module_config.json
/building_sizer/*.txt
/building_sizer/*.prof
/building_sizer/*.prof

/cluster_requests/slurm_output_files
110 changes: 105 additions & 5 deletions building_sizer/building_sizer_algorithm_no_utsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"""

import dataclasses

import time
import subprocess
from typing import Any, Dict, List, Optional, Tuple
import json
import os
Expand All @@ -20,6 +21,7 @@
from building_sizer import (
individual_encoding_no_utsp,
evolutionary_algorithm_no_utsp as evo_alg,
hisim_simulation_no_utsp
)

# Add the parent directory to the system path
Expand Down Expand Up @@ -236,10 +238,9 @@ def get_results_from_requisite_hisim_configs(
PostProcessingOptions.COMPUTE_CAPEX
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_KPIS_AND_WRITE_TO_REPORT
)
PostProcessingOptions.COMPUTE_KPIS)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_ALL_KPIS_TO_JSON
PostProcessingOptions.WRITE_KPIS_TO_JSON
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_KPIS_TO_JSON_FOR_BUILDING_SIZER
Expand Down Expand Up @@ -296,6 +297,91 @@ def get_results_from_requisite_hisim_configs(
result_dict.update({hisim_config_path: kpis_building_sizer})
return result_dict

def get_results_from_requisite_hisim_configs_slurm(
requisite_hisim_config_paths: List[str], main_building_sizer_request_directory: str
) -> Dict[str, Dict]:
"""
Collects the results from the HiSim requests sent in the previous iteration.
:param requisite_requests: List of previous HiSIM requests
:type requisite_requests: List[TimeSeriesRequest]
:param url: url for connection to the UTSP
:type url: str
:param api_key: password for the connection to the UTSP
:type api_key: str
:return: dictionary of processed hisim requests (HiSIM results)
:rtype: Dict[str, ResultDelivery]
"""
# run HiSim for each config and get kpis and store in dictionary
# Step 1: Check if result_dict_path exists, if not create an empty dict in it
result_dict_path = os.path.join(main_building_sizer_request_directory, "result_dict.json")
result_dict: Dict = {}
if not os.path.exists(result_dict_path):
with open(result_dict_path, 'w') as file:
json.dump(result_dict, file)
# set simulation parameters here
year = 2021
seconds_per_timestep = 60 * 15
my_simulation_parameters = SimulationParameters.one_day_only(
year=year, seconds_per_timestep=seconds_per_timestep
)
# set postprocessing options
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.PREPARE_OUTPUTS_FOR_SCENARIO_EVALUATION
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_OPEX
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_CAPEX
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_KPIS)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_KPIS_TO_JSON
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_KPIS_TO_JSON_FOR_BUILDING_SIZER
)
# set logging level to 1
my_simulation_parameters.logging_level = 3

for index, hisim_config_path in enumerate(requisite_hisim_config_paths):
# Get result by calling building_sizer_algorithm_no_utsp on cluster

# SLURM script to execute
slurm_script = "/fast/home/k-rieck/HiSim-Building-Sizer/cluster_requests/job_array_hisim_simulation.sh"

# Call the SLURM script with subprocess and pass the two parameters
slurm_result_hisim_simulation = subprocess.run(["sbatch", slurm_script, hisim_config_path, "household_cluster", main_building_sizer_request_directory, result_dict_path], check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# Extract the job ID from the output of `sbatch`
# if stdout is none check if any errors occured
if slurm_result_hisim_simulation.stdout is None:
print(f"Error: {slurm_result_hisim_simulation.stderr}")
job_id = slurm_result_hisim_simulation.stdout.strip().split()[-1]
print(f"Submitted SLURM job with ID {job_id}")

# Local alternative execution
# hisim_simulation_no_utsp.run_hisim_simulation_and_collect_kpis(hisim_config_path, "household_cluster", main_building_sizer_request_directory, result_dict_path)

# Once the job is finished, check if the result file exists
timeout = 600 # Timeout in seconds (adjust as needed)
start_time = time.time()

while not result_dict:
with open(result_dict_path, "r", encoding="utf-8") as result_file:
result_dict = json.load(result_file)
if time.time() - start_time > timeout:
raise TimeoutError(f"Result dict {result_dict_path} was not filled within the timeout period.")
print(f"Waiting for result dict {result_dict_path} to be filled with values...")
time.sleep(10)

if result_dict:
return result_dict
else:
raise ValueError("Result dict is empty ", result_dict)


def trigger_next_iteration(
request: BuildingSizerRequest,
Expand Down Expand Up @@ -350,7 +436,7 @@ def building_sizer_iteration(
the result of this iteration
:rtype: Tuple[Optional[TimeSeriesRequest], Any]
"""
result_dict = get_results_from_requisite_hisim_configs(
result_dict = get_results_from_requisite_hisim_configs_slurm(
request.requisite_hisim_config_paths, main_building_sizer_request_directory
)

Expand Down Expand Up @@ -461,3 +547,17 @@ def main_without_utsp(
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w+", encoding="utf-8") as result_file:
result_file.write(building_sizer_result_json)


if __name__ == "__main__":
if len(sys.argv) < 3:
log.information("Building sizer algorithm needs two arguments.")
sys.exit(1)
BUILDING_SIZER_REQUEST_JSON = sys.argv[1]
REQUEST_DIRECTORY = sys.argv[2]
# Deserialize the JSON string into a Python dictionary
BUILDING_SIZER_REQUEST = BuildingSizerRequest.from_json(BUILDING_SIZER_REQUEST_JSON)

print("calling " + str(BUILDING_SIZER_REQUEST) + " with request directory " + REQUEST_DIRECTORY)
main_without_utsp(request=BUILDING_SIZER_REQUEST,
main_building_sizer_request_directory=REQUEST_DIRECTORY)
137 changes: 137 additions & 0 deletions building_sizer/hisim_simulation_no_utsp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Run HiSim simulation."""
# Add the parent directory to the system path
import sys
import re
import os
import json
from typing import Dict, Optional
sys.path.append("/fast/home/k-rieck/repositories/HiSim")
from hisim.building_sizer_utils.interface_configs.system_config import (
EnergySystemConfig,
)
from hisim.building_sizer_utils.interface_configs.archetype_config import (
ArcheTypeConfig,
)
from hisim.building_sizer_utils.interface_configs.modular_household_config import (
ModularHouseholdConfig,
)
from hisim.building_sizer_utils.interface_configs.kpi_config import KPIConfig
from hisim import hisim_main
from hisim.simulationparameters import SimulationParameters
from hisim.postprocessingoptions import PostProcessingOptions
from hisim.result_path_provider import (
ResultPathProviderSingleton,
SortingOptionEnum,
)
from hisim import log

def run_hisim_simulation_and_collect_kpis(
hisim_config_path: str, household_module: str, main_building_sizer_request_directory: str, result_dict_path: str) -> Dict[str, Dict]:
"""
Collects the results from the HiSim request.
"""
# run HiSim for each config and get kpis and store in dictionary
# set simulation parameters here
year = 2021
seconds_per_timestep = 60 * 15
my_simulation_parameters = SimulationParameters.one_day_only(
year=year, seconds_per_timestep=seconds_per_timestep
)
# set postprocessing options
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.PREPARE_OUTPUTS_FOR_SCENARIO_EVALUATION
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_OPEX
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_CAPEX
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.COMPUTE_KPIS)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_KPIS_TO_JSON
)
my_simulation_parameters.post_processing_options.append(
PostProcessingOptions.WRITE_KPIS_TO_JSON_FOR_BUILDING_SIZER
)
# set logging level to 1
my_simulation_parameters.logging_level = 3
household_module = household_module
path_to_module = f"/fast/home/k-rieck/repositories/HiSim/system_setups/{household_module}.py"
# set hisim results directory
# if requisite_hisim_config_path is given, get hash number and sampling mode for result path
if hisim_config_path is not None:
config_filename_splitted = hisim_config_path.split("/")
scenario_hash_string = re.findall(r"\-?\d+", config_filename_splitted[-1])[
0
]
further_result_folder_description = config_filename_splitted[-2]

hisim_result_directory = os.path.join(
main_building_sizer_request_directory, "hisim_results"
)

ResultPathProviderSingleton().set_important_result_path_information(
module_directory=hisim_result_directory,
model_name=household_module,
further_result_folder_description=os.path.join(
*[further_result_folder_description,]
),
variant_name="_",
scenario_hash_string=scenario_hash_string,
sorting_option=SortingOptionEnum.MASS_SIMULATION_WITH_HASH_ENUMERATION,
)
# make dir if not exist yet
if not os.path.isdir(ResultPathProviderSingleton().get_result_directory_name()):
os.makedirs(ResultPathProviderSingleton().get_result_directory_name())
my_simulation_parameters.result_directory = (
ResultPathProviderSingleton().get_result_directory_name()
)
# run hisim simulation
hisim_main.main(
path_to_module=path_to_module,
my_module_config=hisim_config_path,
my_simulation_parameters=my_simulation_parameters,
)

# get results for each simulation
kpi_json = "kpi_config_for_building_sizer.json"
with open(
os.path.join(my_simulation_parameters.result_directory, kpi_json),
"r",
encoding="utf-8",
) as result_file:
kpis_building_sizer = json.load(result_file)
# add configs and respective kpis to dictionary
current_result_dict = {hisim_config_path: kpis_building_sizer}


# Step 2: Open the JSON file, read the content, and load it into a Python dictionary
with open(result_dict_path, 'r') as file:
data = json.load(file)

# Step 3: Append the new dictionary (this assumes `data` is a dictionary)
data.update(current_result_dict)

# Step 4: Write the updated dictionary back to the JSON file
with open(result_dict_path, 'w') as file:
json.dump(data, file, indent=4)
print(f"write {data} to {result_dict_path}")

if __name__ == "__main__":
if len(sys.argv) < 5:
log.information("HiSim simulation script needs four arguments.")
sys.exit(1)
HISIM_CONFIG_PATH = sys.argv[1]
HOUSEHOLD_MODULE = sys.argv[2]
MAIN_REQUEST_DIRECTORY = sys.argv[3]
RESULT_DICT_PATH = sys.argv[4]

print("calling " + HOUSEHOLD_MODULE + " with config " + HISIM_CONFIG_PATH + " and request directory " + MAIN_REQUEST_DIRECTORY)
run_hisim_simulation_and_collect_kpis(hisim_config_path=HISIM_CONFIG_PATH,
household_module=HOUSEHOLD_MODULE,
main_building_sizer_request_directory=MAIN_REQUEST_DIRECTORY,
result_dict_path=RESULT_DICT_PATH)


58 changes: 51 additions & 7 deletions building_sizer/start_building_sizer_no_utsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import sys
import json
import os
import time
import subprocess
from datetime import datetime
from typing import Dict, List

Expand Down Expand Up @@ -137,8 +139,8 @@ def main(archetype_config: ArcheTypeConfig = ArcheTypeConfig()):

# Create an initial simulation configuration for the building sizer
initial_building_sizer_request = BuildingSizerRequest(
remaining_iterations=10,
discrete_iterations=10,
remaining_iterations=4,
discrete_iterations=4,
population_size=3,
crossover_probability=0.5,
mutation_probability=0.5,
Expand Down Expand Up @@ -190,17 +192,59 @@ def main(archetype_config: ArcheTypeConfig = ArcheTypeConfig()):
request=building_sizer_request,
main_building_sizer_request_directory=main_building_sizer_request_directory,
)

# # Get result by calling building_sier_algorithm_no_utsp on cluster
# # SLURM script to execute
# slurm_script = "/fast/home/k-rieck/HiSim-Building-Sizer/cluster_requests/job_array_building_sizer_algorithm.sh"
# # Serialize the `building_sizer_request` object to a JSON string
# json_bs_request_params = json.dumps(building_sizer_request.to_dict())

# # Call the SLURM script with subprocess and pass the two parameters
# slurm_result_bs_algorithm = subprocess.run(["sbatch", slurm_script, json_bs_request_params, main_building_sizer_request_directory], check=True, capture_output=True, text=True)

# # building_sizer_algorithm_no_utsp.main_without_utsp(
# # request=building_sizer_request,
# # main_building_sizer_request_directory=main_building_sizer_request_directory,
# # )
# # Extract the job ID from the output of `sbatch`
# print("slrum result", slurm_result_bs_algorithm)
# print("slrum result stdout", slurm_result_bs_algorithm.stdout)
# # if stdout is none check if any errors occured
# if slurm_result_bs_algorithm.stdout is None:
# print(f"Error: {slurm_result_bs_algorithm.stderr}")
# job_id = slurm_result_bs_algorithm.stdout.strip().split()[-1]
# print(f"Submitted SLURM job with ID {job_id}")

# # Check the job status until it completes
# def is_job_finished(job_id):
# result = subprocess.run(["squeue", "--job", job_id], capture_output=True, text=True)
# return job_id not in result.stdout

# # Polling loop: wait for the job to finish
# while not is_job_finished(job_id):
# print(f"Waiting for SLURM job {job_id} to complete...")
# time.sleep(10) # Check every 10 seconds

# Define the path to the result file
result_file = os.path.join(main_building_sizer_request_directory, "results", "status.json")

# Once the job is finished, check if the result file exists
timeout = 600 # Timeout in seconds (adjust as needed)
start_time = time.time()
while not os.path.exists(result_file):
if time.time() - start_time > timeout:
raise TimeoutError(f"Result file '{result_file}' was not created within the timeout period.")
print(f"Waiting for '{result_file}' to be created...")
time.sleep(10)

# Once the file exists, read it
# Get the content of the result file created by the Building Sizer
# status_json = result.data["status.json"].decode()
with open(
os.path.join(
main_building_sizer_request_directory, "results", "status.json"
),
result_file,
"r",
encoding="utf-8",
) as file:
status_json = json.load(file)
print("File read successfully:", status_json)
building_sizer_result: BuildingSizerResult = BuildingSizerResult.from_dict(status_json) # type: ignore

# Check if this was the final iteration and the building sizer is finished
Expand Down
Empty file added cluster_requests/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions cluster_requests/job_array_building_sizer_algorithm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
#SBATCH --job-name=bs_algorithm
#SBATCH --ntasks=3
#SBATCH --cpus-per-task=3
#SBATCH --array=1
#SBATCH --output=/fast/home/k-rieck/HiSim-Building-Sizer/cluster_requests/slurm_output_files/out_algo_%A_%a.txt
#SBATCH --error=/fast/home/k-rieck/HiSim-Building-Sizer/cluster_requests/slurm_output_files/err_algo_%A_%a.txt
#SBATCH --exclude=cn[33-55]
#SBATCH --nice=10

# Input parameters from Python script
input_param1=$1
input_param2=$2
# Your SLURM task command, for example, calling a Python script with the parameters
# This script is called in /fast/home/k-rieck/HiSim-Building-Sizer/building_sizer/start_building_sizer_no_utsp.py
python /fast/home/k-rieck/HiSim-Building-Sizer/building_sizer/building_sizer_algorithm_no_utsp.py "$input_param1" "$input_param2"
Loading

0 comments on commit 59e4607

Please sign in to comment.