I'm using pyomo to model a system consisting of subsystems which themselves consist of subsystems. Therefore, I use nested Blocks. Every block is described in a separate file. I want to be able to switch which models I use as subsystems. I'm wondering whether it's possible to give the outermost model the inner models as arguments. While this is straight forward for one level of hierarchy (a block in the outermost model), I can't seem to find a way to make two levels of hierarchy work (pass the innermost model to the 1st level). This would be very handy as I could change which inner models I load without changing the import model from file in the model file's headers all the time.
Here is an example code that shows my approach. For what I want to do I would need to be able to pass non-model related arguments to the pyo.Block function, which doesn't seem to work.
## model file
import pyomo.environ as pyo
def func_model(subblock,subsubblock):
model = pyo.AbstractModel()
model.idx = pyo.RangeSet()
model.subblock = pyo.Block(model.idx, **subsubblock**, rule = subblock)
return model
### script
def subblock(submodel,idx, **subsubblock**):
submodel.jdx = pyo.RangeSet()
submodel.subsubblock = pyo.Block(submodel.jdx, rule = subsubblock)
return submodel
def subsubblock(subsubmodel):
subsubmodel.kdx = pyo.RangeSet()
return subsubmodel
Now I pass the data from a dict
data = {
None: {
"idx" : {None: [1,2]},
"subblock": {
"jdx": {None: [1]},
"subsubblock": {
"kdx": {None: [1,2,3]}
}
}
}
}
and create the instance
model = func_model(subblock, subsubblock)
instance = model.create_instance(data)
Now checking the model.pprint() results in
2 Set Declarations
subblock_index : Size=1, Index=None, Ordered=True
Key : Dimen : Domain : Size : Members
None : 2 : idx*subblock_index_1 : 0 : {}
subblock_index_1 : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 1 : {None,}
2 RangeSet Declarations
idx : Dimen=1, Size=0, Bounds=(None, None)
Key : Finite : Members
None : True : []
kdx : Dimen=1, Size=0, Bounds=(None, None)
Key : Finite : Members
None : True : []
1 Block Declarations
subblock : Size=0, Index=subblock_index, Active=True
5 Declarations: idx subblock_index_1 subblock_index subblock kdx
So the nesting didn't work.
Edit:
As pointed out by jsiirola, nesting in combination with abstract models can get very cumbersome. So I tried to model it using concrete models and nest the blocks "by hand".
import pyomo.environ as pyo
def func_model():
model = pyo.AbstractModel()
model.idx_lb = pyo.Param()
model.idx_ub = pyo.Param()
model.idx = pyo.RangeSet(model.idx_lb, model.idx_ub)
#model.subblock = pyo.Block(model.idx, rule = subblock)
return model
def subblock():
submodel = pyo.AbstractModel()
submodel.jdx_ub = pyo.Param()
submodel.jdx = pyo.RangeSet(submodel.jdx_ub)
#submodel.subsubblock = pyo.Block(submodel.jdx, rule = subsubblock)
return submodel
def subsubblock():
subsubmodel = pyo.AbstractModel()
subsubmodel.kdx_ub = pyo.Param()
subsubmodel.kdx = pyo.RangeSet(subsubmodel.kdx_ub)
return subsubmodel
Then, creating the subsubmodel
ssb_data = {None: {"kdx_ub" : {None: 3}}}
ss_model = subsubblock()
ss_instance = ss_model.create_instance(ssb_data)
and adding this into the submodel
import copy
sb_data = {None: {"jdx_ub": {None: 2}}}
s_model = subblock()
s_instance = s_model.create_instance(sb_data)
def copy_func(m,j):
return copy.deepcopy(ss_instance)
s_instance.subsubblock = pyo.Block(s_instance.jdx, rule = copy_func)
I would also do the same for nesting the subblock into the model.
I think this is much better than the cumbersome abstract model instantiation I would normally do. However, in my real example I have a nested model that is interconnected. E.g., my (outermost) model puts constraints on the subblock's parameters. Is there any way to handle this with the upper approach?