Skip to content

Make system capacity an input for most cost models #192

@elenya-grant

Description

@elenya-grant

some cost models won't be accurate in capacity optimizations to minimize some cost or financial value (I think)

Many of the cost models in H2I calculate CapEx and OpEx for a component based on its capacity and some input or calculated costs in $/capacity unit. I'll use the electrolyzer as an example:

  • performance model: ECOElectrolyzerPerformanceModel in h2integrate/converters/hydrogen/eco_tools_pem_electrolyzer.py
  • cost model: SingliticoCostModel in h2integrate/converters/hydrogen/singlitico_cost_model.py

Suppose we want to optimize the electrolyzer capacity to meet some annual hydrogen demand and minimize LCOH.

ECOElectrolyzerPerformanceModel has the electrolyzer_size_mw as an input, initialized to the rating value provided in the config. SingliticoCostModel does not have electrolyzer_size_mw as an input and only uses the value provided in the config. My understanding is that if we are trying to optimize the electrolyzer capacity, then the performance model will see that capacity change (because the electrolyzer capacity is an input) but the cost model won't (because it'll always be using the same value that's in the config). This means the following situation is possible:

  • in the config, the electrolyzer rating is 100 MW
  • the optimizer runs an electrolyzer with a rating of 50 MW
  • the performance model runs an electrolyzer rated at 50 MW
  • the cost model outputs the same cost as the 100 MW system, even though the optimizer is running a 50 MW system.
  • The capital cost and fixed maintenance cost of the electrolyzer never changes as the electrolyzer capacity is varied in the optimizer. The only thing that changes is the electrolyzer hydrogen production.

Proposed solution

Access the component capacity needed for capex and opex calculations within cost models from inputs rather than the config. Or - begin to include component capacity as as input in cost model base classes (if that ever makes sense). Here's an example of what I propose all cost models look like:

class myExampleCostModel(ExampleCostBaseClass):
    def setup(self):
        super().setup()
        self.config = ExampleCostModelConfig.from_dict(
            merge_shared_inputs(self.options["tech_config"]["model_inputs"], "cost")
        )
        self.add_input(
                    "example_capacity",
                    val=self.config.example_capacity_kW,
                    units="kW",
                    desc="example component rated capacity in kW",
                )
    def compute(self, inputs, outputs):
        capacity = inputs['example_capacity'][0]
        capex = self.config.capex_per_kW*capacity
        opex = self.config.opex_per_kW_per_year*capacity
        outputs["CapEx"] = capex
        outputs["OpEx"] = opex

Alternatives considered

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions