diff --git a/pyomo/network/port.py b/pyomo/network/port.py index 93f94461ff3..4f12fce20f8 100644 --- a/pyomo/network/port.py +++ b/pyomo/network/port.py @@ -30,7 +30,7 @@ IPyomoScriptModifyInstance, TransformationFactory from pyomo.core.kernel.component_map import ComponentMap -from pyomo.network.util import replicate_var +from pyomo.network.util import create_var, tighten_var_domain logger = logging.getLogger('pyomo.network') @@ -205,10 +205,6 @@ def is_extensive(self, name): """Return True if the rule for this port member is Port.Extensive""" return self.rule_for(name) is Port.Extensive - def is_conservative(self, name): - """Return True if the rule for this port member is Port.Conservative""" - return self.rule_for(name) is Port.Conservative - def fix(self): """ Fix all variables in the port at their current values. @@ -385,13 +381,23 @@ def _add_from_container(self, port, items): if type(items) is dict: for key, val in iteritems(items): if type(val) is tuple: - port.add(val[0], key, val[1]) + if len(val) == 2: + obj, rule = val + port.add(obj, key, rule) + else: + obj, rule, kwds = val + port.add(obj, key, rule, **kwds) else: port.add(val, key) else: for val in self._initialize: if type(val) is tuple: - port.add(val[0], rule=val[1]) + if len(val) == 2: + obj, rule = val + port.add(obj, rule=rule) + else: + obj, rule, kwds = val + port.add(obj, rule=rule, **kwds) else: port.add(val) @@ -452,7 +458,7 @@ def Equality(port, name, index_set): Port._add_equality_constraint(arc, name, index_set) @staticmethod - def Extensive(port, name, index_set, include_splitfrac=False, + def Extensive(port, name, index_set, include_splitfrac=None, write_var_sum=True): """ Arc Expansion procedure for extensive variable properties @@ -501,23 +507,6 @@ def Extensive(port, name, index_set, include_splitfrac=False, include_splitfrac=include_splitfrac, write_var_sum=write_var_sum) in_vars = Port._Combine(port, name, index_set) - @staticmethod - def Conservative(port, name, index_set): - """ - Arc Expansion procedure for conservative variable properties. - - This procedure is the rule to use when variable quantities should - be conserved without fixing a split for inlets or fixing combinations - for outlet. - - It acts like Extensive but does not introduces a split variable - nor a split constraint. - """ - - port_parent = port.parent_block() - out_vars = Port._Split_Conservative(port, name, index_set) - in_vars = Port._Combine(port, name, index_set) - @staticmethod def _Combine(port, name, index_set): port_parent = port.parent_block() @@ -541,6 +530,9 @@ def _Combine(port, name, index_set): evar = Port._create_evar(port.vars[name], name, eblock, index_set) in_vars.append(evar) + if len(sources) == 1: + tighten_var_domain(port.vars[name], in_vars[0], index_set) + # Create constraint: var == sum of evars # Same logic as Port._Split cname = unique_component_name(port_parent, "%s_%s_insum" % @@ -556,12 +548,11 @@ def rule(m, *args): return in_vars @staticmethod - def _Split(port, name, index_set, include_splitfrac=False, + def _Split(port, name, index_set, include_splitfrac=None, write_var_sum=True): port_parent = port.parent_block() var = port.vars[name] out_vars = [] - no_splitfrac = False dests = port.dests(active=True) if not len(dests): @@ -577,7 +568,8 @@ def _Split(port, name, index_set, include_splitfrac=False, "Cannot fix splitfrac not at 1 for port '%s' with a " "single dest '%s'" % (port.name, dests[0].name)) - no_splitfrac = True + if include_splitfrac is not True: + include_splitfrac = False if len(dests[0].destination.sources(active=True)) == 1: # This is a 1-to-1 connection, no need for evar, just equality. @@ -592,7 +584,7 @@ def _Split(port, name, index_set, include_splitfrac=False, evar = Port._create_evar(port.vars[name], name, eblock, index_set) out_vars.append(evar) - if no_splitfrac: + if include_splitfrac is False: continue # Create and potentially initialize split fraction variables. @@ -627,7 +619,7 @@ def _Split(port, name, index_set, include_splitfrac=False, "splitfracs, please pass the " " include_splitfrac=True argument." % (port.name, arc.name)) - no_splitfrac = True + include_splitfrac = False continue eblock.splitfrac = Var() @@ -647,6 +639,9 @@ def rule(m, *args): con = Constraint(index_set, rule=rule) eblock.add_component(cname, con) + if len(dests) == 1: + tighten_var_domain(port.vars[name], out_vars[0], index_set) + if write_var_sum: # Create var total sum constraint: var == sum of evars # Need to alphanum port name in case it is indexed. @@ -661,7 +656,7 @@ def rule(m, *args): port_parent.add_component(cname, con) else: # OR create constraint on splitfrac vars: sum == 1 - if no_splitfrac: + if include_splitfrac is False: raise ValueError( "Cannot choose to write split fraction sum constraint for " "ports with a single destination or a single Extensive " @@ -676,56 +671,6 @@ def rule(m, *args): return out_vars - @staticmethod - def _Split_Conservative(port, name, index_set): - port_parent = port.parent_block() - var = port.vars[name] - out_vars = [] - no_splitfrac = False - dests = port.dests(active=True) - - if not len(dests): - return out_vars - - if len(dests) == 1: - # No need for splitting on one outlet. - # Make sure they do not try to fix splitfrac not at 1. - splitfracspec = port.get_split_fraction(dests[0]) - if splitfracspec is not None: - if splitfracspec[0] != 1 and splitfracspec[1] is True: - raise ValueError( - "Cannot fix splitfrac not at 1 for port '%s' with a " - "single dest '%s'" % (port.name, dests[0].name)) - - if len(dests[0].destination.sources(active=True)) == 1: - # This is a 1-to-1 connection, no need for evar, just equality. - arc = dests[0] - Port._add_equality_constraint(arc, name, index_set) - return out_vars - - for arc in dests: - eblock = arc.expanded_block - - # Make and record new variables for every arc with this member. - evar = Port._create_evar(port.vars[name], name, eblock, index_set) - out_vars.append(evar) - - # Create var total sum constraint: var == sum of evars - # Need to alphanum port name in case it is indexed. - cname = unique_component_name(port_parent, "%s_%s_outsum" % - (alphanum_label_from_name(port.local_name), name)) - - def rule(m, *args): - if len(args): - return sum(evar[args] for evar in out_vars) == var[args] - else: - return sum(evar for evar in out_vars) == var - - con = Constraint(index_set, rule=rule) - port_parent.add_component(cname, con) - - return out_vars - @staticmethod def _add_equality_constraint(arc, name, index_set): # This function will add the equality constraint if it doesn't exist. @@ -751,10 +696,9 @@ def _create_evar(member, name, eblock, index_set): # before making a new one. evar = eblock.component(name) if evar is None: - evar = replicate_var(member, name, eblock, index_set) + evar = create_var(member, name, eblock, index_set) return evar - class SimplePort(Port, _PortData): def __init__(self, *args, **kwd): diff --git a/pyomo/network/tests/test_arc.py b/pyomo/network/tests/test_arc.py index 4b610798be2..19375af5217 100644 --- a/pyomo/network/tests/test_arc.py +++ b/pyomo/network/tests/test_arc.py @@ -984,14 +984,14 @@ def test_inactive(self): 1 Declarations: v_equality """) - def test_conservative_single_var(self): + def test_extensive_no_splitfrac_single_var(self): m = ConcreteModel() m.x = Var() m.y = Var() m.z = Var() - m.p1 = Port(initialize={'v': (m.x, Port.Conservative)}) - m.p2 = Port(initialize={'v': (m.y, Port.Conservative)}) - m.p3 = Port(initialize={'v': (m.z, Port.Conservative)}) + m.p1 = Port(initialize={'v': (m.x, Port.Extensive, {'include_splitfrac':False})}) + m.p2 = Port(initialize={'v': (m.y, Port.Extensive, {'include_splitfrac':False})}) + m.p3 = Port(initialize={'v': (m.z, Port.Extensive, {'include_splitfrac':False})}) m.a1 = Arc(source=m.p1, destination=m.p2) m.a2 = Arc(source=m.p1, destination=m.p3) @@ -1136,7 +1136,7 @@ def test_extensive_single_var(self): 13 Declarations: x y z p1 p2 p3 a1 a2 a1_expanded a2_expanded p1_v_outsum p2_v_insum p3_v_insum """) - def test_conservative_expansion(self): + def test_extensive_no_splitfrac_expansion(self): m = ConcreteModel() m.time = Set(initialize=[1, 2, 3]) @@ -1147,12 +1147,12 @@ def test_conservative_expansion(self): def source_block(b): b.t = Set(initialize=[1, 2, 3]) b.p_out = Var(b.t) - b.outlet = Port(initialize={'p': (b.p_out, Port.Conservative)}) + b.outlet = Port(initialize={'p': (b.p_out, Port.Extensive, {'include_splitfrac':False})}) def load_block(b): b.t = Set(initialize=[1, 2, 3]) b.p_in = Var(b.t) - b.inlet = Port(initialize={'p': (b.p_in, Port.Conservative)}) + b.inlet = Port(initialize={'p': (b.p_in, Port.Extensive, {'include_splitfrac':False})}) source_block(m.source) load_block(m.load1) @@ -1530,9 +1530,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1558,9 +1558,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1586,9 +1586,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1614,9 +1614,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : 0 : None : None : False : True : Reals + b : 0 : None : None : False : True : Reals + c : 0 : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1657,9 +1657,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1685,9 +1685,9 @@ def test_extensive_expansion(self): 2 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : 0 : None : None : False : True : Reals + b : 0 : None : None : False : True : Reals + c : 0 : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1702,9 +1702,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1730,9 +1730,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1758,9 +1758,9 @@ def test_extensive_expansion(self): 3 Var Declarations flow : Size=3, Index=comp Key : Lower : Value : Upper : Fixed : Stale : Domain - a : 0 : None : None : False : True : NonNegativeReals - b : 0 : None : None : False : True : NonNegativeReals - c : 0 : None : None : False : True : NonNegativeReals + a : None : None : None : False : True : Reals + b : None : None : None : False : True : Reals + c : None : None : None : False : True : Reals mass : Size=1, Index=None Key : Lower : Value : Upper : Fixed : Stale : Domain None : None : None : None : False : True : Reals @@ -1873,4 +1873,4 @@ def test_extensive_expansion(self): if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pyomo/network/util.py b/pyomo/network/util.py index c4b3f25a054..73cb0bc6ef7 100644 --- a/pyomo/network/util.py +++ b/pyomo/network/util.py @@ -11,40 +11,63 @@ from pyomo.core import Var from pyomo.core.base.indexed_component import UnindexedComponent_set -def replicate_var(comp, name, block, index_set=None): - """ - Create a new variable that will have the same indexing set, domain, - and bounds as the provided component, and add it to the given block. - Optionally pass an index set to use that to build the variable, but - this set must be symmetric to comp's index set. - """ +def create_var(comp, name, block, index_set=None): if index_set is None: if comp.is_indexed(): index_set = comp.index_set() else: index_set = UnindexedComponent_set - var_args = {} - # try: - # var_args['domain'] = comp.domain - # except AttributeError: - # pass - # try: - # var_args['bounds'] = comp.bounds - # except AttributeError: - # pass - - new_var = Var(index_set, **var_args) + new_var = Var(index_set) block.add_component(name, new_var) + return new_var + +def _tighten(src, dest): + starting_lb = dest.lb + starting_ub = dest.ub + if not src.is_continuous(): + dest.domain = src.domain + if src.lb is not None: + if starting_lb is None: + dest.setlb(src.lb) + else: + dest.setlb(max(starting_lb, src.lb)) + if src.ub is not None: + if starting_ub is None: + dest.setub(src.ub) + else: + dest.setub(min(starting_ub, src.ub)) + +def tighten_var_domain(comp, new_var, index_set=None): + if index_set is None: + if comp.is_indexed(): + index_set = comp.index_set() + else: + index_set = UnindexedComponent_set + if comp.is_indexed(): for i in index_set: try: # set bounds for every member in case they differ - pass - # new_var[i].domain = comp[i].domain - # new_var[i].setlb(comp[i].lb) - # new_var[i].setub(comp[i].ub) + _tighten(comp[i], new_var[i]) except AttributeError: break + else: + try: + # set bounds for every member in case they differ + _tighten(comp, new_var) + except AttributeError: + pass return new_var + +def replicate_var(comp, name, block, index_set=None): + """ + Create a new variable that will have the same indexing set, domain, + and bounds as the provided component, and add it to the given block. + Optionally pass an index set to use that to build the variable, but + this set must be symmetric to comp's index set. + """ + new_var = create_var(comp, name, block, index_set) + tighten_var_domain(comp, new_var, index_set) + return new_var