-
Notifications
You must be signed in to change notification settings - Fork 27
Enable load following optimization dispatch with Pyomo #407
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 all commits
168766a
df63126
73f87d7
80782e1
6971220
5a2ce87
c7923c4
38c929d
03d7c1d
9509748
5fa7165
2194701
c2f1bbb
4d676fb
0c642d0
22e411f
efc2069
8650683
e947332
581e101
1dbb52b
4f2faea
39ad186
ec3ad18
b1cc99b
24cc0b8
abf9d49
a2da8b9
5a26e58
72205b2
5c16393
6de9803
dc059b9
5d36008
ba6d65d
bb8e7d8
6a1d050
57bf534
0acaa30
e3d0315
a509354
2342730
c2604b6
5f33847
363bfbf
f42f935
bfd4e9c
db32b3a
2c6e461
75f9ff6
1f6dfbb
d3802a9
53a8da9
948f11d
99ad4d1
5a7ced1
1d7d659
7324fb1
028598e
297c149
4b4a857
0471fa7
e474c2c
fb85bc7
40d5f57
31a9bd4
fbf290e
c8ee38f
54885a7
2f500e3
9e1e9e9
d13e55c
6768934
e21b33f
4e8ee73
8c23c1b
0198e0d
617c030
695615b
8c6dca8
16f6441
b1242c6
d1dd047
f36429e
7e93ef3
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,5 @@ | ||
| name: "driver_config" | ||
| description: "This analysis runs a hybrid plant to dispatch storage optimally to meet an electrical load." | ||
|
|
||
| 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 TX, USA..." | ||
|
|
||
| sites: | ||
| site: | ||
| latitude: 35.2018863 | ||
| longitude: -101.945027 | ||
|
|
||
| resources: | ||
| wind_resource: | ||
| resource_model: "WTKNRELDeveloperAPIWindResource" | ||
| resource_parameters: | ||
| resource_year: 2012 | ||
|
|
||
| plant: | ||
| plant_life: 30 | ||
|
|
||
| # 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: [ | ||
| ["wind", "battery", "electricity", "cable"], | ||
| ] | ||
|
|
||
| # array of arrays containing left-to-right technology, technology doing the dispatching | ||
| # in this case, battery is connected to battery because there are controls rules for | ||
| # the battery and battery is controlling the dispatching | ||
| tech_to_dispatch_connections: [ | ||
| ["wind", "battery"], | ||
| ["battery", "battery"], | ||
| ] | ||
|
|
||
| resource_to_tech_connections: [ | ||
| # connect the wind resource to the wind technology | ||
| ['site.wind_resource', 'wind', 'wind_resource_data'], | ||
| ] | ||
|
|
||
| finance_parameters: | ||
| finance_groups: | ||
| profast_model: | ||
| commodity: "electricity" | ||
| finance_model: "ProFastLCO" | ||
| 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 # p-tax https://www.house.mn.gov/hrd/issinfo/clsrates.aspx # insurance 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.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" - 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 | ||
| depr_period: 5 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507 | ||
| cost_adjustment_parameters: | ||
| cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year | ||
| target_dollar_year: 2022 | ||
| finance_subgroups: | ||
| all_electricity: | ||
| commodity: "electricity" | ||
| finance_groups: ["profast_model"] | ||
| technologies: ["wind", "battery"] | ||
|
|
||
|
|
||
| # dispatched_electricity: | ||
| # commodity: "electricity" | ||
| # commodity_stream: "battery" #use only dispatched electricity from battery in finance calc | ||
| # technologies: ["wind", "battery"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| name: "H2Integrate_config" | ||
|
|
||
| system_summary: "This hybrid plant contains wind and battery storage technologies. The system is designed to dispatch storage optimally meet a specific electrical load." | ||
|
|
||
| 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,78 @@ | ||
| import numpy as np | ||
| from matplotlib import pyplot as plt | ||
|
|
||
| from h2integrate.core.h2integrate_model import H2IntegrateModel | ||
|
|
||
|
|
||
| # Create an H2Integrate model | ||
| model = H2IntegrateModel("pyomo_optimized_dispatch.yaml") | ||
|
|
||
| demand_profile = np.ones(8760) * 100.0 | ||
|
|
||
|
|
||
| # TODO: Update with demand module once it is developed | ||
|
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. Do you want to address this TODO? I thought you could set the demand in the tech config?
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. Currently, you have to set up the problem, then assign the battery demand signal to the battery model, then run the model. #385 is an issue to address this |
||
| model.setup() | ||
| model.prob.set_val("battery.electricity_demand", demand_profile, units="MW") | ||
|
|
||
| # Run the model | ||
| model.run() | ||
|
|
||
| # Plot the results | ||
| fig, ax = plt.subplots(2, 1, sharex=True, figsize=(8, 6)) | ||
|
|
||
| start_hour = 0 | ||
| end_hour = 200 | ||
|
|
||
| ax[0].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.SOC", units="percent")[start_hour:end_hour], | ||
| label="SOC", | ||
| ) | ||
| ax[0].set_ylabel("SOC (%)") | ||
| ax[0].set_ylim([0, 110]) | ||
| ax[0].axhline(y=90.0, linestyle=":", color="k", alpha=0.5, label="Max Charge") | ||
| ax[0].legend() | ||
|
|
||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.electricity_in", units="MW")[start_hour:end_hour], | ||
| linestyle="-", | ||
| label="Electricity In (MW)", | ||
| ) | ||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.unused_electricity_out", units="MW")[start_hour:end_hour], | ||
| linestyle=":", | ||
| label="Unused Electricity (MW)", | ||
| ) | ||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.unmet_electricity_demand_out", units="MW")[start_hour:end_hour], | ||
| linestyle=":", | ||
| label="Unmet Electrical Demand (MW)", | ||
| ) | ||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.electricity_out", units="MW")[start_hour:end_hour], | ||
| linestyle="-", | ||
| label="Electricity Out (MW)", | ||
| ) | ||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| model.prob.get_val("battery.battery_electricity_discharge", units="MW")[start_hour:end_hour], | ||
| linestyle="-.", | ||
| label="Battery Electricity Out (MW)", | ||
| ) | ||
| ax[1].plot( | ||
| range(start_hour, end_hour), | ||
| demand_profile[start_hour:end_hour], | ||
| linestyle="--", | ||
| label="Electrical Demand (MW)", | ||
| ) | ||
| ax[1].set_ylim([-1e2, 2.5e2]) | ||
| ax[1].set_ylabel("Electricity Hourly (MW)") | ||
| ax[1].set_xlabel("Timestep (hr)") | ||
|
|
||
| plt.legend(ncol=2, frameon=False) | ||
| plt.tight_layout() | ||
| plt.savefig("optimized_dispatch_plot.png", dpi=300) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After further reviewing the PR, I think there's some additions to the doc page that would really help with overall understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some responses to the comments above:
hybrid_rule. The ports define what variables are passed from the individual technology dispatch models to thehybrid_ruleproblem. "Arcs" are what actually form the connection between the ports. This means that the storage dispatch pyomo model has a port forsystem_production,hybrid_rulehas a port forsystem_production, and an arc connects these two endpoints to connect this variable across the pyomo models.hybrid_rulebecausehybrid_rulecurrently only works as an aggregator of models and only includes things that you would need access to all the individual pyomo models to know.