From 3634e442642629657a226174ee5c13a5ea75c5d5 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Thu, 4 Sep 2025 08:37:42 -0500 Subject: [PATCH 01/67] attempt at subgroups --- h2integrate/core/h2integrate_model.py | 350 ++++++++++++++++++-------- 1 file changed, 241 insertions(+), 109 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 12607ed8e..a1b9c7a57 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -272,143 +272,275 @@ def create_financial_model(self): """ Creates and configures the financial model for the plant. - Creates financial groups based on the technology configurations - and adds the appropriate financial components to each group. + - If subgroups are defined in finance_parameters: + * The first tuple in each subgroup is (commodity_type, finance_model). + * The remaining tuples list technologies belonging to that subgroup. + - If no subgroups are defined: + * A default subgroup is created with all technologies. + * commodity_type and finance_model are pulled from finance_parameters. """ if "finance_parameters" not in self.plant_config: return - # Create a dictionary to hold financial groups + subgroups = self.plant_config["finance_parameters"].get("subgroups", []) financial_groups = {} - # Loop through each technology and add it to the appropriate financial group - for tech_name, individual_tech_config in self.technology_config["technologies"].items(): - financial_group_id = individual_tech_config.get("financial_model", {}).get("group") - if financial_group_id is not None: - if financial_group_id not in financial_groups: - financial_groups[financial_group_id] = {} - financial_groups[financial_group_id][tech_name] = individual_tech_config + if not subgroups: + # --- Default behavior --- + commodity_type = self.plant_config["finance_parameters"].get("commodity_type") + finance_model_name = self.plant_config["finance_parameters"].get("finance_model") - # Find technologies not assigned to any financial group and add them to group "default" - all_grouped_techs = set() - for group in financial_groups.values(): - all_grouped_techs.update(group.keys()) + if not commodity_type or not finance_model_name: + raise ValueError( + "finance_parameters must define 'commodity_type' and 'finance_model' " + "if no subgroups are provided." + ) - for tech_name, tech_config in self.technology_config["technologies"].items(): - if tech_name not in all_grouped_techs: - if "default" not in financial_groups: - financial_groups["default"] = {} - financial_groups["default"][tech_name] = tech_config - - # Add each financial group to the plant - for group_id, tech_configs in financial_groups.items(): - commodity_types = [] - if "steel" in tech_configs: - commodity_types.append("steel") - if "electrolyzer" in tech_configs: - commodity_types.append("hydrogen") - if "methanol" in tech_configs: - commodity_types.append("methanol") - if "ammonia" in tech_configs: - commodity_types.append("ammonia") - if "air_separator" in tech_configs: - commodity_types.append("nitrogen") - if "geoh2" in tech_configs: - commodity_types.append("hydrogen") - if "doc" in tech_configs: - commodity_types.append("co2") - if "oae" in tech_configs: - commodity_types.append("co2") - for tech in electricity_producing_techs: - if tech in tech_configs: - commodity_types.append("electricity") - break - - # Steel, methanol provides their own financials - if any(c in commodity_types for c in ("steel", "methanol")): - continue - - # GeoH2 provides own financials - if "geoh2" in tech_configs: - continue - - if commodity_types == []: - continue + # Collect all technologies into one subgroup + all_techs = tuple(self.technology_config["technologies"].keys()) + subgroup = [(commodity_type, finance_model_name), all_techs] + subgroups = [subgroup] - financial_group = om.Group() + # --- Normal subgroup handling --- + for _idx, subgroup in enumerate(subgroups): + if not subgroup or not isinstance(subgroup[0], tuple) or len(subgroup[0]) < 2: + raise ValueError( + f"Invalid subgroup format: {subgroup}. " + f"Expected first tuple to be (commodity_type, finance_model)." + ) - # Determine all technologies that should be included across all - # commodity types for this group - all_included_techs = set() - for commodity_type in commodity_types: - if commodity_type not in ["steel", "methanol"]: # These handle their own financials - included_techs = self.get_included_technologies( - tech_configs, commodity_type, self.plant_config - ) - all_included_techs.update(included_techs) + commodity_type, finance_model_name = subgroup[0] - # Filter tech_configs to only include technologies that are in at least one stackup - filtered_tech_configs_for_capex = { - tech: config for tech, config in tech_configs.items() if tech in all_included_techs + # Remaining tuples = technologies + tech_names = [tech for tup in subgroup[1:] for tech in tup] + + tech_configs = { + tech: self.technology_config["technologies"][tech] + for tech in tech_names + if tech in self.technology_config["technologies"] } - # Add the ExecComp to the plant model - financial_group.add_subsystem( - "electricity_sum", ElectricitySumComp(tech_configs=filtered_tech_configs_for_capex) - ) + if not tech_configs: + raise ValueError( + f"Subgroup {subgroup} contains no valid technologies. " + f"Available techs: {list(self.technology_config['technologies'].keys())}" + ) + + financial_group = om.Group() + + if commodity_type == "electricity": + financial_group.add_subsystem( + "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) + ) - # Add adjusted capex component + # Add adjusted capex/opex adjusted_capex_opex_comp = AdjustedCapexOpexComp( driver_config=self.driver_config, - tech_config=filtered_tech_configs_for_capex, + tech_config=tech_configs, plant_config=self.plant_config, ) financial_group.add_subsystem( "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) - # Add profast components - for idx, commodity_type in enumerate(commodity_types): - # Get included technologies for this commodity type - included_techs = self.get_included_technologies( - tech_configs, commodity_type, self.plant_config - ) + # Add financial model component + fin_model = self.supported_models.get(finance_model_name) + if fin_model is None: + raise ValueError(f"Financial model '{finance_model_name}' not found.") - # Filter tech_configs to only include the technologies in the stackup - filtered_tech_configs = { - tech: config for tech, config in tech_configs.items() if tech in included_techs - } - - fin_model_name = self.plant_config["finance_parameters"].get("finance_model") - if isinstance(fin_model_name, list): - for model_name in fin_model_name: - fin_model = self.supported_models.get(model_name) - fin_comp = fin_model( - driver_config=self.driver_config, - tech_config=filtered_tech_configs, - plant_config=self.plant_config, - commodity_type=commodity_type, - ) - financial_group.add_subsystem( - f"{model_name}_{idx}", fin_comp, promotes=["*"] - ) - else: - fin_model = self.supported_models.get(fin_model_name) - fin_comp = fin_model( - driver_config=self.driver_config, - tech_config=filtered_tech_configs, - plant_config=self.plant_config, - commodity_type=commodity_type, - ) - financial_group.add_subsystem( - f"{fin_model_name}_{idx}", fin_comp, promotes=["*"] - ) + fin_comp = fin_model( + driver_config=self.driver_config, + tech_config=tech_configs, + plant_config=self.plant_config, + commodity_type=commodity_type, + ) - self.plant.add_subsystem(f"financials_group_{group_id}", financial_group) + financial_group.add_subsystem( + f"{finance_model_name}_{commodity_type}", fin_comp, promotes=["*"] + ) + + self.plant.add_subsystem(f"financials_group_{commodity_type}", financial_group) self.financial_groups = financial_groups + # for idx, subgroup in enumerate(subgroups): + # # First tuple = commodity type + # if not subgroup or not isinstance(subgroup[0], tuple): + # raise ValueError(f"Invalid subgroup format: {subgroup}") + + # commodity_type = subgroup[0][0] + + # # Remaining tuples = tech sets + # tech_names = [tech for tup in subgroup[1:] for tech in tup] + + # tech_configs = { + # tech: self.technology_config["technologies"][tech] + # for tech in tech_names + # if tech in self.technology_config["technologies"] + # } + + # if not tech_configs: + # raise ValueError( + # f"Subgroup {subgroup} contains no valid technologies. " + # f"Available techs: {list(self.technology_config['technologies'].keys())}" + # ) + + # # Add electricity sum if applicable + # if "electricity" in commodity_type: + # financial_group.add_subsystem( + # "electricity_sum", ElectricitySumComp(tech_configs=filtered_tech_configs) + # ) + + # # Loop through each technology and add it to the appropriate financial group + # for tech_name, individual_tech_config in self.technology_config["technologies"].items(): + # financial_group_id = individual_tech_config.get("financial_model", {}).get("group") + # if financial_group_id is not None: + # if financial_group_id not in financial_groups: + # financial_groups[financial_group_id] = {} + # financial_groups[financial_group_id][tech_name] = individual_tech_config + + # # Find technologies not assigned to any financial group and add them to group "default" + # all_grouped_techs = set() + # for group in financial_groups.values(): + # all_grouped_techs.update(group.keys()) + + # for tech_name, tech_config in self.technology_config["technologies"].items(): + # if tech_name not in all_grouped_techs: + # if "default" not in financial_groups: + # financial_groups["default"] = {} + # financial_groups["default"][tech_name] = tech_config + + # # Add each financial group to the plant + # for group_id, tech_configs in financial_groups.items(): + # commodity_types = [] + # if "steel" in tech_configs: + # commodity_types.append("steel") + # if "electrolyzer" in tech_configs: + # commodity_types.append("hydrogen") + # if "methanol" in tech_configs: + # commodity_types.append("methanol") + # if "ammonia" in tech_configs: + # commodity_types.append("ammonia") + # if "air_separator" in tech_configs: + # commodity_types.append("nitrogen") + # if "geoh2" in tech_configs: + # commodity_types.append("hydrogen") + # if "doc" in tech_configs: + # commodity_types.append("co2") + # if "oae" in tech_configs: + # commodity_types.append("co2") + # for tech in electricity_producing_techs: + # if tech in tech_configs: + # commodity_types.append("electricity") + # break + + # # Steel, methanol provides their own financials + # if any(c in commodity_types for c in ("steel", "methanol")): + # continue + + # # GeoH2 provides own financials + # if "geoh2" in tech_configs: + # continue + + # if commodity_types == []: + # continue + + # financial_group = om.Group() + + # # Determine all technologies that should be included across all + # # commodity types for this group + # all_included_techs = set() + # for commodity_type in commodity_types: + # # These handle their own financials + # if commodity_type not in ["steel", "methanol"]: + # included_techs = self.get_included_technologies( + # tech_configs, commodity_type, self.plant_config + # ) + # all_included_techs.update(included_techs) + + # # Filter tech_configs to only include technologies that are in at least one stackup + # filtered_tech_configs_for_capex = { + # tech: config for tech, config in tech_configs.items() + # if tech in all_included_techs + # } + + # # Add the ExecComp to the plant model + # financial_group.add_subsystem( + # "electricity_sum", ElectricitySumComp( + # tech_configs=filtered_tech_configs_for_capex) + # ) + + # # Add adjusted capex component + # adjusted_capex_opex_comp = AdjustedCapexOpexComp( + # driver_config=self.driver_config, + # tech_config=filtered_tech_configs_for_capex, + # plant_config=self.plant_config, + # ) + # financial_group.add_subsystem( + # "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] + # ) + + # # Add profast components + # for idx, commodity_type in enumerate(commodity_types): + # # Get included technologies for this commodity type + # included_techs = self.get_included_technologies( + # tech_configs, commodity_type, self.plant_config + # ) + + # # Filter tech_configs to only include the technologies in the stackup + # filtered_tech_configs = { + # tech: config for tech, config in tech_configs.items() if tech in + # included_techs + # } + + # fin_model_name = self.plant_config["finance_parameters"].get("finance_model") + # if isinstance(fin_model_name, list): + # for model_name in fin_model_name: + # fin_model = self.supported_models.get(model_name) + # fin_comp = fin_model( + # driver_config=self.driver_config, + # tech_config=filtered_tech_configs, + # plant_config=self.plant_config, + # commodity_type=commodity_type, + # ) + # financial_group.add_subsystem( + # f"{model_name}_{idx}", fin_comp, promotes=["*"] + # ) + # else: + # fin_model = self.supported_models.get(fin_model_name) + # fin_comp = fin_model( + # driver_config=self.driver_config, + # tech_config=filtered_tech_configs, + # plant_config=self.plant_config, + # commodity_type=commodity_type, + # ) + # financial_group.add_subsystem( + # f"{fin_model_name}_{idx}", fin_comp, promotes=["*"] + # ) + + # self.plant.add_subsystem(f"financials_group_{group_id}", financial_group) + + # self.financial_groups = financial_groups + + # def create_fianancial_subgroups(self,tech_config,plant_config): + # # This blends the financial_groups and get_included_technologies methods + # # The input will be a tuple in the yaml of the included technologies in the subgroup + # # in another tuple it will be the commodity_type for that subgroup + # financial_subgroups = {} + # for subgroup in plant_config["finance_parameters"].get("financial_subgroups", []): + # commodity_type, included_techs = subgroup + # self.plant.add_subsystem(f"financials_group_{commodity_type}", financial_group) + + # missing_techs = [tech for tech in included_techs if tech not in tech_config] + # if missing_techs: + # raise ValueError( + # f"Included technology(ies) {missing_techs} not found in tech_config. " + # f"Available techs: {list(tech_config.keys())}" + # ) + # financial_subgroups[commodity_type] = included_techs + # return financial_subgroups + def get_included_technologies(self, tech_config, commodity_type, plant_config): """ Determine which technologies should be included in the financial metrics. From b793d7cac928b2bc41b397a5cc62a4a798e2c029 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:18:31 -0600 Subject: [PATCH 02/67] updated create_financial_model for new subgroup setup --- h2integrate/core/h2integrate_model.py | 103 +++++++++++++++++++------- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index a1b9c7a57..664f3f42f 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -283,10 +283,10 @@ def create_financial_model(self): if "finance_parameters" not in self.plant_config: return - subgroups = self.plant_config["finance_parameters"].get("subgroups", []) + subgroups = self.plant_config["finance_parameters"].get("subgroups", None) financial_groups = {} - if not subgroups: + if subgroups is None: # --- Default behavior --- commodity_type = self.plant_config["finance_parameters"].get("commodity_type") finance_model_name = self.plant_config["finance_parameters"].get("finance_model") @@ -303,17 +303,19 @@ def create_financial_model(self): subgroups = [subgroup] # --- Normal subgroup handling --- - for _idx, subgroup in enumerate(subgroups): - if not subgroup or not isinstance(subgroup[0], tuple) or len(subgroup[0]) < 2: - raise ValueError( - f"Invalid subgroup format: {subgroup}. " - f"Expected first tuple to be (commodity_type, finance_model)." - ) - - commodity_type, finance_model_name = subgroup[0] - - # Remaining tuples = technologies - tech_names = [tech for tup in subgroup[1:] for tech in tup] + for subgroup_name, subgroup_params in subgroups.items(): + commodity_type = subgroup_params.get("commodity", None) + commodity_desc = subgroup_params.get("commodity_desc", "") + finance_model_nicknames = subgroup_params.get( + "finance_groups", ["default"] + ) # TODO: what if they only have one finance model + tech_names = subgroup_params.get("technologies") + if isinstance(finance_model_nicknames, str): + finance_model_nicknames = [finance_model_nicknames] + + # check commodity type + if commodity_type is None: + raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") tech_configs = { tech: self.technology_config["technologies"][tech] @@ -344,23 +346,70 @@ def create_financial_model(self): "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) - # Add financial model component - fin_model = self.supported_models.get(finance_model_name) - if fin_model is None: - raise ValueError(f"Financial model '{finance_model_name}' not found.") + for finance_model_nickname in finance_model_nicknames: + if any( + tech_name == finance_model_nickname + for tech_name, tech_params in tech_configs.items() + ): + tech_finance_model_name = ( + tech_configs.get(finance_model_nickname) + .get("financial_model", {}) + .get("model") + ) + tech_cost_model_name = ( + tech_configs.get(finance_model_nickname).get("cost_model", {}).get("model") + ) - fin_comp = fin_model( - driver_config=self.driver_config, - tech_config=tech_configs, - plant_config=self.plant_config, - commodity_type=commodity_type, - ) + # check for coupled cost/finance models and skip + if tech_cost_model_name != tech_finance_model_name: + fin_model = self.supported_models.get(tech_finance_model_name) + # use financial model from tech config + fin_comp = fin_model( + driver_config=self.driver_config, + tech_config=tech_configs[finance_model_nickname], + plant_config=self.plant_config, + ) + else: + finance_model_config = self.plant_config["finance_parameters"].get( + finance_model_nickname + ) + model_name = finance_model_config.get("finance_model") + fin_model_inputs = finance_model_config.get("model_inputs") - financial_group.add_subsystem( - f"{finance_model_name}_{commodity_type}", fin_comp, promotes=["*"] - ) + # Add financial model component + fin_model = self.supported_models.get(model_name) + + if fin_model is None: + raise ValueError(f"Financial model '{model_name}' not found.") + + finance_params = { + k: v + for k, v in self.plant_config["finance_parameters"].items() + if "finance_model" in v + } + + finance_params.update(fin_model_inputs) + plant_inputs = { + k: v for k, v in self.plant_config.items() if k != "finance_parameters" + } + filtered_plant_config = plant_inputs.update(finance_params) + + fin_comp = fin_model( + driver_config=self.driver_config, + tech_config=tech_configs, + plant_config=filtered_plant_config, + commodity_type=commodity_type, + description=commodity_desc, + ) + + finance_subsystem_name = ( + f"{model_name}_{commodity_type}" + if commodity_desc == "" + else f"{model_name}_{commodity_type}_{commodity_desc}" + ) + financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) - self.plant.add_subsystem(f"financials_group_{commodity_type}", financial_group) + self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_group) self.financial_groups = financial_groups From e019bf8308ceb2181031bb2933c9264f91c28619 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:19:51 -0600 Subject: [PATCH 03/67] updated profast financial to take in description --- h2integrate/core/profast_financial.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/h2integrate/core/profast_financial.py b/h2integrate/core/profast_financial.py index 59c73b2ad..18681e4b7 100644 --- a/h2integrate/core/profast_financial.py +++ b/h2integrate/core/profast_financial.py @@ -409,6 +409,7 @@ def initialize(self): self.options.declare("plant_config", types=dict) self.options.declare("tech_config", types=dict) self.options.declare("commodity_type", types=str) + self.options.declare("description", types=str, default="") def setup(self): if self.options["commodity_type"] == "electricity": @@ -418,7 +419,24 @@ def setup(self): commodity_units = "kg/year" lco_units = "USD/kg" - self.LCO_str = f"LCO{self.options['commodity_type'][0].upper()}" + if ( + self.options["description"] == "" + or self.options["description"] == self.options["commodity_type"] + ): + self.LCO_str = f"LCO{self.options['commodity_type'][0].upper()}" + else: + LCO_base_str = f"LCO{self.options['commodity_type'][0].upper()}" + LCO_desc_str = ( + self.options["description"] + .replace(self.options["commodity_type"], "") + .strip() + .strip("_()-") + ) + if LCO_desc_str == "": + self.LCO_str = LCO_base_str + else: + self.LCO_str = f"{LCO_base_str}_{LCO_desc_str}" + self.add_output(self.LCO_str, val=0.0, units=lco_units) if self.options["commodity_type"] == "co2": @@ -561,7 +579,11 @@ def compute(self, inputs, outputs): fdesc = self.options["plant_config"]["finance_parameters"]["model_inputs"][ "profast_output_description" ] - fname = f"{fdesc}_{self.options['commodity_type']}.yaml" + if self.options["description"] == "": + fname = f"{fdesc}_{self.options['commodity_type']}.yaml" + else: + fname = f"{fdesc}_{self.options['commodity_type']}_{self.LCO_str}.yaml" + fpath = Path(output_dir) / fname output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) From 33c528e4b11cfb159d6f619d5b649db7e2d6fa57 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:12:45 -0600 Subject: [PATCH 04/67] updated connect_technologies and example 1 and create_financial_model --- .../01_onshore_steel_mn/plant_config.yaml | 65 ++-- examples/01_onshore_steel_mn/tech_config.yaml | 3 +- h2integrate/core/h2integrate_model.py | 323 ++++++------------ h2integrate/core/profast_financial.py | 4 + 4 files changed, 146 insertions(+), 249 deletions(-) diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index 0f91c7225..2012440ca 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -27,7 +27,7 @@ technology_interconnections: [ ["hopp", "electrolyzer", "electricity", "cable"], ["electrolyzer", "h2_storage", "efficiency"], ["electrolyzer", "h2_storage", "hydrogen", "pipe"], - ["financials_group_default", "steel", "LCOH"], + ["financials_subgroup_hydrogen", "steel", ["LCOH_delivered","LCOH"]], ["hopp", "steel", "electricity", "cable"], # etc ] @@ -39,32 +39,47 @@ plant: hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + profast_model: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] wind: expected_plant_cost: 'none' - technologies_included_in_metrics: - LCOE: ["hopp"] - LCOH: ["hopp", "electrolyzer", "h2_storage"] + # technologies_included_in_metrics: + # LCOE: ["hopp"] + # LCOH: ["hopp", "electrolyzer", "h2_storage"] + subgroups: + electricity: + commodity: "electricity" + finance_groups: ["profast_model"] + technologies: ["hopp"] + hydrogen: + commodity: "hydrogen" + commodity_desc: "delivered" + finance_groups: ["profast_model"] + technologies: ["hopp","electrolyzer","h2_storage"] + steel: + commodity: "steel" + finance_groups: ["steel"] + technologies: ["steel"] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 diff --git a/examples/01_onshore_steel_mn/tech_config.yaml b/examples/01_onshore_steel_mn/tech_config.yaml index c508fd9d8..10dba7366 100644 --- a/examples/01_onshore_steel_mn/tech_config.yaml +++ b/examples/01_onshore_steel_mn/tech_config.yaml @@ -68,7 +68,8 @@ technologies: cost_model: model: "steel_cost" financial_model: - group: steel + model: "steel_cost" + # group: steel model_inputs: shared_parameters: capacity_factor: 0.9 diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 664f3f42f..e92a87550 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -241,17 +241,20 @@ def create_technology_models(self): if "model" in individual_tech_config["financial_model"]: financial_name = individual_tech_config["financial_model"]["model"] - financial_object = self.supported_models[financial_name] - tech_group.add_subsystem( - f"{tech_name}_financial", - financial_object( - driver_config=self.driver_config, - plant_config=self.plant_config, - tech_config=individual_tech_config, - ), - promotes=["*"], - ) - self.financial_models.append(financial_object) + if financial_name != individual_tech_config.get("cost_model", {}).get( + "model", "" + ): + financial_object = self.supported_models[financial_name] + tech_group.add_subsystem( + f"{tech_name}_financial", + financial_object( + driver_config=self.driver_config, + plant_config=self.plant_config, + tech_config=individual_tech_config, + ), + promotes=["*"], + ) + self.financial_models.append(financial_object) def _process_model(self, model_type, individual_tech_config, tech_group): # Generalized function to process model definitions @@ -286,6 +289,28 @@ def create_financial_model(self): subgroups = self.plant_config["finance_parameters"].get("subgroups", None) financial_groups = {} + default_finance_model_nickname = "default" + # only one finance model is being used with subgroups + if ( + "finance_model" in self.plant_config["finance_parameters"] + and "model_inputs" in self.plant_config["finance_parameters"] + ): + if default_finance_model_nickname in self.plant_config["finance_parameters"]: + default_finance_model_nickname = [ + f"default_{i}" + for i in range(5) + if f"default_{i}" not in self.plant_config["finance_parameters"] + ][0] + default_model_name = self.plant_config["finance_parameters"].pop("finance_model") + default_model_inputs = self.plant_config["finance_parameters"].pop("model_inputs") + default_model_dict = { + default_finance_model_nickname: { + "finance_model": default_model_name, + "model_inputs": default_model_inputs, + } + } + self.plant_config["finance_parameters"].update({default_model_dict}) + if subgroups is None: # --- Default behavior --- commodity_type = self.plant_config["finance_parameters"].get("commodity_type") @@ -297,19 +322,29 @@ def create_financial_model(self): "if no subgroups are provided." ) + # NOTE: should we group all the technologies no? # Collect all technologies into one subgroup - all_techs = tuple(self.technology_config["technologies"].keys()) - subgroup = [(commodity_type, finance_model_name), all_techs] - subgroups = [subgroup] - + all_techs = list(self.technology_config["technologies"].keys()) + subgroup = { + "commodity": commodity_type, + "finance_groups": [default_finance_model_nickname], + "technologies": all_techs, + } + subgroups = {"all": subgroup} + # for tech_name, tech_config in self.technology_config["technologies"].items(): + # if tech_name not in all_grouped_techs: + # if "default" not in financial_groups: + # financial_groups["default"] = {} + # financial_groups["default"][tech_name] = tech_config # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): commodity_type = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") finance_model_nicknames = subgroup_params.get( - "finance_groups", ["default"] + "finance_groups", [default_finance_model_nickname] ) # TODO: what if they only have one finance model tech_names = subgroup_params.get("technologies") + if isinstance(finance_model_nicknames, str): finance_model_nicknames = [finance_model_nicknames] @@ -329,12 +364,21 @@ def create_financial_model(self): f"Available techs: {list(self.technology_config['technologies'].keys())}" ) + financial_groups.update( + { + subgroup_name: { + "tech_configs": tech_configs, + "commodity": commodity_type, + } + } + ) financial_group = om.Group() - if commodity_type == "electricity": - financial_group.add_subsystem( - "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) - ) + # any(k in electricity_producing_techs for k in self.technology_config["technologies"]) + # TODO: dont add this subsystem unless required + financial_group.add_subsystem( + "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) + ) # Add adjusted capex/opex adjusted_capex_opex_comp = AdjustedCapexOpexComp( @@ -342,11 +386,14 @@ def create_financial_model(self): tech_config=tech_configs, plant_config=self.plant_config, ) + financial_group.add_subsystem( "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) for finance_model_nickname in finance_model_nicknames: + # check if using tech-specific finance model + skip_model = False if any( tech_name == finance_model_nickname for tech_name, tech_params in tech_configs.items() @@ -366,9 +413,11 @@ def create_financial_model(self): # use financial model from tech config fin_comp = fin_model( driver_config=self.driver_config, - tech_config=tech_configs[finance_model_nickname], + tech_config=tech_configs[finance_model_nickname].get("model_inputs"), plant_config=self.plant_config, ) + else: + skip_model = True else: finance_model_config = self.plant_config["finance_parameters"].get( finance_model_nickname @@ -382,17 +431,17 @@ def create_financial_model(self): if fin_model is None: raise ValueError(f"Financial model '{model_name}' not found.") - finance_params = { - k: v - for k, v in self.plant_config["finance_parameters"].items() - if "finance_model" in v - } - - finance_params.update(fin_model_inputs) - plant_inputs = { + filtered_plant_config = { k: v for k, v in self.plant_config.items() if k != "finance_parameters" } - filtered_plant_config = plant_inputs.update(finance_params) + + filtered_plant_config.update( + { + "finance_parameters": { + "model_inputs": fin_model_inputs, + } + } + ) fin_comp = fin_model( driver_config=self.driver_config, @@ -402,194 +451,19 @@ def create_financial_model(self): description=commodity_desc, ) + if not skip_model: finance_subsystem_name = ( - f"{model_name}_{commodity_type}" + f"{finance_model_nickname}_{commodity_type}" if commodity_desc == "" - else f"{model_name}_{commodity_type}_{commodity_desc}" + else f"{finance_model_nickname}_{commodity_type}_{commodity_desc}" ) + financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_group) self.financial_groups = financial_groups - # for idx, subgroup in enumerate(subgroups): - # # First tuple = commodity type - # if not subgroup or not isinstance(subgroup[0], tuple): - # raise ValueError(f"Invalid subgroup format: {subgroup}") - - # commodity_type = subgroup[0][0] - - # # Remaining tuples = tech sets - # tech_names = [tech for tup in subgroup[1:] for tech in tup] - - # tech_configs = { - # tech: self.technology_config["technologies"][tech] - # for tech in tech_names - # if tech in self.technology_config["technologies"] - # } - - # if not tech_configs: - # raise ValueError( - # f"Subgroup {subgroup} contains no valid technologies. " - # f"Available techs: {list(self.technology_config['technologies'].keys())}" - # ) - - # # Add electricity sum if applicable - # if "electricity" in commodity_type: - # financial_group.add_subsystem( - # "electricity_sum", ElectricitySumComp(tech_configs=filtered_tech_configs) - # ) - - # # Loop through each technology and add it to the appropriate financial group - # for tech_name, individual_tech_config in self.technology_config["technologies"].items(): - # financial_group_id = individual_tech_config.get("financial_model", {}).get("group") - # if financial_group_id is not None: - # if financial_group_id not in financial_groups: - # financial_groups[financial_group_id] = {} - # financial_groups[financial_group_id][tech_name] = individual_tech_config - - # # Find technologies not assigned to any financial group and add them to group "default" - # all_grouped_techs = set() - # for group in financial_groups.values(): - # all_grouped_techs.update(group.keys()) - - # for tech_name, tech_config in self.technology_config["technologies"].items(): - # if tech_name not in all_grouped_techs: - # if "default" not in financial_groups: - # financial_groups["default"] = {} - # financial_groups["default"][tech_name] = tech_config - - # # Add each financial group to the plant - # for group_id, tech_configs in financial_groups.items(): - # commodity_types = [] - # if "steel" in tech_configs: - # commodity_types.append("steel") - # if "electrolyzer" in tech_configs: - # commodity_types.append("hydrogen") - # if "methanol" in tech_configs: - # commodity_types.append("methanol") - # if "ammonia" in tech_configs: - # commodity_types.append("ammonia") - # if "air_separator" in tech_configs: - # commodity_types.append("nitrogen") - # if "geoh2" in tech_configs: - # commodity_types.append("hydrogen") - # if "doc" in tech_configs: - # commodity_types.append("co2") - # if "oae" in tech_configs: - # commodity_types.append("co2") - # for tech in electricity_producing_techs: - # if tech in tech_configs: - # commodity_types.append("electricity") - # break - - # # Steel, methanol provides their own financials - # if any(c in commodity_types for c in ("steel", "methanol")): - # continue - - # # GeoH2 provides own financials - # if "geoh2" in tech_configs: - # continue - - # if commodity_types == []: - # continue - - # financial_group = om.Group() - - # # Determine all technologies that should be included across all - # # commodity types for this group - # all_included_techs = set() - # for commodity_type in commodity_types: - # # These handle their own financials - # if commodity_type not in ["steel", "methanol"]: - # included_techs = self.get_included_technologies( - # tech_configs, commodity_type, self.plant_config - # ) - # all_included_techs.update(included_techs) - - # # Filter tech_configs to only include technologies that are in at least one stackup - # filtered_tech_configs_for_capex = { - # tech: config for tech, config in tech_configs.items() - # if tech in all_included_techs - # } - - # # Add the ExecComp to the plant model - # financial_group.add_subsystem( - # "electricity_sum", ElectricitySumComp( - # tech_configs=filtered_tech_configs_for_capex) - # ) - - # # Add adjusted capex component - # adjusted_capex_opex_comp = AdjustedCapexOpexComp( - # driver_config=self.driver_config, - # tech_config=filtered_tech_configs_for_capex, - # plant_config=self.plant_config, - # ) - # financial_group.add_subsystem( - # "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] - # ) - - # # Add profast components - # for idx, commodity_type in enumerate(commodity_types): - # # Get included technologies for this commodity type - # included_techs = self.get_included_technologies( - # tech_configs, commodity_type, self.plant_config - # ) - - # # Filter tech_configs to only include the technologies in the stackup - # filtered_tech_configs = { - # tech: config for tech, config in tech_configs.items() if tech in - # included_techs - # } - - # fin_model_name = self.plant_config["finance_parameters"].get("finance_model") - # if isinstance(fin_model_name, list): - # for model_name in fin_model_name: - # fin_model = self.supported_models.get(model_name) - # fin_comp = fin_model( - # driver_config=self.driver_config, - # tech_config=filtered_tech_configs, - # plant_config=self.plant_config, - # commodity_type=commodity_type, - # ) - # financial_group.add_subsystem( - # f"{model_name}_{idx}", fin_comp, promotes=["*"] - # ) - # else: - # fin_model = self.supported_models.get(fin_model_name) - # fin_comp = fin_model( - # driver_config=self.driver_config, - # tech_config=filtered_tech_configs, - # plant_config=self.plant_config, - # commodity_type=commodity_type, - # ) - # financial_group.add_subsystem( - # f"{fin_model_name}_{idx}", fin_comp, promotes=["*"] - # ) - - # self.plant.add_subsystem(f"financials_group_{group_id}", financial_group) - - # self.financial_groups = financial_groups - - # def create_fianancial_subgroups(self,tech_config,plant_config): - # # This blends the financial_groups and get_included_technologies methods - # # The input will be a tuple in the yaml of the included technologies in the subgroup - # # in another tuple it will be the commodity_type for that subgroup - # financial_subgroups = {} - # for subgroup in plant_config["finance_parameters"].get("financial_subgroups", []): - # commodity_type, included_techs = subgroup - # self.plant.add_subsystem(f"financials_group_{commodity_type}", financial_group) - - # missing_techs = [tech for tech in included_techs if tech not in tech_config] - # if missing_techs: - # raise ValueError( - # f"Included technology(ies) {missing_techs} not found in tech_config. " - # f"Available techs: {list(tech_config.keys())}" - # ) - # financial_subgroups[commodity_type] = included_techs - # return financial_subgroups - def get_included_technologies(self, tech_config, commodity_type, plant_config): """ Determine which technologies should be included in the financial metrics. @@ -739,7 +613,9 @@ def connect_technologies(self): # same name if the cost and financial models are not None if "finance_parameters" in self.plant_config: # Connect the outputs of the technology models to the appropriate financial groups - for group_id, tech_configs in self.financial_groups.items(): + for group_id, group_configs in self.financial_groups.items(): + tech_configs = group_configs.get("tech_configs") + primary_commodity_type = group_configs.get("commodity") # Skip steel financials; it provides its own financials if any(c in tech_configs for c in ("steel", "methanol", "geoh2")): continue @@ -788,15 +664,15 @@ def connect_technologies(self): if tech_name in electricity_producing_techs and tech_name in all_included_techs: self.plant.connect( f"{tech_name}.electricity_out", - f"financials_group_{group_id}.electricity_sum.electricity_{tech_name}", + f"financials_subgroup_{group_id}.electricity_sum.electricity_{tech_name}", ) plant_producing_electricity = True - if plant_producing_electricity: + if plant_producing_electricity and primary_commodity_type == "electricity": # Connect total electricity produced to the financial group self.plant.connect( - f"financials_group_{group_id}.electricity_sum.total_electricity_produced", - f"financials_group_{group_id}.total_electricity_produced", + f"financials_subgroup_{group_id}.electricity_sum.total_electricity_produced", + f"financials_subgroup_{group_id}.total_electricity_produced", ) # Only connect technologies that are included in the financial stackup @@ -807,48 +683,49 @@ def connect_technologies(self): if tech_name in all_included_techs: self.plant.connect( - f"{tech_name}.CapEx", f"financials_group_{group_id}.capex_{tech_name}" + f"{tech_name}.CapEx", + f"financials_subgroup_{group_id}.capex_{tech_name}", ) self.plant.connect( - f"{tech_name}.OpEx", f"financials_group_{group_id}.opex_{tech_name}" + f"{tech_name}.OpEx", f"financials_subgroup_{group_id}.opex_{tech_name}" ) self.plant.connect( f"{tech_name}.cost_year", - f"financials_group_{group_id}.cost_year_{tech_name}", + f"financials_subgroup_{group_id}.cost_year_{tech_name}", ) if "electrolyzer" in tech_name: self.plant.connect( f"{tech_name}.total_hydrogen_produced", - f"financials_group_{group_id}.total_hydrogen_produced", + f"financials_subgroup_{group_id}.total_hydrogen_produced", ) self.plant.connect( f"{tech_name}.time_until_replacement", - f"financials_group_{group_id}.{tech_name}_time_until_replacement", + f"financials_subgroup_{group_id}.{tech_name}_time_until_replacement", ) if "ammonia" in tech_name: self.plant.connect( f"{tech_name}.total_ammonia_produced", - f"financials_group_{group_id}.total_ammonia_produced", + f"financials_subgroup_{group_id}.total_ammonia_produced", ) if "doc" in tech_name: self.plant.connect( f"{tech_name}.co2_capture_mtpy", - f"financials_group_{group_id}.co2_capture_kgpy", + f"financials_subgroup_{group_id}.co2_capture_kgpy", ) if "oae" in tech_name: self.plant.connect( f"{tech_name}.co2_capture_mtpy", - f"financials_group_{group_id}.co2_capture_kgpy", + f"financials_subgroup_{group_id}.co2_capture_kgpy", ) if "air_separator" in tech_name: self.plant.connect( f"{tech_name}.total_nitrogen_produced", - f"financials_group_{group_id}.total_nitrogen_produced", + f"financials_subgroup_{group_id}.total_nitrogen_produced", ) self.plant.options["auto_order"] = True diff --git a/h2integrate/core/profast_financial.py b/h2integrate/core/profast_financial.py index 18681e4b7..d3305040b 100644 --- a/h2integrate/core/profast_financial.py +++ b/h2integrate/core/profast_financial.py @@ -499,6 +499,9 @@ def compute(self, inputs, outputs): capacity = float(inputs[f"total_{self.options['commodity_type']}_produced"][0]) / 365.0 else: capacity = float(inputs["co2_capture_kgpy"]) / 365.0 + if capacity == 0.0: + raise ValueError("Capacity cannot be zero") + profast_params["capacity"] = capacity # TODO: update to actual daily capacity profast_params["long term utilization"] = 1 # TODO: update to capacity factor @@ -567,6 +570,7 @@ def compute(self, inputs, outputs): pf_dict["capital_items"] = capital_items pf_dict["fixed_costs"] = fixed_costs # create ProFAST object + pf = create_and_populate_profast(pf_dict) # simulate ProFAST sol, summary, price_breakdown = run_profast(pf) From f952a55842e220deec1a64c982008d9f6e78b20c Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:44:02 -0600 Subject: [PATCH 05/67] added other outputs to ProFastComp and updated example 1 test variable names --- h2integrate/core/profast_financial.py | 20 +++++++++++++++++++- tests/h2integrate/test_all_examples.py | 8 +++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/h2integrate/core/profast_financial.py b/h2integrate/core/profast_financial.py index d3305040b..09a77a6b0 100644 --- a/h2integrate/core/profast_financial.py +++ b/h2integrate/core/profast_financial.py @@ -424,6 +424,7 @@ def setup(self): or self.options["description"] == self.options["commodity_type"] ): self.LCO_str = f"LCO{self.options['commodity_type'][0].upper()}" + self.output_txt = self.options["commodity_type"].lower() else: LCO_base_str = f"LCO{self.options['commodity_type'][0].upper()}" LCO_desc_str = ( @@ -433,11 +434,23 @@ def setup(self): .strip("_()-") ) if LCO_desc_str == "": + self.output_txt = self.options["commodity_type"].lower() self.LCO_str = LCO_base_str else: + self.output_txt = f"{self.options['commodity_type'].lower()}_{LCO_desc_str}" self.LCO_str = f"{LCO_base_str}_{LCO_desc_str}" self.add_output(self.LCO_str, val=0.0, units=lco_units) + self.outputs_to_units = { + "wacc": "percent", + "crf": "percent", + "irr": "percent", + "profit_index": "unitless", + "investor_payback_period": "yr", + "price": lco_units, + } + for output_var, units in self.outputs_to_units.items(): + self.add_output(f"{output_var}_{self.output_txt}", val=0.0, units=units) if self.options["commodity_type"] == "co2": self.add_input("co2_capture_kgpy", val=0.0, units="kg/year") @@ -595,4 +608,9 @@ def compute(self, inputs, outputs): d = dict_to_yaml_formatting(d) write_yaml(d, fpath) - outputs[self.LCO_str] = sol["price"] # TODO: replace with sol['lco'] + outputs[self.LCO_str] = sol["lco"] + for output_var in self.outputs_to_units.keys(): + val = sol[output_var.replace("_", " ")] + if isinstance(val, (np.ndarray, list, tuple)): # only for IRR + val = val[-1] + outputs[f"{output_var}_{self.output_txt}"] = val diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index c2e55db7c..c3e24f4b7 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -24,7 +24,9 @@ def test_steel_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOH")[0], rel=1e-3) + pytest.approx( + model.prob.get_val("financials_subgroup_hydrogen.LCOH_delivered")[0], rel=1e-3 + ) == 7.47944016 ) @@ -34,7 +36,7 @@ def test_steel_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 5.10869916e09 ) @@ -42,7 +44,7 @@ def test_steel_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 96349901.77625626 ) From 01c294a7a4c4b58f601a755463d22e7ad566e82d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:55:35 -0600 Subject: [PATCH 06/67] updated docstring for ProFastComp outputs --- h2integrate/core/profast_financial.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/h2integrate/core/profast_financial.py b/h2integrate/core/profast_financial.py index 09a77a6b0..7d45d120d 100644 --- a/h2integrate/core/profast_financial.py +++ b/h2integrate/core/profast_financial.py @@ -388,6 +388,15 @@ class ProFastComp(om.ExplicitComponent): of the commodity). For example, LCOH if commodity_type is "hydrogen". If commodity_type is "hydrogen", "ammonia", "nitrogen", or "co2", the units are USD/kg. If the commodity type is "electricity", the units are USD/kWh. + wacc_ (float): weighted average cost of capital as a fraction. + crf_ (float): capital recovery factor as a fraction. + irr_ (float): internal rate of return as a fraction. + profit_index_ (float): + investor_payback_period_ (float): time until initial investment costs are + recovered in years. + price_ (float): first year price of commodity in same units as `LCOx` + + Methods: initialize(): Declares component options. From c8c23efc00dffc3b8a269d07db51a5dc7ae15504 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:14:44 -0600 Subject: [PATCH 07/67] moved commodity type from tech config keys to method and added commodity_units to supported models --- h2integrate/core/h2integrate_model.py | 27 +++++---------------------- h2integrate/core/supported_models.py | 10 ++++++++++ h2integrate/core/utilities.py | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index e92a87550..cc94a2416 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -6,7 +6,10 @@ import openmdao.api as om from h2integrate.core.finances import AdjustedCapexOpexComp -from h2integrate.core.utilities import create_xdsm_from_config +from h2integrate.core.utilities import ( + create_xdsm_from_config, + determine_commodity_types_from_technology_names, +) from h2integrate.core.feedstocks import FeedstockComponent from h2integrate.core.resource_summer import ElectricitySumComp from h2integrate.core.supported_models import supported_models, electricity_producing_techs @@ -623,27 +626,7 @@ def connect_technologies(self): plant_producing_electricity = False # Determine which commodity types this financial group handles - commodity_types = [] - if "steel" in tech_configs: - commodity_types.append("steel") - if "electrolyzer" in tech_configs: - commodity_types.append("hydrogen") - if "methanol" in tech_configs: - commodity_types.append("methanol") - if "ammonia" in tech_configs: - commodity_types.append("ammonia") - if "geoh2" in tech_configs: - commodity_types.append("hydrogen") - if "doc" in tech_configs: - commodity_types.append("co2") - if "air_separator" in tech_configs: - commodity_types.append("nitrogen") - if "oae" in tech_configs: - commodity_types.append("co2") - for tech in electricity_producing_techs: - if tech in tech_configs: - commodity_types.append("electricity") - break + commodity_types = determine_commodity_types_from_technology_names(tech_configs) # Get all included technologies for all commodity types in this group all_included_techs = set() diff --git a/h2integrate/core/supported_models.py b/h2integrate/core/supported_models.py index dcc14da95..63df4cbe3 100644 --- a/h2integrate/core/supported_models.py +++ b/h2integrate/core/supported_models.py @@ -138,3 +138,13 @@ } electricity_producing_techs = ["wind", "solar", "pv", "river", "hopp"] + +commodity_units = { + "hydrogen": "kg", + "steel": "t", # metric ton, 1000 kg + "ammonia": "kg", + "nitrogen": "kg", + "co2": "kg", + "electricity": "kW", + "water": "galUS", +} diff --git a/h2integrate/core/utilities.py b/h2integrate/core/utilities.py index c7b989c3a..8d368dd1f 100644 --- a/h2integrate/core/utilities.py +++ b/h2integrate/core/utilities.py @@ -5,6 +5,8 @@ import numpy as np from attrs import Attribute, field, define +from h2integrate.core.supported_models import electricity_producing_techs + try: from pyxdsm.XDSM import FUNC, XDSM @@ -285,3 +287,28 @@ def dict_to_yaml_formatting(orig_dict): else: orig_dict[key] = float(val) return orig_dict + + +def determine_commodity_types_from_technology_names(tech_configs): + commodity_types = [] + if "steel" in tech_configs: + commodity_types.append("steel") + if "electrolyzer" in tech_configs: + commodity_types.append("hydrogen") + if "methanol" in tech_configs: + commodity_types.append("methanol") + if "ammonia" in tech_configs: + commodity_types.append("ammonia") + if "geoh2" in tech_configs: + commodity_types.append("hydrogen") + if "doc" in tech_configs: + commodity_types.append("co2") + if "air_separator" in tech_configs: + commodity_types.append("nitrogen") + if "oae" in tech_configs: + commodity_types.append("co2") + for tech in electricity_producing_techs: + if tech in tech_configs: + commodity_types.append("electricity") + break + return commodity_types From 2acd9de319ee87363b32958fccf658b27693140a Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:32:06 -0600 Subject: [PATCH 08/67] updated logic for technology specific finance models --- h2integrate/core/h2integrate_model.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index cc94a2416..473979da4 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -406,20 +406,9 @@ def create_financial_model(self): .get("financial_model", {}) .get("model") ) - tech_cost_model_name = ( - tech_configs.get(finance_model_nickname).get("cost_model", {}).get("model") - ) - # check for coupled cost/finance models and skip - if tech_cost_model_name != tech_finance_model_name: - fin_model = self.supported_models.get(tech_finance_model_name) - # use financial model from tech config - fin_comp = fin_model( - driver_config=self.driver_config, - tech_config=tech_configs[finance_model_nickname].get("model_inputs"), - plant_config=self.plant_config, - ) - else: + # this is created in create_technologies() + if tech_finance_model_name is not None: skip_model = True else: finance_model_config = self.plant_config["finance_parameters"].get( @@ -716,7 +705,7 @@ def connect_technologies(self): # Check if there are any connections FROM a financial group to ammonia # This handles the case where LCOH is computed in the financial group and passed to ammonia for connection in technology_interconnections: - if connection[0].startswith("financials_group_") and connection[1] == "ammonia": + if connection[0].startswith("financials_subgroup_") and connection[1] == "ammonia": # If the connection is from a financial group, set solvers for the # plant to resolve the coupling self.plant.nonlinear_solver = om.NonlinearBlockGS() From 89efdc0e15ca2420977d076f364e315970ca53da Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:50:56 -0600 Subject: [PATCH 09/67] fixed circular import error and removed commodity units from supported models --- h2integrate/core/h2integrate_model.py | 6 ++++-- h2integrate/core/supported_models.py | 10 ---------- h2integrate/core/utilities.py | 4 +--- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index bbedbf4d9..dea037b54 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -312,7 +312,7 @@ def create_financial_model(self): "model_inputs": default_model_inputs, } } - self.plant_config["finance_parameters"].update({default_model_dict}) + self.plant_config["finance_parameters"].update(default_model_dict) if subgroups is None: # --- Default behavior --- @@ -617,7 +617,9 @@ def connect_technologies(self): plant_producing_electricity = False # Determine which commodity types this financial group handles - commodity_types = determine_commodity_types_from_technology_names(tech_configs) + commodity_types = determine_commodity_types_from_technology_names( + tech_configs, electricity_producing_techs + ) # Get all included technologies for all commodity types in this group all_included_techs = set() diff --git a/h2integrate/core/supported_models.py b/h2integrate/core/supported_models.py index dc49247ae..1d2dec768 100644 --- a/h2integrate/core/supported_models.py +++ b/h2integrate/core/supported_models.py @@ -142,13 +142,3 @@ } electricity_producing_techs = ["wind", "solar", "pv", "river", "hopp"] - -commodity_units = { - "hydrogen": "kg", - "steel": "t", # metric ton, 1000 kg - "ammonia": "kg", - "nitrogen": "kg", - "co2": "kg", - "electricity": "kW", - "water": "galUS", -} diff --git a/h2integrate/core/utilities.py b/h2integrate/core/utilities.py index 8d368dd1f..f7eecd17e 100644 --- a/h2integrate/core/utilities.py +++ b/h2integrate/core/utilities.py @@ -5,8 +5,6 @@ import numpy as np from attrs import Attribute, field, define -from h2integrate.core.supported_models import electricity_producing_techs - try: from pyxdsm.XDSM import FUNC, XDSM @@ -289,7 +287,7 @@ def dict_to_yaml_formatting(orig_dict): return orig_dict -def determine_commodity_types_from_technology_names(tech_configs): +def determine_commodity_types_from_technology_names(tech_configs, electricity_producing_techs): commodity_types = [] if "steel" in tech_configs: commodity_types.append("steel") From eb1e371c21732d91b4ac21aa231da64826d17468 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:56:08 -0600 Subject: [PATCH 10/67] created finance folder and updated imports --- h2integrate/core/h2integrate_model.py | 2 +- h2integrate/core/supported_models.py | 2 +- h2integrate/core/test/test_finances.py | 2 +- h2integrate/finances/__init__.py | 0 h2integrate/{core => finances}/finances.py | 0 h2integrate/{core => finances}/profast_financial.py | 0 6 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 h2integrate/finances/__init__.py rename h2integrate/{core => finances}/finances.py (100%) rename h2integrate/{core => finances}/profast_financial.py (100%) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index dea037b54..b33357620 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -5,12 +5,12 @@ import numpy as np import openmdao.api as om -from h2integrate.core.finances import AdjustedCapexOpexComp from h2integrate.core.utilities import ( create_xdsm_from_config, determine_commodity_types_from_technology_names, ) from h2integrate.core.feedstocks import FeedstockComponent +from h2integrate.finances.finances import AdjustedCapexOpexComp from h2integrate.core.resource_summer import ElectricitySumComp from h2integrate.core.supported_models import supported_models, electricity_producing_techs from h2integrate.core.inputs.validation import load_tech_yaml, load_plant_yaml, load_driver_yaml diff --git a/h2integrate/core/supported_models.py b/h2integrate/core/supported_models.py index 1d2dec768..c8e5264c9 100644 --- a/h2integrate/core/supported_models.py +++ b/h2integrate/core/supported_models.py @@ -2,8 +2,8 @@ from h2integrate.transporters.pipe import PipePerformanceModel from h2integrate.transporters.cable import CablePerformanceModel from h2integrate.converters.steel.steel import SteelPerformanceModel, SteelCostAndFinancialModel -from h2integrate.core.profast_financial import ProFastComp from h2integrate.converters.wind.wind_plant import WindPlantCostModel, WindPlantPerformanceModel +from h2integrate.finances.profast_financial import ProFastComp from h2integrate.converters.hopp.hopp_wrapper import HOPPComponent from h2integrate.converters.solar.solar_pysam import PYSAMSolarPlantPerformanceModel from h2integrate.storage.hydrogen.eco_storage import H2Storage diff --git a/h2integrate/core/test/test_finances.py b/h2integrate/core/test/test_finances.py index cd773341d..1f01686c7 100644 --- a/h2integrate/core/test/test_finances.py +++ b/h2integrate/core/test/test_finances.py @@ -6,7 +6,7 @@ from pytest import approx from h2integrate.core.inputs.validation import load_tech_yaml, load_plant_yaml, load_driver_yaml -from h2integrate.core.profast_financial import ProFastComp +from h2integrate.finances.profast_financial import ProFastComp examples_dir = Path(__file__).resolve().parent.parent.parent.parent / "examples/." diff --git a/h2integrate/finances/__init__.py b/h2integrate/finances/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/h2integrate/core/finances.py b/h2integrate/finances/finances.py similarity index 100% rename from h2integrate/core/finances.py rename to h2integrate/finances/finances.py diff --git a/h2integrate/core/profast_financial.py b/h2integrate/finances/profast_financial.py similarity index 100% rename from h2integrate/core/profast_financial.py rename to h2integrate/finances/profast_financial.py From 711fc295c75ac6e2ea7b15f05b6452e7a0d5b9b1 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:09:05 -0600 Subject: [PATCH 11/67] updated finance group connection logic for commodities in connect_technologies --- h2integrate/core/h2integrate_model.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index b33357620..9e6cedd7e 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -671,34 +671,35 @@ def connect_technologies(self): ) if "electrolyzer" in tech_name: - self.plant.connect( - f"{tech_name}.total_hydrogen_produced", - f"financials_subgroup_{group_id}.total_hydrogen_produced", - ) self.plant.connect( f"{tech_name}.time_until_replacement", f"financials_subgroup_{group_id}.{tech_name}_time_until_replacement", ) + if primary_commodity_type == "hydrogen": + self.plant.connect( + f"{tech_name}.total_hydrogen_produced", + f"financials_subgroup_{group_id}.total_hydrogen_produced", + ) - if "ammonia" in tech_name: + if "ammonia" in tech_name and primary_commodity_type == "ammonia": self.plant.connect( f"{tech_name}.total_ammonia_produced", f"financials_subgroup_{group_id}.total_ammonia_produced", ) - if "doc" in tech_name: + if "doc" in tech_name and primary_commodity_type == "co2": self.plant.connect( f"{tech_name}.co2_capture_mtpy", f"financials_subgroup_{group_id}.co2_capture_kgpy", ) - if "oae" in tech_name: + if "oae" in tech_name and primary_commodity_type == "co2": self.plant.connect( f"{tech_name}.co2_capture_mtpy", f"financials_subgroup_{group_id}.co2_capture_kgpy", ) - if "air_separator" in tech_name: + if "air_separator" in tech_name and primary_commodity_type == "nitrogen": self.plant.connect( f"{tech_name}.total_nitrogen_produced", f"financials_subgroup_{group_id}.total_nitrogen_produced", From c6516174fdc144b80c790dfe28961de274dbf8aa Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:25:57 -0600 Subject: [PATCH 12/67] updated example 2 input files --- examples/01_onshore_steel_mn/plant_config.yaml | 5 ----- examples/02_texas_ammonia/plant_config.yaml | 14 +++++++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index 2012440ca..b869ddb33 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -61,11 +61,6 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - wind: - expected_plant_cost: 'none' - # technologies_included_in_metrics: - # LCOE: ["hopp"] - # LCOH: ["hopp", "electrolyzer", "h2_storage"] subgroups: electricity: commodity: "electricity" diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index 252571afc..fe88c50e4 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -28,7 +28,7 @@ technology_interconnections: [ ["electrolyzer", "h2_storage", "efficiency"], ["electrolyzer", "h2_storage", "hydrogen", "pipe"], ["h2_storage", "ammonia", "hydrogen", "pipe"], - ["financials_group_default", "ammonia", "LCOH"], + ["financials_subgroup_hydrogen", "ammonia", "LCOH"], # etc ] @@ -62,8 +62,16 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - wind: - expected_plant_cost: 'none' cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp"] + hydrogen: + commodity: "hydrogen" + technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + ammonia: + commodity: "ammonia" + technologies: ["hopp","electrolyzer","h2_storage","ammonia"] From e90bf5891679772003ba90cb24a775bddfd105d1 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 13:36:58 -0500 Subject: [PATCH 13/67] name commodity not commmodity_type --- h2integrate/core/h2integrate_model.py | 68 +++++++-------------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 9e6cedd7e..bffcc2bb9 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -279,11 +279,11 @@ def create_financial_model(self): Creates and configures the financial model for the plant. - If subgroups are defined in finance_parameters: - * The first tuple in each subgroup is (commodity_type, finance_model). + * The first tuple in each subgroup is (commodity, finance_model). * The remaining tuples list technologies belonging to that subgroup. - If no subgroups are defined: * A default subgroup is created with all technologies. - * commodity_type and finance_model are pulled from finance_parameters. + * commodity and finance_model are pulled from finance_parameters. """ if "finance_parameters" not in self.plant_config: @@ -316,12 +316,16 @@ def create_financial_model(self): if subgroups is None: # --- Default behavior --- - commodity_type = self.plant_config["finance_parameters"].get("commodity_type") - finance_model_name = self.plant_config["finance_parameters"].get("finance_model") + commodity = self.plant_config["finance_parameters"].get("commodity") + finance_model_name = ( + self.plant_config["finance_parameters"] + .get(default_finance_model_nickname, {}) + .get("finance_model") + ) - if not commodity_type or not finance_model_name: + if not commodity or not finance_model_name: raise ValueError( - "finance_parameters must define 'commodity_type' and 'finance_model' " + "finance_parameters must define 'commodity' and 'finance_model' " "if no subgroups are provided." ) @@ -329,7 +333,7 @@ def create_financial_model(self): # Collect all technologies into one subgroup all_techs = list(self.technology_config["technologies"].keys()) subgroup = { - "commodity": commodity_type, + "commodity": commodity, "finance_groups": [default_finance_model_nickname], "technologies": all_techs, } @@ -341,7 +345,7 @@ def create_financial_model(self): # financial_groups["default"][tech_name] = tech_config # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): - commodity_type = subgroup_params.get("commodity", None) + commodity = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") finance_model_nicknames = subgroup_params.get( "finance_groups", [default_finance_model_nickname] @@ -352,7 +356,7 @@ def create_financial_model(self): finance_model_nicknames = [finance_model_nicknames] # check commodity type - if commodity_type is None: + if commodity is None: raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") tech_configs = { @@ -371,7 +375,7 @@ def create_financial_model(self): { subgroup_name: { "tech_configs": tech_configs, - "commodity": commodity_type, + "commodity": commodity, } } ) @@ -439,15 +443,15 @@ def create_financial_model(self): driver_config=self.driver_config, tech_config=tech_configs, plant_config=filtered_plant_config, - commodity_type=commodity_type, + commodity_type=commodity, description=commodity_desc, ) if not skip_model: finance_subsystem_name = ( - f"{finance_model_nickname}_{commodity_type}" + f"{finance_model_nickname}_{commodity}" if commodity_desc == "" - else f"{finance_model_nickname}_{commodity_type}_{commodity_desc}" + else f"{finance_model_nickname}_{commodity}_{commodity_desc}" ) financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) @@ -456,44 +460,6 @@ def create_financial_model(self): self.financial_groups = financial_groups - def get_included_technologies(self, tech_config, commodity_type, plant_config): - """ - Determine which technologies should be included in the financial metrics. - - Args: - tech_config: Dictionary of technology configurations - commodity_type: Type of commodity (e.g., 'hydrogen', 'electricity', 'ammonia') - plant_config: Plant configuration dictionary - - Returns: - List of technology names to include in the financial stackup - """ - # Check if the user defined specific technologies to include in the metrics. - # If provided, only include those technologies in the stackup. - # If not provided, include all technologies in the financial group in the stackup. - metric_key = f"LCO{commodity_type[0].upper()}" - - included_techs = ( - plant_config["finance_parameters"] - .get("technologies_included_in_metrics", {}) - .get(metric_key, None) - ) - - # Check if the included technologies are valid - if included_techs is not None: - missing_techs = [tech for tech in included_techs if tech not in tech_config] - if missing_techs: - raise ValueError( - f"Included technology(ies) {missing_techs} not found in tech_config. " - f"Available techs: {list(tech_config.keys())}" - ) - - # If no specific technologies are included, default to all technologies in tech_config - if included_techs is None: - included_techs = list(tech_config.keys()) - - return included_techs - def connect_technologies(self): technology_interconnections = self.plant_config.get("technology_interconnections", []) From 0ae12216154a79d3ac6b1a44cfbbf16f5c49f834 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:42:59 -0600 Subject: [PATCH 14/67] updated example 4 plant config --- examples/04_geo_h2/plant_config.yaml | 31 ---------------------------- 1 file changed, 31 deletions(-) diff --git a/examples/04_geo_h2/plant_config.yaml b/examples/04_geo_h2/plant_config.yaml index b2541761c..1c2dcc1c3 100644 --- a/examples/04_geo_h2/plant_config.yaml +++ b/examples/04_geo_h2/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located west of the Wolf Hollow II NGCC power plant site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -28,32 +26,3 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 #percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From 402f67178c8a873751a213b670dab51a1c38ea25 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 13:54:08 -0500 Subject: [PATCH 15/67] change commodity_type to commodity --- h2integrate/core/h2integrate_model.py | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 9e6cedd7e..f78b51f26 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -279,11 +279,11 @@ def create_financial_model(self): Creates and configures the financial model for the plant. - If subgroups are defined in finance_parameters: - * The first tuple in each subgroup is (commodity_type, finance_model). + * The first tuple in each subgroup is (commodity, finance_model). * The remaining tuples list technologies belonging to that subgroup. - If no subgroups are defined: * A default subgroup is created with all technologies. - * commodity_type and finance_model are pulled from finance_parameters. + * commodity and finance_model are pulled from finance_parameters. """ if "finance_parameters" not in self.plant_config: @@ -316,12 +316,16 @@ def create_financial_model(self): if subgroups is None: # --- Default behavior --- - commodity_type = self.plant_config["finance_parameters"].get("commodity_type") - finance_model_name = self.plant_config["finance_parameters"].get("finance_model") + commodity = self.plant_config["finance_parameters"].get("commodity") + finance_model_name = ( + self.plant_config["finance_parameters"] + .get(default_finance_model_nickname, {}) + .get("finance_model") + ) - if not commodity_type or not finance_model_name: + if not commodity or not finance_model_name: raise ValueError( - "finance_parameters must define 'commodity_type' and 'finance_model' " + "finance_parameters must define 'commodity' and 'finance_model' " "if no subgroups are provided." ) @@ -329,7 +333,7 @@ def create_financial_model(self): # Collect all technologies into one subgroup all_techs = list(self.technology_config["technologies"].keys()) subgroup = { - "commodity": commodity_type, + "commodity": commodity, "finance_groups": [default_finance_model_nickname], "technologies": all_techs, } @@ -341,7 +345,7 @@ def create_financial_model(self): # financial_groups["default"][tech_name] = tech_config # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): - commodity_type = subgroup_params.get("commodity", None) + commodity = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") finance_model_nicknames = subgroup_params.get( "finance_groups", [default_finance_model_nickname] @@ -352,7 +356,7 @@ def create_financial_model(self): finance_model_nicknames = [finance_model_nicknames] # check commodity type - if commodity_type is None: + if commodity is None: raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") tech_configs = { @@ -371,7 +375,7 @@ def create_financial_model(self): { subgroup_name: { "tech_configs": tech_configs, - "commodity": commodity_type, + "commodity": commodity, } } ) @@ -439,15 +443,15 @@ def create_financial_model(self): driver_config=self.driver_config, tech_config=tech_configs, plant_config=filtered_plant_config, - commodity_type=commodity_type, + commodity_type=commodity, description=commodity_desc, ) if not skip_model: finance_subsystem_name = ( - f"{finance_model_nickname}_{commodity_type}" + f"{finance_model_nickname}_{commodity}" if commodity_desc == "" - else f"{finance_model_nickname}_{commodity_type}_{commodity_desc}" + else f"{finance_model_nickname}_{commodity}_{commodity_desc}" ) financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) From b1ee9fe85f5aed3d15db87fbb9a5e1a214347528 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:54:11 -0600 Subject: [PATCH 16/67] updated example 6 input file --- examples/06_custom_tech/plant_config.yaml | 31 ----------------------- 1 file changed, 31 deletions(-) diff --git a/examples/06_custom_tech/plant_config.yaml b/examples/06_custom_tech/plant_config.yaml index c87250493..14c433690 100644 --- a/examples/06_custom_tech/plant_config.yaml +++ b/examples/06_custom_tech/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant models a wind plant with a custom paper mill model in M site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -33,29 +28,3 @@ technology_interconnections: [ ["wind", "paper_mill", "electricity", "cable"], # etc ] - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From 06aeaa2a85544fed352d20c98e46e19202a1d05d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:59:35 -0600 Subject: [PATCH 17/67] updated example 8 input files --- .../08_wind_electrolyzer/plant_config.yaml | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/examples/08_wind_electrolyzer/plant_config.yaml b/examples/08_wind_electrolyzer/plant_config.yaml index 18c2b58ed..8360e5093 100644 --- a/examples/08_wind_electrolyzer/plant_config.yaml +++ b/examples/08_wind_electrolyzer/plant_config.yaml @@ -4,8 +4,7 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 + # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +20,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -33,31 +29,3 @@ technology_interconnections: [ ["wind", "electrolyzer", "electricity", "cable"], # etc ] - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - wind: - expected_plant_cost: 'none' - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From 760a47ad76fec8f12df6275f0417fef89b02f4d9 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:20:25 -0600 Subject: [PATCH 18/67] updated example 10 input file --- examples/10_electrolyzer_om/plant_config.yaml | 79 +++++++++++++------ 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/examples/10_electrolyzer_om/plant_config.yaml b/examples/10_electrolyzer_om/plant_config.yaml index b533534e7..e9a36f2b9 100644 --- a/examples/10_electrolyzer_om/plant_config.yaml +++ b/examples/10_electrolyzer_om/plant_config.yaml @@ -21,9 +21,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -35,27 +32,61 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + lcoe_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.0615 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.015 + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 + debt_interest_rate: 0.0439 + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + lcoh_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.1089 + debt_equity_ratio: 0.62 + property_tax_and_insurance: 0.03 + total_income_tax_rate: 0.2574 + capital_gains_tax_rate: 0.15 + sales_tax_rate: 0.0 + debt_interest_rate: 0.05 + debt_type: "Revolving debt" + loan_period_if_used: 0 + cash_onhand_months: 1 + non_depr_assets: 250000 + end_of_proj_sale_non_depr_assets: 250000 + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + electricity: + commodity: "electricity" + finance_groups: ["lcoe_financials"] + technologies: ["wind"] + hydrogen: + commodity: "hydrogen" + commodity_desc: "delivered" + finance_groups: ["lcoh_financials"] + technologies: ["wind","electrolyzer"] From 43223a5a7f70a373ff6ac8161261cae1b4f07e2f Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 13:36:58 -0500 Subject: [PATCH 19/67] name commodity not commmodity_type --- h2integrate/core/h2integrate_model.py | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 9e6cedd7e..56273d18e 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -279,11 +279,11 @@ def create_financial_model(self): Creates and configures the financial model for the plant. - If subgroups are defined in finance_parameters: - * The first tuple in each subgroup is (commodity_type, finance_model). + * The first tuple in each subgroup is (commodity, finance_model). * The remaining tuples list technologies belonging to that subgroup. - If no subgroups are defined: * A default subgroup is created with all technologies. - * commodity_type and finance_model are pulled from finance_parameters. + * commodity and finance_model are pulled from finance_parameters. """ if "finance_parameters" not in self.plant_config: @@ -316,12 +316,16 @@ def create_financial_model(self): if subgroups is None: # --- Default behavior --- - commodity_type = self.plant_config["finance_parameters"].get("commodity_type") - finance_model_name = self.plant_config["finance_parameters"].get("finance_model") + commodity = self.plant_config["finance_parameters"].get("commodity") + finance_model_name = ( + self.plant_config["finance_parameters"] + .get(default_finance_model_nickname, {}) + .get("finance_model") + ) - if not commodity_type or not finance_model_name: + if not commodity or not finance_model_name: raise ValueError( - "finance_parameters must define 'commodity_type' and 'finance_model' " + "finance_parameters must define 'commodity' and 'finance_model' " "if no subgroups are provided." ) @@ -329,7 +333,7 @@ def create_financial_model(self): # Collect all technologies into one subgroup all_techs = list(self.technology_config["technologies"].keys()) subgroup = { - "commodity": commodity_type, + "commodity": commodity, "finance_groups": [default_finance_model_nickname], "technologies": all_techs, } @@ -341,7 +345,7 @@ def create_financial_model(self): # financial_groups["default"][tech_name] = tech_config # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): - commodity_type = subgroup_params.get("commodity", None) + commodity = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") finance_model_nicknames = subgroup_params.get( "finance_groups", [default_finance_model_nickname] @@ -352,7 +356,7 @@ def create_financial_model(self): finance_model_nicknames = [finance_model_nicknames] # check commodity type - if commodity_type is None: + if commodity is None: raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") tech_configs = { @@ -371,7 +375,7 @@ def create_financial_model(self): { subgroup_name: { "tech_configs": tech_configs, - "commodity": commodity_type, + "commodity": commodity, } } ) @@ -439,15 +443,15 @@ def create_financial_model(self): driver_config=self.driver_config, tech_config=tech_configs, plant_config=filtered_plant_config, - commodity_type=commodity_type, + commodity_type=commodity, description=commodity_desc, ) if not skip_model: finance_subsystem_name = ( - f"{finance_model_nickname}_{commodity_type}" + f"{finance_model_nickname}_{commodity}" if commodity_desc == "" - else f"{finance_model_nickname}_{commodity_type}_{commodity_desc}" + else f"{finance_model_nickname}_{commodity}_{commodity_desc}" ) financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) @@ -459,12 +463,10 @@ def create_financial_model(self): def get_included_technologies(self, tech_config, commodity_type, plant_config): """ Determine which technologies should be included in the financial metrics. - Args: tech_config: Dictionary of technology configurations commodity_type: Type of commodity (e.g., 'hydrogen', 'electricity', 'ammonia') plant_config: Plant configuration dictionary - Returns: List of technology names to include in the financial stackup """ From 24370596497f1d377008183be276c96c32dcab37 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:42:59 -0600 Subject: [PATCH 20/67] updated example 4 plant config --- examples/04_geo_h2/plant_config.yaml | 31 ---------------------------- 1 file changed, 31 deletions(-) diff --git a/examples/04_geo_h2/plant_config.yaml b/examples/04_geo_h2/plant_config.yaml index b2541761c..1c2dcc1c3 100644 --- a/examples/04_geo_h2/plant_config.yaml +++ b/examples/04_geo_h2/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located west of the Wolf Hollow II NGCC power plant site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -28,32 +26,3 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 #percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From 3ab3791f72ad32738fef5958923acfbe6970dc52 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:54:11 -0600 Subject: [PATCH 21/67] updated example 6 input file --- examples/06_custom_tech/plant_config.yaml | 31 ----------------------- 1 file changed, 31 deletions(-) diff --git a/examples/06_custom_tech/plant_config.yaml b/examples/06_custom_tech/plant_config.yaml index c87250493..14c433690 100644 --- a/examples/06_custom_tech/plant_config.yaml +++ b/examples/06_custom_tech/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant models a wind plant with a custom paper mill model in M site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -33,29 +28,3 @@ technology_interconnections: [ ["wind", "paper_mill", "electricity", "cable"], # etc ] - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From 7653f25a839ee5634467693407ce50bfaafdd20c Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:59:35 -0600 Subject: [PATCH 22/67] updated example 8 input files --- .../08_wind_electrolyzer/plant_config.yaml | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/examples/08_wind_electrolyzer/plant_config.yaml b/examples/08_wind_electrolyzer/plant_config.yaml index 18c2b58ed..8360e5093 100644 --- a/examples/08_wind_electrolyzer/plant_config.yaml +++ b/examples/08_wind_electrolyzer/plant_config.yaml @@ -4,8 +4,7 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 + # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +20,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -33,31 +29,3 @@ technology_interconnections: [ ["wind", "electrolyzer", "electricity", "cable"], # etc ] - -finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - wind: - expected_plant_cost: 'none' - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 From b2d302e4a1c87bad1de63a503a302cfdae85bfb8 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:20:25 -0600 Subject: [PATCH 23/67] updated example 10 input file --- examples/10_electrolyzer_om/plant_config.yaml | 79 +++++++++++++------ 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/examples/10_electrolyzer_om/plant_config.yaml b/examples/10_electrolyzer_om/plant_config.yaml index b533534e7..e9a36f2b9 100644 --- a/examples/10_electrolyzer_om/plant_config.yaml +++ b/examples/10_electrolyzer_om/plant_config.yaml @@ -21,9 +21,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections @@ -35,27 +32,61 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + lcoe_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.0615 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.015 + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 + debt_interest_rate: 0.0439 + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + lcoh_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.1089 + debt_equity_ratio: 0.62 + property_tax_and_insurance: 0.03 + total_income_tax_rate: 0.2574 + capital_gains_tax_rate: 0.15 + sales_tax_rate: 0.0 + debt_interest_rate: 0.05 + debt_type: "Revolving debt" + loan_period_if_used: 0 + cash_onhand_months: 1 + non_depr_assets: 250000 + end_of_proj_sale_non_depr_assets: 250000 + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + electricity: + commodity: "electricity" + finance_groups: ["lcoe_financials"] + technologies: ["wind"] + hydrogen: + commodity: "hydrogen" + commodity_desc: "delivered" + finance_groups: ["lcoh_financials"] + technologies: ["wind","electrolyzer"] From ea833af4ddef474e326465eb8beed353afa2e37d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:40:47 -0600 Subject: [PATCH 24/67] typo fix in example 10 python filename --- .../{run_elecrolyzer_om.py => run_electrolyzer_om.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/10_electrolyzer_om/{run_elecrolyzer_om.py => run_electrolyzer_om.py} (100%) diff --git a/examples/10_electrolyzer_om/run_elecrolyzer_om.py b/examples/10_electrolyzer_om/run_electrolyzer_om.py similarity index 100% rename from examples/10_electrolyzer_om/run_elecrolyzer_om.py rename to examples/10_electrolyzer_om/run_electrolyzer_om.py From 1783d4a99f6ac0b5dfad5f17df61d58c8ad47951 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 14:52:36 -0500 Subject: [PATCH 25/67] update subgroup default naming --- h2integrate/core/h2integrate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 56273d18e..fe51991d1 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -337,7 +337,7 @@ def create_financial_model(self): "finance_groups": [default_finance_model_nickname], "technologies": all_techs, } - subgroups = {"all": subgroup} + subgroups = {default_finance_model_nickname: subgroup} # for tech_name, tech_config in self.technology_config["technologies"].items(): # if tech_name not in all_grouped_techs: # if "default" not in financial_groups: From 0af3b0ca5b46d72483f533bc386b32bc6ea51015 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 14:53:14 -0500 Subject: [PATCH 26/67] remove unused file --- examples/03_methanol/plant_config.yaml | 65 -------------------------- 1 file changed, 65 deletions(-) delete mode 100644 examples/03_methanol/plant_config.yaml diff --git a/examples/03_methanol/plant_config.yaml b/examples/03_methanol/plant_config.yaml deleted file mode 100644 index 861b4f7b8..000000000 --- a/examples/03_methanol/plant_config.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: "plant_config" -description: "This plant is located west of the Wolf Hollow II NGCC power plant in Texas" - -site: - latitude: 32.34 - longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 - - # array of polygons defining boundaries with x/y coords - boundaries: [ - { - x: [0.0, 1000.0, 1000.0, 0.0], - y: [0.0, 0.0, 100.0, 1000.0], - }, - { - x: [2000.0, 2500.0, 2000.0], - y: [2000.0, 2000.0, 2500.0], - } - ] - -# array of arrays containing left-to-right technology -# interconnections; can support bidirectional connections -# with the reverse definition. -# this will naturally grow as we mature the interconnected tech -technology_interconnections: [ -] - -plant: - plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.027498168 # based off correlations of LBNL PPA data - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True - -finance_parameters: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind - debt_equity_split: False - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax: 0.02 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - property_insurance: 0.01 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - administrative_expense_percent_of_sales: 0.00 # percent of sales H2FAST default - depreciation_method: "MACRS" # can be "MACRS" or "Straight line" - MACRS may be better and can reduce LCOH by more than $1/kg and is spec'd in the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - depreciation_period: 5 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - depreciation_period_electrolyzer: 7 # based on PEM Electrolysis H2A Production Case Study Documentation estimate of 7 years. also see https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - options: - commodity_type: "methanol" - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 - -policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, then prevailing wage multiplier if applicable, then inflation - electricity_itc: 0 - electricity_ptc: 0 - h2_ptc: 0 - h2_storage_itc: 0 From 9aba35ee11e347b5a8cac64ecc20d699f25ae125 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 15:00:13 -0500 Subject: [PATCH 27/67] update examples --- .../co2_hydrogenation/plant_config_co2h.yaml | 18 +++++++--- .../03_methanol/smr/plant_config_smr.yaml | 34 ------------------- examples/05_wind_h2_opt/driver_config.yaml | 2 +- examples/05_wind_h2_opt/plant_config.yaml | 7 ++++ .../07_run_of_river_plant/plant_config.yaml | 6 +--- .../direct_ocean_capture/plant_config.yaml | 7 ++++ .../plant_config.yaml | 4 +-- .../plant_config_financials.yaml | 12 +++++-- .../tech_config_financials.yaml | 1 - .../11_hybrid_energy_plant/driver_config.yaml | 2 +- .../11_hybrid_energy_plant/plant_config.yaml | 1 + examples/13_air_separator/plant_config.yaml | 9 +---- .../plant_config.yaml | 10 ++++-- .../17_splitter_wind_doc_h2/plant_config.yaml | 14 +++++--- 14 files changed, 59 insertions(+), 68 deletions(-) diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index b2e46e057..006185ec8 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -26,8 +26,8 @@ site: technology_interconnections: [ ["hopp", "electrolyzer", "electricity", "cable"], ["electrolyzer", "methanol", "hydrogen", "pipe"], - ["financials_group_1", "methanol", "LCOE"], - ["financials_group_1", "methanol", "LCOH"], + ["financials_subgroup_electricity", "methanol", "LCOE"], + ["financials_subgroup_hydrogen", "methanol", "LCOH"], ] plant: @@ -37,7 +37,6 @@ plant: hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: - finance_model: "ProFastComp" model_inputs: params: @@ -59,8 +58,17 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - options: - commodity_type: "methanol" + subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp"] + hydrogen: + commodity: "hydrogen" + technologies: ["hopp","electrolyzer"] + methanol: + commodity: "methanol" + finance_groups: ["methanol"] + technologies: ["methanol"] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 diff --git a/examples/03_methanol/smr/plant_config_smr.yaml b/examples/03_methanol/smr/plant_config_smr.yaml index 7b53ef179..413a93de9 100644 --- a/examples/03_methanol/smr/plant_config_smr.yaml +++ b/examples/03_methanol/smr/plant_config_smr.yaml @@ -31,37 +31,3 @@ plant: grid_connection: False # option, can be turned on or off ppa_price: 0.027498168 # based off correlations of LBNL PPA data hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True - -finance_parameters: - finance_model: "ProFastComp" - profast_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx and https://www.nrel.gov/docs/fy25osti/91775.pdf - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - options: - commodity_type: "methanol" - cost_adjustment_parameters: - cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year - target_dollar_year: 2022 - -policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, then prevailing wage multiplier if applicable, then inflation - electricity_itc: 0 - electricity_ptc: 0 - h2_ptc: 0 - h2_storage_itc: 0 diff --git a/examples/05_wind_h2_opt/driver_config.yaml b/examples/05_wind_h2_opt/driver_config.yaml index f2ae451a4..b950c4591 100644 --- a/examples/05_wind_h2_opt/driver_config.yaml +++ b/examples/05_wind_h2_opt/driver_config.yaml @@ -30,7 +30,7 @@ constraints: units: kg/year objective: - name: financials_group_default.LCOH + name: financials_subgroup_hydrogen.LCOH recorder: flag: True diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index 3b9a2c692..54fb8b012 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -59,3 +59,10 @@ finance_parameters: cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + electricity: + commodity: "electricity" + technologies: ["wind"] + hydrogen: + commodity: "hydrogen" + technologies: ["wind","electrolyzer"] diff --git a/examples/07_run_of_river_plant/plant_config.yaml b/examples/07_run_of_river_plant/plant_config.yaml index 08dc689fa..92bb919a0 100644 --- a/examples/07_run_of_river_plant/plant_config.yaml +++ b/examples/07_run_of_river_plant/plant_config.yaml @@ -46,6 +46,7 @@ plant: hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: + commodity: "electricity" finance_model: "ProFastComp" model_inputs: params: @@ -70,8 +71,3 @@ finance_parameters: cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 -policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, then prevailing wage multiplier if applicable, then inflation - electricity_itc: 0 - electricity_ptc: 0 - h2_ptc: 0 - h2_storage_itc: 0 diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 9d3777bb6..80e8e2ca1 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -59,3 +59,10 @@ finance_parameters: cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp"] + co2: + commodity: "co2" + technologies: ["hopp","doc"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index 1aaf7e3b5..bbc19c388 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -56,9 +56,7 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - discount_years: - hopp: 2022 - oae: 2023 cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # 2.5% inflation rate for cost adjustments target_dollar_year: 2022 + subgroups: diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml index db5822ae2..a8f586904 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml @@ -31,7 +31,7 @@ plant: # this will naturally grow as we mature the interconnected tech technology_interconnections: [ ["hopp", "oae", "electricity", "cable"], - ["financials_group_default", "oae", "LCOE"] + ["financials_subgroup_electricity", "oae", "LCOE"] # etc ] @@ -57,8 +57,14 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - technologies_included_in_metrics: - LCOE: ["hopp"] + subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp"] + # oae: + # commodity: "co2" + # finance_groups: ["oae"] + # technologies: ["oae"] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # 2.5% inflation rate for cost adjustments target_dollar_year: 2022 diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml index 323efd999..e5049888d 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml @@ -18,7 +18,6 @@ technologies: model: "ocean_alkalinity_enhancement_performance" financial_model: model: "ocean_alkalinity_enhancement_cost_financial" - group: "oae" model_inputs: performance_parameters: number_ed_min: 1 diff --git a/examples/11_hybrid_energy_plant/driver_config.yaml b/examples/11_hybrid_energy_plant/driver_config.yaml index f642f2a7e..e2dfcb4fd 100644 --- a/examples/11_hybrid_energy_plant/driver_config.yaml +++ b/examples/11_hybrid_energy_plant/driver_config.yaml @@ -60,4 +60,4 @@ constraints: units: unitless objective: - name: financials_group_default.LCOE + name: financials_subgroup_default.LCOE diff --git a/examples/11_hybrid_energy_plant/plant_config.yaml b/examples/11_hybrid_energy_plant/plant_config.yaml index f9e4652fe..b16635ac9 100644 --- a/examples/11_hybrid_energy_plant/plant_config.yaml +++ b/examples/11_hybrid_energy_plant/plant_config.yaml @@ -26,6 +26,7 @@ plant: installation_time: 12 # months finance_parameters: + commodity: "electricity" finance_model: "ProFastComp" model_inputs: params: diff --git a/examples/13_air_separator/plant_config.yaml b/examples/13_air_separator/plant_config.yaml index e22d5d2e1..c5350f48d 100644 --- a/examples/13_air_separator/plant_config.yaml +++ b/examples/13_air_separator/plant_config.yaml @@ -35,6 +35,7 @@ plant: hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: + commodity: "nitrogen" finance_model: "ProFastComp" model_inputs: params: @@ -56,14 +57,6 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - wind: - expected_plant_cost: 'none' cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2025 - -policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, then prevailing wage multiplier if applicable, then inflation - electricity_itc: 0 - electricity_ptc: 0 - h2_ptc: 0 - h2_storage_itc: 0 diff --git a/examples/15_wind_solar_electrolyzer/plant_config.yaml b/examples/15_wind_solar_electrolyzer/plant_config.yaml index d58c4b8b0..557ccdc58 100644 --- a/examples/15_wind_solar_electrolyzer/plant_config.yaml +++ b/examples/15_wind_solar_electrolyzer/plant_config.yaml @@ -61,9 +61,13 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - technologies_included_in_metrics: - LCOE: ["wind", "solar"] - LCOH: ["wind", "solar", "electrolyzer"] + subgroups: + electricity: + commodity: "electricity" + technologies: ["wind","solar"] + hydrogen: + commodity: "hydrogen" + technologies: ["wind","solar","electrolyzer"] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 diff --git a/examples/17_splitter_wind_doc_h2/plant_config.yaml b/examples/17_splitter_wind_doc_h2/plant_config.yaml index 7266eb81c..77cff1ba3 100644 --- a/examples/17_splitter_wind_doc_h2/plant_config.yaml +++ b/examples/17_splitter_wind_doc_h2/plant_config.yaml @@ -63,7 +63,13 @@ finance_parameters: cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - technologies_included_in_metrics: - LCOE: ["hopp"] - LCOH: ["hopp", "electrolyzer"] - LCOC: ["hopp", "doc"] + subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp"] + hydrogen: + commodity: "hydrogen" + technologies: ["hopp","electrolyzer"] + co2: + commodity: "co2" + technologies: ["hopp","doc"] From 7d75945060c7d690e28756f97b49ed901053a424 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 5 Sep 2025 15:23:10 -0500 Subject: [PATCH 28/67] update test_all_examples --- examples/05_wind_h2_opt/plant_config.yaml | 2 +- .../direct_ocean_capture/plant_config.yaml | 2 +- .../plant_config.yaml | 6 +++ tests/h2integrate/test_all_examples.py | 40 ++++++++++--------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index 54fb8b012..f1488126b 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -62,7 +62,7 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["wind"] + technologies: ["wind","electrolyzer"] hydrogen: commodity: "hydrogen" technologies: ["wind","electrolyzer"] diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 80e8e2ca1..5e58689ac 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -62,7 +62,7 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["hopp"] + technologies: ["hopp", "doc"] co2: commodity: "co2" technologies: ["hopp","doc"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index bbc19c388..737938cb5 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -60,3 +60,9 @@ finance_parameters: cost_year_adjustment_inflation: 0.025 # 2.5% inflation rate for cost adjustments target_dollar_year: 2022 subgroups: + electricity: + commodity: "electricity" + technologies: ["hopp", "oae"] + co2: + commodity: "co2" + technologies: ["hopp","oae"] diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index c3e24f4b7..fe04338de 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -274,7 +274,7 @@ def test_wind_h2_opt_example(subtests): with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) == 0.151189 ) @@ -302,8 +302,8 @@ def test_wind_h2_opt_example(subtests): assert len(cases) > 1, "Not enough cases recorded in SQL file." # Get initial and final LCOH values - initial_lcoh = cases[0].outputs["financials_group_default.LCOH"][0] - final_lcoh = cases[-1].outputs["financials_group_default.LCOH"][0] + initial_lcoh = cases[0].outputs["financials_subgroup_hydrogen.LCOH"][0] + final_lcoh = cases[-1].outputs["financials_subgroup_hydrogen.LCOH"][0] with subtests.test("Check LCOH changed"): assert final_lcoh != initial_lcoh @@ -311,14 +311,14 @@ def test_wind_h2_opt_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 2783126102 ) with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 75543899 ) @@ -371,13 +371,13 @@ def test_wind_wave_doc_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOC"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOC")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC")[0], rel=1e-3) == 2.26955589 ) with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) == 1.05281478 ) @@ -398,19 +398,19 @@ def test_splitter_wind_doc_h2_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOH")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 10.25515911 ) with subtests.test("Check LCOC"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOC")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC")[0], rel=1e-3) == 14.19802243 ) with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) == 0.1385128 ) @@ -427,12 +427,12 @@ def test_hydro_example(subtests): model.post_process() - print(model.prob.get_val("financials_group_default.LCOE")) + print(model.prob.get_val("financials_subgroup_default.LCOE")) # Subtests for checking specific values with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE"), rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_default.LCOE"), rel=1e-3) == 0.17653979 ) @@ -451,7 +451,7 @@ def test_hybrid_energy_plant_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOE"): - assert model.prob.get_val("financials_group_default.LCOE", units="USD/MW/h")[0] < 83.2123 + assert model.prob.get_val("financials_subgroup_default.LCOE", units="USD/MW/h")[0] < 83.2123 def test_asu_example(subtests): @@ -470,7 +470,7 @@ def test_asu_example(subtests): with subtests.test("Check LCON"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.LCON", units="USD/kg")[0], + model.prob.get_val("financials_subgroup_default.LCON", units="USD/kg")[0], abs=1e-4, ) == 0.309041977334972 @@ -524,11 +524,12 @@ def test_wind_wave_oae_example(subtests): # Note: These are placeholder values. Update with actual values after running the test # when MCM package is properly installed and configured with subtests.test("Check LCOC"): - assert pytest.approx(model.prob.get_val("financials_group_default.LCOC"), rel=1e-3) == 37.82 + assert pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC"), rel=1e-3) == 37.82 with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE"), rel=1e-3) == 0.36956 + pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE"), rel=1e-3) + == 0.36956 ) @@ -550,7 +551,8 @@ def test_wind_wave_oae_example_with_financials(subtests): # when MCM package is properly installed and configured with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOE"), rel=1e-3) == 0.09180 + pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE"), rel=1e-3) + == 0.09180 ) with subtests.test("Check Carbon Credit"): @@ -571,7 +573,7 @@ def test_wind_solar_electrolyzer_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.LCOE", units="USD/MW/h")[0], + model.prob.get_val("financials_subgroup_electricity.LCOE", units="USD/MW/h")[0], rel=1e-5, ) == 54.12889 @@ -580,7 +582,7 @@ def test_wind_solar_electrolyzer_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.LCOH", units="USD/kg")[0], + model.prob.get_val("financials_subgroup_hydrogen.LCOH", units="USD/kg")[0], rel=1e-5, ) == 5.33209234 From 632229127897a331a72cd70180e996fc73499515 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:27:03 -0600 Subject: [PATCH 29/67] added functionality to enable multiple finance models per subgroup --- examples/10_electrolyzer_om/plant_config.yaml | 6 ++---- h2integrate/core/h2integrate_model.py | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/examples/10_electrolyzer_om/plant_config.yaml b/examples/10_electrolyzer_om/plant_config.yaml index e9a36f2b9..056c34988 100644 --- a/examples/10_electrolyzer_om/plant_config.yaml +++ b/examples/10_electrolyzer_om/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -71,6 +69,7 @@ finance_parameters: debt_type: "Revolving debt" loan_period_if_used: 0 cash_onhand_months: 1 + admin_expense: 0.00 # percent of sales H2FAST default non_depr_assets: 250000 end_of_proj_sale_non_depr_assets: 250000 capital_items: @@ -87,6 +86,5 @@ finance_parameters: technologies: ["wind"] hydrogen: commodity: "hydrogen" - commodity_desc: "delivered" - finance_groups: ["lcoh_financials"] + finance_groups: ["lcoe_financials","lcoh_financials"] technologies: ["wind","electrolyzer"] diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 56273d18e..89995d6c4 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -337,7 +337,7 @@ def create_financial_model(self): "finance_groups": [default_finance_model_nickname], "technologies": all_techs, } - subgroups = {"all": subgroup} + subgroups = {default_finance_model_nickname: subgroup} # for tech_name, tech_config in self.technology_config["technologies"].items(): # if tech_name not in all_grouped_techs: # if "default" not in financial_groups: @@ -349,7 +349,7 @@ def create_financial_model(self): commodity_desc = subgroup_params.get("commodity_desc", "") finance_model_nicknames = subgroup_params.get( "finance_groups", [default_finance_model_nickname] - ) # TODO: what if they only have one finance model + ) tech_names = subgroup_params.get("technologies") if isinstance(finance_model_nicknames, str): @@ -439,12 +439,26 @@ def create_financial_model(self): } ) + commodity_desc = subgroup_params.get("commodity_desc", "") + commodity_output_desc = subgroup_params.get("commodity_desc", "") + + if len(finance_model_nicknames) > 1: + non_tech_financials = [ + k + for k in finance_model_nicknames + if k in self.plant_config["finance_parameters"] + ] + if len(non_tech_financials) > 1: + commodity_output_desc = ( + commodity_output_desc + f"_{finance_model_nickname}" + ) + fin_comp = fin_model( driver_config=self.driver_config, tech_config=tech_configs, plant_config=filtered_plant_config, commodity_type=commodity, - description=commodity_desc, + description=commodity_output_desc, ) if not skip_model: From 11955f3f1471af5f0832fc56f4c9f2ec75d50991 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:33:57 -0600 Subject: [PATCH 30/67] updated example 12 and example tests for even numbered examples --- examples/12_ammonia_synloop/plant_config.yaml | 19 +++++---- tests/h2integrate/test_all_examples.py | 40 +++++++++++++++---- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/examples/12_ammonia_synloop/plant_config.yaml b/examples/12_ammonia_synloop/plant_config.yaml index 6a11d32fe..d77dd6cfc 100644 --- a/examples/12_ammonia_synloop/plant_config.yaml +++ b/examples/12_ammonia_synloop/plant_config.yaml @@ -4,8 +4,8 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 + # elevation_m: 439.0 + # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -33,9 +33,9 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True + # grid_connection: False # option, can be turned on or off + # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf + # hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: finance_model: "ProFastComp" @@ -59,8 +59,13 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 7 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - wind: - expected_plant_cost: 'none' cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + h2: + commodity: "hydrogen" + technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + nh3: + commodity: "ammonia" + technologies: ["hopp","electrolyzer","h2_storage","ammonia"] diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index c3e24f4b7..23f34ed61 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -102,7 +102,7 @@ def test_simple_ammonia_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 2678403968.6 ) @@ -110,7 +110,7 @@ def test_simple_ammonia_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 64338137.8 ) @@ -118,13 +118,13 @@ def test_simple_ammonia_example(subtests): # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOH")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 4.233055 ) # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOA"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOA")[0], rel=1e-3) + pytest.approx(model.prob.get_val("financials_subgroup_ammonia.LCOA")[0], rel=1e-3) == 1.02470046 ) @@ -187,7 +187,7 @@ def test_ammonia_synloop_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_capex_adjusted")[0], rel=1e-6 + model.prob.get_val("financials_subgroup_nh3.total_capex_adjusted")[0], rel=1e-6 ) == 3.7289e09 ) @@ -195,20 +195,20 @@ def test_ammonia_synloop_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.total_opex_adjusted")[0], rel=1e-6 + model.prob.get_val("financials_subgroup_nh3.total_opex_adjusted")[0], rel=1e-6 ) == 78480154.4 ) with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOH")[0], rel=1e-6) + pytest.approx(model.prob.get_val("financials_subgroup_h2.LCOH")[0], rel=1e-6) == 5.659321302703965 ) with subtests.test("Check LCOA"): assert ( - pytest.approx(model.prob.get_val("financials_group_default.LCOA")[0], rel=1e-6) + pytest.approx(model.prob.get_val("financials_subgroup_nh3.LCOA")[0], rel=1e-6) == 1.067030996544544 ) @@ -597,3 +597,27 @@ def test_wind_solar_electrolyzer_example(subtests): ) with subtests.test("Check electrolyzer input power"): assert pytest.approx(total_generation.sum(), rel=1e-5) == total_energy_to_electrolyzer.sum() + + +def test_electrolyzer_om_example(subtests): + # Change the current working directory to the example's directory + os.chdir(EXAMPLE_DIR / "10_electrolyzer_om") + + # Create a H2Integrate model + model = H2IntegrateModel(Path.cwd() / "electrolyzer_om.yaml") + + model.run() + + lcoe = model.prob.get_val("financials_subgroup_electricity.LCOE", units="USD/MW/h")[0] + lcoh_with_lcoh_financials = model.prob.get_val( + "financials_subgroup_hydrogen.LCOH_lcoh_financials", units="USD/kg" + )[0] + lcoh_with_lcoe_financials = model.prob.get_val( + "financials_subgroup_hydrogen.LCOH_lcoe_financials", units="USD/kg" + )[0] + with subtests.test("Check LCOE"): + assert pytest.approx(lcoe, rel=1e-5) == 40.12819 + with subtests.test("Check LCOH with lcoh_financials"): + assert pytest.approx(lcoh_with_lcoh_financials, rel=1e-5) == 13.18328175 + with subtests.test("Check LCOH with lcoe_financials"): + assert pytest.approx(lcoh_with_lcoe_financials, rel=1e-5) == 8.05688467 From b33dd90386705bddf2e14e8dd1126c7de0ddb70e Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:34:37 -0600 Subject: [PATCH 31/67] updated handling description in ProFastComp --- h2integrate/finances/profast_financial.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index 7d45d120d..1d0b6174d 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -421,6 +421,7 @@ def initialize(self): self.options.declare("description", types=str, default="") def setup(self): + self.cnt = 0 if self.options["commodity_type"] == "electricity": commodity_units = "kW*h/year" lco_units = "USD/kW/h" @@ -428,26 +429,18 @@ def setup(self): commodity_units = "kg/year" lco_units = "USD/kg" - if ( - self.options["description"] == "" - or self.options["description"] == self.options["commodity_type"] - ): + if self.options["description"] == "": self.LCO_str = f"LCO{self.options['commodity_type'][0].upper()}" self.output_txt = self.options["commodity_type"].lower() else: LCO_base_str = f"LCO{self.options['commodity_type'][0].upper()}" - LCO_desc_str = ( - self.options["description"] - .replace(self.options["commodity_type"], "") - .strip() - .strip("_()-") - ) - if LCO_desc_str == "": + desc_str = self.options["description"].strip().strip("_()-") + if desc_str == "": self.output_txt = self.options["commodity_type"].lower() self.LCO_str = LCO_base_str else: - self.output_txt = f"{self.options['commodity_type'].lower()}_{LCO_desc_str}" - self.LCO_str = f"{LCO_base_str}_{LCO_desc_str}" + self.output_txt = f"{self.options['commodity_type'].lower()}_{desc_str}" + self.LCO_str = f"{LCO_base_str}_{desc_str}" self.add_output(self.LCO_str, val=0.0, units=lco_units) self.outputs_to_units = { @@ -610,6 +603,10 @@ def compute(self, inputs, outputs): else: fname = f"{fdesc}_{self.options['commodity_type']}_{self.LCO_str}.yaml" + if self.cnt > 0: + fname = fname.replace(".yaml", f"_{self.cnt}.yaml") + self.cnt += 1 + fpath = Path(output_dir) / fname output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) From 913807bf0a159f00f4fbf79fafcd70de6ae623fd Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:35:01 -0600 Subject: [PATCH 32/67] updated ProFastComp output things and wrapped up even examples --- examples/02_texas_ammonia/plant_config.yaml | 3 +- .../inputs/plant_config.yaml | 19 ++++--- h2integrate/finances/profast_financial.py | 53 ++++++++++++------- h2integrate/tools/profast_tools.py | 14 ++++- tests/h2integrate/test_all_examples.py | 12 ++--- 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index fe88c50e4..efec39756 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -41,7 +41,8 @@ plant: finance_parameters: finance_model: "ProFastComp" model_inputs: - save_profast_to_file: True + save_profast_config: True + save_profast_results: False profast_output_description: "profast_output" params: analysis_start_year: 2032 diff --git a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml index 796902871..08b829108 100644 --- a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml +++ b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml @@ -4,8 +4,8 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 + # elevation_m: 439.0 + # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -31,9 +31,9 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True + # grid_connection: False # option, can be turned on or off + # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf + # hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: @@ -58,8 +58,13 @@ finance_parameters: depr_type: "MACRS" # can be "MACRS" or "Straight line" depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 refurb: [0.] - wind: - expected_plant_cost: 'none' cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 + subgroups: + elec: + commodity: "electricity" + technologies: ["wind","electrolyzer","h2_storage"] + h2: + commodity: "hydrogen" + technologies: ["wind","electrolyzer","h2_storage"] diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index 1d0b6174d..c515d4710 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -14,7 +14,12 @@ ) from h2integrate.core.dict_utils import update_defaults from h2integrate.core.validators import gt_zero, contains, gte_zero, range_val -from h2integrate.tools.profast_tools import run_profast, create_and_populate_profast +from h2integrate.tools.profast_tools import ( + run_profast, + make_price_breakdown, + create_and_populate_profast, + format_profast_price_breakdown_per_year, +) from h2integrate.core.inputs.validation import write_yaml from h2integrate.tools.profast_reverse_tools import convert_pf_to_dict @@ -421,7 +426,6 @@ def initialize(self): self.options.declare("description", types=str, default="") def setup(self): - self.cnt = 0 if self.options["commodity_type"] == "electricity": commodity_units = "kW*h/year" lco_units = "USD/kW/h" @@ -591,28 +595,39 @@ def compute(self, inputs, outputs): sol, summary, price_breakdown = run_profast(pf) # Check whether to export profast object to .yaml file - if self.options["plant_config"]["finance_parameters"]["model_inputs"].get( - "save_profast_to_file", False - ): + save_results = self.options["plant_config"]["finance_parameters"]["model_inputs"].get( + "save_profast_results", False + ) + save_config = self.options["plant_config"]["finance_parameters"]["model_inputs"].get( + "save_profast_config", False + ) + if save_results or save_config: + pf_config_dict = convert_pf_to_dict(pf) + output_dir = self.options["driver_config"]["general"]["folder_output"] - fdesc = self.options["plant_config"]["finance_parameters"]["model_inputs"][ - "profast_output_description" - ] - if self.options["description"] == "": - fname = f"{fdesc}_{self.options['commodity_type']}.yaml" - else: - fname = f"{fdesc}_{self.options['commodity_type']}_{self.LCO_str}.yaml" + fdesc = self.options["plant_config"]["finance_parameters"]["model_inputs"].get( + "profast_output_description", "ProFastComp" + ) - if self.cnt > 0: - fname = fname.replace(".yaml", f"_{self.cnt}.yaml") - self.cnt += 1 + # if self.options["description"] == "": + # fbasename = f"{fdesc}_{self.options['commodity_type']}" + # else: + fbasename = f"{fdesc}_{self.output_txt}" - fpath = Path(output_dir) / fname output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) - d = convert_pf_to_dict(pf) - d = dict_to_yaml_formatting(d) - write_yaml(d, fpath) + if save_config: + pf_config_dict = dict_to_yaml_formatting(pf_config_dict) + config_fpath = Path(output_dir) / f"{fbasename}_config.yaml" + write_yaml(pf_config_dict, config_fpath) + if save_results: + lco_breakdown, lco_check = make_price_breakdown(price_breakdown, pf_config_dict) + price_breakdown_formatted = format_profast_price_breakdown_per_year(price_breakdown) + pf_breakdown_fpath = Path(output_dir) / f"{fbasename}_profast_price_breakdown.csv" + lco_breakdown_fpath = Path(output_dir) / f"{fbasename}_LCO_breakdown.yaml" + price_breakdown_formatted.to_csv(pf_breakdown_fpath) + lco_breakdown = dict_to_yaml_formatting(lco_breakdown) + write_yaml(lco_breakdown, lco_breakdown_fpath) outputs[self.LCO_str] = sol["lco"] for output_var in self.outputs_to_units.keys(): diff --git a/h2integrate/tools/profast_tools.py b/h2integrate/tools/profast_tools.py index b462200f4..e848fd3a3 100644 --- a/h2integrate/tools/profast_tools.py +++ b/h2integrate/tools/profast_tools.py @@ -1,4 +1,5 @@ import numpy as np +import pandas as pd import ProFAST @@ -115,7 +116,7 @@ def make_price_breakdown(price_breakdown, pf_config): price_breakdown_capex = {} price_breakdown_fixed_cost = {} full_price_breakdown = {} - lco_str = "LCO{}".format(pf_config["params"]["commodity"]["name"][0]) + lco_str = "LCO{}".format(pf_config["params"]["commodity"]["name"][0].upper()) lco_units = "$/{}".format(pf_config["params"]["commodity"]["unit"]) config_keys = list(pf_config.keys()) if "capital_items" in config_keys: @@ -219,3 +220,14 @@ def create_years_of_operation( ) year_keys = [f"{y}" for y in years_of_operation] return year_keys + + +def format_profast_price_breakdown_per_year(price_breakdown): + n_years = len(price_breakdown.iloc[0]["Amount"]) + year_cols = [f"Year {i} Amount" for i in range(n_years)] + + amount_df = pd.DataFrame(np.array(price_breakdown["Amount"].to_list()), columns=year_cols) + formatted_df = pd.concat( + [price_breakdown[["Type", "Name"]], amount_df, price_breakdown["NPV"]], axis=1 + ) + return formatted_df diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index 3cb6fca00..fe6d62a9f 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -131,13 +131,13 @@ def test_simple_ammonia_example(subtests): # Check that the expected output files exist outputs_dir = Path.cwd() / "outputs" assert ( - outputs_dir / "profast_output_ammonia.yaml" + outputs_dir / "profast_output_ammonia_config.yaml" ).is_file(), "profast_output_ammonia.yaml not found" assert ( - outputs_dir / "profast_output_electricity.yaml" + outputs_dir / "profast_output_electricity_config.yaml" ).is_file(), "profast_output_electricity.yaml not found" assert ( - outputs_dir / "profast_output_hydrogen.yaml" + outputs_dir / "profast_output_hydrogen_config.yaml" ).is_file(), "profast_output_hydrogen.yaml not found" @@ -244,7 +244,7 @@ def test_co2h_methanol_example(subtests): # Check levelized cost of methanol (LCOM) with subtests.test("Check CO2 Hydrogenation LCOM"): - assert pytest.approx(model.prob.get_val("methanol.LCOM"), rel=1e-6) == 1.38341179 + assert pytest.approx(model.prob.get_val("methanol.LCOM")[0], rel=1e-6) == 1.38341179 def test_wind_h2_opt_example(subtests): @@ -491,7 +491,7 @@ def test_hydrogen_dispatch_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.LCOE", units="USD/MW/h")[0], + model.prob.get_val("financials_subgroup_elec.LCOE", units="USD/MW/h")[0], rel=1e-5, ) == 106.13987 @@ -500,7 +500,7 @@ def test_hydrogen_dispatch_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx( - model.prob.get_val("financials_group_default.LCOH", units="USD/kg")[0], + model.prob.get_val("financials_subgroup_h2.LCOH", units="USD/kg")[0], rel=1e-5, ) == 5.68452215 From 19dce3c717b909de1e307a90279be48b22e278bd Mon Sep 17 00:00:00 2001 From: kbrunik Date: Mon, 8 Sep 2025 08:34:04 -0500 Subject: [PATCH 33/67] remove unused import --- h2integrate/core/h2integrate_model.py | 2 -- h2integrate/core/test/test_framework.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 048b40b40..0daa71238 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -9,9 +9,7 @@ create_xdsm_from_config, determine_commodity_types_from_technology_names, ) -from h2integrate.core.feedstocks import FeedstockComponent from h2integrate.finances.finances import AdjustedCapexOpexComp - from h2integrate.core.resource_summer import ElectricitySumComp from h2integrate.core.supported_models import supported_models, electricity_producing_techs from h2integrate.core.inputs.validation import load_tech_yaml, load_plant_yaml, load_driver_yaml diff --git a/h2integrate/core/test/test_framework.py b/h2integrate/core/test/test_framework.py index 6fa8c05dc..e5b31f109 100644 --- a/h2integrate/core/test/test_framework.py +++ b/h2integrate/core/test/test_framework.py @@ -215,7 +215,7 @@ def test_technology_connections(): # Load the plant_config YAML content plant_config_data = load_plant_yaml(temp_plant_config) - new_connection = (["financials_group_default", "steel", ("LCOE", "electricity_cost")],) + new_connection = (["financials_subgroup_electricity", "steel", ("LCOE", "electricity_cost")],) new_tech_interconnections = ( plant_config_data["technology_interconnections"][0:4] + list(new_connection) From 0d4434dc340aedb22a41368b7474c45af66df68c Mon Sep 17 00:00:00 2001 From: kbrunik Date: Mon, 8 Sep 2025 08:42:28 -0500 Subject: [PATCH 34/67] update docstring --- h2integrate/core/h2integrate_model.py | 63 +++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 0daa71238..195f66648 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -291,14 +291,61 @@ def _process_model(self, model_type, individual_tech_config, tech_group): def create_financial_model(self): """ - Creates and configures the financial model for the plant. - - - If subgroups are defined in finance_parameters: - * The first tuple in each subgroup is (commodity, finance_model). - * The remaining tuples list technologies belonging to that subgroup. - - If no subgroups are defined: - * A default subgroup is created with all technologies. - * commodity and finance_model are pulled from finance_parameters. + Create and configure the financial model(s) for the plant. + + This method initializes financial subsystems for the plant based on the + configuration provided in ``self.plant_config["finance_parameters"]``. It + supports both default single-model setups and multiple subgroup-specific + financial models. + + Behavior: + * If ``finance_parameters`` is not defined in the plant configuration, + no financial model is created. + * If no subgroups are defined, all technologies are grouped together + under a default finance model. ``commodity`` and ``finance_model`` are + required in this case. + * If subgroups are provided, each subgroup defines its own set of + technologies, associated commodity, and financial model(s). + Each subgroup is nested under a unique name of your choice under + ["finance_parameters"]["subgroups"] in the plant configuration. + * Subsystems such as ``ElectricitySumComp``, ``AdjustedCapexOpexComp``, + and the selected financial models are added to each subgroup's + financial group. + * Supports both global finance models and technology-specific finance + models. Technology-specific finance models are defined in the technology + configuration. + + Raises: + ValueError: + If ``finance_parameters`` are incomplete (e.g., missing + ``commodity`` or ``finance_model``) when no subgroups are defined. + ValueError: + If a subgroup has no valid technologies. + ValueError: + If a specified financial model is not found in + ``self.supported_models``. + + Side Effects: + * Updates ``self.plant_config["finance_parameters"]`` if only a single + finance model is provided (wraps it in a default group). + * Constructs and attaches OpenMDAO financial subsystem groups to the + plant model under names ``financials_subgroup_``. + * Stores processed subgroup configurations in + ``self.financial_groups``. + + Example: + Suppose ``plant_config["finance_parameters"]`` defines a single finance + model without subgroups: + + >>> self.plant_config["finance_parameters"] = { + ... "commodity": "hydrogen", + ... "finance_model": "ProFastComp", + ... "model_inputs": {"discount_rate": 0.08}, + ... } + >>> self.create_financial_model() + # Creates a default subgroup containing all technologies and + # attaches a ProFAST financial model component to the plant. + """ if "finance_parameters" not in self.plant_config: From a4359ceca95db57bdcc159074b6dad847f9e09c1 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Mon, 8 Sep 2025 09:08:23 -0500 Subject: [PATCH 35/67] update ng example --- examples/16_natural_gas/plant_config.yaml | 1 + tests/h2integrate/test_all_examples.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/16_natural_gas/plant_config.yaml b/examples/16_natural_gas/plant_config.yaml index 025ad458a..a78b28cfd 100644 --- a/examples/16_natural_gas/plant_config.yaml +++ b/examples/16_natural_gas/plant_config.yaml @@ -38,6 +38,7 @@ plant: n_timesteps: 8760 finance_parameters: + commodity: "electricity" finance_model: "ProFastComp" model_inputs: params: diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index cfb03562e..26ff88467 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -615,7 +615,7 @@ def test_natural_gas_example(subtests): assert pytest.approx(total_opex, rel=1e-6) == 4849951.2195122 with subtests.test("Check LCOE"): - lcoe = model.prob.get_val("financials_group_default.LCOE")[0] + lcoe = model.prob.get_val("financials_subgroup_default.LCOE")[0] assert pytest.approx(lcoe, rel=1e-6) == 0.12959097 # Test feedstock-specific values From 651e891f914da81fbeb3bd763f333a3a1eb7f381 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 09:40:18 -0600 Subject: [PATCH 36/67] added new doc files for finance --- docs/_toc.yml | 9 + docs/finance_models/ProFastComp.md | 208 ++++++++++ docs/finance_models/finance_index.md | 3 + docs/user_guide/connecting_technologies.md | 19 +- docs/user_guide/cost_years.md | 59 +++ .../specifying_finance_parameters.md | 368 ++++++------------ 6 files changed, 413 insertions(+), 253 deletions(-) create mode 100644 docs/finance_models/ProFastComp.md create mode 100644 docs/finance_models/finance_index.md create mode 100644 docs/user_guide/cost_years.md diff --git a/docs/_toc.yml b/docs/_toc.yml index 3216fa577..205d5c096 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -17,6 +17,15 @@ parts: - file: user_guide/postprocessing_results - file: user_guide/how_to_interface_with_user_defined_model - file: user_guide/specifying_finance_parameters + sections: + - file: user_guide/cost_years + - file: finance_models/finance_index + sections: + - file: finance_models/ProFastComp + + + sections: + - caption: Technology Models chapters: diff --git a/docs/finance_models/ProFastComp.md b/docs/finance_models/ProFastComp.md new file mode 100644 index 000000000..dee1b102d --- /dev/null +++ b/docs/finance_models/ProFastComp.md @@ -0,0 +1,208 @@ + +(profastcomp:profastcompmodel)= +# ProFastComp +The ``"ProFastComp"`` finance model calculates levelized cost of commodity using ProFAST. + +The inputs for the `ProFastComp` model are outlined in this section: + +(profastcomp:overview)= +## Finance parameters overview +The main inputs for `ProFastComp` model include: +- optional: information to export the ProFAST config to a .yaml file +- required: financial parameters (`params` section). These can be input in the `ProFastComp` format or the `ProFAST` format. These two formats are described in the following sections: + - [ProFastComp format](profastcomp:direct_opt) + - [ProFAST format](profastcomp:pf_params_opt) +- required: default capital item parameters (`capital_items` section). These parameters can be over-written for specific technologies if specified in the `tech_config`. Example usage of over-writing values in the `tech_config` is outlined [here](profastcomp:tech_specific_finance) + +```yaml +finance_parameters: + finance_model: "ProFastComp" + model_inputs: #inputs for the finance_model + save_profast_to_file: True #optional, will save ProFAST entries to .yaml file in the folder specified in the driver_config (`driver_config["general"]["folder_output"]`) + profast_output_description: "profast_config" #required if `save_profast_to_file` is True, used to name the output file. + params: #Financial parameters section + capital_items: #Required: section for default parameters for capital items + depr_type: "MACRS" #Required: depreciation method for capital items, can be "MACRS" or "Straight line" + depr_period: 5 #Required: depreciation period for capital items + refurb: [0.] #Optional: replacement schedule as a fraction of the capital cost. Defaults to [0.] + fixed_costs: #Optional section for default parameters for fixed cost items + escalation: #escalation rate for fixed costs, will default to `inflation_rate` specific in the params section + unit: "$/year" #optional unit of the cost. Defaults to $/year + usage: 1.0 #usage multiplier, most commonly is set to 1 and defaults to 1.0 + +``` + +(profastcomp:direct_opt)= +## Providing Finance Parameters: ProFastComp Format +Below is an example inputting financial parameters directly in the `finance_parameters` section of `plant_config`: + +```yaml +finance_parameters: + finance_model: "ProFastComp" #finance model + model_inputs: #inputs for finance_model + params: #Financing parameters + analysis_start_year: 2032 #year that financial analysis starts + installation_time: 36 #installation period in months + # Inflation parameters + inflation_rate: 0.0 # 0 for nominal analysis + # Finance parameters + discount_rate: 0.09 + debt_equity_ratio: 2.62 + property_tax_and_insurance: 0.03 + total_income_tax_rate: 0.257 + capital_gains_tax_rate: 0.15 + sales_tax_rate: 0.07375 + debt_interest_rate: 0.07 + debt_type: "Revolving debt" #"Revolving debt" or "One time loan" + loan_period_if_used: 0 #loan period if debt_type is 'One time loan' + cash_onhand_months: 1 + admin_expense: 0.00 #administrative expense as a fraction of sales + #default parameters for capital items unless specified in tech_config + capital_items: + depr_type: "MACRS" #depreciation method for capital items, can be "MACRS" or "Straight line" + depr_period: 5 #depreciation period for capital items in years. + refurb: [0.] #refurbishment schedule, values represent the replacement cost as a fraction of the CapEx + + # To adjust costs for technologies to target_dollar_year + cost_adjustment_parameters: + target_dollar_year: 2022 + cost_year_adjustment_inflation: 0.025 +``` + +This approach also relies on data from `plant_config`: +- `plant_life`: used as the `operating life` ProFAST parameter + + +```{note} +`inflation_rate` is used to populate the escalation and inflation rates in ProFAST entries with a value of 0 corresponding to a *nominal analysis*. +``` + +(profastcomp:pf_params_opt)= +## Providing Finance Parameters: ProFAST format + +```{note} +To avoid errors, please check that `plant_config['plant']['plant_life']` is equal to `plant_config['finance_parameters']['model_inputs']['params']['operating life']`. Or remove `operating life` from the finance parameter inputs.` + + +| plant config parameter | equivalent `params` parameter | +| -------- | ------- | +| `plant['plant']['plant_life']` | `operating life` | +``` + +Below is an example of the `finance_parameters` section of `plant_config` if using ProFAST input format to specify financial parameters: + +```yaml +finance_parameters: + finance_model: "ProFastComp" + model_inputs: + params: !include "profast_params.yaml" #Finance information + capital_items: #default parameters for capital items unless specified in tech_config + depr_type: "MACRS" ##depreciation method for capital items, can be "MACRS" or "Straight line" + depr_period: 5 #depreciation period for capital items + refurb: [0.] + cost_adjustment_parameters: + target_dollar_year: 2022 + cost_year_adjustment_inflation: 0.025 # used to adjust costs for technologies to target_dollar_year +``` + +Below is an example of a valid ProFAST params config that may be specified in the `finance_parameters['model_inputs']['params]` section of `plant_config`: +```yaml +# Installation information +maintenance: + value: 0.0 + escalation: 0.0 +non depr assets: 250000 #such as land cost +end of proj sale non depr assets: 250000 #such as land cost +installation cost: + value: 0.0 + depr type: "Straight line" + depr period: 4 + depreciable: False +# Incentives information +incidental revenue: + value: 0.0 + escalation: 0.0 +annual operating incentive: + value: 0.0 + decay: 0.0 + sunset years: 0 + taxable: true +one time cap inct: + value: 0.0 + depr type: "MACRS" + depr period: 5 + depreciable: True +# Sales information +analysis start year: 2032 +operating life: 30 #if included, should equal plant_config['plant']['plant_life'] +installation months: 36 +demand rampup: 0 +# Take or pay specification +TOPC: + unit price: 0.0 + decay: 0.0 + support utilization: 0.0 + sunset years: 0 +# Other operating expenses +credit card fees: 0.0 +sales tax: 0.0 +road tax: + value: 0.0 + escalation: 0.0 +labor: + value: 0.0 + rate: 0.0 + escalation: 0.0 +rent: + value: 0.0 + escalation: 0.0 +license and permit: + value: 0.0 + escalation: 0.0 +admin expense: 0.0 +property tax and insurance: 0.015 +# Financing information +sell undepreciated cap: True +capital gains tax rate: 0.15 +total income tax rate: 0.2574 +leverage after tax nominal discount rate: 0.0948 +debt equity ratio of initial financing: 1.72 +debt interest rate: 0.046 +debt type: "Revolving debt" +general inflation rate: 0.0 +cash onhand: 1 # number of months with cash on-hand +tax loss carry forward years: 0 +tax losses monetized: True +loan period if used: 0 +``` + +(profastcomp:tech_specific_finance)= +## Over-ride defaults for specific technologies + +Capital item entries can be over-written for individual technologies. + +#### **Over-write depreciation period:** + +Suppose the default depreciation period for capital items is 5 years (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['depr_period']`), but we want the depreciation period for the electrolyzer to be 7 years. This can be done in the `tech_config` as shown below: +```yaml +technologies: + electrolyzer: + model_inputs: + finance_parameters: + capital_items: + depr_period: 7 +``` + + +#### **Custom refurbishment period:** + +Suppose the default refurbishment schedule for capital items is `[0.]` (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['refurb']`), but we want our battery to be replaced in 15-years and the replacement cost is equal to the capital cost. This can be accomplished in the tech_config as shown below: +```yaml +technologies: + battery: + model_inputs: + finance_parameters: + capital_items: + refurbishment_period_years: 15 + replacement_cost_percent: 1.0 +``` diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md new file mode 100644 index 000000000..c05612b65 --- /dev/null +++ b/docs/finance_models/finance_index.md @@ -0,0 +1,3 @@ + +# Finance Models Overview +- [``"ProFastComp"``](profastcomp:profastcompmodel) calculates levelized cost of commodity using ProFAST. diff --git a/docs/user_guide/connecting_technologies.md b/docs/user_guide/connecting_technologies.md index dc8f5c6b5..6d2ae23cb 100644 --- a/docs/user_guide/connecting_technologies.md +++ b/docs/user_guide/connecting_technologies.md @@ -16,6 +16,7 @@ Technology interconnections are defined as an array of arrays in your `plant_con technology_interconnections: [ ["source_tech", "destination_tech", "variable_name", "transport_type"], ["tech_a", "tech_b", "shared_parameter"], + ["tech_a", "tech_b", ["tech_a_param_name","tech_b_param_name"]], # ... more connections ] ``` @@ -33,6 +34,7 @@ There are two connection formats: - **transport_type**: The transport component to use (e.g., "cable", "pipeline") #### 3-element connections (direct connections) +##### Same shared parameter name ```yaml ["source_tech", "destination_tech", "shared_parameter"] ``` @@ -41,6 +43,17 @@ There are two connection formats: - **destination_tech**: Name of the technology receiving the input - **shared_parameter**: The exact parameter name to connect (e.g., "capacity_factor", "electrolyzer_degradation") +##### Different shared parameter names +```yaml +["source_tech", "destination_tech", ["source_parameter","destination_parameter]] +``` + +- **source_tech**: Name of the technology providing the output +- **destination_tech**: Name of the technology receiving the input +- **source_parameter**: The parameter name of ``"source_tech"`` +- **destination_parameter**: The parameter name of ``"destination_tech"`` + + ### Internal connection logic H2Integrate processes these connections in the `connect_technologies()` method of `h2integrate_model.py`. Here's what happens internally: @@ -95,12 +108,12 @@ No additional configuration parameters are needed - the combiner simply adds the ### Inputs and outputs - **Inputs**: - - `electricity_input1`: Power from the first source (kW) - - `electricity_input2`: Power from the second source (kW) + - `electricity_in1`: Power from the first source (kW) + - `electricity_in2`: Power from the second source (kW) - **Output**: - `electricity_out`: Combined power output (kW) -The relationship is straightforward: `electricity_out = electricity_input1 + electricity_input2` +The relationship is straightforward: `electricity_out = electricity_in1 + electricity_in2` ### Usage example diff --git a/docs/user_guide/cost_years.md b/docs/user_guide/cost_years.md new file mode 100644 index 000000000..244aa4d7c --- /dev/null +++ b/docs/user_guide/cost_years.md @@ -0,0 +1,59 @@ +(cost:cost_years)= +# Cost year of Cost Models +Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. + +## Cost models with inherent cost year + +### Summary of cost models that are based around a cost year +| Cost Model | Cost Year | +| :---------------------- | :---------------: | +| `basic_electrolyzer_cost`| 2016 | +| `pem_electrolyzer_cost`| 2021 | +| `singlitico_electrolyzer_cost`| 2021 | +| `h2_storage` with `'mch'` storage type | 2024 | +| `h2_storage` for geologic storage or buried pipe | 2018 | +| `simple_ammonia_cost` | 2022 | +| `direct_ocean_capture_cost` | 2023 | +| `ocean_alkalinity_enhancement_cost` | 2024 | +| `ocean_alkalinity_enhancement_cost_financial` | 2024 | +| `steel_cost` | 2022 | +| `reverse_osmosis_desalination_cost` | 2013 | +| `synloop_ammonia_cost` | N/A (adjusts costs to `target_dollar_year` within cost model) | + + +## Cost models with user input cost year + +### Summary of cost models that have user-input cost year +| Cost Model | +| :---------------------- | +| `wind_plant_cost` | +| `atb_utility_pv_cost` | +| `atb_comm_res_pv_cost` | +| `simple_ASU_cost` | +| `hopp` | +| `run_of_river_hydro_cost` | +| `smr_methanol_plant_cost` | +| `stimulated_geoh2_cost` | +| `natural_geoh2_cost` | +| `wombat` | +| `hydrogen_tank_cost` | +| `custom_electrolyzer_cost` | + +### Example tech_config input for user-input cost year +```yaml +technologies: + solar: + performance_model: + model: "pysam_solar_plant_performance" + cost_model: + model: "atb_utility_pv_cost" + model_inputs: + performance_parameters: + pv_capacity_kWdc: 100000 + dc_ac_ratio: 1.34 + ... + cost_parameters: + capex_per_kWac: 1044 + opex_per_kWac_per_year: 18 + cost_year: 2022 +``` diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 07ac0e84b..622626c83 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -6,277 +6,145 @@ The finance model, finance model specific inputs, and cost adjustment informatio The `plant_life` parameter from the `plant` section of the `plant_config` is also used in finance calculations as the operating life of the plant. ``` -The finance parameters section requires the following information: -- `finance_model`: name of the finance model. Current finance model options include: - - [`'ProFastComp'`](finance:profastcompmodel): calculates levelized cost of commodity using ProFAST -- `model_inputs`: inputs that are specific to the finance model +The `finance_parameters` section requires the following information: - `cost_adjustment_parameters`: - `target_dollar_year`: dollar-year to convert costs to. - - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](#cost-year-of-cost-models)) to the `target_dollar_year` + - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](cost:cost_years) to the `target_dollar_year` -(finance:profastcompmodel) -# Finance Parameters: ProFastComp -The inputs for the `ProFastComp` model are outlined in this section: +Other common variables to include in the `finance_parameters` section are: +- **finance_model** (string): Name of the general financial model to use. This should not be a technology specific finance model. +- **model_inputs** (dictionary): Input parameters for `finance_model` +- **commodity** (str): The commodity used for calculations in the `finance_model` +- **commodity_desc** (str): Optional additional description of the commodity. -(finance:overview)= -## Finance parameters overview -The main inputs for `ProFastComp` model include: -- optional: information to export the ProFAST config to a .yaml file -- required: financial parameters (`params` section). These can be input in the `ProFastComp` format or the `ProFAST` format. These two formats are described in the following sections: - - [ProFastComp format](finance:direct_opt) - - [ProFAST format](finance:pf_params_opt) -- required: default capital item parameters (`capital_items` section). These parameters can be over-written for specific technologies if specified in the `tech_config`. Example usage of over-writing values in the `tech_config` is outlined [here](finance:tech_specific_finance) -```yaml -finance_parameters: - finance_model: "ProFastComp" - model_inputs: #inputs for the finance_model - save_profast_to_file: True #optional, will save ProFAST entries to .yaml file in the folder specified in the driver_config (`driver_config["general"]["folder_output"]`) - profast_output_description: "profast_config" #required if `save_profast_to_file` is True, used to name the output file. - params: #Financial parameters section - capital_items: #Required: section for default parameters for capital items - depr_type: "MACRS" #Required: depreciation method for capital items, can be "MACRS" or "Straight line" - depr_period: 5 #Required: depreciation period for capital items - refurb: [0.] #Optional: replacement schedule as a fraction of the capital cost. Defaults to [0.] - fixed_costs: #Optional section for default parameters for fixed cost items - escalation: #escalation rate for fixed costs, will default to `inflation_rate` specific in the params section - unit: "$/year" #optional unit of the cost. Defaults to $/year - usage: 1.0 #usage multiplier, most commonly is set to 1 and defaults to 1.0 - -``` - -(finance:direct_opt)= -## Providing Finance Parameters: ProFastComp Format -Below is an example inputting financial parameters directly in the `finance_parameters` section of `plant_config`: +## Not specifying subgroups +no specified subgroups means you have to use 1 finance model and one commodity. +General format: ```yaml finance_parameters: - finance_model: "ProFastComp" #finance model - model_inputs: #inputs for finance_model - params: #Financing parameters - analysis_start_year: 2032 #year that financial analysis starts - installation_time: 36 #installation period in months - # Inflation parameters - inflation_rate: 0.0 # 0 for nominal analysis - # Finance parameters - discount_rate: 0.09 - debt_equity_ratio: 2.62 - property_tax_and_insurance: 0.03 - total_income_tax_rate: 0.257 - capital_gains_tax_rate: 0.15 - sales_tax_rate: 0.07375 - debt_interest_rate: 0.07 - debt_type: "Revolving debt" #"Revolving debt" or "One time loan" - loan_period_if_used: 0 #loan period if debt_type is 'One time loan' - cash_onhand_months: 1 - admin_expense: 0.00 #administrative expense as a fraction of sales - #default parameters for capital items unless specified in tech_config - capital_items: - depr_type: "MACRS" #depreciation method for capital items, can be "MACRS" or "Straight line" - depr_period: 5 #depreciation period for capital items in years. - refurb: [0.] #refurbishment schedule, values represent the replacement cost as a fraction of the CapEx - - # To adjust costs for technologies to target_dollar_year - cost_adjustment_parameters: - target_dollar_year: 2022 - cost_year_adjustment_inflation: 0.025 + commodity: "commodity_a" + finance_model: "finance_model_a" #ex: "ProFastComp" + model_inputs: #dictionary of inputs for finance_model_a ``` +- **commodity_a**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". +- **finance_model_a**: a general finance model. Ex: "ProFastComp". -This approach also relies on data from `plant_config`: -- `plant_life`: used as the `operating life` ProFAST parameter - +- output naming convention: + - `financials_subgroup_default.` +- examples that use this format: + - [Example 7](/examples/07_run_of_river_plant/plant_config.yaml) +- assumptions and logic: + - the financial model will include costs from all technologies in the `tech_config` -```{note} -`inflation_rate` is used to populate the escalation and inflation rates in ProFAST entries with a value of 0 corresponding to a *nominal analysis*. -``` -(finance:pf_params_opt)= -## Providing Finance Parameters: ProFAST format - -```{note} -To avoid errors, please check that `plant_config['plant']['plant_life']` is equal to `plant_config['finance_parameters']['model_inputs']['params']['operating life']`. Or remove `operating life` from the finance parameter inputs.` - - -| plant config parameter | equivalent `params` parameter | -| -------- | ------- | -| `plant['plant']['plant_life']` | `operating life` | -``` - -Below is an example of the `finance_parameters` section of `plant_config` if using ProFAST input format to specify financial parameters: +## Single financial model with subgroups +General format: ```yaml finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: !include "profast_params.yaml" #Finance information - capital_items: #default parameters for capital items unless specified in tech_config - depr_type: "MACRS" ##depreciation method for capital items, can be "MACRS" or "Straight line" - depr_period: 5 #depreciation period for capital items - refurb: [0.] - cost_adjustment_parameters: - target_dollar_year: 2022 - cost_year_adjustment_inflation: 0.025 # used to adjust costs for technologies to target_dollar_year -``` - -Below is an example of a valid ProFAST params config that may be specified in the `finance_parameters['model_inputs']['params]` section of `plant_config`: -```yaml -# Installation information -maintenance: - value: 0.0 - escalation: 0.0 -non depr assets: 250000 #such as land cost -end of proj sale non depr assets: 250000 #such as land cost -installation cost: - value: 0.0 - depr type: "Straight line" - depr period: 4 - depreciable: False -# Incentives information -incidental revenue: - value: 0.0 - escalation: 0.0 -annual operating incentive: - value: 0.0 - decay: 0.0 - sunset years: 0 - taxable: true -one time cap inct: - value: 0.0 - depr type: "MACRS" - depr period: 5 - depreciable: True -# Sales information -analysis start year: 2032 -operating life: 30 #if included, should equal plant_config['plant']['plant_life'] -installation months: 36 -demand rampup: 0 -# Take or pay specification -TOPC: - unit price: 0.0 - decay: 0.0 - support utilization: 0.0 - sunset years: 0 -# Other operating expenses -credit card fees: 0.0 -sales tax: 0.0 -road tax: - value: 0.0 - escalation: 0.0 -labor: - value: 0.0 - rate: 0.0 - escalation: 0.0 -rent: - value: 0.0 - escalation: 0.0 -license and permit: - value: 0.0 - escalation: 0.0 -admin expense: 0.0 -property tax and insurance: 0.015 -# Financing information -sell undepreciated cap: True -capital gains tax rate: 0.15 -total income tax rate: 0.2574 -leverage after tax nominal discount rate: 0.0948 -debt equity ratio of initial financing: 1.72 -debt interest rate: 0.046 -debt type: "Revolving debt" -general inflation rate: 0.0 -cash onhand: 1 # number of months with cash on-hand -tax loss carry forward years: 0 -tax losses monetized: True -loan period if used: 0 + finance_model: "finance_model_a" #ex: "ProFastComp" + model_inputs: #dictionary of inputs for finance_model_a + subgroups: + subgroup_a: + commodity: "commodity_a" #required + commodity_desc: "" #optional description of commodity_a + technologies: ["tech_a"] + subgroup_b: + commodity: "commodity_b" #required + commodity_desc: "" #optional description of commodity_b + technologies: ["tech_a","tech_b"] ``` -(finance:tech_specific_finance)= -## Over-ride defaults for specific technologies - -Capital item entries can be over-written for individual technologies. - -#### **Over-write depreciation period:** - -Suppose the default depreciation period for capital items is 5 years (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['depr_period']`), but we want the depreciation period for the electrolyzer to be 7 years. This can be done in the `tech_config` as shown below: -```yaml -technologies: - electrolyzer: - model_inputs: - finance_parameters: - capital_items: - depr_period: 7 -``` - - -#### **Custom refurbishment period:** - -Suppose the default refurbishment schedule for capital items is `[0.]` (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['refurb']`), but we want our battery to be replaced in 15-years and the replacement cost is equal to the capital cost. This can be accomplished in the tech_config as shown below: +- Format explained: + - **finance_model_a**: a general finance model. Ex: "ProFastComp". + - **subgroup_a**: + - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. + - **subgroup_b**: + - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. + - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + +- naming convention: `financials_subgroup_default.` +- examples that use this format: + - [Example 02](/examples/02_texas_ammonia/plant_config.yaml) + - [Example 03 - CO2H](/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) + - [Example 05](/examples/) + - 11, 12, 13, 14, 15, 17 + - [Example 09 - OAE](/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml) + - [Example 09 - DOC](/examples/09_co2/direct_ocean_capture/plant_config.yaml) + - [Example 09 - OAE Financials](/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml) +- assumptions and logic: + +## Nicknamed financial model with subgroups +This is an alternative format to [above](#single-financial-model-with-subgroups) that highlights some logic for [specifying multiple finance models](#multiple-financial-models-with-subgroups) + +General format: ```yaml -technologies: - battery: - model_inputs: - finance_parameters: - capital_items: - refurbishment_period_years: 15 - replacement_cost_percent: 1.0 +finance_parameters: + finance_model_nickname_a: + finance_model: "finance_model_a" #ex: "ProFastComp" + model_inputs: #dictionary of inputs for finance_model_a + subgroups: + subgroup_a: + commodity: "commodity_a" #required + commodity_desc: "" #optional description of commodity_a + finance_groups: [finance_model_nickname_a] #required in this case + technologies: ["tech_a"] + subgroup_b: + commodity: "commodity_b" #required + commodity_desc: "" #optional description of commodity_b + finance_groups: [finance_model_nickname_a] #required in this case + technologies: ["tech_b","tech_c"] ``` +- output naming convention: + - `financials_subgroup_.` + - `financials_subgroup_.` +- examples that use this format: + - [Example 01](/examples/01_onshore_steel_mn/plant_config.yaml) +- assumptions and logic: + - `subgroup_a` -# Cost year of Cost Models -Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. - -## Cost models with inherent cost year - -### Summary of cost models that are based around a cost year -| Cost Model | Cost Year | -| :---------------------- | :---------------: | -| `basic_electrolyzer_cost`| 2016 | -| `pem_electrolyzer_cost`| 2021 | -| `singlitico_electrolyzer_cost`| 2021 | -| `h2_storage` with `'mch'` storage type | 2024 | -| `h2_storage` for geologic storage or buried pipe | 2018 | -| `simple_ammonia_cost` | 2022 | -| `direct_ocean_capture_cost` | 2023 | -| `ocean_alkalinity_enhancement_cost` | 2024 | -| `ocean_alkalinity_enhancement_cost_financial` | 2024 | -| `steel_cost` | 2022 | -| `reverse_osmosis_desalination_cost` | 2013 | -| `synloop_ammonia_cost` | N/A (adjusts costs to `target_dollar_year` within cost model) | - +## Multiple financial models with subgroups +multiple financial models and/or multiple commodity types -## Cost models with user input cost year - -### Summary of cost models that have user-input cost year -| Cost Model | -| :---------------------- | -| `wind_plant_cost` | -| `atb_utility_pv_cost` | -| `atb_comm_res_pv_cost` | -| `simple_ASU_cost` | -| `hopp` | -| `run_of_river_hydro_cost` | -| `smr_methanol_plant_cost` | -| `stimulated_geoh2_cost` | -| `natural_geoh2_cost` | -| `wombat` | -| `hydrogen_tank_cost` | -| `custom_electrolyzer_cost` | - -### Example tech_config input for user-input cost year +General format: ```yaml -technologies: - solar: - performance_model: - model: "pysam_solar_plant_performance" - cost_model: - model: "atb_utility_pv_cost" - model_inputs: - performance_parameters: - pv_capacity_kWdc: 100000 - dc_ac_ratio: 1.34 - ... - cost_parameters: - capex_per_kWac: 1044 - opex_per_kWac_per_year: 18 - cost_year: 2022 - +finance_parameters: + finance_model_nickname_a: + finance_model: "finance_model_a" #ex: "ProFastComp" + model_inputs: #dictionary of inputs for the finance_model_a + finance_model_nickname_b: + finance_model: "finance_model_a" #ex: "ProFastComp" + model_inputs: #dictionary of inputs for finance_model_a + finance_model_nickname_c: + finance_model: "finance_model_c" #ex: "NPVFinancial" + model_inputs: #dictionary of inputs for finance_model_c + subgroups: + subgroup_a: + commodity: "commodity_a" #required + commodity_desc: "desc_a" #optional description of commodity_a + finance_groups: [finance_model_nickname_a] + technologies: ["tech_a"] + subgroup_b: + commodity: "commodity_a" #required + commodity_desc: "desc_b" #optional description of commodity_b + finance_groups: [finance_model_nickname_a, finance_model_nickname_b] + technologies: ["tech_b","tech_c"] + subgroup_c: + commodity: "commodity_b" #required + commodity_desc: "" #optional description of commodity_b + finance_groups: [finance_model_nickname_a, finance_model_nickname_c] + technologies: ["tech_b","tech_c"] ``` +- naming convention: + - `financials_subgroup_.price__` + - `financials_subgroup_.price___` + - `financials_subgroup_.price___` + - `financials_subgroup_.NPV__` + - `financials_subgroup_.NPV__` +- examples that use this format: + - [Example 10](/examples/10_electrolyzer_om/plant_config.yaml) +- assumptions and logic: From b6d329e8155b458598ea5be91bf28391151bd07d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 09:56:03 -0600 Subject: [PATCH 37/67] fixed failing tests --- examples/16_natural_gas/plant_config.yaml | 3 --- h2integrate/core/test/test_finances.py | 12 +++++++++--- tests/h2integrate/test_all_examples.py | 12 ++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/examples/16_natural_gas/plant_config.yaml b/examples/16_natural_gas/plant_config.yaml index a78b28cfd..9f5d21edc 100644 --- a/examples/16_natural_gas/plant_config.yaml +++ b/examples/16_natural_gas/plant_config.yaml @@ -31,9 +31,6 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.027498168 # based off correlations of LBNL PPA data - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True simulation: n_timesteps: 8760 diff --git a/h2integrate/core/test/test_finances.py b/h2integrate/core/test/test_finances.py index 1f01686c7..b2c7026c1 100644 --- a/h2integrate/core/test/test_finances.py +++ b/h2integrate/core/test/test_finances.py @@ -101,10 +101,13 @@ def test_modified_lcoe_calc(self): tech_config = load_tech_yaml(example_case_dir / "tech_config.yaml") plant_config = load_plant_yaml(example_case_dir / "plant_config.yaml") driver_config = load_driver_yaml(example_case_dir / "driver_config.yaml") + finance_inputs = plant_config["finance_parameters"].pop("profast_model") + plant_config_filtered = {k: v for k, v in plant_config.items() if k != "finance_parameters"} + plant_config_filtered.update({"finance_parameters": finance_inputs}) # Run ProFastComp with loaded configs prob = om.Problem() comp = ProFastComp( - plant_config=plant_config, + plant_config=plant_config_filtered, tech_config=tech_config["technologies"], driver_config=driver_config, commodity_type="electricity", @@ -137,14 +140,17 @@ def test_lcoe_with_selected_technologies(self): driver_config = load_driver_yaml(example_case_dir / "driver_config.yaml") # Only include HOPP and electrolyzer in metrics - plant_config["finance_parameters"]["technologies_included_in_metrics"]["LCOE"] = [ + plant_config["finance_parameters"]["subgroups"]["electricity"]["technologies"] = [ "hopp", "steel", ] + finance_inputs = plant_config["finance_parameters"].pop("profast_model") + plant_config_filtered = {k: v for k, v in plant_config.items() if k != "finance_parameters"} + plant_config_filtered.update({"finance_parameters": finance_inputs}) prob = om.Problem() comp = ProFastComp( - plant_config=plant_config, + plant_config=plant_config_filtered, tech_config=tech_config["technologies"], driver_config=driver_config, commodity_type="electricity", diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index 26ff88467..20b546bd4 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -584,34 +584,34 @@ def test_natural_gas_example(subtests): with subtests.test("Check total electricity produced"): total_electricity = model.prob.get_val( - "financials_group_default.electricity_sum.total_electricity_produced" + "financials_subgroup_default.electricity_sum.total_electricity_produced" )[0] assert pytest.approx(total_electricity, rel=1e-6) == 1.168e8 with subtests.test("Check opex adjusted ng_feedstock"): opex_ng_feedstock = model.prob.get_val( - "financials_group_default.opex_adjusted_ng_feedstock" + "financials_subgroup_default.opex_adjusted_ng_feedstock" )[0] assert pytest.approx(opex_ng_feedstock, rel=1e-6) == 3589463.41463415 with subtests.test("Check capex adjusted natural_gas_plant"): capex_ng_plant = model.prob.get_val( - "financials_group_default.capex_adjusted_natural_gas_plant" + "financials_subgroup_default.capex_adjusted_natural_gas_plant" )[0] assert pytest.approx(capex_ng_plant, rel=1e-6) == 97560975.60975611 with subtests.test("Check opex adjusted natural_gas_plant"): opex_ng_plant = model.prob.get_val( - "financials_group_default.opex_adjusted_natural_gas_plant" + "financials_subgroup_default.opex_adjusted_natural_gas_plant" )[0] assert pytest.approx(opex_ng_plant, rel=1e-6) == 1260487.80487805 with subtests.test("Check total adjusted CapEx"): - total_capex = model.prob.get_val("financials_group_default.total_capex_adjusted")[0] + total_capex = model.prob.get_val("financials_subgroup_default.total_capex_adjusted")[0] assert pytest.approx(total_capex, rel=1e-6) == 97658536.58536586 with subtests.test("Check total adjusted OpEx"): - total_opex = model.prob.get_val("financials_group_default.total_opex_adjusted")[0] + total_opex = model.prob.get_val("financials_subgroup_default.total_opex_adjusted")[0] assert pytest.approx(total_opex, rel=1e-6) == 4849951.2195122 with subtests.test("Check LCOE"): From 08504dca66888776a76a17ebba501bebc982bdda Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:10:07 -0600 Subject: [PATCH 38/67] updated example input files 1-3 and has co2h methanol example test working --- examples/01_onshore_steel_mn/plant_config.yaml | 5 ----- examples/02_texas_ammonia/plant_config.yaml | 5 ----- .../03_methanol/co2_hydrogenation/plant_config_co2h.yaml | 7 +------ .../03_methanol/co2_hydrogenation/tech_config_co2h.yaml | 5 ----- examples/03_methanol/smr/plant_config_smr.yaml | 4 ---- 5 files changed, 1 insertion(+), 25 deletions(-) diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index b869ddb33..4cc974e67 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -34,9 +32,6 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: profast_model: diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index efec39756..8cb26e91c 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -34,9 +32,6 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: finance_model: "ProFastComp" diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index 006185ec8..49c6554f8 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -4,8 +4,6 @@ description: "This plant is located west of the Wolf Hollow II NGCC power plant site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -32,9 +30,6 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: True # option, can be turned on or off - ppa_price: 0.027498168 # based off correlations of LBNL PPA data - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: finance_model: "ProFastComp" @@ -61,7 +56,7 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["hopp"] + technologies: ["hopp","electrolyzer"] hydrogen: commodity: "hydrogen" technologies: ["hopp","electrolyzer"] diff --git a/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml index 046230af5..e1e436813 100644 --- a/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml @@ -8,8 +8,6 @@ technologies: config: !include tech_inputs/hopp_config.yaml cost_model: model: "hopp" - financial_model: - group: "1" electrolyzer_rating: 156. # MW model_inputs: cost_parameters: @@ -19,8 +17,6 @@ technologies: model: "eco_pem_electrolyzer_performance" cost_model: model: "singlitico_electrolyzer_cost" - financial_model: - group: "1" model_inputs: shared_parameters: location: "onshore" @@ -48,7 +44,6 @@ technologies: model: "co2h_methanol_plant_cost" financial_model: model: "co2h_methanol_plant_financial" - group: "2" model_inputs: shared_parameters: plant_capacity_kgpy: 127893196.8 diff --git a/examples/03_methanol/smr/plant_config_smr.yaml b/examples/03_methanol/smr/plant_config_smr.yaml index 413a93de9..ccf2666a1 100644 --- a/examples/03_methanol/smr/plant_config_smr.yaml +++ b/examples/03_methanol/smr/plant_config_smr.yaml @@ -4,8 +4,6 @@ description: "This plant is located west of the Wolf Hollow II NGCC power plant site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -28,6 +26,4 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off ppa_price: 0.027498168 # based off correlations of LBNL PPA data - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True From c9e0766dd3c48a3c6061e01c57991ae12c841794 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Mon, 8 Sep 2025 11:27:04 -0500 Subject: [PATCH 39/67] LCOE requires all tech --- examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index 006185ec8..5c23968af 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -61,7 +61,7 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["hopp"] + technologies: ["hopp","electrolyzer"] hydrogen: commodity: "hydrogen" technologies: ["hopp","electrolyzer"] From 3b6a7311441120774c866f71be138878c55522c6 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:35:26 -0600 Subject: [PATCH 40/67] removed unused infor from example plant_configs --- examples/05_wind_h2_opt/plant_config.yaml | 5 ----- examples/07_run_of_river_plant/plant_config.yaml | 5 ----- examples/09_co2/direct_ocean_capture/plant_config.yaml | 5 ----- .../ocean_alkalinity_enhancement/plant_config.yaml | 5 ----- .../plant_config_financials.yaml | 5 ----- examples/11_hybrid_energy_plant/plant_config.yaml | 10 +++++----- examples/12_ammonia_synloop/plant_config.yaml | 5 ----- examples/13_air_separator/plant_config.yaml | 5 ----- .../14_wind_hydrogen_dispatch/inputs/plant_config.yaml | 6 ------ examples/15_wind_solar_electrolyzer/plant_config.yaml | 4 +--- examples/16_natural_gas/plant_config.yaml | 2 -- 11 files changed, 6 insertions(+), 51 deletions(-) diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index f1488126b..f6753bc38 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections diff --git a/examples/07_run_of_river_plant/plant_config.yaml b/examples/07_run_of_river_plant/plant_config.yaml index 92bb919a0..7a2cfbb3d 100644 --- a/examples/07_run_of_river_plant/plant_config.yaml +++ b/examples/07_run_of_river_plant/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in Kansas" site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -41,9 +39,6 @@ resource_to_tech_connections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.027498168 # based off correlations of LBNL PPA data - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: commodity: "electricity" diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 5e58689ac..60537f170 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in OR, USA" site: latitude: 43.807 longitude: -124.816 - elevation_m: 439.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index 737938cb5..2283f6402 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in OR, USA" site: latitude: 43.807 longitude: -124.816 - elevation_m: 439.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml index a8f586904..0f4f29f7d 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml @@ -4,8 +4,6 @@ description: "This plant is located in Puget Sound WA, USA" site: latitude: 48.153889 longitude: -122.774111 - elevation_m: 100 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 20 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True # array of arrays containing left-to-right technology # interconnections; can support bidirectional connections diff --git a/examples/11_hybrid_energy_plant/plant_config.yaml b/examples/11_hybrid_energy_plant/plant_config.yaml index b16635ac9..3bfb68186 100644 --- a/examples/11_hybrid_energy_plant/plant_config.yaml +++ b/examples/11_hybrid_energy_plant/plant_config.yaml @@ -4,8 +4,8 @@ description: "This plant is located in TX, USA near Sheffield" site: latitude: 30.6617 longitude: -101.7096 - elevation_m: 930.0 - time_zone: -5 + # elevation_m: 930.0 + # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +21,9 @@ site: plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - installation_time: 12 # months + # grid_connection: False # option, can be turned on or off + # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf + # installation_time: 12 # months finance_parameters: commodity: "electricity" diff --git a/examples/12_ammonia_synloop/plant_config.yaml b/examples/12_ammonia_synloop/plant_config.yaml index d77dd6cfc..32b342336 100644 --- a/examples/12_ammonia_synloop/plant_config.yaml +++ b/examples/12_ammonia_synloop/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - # elevation_m: 439.0 - # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -33,9 +31,6 @@ technology_interconnections: [ plant: plant_life: 30 - # grid_connection: False # option, can be turned on or off - # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - # hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: finance_model: "ProFastComp" diff --git a/examples/13_air_separator/plant_config.yaml b/examples/13_air_separator/plant_config.yaml index c5350f48d..23b851a48 100644 --- a/examples/13_air_separator/plant_config.yaml +++ b/examples/13_air_separator/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - elevation_m: 439.0 - time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -30,9 +28,6 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off - ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True finance_parameters: commodity: "nitrogen" diff --git a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml index 08b829108..9fe7fb9ab 100644 --- a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml +++ b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 47.5233 longitude: -92.5366 - # elevation_m: 439.0 - # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -31,10 +29,6 @@ technology_interconnections: [ plant: plant_life: 30 - # grid_connection: False # option, can be turned on or off - # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - # hybrid_electricity_estimated_cf: 0.492 #should equal 1 if grid_connection = True - finance_parameters: finance_model: "ProFastComp" diff --git a/examples/15_wind_solar_electrolyzer/plant_config.yaml b/examples/15_wind_solar_electrolyzer/plant_config.yaml index 557ccdc58..b409f6f55 100644 --- a/examples/15_wind_solar_electrolyzer/plant_config.yaml +++ b/examples/15_wind_solar_electrolyzer/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in MN, USA..." site: latitude: 30.6617 longitude: -101.7096 - elevation_m: 0.0 #unsure - time_zone: -5 #unsure year: 2013 # array of polygons defining boundaries with x/y coords @@ -35,10 +33,10 @@ technology_interconnections: [ plant: plant_life: 30 - grid_connection: False # option, can be turned on or off simulation: n_timesteps: 8760 dt: 3600 + finance_parameters: finance_model: "ProFastComp" model_inputs: diff --git a/examples/16_natural_gas/plant_config.yaml b/examples/16_natural_gas/plant_config.yaml index 9f5d21edc..65b1430cd 100644 --- a/examples/16_natural_gas/plant_config.yaml +++ b/examples/16_natural_gas/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in Texas" site: latitude: 32.34 longitude: -98.27 - elevation_m: 440.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ From 4b16b20420114e1f2211d9192cf786c70c525a6d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:36:52 -0600 Subject: [PATCH 41/67] deleted commented out lines in example 11 plant config --- examples/11_hybrid_energy_plant/plant_config.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/11_hybrid_energy_plant/plant_config.yaml b/examples/11_hybrid_energy_plant/plant_config.yaml index 3bfb68186..966e1ceb0 100644 --- a/examples/11_hybrid_energy_plant/plant_config.yaml +++ b/examples/11_hybrid_energy_plant/plant_config.yaml @@ -4,8 +4,6 @@ description: "This plant is located in TX, USA near Sheffield" site: latitude: 30.6617 longitude: -101.7096 - # elevation_m: 930.0 - # time_zone: -5 # array of polygons defining boundaries with x/y coords boundaries: [ @@ -21,9 +19,6 @@ site: plant: plant_life: 30 - # grid_connection: False # option, can be turned on or off - # ppa_price: 0.025 # $/kWh based on 2022 land based wind market report (ERCOT area ppa prices) https://www.energy.gov/sites/default/files/2022-08/land_based_wind_market_report_2202.pdf - # installation_time: 12 # months finance_parameters: commodity: "electricity" From 1d7bf9e1fd0a77fef8e7ad670604b20b946dbede Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Mon, 8 Sep 2025 12:30:00 -0600 Subject: [PATCH 42/67] tried to add more description to finance docs --- docs/finance_models/ProFastComp.md | 27 +++- docs/finance_models/finance_index.md | 23 +++- .../specifying_finance_parameters.md | 118 +++++++++++++----- 3 files changed, 132 insertions(+), 36 deletions(-) diff --git a/docs/finance_models/ProFastComp.md b/docs/finance_models/ProFastComp.md index dee1b102d..3964ec8ba 100644 --- a/docs/finance_models/ProFastComp.md +++ b/docs/finance_models/ProFastComp.md @@ -3,7 +3,13 @@ # ProFastComp The ``"ProFastComp"`` finance model calculates levelized cost of commodity using ProFAST. -The inputs for the `ProFastComp` model are outlined in this section: +The inputs, outputs, and naming convention for the `ProFastComp` model are outlined in this section. +- [Input parameters](profastcomp:overview) + - [H2Integrate format](profastcomp:direct_opt) + - [ProFAST format](profastcomp:pf_params_opt) + - [Override defaults for specific technologies](profastcomp:tech_specific_finance) +- [Output values and naming convention](profastcomp:outputs) + (profastcomp:overview)= ## Finance parameters overview @@ -206,3 +212,22 @@ technologies: refurbishment_period_years: 15 replacement_cost_percent: 1.0 ``` + +(profastcomp:outputs)= +## Output values and naming convention +``ProFastComp`` outputs the following data: +- `LCO`: levelized cost of commodity in USD/commodity unit. +- `wacc_`: weighted average cost of capital as a fraction. +- `crf_`: capital recovery factor as a fraction. +- `irr_`: internal rate of return as a fraction. +- `profit_index_` +- `investor_payback_period_`: time until initial investment costs are recovered in years. +- `price_`: first year price of commodity in same units as levelized cost. + +**Naming convention**: +- ``: + - if `commodity_desc` is **not** provided, then `` this is just `commodity`. For example, `wacc_hydrogen` if the `commodity` is `"hydrogen"`. + - if `commodity_desc` is provided, then `` is `_`. For example, `wacc_hydrogen_produced` if the `commodity` is `"hydrogen"` and `commodity_desc` is `"produced"` +- ``: + - if `commodity_desc` is **not** provided, then `` is the upper-case first letter of the `commodity`. For example, `LCOH` if the `commodity` is `"hydrogen"` + - if `commodity_desc` is provided, then `` is the upper-case first letter of the `commodity` followed by the `commodity_desc` descriptor. For example, `LCOH_produced` if the `commodity` is `"hydrogen"` and the `commodity_desc` is `"produced"`. diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md index c05612b65..73710e87f 100644 --- a/docs/finance_models/finance_index.md +++ b/docs/finance_models/finance_index.md @@ -1,3 +1,24 @@ +(finance:overview)= # Finance Models Overview -- [``"ProFastComp"``](profastcomp:profastcompmodel) calculates levelized cost of commodity using ProFAST. + +**General finance models** are not technology specific finance models. These models live in the `h2integrate/finances/` folder. These models are input the `driver_config`, `tech_config`, `plant_config`, `commodity_type`, and a `description` as the options. + +- `driver_config` (dict): the `folder_outputs` specified here may be used by the finance model if the finance model outputs data to a file. +- `tech_config` (dict): the technology configs for the technologies to include in the finance calculations +- `plant_config` (dict): contains the `model_inputs` for the finance model. +- `commodity_type` (str): the name of the commodity to use in the finance calculation. +- `description` (str, optional): an additional description to use for naming outputs of the finance model. + +```{note} +The `commodity_type` and `description` are used in the finance model naming convention. Specifics on the output naming convention for each finance model can be found in their docs. +``` + +```{note} +The `tech_config` and `plant_config` may be reformatted from their original format within `H2IntegrateModel` when creating the finance models. +``` + +(finance:supportedmodels)= +## Currently supported general finance models + +- [``"ProFastComp"``](profastcomp:profastcompmodel): calculates levelized cost of commodity using ProFAST. diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 622626c83..61b8ccc49 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -6,7 +6,7 @@ The finance model, finance model specific inputs, and cost adjustment informatio The `plant_life` parameter from the `plant` section of the `plant_config` is also used in finance calculations as the operating life of the plant. ``` -The `finance_parameters` section requires the following information: +The `finance_parameters` section always requires the following information: - `cost_adjustment_parameters`: - `target_dollar_year`: dollar-year to convert costs to. - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](cost:cost_years) to the `target_dollar_year` @@ -14,12 +14,19 @@ The `finance_parameters` section requires the following information: Other common variables to include in the `finance_parameters` section are: - **finance_model** (string): Name of the general financial model to use. This should not be a technology specific finance model. - **model_inputs** (dictionary): Input parameters for `finance_model` -- **commodity** (str): The commodity used for calculations in the `finance_model` -- **commodity_desc** (str): Optional additional description of the commodity. +- **commodity** (str): The commodity used for calculations in the `finance_model`. This is input into general finance models as the `commodity_type` (see [here](finance:overview) for details on general finance models input). +- **commodity_desc** (str): Optional additional description of the commodity. This is input into the general finance model as the `description` if a single finance model is used for a subgroup (see [here](finance:overview) for details on general finance models inputs). This is not used if subgroups are not specified. +These variables may be included in `finance_parameters` in different formats depending on the use-case. The different formatting options are: +- [Without subgroups](finparam:nosubgroups) +- [Single finance model with subgroups](finparams:singlemodelsubgroups) +- [Nicknamed single finance model with subgroups](finparams:namedsinglemodelsubgroups) +- [Multiple finance models with subgroups](finparams:multimodelsubgroups) + +(finparam:nosubgroups)= ## Not specifying subgroups -no specified subgroups means you have to use 1 finance model and one commodity. +If no `subgroups` are specified, the user is limited to a single general finance model and a single commodity. The finance model will include costs from all the technologies included in the `tech_config` file. General format: ```yaml @@ -28,18 +35,19 @@ finance_parameters: finance_model: "finance_model_a" #ex: "ProFastComp" model_inputs: #dictionary of inputs for finance_model_a ``` -- **commodity_a**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". -- **finance_model_a**: a general finance model. Ex: "ProFastComp". +- Format explained: + - **commodity_a**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". + - **finance_model_a**: a general finance model. Ex: "ProFastComp". - output naming convention: - - `financials_subgroup_default.` + - `financials_subgroup_default.` where `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the commodity (`commodity_a` in this example). - examples that use this format: - - [Example 7](/examples/07_run_of_river_plant/plant_config.yaml) -- assumptions and logic: - - the financial model will include costs from all technologies in the `tech_config` + - [Example 7](https://github.com/NREL/H2Integrate/blob/develop/examples/07_run_of_river_plant/plant_config.yaml) +(finparams:singlemodelsubgroups)= ## Single financial model with subgroups +This format assumes that all subgroups use the same finance model. The `finance_groups` parameter for the `subgroups` is not required. General format: ```yaml @@ -66,19 +74,23 @@ finance_parameters: - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. -- naming convention: `financials_subgroup_default.` +- output naming convention: + - `financials_subgroup_.` + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **commodity_desc** for **subgroup_a**. (which is an empty string in this example). + - `financials_subgroup_.` + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **commodity_desc** for **subgroup_b**. (which is an empty string in this example). - examples that use this format: - - [Example 02](/examples/02_texas_ammonia/plant_config.yaml) - - [Example 03 - CO2H](/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) - - [Example 05](/examples/) - - 11, 12, 13, 14, 15, 17 - - [Example 09 - OAE](/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml) - - [Example 09 - DOC](/examples/09_co2/direct_ocean_capture/plant_config.yaml) - - [Example 09 - OAE Financials](/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml) -- assumptions and logic: - + - [Example 02](https://github.com/NREL/H2Integrate/tree/develop/examples/02_texas_ammonia/plant_config.yaml) + - [Example 03 - CO2H](https://github.com/NREL/H2Integrate/tree/develop/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) + - [Example 09 - OAE](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml) + - [Example 09 - DOC](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/direct_ocean_capture/plant_config.yaml) + - [Example 09 - OAE Financials](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml) + + +(finparams:namedsinglemodelsubgroups)= ## Nicknamed financial model with subgroups -This is an alternative format to [above](#single-financial-model-with-subgroups) that highlights some logic for [specifying multiple finance models](#multiple-financial-models-with-subgroups) +This is an alternative format to [above](finparams:singlemodelsubgroups) that highlights some logic for [specifying multiple finance models](finparams:multimodelsubgroups). In this case, the `finance_groups` parameter is required for each `subgroup`. General format: ```yaml @@ -99,16 +111,29 @@ finance_parameters: technologies: ["tech_b","tech_c"] ``` +- Format explained: + - **finance_model_nickname_a**: the name used to reference a specific finance model and its corresponding inputs. + - **finance_model_a**: a general finance model. Ex: "ProFastComp". + - **subgroup_a**: + - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. + - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_a** finance calculations. + - **subgroup_b**: + - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. + - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_b** finance calculations. - output naming convention: - - `financials_subgroup_.` + - `financials_subgroup_.` + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **commodity_desc** for **subgroup_a**. (which is an empty string in this example). - `financials_subgroup_.` + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **commodity_desc** for **subgroup_b**. (which is an empty string in this example). - examples that use this format: - - [Example 01](/examples/01_onshore_steel_mn/plant_config.yaml) -- assumptions and logic: - - `subgroup_a` + - [Example 01](https://github.com/NREL/H2Integrate/blob/develop/examples/01_onshore_steel_mn/plant_config.yaml) + +(finparams:multimodelsubgroups)= ## Multiple financial models with subgroups -multiple financial models and/or multiple commodity types +This format is used to specify multiple general finance models and/or multiple commodity types. General format: ```yaml @@ -118,7 +143,7 @@ finance_parameters: model_inputs: #dictionary of inputs for the finance_model_a finance_model_nickname_b: finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for finance_model_a + model_inputs: #dictionary of inputs for finance_model_a, these may be different than the model inputs for `finance_model_nickname_a` finance_model_nickname_c: finance_model: "finance_model_c" #ex: "NPVFinancial" model_inputs: #dictionary of inputs for finance_model_c @@ -139,12 +164,37 @@ finance_parameters: finance_groups: [finance_model_nickname_a, finance_model_nickname_c] technologies: ["tech_b","tech_c"] ``` +- Format explained: + - **finance_model_nickname_a**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. + - **finance_model_nickname_b**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. + - **finance_model_nickname_c**: the name used to reference a specific finance model (`finance_model_c` in this example) and its corresponding inputs. + - **finance_model_a**: a general finance model. Ex: "ProFastComp". + - **finance_model_c**: a general finance model. Ex: "ProFastComp". + - **subgroup_a**: + - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a` (defined by **finance_model_nickname_a**) + - **desc_a**: some descriptor for the finance output parameters from the **subgroup_a** financial model(s) + - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_a** finance calculations. + - **subgroup_b**: + - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a` (defined by **finance_model_nickname_a** and **finance_model_nickname_b**) + - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + - **desc_b**: some descriptor for the finance output parameters from the **subgroup_b** financial model(s) + - **finance_model_nickname_a** and **finance_model_nickname_b**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_b** finance calculations. + - **subgroup_c**: + - **commodity_b**: commodity produced by either `tech_b` and/or `tech_c` to use in the financial calculations done in `finance_model_a` (defined by **finance_model_nickname_a**) and `finance_model_c` (defined by **finance_model_nickname_c**). + - **tech_b** and **tech_c**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_c`. + - **finance_model_nickname_a** and **finance_model_nickname_c**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_c** finance calculations. - naming convention: - - `financials_subgroup_.price__` - - `financials_subgroup_.price___` - - `financials_subgroup_.price___` - - `financials_subgroup_.NPV__` - - `financials_subgroup_.NPV__` + - **subgroup_a** outputs: suppose `finance_model_a` outputs `price` parameter. + - `financials_subgroup_.price__` + - **subgroup_b** outputs: suppose `finance_model_a` outputs `price` parameter. + - `financials_subgroup_.price___` + - `financials_subgroup_.price___` + - **subgroup_c** outputs: suppose `finance_model_a` outputs `price` parameter and `finance_model_c` outputs `NPV` parameter. + - `financials_subgroup_.NPV__` + - `financials_subgroup_.NPV__` + - `financials_subgroup_.price__` + - `financials_subgroup_.price__` + - examples that use this format: - - [Example 10](/examples/10_electrolyzer_om/plant_config.yaml) -- assumptions and logic: + - [Example 10](https://github.com/NREL/H2Integrate/blob/develop/examples/10_electrolyzer_om/plant_config.yaml) From e0ced20ff83637699fbcaea7e2a6a72631d8da38 Mon Sep 17 00:00:00 2001 From: John Jasa Date: Mon, 8 Sep 2025 16:50:55 -0600 Subject: [PATCH 43/67] Nitpicky doc changes --- .../specifying_finance_parameters.md | 108 +++++++++--------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 61b8ccc49..7b320918b 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -1,5 +1,8 @@ # Finance Parameters +This doc page describes how to specify finance parameters for different analyses and explains when to use different ways of setting up finance groups. +The finance parameters are an important part of the plant configuration as they define the financial aspects of the plant's operation and investment. + The finance model, finance model specific inputs, and cost adjustment information (regardless of the finance model) are required in the `finance_parameters` of the `plant_config`. ```{note} @@ -9,23 +12,24 @@ The `plant_life` parameter from the `plant` section of the `plant_config` is als The `finance_parameters` section always requires the following information: - `cost_adjustment_parameters`: - `target_dollar_year`: dollar-year to convert costs to. - - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](cost:cost_years) to the `target_dollar_year` + - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](cost:cost_years)) to the `target_dollar_year` Other common variables to include in the `finance_parameters` section are: -- **finance_model** (string): Name of the general financial model to use. This should not be a technology specific finance model. -- **model_inputs** (dictionary): Input parameters for `finance_model` -- **commodity** (str): The commodity used for calculations in the `finance_model`. This is input into general finance models as the `commodity_type` (see [here](finance:overview) for details on general finance models input). -- **commodity_desc** (str): Optional additional description of the commodity. This is input into the general finance model as the `description` if a single finance model is used for a subgroup (see [here](finance:overview) for details on general finance models inputs). This is not used if subgroups are not specified. +- **`finance_model`** (string): Name of the general financial model to use. This should not be a technology specific finance model. +- **`model_inputs`** (dictionary): Input parameters for `finance_model` +- **`commodity`** (str): The commodity used for calculations in the `finance_model`. This is input into general finance models as the `commodity_type` (see [here](finance:overview) for details on general finance models input). +- **`commodity_desc`** (str): Optional additional description of the commodity. This is input into the general finance model as the `description` if a single finance model is used for a subgroup (see [here](finance:overview) for details on general finance models inputs). This is not used if subgroups are not specified. These variables may be included in `finance_parameters` in different formats depending on the use-case. The different formatting options are: -- [Without subgroups](finparam:nosubgroups) +- [Without specifying subgroups](finparam:nosubgroups) - [Single finance model with subgroups](finparams:singlemodelsubgroups) - [Nicknamed single finance model with subgroups](finparams:namedsinglemodelsubgroups) - [Multiple finance models with subgroups](finparams:multimodelsubgroups) +We'll now walk through explanations for each of these formats, showing details for each type and providing links to examples that use that format. (finparam:nosubgroups)= -## Not specifying subgroups +## Without specifying subgroups If no `subgroups` are specified, the user is limited to a single general finance model and a single commodity. The finance model will include costs from all the technologies included in the `tech_config` file. General format: @@ -37,8 +41,8 @@ finance_parameters: ``` - Format explained: - - **commodity_a**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". - - **finance_model_a**: a general finance model. Ex: "ProFastComp". + - **`commodity_a`**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". + - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". - output naming convention: - `financials_subgroup_default.` where `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the commodity (`commodity_a` in this example). - examples that use this format: @@ -66,19 +70,19 @@ finance_parameters: ``` - Format explained: - - **finance_model_a**: a general finance model. Ex: "ProFastComp". - - **subgroup_a**: - - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. - - **subgroup_b**: - - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. - - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". + - **`subgroup_a`**: + - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. + - **`subgroup_b`**: + - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. + - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - output naming convention: - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **commodity_desc** for **subgroup_a**. (which is an empty string in this example). + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **`commodity_desc`** for **`subgroup_a`**. (which is an empty string in this example). - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **commodity_desc** for **subgroup_b**. (which is an empty string in this example). + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **`commodity_desc`** for **`subgroup_b`**. (which is an empty string in this example). - examples that use this format: - [Example 02](https://github.com/NREL/H2Integrate/tree/develop/examples/02_texas_ammonia/plant_config.yaml) - [Example 03 - CO2H](https://github.com/NREL/H2Integrate/tree/develop/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) @@ -112,21 +116,21 @@ finance_parameters: ``` - Format explained: - - **finance_model_nickname_a**: the name used to reference a specific finance model and its corresponding inputs. - - **finance_model_a**: a general finance model. Ex: "ProFastComp". - - **subgroup_a**: - - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. - - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_a** finance calculations. - - **subgroup_b**: - - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. - - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_b** finance calculations. + - **`finance_model_nickname_a`**: the name used to reference a specific finance model and its corresponding inputs. + - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". + - **`subgroup_a`**: + - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. + - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_a`** finance calculations. + - **`subgroup_b`**: + - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. + - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_b`** finance calculations. - output naming convention: - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **commodity_desc** for **subgroup_a**. (which is an empty string in this example). + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **`commodity_desc`** for **`subgroup_a`**. (which is an empty string in this example). - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **commodity_desc** for **subgroup_b**. (which is an empty string in this example). + - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **`commodity_desc`** for **`subgroup_b`**. (which is an empty string in this example). - examples that use this format: - [Example 01](https://github.com/NREL/H2Integrate/blob/develop/examples/01_onshore_steel_mn/plant_config.yaml) @@ -165,32 +169,32 @@ finance_parameters: technologies: ["tech_b","tech_c"] ``` - Format explained: - - **finance_model_nickname_a**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. - - **finance_model_nickname_b**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. - - **finance_model_nickname_c**: the name used to reference a specific finance model (`finance_model_c` in this example) and its corresponding inputs. - - **finance_model_a**: a general finance model. Ex: "ProFastComp". - - **finance_model_c**: a general finance model. Ex: "ProFastComp". - - **subgroup_a**: - - **commodity_a**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **tech_a**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a` (defined by **finance_model_nickname_a**) - - **desc_a**: some descriptor for the finance output parameters from the **subgroup_a** financial model(s) - - **finance_model_nickname_a**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_a** finance calculations. - - **subgroup_b**: - - **commodity_b**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a` (defined by **finance_model_nickname_a** and **finance_model_nickname_b**) - - **tech_a** and **tech_b**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - - **desc_b**: some descriptor for the finance output parameters from the **subgroup_b** financial model(s) - - **finance_model_nickname_a** and **finance_model_nickname_b**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_b** finance calculations. - - **subgroup_c**: - - **commodity_b**: commodity produced by either `tech_b` and/or `tech_c` to use in the financial calculations done in `finance_model_a` (defined by **finance_model_nickname_a**) and `finance_model_c` (defined by **finance_model_nickname_c**). - - **tech_b** and **tech_c**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_c`. - - **finance_model_nickname_a** and **finance_model_nickname_c**: the nickname(s) of the finance model and corresponding inputs to use for **subgroup_c** finance calculations. + - **`finance_model_nickname_a`**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. + - **`finance_model_nickname_b`**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. + - **`finance_model_nickname_c`**: the name used to reference a specific finance model (`finance_model_c` in this example) and its corresponding inputs. + - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". + - **`finance_model_c`**: a general finance model. Ex: "ProFastComp". + - **`subgroup_a`**: + - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. + - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a` (defined by **`finance_model_nickname_a`**) + - **`desc_a`**: some descriptor for the finance output parameters from the **`subgroup_a`** financial model(s) + - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_a`** finance calculations. + - **`subgroup_b`**: + - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a` (defined by **`finance_model_nickname_a`** and **`finance_model_nickname_b`**) + - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. + - **`desc_b`**: some descriptor for the finance output parameters from the **`subgroup_b`** financial model(s) + - **`finance_model_nickname_a`** and **`finance_model_nickname_b`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_b`** finance calculations. + - **`subgroup_c`**: + - **`commodity_b`**: commodity produced by either `tech_b` and/or `tech_c` to use in the financial calculations done in `finance_model_a` (defined by **`finance_model_nickname_a`**) and `finance_model_c` (defined by **`finance_model_nickname_c`**). + - **`tech_b`** and **`tech_c`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_c`. + - **`finance_model_nickname_a`** and **`finance_model_nickname_c`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_c`** finance calculations. - naming convention: - - **subgroup_a** outputs: suppose `finance_model_a` outputs `price` parameter. + - **`subgroup_a`** outputs: suppose `finance_model_a` outputs `price` parameter. - `financials_subgroup_.price__` - - **subgroup_b** outputs: suppose `finance_model_a` outputs `price` parameter. + - **`subgroup_b`** outputs: suppose `finance_model_a` outputs `price` parameter. - `financials_subgroup_.price___` - `financials_subgroup_.price___` - - **subgroup_c** outputs: suppose `finance_model_a` outputs `price` parameter and `finance_model_c` outputs `NPV` parameter. + - **`subgroup_c`** outputs: suppose `finance_model_a` outputs `price` parameter and `finance_model_c` outputs `NPV` parameter. - `financials_subgroup_.NPV__` - `financials_subgroup_.NPV__` - `financials_subgroup_.price__` From 84e43528d22e1a7403226c19e5a29317d0c758c4 Mon Sep 17 00:00:00 2001 From: John Jasa Date: Tue, 9 Sep 2025 00:05:25 -0600 Subject: [PATCH 44/67] Minor doc and logic cleanup for finance models --- docs/_toc.yml | 4 --- docs/finance_models/ProFastComp.md | 26 ++++++-------- docs/finance_models/finance_index.md | 2 +- docs/user_guide/connecting_technologies.md | 8 ++--- docs/user_guide/cost_years.md | 2 +- .../specifying_finance_parameters.md | 8 ++--- .../01_onshore_steel_mn/plant_config.yaml | 4 +-- examples/01_onshore_steel_mn/tech_config.yaml | 1 - examples/02_texas_ammonia/plant_config.yaml | 4 +-- .../co2_hydrogenation/plant_config_co2h.yaml | 4 +-- examples/05_wind_h2_opt/plant_config.yaml | 4 +-- .../direct_ocean_capture/plant_config.yaml | 2 +- .../plant_config.yaml | 2 +- examples/10_electrolyzer_om/plant_config.yaml | 4 +-- examples/12_ammonia_synloop/plant_config.yaml | 4 +-- .../inputs/plant_config.yaml | 4 +-- .../plant_config.yaml | 6 ++-- .../17_splitter_wind_doc_h2/plant_config.yaml | 4 +-- h2integrate/core/h2integrate_model.py | 12 +++---- h2integrate/core/inputs/plant_schema.yaml | 4 +-- h2integrate/core/utilities.py | 4 +++ h2integrate/finances/finances.py | 35 ++++++++++++++++--- h2integrate/finances/profast_financial.py | 12 +++---- .../iron/rosner_override/finance_model.py | 2 +- 24 files changed, 89 insertions(+), 73 deletions(-) diff --git a/docs/_toc.yml b/docs/_toc.yml index cb0a4e36a..21d267b6f 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -23,10 +23,6 @@ parts: sections: - file: finance_models/ProFastComp - - sections: - - - caption: Technology Models chapters: - file: technology_models/feedstocks diff --git a/docs/finance_models/ProFastComp.md b/docs/finance_models/ProFastComp.md index 3964ec8ba..d73040015 100644 --- a/docs/finance_models/ProFastComp.md +++ b/docs/finance_models/ProFastComp.md @@ -1,24 +1,19 @@ (profastcomp:profastcompmodel)= # ProFastComp -The ``"ProFastComp"`` finance model calculates levelized cost of commodity using ProFAST. +The `ProFastComp` finance model calculates levelized cost of commodity using [ProFAST](https://github.com/NREL/ProFAST). -The inputs, outputs, and naming convention for the `ProFastComp` model are outlined in this section. -- [Input parameters](profastcomp:overview) - - [H2Integrate format](profastcomp:direct_opt) - - [ProFAST format](profastcomp:pf_params_opt) - - [Override defaults for specific technologies](profastcomp:tech_specific_finance) -- [Output values and naming convention](profastcomp:outputs) +The inputs, outputs, and naming convention for the `ProFastComp` model are outlined in this doc page. (profastcomp:overview)= ## Finance parameters overview The main inputs for `ProFastComp` model include: -- optional: information to export the ProFAST config to a .yaml file - required: financial parameters (`params` section). These can be input in the `ProFastComp` format or the `ProFAST` format. These two formats are described in the following sections: - [ProFastComp format](profastcomp:direct_opt) - [ProFAST format](profastcomp:pf_params_opt) -- required: default capital item parameters (`capital_items` section). These parameters can be over-written for specific technologies if specified in the `tech_config`. Example usage of over-writing values in the `tech_config` is outlined [here](profastcomp:tech_specific_finance) +- required: default capital item parameters (`capital_items` section). These parameters can be overridden for specific technologies if specified in the `tech_config`. Example usage of overriding values in the `tech_config` is outlined [here](profastcomp:tech_specific_finance) +- optional: information to export the ProFAST config to a .yaml file ```yaml finance_parameters: @@ -183,11 +178,12 @@ loan period if used: 0 ``` (profastcomp:tech_specific_finance)= -## Over-ride defaults for specific technologies +## Override defaults for specific technologies -Capital item entries can be over-written for individual technologies. +Capital item entries can be overridden for individual technologies. +This means that specific technologies can have different financial parameters defined in `tech_config` than the defaults set in the `plant_config`. -#### **Over-write depreciation period:** +### **Override depreciation period:** Suppose the default depreciation period for capital items is 5 years (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['depr_period']`), but we want the depreciation period for the electrolyzer to be 7 years. This can be done in the `tech_config` as shown below: ```yaml @@ -200,7 +196,7 @@ technologies: ``` -#### **Custom refurbishment period:** +### **Custom refurbishment period:** Suppose the default refurbishment schedule for capital items is `[0.]` (set in the `plant_config['finance_parameters']['model_inputs]['capital_items']['refurb']`), but we want our battery to be replaced in 15-years and the replacement cost is equal to the capital cost. This can be accomplished in the tech_config as shown below: ```yaml @@ -215,8 +211,8 @@ technologies: (profastcomp:outputs)= ## Output values and naming convention -``ProFastComp`` outputs the following data: -- `LCO`: levelized cost of commodity in USD/commodity unit. +``ProFastComp`` outputs the following data following the naming convention detailed below: +- `LCO`: levelized cost of commodity in USD/commodity unit, e.g. `LCOH_produced` for hydrogen produced. - `wacc_`: weighted average cost of capital as a fraction. - `crf_`: capital recovery factor as a fraction. - `irr_`: internal rate of return as a fraction. diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md index 73710e87f..6f3c3ae2b 100644 --- a/docs/finance_models/finance_index.md +++ b/docs/finance_models/finance_index.md @@ -2,7 +2,7 @@ (finance:overview)= # Finance Models Overview -**General finance models** are not technology specific finance models. These models live in the `h2integrate/finances/` folder. These models are input the `driver_config`, `tech_config`, `plant_config`, `commodity_type`, and a `description` as the options. +**General finance models** are not technology specific finance models. These models live in the `h2integrate/finances/` folder. These models accept `driver_config`, `tech_config`, `plant_config`, `commodity_type`, and a `description` as the inputs and options. - `driver_config` (dict): the `folder_outputs` specified here may be used by the finance model if the finance model outputs data to a file. - `tech_config` (dict): the technology configs for the technologies to include in the finance calculations diff --git a/docs/user_guide/connecting_technologies.md b/docs/user_guide/connecting_technologies.md index 6d2ae23cb..da5b62e52 100644 --- a/docs/user_guide/connecting_technologies.md +++ b/docs/user_guide/connecting_technologies.md @@ -16,7 +16,7 @@ Technology interconnections are defined as an array of arrays in your `plant_con technology_interconnections: [ ["source_tech", "destination_tech", "variable_name", "transport_type"], ["tech_a", "tech_b", "shared_parameter"], - ["tech_a", "tech_b", ["tech_a_param_name","tech_b_param_name"]], + ["tech_a", "tech_b", ["tech_a_param_name", "tech_b_param_name"]], # ... more connections ] ``` @@ -45,13 +45,13 @@ There are two connection formats: ##### Different shared parameter names ```yaml -["source_tech", "destination_tech", ["source_parameter","destination_parameter]] +["source_tech", "destination_tech", ("source_parameter", "destination_parameter")] ``` - **source_tech**: Name of the technology providing the output - **destination_tech**: Name of the technology receiving the input -- **source_parameter**: The parameter name of ``"source_tech"`` -- **destination_parameter**: The parameter name of ``"destination_tech"`` +- **source_parameter**: The name of the parameter within ``"source_tech"`` +- **destination_parameter**: The name of the parameter within ``"destination_tech"`` ### Internal connection logic diff --git a/docs/user_guide/cost_years.md b/docs/user_guide/cost_years.md index 244aa4d7c..10d0994e2 100644 --- a/docs/user_guide/cost_years.md +++ b/docs/user_guide/cost_years.md @@ -1,6 +1,6 @@ (cost:cost_years)= # Cost year of Cost Models -Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. +Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user-provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. ## Cost models with inherent cost year diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 7b320918b..00d28341c 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -66,7 +66,7 @@ finance_parameters: subgroup_b: commodity: "commodity_b" #required commodity_desc: "" #optional description of commodity_b - technologies: ["tech_a","tech_b"] + technologies: ["tech_a", "tech_b"] ``` - Format explained: @@ -112,7 +112,7 @@ finance_parameters: commodity: "commodity_b" #required commodity_desc: "" #optional description of commodity_b finance_groups: [finance_model_nickname_a] #required in this case - technologies: ["tech_b","tech_c"] + technologies: ["tech_b", "tech_c"] ``` - Format explained: @@ -161,12 +161,12 @@ finance_parameters: commodity: "commodity_a" #required commodity_desc: "desc_b" #optional description of commodity_b finance_groups: [finance_model_nickname_a, finance_model_nickname_b] - technologies: ["tech_b","tech_c"] + technologies: ["tech_b", "tech_c"] subgroup_c: commodity: "commodity_b" #required commodity_desc: "" #optional description of commodity_b finance_groups: [finance_model_nickname_a, finance_model_nickname_c] - technologies: ["tech_b","tech_c"] + technologies: ["tech_b", "tech_c"] ``` - Format explained: - **`finance_model_nickname_a`**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index 4cc974e67..237500741 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -25,7 +25,7 @@ technology_interconnections: [ ["hopp", "electrolyzer", "electricity", "cable"], ["electrolyzer", "h2_storage", "efficiency"], ["electrolyzer", "h2_storage", "hydrogen", "pipe"], - ["financials_subgroup_hydrogen", "steel", ["LCOH_delivered","LCOH"]], + ["financials_subgroup_hydrogen", "steel", ["LCOH_delivered", "LCOH"]], ["hopp", "steel", "electricity", "cable"], # etc ] @@ -65,7 +65,7 @@ finance_parameters: commodity: "hydrogen" commodity_desc: "delivered" finance_groups: ["profast_model"] - technologies: ["hopp","electrolyzer","h2_storage"] + technologies: ["hopp", "electrolyzer", "h2_storage"] steel: commodity: "steel" finance_groups: ["steel"] diff --git a/examples/01_onshore_steel_mn/tech_config.yaml b/examples/01_onshore_steel_mn/tech_config.yaml index 10dba7366..6fe35ab2e 100644 --- a/examples/01_onshore_steel_mn/tech_config.yaml +++ b/examples/01_onshore_steel_mn/tech_config.yaml @@ -69,7 +69,6 @@ technologies: model: "steel_cost" financial_model: model: "steel_cost" - # group: steel model_inputs: shared_parameters: capacity_factor: 0.9 diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index 8cb26e91c..4075f501c 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -67,7 +67,7 @@ finance_parameters: technologies: ["hopp"] hydrogen: commodity: "hydrogen" - technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] ammonia: commodity: "ammonia" - technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index 49c6554f8..fbe682715 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -56,10 +56,10 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["hopp","electrolyzer"] + technologies: ["hopp", "electrolyzer"] hydrogen: commodity: "hydrogen" - technologies: ["hopp","electrolyzer"] + technologies: ["hopp", "electrolyzer"] methanol: commodity: "methanol" finance_groups: ["methanol"] diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index f6753bc38..f8d04ea72 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -57,7 +57,7 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["wind","electrolyzer"] + technologies: ["wind", "electrolyzer"] hydrogen: commodity: "hydrogen" - technologies: ["wind","electrolyzer"] + technologies: ["wind", "electrolyzer"] diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 60537f170..45d9e04cd 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -60,4 +60,4 @@ finance_parameters: technologies: ["hopp", "doc"] co2: commodity: "co2" - technologies: ["hopp","doc"] + technologies: ["hopp", "doc"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index 2283f6402..ae8952a24 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -60,4 +60,4 @@ finance_parameters: technologies: ["hopp", "oae"] co2: commodity: "co2" - technologies: ["hopp","oae"] + technologies: ["hopp", "oae"] diff --git a/examples/10_electrolyzer_om/plant_config.yaml b/examples/10_electrolyzer_om/plant_config.yaml index 056c34988..777426034 100644 --- a/examples/10_electrolyzer_om/plant_config.yaml +++ b/examples/10_electrolyzer_om/plant_config.yaml @@ -86,5 +86,5 @@ finance_parameters: technologies: ["wind"] hydrogen: commodity: "hydrogen" - finance_groups: ["lcoe_financials","lcoh_financials"] - technologies: ["wind","electrolyzer"] + finance_groups: ["lcoe_financials", "lcoh_financials"] + technologies: ["wind", "electrolyzer"] diff --git a/examples/12_ammonia_synloop/plant_config.yaml b/examples/12_ammonia_synloop/plant_config.yaml index 32b342336..8aaa7a622 100644 --- a/examples/12_ammonia_synloop/plant_config.yaml +++ b/examples/12_ammonia_synloop/plant_config.yaml @@ -60,7 +60,7 @@ finance_parameters: subgroups: h2: commodity: "hydrogen" - technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] nh3: commodity: "ammonia" - technologies: ["hopp","electrolyzer","h2_storage","ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] diff --git a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml index 9fe7fb9ab..f50173e80 100644 --- a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml +++ b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml @@ -58,7 +58,7 @@ finance_parameters: subgroups: elec: commodity: "electricity" - technologies: ["wind","electrolyzer","h2_storage"] + technologies: ["wind", "electrolyzer", "h2_storage"] h2: commodity: "hydrogen" - technologies: ["wind","electrolyzer","h2_storage"] + technologies: ["wind", "electrolyzer", "h2_storage"] diff --git a/examples/15_wind_solar_electrolyzer/plant_config.yaml b/examples/15_wind_solar_electrolyzer/plant_config.yaml index b409f6f55..f8c235a82 100644 --- a/examples/15_wind_solar_electrolyzer/plant_config.yaml +++ b/examples/15_wind_solar_electrolyzer/plant_config.yaml @@ -28,7 +28,7 @@ technology_interconnections: [ # ["hopp", "electrolyzer", "electricity", "cable"], ["wind", "combiner", "electricity", "cable"], #source_tech, dest_tech, transport_item, transport_type = connection ["solar", "combiner", "electricity", "cable"], - ["combiner","electrolyzer","electricity","cable"], + ["combiner", "electrolyzer", "electricity", "cable"], ] plant: @@ -62,10 +62,10 @@ finance_parameters: subgroups: electricity: commodity: "electricity" - technologies: ["wind","solar"] + technologies: ["wind", "solar"] hydrogen: commodity: "hydrogen" - technologies: ["wind","solar","electrolyzer"] + technologies: ["wind", "solar", "electrolyzer"] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 diff --git a/examples/17_splitter_wind_doc_h2/plant_config.yaml b/examples/17_splitter_wind_doc_h2/plant_config.yaml index 77cff1ba3..086749548 100644 --- a/examples/17_splitter_wind_doc_h2/plant_config.yaml +++ b/examples/17_splitter_wind_doc_h2/plant_config.yaml @@ -69,7 +69,7 @@ finance_parameters: technologies: ["hopp"] hydrogen: commodity: "hydrogen" - technologies: ["hopp","electrolyzer"] + technologies: ["hopp", "electrolyzer"] co2: commodity: "co2" - technologies: ["hopp","doc"] + technologies: ["hopp", "doc"] diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 195f66648..6d34bea3a 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -452,7 +452,7 @@ def create_financial_model(self): # Add adjusted capex/opex adjusted_capex_opex_comp = AdjustedCapexOpexComp( driver_config=self.driver_config, - tech_config=tech_configs, + tech_configs=tech_configs, plant_config=self.plant_config, ) @@ -781,13 +781,9 @@ def connect_technologies(self): f"financials_subgroup_{group_id}.total_ammonia_produced", ) - if "doc" in tech_name and primary_commodity_type == "co2": - self.plant.connect( - f"{tech_name}.co2_capture_mtpy", - f"financials_subgroup_{group_id}.co2_capture_kgpy", - ) - - if "oae" in tech_name and primary_commodity_type == "co2": + if ( + "doc" in tech_name or "oae" in tech_name + ) and primary_commodity_type == "co2": self.plant.connect( f"{tech_name}.co2_capture_mtpy", f"financials_subgroup_{group_id}.co2_capture_kgpy", diff --git a/h2integrate/core/inputs/plant_schema.yaml b/h2integrate/core/inputs/plant_schema.yaml index 818a6f1fa..112a7865a 100644 --- a/h2integrate/core/inputs/plant_schema.yaml +++ b/h2integrate/core/inputs/plant_schema.yaml @@ -80,8 +80,8 @@ properties: description: timezone as UTC offset corresponding to start_time minimum: -12 maximum: 14 - required: ["dt","n_timesteps"] - required: ["plant_life","simulation"] + required: ["dt", "n_timesteps"] + required: ["plant_life", "simulation"] technology_interconnections: type: array description: Array of arrays representing technology interconnections diff --git a/h2integrate/core/utilities.py b/h2integrate/core/utilities.py index f7eecd17e..a4a8f1383 100644 --- a/h2integrate/core/utilities.py +++ b/h2integrate/core/utilities.py @@ -288,7 +288,10 @@ def dict_to_yaml_formatting(orig_dict): def determine_commodity_types_from_technology_names(tech_configs, electricity_producing_techs): + """Determines the commodity types present in a plant based on the technology names.""" + commodity_types = [] + if "steel" in tech_configs: commodity_types.append("steel") if "electrolyzer" in tech_configs: @@ -309,4 +312,5 @@ def determine_commodity_types_from_technology_names(tech_configs, electricity_pr if tech in tech_configs: commodity_types.append("electricity") break + return commodity_types diff --git a/h2integrate/finances/finances.py b/h2integrate/finances/finances.py index c64cf811f..0deef1ec4 100644 --- a/h2integrate/finances/finances.py +++ b/h2integrate/finances/finances.py @@ -3,13 +3,40 @@ class AdjustedCapexOpexComp(om.ExplicitComponent): + """ + OpenMDAO component to adjust CapEx and OpEx values for multiple technologies to a target + dollar year using inflation. + + This component takes in capital expenditures (CapEx), operational expenditures (OpEx), + and their associated cost years for each technology, and adjusts them to a specified target + dollar year using a given inflation rate. The adjusted values are output for each technology, + along with the total adjusted CapEx and OpEx across all technologies. + + Attributes: + inflation_rate (float): The annual inflation rate used for cost adjustment. + target_dollar_year (int): The year to which all costs are adjusted. + + Inputs: + capex_{tech} (float, USD): Capital expenditure for each technology. + opex_{tech} (float, USD/year): Operational expenditure for each technology. + cost_year_{tech} (int): Dollar year for the costs of each technology. + + Outputs: + capex_adjusted_{tech} (float, USD): CapEx for each technology adjusted to the + target dollar year. + opex_adjusted_{tech} (float, USD/year): OpEx for each technology adjusted to the + target dollar year. + total_capex_adjusted (float, USD): Total adjusted CapEx across all technologies. + total_opex_adjusted (float, USD/year): Total adjusted OpEx across all technologies. + """ + def initialize(self): self.options.declare("driver_config", types=dict) - self.options.declare("tech_config", types=dict) + self.options.declare("tech_configs", types=dict) self.options.declare("plant_config", types=dict) def setup(self): - tech_config = self.options["tech_config"] + tech_configs = self.options["tech_configs"] plant_config = self.options["plant_config"] self.inflation_rate = plant_config["finance_parameters"]["cost_adjustment_parameters"][ "cost_year_adjustment_inflation" @@ -18,7 +45,7 @@ def setup(self): "target_dollar_year" ] - for tech in tech_config: + for tech in tech_configs: self.add_input(f"capex_{tech}", val=0.0, units="USD") self.add_input(f"opex_{tech}", val=0.0, units="USD/year") self.add_discrete_input(f"cost_year_{tech}", val=0, desc="Dollar year for costs") @@ -32,7 +59,7 @@ def setup(self): def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): total_capex_adjusted = 0.0 total_opex_adjusted = 0.0 - for tech in self.options["tech_config"]: + for tech in self.options["tech_configs"]: capex = float(inputs[f"capex_{tech}"][0]) opex = float(inputs[f"opex_{tech}"][0]) cost_year = int(discrete_inputs[f"cost_year_{tech}"]) diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index c515d4710..b11477fb0 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -148,7 +148,7 @@ class BasicProFASTParameterConfig(BaseConfig): debt_interest_rate (float): interest rate on debt inflation_rate (float): escalation rate. Set to zero for a nominal analysis. cash_onhand_months (int): number of months with cash onhand. - admin_expense (float): adminstrative expense as a fraction of sales + admin_expense (float): administrative expense as a fraction of sales non_depr_assets (float, optional): cost (in `$`) of nondepreciable assets, such as land. Defaults to 0. end_of_proj_sale_non_depr_assets (float, optional): cost (in `$`) of nondepreciable assets @@ -217,7 +217,7 @@ class BasicProFASTParameterConfig(BaseConfig): default={ "name": None, "unit": None, - "inital price": 100, + "initial price": 100, "escalation": 0.0, } ) @@ -433,17 +433,15 @@ def setup(self): commodity_units = "kg/year" lco_units = "USD/kg" + LCO_base_str = f"LCO{self.options['commodity_type'][0].upper()}" + self.output_txt = self.options["commodity_type"].lower() if self.options["description"] == "": - self.LCO_str = f"LCO{self.options['commodity_type'][0].upper()}" - self.output_txt = self.options["commodity_type"].lower() + self.LCO_str = LCO_base_str else: - LCO_base_str = f"LCO{self.options['commodity_type'][0].upper()}" desc_str = self.options["description"].strip().strip("_()-") if desc_str == "": - self.output_txt = self.options["commodity_type"].lower() self.LCO_str = LCO_base_str else: - self.output_txt = f"{self.options['commodity_type'].lower()}_{desc_str}" self.LCO_str = f"{LCO_base_str}_{desc_str}" self.add_output(self.LCO_str, val=0.0, units=lco_units) diff --git a/h2integrate/simulation/technologies/iron/rosner_override/finance_model.py b/h2integrate/simulation/technologies/iron/rosner_override/finance_model.py index 1f46c44b2..1fb03b8d3 100644 --- a/h2integrate/simulation/technologies/iron/rosner_override/finance_model.py +++ b/h2integrate/simulation/technologies/iron/rosner_override/finance_model.py @@ -18,7 +18,7 @@ def main(config): Args: config (object): Configuration object containing: product_selection (str): The selected technology - (e.g., "ng_dri", "h2_dri","ng_eaf", "h2_eaf"). + (e.g., "ng_dri", "h2_dri", "ng_eaf", "h2_eaf"). performance (object): Contains plant performance data as a dataframe. cost (object): Contains cost data as a dataframe. params (dict): Key financial and operational parameters, including: From afac62bc4aaa0aaa9a082dbeee6ea1d6298469c9 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Tue, 9 Sep 2025 09:12:35 -0500 Subject: [PATCH 45/67] include description in financial outputs --- h2integrate/finances/profast_financial.py | 1 + 1 file changed, 1 insertion(+) diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index b11477fb0..d8f2cff42 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -442,6 +442,7 @@ def setup(self): if desc_str == "": self.LCO_str = LCO_base_str else: + self.output_txt = f"{self.options['commodity_type'].lower()}_{desc_str}" self.LCO_str = f"{LCO_base_str}_{desc_str}" self.add_output(self.LCO_str, val=0.0, units=lco_units) From 4d2f7b621fa50a0680ebb3e2c5965ddcb24f1825 Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:20:41 -0600 Subject: [PATCH 46/67] removed legacy finance groups from example tech configs --- examples/01_onshore_steel_mn/tech_config.yaml | 6 ------ .../tech_config_financials.yaml | 2 -- examples/15_wind_solar_electrolyzer/tech_config.yaml | 2 -- 3 files changed, 10 deletions(-) diff --git a/examples/01_onshore_steel_mn/tech_config.yaml b/examples/01_onshore_steel_mn/tech_config.yaml index 6fe35ab2e..2c08bf27e 100644 --- a/examples/01_onshore_steel_mn/tech_config.yaml +++ b/examples/01_onshore_steel_mn/tech_config.yaml @@ -8,8 +8,6 @@ technologies: config: !include tech_inputs/hopp_config.yaml cost_model: model: "hopp" - financial_model: - group: default electrolyzer_rating: 720. # MW model_inputs: cost_parameters: @@ -19,8 +17,6 @@ technologies: model: "eco_pem_electrolyzer_performance" cost_model: model: "singlitico_electrolyzer_cost" - financial_model: - group: default model_inputs: shared_parameters: location: "onshore" @@ -49,8 +45,6 @@ technologies: model: "pass_through_controller" cost_model: model: "h2_storage" - financial_model: - group: default model_inputs: performance_parameters: rating: 720. diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml index e5049888d..a9c1e32db 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml @@ -8,8 +8,6 @@ technologies: config: !include tech_inputs/hopp_config.yaml cost_model: model: "hopp" - financial_model: - group: "default" model_inputs: cost_parameters: cost_year: 2022 diff --git a/examples/15_wind_solar_electrolyzer/tech_config.yaml b/examples/15_wind_solar_electrolyzer/tech_config.yaml index e4f691d0d..8406a0ff8 100644 --- a/examples/15_wind_solar_electrolyzer/tech_config.yaml +++ b/examples/15_wind_solar_electrolyzer/tech_config.yaml @@ -73,8 +73,6 @@ technologies: model: "eco_pem_electrolyzer_performance" cost_model: model: "singlitico_electrolyzer_cost" - financial_model: - group: default model_inputs: shared_parameters: location: "onshore" From c9735660f99353e08729770417c846e0110e0cfb Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:06:23 -0600 Subject: [PATCH 47/67] added comments and removed commented out code in h2integrate_model and profast_financial --- h2integrate/core/h2integrate_model.py | 130 ++++++++++++---------- h2integrate/finances/profast_financial.py | 3 - 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 6d34bea3a..9daf5f724 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -361,6 +361,8 @@ def create_financial_model(self): and "model_inputs" in self.plant_config["finance_parameters"] ): if default_finance_model_nickname in self.plant_config["finance_parameters"]: + # create a default finance model nickname if user has an unused finance + # group nicknamed "default". default_finance_model_nickname = [ f"default_{i}" for i in range(5) @@ -391,7 +393,6 @@ def create_financial_model(self): "if no subgroups are provided." ) - # NOTE: should we group all the technologies no? # Collect all technologies into one subgroup all_techs = list(self.technology_config["technologies"].keys()) subgroup = { @@ -400,11 +401,7 @@ def create_financial_model(self): "technologies": all_techs, } subgroups = {default_finance_model_nickname: subgroup} - # for tech_name, tech_config in self.technology_config["technologies"].items(): - # if tech_name not in all_grouped_techs: - # if "default" not in financial_groups: - # financial_groups["default"] = {} - # financial_groups["default"][tech_name] = tech_config + # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): commodity = subgroup_params.get("commodity", None) @@ -443,7 +440,6 @@ def create_financial_model(self): ) financial_group = om.Group() - # any(k in electricity_producing_techs for k in self.technology_config["technologies"]) # TODO: dont add this subsystem unless required financial_group.add_subsystem( "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) @@ -462,7 +458,6 @@ def create_financial_model(self): for finance_model_nickname in finance_model_nicknames: # check if using tech-specific finance model - skip_model = False if any( tech_name == finance_model_nickname for tech_name, tech_params in tech_configs.items() @@ -475,63 +470,86 @@ def create_financial_model(self): # this is created in create_technologies() if tech_finance_model_name is not None: - skip_model = True - else: - finance_model_config = self.plant_config["finance_parameters"].get( - finance_model_nickname - ) - model_name = finance_model_config.get("finance_model") - fin_model_inputs = finance_model_config.get("model_inputs") + # tech specific finance models are created in create_technologies() + # and do not need to be included in the general finance models + continue - # Add financial model component - fin_model = self.supported_models.get(model_name) + # if not using a tech-specific finance model, get the finance model and inputs for + # the finance model group specified by finance_model_nickname + finance_model_config = self.plant_config["finance_parameters"].get( + finance_model_nickname + ) + model_name = finance_model_config.get("finance_model") # finance model + fin_model_inputs = finance_model_config.get( + "model_inputs" + ) # inputs to finance model - if fin_model is None: - raise ValueError(f"Financial model '{model_name}' not found.") + # Add financial model component + fin_model = self.supported_models.get(model_name) - filtered_plant_config = { - k: v for k, v in self.plant_config.items() if k != "finance_parameters" - } + if fin_model is None: + raise ValueError(f"Financial model '{model_name}' not found.") - filtered_plant_config.update( - { - "finance_parameters": { - "model_inputs": fin_model_inputs, - } - } - ) + # filter the plant_config so the finance_parameters only includes data for + # this finance model group - commodity_desc = subgroup_params.get("commodity_desc", "") - commodity_output_desc = subgroup_params.get("commodity_desc", "") - - if len(finance_model_nicknames) > 1: - non_tech_financials = [ - k - for k in finance_model_nicknames - if k in self.plant_config["finance_parameters"] - ] - if len(non_tech_financials) > 1: - commodity_output_desc = ( - commodity_output_desc + f"_{finance_model_nickname}" - ) + # first, grab information from the plant config, except the finance paramters + filtered_plant_config = { + k: v for k, v in self.plant_config.items() if k != "finance_parameters" + } - fin_comp = fin_model( - driver_config=self.driver_config, - tech_config=tech_configs, - plant_config=filtered_plant_config, - commodity_type=commodity, - description=commodity_output_desc, - ) + # then, reformat the finance_parameters to only include inputs for the + # finance group specified by finance_model_nickname + filtered_plant_config.update( + { + "finance_parameters": { + "finance_model": model_name, # unused by the finance model + "model_inputs": fin_model_inputs, # inputs for finance model + } + } + ) - if not skip_model: - finance_subsystem_name = ( - f"{finance_model_nickname}_{commodity}" - if commodity_desc == "" - else f"{finance_model_nickname}_{commodity}_{commodity_desc}" - ) + commodity_desc = subgroup_params.get("commodity_desc", "") + commodity_output_desc = subgroup_params.get("commodity_desc", "") + + # check if multiple finance model groups are specified for the subgroup + if len(finance_model_nicknames) > 1: + # check that the finance model groups do not include tech-specific finances + non_tech_financials = [ + k + for k in finance_model_nicknames + if k in self.plant_config["finance_parameters"] + ] + + # if multiple non-tech specific finance model groups are specified for the + # subgroup, the outputs of the finance model must have unique names to + # avoid errors. + if len(non_tech_financials) > 1: + # finance models name their outputs based on the description and commodity + # update the description to include the finance model nickname to ensure + # uniquely named outputs + commodity_output_desc = commodity_output_desc + f"_{finance_model_nickname}" + + # create the finance component + fin_comp = fin_model( + driver_config=self.driver_config, + tech_config=tech_configs, + plant_config=filtered_plant_config, + commodity_type=commodity, + description=commodity_output_desc, + ) + + # name the finance component based on the commodity and description + finance_subsystem_name = ( + f"{finance_model_nickname}_{commodity}" + if commodity_desc == "" + else f"{finance_model_nickname}_{commodity}_{commodity_desc}" + ) - financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) + # add the finance component to the finance group + financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) + # add the finance group to the subgroup self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_group) self.financial_groups = financial_groups diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index d8f2cff42..1c8fd4241 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -608,9 +608,6 @@ def compute(self, inputs, outputs): "profast_output_description", "ProFastComp" ) - # if self.options["description"] == "": - # fbasename = f"{fdesc}_{self.options['commodity_type']}" - # else: fbasename = f"{fdesc}_{self.output_txt}" output_dir = Path(output_dir) From 59908ce77a21674c7733b0052c314cb03c8ababb Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:09:38 -0600 Subject: [PATCH 48/67] updated finance_index.md note --- docs/finance_models/finance_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md index 6f3c3ae2b..e15b73922 100644 --- a/docs/finance_models/finance_index.md +++ b/docs/finance_models/finance_index.md @@ -15,7 +15,7 @@ The `commodity_type` and `description` are used in the finance model naming conv ``` ```{note} -The `tech_config` and `plant_config` may be reformatted from their original format within `H2IntegrateModel` when creating the finance models. +The `tech_config` and `plant_config` are reformatted from their original format within `H2IntegrateModel` when creating the finance models as a preprocessing step. ``` (finance:supportedmodels)= From 1c5aa04a79cca2f96be602cb7e3adbe53df6e58c Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 12 Sep 2025 13:13:24 -0500 Subject: [PATCH 49/67] change from nickname to name --- h2integrate/core/h2integrate_model.py | 52 +++++++++++++-------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 9daf5f724..6f988ab0e 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -354,16 +354,16 @@ def create_financial_model(self): subgroups = self.plant_config["finance_parameters"].get("subgroups", None) financial_groups = {} - default_finance_model_nickname = "default" + default_finance_model_name = "default" # only one finance model is being used with subgroups if ( "finance_model" in self.plant_config["finance_parameters"] and "model_inputs" in self.plant_config["finance_parameters"] ): - if default_finance_model_nickname in self.plant_config["finance_parameters"]: - # create a default finance model nickname if user has an unused finance - # group nicknamed "default". - default_finance_model_nickname = [ + if default_finance_model_name in self.plant_config["finance_parameters"]: + # create a default finance model name if user has an unused finance + # group named "default". + default_finance_model_name = [ f"default_{i}" for i in range(5) if f"default_{i}" not in self.plant_config["finance_parameters"] @@ -371,7 +371,7 @@ def create_financial_model(self): default_model_name = self.plant_config["finance_parameters"].pop("finance_model") default_model_inputs = self.plant_config["finance_parameters"].pop("model_inputs") default_model_dict = { - default_finance_model_nickname: { + default_finance_model_name: { "finance_model": default_model_name, "model_inputs": default_model_inputs, } @@ -383,7 +383,7 @@ def create_financial_model(self): commodity = self.plant_config["finance_parameters"].get("commodity") finance_model_name = ( self.plant_config["finance_parameters"] - .get(default_finance_model_nickname, {}) + .get(default_finance_model_name, {}) .get("finance_model") ) @@ -397,22 +397,22 @@ def create_financial_model(self): all_techs = list(self.technology_config["technologies"].keys()) subgroup = { "commodity": commodity, - "finance_groups": [default_finance_model_nickname], + "finance_groups": [default_finance_model_name], "technologies": all_techs, } - subgroups = {default_finance_model_nickname: subgroup} + subgroups = {default_finance_model_name: subgroup} # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): commodity = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") - finance_model_nicknames = subgroup_params.get( - "finance_groups", [default_finance_model_nickname] + finance_model_names = subgroup_params.get( + "finance_groups", [default_finance_model_name] ) tech_names = subgroup_params.get("technologies") - if isinstance(finance_model_nicknames, str): - finance_model_nicknames = [finance_model_nicknames] + if isinstance(finance_model_names, str): + finance_model_names = [finance_model_names] # check commodity type if commodity is None: @@ -456,16 +456,14 @@ def create_financial_model(self): "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) - for finance_model_nickname in finance_model_nicknames: + for finance_model_name in finance_model_names: # check if using tech-specific finance model if any( - tech_name == finance_model_nickname + tech_name == finance_model_name for tech_name, tech_params in tech_configs.items() ): tech_finance_model_name = ( - tech_configs.get(finance_model_nickname) - .get("financial_model", {}) - .get("model") + tech_configs.get(finance_model_name).get("financial_model", {}).get("model") ) # this is created in create_technologies() @@ -475,9 +473,9 @@ def create_financial_model(self): continue # if not using a tech-specific finance model, get the finance model and inputs for - # the finance model group specified by finance_model_nickname + # the finance model group specified by finance_model_name finance_model_config = self.plant_config["finance_parameters"].get( - finance_model_nickname + finance_model_name ) model_name = finance_model_config.get("finance_model") # finance model fin_model_inputs = finance_model_config.get( @@ -499,7 +497,7 @@ def create_financial_model(self): } # then, reformat the finance_parameters to only include inputs for the - # finance group specified by finance_model_nickname + # finance group specified by finance_model_name filtered_plant_config.update( { "finance_parameters": { @@ -513,11 +511,11 @@ def create_financial_model(self): commodity_output_desc = subgroup_params.get("commodity_desc", "") # check if multiple finance model groups are specified for the subgroup - if len(finance_model_nicknames) > 1: + if len(finance_model_names) > 1: # check that the finance model groups do not include tech-specific finances non_tech_financials = [ k - for k in finance_model_nicknames + for k in finance_model_names if k in self.plant_config["finance_parameters"] ] @@ -526,9 +524,9 @@ def create_financial_model(self): # avoid errors. if len(non_tech_financials) > 1: # finance models name their outputs based on the description and commodity - # update the description to include the finance model nickname to ensure + # update the description to include the finance model name to ensure # uniquely named outputs - commodity_output_desc = commodity_output_desc + f"_{finance_model_nickname}" + commodity_output_desc = commodity_output_desc + f"_{finance_model_name}" # create the finance component fin_comp = fin_model( @@ -541,9 +539,9 @@ def create_financial_model(self): # name the finance component based on the commodity and description finance_subsystem_name = ( - f"{finance_model_nickname}_{commodity}" + f"{finance_model_name}_{commodity}" if commodity_desc == "" - else f"{finance_model_nickname}_{commodity}_{commodity_desc}" + else f"{finance_model_name}_{commodity}_{commodity_desc}" ) # add the finance component to the finance group From 959273a421ea32545fdb333898e0a32755525528 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 12 Sep 2025 14:10:21 -0500 Subject: [PATCH 50/67] add error if invalid tech provided in subgroup --- h2integrate/core/h2integrate_model.py | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 6f988ab0e..623fbd7de 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -347,7 +347,7 @@ def create_financial_model(self): # attaches a ProFAST financial model component to the plant. """ - + # if there aren't any finance parameters don't setup a financial model if "finance_parameters" not in self.plant_config: return @@ -361,13 +361,11 @@ def create_financial_model(self): and "model_inputs" in self.plant_config["finance_parameters"] ): if default_finance_model_name in self.plant_config["finance_parameters"]: - # create a default finance model name if user has an unused finance - # group named "default". - default_finance_model_name = [ - f"default_{i}" - for i in range(5) - if f"default_{i}" not in self.plant_config["finance_parameters"] - ][0] + # treat "default" as a protected key + raise ValueError( + "`default` is a protected finance group name, please change the " + "name of the finance group to something else." + ) default_model_name = self.plant_config["finance_parameters"].pop("finance_model") default_model_inputs = self.plant_config["finance_parameters"].pop("model_inputs") default_model_dict = { @@ -418,17 +416,17 @@ def create_financial_model(self): if commodity is None: raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") - tech_configs = { - tech: self.technology_config["technologies"][tech] - for tech in tech_names - if tech in self.technology_config["technologies"] - } - - if not tech_configs: - raise ValueError( - f"Subgroup {subgroup} contains no valid technologies. " - f"Available techs: {list(self.technology_config['technologies'].keys())}" - ) + tech_configs = {} + for tech in tech_names: + if tech in self.technology_config["technologies"]: + tech_configs[tech] = self.technology_config["technologies"][tech] + else: + raise KeyError( + f"Technology '{tech}' not found in the technology configuration, " + f"but is listed in subgroup '{subgroup_name}', " + "Available " + f"technologies: {list(self.technology_config['technologies'].keys())}" + ) financial_groups.update( { From 2f92a0a6430288aa254eb26a5db2ff1954e928ff Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 12 Sep 2025 16:08:49 -0500 Subject: [PATCH 51/67] subgroups --- h2integrate/core/h2integrate_model.py | 33 +++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 623fbd7de..2ec4d76e5 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -331,7 +331,7 @@ def create_financial_model(self): * Constructs and attaches OpenMDAO financial subsystem groups to the plant model under names ``financials_subgroup_``. * Stores processed subgroup configurations in - ``self.financial_groups``. + ``self.financial_subgroups``. Example: Suppose ``plant_config["finance_parameters"]`` defines a single finance @@ -352,7 +352,8 @@ def create_financial_model(self): return subgroups = self.plant_config["finance_parameters"].get("subgroups", None) - financial_groups = {} + + financial_subgroups = {} default_finance_model_name = "default" # only one finance model is being used with subgroups @@ -428,7 +429,7 @@ def create_financial_model(self): f"technologies: {list(self.technology_config['technologies'].keys())}" ) - financial_groups.update( + financial_subgroups.update( { subgroup_name: { "tech_configs": tech_configs, @@ -436,12 +437,12 @@ def create_financial_model(self): } } ) - financial_group = om.Group() + financial_subgroup = om.Group() - # TODO: dont add this subsystem unless required - financial_group.add_subsystem( - "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) - ) + if commodity == "electricity": + financial_subgroup.add_subsystem( + "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) + ) # Add adjusted capex/opex adjusted_capex_opex_comp = AdjustedCapexOpexComp( @@ -450,7 +451,7 @@ def create_financial_model(self): plant_config=self.plant_config, ) - financial_group.add_subsystem( + financial_subgroup.add_subsystem( "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) @@ -543,12 +544,12 @@ def create_financial_model(self): ) # add the finance component to the finance group - financial_group.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) + financial_subgroup.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) # add the finance group to the subgroup - self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_group) + self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_subgroup) - self.financial_groups = financial_groups + self.financial_subgroups = financial_subgroups def get_included_technologies(self, tech_config, commodity_type, plant_config): """ @@ -715,7 +716,7 @@ def connect_technologies(self): # same name if the cost and financial models are not None if "finance_parameters" in self.plant_config: # Connect the outputs of the technology models to the appropriate financial groups - for group_id, group_configs in self.financial_groups.items(): + for group_id, group_configs in self.financial_subgroups.items(): tech_configs = group_configs.get("tech_configs") primary_commodity_type = group_configs.get("commodity") # Skip steel financials; it provides its own financials @@ -745,7 +746,11 @@ def connect_technologies(self): # Only connect if the technology is included in at least one commodity's stackup # and in this financial group for tech_name in tech_configs.keys(): - if tech_name in electricity_producing_techs and tech_name in all_included_techs: + if ( + tech_name in electricity_producing_techs + and tech_name in all_included_techs + and primary_commodity_type == "electricity" + ): self.plant.connect( f"{tech_name}.electricity_out", f"financials_subgroup_{group_id}.electricity_sum.electricity_{tech_name}", From 77ef61d3b13b365e11be38bc22297ea754207ff4 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 12 Sep 2025 16:24:46 -0500 Subject: [PATCH 52/67] remove old logic --- h2integrate/core/h2integrate_model.py | 133 ++++++++------------------ h2integrate/core/utilities.py | 29 ------ 2 files changed, 38 insertions(+), 124 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 2ec4d76e5..f4d1d8e50 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -5,10 +5,7 @@ import numpy as np import openmdao.api as om -from h2integrate.core.utilities import ( - create_xdsm_from_config, - determine_commodity_types_from_technology_names, -) +from h2integrate.core.utilities import create_xdsm_from_config from h2integrate.finances.finances import AdjustedCapexOpexComp from h2integrate.core.resource_summer import ElectricitySumComp from h2integrate.core.supported_models import supported_models, electricity_producing_techs @@ -490,7 +487,7 @@ def create_financial_model(self): # filter the plant_config so the finance_parameters only includes data for # this finance model group - # first, grab information from the plant config, except the finance paramters + # first, grab information from the plant config, except the finance parameters filtered_plant_config = { k: v for k, v in self.plant_config.items() if k != "finance_parameters" } @@ -551,42 +548,6 @@ def create_financial_model(self): self.financial_subgroups = financial_subgroups - def get_included_technologies(self, tech_config, commodity_type, plant_config): - """ - Determine which technologies should be included in the financial metrics. - Args: - tech_config: Dictionary of technology configurations - commodity_type: Type of commodity (e.g., 'hydrogen', 'electricity', 'ammonia') - plant_config: Plant configuration dictionary - Returns: - List of technology names to include in the financial stackup - """ - # Check if the user defined specific technologies to include in the metrics. - # If provided, only include those technologies in the stackup. - # If not provided, include all technologies in the financial group in the stackup. - metric_key = f"LCO{commodity_type[0].upper()}" - - included_techs = ( - plant_config["finance_parameters"] - .get("technologies_included_in_metrics", {}) - .get(metric_key, None) - ) - - # Check if the included technologies are valid - if included_techs is not None: - missing_techs = [tech for tech in included_techs if tech not in tech_config] - if missing_techs: - raise ValueError( - f"Included technology(ies) {missing_techs} not found in tech_config. " - f"Available techs: {list(tech_config.keys())}" - ) - - # If no specific technologies are included, default to all technologies in tech_config - if included_techs is None: - included_techs = list(tech_config.keys()) - - return included_techs - def connect_technologies(self): technology_interconnections = self.plant_config.get("technology_interconnections", []) @@ -725,30 +686,13 @@ def connect_technologies(self): plant_producing_electricity = False - # Determine which commodity types this financial group handles - commodity_types = determine_commodity_types_from_technology_names( - tech_configs, electricity_producing_techs - ) - - # Get all included technologies for all commodity types in this group - all_included_techs = set() - for commodity_type in commodity_types: - if commodity_type not in [ - "steel", - "methanol", - ]: # These handle their own financials - included_techs = self.get_included_technologies( - tech_configs, commodity_type, self.plant_config - ) - all_included_techs.update(included_techs) - # Loop through technologies and connect electricity outputs to the ExecComp # Only connect if the technology is included in at least one commodity's stackup # and in this financial group for tech_name in tech_configs.keys(): if ( tech_name in electricity_producing_techs - and tech_name in all_included_techs + # and tech_name in all_included_techs and primary_commodity_type == "electricity" ): self.plant.connect( @@ -770,49 +714,48 @@ def connect_technologies(self): if "splitter" in tech_name or "combiner" in tech_name: continue - if tech_name in all_included_techs: - self.plant.connect( - f"{tech_name}.CapEx", - f"financials_subgroup_{group_id}.capex_{tech_name}", - ) - self.plant.connect( - f"{tech_name}.OpEx", f"financials_subgroup_{group_id}.opex_{tech_name}" - ) + self.plant.connect( + f"{tech_name}.CapEx", + f"financials_subgroup_{group_id}.capex_{tech_name}", + ) + self.plant.connect( + f"{tech_name}.OpEx", f"financials_subgroup_{group_id}.opex_{tech_name}" + ) + self.plant.connect( + f"{tech_name}.cost_year", + f"financials_subgroup_{group_id}.cost_year_{tech_name}", + ) + + if "electrolyzer" in tech_name: self.plant.connect( - f"{tech_name}.cost_year", - f"financials_subgroup_{group_id}.cost_year_{tech_name}", + f"{tech_name}.time_until_replacement", + f"financials_subgroup_{group_id}.{tech_name}_time_until_replacement", ) - - if "electrolyzer" in tech_name: + if primary_commodity_type == "hydrogen": self.plant.connect( - f"{tech_name}.time_until_replacement", - f"financials_subgroup_{group_id}.{tech_name}_time_until_replacement", + f"{tech_name}.total_hydrogen_produced", + f"financials_subgroup_{group_id}.total_hydrogen_produced", ) - if primary_commodity_type == "hydrogen": - self.plant.connect( - f"{tech_name}.total_hydrogen_produced", - f"financials_subgroup_{group_id}.total_hydrogen_produced", - ) - if "ammonia" in tech_name and primary_commodity_type == "ammonia": - self.plant.connect( - f"{tech_name}.total_ammonia_produced", - f"financials_subgroup_{group_id}.total_ammonia_produced", - ) + if "ammonia" in tech_name and primary_commodity_type == "ammonia": + self.plant.connect( + f"{tech_name}.total_ammonia_produced", + f"financials_subgroup_{group_id}.total_ammonia_produced", + ) - if ( - "doc" in tech_name or "oae" in tech_name - ) and primary_commodity_type == "co2": - self.plant.connect( - f"{tech_name}.co2_capture_mtpy", - f"financials_subgroup_{group_id}.co2_capture_kgpy", - ) + if ( + "doc" in tech_name or "oae" in tech_name + ) and primary_commodity_type == "co2": + self.plant.connect( + f"{tech_name}.co2_capture_mtpy", + f"financials_subgroup_{group_id}.co2_capture_kgpy", + ) - if "air_separator" in tech_name and primary_commodity_type == "nitrogen": - self.plant.connect( - f"{tech_name}.total_nitrogen_produced", - f"financials_subgroup_{group_id}.total_nitrogen_produced", - ) + if "air_separator" in tech_name and primary_commodity_type == "nitrogen": + self.plant.connect( + f"{tech_name}.total_nitrogen_produced", + f"financials_subgroup_{group_id}.total_nitrogen_produced", + ) self.plant.options["auto_order"] = True diff --git a/h2integrate/core/utilities.py b/h2integrate/core/utilities.py index a4a8f1383..c7b989c3a 100644 --- a/h2integrate/core/utilities.py +++ b/h2integrate/core/utilities.py @@ -285,32 +285,3 @@ def dict_to_yaml_formatting(orig_dict): else: orig_dict[key] = float(val) return orig_dict - - -def determine_commodity_types_from_technology_names(tech_configs, electricity_producing_techs): - """Determines the commodity types present in a plant based on the technology names.""" - - commodity_types = [] - - if "steel" in tech_configs: - commodity_types.append("steel") - if "electrolyzer" in tech_configs: - commodity_types.append("hydrogen") - if "methanol" in tech_configs: - commodity_types.append("methanol") - if "ammonia" in tech_configs: - commodity_types.append("ammonia") - if "geoh2" in tech_configs: - commodity_types.append("hydrogen") - if "doc" in tech_configs: - commodity_types.append("co2") - if "air_separator" in tech_configs: - commodity_types.append("nitrogen") - if "oae" in tech_configs: - commodity_types.append("co2") - for tech in electricity_producing_techs: - if tech in tech_configs: - commodity_types.append("electricity") - break - - return commodity_types From c56e08f2e8f67278934d40f78c68ec58f723922d Mon Sep 17 00:00:00 2001 From: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:18:36 -0600 Subject: [PATCH 53/67] added error if default_finance_model_name is included in the finance parameters --- h2integrate/core/h2integrate_model.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index f4d1d8e50..8c71b4c7a 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -359,11 +359,13 @@ def create_financial_model(self): and "model_inputs" in self.plant_config["finance_parameters"] ): if default_finance_model_name in self.plant_config["finance_parameters"]: - # treat "default" as a protected key - raise ValueError( - "`default` is a protected finance group name, please change the " - "name of the finance group to something else." + # throw an error if the user has an unused finance group named "default". + msg = ( + "Invalid key `default` in plant_config['finance_parameters']. " + "Please rename the `default` key to something else or remove it. " + "The name `default` will be used to reference the finance model group." ) + raise ValueError(msg) default_model_name = self.plant_config["finance_parameters"].pop("finance_model") default_model_inputs = self.plant_config["finance_parameters"].pop("model_inputs") default_model_dict = { From 4edeba7b8a3fc22da17b69428047ae2950cf01fb Mon Sep 17 00:00:00 2001 From: Jared Thomas Date: Fri, 12 Sep 2025 15:08:32 -0600 Subject: [PATCH 54/67] fix refs and a few minor other things --- docs/_toc.yml | 1 + docs/technology_models/atb_costs_pv.md | 2 ++ docs/technology_models/pvwattsv8_solar_pv.md | 3 +++ docs/technology_models/technology_overview.md | 13 +++++++++---- docs/user_guide/cost_years.md | 3 ++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/_toc.yml b/docs/_toc.yml index 21d267b6f..8a403b637 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -25,6 +25,7 @@ parts: - caption: Technology Models chapters: + - file: technology_models/technology_overview - file: technology_models/feedstocks - file: technology_models/run_of_river - file: technology_models/direct_ocean_capture diff --git a/docs/technology_models/atb_costs_pv.md b/docs/technology_models/atb_costs_pv.md index 7b5868024..10c12b04a 100644 --- a/docs/technology_models/atb_costs_pv.md +++ b/docs/technology_models/atb_costs_pv.md @@ -11,6 +11,7 @@ As mentioned on the [Utility-Scale PV ATB page](https://atb.nrel.gov/electricity - The `"atb_utility_pv_cost"` model has costs input in `$/kW-AC` to match the ATB and the outputted capacity in kW-AC from the PV performance model. Example usage of this cost model in the `tech_config.yaml` file is shown [in the first section below](#utility-scale-pv-cost-model). - The `"atb_comm_res_pv_cost"` model has costs input in `$/kW-DC` and the PV capacity is input in kW-DC from the **shared input parameter** of the PV performance model. Example usage of this cost model in the `tech_config.yaml` file is shown [in the second section below](#commercial-and-residential-pv-cost-model). +(utility-scale-pv-cost-model)= ## Utility-Scale PV Cost Model The inputs for `cost_parameters` are `capex_per_kWac` and `opex_per_kWac_per_year`. From the ATB workbook, a value for `capex_per_kWac` can be found on the `Solar - Utility PV` sheet under the "Overnight Capital Cost" section or the "CAPEX" section. The values in the "CAPEX" section include overnight capital costs, construction finance factor, and grid connection costs. A value for `opex_per_kWac_per_year` can be found on the `Solar - Utility PV` sheet under the "Fixed Operation and Maintenance Expenses" section. @@ -34,6 +35,7 @@ technologies: opex_per_kWac_per_year: 18 ``` +(commercial-and-residential-pv-cost-model)= ## Commercial and Residential PV Cost Model The inputs for `cost_parameters` are `capex_per_kWdc` and `opex_per_kWdc_per_year`. From the ATB workbook, a value for `capex_per_kWdc` can be found on the `Solar - PV Dist. Comm` or `Solar - PV Dist. Res` sheet under the "Overnight Capital Cost" section or the "CAPEX" section. The values in the "CAPEX" section include overnight capital costs, construction finance factor, and grid connection costs. A value for `opex_per_kWdc_per_year` can be found on the `Solar - PV Dist. Comm` or `Solar - PV Dist. Res` sheet under the "Fixed Operation and Maintenance Expenses" section. diff --git a/docs/technology_models/pvwattsv8_solar_pv.md b/docs/technology_models/pvwattsv8_solar_pv.md index 08987f545..76299bc79 100644 --- a/docs/technology_models/pvwattsv8_solar_pv.md +++ b/docs/technology_models/pvwattsv8_solar_pv.md @@ -28,6 +28,7 @@ technologies: ``` +(performance-parameters)= ## Performance Parameters - `pv_capacity_kWdc` (required): capacity of the PV system in kW-DC - `dc_ac_ratio`: the ratio of DC capacity to AC capacity, equal to `pv_capacity_kWdc/pv_capacity_kWac` is used to calculate the PV capacity in kW-AC and is equivalent as the inverter rated power. An inverter is used in PV systems to convert DC power (output from panels) to AC power (input to AC microgrid). The PV capacity in kW-AC is `pv_capacity_kWdc/dc_ac_ratio`. A general default `dc_ac_ratio` is between 1.2 and 1.3. **This is required if** `dc_ac_ratio` is not either loaded from a default Pvwattsv8 config OR not included in the `pysam_options` dictionary under the `SystemDesign` group. @@ -70,6 +71,7 @@ $$ - [Shading](https://nrel-pysam.readthedocs.io/en/main/modules/Pvwattsv8.html#shading-group) - [AdjustmentFactors](https://nrel-pysam.readthedocs.io/en/main/modules/Pvwattsv8.html#adjustmentfactors-group) +(systemdesign-group)= ### SystemDesign group ```{note} Do not include the `system_capacity` parameter of the `SystemDesign` group. The system capacity should be set in the performance parameters with the variable `pv_capacity_kWdc`. @@ -106,6 +108,7 @@ Some common design parameters that a user may want to specify within the [System - `tilt_angle_func` is set to "none" and `tilt` is specified under the performance parameters. ``` +(solarresource-group)= ### SolarResource group Solar resource data is downloaded from the [National Solar Resource Database](https://developer.nrel.gov/docs/solar/nsrdb/psm3-2-2-download/) and input as the `solar_resource_data` variable in the Pvwattsv8 SolarResource Group. Some other common resource parameters that a user may want to specify within the [SolarResource Group](https://nrel-pysam.readthedocs.io/en/main/modules/Pvwattsv8.html#solarresource-group) are: - `use_wf_albedo` (bool): if True, use albedo from weather file (if valid). If False, use value for `albedo_default`. Defaults to True. diff --git a/docs/technology_models/technology_overview.md b/docs/technology_models/technology_overview.md index 025c4dfb6..52427106d 100644 --- a/docs/technology_models/technology_overview.md +++ b/docs/technology_models/technology_overview.md @@ -8,6 +8,7 @@ Currently, H2I recognizes four types of models: - [Storage](#storage) - [Controllers](#controller) +(resource)= ## Resource `Resource` models process resource data that is usually passed to a technology model. @@ -19,6 +20,7 @@ Currently, H2I recognizes four types of models: The `Resource` models are under development. Many of the resources are currently integrated into the `Converter` model directly, notably this is true for the wind resource used in the `wind` converter and solar resource used in the `solar` converter. ``` +(converters)= ## Converters `Converter` models are technologies that: - converts energy available in the 'Primary Input' to another form of energy ('Primary Commodity') OR @@ -42,7 +44,7 @@ The inputs, outputs, and corresponding technology that are currently available i | `air_separator` | nitrogen | electricity | | `desal` | water | electricity | - +(transport)= ## Transport `Transport` models are used to either: - connect the 'Transport Commodity' from a technology that produces the 'Transport Commodity' to a technology that consumes or stores the 'Transport Commodity' OR @@ -60,6 +62,7 @@ The inputs, outputs, and corresponding technology that are currently available i Connection: `[source_tech, dest_tech, transport_commodity, transport_technology]` +(storage)= ## Storage `Storage` technologies input and output the 'Storage Commodity' at different times. These technologies can be filled or charged, then unfilled or discharged at some later time. These models are usually constrained by two key model parameters: storage capacity and charge/discharge rate. @@ -68,6 +71,7 @@ Connection: `[source_tech, dest_tech, transport_commodity, transport_technology] | `h2_storage` | hydrogen | | `battery` | electricity | +(controller)= ## Controller `Controller` models are used to control the `Storage` models and resource flows. @@ -84,12 +88,13 @@ Below summarizes the available performance, cost, and financial models for each - [Transport](#transport-models) - [Storage](#storage-models) - +(resource-models)= ### Resource models - `river`: - performance models: + `river_resource` +(converter-models)= ### Converter models - `wind`: wind turbine - performance models: @@ -173,7 +178,7 @@ Below summarizes the available performance, cost, and financial models for each - cost models: + `'reverse_osmosis_desalination_cost'` - +(transport-models)= ### Transport Models - `cable` - performance models: @@ -185,7 +190,7 @@ Below summarizes the available performance, cost, and financial models for each - performance models: + `'combiner_performance'` - +(storage-models)= ### Storage Models - `h2_storage`: hydrogen storage - combined performance and cost diff --git a/docs/user_guide/cost_years.md b/docs/user_guide/cost_years.md index 10d0994e2..81fc07858 100644 --- a/docs/user_guide/cost_years.md +++ b/docs/user_guide/cost_years.md @@ -2,6 +2,7 @@ # Cost year of Cost Models Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user-provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. +(cost-models-with-inherent-cost-year)= ## Cost models with inherent cost year ### Summary of cost models that are based around a cost year @@ -20,7 +21,7 @@ Some cost models are derived from literature and output costs (CapEx and OpEx) i | `reverse_osmosis_desalination_cost` | 2013 | | `synloop_ammonia_cost` | N/A (adjusts costs to `target_dollar_year` within cost model) | - +(cost-models-with-user-input-cost-year)= ## Cost models with user input cost year ### Summary of cost models that have user-input cost year From 4c32535e6972e7d28c50360b02c328f7ca0a7a37 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Fri, 12 Sep 2025 21:23:47 -0500 Subject: [PATCH 55/67] update examples with new logic --- .../01_onshore_steel_mn/plant_config.yaml | 2 +- examples/01_onshore_steel_mn/tech_config.yaml | 2 +- examples/02_texas_ammonia/plant_config.yaml | 53 ++-- .../co2_hydrogenation/plant_config_co2h.yaml | 49 ++-- .../co2_hydrogenation/tech_config_co2h.yaml | 2 +- examples/03_methanol/smr/tech_config_smr.yaml | 2 +- examples/04_geo_h2/tech_config_natural.yaml | 2 +- .../04_geo_h2/tech_config_stimulated.yaml | 2 +- examples/05_wind_h2_opt/driver_config.yaml | 2 +- examples/05_wind_h2_opt/plant_config.yaml | 45 ++-- examples/06_custom_tech/tech_config.yaml | 2 +- .../07_run_of_river_plant/plant_config.yaml | 45 ++-- .../direct_ocean_capture/plant_config.yaml | 45 ++-- .../plant_config.yaml | 45 ++-- .../plant_config_financials.yaml | 47 ++-- .../tech_config_financials.yaml | 2 +- examples/10_electrolyzer_om/plant_config.yaml | 95 +++---- .../11_hybrid_energy_plant/driver_config.yaml | 2 +- .../11_hybrid_energy_plant/plant_config.yaml | 45 ++-- examples/12_ammonia_synloop/plant_config.yaml | 45 ++-- examples/13_air_separator/plant_config.yaml | 45 ++-- .../hydrogen_dispatch.ipynb | 236 ++++++++++-------- .../inputs/plant_config.yaml | 45 ++-- .../plant_config.yaml | 45 ++-- examples/16_natural_gas/plant_config.yaml | 45 ++-- .../17_splitter_wind_doc_h2/plant_config.yaml | 45 ++-- h2integrate/core/h2integrate_model.py | 210 +++++++++------- 27 files changed, 630 insertions(+), 575 deletions(-) diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index 237500741..45ddb0e30 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -25,7 +25,7 @@ technology_interconnections: [ ["hopp", "electrolyzer", "electricity", "cable"], ["electrolyzer", "h2_storage", "efficiency"], ["electrolyzer", "h2_storage", "hydrogen", "pipe"], - ["financials_subgroup_hydrogen", "steel", ["LCOH_delivered", "LCOH"]], + ["finance_subgroup_hydrogen", "steel", ["LCOH_delivered", "LCOH"]], ["hopp", "steel", "electricity", "cable"], # etc ] diff --git a/examples/01_onshore_steel_mn/tech_config.yaml b/examples/01_onshore_steel_mn/tech_config.yaml index 2c08bf27e..fd4775e5e 100644 --- a/examples/01_onshore_steel_mn/tech_config.yaml +++ b/examples/01_onshore_steel_mn/tech_config.yaml @@ -61,7 +61,7 @@ technologies: model: "steel_performance" cost_model: model: "steel_cost" - financial_model: + finance_model: model: "steel_cost" model_inputs: shared_parameters: diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index 4075f501c..0e6d15f36 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -26,7 +26,7 @@ technology_interconnections: [ ["electrolyzer", "h2_storage", "efficiency"], ["electrolyzer", "h2_storage", "hydrogen", "pipe"], ["h2_storage", "ammonia", "hydrogen", "pipe"], - ["financials_subgroup_hydrogen", "ammonia", "LCOH"], + ["finance_subgroup_hydrogen", "ammonia", "LCOH"], # etc ] @@ -34,34 +34,35 @@ plant: plant_life: 30 finance_parameters: - finance_model: "ProFastComp" - model_inputs: - save_profast_config: True - save_profast_results: False - profast_output_description: "profast_output" - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for real analysis - discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.025 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx and # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf - total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + save_profast_config: True + save_profast_results: False + profast_output_description: "profast_output" + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for real analysis + discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.025 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx and # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp"] diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index fbe682715..ddf6e571e 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -24,36 +24,37 @@ site: technology_interconnections: [ ["hopp", "electrolyzer", "electricity", "cable"], ["electrolyzer", "methanol", "hydrogen", "pipe"], - ["financials_subgroup_electricity", "methanol", "LCOE"], - ["financials_subgroup_hydrogen", "methanol", "LCOH"], + ["finance_subgroup_electricity", "methanol", "LCOE"], + ["finance_subgroup_hydrogen", "methanol", "LCOH"], ] plant: plant_life: 30 finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - subgroups: + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB basline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp", "electrolyzer"] diff --git a/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml index e1e436813..f90f58190 100644 --- a/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/tech_config_co2h.yaml @@ -42,7 +42,7 @@ technologies: model: "co2h_methanol_plant_performance" cost_model: model: "co2h_methanol_plant_cost" - financial_model: + finance_model: model: "co2h_methanol_plant_financial" model_inputs: shared_parameters: diff --git a/examples/03_methanol/smr/tech_config_smr.yaml b/examples/03_methanol/smr/tech_config_smr.yaml index 067652bcd..ff52dd360 100644 --- a/examples/03_methanol/smr/tech_config_smr.yaml +++ b/examples/03_methanol/smr/tech_config_smr.yaml @@ -7,7 +7,7 @@ technologies: model: "smr_methanol_plant_performance" cost_model: model: "smr_methanol_plant_cost" - financial_model: + finance_model: model: "smr_methanol_plant_financial" model_inputs: shared_parameters: diff --git a/examples/04_geo_h2/tech_config_natural.yaml b/examples/04_geo_h2/tech_config_natural.yaml index 818a0d074..fc4467797 100644 --- a/examples/04_geo_h2/tech_config_natural.yaml +++ b/examples/04_geo_h2/tech_config_natural.yaml @@ -7,7 +7,7 @@ technologies: model: "natural_geoh2_performance" cost_model: model: "natural_geoh2_cost" - financial_model: + finance_model: model: "natural_geoh2" model_inputs: shared_parameters: diff --git a/examples/04_geo_h2/tech_config_stimulated.yaml b/examples/04_geo_h2/tech_config_stimulated.yaml index dda5a431e..505e3e6d7 100644 --- a/examples/04_geo_h2/tech_config_stimulated.yaml +++ b/examples/04_geo_h2/tech_config_stimulated.yaml @@ -7,7 +7,7 @@ technologies: model: "stimulated_geoh2_performance" cost_model: model: "stimulated_geoh2_cost" - financial_model: + finance_model: model: "stimulated_geoh2" model_inputs: shared_parameters: diff --git a/examples/05_wind_h2_opt/driver_config.yaml b/examples/05_wind_h2_opt/driver_config.yaml index b950c4591..d84bc9742 100644 --- a/examples/05_wind_h2_opt/driver_config.yaml +++ b/examples/05_wind_h2_opt/driver_config.yaml @@ -30,7 +30,7 @@ constraints: units: kg/year objective: - name: financials_subgroup_hydrogen.LCOH + name: finance_subgroup_hydrogen.LCOH recorder: flag: True diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index f8d04ea72..ed72ede45 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -30,31 +30,32 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" technologies: ["wind", "electrolyzer"] diff --git a/examples/06_custom_tech/tech_config.yaml b/examples/06_custom_tech/tech_config.yaml index a8a760673..ecb36885f 100644 --- a/examples/06_custom_tech/tech_config.yaml +++ b/examples/06_custom_tech/tech_config.yaml @@ -45,7 +45,7 @@ technologies: model: paper_mill_cost model_class_name: PaperMillCost model_location: user_defined_model/paper_mill.py - financial_model: + finance_model: model: paper_mill_finance model_class_name: PaperMillFinance model_location: user_defined_model/paper_mill.py diff --git a/examples/07_run_of_river_plant/plant_config.yaml b/examples/07_run_of_river_plant/plant_config.yaml index 7a2cfbb3d..ae3de2f4a 100644 --- a/examples/07_run_of_river_plant/plant_config.yaml +++ b/examples/07_run_of_river_plant/plant_config.yaml @@ -41,28 +41,29 @@ plant: plant_life: 30 finance_parameters: - commodity: "electricity" - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + commodity: "electricity" + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.308 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 45d9e04cd..8a93d56b8 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -30,31 +30,32 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp", "doc"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index ae8952a24..9dc6bd5c0 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -30,31 +30,32 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # 2.5% inflation rate for cost adjustments target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp", "oae"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml index 0f4f29f7d..fb6d6caa6 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml @@ -26,33 +26,34 @@ plant: # this will naturally grow as we mature the interconnected tech technology_interconnections: [ ["hopp", "oae", "electricity", "cable"], - ["financials_subgroup_electricity", "oae", "LCOE"] + ["finance_subgroup_electricity", "oae", "LCOE"] # etc ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - subgroups: + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml index a9c1e32db..5e1614cb3 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement_financials/tech_config_financials.yaml @@ -14,7 +14,7 @@ technologies: oae: performance_model: model: "ocean_alkalinity_enhancement_performance" - financial_model: + finance_model: model: "ocean_alkalinity_enhancement_cost_financial" model_inputs: performance_parameters: diff --git a/examples/10_electrolyzer_om/plant_config.yaml b/examples/10_electrolyzer_om/plant_config.yaml index 777426034..5ce6b2a56 100644 --- a/examples/10_electrolyzer_om/plant_config.yaml +++ b/examples/10_electrolyzer_om/plant_config.yaml @@ -30,56 +30,57 @@ technology_interconnections: [ ] finance_parameters: - lcoe_financials: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.0615 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.015 - total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.0 - debt_interest_rate: 0.0439 - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - lcoh_financials: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.1089 - debt_equity_ratio: 0.62 - property_tax_and_insurance: 0.03 - total_income_tax_rate: 0.2574 - capital_gains_tax_rate: 0.15 - sales_tax_rate: 0.0 - debt_interest_rate: 0.05 - debt_type: "Revolving debt" - loan_period_if_used: 0 - cash_onhand_months: 1 - admin_expense: 0.00 # percent of sales H2FAST default - non_depr_assets: 250000 - end_of_proj_sale_non_depr_assets: 250000 - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + lcoe_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.0615 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.015 + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 + debt_interest_rate: 0.0439 + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + lcoh_financials: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.1089 + debt_equity_ratio: 0.62 + property_tax_and_insurance: 0.03 + total_income_tax_rate: 0.2574 + capital_gains_tax_rate: 0.15 + sales_tax_rate: 0.0 + debt_interest_rate: 0.05 + debt_type: "Revolving debt" + loan_period_if_used: 0 + cash_onhand_months: 1 + admin_expense: 0.00 # percent of sales H2FAST default + non_depr_assets: 250000 + end_of_proj_sale_non_depr_assets: 250000 + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" finance_groups: ["lcoe_financials"] diff --git a/examples/11_hybrid_energy_plant/driver_config.yaml b/examples/11_hybrid_energy_plant/driver_config.yaml index e2dfcb4fd..23e7b1050 100644 --- a/examples/11_hybrid_energy_plant/driver_config.yaml +++ b/examples/11_hybrid_energy_plant/driver_config.yaml @@ -60,4 +60,4 @@ constraints: units: unitless objective: - name: financials_subgroup_default.LCOE + name: finance_subgroup_default.LCOE diff --git a/examples/11_hybrid_energy_plant/plant_config.yaml b/examples/11_hybrid_energy_plant/plant_config.yaml index 966e1ceb0..6112af41c 100644 --- a/examples/11_hybrid_energy_plant/plant_config.yaml +++ b/examples/11_hybrid_energy_plant/plant_config.yaml @@ -21,28 +21,29 @@ plant: plant_life: 30 finance_parameters: - commodity: "electricity" - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 12 # months - inflation_rate: 0.025 # 0 for nominal analysis - discount_rate: 0.089 # nominal return - debt_equity_ratio: 2.1746031746031744 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.015 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.045 - debt_interest_rate: 0.06 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.01 - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + commodity: "electricity" + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 12 # months + inflation_rate: 0.025 # 0 for nominal analysis + discount_rate: 0.089 # nominal return + debt_equity_ratio: 2.1746031746031744 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.015 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.045 + debt_interest_rate: 0.06 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.01 + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2025 diff --git a/examples/12_ammonia_synloop/plant_config.yaml b/examples/12_ammonia_synloop/plant_config.yaml index 8aaa7a622..d44053983 100644 --- a/examples/12_ammonia_synloop/plant_config.yaml +++ b/examples/12_ammonia_synloop/plant_config.yaml @@ -33,31 +33,32 @@ plant: plant_life: 30 finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for real analysis - discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 7 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for real analysis + discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 7 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: h2: commodity: "hydrogen" technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] diff --git a/examples/13_air_separator/plant_config.yaml b/examples/13_air_separator/plant_config.yaml index 23b851a48..3f87e9cb8 100644 --- a/examples/13_air_separator/plant_config.yaml +++ b/examples/13_air_separator/plant_config.yaml @@ -30,28 +30,29 @@ plant: plant_life: 30 finance_parameters: - commodity: "nitrogen" - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for real analysis - discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + commodity: "nitrogen" + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for real analysis + discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 7 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2025 diff --git a/examples/14_wind_hydrogen_dispatch/hydrogen_dispatch.ipynb b/examples/14_wind_hydrogen_dispatch/hydrogen_dispatch.ipynb index eb306d865..e4b3f13ca 100644 --- a/examples/14_wind_hydrogen_dispatch/hydrogen_dispatch.ipynb +++ b/examples/14_wind_hydrogen_dispatch/hydrogen_dispatch.ipynb @@ -44,15 +44,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/Users/egrant/Documents/projects/GreenHEART/examples/14_wind_hydrogen_dispatch/log/hybrid_systems_2025-08-22T12.14.13.550319.log\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/egrant/opt/anaconda3/envs/h2i_env/lib/python3.11/site-packages/fastkml/__init__.py:28: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", - " from pkg_resources import DistributionNotFound\n" + "/Users/kbrunik/github/H2Integrate/examples/14_wind_hydrogen_dispatch/log/hybrid_systems_2025-09-12T21.16.47.965370.log\n" ] } ], @@ -64,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "bcabc113", "metadata": {}, "outputs": [ @@ -80,128 +72,161 @@ "name": "stdout", "output_type": "stream", "text": [ - "38 Input(s) in 'model'\n", + "47 Input(s) in 'model'\n", "\n", - "varname val units prom_name \n", - "----------------------------------------- -------------------- --------- ------------------------------------------------------------\n", + "varname val units prom_name \n", + "----------------------------------------- -------------------- --------- ---------------------------------------------------------\n", "plant\n", " wind_to_electrolyzer_cable\n", - " electricity_in |40393548.72275606| kW wind_to_electrolyzer_cable.electricity_in \n", + " electricity_in |40393548.93300275| kW wind_to_electrolyzer_cable.electricity_in \n", " electrolyzer\n", " eco_pem_electrolyzer_performance\n", - " electricity_in |40393548.72275606| kW electrolyzer.electricity_in \n", - " n_clusters [18.] unitless electrolyzer.n_clusters \n", + " electricity_in |40393548.93300279| kW electrolyzer.electricity_in \n", + " n_clusters [18.] unitless electrolyzer.n_clusters \n", " singlitico_electrolyzer_cost\n", - " total_hydrogen_produced [58145418.05837836] kg/year electrolyzer.total_hydrogen_produced \n", - " electricity_in |40393548.72275606| kW electrolyzer.electricity_in \n", - " electrolyzer_size_mw [720.] MW electrolyzer.electrolyzer_size_mw \n", + " total_hydrogen_produced [58145418.38681877] kg/year electrolyzer.total_hydrogen_produced \n", + " electricity_in |40393548.93300275| kW electrolyzer.electricity_in \n", + " electrolyzer_size_mw [720.] MW electrolyzer.electrolyzer_size_mw \n", " electrolyzer_to_h2_storage_pipe\n", - " hydrogen_in |219.95801082| kg/s electrolyzer_to_h2_storage_pipe.hydrogen_in \n", + " hydrogen_in |219.95801192| kg/s electrolyzer_to_h2_storage_pipe.hydrogen_in \n", " h2_storage\n", " h2_storage\n", - " hydrogen_in |791848.83895374| kg/h h2_storage.hydrogen_in \n", - " rated_h2_production_kg_pr_hr [0.] kg/h h2_storage.rated_h2_production_kg_pr_hr \n", - " efficiency [0.77728804] None h2_storage.efficiency \n", + " hydrogen_in |791848.84291256| kg/h h2_storage.hydrogen_in \n", + " rated_h2_production_kg_pr_hr [0.] kg/h h2_storage.rated_h2_production_kg_pr_hr \n", + " efficiency [0.77728804] None h2_storage.efficiency \n", " demand_open_loop_controller\n", - " hydrogen_in |791848.83895374| kg/h h2_storage.hydrogen_in \n", - " hydrogen_demand_profile |467974.3582719| kg/h**2 h2_storage.hydrogen_demand_profile \n", - " financials_group_default\n", + " hydrogen_in |791848.84291256| kg/h h2_storage.hydrogen_in \n", + " hydrogen_demand_profile |467974.3582719| kg/h**2 h2_storage.hydrogen_demand_profile \n", + " finance_subgroup_h2\n", + " adjusted_capex_opex_comp\n", + " capex_wind [1.245e+09] USD finance_subgroup_h2.capex_wind \n", + " opex_wind [37350000.] USD/year finance_subgroup_h2.opex_wind \n", + " capex_electrolyzer [6.75464089e+08] USD finance_subgroup_h2.capex_electrolyzer \n", + " opex_electrolyzer [16541049.81608545] USD/year finance_subgroup_h2.opex_electrolyzer \n", + " capex_h2_storage [1.28437699e+08] USD finance_subgroup_h2.capex_h2_storage \n", + " opex_h2_storage [4330693.5188038] USD/year finance_subgroup_h2.opex_h2_storage \n", + " cost_year_wind 2019 n/a finance_subgroup_h2.cost_year_wind \n", + " cost_year_electrolyzer 2021 n/a finance_subgroup_h2.cost_year_electrolyzer \n", + " cost_year_h2_storage 2018 n/a finance_subgroup_h2.cost_year_h2_storage \n", + " default_hydrogen\n", + " total_hydrogen_produced [58145418.38681877] kg/year finance_subgroup_h2.total_hydrogen_produced \n", + " capex_adjusted_wind [1.34072883e+09] USD finance_subgroup_h2.capex_adjusted_wind \n", + " opex_adjusted_wind [40221864.84374999] USD/year finance_subgroup_h2.opex_adjusted_wind \n", + " capex_adjusted_electrolyzer [6.92350691e+08] USD finance_subgroup_h2.capex_adjusted_electrolyzer \n", + " opex_adjusted_electrolyzer [16954576.06148758] USD/year finance_subgroup_h2.opex_adjusted_electrolyzer \n", + " capex_adjusted_h2_storage [1.41771187e+08] USD finance_subgroup_h2.capex_adjusted_h2_storage \n", + " opex_adjusted_h2_storage [4780275.33140178] USD/year finance_subgroup_h2.opex_adjusted_h2_storage \n", + " electrolyzer_time_until_replacement [32414.89402766] h finance_subgroup_h2.electrolyzer_time_until_replacement \n", + " finance_subgroup_elec\n", " electricity_sum\n", - " electricity_wind |40393548.72275606| kW financials_group_default.electricity_sum.electricity_wind \n", + " electricity_wind |40393548.93300275| kW finance_subgroup_elec.electricity_sum.electricity_wind \n", " adjusted_capex_opex_comp\n", - " capex_wind [1.245e+09] USD financials_group_default.capex_wind \n", - " opex_wind [37350000.] USD/year financials_group_default.opex_wind \n", - " capex_electrolyzer [6.75464089e+08] USD financials_group_default.capex_electrolyzer \n", - " opex_electrolyzer [16541049.81608545] USD/year financials_group_default.opex_electrolyzer \n", - " capex_h2_storage [1.28437699e+08] USD financials_group_default.capex_h2_storage \n", - " opex_h2_storage [4330693.5188056] USD/year financials_group_default.opex_h2_storage \n", - " cost_year_wind 2019 n/a financials_group_default.cost_year_wind \n", - " cost_year_electrolyzer 2021 n/a financials_group_default.cost_year_electrolyzer \n", - " cost_year_h2_storage 2018 n/a financials_group_default.cost_year_h2_storage \n", - " ProFastComp_0\n", - " total_hydrogen_produced [58145418.05837836] kg/year financials_group_default.total_hydrogen_produced \n", - " capex_adjusted_wind [1.34072883e+09] USD financials_group_default.capex_adjusted_wind \n", - " opex_adjusted_wind [40221864.84374999] USD/year financials_group_default.opex_adjusted_wind \n", - " capex_adjusted_electrolyzer [6.92350691e+08] USD financials_group_default.capex_adjusted_electrolyzer \n", - " opex_adjusted_electrolyzer [16954576.06148758] USD/year financials_group_default.opex_adjusted_electrolyzer \n", - " capex_adjusted_h2_storage [1.41771187e+08] USD financials_group_default.capex_adjusted_h2_storage \n", - " opex_adjusted_h2_storage [4780275.33140376] USD/year financials_group_default.opex_adjusted_h2_storage \n", - " electrolyzer_time_until_replacement [32414.89403159] h financials_group_default.electrolyzer_time_until_replacement\n", - " ProFastComp_1\n", - " total_electricity_produced [3.11408814e+09] kW*h/year financials_group_default.total_electricity_produced \n", - " capex_adjusted_wind [1.34072883e+09] USD financials_group_default.capex_adjusted_wind \n", - " opex_adjusted_wind [40221864.84374999] USD/year financials_group_default.opex_adjusted_wind \n", - " capex_adjusted_electrolyzer [6.92350691e+08] USD financials_group_default.capex_adjusted_electrolyzer \n", - " opex_adjusted_electrolyzer [16954576.06148758] USD/year financials_group_default.opex_adjusted_electrolyzer \n", - " capex_adjusted_h2_storage [1.41771187e+08] USD financials_group_default.capex_adjusted_h2_storage \n", - " opex_adjusted_h2_storage [4780275.33140376] USD/year financials_group_default.opex_adjusted_h2_storage \n", - " electrolyzer_time_until_replacement [32414.89403159] h financials_group_default.electrolyzer_time_until_replacement\n", + " capex_wind [1.245e+09] USD finance_subgroup_elec.capex_wind \n", + " opex_wind [37350000.] USD/year finance_subgroup_elec.opex_wind \n", + " capex_electrolyzer [6.75464089e+08] USD finance_subgroup_elec.capex_electrolyzer \n", + " opex_electrolyzer [16541049.81608545] USD/year finance_subgroup_elec.opex_electrolyzer \n", + " capex_h2_storage [1.28437699e+08] USD finance_subgroup_elec.capex_h2_storage \n", + " opex_h2_storage [4330693.5188038] USD/year finance_subgroup_elec.opex_h2_storage \n", + " cost_year_wind 2019 n/a finance_subgroup_elec.cost_year_wind \n", + " cost_year_electrolyzer 2021 n/a finance_subgroup_elec.cost_year_electrolyzer \n", + " cost_year_h2_storage 2018 n/a finance_subgroup_elec.cost_year_h2_storage \n", + " default_electricity\n", + " total_electricity_produced [3.11408816e+09] kW*h/year finance_subgroup_elec.total_electricity_produced \n", + " capex_adjusted_wind [1.34072883e+09] USD finance_subgroup_elec.capex_adjusted_wind \n", + " opex_adjusted_wind [40221864.84374999] USD/year finance_subgroup_elec.opex_adjusted_wind \n", + " capex_adjusted_electrolyzer [6.92350691e+08] USD finance_subgroup_elec.capex_adjusted_electrolyzer \n", + " opex_adjusted_electrolyzer [16954576.06148758] USD/year finance_subgroup_elec.opex_adjusted_electrolyzer \n", + " capex_adjusted_h2_storage [1.41771187e+08] USD finance_subgroup_elec.capex_adjusted_h2_storage \n", + " opex_adjusted_h2_storage [4780275.33140178] USD/year finance_subgroup_elec.opex_adjusted_h2_storage \n", + " electrolyzer_time_until_replacement [32414.89402766] h finance_subgroup_elec.electrolyzer_time_until_replacement\n", "\n", "\n", - "41 Explicit Output(s) in 'model'\n", + "61 Explicit Output(s) in 'model'\n", "\n", - "varname val units prom_name \n", - "------------------------------------ -------------------- --------- -------------------------------------------------------------------\n", + "varname val units prom_name \n", + "----------------------------------------- -------------------- --------- ----------------------------------------------------------------\n", "site\n", " site_component\n", - " latitude [47.5233] None latitude \n", - " longitude [-92.5366] None longitude \n", - " elevation_m [439.] None elevation_m \n", - " time_zone [-5.] None time_zone \n", - " boundary_0_x |1414.21356237| None boundary_0_x \n", - " boundary_0_y |1004.98756211| None boundary_0_y \n", - " boundary_1_x |3774.91721764| None boundary_1_x \n", - " boundary_1_y |3774.91721764| None boundary_1_y \n", + " latitude [47.5233] None latitude \n", + " longitude [-92.5366] None longitude \n", + " elevation_m [0.] None elevation_m \n", + " time_zone [0.] None time_zone \n", + " boundary_0_x |1414.21356237| None boundary_0_x \n", + " boundary_0_y |1004.98756211| None boundary_0_y \n", + " boundary_1_x |3774.91721764| None boundary_1_x \n", + " boundary_1_y |3774.91721764| None boundary_1_y \n", "plant\n", " wind\n", " wind_plant_performance\n", - " electricity_out |40393548.72275606| kW wind.electricity_out \n", + " electricity_out |40393548.93300275| kW wind.electricity_out \n", " wind_plant_cost\n", - " CapEx [1.245e+09] USD wind.CapEx \n", - " OpEx [37350000.] USD/year wind.OpEx \n", - " cost_year 2019 n/a wind.cost_year \n", + " CapEx [1.245e+09] USD wind.CapEx \n", + " OpEx [37350000.] USD/year wind.OpEx \n", + " cost_year 2019 n/a wind.cost_year \n", " wind_to_electrolyzer_cable\n", - " electricity_out |40393548.72275606| kW wind_to_electrolyzer_cable.electricity_out \n", + " electricity_out |40393548.93300275| kW wind_to_electrolyzer_cable.electricity_out \n", " electrolyzer\n", " eco_pem_electrolyzer_performance\n", - " hydrogen_out |791848.83895374| kg/h electrolyzer.hydrogen_out \n", - " time_until_replacement [32414.89403159] h electrolyzer.time_until_replacement \n", - " total_hydrogen_produced [58145418.05837836] kg/year electrolyzer.total_hydrogen_produced \n", - " efficiency [0.77728804] None electrolyzer.efficiency \n", - " rated_h2_production_kg_pr_hr [14118.38052448] kg/h electrolyzer.rated_h2_production_kg_pr_hr \n", - " electrolyzer_size_mw [720.] MW electrolyzer.electrolyzer_size_mw \n", + " hydrogen_out |791848.84291256| kg/h electrolyzer.hydrogen_out \n", + " time_until_replacement [32414.89402766] h electrolyzer.time_until_replacement \n", + " total_hydrogen_produced [58145418.38681877] kg/year electrolyzer.total_hydrogen_produced \n", + " efficiency [0.77728804] None electrolyzer.efficiency \n", + " rated_h2_production_kg_pr_hr [14118.38052473] kg/h electrolyzer.rated_h2_production_kg_pr_hr \n", + " electrolyzer_size_mw [720.] MW electrolyzer.electrolyzer_size_mw \n", " singlitico_electrolyzer_cost\n", - " CapEx [6.75464089e+08] USD electrolyzer.CapEx \n", - " OpEx [16541049.81608545] USD/year electrolyzer.OpEx \n", - " cost_year 2021 n/a electrolyzer.cost_year \n", + " CapEx [6.75464089e+08] USD electrolyzer.CapEx \n", + " OpEx [16541049.81608545] USD/year electrolyzer.OpEx \n", + " cost_year 2021 n/a electrolyzer.cost_year \n", " electrolyzer_to_h2_storage_pipe\n", - " hydrogen_out |219.95801082| kg/s electrolyzer_to_h2_storage_pipe.hydrogen_out \n", + " hydrogen_out |219.95801192| kg/s electrolyzer_to_h2_storage_pipe.hydrogen_out \n", " h2_storage\n", " h2_storage\n", - " CapEx [1.28437699e+08] USD h2_storage.CapEx \n", - " OpEx [4330693.5188056] USD/year h2_storage.OpEx \n", - " cost_year 2018 n/a h2_storage.cost_year \n", + " CapEx [1.28437699e+08] USD h2_storage.CapEx \n", + " OpEx [4330693.5188038] USD/year h2_storage.OpEx \n", + " cost_year 2018 n/a h2_storage.cost_year \n", " demand_open_loop_controller\n", - " hydrogen_out |440928.0814644| kg/h h2_storage.hydrogen_out \n", - " hydrogen_soc |74.53109573| unitless h2_storage.hydrogen_soc \n", - " hydrogen_curtailed |376863.76618072| kg/h h2_storage.hydrogen_curtailed \n", - " hydrogen_missed_load |133351.12119996| kg/h h2_storage.hydrogen_missed_load \n", - " financials_group_default\n", + " hydrogen_out |440928.08133911| kg/h h2_storage.hydrogen_out \n", + " hydrogen_soc |74.53109581| unitless h2_storage.hydrogen_soc \n", + " hydrogen_curtailed |376863.77252| kg/h h2_storage.hydrogen_curtailed \n", + " hydrogen_missed_load |133351.12099949| kg/h h2_storage.hydrogen_missed_load \n", + " finance_subgroup_h2\n", + " adjusted_capex_opex_comp\n", + " capex_adjusted_wind [1.34072883e+09] USD finance_subgroup_h2.capex_adjusted_wind \n", + " opex_adjusted_wind [40221864.84374999] USD/year finance_subgroup_h2.opex_adjusted_wind \n", + " capex_adjusted_electrolyzer [6.92350691e+08] USD finance_subgroup_h2.capex_adjusted_electrolyzer \n", + " opex_adjusted_electrolyzer [16954576.06148758] USD/year finance_subgroup_h2.opex_adjusted_electrolyzer \n", + " capex_adjusted_h2_storage [1.41771187e+08] USD finance_subgroup_h2.capex_adjusted_h2_storage \n", + " opex_adjusted_h2_storage [4780275.33140178] USD/year finance_subgroup_h2.opex_adjusted_h2_storage \n", + " total_capex_adjusted [2.17485071e+09] USD finance_subgroup_h2.total_capex_adjusted \n", + " total_opex_adjusted [61956716.23663935] USD/year finance_subgroup_h2.total_opex_adjusted \n", + " default_hydrogen\n", + " LCOH [5.68452212] USD/kg finance_subgroup_h2.LCOH \n", + " wacc_hydrogen [0.06250448] percent finance_subgroup_h2.wacc_hydrogen \n", + " crf_hydrogen [0.07227903] percent finance_subgroup_h2.crf_hydrogen \n", + " irr_hydrogen [0.09] percent finance_subgroup_h2.irr_hydrogen \n", + " profit_index_hydrogen [1.80334107] unitless finance_subgroup_h2.profit_index_hydrogen \n", + " investor_payback_period_hydrogen [9.] year finance_subgroup_h2.investor_payback_period_hydrogen \n", + " price_hydrogen [5.68452212] USD/kg finance_subgroup_h2.price_hydrogen \n", + " finance_subgroup_elec\n", " electricity_sum\n", - " total_electricity_produced [3.11408814e+09] kW*h/year financials_group_default.electricity_sum.total_electricity_produced\n", + " total_electricity_produced [3.11408816e+09] kW*h/year finance_subgroup_elec.electricity_sum.total_electricity_produced\n", " adjusted_capex_opex_comp\n", - " capex_adjusted_wind [1.34072883e+09] USD financials_group_default.capex_adjusted_wind \n", - " opex_adjusted_wind [40221864.84374999] USD/year financials_group_default.opex_adjusted_wind \n", - " capex_adjusted_electrolyzer [6.92350691e+08] USD financials_group_default.capex_adjusted_electrolyzer \n", - " opex_adjusted_electrolyzer [16954576.06148758] USD/year financials_group_default.opex_adjusted_electrolyzer \n", - " capex_adjusted_h2_storage [1.41771187e+08] USD financials_group_default.capex_adjusted_h2_storage \n", - " opex_adjusted_h2_storage [4780275.33140376] USD/year financials_group_default.opex_adjusted_h2_storage \n", - " total_capex_adjusted [2.17485071e+09] USD financials_group_default.total_capex_adjusted \n", - " total_opex_adjusted [61956716.23664133] USD/year financials_group_default.total_opex_adjusted \n", - " ProFastComp_0\n", - " LCOH [5.68452215] USD/kg financials_group_default.LCOH \n", - " ProFastComp_1\n", - " LCOE [0.10613987] USD/kW/h financials_group_default.LCOE \n", + " capex_adjusted_wind [1.34072883e+09] USD finance_subgroup_elec.capex_adjusted_wind \n", + " opex_adjusted_wind [40221864.84374999] USD/year finance_subgroup_elec.opex_adjusted_wind \n", + " capex_adjusted_electrolyzer [6.92350691e+08] USD finance_subgroup_elec.capex_adjusted_electrolyzer \n", + " opex_adjusted_electrolyzer [16954576.06148758] USD/year finance_subgroup_elec.opex_adjusted_electrolyzer \n", + " capex_adjusted_h2_storage [1.41771187e+08] USD finance_subgroup_elec.capex_adjusted_h2_storage \n", + " opex_adjusted_h2_storage [4780275.33140178] USD/year finance_subgroup_elec.opex_adjusted_h2_storage \n", + " total_capex_adjusted [2.17485071e+09] USD finance_subgroup_elec.total_capex_adjusted \n", + " total_opex_adjusted [61956716.23663935] USD/year finance_subgroup_elec.total_opex_adjusted \n", + " default_electricity\n", + " LCOE [0.10613987] USD/kW/h finance_subgroup_elec.LCOE \n", + " wacc_electricity [0.06250448] percent finance_subgroup_elec.wacc_electricity \n", + " crf_electricity [0.07227903] percent finance_subgroup_elec.crf_electricity \n", + " irr_electricity [0.09] percent finance_subgroup_elec.irr_electricity \n", + " profit_index_electricity [1.80334107] unitless finance_subgroup_elec.profit_index_electricity \n", + " investor_payback_period_electricity [9.] year finance_subgroup_elec.investor_payback_period_electricity \n", + " price_electricity [0.10613987] USD/kW/h finance_subgroup_elec.price_electricity \n", "\n", "\n", "0 Implicit Output(s) in 'model'\n", @@ -315,7 +340,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.11.13 ('h2i_env')", + "display_name": "h2integrate-mcm", "language": "python", "name": "python3" }, @@ -329,12 +354,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.13" - }, - "vscode": { - "interpreter": { - "hash": "e55566d5f9cb5003b92ad1d2254e8146f3d62519cfa868f35d73d51fb57327c6" - } + "version": "3.11.12" } }, "nbformat": 4, diff --git a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml index f50173e80..6d3b6c212 100644 --- a/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml +++ b/examples/14_wind_hydrogen_dispatch/inputs/plant_config.yaml @@ -31,31 +31,32 @@ plant: plant_life: 30 finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2030 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2030 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: elec: commodity: "electricity" technologies: ["wind", "electrolyzer", "h2_storage"] diff --git a/examples/15_wind_solar_electrolyzer/plant_config.yaml b/examples/15_wind_solar_electrolyzer/plant_config.yaml index f8c235a82..ddbef2829 100644 --- a/examples/15_wind_solar_electrolyzer/plant_config.yaml +++ b/examples/15_wind_solar_electrolyzer/plant_config.yaml @@ -38,28 +38,29 @@ plant: dt: 3600 finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - subgroups: + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + finance_subgroups: electricity: commodity: "electricity" technologies: ["wind", "solar"] diff --git a/examples/16_natural_gas/plant_config.yaml b/examples/16_natural_gas/plant_config.yaml index 65b1430cd..0955376a2 100644 --- a/examples/16_natural_gas/plant_config.yaml +++ b/examples/16_natural_gas/plant_config.yaml @@ -33,28 +33,29 @@ plant: n_timesteps: 8760 finance_parameters: - commodity: "electricity" - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 - inflation_rate: 0.0 - discount_rate: 0.09 - debt_equity_ratio: 2.62 - property_tax_and_insurance: 0.03 - total_income_tax_rate: 0.308 - capital_gains_tax_rate: 0.15 - sales_tax_rate: 0.07375 - debt_interest_rate: 0.07 - debt_type: "Revolving debt" - loan_period_if_used: 0 - cash_onhand_months: 1 - admin_expense: 0.00 - capital_items: - depr_type: "MACRS" - depr_period: 5 - refurb: [0.] + finance_groups: + commodity: "electricity" + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 + inflation_rate: 0.0 + discount_rate: 0.09 + debt_equity_ratio: 2.62 + property_tax_and_insurance: 0.03 + total_income_tax_rate: 0.308 + capital_gains_tax_rate: 0.15 + sales_tax_rate: 0.07375 + debt_interest_rate: 0.07 + debt_type: "Revolving debt" + loan_period_if_used: 0 + cash_onhand_months: 1 + admin_expense: 0.00 + capital_items: + depr_type: "MACRS" + depr_period: 5 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 target_dollar_year: 2022 diff --git a/examples/17_splitter_wind_doc_h2/plant_config.yaml b/examples/17_splitter_wind_doc_h2/plant_config.yaml index 086749548..3642fe6b9 100644 --- a/examples/17_splitter_wind_doc_h2/plant_config.yaml +++ b/examples/17_splitter_wind_doc_h2/plant_config.yaml @@ -39,31 +39,32 @@ technology_interconnections: [ ] finance_parameters: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for real analysis - discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 7 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] + finance_groups: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for real analysis + discount_rate: 0.06 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 0.724 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.025 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.2574 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.0 # average combined state and local sales tax https://taxfoundation.org/location/texas/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 7 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] cost_adjustment_parameters: cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year target_dollar_year: 2022 - subgroups: + finance_subgroups: electricity: commodity: "electricity" technologies: ["hopp"] diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 8c71b4c7a..e5fc5f309 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -13,10 +13,11 @@ from h2integrate.core.pose_optimization import PoseOptimization -try: - import pyxdsm -except ImportError: - pyxdsm = None +# try: +# import pyxdsm +# except ImportError: +# pyxdsm = None +pyxdsm = None class H2IntegrateModel: @@ -47,7 +48,7 @@ def __init__(self, config_file): # they will need tech_config but not driver or plant config self.create_technology_models() - self.create_financial_model() + self.create_finance_model() # connect technologies # technologies are connected within the `technology_interconnections` section of the @@ -182,7 +183,7 @@ def create_technology_models(self): self.performance_models = [] self.control_strategies = [] self.cost_models = [] - self.financial_models = [] + self.finance_models = [] combined_performance_and_cost_models = ["hopp", "h2_storage", "wombat"] @@ -201,11 +202,11 @@ def create_technology_models(self): tech_group = self.plant.add_subsystem(tech_name, om.Group()) self.tech_names.append(tech_name) - # Check if performance, cost, and financial models are the same + # Check if performance, cost, and finance models are the same # and in combined_performance_and_cost_models perf_model = individual_tech_config.get("performance_model", {}).get("model") cost_model = individual_tech_config.get("cost_model", {}).get("model") - individual_tech_config.get("financial_model", {}).get("model") + individual_tech_config.get("finance_model", {}).get("model") if ( perf_model and perf_model == cost_model @@ -219,7 +220,7 @@ def create_technology_models(self): tech_group.add_subsystem(tech_name, comp, promotes=["*"]) self.performance_models.append(comp) self.cost_models.append(comp) - self.financial_models.append(comp) + self.finance_models.append(comp) # Catch control models for systems that have the same performance & cost models if "control_strategy" in individual_tech_config: @@ -230,7 +231,7 @@ def create_technology_models(self): continue # Process the models - # TODO: integrate financial_model into the loop below + # TODO: integrate finance_model into the loop below model_types = ["performance_model", "control_strategy", "cost_model"] for model_type in model_types: if model_type in individual_tech_config: @@ -241,25 +242,25 @@ def create_technology_models(self): elif model_type == "performance_model": raise KeyError("Model definition requires 'performance_model'.") - # Process the financial models - if "financial_model" in individual_tech_config: - if "model" in individual_tech_config["financial_model"]: - financial_name = individual_tech_config["financial_model"]["model"] + # Process the finance models + if "finance_model" in individual_tech_config: + if "model" in individual_tech_config["finance_model"]: + finance_name = individual_tech_config["finance_model"]["model"] - if financial_name != individual_tech_config.get("cost_model", {}).get( + if finance_name != individual_tech_config.get("cost_model", {}).get( "model", "" ): - financial_object = self.supported_models[financial_name] + finance_object = self.supported_models[finance_name] tech_group.add_subsystem( - f"{tech_name}_financial", - financial_object( + f"{tech_name}_finance", + finance_object( driver_config=self.driver_config, plant_config=self.plant_config, tech_config=individual_tech_config, ), promotes=["*"], ) - self.financial_models.append(financial_object) + self.finance_models.append(finance_object) for tech_name, individual_tech_config in self.technology_config["technologies"].items(): cost_model = individual_tech_config.get("cost_model", {}).get("model") @@ -286,109 +287,128 @@ def _process_model(self, model_type, individual_tech_config, tech_group): ) return model_object - def create_financial_model(self): + def create_finance_model(self): """ - Create and configure the financial model(s) for the plant. + Create and configure the finance model(s) for the plant. - This method initializes financial subsystems for the plant based on the + This method initializes finance subsystems for the plant based on the configuration provided in ``self.plant_config["finance_parameters"]``. It - supports both default single-model setups and multiple subgroup-specific - financial models. + supports both default (single-model) setups and multiple/distinct (subgroup-specific) + finance models. + + Within this framework, a finance subgroup serves as a flexible grouping mechanism for + calculating finance metrics across different subsets of technologies. + These groupings can draw on varying finance inputs or models within the same simulation. + To support a wide range of use cases, such as evaluating metrics for only part of a larger + system, finance subgroups may reference multiple finance_groups and may overlap + partially or fully with the technologies included in other finance subgroups. Behavior: * If ``finance_parameters`` is not defined in the plant configuration, - no financial model is created. + no finance model is created. * If no subgroups are defined, all technologies are grouped together - under a default finance model. ``commodity`` and ``finance_model`` are + under a default finance group. ``commodity`` and ``finance_model`` are required in this case. * If subgroups are provided, each subgroup defines its own set of - technologies, associated commodity, and financial model(s). + technologies, associated commodity, and finance model(s). Each subgroup is nested under a unique name of your choice under ["finance_parameters"]["subgroups"] in the plant configuration. * Subsystems such as ``ElectricitySumComp``, ``AdjustedCapexOpexComp``, - and the selected financial models are added to each subgroup's - financial group. + and the selected finance models are added to each subgroup's + finance group. * Supports both global finance models and technology-specific finance models. Technology-specific finance models are defined in the technology configuration. Raises: ValueError: - If ``finance_parameters`` are incomplete (e.g., missing + If ["finance_parameters"]["finance_group"] is incomplete (e.g., missing ``commodity`` or ``finance_model``) when no subgroups are defined. ValueError: - If a subgroup has no valid technologies. + If a subgroup has an invalid technology. ValueError: - If a specified financial model is not found in + If a specified finance model is not found in ``self.supported_models``. Side Effects: - * Updates ``self.plant_config["finance_parameters"]`` if only a single - finance model is provided (wraps it in a default group). - * Constructs and attaches OpenMDAO financial subsystem groups to the - plant model under names ``financials_subgroup_``. + * Updates ``self.plant_config["finance_parameters"]["finance_group"] if only a single + finance model is provided (wraps it in a default finance subgroup). + * Constructs and attaches OpenMDAO finance subsystem groups to the + plant model under names ``finance_subgroup_``. * Stores processed subgroup configurations in - ``self.financial_subgroups``. + ``self.finance_subgroups``. Example: - Suppose ``plant_config["finance_parameters"]`` defines a single finance + Suppose ``plant_config["finance_parameters"]["finance_group"]`` defines a single finance model without subgroups: - >>> self.plant_config["finance_parameters"] = { + >>> self.plant_config["finance_parameters"]["finance_group"] = { ... "commodity": "hydrogen", ... "finance_model": "ProFastComp", ... "model_inputs": {"discount_rate": 0.08}, ... } - >>> self.create_financial_model() + >>> self.create_finance_model() # Creates a default subgroup containing all technologies and - # attaches a ProFAST financial model component to the plant. + # attaches a ProFAST finance model component to the plant. """ - # if there aren't any finance parameters don't setup a financial model + # if there aren't any finance parameters don't setup a finance model if "finance_parameters" not in self.plant_config: return - subgroups = self.plant_config["finance_parameters"].get("subgroups", None) + subgroups = self.plant_config["finance_parameters"].get("finance_subgroups", None) + + if "finance_groups" not in self.plant_config["finance_parameters"]: + raise ValueError("plant_config['finance_parameters'] must define 'finance_groups'.") - financial_subgroups = {} + finance_subgroups = {} default_finance_model_name = "default" # only one finance model is being used with subgroups if ( - "finance_model" in self.plant_config["finance_parameters"] - and "model_inputs" in self.plant_config["finance_parameters"] + "finance_model" in self.plant_config["finance_parameters"]["finance_groups"] + and "model_inputs" in self.plant_config["finance_parameters"]["finance_groups"] ): - if default_finance_model_name in self.plant_config["finance_parameters"]: + if ( + default_finance_model_name + in self.plant_config["finance_parameters"]["finance_groups"] + ): # throw an error if the user has an unused finance group named "default". msg = ( - "Invalid key `default` in plant_config['finance_parameters']. " + "Invalid key `default` in " + "plant_config['finance_parameters']['finance_groups']. " "Please rename the `default` key to something else or remove it. " "The name `default` will be used to reference the finance model group." ) raise ValueError(msg) - default_model_name = self.plant_config["finance_parameters"].pop("finance_model") - default_model_inputs = self.plant_config["finance_parameters"].pop("model_inputs") + default_model_name = self.plant_config["finance_parameters"]["finance_groups"].pop( + "finance_model" + ) + default_model_inputs = self.plant_config["finance_parameters"]["finance_groups"].pop( + "model_inputs" + ) default_model_dict = { default_finance_model_name: { "finance_model": default_model_name, "model_inputs": default_model_inputs, } } - self.plant_config["finance_parameters"].update(default_model_dict) + self.plant_config["finance_parameters"]["finance_groups"].update(default_model_dict) if subgroups is None: # --- Default behavior --- - commodity = self.plant_config["finance_parameters"].get("commodity") + commodity = self.plant_config["finance_parameters"]["finance_groups"].get("commodity") finance_model_name = ( - self.plant_config["finance_parameters"] + self.plant_config["finance_parameters"]["finance_groups"] .get(default_finance_model_name, {}) .get("finance_model") ) if not commodity or not finance_model_name: raise ValueError( - "finance_parameters must define 'commodity' and 'finance_model' " - "if no subgroups are provided." + "plant_config['finance_parameters']['finance_groups']" + "must define 'commodity' and 'finance_model' " + "if no finance_subgroups are provided." ) # Collect all technologies into one subgroup @@ -428,7 +448,7 @@ def create_financial_model(self): f"technologies: {list(self.technology_config['technologies'].keys())}" ) - financial_subgroups.update( + finance_subgroups.update( { subgroup_name: { "tech_configs": tech_configs, @@ -436,10 +456,10 @@ def create_financial_model(self): } } ) - financial_subgroup = om.Group() + finance_subgroup = om.Group() if commodity == "electricity": - financial_subgroup.add_subsystem( + finance_subgroup.add_subsystem( "electricity_sum", ElectricitySumComp(tech_configs=tech_configs) ) @@ -450,7 +470,7 @@ def create_financial_model(self): plant_config=self.plant_config, ) - financial_subgroup.add_subsystem( + finance_subgroup.add_subsystem( "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) @@ -461,7 +481,7 @@ def create_financial_model(self): for tech_name, tech_params in tech_configs.items() ): tech_finance_model_name = ( - tech_configs.get(finance_model_name).get("financial_model", {}).get("model") + tech_configs.get(finance_model_name).get("finance_model", {}).get("model") ) # this is created in create_technologies() @@ -472,19 +492,19 @@ def create_financial_model(self): # if not using a tech-specific finance model, get the finance model and inputs for # the finance model group specified by finance_model_name - finance_model_config = self.plant_config["finance_parameters"].get( - finance_model_name - ) + finance_model_config = self.plant_config["finance_parameters"][ + "finance_groups" + ].get(finance_model_name) model_name = finance_model_config.get("finance_model") # finance model fin_model_inputs = finance_model_config.get( "model_inputs" ) # inputs to finance model - # Add financial model component + # Add finance model component fin_model = self.supported_models.get(model_name) if fin_model is None: - raise ValueError(f"Financial model '{model_name}' not found.") + raise ValueError(f"finance model '{model_name}' not found.") # filter the plant_config so the finance_parameters only includes data for # this finance model group @@ -508,19 +528,19 @@ def create_financial_model(self): commodity_desc = subgroup_params.get("commodity_desc", "") commodity_output_desc = subgroup_params.get("commodity_desc", "") - # check if multiple finance model groups are specified for the subgroup + # check if multiple finance models are specified for the subgroup if len(finance_model_names) > 1: # check that the finance model groups do not include tech-specific finances - non_tech_financials = [ + non_tech_finances = [ k for k in finance_model_names - if k in self.plant_config["finance_parameters"] + if k in self.plant_config["finance_parameters"]["finance_groups"] ] # if multiple non-tech specific finance model groups are specified for the # subgroup, the outputs of the finance model must have unique names to # avoid errors. - if len(non_tech_financials) > 1: + if len(non_tech_finances) > 1: # finance models name their outputs based on the description and commodity # update the description to include the finance model name to ensure # uniquely named outputs @@ -543,12 +563,12 @@ def create_financial_model(self): ) # add the finance component to the finance group - financial_subgroup.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) + finance_subgroup.add_subsystem(finance_subsystem_name, fin_comp, promotes=["*"]) # add the finance group to the subgroup - self.plant.add_subsystem(f"financials_subgroup_{subgroup_name}", financial_subgroup) + self.plant.add_subsystem(f"finance_subgroup_{subgroup_name}", finance_subgroup) - self.financial_subgroups = financial_subgroups + self.finance_subgroups = finance_subgroups def connect_technologies(self): technology_interconnections = self.plant_config.get("technology_interconnections", []) @@ -675,14 +695,14 @@ def connect_technologies(self): # Connect the resource output to the technology input self.model.connect(f"{resource_name}.{variable}", f"{tech_name}.{variable}") - # TODO: connect outputs of the technology models to the cost and financial models of the - # same name if the cost and financial models are not None + # TODO: connect outputs of the technology models to the cost and finance models of the + # same name if the cost and finance models are not None if "finance_parameters" in self.plant_config: - # Connect the outputs of the technology models to the appropriate financial groups - for group_id, group_configs in self.financial_subgroups.items(): + # Connect the outputs of the technology models to the appropriate finance groups + for group_id, group_configs in self.finance_subgroups.items(): tech_configs = group_configs.get("tech_configs") primary_commodity_type = group_configs.get("commodity") - # Skip steel financials; it provides its own financials + # Skip steel finances; it provides its own finances if any(c in tech_configs for c in ("steel", "methanol", "geoh2")): continue @@ -690,7 +710,7 @@ def connect_technologies(self): # Loop through technologies and connect electricity outputs to the ExecComp # Only connect if the technology is included in at least one commodity's stackup - # and in this financial group + # and in this finance group for tech_name in tech_configs.keys(): if ( tech_name in electricity_producing_techs @@ -699,18 +719,18 @@ def connect_technologies(self): ): self.plant.connect( f"{tech_name}.electricity_out", - f"financials_subgroup_{group_id}.electricity_sum.electricity_{tech_name}", + f"finance_subgroup_{group_id}.electricity_sum.electricity_{tech_name}", ) plant_producing_electricity = True if plant_producing_electricity and primary_commodity_type == "electricity": - # Connect total electricity produced to the financial group + # Connect total electricity produced to the finance group self.plant.connect( - f"financials_subgroup_{group_id}.electricity_sum.total_electricity_produced", - f"financials_subgroup_{group_id}.total_electricity_produced", + f"finance_subgroup_{group_id}.electricity_sum.total_electricity_produced", + f"finance_subgroup_{group_id}.total_electricity_produced", ) - # Only connect technologies that are included in the financial stackup + # Only connect technologies that are included in the finance stackup for tech_name in tech_configs.keys(): # For now, assume splitters and combiners do not add any costs if "splitter" in tech_name or "combiner" in tech_name: @@ -718,31 +738,31 @@ def connect_technologies(self): self.plant.connect( f"{tech_name}.CapEx", - f"financials_subgroup_{group_id}.capex_{tech_name}", + f"finance_subgroup_{group_id}.capex_{tech_name}", ) self.plant.connect( - f"{tech_name}.OpEx", f"financials_subgroup_{group_id}.opex_{tech_name}" + f"{tech_name}.OpEx", f"finance_subgroup_{group_id}.opex_{tech_name}" ) self.plant.connect( f"{tech_name}.cost_year", - f"financials_subgroup_{group_id}.cost_year_{tech_name}", + f"finance_subgroup_{group_id}.cost_year_{tech_name}", ) if "electrolyzer" in tech_name: self.plant.connect( f"{tech_name}.time_until_replacement", - f"financials_subgroup_{group_id}.{tech_name}_time_until_replacement", + f"finance_subgroup_{group_id}.{tech_name}_time_until_replacement", ) if primary_commodity_type == "hydrogen": self.plant.connect( f"{tech_name}.total_hydrogen_produced", - f"financials_subgroup_{group_id}.total_hydrogen_produced", + f"finance_subgroup_{group_id}.total_hydrogen_produced", ) if "ammonia" in tech_name and primary_commodity_type == "ammonia": self.plant.connect( f"{tech_name}.total_ammonia_produced", - f"financials_subgroup_{group_id}.total_ammonia_produced", + f"finance_subgroup_{group_id}.total_ammonia_produced", ) if ( @@ -750,22 +770,22 @@ def connect_technologies(self): ) and primary_commodity_type == "co2": self.plant.connect( f"{tech_name}.co2_capture_mtpy", - f"financials_subgroup_{group_id}.co2_capture_kgpy", + f"finance_subgroup_{group_id}.co2_capture_kgpy", ) if "air_separator" in tech_name and primary_commodity_type == "nitrogen": self.plant.connect( f"{tech_name}.total_nitrogen_produced", - f"financials_subgroup_{group_id}.total_nitrogen_produced", + f"finance_subgroup_{group_id}.total_nitrogen_produced", ) self.plant.options["auto_order"] = True - # Check if there are any connections FROM a financial group to ammonia - # This handles the case where LCOH is computed in the financial group and passed to ammonia + # Check if there are any connections FROM a finance group to ammonia + # This handles the case where LCOH is computed in the finance group and passed to ammonia for connection in technology_interconnections: - if connection[0].startswith("financials_subgroup_") and connection[1] == "ammonia": - # If the connection is from a financial group, set solvers for the + if connection[0].startswith("finance_subgroup_") and connection[1] == "ammonia": + # If the connection is from a finance group, set solvers for the # plant to resolve the coupling self.plant.nonlinear_solver = om.NonlinearBlockGS() self.plant.linear_solver = om.DirectSolver() From 495db8dc42765f78fd1f7917f0ba08a34b214e85 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 07:40:24 -0500 Subject: [PATCH 56/67] clarify finance groups --- h2integrate/core/h2integrate_model.py | 54 +++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index e5fc5f309..2b0900563 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -363,14 +363,14 @@ def create_finance_model(self): finance_subgroups = {} - default_finance_model_name = "default" + default_finance_group_name = "default" # only one finance model is being used with subgroups if ( "finance_model" in self.plant_config["finance_parameters"]["finance_groups"] and "model_inputs" in self.plant_config["finance_parameters"]["finance_groups"] ): if ( - default_finance_model_name + default_finance_group_name in self.plant_config["finance_parameters"]["finance_groups"] ): # throw an error if the user has an unused finance group named "default". @@ -388,7 +388,7 @@ def create_finance_model(self): "model_inputs" ) default_model_dict = { - default_finance_model_name: { + default_finance_group_name: { "finance_model": default_model_name, "model_inputs": default_model_inputs, } @@ -400,7 +400,7 @@ def create_finance_model(self): commodity = self.plant_config["finance_parameters"]["finance_groups"].get("commodity") finance_model_name = ( self.plant_config["finance_parameters"]["finance_groups"] - .get(default_finance_model_name, {}) + .get(default_finance_group_name, {}) .get("finance_model") ) @@ -415,22 +415,22 @@ def create_finance_model(self): all_techs = list(self.technology_config["technologies"].keys()) subgroup = { "commodity": commodity, - "finance_groups": [default_finance_model_name], + "finance_groups": [default_finance_group_name], "technologies": all_techs, } - subgroups = {default_finance_model_name: subgroup} + subgroups = {default_finance_group_name: subgroup} # --- Normal subgroup handling --- for subgroup_name, subgroup_params in subgroups.items(): commodity = subgroup_params.get("commodity", None) commodity_desc = subgroup_params.get("commodity_desc", "") - finance_model_names = subgroup_params.get( - "finance_groups", [default_finance_model_name] + finance_group_names = subgroup_params.get( + "finance_groups", [default_finance_group_name] ) tech_names = subgroup_params.get("technologies") - if isinstance(finance_model_names, str): - finance_model_names = [finance_model_names] + if isinstance(finance_group_names, str): + finance_group_names = [finance_group_names] # check commodity type if commodity is None: @@ -474,29 +474,29 @@ def create_finance_model(self): "adjusted_capex_opex_comp", adjusted_capex_opex_comp, promotes=["*"] ) - for finance_model_name in finance_model_names: + for finance_group_name in finance_group_names: # check if using tech-specific finance model if any( - tech_name == finance_model_name + tech_name == finance_group_name for tech_name, tech_params in tech_configs.items() ): - tech_finance_model_name = ( - tech_configs.get(finance_model_name).get("finance_model", {}).get("model") + tech_finance_group_name = ( + tech_configs.get(finance_group_name).get("finance_model", {}).get("model") ) # this is created in create_technologies() - if tech_finance_model_name is not None: + if tech_finance_group_name is not None: # tech specific finance models are created in create_technologies() # and do not need to be included in the general finance models continue - # if not using a tech-specific finance model, get the finance model and inputs for - # the finance model group specified by finance_model_name - finance_model_config = self.plant_config["finance_parameters"][ + # if not using a tech-specific finance group, get the finance model and inputs for + # the finance model group specified by finance_group_name + finance_group_config = self.plant_config["finance_parameters"][ "finance_groups" - ].get(finance_model_name) - model_name = finance_model_config.get("finance_model") # finance model - fin_model_inputs = finance_model_config.get( + ].get(finance_group_name) + model_name = finance_group_config.get("finance_model") # finance model + fin_model_inputs = finance_group_config.get( "model_inputs" ) # inputs to finance model @@ -515,7 +515,7 @@ def create_finance_model(self): } # then, reformat the finance_parameters to only include inputs for the - # finance group specified by finance_model_name + # finance group specified by finance_group_name filtered_plant_config.update( { "finance_parameters": { @@ -529,11 +529,11 @@ def create_finance_model(self): commodity_output_desc = subgroup_params.get("commodity_desc", "") # check if multiple finance models are specified for the subgroup - if len(finance_model_names) > 1: + if len(finance_group_names) > 1: # check that the finance model groups do not include tech-specific finances non_tech_finances = [ k - for k in finance_model_names + for k in finance_group_names if k in self.plant_config["finance_parameters"]["finance_groups"] ] @@ -544,7 +544,7 @@ def create_finance_model(self): # finance models name their outputs based on the description and commodity # update the description to include the finance model name to ensure # uniquely named outputs - commodity_output_desc = commodity_output_desc + f"_{finance_model_name}" + commodity_output_desc = commodity_output_desc + f"_{finance_group_name}" # create the finance component fin_comp = fin_model( @@ -557,9 +557,9 @@ def create_finance_model(self): # name the finance component based on the commodity and description finance_subsystem_name = ( - f"{finance_model_name}_{commodity}" + f"{commodity}_finance_{finance_group_name}" if commodity_desc == "" - else f"{finance_model_name}_{commodity}_{commodity_desc}" + else f"{commodity}_{commodity_desc}_finance_{finance_group_name}" ) # add the finance component to the finance group From aea4ff86bf0eb7a6defafb15ca15eb0bae8bbfca Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 07:40:50 -0500 Subject: [PATCH 57/67] fix example test --- tests/h2integrate/test_all_examples.py | 101 ++++++++++++------------- 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index 20b546bd4..923570242 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -25,7 +25,7 @@ def test_steel_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.LCOH_delivered")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.LCOH_delivered")[0], rel=1e-3 ) == 7.47944016 ) @@ -36,7 +36,7 @@ def test_steel_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 5.10869916e09 ) @@ -44,7 +44,7 @@ def test_steel_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 96349901.77625626 ) @@ -102,7 +102,7 @@ def test_simple_ammonia_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 2678403968.6 ) @@ -110,7 +110,7 @@ def test_simple_ammonia_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 64338137.8 ) @@ -118,13 +118,13 @@ def test_simple_ammonia_example(subtests): # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_hydrogen.LCOH")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 4.233055 ) # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOA"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_ammonia.LCOA")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_ammonia.LCOA")[0], rel=1e-3) == 1.02470046 ) @@ -187,7 +187,7 @@ def test_ammonia_synloop_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_nh3.total_capex_adjusted")[0], rel=1e-6 + model.prob.get_val("finance_subgroup_nh3.total_capex_adjusted")[0], rel=1e-6 ) == 3.7289e09 ) @@ -195,20 +195,20 @@ def test_ammonia_synloop_example(subtests): with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_nh3.total_opex_adjusted")[0], rel=1e-6 + model.prob.get_val("finance_subgroup_nh3.total_opex_adjusted")[0], rel=1e-6 ) == 78480154.4 ) with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_h2.LCOH")[0], rel=1e-6) + pytest.approx(model.prob.get_val("finance_subgroup_h2.LCOH")[0], rel=1e-6) == 5.659321302703965 ) with subtests.test("Check LCOA"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_nh3.LCOA")[0], rel=1e-6) + pytest.approx(model.prob.get_val("finance_subgroup_nh3.LCOA")[0], rel=1e-6) == 1.067030996544544 ) @@ -274,7 +274,7 @@ def test_wind_h2_opt_example(subtests): with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE")[0], rel=1e-3) == 0.151189 ) @@ -302,8 +302,8 @@ def test_wind_h2_opt_example(subtests): assert len(cases) > 1, "Not enough cases recorded in SQL file." # Get initial and final LCOH values - initial_lcoh = cases[0].outputs["financials_subgroup_hydrogen.LCOH"][0] - final_lcoh = cases[-1].outputs["financials_subgroup_hydrogen.LCOH"][0] + initial_lcoh = cases[0].outputs["finance_subgroup_hydrogen.LCOH"][0] + final_lcoh = cases[-1].outputs["finance_subgroup_hydrogen.LCOH"][0] with subtests.test("Check LCOH changed"): assert final_lcoh != initial_lcoh @@ -311,14 +311,14 @@ def test_wind_h2_opt_example(subtests): with subtests.test("Check total adjusted CapEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) == 2783126102 ) with subtests.test("Check total adjusted OpEx"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 + model.prob.get_val("finance_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) == 75543899 ) @@ -347,12 +347,7 @@ def test_paper_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOP"): - assert ( - pytest.approx( - model.prob.get_val("plant.paper_mill.paper_mill_financial.LCOP"), rel=1e-3 - ) - == 51.91476681 - ) + assert pytest.approx(model.prob.get_val("paper_mill.LCOP"), rel=1e-3) == 51.91476681 @unittest.skipUnless(importlib.util.find_spec("mcm") is not None, "mcm is not installed") @@ -371,13 +366,13 @@ def test_wind_wave_doc_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOC"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_co2.LCOC")[0], rel=1e-3) == 2.26955589 ) with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE")[0], rel=1e-3) == 1.05281478 ) @@ -398,19 +393,19 @@ def test_splitter_wind_doc_h2_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOH"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_hydrogen.LCOH")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) == 10.25515911 ) with subtests.test("Check LCOC"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_co2.LCOC")[0], rel=1e-3) == 14.19802243 ) with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE")[0], rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE")[0], rel=1e-3) == 0.1385128 ) @@ -427,12 +422,12 @@ def test_hydro_example(subtests): model.post_process() - print(model.prob.get_val("financials_subgroup_default.LCOE")) + print(model.prob.get_val("finance_subgroup_default.LCOE")) # Subtests for checking specific values with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_default.LCOE"), rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_default.LCOE"), rel=1e-3) == 0.17653979 ) @@ -451,7 +446,7 @@ def test_hybrid_energy_plant_example(subtests): # Subtests for checking specific values with subtests.test("Check LCOE"): - assert model.prob.get_val("financials_subgroup_default.LCOE", units="USD/MW/h")[0] < 83.2123 + assert model.prob.get_val("finance_subgroup_default.LCOE", units="USD/MW/h")[0] < 83.2123 def test_asu_example(subtests): @@ -470,7 +465,7 @@ def test_asu_example(subtests): with subtests.test("Check LCON"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_default.LCON", units="USD/kg")[0], + model.prob.get_val("finance_subgroup_default.LCON", units="USD/kg")[0], abs=1e-4, ) == 0.309041977334972 @@ -491,7 +486,7 @@ def test_hydrogen_dispatch_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_elec.LCOE", units="USD/MW/h")[0], + model.prob.get_val("finance_subgroup_elec.LCOE", units="USD/MW/h")[0], rel=1e-5, ) == 106.13987 @@ -500,7 +495,7 @@ def test_hydrogen_dispatch_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_h2.LCOH", units="USD/kg")[0], + model.prob.get_val("finance_subgroup_h2.LCOH", units="USD/kg")[0], rel=1e-5, ) == 5.68452215 @@ -524,17 +519,17 @@ def test_wind_wave_oae_example(subtests): # Note: These are placeholder values. Update with actual values after running the test # when MCM package is properly installed and configured with subtests.test("Check LCOC"): - assert pytest.approx(model.prob.get_val("financials_subgroup_co2.LCOC"), rel=1e-3) == 37.82 + assert pytest.approx(model.prob.get_val("finance_subgroup_co2.LCOC"), rel=1e-3) == 37.82 with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE"), rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE"), rel=1e-3) == 0.36956 ) @unittest.skipUnless(importlib.util.find_spec("mcm") is not None, "mcm is not installed") -def test_wind_wave_oae_example_with_financials(subtests): +def test_wind_wave_oae_example_with_finance(subtests): # Change the current working directory to the example's directory os.chdir(EXAMPLE_DIR / "09_co2/ocean_alkalinity_enhancement_financials") @@ -551,7 +546,7 @@ def test_wind_wave_oae_example_with_financials(subtests): # when MCM package is properly installed and configured with subtests.test("Check LCOE"): assert ( - pytest.approx(model.prob.get_val("financials_subgroup_electricity.LCOE"), rel=1e-3) + pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE"), rel=1e-3) == 0.09180 ) @@ -584,38 +579,38 @@ def test_natural_gas_example(subtests): with subtests.test("Check total electricity produced"): total_electricity = model.prob.get_val( - "financials_subgroup_default.electricity_sum.total_electricity_produced" + "finance_subgroup_default.electricity_sum.total_electricity_produced" )[0] assert pytest.approx(total_electricity, rel=1e-6) == 1.168e8 with subtests.test("Check opex adjusted ng_feedstock"): opex_ng_feedstock = model.prob.get_val( - "financials_subgroup_default.opex_adjusted_ng_feedstock" + "finance_subgroup_default.opex_adjusted_ng_feedstock" )[0] assert pytest.approx(opex_ng_feedstock, rel=1e-6) == 3589463.41463415 with subtests.test("Check capex adjusted natural_gas_plant"): capex_ng_plant = model.prob.get_val( - "financials_subgroup_default.capex_adjusted_natural_gas_plant" + "finance_subgroup_default.capex_adjusted_natural_gas_plant" )[0] assert pytest.approx(capex_ng_plant, rel=1e-6) == 97560975.60975611 with subtests.test("Check opex adjusted natural_gas_plant"): opex_ng_plant = model.prob.get_val( - "financials_subgroup_default.opex_adjusted_natural_gas_plant" + "finance_subgroup_default.opex_adjusted_natural_gas_plant" )[0] assert pytest.approx(opex_ng_plant, rel=1e-6) == 1260487.80487805 with subtests.test("Check total adjusted CapEx"): - total_capex = model.prob.get_val("financials_subgroup_default.total_capex_adjusted")[0] + total_capex = model.prob.get_val("finance_subgroup_default.total_capex_adjusted")[0] assert pytest.approx(total_capex, rel=1e-6) == 97658536.58536586 with subtests.test("Check total adjusted OpEx"): - total_opex = model.prob.get_val("financials_subgroup_default.total_opex_adjusted")[0] + total_opex = model.prob.get_val("finance_subgroup_default.total_opex_adjusted")[0] assert pytest.approx(total_opex, rel=1e-6) == 4849951.2195122 with subtests.test("Check LCOE"): - lcoe = model.prob.get_val("financials_subgroup_default.LCOE")[0] + lcoe = model.prob.get_val("finance_subgroup_default.LCOE")[0] assert pytest.approx(lcoe, rel=1e-6) == 0.12959097 # Test feedstock-specific values @@ -656,7 +651,7 @@ def test_wind_solar_electrolyzer_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_electricity.LCOE", units="USD/MW/h")[0], + model.prob.get_val("finance_subgroup_electricity.LCOE", units="USD/MW/h")[0], rel=1e-5, ) == 54.12889 @@ -665,7 +660,7 @@ def test_wind_solar_electrolyzer_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx( - model.prob.get_val("financials_subgroup_hydrogen.LCOH", units="USD/kg")[0], + model.prob.get_val("finance_subgroup_hydrogen.LCOH", units="USD/kg")[0], rel=1e-5, ) == 5.33209234 @@ -693,16 +688,16 @@ def test_electrolyzer_om_example(subtests): model.run() - lcoe = model.prob.get_val("financials_subgroup_electricity.LCOE", units="USD/MW/h")[0] - lcoh_with_lcoh_financials = model.prob.get_val( - "financials_subgroup_hydrogen.LCOH_lcoh_financials", units="USD/kg" + lcoe = model.prob.get_val("finance_subgroup_electricity.LCOE", units="USD/MW/h")[0] + lcoh_with_lcoh_finance = model.prob.get_val( + "finance_subgroup_hydrogen.LCOH_lcoh_financials", units="USD/kg" )[0] - lcoh_with_lcoe_financials = model.prob.get_val( - "financials_subgroup_hydrogen.LCOH_lcoe_financials", units="USD/kg" + lcoh_with_lcoe_finance = model.prob.get_val( + "finance_subgroup_hydrogen.LCOH_lcoe_financials", units="USD/kg" )[0] with subtests.test("Check LCOE"): assert pytest.approx(lcoe, rel=1e-5) == 40.12819 with subtests.test("Check LCOH with lcoh_financials"): - assert pytest.approx(lcoh_with_lcoh_financials, rel=1e-5) == 13.18328175 + assert pytest.approx(lcoh_with_lcoh_finance, rel=1e-5) == 13.18328175 with subtests.test("Check LCOH with lcoe_financials"): - assert pytest.approx(lcoh_with_lcoe_financials, rel=1e-5) == 8.05688467 + assert pytest.approx(lcoh_with_lcoe_finance, rel=1e-5) == 8.05688467 From 60613566782f4353753c6ab376d2e7abd7ac710b Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 08:05:01 -0500 Subject: [PATCH 58/67] update example tests and collect_custom_models --- .../01_onshore_steel_mn/plant_config.yaml | 47 ++++++++++--------- h2integrate/core/h2integrate_model.py | 2 +- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/examples/01_onshore_steel_mn/plant_config.yaml b/examples/01_onshore_steel_mn/plant_config.yaml index 45ddb0e30..e7fd38ea7 100644 --- a/examples/01_onshore_steel_mn/plant_config.yaml +++ b/examples/01_onshore_steel_mn/plant_config.yaml @@ -34,29 +34,30 @@ plant: plant_life: 30 finance_parameters: - profast_model: - finance_model: "ProFastComp" - model_inputs: - params: - analysis_start_year: 2032 - installation_time: 36 # months - inflation_rate: 0.0 # 0 for nominal analysis - discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind - debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind - property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx - total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) - capital_gains_tax_rate: 0.15 # H2FAST default - sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ - debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind - debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH - loan_period_if_used: 0 # H2FAST default, not used for revolving debt - cash_onhand_months: 1 # H2FAST default - admin_expense: 0.00 # percent of sales H2FAST default - capital_items: - depr_type: "MACRS" # can be "MACRS" or "Straight line" - depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 - refurb: [0.] - subgroups: + finance_groups: + profast_model: + finance_model: "ProFastComp" + model_inputs: + params: + analysis_start_year: 2032 + installation_time: 36 # months + inflation_rate: 0.0 # 0 for nominal analysis + discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind + debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind + property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx + total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8) + capital_gains_tax_rate: 0.15 # H2FAST default + sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/ + debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind + debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH + loan_period_if_used: 0 # H2FAST default, not used for revolving debt + cash_onhand_months: 1 # H2FAST default + admin_expense: 0.00 # percent of sales H2FAST default + capital_items: + depr_type: "MACRS" # can be "MACRS" or "Straight line" + depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 + refurb: [0.] + finance_subgroups: electricity: commodity: "electricity" finance_groups: ["profast_model"] diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 2b0900563..1557d2aa2 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -82,7 +82,7 @@ def collect_custom_models(self): """ for tech_name, tech_config in self.technology_config["technologies"].items(): - for model_type in ["performance_model", "cost_model", "financial_model"]: + for model_type in ["performance_model", "cost_model", "finance_model"]: if model_type in tech_config: model_name = tech_config[model_type].get("model") if (model_name not in self.supported_models) and (model_name is not None): From fbca82468b05f53aee0848e9d05d015427d88b6c Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 08:24:01 -0500 Subject: [PATCH 59/67] update tests --- h2integrate/core/test/test_finances.py | 6 +- h2integrate/core/test/test_framework.py | 58 +------------------ .../test_hydrogen/test_geologic_h2.py | 4 +- 3 files changed, 7 insertions(+), 61 deletions(-) diff --git a/h2integrate/core/test/test_finances.py b/h2integrate/core/test/test_finances.py index b2c7026c1..9e4944295 100644 --- a/h2integrate/core/test/test_finances.py +++ b/h2integrate/core/test/test_finances.py @@ -101,7 +101,7 @@ def test_modified_lcoe_calc(self): tech_config = load_tech_yaml(example_case_dir / "tech_config.yaml") plant_config = load_plant_yaml(example_case_dir / "plant_config.yaml") driver_config = load_driver_yaml(example_case_dir / "driver_config.yaml") - finance_inputs = plant_config["finance_parameters"].pop("profast_model") + finance_inputs = plant_config["finance_parameters"]["finance_groups"].pop("profast_model") plant_config_filtered = {k: v for k, v in plant_config.items() if k != "finance_parameters"} plant_config_filtered.update({"finance_parameters": finance_inputs}) # Run ProFastComp with loaded configs @@ -140,11 +140,11 @@ def test_lcoe_with_selected_technologies(self): driver_config = load_driver_yaml(example_case_dir / "driver_config.yaml") # Only include HOPP and electrolyzer in metrics - plant_config["finance_parameters"]["subgroups"]["electricity"]["technologies"] = [ + plant_config["finance_parameters"]["finance_subgroups"]["electricity"]["technologies"] = [ "hopp", "steel", ] - finance_inputs = plant_config["finance_parameters"].pop("profast_model") + finance_inputs = plant_config["finance_parameters"]["finance_groups"].pop("profast_model") plant_config_filtered = {k: v for k, v in plant_config.items() if k != "finance_parameters"} plant_config_filtered.update({"finance_parameters": finance_inputs}) diff --git a/h2integrate/core/test/test_framework.py b/h2integrate/core/test/test_framework.py index e5b31f109..ac6b6387f 100644 --- a/h2integrate/core/test/test_framework.py +++ b/h2integrate/core/test/test_framework.py @@ -83,7 +83,7 @@ def test_custom_financial_model_grouping(subtests): tech_config_data = load_tech_yaml(temp_tech_config) # Modify the financial_model entry for one of the technologies - tech_config_data["technologies"]["steel"]["financial_model"]["group"] = "test_financial_group" + tech_config_data["technologies"]["steel"]["finance_model"]["group"] = "test_financial_group" tech_config_data["technologies"]["electrolyzer"].pop("financial_model", None) # Save the modified tech_config YAML back @@ -110,60 +110,6 @@ def test_custom_financial_model_grouping(subtests): temp_highlevel_yaml.unlink(missing_ok=True) -def test_get_included_technologies(): - # Create a mock model instance - model = H2IntegrateModel.__new__(H2IntegrateModel) - - # Test case 1: No specific technologies defined (should include all) - tech_config = { - "wind": {"performance_model": {"model": "test"}}, - "electrolyzer": {"performance_model": {"model": "test"}}, - "storage": {"performance_model": {"model": "test"}}, - } - plant_config = {"finance_parameters": {}} - - included_techs = model.get_included_technologies(tech_config, "hydrogen", plant_config) - expected = ["wind", "electrolyzer", "storage"] - assert set(included_techs) == set(expected), f"Expected {expected}, got {included_techs}" - - # Test case 2: Specific technologies defined for LCOH - plant_config_with_specific = { - "finance_parameters": { - "technologies_included_in_metrics": {"LCOH": ["electrolyzer", "wind"]} - } - } - - included_techs = model.get_included_technologies( - tech_config, "hydrogen", plant_config_with_specific - ) - expected = ["electrolyzer", "wind"] - assert set(included_techs) == set(expected), f"Expected {expected}, got {included_techs}" - - # Test case 3: Test different commodity type (electricity) - plant_config_with_lcoe = { - "finance_parameters": {"technologies_included_in_metrics": {"LCOE": ["wind"]}} - } - - included_techs = model.get_included_technologies( - tech_config, "electricity", plant_config_with_lcoe - ) - expected = ["wind"] - assert set(included_techs) == set(expected), f"Expected {expected}, got {included_techs}" - - # Test case 4: Invalid technology should raise error - plant_config_invalid = { - "finance_parameters": { - "technologies_included_in_metrics": {"LCOH": ["electrolyzer", "invalid_tech"]} - } - } - - try: - model.get_included_technologies(tech_config, "hydrogen", plant_config_invalid) - raise AssertionError("Should have raised ValueError for invalid technology") - except ValueError as e: - assert "invalid_tech" in str(e), f"Error message should mention invalid_tech: {e}" - - def test_unsupported_simulation_parameters(): orig_plant_config = EXAMPLE_DIR / "01_onshore_steel_mn" / "plant_config.yaml" temp_plant_config_ntimesteps = Path.cwd() / "temp_plant_config_ntimesteps.yaml" @@ -215,7 +161,7 @@ def test_technology_connections(): # Load the plant_config YAML content plant_config_data = load_plant_yaml(temp_plant_config) - new_connection = (["financials_subgroup_electricity", "steel", ("LCOE", "electricity_cost")],) + new_connection = (["finance_subgroup_electricity", "steel", ("LCOE", "electricity_cost")],) new_tech_interconnections = ( plant_config_data["technology_interconnections"][0:4] + list(new_connection) diff --git a/tests/h2integrate/test_hydrogen/test_geologic_h2.py b/tests/h2integrate/test_hydrogen/test_geologic_h2.py index 6889423e3..3a06ed2db 100644 --- a/tests/h2integrate/test_hydrogen/test_geologic_h2.py +++ b/tests/h2integrate/test_hydrogen/test_geologic_h2.py @@ -17,7 +17,7 @@ def test_natural_geoh2(subtests): h2_prod = h2i_nat.plant.geoh2.natural_geoh2_performance.get_val("hydrogen_out") assert np.mean(h2_prod) == approx(48.94393478, 1e-6) with subtests.test("LCOH"): - lcoh = h2i_nat.plant.geoh2.geoh2_financial.get_val("LCOH") + lcoh = h2i_nat.plant.geoh2.geoh2_finance.get_val("LCOH") assert lcoh == approx(3.14353262, 1e-6) @@ -29,5 +29,5 @@ def test_stimulated_geoh2(subtests): h2_prod = h2i_stim.plant.geoh2.stimulated_geoh2_performance.get_val("hydrogen_out") assert np.mean(h2_prod) == approx(108.49331847, 1e-6) with subtests.test("LCOH"): - lcoh = h2i_stim.plant.geoh2.geoh2_financial.get_val("LCOH") + lcoh = h2i_stim.plant.geoh2.geoh2_finance.get_val("LCOH") assert lcoh == approx(1.8666849, 1e-6) From fb77991aaa69a2e25e534b01b741ffb1a0efda16 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 09:01:05 -0500 Subject: [PATCH 60/67] update specifying finance parameters md --- .../specifying_finance_parameters.md | 277 +++++++----------- 1 file changed, 113 insertions(+), 164 deletions(-) diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 00d28341c..9bd735436 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -1,204 +1,153 @@ # Finance Parameters - -This doc page describes how to specify finance parameters for different analyses and explains when to use different ways of setting up finance groups. -The finance parameters are an important part of the plant configuration as they define the financial aspects of the plant's operation and investment. - -The finance model, finance model specific inputs, and cost adjustment information (regardless of the finance model) are required in the `finance_parameters` of the `plant_config`. - +## Overview +The `finance_parameters` section of the `plant_config` defines the financial subsystems of the plant. These parameters configure how costs, revenues, and investment metrics are calculated across all or parts of the modeled system. ```{note} The `plant_life` parameter from the `plant` section of the `plant_config` is also used in finance calculations as the operating life of the plant. ``` -The `finance_parameters` section always requires the following information: +At minimum, `finance_parameters` must include: - `cost_adjustment_parameters`: - `target_dollar_year`: dollar-year to convert costs to. - - `cost_year_adjustment_inflation` is used to adjust costs for each technology from the cost year of the technology model (see [details on cost years and cost models here](cost:cost_years)) to the `target_dollar_year` - -Other common variables to include in the `finance_parameters` section are: -- **`finance_model`** (string): Name of the general financial model to use. This should not be a technology specific finance model. -- **`model_inputs`** (dictionary): Input parameters for `finance_model` -- **`commodity`** (str): The commodity used for calculations in the `finance_model`. This is input into general finance models as the `commodity_type` (see [here](finance:overview) for details on general finance models input). -- **`commodity_desc`** (str): Optional additional description of the commodity. This is input into the general finance model as the `description` if a single finance model is used for a subgroup (see [here](finance:overview) for details on general finance models inputs). This is not used if subgroups are not specified. - -These variables may be included in `finance_parameters` in different formats depending on the use-case. The different formatting options are: -- [Without specifying subgroups](finparam:nosubgroups) -- [Single finance model with subgroups](finparams:singlemodelsubgroups) -- [Nicknamed single finance model with subgroups](finparams:namedsinglemodelsubgroups) -- [Multiple finance models with subgroups](finparams:multimodelsubgroups) - -We'll now walk through explanations for each of these formats, showing details for each type and providing links to examples that use that format. + - `cost_year_adjustment_inflation`: used to adjust costs for each technology from its native cost year to the `target_dollar_year` (see [details on cost years and cost models here](cost:cost_years)) + +Other variables in finance_parameters vary depending on the financial analysis structure. There are two major modes of operation: +- **Single-model mode (default)**: All technologies are grouped together into a single financial calculation. +- **Subgroup mode**: Technologies are split into one or more subgroups, each with its own commodity and one or more finance models. + +### Finance Groups vs. Finance Subgroups +Within this framework, there are two distinct layers: + +#### Finance groups + A finance group contains the attributes needed to run one finance model: + - `finance_model`: + The name of the financial model to use (e.g., `ProFastComp`). Must correspond to one of the available models in `self.supported_models`. + - `model_inputs`: + A dictionary of parameters passed into the chosen finance model. These provide customization of assumptions such as discount rate, debt fraction, or cost escalation. + - `commodity`: + The product or service whose financial performance is being analyzed (e.g., hydrogen, electricity). Required if `finance_subgroups` are not used, otherwise defined within `finance_subgroups`. + +#### Finance subgroups + Subgroups are flexible collections of technologies that map to one or more finance groups. They allow you to: + - Calculate financial metrics for only part of the system. + - Compare different views of the same system (e.g., delivered vs. produced cost of hydrogen). + - Run multiple models (e.g., LCOH, LCOE, LCOS, or NPV) on overlapping or distinct sets of technologies. + - Model distributed or multi-location systems finances within a single plant configuration. + + Subgroups contain information on how to construct the specific subgroup: + - `commodity`: + The product or service whose financial performance is being analyzed (e.g., hydrogen, electricity). Each finance subgroup is tied to a single commodity. + - `technologies`: + Technologies to be include in the specific subgroup calculation (e.g., you might only want electricity producing technologies in the levelized cost of energy calculation). + - `finance_groups`: + Refers to the `finance_groups` that contain the `finance_model` and `model_inputs`. Required if multiple `finance_groups` are being used. Technology-specific `finance_groups` can be called by using the technology name listed in the `tech_config` (e.g., `steel` to use the steel specific finance model). + - `commodity_desc (optional)`: + A text label to further distinguish outputs for a commodity. This is particularly useful when multiple finance models or subgroups reference the same commodity but need to produce separate outputs. + +```{important} +If no subgroups are defined, a **default subgroup** is created that contains *all technologies* and references the default finance model and commodity defined in `finance_groups`. +``` (finparam:nosubgroups)= -## Without specifying subgroups -If no `subgroups` are specified, the user is limited to a single general finance model and a single commodity. The finance model will include costs from all the technologies included in the `tech_config` file. +## Single-Model (No Subgroups) +If no `finance_subgroups` are specified, all technologies are automatically grouped into a single default subgroup. In this case: + - `commodity` and `finance_model` must be defined directly in `finance_groups`. + - A default subgroup named `default` is created internally. General format: ```yaml finance_parameters: - commodity: "commodity_a" - finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for finance_model_a + finance_groups: + commodity: "hydrogen" + finance_model: "ProFastComp" + model_inputs: + discount_rate: 0.08 ``` -- Format explained: - - **`commodity_a`**: a commodity produced by at least one technology in `tech_config`. Ex: "hydrogen". - - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". -- output naming convention: - - `financials_subgroup_default.` where `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the commodity (`commodity_a` in this example). -- examples that use this format: - - [Example 7](https://github.com/NREL/H2Integrate/blob/develop/examples/07_run_of_river_plant/plant_config.yaml) +Outputs are named: +``` +finance_subgroup_default. +``` + +Examples: +- [Example 7](https://github.com/NREL/H2Integrate/blob/develop/examples/07_run_of_river_plant/plant_config.yaml) (finparams:singlemodelsubgroups)= -## Single financial model with subgroups -This format assumes that all subgroups use the same finance model. The `finance_groups` parameter for the `subgroups` is not required. +## Single Finance Model With Subgroups +If `finance_groups` contains a single model definition, you may split technologies into multiple subgroups. Each subgroup defines its own `commodity` and list of `technologies` but uses the shared finance model. + +In this case you see that the commodity is not defined within the `finance_groups` and is instead defined within the `finance_subgroups`. General format: ```yaml finance_parameters: - finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for finance_model_a - subgroups: + finance_groups: + finance_model: "ProFastComp" + model_inputs: #dictionary of inputs for ProFastComp + finance_subgroups: subgroup_a: - commodity: "commodity_a" #required - commodity_desc: "" #optional description of commodity_a - technologies: ["tech_a"] + commodity: "hydrogen" #required + technologies: ["electrolyzer"] subgroup_b: - commodity: "commodity_b" #required - commodity_desc: "" #optional description of commodity_b - technologies: ["tech_a", "tech_b"] + commodity: "ammonia" #required + technologies: ["electrolyzer", "asu"] +``` + +Outputs are named: +``` +finance_subgroup_. ``` -- Format explained: - - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". - - **`subgroup_a`**: - - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. - - **`subgroup_b`**: - - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. - - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - -- output naming convention: - - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **`commodity_desc`** for **`subgroup_a`**. (which is an empty string in this example). - - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **`commodity_desc`** for **`subgroup_b`**. (which is an empty string in this example). -- examples that use this format: - - [Example 02](https://github.com/NREL/H2Integrate/tree/develop/examples/02_texas_ammonia/plant_config.yaml) - - [Example 03 - CO2H](https://github.com/NREL/H2Integrate/tree/develop/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) - - [Example 09 - OAE](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml) - - [Example 09 - DOC](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/direct_ocean_capture/plant_config.yaml) - - [Example 09 - OAE Financials](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/ocean_alkalinity_enhancement_financials/plant_config_financials.yaml) +Examples: +- [Example 02](https://github.com/NREL/H2Integrate/tree/develop/examples/02_texas_ammonia/plant_config.yaml) +- [Example 03 - CO2H](https://github.com/NREL/H2Integrate/tree/develop/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml) +- [Example 09 - DOC](https://github.com/NREL/H2Integrate/tree/develop/examples/09_co2/direct_ocean_capture/plant_config.yaml) -(finparams:namedsinglemodelsubgroups)= -## Nicknamed financial model with subgroups -This is an alternative format to [above](finparams:singlemodelsubgroups) that highlights some logic for [specifying multiple finance models](finparams:multimodelsubgroups). In this case, the `finance_groups` parameter is required for each `subgroup`. - -General format: -```yaml -finance_parameters: - finance_model_nickname_a: - finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for finance_model_a - subgroups: - subgroup_a: - commodity: "commodity_a" #required - commodity_desc: "" #optional description of commodity_a - finance_groups: [finance_model_nickname_a] #required in this case - technologies: ["tech_a"] - subgroup_b: - commodity: "commodity_b" #required - commodity_desc: "" #optional description of commodity_b - finance_groups: [finance_model_nickname_a] #required in this case - technologies: ["tech_b", "tech_c"] +```{note} +Within `finance_groups`, the `commodity`, `finance_model`, and `model_inputs` may be placed under a named key for the finance group and indented one level deeper. This structure is optional when only a single finance model is used, but it is supported for consistency with the format required when specifying multiple finance models (see [Specifying Multiple Finance Groups](finparams:multimodelsubgroups)). ``` -- Format explained: - - **`finance_model_nickname_a`**: the name used to reference a specific finance model and its corresponding inputs. - - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". - - **`subgroup_a`**: - - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a`. - - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_a`** finance calculations. - - **`subgroup_b`**: - - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a`. - - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_b`** finance calculations. -- output naming convention: - - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_a` in this example) and the **`commodity_desc`** for **`subgroup_a`**. (which is an empty string in this example). - - `financials_subgroup_.` - - `` is an output from `finance_model_a` whose name includes the output parameter and some representation of the **commodity** (`commodity_b` in this example) and the **`commodity_desc`** for **`subgroup_b`**. (which is an empty string in this example). -- examples that use this format: - - [Example 01](https://github.com/NREL/H2Integrate/blob/develop/examples/01_onshore_steel_mn/plant_config.yaml) - - (finparams:multimodelsubgroups)= ## Multiple financial models with subgroups -This format is used to specify multiple general finance models and/or multiple commodity types. +When multiple finance models are needed (e.g., to calculate both NPV and LCOH, or to compare multiple finance cases), multiple finance groups can be defined and assigned to subgroups. General format: ```yaml finance_parameters: - finance_model_nickname_a: - finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for the finance_model_a - finance_model_nickname_b: - finance_model: "finance_model_a" #ex: "ProFastComp" - model_inputs: #dictionary of inputs for finance_model_a, these may be different than the model inputs for `finance_model_nickname_a` - finance_model_nickname_c: - finance_model: "finance_model_c" #ex: "NPVFinancial" - model_inputs: #dictionary of inputs for finance_model_c - subgroups: + finance_groups: + group_a: + finance_model: "ProFastComp" + model_inputs: {discount_rate: 0.08} + group_b: + finance_model: "NPVFinancial" + model_inputs: {discount_rate: 0.05} + finance_subgroups: subgroup_a: - commodity: "commodity_a" #required - commodity_desc: "desc_a" #optional description of commodity_a - finance_groups: [finance_model_nickname_a] - technologies: ["tech_a"] + commodity: "hydrogen" + finance_groups: ["group_a"] + technologies: ["electrolyzer"] subgroup_b: - commodity: "commodity_a" #required - commodity_desc: "desc_b" #optional description of commodity_b - finance_groups: [finance_model_nickname_a, finance_model_nickname_b] - technologies: ["tech_b", "tech_c"] - subgroup_c: - commodity: "commodity_b" #required - commodity_desc: "" #optional description of commodity_b - finance_groups: [finance_model_nickname_a, finance_model_nickname_c] - technologies: ["tech_b", "tech_c"] + commodity: "hydrogen" + commodity_desc: "delivered" + finance_groups: ["group_a", "group_b"] + technologies: ["pipeline", "storage"] +``` +Output naming: +- If multiple finance groups are in the same subgroup, output names include the subgroup, commodity, description (if provided), and finance group name to avoid collisions in the OpenMDAO framework: ``` -- Format explained: - - **`finance_model_nickname_a`**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. - - **`finance_model_nickname_b`**: the name used to reference a specific finance model (`finance_model_a` in this example) and its corresponding inputs. - - **`finance_model_nickname_c`**: the name used to reference a specific finance model (`finance_model_c` in this example) and its corresponding inputs. - - **`finance_model_a`**: a general finance model. Ex: "ProFastComp". - - **`finance_model_c`**: a general finance model. Ex: "ProFastComp". - - **`subgroup_a`**: - - **`commodity_a`**: commodity produced by `tech_a` to use in the financial calculation done in `finance_model_a`. - - **`tech_a`**: a technology key in `tech_config['technologies']`, these costs are included in the financial calculation done in `finance_model_a` (defined by **`finance_model_nickname_a`**) - - **`desc_a`**: some descriptor for the finance output parameters from the **`subgroup_a`** financial model(s) - - **`finance_model_nickname_a`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_a`** finance calculations. - - **`subgroup_b`**: - - **`commodity_b`**: commodity produced by either `tech_a` and/or `tech_b` to use in the financial calculation done in `finance_model_a` (defined by **`finance_model_nickname_a`** and **`finance_model_nickname_b`**) - - **`tech_a`** and **`tech_b`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_a`. - - **`desc_b`**: some descriptor for the finance output parameters from the **`subgroup_b`** financial model(s) - - **`finance_model_nickname_a`** and **`finance_model_nickname_b`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_b`** finance calculations. - - **`subgroup_c`**: - - **`commodity_b`**: commodity produced by either `tech_b` and/or `tech_c` to use in the financial calculations done in `finance_model_a` (defined by **`finance_model_nickname_a`**) and `finance_model_c` (defined by **`finance_model_nickname_c`**). - - **`tech_b`** and **`tech_c`**: technology keys in `tech_config['technologies']`, the costs of these technologies are included in the financial calculation done in `finance_model_c`. - - **`finance_model_nickname_a`** and **`finance_model_nickname_c`**: the nickname(s) of the finance model and corresponding inputs to use for **`subgroup_c`** finance calculations. -- naming convention: - - **`subgroup_a`** outputs: suppose `finance_model_a` outputs `price` parameter. - - `financials_subgroup_.price__` - - **`subgroup_b`** outputs: suppose `finance_model_a` outputs `price` parameter. - - `financials_subgroup_.price___` - - `financials_subgroup_.price___` - - **`subgroup_c`** outputs: suppose `finance_model_a` outputs `price` parameter and `finance_model_c` outputs `NPV` parameter. - - `financials_subgroup_.NPV__` - - `financials_subgroup_.NPV__` - - `financials_subgroup_.price__` - - `financials_subgroup_.price__` - -- examples that use this format: - - [Example 10](https://github.com/NREL/H2Integrate/blob/develop/examples/10_electrolyzer_om/plant_config.yaml) +finance_subgroup_subgroup_a.price_hydrogen_group_a +finance_subgroup_subgroup_b.price_hydrogen_delivered_group_a +finance_subgroup_subgroup_b.NPV_hydrogen_delivered_group_b +``` + +Examples: +- [Example 10](https://github.com/NREL/H2Integrate/blob/develop/examples/10_electrolyzer_om/plant_config.yaml) + +### Key Behaviors +- If `finance_parameters` is missing --> no finance model is created. +- If no `finance_subgroups` are defined → a default subgroup containing all technologies is created automatically. +- Finance groups must not include a key named "default", as this is reserved for internal use. +- Each subgroup must reference valid technology keys from technology_config['technologies']. Invalid keys raise errors. +- Finance models must be listed in `self.supported_models`. Unknown models raise errors. From 9bf0438f951f80bf6a9af66b374619c1ce1e30d1 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 09:17:43 -0500 Subject: [PATCH 61/67] update docs --- docs/finance_models/finance_index.md | 6 +----- docs/user_guide/specifying_finance_parameters.md | 6 ++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md index e15b73922..3b52bcdb6 100644 --- a/docs/finance_models/finance_index.md +++ b/docs/finance_models/finance_index.md @@ -6,7 +6,7 @@ - `driver_config` (dict): the `folder_outputs` specified here may be used by the finance model if the finance model outputs data to a file. - `tech_config` (dict): the technology configs for the technologies to include in the finance calculations -- `plant_config` (dict): contains the `model_inputs` for the finance model. +- `plant_config` (dict): contains the `finance_parameters` for the finance model (see [Finance Parameters](financeparameters:specifiyingfinanceparameters)). - `commodity_type` (str): the name of the commodity to use in the finance calculation. - `description` (str, optional): an additional description to use for naming outputs of the finance model. @@ -14,10 +14,6 @@ The `commodity_type` and `description` are used in the finance model naming convention. Specifics on the output naming convention for each finance model can be found in their docs. ``` -```{note} -The `tech_config` and `plant_config` are reformatted from their original format within `H2IntegrateModel` when creating the finance models as a preprocessing step. -``` - (finance:supportedmodels)= ## Currently supported general finance models diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 9bd735436..958a20d5c 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -1,3 +1,4 @@ +(financeparameters:specifiyingfinanceparameters)= # Finance Parameters ## Overview The `finance_parameters` section of the `plant_config` defines the financial subsystems of the plant. These parameters configure how costs, revenues, and investment metrics are calculated across all or parts of the modeled system. @@ -137,8 +138,9 @@ finance_parameters: Output naming: - If multiple finance groups are in the same subgroup, output names include the subgroup, commodity, description (if provided), and finance group name to avoid collisions in the OpenMDAO framework: ``` -finance_subgroup_subgroup_a.price_hydrogen_group_a -finance_subgroup_subgroup_b.price_hydrogen_delivered_group_a +finance_subgroup_.__ +finance_subgroup_subgroup_a.LCOH_group_a +finance_subgroup_subgroup_b.LCOH_delivered_group_a finance_subgroup_subgroup_b.NPV_hydrogen_delivered_group_b ``` From 9dace9a409a1a843d4037108ac3f1b23c7d67398 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 09:30:51 -0500 Subject: [PATCH 62/67] update examples and tests based on correct techs for subgroups --- examples/02_texas_ammonia/plant_config.yaml | 2 +- .../co2_hydrogenation/plant_config_co2h.yaml | 2 +- examples/05_wind_h2_opt/plant_config.yaml | 2 +- .../direct_ocean_capture/plant_config.yaml | 2 +- .../plant_config.yaml | 2 +- examples/12_ammonia_synloop/plant_config.yaml | 2 +- tests/h2integrate/test_all_examples.py | 16 ++++++++-------- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/02_texas_ammonia/plant_config.yaml b/examples/02_texas_ammonia/plant_config.yaml index 0e6d15f36..937b1e03d 100644 --- a/examples/02_texas_ammonia/plant_config.yaml +++ b/examples/02_texas_ammonia/plant_config.yaml @@ -68,7 +68,7 @@ finance_parameters: technologies: ["hopp"] hydrogen: commodity: "hydrogen" - technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage"] ammonia: commodity: "ammonia" technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] diff --git a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml index ddf6e571e..35f4a928a 100644 --- a/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml +++ b/examples/03_methanol/co2_hydrogenation/plant_config_co2h.yaml @@ -57,7 +57,7 @@ finance_parameters: finance_subgroups: electricity: commodity: "electricity" - technologies: ["hopp", "electrolyzer"] + technologies: ["hopp"] hydrogen: commodity: "hydrogen" technologies: ["hopp", "electrolyzer"] diff --git a/examples/05_wind_h2_opt/plant_config.yaml b/examples/05_wind_h2_opt/plant_config.yaml index ed72ede45..63d049174 100644 --- a/examples/05_wind_h2_opt/plant_config.yaml +++ b/examples/05_wind_h2_opt/plant_config.yaml @@ -58,7 +58,7 @@ finance_parameters: finance_subgroups: electricity: commodity: "electricity" - technologies: ["wind", "electrolyzer"] + technologies: ["wind"] hydrogen: commodity: "hydrogen" technologies: ["wind", "electrolyzer"] diff --git a/examples/09_co2/direct_ocean_capture/plant_config.yaml b/examples/09_co2/direct_ocean_capture/plant_config.yaml index 8a93d56b8..132897ae2 100644 --- a/examples/09_co2/direct_ocean_capture/plant_config.yaml +++ b/examples/09_co2/direct_ocean_capture/plant_config.yaml @@ -58,7 +58,7 @@ finance_parameters: finance_subgroups: electricity: commodity: "electricity" - technologies: ["hopp", "doc"] + technologies: ["hopp"] co2: commodity: "co2" technologies: ["hopp", "doc"] diff --git a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml index 9dc6bd5c0..cec912b9b 100644 --- a/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml +++ b/examples/09_co2/ocean_alkalinity_enhancement/plant_config.yaml @@ -58,7 +58,7 @@ finance_parameters: finance_subgroups: electricity: commodity: "electricity" - technologies: ["hopp", "oae"] + technologies: ["hopp"] co2: commodity: "co2" technologies: ["hopp", "oae"] diff --git a/examples/12_ammonia_synloop/plant_config.yaml b/examples/12_ammonia_synloop/plant_config.yaml index d44053983..9a3c790b0 100644 --- a/examples/12_ammonia_synloop/plant_config.yaml +++ b/examples/12_ammonia_synloop/plant_config.yaml @@ -61,7 +61,7 @@ finance_parameters: finance_subgroups: h2: commodity: "hydrogen" - technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] + technologies: ["hopp", "electrolyzer", "h2_storage"] nh3: commodity: "ammonia" technologies: ["hopp", "electrolyzer", "h2_storage", "ammonia"] diff --git a/tests/h2integrate/test_all_examples.py b/tests/h2integrate/test_all_examples.py index 923570242..a24256b29 100644 --- a/tests/h2integrate/test_all_examples.py +++ b/tests/h2integrate/test_all_examples.py @@ -104,7 +104,7 @@ def test_simple_ammonia_example(subtests): pytest.approx( model.prob.get_val("finance_subgroup_hydrogen.total_capex_adjusted")[0], rel=1e-3 ) - == 2678403968.6 + == 2577162708.3 ) with subtests.test("Check total adjusted OpEx"): @@ -112,14 +112,14 @@ def test_simple_ammonia_example(subtests): pytest.approx( model.prob.get_val("finance_subgroup_hydrogen.total_opex_adjusted")[0], rel=1e-3 ) - == 64338137.8 + == 53161706.5 ) # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOH"): assert ( pytest.approx(model.prob.get_val("finance_subgroup_hydrogen.LCOH")[0], rel=1e-3) - == 4.233055 + == 3.970 ) # Currently underestimated compared to the Reference Design Doc with subtests.test("Check LCOA"): @@ -203,7 +203,7 @@ def test_ammonia_synloop_example(subtests): with subtests.test("Check LCOH"): assert ( pytest.approx(model.prob.get_val("finance_subgroup_h2.LCOH")[0], rel=1e-6) - == 5.659321302703965 + == 3.9705799098258776 ) with subtests.test("Check LCOA"): @@ -244,7 +244,7 @@ def test_co2h_methanol_example(subtests): # Check levelized cost of methanol (LCOM) with subtests.test("Check CO2 Hydrogenation LCOM"): - assert pytest.approx(model.prob.get_val("methanol.LCOM")[0], rel=1e-6) == 1.38341179 + assert pytest.approx(model.prob.get_val("methanol.LCOM")[0], rel=1e-6) == 1.381162 def test_wind_h2_opt_example(subtests): @@ -275,7 +275,7 @@ def test_wind_h2_opt_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE")[0], rel=1e-3) - == 0.151189 + == 0.059311 ) with subtests.test("Check electrolyzer size"): @@ -373,7 +373,7 @@ def test_wind_wave_doc_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE")[0], rel=1e-3) - == 1.05281478 + == 0.330057 ) @@ -524,7 +524,7 @@ def test_wind_wave_oae_example(subtests): with subtests.test("Check LCOE"): assert ( pytest.approx(model.prob.get_val("finance_subgroup_electricity.LCOE"), rel=1e-3) - == 0.36956 + == 0.367 ) From caaaca5dd3db502764375e9eeae20609b037f9ac Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 09:41:58 -0500 Subject: [PATCH 63/67] respond to PR comments --- h2integrate/core/h2integrate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 1557d2aa2..71d682359 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -500,7 +500,7 @@ def create_finance_model(self): "model_inputs" ) # inputs to finance model - # Add finance model component + # get finance model component definition fin_model = self.supported_models.get(model_name) if fin_model is None: From 5ef5e2ddeda27535b32b09907f2a61f7b111e190 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Sat, 13 Sep 2025 09:45:51 -0500 Subject: [PATCH 64/67] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec81a707..9be2c7237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ - Added `simulation` section under `plant_config['plant']` that has information such as number of timesteps in the simulation, time step interval in seconds, simulation start time, and time zone. - Added `"custom_electrolyzer_cost"` model, an electrolyzer cost model that allows for user-defined capex and opex values - Made `pipe` and `cable` substance-agnostic rather than hard-coded for `hydrogen` and `electricity` +- Change finance handling to use `finance_subgroups` and `finance_groups` defined in the `plant_config` rather than previous `financial_groups` in the `tech_config` and `technologies_to_include_in_metrics` in `plant_config` ## 0.3.0 [May 2 2025] From c6c11a5c7c1903f29471cfe8748431931c3278ec Mon Sep 17 00:00:00 2001 From: kbrunik Date: Mon, 15 Sep 2025 10:45:18 -0500 Subject: [PATCH 65/67] add text to subgroups explaination --- docs/user_guide/specifying_finance_parameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 958a20d5c..547dcafcc 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -77,7 +77,7 @@ Examples: ## Single Finance Model With Subgroups If `finance_groups` contains a single model definition, you may split technologies into multiple subgroups. Each subgroup defines its own `commodity` and list of `technologies` but uses the shared finance model. -In this case you see that the commodity is not defined within the `finance_groups` and is instead defined within the `finance_subgroups`. +In this case you see that the commodity is not defined within the `finance_groups` and is instead defined within the `finance_subgroups`. In this example there are two separate financial calculations one for `subgroup_a`, which is for the hydrogen commodity and one for `subgroup_b`, which is for the ammonia commodity and includes the "electrolyzer" and "asu". If you had additional technologies in your `tech_config` besides those, they would have to be included in the `finance_subgroups` to be included in the financial calculations. General format: ```yaml From 1c8e570aa75881bb5ee6a13dfb59fba597bdd738 Mon Sep 17 00:00:00 2001 From: John Jasa Date: Mon, 15 Sep 2025 13:22:10 -0600 Subject: [PATCH 66/67] Minor docs updates --- docs/finance_models/finance_index.md | 5 ++-- docs/technology_models/technology_overview.md | 10 +++---- docs/user_guide/cost_years.md | 5 +++- .../specifying_finance_parameters.md | 26 +++++++++++-------- h2integrate/core/h2integrate_model.py | 15 ++++++----- h2integrate/finances/profast_financial.py | 2 -- h2integrate/tools/profast_tools.py | 15 +++++++++++ 7 files changed, 50 insertions(+), 28 deletions(-) diff --git a/docs/finance_models/finance_index.md b/docs/finance_models/finance_index.md index 3b52bcdb6..5df3e83e9 100644 --- a/docs/finance_models/finance_index.md +++ b/docs/finance_models/finance_index.md @@ -2,7 +2,8 @@ (finance:overview)= # Finance Models Overview -**General finance models** are not technology specific finance models. These models live in the `h2integrate/finances/` folder. These models accept `driver_config`, `tech_config`, `plant_config`, `commodity_type`, and a `description` as the inputs and options. +**General finance models** compute finance metrics and are not specific to individual technologies. +These models live in the `h2integrate/finances/` folder and accept `driver_config`, `tech_config`, `plant_config`, `commodity_type`, and a `description` as the inputs and options. - `driver_config` (dict): the `folder_outputs` specified here may be used by the finance model if the finance model outputs data to a file. - `tech_config` (dict): the technology configs for the technologies to include in the finance calculations @@ -17,4 +18,4 @@ The `commodity_type` and `description` are used in the finance model naming conv (finance:supportedmodels)= ## Currently supported general finance models -- [``"ProFastComp"``](profastcomp:profastcompmodel): calculates levelized cost of commodity using ProFAST. +- [``ProFastComp``](profastcomp:profastcompmodel): calculates levelized cost of commodity using ProFAST. diff --git a/docs/technology_models/technology_overview.md b/docs/technology_models/technology_overview.md index 52427106d..6d8f09641 100644 --- a/docs/technology_models/technology_overview.md +++ b/docs/technology_models/technology_overview.md @@ -89,13 +89,13 @@ Below summarizes the available performance, cost, and financial models for each - [Storage](#storage-models) (resource-models)= -### Resource models +## Resource models - `river`: - performance models: + `river_resource` (converter-models)= -### Converter models +## Converter models - `wind`: wind turbine - performance models: + `'wind_plant_performance'` @@ -179,7 +179,7 @@ Below summarizes the available performance, cost, and financial models for each + `'reverse_osmosis_desalination_cost'` (transport-models)= -### Transport Models +## Transport Models - `cable` - performance models: + `'cable'` @@ -191,7 +191,7 @@ Below summarizes the available performance, cost, and financial models for each + `'combiner_performance'` (storage-models)= -### Storage Models +## Storage Models - `h2_storage`: hydrogen storage - combined performance and cost + `'h2_storage'` @@ -200,6 +200,6 @@ Below summarizes the available performance, cost, and financial models for each - cost models: + `'hydrogen_tank_cost'` -### Controller Models +## Controller Models - `pass_through_controller` - `demand_open_loop_controller` diff --git a/docs/user_guide/cost_years.md b/docs/user_guide/cost_years.md index 81fc07858..dba62d317 100644 --- a/docs/user_guide/cost_years.md +++ b/docs/user_guide/cost_years.md @@ -1,6 +1,9 @@ (cost:cost_years)= # Cost year of Cost Models -Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. Some cost models require users to input the key cost information, and the output costs are in the same cost year as the user-provided costs. For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. For [cost models based on user-provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. +Some cost models are derived from literature and output costs (CapEx and OpEx) in a specific dollar-year. +Some cost models require users to input the key cost information and the output costs are in the same cost year as the user-provided costs. +For [cost models with a built-in cost year](#cost-models-with-inherent-cost-year), the cost year is not required as an input for the cost model. +For [cost models based on user-provided costs](#cost-models-with-user-input-cost-year), the `cost_year` should be included in the tech_config for that technology. (cost-models-with-inherent-cost-year)= ## Cost models with inherent cost year diff --git a/docs/user_guide/specifying_finance_parameters.md b/docs/user_guide/specifying_finance_parameters.md index 547dcafcc..8c041cf05 100644 --- a/docs/user_guide/specifying_finance_parameters.md +++ b/docs/user_guide/specifying_finance_parameters.md @@ -16,7 +16,7 @@ Other variables in finance_parameters vary depending on the financial analysis s - **Subgroup mode**: Technologies are split into one or more subgroups, each with its own commodity and one or more finance models. ### Finance Groups vs. Finance Subgroups -Within this framework, there are two distinct layers: +Within this framework, there are two distinct layers, **finance groups** and **finance subgroups**: #### Finance groups A finance group contains the attributes needed to run one finance model: @@ -24,32 +24,36 @@ Within this framework, there are two distinct layers: The name of the financial model to use (e.g., `ProFastComp`). Must correspond to one of the available models in `self.supported_models`. - `model_inputs`: A dictionary of parameters passed into the chosen finance model. These provide customization of assumptions such as discount rate, debt fraction, or cost escalation. - - `commodity`: + - `commodity` (conditionally required): The product or service whose financial performance is being analyzed (e.g., hydrogen, electricity). Required if `finance_subgroups` are not used, otherwise defined within `finance_subgroups`. #### Finance subgroups Subgroups are flexible collections of technologies that map to one or more finance groups. They allow you to: - Calculate financial metrics for only part of the system. - - Compare different views of the same system (e.g., delivered vs. produced cost of hydrogen). + - Compare different metrics of the same system (e.g., delivered vs. produced cost of hydrogen). - Run multiple models (e.g., LCOH, LCOE, LCOS, or NPV) on overlapping or distinct sets of technologies. - Model distributed or multi-location systems finances within a single plant configuration. Subgroups contain information on how to construct the specific subgroup: - `commodity`: The product or service whose financial performance is being analyzed (e.g., hydrogen, electricity). Each finance subgroup is tied to a single commodity. - - `technologies`: - Technologies to be include in the specific subgroup calculation (e.g., you might only want electricity producing technologies in the levelized cost of energy calculation). + - `technologies`: + Technologies to include in the specific subgroup calculation (e.g., you might only want to include technologies that produce electricity in the levelized cost of energy calculation). - `finance_groups`: - Refers to the `finance_groups` that contain the `finance_model` and `model_inputs`. Required if multiple `finance_groups` are being used. Technology-specific `finance_groups` can be called by using the technology name listed in the `tech_config` (e.g., `steel` to use the steel specific finance model). - - `commodity_desc (optional)`: + List of `finance_groups` that contain the `finance_model` and `model_inputs`. Required if multiple `finance_groups` are being used. Technology-specific `finance_groups` can be called by using the technology name listed in the `tech_config` (e.g., `steel` to use the steel specific finance model). + - `commodity_desc` (optional): A text label to further distinguish outputs for a commodity. This is particularly useful when multiple finance models or subgroups reference the same commodity but need to produce separate outputs. ```{important} If no subgroups are defined, a **default subgroup** is created that contains *all technologies* and references the default finance model and commodity defined in `finance_groups`. ``` +## Example finance configurations + +We'll now walk through three common configurations, highlighting the differences in the `plant_config` files and showcasing examples that use each approach. + (finparam:nosubgroups)= -## Single-Model (No Subgroups) +### Single-model (no subgroups) If no `finance_subgroups` are specified, all technologies are automatically grouped into a single default subgroup. In this case: - `commodity` and `finance_model` must be defined directly in `finance_groups`. - A default subgroup named `default` is created internally. @@ -74,7 +78,7 @@ Examples: (finparams:singlemodelsubgroups)= -## Single Finance Model With Subgroups +### Single finance model with subgroups If `finance_groups` contains a single model definition, you may split technologies into multiple subgroups. Each subgroup defines its own `commodity` and list of `technologies` but uses the shared finance model. In this case you see that the commodity is not defined within the `finance_groups` and is instead defined within the `finance_subgroups`. In this example there are two separate financial calculations one for `subgroup_a`, which is for the hydrogen commodity and one for `subgroup_b`, which is for the ammonia commodity and includes the "electrolyzer" and "asu". If you had additional technologies in your `tech_config` besides those, they would have to be included in the `finance_subgroups` to be included in the financial calculations. @@ -111,7 +115,7 @@ Within `finance_groups`, the `commodity`, `finance_model`, and `model_inputs` ma ``` (finparams:multimodelsubgroups)= -## Multiple financial models with subgroups +### Multiple financial models with subgroups When multiple finance models are needed (e.g., to calculate both NPV and LCOH, or to compare multiple finance cases), multiple finance groups can be defined and assigned to subgroups. General format: @@ -147,7 +151,7 @@ finance_subgroup_subgroup_b.NPV_hydrogen_delivered_group_b Examples: - [Example 10](https://github.com/NREL/H2Integrate/blob/develop/examples/10_electrolyzer_om/plant_config.yaml) -### Key Behaviors +#### Key Behaviors - If `finance_parameters` is missing --> no finance model is created. - If no `finance_subgroups` are defined → a default subgroup containing all technologies is created automatically. - Finance groups must not include a key named "default", as this is reserved for internal use. diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index 71d682359..a96644502 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -13,11 +13,10 @@ from h2integrate.core.pose_optimization import PoseOptimization -# try: -# import pyxdsm -# except ImportError: -# pyxdsm = None -pyxdsm = None +try: + import pyxdsm +except ImportError: + pyxdsm = None class H2IntegrateModel: @@ -406,7 +405,7 @@ def create_finance_model(self): if not commodity or not finance_model_name: raise ValueError( - "plant_config['finance_parameters']['finance_groups']" + "plant_config['finance_parameters']['finance_groups'] " "must define 'commodity' and 'finance_model' " "if no finance_subgroups are provided." ) @@ -434,7 +433,9 @@ def create_finance_model(self): # check commodity type if commodity is None: - raise ValueError(f"Missing ``commodity`` provided in subgroup {subgroup_name}") + raise ValueError( + f"Required parameter ``commodity`` not provided in subgroup {subgroup_name}." + ) tech_configs = {} for tech in tech_names: diff --git a/h2integrate/finances/profast_financial.py b/h2integrate/finances/profast_financial.py index 1c8fd4241..7608206e6 100644 --- a/h2integrate/finances/profast_financial.py +++ b/h2integrate/finances/profast_financial.py @@ -401,8 +401,6 @@ class ProFastComp(om.ExplicitComponent): recovered in years. price_ (float): first year price of commodity in same units as `LCOx` - - Methods: initialize(): Declares component options. setup(): Defines inputs and outputs based on user configuration and validates diff --git a/h2integrate/tools/profast_tools.py b/h2integrate/tools/profast_tools.py index e848fd3a3..b62481a8d 100644 --- a/h2integrate/tools/profast_tools.py +++ b/h2integrate/tools/profast_tools.py @@ -223,6 +223,21 @@ def create_years_of_operation( def format_profast_price_breakdown_per_year(price_breakdown): + """ + Formats a price breakdown DataFrame to expand yearly amounts into separate columns. + + Args: + price_breakdown (pd.DataFrame): A DataFrame containing "Type", "Name", "Amount", and "NPV" + - "Amount" should be an array-like object per row, representing values for each year. + Returns: + pd.DataFrame: A formatted DataFrame with columns: + - "Type" + - "Name" + - "Year {i} Amount" for each year (e.g., "Year 0 Amount", "Year 1 Amount", ...) + - "NPV" + Each row corresponds to an entry in the input DataFrame, + with yearly amounts expanded into separate columns. + """ n_years = len(price_breakdown.iloc[0]["Amount"]) year_cols = [f"Year {i} Amount" for i in range(n_years)] From d0677173d42b91359171d1534c983f5b94e8e10a Mon Sep 17 00:00:00 2001 From: John Jasa Date: Mon, 15 Sep 2025 13:27:10 -0600 Subject: [PATCH 67/67] Removed notion of `time_zone` for now --- docs/user_guide/how_to_set_up_an_analysis.ipynb | 8 +------- examples/17_splitter_wind_doc_h2/plant_config.yaml | 1 - h2integrate/core/h2integrate_model.py | 1 - h2integrate/core/inputs/plant_schema.yaml | 5 ----- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/docs/user_guide/how_to_set_up_an_analysis.ipynb b/docs/user_guide/how_to_set_up_an_analysis.ipynb index af6024139..76abe3272 100644 --- a/docs/user_guide/how_to_set_up_an_analysis.ipynb +++ b/docs/user_guide/how_to_set_up_an_analysis.ipynb @@ -133,7 +133,6 @@ " latitude: 47.5233\n", " longitude: -92.5366\n", " elevation_m: 439.0\n", - " time_zone: -5\n", "\n", "# array of arrays containing left-to-right technology\n", "# interconnections; can support bidirectional connections\n", @@ -587,7 +586,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.11.13 ('h2i_env')", + "display_name": "hopp", "language": "python", "name": "python3" }, @@ -602,11 +601,6 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.13" - }, - "vscode": { - "interpreter": { - "hash": "e55566d5f9cb5003b92ad1d2254e8146f3d62519cfa868f35d73d51fb57327c6" - } } }, "nbformat": 4, diff --git a/examples/17_splitter_wind_doc_h2/plant_config.yaml b/examples/17_splitter_wind_doc_h2/plant_config.yaml index 3642fe6b9..c6d23776f 100644 --- a/examples/17_splitter_wind_doc_h2/plant_config.yaml +++ b/examples/17_splitter_wind_doc_h2/plant_config.yaml @@ -5,7 +5,6 @@ site: latitude: 43.807 longitude: -124.816 elevation_m: 439.0 - time_zone: -6 # array of polygons defining boundaries with x/y coords boundaries: [ diff --git a/h2integrate/core/h2integrate_model.py b/h2integrate/core/h2integrate_model.py index a96644502..bdcffc1b9 100644 --- a/h2integrate/core/h2integrate_model.py +++ b/h2integrate/core/h2integrate_model.py @@ -135,7 +135,6 @@ def create_site_model(self): site_component.add_output("latitude", val=site_config.get("latitude", 0.0)) site_component.add_output("longitude", val=site_config.get("longitude", 0.0)) site_component.add_output("elevation_m", val=site_config.get("elevation_m", 0.0)) - site_component.add_output("time_zone", val=site_config.get("time_zone", 0)) # Add boundaries if they exist site_config = self.plant_config.get("site", {}) diff --git a/h2integrate/core/inputs/plant_schema.yaml b/h2integrate/core/inputs/plant_schema.yaml index 112a7865a..b5616e235 100644 --- a/h2integrate/core/inputs/plant_schema.yaml +++ b/h2integrate/core/inputs/plant_schema.yaml @@ -27,11 +27,6 @@ properties: type: number description: Elevation of the site in meters minimum: 0 - time_zone: - type: number - description: Time zone offset from UTC in hours - minimum: -12 - maximum: 14 boundaries: type: array description: Array of polygons defining boundaries with x/y coordinates