-
Notifications
You must be signed in to change notification settings - Fork 27
Iron: Adding electrowinning capabilities #432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 20 commits
26a6ec6
3a803fd
6fa32fd
9d4ff83
84aad1c
674cd47
0728b2d
9601cce
3351d51
6310a12
6455234
4f978b8
bd5cdb4
9388822
db6864b
c130111
0b41cbf
ab63af7
47b6535
149ee57
efa47cc
5b44977
502aa8c
8c79a30
95c5e70
d30a44a
3992372
6845b13
9229ed7
bde4c2d
d3af390
4e51c3b
504785a
821ba8b
674dac1
c34f4e5
71ea58a
a7e62c7
f4b24f5
c4cc271
09fa9a2
98bb8c5
fe7d704
2a0d6d5
6464330
85a11a7
39b9ba7
89b1c5f
4b43951
71967dc
f8ef558
1544cbd
1c5a82a
9038c6e
1ea3e07
61dced2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| name: "H2Integrate_config" | ||
|
|
||
| system_summary: "This reference hybrid plant is located in Minnesota and contains wind, solar, and battery storage technologies. The system is designed to produce hydrogen using an electrolyzer and also produce steel using a grid-connected plant." | ||
|
|
||
| driver_config: "driver_config.yaml" | ||
| technology_config: "tech_config.yaml" | ||
| plant_config: "plant_config.yaml" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| name: "driver_config" | ||
| description: "This analysis runs a hybrid plant to match the first example in H2Integrate" | ||
jmartin4u marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| general: | ||
| folder_output: outputs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| name: "plant_config" | ||
| description: "This plant is located in MN, USA..." | ||
|
|
||
| site: | ||
| latitude: 41.717 | ||
| longitude: -88.398 | ||
|
|
||
| # array of polygons defining boundaries with x/y coords | ||
| boundaries: [ | ||
elenya-grant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| 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: [ | ||
| # connect feedstocks to iron mine | ||
| ["grid_feedstock","iron_mine","electricity","cable"], | ||
| ["mine_feedstock","iron_mine","crude_ore","pipe"], | ||
| # connect feedstocks to iron plant | ||
| ["iron_mine","iron_plant","iron_ore","iron_transport"], | ||
| ["ewin_grid_feedstock","iron_plant","electricity","cable"], | ||
| ] | ||
|
|
||
| plant: | ||
| plant_life: 30 | ||
| finance_parameters: | ||
| 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 | ||
| target_dollar_year: 2022 | ||
| finance_subgroups: | ||
| iron_ore: | ||
| commodity: "iron_ore" | ||
| commodity_stream: "iron_mine" | ||
| technologies: ["iron_mine", "grid_feedstock", "mine_feedstock"] | ||
| sponge_iron: | ||
| commodity: "sponge_iron" | ||
| commodity_stream: "iron_plant" | ||
| technologies: | ||
| - "iron_mine" | ||
| - "grid_feedstock" | ||
| - "mine_feedstock" | ||
| - "iron_transport" | ||
| - "iron_plant" | ||
| - "ewin_grid_feedstock" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| from pathlib import Path | ||
|
|
||
| from h2integrate.tools.run_cases import modify_tech_config, load_tech_config_cases | ||
| from h2integrate.core.h2integrate_model import H2IntegrateModel | ||
|
|
||
|
|
||
| # Create H2Integrate model | ||
| model = H2IntegrateModel("27_iron_electrowinning.yaml") | ||
|
|
||
| # Load cases | ||
| case_file = Path("test_inputs.csv") | ||
| cases = load_tech_config_cases(case_file) | ||
|
|
||
| # Modify and run the model for different cases | ||
| casenames = [ | ||
| "AHE", | ||
| "MSE", | ||
| "MOE", | ||
| ] | ||
| lcois = [] | ||
|
|
||
| for casename in casenames: | ||
| model = modify_tech_config(model, cases[casename]) | ||
| model.run() | ||
| model.post_process() | ||
| lcois.append(float(model.model.get_val("finance_subgroup_sponge_iron.price_sponge_iron")[0])) | ||
|
|
||
| # Compare the LCOIs from each electrowinning type | ||
| print(lcois) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| name: "technology_config" | ||
| description: "This hybrid plant produces iron" | ||
|
|
||
| technologies: | ||
| grid_feedstock: #electricity feedstock for iron ore | ||
| performance_model: | ||
| model: "feedstock_performance" | ||
| cost_model: | ||
| model: "feedstock_cost" | ||
| model_inputs: | ||
| shared_parameters: | ||
| feedstock_type: "electricity" | ||
| units: "MW" | ||
| performance_parameters: | ||
| rated_capacity: 30. # MW, need 27.913 MW per timestep for iron ore | ||
| cost_parameters: | ||
| cost_year: 2022 | ||
| price: 58.02 #USD/MW | ||
| annual_cost: 0. | ||
| start_up_cost: 0. | ||
|
|
||
| mine_feedstock: #iron ore feedstock | ||
| performance_model: | ||
| model: "feedstock_performance" | ||
| cost_model: | ||
| model: "feedstock_cost" | ||
| model_inputs: | ||
| shared_parameters: | ||
| feedstock_type: "crude_ore" | ||
| units: "t/h" | ||
| performance_parameters: | ||
| rated_capacity: 2000. # need 828.50385048 t/h | ||
| cost_parameters: | ||
| cost_year: 2022 | ||
| price: 0.0 | ||
| annual_cost: 0. | ||
| start_up_cost: 0. | ||
|
|
||
| iron_mine: | ||
| performance_model: | ||
| model: "iron_mine_performance_martin" | ||
| cost_model: | ||
| model: "iron_mine_cost_martin" | ||
| model_inputs: | ||
| shared_parameters: | ||
| mine: "Northshore" | ||
| taconite_pellet_type: "drg" | ||
| max_ore_production_rate_tonnes_per_hr: 250 | ||
|
|
||
| iron_transport: | ||
| performance_model: | ||
| model: "iron_transport_performance" | ||
| cost_model: | ||
| model: "iron_transport_cost" | ||
| model_inputs: | ||
| performance_parameters: | ||
| find_closest_ship_site: False | ||
| shipment_site: "Chicago" | ||
| cost_parameters: | ||
| transport_year: 2022 | ||
| cost_year: 2022 | ||
|
|
||
| ewin_grid_feedstock: #electricity feedstock for iron dri | ||
| performance_model: | ||
| model: "feedstock_performance" | ||
| cost_model: | ||
| model: "feedstock_cost" | ||
| model_inputs: | ||
| shared_parameters: | ||
| feedstock_type: "electricity" | ||
| units: "kW" | ||
| performance_parameters: | ||
| rated_capacity: 600000. | ||
| cost_parameters: | ||
| cost_year: 2022 | ||
| price: 0.05802 #USD/kW | ||
| annual_cost: 0. | ||
| start_up_cost: 0. | ||
|
|
||
| iron_plant: | ||
| performance_model: | ||
| model: "humbert_electrowinning_performance" | ||
| cost_model: | ||
| model: "humbert_stinn_electrowinning_cost" | ||
| model_inputs: | ||
| shared_parameters: | ||
| electrolysis_type: "ahe" | ||
| performance_parameters: | ||
| ore_fe_wt_pct: 65 | ||
| capacity_mw: 600 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Index 0,Index 1,Index 2,Index 3,Index 4,Type,AHE,MSE,MOE | ||
| technologies,iron_plant,model_inputs,shared_parameters,electrolysis_type,str,ahe,mse,moe |
jmartin4u marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||
| import numpy as np | ||||||
| import openmdao.api as om | ||||||
| from attrs import field, define | ||||||
|
|
||||||
| from h2integrate.core.utilities import BaseConfig, merge_shared_inputs | ||||||
| from h2integrate.core.validators import contains | ||||||
|
|
||||||
|
|
||||||
| @define | ||||||
| class HumbertEwinConfig(BaseConfig): | ||||||
| electrolysis_type: str = field( | ||||||
| kw_only=True, converter=(str.lower, str.strip), validator=contains(["ahe", "mse", "moe"]) | ||||||
| ) # product selection | ||||||
| ore_fe_wt_pct: float = field(kw_only=True) | ||||||
| capacity_mw: float = field(kw_only=True) | ||||||
|
|
||||||
|
|
||||||
| class HumbertEwinPerformanceComponent(om.ExplicitComponent): | ||||||
| """ | ||||||
| Humbert: doi.org/10.1007/s40831-024-00878-3 | ||||||
| """ | ||||||
|
|
||||||
| def initialize(self): | ||||||
| self.options.declare("driver_config", types=dict) | ||||||
| self.options.declare("plant_config", types=dict) | ||||||
| self.options.declare("tech_config", types=dict) | ||||||
|
|
||||||
| def setup(self): | ||||||
| n_timesteps = self.options["plant_config"]["plant"]["simulation"]["n_timesteps"] | ||||||
| self.config = HumbertEwinConfig.from_dict( | ||||||
| merge_shared_inputs(self.options["tech_config"]["model_inputs"], "performance"), | ||||||
| strict=False, | ||||||
|
||||||
| ) | ||||||
|
|
||||||
| ewin_type = self.config.electrolysis_type | ||||||
| # Lookup specific energy consumption from Humbert Table 10 | ||||||
| if ewin_type == "ahe": | ||||||
| spec_energy_cons_lo = 2.781 | ||||||
| spec_energy_cons_hi = 3.779 | ||||||
| elif ewin_type == "mse": | ||||||
| spec_energy_cons_lo = 2.720 | ||||||
| spec_energy_cons_hi = 3.138 | ||||||
| elif ewin_type == "moe": | ||||||
| spec_energy_cons_lo = 2.89 | ||||||
| spec_energy_cons_hi = 4.45 | ||||||
| spec_energy_cons_fe = (spec_energy_cons_lo + spec_energy_cons_hi) / 2 # kWh/kg_Fe | ||||||
jmartin4u marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| self.add_input("electricity_in", val=0.0, shape=n_timesteps, units="kW") | ||||||
| self.add_input("iron_ore_in", val=0.0, shape=n_timesteps, units="kg/h") | ||||||
| self.add_input("ore_fe_wt_pct", val=self.config.ore_fe_wt_pct, units="percent") | ||||||
| self.add_input("spec_energy_cons_fe", val=spec_energy_cons_fe, units="kW*h/kg") | ||||||
| self.add_input("capacity", val=self.config.capacity_mw, units="MW") | ||||||
|
|
||||||
| self.add_output( | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you add
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding |
||||||
| "electricity_consumed", | ||||||
| val=0.0, | ||||||
| shape=n_timesteps, | ||||||
| units="kW", | ||||||
| desc="Electricity consumed", | ||||||
| ) | ||||||
| self.add_output("limiting_input", val=0.0, shape=n_timesteps, units=None) | ||||||
jmartin4u marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| self.add_output("sponge_iron_out", val=0.0, shape=n_timesteps, units="kg/h") | ||||||
| self.add_output("total_sponge_iron_produced", val=0.0, units="kg/year") | ||||||
| self.add_output("output_capacity", val=0.0, units="kg/year") | ||||||
|
|
||||||
| def compute(self, inputs, outputs): | ||||||
| n_timesteps = self.options["plant_config"]["plant"]["simulation"]["n_timesteps"] | ||||||
|
|
||||||
| # Parse inputs | ||||||
| elec_in = inputs["electricity_in"] | ||||||
| ore_in = inputs["iron_ore_in"] | ||||||
| pct_fe = inputs["ore_fe_wt_pct"] | ||||||
| kwh_kg_fe = inputs["spec_energy_cons_fe"] | ||||||
| cap_kw = inputs["capacity"] * 1000 | ||||||
jmartin4u marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| # If no connected input, set ore / electricity to max needed | ||||||
| if self.get_source("electricity_in")[:9] == "_auto_ivc": | ||||||
jmartin4u marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| elec_in = np.full(n_timesteps, cap_kw) | ||||||
| if self.get_source("iron_ore_in")[:9] == "_auto_ivc": | ||||||
| ore_in = np.full(n_timesteps, cap_kw / kwh_kg_fe / pct_fe * 100) | ||||||
|
|
||||||
| # Calculate max iron production for each input | ||||||
| fe_from_ore = ore_in * pct_fe / 100 | ||||||
elenya-grant marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| fe_from_elec = elec_in / kwh_kg_fe | ||||||
|
|
||||||
| # Limiting iron production per hour by each input | ||||||
| fe_prod = np.minimum.reduce([fe_from_ore, fe_from_elec]) | ||||||
| limiters = np.argmin([fe_from_ore, fe_from_elec], axis=0) | ||||||
|
|
||||||
| # Limiting iron production per hour by capacity | ||||||
| fe_prod = np.minimum.reduce([fe_prod, np.full(len(fe_prod), cap_kw / kwh_kg_fe)]) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this keep the output an array if |
||||||
| cap_lim = 1 - np.argmax([fe_prod, np.full(len(fe_prod), cap_kw / kwh_kg_fe)], axis=0) | ||||||
jmartin4u marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| # Determine what the limiting factor is for each hour | ||||||
jmartin4u marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| limiters = np.maximum.reduce([cap_lim * 2, limiters]) | ||||||
| outputs["limiting_input"] = limiters | ||||||
|
|
||||||
| # Determine actual electricity consumption from iron consumption | ||||||
| elec_consume = fe_prod * kwh_kg_fe | ||||||
|
|
||||||
| # Return iron production | ||||||
| outputs["sponge_iron_out"] = fe_prod | ||||||
| outputs["electricity_consumed"] = elec_consume | ||||||
| outputs["total_sponge_iron_produced"] = np.sum(fe_prod) | ||||||
| outputs["output_capacity"] = cap_kw / kwh_kg_fe * 8760 | ||||||
Uh oh!
There was an error while loading. Please reload this page.