diff --git a/greenheart/simulation/technologies/ammonia/ammonia.py b/greenheart/simulation/technologies/ammonia/ammonia.py index ac2823ea1..4bed7415c 100644 --- a/greenheart/simulation/technologies/ammonia/ammonia.py +++ b/greenheart/simulation/technologies/ammonia/ammonia.py @@ -163,26 +163,26 @@ class AmmoniaSizeModelConfig: feedstock details. Attributes: - hydrogen_amount_kg Optional (float): The amount of hydrogen available in kilograms to - make ammonia. - ammonia_plant_size_kg Optional (float): The amount of desired ammonia production in - kilograms. + hydrogen_amount_kg Optional (float): The amount of hydrogen available in kilograms + per year to make ammonia. + ammonia_plant_size_kgpy Optional (float): The amount of desired ammonia production in + kilograms per year. plant_capcity_factor (float): The ammonia plant capacity factor. feedstocks (Feedstocks): An instance of the `Feedstocks` class detailing the costs and consumption rates of resources used in production. """ plant_capacity_factor: float - feedstock: Feedstocks + feedstocks: Feedstocks hydrogen_amount_kg: Optional[float] = field(default=None) - ammonia_plant_size_kg: Optional[float] = field(default=None) + ammonia_plant_size_kgpy: Optional[float] = field(default=None) def __attrs_post_init__(self): - if self.hydrogen_amount_kg is None and self.ammonia_plant_size_kg is None: - raise ValueError("`hydrogen_amount_kg` or `ammonia_plant_size_kg` is a required input.") + if self.hydrogen_amount_kg is None and self.ammonia_plant_size_kgpy is None: + raise ValueError("`hydrogen_amount_kg` or `ammonia_plant_size_kgpy` is a required input.") - if self.hydrogen_amount_kg and self.ammonia_plant_size_kg: - raise ValueError("can only select one input: `hydrogen_amount_kg` or `ammonia_plant_size_kg`.") + if self.hydrogen_amount_kg and self.ammonia_plant_size_kgpy: + raise ValueError("can only select one input: `hydrogen_amount_kg` or `ammonia_plant_size_kgpy`.") @define class AmmoniaSizeModelOutputs: @@ -190,34 +190,34 @@ class AmmoniaSizeModelOutputs: Outputs from the ammonia size model. Attributes: - ammonia_plant_size_kg (float): If amount of hydrogen in kilograms is input, - the size of the ammonia plant in kilograms is output. - hydrogen_amount_kg (float): If amount of ammonia production in kilograms is input, - the amount of necessary hydrogen feedstock in kilograms is output. + ammonia_plant_size_kgpy (float): If amount of hydrogen in kilograms per year is input, + the size of the ammonia plant in kilograms per year is output. + hydrogen_amount_kg (float): If amount of ammonia production in kilograms per year is input, + the amount of necessary hydrogen feedstock in kilograms per year is output. """ - ammonia_plant_size_kg: float + ammonia_plant_size_kgpy: float hydrogen_amount_kg: float def run_size_ammonia_plant(config: AmmoniaSizeModelConfig) -> AmmoniaSizeModelOutputs: if config.hydrogen_amount_kg: - ammonia_plant_size_kg = (config.hydrogen_amount_kg - / config.feedstock.hydrogen_consumption + ammonia_plant_size_kgpy = (config.hydrogen_amount_kg + / config.feedstocks.hydrogen_consumption * config.plant_capacity_factor ) hydrogen_amount_kg = config.hydrogen_amount_kg - if config.ammonia_plant_size_kg: - hydrogen_amount_kg = (config.ammonia_plant_size_kg - * config.feedstock.hydrogen_consumption + if config.ammonia_plant_size_kgpy: + hydrogen_amount_kg = (config.ammonia_plant_size_kgpy + * config.feedstocks.hydrogen_consumption / config.plant_capacity_factor ) - ammonia_plant_size_kg = (config.ammonia_plant_size_kg + ammonia_plant_size_kgpy = (config.ammonia_plant_size_kgpy / config.plant_capacity_factor ) return AmmoniaSizeModelOutputs( - ammonia_plant_size_kg=ammonia_plant_size_kg, + ammonia_plant_size_kgpy=ammonia_plant_size_kgpy, hydrogen_amount_kg=hydrogen_amount_kg ) diff --git a/greenheart/simulation/technologies/steel.py b/greenheart/simulation/technologies/steel/steel.py similarity index 91% rename from greenheart/simulation/technologies/steel.py rename to greenheart/simulation/technologies/steel/steel.py index e2c2c0623..8974ed74d 100644 --- a/greenheart/simulation/technologies/steel.py +++ b/greenheart/simulation/technologies/steel/steel.py @@ -1,8 +1,8 @@ -from typing import Dict, Union +from typing import Dict, Union, Optional import ProFAST import pandas as pd -from attrs import define, Factory +from attrs import define, Factory, field @define @@ -191,6 +191,72 @@ class SteelCostModelOutputs(SteelCosts): spare_parts_cost: float misc_owners_costs: float +@define +class SteelSizeModelConfig: + """ + Configuration inputs for the steel sizing model, including plant capacity and + feedstock details. + + Attributes: + hydrogen_amount_kg Optional (float): The amount of hydrogen available in kilograms + per year to make steel. + steel_plant_size_mtpy Optional (float): The amount of desired steel production in + metric tonnes per year. + plant_capcity_factor (float): The steel plant capacity factor. + feedstocks (Feedstocks): An instance of the `Feedstocks` class detailing the + costs and consumption rates of resources used in production. + """ + plant_capacity_factor: float + feedstocks: Feedstocks + hydrogen_amount_kg: Optional[float] = field(default=None) + steel_plant_size_mtpy: Optional[float] = field(default=None) + + + def __attrs_post_init__(self): + if self.hydrogen_amount_kg is None and self.steel_plant_size_mtpy is None: + raise ValueError("`hydrogen_amount_kg` or `steel_plant_size_mtpy` is a required input.") + + if self.hydrogen_amount_kg and self.steel_plant_size_mtpy: + raise ValueError("can only select one input: `hydrogen_amount_kg` or `steel_plant_size_mtpy`.") + +@define +class SteelSizeModelOutputs: + """ + Outputs from the steel size model. + + Attributes: + steel_plant_size_mtpy (float): If amount of hydrogen in kilograms per year is input, + the size of the steel plant in metric tonnes per year is output. + hydrogen_amount_kg (float): If amount of steel production in metric tonnes per year is input, + the amount of necessary hydrogen feedstock in kilograms per year is output. + """ + steel_plant_size_mtpy: float + hydrogen_amount_kg: float + +def run_size_steel_plant(config: SteelSizeModelConfig) -> SteelSizeModelOutputs: + + if config.hydrogen_amount_kg: + steel_plant_size_mtpy = (config.hydrogen_amount_kg + / 1000 + / config.feedstocks.hydrogen_consumption + * config.plant_capacity_factor + ) + hydrogen_amount_kg = config.hydrogen_amount_kg + + if config.steel_plant_size_mtpy: + hydrogen_amount_kg = (config.steel_plant_size_mtpy + * 1000 + * config.feedstocks.hydrogen_consumption + / config.plant_capacity_factor + ) + steel_plant_size_mtpy = (config.steel_plant_size_mtpy + / config.plant_capacity_factor + ) + + return SteelSizeModelOutputs( + steel_plant_size_mtpy=steel_plant_size_mtpy, + hydrogen_amount_kg=hydrogen_amount_kg + ) def run_steel_model(plant_capacity_mtpy: float, plant_capacity_factor: float) -> float: """ diff --git a/tests/greenheart/test_ammonia.py b/tests/greenheart/test_ammonia.py index ce8437f60..6ea895fa5 100644 --- a/tests/greenheart/test_ammonia.py +++ b/tests/greenheart/test_ammonia.py @@ -115,7 +115,7 @@ def test_ammonia_size_h2_input(subtests): config = ammonia.AmmoniaSizeModelConfig( hydrogen_amount_kg=73288888.8888889, plant_capacity_factor=0.9, - feedstock=ammonia.Feedstocks( + feedstocks=ammonia.Feedstocks( electricity_cost=89.42320514456621, hydrogen_cost=4.2986685034417045, cooling_water_cost=0.00291, @@ -127,15 +127,15 @@ def test_ammonia_size_h2_input(subtests): res: ammonia.AmmoniaSizeModelOutputs = ammonia.run_size_ammonia_plant(config) with subtests.test("Ammonia plant size"): - assert res.ammonia_plant_size_kg == approx(334339658.8730839) + assert res.ammonia_plant_size_kgpy == approx(334339658.8730839) with subtests.test("hydrogen input"): assert res.hydrogen_amount_kg == approx(73288888.8888889) def test_ammonia_size_NH3_input(subtests): config = ammonia.AmmoniaSizeModelConfig( - ammonia_plant_size_kg=334339658.8730839, + ammonia_plant_size_kgpy=334339658.8730839, plant_capacity_factor=0.9, - feedstock=ammonia.Feedstocks( + feedstocks=ammonia.Feedstocks( electricity_cost=89.42320514456621, hydrogen_cost=4.2986685034417045, cooling_water_cost=0.00291, @@ -147,6 +147,6 @@ def test_ammonia_size_NH3_input(subtests): res: ammonia.AmmoniaSizeModelOutputs = ammonia.run_size_ammonia_plant(config) with subtests.test("Ammonia plant size"): - assert res.ammonia_plant_size_kg == approx(371488509.8589821) + assert res.ammonia_plant_size_kgpy == approx(371488509.8589821) with subtests.test("hydrogen input"): assert res.hydrogen_amount_kg == approx(73288888.8888889) \ No newline at end of file diff --git a/tests/greenheart/test_steel.py b/tests/greenheart/test_steel.py index 3b36f898a..0dc059f8f 100644 --- a/tests/greenheart/test_steel.py +++ b/tests/greenheart/test_steel.py @@ -137,3 +137,35 @@ def test_steel_finance_model(cost_config): res: steel.SteelFinanceModelOutputs = steel.run_steel_finance_model(config) assert res.sol.get("price") == lcos_expected + +def test_steel_size_h2_input(subtests): + config = steel.SteelSizeModelConfig( + hydrogen_amount_kg=73288888.8888889, + plant_capacity_factor=0.9, + feedstocks=steel.Feedstocks( + natural_gas_prices=ng_prices_dict, oxygen_market_price=0 + ), + ) + + res: steel.SteelSizeModelOutputs = steel.run_size_steel_plant(config) + + with subtests.test("steel plant size"): + assert res.steel_plant_size_mtpy == approx(1000000) + with subtests.test("hydrogen input"): + assert res.hydrogen_amount_kg == approx(73288888.8888889) + +def test_steel_size_NH3_input(subtests): + config = steel.SteelSizeModelConfig( + steel_plant_size_mtpy=1000000, + plant_capacity_factor=0.9, + feedstocks=steel.Feedstocks( + natural_gas_prices=ng_prices_dict, oxygen_market_price=0 + ), + ) + + res: steel.SteelSizeModelOutputs = steel.run_size_steel_plant(config) + + with subtests.test("steel plant size"): + assert res.steel_plant_size_mtpy == approx(1111111.111111111) + with subtests.test("hydrogen input"): + assert res.hydrogen_amount_kg == approx(73288888.8888889) \ No newline at end of file