How to achieve "Load_sharing" in oemof energy system simulation?

Hi,

I use Oemof to model a Hybrid Energy System, which is composed of several diesel generators with different characteristics and Photovoltaic production.
By filling all the system constraints and placing a timeseries of grid demand, the optimization solver like “CBC”, “gurobi” generate an optimal solution, which is actually the dispatching order of energy generation sources.

In my case study, I would like to simulate the off-grid situation, where the “load-sharing” notion is frequently used. For example, in an energy system with 10 generators, in order to simplify the frequency control, we aim to make all the generators run in a same percentage of their nominal power.

However, I could not find the related structure in oemof documentation, is there any mecanisme could do the job ? If not, any idea to achieve it ?

Thanks in advance for your help !

I don’t use oemof but here are two suggestions:

  • add a single equality constraint such the output from each of the ten diesel generators must be the same
  • if the ten diesel generators are sufficiently identical and connected within a “copper shield”, then just use one single generator that is an aggregate of all ten

A copper shield approximation (Kupferplatte in German) means that one can ignore transmission losses, line constraints, phase angles, voltage drops, and such to a first approximation.

Maybe someone from oemof can explain how to add an equality constraint. HTH R

Addition: sorry you said duty not output should be equal. That should also be amenable to an equality constraint as well.

Thank you for your suggestions, Robbie,

I thought about an idea similar to the first suggestion before but I am not that familiar with the inside core of Oemof and I have not achieved it yet.
As you said, I do need someone from Oemof to give me a clearer view.

In terms of your second proposal, it could not be true in me case study because I have more than 20 generators in my energy system and there a 4 types of generators with quite different characteristic.

Even though I tried so hard, but I am afraid I did not understand your last sentence, you mean I can try to find out the place where the power supplt/demand balance is implemented in the coding ? and then implement this load_sharing constraint there ?

Hi @NiceJohnny, sorry that my additional comment was a bit obscure. If you want identical outputs then just constrain the outputs to match:

P_1 = P_2 = ..

And if you want identical duties, then divide those outputs by their prevailing capacities:

P_1/C_1 = P_2/C_2 = ..

The P are optimization variables for production and the C are capacity parameters which can either be constant throughout the run or recalculated prior to each timestep. Note too that my formulation is not in classic optimization form with the right‑hand side being constant and one equation per line. You might also need to introduce some dummy variables to reach that classic form too.

For a deeper look, check figure 5 in the following:

  • Bruckner, Thomas, Robbie Morrison, Chris Handley, and Murray Patterson (2003). “High-resolution modeling of energy-services supply systems using ‘‘deeco’’: overview and application to policy development”. Annals of Operations Research. 121 (1–4): 151–180. doi:10.1023/A:1023359303704. Available via Google Scholar.

This diagram shows how entities within a model are designed, initiated, recalculated, and dispatched. And how they track their state and what their information needs and exports are. But that diagram is in the context of sequential optimization. Maybe you are undertaking capacity expansion, in which case some of the features in figure 5 will not apply.

I cannot help with oemof unfortunately. But try hacking! The worst you can do is destroy your virtual world!

Thank you so much for the explanation, I can feel that this figure gives a lot of information even though I don’t unserstand it by now.
I will take a look on it and start to operate the source code :slight_smile:

1 Like

I really do not understand why you want all generators run in the same partial load. From my point of view it is more efficient to run all needed generators in the optimal operation point and just one in partial load. You could use the OffsetTransformer of oemof.solph to get a more realistic picture.

But if you really want to add such an additional constraint you could have a look at the following reference to learn how to add additional constraints.

Just to note too that you can set a prescribed merit order and have the LP solver honor that ranking. This feature is implemented in a model named xeona. The principle problem is that the method does not scale well, perhaps limited to 20 or 40 plant or so, depending on your hardware, solver, and runtime expectations.

Thank you for your advise, @uwe.krien

Actually, I am trying to model an energy system in an off-grid situation, which means that the energy system is not connected to the national or local grid, therefore, the frequency of this isolated network should all be maintained by the diesel generators, since the PV plant is just a follower of the diesel generators frequency.

When there is a lack of power, all of these generators should be able to provide a reserved capacity called spinning reserve to mitigate the energy lack. The same for the case of energy excess, all of the machines should be able to slow down to reduce the power generation.

Supposing that there are N generators, we run N-1 in its maximum capacity, and 1 in partial load, it is obviously that the reserved capacity is not enough to mitigate the grid uncertainty. More over, runing the generators in its maximum capacity or under it optimal minimum range (e.x. 40% of its nominal power) could significantly reduce its life time. That’s why I would like to have the feature of load_sharing.

