diff --git a/omf/models/microgridDesign.py b/omf/models/microgridDesign.py index 855c953ac..72b325fba 100644 --- a/omf/models/microgridDesign.py +++ b/omf/models/microgridDesign.py @@ -176,7 +176,7 @@ def work(modelDir, inputDict): #"federal_itc_pct": solarItcPercent }, "ElectricStorage": { #"Storage": { - "installed_cost_per_kwh": batteryPowerCost, + "installed_cost_per_kw": batteryPowerCost, #"installed_cost_us_dollars_per_kw": batteryPowerCost, "installed_cost_per_kwh": batteryCapacityCost, #"installed_cost_us_dollars_per_kwh": batteryCapacityCost, @@ -256,14 +256,14 @@ def work(modelDir, inputDict): scenario['ElectricLoad']['critical_loads_kw'] = jsonifiableCriticalLoad # diesel has a quirk in how it gets inputted to REopt such that when strictly specified, allOutputData["sizeDiesel1"] = allInputData['dieselMax'] + allInputData['genExisting'] #todo: check if still true for reopt.jl - if dieselMax - genExisting > 0: - scenario['Generator']['max_kw'] = dieselMax - genExisting - else: - scenario['Generator']['max_kw'] = 0 - if dieselMin - genExisting > 0: - scenario['Generator']['min_kw'] = dieselMin - genExisting - else: - scenario['Generator']['min_kw'] = 0 + #if dieselMax - genExisting > 0: + # scenario['Generator']['max_kw'] = dieselMax - genExisting + #else: + # scenario['Generator']['max_kw'] = 0 + #if dieselMin - genExisting > 0: + # scenario['Generator']['min_kw'] = dieselMin - genExisting + #else: + # scenario['Generator']['min_kw'] = 0 #adding outage results (REopt.jl) #scenario['ElectricUtility']['outage_durations'] = [ outage_duration ] #not sure if correct diff --git a/omf/solvers/reopt_jl/REoptSolver/Manifest.toml b/omf/solvers/reopt_jl/REoptSolver/Manifest.toml index c411795e4..2feba7265 100644 --- a/omf/solvers/reopt_jl/REoptSolver/Manifest.toml +++ b/omf/solvers/reopt_jl/REoptSolver/Manifest.toml @@ -390,7 +390,7 @@ uuid = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" version = "1.7.5" [[deps.HiGHS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "10bf0ecdf70f643bfc1948a6af0a98be3950a3fc" uuid = "8fd58aa0-07eb-5a78-9b36-339c94fd15ea" version = "1.6.0+0" @@ -811,9 +811,9 @@ uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" [[deps.PyCall]] deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"] -git-tree-sha1 = "c9932f1c60d2e653df4f06d76108af8fde2200c0" +git-tree-sha1 = "9816a3826b0ebf49ab4926e2b18842ad8b5c8f04" uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" -version = "1.96.3" +version = "1.96.4" [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] diff --git a/omf/solvers/reopt_jl/__init__.py b/omf/solvers/reopt_jl/__init__.py index 377f249c6..622b6c9d4 100644 --- a/omf/solvers/reopt_jl/__init__.py +++ b/omf/solvers/reopt_jl/__init__.py @@ -4,65 +4,87 @@ thisDir = os.path.abspath(os.path.dirname(__file__)) -def build_julia_image(): - ''' Creates REoptSolver sysimage -> reopt_jl.so ''' - - os.system(f'''julia --project="{thisDir}/REoptSolver" -e ' - import Pkg; Pkg.instantiate(); - import REoptSolver; using PackageCompiler; - PackageCompiler.create_sysimage(["REoptSolver"]; sysimage_path="{thisDir}/reopt_jl.so", - precompile_execution_file="{thisDir}/precompile_reopt.jl", cpu_target="generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)") - ' ''') - #potential add: boolean to determine if you want to check your install def install_reopt_jl(system : list = platform.system()): ''' Installs dependencies necessary for running REoptSolver and creates sysimage to reduce precompile time ''' - - if os.path.isfile(f'{thisDir}/instantiated.txt'): + instantiated_path = os.path.join(thisDir,"instantiated.txt") + project_path = os.path.join(thisDir,"REoptSolver") + sysimage_path = os.path.join(thisDir,"reopt_jl.so") + precompile_path = os.path.join(thisDir,"precompile_reopt.jl") + + if os.path.isfile(instantiated_path): print("reopt_jl dependencies installed - to reinstall remove file: instantiated.txt") - if not os.path.isfile(f'{thisDir}/reopt_jl.so'): + if not os.path.isfile(sysimage_path): print("error: reopt_jl sysimage not found - remove instantiated.txt to build") return - try: + try: + install_pyjulia = [ + 'pip3 show julia 1>/dev/null 2>/dev/null || pip3 install julia ', + '''python3 -c 'import julia; julia.install()' ''' + ] + #adding abilty to use pre-made sysimage (not instantiated) + if os.path.exists(sysimage_path): + print("pre-built reopt_jl.so sysimage found") + build_julia_image = [ + f'chmod +x {sysimage_path}' + ] + else: + build_julia_image = [ + f'''julia --project={project_path} -e ' + import Pkg; Pkg.instantiate(); + import REoptSolver; using PackageCompiler; + PackageCompiler.create_sysimage(["REoptSolver"]; sysimage_path="{sysimage_path}", + precompile_execution_file="{precompile_path}", cpu_target="generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)") + ' ''' + ] if system == "Darwin": commands = [ ''' HOMEBREW_NO_AUTO_UPDATE=1 brew list julia 1>/dev/null 2>/dev/null || { brew tap homebrew/core; brew install julia; } ''' ] + commands += install_pyjulia + commands += build_julia_image + commands += [ f'touch {instantiated_path}' ] elif system == "Linux": - print("running installation for Linux: work in progress") commands = [ 'sudo apt-get install wget', 'wget https://julialang-s3.julialang.org/bin/linux/x64/1.9/julia-1.9.4-linux-x86_64.tar.gz ', #'''python3 -c 'from urllib.request import urlretrieve as wget; wget("https://julialang-s3.julialang.org/bin/linux/x64/1.9/julia-1.9.4-linux-x86_64.tar.gz", "./julia-1.9.4-linux-x86_64.tar.gz") ' ''', - 'tar -x -f "julia-1.9.4-linux-x86_64.tar.gz" -C /usr/local --strip-components 1' + 'sudo tar -xvzf "julia-1.9.4-linux-x86_64.tar.gz" -C /usr/local --strip-components 1' + ] + commands += install_pyjulia + commands += build_julia_image + commands += [ f'touch {instantiated_path}' ] + elif system == "Windows": + commands = [ + f'cd {thisDir} & del julia-1.9.4-win64.zip', + f'cd {thisDir} & curl -o julia-1.9.4-win64.zip https://julialang-s3.julialang.org/bin/winnt/x64/1.9/julia-1.9.4-win64.zip', + f'cd {thisDir} & tar -x -f "julia-1.9.4-win64.zip' ] + commands += install_pyjulia + commands += [ + f'''cd {thisDir}\\julia-1.9.4\\bin & julia --project={project_path} -e ' + import Pkg; Pkg.instantiate(); + import REoptSolver; using PackageCompiler; + PackageCompiler.create_sysimage(["REoptSolver"]; sysimage_path="{sysimage_path}", + precompile_execution_file="{precompile_path}", cpu_target="generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)") + ' ''' + f'copy nul {thisDir}\\instantiated.txt' ] else: - print(f'No installation script available yet for {system}') - return - - commands += [ - 'pip3 show julia 1>/dev/null 2>/dev/null || pip3 install julia ', - '''python3 -c 'import julia; julia.install()' ''' - ] + raise ValueError(f'No installation script available yet for {system}') for command in commands: os.system(command) - build_julia_image() - if os.path.isfile(f'{thisDir}/reopt_jl.so'): - os.system(f'touch "{thisDir}/instantiated.txt"') - print("reopt_jl installation completed successfully") - else: - print("error: reopt_jl.so not found") except Exception as e: print(e) return - + ######################################################## -#functions for converting REopt input to REopt.jl input +#functions for converting REopt input to REopt.jl input +# -> used for integration testing purposes ######################################################## def init_reopt_to_julia_dict(): @@ -142,23 +164,12 @@ def init_reopt_to_julia_dict(): return (to_jl_dict, not_included_in_jl) - -class KeyNotRecognizedError(Exception): - def __init__(self, key): - super().__init__(f"Key not found in API -> Julia JSON translator: {key}") - self.key = key - -class SectionLookupError(Exception): - def __init__(self, key): - super().__init__(f"No sections found for key: {key} in API -> Julia JSON translator") - self.key = key - def check_key(key, to_jl_dict, not_included_in_jl): ''' checks if variable name is used in REopt.jl ''' if key in not_included_in_jl: return False elif key not in to_jl_dict: - raise(KeyNotRecognizedError) + raise ValueError(f"Key not found in API -> Julia JSON translator: {key}") else: return True @@ -174,7 +185,7 @@ def check_input(key,var,to_jl_dict): elif var_type == float: return float(var) else: - raise(KeyNotRecognizedError) + raise ValueError(f"Key not found in API -> Julia JSON translator: {key}") def get_section(key,section,to_jl_dict): ''' returns converted section name if used in REopt.jl ''' @@ -185,7 +196,7 @@ def get_section(key,section,to_jl_dict): elif len(new_sections) == 1: return list(new_sections)[0] else: - raise(SectionLookupError) + raise ValueError(f"No sections found for key: {key} in API -> Julia JSON translator") def add_variable(section,key,value,jl_json,to_jl_dict): ''' converts variable into REopt.jl version of variable and adds to json ''' @@ -220,19 +231,6 @@ def convert_to_jl(reopt_json): print(e) return new_jl_json -def get_json(inputPath): - ''' for reading input/output json files ''' - with open(inputPath) as j: - inputJson = json.load(j) - return inputJson - -def write_json(outputPath, jsonData): - ''' for writing input/output json files ''' - if os.path.exists(outputPath): - print(f'File {outputPath} already exists: overwriting') - with open(outputPath, "w") as j: - json.dump(jsonData, j) - ########################################################################## # run_reopt_jl ########################################################################## @@ -251,7 +249,8 @@ def get_randomized_api_key(): return REOPT_API_KEYS[key_index] #potential optional inputs (for solver): ratio_gap, threads, max_solutions, verbosity -def run_reopt_jl(path, inputFile="", default=False, convert=False, outages=False, microgrid_only=False, max_runtime_s=None): +#potential other inputs (ease of use): load csv file path ("path_to_csv") => check if "loads_kw" already exists +def run_reopt_jl(path, inputFile="", default=False, outages=False, microgrid_only=False, max_runtime_s=None): ''' calls 'run' function through run_reopt.jl (Julia file) ''' if inputFile == "" and not default: @@ -261,28 +260,29 @@ def run_reopt_jl(path, inputFile="", default=False, convert=False, outages=False install_reopt_jl() constant_file = "Scenario_test_POST.json" - constant_path = f'{path}/{constant_file}' + constant_path = os.path.join(path,constant_file) if inputFile != constant_file: - inputPath = f'{path}/{inputFile}' + inputPath = os.path.join(path,inputFile) if default == True: - inputPath = f'{thisDir}/julia_default.json' - input_json = get_json(inputPath) - write_json(constant_path, input_json) + inputPath = os.path.join(thisDir,"testFiles","julia_default.json") + with open(inputPath) as j: + input_json = json.load(j) + #potential helper function: add_load_path => sets file path in json based on current directory + if default == True: + input_json["ElectricLoad"]["path_to_csv"] = os.path.join(thisDir,"testFiles","loadShape.csv") + with open(constant_path, "w") as j: + json.dump(input_json, j) try: - if convert and not default: #default file is already converted - input_json = get_json(constant_path) - reopt_jl_input_json = convert_to_jl(input_json) - write_json(constant_path, reopt_jl_input_json) - microgrid_only_jl = "false" if not microgrid_only else "true" outages_jl = "false" if not outages else "true" max_runtime_s_jl = "nothing" if max_runtime_s == None else max_runtime_s api_key = get_randomized_api_key() + sysimage_path = os.path.join(thisDir,"reopt_jl.so") - os.system(f'''julia --sysimage={f'{thisDir}/reopt_jl.so'} -e ' + os.system(f'''julia --sysimage={sysimage_path} -e ' using .REoptSolver; ENV["NREL_DEVELOPER_API_KEY"]="{api_key}"; REoptSolver.run("{path}", {outages_jl}, {microgrid_only_jl}, {max_runtime_s_jl}, "{api_key}") @@ -290,40 +290,9 @@ def run_reopt_jl(path, inputFile="", default=False, convert=False, outages=False except Exception as e: print(e) -########################################################################### -# comparing REopt.jl outputs for different test cases / solvers -########################################################################### - -def runAllSolvers(path, testName, fileName="", default=False, convert=True, outages=True, solvers=["HiGHS"], max_runtime_s=None): - - for solver in solvers: - print(f'########## Running {solver} test: {testName}') - start = time.time() - - run_reopt_jl(path, inputFile=fileName, default=default, convert=convert, outages=outages, max_runtime_s=max_runtime_s) - - end = time.time() - runtime = end - start - print(f'########## Completed {solver} test: {testName} in {runtime} seconds') def _test(): - all_solvers = [ "HiGHS" ] # "Ipopt", "ECOS", "Clp", "GLPK", "SCIP", "Cbc" - path = f'{thisDir}/testFiles' - - ########### CONWAY_MG_MAX: - # CONWAY_MG.json copied from CONWAY_MG_MAX/Scenario_test_POST.json - #runAllSolvers(path, "CONWAY_MG_MAX", fileName="CONWAY_MG.json", solvers=all_solvers) - - ############### CE test case - # CE.json copied from CE Test Case/Scenario_test_POST.json - #runAllSolvers(path, "CE Test Case", fileName="CE.json", solvers=all_solvers, max_runtime_s=240) - - ############## CONWAY_30MAY23_SOLARBATTERY - # CONWAY_SB.json copied from CONWAY_30MAY23_SOLARBATTERY/Scenario_test_POST.json - #runAllSolvers(path, "CONWAY_30MAY23_SOLARBATTERY", fileName="CONWAY_SB.json", solvers=all_solvers) - - ####### default julia json (default values from microgridDesign) - runAllSolvers(path, "Julia Default", default=True, solvers=all_solvers) + run_reopt_jl(os.path.join(thisDir,"testFiles"), default=True) if __name__ == "__main__": _test() \ No newline at end of file