Skip to content
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

Add pumped hydro in a generalized & backwards-compatible way #117

Open
wants to merge 1 commit into
base: next_release
Choose a base branch
from

Conversation

josiahjohnston
Copy link
Contributor

Generalized implementation of simple pumped hydro with an example/test case. This implementation requires the sequence of simple_hydro, storage in modules.txt.

My goals were modeling accuracy, backwards compatibility, avoiding redundancy, checks to aid debugging of common errors, and allow pumped hydro to participate in reserves like any other generator.

Pumped hydro is different from normal hydro because of need to the track energy balance, and that average dispatch is not solely determined by average stream flow (but also includes storage decisions). It's also different from normal storage because energy tracking needs to include both stream inflow and any spilled water.

In this implementation, if any pumped hydro generators are specified, the simple_hydro module will validate that the storage module is also included. The pre-existing rules for maintaining average stream flow are restricted to non-pumped hydro. Special rules for pumped hydro energy tracking and streamflow are implemented with an extra few lines in the storage energy tracking constraint, which capture the mathematical requirements of average daily balancing and additionally tracks state-of-charge. If the pumped hydro terms are not available, the storage module will skip that stanza without complaint.

I also updated the storage documentation to reflect how StateOfCharge is unbound for one timepoint in each timeseries (that is, the optimization can choose to start a day with an arbitrary storage level), because I found myself having to re-read through equations to convince myself of that behavior.

I reviewed the pumped_hydro module in Hawaii before starting this. That implementation looked great for a rapid prototype, but had a few drawbacks:

  • Partially replicated components from other modules (such as build & dispatch decisions), and consequently could not trivially integrate with spinning reserves or standard results processing & DB import workflows. This partial replication also complicated QA/QC since I couldn't just rely on the expected behaviors of from established modules.
  • Did not accommodate a streamflow timeseries
  • The features of all-or-nothing build decisions were not appropriate for the problem I was trying to model

The second two bullet points meant I couldn't just reuse that module for my problem, and the first bullet point prompted me to devise an alternate design patterns.

@bmaluenda
Copy link
Member

I haven't been able to find enough time to review the example in detail, but the code looks good. It is great that you found a clever way to implement this behavior using the existing hydro_simple and storage modules, instead of adding a new one.

From what I understand, StateOfCharge is effectively unbound for the first timepoint of each timeseries, but depends on the StateOfCharge and dispatch decisions at the last timepoint. This creates a circular behavior that makes perfect sense if the representative day repeats itself consecutively. If the selected representative days are alternating, this behavior might be a bit imprecise. In such case, the ability to exogenously constraint the initial StateOfCharge (and, thus, also the final StateOfCharge) of each timeseries would be useful. It could be nice to have this idea in a TODO list.

A more advanced implementation that considers the full mass balance using the hydro_system module could be added to the TODO list as well. That would be useful for studies in systems that have cascading pumped hydro.

I do have a more relevant question: It seems to me that in this implementation you assume that the generator will have infinite availability of water at the lower reservoir to pump back to the upper reservoir. If so, it could be good to explicitely note it in the documentation. This behavior impacts the modeling of pumped hydro that doesn't have natural inflows into the upper reservoirs and for cascading water reservoirs. It's not wrong at all, but the modeler should be aware of limitations.

@josiahjohnston
Copy link
Contributor Author

Thanks for the comments Benjamin!
I'll update the documentation to add a description of how energy capacity is handled, with particular emphasis on the lower reservoir. Your interpretation is consistent with the modeling, but there are a more nuances that I'll add in.

Agreed on the to-do list for integrating with the hydro system module, and changing behavior of state of charge from circular to sequential or something else. Circularity vs non-circularity of timeseries has been a standing to-do item, but I don't remember if that's written anywhere.

@bmaluenda
Copy link
Member

I don't really think that state-of-charge tracking should be sequential, given that timeseries don't necessarily represent consecutive time periods. Additionally, making tracking sequential would increase the difficulty of the problem, since pumped hydro dispatch decisions would be linked throughout all timeseries.

I think a reasonable solution could be to allow setting initial (and final) volumes as an input.

@josiahjohnston
Copy link
Contributor Author

Yeah, that makes sense for use cases I've run into. I expect someone will need sequential (rather than circular) behavior at some point, but I haven't encountered it yet.

I added documentation per your suggestions. Look good to you?

@mfripp
Copy link
Member

mfripp commented Aug 9, 2019

As you noted, we don't officially consider timeseries as having any chronological relationship to each other. This impairs our ability to model seasonal phenomena. We could fix this by adding another level of time between timeseries and periods (probably seasons, maybe months of year), but I don't think we're ready to go there yet.

In the past, for hydro, I've just constrained inflow and outflow on each day to match the average reported values for that month of the year. That seemed conservative, since there may be some ability to vary flows from day to day within the month.

In Hawaii, I've assumed that hydrogen consumption might be concentrated during a particular period of the year, i.e., stocks would build up during one part of the year and they would decline during another part. So then it's possible to cover seasonality conservatively by just requiring a storage volume that exceeds the total amount stored at all times of the year. If in fact there are multiple cycles of build up, draw down, build up, draw down, then you could actually get away with less storage capacity. But storage volume is a small part of the cost of using hydrogen, so the conservative approach probably doesn't skew the results too much.

@bmaluenda
Copy link
Member

I still haven't had the time to study the example you provided, but the code and documentation looks good to me.

…t case. This implementation requires the sequence of simple_hydro, storage in modules.txt.

My goals were modeling accuracy, backwards compatibility, avoiding redundancy, checks to aid debugging of common errors, and allow pumped hydro to participate in reserves like any other generator.

Pumped hydro is different from normal hydro because of need to the track energy balance, and that average dispatch is not solely determined by average stream flow (but also includes storage decisions). It's also different from normal storage because energy tracking needs to include both stream inflow and any spilled water.

In this implementation, if any pumped hydro generators are specified, the simple_hydro module will validate that the storage module is also included. The pre-existing rules for maintaining average stream flow are restricted to non-pumped hydro. Special rules for pumped hydro energy tracking and streamflow are implemented with an extra few lines in the storage energy tracking constraint, which capture the mathematical requirements of average daily balancing and additionally tracks state-of-charge. If the pumped hydro terms are not available, the storage module will skip that stanza without complaint.

Storage documentation updates: Describe how StateOfCharge is unbound for one timepoint in each timeseries (that is, the optimization can choose to start a day with an arbitrary storage level), because I found myself having to re-read through equations to convince myself of that behavior. Explicit describe units of various energy-based components in the storage model (to reduce confusion between power & energy). Rewrap line endings to conform to PEP-8 recommendations for reading two files side-by-side in a single screen (or a single file in large font on a small screen)
staadecker pushed a commit that referenced this pull request Jan 28, 2023
Revert inequality to equality for the hydro simple module
staadecker pushed a commit that referenced this pull request Jan 28, 2023
Revert inequality to equality for the hydro simple module
staadecker pushed a commit that referenced this pull request Jan 29, 2023
Revert inequality to equality for the hydro simple module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants