-
Notifications
You must be signed in to change notification settings - Fork 438
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
load shedding and shifting new example #585
base: master
Are you sure you want to change the base?
load shedding and shifting new example #585
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #585 +/- ##
===========================================
+ Coverage 66.30% 78.38% +12.07%
===========================================
Files 26 26
Lines 7090 6948 -142
Branches 1425 1531 +106
===========================================
+ Hits 4701 5446 +745
+ Misses 2105 1179 -926
- Partials 284 323 +39 ☔ View full report in Codecov by Sentry. |
I am currently reviewing this and worked on @AnasAlgarei branch by applying this: https://stackoverflow.com/a/37686809 git clone https://github.com/pz-max/PyPSA.git
cd PyPSA
git remote add AnasAlgarei https://github.com/AnasAlgarei/PyPSA
git fetch AnasAlgarei
git checkout -b load-example AnasAlgarei/load_shedding_shifting_example I opened a PR with suggestion in @AnasAlgarei repo https://github.com/AnasAlgarei/PyPSA/tree/load_shedding_shifting_example |
Load example
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
Hi @pz-max, I just comitted a final draft of the load modelling example. However, it seems that it failed one of the checks with error: |
Ready for review @FabianHofmann. This PR is adding a load modelling example including:
|
Hey @AnasAlgarei, thanks for the nice contribution. After reviewing a bit, some points came up how to improve the example. It would be better to show the effects of load shifting and load shedding separately. I'd propose to rearrange the story to the following outline
or
Thinking about it, I tend to prefer the second option, as it adds more and more complexity. We also discovered some inconsistencies with the load shifting device. It has the carrier 'battery' and a standing loss and efficiencies for charging and discharging. However, load shifting should not be constrained to any of these right? You do not lose energy when you shift load, e.g. you would not need more power if you consume it at a later point. That's it for the start. Sorry that reviewing takes so long and thanks again for the nice work. |
PR is coming on my end. @AnasAlgarei can you merge the PR into your branch? I don't have the rights yet |
Hey @pz-max, I made the suggested adjustments to the file, please let me know of any further adjustments. Thanks for following up on this matter. |
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"**Note.** Here we solve the elastic demand as linear programming (LP) problem and several iterations. It is also possible to solve the problem with **one single** optimization run. However, this requires to reformulate the problem as quadratic programming (QP) problem, see [here](https://github.com/PyPSA/PyPSA/issues/574#issuecomment-1451865672). PyPSA can also do that, however, we need to add the QP solving capability to Linopy. This is already tracked as [feature request](https://github.com/PyPSA/linopy/issues/18). Stay tuned!" |
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.
Needs to be adjusted. Linopy has now capabilities to solve quadratic problems (QP)
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.
@AnasAlgarei , regarding the smart thermostat example.
I think we should also dismiss the "smart thermostat" example. A thermostat is a measurement device. People find it irritating when a thermostat is modelled as a storage unit without a detailed explanation. Can you find another load-shifting example that is more focused ideally on electricity? We need a short and concise description of how the device behaves and why its modelled like e.g. a storage_unit
Further, I think the input:
carrier
could be removed.
-p_nom
could be set positive to say that there is a device.
-cycling losses
(you know which variable I mean) could be removed. It increases unnecessary the complexity.
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.
Needs to be adjusted. Linopy has now capabilities to solve quadratic problems (QP)
Apologies about this, realised I didn't save the last changes before committing, all sorted now.
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.
@AnasAlgarei , regarding the smart thermostat example. I think we should also dismiss the "smart thermostat" example. A thermostat is a measurement device. People find it irritating when a thermostat is modelled as a storage unit without a detailed explanation. Can you find another load-shifting example that is more focused ideally on electricity? We need a short and concise description of how the device behaves and why its modelled like e.g. a storage_unit
Further, I think the input:
* `carrier` could be removed. -`p_nom` could be set positive to say that there is a device. -`cycling losses` (you know which variable I mean) could be removed. It increases unnecessary the complexity.
Thanks for the comments, will work on that and update 👍
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.
Hi @pz-max, I made changes as per your comments. Please review when possible.
for more information, see https://pre-commit.ci
…nasAlgarei/PyPSA into load_shedding_shifting_example
for more information, see https://pre-commit.ci
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.
Hello @AnasAlgarei 🙂 Great example and excellent explanations!
Have added some comments to the code. Apart of them, I'd suggest to keep color coding the same for all the plots: e.g. make load orange everywhere. My feeling is, it would facilitate analysis of the plots. What do you think?
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This notebook provides a simple illustration of how load shedding, load shifting, and elastic demand can be modeled in PyPSA. Through the example, we'll simulate a single day of electrical consumption in a network with solar generation, which could represent a household or a small community.\n", |
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.
To me, a second phrase hasn't sounded very clear at first reading: " a network with solar generation, which could represent...". Would it be possible to re-formulate a bit? Like "solar-powered network, which could represent..."?
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.
Edited to "we'll simulate a single day of electrical consumption in a solar-powered network, which could represent a household or a small community." ✔️
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.
Super! :)
"\n", | ||
"We'll start by constructing a minimal example network to demonstrate the demand modeling cases. The network consists of:\n", | ||
"\n", | ||
"- a single bus with a time span between 04:00 - 20:00.\n", |
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.
Not sure I get the point here. Does it mean that we consider only 04:00-20:00? It would be also great to specify what does the bus represent, assuming that a reader can be not perfectly fluent with PyPSA components. So, a short clarification could be very helpful, like: "a bus which represents a network node, to which generation and loads are connected".
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.
Edited to:
"## Build example network
We'll start by constructing a minimal example network to demonstrate the demand modeling cases. The network consists of:
- a single bus which represents a network node, to which generation and loads are connected.
- an hourly time span of a single day, from 00:00 to 23:00.
- one PV (Photovoltaic) generator, generating only in the daytime.
- an electricity load profile (active), with peak load between 19:00 - 23:00." ✔️
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.
Sounds great! 👍🏽
" carrier=\"electricity\",\n", | ||
" p_nom_extendable=True,\n", | ||
" standing_loss=0.01,\n", | ||
" cyclic_stage_of_charge=True,\n", |
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.
A typo should by fixed: cyclic_stage_of_charge
-> cyclic_state_of_charge
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.
fixed ✔️
" \"StorageUnit\",\n", | ||
" \"EV battery\",\n", | ||
" bus=\"My bus\",\n", | ||
" p_nom=1, # positive to indicate that there is a devise\n", |
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.
Shouldn't it be device
?
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.
fixed ✔️
" )\n", | ||
"\n", | ||
"# Fit a linear curve to the data\n", | ||
"slope, intercept = np.polyfit(updated_average_loads, updated_prices, 1)\n", |
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.
Could it probably make sense to add overlay results of the simulation with the original elasticity curve? @AnasAlgarei what is your feeling about 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.
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.
@AnasAlgarei absolutely agree regarding zooming :) An attempt to keep the original scale doesn't look nice.
I have been thinking about making more evident that the elasticity curve above and here are the same thing, like continuing the elasticity line beyond the modeled points and probably trying to keep the same angle with the axises. But that would require additions to the code, which would be distractive. So, let's keep as is.
I think, the legend will be enough to make the connection between both plots.
Thank you so much @ekatef for the thorough review of the example. Your comments are really appreciated and were taken into account where possible. I agree that setting a consistant colors for all plots makes it look better, I have included that as well. I will commit all changes soon 😃 |
Hello @AnasAlgarei. Awesome! |
Hi @ekatef, I just committed all changes. Kindly have a look at it when possilbe, and let me know what you think. Thanks again for the review 😃 |
Hey @AnasAlgarei! Looks great 😄 |
Hi @AnasAlgarei and @pz-max @ekatef thanks for this example! Fabian and I went through it. We think Load shedding and Load shifting are ready with small adjustments, and we have some concerns re. Elastic demand. I don't see a way to comment lines in Notebook files in a PR, so the comments are above: Load shedding
One should either set p_nom to some value OR set p_nom_extendable=True.
Note that the default value of the capital cost is zero, check Load shiftingFirst, we thought that description of the mechanism should be rather generic, i.e., pointing that it could be EVs, or delay of execution of flexible workloads (and associated power loads) in data centers, and many more. Second, it might be worth mentioning that you use here PyPSA Third, the same comment applies as in load shedding, since you set Elastic demandFirst, the main thing where we were perplexed, is the fact that the concept of the demand elasticity to price is illustrated via p.s. It is interesting that if one checks the marginal price time-series in your bus, the prices in most hours are 1e2 (i.e. load shedding costs), even when load shedding does not take place, since you have a single generator and a single bus. Second, you start using .lopf() instead of .optimise(). If user has a new version of PyPSA and linopy, .lopf() gives an error. So I run n.optimise() instead to reproduce your examples. More generally, is there a reason you change optimization framework across these examples? OtherWe spotted that |
Load shedding exampleLet's kick out network.add( Load shifting exampleCan you make the adjustments as @Irieo is pointing out? (Mostly about changing the description.) Elastic demand@Irieo , I think we could keep the annual electricity price with the argument that most households don't see hourly prices (in UK the partially do with e.g. Octopus Energy). But @AnasAlgarei let's change it as Fabian and Iegor suggest. Might confuse less people. P.S. I hope the marginal price time-series will change. It should be 0 when there is no load shedding. |
The elastic demand should not be done with iterative optimisation. Since PyPSA v0.24.0, you can model elastic demand with a quadratic marginal cost term (https://pypsa.readthedocs.io/en/latest/release_notes.html#pypsa-0-24-0-27th-june-2023) Here's how this can be implemented: elastic_intercept = 2000 # change here to alter level of price elasticity
load = 100 # MW
n.add(
"Generator",
"load-shedding",
bus="electricity",
carrier="load",
marginal_cost_quadratic=elastic_intercept / ( 2 * load),
p_nom=load,
)
n.add("Load", "load", bus="electricity", carrier="load", p_set=load) This turns the model into a QP, but still convex, so performance is not significantly impacted. |
Thank you @Irieo and @FabianHofmann for the informative review and comments, and thank you @pz-max and @ekatef for helping me make this example look better. I will implement the changes suggested in the comments and update here when completed. |
Load example fix
for more information, see https://pre-commit.ci
Closes # (if applicable).
#574
Changes proposed in this Pull Request
This PR includes a new PyPSA example that explains how load shedding and load shifting are represented in PyPSA. Load shedding is added as a generator that substitutes the decreased load, whereas load shifting is presented as a storage unit that could be charged and discharged, meaning that the load is decreased in certain periods and increased in others. This example needs further development to include elastic demand.
Checklist
doc
.environment.yaml
,environment_docs.yaml
andsetup.py
(if applicable).doc/release_notes.rst
of the upcoming release is included.