`BuildAction`

and `BuildCheck`

This is a somewhat advanced topic. In some cases, it is desirable to
trigger actions to be done as part of the model building process. The
`BuildAction`

function provides this capability in a Pyomo model. It
takes as arguments optional index sets and a function to peform the
action. For example,

```
model.BuildBpts = BuildAction(model.J, rule=bpts_build)
```

calls the function `bpts_build`

for each member of `model.J`

. The
function `bpts_build`

should have the model and a variable for the
members of `model.J`

as formal arguments. In this example, the
following would be a valid declaration for the function:

```
def bpts_build(model, j):
```

A full example, which extends the Symbolic Index Sets and Piecewise Linear Expressions examples, is

```
# abstract2piecebuild.py
# Similar to abstract2piece.py, but the breakpoints are created using a build action
from pyomo.environ import *
model = AbstractModel()
model.I = Set()
model.J = Set()
model.a = Param(model.I, model.J)
model.b = Param(model.I)
model.c = Param(model.J)
model.Topx = Param(default=6.1) # range of x variables
model.PieceCnt = Param(default=100)
# the next line declares a variable indexed by the set J
model.x = Var(model.J, domain=NonNegativeReals, bounds=(0,model.Topx))
model.y = Var(model.J, domain=NonNegativeReals)
# to avoid warnings, we set breakpoints beyond the bounds
# we are using a dictionary so that we can have different
# breakpoints for each index. But we won't.
model.bpts = {}
def bpts_build(model, j):
model.bpts[j] = []
for i in range(model.PieceCnt+2):
model.bpts[j].append(float((i*model.Topx)/model.PieceCnt))
# The object model.BuildBpts is not refered to again;
# the only goal is to trigger the action at build time
model.BuildBpts = BuildAction(model.J, rule=bpts_build)
def f4(model, j, xp):
# we not need j in this example, but it is passed as the index for the constraint
return xp**4
model.ComputePieces = Piecewise(model.J, model.y, model.x, pw_pts=model.bpts, pw_constr_type='EQ', f_rule=f4)
def obj_expression(model):
return summation(model.c, model.y)
model.OBJ = Objective(rule=obj_expression)
def ax_constraint_rule(model, i):
# return the expression for the constraint for i
return sum(model.a[i,j] * model.x[j] for j in model.J) >= model.b[i]
# the next line creates one constraint for each member of the set model.I
model.AxbConstraint = Constraint(model.I, rule=ax_constraint_rule)
```

This example uses the build action to create a model component with
breakpoints for a Piecewise Linear Expressions function. The `BuildAction`

is
triggered by the assignment to `model.BuildBpts`

. This object is not
referenced again, the only goal is to cause the execution of
`bpts_build,`

which places data in the `model.bpts`

dictionary.
Note that if `model.bpts`

had been a `Set`

, then it could have been
created with an `initialize`

argument to the `Set`

declaration. Since it is a special-purpose dictionary to support the
Piecewise Linear Expressions functionality in Pyomo, we use a `BuildAction`

.

Another application of `BuildAction`

can be intialization of Pyomo
model data from Python data structures, or efficient initialization of
Pyomo model data from other Pyomo model data. Consider the
Sparse Index Sets example. Rather than using an initialization for
each list of sets `NodesIn`

and `NodesOut`

separately using
`initialize`

, it is a little more efficient and probably a little
clearer, to use a build action.

The full model is:

```
# Isinglebuild.py
# NodesIn and NodesOut are created by a build action using the Arcs
from pyomo.environ import *
model = AbstractModel()
model.Nodes = Set()
model.Arcs = Set(dimen=2)
model.NodesOut = Set(model.Nodes, within=model.Nodes, initialize=[])
model.NodesIn = Set(model.Nodes, within=model.Nodes, initialize=[])
def Populate_In_and_Out(model):
# loop over the arcs and put the end points in the appropriate places
for (i,j) in model.Arcs:
model.NodesIn[j].add(i)
model.NodesOut[i].add(j)
model.In_n_Out = BuildAction(rule = Populate_In_and_Out)
model.Flow = Var(model.Arcs, domain=NonNegativeReals)
model.FlowCost = Param(model.Arcs)
model.Demand = Param(model.Nodes)
model.Supply = Param(model.Nodes)
def Obj_rule(model):
return summation(model.FlowCost, model.Flow)
model.Obj = Objective(rule=Obj_rule, sense=minimize)
def FlowBalance_rule(model, node):
return model.Supply[node] \
+ sum(model.Flow[i, node] for i in model.NodesIn[node]) \
- model.Demand[node] \
- sum(model.Flow[node, j] for j in model.NodesOut[node]) \
== 0
model.FlowBalance = Constraint(model.Nodes, rule=FlowBalance_rule)
```

for this model, the same data file can be used as for Isinglecomm.py in Sparse Index Sets such as the toy data file:

```
set Nodes := CityA CityB CityC ;
set Arcs :=
CityA CityB
CityA CityC
CityC CityB
;
param : FlowCost :=
CityA CityB 1.4
CityA CityC 2.7
CityC CityB 1.6
;
param Demand :=
CityA 0
CityB 1
CityC 1
;
param Supply :=
CityA 2
CityB 0
CityC 0
;
```

Build actions can also be a way to implement data validation,
particularly when multiple Sets or Parameters must be analyzed. However,
the the `BuildCheck`

component is prefered for this purpose. It
executes its rule just like a `BuildAction`

but will terminate the
construction of the model instance if the rule returns `False`

.