Pyomo Blocks: indexed vs not-indexed blocks on time-dependent problems

511 views
Skip to first unread message

becker.ph...@gmail.com

unread,
May 23, 2018, 5:09:02 AM5/23/18
to Pyomo Forum
The Pyomo book proposes this structure of blocks for a time-dependent problem.

There is a Pyomo Block 'Electrical Grid Model' which is repeated for every time step, means indexed by the time series.
model.T = range(120)

def electrical_grid_block(grid, t):
   
<containing: bus, transmission line, generator>
model
.Grid = Block(model.T,rule=electrical_grid_block)

Makes totally sense to me until I want to model blocks which have to refer to a past time step. For example a storage needs the value of its storage level from last time step. The only solution I thought of: skipping the idea of indexed blocks and passing the time series into the storage block like this:

def storage(s):
    s.storage_level = Var(model.T, bounds=(0,300))
    s.power = Var(model.T, bounds=(-2,2))

    # Constraints
    def constr_rule_speicher(s,t):
        if t >= 2:
            return s.storage_level[t] == - s.power[t] * dt + s.storage_level[t-1]
        elif t == 1:
            return s.storage_level[t] == 150.0
    s.storage_constraints = Constraint(model.T, rule=constr_rule_speicher)

model.storage = Block(rule=storage)

I don't like this solution very much because I believe the object oriented manner of problem modeling gets lost.

Any different ideas to mine?

Unfortunately the documentation around Pyomo doesn't give any example for this kind of case.

Michel Jadoul

unread,
May 23, 2018, 7:13:44 AM5/23/18
to pyomo...@googlegroups.com
Personally, I have not understood the purpose or advantage of any indexed object in Pyomo.
(as well as Rules and as well as AbstractModel).

For this reason, I don't use them.
I even stopped using Block objects directly in my models (complicated object).

Instead, I create "almost empty objects" where I add variables and attributes at will, including other "almost empty objects".
I call these empty objects a  SimpleModel .
I have equipped SimpleModel  with a method that scans a SimpleModel  and create a ConcretModel out of it (recusively).
(some overhead, but quite acceptable for me)

This is very convenient for me.
Because the " almost empty objects" have a dedicated meaning in my domain. (equipments, fluids, plants, ...)

I do prefer a full object-oriented approach, based on objects within my domain of application.
I have no taste for mathematical notations to model my problems.
In summary: I talk equipments, fluids, plants, ...

Regarding indexed objects, normal Python lists are sufficient and offer all what we need.

Would like to know your point of view.

Regards,

Michel

Bynum, Michael Lee

unread,
May 23, 2018, 9:27:48 AM5/23/18
to pyomo...@googlegroups.com
I would suggest placing any constraints that span multiple time steps on the top level model. Then the individual blocks for each time step only need to worry about themselves, if that makes any sense.

Michael

--
You received this message because you are subscribed to the Google Groups "Pyomo Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyomo-forum...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Laird, Carl Damon

unread,
May 23, 2018, 1:27:44 PM5/23/18
to pyomo...@googlegroups.com

Hi,

I believe that the Optimal Multi-Period Lot sizing problem in the book addresses this exact issue. Individual blocks are created for each of the time periods, and a constraint is added to link the time periods in the outer level. See that example and, in particular, the constraint:

def i_linking_rule(m, t):

   if t == m.T.first():

      return m.lsb[t].i0 == i0

   return m.lsb[t].i0 == m.lsb[t-1].i

model.i_linking = Constraint(model.T, rule=i_linking_rule)

 

Hope this helps,

 

Carl.

From: <pyomo...@googlegroups.com> on behalf of "becker.ph...@gmail.com" <becker.ph...@gmail.com>
Reply-To: "pyomo...@googlegroups.com" <pyomo...@googlegroups.com>
Date: Wednesday, May 23, 2018 at 4:09 AM
To: Pyomo Forum <pyomo...@googlegroups.com>
Subject: [EXTERNAL] Pyomo Blocks: indexed vs not-indexed blocks on time-dependent problems

 

The Pyomo book proposes this structure of blocks for a time-dependent problem.

mage removed by sender.

--

becker.ph...@gmail.com

unread,
May 24, 2018, 6:49:11 AM5/24/18
to Pyomo Forum
Hey thanks, 

that's the same. It doesn't satisfy me. This loses the object oriented manner. 

In the book it says: "it is generally good object-oriented practice to only reference components from the current or lower levels in the hierarchy. This promotes reusability of your blocks within other models without strong assumptions about the structure of the owning or parent blocks"