Thank you again for your help ! By the way, I have another question for you. I use the component “Transformer” to simulate the diesel generator. Inside of this component, I use the nonconvex flow at the outputs for the startup_cost and minimum_uptime and I use the normal flow at the input for my fuel consumption bus. As I understand, without the load_sharing constriant, they should be in the situation like you mentionned, N-1 in maximum capacity and 1 in partial load, but what I get is all of them run in their minimum capacity.

On your opinion, the solver try to minimize the objective expression in the blocks of Nonconvex flow or the objective expression of the blocks of normal flow? Or the solver try to minimize both of them ?

The solver will try to maximize or minimize the goal function as instructed. You need to look at the goal function therefore to determine what is happening. I guess that oemof can readily export the goal function weightings? Otherwise you will need to study the code in the relevant modules and see how the goal function is being formulated. With my usual caveat that I don’t actually use oemof!

That’s what I have done, I read the documentation of oemof and I found the page of oemof.solph.Flow.
https://oemof-solph.readthedocs.io/en/latest/reference/oemof.solph.html#oemof.solph.network.flow.Flow

It is indicated that the if nonconvex object occurs, the mathematical model will be changed, the system constraint from NonConvexFlow will be used instead of Flow.

The figure below is my code when creating a generator, from this figure, we can see that the inputs bus use the blocks of solph.Flow, but the outputs bus use the blocks of solph.NonConvex.


Then, it is the function “_add_objective” in oemof.models.
oemof_models
and the function “_objective_expression” at “class NonConvexFlow(SimpleBlock)” classe in oemof.blocks

I am wondering which is the main objective function or it is multiple objectives function. I am still trying to understand the goal function as you said @robbie.morrison, but oemof is not quite intuitive since it combines the notion of pyomo and the notion of class when creating energy system.

I still have a lot of work to do for my coding skill :slight_smile: , but at least it proves that my direction is correct.

The terms “objective function” and “goal function” are synonyms. The solver will only see one objective function but the various components will need to finalize and supply their own contributions to that single function. Maybe that is what you mean by multiple objective functions?

I don’t use pyomo but I understand that the JuMP library for the julia language is a major improvement:

This might be useful too, just out:

Hendrik Huyskens from the Reiner Lemoine Institute modelled an offgrid system with spinning reserve in oemof.solph. As I know they used the OffsetTransformer class of oemof.solph. I do not think that he is active in this forum so you should contact him directly. I am not sure if he published the code, but he might be open to give it to you.

It still would be nice to post the results here if you got interesting advices.

Thank you very much for your help, @robbie.morrison

I have already taken a course of Python 3, I need more pratical work to get more familiar with the class notion. (Most of the course of Python talks about the data science, the use of pandas, numpy, etc).

I am still working on it and I feel that I am close to the answer.

Thank you very much for your advice ! @uwe.krien

I will try to contact him for this and I will surely come back to post if I can get intersting and useful answer. :slight_smile:

Thank you again for your advice @uwe.krien, I have already sent an email to Hendrik Huyskens, hoping that he is available to take a look on my questions.

In the meantime, I find out something interesting on the oemof example code. In v3.0x, the "Startup_shutdown.py " has a section that show us the process of calculation, but I don’t understand where the 12 units load from ? (see in the picture below)

Oemof_cost_calculation
Do you have any idea ?

Many thanks to @Hendrik Huyskens for providing me his original code and others’ help, I finally finished by getting inspired for the spinning reserve sizing from the code of project micrOgridS, which is also form the Reiner Lemoine Institute, to define a constraint for the pyomo optimization model.

But the feature of “load sharing” is not finisied yet since the code I got is not related to this kind of feature, I will be back if I can achieve it later.

If anyone who wants to get the original code of micrOgridS projet, don’t hesitate to contact me, I can provide the link.

Actually, I think it is possible to have “all at the same load” using oemof.solph.constraints.equate_variables(). You would have to use the Pyomo variables after the model is created, but it should be possible.

Thank you for your advice, @pschoen

It seems to correspond the feature that I have been searching for, that is interesting !

I will take a look at it later once I finished my actual work :slight_smile:

I thought a bit further and I think it is even possible without fiddling with Pyomo variables: You might use a virtual input “load” for all power plants and connect them all to the same transformer. This way, the same virtual “load flow” would go to all plants. This approach is complementary to Eq. (11) in [2012.12664] Simultaneous optimisation of temperature and energy in linear energy system models, if you want a reference.