def test_empty_singleton(self): a = Constraint() a.construct() # # Even though we construct a SimpleConstraint, # if it is not initialized that means it is "empty" # and we should encounter errors when trying to access the # _ConstraintData interface methods until we assign # something to the constraint. # self.assertEqual(a._constructed, True) self.assertEqual(len(a), 0) try: a() self.fail("Component is empty") except ValueError: pass try: a.body self.fail("Component is empty") except ValueError: pass try: a.lower self.fail("Component is empty") except ValueError: pass try: a.upper self.fail("Component is empty") except ValueError: pass try: a.equality self.fail("Component is empty") except ValueError: pass try: a.strict_lower self.fail("Component is empty") except ValueError: pass try: a.strict_upper self.fail("Component is empty") except ValueError: pass x = Var(initialize=1.0) x.construct() a.set_value((0, x, 2)) self.assertEqual(len(a), 1) self.assertEqual(a(), 1) self.assertEqual(a.body(), 1) self.assertEqual(a.lower(), 0) self.assertEqual(a.upper(), 2) self.assertEqual(a.equality, False) self.assertEqual(a.strict_lower, False) self.assertEqual(a.strict_upper, False)
def test_unconstructed_singleton(self): a = Constraint() self.assertEqual(a._constructed, False) self.assertEqual(len(a), 0) try: a() self.fail("Component is unconstructed") except ValueError: pass try: a.body self.fail("Component is unconstructed") except ValueError: pass try: a.lower self.fail("Component is unconstructed") except ValueError: pass try: a.upper self.fail("Component is unconstructed") except ValueError: pass try: a.equality self.fail("Component is unconstructed") except ValueError: pass try: a.strict_lower self.fail("Component is unconstructed") except ValueError: pass try: a.strict_upper self.fail("Component is unconstructed") except ValueError: pass x = Var(initialize=1.0) x.construct() a.construct() a.set_value((0, x, 2)) self.assertEqual(len(a), 1) self.assertEqual(a(), 1) self.assertEqual(a.body(), 1) self.assertEqual(a.lower(), 0) self.assertEqual(a.upper(), 2) self.assertEqual(a.equality, False) self.assertEqual(a.strict_lower, False) self.assertEqual(a.strict_upper, False)
def Xtest_list_domain_bad_missing(self): with self.assertRaises(ValueError) as cm: self.model = ConcreteModel() self.model.y = Var([1, 2], within=[1, 4, 5])
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`DR` | | | | The set of projects of the :code:`dr` operational type. | +-------------------------------------------------------------------------+ | | :code:`DR_OPR_TMPS` | | | | Two-dimensional set with projects of the :code:`dr` | | operational type and their operational timepoints. | +-------------------------------------------------------------------------+ | | :code:`DR_OPR_HRZS` | | | | Two-dimensional set with projects of the :code:`dr` | | operational type and their operational horizons. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Variables | +=========================================================================+ | | :code:`DR_Shift_Up_MW` | | | *Defined over*: :code:`DR_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | Load added (in MW) in each operational timepoint. | +-------------------------------------------------------------------------+ | | :code:`DR_Shift_Down_MW` | | | *Defined over*: :code:`DR_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | Load removed (in MW) in each operational timepoint. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Constraints | +=========================================================================+ | | :code:`DR_Max_Shift_Up_Constraint` | | | *Defined over*: :code:`DR_OPR_TMPS` | | | | Limits the added load to the available power capacity. | +-------------------------------------------------------------------------+ | | :code:`DR_Max_Shift_Down_Constraint` | | | *Defined over*: :code:`DR_OPR_TMPS` | | | | Limits the removed load to the available power capacity. | +-------------------------------------------------------------------------+ | | :code:`DR_Energy_Balance_Constraint` | | | *Defined over*: :code:`DR_OPR_HRZS` | | | | Ensures no energy losses or gains when shifting load within the horizon.| +-------------------------------------------------------------------------+ | | :code:`DR_Energy_Budget_Constraint` | | | *Defined˚ over*: :code:`DR_OPR_HRZS` | | | | Total energy that can be shifted on each horizon should be less than | | or equal to budget. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.DR = Set( within=m.PROJECTS, initialize=lambda mod: subset_init_by_param_value( mod, "PROJECTS", "operational_type", "dr"), ) m.DR_OPR_TMPS = Set( dimen=2, within=m.PRJ_OPR_TMPS, initialize=lambda mod: list( set((g, tmp) for (g, tmp) in mod.PRJ_OPR_TMPS if g in mod.DR)), ) m.DR_OPR_HRZS = Set( dimen=2, initialize=lambda mod: list( set((g, mod.horizon[tmp, mod.balancing_type_project[g]]) for (g, tmp) in mod.PRJ_OPR_TMPS if g in mod.DR)), ) # Variables ########################################################################### m.DR_Shift_Up_MW = Var(m.DR_OPR_TMPS, within=NonNegativeReals) m.DR_Shift_Down_MW = Var(m.DR_OPR_TMPS, within=NonNegativeReals) # Constraints ########################################################################### m.DR_Max_Shift_Up_Constraint = Constraint(m.DR_OPR_TMPS, rule=max_shift_up_rule) m.DR_Max_Shift_Down_Constraint = Constraint(m.DR_OPR_TMPS, rule=max_shift_down_rule) m.DR_Energy_Balance_Constraint = Constraint(m.DR_OPR_HRZS, rule=energy_balance_rule) m.DR_Energy_Budget_Constraint = Constraint(m.DR_OPR_HRZS, rule=energy_budget_rule)
def build(self): super(ReactionBlockData, self).build() self.reaction_rate = Var(["r1", "r2"]) self.dh_rxn = {"r1": 10, "r2": 20, "e1": 30, "e2": 40}
def b3(b3, i, j, k): b3.v = Var() b3.v1 = Var(m.d1_3) b3.v2 = Var(m.d1_1, m.d1_2) b3.vn = Var(m.d1_1, m.dn, m.d2)
def b1(b1, i): b1.v = Var() b1.v1 = Var(m.d1_3) b1.v2 = Var(m.d1_1, m.d1_2) b1.vn = Var(m.dn, m.d1_2)
def test_generate_cuid_string_map(self): model = Block(concrete=True) model.x = Var() model.y = Var([1, 2]) model.V = Var([('a', 'b'), (1, '2'), (3, 4)]) model.b = Block(concrete=True) model.b.z = Var([1, '2']) setattr(model.b, '.H', Var(['a', 2])) model.B = Block(['a', 2], concrete=True) setattr(model.B['a'], '.k', Var()) model.B[2].b = Block() model.B[2].b.x = Var() model.add_component('c tuple', Constraint(Any)) model.component('c tuple')[(1, )] = model.x >= 0 cuids = ( ComponentUID.generate_cuid_string_map(model, repr_version=1), ComponentUID.generate_cuid_string_map(model), ) self.assertEqual(len(cuids[0]), 29) self.assertEqual(len(cuids[1]), 29) for obj in [ model, model.x, model.y, model.y_index, model.y[1], model.y[2], model.V, model.V_index, model.V['a', 'b'], model.V[1, '2'], model.V[3, 4], model.b, model.b.z, model.b.z_index, model.b.z[1], model.b.z['2'], getattr(model.b, '.H'), getattr(model.b, '.H_index'), getattr(model.b, '.H')['a'], getattr(model.b, '.H')[2], model.B, model.B_index, model.B['a'], getattr(model.B['a'], '.k'), model.B[2], model.B[2].b, model.B[2].b.x, model.component('c tuple')[(1, )] ]: self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj]) self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj]) cuids = ( ComponentUID.generate_cuid_string_map(model, descend_into=False, repr_version=1), ComponentUID.generate_cuid_string_map(model, descend_into=False), ) self.assertEqual(len(cuids[0]), 18) self.assertEqual(len(cuids[1]), 18) for obj in [ model, model.x, model.y, model.y_index, model.y[1], model.y[2], model.V, model.V_index, model.V['a', 'b'], model.V[1, '2'], model.V[3, 4], model.b, model.B, model.B_index, model.B['a'], model.B[2], model.component('c tuple')[(1, )] ]: self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj]) self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj]) cuids = ( ComponentUID.generate_cuid_string_map(model, ctype=Var, repr_version=1), ComponentUID.generate_cuid_string_map(model, ctype=Var), ) self.assertEqual(len(cuids[0]), 22) self.assertEqual(len(cuids[1]), 22) for obj in [ model, model.x, model.y, model.y[1], model.y[2], model.V, model.V['a', 'b'], model.V[1, '2'], model.V[3, 4], model.b, model.b.z, model.b.z[1], model.b.z['2'], getattr(model.b, '.H'), getattr(model.b, '.H')['a'], getattr(model.b, '.H')[2], model.B, model.B['a'], getattr(model.B['a'], '.k'), model.B[2], model.B[2].b, model.B[2].b.x ]: self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj]) self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj]) cuids = ( ComponentUID.generate_cuid_string_map(model, ctype=Var, descend_into=False, repr_version=1), ComponentUID.generate_cuid_string_map(model, ctype=Var, descend_into=False), ) self.assertEqual(len(cuids[0]), 9) self.assertEqual(len(cuids[1]), 9) for obj in [ model, model.x, model.y, model.y[1], model.y[2], model.V, model.V['a', 'b'], model.V[1, '2'], model.V[3, 4] ]: self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj]) self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj])
def test_var_value_None(self): m = ConcreteModel() m.x = Var(bounds=(0, 1)) m.obj = Objective(expr=m.x) SolverFactory('multistart').solve(m)
def test_no_obj(self): m = ConcreteModel() m.x = Var() with self.assertRaisesRegexp(RuntimeError, "no active objective"): SolverFactory('multistart').solve(m)
def build_eight_process_flowsheet(): """Build flowsheet for the 8 process problem.""" m = ConcreteModel(name='DuranEx3 Disjunctive') """Set declarations""" m.streams = RangeSet(2, 25, doc="process streams") m.units = RangeSet(1, 8, doc="process units") """Parameter and initial point declarations""" # FIXED COST INVESTMENT COEFF FOR PROCESS UNITS # Format: process #: cost fixed_cost = {1: 5, 2: 8, 3: 6, 4: 10, 5: 6, 6: 7, 7: 4, 8: 5} CF = m.CF = Param(m.units, initialize=fixed_cost) # VARIABLE COST COEFF FOR PROCESS UNITS - STREAMS # Format: stream #: cost variable_cost = {3: -10, 5: -15, 9: -40, 19: 25, 21: 35, 25: -35, 17: 80, 14: 15, 10: 15, 2: 1, 4: 1, 18: -65, 20: -60, 22: -80} CV = m.CV = Param(m.streams, initialize=variable_cost, default=0) # initial point information for stream flows initX = {2: 2, 3: 1.5, 6: 0.75, 7: 0.5, 8: 0.5, 9: 0.75, 11: 1.5, 12: 1.34, 13: 2, 14: 2.5, 17: 2, 18: 0.75, 19: 2, 20: 1.5, 23: 1.7, 24: 1.5, 25: 0.5} """Variable declarations""" # FLOWRATES OF PROCESS STREAMS m.flow = Var(m.streams, domain=NonNegativeReals, initialize=initX, bounds=(0, 10)) # OBJECTIVE FUNCTION CONSTANT TERM CONSTANT = m.constant = Param(initialize=122.0) """Constraint definitions""" # INPUT-OUTPUT RELATIONS FOR process units 1 through 8 m.use_unit1 = Disjunct() m.use_unit1.inout1 = Constraint(expr=exp(m.flow[3]) - 1 == m.flow[2]) m.use_unit1.no_unit2_flow1 = Constraint(expr=m.flow[4] == 0) m.use_unit1.no_unit2_flow2 = Constraint(expr=m.flow[5] == 0) m.use_unit2 = Disjunct() m.use_unit2.inout2 = Constraint( expr=exp(m.flow[5] / 1.2) - 1 == m.flow[4]) m.use_unit2.no_unit1_flow1 = Constraint(expr=m.flow[2] == 0) m.use_unit2.no_unit1_flow2 = Constraint(expr=m.flow[3] == 0) m.use_unit3 = Disjunct() m.use_unit3.inout3 = Constraint( expr=1.5 * m.flow[9] + m.flow[10] == m.flow[8]) m.no_unit3 = Disjunct() m.no_unit3.no_unit3_flow1 = Constraint(expr=m.flow[9] == 0) m.no_unit3.flow_pass_through = Constraint(expr=m.flow[10] == m.flow[8]) m.use_unit4 = Disjunct() m.use_unit4.inout4 = Constraint( expr=1.25 * (m.flow[12] + m.flow[14]) == m.flow[13]) m.use_unit4.no_unit5_flow = Constraint(expr=m.flow[15] == 0) m.use_unit5 = Disjunct() m.use_unit5.inout5 = Constraint(expr=m.flow[15] == 2 * m.flow[16]) m.use_unit5.no_unit4_flow1 = Constraint(expr=m.flow[12] == 0) m.use_unit5.no_unit4_flow2 = Constraint(expr=m.flow[14] == 0) m.no_unit4or5 = Disjunct() m.no_unit4or5.no_unit5_flow = Constraint(expr=m.flow[15] == 0) m.no_unit4or5.no_unit4_flow1 = Constraint(expr=m.flow[12] == 0) m.no_unit4or5.no_unit4_flow2 = Constraint(expr=m.flow[14] == 0) m.use_unit6 = Disjunct() m.use_unit6.inout6 = Constraint( expr=exp(m.flow[20] / 1.5) - 1 == m.flow[19]) m.use_unit6.no_unit7_flow1 = Constraint(expr=m.flow[21] == 0) m.use_unit6.no_unit7_flow2 = Constraint(expr=m.flow[22] == 0) m.use_unit7 = Disjunct() m.use_unit7.inout7 = Constraint(expr=exp(m.flow[22]) - 1 == m.flow[21]) m.use_unit7.no_unit6_flow1 = Constraint(expr=m.flow[19] == 0) m.use_unit7.no_unit6_flow2 = Constraint(expr=m.flow[20] == 0) m.no_unit6or7 = Disjunct() m.no_unit6or7.no_unit7_flow1 = Constraint(expr=m.flow[21] == 0) m.no_unit6or7.no_unit7_flow2 = Constraint(expr=m.flow[22] == 0) m.no_unit6or7.no_unit6_flow = Constraint(expr=m.flow[19] == 0) m.no_unit6or7.no_unit6_flow2 = Constraint(expr=m.flow[20] == 0) m.use_unit8 = Disjunct() m.use_unit8.inout8 = Constraint( expr=exp(m.flow[18]) - 1 == m.flow[10] + m.flow[17]) m.no_unit8 = Disjunct() m.no_unit8.no_unit8_flow1 = Constraint(expr=m.flow[10] == 0) m.no_unit8.no_unit8_flow2 = Constraint(expr=m.flow[17] == 0) m.no_unit8.no_unit8_flow3 = Constraint(expr=m.flow[18] == 0) # Mass balance equations m.massbal1 = Constraint(expr=m.flow[13] == m.flow[19] + m.flow[21]) m.massbal2 = Constraint( expr=m.flow[17] == m.flow[9] + m.flow[16] + m.flow[25]) m.massbal3 = Constraint(expr=m.flow[11] == m.flow[12] + m.flow[15]) m.massbal4 = Constraint( expr=m.flow[3] + m.flow[5] == m.flow[6] + m.flow[11]) m.massbal5 = Constraint(expr=m.flow[6] == m.flow[7] + m.flow[8]) m.massbal6 = Constraint(expr=m.flow[23] == m.flow[20] + m.flow[22]) m.massbal7 = Constraint(expr=m.flow[23] == m.flow[14] + m.flow[24]) # process specifications m.specs1 = Constraint(expr=m.flow[10] <= 0.8 * m.flow[17]) m.specs2 = Constraint(expr=m.flow[10] >= 0.4 * m.flow[17]) m.specs3 = Constraint(expr=m.flow[12] <= 5 * m.flow[14]) m.specs4 = Constraint(expr=m.flow[12] >= 2 * m.flow[14]) # pure integer constraints m.use1or2 = Disjunction(expr=[m.use_unit1, m.use_unit2]) m.use4or5maybe = Disjunction( expr=[m.use_unit4, m.use_unit5, m.no_unit4or5]) m.use4or5 = Constraint( expr=m.use_unit4.indicator_var + m.use_unit5.indicator_var <= 1) m.use6or7maybe = Disjunction( expr=[m.use_unit6, m.use_unit7, m.no_unit6or7]) m.use4implies6or7 = Constraint( expr=m.use_unit6.indicator_var + m.use_unit7.indicator_var - m.use_unit4.indicator_var == 0) m.use3maybe = Disjunction(expr=[m.use_unit3, m.no_unit3]) m.either3ornot = Constraint( expr=m.use_unit3.indicator_var + m.no_unit3.indicator_var == 1) m.use8maybe = Disjunction(expr=[m.use_unit8, m.no_unit8]) m.use3implies8 = Constraint( expr=m.use_unit3.indicator_var - m.use_unit8.indicator_var <= 0) """Profit (objective) function definition""" m.profit = Objective(expr=sum( getattr(m, 'use_unit%s' % (unit,)).indicator_var * CF[unit] for unit in m.units) + sum(m.flow[stream] * CV[stream] for stream in m.streams) + CONSTANT, sense=minimize) """Bound definitions""" # x (flow) upper bounds x_ubs = {3: 2, 5: 2, 9: 2, 10: 1, 14: 1, 17: 2, 19: 2, 21: 2, 25: 3} for i, x_ub in x_ubs.items(): m.flow[i].setub(x_ub) # # optimal solution # m.use_unit1.indicator_var = 0 # m.use_unit2.indicator_var = 1 # m.use_unit3.indicator_var = 0 # m.no_unit3.indicator_var = 1 # m.use_unit4.indicator_var = 1 # m.use_unit5.indicator_var = 0 # m.no_unit4or5.indicator_var = 0 # m.use_unit6.indicator_var = 1 # m.use_unit7.indicator_var = 0 # m.no_unit6or7.indicator_var = 0 # m.use_unit8.indicator_var = 1 # m.no_unit8.indicator_var = 0 return m
def test_check_productexpression(self): m = self.m m.p = Param(initialize=5) m.mp = Param(initialize=5, mutable=True) m.y = Var() m.z = Var() t = IndexTemplate(m.t) # Check multiplication by constant e = 5 * m.dv[t] == m.v[t] temp = _check_productexpression(e, 0) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) e = m.v[t] == 5 * m.dv[t] temp = _check_productexpression(e, 1) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) # Check multiplication by fixed param e = m.p * m.dv[t] == m.v[t] temp = _check_productexpression(e, 0) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) e = m.v[t] == m.p * m.dv[t] temp = _check_productexpression(e, 1) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) # Check multiplication by mutable param e = m.mp * m.dv[t] == m.v[t] temp = _check_productexpression(e, 0) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(m.mp, temp[1].arg(1)) # Reciprocal self.assertIs(e.arg(1), temp[1].arg(0)) e = m.v[t] == m.mp * m.dv[t] temp = _check_productexpression(e, 1) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(m.mp, temp[1].arg(1)) # Reciprocal self.assertIs(e.arg(0), temp[1].arg(0)) # Check multiplication by var e = m.y * m.dv[t] / m.z == m.v[t] temp = _check_productexpression(e, 0) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(e.arg(1), temp[1].arg(0).arg(0)) self.assertIs(m.z, temp[1].arg(0).arg(1)) e = m.v[t] == m.y * m.dv[t] / m.z temp = _check_productexpression(e, 1) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(e.arg(0), temp[1].arg(0).arg(0)) self.assertIs(m.z, temp[1].arg(0).arg(1)) # Check having the DerivativeVar in the denominator e = m.y / (m.dv[t] * m.z) == m.mp temp = _check_productexpression(e, 0) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(m.y, temp[1].arg(0)) self.assertIs(e.arg(1), temp[1].arg(1).arg(0)) e = m.mp == m.y / (m.dv[t] * m.z) temp = _check_productexpression(e, 1) self.assertIs(m.dv, temp[0]._base) self.assertIs(type(temp[1]), EXPR.DivisionExpression) self.assertIs(m.y, temp[1].arg(0)) self.assertIs(e.arg(0), temp[1].arg(1).arg(0)) # Check expression with no DerivativeVar e = m.v[t] * m.y / m.z == m.v[t] * m.y / m.z temp = _check_productexpression(e, 0) self.assertIsNone(temp) temp = _check_productexpression(e, 1) self.assertIsNone(temp)
def test_sim_initialization_multi_index2(self): m = self.m m.s2 = Set(initialize=[(1, 1), (2, 2)]) m.w1 = Var(m.t, m.s2) m.dw1 = DerivativeVar(m.w1) m.w2 = Var(m.s2, m.t) m.dw2 = DerivativeVar(m.w2) m.w3 = Var([0, 1], m.t, m.s2) m.dw3 = DerivativeVar(m.w3) t = IndexTemplate(m.t) def _deq1(m, t, i, j): return m.dw1[t, i, j] == m.w1[t, i, j] m.deq1 = Constraint(m.t, m.s2, rule=_deq1) def _deq2(m, *idx): return m.dw2[idx] == m.w2[idx] m.deq2 = Constraint(m.s2, m.t, rule=_deq2) def _deq3(m, i, t, j, k): return m.dw3[i, t, j, k] == m.w1[t, j, k] + m.w2[j, k, t] m.deq3 = Constraint([0, 1], m.t, m.s2, rule=_deq3) mysim = Simulator(m) self.assertIs(mysim._contset, m.t) self.assertEqual(len(mysim._diffvars), 8) self.assertTrue(_GetItemIndexer(m.w1[t, 1, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w1[t, 2, 2]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[1, 1, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[2, 2, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[0, t, 1, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[1, t, 2, 2]) in mysim._diffvars) self.assertEqual(len(mysim._derivlist), 8) self.assertTrue(_GetItemIndexer(m.dw1[t, 1, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw1[t, 2, 2]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[1, 1, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[2, 2, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[0, t, 1, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[1, t, 2, 2]) in mysim._derivlist) self.assertEqual(len(mysim._templatemap), 4) self.assertTrue(_GetItemIndexer(m.w1[t, 1, 1]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w1[t, 2, 2]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[1, 1, t]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[2, 2, t]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[0, t, 1, 1]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[1, t, 2, 2]) in mysim._templatemap) self.assertEqual(len(mysim._rhsdict), 8) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1, 1])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 2, 2])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw2[1, 1, t])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw2[2, 2, t])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw3[0, t, 1, 1])], EXPR.SumExpression)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw3[1, t, 2, 2])], EXPR.SumExpression)) self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1, 1])].name, 'w1[{t},1,1]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 2, 2])].name, 'w1[{t},2,2]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw2[1, 1, t])].name, 'w2[1,1,{t}]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw2[2, 2, t])].name, 'w2[2,2,{t}]') self.assertEqual(len(mysim._rhsfun(0, [0] * 8)), 8) self.assertIsNone(mysim._tsim) self.assertIsNone(mysim._simsolution) m.del_component('deq1') m.del_component('deq1_index') m.del_component('deq2') m.del_component('deq2_index') m.del_component('deq3') m.del_component('deq3_index')
def test_sim_initialization_multi_index(self): m = self.m m.w1 = Var(m.t, m.s) m.dw1 = DerivativeVar(m.w1) m.w2 = Var(m.s, m.t) m.dw2 = DerivativeVar(m.w2) m.w3 = Var([0, 1], m.t, m.s) m.dw3 = DerivativeVar(m.w3) t = IndexTemplate(m.t) def _deq1(m, t, s): return m.dw1[t, s] == m.w1[t, s] m.deq1 = Constraint(m.t, m.s, rule=_deq1) def _deq2(m, s, t): return m.dw2[s, t] == m.w2[s, t] m.deq2 = Constraint(m.s, m.t, rule=_deq2) def _deq3(m, i, t, s): return m.dw3[i, t, s] == m.w1[t, s] + m.w2[i + 1, t] m.deq3 = Constraint([0, 1], m.t, m.s, rule=_deq3) mysim = Simulator(m) self.assertIs(mysim._contset, m.t) self.assertEqual(len(mysim._diffvars), 12) self.assertTrue(_GetItemIndexer(m.w1[t, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w1[t, 3]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[1, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[3, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[0, t, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[1, t, 3]) in mysim._diffvars) self.assertEqual(len(mysim._derivlist), 12) self.assertTrue(_GetItemIndexer(m.dw1[t, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw1[t, 3]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[1, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[3, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[0, t, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[1, t, 3]) in mysim._derivlist) self.assertEqual(len(mysim._templatemap), 6) self.assertTrue(_GetItemIndexer(m.w1[t, 1]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w1[t, 3]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[1, t]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[3, t]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[0, t, 1]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[1, t, 3]) in mysim._templatemap) self.assertEqual(len(mysim._rhsdict), 12) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1])], Param)) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 3])], Param)) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw2[1, t])], Param)) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw2[3, t])], Param)) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw3[0, t, 1])], EXPR.SumExpression)) self.assertTrue( isinstance(mysim._rhsdict[_GetItemIndexer(m.dw3[1, t, 3])], EXPR.SumExpression)) self.assertEqual( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1])].name, 'w1[{t},1]') self.assertEqual( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 3])].name, 'w1[{t},3]') self.assertEqual( mysim._rhsdict[_GetItemIndexer(m.dw2[1, t])].name, 'w2[1,{t}]') self.assertEqual( mysim._rhsdict[_GetItemIndexer(m.dw2[3, t])].name, 'w2[3,{t}]') self.assertEqual(len(mysim._rhsfun(0, [0] * 12)), 12) self.assertIsNone(mysim._tsim) self.assertIsNone(mysim._simsolution) m.del_component('deq1') m.del_component('deq1_index') m.del_component('deq2') m.del_component('deq2_index') m.del_component('deq3') m.del_component('deq3_index')
def test_separable_diffeq_case6(self): m = self.m m.w = Var(m.t, m.s) m.dw = DerivativeVar(m.w) m.p = Param(initialize=5) m.mp = Param(initialize=5, mutable=True) m.y = Var() t = IndexTemplate(m.t) def _deqv(m, i): return m.v[i]**2 + m.v[i] == m.dv[i] + m.y m.deqv = Constraint(m.t, rule=_deqv) def _deqw(m, i, j): return m.w[i, j]**2 + m.w[i, j] == m.y + m.dw[i, j] m.deqw = Constraint(m.t, m.s, rule=_deqw) mysim = Simulator(m) self.assertEqual(len(mysim._diffvars), 4) self.assertEqual(mysim._diffvars[0], _GetItemIndexer(m.v[t])) self.assertEqual(mysim._diffvars[1], _GetItemIndexer(m.w[t, 1])) self.assertEqual(mysim._diffvars[2], _GetItemIndexer(m.w[t, 2])) self.assertEqual(len(mysim._derivlist), 4) self.assertEqual(mysim._derivlist[0], _GetItemIndexer(m.dv[t])) self.assertEqual(mysim._derivlist[1], _GetItemIndexer(m.dw[t, 1])) self.assertEqual(mysim._derivlist[2], _GetItemIndexer(m.dw[t, 2])) self.assertEqual(len(mysim._rhsdict), 4) m.del_component('deqv') m.del_component('deqw') m.del_component('deqv_index') m.del_component('deqw_index') def _deqv(m, i): return m.v[i]**2 + m.v[i] == m.mp + m.dv[i] m.deqv = Constraint(m.t, rule=_deqv) def _deqw(m, i, j): return m.w[i, j]**2 + m.w[i, j] == m.dw[i, j] + m.p m.deqw = Constraint(m.t, m.s, rule=_deqw) mysim = Simulator(m) self.assertEqual(len(mysim._diffvars), 4) self.assertEqual(mysim._diffvars[0], _GetItemIndexer(m.v[t])) self.assertEqual(mysim._diffvars[1], _GetItemIndexer(m.w[t, 1])) self.assertEqual(mysim._diffvars[2], _GetItemIndexer(m.w[t, 2])) self.assertEqual(len(mysim._derivlist), 4) self.assertEqual(mysim._derivlist[0], _GetItemIndexer(m.dv[t])) self.assertEqual(mysim._derivlist[1], _GetItemIndexer(m.dw[t, 1])) self.assertEqual(mysim._derivlist[2], _GetItemIndexer(m.dw[t, 2])) self.assertEqual(len(mysim._rhsdict), 4) m.del_component('deqv') m.del_component('deqw') m.del_component('deqv_index') m.del_component('deqw_index') m.del_component('w') m.del_component('dw') m.del_component('p') m.del_component('mp') m.del_component('y')
def define_state(b): b.temperature = Var(initialize=100) b.mole_frac_phase_comp = Var(b.params.phase_list, b.params.component_list, initialize=0.5)
def define_state(b): # FcTP formulation always requires a flash, so set flag to True # TODO: should have some checking to make sure developers implement this properly b.always_flash = True units = b.params.get_metadata().derived_units # Get bounds and initial values from config args f_bounds, f_init = get_bounds_from_config(b, "flow_mol_comp", units["flow_mole"]) t_bounds, t_init = get_bounds_from_config(b, "temperature", units["temperature"]) p_bounds, p_init = get_bounds_from_config(b, "pressure", units["pressure"]) # Add state variables b.flow_mol_comp = Var(b.params.component_list, initialize=f_init, domain=NonNegativeReals, bounds=f_bounds, doc=' Component molar flowrate', units=units["flow_mole"]) b.pressure = Var(initialize=p_init, domain=NonNegativeReals, bounds=p_bounds, doc='State pressure', units=units["pressure"]) b.temperature = Var(initialize=t_init, domain=NonNegativeReals, bounds=t_bounds, doc='State temperature', units=units["temperature"]) # Add supporting variables b.flow_mol = Expression(expr=sum(b.flow_mol_comp[j] for j in b.params.component_list), doc="Total molar flowrate") if f_init is None: fp_init = None else: fp_init = f_init / len(b.params.phase_list) b.flow_mol_phase = Var(b.params.phase_list, initialize=fp_init, domain=NonNegativeReals, bounds=f_bounds, doc='Phase molar flow rates', units=units["flow_mole"]) b.mole_frac_comp = Var(b.params.component_list, bounds=(0, None), initialize=1 / len(b.params.component_list), doc='Mixture mole fractions', units=None) b.mole_frac_phase_comp = Var(b.params._phase_component_set, initialize=1 / len(b.params.component_list), bounds=(0, None), doc='Phase mole fractions', units=None) b.phase_frac = Var(b.params.phase_list, initialize=1 / len(b.params.phase_list), bounds=(0, None), doc='Phase fractions', units=None) # Add supporting constraints def rule_mole_frac_comp(b, j): if len(b.params.component_list) > 1: return b.flow_mol_comp[j] == b.mole_frac_comp[j] * sum( b.flow_mol_comp[k] for k in b.params.component_list) else: return b.mole_frac_comp[j] == 1 b.mole_frac_comp_eq = Constraint(b.params.component_list, rule=rule_mole_frac_comp) if len(b.params.phase_list) == 1: def rule_total_mass_balance(b): return b.flow_mol_phase[b.params.phase_list[1]] == b.flow_mol b.total_flow_balance = Constraint(rule=rule_total_mass_balance) def rule_comp_mass_balance(b, i): return b.mole_frac_comp[i]*1e3 == \ 1e3*b.mole_frac_phase_comp[b.params.phase_list[1], i] b.component_flow_balances = Constraint(b.params.component_list, rule=rule_comp_mass_balance) def rule_phase_frac(b, p): return b.phase_frac[p] == 1 b.phase_fraction_constraint = Constraint(b.params.phase_list, rule=rule_phase_frac) elif len(b.params.phase_list) == 2: # For two phase, use Rachford-Rice formulation def rule_total_mass_balance(b): return sum(b.flow_mol_phase[p] for p in b.params.phase_list) == \ b.flow_mol b.total_flow_balance = Constraint(rule=rule_total_mass_balance) def rule_comp_mass_balance(b, i): return b.flow_mol_comp[i] == sum( b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i] for p in b.params.phase_list if (p, i) in b.params._phase_component_set) b.component_flow_balances = Constraint(b.params.component_list, rule=rule_comp_mass_balance) def rule_mole_frac(b): return 1e3*sum(b.mole_frac_phase_comp[b.params.phase_list[1], i] for i in b.params.component_list if (b.params.phase_list[1], i) in b.params._phase_component_set) -\ 1e3*sum(b.mole_frac_phase_comp[b.params.phase_list[2], i] for i in b.params.component_list if (b.params.phase_list[2], i) in b.params._phase_component_set) == 0 b.sum_mole_frac = Constraint(rule=rule_mole_frac) def rule_phase_frac(b, p): return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p] b.phase_fraction_constraint = Constraint(b.params.phase_list, rule=rule_phase_frac) else: # Otherwise use a general formulation def rule_comp_mass_balance(b, i): return b.flow_mol_comp[i] == sum( b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i] for p in b.params.phase_list if (p, i) in b.params._phase_component_set) b.component_flow_balances = Constraint(b.params.component_list, rule=rule_comp_mass_balance) def rule_mole_frac(b, p): return 1e3 * sum(b.mole_frac_phase_comp[p, i] for i in b.params.component_list if (p, i) in b.params._phase_component_set) == 1e3 b.sum_mole_frac = Constraint(b.params.phase_list, rule=rule_mole_frac) def rule_phase_frac(b, p): return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p] b.phase_fraction_constraint = Constraint(b.params.phase_list, rule=rule_phase_frac) # ------------------------------------------------------------------------- # General Methods def get_material_flow_terms_FTPx(p, j): """Create material flow terms for control volume.""" if j in b.params.component_list: return b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, j] else: return 0 b.get_material_flow_terms = get_material_flow_terms_FTPx def get_enthalpy_flow_terms_FTPx(p): """Create enthalpy flow terms.""" return b.flow_mol_phase[p] * b.enth_mol_phase[p] b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx def get_material_density_terms_FTPx(p, j): """Create material density terms.""" if j in b.params.component_list: return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j] else: return 0 b.get_material_density_terms = get_material_density_terms_FTPx def get_energy_density_terms_FTPx(p): """Create energy density terms.""" return b.dens_mol_phase[p] * b.enth_mol_phase[p] b.get_energy_density_terms = get_energy_density_terms_FTPx def default_material_balance_type_FTPx(): return MaterialBalanceType.componentTotal b.default_material_balance_type = default_material_balance_type_FTPx def default_energy_balance_type_FTPx(): return EnergyBalanceType.enthalpyTotal b.default_energy_balance_type = default_energy_balance_type_FTPx def get_material_flow_basis_FTPx(): return MaterialFlowBasis.molar b.get_material_flow_basis = get_material_flow_basis_FTPx def define_state_vars_FTPx(): """Define state vars.""" return { "flow_mol_comp": b.flow_mol_comp, "temperature": b.temperature, "pressure": b.pressure } b.define_state_vars = define_state_vars_FTPx def define_display_vars_FTPx(): """Define display vars.""" return { "Molar Flowrate": b.flow_mol_comp, "Temperature": b.temperature, "Pressure": b.pressure } b.define_display_vars = define_display_vars_FTPx
# # Author: Gabe Hackebeil # Purpose: For regression testing to ensure that the Pyomo # NL writer properly reclassifies nonlinear expressions # as linear or trivial when fixing variables or params # cause such a situation. # # This test model relies on the gjh_asl_json executable. It # will not solve if sent to a real optimizer. # from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint, simple_constraint_rule model = ConcreteModel() model.x = Var() model.y = Var() model.z = Var() model.q = Param(initialize=0.0) model.p = Param(initialize=0.0, mutable=True) model.obj = Objective( expr=model.x*model.y +\ model.z*model.y +\ model.q*model.y +\ model.y*model.y*model.q +\ model.p*model.y +\ model.y*model.y*model.p +\ model.y*model.y*model.z +\ model.z*(model.y**2)) model.con1 = Constraint(expr=model.x * model.y == 0)
def test_findComponentOn_nestedTuples(self): # Tests for #1069 m = ConcreteModel() m.x = Var() m.c = Constraint(Any) m.c[0] = m.x >= 0 m.c[(1, )] = m.x >= 1 m.c[(2, )] = m.x >= 2 m.c[2] = m.x >= 3 self.assertIs(ComponentUID(m.c[0]).find_component_on(m), m.c[0]) self.assertIs(ComponentUID('c[0]').find_component_on(m), m.c[0]) self.assertIsNone(ComponentUID('c[(0,)]').find_component_on(m)) self.assertIs( ComponentUID(m.c[(1, )]).find_component_on(m), m.c[(1, )]) self.assertIs(ComponentUID('c[(1,)]').find_component_on(m), m.c[(1, )]) self.assertIsNone(ComponentUID('c[1]').find_component_on(m)) self.assertIs(ComponentUID('c[(2,)]').find_component_on(m), m.c[(2, )]) self.assertIs(ComponentUID('c[2]').find_component_on(m), m.c[2]) self.assertEqual(len(m.c), 4) self.assertEqual(repr(ComponentUID(m.c[0])), "c[0]") self.assertEqual(repr(ComponentUID(m.c[(1, )])), "c[(1,)]") self.assertEqual(str(ComponentUID(m.c[0])), "c[0]") self.assertEqual(str(ComponentUID(m.c[(1, )])), "c[(1,)]") m = ConcreteModel() m.x = Var() m.c = Constraint([0, 1]) m.c[0] = m.x >= 0 m.c[(1, )] = m.x >= 1 self.assertIs(ComponentUID(m.c[0]).find_component_on(m), m.c[0]) self.assertIs(ComponentUID(m.c[(0, )]).find_component_on(m), m.c[0]) self.assertIs(ComponentUID('c[0]').find_component_on(m), m.c[0]) self.assertIs(ComponentUID('c[(0,)]').find_component_on(m), m.c[0]) self.assertIs(ComponentUID(m.c[1]).find_component_on(m), m.c[1]) self.assertIs(ComponentUID(m.c[(1, )]).find_component_on(m), m.c[1]) self.assertIs(ComponentUID('c[(1,)]').find_component_on(m), m.c[1]) self.assertIs(ComponentUID('c[1]').find_component_on(m), m.c[1]) self.assertEqual(len(m.c), 2) m = ConcreteModel() m.b = Block(Any) m.b[0].c = Block(Any) m.b[0].c[0].x = Var() m.b[(1, )].c = Block(Any) m.b[(1, )].c[(1, )].x = Var() ref = m.b[0].c[0].x self.assertIs(ComponentUID(ref).find_component_on(m), ref) ref = 'm.b[0].c[(0,)].x' self.assertIsNone(ComponentUID(ref).find_component_on(m)) ref = m.b[(1, )].c[(1, )].x self.assertIs(ComponentUID(ref).find_component_on(m), ref) ref = 'm.b[(1,)].c[1].x' self.assertIsNone(ComponentUID(ref).find_component_on(m)) buf = {} ref = m.b[0].c[0].x self.assertIs( ComponentUID(ref, cuid_buffer=buf).find_component_on(m), ref) self.assertEqual(len(buf), 3) ref = 'm.b[0].c[(0,)].x' self.assertIsNone( ComponentUID(ref, cuid_buffer=buf).find_component_on(m)) self.assertEqual(len(buf), 3) ref = m.b[(1, )].c[(1, )].x self.assertIs( ComponentUID(ref, cuid_buffer=buf).find_component_on(m), ref) self.assertEqual(len(buf), 4) ref = 'm.b[(1,)].c[1].x' self.assertIsNone( ComponentUID(ref, cuid_buffer=buf).find_component_on(m)) self.assertEqual(len(buf), 4)
# ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects # Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ from pyomo.environ import Var class Foo: pass anotherObject = Foo() anotherObject.x = Var([10]) anotherObject.x.value = 42
def b2(b2, i, j): b2.v = Var() b2.v1 = Var(m.d1_3) b2.v2 = Var(m.d1_1, m.d1_2) b2.vn = Var(m.d1_1, m.dn, m.d1_2)
def test_xfrm_special_atoms_nonroot(self): m = ConcreteModel() m.s = RangeSet(3) m.Y = BooleanVar(m.s) m.p = LogicalConstraint( expr=m.Y[1].implies(atleast(2, m.Y[1], m.Y[2], m.Y[3]))) TransformationFactory('core.logical_to_linear').apply_to(m) Y_aug = m.logic_to_linear.augmented_vars self.assertEqual(len(Y_aug), 1) self.assertEqual(Y_aug[1].domain, BooleanSet) _constrs_contained_within( self, [ (None, sum(m.Y[:].get_associated_binary()) - \ (1 + 2 * Y_aug[1].get_associated_binary()), 0), (1, (1 - m.Y[1].get_associated_binary()) + \ Y_aug[1].get_associated_binary(), None), (None, 2 - 2 * (1 - Y_aug[1].get_associated_binary()) - \ sum(m.Y[:].get_associated_binary()), 0) ], m.logic_to_linear.transformed_constraints) m = ConcreteModel() m.s = RangeSet(3) m.Y = BooleanVar(m.s) m.p = LogicalConstraint( expr=m.Y[1].implies(atmost(2, m.Y[1], m.Y[2], m.Y[3]))) TransformationFactory('core.logical_to_linear').apply_to(m) Y_aug = m.logic_to_linear.augmented_vars self.assertEqual(len(Y_aug), 1) self.assertEqual(Y_aug[1].domain, BooleanSet) _constrs_contained_within( self, [ (None, sum(m.Y[:].get_associated_binary()) - \ (1 - Y_aug[1].get_associated_binary() + 2), 0), (1, (1 - m.Y[1].get_associated_binary()) + \ Y_aug[1].get_associated_binary(), None), (None, 3 - 3 * Y_aug[1].get_associated_binary() - \ sum(m.Y[:].get_associated_binary()), 0) ], m.logic_to_linear.transformed_constraints) m = ConcreteModel() m.s = RangeSet(3) m.Y = BooleanVar(m.s) m.p = LogicalConstraint( expr=m.Y[1].implies(exactly(2, m.Y[1], m.Y[2], m.Y[3]))) TransformationFactory('core.logical_to_linear').apply_to(m) Y_aug = m.logic_to_linear.augmented_vars self.assertEqual(len(Y_aug), 3) self.assertEqual(Y_aug[1].domain, BooleanSet) _constrs_contained_within( self, [ (1, (1 - m.Y[1].get_associated_binary()) + \ Y_aug[1].get_associated_binary(), None), (None, sum(m.Y[:].get_associated_binary()) - \ (1 - Y_aug[1].get_associated_binary() + 2), 0), (None, 2 - 2 * (1 - Y_aug[1].get_associated_binary()) - \ sum(m.Y[:].get_associated_binary()), 0), (1, sum(Y_aug[:].get_associated_binary()), None), (None, sum(m.Y[:].get_associated_binary()) - \ (1 + 2 * (1 - Y_aug[2].get_associated_binary())), 0), (None, 3 - 3 * (1 - Y_aug[3].get_associated_binary()) - \ sum(m.Y[:].get_associated_binary()), 0), ], m.logic_to_linear.transformed_constraints) # Note: x is now a variable m = ConcreteModel() m.s = RangeSet(3) m.Y = BooleanVar(m.s) m.x = Var(bounds=(1, 3)) m.p = LogicalConstraint( expr=m.Y[1].implies(exactly(m.x, m.Y[1], m.Y[2], m.Y[3]))) TransformationFactory('core.logical_to_linear').apply_to(m) Y_aug = m.logic_to_linear.augmented_vars self.assertEqual(len(Y_aug), 3) self.assertEqual(Y_aug[1].domain, BooleanSet) _constrs_contained_within( self, [ (1, (1 - m.Y[1].get_associated_binary()) + \ Y_aug[1].get_associated_binary(), None), (None, sum(m.Y[:].get_associated_binary()) - \ (m.x + 2 * (1 - Y_aug[1].get_associated_binary())), 0), (None, m.x - 3 * (1 - Y_aug[1].get_associated_binary()) - \ sum(m.Y[:].get_associated_binary()), 0), (1, sum(Y_aug[:].get_associated_binary()), None), (None, sum(m.Y[:].get_associated_binary()) - \ (m.x - 1 + 3 * (1 - Y_aug[2].get_associated_binary())), 0), (None, m.x + 1 - 4 * (1 - Y_aug[3].get_associated_binary()) - \ sum(m.Y[:].get_associated_binary()), 0), ], m.logic_to_linear.transformed_constraints)
def _make_performance(self): """ Define constraints which describe the behaviour of the unit model. Args: None Returns: None """ # Set references to balance terms at unit level self.heat_duty_side_1 = Reference(self.side_1.heat) self.heat_duty_side_2 = Reference(self.side_2.heat) self.heat_duty_side_3 = Reference(self.side_3.heat) if self.config.has_pressure_change is True: self.deltaP_side_1 = Reference(self.side_1.deltaP) self.deltaP_side_2 = Reference(self.side_2.deltaP) self.deltaP_side_3 = Reference(self.side_3.deltaP) # Performance parameters and variables # Temperature driving force self.temperature_driving_force_side_2 = Var(self.flowsheet().config. time, initialize=1.0, doc='Mean driving force ' 'for heat exchange') # Temperature driving force self.temperature_driving_force_side_3 = Var(self.flowsheet(). config.time, initialize=1.0, doc='Mean driving force ' 'for heat exchange') # Temperature difference at side 2 inlet self.side_2_inlet_dT = Var(self.flowsheet().config.time, initialize=1.0, doc='Temperature difference ' 'at side 2 inlet') # Temperature difference at side 2 outlet self.side_2_outlet_dT = Var(self.flowsheet().config.time, initialize=1.0, doc='Temperature difference ' 'at side 2 outlet') # Temperature difference at side 3 inlet self.side_3_inlet_dT = Var(self.flowsheet().config.time, initialize=1.0, doc='Temperature difference' ' at side 3 inlet') # Temperature difference at side 3 outlet self.side_3_outlet_dT = Var(self.flowsheet().config.time, initialize=1.0, doc='Temperature difference ' 'at side 3 outlet') # Driving force side 2 (Underwood approximation) @self.Constraint(self.flowsheet().config.time, doc="Log mean temperature difference calculation " "using Underwood approximation") def LMTD_side_2(b, t): return b.temperature_driving_force_side_2[t] == \ ((b.side_2_inlet_dT[t]**(1/3) + b.side_2_outlet_dT[t]**(1/3))/2)**(3) # Driving force side 3 (Underwood approximation) @self.Constraint(self.flowsheet().config.time, doc="Log mean temperature difference calculation " "using Underwood approximation") def LMTD_side_3(b, t): return b.temperature_driving_force_side_3[t] == \ ((b.side_3_inlet_dT[t]**(1/3) + b.side_3_outlet_dT[t]**(1/3))/2)**(3) # Heat duty side 2 @self.Constraint(self.flowsheet().config.time, doc="Heat transfer rate") def heat_duty_side_2_eqn(b, t): return b.heat_duty_side_2[t] == \ (b.ua_side_2[t] * b.temperature_driving_force_side_2[t]) # Heat duty side 3 @self.Constraint(self.flowsheet().config.time, doc="Heat transfer rate") def heat_duty_side_3_eqn(b, t): return b.heat_duty_side_3[t] == \ (b.ua_side_3[t]*b.temperature_driving_force_side_3[t]) # Energy balance equation @self.Constraint(self.flowsheet().config.time, doc="Energy balance between two sides") def heat_duty_side_1_eqn(b, t): return -b.heat_duty_side_1[t]*(1-b.frac_heatloss) == \ (b.heat_duty_side_2[t] + b.heat_duty_side_3[t])
def _make_performance(self): """ Define constraints which describe the behaviour of the unit model. """ # thermal conductivity of drum self.cond_therm_metal = Param(initialize=40, mutable=True) # thermal conductivity of insulation self.cond_therm_insulation = Param(initialize=0.08, mutable=True) # thermal conductivity of air self.cond_therm_air = Param(initialize=0.03, mutable=True) # thermal diffusivity of drum #self.diff_therm_metal = Param(initialize = 9.5E-6, mutable=True) # material density of drum self.density_thermal_metal = Param(initialize=7753, mutable=True) # heat capacity of drum self.heat_capacity_thermal_drum = Param(initialize=486, mutable=True) # Young modulus self.Young_modulus = Param(initialize=2.07E5, mutable=True) # Poisson's ratio self.Poisson_ratio = Param(initialize=0.292, mutable=True) # Coefficient of thermal expansion self.coefficient_therm_expansion = Param(initialize=1.4E-5, mutable=True) # constant related to Ra, (gravity*expansion_coefficient*density/viscosity/thermal_diffusivity)^(1/6) # use properties at 50 C and 1 atm, 6.84e7^(1/6)=20.223 self.const_Ra_root6 = Param(initialize=20.223, mutable=True) # constant related to Nu for free convection, 0.387/(1+0.721*Pr^(-9/16))^(8/27) # use properties at 50 C and 1 atm self.const_Nu = Param(initialize=0.322, mutable=True) # ambient pressure self.pres_amb = Param(initialize=0.81E5, doc="ambient pressure") # ambient temperature self.temp_amb = Var(self.flowsheet().config.time, initialize=300, doc="ambient temperature") # inside heat transfer coefficient self.h_in = Var(self.flowsheet().config.time, initialize=1, doc="inside heat transfer coefficient") # outside heat transfer coefficient self.h_out = Var(self.flowsheet().config.time, initialize=1, doc="outside heat transfer coefficient") # insulation free convection heat transfer coefficient self.h_free_conv = Var( self.flowsheet().config.time, initialize=1, doc="insulation free convection heat transfer coefficient") # Ra number of free convection self.N_Ra_root6 = Var( self.flowsheet().config.time, initialize=80, doc="1/6 power of Ra number of free convection of air") # Nu number of free convection self.N_Nu = Var(self.flowsheet().config.time, initialize=1, doc="Nu number of free convection of air") # Define the continuous domains for model self.r = ContinuousSet(bounds=(self.rin_drum, self.rout_drum)) # Temperature across wall thickness self.T = Var(self.flowsheet().config.time, self.r, bounds=(280, 800), initialize=550) # Declare derivatives in the model if self.config.dynamic is True: self.dTdt = DerivativeVar(self.T, wrt=self.flowsheet().config.time) self.dTdr = DerivativeVar(self.T, wrt=self.r) self.d2Tdr2 = DerivativeVar(self.T, wrt=(self.r, self.r)) discretizer = TransformationFactory('dae.finite_difference') discretizer.apply_to(self, nfe=self.config.finite_elements, wrt=self.r, scheme='CENTRAL') # Add performance variables self.level = Var(self.flowsheet().config.time, initialize=1.0, doc='Water level from the bottom of the drum') # Velocity of fluid inside downcomer pipe self.velocity_downcomer = Var( self.flowsheet().config.time, initialize=10.0, doc='Liquid water velocity at the top of downcomer') # Pressure change due to contraction self.deltaP_contraction = Var(self.flowsheet().config.time, initialize=-1.0, doc='Pressure change due to contraction') # Pressure change due to gravity self.deltaP_gravity = Var(self.flowsheet().config.time, initialize=1.0, doc='Pressure change due to gravity') # thermal diffusivity of drum @self.Expression(doc="Thermal diffusivity of drum") def diff_therm_metal(b): return b.cond_therm_metal / (b.density_thermal_metal * b.heat_capacity_thermal_drum) # Expressure for the angle from the drum center to the circumference point at water level @self.Expression(self.flowsheet().config.time, doc="angle of water level") def alpha_drum(b, t): return asin((b.level[t] - b.rin_drum) / b.rin_drum) # Expressure for the fraction of wet area @self.Expression(self.flowsheet().config.time, doc="fraction of wet area") def frac_wet_area(b, t): return (b.alpha_drum[t] + b.pi / 2) / b.pi # Constraint for volume liquid in drum @self.Constraint(self.flowsheet().config.time, doc="volume of liquid in drum") def volume_eqn(b, t): return b.volume[t] == \ ((b.alpha_drum[t]+0.5*b.pi)*b.rin_drum**2 + \ b.rin_drum*cos(b.alpha_drum[t])*(b.level[t]-b.rin_drum))* \ b.length_drum # Equation for velocity at the entrance of downcomer @self.Constraint(self.flowsheet().config.time, doc="Vecolity at entrance of downcomer") def velocity_eqn(b, t): return b.velocity_downcomer[t]*0.25*b.pi*b.diameter_downcomer**2*b.count_downcomer \ == b.control_volume.properties_out[t].flow_vol # Pressure change equation for contraction, -0.5*1/2*density*velocity^2 for stagnation head loss # plus 1/2*density*velocity^2 dynamic head (acceleration pressure change) @self.Constraint(self.flowsheet().config.time, doc="pressure change due to contraction") def pressure_change_contraction_eqn(b, t): return b.deltaP_contraction[t] == \ -0.75 * b.control_volume.properties_out[t].dens_mass_phase["Liq"] * \ b.velocity_downcomer[t]**2 # Pressure change equation for gravity, density*gravity*height @self.Constraint(self.flowsheet().config.time, doc="pressure change due to gravity") def pressure_change_gravity_eqn(b, t): return b.deltaP_gravity[t] == \ b.control_volume.properties_out[t].dens_mass_phase["Liq"] * b.gravity * b.level[t] # Total pressure change equation @self.Constraint(self.flowsheet().config.time, doc="pressure drop") def pressure_change_total_eqn(b, t): return b.deltaP[t] == b.deltaP_contraction[t] + b.deltaP_gravity[t] # Constraint for heat conduction equation @self.Constraint(self.flowsheet().config.time, self.r, doc="1-D heat conduction equation through radius") def heat_conduction_eqn(b, t, r): if r == b.r.first() or r == b.r.last(): return Constraint.Skip if self.config.dynamic is True: return b.dTdt[t, r] == b.diff_therm_metal * b.d2Tdr2[ t, r] + b.diff_therm_metal * (1 / r) * b.dTdr[t, r] else: return 0 == b.diff_therm_metal * b.d2Tdr2[ t, r] + b.diff_therm_metal * (1 / r) * b.dTdr[t, r] @self.Constraint(self.flowsheet().config.time, doc="inner wall BC") def inner_wall_bc_eqn(b, t): return b.h_in[t]*(b.control_volume.properties_out[t].temperature - b.T[t,b.r.first()]) == \ -b.dTdr[t,b.r.first()]*b.cond_therm_metal @self.Constraint(self.flowsheet().config.time, doc="outer wall BC") def outer_wall_bc_eqn(b, t): return b.h_out[t]*(b.T[t,b.r.last()] - b.temp_amb[t]) == \ -b.dTdr[t, b.r.last()]*b.cond_therm_metal # Inner wall BC for dTdt @self.Constraint(self.flowsheet().config.time, doc="extra inner wall temperature derivative") def extra_at_inner_wall_eqn(b, t): if self.config.dynamic is True: term = b.dTdt[t, b.r.first()] else: term = 0 return term == 4*b.diff_therm_metal*(b.r.first()+b.r[2])/ \ (b.r[2]-b.r.first())**2/(3*b.r.first()+b.r[2])*(b.T[t,b.r[2]] \ -b.T[t,b.r.first()]) + 8*b.diff_therm_metal/b.cond_therm_metal*b.h_in[t]*b.r.first()/ \ (b.r[2]-b.r.first())/(3*b.r.first()+b.r[2])*(b.control_volume.properties_out[t].temperature \ -b.T[t,b.r.first()]) @self.Constraint(self.flowsheet().config.time, doc="extra outer wall temperature derivative") def extra_at_outer_wall_eqn(b, t): if self.config.dynamic is True: term = b.dTdt[t, b.r.last()] else: term = 0 return term == 4*b.diff_therm_metal*(b.r.last()+b.r[-2])/ \ (b.r.last()-b.r[-2])**2/(3*b.r.last()+b.r[-2])*(b.T[t,b.r[-2]] \ -b.T[t,b.r.last()]) + 8*b.diff_therm_metal/b.cond_therm_metal*b.h_out[t]*b.r.last()/ \ (b.r.last()-b.r[-2])/(3*b.r.last()+b.r[-2])*(b.temp_amb[t] \ -b.T[t,b.r.last()]) # reduced pressure expression @self.Expression(self.flowsheet().config.time, doc="reduced pressure") def pres_reduced(b, t): return b.control_volume.properties_out[t].pressure / 2.2048e7 # calculate inner side heat transfer coefficient with minimum temperature difference set to sqrt(0.1) # multipling wet area fraction to convert it to the value based on total circumference @self.Constraint(self.flowsheet().config.time, doc="inner side heat transfer coefficient") def h_in_eqn(b, t): return b.h_in[t] == 2178.6*(b.control_volume.properties_out[t].pressure/2.2048e7)**0.36\ /(-log10(b.control_volume.properties_out[t].pressure/2.2048e7))**1.65 *\ (0.1+(b.control_volume.properties_out[t].temperature-b.T[t,b.r.first()])**2)*b.frac_wet_area[t] # Expressure for insulation heat transfer (conduction) resistance based on drum metal outside diameter @self.Expression(doc="heat transfer resistance of insulation layer") def resistance_insulation(b): return b.rout_drum * log((b.rout_drum + b.thickness_insulation) / b.rout_drum) / b.cond_therm_insulation # h_out equation considering conduction through insulation and free convection between insulation and ambient @self.Constraint(self.flowsheet().config.time, doc="outer side heat transfer coefficient") def h_out_eqn(b, t): return b.h_out[t] * (b.resistance_insulation + 1 / b.h_free_conv[t]) == 1.0 # Expressure for outside insulation wall temperature (skin temperature) @self.Expression(self.flowsheet().config.time, doc="outside insulation wall temperature") def temp_insulation_outside(b, t): return b.temp_amb[t] + (b.T[t, b.r.last()] - b.temp_amb[t] ) * b.h_out[t] / b.h_free_conv[t] # Ra number equation @self.Constraint(self.flowsheet().config.time, doc="Ra number of free convection") def Ra_number_eqn(b, t): return b.N_Ra_root6[t] == b.const_Ra_root6 * sqrt( b.dout_drum + 2 * b.thickness_insulation) * ( b.T[t, b.r.last()] - b.temp_amb[t])**0.166667 # Nu number equation @self.Constraint(self.flowsheet().config.time, doc=" Nu number of free convection") def Nu_number_eqn(b, t): return b.N_Nu[t] == (0.6 + b.const_Nu * b.N_Ra_root6[t])**2 # free convection coefficient based on the drum metal outside diameter @self.Constraint( self.flowsheet().config.time, doc= "free convection heat transfer coefficient between insulation wall and ambient" ) def h_free_conv_eqn(b, t): return b.h_free_conv[ t] == b.N_Nu[t] * b.cond_therm_air / b.dout_drum @self.Constraint(self.flowsheet().config.time, doc="heat loss of water") def heat_loss_eqn(b, t): return b.heat_duty[t] == b.area_drum*b.h_in[t]*(b.T[t,b.r.first()]\ -b.control_volume.properties_out[t].temperature) ### Calculate mechanical and thermal stresses # ---------------------------------------------------------------------------------------------- # Integer indexing for radius domain self.rindex = Param(self.r, initialize=1, mutable=True, doc="inter indexing for radius domain") # calculate integral point for mean temperature in the wall @self.Expression(self.flowsheet().config.time, doc="integral point used to estimate mean temperature" ) def int_mean_temp(b, t): return 2*(b.r[2]-b.r[1])/(b.rout_drum**2-b.rin_drum**2)*(sum(0.5*(b.r[i-1]*b.T[t,b.r[i-1]]+\ b.r[i]*b.T[t,b.r[i]]) for i in range(2,len(b.r)+1))) for index_r, value_r in enumerate(self.r, 1): self.rindex[value_r] = index_r @self.Expression(self.flowsheet().config.time, self.r, doc="integral point at each element") def int_temp(b, t, r): if b.rindex[r].value == 1: return b.T[t, b.r.first()] else: return 2*(b.r[2]-b.r[1])/(b.r[b.rindex[r].value]**2-b.rin_drum**2)\ *(sum(0.5*(b.r[j-1]*b.T[t,b.r[j-1]]+b.r[j]*b.T[t,b.r[j]])\ for j in range(2,b.rindex[r].value+1))) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at radial direction") def therm_stress_radial(b, t, r): return 0.5*b.Young_modulus*b.coefficient_therm_expansion/(1-b.Poisson_ratio)\ *((1-b.rin_drum**2/r**2)*(b.int_mean_temp[t]-b.int_temp[t,r])) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at circumferential direction") def therm_stress_circumferential(b, t, r): return 0.5*b.Young_modulus*b.coefficient_therm_expansion/(1-b.Poisson_ratio)\ *((1+b.rin_drum**2/r**2)*b.int_mean_temp[t]+(1-b.rin_drum**2/r**2)*b.int_temp[t,r]-2*b.T[t,r]) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at axial direction") def therm_stress_axial(b, t, r): return b.Young_modulus * b.coefficient_therm_expansion / ( 1 - b.Poisson_ratio) * (b.int_mean_temp[t] - b.T[t, r]) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at radial direction") def mech_stress_radial(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2)+(1E-5*(b.pres_amb-b.control_volume.properties_out[t].pressure)\ *b.rin_drum**2*b.rout_drum**2/(r**2*(b.rout_drum**2-b.rin_drum**2)))) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at circumferential direction") def mech_stress_circumferential(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2)-(1E-5*(b.pres_amb-b.control_volume.properties_out[t].pressure)\ *b.rin_drum**2*b.rout_drum**2/(r**2*(b.rout_drum**2-b.rin_drum**2)))) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at axial direction") def mech_stress_axial(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2))
class SteamValveData(PressureChangerData): # Same settings as the default pressure changer, but force to expander with # isentropic efficiency CONFIG = PressureChangerData.CONFIG() _define_config(CONFIG) def build(self): super().build() self.valve_opening = Var(self.flowsheet().config.time, initialize=1, doc="Fraction open for valve from 0 to 1") self.Cv = Var(initialize=0.1, doc="Valve flow coefficent, for vapor " "[mol/s/Pa] for liquid [mol/s/Pa^0.5]") self.flow_scale = Param( mutable=True, default=1e3, doc= "Scaling factor for pressure flow relation should be approximatly" " the same order of magnitude as the expected flow.") self.Cv.fix() self.valve_opening.fix() # set up the valve function rule. I'm not sure these matter too much # for us, but the options are easy enough to provide. if self.config.valve_function == ValveFunctionType.linear: rule = _linear_rule elif self.config.valve_function == ValveFunctionType.quick_opening: rule = _quick_open_rule elif self.config.valve_function == ValveFunctionType.equal_percentage: self.alpha = Var(initialize=1, doc="Valve function parameter") self.alpha.fix() rule = equal_percentage_rule else: rule = self.config.valve_function_rule self.valve_function = Expression(self.flowsheet().config.time, rule=rule, doc="Valve function expression") if self.config.phase == "Liq": rule = _liquid_pressure_flow_rule else: rule = _vapor_pressure_flow_rule self.pressure_flow_equation = Constraint(self.flowsheet().config.time, rule=rule) def initialize(self, state_args={}, outlvl=0, solver='ipopt', optarg={ 'tol': 1e-6, 'max_iter': 30 }): """ Initialize the turbine stage model. This deactivates the specialized constraints, then does the isentropic turbine initialization, then reactivates the constraints and solves. Args: state_args (dict): Initial state for property initialization outlvl (int): Amount of output (0 to 3) 0 is lowest solver (str): Solver to use for initialization optarg (dict): Solver arguments dictionary """ stee = True if outlvl >= 3 else False # sp is what to save to make sure state after init is same as the start # saves value, fixed, and active state, doesn't load originally free # values, this makes sure original problem spec is same but initializes # the values of free vars sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) self.deltaP[:].unfix() self.ratioP[:].unfix() # fix inlet and free outlet for t in self.flowsheet().config.time: for k, v in self.inlet.vars.items(): v[t].fix() for k, v in self.outlet.vars.items(): v[t].unfix() # to calculate outlet pressure Pout = self.outlet.pressure[t] Pin = self.inlet.pressure[t] if self.deltaP[t].value is not None: prdp = value((self.deltaP[t] - Pin) / Pin) else: prdp = -100 # crazy number to say don't use deltaP as guess if value(Pout / Pin) > 1 or value(Pout / Pin) < 0.0: if value(self.ratioP[t]) <= 1 and value(self.ratioP[t]) >= 0: Pout.value = value(Pin * self.ratioP[t]) elif prdp <= 1 and prdp >= 0: Pout.value = value(prdp * Pin) else: Pout.value = value(Pin * 0.95) self.deltaP[t] = value(Pout - Pin) self.ratioP[t] = value(Pout / Pin) # Make sure the initialization problem has no degrees of freedom # This shouldn't happen here unless there is a bug in this dof = degrees_of_freedom(self) try: assert (dof == 0) except: _log.exception("degrees_of_freedom = {}".format(dof)) raise # one bad thing about reusing this is that the log messages aren't # really compatible with being nested inside another initialization super().initialize(state_args=state_args, outlvl=outlvl, solver=solver, optarg=optarg) # reload original spec from_json(self, sd=istate, wts=sp) def _get_performance_contents(self, time_point=0): pc = super()._get_performance_contents(time_point=time_point) pc["vars"]["Opening"] = self.valve_opening[time_point] pc["vars"]["Valve Coefficient"] = self.Cv if self.config.valve_function == ValveFunctionType.equal_percentage: pc["vars"]["alpha"] = self.alpha pc["params"] = {} pc["params"]["Flow Scaling"] = self.flow_scale return pc
class Drum1DData(UnitModelBlockData): """ 1-D Boiler Drum Unit Operation Class """ CONFIG = ConfigBlock() CONFIG.declare( "dynamic", ConfigValue(domain=In([useDefault, True, False]), default=useDefault, description="Dynamic model flag", doc="""Indicates whether this model will be dynamic or not, **default** = useDefault. **Valid values:** { **useDefault** - get flag from parent (default = False), **True** - set as a dynamic model, **False** - set as a steady-state model.}""")) CONFIG.declare( "has_holdup", ConfigValue( default=False, domain=In([True, False]), description="Holdup construction flag", doc="""Indicates whether holdup terms should be constructed or not. Must be True if dynamic = True, **default** - False. **Valid values:** { **True** - construct holdup terms, **False** - do not construct holdup terms}""")) CONFIG.declare( "material_balance_type", ConfigValue( default=MaterialBalanceType.componentPhase, domain=In(MaterialBalanceType), description="Material balance construction flag", doc="""Indicates what type of material balance should be constructed, **default** - MaterialBalanceType.componentPhase. **Valid values:** { **MaterialBalanceType.none** - exclude material balances, **MaterialBalanceType.componentPhase** - use phase component balances, **MaterialBalanceType.componentTotal** - use total component balances, **MaterialBalanceType.elementTotal** - use total element balances, **MaterialBalanceType.total** - use total material balance.}""")) CONFIG.declare( "energy_balance_type", ConfigValue( default=EnergyBalanceType.enthalpyTotal, domain=In(EnergyBalanceType), description="Energy balance construction flag", doc="""Indicates what type of energy balance should be constructed, **default** - EnergyBalanceType.enthalpyTotal. **Valid values:** { **EnergyBalanceType.none** - exclude energy balances, **EnergyBalanceType.enthalpyTotal** - single ethalpy balance for material, **EnergyBalanceType.enthalpyPhase** - ethalpy balances for each phase, **EnergyBalanceType.energyTotal** - single energy balance for material, **EnergyBalanceType.energyPhase** - energy balances for each phase.}""")) CONFIG.declare( "momentum_balance_type", ConfigValue( default=MomentumBalanceType.pressureTotal, domain=In(MomentumBalanceType), description="Momentum balance construction flag", doc="""Indicates what type of momentum balance should be constructed, **default** - MomentumBalanceType.pressureTotal. **Valid values:** { **MomentumBalanceType.none** - exclude momentum balances, **MomentumBalanceType.pressureTotal** - single pressure balance for material, **MomentumBalanceType.pressurePhase** - pressure balances for each phase, **MomentumBalanceType.momentumTotal** - single momentum balance for material, **MomentumBalanceType.momentumPhase** - momentum balances for each phase.}""")) CONFIG.declare( "has_heat_transfer", ConfigValue( default=False, domain=In([True, False]), description="Heat transfer term construction flag", doc= """Indicates whether terms for heat transfer should be constructed, **default** - False. **Valid values:** { **True** - include heat transfer terms, **False** - exclude heat transfer terms.}""")) CONFIG.declare( "has_pressure_change", ConfigValue( default=False, domain=In([True, False]), description="Pressure change term construction flag", doc="""Indicates whether terms for pressure change should be constructed, **default** - False. **Valid values:** { **True** - include pressure change terms, **False** - exclude pressure change terms.}""")) CONFIG.declare( "property_package", ConfigValue( default=useDefault, domain=is_physical_parameter_block, description="Property package to use for control volume", doc= """Property parameter object used to define property calculations, **default** - useDefault. **Valid values:** { **useDefault** - use default package from parent model or flowsheet, **PhysicalParameterObject** - a PhysicalParameterBlock object.}""")) CONFIG.declare( "property_package_args", ConfigBlock( implicit=True, description="Arguments to use for constructing property packages", doc= """A ConfigBlock with arguments to be passed to a property block(s) and used when constructing these, **default** - None. **Valid values:** { see property package for documentation.}""")) CONFIG.declare( "finite_elements", ConfigValue( default=5, domain=int, description="Number of finite elements length domain", doc="""Number of finite elements to use when discretizing length domain (default=5). Should set to the number of tube rows""")) CONFIG.declare( "collocation_points", ConfigValue( default=3, domain=int, description="Number of collocation points per finite element", doc="""Number of collocation points to use per finite element when discretizing length domain (default=3)""")) CONFIG.declare( "inside_diameter", ConfigValue(default=1.0, description='inside diameter of drum', doc='define inside diameter of drum')) CONFIG.declare( "thickness", ConfigValue(default=0.1, description='drum wall thickness', doc='define drum wall thickness')) def build(self): """ Begin building model (pre-DAE transformation). Args: None Returns: None """ # Call UnitModel.build to setup dynamics super(Drum1DData, self).build() # Build Control Volume self.control_volume = ControlVolume0DBlock( default={ "dynamic": self.config.dynamic, "has_holdup": self.config.has_holdup, "property_package": self.config.property_package, "property_package_args": self.config.property_package_args }) self.control_volume.add_geometry() self.control_volume.add_state_blocks(has_phase_equilibrium=False) self.control_volume.add_material_balances( balance_type=self.config.material_balance_type, has_rate_reactions=False, has_equilibrium_reactions=False) self.control_volume.add_energy_balances( balance_type=self.config.energy_balance_type, has_heat_of_reaction=False, has_heat_transfer=self.config.has_heat_transfer) self.control_volume.add_momentum_balances( balance_type=self.config.momentum_balance_type, has_pressure_change=True) # Add Ports self.add_inlet_port() self.add_outlet_port() # Add object references add_object_reference(self, "volume", self.control_volume.volume) # Set references to balance terms at unit level if (self.config.has_heat_transfer is True and self.config.energy_balance_type != EnergyBalanceType.none): add_object_reference(self, "heat_duty", self.control_volume.heat) if (self.config.has_pressure_change is True and self.config.momentum_balance_type != 'none'): add_object_reference(self, "deltaP", self.control_volume.deltaP) # Set Unit Geometry and Holdup Volume self._set_geometry() # Construct performance equations self._make_performance() def _set_geometry(self): """ Define the geometry of the unit as necessary """ # Constant pi self.pi = Param(initialize=math.pi, doc="Pi") # Constant gravity self.gravity = Param(initialize=9.806, doc="Gravity") di = self.config.inside_diameter thk = self.config.thickness # Inside diameter of drum self.diameter_drum = Param(initialize=di, doc="Inside diameter of drum") # Thickness of drum wall self.thickness_drum = Param(initialize=thk, doc="wall thickness of drum") # Thickness of insulation layer self.thickness_insulation = Var(initialize=0.2, doc="Insulation layer thickness") # Length of drum self.length_drum = Var(initialize=10, doc="Horizontal length of drum") # Number of downcomers connected at the bottom of drum, used to calculate contrac self.count_downcomer = Var( initialize=4, doc="Number of downcomers connected to drum") # Inside diameter of downcomer self.diameter_downcomer = Var(initialize=0.6, doc="Inside diameter of downcomer") # Inside Radius expression @self.Expression(doc="Inside radius of drum") def rin_drum(b): return 0.5 * b.diameter_drum # Outside Radius expression @self.Expression(doc="Outside radius of drum") def rout_drum(b): return b.rin_drum + b.thickness_drum # Outside diameter expression @self.Expression(doc="Outside radius of drum") def dout_drum(b): return b.diameter_drum + 2 * b.thickness_drum # Inner surface area (ignore two hemispheres at ends) @self.Expression(doc="Inner surface area") def area_drum(b): return b.pi * b.diameter_drum * b.length_drum def _make_performance(self): """ Define constraints which describe the behaviour of the unit model. """ # thermal conductivity of drum self.cond_therm_metal = Param(initialize=40, mutable=True) # thermal conductivity of insulation self.cond_therm_insulation = Param(initialize=0.08, mutable=True) # thermal conductivity of air self.cond_therm_air = Param(initialize=0.03, mutable=True) # thermal diffusivity of drum #self.diff_therm_metal = Param(initialize = 9.5E-6, mutable=True) # material density of drum self.density_thermal_metal = Param(initialize=7753, mutable=True) # heat capacity of drum self.heat_capacity_thermal_drum = Param(initialize=486, mutable=True) # Young modulus self.Young_modulus = Param(initialize=2.07E5, mutable=True) # Poisson's ratio self.Poisson_ratio = Param(initialize=0.292, mutable=True) # Coefficient of thermal expansion self.coefficient_therm_expansion = Param(initialize=1.4E-5, mutable=True) # constant related to Ra, (gravity*expansion_coefficient*density/viscosity/thermal_diffusivity)^(1/6) # use properties at 50 C and 1 atm, 6.84e7^(1/6)=20.223 self.const_Ra_root6 = Param(initialize=20.223, mutable=True) # constant related to Nu for free convection, 0.387/(1+0.721*Pr^(-9/16))^(8/27) # use properties at 50 C and 1 atm self.const_Nu = Param(initialize=0.322, mutable=True) # ambient pressure self.pres_amb = Param(initialize=0.81E5, doc="ambient pressure") # ambient temperature self.temp_amb = Var(self.flowsheet().config.time, initialize=300, doc="ambient temperature") # inside heat transfer coefficient self.h_in = Var(self.flowsheet().config.time, initialize=1, doc="inside heat transfer coefficient") # outside heat transfer coefficient self.h_out = Var(self.flowsheet().config.time, initialize=1, doc="outside heat transfer coefficient") # insulation free convection heat transfer coefficient self.h_free_conv = Var( self.flowsheet().config.time, initialize=1, doc="insulation free convection heat transfer coefficient") # Ra number of free convection self.N_Ra_root6 = Var( self.flowsheet().config.time, initialize=80, doc="1/6 power of Ra number of free convection of air") # Nu number of free convection self.N_Nu = Var(self.flowsheet().config.time, initialize=1, doc="Nu number of free convection of air") # Define the continuous domains for model self.r = ContinuousSet(bounds=(self.rin_drum, self.rout_drum)) # Temperature across wall thickness self.T = Var(self.flowsheet().config.time, self.r, bounds=(280, 800), initialize=550) # Declare derivatives in the model if self.config.dynamic is True: self.dTdt = DerivativeVar(self.T, wrt=self.flowsheet().config.time) self.dTdr = DerivativeVar(self.T, wrt=self.r) self.d2Tdr2 = DerivativeVar(self.T, wrt=(self.r, self.r)) discretizer = TransformationFactory('dae.finite_difference') discretizer.apply_to(self, nfe=self.config.finite_elements, wrt=self.r, scheme='CENTRAL') # Add performance variables self.level = Var(self.flowsheet().config.time, initialize=1.0, doc='Water level from the bottom of the drum') # Velocity of fluid inside downcomer pipe self.velocity_downcomer = Var( self.flowsheet().config.time, initialize=10.0, doc='Liquid water velocity at the top of downcomer') # Pressure change due to contraction self.deltaP_contraction = Var(self.flowsheet().config.time, initialize=-1.0, doc='Pressure change due to contraction') # Pressure change due to gravity self.deltaP_gravity = Var(self.flowsheet().config.time, initialize=1.0, doc='Pressure change due to gravity') # thermal diffusivity of drum @self.Expression(doc="Thermal diffusivity of drum") def diff_therm_metal(b): return b.cond_therm_metal / (b.density_thermal_metal * b.heat_capacity_thermal_drum) # Expressure for the angle from the drum center to the circumference point at water level @self.Expression(self.flowsheet().config.time, doc="angle of water level") def alpha_drum(b, t): return asin((b.level[t] - b.rin_drum) / b.rin_drum) # Expressure for the fraction of wet area @self.Expression(self.flowsheet().config.time, doc="fraction of wet area") def frac_wet_area(b, t): return (b.alpha_drum[t] + b.pi / 2) / b.pi # Constraint for volume liquid in drum @self.Constraint(self.flowsheet().config.time, doc="volume of liquid in drum") def volume_eqn(b, t): return b.volume[t] == \ ((b.alpha_drum[t]+0.5*b.pi)*b.rin_drum**2 + \ b.rin_drum*cos(b.alpha_drum[t])*(b.level[t]-b.rin_drum))* \ b.length_drum # Equation for velocity at the entrance of downcomer @self.Constraint(self.flowsheet().config.time, doc="Vecolity at entrance of downcomer") def velocity_eqn(b, t): return b.velocity_downcomer[t]*0.25*b.pi*b.diameter_downcomer**2*b.count_downcomer \ == b.control_volume.properties_out[t].flow_vol # Pressure change equation for contraction, -0.5*1/2*density*velocity^2 for stagnation head loss # plus 1/2*density*velocity^2 dynamic head (acceleration pressure change) @self.Constraint(self.flowsheet().config.time, doc="pressure change due to contraction") def pressure_change_contraction_eqn(b, t): return b.deltaP_contraction[t] == \ -0.75 * b.control_volume.properties_out[t].dens_mass_phase["Liq"] * \ b.velocity_downcomer[t]**2 # Pressure change equation for gravity, density*gravity*height @self.Constraint(self.flowsheet().config.time, doc="pressure change due to gravity") def pressure_change_gravity_eqn(b, t): return b.deltaP_gravity[t] == \ b.control_volume.properties_out[t].dens_mass_phase["Liq"] * b.gravity * b.level[t] # Total pressure change equation @self.Constraint(self.flowsheet().config.time, doc="pressure drop") def pressure_change_total_eqn(b, t): return b.deltaP[t] == b.deltaP_contraction[t] + b.deltaP_gravity[t] # Constraint for heat conduction equation @self.Constraint(self.flowsheet().config.time, self.r, doc="1-D heat conduction equation through radius") def heat_conduction_eqn(b, t, r): if r == b.r.first() or r == b.r.last(): return Constraint.Skip if self.config.dynamic is True: return b.dTdt[t, r] == b.diff_therm_metal * b.d2Tdr2[ t, r] + b.diff_therm_metal * (1 / r) * b.dTdr[t, r] else: return 0 == b.diff_therm_metal * b.d2Tdr2[ t, r] + b.diff_therm_metal * (1 / r) * b.dTdr[t, r] @self.Constraint(self.flowsheet().config.time, doc="inner wall BC") def inner_wall_bc_eqn(b, t): return b.h_in[t]*(b.control_volume.properties_out[t].temperature - b.T[t,b.r.first()]) == \ -b.dTdr[t,b.r.first()]*b.cond_therm_metal @self.Constraint(self.flowsheet().config.time, doc="outer wall BC") def outer_wall_bc_eqn(b, t): return b.h_out[t]*(b.T[t,b.r.last()] - b.temp_amb[t]) == \ -b.dTdr[t, b.r.last()]*b.cond_therm_metal # Inner wall BC for dTdt @self.Constraint(self.flowsheet().config.time, doc="extra inner wall temperature derivative") def extra_at_inner_wall_eqn(b, t): if self.config.dynamic is True: term = b.dTdt[t, b.r.first()] else: term = 0 return term == 4*b.diff_therm_metal*(b.r.first()+b.r[2])/ \ (b.r[2]-b.r.first())**2/(3*b.r.first()+b.r[2])*(b.T[t,b.r[2]] \ -b.T[t,b.r.first()]) + 8*b.diff_therm_metal/b.cond_therm_metal*b.h_in[t]*b.r.first()/ \ (b.r[2]-b.r.first())/(3*b.r.first()+b.r[2])*(b.control_volume.properties_out[t].temperature \ -b.T[t,b.r.first()]) @self.Constraint(self.flowsheet().config.time, doc="extra outer wall temperature derivative") def extra_at_outer_wall_eqn(b, t): if self.config.dynamic is True: term = b.dTdt[t, b.r.last()] else: term = 0 return term == 4*b.diff_therm_metal*(b.r.last()+b.r[-2])/ \ (b.r.last()-b.r[-2])**2/(3*b.r.last()+b.r[-2])*(b.T[t,b.r[-2]] \ -b.T[t,b.r.last()]) + 8*b.diff_therm_metal/b.cond_therm_metal*b.h_out[t]*b.r.last()/ \ (b.r.last()-b.r[-2])/(3*b.r.last()+b.r[-2])*(b.temp_amb[t] \ -b.T[t,b.r.last()]) # reduced pressure expression @self.Expression(self.flowsheet().config.time, doc="reduced pressure") def pres_reduced(b, t): return b.control_volume.properties_out[t].pressure / 2.2048e7 # calculate inner side heat transfer coefficient with minimum temperature difference set to sqrt(0.1) # multipling wet area fraction to convert it to the value based on total circumference @self.Constraint(self.flowsheet().config.time, doc="inner side heat transfer coefficient") def h_in_eqn(b, t): return b.h_in[t] == 2178.6*(b.control_volume.properties_out[t].pressure/2.2048e7)**0.36\ /(-log10(b.control_volume.properties_out[t].pressure/2.2048e7))**1.65 *\ (0.1+(b.control_volume.properties_out[t].temperature-b.T[t,b.r.first()])**2)*b.frac_wet_area[t] # Expressure for insulation heat transfer (conduction) resistance based on drum metal outside diameter @self.Expression(doc="heat transfer resistance of insulation layer") def resistance_insulation(b): return b.rout_drum * log((b.rout_drum + b.thickness_insulation) / b.rout_drum) / b.cond_therm_insulation # h_out equation considering conduction through insulation and free convection between insulation and ambient @self.Constraint(self.flowsheet().config.time, doc="outer side heat transfer coefficient") def h_out_eqn(b, t): return b.h_out[t] * (b.resistance_insulation + 1 / b.h_free_conv[t]) == 1.0 # Expressure for outside insulation wall temperature (skin temperature) @self.Expression(self.flowsheet().config.time, doc="outside insulation wall temperature") def temp_insulation_outside(b, t): return b.temp_amb[t] + (b.T[t, b.r.last()] - b.temp_amb[t] ) * b.h_out[t] / b.h_free_conv[t] # Ra number equation @self.Constraint(self.flowsheet().config.time, doc="Ra number of free convection") def Ra_number_eqn(b, t): return b.N_Ra_root6[t] == b.const_Ra_root6 * sqrt( b.dout_drum + 2 * b.thickness_insulation) * ( b.T[t, b.r.last()] - b.temp_amb[t])**0.166667 # Nu number equation @self.Constraint(self.flowsheet().config.time, doc=" Nu number of free convection") def Nu_number_eqn(b, t): return b.N_Nu[t] == (0.6 + b.const_Nu * b.N_Ra_root6[t])**2 # free convection coefficient based on the drum metal outside diameter @self.Constraint( self.flowsheet().config.time, doc= "free convection heat transfer coefficient between insulation wall and ambient" ) def h_free_conv_eqn(b, t): return b.h_free_conv[ t] == b.N_Nu[t] * b.cond_therm_air / b.dout_drum @self.Constraint(self.flowsheet().config.time, doc="heat loss of water") def heat_loss_eqn(b, t): return b.heat_duty[t] == b.area_drum*b.h_in[t]*(b.T[t,b.r.first()]\ -b.control_volume.properties_out[t].temperature) ### Calculate mechanical and thermal stresses # ---------------------------------------------------------------------------------------------- # Integer indexing for radius domain self.rindex = Param(self.r, initialize=1, mutable=True, doc="inter indexing for radius domain") # calculate integral point for mean temperature in the wall @self.Expression(self.flowsheet().config.time, doc="integral point used to estimate mean temperature" ) def int_mean_temp(b, t): return 2*(b.r[2]-b.r[1])/(b.rout_drum**2-b.rin_drum**2)*(sum(0.5*(b.r[i-1]*b.T[t,b.r[i-1]]+\ b.r[i]*b.T[t,b.r[i]]) for i in range(2,len(b.r)+1))) for index_r, value_r in enumerate(self.r, 1): self.rindex[value_r] = index_r @self.Expression(self.flowsheet().config.time, self.r, doc="integral point at each element") def int_temp(b, t, r): if b.rindex[r].value == 1: return b.T[t, b.r.first()] else: return 2*(b.r[2]-b.r[1])/(b.r[b.rindex[r].value]**2-b.rin_drum**2)\ *(sum(0.5*(b.r[j-1]*b.T[t,b.r[j-1]]+b.r[j]*b.T[t,b.r[j]])\ for j in range(2,b.rindex[r].value+1))) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at radial direction") def therm_stress_radial(b, t, r): return 0.5*b.Young_modulus*b.coefficient_therm_expansion/(1-b.Poisson_ratio)\ *((1-b.rin_drum**2/r**2)*(b.int_mean_temp[t]-b.int_temp[t,r])) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at circumferential direction") def therm_stress_circumferential(b, t, r): return 0.5*b.Young_modulus*b.coefficient_therm_expansion/(1-b.Poisson_ratio)\ *((1+b.rin_drum**2/r**2)*b.int_mean_temp[t]+(1-b.rin_drum**2/r**2)*b.int_temp[t,r]-2*b.T[t,r]) @self.Expression(self.flowsheet().config.time, self.r, doc="thermal stress at axial direction") def therm_stress_axial(b, t, r): return b.Young_modulus * b.coefficient_therm_expansion / ( 1 - b.Poisson_ratio) * (b.int_mean_temp[t] - b.T[t, r]) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at radial direction") def mech_stress_radial(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2)+(1E-5*(b.pres_amb-b.control_volume.properties_out[t].pressure)\ *b.rin_drum**2*b.rout_drum**2/(r**2*(b.rout_drum**2-b.rin_drum**2)))) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at circumferential direction") def mech_stress_circumferential(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2)-(1E-5*(b.pres_amb-b.control_volume.properties_out[t].pressure)\ *b.rin_drum**2*b.rout_drum**2/(r**2*(b.rout_drum**2-b.rin_drum**2)))) @self.Expression(self.flowsheet().config.time, self.r, doc="mechanical stress at axial direction") def mech_stress_axial(b, t, r): return 0.1*(1E-5*(b.control_volume.properties_out[t].pressure*b.rin_drum**2-b.pres_amb*b.rout_drum**2)\ /(b.rout_drum**2-b.rin_drum**2)) def set_initial_condition(self): if self.config.dynamic is True: self.control_volume.material_accumulation[:, :, :].value = 0 self.control_volume.energy_accumulation[:, :].value = 0 self.dTdt[:, :].value = 0 self.control_volume.material_accumulation[0, :, :].fix(0) self.control_volume.energy_accumulation[0, :].fix(0) self.dTdt[0, :].fix(0) def initialize(blk, state_args={}, outlvl=0, solver='ipopt', optarg={'tol': 1e-6}): ''' Drum1D initialization routine. Keyword Arguments: state_args : a dict of arguments to be passed to the property package(s) for the control_volume of the model to provide an initial state for initialization (see documentation of the specific property package) (default = {}). outlvl : sets output level of initialisation routine * 0 = no output (default) * 1 = return solver state for each step in routine * 2 = return solver state for each step in subroutines * 3 = include solver output infomation (tee=True) optarg : solver options dictionary object (default={'tol': 1e-6}) solver : str indicating whcih solver to use during initialization (default = 'ipopt') Returns: None ''' init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") opt = SolverFactory(solver) opt.options = optarg flags = blk.control_volume.initialize(outlvl=outlvl + 1, optarg=optarg, solver=solver, state_args=state_args) init_log.info_high("Initialization Step 1 Complete.") # set initial values for T r_mid = value((blk.r.first() + blk.r.last()) / 2) # assume outside wall temperature is 10 K lower than fluid temperature T_out = value(blk.control_volume.properties_in[0].temperature - 1) T_mid = value( (T_out + blk.control_volume.properties_in[0].temperature) / 2) slope = value((T_out-blk.control_volume.properties_in[0].temperature)/ \ (blk.r.last()-blk.r.first())/3) for x in blk.r: blk.T[:, x].fix(T_mid + slope * (x - r_mid)) blk.T[:, :].unfix() # Fix outlet enthalpy and pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.fix(value(blk.control_volume.properties_in[0].pressure) \ - 5000.0) blk.control_volume.properties_out[t].enth_mol.fix( value(blk.control_volume.properties_in[0].enth_mol)) blk.pressure_change_total_eqn.deactivate() blk.heat_loss_eqn.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) # Unfix outlet enthalpy and pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.unfix() blk.control_volume.properties_out[t].enth_mol.unfix() blk.pressure_change_total_eqn.activate() blk.heat_loss_eqn.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) blk.control_volume.release_state(flags, outlvl - 1) init_log.info("Initialization Complete.") def calculate_scaling_factors(self): for v in self.deltaP_gravity.values(): if iscale.get_scaling_factor(v, warning=True) is None: iscale.set_scaling_factor(v, 1e-3) for v in self.deltaP_contraction.values(): if iscale.get_scaling_factor(v, warning=True) is None: iscale.set_scaling_factor(v, 1e-3) for t, c in self.heat_loss_eqn.items(): sf = iscale.get_scaling_factor(self.heat_duty[t], default=1, warning=True) iscale.constraint_scaling_transform(c, sf) for t, c in self.pressure_change_contraction_eqn.items(): sf = iscale.get_scaling_factor(self.deltaP_contraction[t], default=1, warning=True) iscale.constraint_scaling_transform(c, sf) for t, c in self.pressure_change_gravity_eqn.items(): sf = iscale.get_scaling_factor(self.deltaP_gravity[t], default=1, warning=True) iscale.constraint_scaling_transform(c, sf) for t, c in self.pressure_change_total_eqn.items(): sf = iscale.get_scaling_factor(self.deltaP[t], default=1, warning=True) iscale.constraint_scaling_transform(c, sf)
def Xtest_list_domain_empty(self): with self.assertRaises(ValueError) as cm: self.model = ConcreteModel() self.model.y = Var([1, 2], within=[])
def test_nonnegativity_transformation_2(self): self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals) self.model.y3 = Var(self.model.S, domain=NegativeIntegers) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1 or args[0] == 0: return NonNegativeReals elif args[0] == 1: return NonNegativeIntegers elif args[0] == 2: return NonPositiveReals elif args[0] == 3: return NonPositiveIntegers elif args[0] == 4: return NegativeReals elif args[0] == 5: return NegativeIntegers elif args[0] == 6: return PositiveReals elif args[0] == 7: return PositiveIntegers elif args[0] == 8: return Reals elif args[0] == 9: return Integers elif args[0] == 10: return Binary else: return NonNegativeReals self.model.x4 = Var(domain=domainRule) self.model.y4 = Var(self.model.S, domain=domainRule) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule) instance = self.model.create_instance() xfrm = TransformationFactory('core.nonnegative_vars') transformed = xfrm.create_using(instance) # Make sure everything is nonnegative for c in ('x', 'y', 'z'): for n in ('1', '2', '3', '4'): var = transformed.__getattribute__(c + n) for ndx in var._index: self.assertTrue(self.nonnegativeBounds(var[ndx]))
def Xtest_list_domain_bad_duplicates(self): with self.assertRaises(ValueError) as cm: self.model = ConcreteModel() self.model.y = Var([1, 2], within=[1, 1, 2, 3])
def test_nonnegative_transform_3(self): self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals, bounds=(-10, 10)) self.model.y3 = Var(self.model.S, domain=NegativeIntegers, bounds=(-10, 10)) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals, bounds=(-10, 10)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1: arg = 0 else: arg = args[1] if len(args) == 1 or arg == 0: return NonNegativeReals elif arg == 1: return NonNegativeIntegers elif arg == 2: return NonPositiveReals elif arg == 3: return NonPositiveIntegers elif arg == 4: return NegativeReals elif arg == 5: return NegativeIntegers elif arg == 6: return PositiveReals elif arg == 7: return PositiveIntegers elif arg == 8: return Reals elif arg == 9: return Integers elif arg == 10: return Binary else: return Reals self.model.x4 = Var(domain=domainRule, bounds=(-10, 10)) self.model.y4 = Var(self.model.S, domain=domainRule, bounds=(-10, 10)) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule, bounds=(-10, 10)) def objRule(model): return sum(5*sum_product(model.__getattribute__(c+n)) \ for c in ('x', 'y', 'z') for n in ('1', '2', '3', '4')) self.model.obj = Objective(rule=objRule) transform = TransformationFactory('core.nonnegative_vars') instance = self.model.create_instance() transformed = transform.create_using(instance) opt = SolverFactory("glpk") instance_sol = opt.solve(instance) transformed_sol = opt.solve(transformed) self.assertEqual( instance_sol["Solution"][0]["Objective"]['obj']["value"], transformed_sol["Solution"][0]["Objective"]['obj']["value"])
def test_standard_form_transform_2(self): """ Same as #1, but adds constraints """ self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals, bounds=(-10, 10)) self.model.y3 = Var(self.model.S, domain=NegativeIntegers, bounds=(-10, 10)) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals, bounds=(-10, 10)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1: arg = 0 else: arg = args[1] if len(args) == 1 or arg == 0: return NonNegativeReals elif arg == 1: return NonNegativeIntegers elif arg == 2: return NonPositiveReals elif arg == 3: return NonPositiveIntegers elif arg == 4: return NegativeReals elif arg == 5: return NegativeIntegers elif arg == 6: return PositiveReals elif arg == 7: return PositiveIntegers elif arg == 8: return Reals elif arg == 9: return Integers elif arg == 10: return Binary else: return Reals self.model.x4 = Var(domain=domainRule, bounds=(-10, 10)) self.model.y4 = Var(self.model.S, domain=domainRule, bounds=(-10, 10)) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule, bounds=(-10, 10)) # Add some constraints def makeXConRule(var): def xConRule(model, var): return (-1, var, 1) def makeYConRule(var): def yConRule(model, var, s): return (-1, var[s], 1) def makeZConRule(var): def zConRule(model, var, s, t): return (-1, var[s, t], 1) for n in ('1', '2', '3', '4'): self.model.__setattr__( "x" + n + "_constraint", Constraint( rule=makeXConRule(self.model.__getattribute__("x" + n)))) self.model.__setattr__( "y" + n + "_constraint", Constraint( rule=makeYConRule(self.model.__getattribute__("y" + n)))) self.model.__setattr__( "z" + n + "_constraint", Constraint( rule=makeZConRule(self.model.__getattribute__("z" + n)))) def objRule(model): return sum(5*sum_product(model.__getattribute__(c+n)) \ for c in ('x', 'y', 'z') for n in ('1', '2', '3', '4')) self.model.obj = Objective(rule=objRule) transform = StandardForm() instance = self.model.create_instance() transformed = transform(instance) opt = SolverFactory("glpk") instance_sol = opt.solve(instance) transformed_sol = opt.solve(transformed) self.assertEqual( instance_sol["Solution"][0]["Objective"]['obj']["value"], transformed_sol["Solution"][0]["Objective"]['obj']["value"])
def build(self): """ Build the PID block """ super().build() # Do nothing if steady-state if self.config.dynamic is True: # Check for required config if self.config.pv is None: raise ConfigurationError("Controller configuration" " requires 'pv'") if self.config.mv is None: raise ConfigurationError("Controller configuration" " requires 'mv'") # Shorter pointers to time set information time_set = self.flowsheet().time self.pv = Reference(self.config.pv) self.mv = Reference(self.config.mv) # Parameters self.mv_lb = Param(mutable=True, initialize=0.05, doc="Controller output lower bound") self.mv_ub = Param(mutable=True, initialize=1, doc="Controller output upper bound") # Variable for basic controller settings may change with time. self.setpoint = Var(time_set, initialize=0.5, doc="Setpoint") self.gain_p = Var(time_set, initialize=0.1, doc="Gain for proportional part") if self.config.type == 'PI' or self.config.type == 'PID': self.gain_i = Var(time_set, initialize=0.1, doc="Gain for integral part") if self.config.type == 'PD' or self.config.type == 'PID': self.gain_d = Var(time_set, initialize=0.01, doc="Gain for derivative part") self.mv_ref = Var(initialize=0.5, doc="bias value of manipulated variable") if self.config.type == 'P' or self.config.type == 'PI': @self.Expression(time_set, doc="Error expression") def error(b, t): return b.setpoint[t] - b.pv[t] else: self.error = Var(time_set, initialize=0, doc="Error variable") @self.Constraint(time_set, doc="Error variable") def error_eqn(b, t): return b.error[t] == b.setpoint[t] - b.pv[t] if self.config.type == 'PI' or self.config.type == 'PID': self.integral_of_error = Var(time_set, initialize=0, doc="Integral term") self.error_from_integral = DerivativeVar( self.integral_of_error, wrt=self.flowsheet().time, initialize=0) @self.Constraint(time_set, doc="Error calculated by" " derivative of integral") def error_from_integral_eqn(b, t): return b.error[t] == b.error_from_integral[t] if self.config.type == 'PID' or self.config.type == 'PD': self.derivative_of_error = DerivativeVar( self.error, wrt=self.flowsheet().time, initialize=0) @self.Expression(time_set, doc="Proportional output") def mv_p_only(b, t): return b.gain_p[t] * b.error[t] @self.Expression(time_set, doc="Proportional output and reference") def mv_p_only_with_ref(b, t): return b.gain_p[t] * b.error[t] + b.mv_ref if self.config.type == 'PI' or self.config.type == 'PID': @self.Expression(time_set, doc="Integral output") def mv_i_only(b, t): return b.gain_i[t] * b.integral_of_error[t] if self.config.type == 'PD' or self.config.type == 'PID': @self.Expression(time_set, doc="Derivative output") def mv_d_only(b, t): return b.gain_d[t] * b.derivative_of_error[t] @self.Expression(time_set, doc="Unbounded output for manimulated variable") def mv_unbounded(b, t): if self.config.type == 'PID': return (b.mv_ref + b.gain_p[t] * b.error[t] + b.gain_i[t] * b.integral_of_error[t] + b.gain_d[t] * b.derivative_of_error[t]) elif self.config.type == 'PI': return (b.mv_ref + b.gain_p[t] * b.error[t] + b.gain_i[t] * b.integral_of_error[t]) elif self.config.type == 'PD': return (b.mv_ref + b.gain_p[t] * b.error[t] + b.gain_d[t] * b.derivative_of_error[t]) else: return b.mv_ref + b.gain_p[t] * b.error[t] @self.Constraint(time_set, doc="Bounded output of manipulated variable") def mv_eqn(b, t): if t == b.flowsheet().time.first(): return Constraint.Skip else: if self.config.bounded_output is True: return (b.mv[t]-b.mv_lb) * \ (1+exp(-4/(b.mv_ub-b.mv_lb) * (b.mv_unbounded[t] - (b.mv_lb + b.mv_ub)/2))) == b.mv_ub-b.mv_lb else: return b.mv[t] == b.mv_unbounded[t] if self.config.bounded_output is True: if self.config.type == 'PI' or self.config.type == 'PID': @self.Expression(time_set, doc="Integral error" " at error 0 and mv_ref") def integral_of_error_ref(b, t): return ((b.mv_lb + b.mv_ub) / 2 - b.mv_ref - log((b.mv_ub - b.mv_lb) / (b.mv_ref - b.mv_lb) - 1) / 4 * (b.mv_ub - b.mv_lb)) / b.gain_i[t] @self.Expression(time_set, doc="Integral error at error 0 and" " output value at current mv") def integral_of_error_mv(b, t): return ((b.mv_lb + b.mv_ub) / 2 - b.mv_ref - log((b.mv_ub - b.mv_lb) / (b.mv[t] - b.mv_lb) - 1) / 4 * (b.mv_ub - b.mv_lb)) / b.gain_i[t]