def makeTwoTermIndexedDisjunction_BoundedVars(): """Two-term indexed disjunction. Adds nothing to above--exists for historic reasons""" m = ConcreteModel() m.s = Set(initialize=[1, 2, 3]) m.a = Var(m.s, bounds=(-100, 100)) def disjunct_rule(d, s, flag): m = d.model() if flag: d.c = Constraint(expr=m.a[s] >= 6) else: d.c = Constraint(expr=m.a[s] <= 3) m.disjunct = Disjunct(m.s, [0, 1], rule=disjunct_rule) def disjunction_rule(m, s): return [m.disjunct[s, flag] for flag in [0, 1]] m.disjunction = Disjunction(m.s, rule=disjunction_rule) return m
def makeTwoTermMultiIndexedDisjunction(): """Two-term indexed disjunction with tuple indices""" m = ConcreteModel() m.s = Set(initialize=[1, 2]) m.t = Set(initialize=['A', 'B']) m.a = Var(m.s, m.t, bounds=(2, 7)) def d_rule(disjunct, flag, s, t): m = disjunct.model() if flag: disjunct.c = Constraint(expr=m.a[s, t] == 0) else: disjunct.c = Constraint(expr=m.a[s, t] >= 5) m.disjunct = Disjunct([0, 1], m.s, m.t, rule=d_rule) def disj_rule(m, s, t): return [m.disjunct[0, s, t], m.disjunct[1, s, t]] m.disjunction = Disjunction(m.s, m.t, rule=disj_rule) return m
def makeNonQuadraticNonlinearGDP(): """We use this in testing between steps--Needed non-quadratic and not additively separable constraint expressions on a Disjunct.""" m = ConcreteModel() m.I = RangeSet(1, 4) m.I1 = RangeSet(1, 2) m.I2 = RangeSet(3, 4) m.x = Var(m.I, bounds=(-2, 6)) # sum of 4-norms... m.disjunction = Disjunction( expr=[[sum(m.x[i]**4 for i in m.I1)**(1/4) + \ sum(m.x[i]**4 for i in m.I2)**(1/4) <= 1], [sum((3 - m.x[i])**4 for i in m.I1)**(1/4) + sum((3 - m.x[i])**4 for i in m.I2)**(1/4) <= 1]]) m.obj = Objective(expr=m.x[2] - m.x[1], sense=maximize) return m
def makeTwoTermIndexedDisjunction(): """Two-term indexed disjunction""" m = ConcreteModel() m.A = Set(initialize=[1, 2, 3]) m.B = Set(initialize=['a', 'b']) m.x = Var(m.A, bounds=(-10, 10)) def disjunct_rule(d, i, k): m = d.model() if k == 'a': d.cons_a = Constraint(expr=m.x[i] >= 5) if k == 'b': d.cons_b = Constraint(expr=m.x[i] <= 0) m.disjunct = Disjunct(m.A, m.B, rule=disjunct_rule) def disj_rule(m, i): return [m.disjunct[i, k] for k in m.B] m.disjunction = Disjunction(m.A, rule=disj_rule) return m
def test_GDP_integer_vars_infeasible(self): m = ConcreteModel() m.x = Var(bounds=(0, 1)) m.y = Var(domain=Integers, bounds=(0, 5)) m.d = Disjunction(expr=[[ m.x >= m.y, m.y >= 3.5 ], [ m.x >= m.y, m.y >= 2.5 ]]) m.o = Objective(expr=m.x) res = SolverFactory('gdpopt').solve( m, strategy='GLOA', mip_solver=mip_solver, nlp_solver=nlp_solver, minlp_solver=minlp_solver ) self.assertEqual(res.solver.termination_condition, TerminationCondition.infeasible)
def test_solve_linear_GDP_unbounded(self): m = ConcreteModel() m.GDPopt_utils = Block() m.x = Var(bounds=(-1, 10)) m.y = Var(bounds=(2, 3)) m.z = Var() m.d = Disjunction(expr=[[m.x + m.y >= 5], [m.x - m.y <= 3]]) m.o = Objective(expr=m.z) m.GDPopt_utils.variable_list = [m.x, m.y, m.z] m.GDPopt_utils.disjunct_list = [ m.d._autodisjuncts[0], m.d._autodisjuncts[1] ] output = StringIO() with LoggingIntercept(output, 'pyomo.contrib.gdpopt', logging.WARNING): solve_linear_GDP(m, GDPoptSolveData(), GDPoptSolver.CONFIG(dict(mip_solver=mip_solver))) self.assertIn( "Linear GDP was unbounded. Resolving with arbitrary bound values", output.getvalue().strip())
def makeNestedDisjunctions(): m = ConcreteModel() m.x = Var(bounds=(-9, 9)) m.z = Var(bounds=(0, 10)) m.a = Var(bounds=(0, 23)) def disjunct_rule(disjunct, flag): m = disjunct.model() if flag: def innerdisj_rule(disjunct, flag): m = disjunct.model() if flag: disjunct.c = Constraint(expr=m.z >= 5) else: disjunct.c = Constraint(expr=m.z == 0) disjunct.innerdisjunct = Disjunct([0, 1], rule=innerdisj_rule) @disjunct.Disjunction([0]) def innerdisjunction(b, i): return [b.innerdisjunct[0], b.innerdisjunct[1]] disjunct.c = Constraint(expr=m.a <= 2) else: disjunct.c = Constraint(expr=m.x == 2) m.disjunct = Disjunct([0, 1], rule=disjunct_rule) # I want a SimpleDisjunct with a disjunction in it too def simpledisj_rule(disjunct): m = disjunct.model() @disjunct.Disjunct() def innerdisjunct0(disjunct): disjunct.c = Constraint(expr=m.x <= 2) @disjunct.Disjunct() def innerdisjunct1(disjunct): disjunct.c = Constraint(expr=m.x >= 4) disjunct.innerdisjunction = Disjunction( expr=[disjunct.innerdisjunct0, disjunct.innerdisjunct1]) m.simpledisjunct = Disjunct(rule=simpledisj_rule) m.disjunction = Disjunction( expr=[m.simpledisjunct, m.disjunct[0], m.disjunct[1]]) return m
def makeThreeTermIndexedDisj(): m = ConcreteModel() m.s = Set(initialize=[1, 2]) m.a = Var(m.s, bounds=(2, 7)) def d_rule(disjunct, flag, s): m = disjunct.model() if flag == 0: disjunct.c = Constraint(expr=m.a[s] == 0) elif flag == 1: disjunct.c = Constraint(expr=m.a[s] >= 5) else: disjunct.c = Constraint(expr=inequality(2, m.a[s], 4)) m.disjunct = Disjunct([0, 1, 2], m.s, rule=d_rule) def disj_rule(m, s): return [m.disjunct[0, s], m.disjunct[1, s], m.disjunct[2, s]] m.disjunction = Disjunction(m.s, rule=disj_rule) return m
def test_induced_linear_in_disjunct(self): m = ConcreteModel() m.x = Var([0], bounds=(-3, 8)) m.y = Var(RangeSet(2), domain=Binary) m.logical = ConstraintList() m.logical.add(expr=m.y[1] + m.y[2] == 1) m.v = Var([1]) m.v[1].setlb(-2) m.v[1].setub(7) m.bilinear_outside = Constraint(expr=m.x[0] * m.v[1] >= 2) m.disjctn = Disjunction( expr=[[m.x[0] * m.v[1] == 3, 2 * m.x[0] == m.y[1] + m.y[2]], [m.x[0] * m.v[1] == 4]]) TransformationFactory('contrib.induced_linearity').apply_to(m) self.assertEqual( m.disjctn.disjuncts[0].constraint[1].body.polynomial_degree(), 1) self.assertEqual(m.bilinear_outside.body.polynomial_degree(), 2) self.assertEqual( m.disjctn.disjuncts[1].constraint[1].body.polynomial_degree(), 2)
def build_model(): m = ConcreteModel() m.x1 = Var(domain=NonNegativeReals, bounds=(0, 8)) m.x2 = Var(domain=NonNegativeReals, bounds=(0, 8)) m.c = Var(domain=NonNegativeReals, bounds=(1, 3)) m.y1 = Disjunct() m.y2 = Disjunct() m.y3 = Disjunct() m.y1.constr1 = Constraint(expr=m.x1**2 + m.x2**2 - 1 <= 0) m.y1.constr2 = Constraint(expr=m.c == 2) m.y2.constr1 = Constraint(expr=(m.x1 - 4)**2 + (m.x2 - 1)**2 - 1 <= 0) m.y2.constr2 = Constraint(expr=m.c == 1) m.y3.constr1 = Constraint(expr=(m.x1 - 2)**2 + (m.x2 - 4)**2 - 1 <= 0) m.y3.constr2 = Constraint(expr=m.c == 3) m.GPD123 = Disjunction(expr=[m.y1, m.y2, m.y3]) m.obj = Objective(expr=(m.x1 - 3)**2 + (m.x2 - 2)**2 + m.c, sense=minimize) return m
def makeThreeTermDisjunctionWithOneVarInOneDisjunct(): """This is to make sure hull doesn't create more disaggregated variables than it needs to: Here, x only appears in the first Disjunct, so we only need two copies: one as usual for that disjunct and then one other that is free if either of the second two Disjuncts is active and 0 otherwise. """ m = ConcreteModel() m.x = Var(bounds=(-2,8)) m.y = Var(bounds=(3,4)) m.d1 = Disjunct() m.d1.c1 = Constraint(expr=m.x <= 3) m.d1.c2 = Constraint(expr=m.y >= 3.5) m.d2 = Disjunct() m.d2.c1 = Constraint(expr=m.y >= 3.7) m.d3 = Disjunct() m.d3.c1 = Constraint(expr=m.y >= 3.9) m.disjunction = Disjunction(expr=[m.d1, m.d2, m.d3]) return m
def makeTwoTermDisj_BlockOnDisj(): m = ConcreteModel() m.x = Var(bounds=(0, 1000)) m.y = Var(bounds=(0, 800)) def disj_rule(d, flag): m = d.model() if flag: d.b = Block() d.b.c = Constraint(expr=m.x == 0) d.add_component('b.c', Constraint(expr=m.y >= 9)) d.b.anotherblock = Block() d.b.anotherblock.c = Constraint(expr=m.y >= 11) d.bb = Block([1]) d.bb[1].c = Constraint(expr=m.x == 0) else: d.c = Constraint(expr=m.x >= 80) m.evil = Disjunct([0, 1], rule=disj_rule) m.disjunction = Disjunction(expr=[m.evil[0], m.evil[1]]) return m
def makeTwoTermDisj_Nonlinear(): """Single two-term disjunction which has all of ==, <=, and >= and one nonlinear constraint. """ m = ConcreteModel() m.w = Var(bounds=(2, 7)) m.x = Var(bounds=(1, 8)) m.y = Var(bounds=(-10, -3)) def d_rule(disjunct, flag): m = disjunct.model() if flag: disjunct.c1 = Constraint(expr=m.x >= 2) disjunct.c2 = Constraint(expr=m.w == 3) disjunct.c3 = Constraint(expr=(1, m.x, 3)) else: disjunct.c = Constraint(expr=m.x + m.y**2 <= 14) m.d = Disjunct([0, 1], rule=d_rule) m.disjunction = Disjunction(expr=[m.d[0], m.d[1]]) return m
def test_subproblem_preprocessing_encounters_trivial_constraints(self): m = ConcreteModel() m.x = Var(bounds=(0, 10)) m.z = Var(bounds=(-10, 10)) m.disjunction = Disjunction(expr=[[m.x == 0, m.z >= 4], [m.x + m.z <= 0]]) m.cons = Constraint(expr=m.x*m.z <= 0) m.obj = Objective(expr=-m.z) m.disjunction.disjuncts[0].indicator_var.fix(True) m.disjunction.disjuncts[1].indicator_var.fix(False) SolverFactory('gdpopt').solve(m, strategy='RIC', mip_solver=mip_solver, nlp_solver=nlp_solver, init_strategy='fix_disjuncts') # The real test is that this doesn't throw an error when we preprocess # to solve the first subproblem (in the initialization). The nonlinear # constraint becomes trivial, which we need to make sure is handled # correctly. self.assertEqual(value(m.x), 0) self.assertEqual(value(m.z), 10) self.assertTrue(value(m.disjunction.disjuncts[0].indicator_var)) self.assertFalse(value(m.disjunction.disjuncts[1].indicator_var))
def test_deactivate_without_fixing_indicator(self): m = ConcreteModel() m.x = Var() m.d1 = Disjunct() m.d1.constraint = Constraint(expr=m.x <= 0) m.d = Disjunction(expr=[m.d1, m.x >= 1, m.x >= 5]) d2 = m.d.disjuncts[1].parent_component() self.assertEqual(len(m.component_map(Disjunction)), 1) self.assertEqual(len(m.component_map(Disjunct)), 2) self.assertIsNot(m.d1, d2) self.assertTrue(m.d1.active) self.assertTrue(d2.active) self.assertTrue(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertFalse(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) m.d.disjuncts[0]._deactivate_without_fixing_indicator() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertFalse(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) m.d.disjuncts[1]._deactivate_without_fixing_indicator() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertFalse(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertFalse(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed())
def test_assert_units_consistent_all_components(self): # test all scalar components consistent u = units m = self._create_model_and_vars() m.obj = Objective(expr=m.dx/m.t - m.vx) m.con = Constraint(expr=m.dx/m.t == m.vx) # vars already added m.exp = Expression(expr=m.dx/m.t - m.vx) m.suff = Suffix(direction=Suffix.LOCAL) # params already added # sets already added m.rs = RangeSet(5) m.disj1 = Disjunct() m.disj1.constraint = Constraint(expr=m.dx/m.t <= m.vx) m.disj2 = Disjunct() m.disj2.constraint = Constraint(expr=m.dx/m.t <= m.vx) m.disjn = Disjunction(expr=[m.disj1, m.disj2]) # block tested as part of model m.extfn = ExternalFunction(python_callback_function, units=u.m/u.s, arg_units=[u.m, u.s]) m.conext = Constraint(expr=m.extfn(m.dx, m.t) - m.vx==0) m.cset = ContinuousSet(bounds=(0,1)) m.svar = Var(m.cset, units=u.m) m.dvar = DerivativeVar(sVar=m.svar, units=u.m/u.s) def prt1_rule(m): return {'avar': m.dx} def prt2_rule(m): return {'avar': m.dy} m.prt1 = Port(rule=prt1_rule) m.prt2 = Port(rule=prt2_rule) def arcrule(m): return dict(source=m.prt1, destination=m.prt2) m.arc = Arc(rule=arcrule) # complementarities do not work yet # The expression system removes the u.m since it is multiplied by zero. # We need to change the units_container to allow 0 when comparing units # m.compl = Complementarity(expr=complements(m.dx/m.t >= m.vx, m.dx == 0*u.m)) assert_units_consistent(m)
def makeTwoTermDisj_boxes(): m = ConcreteModel() m.x = Var(bounds=(0, 5)) m.y = Var(bounds=(0, 5)) def d_rule(disjunct, flag): m = disjunct.model() if flag: disjunct.c1 = Constraint(expr=inequality(1, m.x, 2)) disjunct.c2 = Constraint(expr=inequality(3, m.y, 4)) else: disjunct.c1 = Constraint(expr=inequality(3, m.x, 4)) disjunct.c2 = Constraint(expr=inequality(1, m.y, 2)) m.d = Disjunct([0, 1], rule=d_rule) def disj_rule(m): return [m.d[0], m.d[1]] m.disjunction = Disjunction(rule=disj_rule) m.obj = Objective(expr=m.x + 2 * m.y) return m
def makeTwoTermDisj_BlockOnDisj(): """SimpleDisjunction where one of the Disjuncts contains three different blocks: two simple and one indexed""" m = ConcreteModel() m.x = Var(bounds=(0, 1000)) m.y = Var(bounds=(0, 800)) def disj_rule(d, flag): m = d.model() if flag: d.b = Block() d.b.c = Constraint(expr=m.x == 0) d.add_component('b.c', Constraint(expr=m.y >= 9)) d.b.anotherblock = Block() d.b.anotherblock.c = Constraint(expr=m.y >= 11) d.bb = Block([1]) d.bb[1].c = Constraint(expr=m.x == 0) else: d.c = Constraint(expr=m.x >= 80) m.evil = Disjunct([0, 1], rule=disj_rule) m.disjunction = Disjunction(expr=[m.evil[0], m.evil[1]]) return m
def test_reclassify_deactivated_disjuncts(self): m = ConcreteModel() m.d = Disjunct([1, 2, 3]) m.disjunction = Disjunction(expr=[m.d[1], m.d[2], m.d[3]]) m.d[1].deactivate() m.d[2].indicator_var = True m.d[3].indicator_var = False TransformationFactory('gdp.fix_disjuncts').apply_to(m) self.assertTrue(m.d[1].indicator_var.fixed) self.assertFalse(value(m.d[1].indicator_var)) self.assertFalse(m.d[1].active) self.assertEqual(m.d[1].ctype, Block) self.assertTrue(m.d[2].indicator_var.fixed) self.assertTrue(value(m.d[2].indicator_var)) self.assertTrue(m.d[2].active) self.assertTrue(m.d[3].indicator_var.fixed) self.assertFalse(value(m.d[3].indicator_var)) self.assertFalse(m.d[3].active) self.assertEqual(m.d[1].ctype, Block) self.assertEqual(m.d[2].ctype, Block)
def localVar(): """Two-term disjunction which declares a local variable y on one of the disjuncts, which is used in the objective function as well. Used to test that we will treat y as global in the transformations, despite where it is declared. """ # y appears in a global constraint and a single disjunct. m = ConcreteModel() m.x = Var(bounds=(0, 3)) m.disj1 = Disjunct() m.disj1.cons = Constraint(expr=m.x >= 1) m.disj2 = Disjunct() m.disj2.y = Var(bounds=(1, 3)) m.disj2.cons = Constraint(expr=m.x + m.disj2.y == 3) m.disjunction = Disjunction(expr=[m.disj1, m.disj2]) # This makes y global actually... But in disguise. m.objective = Objective(expr=m.x + m.disj2.y) return m
def makeTwoTermDisj_IndexedConstraints(): m = ConcreteModel() m.s = Set(initialize=[1, 2]) m.a = Var(m.s) m.b = Block() def disj1_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] == 0 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj1 = Disjunct(rule=disj1_rule) def disj2_rule(disjunct): m = disjunct.model() def c_rule(d, s): return m.a[s] <= 3 disjunct.c = Constraint(m.s, rule=c_rule) m.b.simpledisj2 = Disjunct(rule=disj2_rule) m.b.disjunction = Disjunction(expr=[m.b.simpledisj1, m.b.simpledisj2]) return m
def makeModel(): m = ConcreteModel() m.x = Var(bounds=(0, 5)) m.y = Var(bounds=(0, 5)) def d_rule(disjunct, flag): m = disjunct.model() if flag: disjunct.c1 = Constraint(expr=1 <= m.x <= 2) disjunct.c2 = Constraint(expr=3 <= m.y <= 4) else: disjunct.c1 = Constraint(expr=3 <= m.x <= 4) disjunct.c2 = Constraint(expr=1 <= m.y <= 2) m.d = Disjunct([0, 1], rule=d_rule) def disj_rule(m): return [m.d[0], m.d[1]] m.disjunction = Disjunction(rule=disj_rule) m.obj = Objective(expr=m.x + 2 * m.y) return m
def makeDuplicatedNestedDisjunction(): m = ConcreteModel() m.x = Var(bounds=(0, 8)) def outerdisj_rule(d, flag): m = d.model() if flag: def innerdisj_rule(d, flag): m = d.model() if flag: d.c = Constraint(expr=m.x >= 2) else: d.c = Constraint(expr=m.x == 0) d.innerdisjunct = Disjunct([0, 1], rule=innerdisj_rule) d.innerdisjunction = Disjunction(expr=[d.innerdisjunct[0], d.innerdisjunct[1]]) d.duplicateddisjunction = Disjunction(expr=[d.innerdisjunct[0], d.innerdisjunct[1]]) else: d.c = Constraint(expr=m.x == 8) m.outerdisjunct = Disjunct([0, 1], rule=outerdisj_rule) m.disjunction = Disjunction(expr=[m.outerdisjunct[0], m.outerdisjunct[1]]) return m
def _apply_to(self, instance, **kwds): # TODO: This data should be stored differently. We cannot nest this transformation with itself if getattr(instance, 'bilinear_data_', None) is None: instance.bilinear_data_ = Block() instance.bilinear_data_.cache = {} instance.bilinear_data_.vlist = VarList() instance.bilinear_data_.vlist_boolean = [] instance.bilinear_data_.IDX = Set() instance.bilinear_data_.disjuncts_ = Disjunct(instance.bilinear_data_.IDX*[0,1]) instance.bilinear_data_.disjunction_data = {} instance.bilinear_data_.o_expr = {} instance.bilinear_data_.c_body = {} # # Iterate over all blocks # for block in instance.block_data_objects( active=True, sort=SortComponents.deterministic ): self._transformBlock(block, instance) # # WEH: I wish I had a DisjunctList and DisjunctionList object... # def rule(block, i): return instance.bilinear_data_.disjunction_data[i] instance.bilinear_data_.disjunction_ = Disjunction(instance.bilinear_data_.IDX, rule=rule)
m.D2 = Disjunct( m.I, m.t) # case where level in next tank is at or above inlet height for t in m.t: for i in range(1, Ntank): disj = m.D2[i, t] disj.high_level = Constraint(expr=m.L[i + 1, t] >= m.H[i + 1]) disj.high_dynamics = Constraint(expr=m.delL[i, t] == m.L[i, t] - (m.L[i + 1, t] - m.H[i + 1])) m.BigM[disj.high_level] = 0.3 def _disjunction_rule(m, i, t): return [m.D1[i, t], m.D2[i, t]] m.djcn = Disjunction(m.I, m.t, rule=_disjunction_rule) # add rate of change constraints to valve opening problem @m.ConstraintList() def RoC(m): t = 0 for tplus in m.t: if tplus > 0: for i in m.I: yield (m.w[i, tplus] - m.w[i, t])**2 <= 0.04 yield (m.w0[tplus] - m.w0[t])**2 <= 0.04 t = tplus
def build_model(use_cafaro_approximation, num_stages): """Build the model.""" m = common.build_model(use_cafaro_approximation, num_stages) # list of tuples (num_modules, module_size) configurations_list = [] for size in m.module_sizes: configs = [(i + 1, size) for i in range(m.max_num_modules[size])] configurations_list += configs # Map of config indx: (# modules, module size) m.configurations_map = {(k + 1): v for k, v in enumerate(configurations_list)} m.module_index_set = RangeSet(len(configurations_list)) m.module_config_active = Var( m.valid_matches, m.stages, m.module_index_set, doc="Binary for if which module configuration is active for a match.", domain=Binary, initialize=0) @m.Param(m.module_index_set, doc="Area of each configuration") def module_area(m, indx): num_modules, size = m.configurations_map[indx] return num_modules * size @m.Param(m.valid_matches, m.module_index_set, doc="Area cost for each modular configuration.") def modular_size_cost(m, hot, cold, indx): num_modules, size = m.configurations_map[indx] return num_modules * m.module_area_cost[hot, cold, size] @m.Param(m.valid_matches, m.module_index_set, doc="Fixed cost for each modular exchanger size.") def modular_fixed_cost(m, hot, cold, indx): num_modules, size = m.configurations_map[indx] return num_modules * m.module_fixed_unit_cost m.LMTD_discretize = Var(m.hot_streams, m.cold_streams, m.stages, m.module_index_set, doc="Discretized log mean temperature difference", bounds=(0, 500), initialize=0) for hot, cold, stg in m.valid_matches * m.stages: disj = m.exchanger_exists[hot, cold, stg] disj.choose_one_config = Constraint(expr=sum( m.module_config_active[hot, cold, stg, indx] for indx in m.module_index_set) == 1) disj.exchanger_area_cost = Constraint( expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 == sum( m.modular_size_cost[hot, cold, indx] * 1E-3 * m.module_config_active[hot, cold, stg, indx] for indx in m.module_index_set)) disj.exchanger_fixed_cost = Constraint( expr=m.exchanger_fixed_cost[stg, hot, cold] == sum( m.modular_fixed_cost[hot, cold, indx] * 1E-3 * m.module_config_active[hot, cold, stg, indx] for indx in m.module_index_set)) disj.discretize_area = Constraint( expr=m.exchanger_area[stg, hot, cold] == sum( m.module_area[indx] * m.module_config_active[hot, cold, stg, indx] for indx in m.module_index_set)) disj.discretized_LMTD = Constraint(expr=m.LMTD[hot, cold, stg] == sum( m.LMTD_discretize[hot, cold, stg, indx] for indx in m.module_index_set)) @disj.Constraint(m.module_index_set) def discretized_LMTD_LB(disj, indx): return (m.LMTD[hot, cold, stg].lb * m.module_config_active[hot, cold, stg, indx] ) <= m.LMTD_discretize[hot, cold, stg, indx] @disj.Constraint(m.module_index_set) def discretized_LMTD_UB(disj, indx): return m.LMTD_discretize[hot, cold, stg, indx] <= ( m.LMTD[hot, cold, stg].ub * m.module_config_active[hot, cold, stg, indx]) disj.exchanger_required_area = Constraint( expr=m.U[hot, cold] * sum(m.module_area[indx] * m.LMTD_discretize[hot, cold, stg, indx] for indx in m.module_index_set) >= m.heat_exchanged[hot, cold, stg]) @m.Disjunct(m.module_sizes) def module_type(disj, size): """Disjunct for selection of one module type.""" @disj.Constraint(m.valid_matches, m.stages, m.module_index_set) def no_other_module_types(_, hot, cold, stg, indx): # num_modules, size = configurations_map[indx] if m.configurations_map[indx][1] != size: return m.module_config_active[hot, cold, stg, indx] == 0 else: return Constraint.NoConstraint # disj.no_other_module_types = Constraint( # expr=sum( # m.module_config_active[hot, cold, stg, indx] # for indx in m.module_index_set # if m.configurations_map[indx][1] != size) == 0 # ) m.select_one_module_type = Disjunction( expr=[m.module_type[area] for area in m.module_sizes]) return m
def build_model(use_cafaro_approximation, num_stages): """Build the model.""" m = common.build_model(use_cafaro_approximation, num_stages) m.possible_sizes = Set(initialize=[10 * (i + 1) for i in range(50)]) m.module_size_active = Var( m.valid_matches, m.stages, m.possible_sizes, doc="Total area of modular exchangers for each match.", domain=Binary, initialize=0) num_modules_required = {} for size in m.possible_sizes: # For each possible size, calculate the number of exchangers of each # area required to satisfy that size. # Use progressively smaller exchangers to satisfy the size remaining_size = size module_sizes = sorted(m.module_sizes, reverse=True) for area in module_sizes: num_modules_required[size, area] = remaining_size // area remaining_size = remaining_size % area @m.Param(m.valid_matches, m.possible_sizes, m.module_sizes, doc="Number of exchangers of each area required to " "yield a certain total size.") def modular_num_exchangers(m, hot, cold, size, area): return num_modules_required[size, area] @m.Param(m.valid_matches, m.possible_sizes, doc="Area cost for each modular exchanger size.") def modular_size_cost(m, hot, cold, size): return sum(m.modular_num_exchangers[hot, cold, size, area] * m.module_area_cost[hot, cold, area] for area in m.module_sizes) @m.Param(m.valid_matches, m.possible_sizes, doc="Fixed cost for each modular exchanger size.") def modular_fixed_cost(m, hot, cold, size): return sum(m.modular_num_exchangers[hot, cold, size, area] * m.module_fixed_unit_cost for area in m.module_sizes) m.LMTD_discretize = Var(m.hot_streams, m.cold_streams, m.stages, m.possible_sizes, doc="Discretized log mean temperature difference", bounds=(0, 500), initialize=0) for hot, cold, stg in m.valid_matches * m.stages: disj = m.exchanger_exists[hot, cold, stg] disj.conventional = Disjunct() if not use_cafaro_approximation: disj.conventional.exchanger_area_cost = Constraint( expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= m.exchanger_area_cost_factor[hot, cold] * 1E-3 * m.exchanger_area[stg, hot, cold]**m.area_cost_exponent) else: disj.conventional.exchanger_area_cost = Constraint( expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= m.exchanger_area_cost_factor[hot, cold] * 1E-3 * m.cafaro_k * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1)) m.BigM[disj.conventional.exchanger_area_cost] = 100 disj.conventional.exchanger_fixed_cost = Constraint( expr=m.exchanger_fixed_cost[ stg, hot, cold] == m.exchanger_fixed_unit_cost[hot, cold]) @disj.conventional.Constraint(m.possible_sizes) def no_modules(_, size): return m.module_size_active[hot, cold, stg, size] == 0 # Area requirement disj.conventional.exchanger_required_area = Constraint( expr=m.exchanger_area[stg, hot, cold] * m.U[hot, cold] * m.LMTD[hot, cold, stg] >= m.heat_exchanged[hot, cold, stg]) m.BigM[disj.conventional.exchanger_required_area] = 5000 disj.modular = Disjunct() disj.modular.choose_one_config = Constraint(expr=sum( m.module_size_active[hot, cold, stg, size] for size in m.possible_sizes) == 1) disj.modular.exchanger_area_cost = Constraint( expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 == sum( m.modular_size_cost[hot, cold, size] * 1E-3 * m.module_size_active[hot, cold, stg, size] for size in m.possible_sizes)) disj.modular.exchanger_fixed_cost = Constraint( expr=m.exchanger_fixed_cost[stg, hot, cold] == sum( m.modular_fixed_cost[hot, cold, size] * 1E-3 * m.module_size_active[hot, cold, stg, size] for size in m.possible_sizes)) disj.modular.discretize_area = Constraint( expr=m.exchanger_area[stg, hot, cold] == sum( area * m.module_size_active[hot, cold, stg, area] for area in m.possible_sizes)) disj.modular.discretized_LMTD = Constraint( expr=m.LMTD[hot, cold, stg] == sum(m.LMTD_discretize[hot, cold, stg, size] for size in m.possible_sizes)) @disj.modular.Constraint(m.possible_sizes) def discretized_LMTD_LB(disj, size): return (m.LMTD[hot, cold, stg].lb * m.module_size_active[hot, cold, stg, size] ) <= m.LMTD_discretize[hot, cold, stg, size] @disj.modular.Constraint(m.possible_sizes) def discretized_LMTD_UB(disj, size): return m.LMTD_discretize[hot, cold, stg, size] <= ( m.LMTD[hot, cold, stg].ub * m.module_size_active[hot, cold, stg, size]) disj.modular.exchanger_required_area = Constraint( expr=m.U[hot, cold] * sum(area * m.LMTD_discretize[hot, cold, stg, area] for area in m.possible_sizes) >= m.heat_exchanged[hot, cold, stg]) disj.modular_or_not = Disjunction( expr=[disj.modular, disj.conventional]) return m
def test_construct_implicit_disjuncts(self): m = ConcreteModel() m.x = Var() m.y = Var() m.d = Disjunction(expr=[m.x <= 0, m.y >= 1]) self.assertEqual(len(m.component_map(Disjunction)), 1) self.assertEqual(len(m.component_map(Disjunct)), 1) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[0][:2], "d_") disjuncts = m.d.disjuncts self.assertEqual(len(disjuncts), 2) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.x) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.y) # Test that the implicit disjuncts get a unique name m.add_component('e_disjuncts', Var()) m.e = Disjunction(expr=[m.y <= 0, m.x >= 1]) self.assertEqual(len(m.component_map(Disjunction)), 2) self.assertEqual(len(m.component_map(Disjunct)), 2) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[1][:12], "e_disjuncts_") disjuncts = m.e.disjuncts self.assertEqual(len(disjuncts), 2) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.y) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.x) self.assertEqual(len(disjuncts[0].parent_component().name), 13) self.assertEqual(disjuncts[0].name[:12], "e_disjuncts_") # Test that the implicit disjuncts can be lists/tuples/generators def _gen(): yield m.y <= 4 yield m.x >= 5 m.f = Disjunction(expr=[[m.y <= 0, m.x >= 1], ( m.y <= 2, m.x >= 3), _gen()]) self.assertEqual(len(m.component_map(Disjunction)), 3) self.assertEqual(len(m.component_map(Disjunct)), 3) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[2][:12], "f_disjuncts") disjuncts = m.f.disjuncts self.assertEqual(len(disjuncts), 3) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.y) self.assertEqual(disjuncts[0].constraint[1].upper, 0) self.assertIs(disjuncts[0].constraint[2].body, m.x) self.assertEqual(disjuncts[0].constraint[2].lower, 1) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.y) self.assertEqual(disjuncts[1].constraint[1].upper, 2) self.assertIs(disjuncts[1].constraint[2].body, m.x) self.assertEqual(disjuncts[1].constraint[2].lower, 3) self.assertIs(disjuncts[2].parent_block(), m) self.assertIs(disjuncts[2].constraint[1].body, m.y) self.assertEqual(disjuncts[2].constraint[1].upper, 4) self.assertIs(disjuncts[2].constraint[2].body, m.x) self.assertEqual(disjuncts[2].constraint[2].lower, 5) self.assertEqual(len(disjuncts[0].parent_component().name), 11) self.assertEqual(disjuncts[0].name, "f_disjuncts[0]")
def build_nonexclusive_model(): m = ConcreteModel() m.streams = RangeSet(25) m.x = Var(m.streams, bounds=(0, 50), initialize=5) m.stage1_split = Constraint(expr=m.x[1] == m.x[2] + m.x[4]) m.unit1 = Disjunction(expr=[ [ # Unit 1 m.x[2] == exp(m.x[3]) - 1, ], [ # No Unit 1 m.x[2] == 0, m.x[3] == 0 ] ]) m.unit2 = Disjunction(expr=[ [ # Unit 2 m.x[5] == log(m.x[4] + 1), ], [ # No Unit 2 m.x[4] == 0, m.x[5] == 0 ] ]) m.stage1_mix = Constraint(expr=m.x[3] + m.x[5] == m.x[6]) m.stage2_split = Constraint(expr=m.x[6] == sum(m.x[i] for i in (7, 9, 11, 13))) m.unit3 = Disjunction(expr=[ [ # Unit 3 m.x[8] == 2 * log(m.x[7]) + 3, m.x[7] >= 0.2, ], [ # No Unit 3 m.x[7] == 0, m.x[8] == 0 ] ]) m.unit4 = Disjunction(expr=[ [ # Unit 4 m.x[10] == 1.8 * log(m.x[9] + 4), ], [ # No Unit 4 m.x[9] == 0, m.x[10] == 0 ] ]) m.unit5 = Disjunction(expr=[ [ # Unit 5 m.x[12] == 1.2 * log(m.x[11]) + 2, m.x[11] >= 0.001, ], [ # No Unit 5 m.x[11] == 0, m.x[12] == 0 ] ]) m.unit6 = Disjunction(expr=[ [ # Unit 6 m.x[15] == sqrt(m.x[14] - 3) * m.x[23] + 1, m.x[14] >= 5, m.x[14] <= 20, ], [ # No Unit 6 m.x[14] == 0, m.x[15] == 0 ] ]) m.stage2_special_mix = Constraint(expr=m.x[14] == m.x[13] + m.x[23]) m.stage2_mix = Constraint(expr=sum(m.x[i] for i in (8, 10, 12, 15)) == m.x[16]) m.stage3_split = Constraint(expr=m.x[16] == sum(m.x[i] for i in (17, 19, 21))) m.unit7 = Disjunction(expr=[ [ # Unit 7 m.x[18] == m.x[17] * 0.9, ], [ # No Unit 7 m.x[17] == 0, m.x[18] == 0 ] ]) m.unit8 = Disjunction(expr=[ [ # Unit 8 m.x[20] == log(m.x[19] ** 1.5) + 2, m.x[19] >= 1, ], [ # No Unit 8 m.x[19] == 0, m.x[20] == 0 ] ]) m.unit9 = Disjunction(expr=[ [ # Unit 9 m.x[22] == log(m.x[21] + sqrt(m.x[21])) + 1, m.x[21] >= 4, ], [ # No Unit 9 m.x[21] == 0, m.x[22] == 0 ] ]) m.stage3_special_split = Constraint(expr=m.x[22] == m.x[23] + m.x[24]) m.stage3_mix = Constraint(expr=m.x[25] == sum(m.x[i] for i in (18, 20, 24))) m.obj = Objective(expr=-10 * m.x[25] + m.x[1]) return m
def build_model(): """ Base Model Optimal solution: Select units 1, 3, 8 Objective value -36.62 """ m = ConcreteModel() m.streams = RangeSet(25) m.x = Var(m.streams, bounds=(0, 50), initialize=5) m.stage1_split = Constraint(expr=m.x[1] == m.x[2] + m.x[4]) m.first_stage = Disjunction(expr=[ [ # Unit 1 m.x[2] == exp(m.x[3]) - 1, m.x[4] == 0, m.x[5] == 0 ], [ # Unit 2 m.x[5] == log(m.x[4] + 1), m.x[2] == 0, m.x[3] == 0 ] ]) m.stage1_mix = Constraint(expr=m.x[3] + m.x[5] == m.x[6]) m.stage2_split = Constraint(expr=m.x[6] == sum(m.x[i] for i in (7, 9, 11, 13))) m.second_stage = Disjunction(expr=[ [ # Unit 3 m.x[8] == 2 * log(m.x[7]) + 3, m.x[7] >= 0.2, ] + [m.x[i] == 0 for i in (9, 10, 11, 12, 14, 15)], [ # Unit 4 m.x[10] == 1.8 * log(m.x[9] + 4), ] + [m.x[i] == 0 for i in (7, 8, 11, 12, 14, 15)], [ # Unit 5 m.x[12] == 1.2 * log(m.x[11]) + 2, m.x[11] >= 0.001, ] + [m.x[i] == 0 for i in (7, 8, 9, 10, 14, 15)], [ # Unit 6 m.x[15] == sqrt(m.x[14] - 3) * m.x[23] + 1, m.x[14] >= 5, m.x[14] <= 20, ] + [m.x[i] == 0 for i in (7, 8, 9, 10, 11, 12)] ]) m.stage2_special_mix = Constraint(expr=m.x[14] == m.x[13] + m.x[23]) m.stage2_mix = Constraint(expr=sum(m.x[i] for i in (8, 10, 12, 15)) == m.x[16]) m.stage3_split = Constraint(expr=m.x[16] == sum(m.x[i] for i in (17, 19, 21))) m.third_stage = Disjunction(expr=[ [ # Unit 7 m.x[18] == m.x[17] * 0.9, ] + [m.x[i] == 0 for i in (19, 20, 21, 22)], [ # Unit 8 m.x[20] == log(m.x[19] ** 1.5) + 2, m.x[19] >= 1, ] + [m.x[i] == 0 for i in (17, 18, 21, 22)], [ # Unit 9 m.x[22] == log(m.x[21] + sqrt(m.x[21])) + 1, m.x[21] >= 4, ] + [m.x[i] == 0 for i in (17, 18, 19, 20)] ]) m.stage3_special_split = Constraint(expr=m.x[22] == m.x[23] + m.x[24]) m.stage3_mix = Constraint(expr=m.x[25] == sum(m.x[i] for i in (18, 20, 24))) m.obj = Objective(expr=-10 * m.x[25] + m.x[1]) return m