Repository: NREL/HOPP (language: python)

Issue:
Add Steel/Ammonia models to greenheart tech
<!--
IMPORTANT NOTES

Is this pull request ready to be merged?
- Do the existing tests pass and new tests added for new code?
- Is all development in a state where you are proud to share it with others and
  willing to ask other people to take the time to review it?
- Is it documented in such a way that a review can reasonably understand what you've
  done and why you've done it? Can other users understand how to use your changes?
If not but opening the pull request will facilitate development, make it a "draft" pull request

This form is written in GitHub's Markdown format. For a reference on this type
of syntax, see GitHub's documentation:
https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax

This template contains guidance for your submission within the < ! - -, - - > blocks.
These are comments in HTML syntax and will not appear in the submission.
Be sure to use the "Preview" feature on GitHub to ensure your submission is formatted as intended.

When including code snippets, please paste the text itself and wrap the code block with
ticks (see the other character on the tilde ~ key in a US keyboard) to format it as code.
For example, Python code should be wrapped in ticks like this:
```python
def a_func():
    return 1

a = 1
b = a_func()
print(a + b)
```
This is preferred over screen shots since it is searchable and others can copy/paste
the text to run it.
-->


# Steel/Ammonia
<!--
Be sure to title your pull request so that readers can scan through the list of PR's and understand
what this one involves. It should be a few well selected words to get the point across. If you have
a hard time choosing a brief title, consider splitting the pull request into multiple pull requests.
Keep in mind that the title will be automatically included in the release notes.
-->

This PR takes steel and ammonia models represented in the green steel project work and updates them for use within the current greenheart framework. Both models have three functions:
- production model: for now, this is just based on capacity/capacity factor
- cost model: calculates capex, opex, installation
- finance model: runs finances with ProFAST

Each function has dataclasses associated with inputs and outputs, providing a clear interface that includes relevant defaults (though these defaults should be investigated in more detail).

## Related issue
<!--
If one exists, link to a related GitHub Issue.
-->

## Impacted areas of the software
<!--
List any modules or other areas which should be impacted by this pull request. This helps to
determine the verification tests.
-->

## Additional supporting information
<!--
Add any other context about the problem here.
-->

## Test results, if applicable
<!--
Add the results from unit tests and regression tests here along with justification for any
failing test cases.
-->

<!--
__ For NREL use __
Release checklist:
- Update the version in
    - [ ] README.md
    - [ ] hopp/VERSION
- [ ] Verify docs builds correctly
- [ ] Create a tag in the NREL/HOPP repository
-->



Discussion / hints:

