Energy system modelling

657 views
Skip to first unread message

DR Mohammed Riadh Habour

unread,
Mar 23, 2023, 9:55:56 PM3/23/23
to pypsa
Hi everyone

I'm modelling the Irish energy system within the Dublin City University, I would like to limit the energy input of the thermal generation (Heavy oil gas turbine) in the model to 35 KTOE as reported in SEAI report 2021. Are there a way to do it using energy carrier or other function in PyPSA ?

Thank you 

barry.m...@dcu.ie

unread,
Mar 24, 2023, 6:19:53 AM3/24/23
to pypsa
Hi Riadh -

I'm assuming you are running lopf(). Then the short answer is that you need to add a custom constraint on the sum of the energy generated (output) by all the relevant generator component(s), over all snapshots for the relevant period (i.e., spanning the year 2021) requiring this sum to be exactly equal to a stipulated (constant) value. You have expressed your requirement in terms of the energy input, but for each generator this will be just be the same as the output divided by the generator efficiency, so that's just some additional constant factors (one for each generator) in the constraint.  

pypsa's default energy unit basis is MWh: so if you want to express the "raw" requirement in ktoe you will also need to incorporate a scaling/conversion from ktoe->MWh. 

The detailed implementation of such a custom constraint will be different depending on whether you are using pyomo or linopy. The documentation linked above gives more guidance and some examples of implementing custom constraints.

Regards - Barry 

Message has been deleted

DR Mohammed Riadh Habour

unread,
Mar 30, 2023, 1:34:11 PM3/30/23
to pypsa

Hi Barry 

 

I had read a document, its a general information, it doesn't indicate how to proceed, if we consider a simple example as :

import pypsa
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pyomo.environ import Constraint

 

IES = pypsa.Network()
IES.set_snapshots(range(8760))

 

IES.add("Bus","power_generation",v_nom=20.)

IES.add("Generator","Natural_gas_generator",bus="power_generation",carrier="gas",lifetime=20,
        marginal_cost=42,capital_cost=450000,efficiency=0.5,p_nom=180, p_nom_extendable=True)
   
IES.add("Generator","Oil_generator",bus="power_generation",carrier="oil",lifetime=20,
        marginal_cost=45,capital_cost=400000,efficiency=0.35,p_nom=30,p_nom_extendable=True)
IES.add("Carrier", "gas")
IES.add("Carrier", "oil")
IES.add("Load","Cosumption",bus="power_generation",p_set=200)

 

IES.lopf(pyomo=True)

 

 

Could you indicate how to custom a constraint using several function to limit the energy into the oil generator by 35 KTOE ?

 Thank you 

 

Riadh

Fabian Neumann

unread,
Mar 31, 2023, 2:12:38 AM3/31/23
to pypsa
Hi Riadh,

you can work around writing custom constraints for this. Use a store component for oil on an oil bus with a given initial filling level and then model the oil generator with a link converting oil to electricity.

The unit conversion between ktoe and MWh you have to do yourself.

Best wishes,

Fabian N

DR Mohammed Riadh Habour

unread,
Apr 1, 2023, 9:10:56 AM4/1/23
to pypsa
Hi  Fabian
If you mean by initial filling level setting up  e_initial = 407050 MWh (35 KTOE), I did it many times and PYPSA show "Optimisation failed with status ok and terminal condition other"

Thank you 
Riadh

barry.m...@dcu.ie

unread,
Apr 3, 2023, 12:25:30 PM4/3/23
to pypsa
HI Riadh -

