Optimizing the loss of exergy

Hi everyone,
I am a student and try to model an energy system that is not only efficient in energy use but also in the exergy use. Which means that in a system with power, heat and gas as “busses” (carrieres), energy which is low in exergy as heat from district heating should be used for the low exergy demand.
Therefore I want to define a quality factor (exergetic efficiency) for all nodes (busses and components), with the aim to minimize the difference of the input flow and the output flow (exergy loss) of the whole energy system instead of minimizing the costs.
As a first step, my system is just comprised of sources, sinks and busses (no transformers) and now my programming problem is to access the inflowing and outflowing flows to a bus.

I tried to add following objective_expression to the block Bus:

` def _objective_expression(self, group=None):

m = self.parent_block()
exergy_loss = 0 
if group is None:
    return None

for n in group:
    for i, o in m.FLOWS:
        for t in m.TIMESTEPS:
            if m.flows[i, o].exergy_factor[0] is not None: 
                    exergy_loss += (m.flow[i, n, t] * m.timeincrement[t] *
                                     m.flows[i, o].exergy_factor[t])
                    exergy_loss += (- m.flow[n, o, t] * m.timeincrement[t] *
                                    m.flows[i, o].exergy_factor[t])

self.exergy_loss = Expression(expr=exergy_loss)
return exergy_loss`

But I think that e.g. m.flow[i, n, t] for the inputs in my bus doesen’t work. Does anybody know how to fix this?

Do you get an error?

Welcome to the oemof community :smiley:

If I understand it correctly you do not need to do this. If you want to multiplicate a factor with the flow variable in the objective function you can use the ‘variable_costs’ attribute because this is exactly the purpose of this attribute.

Maybe the name ‘costs’ is confusing but it is difficult to find a general naming and in my understanding costs could be more than just monetary costs. You could also see it as 'weight` which could be used for emissions or any factor you want to multiplicate with the flow.

@uwe is right, nevertheless if you want to do want you tried I think it might be easier to add the expression externally at the end of your script, otherwise you would need to understand the complete internal logic of oemof.solph.

if you have your Model ‘m’ and the energysystem object es something like this (untested):

```
expr = 0 
 buses = [n for n in es.nodes if isinstance(n, Bus)]  # select all buses
 for b in buses: 
      for i in b.inputs: 
             expr += sum(m.flow[i,n,t] * m.flows[i,n].exergy_cost for t in m.TIMESTEPS) 
      for o in b.outputs:
             expr += -sum(m.flow[n,o,t] * m.flows[n,o].exergy_cost for t in m.TIMESTEPS) 
 # add expression to objective function ot the model 
  m.objective += expr
```

Of course you can add checks

Thank you for your the quick responses.

#uwe:The problem I have with the ‘variable_costs’ is, that I cannot build a difference (the exergy loss) within a node.
E.g. for a simple heating system with a flow of 100:
Source: District heating, exergy_factor=0.226 (because Temp. is 90°C), feeds into target 'b_thermic’
Sink: Heat Demand, exergy_factor=0.156 (because Temp. is 60°C), gets energy from source ‘b_thermic’

So the exergy loss of the bus ‘b_themic’ should be:
Exergy In: (100*0.226)=22.6
Exergy Out: (22.6*0.156)= 3.5
Loss= 22.6-3.5=19.1
And this loss should be minimized. I think the building of this difference is not possible with variable costs. Especially as they are defined in the class flow(SimpleBlock). I hope this explanation is clear.

#simnh: this solution looks good. When you say “externally at the end of your script” do you mean the executing file?

yes somewhere in the .py file where the model exist and is completely constructed except from your additional objective expression.

The objective function is just a sum. If the ‘variable_costs’ for one flow is 0.226 and for the other flow -0.156 in the objective function you will find in_flow x0.226 + outflow x (-0.156). In case both flows are 100 you will get 19.1. But maybe I need to know more about your model to understand the problem.

