-
Notifications
You must be signed in to change notification settings - Fork 22
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
Incorporate a model of demand flexibility #118
Conversation
src/model.jl
Outdated
0 <= load_shed[i in 1:sets.num_load_bus, j in 1:interval_length] | ||
<= bus_demand[sets.load_bus_idx[i], j], | ||
load_shed[i in 1:sets.num_load_bus, j in 1:interval_length], |
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.
It seems like with this change we can shed more load than we have available at a bus?
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.
I ended up constraining the load_shed
variable separate from the variable creation. When demand flexibility is enabled, load_shift_up
and load_shift_dn
will have an effect on the upper bound of load_shed
. I wasn't sure how flexible setting constraints within the variable creation was, so that's why I moved it to its own constraint.
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.
Here is how we modify variable bounds as we step through intervals: https://github.com/Breakthrough-Energy/REISE.jl/blob/develop/src/loop.jl#L86
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.
Thanks for pointing this out! I was just going to reply to your next comment suggesting that this is one of changes that I forgot to account for. Is it preferred to keep the load_shed
variable the same as it was originally (include the constraints in the variable creation) and instead account for load_shift_up
and load_shift_dn
in loop.jl
if demand flexibility is allowed?
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.
We need to make sure that load shedding is appropriately constrained in the first iteration of the loop (when we run _build_model
), as well as appropriately updated in each iteration. We will have to do the same for flexibility (if present).
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.
@danielolsen I'm about to point the same thing out.
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.
I have updated loop.jl
, so load_shed
and the demand flexibility variables (load_shift_up
and load_shift_dn
) should be properly constrained in each iteration.
What do the values in the new flexibility.csv input represent? Are they fractions of the demand that are flexible over the course of the year? |
They are an amount of demand (in MW) that is considered to be flexible at that time. So, if you divide the amount of flexible demand provided by flexibility.csv by the total demand provided by demand.csv, you would get the percentage of demand at that bus that is flexible. |
e5d1072
to
16ada65
Compare
Looking through the conversations here, I think all the comments have been addressed so far, right? @lanesmith |
Yes, I believe so. |
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.
Very nice. Thanks @lanesmith Let's get @danielolsen 's green light before merging.
Sounds good! There is definitely room for improvements (some ideas for some of these improvements have been outlined in some issues), but I think this functionality is a good first version. It should serve as a good foundation for some of the collaborations that are ready to look at demand flexibility. |
bus_zone_idx, bus_idx, bus_share)::SparseMatrixCSC | ||
bus_zone_idx, bus_idx, bus_share | ||
)::SparseMatrixCSC | ||
return zone_to_bus_shares |
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.
The return
is not necessary here, Julia automatically returns the value of the last-evaluated expression.
See https://docs.julialang.org/en/v1/manual/functions/#The-return-Keyword
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.
Agree. But I will vote for keeping these return
statements since they are more explicit and readable even for people not familiar with Julia.
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.
In that case, there are a few places where we should add them for consistency. See #127
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.
Okay, so we'll keep the return
statements? I can add return
statements to some of the functions that don't have them, like _make_branch_map
and _make_sets
.
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.
I think that would be a good idea, thanks for taking care of that.
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.
I added return statements to all functions where it made sense. I believe this should close #127
src/model.jl
Outdated
if load_shed_enabled | ||
if demand_flexibility.enabled | ||
JuMP.@constraint( | ||
m, | ||
load_shed_ub[i in 1:sets.num_load_bus, j in 1:interval_length], | ||
load_shed[i, j] <= bus_demand[sets.load_bus_idx[i], j] | ||
+ load_shift_up[i, j] | ||
- load_shift_dn[i, j] | ||
) | ||
else | ||
JuMP.@constraint( | ||
m, | ||
load_shed_ub[i in 1:sets.num_load_bus, j in 1:interval_length], | ||
load_shed[i, j] <= bus_demand[sets.load_bus_idx[i], j] | ||
) | ||
end | ||
end | ||
|
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.
Can we build up the right-hand-side of this constraint (based on the ordering of variables/parameters here, not based on the 'traditional' RHS definition which is parameters only) using a JuMP expression? I.e. start with the bus demand, add load shifting as necessary, and then constrain based on this expression?
Or equivalently, start with an expression containing load shedding, add load shifting as necessary, and constrain this expression by the bus demand?
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.
Yeah, that's a good idea. That would definitely be cleaner.
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.
I believe I have implemented something similar to what you were describing.
4cae540
to
489a8c1
Compare
946dafe
to
09248fc
Compare
661821e
to
382f1bf
Compare
@danielolsen, I refactored |
Yeah, Julia's scoping rules can be pretty unintuitive, especially coming from Python. I think the modifications to |
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.
Thanks. There's one place where a function gets extra parameters that it doesn't use, once this is removed I think this is ready to merge.
a3cf50b
to
f90f3b9
Compare
Great, thank @BainanXia and @danielolsen for the feedback! |
Purpose
The purpose of this new code is to incorporate a model of demand flexibility into the production cost model. This is a first iteration of the model and will be updated in the future to (1) become more computationally efficient and (2) incorporate the modeling efforts of our external collaborators working on sectoral electrification.
What is the code doing?
The new code includes a model of demand flexibility and supporting functionality to prepare the relevant flexibility data and process the new results. Here is a more in-depth breakdown of the additions by file:
src/REISE.jl
: brief updates to the overall framework to allow demand flexibility to be considered.src/loop.jl
: very brief update to include theFlexibility
object. While not needed in this iteration of the code, information from theFlexibility
object will almost certainly be needed once we start considering the look-ahead period.src/model.jl
: this contains the bulk of the code additions. TheJuMP
model is updated to include new decision variables and constraints that are needed for the demand flexibility model. Some preexisting constraints needed to be updated following the inclusion of the demand flexibility model. Functionality is included to map the demand flexibility profile data (which is originally by load zone) to each bus that contains demand flexibility (which is currently every bus with demand).src/types.jl
: create a newFlexibility
object to store relevant demand flexibility information. Update preexisting types as needed.src/read.jl
: provide functionality to read in a single demand flexibility profile and other associated demand flexibility parameters (e.g., shifting duration). The original plan was to allow for multiple profiles to be read in, thereby allowing the different demand sectors to remain disaggregated. However, after internal discussions, it seems like it is probably best to aggregate these profiles; this functionality would probably be best suited forPreREISE
(similar to what is done inPreREISE/prereise/gather/demanddata/nrel_efs/aggregate_demand.py
). Right now, I'm just reading in the demand flexibility parameters (currently just duration, though curtailment cost could be included at a later time) from a very basic .csv file. I'm open to suggestions on how to best store and read in this data (though this design may remain pretty fluid depending whether the duration parameter is static or more dynamic depending on weather).src/query.jl
: accesses theload_shift_dn
andload_shift_up
variables from theVariablesOfInterest
object.src/save.jl
: saves theload_shift_dn
andload_shift_up
variables.pyreisejl/utility/extract_data.py
: allows theload_shift_dn
andload_shift_up
variables to be accessed and saved as .pkl files.README.md
: update the documentation to include new the demand flexibility model.Where to look?
Most of the new code is located within the
src
folder to update the model (src/REISE.jl
,src/loop.jl
, andsrc/model.jl
) and the supporting functions to prepare the data (src/read.jl
andsrc/types.jl
) and process the results (src/query.jl
andsrc/save.jl
). I also updatepyreisejl/utility/extract_data.py
to allow the Python wrapper functions to work with the new model. Lastly, I also updatedREADME.md
to appropriately document the new model.Testing
The best that I can tell, none of the preexisting tests need to be modified following the inclusion of this code. Additionally, none of the code that I added appears to warrant its own tests (based on current testing convention within this repository). I did run some simple tests on my local machine to serve as a proof of concept. Essentially, the demand flexibility model behaved in a way that was to be expected, with demand trying to shift from times of higher system prices (which can be visualized via the LMP representation) to times of lower system prices. Predictably, the inclusion of the demand flexibility model (in its current state) really slows down the solution time, even when using Gurobi. While I don't expect the difference to be as stark when executed on the server, these results indicate the need for some form of feature reduction, which will be incorporated in the future. For a little more information on these results, please refer to my recent presentation, which is located in the
Deliverables
folder of myExplorations
folder.Visuals
While I don't include any visuals here, there are a couple limited visuals in the presentation that I mentioned in the previous section. That presentation is located in the
Deliverables
folder of myExplorations
folder.Time estimate
I'm not sure. I tried to submit this PR a little earlier, so there should be fewer lines of code to review than in the past. However, it does incorporate a new model and works across quite a few different files in
REISE.jl
.