Fix patch:
diff --git a/greenheart/simulation/technologies/ammonia/__init__.py b/greenheart/simulation/technologies/ammonia/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/greenheart/simulation/technologies/ammonia/ammonia.py b/greenheart/simulation/technologies/ammonia/ammonia.py
new file mode 100644
index 000000000..0f32e43ca
--- /dev/null
+++ b/greenheart/simulation/technologies/ammonia/ammonia.py
@@ -0,0 +1,545 @@
+"""
+Author: Abhineet Gupta and Kaitlin Brunik
+Created: 02/22/2024
+Institution: National Renewable Energy Lab
+Description: This file outputs 
+Costs are in 2018 USD
+
+Sources:
+    - [1] 
+"""
+
+from typing import Dict, Union
+import ProFAST
+
+import pandas as pd
+from attrs import define, Factory
+
+
+@define
+class Feedstocks:
+    """
+    Represents the costs and consumption rates of various feedstocks and resources
+    used in ammonia production.
+
+    Attributes:
+        electricity_cost (float): Cost per MWh of electricity.
+        hydrogen_cost (float): Cost per kg of hydrogen.
+        cooling_water_cost (float): Cost per gallon of cooling water.
+        iron_based_catalyst_cost (float): Cost per kg of iron-based catalyst.
+        oxygen_cost (float): Cost per kg of oxygen.
+        electricity_consumption (float): Electricity consumption in MWh per kg of
+            ammonia production, default is 0.1207 / 1000.
+        hydrogen_consumption (float): Hydrogen consumption in kg per kg of ammonia
+            production, default is 0.197284403.
+        cooling_water_consumption (float): Cooling water consumption in gallons per
+            kg of ammonia production, default is 0.049236824.
+        iron_based_catalyst_consumption (float): Iron-based catalyst consumption in kg
+            per kg of ammonia production, default is 0.000091295354067341.
+        oxygen_byproduct (float): Oxygen byproduct in kg per kg of ammonia production,
+            default is 0.29405077250145.
+    """
+
+    electricity_cost: float
+    hydrogen_cost: float
+    cooling_water_cost: float
+    iron_based_catalyst_cost: float
+    oxygen_cost: float
+    electricity_consumption: float = 0.1207 / 1000
+    hydrogen_consumption = 0.197284403
+    cooling_water_consumption = 0.049236824
+    iron_based_catalyst_consumption = 0.000091295354067341
+    oxygen_byproduct = 0.29405077250145
+
+
+@define
+class AmmoniaCostModelConfig:
+    """
+    Configuration inputs for the ammonia cost model, including plant capacity and
+    feedstock details.
+
+    Attributes:
+        plant_capacity_kgpy (float): Annual production capacity of the plant in kg.
+        plant_capacity_factor (float): The ratio of actual production to maximum
+            possible production over a year.
+        feedstocks (Feedstocks): An instance of the `Feedstocks` class detailing the
+            costs and consumption rates of resources used in production.
+    """
+
+    plant_capacity_kgpy: float
+    plant_capacity_factor: float
+    feedstocks: Feedstocks
+
+
+@define
+class AmmoniaCosts:
+    """
+    Base dataclass for calculated costs related to ammonia production, including
+    capital expenditures (CapEx) and operating expenditures (OpEx).
+
+    Attributes:
+        capex_air_separation_crygenic (float): Capital cost for air separation.
+        capex_haber_bosch (float): Capital cost for the Haber-Bosch process.
+        capex_boiler (float): Capital cost for boilers.
+        capex_cooling_tower (float): Capital cost for cooling towers.
+        capex_direct (float): Direct capital costs.
+        capex_depreciable_nonequipment (float): Depreciable non-equipment capital costs.
+        land_cost (float): Cost of land.
+        labor_cost (float): Annual labor cost.
+        general_administration_cost (float): Annual general and administrative cost.
+        property_tax_insurance (float): Annual property tax and insurance cost.
+        maintenance_cost (float): Annual maintenance cost.
+        total_fixed_operating_cost (float): Total annual fixed operating cost.
+        H2_cost_in_startup_year (float): Hydrogen cost in the startup year.
+        energy_cost_in_startup_year (float): Energy cost in the startup year.
+        non_energy_cost_in_startup_year (float): Non-energy cost in the startup year.
+        variable_cost_in_startup_year (float): Variable cost in the startup year.
+        credits_byproduct (float): Credits from byproducts.
+    """
+
+    # CapEx
+    capex_air_separation_crygenic: float
+    capex_haber_bosch: float
+    capex_boiler: float
+    capex_cooling_tower: float
+    capex_direct: float
+    capex_depreciable_nonequipment: float
+    land_cost: float
+
+    # Fixed OpEx
+    labor_cost: float
+    general_administration_cost: float
+    property_tax_insurance: float
+    maintenance_cost: float
+    total_fixed_operating_cost: float
+
+    # Feedstock and Byproduct costs
+    H2_cost_in_startup_year: float
+    energy_cost_in_startup_year: float
+    non_energy_cost_in_startup_year: float
+    variable_cost_in_startup_year: float
+    credits_byproduct: float
+
+
+@define
+class AmmoniaCostModelOutputs(AmmoniaCosts):
+    """
+    Outputs from the ammonia cost model, extending `AmmoniaCosts` with total capital
+    expenditure calculations.
+
+    Attributes:
+        capex_total (float): The total capital expenditure for the ammonia plant.
+    """
+
+    # CapEx
+    capex_total: float
+
+
+def run_ammonia_model(
+    plant_capacity_kgpy: float, plant_capacity_factor: float
+) -> float:
+    """
+    Calculates the annual ammonia production in kilograms based on the plant's
+    capacity and its capacity factor.
+
+    Args:
+        plant_capacity_kgpy (float): The plant's annual capacity in kilograms per year.
+        plant_capacity_factor (float): The capacity factor of the plant, a ratio of
+            its actual output over a period of time to its potential output if it
+            were possible for it to operate at full capacity continuously over the
+            same period.
+
+    Returns:
+        float: The calculated annual ammonia production in kilograms per year.
+    """
+    ammonia_production_kgpy = plant_capacity_kgpy * plant_capacity_factor
+
+    return ammonia_production_kgpy
+
+
+def run_ammonia_cost_model(config: AmmoniaCostModelConfig) -> AmmoniaCostModelOutputs:
+    """
+    Calculates the various costs associated with ammonia production, including
+    capital expenditures (CapEx), operating expenditures (OpEx), and credits from
+    byproducts, based on the provided configuration settings.
+
+    Args:
+        config (AmmoniaCostModelConfig): Configuration object containing all necessary
+            parameters for the cost calculation, including plant capacity, capacity
+            factor, and feedstock costs.
+
+    Returns:
+        AmmoniaCostModelOutputs: Object containing detailed breakdowns of calculated
+            costs, including total capital expenditure, operating costs, and credits
+            from byproducts.
+    """
+    feedstocks = config.feedstocks
+
+    model_year_CEPCI = 596.2  # TODO: what year
+    equation_year_CEPCI = 541.7  # TODO: what year
+
+    # scale with respect to a baseline plant (What is this?)
+    scaling_ratio = config.plant_capacity_kgpy / (365.0 * 1266638.4)
+
+    # -------------------------------CapEx Costs------------------------------
+    scaling_factor_equipment = 0.6
+
+    capex_scale_factor = scaling_ratio**scaling_factor_equipment
+    capex_air_separation_crygenic = (
+        model_year_CEPCI / equation_year_CEPCI * 22506100 * capex_scale_factor
+    )
+    capex_haber_bosch = (
+        model_year_CEPCI / equation_year_CEPCI * 18642800 * capex_scale_factor
+    )
+    capex_boiler = model_year_CEPCI / equation_year_CEPCI * 7069100 * capex_scale_factor
+    capex_cooling_tower = (
+        model_year_CEPCI / equation_year_CEPCI * 4799200 * capex_scale_factor
+    )
+    capex_direct = (
+        capex_air_separation_crygenic
+        + capex_haber_bosch
+        + capex_boiler
+        + capex_cooling_tower
+    )
+    capex_depreciable_nonequipment = (
+        capex_direct * 0.42 + 4112701.84103543 * scaling_ratio
+    )
+    capex_total = capex_direct + capex_depreciable_nonequipment
+    land_cost = capex_depreciable_nonequipment  # TODO: determine if this is the right method or the one in Fixed O&M costs
+
+    # -------------------------------Fixed O&M Costs------------------------------
+    scaling_factor_labor = 0.25
+    labor_cost = 57 * 50 * 2080 * scaling_ratio**scaling_factor_labor
+    general_administration_cost = labor_cost * 0.2
+    property_tax_insurance = capex_total * 0.02
+    maintenance_cost = capex_direct * 0.005 * scaling_ratio**scaling_factor_equipment
+    land_cost = 2500000 * capex_scale_factor
+    total_fixed_operating_cost = (
+        land_cost
+        + labor_cost
+        + general_administration_cost
+        + property_tax_insurance
+        + maintenance_cost
+    )
+
+    # -------------------------------Feedstock Costs------------------------------
+    H2_cost_in_startup_year = (
+        feedstocks.hydrogen_cost
+        * feedstocks.hydrogen_consumption
+        * config.plant_capacity_kgpy
+        * config.plant_capacity_factor
+    )
+    energy_cost_in_startup_year = (
+        feedstocks.electricity_cost
+        * feedstocks.electricity_consumption
+        * config.plant_capacity_kgpy
+        * config.plant_capacity_factor
+    )
+    non_energy_cost_in_startup_year = (
+        (
+            (feedstocks.cooling_water_cost * feedstocks.cooling_water_consumption)
+            + (
+                feedstocks.iron_based_catalyst_cost
+                * feedstocks.iron_based_catalyst_consumption
+            )
+        )
+        * config.plant_capacity_kgpy
+        * config.plant_capacity_factor
+    )
+    variable_cost_in_startup_year = (
+        energy_cost_in_startup_year + non_energy_cost_in_startup_year
+    )
+    # -------------------------------Byproduct Costs------------------------------
+    credits_byproduct = (
+        feedstocks.oxygen_cost
+        * feedstocks.oxygen_byproduct
+        * config.plant_capacity_kgpy
+        * config.plant_capacity_factor
+    )
+
+    return AmmoniaCostModelOutputs(
+        # Capex
+        capex_air_separation_crygenic=capex_air_separation_crygenic,
+        capex_haber_bosch=capex_haber_bosch,
+        capex_boiler=capex_boiler,
+        capex_cooling_tower=capex_cooling_tower,
+        capex_direct=capex_direct,
+        capex_depreciable_nonequipment=capex_depreciable_nonequipment,
+        capex_total=capex_total,
+        land_cost=land_cost,
+        # Fixed OpEx
+        labor_cost=labor_cost,
+        general_administration_cost=general_administration_cost,
+        property_tax_insurance=property_tax_insurance,
+        maintenance_cost=maintenance_cost,
+        total_fixed_operating_cost=total_fixed_operating_cost,
+        # Feedstock & Byproducts
+        H2_cost_in_startup_year=H2_cost_in_startup_year,
+        energy_cost_in_startup_year=energy_cost_in_startup_year,
+        non_energy_cost_in_startup_year=non_energy_cost_in_startup_year,
+        variable_cost_in_startup_year=variable_cost_in_startup_year,
+        credits_byproduct=credits_byproduct,
+    )
+
+
+@define
+class AmmoniaFinanceModelConfig:
+    """
+    Configuration for the financial model of an ammonia production plant, including
+    operational parameters, cost inputs, and financial assumptions.
+
+    Attributes:
+        plant_life (int): Expected operational life of the plant in years.
+        plant_capacity_kgpy (float): Annual production capacity of the plant in kilograms.
+        plant_capacity_factor (float): The fraction of the year that the plant operates
+            at full capacity.
+        grid_prices (Dict[str, float]): Electricity prices per kWh, indexed by year.
+        feedstocks (Feedstocks): Instance of `Feedstocks` detailing costs and consumption
+            rates of inputs.
+        costs (Union[AmmoniaCosts, AmmoniaCostModelOutputs]): Pre-calculated capital and
+            operating costs for the plant.
+        financial_assumptions (Dict[str, float]): Key financial metrics and assumptions
+            for the model, such as discount rate and inflation rate. Default is an
+            empty dict but should be populated with relevant values.
+        install_years (int): Number of years over which the plant is installed and
+            ramped up to full production, default is 3 years.
+        gen_inflation (float): General inflation rate, default is 0.0.
+    """
+
+    plant_life: int
+    plant_capacity_kgpy: float
+    plant_capacity_factor: float
+    grid_prices: Dict[str, float]
+    feedstocks: Feedstocks
+    costs: Union[AmmoniaCosts, AmmoniaCostModelOutputs]
+    financial_assumptions: Dict[str, float] = Factory(dict)
+    install_years: int = 3
+    gen_inflation: float = 0.0
+
+
+@define
+class AmmoniaFinanceModelOutputs:
+    """
+    Outputs from the financial model of an ammonia production plant, providing detailed
+    financial analysis and projections.
+
+    Attributes:
+        sol (dict): Solution to the financial model, containing key performance indicators
+            like Net Present Value (NPV), Internal Rate of Return (IRR), and payback
+            period.
+        summary (dict): Summary of the financial analysis, providing a high-level overview
+            of the plant's financial viability.
+        price_breakdown (pd.DataFrame): Detailed breakdown of costs contributing to the
+            production price of ammonia.
+        ammonia_price_breakdown (dict): Breakdown of the ammonia production cost into
+            component costs, showing the contribution of each cost element to the
+            overall production cost.
+    """
+
+    sol: dict
+    summary: dict
+    price_breakdown: pd.DataFrame
+
+
+def run_ammonia_finance_model(
+    config: AmmoniaFinanceModelConfig,
+) -> AmmoniaFinanceModelOutputs:
+    """
+    Executes the financial analysis for an ammonia production plant based on the
+    provided configuration settings. This analysis includes calculating the Net
+    Present Value (NPV), Internal Rate of Return (IRR), payback period, and
+    providing a detailed cost breakdown for producing ammonia.
+
+    This function leverages the configuration specified in `AmmoniaFinanceModelConfig`,
+    including plant operational parameters, grid prices, feedstock costs, pre-calculated
+    CapEx and OpEx, and financial assumptions to evaluate the financial performance of
+    the ammonia production facility.
+
+    Args:
+        config (AmmoniaFinanceModelConfig): Configuration object containing all the
+            necessary parameters for the financial analysis, including assumptions
+            and pre-calculated cost inputs.
+
+    Returns:
+        AmmoniaFinanceModelOutputs: An object containing the results of the financial
+            analysis. This includes a solution dictionary with key financial metrics,
+            a summary of the financial viability, a price breakdown of ammonia
+            production costs, and a detailed breakdown of how each cost component
+            contributes to the overall cost of ammonia.
+    """
+    feedstocks = config.feedstocks
+    costs = config.costs
+
+    # Set up ProFAST
+    pf = ProFAST.ProFAST("blank")
+
+    # apply all params passed through from config
+    for param, val in config.financial_assumptions.items():
+        pf.set_params(param, val)
+
+    analysis_start = int(list(config.grid_prices.keys())[0]) - config.install_years
+
+    # Fill these in - can have most of them as 0 also
+    pf.set_params(
+        "commodity",
+        {
+            "name": "Ammonia",
+            "unit": "kg",
+            "initial price": 1000,
+            "escalation": config.gen_inflation,
+        },
+    )
+    pf.set_params("capacity", config.plant_capacity_kgpy / 365)  # units/day
+    pf.set_params(
+        "maintenance",
+        {"value": 0, "escalation": config.gen_inflation},
+    )
+    pf.set_params("analysis start year", analysis_start)
+    pf.set_params("operating life", config.plant_life)
+    pf.set_params("installation months", 12 * config.install_years)
+    pf.set_params(
+        "installation cost",
+        {
+            "value": costs.total_fixed_operating_cost,
+            "depr type": "Straight line",
+            "depr period": 4,
+            "depreciable": False,
+        },
+    )
+    pf.set_params("non depr assets", costs.land_cost)
+    pf.set_params(
+        "end of proj sale non depr assets",
+        costs.land_cost * (1 + config.gen_inflation) ** config.plant_life,
+    )
+    pf.set_params("demand rampup", 0)
+    pf.set_params("long term utilization", config.plant_capacity_factor)
+    pf.set_params("credit card fees", 0)
+    pf.set_params("sales tax", 0)
+    pf.set_params(
+        "license and permit", {"value": 00, "escalation": config.gen_inflation}
+    )
+    pf.set_params("rent", {"value": 0, "escalation": config.gen_inflation})
+    pf.set_params("property tax and insurance", 0)
+    pf.set_params("admin expense", 0)
+    pf.set_params("sell undepreciated cap", True)
+    pf.set_params("tax losses monetized", True)
+    pf.set_params("general inflation rate", config.gen_inflation)
+    pf.set_params("debt type", "Revolving debt")
+    pf.set_params("cash onhand", 1)
+
+    # ----------------------------------- Add capital items to ProFAST ----------------
+    pf.add_capital_item(
+        name="Air Separation by Cryogenic",
+        cost=costs.capex_air_separation_crygenic,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Haber Bosch",
+        cost=costs.capex_haber_bosch,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Boiler and Steam Turbine",
+        cost=costs.capex_boiler,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Cooling Tower",
+        cost=costs.capex_cooling_tower,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Depreciable Nonequipment",
+        cost=costs.capex_depreciable_nonequipment,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+
+    # -------------------------------------- Add fixed costs--------------------------------
+    pf.add_fixed_cost(
+        name="Labor Cost",
+        usage=1,
+        unit="$/year",
+        cost=costs.labor_cost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Maintenance Cost",
+        usage=1,
+        unit="$/year",
+        cost=costs.maintenance_cost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Administrative Expense",
+        usage=1,
+        unit="$/year",
+        cost=costs.general_administration_cost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Property tax and insurance",
+        usage=1,
+        unit="$/year",
+        cost=costs.property_tax_insurance,
+        escalation=0.0,
+    )
+
+    # ---------------------- Add feedstocks, note the various cost options-------------------
+    pf.add_feedstock(
+        name="Hydrogen",
+        usage=feedstocks.hydrogen_consumption,
+        unit="kilogram of hydrogen per kilogram of ammonia",
+        cost=feedstocks.hydrogen_cost,
+        escalation=config.gen_inflation,
+    )
+
+    pf.add_feedstock(
+        name="Electricity",
+        usage=feedstocks.electricity_consumption,
+        unit="MWh per kilogram of ammonia",
+        cost=config.grid_prices,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Cooling water",
+        usage=feedstocks.cooling_water_consumption,
+        unit="Gallon per kilogram of ammonia",
+        cost=feedstocks.cooling_water_cost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Iron based catalyst",
+        usage=feedstocks.iron_based_catalyst_consumption,
+        unit="kilogram of catalyst per kilogram of ammonia",
+        cost=feedstocks.iron_based_catalyst_cost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_coproduct(
+        name="Oxygen byproduct",
+        usage=feedstocks.oxygen_byproduct,
+        unit="kilogram of oxygen per kilogram of ammonia",
+        cost=feedstocks.oxygen_cost,
+        escalation=config.gen_inflation,
+    )
+
+    # ------------------------------ Set up outputs ---------------------------
+
+    sol = pf.solve_price()
+    summary = pf.get_summary_vals()
+    price_breakdown = pf.get_cost_breakdown()
+
+    return AmmoniaFinanceModelOutputs(
+        sol=sol,
+        summary=summary,
+        price_breakdown=price_breakdown,
+    )
diff --git a/greenheart/simulation/technologies/steel.py b/greenheart/simulation/technologies/steel.py
new file mode 100644
index 000000000..e2c2c0623
--- /dev/null
+++ b/greenheart/simulation/technologies/steel.py
@@ -0,0 +1,766 @@
+from typing import Dict, Union
+
+import ProFAST
+import pandas as pd
+from attrs import define, Factory
+
+
+@define
+class Feedstocks:
+    """
+    Represents the consumption rates and costs of various feedstocks used in steel
+    production.
+
+    Attributes:
+        natural_gas_prices (Dict[str, float]):
+            Natural gas costs, indexed by year ($/GJ).
+        excess_oxygen (float): Excess oxygen produced (kgO2), default = 395.
+        lime_unitcost (float): Cost per metric tonne of lime ($/metric tonne).
+        carbon_unitcost (float): Cost per metric tonne of carbon ($/metric tonne).
+        electricity_cost (float):
+            Electricity cost per metric tonne of steel production ($/metric tonne).
+        iron_ore_pellet_unitcost (float):
+            Cost per metric tonne of iron ore ($/metric tonne).
+        oxygen_market_price (float):
+            Market price per kg of oxygen ($/kgO2).
+        raw_water_unitcost (float):
+            Cost per metric tonne of raw water ($/metric tonne).
+        iron_ore_consumption (float):
+            Iron ore consumption per metric tonne of steel production (metric tonnes).
+        raw_water_consumption (float):
+            Raw water consumption per metric tonne of steel production (metric tonnes).
+        lime_consumption (float):
+            Lime consumption per metric tonne of steel production (metric tonnes).
+        carbon_consumption (float):
+            Carbon consumption per metric tonne of steel production (metric tonnes).
+        hydrogen_consumption (float):
+            Hydrogen consumption per metric tonne of steel production (metric tonnes).
+        natural_gas_consumption (float):
+            Natural gas consumption per metric tonne of steel production (GJ-LHV).
+        electricity_consumption (float):
+            Electricity consumption per metric tonne of steel production (MWh).
+        slag_disposal_unitcost (float):
+            Cost per metric tonne of slag disposal ($/metric tonne).
+        slag_production (float):
+            Slag production per metric tonne of steel production (metric tonnes).
+        maintenance_materials_unitcost (float):
+            Cost per metric tonne of annual steel slab production at real capacity
+            factor ($/metric tonne).
+    """
+
+    natural_gas_prices: Dict[str, float]
+    excess_oxygen: float = 395
+    lime_unitcost: float = 122.1
+    carbon_unitcost: float = 236.97
+    electricity_cost: float = 48.92
+    iron_ore_pellet_unitcost: float = 207.35
+    oxygen_market_price: float = 0.03
+    raw_water_unitcost: float = 0.59289
+    iron_ore_consumption: float = 1.62927
+    raw_water_consumption: float = 0.80367
+    lime_consumption: float = 0.01812
+    carbon_consumption: float = 0.0538
+    hydrogen_consumption: float = 0.06596
+    natural_gas_consumption: float = 0.71657
+    electricity_consumption: float = 0.5502
+    slag_disposal_unitcost: float = 37.63
+    slag_production: float = 0.17433
+    maintenance_materials_unitcost: float = 7.72
+
+
+@define
+class SteelCostModelConfig:
+    """
+    Configuration for the steel cost model, including operational parameters and
+    feedstock costs.
+
+    Attributes:
+        operational_year (int): The year of operation for cost estimation.
+        plant_capacity_mtpy (float): Plant capacity in metric tons per year.
+        lcoh (float): Levelized cost of hydrogen ($/kg).
+        feedstocks (Feedstocks):
+            An instance of the Feedstocks class containing feedstock consumption
+            rates and costs.
+        o2_heat_integration (bool):
+            Indicates whether oxygen and heat integration is used, affecting preheating
+            CapEx, cooling CapEx, and oxygen sales. Default is True.
+        co2_fuel_emissions (float):
+            CO2 emissions from fuel per metric tonne of steel production.
+        co2_carbon_emissions (float):
+            CO2 emissions from carbon per metric tonne of steel production.
+        surface_water_discharge (float):
+            Surface water discharge per metric tonne of steel production.
+    """
+
+    operational_year: int
+    plant_capacity_mtpy: float
+    lcoh: float
+    feedstocks: Feedstocks
+    o2_heat_integration: bool = True
+    co2_fuel_emissions: float = 0.03929
+    co2_carbon_emissions: float = 0.17466
+    surface_water_discharge: float = 0.42113
+
+
+@define
+class SteelCosts:
+    """
+    Base dataclass for calculated steel costs.
+
+    Attributes:
+        capex_eaf_casting (float):
+            Capital expenditure for electric arc furnace and casting.
+        capex_shaft_furnace (float): Capital expenditure for shaft furnace.
+        capex_oxygen_supply (float): Capital expenditure for oxygen supply.
+        capex_h2_preheating (float): Capital expenditure for hydrogen preheating.
+        capex_cooling_tower (float): Capital expenditure for cooling tower.
+        capex_piping (float): Capital expenditure for piping.
+        capex_elec_instr (float):
+            Capital expenditure for electrical and instrumentation.
+        capex_buildings_storage_water (float):
+            Capital expenditure for buildings, storage, and water service.
+        capex_misc (float):
+            Capital expenditure for miscellaneous items.
+        labor_cost_annual_operation (float): Annual operating labor cost.
+        labor_cost_maintenance (float): Maintenance labor cost.
+        labor_cost_admin_support (float): Administrative and support labor cost.
+        property_tax_insurance (float): Cost for property tax and insurance.
+        land_cost (float): Cost of land.
+        installation_cost (float): Cost of installation.
+
+    Note:
+        These represent the minimum set of required cost data for
+        `run_steel_finance_model`, as well as base data for `SteelCostModelOutputs`.
+    """
+
+    capex_eaf_casting: float
+    capex_shaft_furnace: float
+    capex_oxygen_supply: float
+    capex_h2_preheating: float
+    capex_cooling_tower: float
+    capex_piping: float
+    capex_elec_instr: float
+    capex_buildings_storage_water: float
+    capex_misc: float
+    labor_cost_annual_operation: float
+    labor_cost_maintenance: float
+    labor_cost_admin_support: float
+    property_tax_insurance: float
+    land_cost: float
+    installation_cost: float
+
+
+@define
+class SteelCostModelOutputs(SteelCosts):
+    """
+    Outputs of the steel cost model, extending the SteelCosts data with total
+    cost calculations and specific cost components related to the operation and
+    installation of a steel production plant.
+
+    Attributes:
+        total_plant_cost (float):
+            The total capital expenditure (CapEx) for the steel plant.
+        total_fixed_operating_cost (float):
+            The total annual operating expenditure (OpEx), including labor,
+            maintenance, administrative support, and property tax/insurance.
+        labor_cost_fivemonth (float):
+            Cost of labor for the first five months of operation, often used in startup
+            cost calculations.
+        maintenance_materials_onemonth (float):
+            Cost of maintenance materials for one month of operation.
+        non_fuel_consumables_onemonth (float):
+            Cost of non-fuel consumables for one month of operation.
+        waste_disposal_onemonth (float):
+            Cost of waste disposal for one month of operation.
+        monthly_energy_cost (float):
+            Cost of energy (electricity, natural gas, etc.) for one month of operation.
+        spare_parts_cost (float):
+            Cost of spare parts as part of the initial investment.
+        misc_owners_costs (float):
+            Miscellaneous costs incurred by the owner, including but not limited to,
+            initial supply stock, safety equipment, and initial training programs.
+    """
+
+    total_plant_cost: float
+    total_fixed_operating_cost: float
+    labor_cost_fivemonth: float
+    maintenance_materials_onemonth: float
+    non_fuel_consumables_onemonth: float
+    waste_disposal_onemonth: float
+    monthly_energy_cost: float
+    spare_parts_cost: float
+    misc_owners_costs: float
+
+
+def run_steel_model(plant_capacity_mtpy: float, plant_capacity_factor: float) -> float:
+    """
+    Calculates the annual steel production in metric tons based on plant capacity and
+    capacity factor.
+
+    Args:
+        plant_capacity_mtpy (float):
+            The plant's annual capacity in metric tons per year.
+        plant_capacity_factor (float):
+            The capacity factor of the plant.
+
+    Returns:
+        float: The calculated annual steel production in metric tons per year.
+    """
+    steel_production_mtpy = plant_capacity_mtpy * plant_capacity_factor
+
+    return steel_production_mtpy
+
+
+def run_steel_cost_model(config: SteelCostModelConfig) -> SteelCostModelOutputs:
+    """
+    Calculates the capital expenditure (CapEx) and operating expenditure (OpEx) for
+    a steel manufacturing plant based on the provided configuration.
+
+    Args:
+        config (SteelCostModelConfig):
+            Configuration object containing all necessary parameters for the cost
+            model, including plant capacity, feedstock costs, and integration options
+            for oxygen and heat.
+
+    Returns:
+        SteelCostModelOutputs: An object containing detailed breakdowns of capital and
+        operating costs, as well as total plant cost and other financial metrics.
+
+    Note:
+        The calculation includes various cost components such as electric arc furnace
+        (EAF) casting, shaft furnace, oxygen supply, hydrogen preheating, cooling tower,
+        and more, adjusted based on the Chemical Engineering Plant Cost Index (CEPCI).
+    """
+    feedstocks = config.feedstocks
+
+    model_year_CEPCI = 596.2
+    equation_year_CEPCI = 708.8
+
+    capex_eaf_casting = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 352191.5237
+        * config.plant_capacity_mtpy**0.456
+    )
+    capex_shaft_furnace = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 489.68061
+        * config.plant_capacity_mtpy**0.88741
+    )
+    capex_oxygen_supply = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 1715.21508
+        * config.plant_capacity_mtpy**0.64574
+    )
+    if config.o2_heat_integration:
+        capex_h2_preheating = (
+            model_year_CEPCI
+            / equation_year_CEPCI
+            * (1 - 0.4)
+            * (45.69123 * config.plant_capacity_mtpy**0.86564)
+        )  # Optimistic ballpark estimate of 60% reduction in preheating
+        capex_cooling_tower = (
+            model_year_CEPCI
+            / equation_year_CEPCI
+            * (1 - 0.3)
+            * (2513.08314 * config.plant_capacity_mtpy**0.63325)
+        )  # Optimistic ballpark estimate of 30% reduction in cooling
+    else:
+        capex_h2_preheating = (
+            model_year_CEPCI
+            / equation_year_CEPCI
+            * 45.69123
+            * config.plant_capacity_mtpy**0.86564
+        )
+        capex_cooling_tower = (
+            model_year_CEPCI
+            / equation_year_CEPCI
+            * 2513.08314
+            * config.plant_capacity_mtpy**0.63325
+        )
+    capex_piping = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 11815.72718
+        * config.plant_capacity_mtpy**0.59983
+    )
+    capex_elec_instr = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 7877.15146
+        * config.plant_capacity_mtpy**0.59983
+    )
+    capex_buildings_storage_water = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 1097.81876
+        * config.plant_capacity_mtpy**0.8
+    )
+    capex_misc = (
+        model_year_CEPCI
+        / equation_year_CEPCI
+        * 7877.1546
+        * config.plant_capacity_mtpy**0.59983
+    )
+
+    total_plant_cost = (
+        capex_eaf_casting
+        + capex_shaft_furnace
+        + capex_oxygen_supply
+        + capex_h2_preheating
+        + capex_cooling_tower
+        + capex_piping
+        + capex_elec_instr
+        + capex_buildings_storage_water
+        + capex_misc
+    )
+
+    # -------------------------------Fixed O&M Costs------------------------------
+
+    labor_cost_annual_operation = (
+        69375996.9
+        * ((config.plant_capacity_mtpy / 365 * 1000) ** 0.25242)
+        / ((1162077 / 365 * 1000) ** 0.25242)
+    )
+    labor_cost_maintenance = 0.00863 * total_plant_cost
+    labor_cost_admin_support = 0.25 * (
+        labor_cost_annual_operation + labor_cost_maintenance
+    )
+
+    property_tax_insurance = 0.02 * total_plant_cost
+
+    total_fixed_operating_cost = (
+        labor_cost_annual_operation
+        + labor_cost_maintenance
+        + labor_cost_admin_support
+        + property_tax_insurance
+    )
+
+    # ---------------------- Owner's (Installation) Costs --------------------------
+    labor_cost_fivemonth = (
+        5
+        / 12
+        * (
+            labor_cost_annual_operation
+            + labor_cost_maintenance
+            + labor_cost_admin_support
+        )
+    )
+
+    maintenance_materials_onemonth = (
+        feedstocks.maintenance_materials_unitcost * config.plant_capacity_mtpy / 12
+    )
+    non_fuel_consumables_onemonth = (
+        config.plant_capacity_mtpy
+        * (
+            feedstocks.raw_water_consumption * feedstocks.raw_water_unitcost
+            + feedstocks.lime_consumption * feedstocks.lime_unitcost
+            + feedstocks.carbon_consumption * feedstocks.carbon_unitcost
+            + feedstocks.iron_ore_consumption * feedstocks.iron_ore_pellet_unitcost
+        )
+        / 12
+    )
+
+    waste_disposal_onemonth = (
+        config.plant_capacity_mtpy
+        * feedstocks.slag_disposal_unitcost
+        * feedstocks.slag_production
+        / 12
+    )
+
+    monthly_energy_cost = (
+        config.plant_capacity_mtpy
+        * (
+            feedstocks.hydrogen_consumption * config.lcoh * 1000
+            + feedstocks.natural_gas_consumption
+            * feedstocks.natural_gas_prices[str(config.operational_year)]
+            + feedstocks.electricity_consumption * feedstocks.electricity_cost
+        )
+        / 12
+    )
+    two_percent_tpc = 0.02 * total_plant_cost
+
+    fuel_consumables_60day_supply_cost = (
+        config.plant_capacity_mtpy
+        * (
+            feedstocks.raw_water_consumption * feedstocks.raw_water_unitcost
+            + feedstocks.lime_consumption * feedstocks.lime_unitcost
+            + feedstocks.carbon_consumption * feedstocks.carbon_unitcost
+            + feedstocks.iron_ore_consumption * feedstocks.iron_ore_pellet_unitcost
+        )
+        / 365
+        * 60
+    )
+
+    spare_parts_cost = 0.005 * total_plant_cost
+    land_cost = 0.775 * config.plant_capacity_mtpy
+    misc_owners_costs = 0.15 * total_plant_cost
+
+    installation_cost = (
+        labor_cost_fivemonth
+        + two_percent_tpc
+        + fuel_consumables_60day_supply_cost
+        + spare_parts_cost
+        + misc_owners_costs
+    )
+
+    return SteelCostModelOutputs(
+        # CapEx
+        capex_eaf_casting=capex_eaf_casting,
+        capex_shaft_furnace=capex_shaft_furnace,
+        capex_oxygen_supply=capex_oxygen_supply,
+        capex_h2_preheating=capex_h2_preheating,
+        capex_cooling_tower=capex_cooling_tower,
+        capex_piping=capex_piping,
+        capex_elec_instr=capex_elec_instr,
+        capex_buildings_storage_water=capex_buildings_storage_water,
+        capex_misc=capex_misc,
+        total_plant_cost=total_plant_cost,
+        # Fixed OpEx
+        labor_cost_annual_operation=labor_cost_annual_operation,
+        labor_cost_maintenance=labor_cost_maintenance,
+        labor_cost_admin_support=labor_cost_admin_support,
+        property_tax_insurance=property_tax_insurance,
+        total_fixed_operating_cost=total_fixed_operating_cost,
+        # Owner's Installation costs
+        labor_cost_fivemonth=labor_cost_fivemonth,
+        maintenance_materials_onemonth=maintenance_materials_onemonth,
+        non_fuel_consumables_onemonth=non_fuel_consumables_onemonth,
+        waste_disposal_onemonth=waste_disposal_onemonth,
+        monthly_energy_cost=monthly_energy_cost,
+        spare_parts_cost=spare_parts_cost,
+        land_cost=land_cost,
+        misc_owners_costs=misc_owners_costs,
+        installation_cost=installation_cost,
+    )
+
+
+@define
+class SteelFinanceModelConfig:
+    """
+    Configuration for the steel finance model, including plant characteristics, financial assumptions, and cost inputs.
+
+    Attributes:
+        plant_life (int): The operational lifetime of the plant in years.
+        plant_capacity_mtpy (float): Plant capacity in metric tons per year.
+        plant_capacity_factor (float):
+            The fraction of the year the plant operates at full capacity.
+        steel_production_mtpy (float): Annual steel production in metric tons.
+        lcoh (float): Levelized cost of hydrogen.
+        grid_prices (Dict[str, float]): Electricity prices per unit.
+        feedstocks (Feedstocks):
+            The feedstocks required for steel production, including types and costs.
+        costs (Union[SteelCosts, SteelCostModelOutputs]):
+            Calculated CapEx and OpEx costs.
+        o2_heat_integration (bool): Indicates if oxygen and heat integration is used.
+        financial_assumptions (Dict[str, float]):
+            Financial assumptions for model calculations.
+        install_years (int): The number of years over which the plant is installed.
+        gen_inflation (float): General inflation rate.
+    """
+
+    plant_life: int
+    plant_capacity_mtpy: float
+    plant_capacity_factor: float
+    steel_production_mtpy: float
+    lcoh: float
+    grid_prices: Dict[str, float]
+    feedstocks: Feedstocks
+    costs: Union[SteelCosts, SteelCostModelOutputs]
+    o2_heat_integration: bool = True
+    financial_assumptions: Dict[str, float] = Factory(dict)
+    install_years: int = 3
+    gen_inflation: float = 0.00
+
+
+@define
+class SteelFinanceModelOutputs:
+    """
+    Represents the outputs of the steel finance model, encapsulating the results of financial analysis for steel production.
+
+    Attributes:
+        sol (dict):
+            A dictionary containing the solution to the financial model, including key
+            financial indicators such as NPV (Net Present Value), IRR (Internal Rate of
+            Return), and breakeven price.
+        summary (dict):
+            A summary of key results from the financial analysis, providing a
+            high-level overview of financial metrics and performance indicators.
+        price_breakdown (pd.DataFrame):
+            A Pandas DataFrame detailing the cost breakdown for producing steel,
+            including both capital and operating expenses, as well as the impact of
+            various cost factors on the overall price of steel.
+    """
+
+    sol: dict
+    summary: dict
+    price_breakdown: pd.DataFrame
+
+
+def run_steel_finance_model(
+    config: SteelFinanceModelConfig,
+) -> SteelFinanceModelOutputs:
+    """
+    Executes the financial model for steel production, calculating the breakeven price
+    of steel and other financial metrics based on the provided configuration and cost
+    models.
+
+    This function integrates various cost components, including capital expenditures
+    (CapEx), operating expenses (OpEx), and owner's costs. It leverages the ProFAST
+    financial analysis software framework.
+
+    Args:
+        config (SteelFinanceModelConfig):
+            Configuration object containing all necessary parameters and assumptions
+            for the financial model, including plant characteristics, cost inputs,
+            financial assumptions, and grid prices.
+
+    Returns:
+        SteelFinanceModelOutputs:
+            Object containing detailed financial analysis results, including solution
+            metrics, summary values, price breakdown, and steel price breakdown per
+            tonne. This output is instrumental in assessing the financial performance
+            and breakeven price for the steel production facility.
+    """
+
+    feedstocks = config.feedstocks
+    costs = config.costs
+
+    # Set up ProFAST
+    pf = ProFAST.ProFAST("blank")
+
+    # apply all params passed through from config
+    for param, val in config.financial_assumptions.items():
+        print(f"setting {param}: {val}")
+        pf.set_params(param, val)
+
+    analysis_start = int(list(config.grid_prices.keys())[0]) - config.install_years
+
+    # Fill these in - can have most of them as 0 also
+    pf.set_params(
+        "commodity",
+        {
+            "name": "Steel",
+            "unit": "metric tonnes",
+            "initial price": 1000,
+            "escalation": config.gen_inflation,
+        },
+    )
+    pf.set_params("capacity", config.plant_capacity_mtpy / 365)  # units/day
+    pf.set_params("maintenance", {"value": 0, "escalation": config.gen_inflation})
+    pf.set_params("analysis start year", analysis_start)
+    pf.set_params("operating life", config.plant_life)
+    pf.set_params("installation months", 12 * config.install_years)
+    pf.set_params(
+        "installation cost",
+        {
+            "value": costs.installation_cost,
+            "depr type": "Straight line",
+            "depr period": 4,
+            "depreciable": False,
+        },
+    )
+    pf.set_params("non depr assets", costs.land_cost)
+    pf.set_params(
+        "end of proj sale non depr assets",
+        costs.land_cost * (1 + config.gen_inflation) ** config.plant_life,
+    )
+    pf.set_params("demand rampup", 5.3)
+    pf.set_params("long term utilization", config.plant_capacity_factor)
+    pf.set_params("credit card fees", 0)
+    pf.set_params("sales tax", 0)
+    pf.set_params(
+        "license and permit", {"value": 00, "escalation": config.gen_inflation}
+    )
+    pf.set_params("rent", {"value": 0, "escalation": config.gen_inflation})
+    pf.set_params("property tax and insurance", 0)
+    pf.set_params("admin expense", 0)
+    pf.set_params("sell undepreciated cap", True)
+    pf.set_params("tax losses monetized", True)
+    pf.set_params("general inflation rate", config.gen_inflation)
+    pf.set_params("debt type", "Revolving debt")
+    pf.set_params("cash onhand", 1)
+
+    # ----------------------------------- Add capital items to ProFAST ----------------
+    pf.add_capital_item(
+        name="EAF & Casting",
+        cost=costs.capex_eaf_casting,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Shaft Furnace",
+        cost=costs.capex_shaft_furnace,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Oxygen Supply",
+        cost=costs.capex_oxygen_supply,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="H2 Pre-heating",
+        cost=costs.capex_h2_preheating,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Cooling Tower",
+        cost=costs.capex_cooling_tower,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Piping",
+        cost=costs.capex_piping,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Electrical & Instrumentation",
+        cost=costs.capex_elec_instr,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Buildings, Storage, Water Service",
+        cost=costs.capex_buildings_storage_water,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+    pf.add_capital_item(
+        name="Other Miscellaneous Costs",
+        cost=costs.capex_misc,
+        depr_type="MACRS",
+        depr_period=7,
+        refurb=[0],
+    )
+
+    # -------------------------------------- Add fixed costs--------------------------------
+    pf.add_fixed_cost(
+        name="Annual Operating Labor Cost",
+        usage=1,
+        unit="$/year",
+        cost=costs.labor_cost_annual_operation,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Maintenance Labor Cost",
+        usage=1,
+        unit="$/year",
+        cost=costs.labor_cost_maintenance,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Administrative & Support Labor Cost",
+        usage=1,
+        unit="$/year",
+        cost=costs.labor_cost_admin_support,
+        escalation=config.gen_inflation,
+    )
+    pf.add_fixed_cost(
+        name="Property tax and insurance",
+        usage=1,
+        unit="$/year",
+        cost=costs.property_tax_insurance,
+        escalation=0.0,
+    )
+    # Putting property tax and insurance here to zero out depcreciation/escalation. Could instead put it in set_params if
+    # we think that is more accurate
+
+    # ---------------------- Add feedstocks, note the various cost options-------------------
+    pf.add_feedstock(
+        name="Maintenance Materials",
+        usage=1.0,
+        unit="Units per metric tonne of steel",
+        cost=feedstocks.maintenance_materials_unitcost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Raw Water Withdrawal",
+        usage=feedstocks.raw_water_consumption,
+        unit="metric tonnes of water per metric tonne of steel",
+        cost=feedstocks.raw_water_unitcost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Lime",
+        usage=feedstocks.lime_consumption,
+        unit="metric tonnes of lime per metric tonne of steel",
+        cost=feedstocks.lime_unitcost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Carbon",
+        usage=feedstocks.carbon_consumption,
+        unit="metric tonnes of carbon per metric tonne of steel",
+        cost=feedstocks.carbon_unitcost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Iron Ore",
+        usage=feedstocks.iron_ore_consumption,
+        unit="metric tonnes of iron ore per metric tonne of steel",
+        cost=feedstocks.iron_ore_pellet_unitcost,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Hydrogen",
+        usage=feedstocks.hydrogen_consumption,
+        unit="metric tonnes of hydrogen per metric tonne of steel",
+        cost=config.lcoh * 1000,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Natural Gas",
+        usage=feedstocks.natural_gas_consumption,
+        unit="GJ-LHV per metric tonne of steel",
+        cost=feedstocks.natural_gas_prices,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Electricity",
+        usage=feedstocks.electricity_consumption,
+        unit="MWh per metric tonne of steel",
+        cost=config.grid_prices,
+        escalation=config.gen_inflation,
+    )
+    pf.add_feedstock(
+        name="Slag Disposal",
+        usage=feedstocks.slag_production,
+        unit="metric tonnes of slag per metric tonne of steel",
+        cost=feedstocks.slag_disposal_unitcost,
+        escalation=config.gen_inflation,
+    )
+
+    pf.add_coproduct(
+        name="Oxygen sales",
+        usage=feedstocks.excess_oxygen,
+        unit="kg O2 per metric tonne of steel",
+        cost=feedstocks.oxygen_market_price,
+        escalation=config.gen_inflation,
+    )
+
+    # ------------------------------ Set up outputs ---------------------------
+
+    sol = pf.solve_price()
+    summary = pf.get_summary_vals()
+    price_breakdown = pf.get_cost_breakdown()
+
+    return SteelFinanceModelOutputs(
+        sol=sol,
+        summary=summary,
+        price_breakdown=price_breakdown,
+    )


Test patch:
diff --git a/tests/greenheart/test_ammonia.py b/tests/greenheart/test_ammonia.py
new file mode 100644
index 000000000..83be7af0e
--- /dev/null
+++ b/tests/greenheart/test_ammonia.py
@@ -0,0 +1,112 @@
+from pytest import approx
+
+from greenheart.simulation.technologies.ammonia import ammonia
+
+
+def test_run_ammonia_model():
+    capacity = 100.0
+    capacity_factor = 0.9
+
+    ammonia_production_kgpy = ammonia.run_ammonia_model(capacity, capacity_factor)
+
+    assert ammonia_production_kgpy == 90
+
+
+def test_ammonia_cost_model(subtests):
+    config = ammonia.AmmoniaCostModelConfig(
+        plant_capacity_kgpy=362560672.27155423,
+        plant_capacity_factor=0.9,
+        feedstocks=ammonia.Feedstocks(
+            electricity_cost=89.42320514456621,
+            hydrogen_cost=4.2986685034417045,
+            cooling_water_cost=0.00291,
+            iron_based_catalyst_cost=23.19977341,
+            oxygen_cost=0,
+        ),
+    )
+
+    res: ammonia.AmmoniaCostModelOutputs = ammonia.run_ammonia_cost_model(config)
+
+    with subtests.test("Total CapEx"):
+        assert res.capex_total == approx(74839480.74961768)
+    with subtests.test("Fixed OpEx"):
+        assert res.total_fixed_operating_cost == approx(10569658.376900101)
+    with subtests.test("Variable costs"):
+        assert res.variable_cost_in_startup_year == approx(4259805.969069265)
+    with subtests.test("Land costs"):
+        assert res.land_cost == approx(2160733.0556864925)
+
+
+def test_ammonia_finance_model():
+    # Parameter -> Hydrogen/Steel/Ammonia
+    financial_assumptions = {
+        "total income tax rate": 0.2574,
+        "capital gains tax rate": 0.15,
+        "leverage after tax nominal discount rate": 0.10893,
+        "debt equity ratio of initial financing": 0.624788,
+        "debt interest rate": 0.050049,
+    }
+
+    cost_config = ammonia.AmmoniaCostModelConfig(
+        plant_capacity_kgpy=362560672.27155423,
+        plant_capacity_factor=0.9,
+        feedstocks=ammonia.Feedstocks(
+            electricity_cost=89.42320514456621,
+            hydrogen_cost=4.2986685034417045,
+            cooling_water_cost=0.00291,
+            iron_based_catalyst_cost=23.19977341,
+            oxygen_cost=0,
+        ),
+    )
+
+    costs: ammonia.AmmoniaCostModelOutputs = ammonia.run_ammonia_cost_model(cost_config)
+
+    plant_capacity_kgpy = 362560672.27155423
+    plant_capacity_factor = 0.9
+
+    config = ammonia.AmmoniaFinanceModelConfig(
+        plant_life=30,
+        plant_capacity_kgpy=plant_capacity_kgpy,
+        plant_capacity_factor=plant_capacity_factor,
+        feedstocks=cost_config.feedstocks,
+        grid_prices={
+            "2035": 89.42320514456621,
+            "2036": 89.97947569251141,
+            "2037": 90.53574624045662,
+            "2038": 91.09201678840184,
+            "2039": 91.64828733634704,
+            "2040": 92.20455788429224,
+            "2041": 89.87291235917809,
+            "2042": 87.54126683406393,
+            "2043": 85.20962130894978,
+            "2044": 82.87797578383562,
+            "2045": 80.54633025872147,
+            "2046": 81.38632144593608,
+            "2047": 82.22631263315068,
+            "2048": 83.0663038203653,
+            "2049": 83.90629500757991,
+            "2050": 84.74628619479452,
+            "2051": 84.74628619479452,
+            "2052": 84.74628619479452,
+            "2053": 84.74628619479452,
+            "2054": 84.74628619479452,
+            "2055": 84.74628619479452,
+            "2056": 84.74628619479452,
+            "2057": 84.74628619479452,
+            "2058": 84.74628619479452,
+            "2059": 84.74628619479452,
+            "2060": 84.74628619479452,
+            "2061": 84.74628619479452,
+            "2062": 84.74628619479452,
+            "2063": 84.74628619479452,
+            "2064": 84.74628619479452,
+        },
+        financial_assumptions=financial_assumptions,
+        costs=costs,
+    )
+
+    lcoa_expected = 0.9322866176899477
+
+    res: ammonia.AmmoniaFinanceModelOutputs = ammonia.run_ammonia_finance_model(config)
+
+    assert res.sol.get("price") == lcoa_expected
diff --git a/tests/greenheart/test_steel.py b/tests/greenheart/test_steel.py
new file mode 100644
index 000000000..3b36f898a
--- /dev/null
+++ b/tests/greenheart/test_steel.py
@@ -0,0 +1,139 @@
+from pytest import approx, fixture
+
+from greenheart.simulation.technologies import steel
+
+ng_prices_dict = {
+    "2035": 3.76232,
+    "2036": 3.776032,
+    "2037": 3.812906,
+    "2038": 3.9107960000000004,
+    "2039": 3.865776,
+    "2040": 3.9617400000000003,
+    "2041": 4.027136,
+    "2042": 4.017166,
+    "2043": 3.9715339999999997,
+    "2044": 3.924314,
+    "2045": 3.903287,
+    "2046": 3.878192,
+    "2047": 3.845413,
+    "2048": 3.813366,
+    "2049": 3.77735,
+    "2050": 3.766164,
+    "2051": 3.766164,
+    "2052": 3.766164,
+    "2053": 3.766164,
+    "2054": 3.766164,
+    "2055": 3.766164,
+    "2056": 3.766164,
+    "2057": 3.766164,
+    "2058": 3.766164,
+    "2059": 3.766164,
+    "2060": 3.766164,
+    "2061": 3.766164,
+    "2062": 3.766164,
+    "2063": 3.766164,
+    "2064": 3.766164,
+}
+grid_prices_dict = {
+    "2035": 89.42320514456621,
+    "2036": 89.97947569251141,
+    "2037": 90.53574624045662,
+    "2038": 91.09201678840184,
+    "2039": 91.64828733634704,
+    "2040": 92.20455788429224,
+    "2041": 89.87291235917809,
+    "2042": 87.54126683406393,
+    "2043": 85.20962130894978,
+    "2044": 82.87797578383562,
+    "2045": 80.54633025872147,
+    "2046": 81.38632144593608,
+    "2047": 82.22631263315068,
+    "2048": 83.0663038203653,
+    "2049": 83.90629500757991,
+    "2050": 84.74628619479452,
+    "2051": 84.74628619479452,
+    "2052": 84.74628619479452,
+    "2053": 84.74628619479452,
+    "2054": 84.74628619479452,
+    "2055": 84.74628619479452,
+    "2056": 84.74628619479452,
+    "2057": 84.74628619479452,
+    "2058": 84.74628619479452,
+    "2059": 84.74628619479452,
+    "2060": 84.74628619479452,
+    "2061": 84.74628619479452,
+    "2062": 84.74628619479452,
+    "2063": 84.74628619479452,
+    "2064": 84.74628619479452,
+}
+
+
+@fixture
+def cost_config():
+    config = steel.SteelCostModelConfig(
+        operational_year=2035,
+        plant_capacity_mtpy=1084408.2137715619,
+        lcoh=4.2986685034417045,
+        feedstocks=steel.Feedstocks(
+            natural_gas_prices=ng_prices_dict, oxygen_market_price=0
+        ),
+        o2_heat_integration=False,
+    )
+    return config
+
+
+def test_run_steel_model():
+    capacity = 100.0
+    capacity_factor = 0.9
+
+    steel_production_mtpy = steel.run_steel_model(capacity, capacity_factor)
+
+    assert steel_production_mtpy == 90.0
+
+
+def test_steel_cost_model(subtests, cost_config):
+
+    res: steel.SteelCostModelOutputs = steel.run_steel_cost_model(cost_config)
+
+    with subtests.test("CapEx"):
+        assert res.total_plant_cost == approx(451513562.41513157)
+    with subtests.test("Fixed OpEx"):
+        assert res.total_fixed_operating_cost == approx(99119892.8431614)
+    with subtests.test("Installation"):
+        assert res.installation_cost == approx(179525207.856775)
+
+
+def test_steel_finance_model(cost_config):
+    # Parameter -> Hydrogen/Steel/Ammonia
+    financial_assumptions = {
+        "total income tax rate": 0.2574,
+        "capital gains tax rate": 0.15,
+        "leverage after tax nominal discount rate": 0.10893,
+        "debt equity ratio of initial financing": 0.624788,
+        "debt interest rate": 0.050049,
+    }
+
+    costs: steel.SteelCostModelOutputs = steel.run_steel_cost_model(cost_config)
+
+    plant_capacity_factor = 0.9
+    steel_production_mtpy = steel.run_steel_model(
+        cost_config.plant_capacity_mtpy, plant_capacity_factor
+    )
+
+    config = steel.SteelFinanceModelConfig(
+        plant_life=30,
+        plant_capacity_mtpy=cost_config.plant_capacity_mtpy,
+        plant_capacity_factor=plant_capacity_factor,
+        steel_production_mtpy=steel_production_mtpy,
+        lcoh=cost_config.lcoh,
+        feedstocks=cost_config.feedstocks,
+        grid_prices=grid_prices_dict,
+        financial_assumptions=financial_assumptions,
+        costs=costs,
+    )
+
+    lcos_expected = 961.2866791076059
+
+    res: steel.SteelFinanceModelOutputs = steel.run_steel_finance_model(config)
+
+    assert res.sol.get("price") == lcos_expected