From 16d525c464956a07f70ab600a0134830c570dbc0 Mon Sep 17 00:00:00 2001 From: jmartin4 Date: Fri, 5 Sep 2025 16:51:30 -0600 Subject: [PATCH 1/8] Switching HB cases --- .../12_ammonia_synloop/run_ammonia_synloop.py | 16 +++- examples/12_ammonia_synloop/test_inputs.csv | 36 +++++++++ h2integrate/tools/inflation/cepci.csv | 1 + h2integrate/tools/run_cases.py | 80 +++++++++++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 examples/12_ammonia_synloop/test_inputs.csv create mode 100644 h2integrate/tools/run_cases.py diff --git a/examples/12_ammonia_synloop/run_ammonia_synloop.py b/examples/12_ammonia_synloop/run_ammonia_synloop.py index 2db1872cd..3b991d49a 100644 --- a/examples/12_ammonia_synloop/run_ammonia_synloop.py +++ b/examples/12_ammonia_synloop/run_ammonia_synloop.py @@ -1,10 +1,24 @@ +from pathlib import Path + +from h2integrate.tools.run_cases import mod_tech_config, load_tech_config_cases from h2integrate.core.h2integrate_model import H2IntegrateModel # Create a H2Integrate model model = H2IntegrateModel("12_ammonia_synloop.yaml") -# Run the model +# Load cases +case_file = Path("test_inputs.csv") +cases = load_tech_config_cases(case_file) + +# Modify and run the model for Haber Bosch Big +case = cases["Haber Bosch Big"] +model = mod_tech_config(model, case) model.run() +model.post_process() +# Modify and run the model for Haber Bosch Small +case = cases["Haber Bosch Small"] +model = mod_tech_config(model, case) +model.run() model.post_process() diff --git a/examples/12_ammonia_synloop/test_inputs.csv b/examples/12_ammonia_synloop/test_inputs.csv new file mode 100644 index 000000000..0e97afb25 --- /dev/null +++ b/examples/12_ammonia_synloop/test_inputs.csv @@ -0,0 +1,36 @@ +Index 0,Index 1,Index 2,Index 3,Index 4,Haber Bosch Big,Haber Bosch Small +technologies,ammonia,model_inputs,shared_parameters,production_capacity,100000,10000 +technologies,ammonia,model_inputs,shared_parameters,catalyst_consumption_rate,9.12954E-05,9.12954E-05 +technologies,ammonia,model_inputs,shared_parameters,catalyst_replacement_interval,3,3 +technologies,ammonia,model_inputs,performance_parameters,capacity_factor,0.9,0.9 +technologies,ammonia,model_inputs,performance_parameters,energy_demand,0.530645243,0.530645243 +technologies,ammonia,model_inputs,performance_parameters,heat_output,0.829995586,0.829995586 +technologies,ammonia,model_inputs,performance_parameters,feed_gas_t,298.95,298.95 +technologies,ammonia,model_inputs,performance_parameters,feed_gas_p,20,20 +technologies,ammonia,model_inputs,performance_parameters,feed_gas_x_n2,0.25,0.25 +technologies,ammonia,model_inputs,performance_parameters,feed_gas_x_h2,0.75,0.75 +technologies,ammonia,model_inputs,performance_parameters,feed_gas_mass_ratio,1.13,1.13 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_t,280.65,280.65 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_p,275,275 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_n2,0.26,0.26 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_h2,0.68,0.68 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_ar,0.02,0.02 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_nh3,0.04,0.04 +technologies,ammonia,model_inputs,performance_parameters,purge_gas_mass_ratio,0.07,0.07 +technologies,ammonia,model_inputs,cost_parameters,water_consumption_rate,0.049236824,0.049236824 +technologies,ammonia,model_inputs,cost_parameters,baseline_capacity,52776.6,52776.6 +technologies,ammonia,model_inputs,cost_parameters,base_cost_year,2016,2016 +technologies,ammonia,model_inputs,cost_parameters,capex_scaling_exponent,0.6,0.6 +technologies,ammonia,model_inputs,cost_parameters,labor_scaling_exponent,0.25,0.25 +technologies,ammonia,model_inputs,cost_parameters,synloop_capex_base,302460908,302460908 +technologies,ammonia,model_inputs,cost_parameters,heat_capex_base,7069100,7069100 +technologies,ammonia,model_inputs,cost_parameters,cool_capex_base,4799200,4799200 +technologies,ammonia,model_inputs,cost_parameters,other_eqpt_capex_base,0,0 +technologies,ammonia,model_inputs,cost_parameters,land_capex_base,4112701.84,4112701.84 +technologies,ammonia,model_inputs,cost_parameters,deprec_noneq_capex_rate,0.42,0.42 +technologies,ammonia,model_inputs,cost_parameters,labor_rate_base,57,57 +technologies,ammonia,model_inputs,cost_parameters,num_workers_base,50,50 +technologies,ammonia,model_inputs,cost_parameters,hours_yr,2080,2080 +technologies,ammonia,model_inputs,cost_parameters,gen_admin,0.2,0.2 +technologies,ammonia,model_inputs,cost_parameters,prop_tax_ins,0.02,0.02 +technologies,ammonia,model_inputs,cost_parameters,maint_rep,0.01,0.01 diff --git a/h2integrate/tools/inflation/cepci.csv b/h2integrate/tools/inflation/cepci.csv index 21e801512..4aa6f59cc 100644 --- a/h2integrate/tools/inflation/cepci.csv +++ b/h2integrate/tools/inflation/cepci.csv @@ -27,3 +27,4 @@ Year,CEPCI 2021,628 2022,794 2023,801 +2024,800 diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py new file mode 100644 index 000000000..87b84e705 --- /dev/null +++ b/h2integrate/tools/run_cases.py @@ -0,0 +1,80 @@ +import operator +from functools import reduce + +import pandas as pd + + +""" +getFromDict() +setFromDict() + +Allows for programmatic calling of items in a nested dict using a variable-length list +E.g. instead of + dataDict[item1][item2][item3][item4][item5] = value +or + dataDict[item1][item2][item3][item4][item5] +We can just say + getFromDict(dataDict, mapList) +or + setFromDict(dataDict, mapList, value) +To achieve the same results, where + mapList = [item1,item2,item3,item4,item5] +""" + + +def getFromDict(dataDict, mapList): + return reduce(operator.getitem, mapList, dataDict) + + +def setInDict(dataDict, mapList, value): + getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value + + +""" +load_tech_config_cases() + +Loads extensive lists of values from a spreadsheet to run many different cases with different +tech_config values. + +Input: case_file (Path) - Path to the .csv file where the different tech_config values are listed. + This .csv must be formatted like so: + "Index 1" | "Index 2" | ... | "Index " | | ... | | + "technologies" | | ... | | | ... || + "technologies" | | ... | | | ... || + . . + . . + . . + "technologies" | | ... | | | ... || + +Output: tech_config_cases - DataFrame with the indexes of the tech_config as a MultiIndex and the + different case names as the column names +""" + + +def load_tech_config_cases(case_file): + tech_config_cases = pd.read_csv(case_file) + column_names = tech_config_cases.columns.values + index_names = list(filter(lambda x: "Index" in x, column_names)) + tech_config_cases = tech_config_cases.set_index(index_names) + + return tech_config_cases + + +""" +mod_tech_config() + +Modifies particular tech_config values on an existing H2I model before it is run + +Inputs: h2i_model: H2IntegrateModel that has been set up but not run + tech_config_case: Series that was indexed from tech_config_cases DataFrame + +Output: h2i_model: H2IntegrateModel that is modified with the new tech_config values + +""" + + +def mod_tech_config(h2i_model, tech_config_case): + for index_list, value in tech_config_case.items(): + setInDict(h2i_model.technology_config, index_list, float(value)) + + return h2i_model From 31949d5f8afc8ff1944e00b62db5717eb7e2afcc Mon Sep 17 00:00:00 2001 From: John Jasa Date: Tue, 23 Sep 2025 13:30:41 -0600 Subject: [PATCH 2/8] Docstring updates --- .../12_ammonia_synloop/run_ammonia_synloop.py | 6 +- h2integrate/tools/run_cases.py | 114 ++++++++++-------- 2 files changed, 67 insertions(+), 53 deletions(-) diff --git a/examples/12_ammonia_synloop/run_ammonia_synloop.py b/examples/12_ammonia_synloop/run_ammonia_synloop.py index 3b991d49a..3b896b535 100644 --- a/examples/12_ammonia_synloop/run_ammonia_synloop.py +++ b/examples/12_ammonia_synloop/run_ammonia_synloop.py @@ -1,6 +1,6 @@ from pathlib import Path -from h2integrate.tools.run_cases import mod_tech_config, load_tech_config_cases +from h2integrate.tools.run_cases import modify_tech_config, load_tech_config_cases from h2integrate.core.h2integrate_model import H2IntegrateModel @@ -13,12 +13,12 @@ # Modify and run the model for Haber Bosch Big case = cases["Haber Bosch Big"] -model = mod_tech_config(model, case) +model = modify_tech_config(model, case) model.run() model.post_process() # Modify and run the model for Haber Bosch Small case = cases["Haber Bosch Small"] -model = mod_tech_config(model, case) +model = modify_tech_config(model, case) model.run() model.post_process() diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py index 87b84e705..60f12b16a 100644 --- a/h2integrate/tools/run_cases.py +++ b/h2integrate/tools/run_cases.py @@ -4,54 +4,71 @@ import pandas as pd -""" -getFromDict() -setFromDict() - -Allows for programmatic calling of items in a nested dict using a variable-length list -E.g. instead of - dataDict[item1][item2][item3][item4][item5] = value -or - dataDict[item1][item2][item3][item4][item5] -We can just say - getFromDict(dataDict, mapList) -or - setFromDict(dataDict, mapList, value) -To achieve the same results, where - mapList = [item1,item2,item3,item4,item5] -""" - - def getFromDict(dataDict, mapList): - return reduce(operator.getitem, mapList, dataDict) + """Get value from nested dictionary using a list of keys. + Allows for programmatic calling of items in a nested dict using a variable-length list. + Instead of dataDict[item1][item2][item3][item4][item5], you can use + getFromDict(dataDict, [item1, item2, item3, item4, item5]). -def setInDict(dataDict, mapList, value): - getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value - + Args: + dataDict (dict): The nested dictionary to access. + mapList (list): List of keys to traverse the nested dictionary. -""" -load_tech_config_cases() + Returns: + The value at the specified nested location in the dictionary. -Loads extensive lists of values from a spreadsheet to run many different cases with different -tech_config values. + Example: + >>> data = {"a": {"b": {"c": 42}}} + >>> getFromDict(data, ["a", "b", "c"]) + 42 + """ + return reduce(operator.getitem, mapList, dataDict) -Input: case_file (Path) - Path to the .csv file where the different tech_config values are listed. - This .csv must be formatted like so: - "Index 1" | "Index 2" | ... | "Index " | | ... | | - "technologies" | | ... | | | ... || - "technologies" | | ... | | | ... || - . . - . . - . . - "technologies" | | ... | | | ... || -Output: tech_config_cases - DataFrame with the indexes of the tech_config as a MultiIndex and the - different case names as the column names -""" +def setInDict(dataDict, mapList, value): + """Set value in nested dictionary using a list of keys. + + Allows for programmatic setting of items in a nested dict using a variable-length list. + Instead of dataDict[item1][item2][item3][item4][item5] = value, you can use + setInDict(dataDict, [item1, item2, item3, item4, item5], value). + + Args: + dataDict (dict): The nested dictionary to modify. + mapList (list): List of keys to traverse the nested dictionary. + value: The value to set at the specified nested location. + + Example: + >>> data = {"a": {"b": {}}} + >>> setInDict(data, ["a", "b", "c"], 42) + >>> data["a"]["b"]["c"] + 42 + """ + getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value def load_tech_config_cases(case_file): + """Load extensive lists of values from a spreadsheet to run many different cases. + + Loads tech_config values from a CSV file to run multiple cases with different + technology configuration values. + + Args: + case_file (Path): Path to the .csv file where the different tech_config values + are listed. The CSV must be formatted with "Index 1", "Index 2", etc. + columns followed by case name columns. Each row should have "technologies" + as the first index value, followed by tech_name and parameter names. + + Returns: + pd.DataFrame: DataFrame with the indexes of the tech_config as a MultiIndex + and the different case names as the column names. + + Note: + The CSV format should be: + | "Index 1" | "Index 2" |...| "Index " | |...| | + | "technologies" | |...| | |...| | + | "technologies" | |...| | |...| | + """ tech_config_cases = pd.read_csv(case_file) column_names = tech_config_cases.columns.values index_names = list(filter(lambda x: "Index" in x, column_names)) @@ -60,20 +77,17 @@ def load_tech_config_cases(case_file): return tech_config_cases -""" -mod_tech_config() - -Modifies particular tech_config values on an existing H2I model before it is run - -Inputs: h2i_model: H2IntegrateModel that has been set up but not run - tech_config_case: Series that was indexed from tech_config_cases DataFrame - -Output: h2i_model: H2IntegrateModel that is modified with the new tech_config values - -""" +def modify_tech_config(h2i_model, tech_config_case): + """Modify particular tech_config values on an existing H2I model before it is run. + Args: + h2i_model: H2IntegrateModel that has been set up but not run. + tech_config_case (pd.Series): Series that was indexed from tech_config_cases + DataFrame containing the parameter values to modify. -def mod_tech_config(h2i_model, tech_config_case): + Returns: + H2IntegrateModel: The H2IntegrateModel with modified tech_config values. + """ for index_list, value in tech_config_case.items(): setInDict(h2i_model.technology_config, index_list, float(value)) From 5407a26fca5c3c2676d0822eaedcf481cdec09e1 Mon Sep 17 00:00:00 2001 From: jmartin4 Date: Wed, 1 Oct 2025 15:54:41 -0600 Subject: [PATCH 3/8] Running while checking data types --- examples/12_ammonia_synloop/test_inputs.csv | 39 ++------------------- h2integrate/tools/run_cases.py | 34 +++++++++++++++--- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/examples/12_ammonia_synloop/test_inputs.csv b/examples/12_ammonia_synloop/test_inputs.csv index 0e97afb25..0082988ff 100644 --- a/examples/12_ammonia_synloop/test_inputs.csv +++ b/examples/12_ammonia_synloop/test_inputs.csv @@ -1,36 +1,3 @@ -Index 0,Index 1,Index 2,Index 3,Index 4,Haber Bosch Big,Haber Bosch Small -technologies,ammonia,model_inputs,shared_parameters,production_capacity,100000,10000 -technologies,ammonia,model_inputs,shared_parameters,catalyst_consumption_rate,9.12954E-05,9.12954E-05 -technologies,ammonia,model_inputs,shared_parameters,catalyst_replacement_interval,3,3 -technologies,ammonia,model_inputs,performance_parameters,capacity_factor,0.9,0.9 -technologies,ammonia,model_inputs,performance_parameters,energy_demand,0.530645243,0.530645243 -technologies,ammonia,model_inputs,performance_parameters,heat_output,0.829995586,0.829995586 -technologies,ammonia,model_inputs,performance_parameters,feed_gas_t,298.95,298.95 -technologies,ammonia,model_inputs,performance_parameters,feed_gas_p,20,20 -technologies,ammonia,model_inputs,performance_parameters,feed_gas_x_n2,0.25,0.25 -technologies,ammonia,model_inputs,performance_parameters,feed_gas_x_h2,0.75,0.75 -technologies,ammonia,model_inputs,performance_parameters,feed_gas_mass_ratio,1.13,1.13 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_t,280.65,280.65 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_p,275,275 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_n2,0.26,0.26 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_h2,0.68,0.68 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_ar,0.02,0.02 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_x_nh3,0.04,0.04 -technologies,ammonia,model_inputs,performance_parameters,purge_gas_mass_ratio,0.07,0.07 -technologies,ammonia,model_inputs,cost_parameters,water_consumption_rate,0.049236824,0.049236824 -technologies,ammonia,model_inputs,cost_parameters,baseline_capacity,52776.6,52776.6 -technologies,ammonia,model_inputs,cost_parameters,base_cost_year,2016,2016 -technologies,ammonia,model_inputs,cost_parameters,capex_scaling_exponent,0.6,0.6 -technologies,ammonia,model_inputs,cost_parameters,labor_scaling_exponent,0.25,0.25 -technologies,ammonia,model_inputs,cost_parameters,synloop_capex_base,302460908,302460908 -technologies,ammonia,model_inputs,cost_parameters,heat_capex_base,7069100,7069100 -technologies,ammonia,model_inputs,cost_parameters,cool_capex_base,4799200,4799200 -technologies,ammonia,model_inputs,cost_parameters,other_eqpt_capex_base,0,0 -technologies,ammonia,model_inputs,cost_parameters,land_capex_base,4112701.84,4112701.84 -technologies,ammonia,model_inputs,cost_parameters,deprec_noneq_capex_rate,0.42,0.42 -technologies,ammonia,model_inputs,cost_parameters,labor_rate_base,57,57 -technologies,ammonia,model_inputs,cost_parameters,num_workers_base,50,50 -technologies,ammonia,model_inputs,cost_parameters,hours_yr,2080,2080 -technologies,ammonia,model_inputs,cost_parameters,gen_admin,0.2,0.2 -technologies,ammonia,model_inputs,cost_parameters,prop_tax_ins,0.02,0.02 -technologies,ammonia,model_inputs,cost_parameters,maint_rep,0.01,0.01 +Index 0,Index 1,Index 2,Index 3,Index 4,Type,Haber Bosch Big,Haber Bosch Small +technologies,ammonia,model_inputs,shared_parameters,production_capacity,float,100000,10000 +technologies,h2_storage,model_inputs,performance_parameters,type,str,salt_cavern,lined_rock_cavern diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py index 60f12b16a..f42495a92 100644 --- a/h2integrate/tools/run_cases.py +++ b/h2integrate/tools/run_cases.py @@ -4,6 +4,26 @@ import pandas as pd +def cast_by_name(type_name, value): + """Cast a string read in from an input file as a data type also given as a string. + Currently allowed data types: int, float, bool, str + + Args: + type_name (str): The data type to cast into, as a string. + value (str): The value, as a string. + + Returns: + The value in the specified data type + + """ + trusted_types = ["int", "float", "bool", "str"] ## others as needed + if type_name in trusted_types: + return __builtins__[type_name](value) + else: + msg = f"Specified data type {type_name} invalid, must be one of {trusted_types}" + raise TypeError(msg) + + def getFromDict(dataDict, mapList): """Get value from nested dictionary using a list of keys. @@ -65,13 +85,14 @@ def load_tech_config_cases(case_file): Note: The CSV format should be: - | "Index 1" | "Index 2" |...| "Index " | |...| | - | "technologies" | |...| | |...| | - | "technologies" | |...| | |...| | + | "Index 1" |...| "Index " | "Type" | |...| | + | "technologies" |...| | "float" | |...| | + | "technologies" |...| | "str" | |...| | """ tech_config_cases = pd.read_csv(case_file) column_names = tech_config_cases.columns.values index_names = list(filter(lambda x: "Index" in x, column_names)) + index_names.append("Type") tech_config_cases = tech_config_cases.set_index(index_names) return tech_config_cases @@ -88,7 +109,10 @@ def modify_tech_config(h2i_model, tech_config_case): Returns: H2IntegrateModel: The H2IntegrateModel with modified tech_config values. """ - for index_list, value in tech_config_case.items(): - setInDict(h2i_model.technology_config, index_list, float(value)) + for index_tup, value in tech_config_case.items(): + index_list = list(index_tup) + data_type = index_list[-1] + index_list = index_list[:-1] + setInDict(h2i_model.technology_config, index_list, cast_by_name(data_type, value)) return h2i_model From c24fb83290181b6531541bd6285c89ad0e2de5f4 Mon Sep 17 00:00:00 2001 From: jmartin4 Date: Wed, 1 Oct 2025 17:06:58 -0600 Subject: [PATCH 4/8] Test getting there --- examples/12_ammonia_synloop/hb_inputs.csv | 3 +++ .../12_ammonia_synloop/run_ammonia_synloop.py | 2 +- examples/12_ammonia_synloop/test_inputs.csv | 8 ++++--- h2integrate/tools/run_cases.py | 6 +++++ h2integrate/tools/test/test_inputs.csv | 5 ++++ h2integrate/tools/test/test_tools.py | 23 +++++++++++++++++++ 6 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 examples/12_ammonia_synloop/hb_inputs.csv create mode 100644 h2integrate/tools/test/test_inputs.csv create mode 100644 h2integrate/tools/test/test_tools.py diff --git a/examples/12_ammonia_synloop/hb_inputs.csv b/examples/12_ammonia_synloop/hb_inputs.csv new file mode 100644 index 000000000..0082988ff --- /dev/null +++ b/examples/12_ammonia_synloop/hb_inputs.csv @@ -0,0 +1,3 @@ +Index 0,Index 1,Index 2,Index 3,Index 4,Type,Haber Bosch Big,Haber Bosch Small +technologies,ammonia,model_inputs,shared_parameters,production_capacity,float,100000,10000 +technologies,h2_storage,model_inputs,performance_parameters,type,str,salt_cavern,lined_rock_cavern diff --git a/examples/12_ammonia_synloop/run_ammonia_synloop.py b/examples/12_ammonia_synloop/run_ammonia_synloop.py index 3b896b535..0dbc9244f 100644 --- a/examples/12_ammonia_synloop/run_ammonia_synloop.py +++ b/examples/12_ammonia_synloop/run_ammonia_synloop.py @@ -8,7 +8,7 @@ model = H2IntegrateModel("12_ammonia_synloop.yaml") # Load cases -case_file = Path("test_inputs.csv") +case_file = Path("hb_inputs.csv") cases = load_tech_config_cases(case_file) # Modify and run the model for Haber Bosch Big diff --git a/examples/12_ammonia_synloop/test_inputs.csv b/examples/12_ammonia_synloop/test_inputs.csv index 0082988ff..45aa97b8f 100644 --- a/examples/12_ammonia_synloop/test_inputs.csv +++ b/examples/12_ammonia_synloop/test_inputs.csv @@ -1,3 +1,5 @@ -Index 0,Index 1,Index 2,Index 3,Index 4,Type,Haber Bosch Big,Haber Bosch Small -technologies,ammonia,model_inputs,shared_parameters,production_capacity,float,100000,10000 -technologies,h2_storage,model_inputs,performance_parameters,type,str,salt_cavern,lined_rock_cavern +Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Float Test,Int Test,Bool Test,Str Test +technologies,electrolyzer,model_inputs,shared_parameters,electrolyzer_capex,,float,1000,1295,1295,1295 +technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,1,2,1,1 +technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,TRUE,FALSE,TRUE +technologies,electrolyzer,model_inputs,performance_parameters,sizing,size_for,str,BOL,BOL,BOL,EOL diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py index f42495a92..69c8624fe 100644 --- a/h2integrate/tools/run_cases.py +++ b/h2integrate/tools/run_cases.py @@ -88,6 +88,9 @@ def load_tech_config_cases(case_file): | "Index 1" |...| "Index " | "Type" | |...| | | "technologies" |...| | "float" | |...| | | "technologies" |...| | "str" | |...| | + + If some parameters are nested deeper than others, make as many Index columns for the deepest- + nested parameters and leave any unused Indexes blank. """ tech_config_cases = pd.read_csv(case_file) column_names = tech_config_cases.columns.values @@ -113,6 +116,9 @@ def modify_tech_config(h2i_model, tech_config_case): index_list = list(index_tup) data_type = index_list[-1] index_list = index_list[:-1] + # Remove nans from blank index fields + while type(index_list[-1]) is not str: + index_list = index_list[:-1] setInDict(h2i_model.technology_config, index_list, cast_by_name(data_type, value)) return h2i_model diff --git a/h2integrate/tools/test/test_inputs.csv b/h2integrate/tools/test/test_inputs.csv new file mode 100644 index 000000000..45aa97b8f --- /dev/null +++ b/h2integrate/tools/test/test_inputs.csv @@ -0,0 +1,5 @@ +Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Float Test,Int Test,Bool Test,Str Test +technologies,electrolyzer,model_inputs,shared_parameters,electrolyzer_capex,,float,1000,1295,1295,1295 +technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,1,2,1,1 +technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,TRUE,FALSE,TRUE +technologies,electrolyzer,model_inputs,performance_parameters,sizing,size_for,str,BOL,BOL,BOL,EOL diff --git a/h2integrate/tools/test/test_tools.py b/h2integrate/tools/test/test_tools.py new file mode 100644 index 000000000..89aef96ed --- /dev/null +++ b/h2integrate/tools/test/test_tools.py @@ -0,0 +1,23 @@ +from pathlib import Path + +from h2integrate import EXAMPLE_DIR +from h2integrate.tools.run_cases import modify_tech_config, load_tech_config_cases +from h2integrate.core.h2integrate_model import H2IntegrateModel + + +def test_tech_config_modifier(subtests): + """Test cases for modifiying and running tech_config from csv. + Using 01 example as test case + """ + + # Make an H2I model from the 01 example + orig_highlevel_yaml = EXAMPLE_DIR / "01_onshore_steel_mn" / "01_onshore_steel_mn.yaml" + model = H2IntegrateModel(orig_highlevel_yaml) + + # Modify using csv + case_file = Path(__file__).resolve().parent / "test_inputs.csv" + cases = load_tech_config_cases(case_file) + with subtests.test("float"): + case = cases["Float Test"] + model = modify_tech_config(model, case) + model.run() From d43e494e1dcb1aae403d31b71d2a095f3cfc8be9 Mon Sep 17 00:00:00 2001 From: jmartin4 Date: Thu, 2 Oct 2025 09:20:33 -0600 Subject: [PATCH 5/8] Test working --- .../12_ammonia_synloop/run_ammonia_synloop.py | 21 ++++++++-------- examples/12_ammonia_synloop/test_inputs.csv | 5 ---- h2integrate/tools/run_cases.py | 10 +++++++- h2integrate/tools/test/test_inputs.csv | 6 ++--- h2integrate/tools/test/test_tools.py | 24 +++++++++++++++++-- 5 files changed, 44 insertions(+), 22 deletions(-) delete mode 100644 examples/12_ammonia_synloop/test_inputs.csv diff --git a/examples/12_ammonia_synloop/run_ammonia_synloop.py b/examples/12_ammonia_synloop/run_ammonia_synloop.py index 0dbc9244f..f81d9c6b2 100644 --- a/examples/12_ammonia_synloop/run_ammonia_synloop.py +++ b/examples/12_ammonia_synloop/run_ammonia_synloop.py @@ -11,14 +11,13 @@ case_file = Path("hb_inputs.csv") cases = load_tech_config_cases(case_file) -# Modify and run the model for Haber Bosch Big -case = cases["Haber Bosch Big"] -model = modify_tech_config(model, case) -model.run() -model.post_process() - -# Modify and run the model for Haber Bosch Small -case = cases["Haber Bosch Small"] -model = modify_tech_config(model, case) -model.run() -model.post_process() +# Modify and run the model for different cases +caselist = [ + "Haber Bosch Big", + "Haber Bosch Small", +] +for casename in caselist: + case = cases[casename] + model = modify_tech_config(model, case) + model.run() + model.post_process() diff --git a/examples/12_ammonia_synloop/test_inputs.csv b/examples/12_ammonia_synloop/test_inputs.csv deleted file mode 100644 index 45aa97b8f..000000000 --- a/examples/12_ammonia_synloop/test_inputs.csv +++ /dev/null @@ -1,5 +0,0 @@ -Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Float Test,Int Test,Bool Test,Str Test -technologies,electrolyzer,model_inputs,shared_parameters,electrolyzer_capex,,float,1000,1295,1295,1295 -technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,1,2,1,1 -technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,TRUE,FALSE,TRUE -technologies,electrolyzer,model_inputs,performance_parameters,sizing,size_for,str,BOL,BOL,BOL,EOL diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py index 69c8624fe..ab58a292f 100644 --- a/h2integrate/tools/run_cases.py +++ b/h2integrate/tools/run_cases.py @@ -16,9 +16,15 @@ def cast_by_name(type_name, value): The value in the specified data type """ + + bool_map = {"true": True, "false": False, "yes": True, "no": False, "1": True, "0": False} + trusted_types = ["int", "float", "bool", "str"] ## others as needed if type_name in trusted_types: - return __builtins__[type_name](value) + if type_name == "bool": + return bool_map.get(value.lower()) + else: + return __builtins__[type_name](value) else: msg = f"Specified data type {type_name} invalid, must be one of {trusted_types}" raise TypeError(msg) @@ -91,6 +97,8 @@ def load_tech_config_cases(case_file): If some parameters are nested deeper than others, make as many Index columns for the deepest- nested parameters and leave any unused Indexes blank. + + See example .csv in h2integrate/tools/test/test_inputs.csv """ tech_config_cases = pd.read_csv(case_file) column_names = tech_config_cases.columns.values diff --git a/h2integrate/tools/test/test_inputs.csv b/h2integrate/tools/test/test_inputs.csv index 45aa97b8f..165cfdfc7 100644 --- a/h2integrate/tools/test/test_inputs.csv +++ b/h2integrate/tools/test/test_inputs.csv @@ -1,5 +1,5 @@ Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Float Test,Int Test,Bool Test,Str Test -technologies,electrolyzer,model_inputs,shared_parameters,electrolyzer_capex,,float,1000,1295,1295,1295 -technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,1,2,1,1 +technologies,steel,model_inputs,cost_parameters,feedstocks,iron_ore_pellet_unitcost,float,103.675,207.35,207.35,207.35 +technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,18,20,18,18 technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,TRUE,FALSE,TRUE -technologies,electrolyzer,model_inputs,performance_parameters,sizing,size_for,str,BOL,BOL,BOL,EOL +technologies,h2_storage,model_inputs,performance_parameters,type,,str,lined_rock_cavern,lined_rock_cavern,lined_rock_cavern,salt_cavern diff --git a/h2integrate/tools/test/test_tools.py b/h2integrate/tools/test/test_tools.py index 89aef96ed..a550cc63d 100644 --- a/h2integrate/tools/test/test_tools.py +++ b/h2integrate/tools/test/test_tools.py @@ -1,5 +1,8 @@ +import os from pathlib import Path +import pytest + from h2integrate import EXAMPLE_DIR from h2integrate.tools.run_cases import modify_tech_config, load_tech_config_cases from h2integrate.core.h2integrate_model import H2IntegrateModel @@ -11,8 +14,9 @@ def test_tech_config_modifier(subtests): """ # Make an H2I model from the 01 example - orig_highlevel_yaml = EXAMPLE_DIR / "01_onshore_steel_mn" / "01_onshore_steel_mn.yaml" - model = H2IntegrateModel(orig_highlevel_yaml) + os.chdir(EXAMPLE_DIR / "01_onshore_steel_mn") + example_yaml = "01_onshore_steel_mn.yaml" + model = H2IntegrateModel(example_yaml) # Modify using csv case_file = Path(__file__).resolve().parent / "test_inputs.csv" @@ -21,3 +25,19 @@ def test_tech_config_modifier(subtests): case = cases["Float Test"] model = modify_tech_config(model, case) model.run() + assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1036.6771578253597 + with subtests.test("bool"): + case = cases["Bool Test"] + model = modify_tech_config(model, case) + model.run() + assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1214.1874646965953 + with subtests.test("int"): + case = cases["Int Test"] + model = modify_tech_config(model, case) + model.run() + assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1190.9501717506432 + with subtests.test("str"): + case = cases["Str Test"] + model = modify_tech_config(model, case) + model.run() + assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1208.0782566057335 From 60b584d9c782e5929bc148dd0e38d9e5eac40cca Mon Sep 17 00:00:00 2001 From: jmartin4 Date: Thu, 2 Oct 2025 10:52:36 -0600 Subject: [PATCH 6/8] Docs --- CHANGELOG.md | 1 + docs/_toc.yml | 1 + .../how_to_run_several_cases_in_sequence.md | 66 +++++++++++++++++++ examples/12_ammonia_synloop/hb_inputs.csv | 9 ++- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 docs/user_guide/how_to_run_several_cases_in_sequence.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e80271e5..0b19acd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Added variable O&M to `CostModelBaseClass` and integrated into finance-related models - Added generic storage model, useful for battery, hydrogen, CO2, or other resource storage. - Added wind resource model, API baseclasses, updated examples, and documentation. +- Added `tools/run_cases.py` with tools to run different `tech_config` cases from a spreadsheet, with new docs page to describe: docs/user_guide/how_to_run_several_cases_in_sequence.md ## 0.3.0 [May 2 2025] diff --git a/docs/_toc.yml b/docs/_toc.yml index d118e9157..5811b0f2b 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -17,6 +17,7 @@ parts: - file: user_guide/design_optimization_in_h2i - file: user_guide/postprocessing_results - file: user_guide/how_to_interface_with_user_defined_model + - file: user_guide/how_to_run_several_cases_in_sequence - file: user_guide/specifying_finance_parameters sections: - file: user_guide/cost_years diff --git a/docs/user_guide/how_to_run_several_cases_in_sequence.md b/docs/user_guide/how_to_run_several_cases_in_sequence.md new file mode 100644 index 000000000..5bfdbdfc9 --- /dev/null +++ b/docs/user_guide/how_to_run_several_cases_in_sequence.md @@ -0,0 +1,66 @@ +# Running several cases in sequence + +## Overview + +If the user has several different cases they want to run with different input parameters, these cases can be set up in an input spreadsheet rather than directly modifying the `tech_config`. +This is done using the functions in `h2integrate/tools/run_cases.py` + +## Setting up a variation of parameters in a .csv + +To set up the inputs for each case, the input .csv should be set up like so: +| Index 0 | Index 1 |...| Index N | Type | | |...| | +|--------------|----------------|---|----------------|-------|-----------------|-----------------|---|-----------------| +| technologies | |...| | float | | |...| | +| technologies | |...| | str | | |...| | +| ... | ... |...| ... | ... | ... | ... |...| ... | +| technologies | |...| | bool | | |...| | + + +## Example: Two different sizes of Haber Bosch ammonia plant + +To demonstrate this capability, we include a short example that modifies the size and hydrogen storage type for a Haber Bosch ammonia plant in `examples/12_ammonia_synloop`. +The example spreadsheet `hb_inputs.csv` shows the format: +|Index 0|Index 1|Index 2|Index 3|Index 4|Index 5|Type|Haber Bosch Big|Haber Bosch Small +|--- |--- |--- |--- |--- |--- |--- |--- |--- | +technologies|ammonia|model_inputs|shared_parameters|production_capacity||float|100000|10000 +technologies|h2_storage|model_inputs|performance_parameters|type||str|salt_cavern|lined_rock_cavern +technologies|electrolyzer|model_inputs|performance_parameters|n_clusters||int|16|2 +technologies|electrolyzer|model_inputs|performance_parameters|include_degradation_penalty||bool|TRUE|FALSE +technologies|electrolyzer|model_inputs|financial_parameters|capital_items|replacement_cost_pecent|float|0.15|0.25 + +Things to note about this format: +- The nested depth of the parameters in the `tech_config` is variable. If some parameters do not use as many levels, leave the unused levels blank (like the Index 5 level for most parameters in the above example) +- The currently available data types for each parameter are `float`, `str`, `int`, and `bool`. Be sure to specify the correct data type for each parameter. +- For parameters delcared as `bool`, you can enter "0", "false", or "no" for `False`, and "1", "true", or "yes" for `True` (case insensitive). When making .csvs in Excel, the default "TRUE" and "FALSE" formatting will work. +- All other parameters not included in the spreadsheet, their values will be kept the same as originally defined in the `tech_config.yaml`. + +The variation of parameters can be run by first creating an H2I model (with a `tech_config.yaml`), then modifying only the `tech_config` values that need to change. +First, the spreadsheet with each case is loaded into a DataFrame using `load_tech_config_cases`. +Then, in a loop, individual cases are selected and the model is modified to use these parameters using `modify_tech_config`. +An example is shown in `run_ammonia_synloop.py`: + +``` +from pathlib import Path + +from h2integrate.tools.run_cases import modify_tech_config, load_tech_config_cases +from h2integrate.core.h2integrate_model import H2IntegrateModel + + +# Create a H2Integrate model +model = H2IntegrateModel("12_ammonia_synloop.yaml") + +# Load cases +case_file = Path("hb_inputs.csv") +cases = load_tech_config_cases(case_file) + +# Modify and run the model for different cases +caselist = [ + "Haber Bosch Big", + "Haber Bosch Small", +] +for casename in caselist: + case = cases[casename] + model = modify_tech_config(model, case) + model.run() + model.post_process() +``` diff --git a/examples/12_ammonia_synloop/hb_inputs.csv b/examples/12_ammonia_synloop/hb_inputs.csv index 0082988ff..b8e770e59 100644 --- a/examples/12_ammonia_synloop/hb_inputs.csv +++ b/examples/12_ammonia_synloop/hb_inputs.csv @@ -1,3 +1,6 @@ -Index 0,Index 1,Index 2,Index 3,Index 4,Type,Haber Bosch Big,Haber Bosch Small -technologies,ammonia,model_inputs,shared_parameters,production_capacity,float,100000,10000 -technologies,h2_storage,model_inputs,performance_parameters,type,str,salt_cavern,lined_rock_cavern +Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Haber Bosch Big,Haber Bosch Small +technologies,ammonia,model_inputs,shared_parameters,production_capacity,,float,100000,10000 +technologies,h2_storage,model_inputs,performance_parameters,type,,str,salt_cavern,lined_rock_cavern +technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,16,2 +technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,FALSE +technologies,electrolyzer,model_inputs,financial_parameters,capital_items,replacement_cost_pecent,float,0.15,0.25 From 2f8003682f8476a513687e461cd4e66e01d1075f Mon Sep 17 00:00:00 2001 From: John Jasa Date: Mon, 6 Oct 2025 20:50:12 -0600 Subject: [PATCH 7/8] Updated docs and tests --- .../how_to_run_several_cases_in_sequence.md | 17 ++++++++++------- examples/12_ammonia_synloop/hb_inputs.csv | 2 +- h2integrate/tools/run_cases.py | 17 ++++++++--------- h2integrate/tools/test/test_inputs.csv | 4 ++-- h2integrate/tools/test/test_tools.py | 18 +++++++++--------- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/user_guide/how_to_run_several_cases_in_sequence.md b/docs/user_guide/how_to_run_several_cases_in_sequence.md index 5bfdbdfc9..fc931b080 100644 --- a/docs/user_guide/how_to_run_several_cases_in_sequence.md +++ b/docs/user_guide/how_to_run_several_cases_in_sequence.md @@ -2,7 +2,7 @@ ## Overview -If the user has several different cases they want to run with different input parameters, these cases can be set up in an input spreadsheet rather than directly modifying the `tech_config`. +If you have several different cases you want to run with different input parameters, these cases can be set up in an input spreadsheet rather than directly modifying the `tech_config`. This is done using the functions in `h2integrate/tools/run_cases.py` ## Setting up a variation of parameters in a .csv @@ -20,22 +20,25 @@ To set up the inputs for each case, the input .csv should be set up like so: To demonstrate this capability, we include a short example that modifies the size and hydrogen storage type for a Haber Bosch ammonia plant in `examples/12_ammonia_synloop`. The example spreadsheet `hb_inputs.csv` shows the format: + |Index 0|Index 1|Index 2|Index 3|Index 4|Index 5|Type|Haber Bosch Big|Haber Bosch Small |--- |--- |--- |--- |--- |--- |--- |--- |--- | technologies|ammonia|model_inputs|shared_parameters|production_capacity||float|100000|10000 technologies|h2_storage|model_inputs|performance_parameters|type||str|salt_cavern|lined_rock_cavern technologies|electrolyzer|model_inputs|performance_parameters|n_clusters||int|16|2 technologies|electrolyzer|model_inputs|performance_parameters|include_degradation_penalty||bool|TRUE|FALSE -technologies|electrolyzer|model_inputs|financial_parameters|capital_items|replacement_cost_pecent|float|0.15|0.25 +technologies|electrolyzer|model_inputs|financial_parameters|capital_items|replacement_cost_percent|float|0.15|0.25 + + +### Things to note about this format -Things to note about this format: -- The nested depth of the parameters in the `tech_config` is variable. If some parameters do not use as many levels, leave the unused levels blank (like the Index 5 level for most parameters in the above example) +- The nested depth of the parameters in the `tech_config` can vary based on the parameter you're setting. If some parameters do not use as many levels, leave the unused levels blank (like the Index 5 level for most parameters in the above example) - The currently available data types for each parameter are `float`, `str`, `int`, and `bool`. Be sure to specify the correct data type for each parameter. -- For parameters delcared as `bool`, you can enter "0", "false", or "no" for `False`, and "1", "true", or "yes" for `True` (case insensitive). When making .csvs in Excel, the default "TRUE" and "FALSE" formatting will work. -- All other parameters not included in the spreadsheet, their values will be kept the same as originally defined in the `tech_config.yaml`. +- For parameters declared as `bool`, you can enter "0", "false", or "no" for `False`, and "1", "true", or "yes" for `True` (case insensitive). When making .csvs in Excel, the default "TRUE" and "FALSE" formatting will work. +- For all other parameters not included in the spreadsheet, their values will be kept the same as originally defined in the `tech_config.yaml`. The variation of parameters can be run by first creating an H2I model (with a `tech_config.yaml`), then modifying only the `tech_config` values that need to change. -First, the spreadsheet with each case is loaded into a DataFrame using `load_tech_config_cases`. +First, the spreadsheet with each case is loaded into a Pandas DataFrame using `load_tech_config_cases`. Then, in a loop, individual cases are selected and the model is modified to use these parameters using `modify_tech_config`. An example is shown in `run_ammonia_synloop.py`: diff --git a/examples/12_ammonia_synloop/hb_inputs.csv b/examples/12_ammonia_synloop/hb_inputs.csv index b8e770e59..20ca4db70 100644 --- a/examples/12_ammonia_synloop/hb_inputs.csv +++ b/examples/12_ammonia_synloop/hb_inputs.csv @@ -3,4 +3,4 @@ technologies,ammonia,model_inputs,shared_parameters,production_capacity,,float,1 technologies,h2_storage,model_inputs,performance_parameters,type,,str,salt_cavern,lined_rock_cavern technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,16,2 technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,FALSE -technologies,electrolyzer,model_inputs,financial_parameters,capital_items,replacement_cost_pecent,float,0.15,0.25 +technologies,electrolyzer,model_inputs,financial_parameters,capital_items,replacement_cost_percent,float,0.15,0.25 diff --git a/h2integrate/tools/run_cases.py b/h2integrate/tools/run_cases.py index ab58a292f..65ef7e197 100644 --- a/h2integrate/tools/run_cases.py +++ b/h2integrate/tools/run_cases.py @@ -14,7 +14,6 @@ def cast_by_name(type_name, value): Returns: The value in the specified data type - """ bool_map = {"true": True, "false": False, "yes": True, "no": False, "1": True, "0": False} @@ -30,12 +29,12 @@ def cast_by_name(type_name, value): raise TypeError(msg) -def getFromDict(dataDict, mapList): +def get_from_dict(dataDict, mapList): """Get value from nested dictionary using a list of keys. Allows for programmatic calling of items in a nested dict using a variable-length list. Instead of dataDict[item1][item2][item3][item4][item5], you can use - getFromDict(dataDict, [item1, item2, item3, item4, item5]). + get_from_dict(dataDict, [item1, item2, item3, item4, item5]). Args: dataDict (dict): The nested dictionary to access. @@ -46,18 +45,18 @@ def getFromDict(dataDict, mapList): Example: >>> data = {"a": {"b": {"c": 42}}} - >>> getFromDict(data, ["a", "b", "c"]) + >>> get_from_dict(data, ["a", "b", "c"]) 42 """ return reduce(operator.getitem, mapList, dataDict) -def setInDict(dataDict, mapList, value): +def set_in_dict(dataDict, mapList, value): """Set value in nested dictionary using a list of keys. Allows for programmatic setting of items in a nested dict using a variable-length list. Instead of dataDict[item1][item2][item3][item4][item5] = value, you can use - setInDict(dataDict, [item1, item2, item3, item4, item5], value). + set_in_dict(dataDict, [item1, item2, item3, item4, item5], value). Args: dataDict (dict): The nested dictionary to modify. @@ -66,11 +65,11 @@ def setInDict(dataDict, mapList, value): Example: >>> data = {"a": {"b": {}}} - >>> setInDict(data, ["a", "b", "c"], 42) + >>> set_in_dict(data, ["a", "b", "c"], 42) >>> data["a"]["b"]["c"] 42 """ - getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value + get_from_dict(dataDict, mapList[:-1])[mapList[-1]] = value def load_tech_config_cases(case_file): @@ -127,6 +126,6 @@ def modify_tech_config(h2i_model, tech_config_case): # Remove nans from blank index fields while type(index_list[-1]) is not str: index_list = index_list[:-1] - setInDict(h2i_model.technology_config, index_list, cast_by_name(data_type, value)) + set_in_dict(h2i_model.technology_config, index_list, cast_by_name(data_type, value)) return h2i_model diff --git a/h2integrate/tools/test/test_inputs.csv b/h2integrate/tools/test/test_inputs.csv index 165cfdfc7..8194d3a11 100644 --- a/h2integrate/tools/test/test_inputs.csv +++ b/h2integrate/tools/test/test_inputs.csv @@ -1,5 +1,5 @@ Index 0,Index 1,Index 2,Index 3,Index 4,Index 5,Type,Float Test,Int Test,Bool Test,Str Test -technologies,steel,model_inputs,cost_parameters,feedstocks,iron_ore_pellet_unitcost,float,103.675,207.35,207.35,207.35 +technologies,solar,model_inputs,performance_parameters,pv_capacity_kWdc,,float,100000.,150000.,150000.,150000. technologies,electrolyzer,model_inputs,performance_parameters,n_clusters,,int,18,20,18,18 technologies,electrolyzer,model_inputs,performance_parameters,include_degradation_penalty,,bool,TRUE,TRUE,FALSE,TRUE -technologies,h2_storage,model_inputs,performance_parameters,type,,str,lined_rock_cavern,lined_rock_cavern,lined_rock_cavern,salt_cavern +technologies,electrolyzer,model_inputs,performance_parameters,sizing,size_for,str,BOL,BOL,BOL,EOL diff --git a/h2integrate/tools/test/test_tools.py b/h2integrate/tools/test/test_tools.py index a550cc63d..62b2b8aa3 100644 --- a/h2integrate/tools/test/test_tools.py +++ b/h2integrate/tools/test/test_tools.py @@ -9,13 +9,13 @@ def test_tech_config_modifier(subtests): - """Test cases for modifiying and running tech_config from csv. - Using 01 example as test case + """Test cases for modifying and running tech_config from csv. + Using 15 example as test case """ - # Make an H2I model from the 01 example - os.chdir(EXAMPLE_DIR / "01_onshore_steel_mn") - example_yaml = "01_onshore_steel_mn.yaml" + # Make an H2I model from the 15 example + os.chdir(EXAMPLE_DIR / "15_wind_solar_electrolyzer") + example_yaml = "15_wind_solar_electrolyzer.yaml" model = H2IntegrateModel(example_yaml) # Modify using csv @@ -25,19 +25,19 @@ def test_tech_config_modifier(subtests): case = cases["Float Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1036.6771578253597 + assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.327792370180044 with subtests.test("bool"): case = cases["Bool Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1214.1874646965953 + assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.226443205147294 with subtests.test("int"): case = cases["Int Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1190.9501717506432 + assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.4601971211592115 with subtests.test("str"): case = cases["Str Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("steel.LCOS")[0], rel=1e-3) == 1208.0782566057335 + assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.226443205147294 From 8a756919e5a3365a13c243f2c4f7ce749ca8c99d Mon Sep 17 00:00:00 2001 From: John Jasa Date: Mon, 6 Oct 2025 21:22:40 -0600 Subject: [PATCH 8/8] pre-commit fix --- h2integrate/tools/test/test_tools.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/h2integrate/tools/test/test_tools.py b/h2integrate/tools/test/test_tools.py index 62b2b8aa3..1221a84df 100644 --- a/h2integrate/tools/test/test_tools.py +++ b/h2integrate/tools/test/test_tools.py @@ -25,19 +25,31 @@ def test_tech_config_modifier(subtests): case = cases["Float Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.327792370180044 + assert ( + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) + == 5.327792370180044 + ) with subtests.test("bool"): case = cases["Bool Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.226443205147294 + assert ( + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) + == 5.226443205147294 + ) with subtests.test("int"): case = cases["Int Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.4601971211592115 + assert ( + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) + == 5.4601971211592115 + ) with subtests.test("str"): case = cases["Str Test"] model = modify_tech_config(model, case) model.run() - assert pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 5.226443205147294 + assert ( + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) + == 5.22644320514729 + )