Your example needs to know the parent (here model 'm').

With Michael, I disagree for the same reason. The reusability of the block gets lost.
Michel, I wanna try blocks first. 

All the best
Philipp

Laird, Carl Damon

unread,
May 24, 2018, 7:27:20 AM5/24/18
to pyomo...@googlegroups.com

Hi Phillip,

 

I think we are crossing our lines here. I am making exactly the same argument as you (which is exactly the same argument made in the book).

These constraints are NOT added in each of the blocks, rather they are added on the main model and they reference blocks that are below the model in the hierarchy. This is consistent with the quote from the book since it references variables at lower levels in the hierarchy only.

 

Specifically,

-          model.i_linking is the constraint set. It is added to “model”, and not to the block.

-          There is no parent lookup here. The “m” that is passed in is the model that contains the constraints.

 

 

What you seem to be implying is that these constraints look “up” the tree, but that is not the case here. Sometimes, I have seen code where a rule for a block rule contains a line to get a parent. This looks something like the following:

 

      m = b.parent()

 

followed by constraints that are added to b, but reference variables on m. This would be an “upwards” reference.

 

(Having said all of this, there are people that prefer this kind of referencing on purpose. This is a matter of opinion. I don’t recommend it, and in fact, I advocate a model design that is exactly that same as you are advocating here.)

 

Regards,

becker.ph...@gmail.com

unread,
May 24, 2018, 9:12:32 AM5/24/18
to Pyomo Forum
Hey,

thanks for your reply, Carl. I agree with you, the books doesn't propose a "look up". 

What I meant was more at an organizational level. A block remains incomplete without the linking rules in the parent model. Means you cannot "finish" one model and get done another until you have modeled all components, with the goal to put them together in one parent model. 

E.g. one colleague models the oven, one colleague the fridge, another the heating in a room. And the last colleague will use one oven, two fridges and two heatings to put them in his room, the parent model. This won't work. 

Also that's why I understand the use of .parent(). It let's you create the linking rule without knowing the "parent". But you could agree on some parameters of the parent which contains all the submodels. In my case I would agree that the parent model contains the range model.T to represent time and on how the variable name of my block will be in this parent. Also not very convenient.  (parent used here as organizational description)

Is there something I am missing out? I'd love to know what you think.

all the best
Philipp

becker.ph...@gmail.com

unread,
Jun 1, 2018, 10:50:49 AM6/1/18
to Pyomo Forum
Hey all, who are still interested in discussing this. This is the solution I found, I am pretty happy about it. I would like to know what you think.

1. I define different rules for different components. In my case components of an energy grid (storage, CHP, …)

def Storage(s):
       s
.T = Set()  #time
       s
.dt = Set() #time step
       
       
++some Variables++
       
++some Constraints++

def CHP(c):
       s.T = Set()  #time
       s
.dt = Set() #time step
       
       
++some Variables++
       
++some Constraints++

This way I create kind of an abstract library for my types of components I might be using for my optimization problem. 

2. The file where I build the actual model will import these components and create abstract models and fill them with data for the parameters.

import components as C

model
= AbstractModel()

#create an abstract Storage with the Storage rule from above
Storage_1 = AbstractModel(rule=C.Storage)

#create a concrete Storage by specifying e.g. size, power etc.
model
.Storage_1 = Storage_1.create_instance('storage_1.dat')

#and maybe create another different storage
model.Storage_2 = Storage_2.create_instance('storage_2.dat')

#add CHP (combined power and heat)
CHP
= AbstractModel(rule=C.CHP)
model
.CHP = CHP.create_instance('chp.dat')

+++ some constraints here, containing Variables from different Storages or CHP +++
+++ here an objective function +++

model
_instance = model.create_instace()

+++ and solve it and print results+++

Very important to notice: I don't use indexed blocks anymore. The time 'T' and the time step 'dt' are in every.dat File.

E.g. 'storage_1.dat' will look like this:

set T := 1 2 3 4 5 6 7 8 9 10;
param dt
:= 1;
param
Energie_Max := 50;
param
Energie_Initial := 0;
...

Summary: I am quite happy about this solution. It separates the development of the components from the data and the optimization problem. So there are these three parts than can be worked on independently. 

The only thing I don't like is the fact, that I have to add the exact same time 'T' and time step 'dt' in each of the .dat files. Maybe there is a way to import in AMPL. Then it would be perfect. I will find that out next week.

Let me know what you think.

All the best
Philipp
Reply all
Reply to author
Forward
0 new messages