I'm not quite sure how you implemented the Store approach to limiting the oil generation in your toy example; but I'm attaching a new version, derived from the example you posted at first, that does this. As you will see, it does solve (I've set "pyomo=False" just to speed it up a little, but you will get essentially the same result with "pyomo=True"). Note that I've adjusted the capital and marginal costs for the Link representing the oil generator so that it will match the costs you had previously specified for an (unconstrained) Generator component (because the Link costs scale according to the "input" or p0 power they have to be adjusted to reflect the assumed conversion efficiency, so that they are comparable to the costs you had previously specified for the oil Generator component). I've tacitly allocated zero costs to the Store itself: which is not physically realistic, but is fine when the Store is just being used as a formal way of expressing the desired limit on total oil generation. If you wish, you can do an additional "consistency check" against your original model, by setting the initial content of the oil store to be in excess of what would be required to satisfy the full load all from oil generation. In that case, this apparent constraint on total oil consumption would no longer be "binding" - it would not affect the outcome (in terms of how much generation of each type is deployed or how it is dispatched): so the outcome would revert the result from your original version with two Generator components (i.e. all the generation could go to oil, because of the way you have set the relative costs).

I hope that helps - Barry

constrain-gen-via-store.ipynb

barry.m...@dcu.ie

unread,
Apr 6, 2023, 4:48:56 AM4/6/23
to pypsa
Hi again Riadh -

Just for completeness, I'm attaching another example notebook showing the custom constraint approach to this same problem, just for illustration. This is perhaps conceptually simpler, as it means you can still represent the two generation sources as pypsa Generators, and you don't have the extra complication of adding a discrete Store, Bus and Link. Depending on what you are ultimately trying to do, this may also generalise better. Note that because the pypsa approach to interfacing with the underlying LP solver(s) has been evolving, there are different possible approaches to implementing custom constraints. This notebook shows three different possibilities (pypsa linopt, linopy via the linopt compatibility layer, and "native" linopy). Of these, the last (native linopy) is recommended for new or future oriented work with pypsa. (As with the previous, Store based, approach I have assumed you want to limit the "upstream" energy from the Oil_generator source - i.e., gross of the conversion efficiency losses. But if you actually intended to limit the generated electricity, net of conversion losses, it should clear enough how you can change the constraint to reflect that.)

Regards - Barry

constrain-gen.ipynb

DR Mohammed Riadh Habour

unread,
Apr 11, 2023, 1:08:35 AM4/11/23
to pypsa

Hi Barry 

 

    As showed in the model the dispatch of oil generation is constant for the whole snapshot, while I would like to use oil power plant only for the backup and renewable energy wind/solar generator as main sources of electricity, to make it clearer, it will be a few cases as fellow:

    If wind/solar generation are equal to demand, battery is not charging, the is not gas/oil power plant electricity generation. 

    If wind/solar generation exceed demand, the exceeding is stored in battery storage with a limit of 5000 MWh and the is not gas/oil power plant electricity generation. 

    If wind/solar generation exceed demand and battery energy capacity, the electricity exceeding is curtailed, the is not gas/oil power plant electricity generation. 

    If wind/solar generation cannot meet demand, the battery storage will fill the gap, the is not gas/oil power plant electricity generation. 

    If both wind/solar generation and battery storage cannot meet demand, the gap is filled by gas/oil power plant electricity. 

 

I would like to ask if PYPSA can do that, if yes, do I have to custom a several constraints? or trick the capital and marginal cost and try many combinations?

 

Kind regards 

Riadh 

barry.m...@dcu.ie

unread,
Apr 11, 2023, 7:00:01 AM4/11/23
to pypsa
Hi Riadh -

To be clear: the example(s) I shared earlier on this thread were exclusively to illustrate how you can force a constraint on aggregate energy from any given generation source over the course of a pypsa modelling period. They were not attempting to do anything more that that.

Your follow up questions are raising a different, more comprehensive, set of issues. I'm attaching a more extensive example that tries to address these. While still very artificial, this is obviously a significantly more complicated notebook. So please ask further questions if there are aspects that are difficult to understand. I have included an option to forcibly disallow VRE curtailment (by making `p_min_pu` equal to `p_max_pu` as suggested in another thread): this works, in the sense that there is indeed no curtailment of VRE output power relative to what is available; but you may be surprised by the result. Depending on the relative costs, this particular model can effectively find another way to simply "discard" any "excess" generation by running both the charge and discharge links to the storage simultaneously and thus "losing" an arbitrary amount of energy in the efficiency losses in each direction. One could, of course, modify the model to disallow this also: but some care is required to ensure that there remains some feasible way of solving within all the imposed constraints.  

Note that this example does not require the use of any custom constraints; in particular, it does not incorporate the earlier possibility to constrain aggregate generation from a given source - but if you wanted to integrate that also, it should be fairly clear how to do so.

Regards - Barry

VRE-storage-FF-OPF-minimal.ipynb
Reply all
Reply to author
Forward
0 new messages