@simnh: with your code, I get the error ‘n’ is not defined (means the 'n’s within ‘expr’) .
I can’t see the mistake…

Sorry, I used b as index…so replace by ‘b’ oder loop “for n in buses”

I chanced the code a bit, as it was not working:

om = OperationalModel(es)
expr = 0

buses = [n for n in es.nodes if isinstance(n, Bus)]  # select all buses
for n in buses:
        for i in n.inputs:
            if om.flows[i, n].exergy_factor[0] is not None: 
                expr += sum(om.flow[i, n, t] * om.timeincrement[t] *
                            om.flows[i, n].exergy_factor[t] for t in om.TIMESTEPS)
        for o in n.outputs:
            if om.flows[n, o].exergy_factor[0] is not None:
                expr += -sum(om.flow[n, o, t] * om.timeincrement[t] *
                             om.flows[n, o].exergy_factor[t] for t in om.TIMESTEPS)
om.objective += expr

But now I get into an endless loop and get the error: “RecursionError: maximum recursion depth exceeded in comparison”.

Hello miraStud. To step back a little and ask what your game plan is? And to add some thoughts.

There is a tradition of using exergy analysis to audit and optimize single site energy facilities and to price co-production (for instance, Keenan 1932). But relative little work in relation to energy system optimization models. I worked on a project called deeco from 1995 thru 2005 (Bruckner et al 2003). deeco tracked the intensive state of the system and used heuristic methods to match flows based on their intensive state (defined here). An underlying assumption was intensive/extensive state orthogonality, meaning that one could first determine the intensive state and then second optimize the extensive state of the network without disrupting the prior intensive state. For example, that DC power flows (extensive) could be solved without disturbing the nominal voltage (intensive). The general justification was the presence of sophisticated control systems to ensure just this situation prevailed (maintain voltage, regulate temperatures and pressures, and so on).

A key paper (Groscurth et al 1995) describes the abstract NEMESS model, which underpinned deeco. The use of exergy (as opposed to energy) as a commodity currency was developed further in my MSc (Morrison 2000), as was the concept of exergetic quality (the report contains generalized exergetic quality equations that I have not seen written down elsewhere).

There is a lot of literature on the thermoeconomics (an unnecessarily grandiose term) of single site facilities, but the problem of cost allocation between streams and across multiple products (including cogenerated steam and power) persists with these methods (this same issue arises in economic input/output modeling and joint production pricing, although mathematical work-arounds have been proposed, such as Patterson et al 2006). Incidentally, thermoeconomic analysis is being “replaced” by user-guided brute-force simulation, as tools (such as Aspen Plus and Ebsilon) improve in terms of their user interface and speed.

At the end of the day, the solver knows nothing of commodities, currencies, or efficiencies, howsoever conceived. It cares not whether you account for and define your transformation and transport processes using the first or second law. But of course you need to be consistent with your underlying formulation, that goes without saying.

Exergy is clearly more pedagogical that energy. But my feeling is that it is better for models to account using the first law and optionally overlay a second law analysis on the results if that provides additional insights. That then does not burden all model users with the need to understand exergy analysis. Hope this helps, Robbie.

References

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.

Groscurth, Helmuth-M, Thomas Bruckner, and Reiner Kümmel (1995). “Modeling of energy-services supply systems”. Energy – The International Journal. 20 (9): 941–958.

Keenan, JH (1932). “A steam-chart for second-law analysis: a study of thermodynamic availability in the steam power plant”. Mechanical Engineering. 54: 95.

Morrison, Robbie. (2000) Optimizing exergy-services supply networks for sustainability — MSc thesis. Otago University, Dunedin, New Zealand: Physics Department.

Patterson, Murray G, Graeme C Wake, Robert McKibbin, and Anthony O Cole (15 March 2006). “Ecological pricing and transformity: a solution method for systems rarely at general equilibrium”. Ecological Economics. 56 (3): 412–423. ISSN 0921-8009. doi:10.1016/j.ecolecon.2005.09.018.

@robbie.morrison: Thank you for your input. I think I have to rethink my optimization approach.

Please consider my thoughts from above as invalid.

My adapted approach of a simple energy system treated as a black box:
On the basis of (Krause 2017) in order to maximize the exergy efficiency for the whole energy system, I want to minimize the multiplicative inverse of the exergy efficiency. Therefore I have to sum up all exergy flows into and out of the system and divide ExergyInput with ExergyOutput (as defined in equation (17) of Krause 2017).
So the code should look similar to that:

buses = [n for n in es.nodes if isinstance(n, Bus)]

I = {}
O = {}
for n in buses:
    I[n] = [i for i in n.inputs]
    O[n] = [o for o in n.outputs]

inEx = 0
outEx = 0

for n in buses:
    for i in I[n]:
        if om.flows[i, n].exergy_factor[0] is not None: 
            inEx += sum(om.flow[i, n, t] * om.timeincrement[t] *
                        om.flows[i, n].exergy_factor[t] for t in om.TIMESTEPS)
    for o in O[n]:
        if om.flows[n, o].exergy_factor[0] is not None:
            outEx += sum(om.flow[n, o, t] * om.timeincrement[t] *
                         om.flows[n, o].exergy_factor[t] for t in om.TIMESTEPS)
expr = inEx/outEx
om.objective += expr

But I still have the problem with the endless loop jumping from:
File “…\pyomo\core\base\expr_coopr3.py”, line 407, in polynomial_degree
x_degree = x.polynomial_degree()
to:
File “…\pyomo\core\base\expression.py”, line 102, in polynomial_degree
return self.expr.polynomial_degree()

Ref:
Krause, T. (2017). Maximizing exergy efficiency in multi-carrier energy systems - IEEE Conference Publication.

Hello miraStud. I just had a quick look at Krause et al (2010) but have nothing sensible to add beyond correcting your citation (I guess I have the right one?). Plus a couple of other interesting looking publications. Robbie

References

Abeysekera, Muditha, Jianzhong Wu, and Nick Jenkins (9 May 2016). Integrated energy systems: an overview of benefits, analysis methods, research gaps and opportunities. United Kingdom: HubNet Association.

Di Somma, M, B Yan, N Bianco, G Graditi, P B Luh, L Mongibello, and V Naso (1 October 2015). “Operation optimization of a distributed energy system considering energy costs and exergy efficiency”. Energy Conversion and Management. 103: 739–751. ISSN 0196-8904. doi:10.1016/j.enconman.2015.07.009.

Krause, Thilo, Florian Kienzle, Simon Art, and Göran Andersson (July 2010). Maximizing exergy efficiency in multi-carrier energy systems. doi:10.1109/PES.2010.5589999. IEEE PES General Meeting. pp 1–8.

I am not an expert in exergy analysis, but in linear programming you may not divide decision variables (only linear functions for constraints and objective function expressions are allowed).

Though, there are other approaches. Right now however, this seems to be a pyomo problem still (you should be able to formulate the problem, even if you will probably not be able to solve it).

Independently of the problem type (LP, NLP) Pyomo breaks down before the problem can be passed to a solver.

Did you try to change the building process of the expression e.g. by not assigning “+=” and just returning one expression?

It might also be a bug and you could ask within the Pyomo forum if rebuilding the expression doesn’t help.

@robbie.morrison: Yes, you are right with your citation of Krause et al (2010)

You are right with your statement above.
The problem is that I want to minimize the difference of Input and Output exergy flows for each ‘Bus’ separately. Which means that e.g. the heat load should be covered with sources with a similar energy quality (here I named it “exergy_factor”). That would lead to a small difference in exergy loss for each Bus.
And as a final step the whole energy system should be optimized. Is that even possible in oemof? As far as I know there is no attempt for multiple objective optimization?

Actually there is/was an attempt to do it by Matteo Prina but I don’t know the state of his work.

By now I do not think that this is necessary to solve your problem. You could just build up an en(/x)ergy system with different temperature levels and define the exergy losses if exergy is transferred to another temperature level. In such a model you could minimise the use of exergy and this will implicitly minimise the exergy losses if you have a defined demand.