def create_embedded(): model = aml.ConcreteModel() model.d1 = aml.Param(mutable=True, initialize=0) model.d2 = aml.Param(mutable=True, initialize=0) model.d3 = aml.Param(mutable=True, initialize=0) model.d4 = aml.Param(mutable=True, initialize=0) # first stage model.x = aml.Var(bounds=(0, 10)) # first stage derived model.y = aml.Expression(expr=model.x + 1) model.fx = aml.Var() # second stage model.z = aml.Var(bounds=(-10, 10)) # second stage derived model.q = aml.Expression(expr=model.z**2) model.fz = aml.Var() model.r = aml.Var() # stage costs model.StageCost = aml.Expression([1, 2]) model.StageCost.add(1, model.fx) model.StageCost.add(2, -model.fz + model.r + model.d1) model.o = aml.Objective(expr=aml.sum_product(model.StageCost)) model.c_first_stage = aml.Constraint(expr=model.x >= 0) # test our handling of intermediate variables that # are created by Piecewise but can not necessarily # be declared on the scenario tree model.p_first_stage = aml.Piecewise(model.fx, model.x, pw_pts=[0., 2., 5., 7., 10.], pw_constr_type='EQ', pw_repn='INC', f_rule=[10., 10., 9., 10., 10.], force_pw=True) model.c_second_stage = aml.Constraint( expr=model.x + model.r * model.d2 >= -100) model.cL_second_stage = aml.Constraint(expr=model.d3 >= -model.r) model.cU_second_stage = aml.Constraint(expr=model.r <= 0) # exercise more of the code by making this an indexed # block model.p_second_stage = aml.Piecewise([1], model.fz, model.z, pw_pts=[-10, -5., 0., 5., 10.], pw_constr_type='EQ', pw_repn='INC', f_rule=[0., 0., -1., model.d4, 1.], force_pw=True) # annotate the model model.varstage = VariableStageAnnotation() # first stage model.varstage.declare(model.x, 1) model.varstage.declare(model.y, 1, derived=True) model.varstage.declare(model.fx, 1, derived=True) model.varstage.declare(model.p_first_stage, 1, derived=True) # second stage model.varstage.declare(model.z, 2) model.varstage.declare(model.q, 2, derived=True) model.varstage.declare(model.fz, 2, derived=True) model.varstage.declare(model.r, 2, derived=True) model.varstage.declare(model.p_second_stage, 2, derived=True) model.stagecost = StageCostAnnotation() for i in [1, 2]: model.stagecost.declare(model.StageCost[i], i) model.stochdata = StochasticDataAnnotation() model.stochdata.declare(model.d1, distribution=TableDistribution([0.0, 1.0, 2.0])) model.stochdata.declare(model.d2, distribution=TableDistribution([0.0, 1.0, 2.0])) model.stochdata.declare(model.d3, distribution=TableDistribution([0.0, 1.0, 2.0])) model.stochdata.declare(model.d4, distribution=TableDistribution([0.0, 1.0, 2.0])) return EmbeddedSP(model)
def _find_error_canceling_reaction(self, reference_subset, milp_software=None): """ Automatically find a valid error canceling reaction given a subset of the available benchmark species. This is done by solving a mixed integer linear programming (MILP) problem similiar to Buerger et al. (https://doi.org/10.1016/j.combustflame.2017.08.013) Args: reference_subset (list): A list of indices from self.reference_species that can participate in the reaction milp_software (list, optional): Solvers to try in order. Defaults to ['lpsolve'] or if pyomo is available defaults to ['lpsolve', 'pyomo']. lpsolve is usually faster. Returns: tuple(ErrorCancelingReaction, np.ndarray) - Reaction with the target species (if a valid reaction is found, else ``None``) - Indices (of the subset) for the species that participated in the return reaction """ if milp_software is None: milp_software = ['lpsolve'] if pyo is not None: milp_software.append('pyomo') # Define the constraints based on the provided subset c_matrix = np.take(self.constraint_matrix, reference_subset, axis=0) c_matrix = np.tile(c_matrix, (2, 1)) sum_constraints = np.sum(c_matrix, 1, dtype=int) targets = -1 * self.target_constraint m = c_matrix.shape[0] n = c_matrix.shape[1] split = int(m / 2) for solver in milp_software: if solver == 'pyomo': # Check that pyomo is available if pyo is None: raise ImportError( 'Cannot import optional package pyomo. Either install this dependency with ' '`conda install -c conda-forge pyomo glpk` or set milp_software to `lpsolve`' ) # Setup the MILP problem using pyomo lp_model = pyo.ConcreteModel() lp_model.i = pyo.RangeSet(0, m - 1) lp_model.j = pyo.RangeSet(0, n - 1) lp_model.r = pyo.RangeSet( 0, split - 1) # indices before the split correspond to reactants lp_model.p = pyo.RangeSet( split, m - 1) # indices after the split correspond to products lp_model.v = pyo.Var(lp_model.i, domain=pyo.NonNegativeIntegers ) # The stoich. coef. we are solving for lp_model.c = pyo.Param( lp_model.i, lp_model.j, initialize=lambda _, i_ind, j_ind: c_matrix[i_ind, j_ind]) lp_model.s = pyo.Param( lp_model.i, initialize=lambda _, i_ind: sum_constraints[i_ind]) lp_model.t = pyo.Param( lp_model.j, initialize=lambda _, j_ind: targets[j_ind]) lp_model.obj = pyo.Objective(rule=_pyo_obj_expression) lp_model.constraints = pyo.Constraint( lp_model.j, rule=_pyo_constraint_rule) # Solve the MILP problem using the GLPK MILP solver (https://www.gnu.org/software/glpk/) opt = pyo.SolverFactory('glpk') results = opt.solve(lp_model, timelimit=1) # Return the solution if a valid reaction is found. Otherwise continue to next solver if results.solver.termination_condition == pyo.TerminationCondition.optimal: # Extract the solution and find the species with non-zero stoichiometric coefficients solution = lp_model.v.extract_values().values() break elif solver == 'lpsolve': # Save the current signal handler sig = signal.getsignal(signal.SIGINT) # Setup the MILP problem using lpsolve lp = lpsolve('make_lp', 0, m) lpsolve('set_verbose', lp, 2) # Reduce the logging from lpsolve lpsolve('set_obj_fn', lp, sum_constraints) lpsolve('set_minim', lp) for j in range(n): lpsolve( 'add_constraint', lp, np.concatenate( (c_matrix[:split, j], -1 * c_matrix[split:, j])), EQ, targets[j]) lpsolve('add_constraint', lp, np.ones(m), LE, 20) # Use at most 20 species (including replicates) lpsolve('set_timeout', lp, 1) # Move on if lpsolve can't find a solution quickly # Constrain v_i to be 4 or less for i in range(m): lpsolve('set_upbo', lp, i, 4) # All v_i must be integers lpsolve('set_int', lp, [True] * m) status = lpsolve('solve', lp) # Reset signal handling since lpsolve changed it try: signal.signal(signal.SIGINT, sig) except ValueError: # This is not being run in the main thread, so we cannot reset signal pass # Return the solution if a valid reaction is found. Otherwise continue to next solver if status == 0: _, solution = lpsolve('get_solution', lp)[:2] break else: raise ValueError( f'Unrecognized MILP solver {solver} for isodesmic reaction generation' ) else: return None, None reaction = ErrorCancelingReaction(self.target, dict()) subset_indices = [] for index, v in enumerate(solution): if v > 0: subset_indices.append(index % split) if index < split: reaction.species.update( {self.reference_species[reference_subset[index]]: -v}) else: reaction.species.update({ self.reference_species[reference_subset[index % split]]: v }) return reaction, np.array(subset_indices)
def test_linear_scaling(self): model = pyo.ConcreteModel() model.x = pyo.Var([1, 2, 3], bounds=(-10, 10), initialize=5.0) model.z = pyo.Var(bounds=(10, 20)) model.obj = pyo.Objective(expr=model.z + model.x[1]) # test scaling of duals as well model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT) model.rc = pyo.Suffix(direction=pyo.Suffix.IMPORT) def con_rule(m, i): if i == 1: return m.x[1] + 2*m.x[2] + 1*m.x[3] == 4.0 if i == 2: return m.x[1] + 2*m.x[2] + 2*m.x[3] == 5.0 if i == 3: return m.x[1] + 3.0*m.x[2] + 1*m.x[3] == 5.0 model.con = pyo.Constraint([1,2,3], rule=con_rule) model.zcon = pyo.Constraint(expr=model.z >= model.x[2]) x_scale = 0.5 obj_scale = 2.0 z_scale = -10.0 con_scale1 = 0.5 con_scale2 = 2.0 con_scale3 = -5.0 zcon_scale = -3.0 unscaled_model = model.clone() unscaled_model.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) unscaled_model.scaling_factor[unscaled_model.obj] = obj_scale unscaled_model.scaling_factor[unscaled_model.x] = x_scale unscaled_model.scaling_factor[unscaled_model.z] = z_scale unscaled_model.scaling_factor[unscaled_model.con[1]] = con_scale1 unscaled_model.scaling_factor[unscaled_model.con[2]] = con_scale2 unscaled_model.scaling_factor[unscaled_model.con[3]] = con_scale3 unscaled_model.scaling_factor[unscaled_model.zcon] = zcon_scale scaled_model = pyo.TransformationFactory('core.scale_model').create_using(unscaled_model) # print('*** unscaled ***') # unscaled_model.pprint() # print('*** scaled ***') # scaled_model.pprint() glpk_solver = pyo.SolverFactory('glpk') if isinstance(glpk_solver, UnknownSolver) or \ (not glpk_solver.available()): raise unittest.SkipTest("glpk solver not available") glpk_solver.solve(unscaled_model) glpk_solver.solve(scaled_model) # check vars self.assertAlmostEqual(pyo.value(unscaled_model.x[1]), pyo.value(scaled_model.scaled_x[1])/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[2]), pyo.value(scaled_model.scaled_x[2])/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[3]), pyo.value(scaled_model.scaled_x[3])/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.z), pyo.value(scaled_model.scaled_z)/z_scale, 4) # check var lb self.assertAlmostEqual(pyo.value(unscaled_model.x[1].lb), pyo.value(scaled_model.scaled_x[1].lb)/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[2].lb), pyo.value(scaled_model.scaled_x[2].lb)/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[3].lb), pyo.value(scaled_model.scaled_x[3].lb)/x_scale, 4) # note: z_scale is negative, therefore, the inequality directions swap self.assertAlmostEqual(pyo.value(unscaled_model.z.lb), pyo.value(scaled_model.scaled_z.ub)/z_scale, 4) # check var ub self.assertAlmostEqual(pyo.value(unscaled_model.x[1].ub), pyo.value(scaled_model.scaled_x[1].ub)/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[2].ub), pyo.value(scaled_model.scaled_x[2].ub)/x_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.x[3].ub), pyo.value(scaled_model.scaled_x[3].ub)/x_scale, 4) # note: z_scale is negative, therefore, the inequality directions swap self.assertAlmostEqual(pyo.value(unscaled_model.z.ub), pyo.value(scaled_model.scaled_z.lb)/z_scale, 4) # check var multipliers (rc) self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[1]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[1]])*x_scale/obj_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[2]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[2]])*x_scale/obj_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[3]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[3]])*x_scale/obj_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.z]), pyo.value(scaled_model.rc[scaled_model.scaled_z])*z_scale/obj_scale, 4) # check constraint multipliers self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[1]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[1]])*con_scale1/obj_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[2]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[2]])*con_scale2/obj_scale, 4) self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[3]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[3]])*con_scale3/obj_scale, 4) # put the solution from the scaled back into the original pyo.TransformationFactory('core.scale_model').propagate_solution(scaled_model, model) # compare var values and rc with the unscaled soln for vm in model.component_objects(ctype=pyo.Var, descend_into=True): cuid = pyo.ComponentUID(vm) vum = cuid.find_component_on(unscaled_model) self.assertEqual((vm in model.rc), (vum in unscaled_model.rc)) if vm in model.rc: self.assertAlmostEqual(pyo.value(model.rc[vm]), pyo.value(unscaled_model.rc[vum]), 4) for k in vm: vmk = vm[k] vumk = vum[k] self.assertAlmostEqual(pyo.value(vmk), pyo.value(vumk), 4) self.assertEqual((vmk in model.rc), (vumk in unscaled_model.rc)) if vmk in model.rc: self.assertAlmostEqual(pyo.value(model.rc[vmk]), pyo.value(unscaled_model.rc[vumk]), 4) # compare constraint duals and value for model_con in model.component_objects(ctype=pyo.Constraint, descend_into=True): cuid = pyo.ComponentUID(model_con) unscaled_model_con = cuid.find_component_on(unscaled_model) self.assertEqual((model_con in model.rc), (unscaled_model_con in unscaled_model.rc)) if model_con in model.dual: self.assertAlmostEqual(pyo.value(model.dual[model_con]), pyo.value(unscaled_model.dual[unscaled_model_con]), 4) for k in model_con: mk = model_con[k] umk = unscaled_model_con[k] self.assertEqual((mk in model.dual), (umk in unscaled_model.dual)) if mk in model.dual: self.assertAlmostEqual(pyo.value(model.dual[mk]), pyo.value(unscaled_model.dual[umk]), 4)
def test_power_plant_costing(): # Create a Concrete Model as the top level object m = pyo.ConcreteModel() # Add a flowsheet object to the model m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.get_costing(year='2018') ########################################################################### # Create costing constraints # ########################################################################### # subcritical PC coal_accounts = ['1.1', '1.2', '1.3'] m.fs.subcritical_PC = pyo.Block() m.fs.subcritical_PC.coal_feed_rate = pyo.Var(initialize=7613.37) # tpd m.fs.subcritical_PC.coal_feed_rate.fix() get_PP_costing(m.fs.subcritical_PC, coal_accounts, m.fs.subcritical_PC.coal_feed_rate, 'tpd', 1) # two-stage, slurry-feed IGCC feedwater_accounts = ['3.1', '3.3', '3.5'] m.fs.IGCC_1 = pyo.Block() m.fs.IGCC_1.feedwater_flow_rate = pyo.Var(initialize=1576062.15) # lb/hr m.fs.IGCC_1.feedwater_flow_rate.fix() get_PP_costing(m.fs.IGCC_1, feedwater_accounts, m.fs.IGCC_1.feedwater_flow_rate, 'lb/hr', 3) # single-stage, slurry-feed, IGCC syngas_accounts = ['6.1', '6.2', '6.3'] m.fs.IGCC_2 = pyo.Block() m.fs.IGCC_2.syngas_flow_rate = pyo.Var(initialize=182335.921) # lb/hr m.fs.IGCC_2.syngas_flow_rate.fix() get_PP_costing(m.fs.IGCC_2, syngas_accounts, m.fs.IGCC_2.syngas_flow_rate, 'lb/hr', 4) # single-stage, dry-feed, IGCC HRSG_accounts = ['7.1', '7.2'] m.fs.IGCC_3 = pyo.Block() m.fs.IGCC_3.HRSG_duty = pyo.Var(initialize=1777.86) # MMBtu/hr m.fs.IGCC_3.HRSG_duty.fix() get_PP_costing(m.fs.IGCC_3, HRSG_accounts, m.fs.IGCC_3.HRSG_duty, 'MMBtu/hr', 5) # NGCC steam_turbine_accounts = ['8.1', '8.2', '8.5'] m.fs.NGCC = pyo.Block() m.fs.NGCC.turbine_power = pyo.Var(initialize=212500) # kW m.fs.NGCC.turbine_power.fix() get_PP_costing(m.fs.NGCC, steam_turbine_accounts, m.fs.NGCC.turbine_power, 'kW', 6) # AUSC PC AUSC_accounts = ['4.9', '8.4'] m.fs.AUSC = pyo.Block() m.fs.AUSC.feedwater_flow = pyo.Var(initialize=3298815.58) # lb/hr m.fs.AUSC.feedwater_flow.fix() get_PP_costing(m.fs.AUSC, AUSC_accounts, m.fs.AUSC.feedwater_flow, 'lb/hr', 7) # add total cost build_flowsheet_cost_constraint(m) # add initialize costing_initialization(m.fs) # try solving solver = get_solver() results = solver.solve(m, tee=True) assert results.solver.termination_condition == \ pyo.TerminationCondition.optimal # all numbers come from the NETL excel file # "201.001.001_BBR4 COE Spreadsheet_Rev0U_20190919_njk.xlsm" assert pytest.approx(pyo.value( m.fs.subcritical_PC.costing.total_plant_cost['1.1']), abs=1e-1) == 2379 / 1e3 assert pytest.approx(pyo.value( m.fs.subcritical_PC.costing.total_plant_cost['1.2']), abs=1e-1) == 6588 / 1e3 assert pytest.approx(pyo.value( m.fs.subcritical_PC.costing.total_plant_cost['1.3']), abs=1e-1) == 61409 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_1.costing.total_plant_cost['3.1']), abs=1e-1) == 10807 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_1.costing.total_plant_cost['3.3']), abs=1e-1) == 2564 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_1.costing.total_plant_cost['3.5']), abs=1e-1) == 923 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_2.costing.total_plant_cost['6.1']), abs=1e-1) == 117850 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_2.costing.total_plant_cost['6.2']), abs=1e-1) == 3207 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_2.costing.total_plant_cost['6.3']), abs=1e-1) == 3770 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_3.costing.total_plant_cost['7.1']), abs=1e-1) == 53530 / 1e3 assert pytest.approx(pyo.value( m.fs.IGCC_3.costing.total_plant_cost['7.2']), abs=1e-1) == 19113 / 1e3 assert pytest.approx(pyo.value(m.fs.NGCC.costing.total_plant_cost['8.1']), abs=1e-1) == 49468 / 1e3 assert pytest.approx(pyo.value(m.fs.NGCC.costing.total_plant_cost['8.2']), abs=1e-1) == 565 / 1e3 assert pytest.approx(pyo.value(m.fs.NGCC.costing.total_plant_cost['8.5']), abs=1e-1) == 4094 / 1e3 assert pytest.approx(pyo.value(m.fs.AUSC.costing.bare_erected_cost['4.9']), abs=1e-1) == 295509 / 1e3 assert pytest.approx(pyo.value(m.fs.AUSC.costing.bare_erected_cost['8.4']), abs=1e-1) == 57265 / 1e3 return m
def test_sCO2_costing(): # Create a Concrete Model as the top level object m = pyo.ConcreteModel() # Add a flowsheet object to the model m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.get_costing(year='2017') # ###################################################### # Primary Heater m.fs.boiler = pyo.Block() m.fs.boiler.heat_duty = pyo.Var(initialize=1461.5e6) m.fs.boiler.heat_duty.fix() m.fs.boiler.temp = pyo.Var(initialize=620) # C m.fs.boiler.temp.fix() get_sCO2_unit_cost(m.fs.boiler, 'Coal-fired heater', m.fs.boiler.heat_duty * (1e-6), temp_C=m.fs.boiler.temp) # ###################################################### # CO2 Turbine m.fs.turbine = pyo.Block() m.fs.turbine.work_isentropic = pyo.Var(initialize=1006.2e6) m.fs.turbine.work_isentropic.fix() m.fs.turbine.temp = pyo.Var(initialize=620) m.fs.turbine.temp.fix() get_sCO2_unit_cost(m.fs.turbine, 'Axial turbine', m.fs.turbine.work_isentropic * (1e-6), temp_C=m.fs.turbine.temp, n_equip=1) # ###################################################### # Generator m.fs.generator = pyo.Block() m.fs.generator.work_isentropic = pyo.Var(initialize=1006.2e6) get_sCO2_unit_cost(m.fs.generator, 'Generator', m.fs.turbine.work_isentropic * (1e-6), n_equip=1) # ###################################################### # High Temperature Recuperator m.fs.HTR = pyo.Block() m.fs.HTR.heat_duty = pyo.Var(initialize=1461e6) # W m.fs.HTR.heat_duty.fix() m.fs.HTR.LMTD = pyo.Var(initialize=21.45) m.fs.HTR.LMTD.fix() m.fs.HTR.temp = pyo.Var(initialize=453) m.fs.HTR.temp.fix() m.fs.HTR.UA = pyo.Var(initialize=1e8) # gives units of W/K @m.fs.Constraint() def HTR_UA_rule(b): return (b.HTR.UA * b.HTR.LMTD == b.HTR.heat_duty) get_sCO2_unit_cost(m.fs.HTR, 'Recuperator', m.fs.HTR.UA, temp_C=m.fs.HTR.temp) # ###################################################### # Low Temperature Recuperator m.fs.LTR = pyo.Block() m.fs.LTR.heat_duty = pyo.Var(initialize=911.7e6) # W m.fs.LTR.heat_duty.fix() m.fs.LTR.LMTD = pyo.Var(initialize=5.21) m.fs.LTR.LMTD.fix() m.fs.LTR.temp = pyo.Var(initialize=216) m.fs.LTR.temp.fix() m.fs.LTR.UA = pyo.Var(initialize=1e8) @m.fs.Constraint() def LTR_UA_rule(b): return (b.LTR.UA * b.LTR.LMTD == b.LTR.heat_duty) get_sCO2_unit_cost(m.fs.LTR, 'Recuperator', m.fs.LTR.UA, temp_C=m.fs.LTR.temp) # ###################################################### # CO2 Cooler, costed using the recouperator not dry cooler m.fs.co2_cooler = pyo.Block() m.fs.co2_cooler.heat_duty = pyo.Var(initialize=739421217) m.fs.co2_cooler.heat_duty.fix() m.fs.co2_cooler.temp = pyo.Var(initialize=81) m.fs.co2_cooler.temp.fix() # Estimating LMTD # Cost from report: $27,780 thousand # Back-calculated UA: 41819213 W/K # Heat duty from report: 2523 MMBTu/hr --> 739421217 W # Estimated LMTD: 17.68 K m.fs.co2_cooler.LMTD = pyo.Var(initialize=5) m.fs.co2_cooler.UA = pyo.Var(initialize=1e5) m.fs.co2_cooler.LMTD.fix(17.68) @m.fs.Constraint() def co2_cooler_UA_rule(b): return (b.co2_cooler.UA * b.co2_cooler.LMTD == b.co2_cooler.heat_duty) get_sCO2_unit_cost(m.fs.co2_cooler, 'Recuperator', m.fs.co2_cooler.UA, temp_C=m.fs.co2_cooler.temp) # ###################################################### # Main Compressor - 5.99 m^3/s in Baseline620 m.fs.main_compressor = pyo.Block() m.fs.main_compressor.flow_vol = pyo.Var(initialize=5.99) m.fs.main_compressor.flow_vol.fix() get_sCO2_unit_cost(m.fs.main_compressor, 'Barrel type compressor', m.fs.main_compressor.flow_vol, n_equip=5.0) # ###################################################### # Main Compressor Motor m.fs.main_compressor_motor = pyo.Block() m.fs.main_compressor_motor.work_isentropic = pyo.Var(initialize=159.7e6) m.fs.main_compressor_motor.work_isentropic.fix() get_sCO2_unit_cost(m.fs.main_compressor_motor, 'Open drip-proof motor', m.fs.main_compressor_motor.work_isentropic * (1e-6), n_equip=5.0) # ###################################################### # Recompressor - 6.89 m^3/s in Baseline620 m.fs.bypass_compressor = pyo.Block() m.fs.bypass_compressor.flow_vol = pyo.Var(initialize=6.89) m.fs.bypass_compressor.flow_vol.fix() get_sCO2_unit_cost(m.fs.bypass_compressor, 'Barrel type compressor', m.fs.bypass_compressor.flow_vol, n_equip=4.0) # ###################################################### # Recompressor Motor m.fs.bypass_compressor_motor = pyo.Block() m.fs.bypass_compressor_motor.work_isentropic = pyo.Var(initialize=124.3e6) m.fs.bypass_compressor_motor.work_isentropic.fix() get_sCO2_unit_cost(m.fs.bypass_compressor_motor, 'Open drip-proof motor', m.fs.bypass_compressor_motor.work_isentropic * (1e-6), n_equip=4.0) # add total cost build_flowsheet_cost_constraint(m) # add initialize costing_initialization(m.fs) # try solving solver = get_solver() results = solver.solve(m, tee=True) assert results.solver.termination_condition == \ pyo.TerminationCondition.optimal assert pytest.approx(pyo.value(m.fs.boiler.costing.equipment_cost), abs=1e-1) == 216300 / 1e3 assert pytest.approx(pyo.value(m.fs.turbine.costing.equipment_cost), abs=1e-1) == 13160 / 1e3 assert pytest.approx(pyo.value(m.fs.generator.costing.equipment_cost), abs=1e-1) == 4756 / 1e3 assert pytest.approx(pyo.value(m.fs.HTR.costing.equipment_cost), abs=1e-1) == 40150 / 1e3 assert pytest.approx(pyo.value(m.fs.LTR.costing.equipment_cost), abs=1e-1) == 81860 / 1e3 assert pytest.approx(pyo.value(m.fs.co2_cooler.costing.equipment_cost), abs=1e-1) == 27780 / 1e3 assert pytest.approx(pyo.value( m.fs.main_compressor.costing.equipment_cost), abs=1e-1) == 31640 / 1e3 assert pytest.approx(pyo.value( m.fs.bypass_compressor.costing.equipment_cost), abs=1e-1) == 26360 / 1e3 assert pytest.approx( pyo.value(m.fs.main_compressor_motor.costing.equipment_cost) + pyo.value(m.fs.bypass_compressor_motor.costing.equipment_cost), abs=1e-1) == 29130 / 1e3 assert pytest.approx(pyo.value( m.fs.bypass_compressor.costing.equipment_cost), abs=1e-1) == 26360 / 1e3 return m
#m.I_ORC = pyo.Set(within = m.I, initialize = ['ORC']) #SET T = all time instants (24 hs in a day) T = 24 #elemnts in the Set m.T= pyo.RangeSet(0,T-1) #Set S = all storages #we have two types of storages, electrical(BATTERT), or thermal storage m.S = pyo.Set(initialize = ['TES','BAT']) m.S_el = pyo.Set(within = m.S, initialize = ['BAT']) m.S_th = pyo.Set(within = m.S, initialize = ['TES']) ##Variables #Real Variables #for the machines that have fuel as the input, second term is time, and the third one is the type of variable. m.f = pyo.Var(m.I_f,m.T,domain = pyo.NonNegativeReals) m.q = pyo.Var(m.I_th,m.T,domain = pyo.NonNegativeReals) m.p = pyo.Var(m.I_el,m.T,domain = pyo.NonNegativeReals) m.el_in = pyo.Var(m.I_el_consum,m.T,domain = pyo.NonNegativeReals) #Binary #This is the on/off variable m.z = pyo.Var(m.I,m.T,domain = pyo.Binary) #Now we define the startup variable. m.dSU = pyo.Var(m.I,m.T,domain = pyo.Binary) #We have not defined the shut-down variable because in this case we had zero cost for shutdown so we have no constraint #Related to storage # u is the state of charge of storage S at time T m.u = pyo.Var(m.S,m.T,domain = pyo.NonNegativeReals) #We have Charge and Discharge variables which will be used in energy balance constraint
import pyomo.environ as pyo, numpy as np from pyomo.environ import * from pyomo.opt import SolverFactory #creation of the model and variables model = pyo.ConcreteModel() model.C = pyo.Var(range(1, 4)) model.n = pyo.Var(range(1, 4), within=Integers, bounds=(0, 1000)) model.b = pyo.Var(within=Binary) C = model.C n = model.n b = model.b M = 100000 #objective function model.obj = pyo.Objective(expr=pyo.summation(C)) #constraint model.total = pyo.Constraint(expr=pyo.summation(n) == 2100) model.C1 = pyo.Constraint(expr=C[1] == 2 * n[1]) # aqui el constrain C2 es reemplazado por por estos atendiendo a la tecnica BigM: # -b * M <= C <= b * M --> constrains C2a y C2b (para cada lado de la desigualdad). # -(1-b) * M <= C - x <= (1-b) * M --> constrains C2c y C3c (para cada lado de la desigualdad). model.C2a = pyo.Constraint(expr=-b * M <= C[2]) model.C2b = pyo.Constraint(expr=C[2] <= b * M) model.C2c = pyo.Constraint(expr=-(1 - b) * M <= C[2] - (6 * n[2] + 1000)) model.C3c = pyo.Constraint(expr=C[2] - (6 * n[2] + 1000) <= (1 - b) * M) model.C2n = pyo.Constraint(expr=n[2] <= b * 1000) model.C3 = pyo.Constraint(expr=C[3] == 7 * n[3])
def filterpyomofit(self, iterationNo): from pyomo import environ def lsqObjPyomo(model): sum = 0 for index in range(model.trainingsize): p_ipo = model.pipo[index] q_ipo = model.qipo[index] P = 0 for i in model.prange: P += model.coeff[i] * p_ipo[i] Q = 0 for i in model.qrange: Q += model.coeff[i] * q_ipo[i - model.M] sum += (model.Y[index] * Q - P)**2 return sum def robustConstrPyomo(model, index): q_ipo = model.qipo[index] Q = 0 for i in model.qrange: Q += model.coeff[i] * q_ipo[i - model.M] return Q >= 1 if (self._strategy != 0): raise Exception( "strategy %d for fitstrategy, %s not yet implemented" % (self.strategy, self.fitstrategy)) # concrete model model = environ.ConcreteModel() model.dimrange = range(self._dim) model.prange = range(self.M) model.qrange = range(self.M, self.M + self.N) model.coeffrange = range(self.M + self.N) model.M = self._M model.N = self._N model.trainingsize = self.trainingsize model.trainingsizerangeforconstr = range(self._trainingsize + iterationNo) model.pipo = self._ipo[:, 0].tolist() model.qipo = self._ipo[:, 1].tolist() model.Y = self._Y.tolist() model.coeff = environ.Var(model.coeffrange) for d in range(self.M + self.N): model.coeff[d].value = 1 model.coeff[self.M].value = 2 model.lsqfit = environ.Objective(rule=lsqObjPyomo, sense=1) model.robustConstr = environ.Constraint( model.trainingsizerangeforconstr, rule=robustConstrPyomo) opt = environ.SolverFactory('filter') # opt.options['eps'] = 1e-10 # opt.options['iprint'] = 1 pyomodebug = self._filterpyomodebug if (pyomodebug == 0): ret = opt.solve(model) elif (pyomodebug == 1): import uuid uniquefn = str(uuid.uuid4()) logfn = "/tmp/%s.log" % (uniquefn) print("Log file name: %s" % (logfn)) ret = opt.solve(model, tee=True, logfile=logfn) model.pprint() ret.write() elif (pyomodebug == 2): opt.options['iprint'] = 1 logfn = "%s/%s_p%d_q%d_ts%s_i%d.log" % ( self._debugfolder, self._fnname, self.m, self.n, self.trainingscale, iterationNo) self.printDebug("Starting filter") ret = opt.solve(model, logfile=logfn) optstatus = { 'message': str(ret.solver.termination_condition), 'status': str(ret.solver.status), 'time': ret.solver.time, 'error_rc': ret.solver.error_rc } coeffs = np.array( [model.coeff[i].value for i in range(self._M + self._N)]) leastSq = model.lsqfit() return coeffs, leastSq, optstatus
def pyomoRobO(self, coeffs, threshold=0.2, solver='filter', r=0): from pyomo import environ def robObjPyomo(model): res = 0 for l in range(len(model.struct_q)): mon = model.struct_q[l] term = 1 for d in model.dimrange: try: exp = mon[d] except: exp = mon term *= model.x[d]**exp res += model.coeffs[l + model.M] * term return res def variableBound(model, i): b = (model.box[i][0], model.box[i][1]) return b model = environ.ConcreteModel() model.struct_q = self._struct_q.tolist() model.coeffs = coeffs.tolist() model.dimrange = range(self._dim) model.box = self.box.tolist() model.M = self._M model.x = environ.Var(model.dimrange, bounds=variableBound) if (r == 0): for d in range(self.dim): model.x[d].value = (self.box[d][0] + self.box[d][1]) / 2 else: for d in range(self.dim): model.x[d].value = np.random.rand() * ( self.box[d][1] - self.box[d][0]) + self.box[d][0] model.robO = environ.Objective(rule=robObjPyomo, sense=1) opt = environ.SolverFactory(solver) """ Control where the log file is written by passing logfile=<name> to the solve method. If you want to print solver log to console, add tee=True to solve method If you want the solution and problem files to be logged, you can set keepfiles=True for that file to not be deleted. Also, if you set keepfiles to True, you can find the location of Solver log file, Solver problem files, and Solver solution file printed on console (usually located in /var/folders/) """ pyomodebug = 0 if (pyomodebug == 0): ret = opt.solve(model) elif (pyomodebug == 1): import uuid uniquefn = str(uuid.uuid4()) logfn = "/tmp/%s.log" % (uniquefn) print("Log file name: %s" % (logfn)) ret = opt.solve(model, tee=True, logfile=logfn) model.pprint() ret.write() optstatus = { 'message': str(ret.solver.termination_condition), 'status': str(ret.solver.status), 'time': ret.solver.time, 'error_rc': ret.solver.error_rc } robO = model.robO() x = np.array([model.x[i].value for i in range(self._dim)]) # info = [{'robustArg':x.tolist(),'robustObj':robO,'log':optstatus}] return x, robO, optstatus
master = pyo.ConcreteModel() # ************************************************************************** # Sets # ************************************************************************** # Hour sets master.H = pyo.RangeSet(1, len(HOURS)-1) master.H_all = pyo.RangeSet(0, len(HOURS)-1) # ************************************************************************** # Variables # ************************************************************************** # Unit commitment for generator master.u = pyo.Var(master.H_all, within=pyo.Binary) # Initialization for u master.u[0].fix(0) # Electricity purchased with the forward contract master.p1 = pyo.Var(master.H_all, within=pyo.NonNegativeReals) # Initialization for p1 master.p1[0].fix(0) # Value function for second stage problem master.alpha = pyo.Var(master.H_all) # Initialization for p1 master.alpha[0].fix(0)
def build(self): """ Build the PID block """ super().build() # do the ProcessBlockData voodoo for config # Check for required config if self.config.pv is None: raise ConfigurationError("Controller configuration requires 'pv'") if self.config.output is None: raise ConfigurationError( "Controller configuration requires 'output'") # Shorter pointers to time set information time_set = self.flowsheet().time t0 = time_set.first() # Variable for basic controller settings may change with time. self.setpoint = pyo.Var(time_set, doc="Setpoint") self.gain = pyo.Var(time_set, doc="Controller gain") self.time_i = pyo.Var(time_set, doc="Integral time") self.time_d = pyo.Var(time_set, doc="Derivative time") # Make the initial derivative term a variable so you can set it. This # should let you carry on from the end of another time period self.err_d0 = pyo.Var(doc="Initial derivative term", initialize=0) self.err_d0.fix() if not self.config.calculate_initial_integral: self.err_i0 = pyo.Var(doc="Initial integral term", initialize=0) self.err_i0.fix() # Make references to the output and measured variables self.pv = pyo.Reference(self.config.pv) # No duplicate self.output = pyo.Reference(self.config.output) # No duplicate # Create an expression for error from setpoint @self.Expression(time_set, doc="Setpoint error") def err(b, t): return self.setpoint[t] - self.pv[t] # Use expressions to allow the some future configuration @self.Expression(time_set) def pterm(b, t): return -self.pv[t] @self.Expression(time_set) def dterm(b, t): return -self.pv[t] @self.Expression(time_set) def iterm(b, t): return self.err[t] # Output limits parameter self.limits = pyo.Param(["l", "h"], mutable=True, doc="controller output limits", initialize={ "l": self.config.lower, "h": self.config.upper }) # Smooth min and max are used to limit output, smoothing parameter here self.smooth_eps = pyo.Param( mutable=True, initialize=1e-4, doc="Smoothing parameter for controller output limits") # This is ugly, but want integral and derivative error as expressions, # nice implementation with variables is harder to initialize and solve @self.Expression(time_set, doc="Derivative error.") def err_d(b, t): if t == t0: return self.err_d0 else: return (b.dterm[t] - b.dterm[time_set.prev(t)])\ /(t - time_set.prev(t)) # Want to fix the output varaible at the first time step to make # solving easier. This calculates the initial integral error to line up # with the initial output value, keeps the controller from initially # jumping. if self.config.calculate_initial_integral: @self.Expression(doc="Initial integral error") def err_i0(b): return b.time_i[t0]*(b.output[0] - b.gain[t0]*b.pterm[t0]\ - b.gain[t0]*b.time_d[t0]*b.err_d[t0])/b.gain[t0] # integral error @self.Expression(time_set, doc="Integral error") def err_i(b, t_end): return b.err_i0 + sum((b.iterm[t] + b.iterm[time_set.prev(t)]) * (t - time_set.prev(t)) / 2.0 for t in time_set if t <= t_end and t > t0) # Calculate the unconstrainted controller output @self.Expression(time_set, doc="Unconstrained contorler output") def unconstrained_output(b, t): return b.gain[t] * (b.pterm[t] + 1.0 / b.time_i[t] * b.err_i[t] + b.time_d[t] * b.err_d[t]) # Add the controller output constraint and limit it with smooth min/max e = self.smooth_eps h = self.limits["h"] l = self.limits["l"] @self.Constraint(time_set, doc="Controller output constraint") def output_constraint(b, t): if t == t0: return pyo.Constraint.Skip else: return self.output[t] ==\ smooth_min( smooth_max(self.unconstrained_output[t], l, e), h, e)
return m.DPh.mass_flow + m.CPh.mass_flow == 100 m.bal_const = pe.Constraint(rule=bal_const_rule) # Inicializar corrientes opt.solve(CPh) opt.solve(DPh) ##################################### # Modelo de emulsion ##################################### m.cd = pe.Set(initialize=['prop', 'appl']) # Conjunto de condiciones ############### Viscosidad de emulsion m.mu = pe.Var(m.cd, domain=pe.PositiveReals) # Emulsion viscosity ############### # Variables compartidas m.vo = pe.Var(domain=pe.PositiveReals, bounds=(0, 1)) # Fraccion volumetrica O/W m.dM = pe.Var(domain=pe.PositiveReals) # D32 [mu m] m.ti = pe.Var(domain=pe.PositiveReals) # Tension interfacial m.k = pe.Var(m.cd, domain=pe.PositiveReals) # Ratio de viscosidades m.Sr = pe.Var(m.cd, domain=pe.PositiveReals) # Shear rate # Variables fijas y limites m.Sr['appl'].fix(100) # Shear rate m.Sr['prop'].value = 1000 #
n = len(cost_matrix) ##-------------------------MODEL INITIALIZATION AND ITS PARAMETERS--------------------## #Model model = pyEnv.ConcreteModel() #Indexes for the cities model.M = pyEnv.RangeSet(n) model.N = pyEnv.RangeSet(n) #Index for the dummy variable u model.U = pyEnv.RangeSet(2, n) #Decision variables xij model.x = pyEnv.Var(model.N, model.M, within=pyEnv.Binary) #Dummy variable ui model.u = pyEnv.Var(model.N, within=pyEnv.NonNegativeIntegers, bounds=(0, n - 1)) #Cost Matrix cij model.c = pyEnv.Param(model.N, model.M, initialize=lambda model, i, j: cost_matrix[i - 1][j - 1]) ##-------------------------OBJECTIVE FUNCTION AND CONSTRAINTS--------------------## def obj_func(model):
model = pyo.ConcreteModel() # ****************************************************************** # Sets # ****************************************************************** # hour set model.H = pyo.RangeSet(0, 23) # ****************************************************************** # Variables # ****************************************************************** # fixed master problem variables model.u = pyo.Var(model.H) model.p1 = pyo.Var(model.H) # electricity produced by generator model.pg = pyo.Var(model.H, within=pyo.NonNegativeReals) # electrictiy bought from retailer model.p2 = pyo.Var(model.H, within=pyo.NonNegativeReals) # ****************************************************************** # Objective function # ****************************************************************** model.OBJ = pyo.Objective(expr=sum( c1 * model.u[h] + l1 * model.p1[h] + c2 * model.pg[h] + l2 * model.p2[h] for h in model.H))
import pyomo.environ as pe from pyomo.contrib.interior_point.interior_point import InteriorPointSolver from pyomo.contrib.interior_point.interface import InteriorPointInterface from pyomo.contrib.interior_point.linalg.mumps_interface import MumpsInterface import logging logging.basicConfig(level=logging.INFO) # Supposedly this sets the root logger's level to INFO. # But when linear_solver.logger logs with debug, # it gets propagated to a mysterious root logger with # level NOTSET... m = pe.ConcreteModel() m.x = pe.Var() m.y = pe.Var() m.obj = pe.Objective(expr=m.x**2 + m.y**2) m.c1 = pe.Constraint(expr=m.y == pe.exp(m.x)) m.c2 = pe.Constraint(expr=m.y >= (m.x - 1)**2) interface = InteriorPointInterface(m) linear_solver = MumpsInterface( # log_filename='lin_sol.log', icntl_options={11: 1}, # Set error level to 1 (most detailed) ) ip_solver = InteriorPointSolver(linear_solver) x, duals_eq, duals_ineq = ip_solver.solve(interface) print(x, duals_eq, duals_ineq)
def post_iter0(self): opt = self.opt # NOTE: the LShaped code negates the objective, so # we do the same here for consistency if 'cross_scen_options' in opt.options and \ 'valid_eta_bound' in opt.options['cross_scen_options']: valid_eta_bound = opt.options['cross_scen_options'][ 'valid_eta_bound'] if not opt.is_minimizing: _eta_init = {k: -v for k, v in valid_eta_bound.items()} else: _eta_init = valid_eta_bound _eta_bounds = lambda m, k: (_eta_init[k], None) else: lb = (-sys.maxsize - 1) * 1. / len(opt.all_scenario_names) _eta_init = lambda m, k: lb _eta_bounds = lambda m, k: (lb, None) # eta is attached to each subproblem, regardless of bundles bundling = opt.bundling for k, s in opt.local_subproblems.items(): s.eta = pyo.Var(opt.all_scenario_names, initialize=_eta_init, bounds=_eta_bounds) if sputils.is_persistent(s._solver_plugin): for var in s.eta.values(): s._solver_plugin.add_var(var) if bundling: ## create a refence to eta on each subproblem for sn in s.scen_list: scenario = opt.local_scenarios[sn] scenario.eta = { k: s.eta[k] for k in opt.all_scenario_names } ## hold the PH object harmless self._disable_W_and_prox() for k, s in opt.local_subproblems.items(): obj = find_active_objective(s) repn = generate_standard_repn(obj.expr, quadratic=True) if len(repn.nonlinear_vars) > 0: raise ValueError( "CrossScenario does not support models with nonlinear objective functions" ) if bundling: ## NOTE: this is slighly wasteful, in that for a bundle ## the first-stage cost appears len(s.scen_list) times ## If this really made a difference, we could use s.ref_vars ## to do the substitution nonant_vardata_list = list() for sn in s.scen_list: nonant_vardata_list.extend( \ opt.local_scenarios[sn]._PySPnode_list[0].nonant_vardata_list) else: nonant_vardata_list = s._PySPnode_list[0].nonant_vardata_list nonant_ids = set((id(var) for var in nonant_vardata_list)) linear_coefs = list(repn.linear_coefs) linear_vars = list(repn.linear_vars) quadratic_coefs = list(repn.quadratic_coefs) # adjust coefficients by scenario/bundle probability scen_prob = s.PySP_prob for i, var in enumerate(repn.linear_vars): if id(var) not in nonant_ids: linear_coefs[i] *= scen_prob for i, (x, y) in enumerate(repn.quadratic_vars): # only multiply through once if id(x) not in nonant_ids: quadratic_coefs[i] *= scen_prob elif id(y) not in nonant_ids: quadratic_coefs[i] *= scen_prob # NOTE: the LShaped code negates the objective, so # we do the same here for consistency if not opt.is_minimizing: for i, coef in enumerate(linear_coefs): linear_coefs[i] = -coef for i, coef in enumerate(quadratic_coefs): quadratic_coefs[i] = -coef # add the other etas if bundling: these_scenarios = set(s.scen_list) else: these_scenarios = [k] eta_scenarios = list() for sn in opt.all_scenario_names: if sn not in these_scenarios: linear_coefs.append(1) linear_vars.append(s.eta[sn]) eta_scenarios.append(sn) expr = LinearExpression(constant=repn.constant, linear_coefs=linear_coefs, linear_vars=linear_vars) if repn.quadratic_vars: expr += pyo.quicksum((coef * x * y for coef, ( x, y) in zip(quadratic_coefs, repn.quadratic_vars))) s._EF_obj = pyo.Expression(expr=expr) if opt.is_minimizing: s._EF_Obj = pyo.Objective(expr=s._EF_obj, sense=pyo.minimize) else: s._EF_Obj = pyo.Objective(expr=-s._EF_obj, sense=pyo.maximize) s._EF_Obj.deactivate() # add cut constraint dicts s._benders_cuts = pyo.Constraint(pyo.Any) s._ib_constr = pyo.Constraint(pyo.Any) self._enable_W_and_prox() # try to get the initial eta LB cuts # (may not be available) opt.spcomm.get_from_cross_cuts()
def _k_medoids_exact(self, distances, n_clusters): """ Parameters ---------- distances : int, required Pairwise distances between each row. n_clusters : int, required Number of clusters. """ # Create model M = pyomo.ConcreteModel() # get distance matrix M.d = distances # set number of clusters M.no_k = n_clusters # Distances is a symmetrical matrix, extract its length length = distances.shape[0] # get indices M.i = [j for j in range(length)] M.j = [j for j in range(length)] # initialize vars M.z = pyomo.Var(M.i, M.j, within=pyomo.Binary) M.y = pyomo.Var(M.i, within=pyomo.Binary) # get objective def objRule(M): return sum(sum(M.d[i, j] * M.z[i, j] for j in M.j) for i in M.i) M.obj = pyomo.Objective(rule=objRule) # s.t. # Assign all candidates to clusters def candToClusterRule(M, j): return sum(M.z[i, j] for i in M.i) == 1 M.candToClusterCon = pyomo.Constraint(M.j, rule=candToClusterRule) # no of clusters def noClustersRule(M): return sum(M.y[i] for i in M.i) == M.no_k M.noClustersCon = pyomo.Constraint(rule=noClustersRule) # cluster relation def clusterRelationRule(M, i, j): return M.z[i, j] <= M.y[i] M.clusterRelationCon = pyomo.Constraint(M.i, M.j, rule=clusterRelationRule) # create optimization problem optprob = opt.SolverFactory(self.solver) if self.solver == 'gurobi': optprob.set_options("Threads=" + str(self.threads) + " TimeLimit=" + str(self.timelimit)) results = optprob.solve(M, tee=False) # check that it does not fail if self.solver == 'gurobi' and results['Solver'][0][ 'Termination condition'].index == 11: print(results['Solver'][0]['Termination message']) return False elif self.solver == 'gurobi' and not results['Solver'][0][ 'Termination condition'].index in [2, 7, 8, 9, 10]: # optimal raise ValueError(results['Solver'][0]['Termination message']) # Get results r_x = np.array([[round(M.z[i, j].value) for i in range(length)] for j in range(length)]) r_y = np.array([round(M.y[j].value) for j in range(length)]) r_obj = pyomo.value(M.obj) return (r_y, r_x.T, r_obj)
import pyomo.environ as pyo model = pyo.ConcreteModel() # @decl1: model.e = pyo.Expression() # @:decl1 model.pprint() model = None model = pyo.ConcreteModel() # @decl2: model.x = pyo.Var() model.e1 = pyo.Expression(expr=model.x + 1) def e2_rule(model): return model.x + 2 model.e2 = pyo.Expression(rule=e2_rule) # @:decl2 model.pprint() del e2_rule model = None model = pyo.ConcreteModel() # @decl3: N = [1, 2, 3]
def create_model(): """Create the flowsheet and add unit models. Fixing model inputs is done in a separate function to try to keep this fairly clean and easy to follow. Args: None Returns: (ConcreteModel) Steam cycle model """ ############################################################################ # Flowsheet and Properties # ############################################################################ m = pyo.ConcreteModel(name="Steam Cycle Model") m.fs = FlowsheetBlock(default={"dynamic": False}) # Add steady state flowsheet # A physical property parameter block for IAPWS-95 with pressure and enthalpy # (PH) state variables. Usually pressure and enthalpy state variables are # more robust especially when the phases are unknown. m.fs.prop_water = iapws95.Iapws95ParameterBlock( default={"phase_presentation": iapws95.PhaseType.MIX}) # A physical property parameter block with temperature, pressure and vapor # fraction (TPx) state variables. There are a few instances where the vapor # fraction is known and the temperature and pressure state variables are # preferable. m.fs.prop_water_tpx = iapws95.Iapws95ParameterBlock( default={ "phase_presentation": iapws95.PhaseType.LG, "state_vars": iapws95.StateVars.TPX, }) ############################################################################ # Turbine with fill-in reheat constraints # ############################################################################ # The TurbineMultistage class allows creation of the full turbine model by # providing several configuration options, including: throttle valves; # high-, intermediate-, and low-pressure sections; steam extractions; and # pressure driven flow. See the IDAES documentation for details. m.fs.turb = HelmTurbineMultistage( default={ "property_package": m.fs.prop_water, "num_parallel_inlet_stages": 4, # number of admission arcs "num_hp": 7, # number of high-pressure stages "num_ip": 10, # number of intermediate-pressure stages "num_lp": 11, # number of low-pressure stages "hp_split_locations": [4, 7], # hp steam extraction locations "ip_split_locations": [5, 10], # ip steam extraction locations "lp_split_locations": [4, 8, 10, 11 ], # lp steam extraction locations "hp_disconnect": [7], # disconnect hp from ip to insert reheater "ip_split_num_outlets": { 10: 3 }, # number of split streams (default is 2) }) # This model is only the steam cycle, and the reheater is part of the boiler. # To fill in the reheater gap, a few constraints for the flow, pressure drop, # and outlet temperature are added. A detailed boiler model can be coupled later. # # hp_split[7] is the splitter directly after the last HP stage. The splitter # outlet "outlet_1" is always taken to be the main steam flow through the turbine. # When the turbine model was instantiated the stream from the HP section to the IP # section was omitted, so the reheater could be inserted. # The flow constraint sets flow from outlet_1 of the splitter equal to # flow into the IP turbine. @m.fs.turb.Constraint(m.fs.time) def constraint_reheat_flow(b, t): return b.ip_stages[1].inlet.flow_mol[t] == b.hp_split[ 7].outlet_1.flow_mol[t] # Create a variable for pressure change in the reheater (assuming # reheat_delta_p should be negative). m.fs.turb.reheat_delta_p = pyo.Var(m.fs.time, initialize=0, units=pyo.units.Pa) # Add a constraint to calculate the IP section inlet pressure based on the # pressure drop in the reheater and the outlet pressure of the HP section. @m.fs.turb.Constraint(m.fs.time) def constraint_reheat_press(b, t): return (b.ip_stages[1].inlet.pressure[t] == b.hp_split[7].outlet_1.pressure[t] + b.reheat_delta_p[t]) # Create a variable for reheat temperature and fix it to the desired reheater # outlet temperature m.fs.turb.reheat_out_T = pyo.Var(m.fs.time, initialize=866, units=pyo.units.K) # Create a constraint for the IP section inlet temperature. @m.fs.turb.Constraint(m.fs.time) def constraint_reheat_temp(b, t): return (b.ip_stages[1].control_volume.properties_in[t].temperature == b.reheat_out_T[t]) ############################################################################ # Add Condenser/hotwell/condensate pump # ############################################################################ # Add a mixer for all the streams coming into the condenser. In this case the # main steam, and the boiler feed pump turbine outlet go to the condenser m.fs.condenser_mix = HelmMixer( default={ "momentum_mixing_type": MomentumMixingType.none, "inlet_list": ["main", "bfpt"], "property_package": m.fs.prop_water, }) # The pressure in the mixer comes from the connection to the condenser. All # the streams coming in and going out of the mixer are equal, but we created # the mixer with no calculation for the unit pressure. Here a constraint that # specifies that the mixer pressure is equal to the main steam pressure is # added. There is also a constraint that specifies the that BFP turbine outlet # pressure is the same as the condenser pressure. Combined with the stream # connections between units, these constraints effectively specify that the # mixer inlet and outlet streams all have the same pressure. @m.fs.condenser_mix.Constraint(m.fs.time) def mixer_pressure_constraint(b, t): return b.main_state[t].pressure == b.mixed_state[t].pressure # The condenser model uses the physical property model with TPx state # variables, while the rest of the model uses PH state variables. To # translate between the two property calculations, an extra port is added to # the mixer which contains temperature, pressure, and vapor fraction # quantities. m.fs.condenser_mix._flow_mol_ref = pyo.Reference( m.fs.condenser_mix.mixed_state[:].flow_mol) m.fs.condenser_mix._temperature_ref = pyo.Reference( m.fs.condenser_mix.mixed_state[:].temperature) m.fs.condenser_mix._pressure_ref = pyo.Reference( m.fs.condenser_mix.mixed_state[:].pressure) m.fs.condenser_mix._vapor_frac_ref = pyo.Reference( m.fs.condenser_mix.mixed_state[:].vapor_frac) m.fs.condenser_mix.outlet_tpx = Port( initialize={ "flow_mol": m.fs.condenser_mix._flow_mol_ref, "temperature": m.fs.condenser_mix._temperature_ref, "pressure": m.fs.condenser_mix._pressure_ref, "vapor_frac": m.fs.condenser_mix._vapor_frac_ref, }) # Add NTU condenser model m.fs.condenser = Condenser( default={ "dynamic": False, "shell": { "has_pressure_change": False, "property_package": m.fs.prop_water }, "tube": { "has_pressure_change": False, "property_package": m.fs.prop_water } }) # Add the condenser hotwell. In steady state a mixer will work. This is # where makeup water is added if needed. m.fs.hotwell = HelmMixer( default={ "momentum_mixing_type": MomentumMixingType.none, "inlet_list": ["condensate", "makeup"], "property_package": m.fs.prop_water, }) # The hotwell is assumed to be at the same pressure as the condenser. @m.fs.hotwell.Constraint(m.fs.time) def mixer_pressure_constraint(b, t): return b.condensate_state[t].pressure == b.mixed_state[t].pressure # Condensate pump (Use compressor model, since it is more robust if vapor form) m.fs.cond_pump = HelmIsentropicCompressor( default={"property_package": m.fs.prop_water}) ############################################################################ # Add low pressure feedwater heaters # ############################################################################ # All the feedwater heater sections will be set to use the Underwood # approximation for LMTD, so create the fwh_config dict to make the config # slightly cleaner fwh_config = { "delta_temperature_callback": delta_temperature_underwood_callback } # The feedwater heater model allows feedwater heaters with a desuperheat, # condensing, and subcooling section to be added an a reasonably simple way. # See the IDAES documentation for more information of configuring feedwater # heaters m.fs.fwh1 = FWH0D( default={ "has_desuperheat": False, "has_drain_cooling": False, "has_drain_mixer": True, "property_package": m.fs.prop_water, "condense": fwh_config, }) # pump for fwh1 condensate, to pump it ahead and mix with feedwater m.fs.fwh1_pump = HelmIsentropicCompressor( default={"property_package": m.fs.prop_water}) # Mix the FWH1 drain back into the feedwater m.fs.fwh1_return = HelmMixer( default={ "momentum_mixing_type": MomentumMixingType.none, "inlet_list": ["feedwater", "fwh1_drain"], "property_package": m.fs.prop_water, }) # Set the mixer pressure to the feedwater pressure @m.fs.fwh1_return.Constraint(m.fs.time) def mixer_pressure_constraint(b, t): return b.feedwater_state[t].pressure == b.mixed_state[t].pressure # Add the rest of the low pressure feedwater heaters m.fs.fwh2 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": True, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) m.fs.fwh3 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": True, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) m.fs.fwh4 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": False, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) ############################################################################ # Add deaerator and boiler feed pump (BFP) # ############################################################################ # The deaerator is basically an open tank with multiple inlets. For steady- # state, a mixer model is sufficient. m.fs.fwh5_da = HelmMixer( default={ "momentum_mixing_type": MomentumMixingType.none, "inlet_list": ["steam", "drain", "feedwater"], "property_package": m.fs.prop_water, }) @m.fs.fwh5_da.Constraint(m.fs.time) def mixer_pressure_constraint(b, t): # Not sure about deaerator pressure, so assume same as feedwater inlet return b.feedwater_state[t].pressure == b.mixed_state[t].pressure # Add the boiler feed pump and boiler feed pump turbine m.fs.bfp = HelmIsentropicCompressor( default={"property_package": m.fs.prop_water}) m.fs.bfpt = HelmTurbineStage(default={"property_package": m.fs.prop_water}) # The boiler feed pump outlet pressure is the same as the condenser @m.fs.Constraint(m.fs.time) def constraint_out_pressure(b, t): return (b.bfpt.control_volume.properties_out[t].pressure == b.condenser.shell.properties_out[t].pressure) # Instead of specifying a fixed efficiency, specify that the steam is just # starting to condense at the outlet of the boiler feed pump turbine. This # ensures approximately the right behavior in the turbine. With a fixed # efficiency, depending on the conditions you can get odd things like steam # fully condensing in the turbine. @m.fs.Constraint(m.fs.time) def constraint_out_enthalpy(b, t): return ( b.bfpt.control_volume.properties_out[t].enth_mol == b.bfpt.control_volume.properties_out[t].enth_mol_sat_phase["Vap"] - 200 * pyo.units.J / pyo.units.mol) # The boiler feed pump power is the same as the power generated by the # boiler feed pump turbine. This constraint determines the steam flow to the # BFP turbine. The turbine work is negative for power out, while pump work # is positive for power in. @m.fs.Constraint(m.fs.time) def constraint_bfp_power(b, t): return 0 == b.bfp.control_volume.work[t] + b.bfpt.control_volume.work[t] ############################################################################ # Add high pressure feedwater heaters # ############################################################################ m.fs.fwh6 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": True, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) m.fs.fwh7 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": True, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) m.fs.fwh8 = FWH0D( default={ "has_desuperheat": True, "has_drain_cooling": True, "has_drain_mixer": False, "property_package": m.fs.prop_water, "desuperheat": fwh_config, "cooling": fwh_config, "condense": fwh_config, }) ############################################################################ # Additional Constraints/Expressions # ############################################################################ # Add a few constraints to allow a for complete plant results despite the # lack of a detailed boiler model. # Boiler pressure drop m.fs.boiler_pressure_drop_fraction = pyo.Var( m.fs.time, initialize=0.01, doc="Fraction of pressure lost from boiler feed pump and turbine inlet", ) @m.fs.Constraint(m.fs.time) def boiler_pressure_drop(b, t): return (m.fs.bfp.control_volume.properties_out[t].pressure * (1 - b.boiler_pressure_drop_fraction[t]) == m.fs.turb.inlet_split.mixed_state[t].pressure) # Again, since the boiler is missing, set the flow of steam into the turbine # equal to the flow of feedwater out of the last feedwater heater. @m.fs.Constraint(m.fs.time) def close_flow(b, t): return (m.fs.bfp.control_volume.properties_out[t].flow_mol == m.fs.turb.inlet_split.mixed_state[t].flow_mol) # Calculate the amount of heat that is added in the boiler, including the # reheater. @m.fs.Expression(m.fs.time) def boiler_heat(b, t): return (b.turb.inlet_split.mixed_state[t].enth_mol * b.turb.inlet_split.mixed_state[t].flow_mol - b.fwh8.desuperheat.tube.properties_out[t].enth_mol * b.fwh8.desuperheat.tube.properties_out[t].flow_mol + b.turb.ip_stages[1].control_volume.properties_in[t].enth_mol * b.turb.ip_stages[1].control_volume.properties_in[t].flow_mol - b.turb.hp_split[7].outlet_1.enth_mol[t] * b.turb.hp_split[7].outlet_1.flow_mol[t]) # Calculate the efficiency of the steam cycle. This doesn't account for # heat loss in the boiler, so actual plant efficiency would be lower. @m.fs.Expression(m.fs.time) def steam_cycle_eff(b, t): return -100 * b.turb.power[t] / b.boiler_heat[t] ############################################################################ ## Create the stream Arcs ## ############################################################################ ############################################################################ # Connect turbine and condenser units # ############################################################################ m.fs.EXHST_MAIN = Arc(source=m.fs.turb.outlet_stage.outlet, destination=m.fs.condenser_mix.main) m.fs.condenser_mix_to_condenser = Arc(source=m.fs.condenser_mix.outlet, destination=m.fs.condenser.inlet_1) m.fs.COND_01 = Arc(source=m.fs.condenser.outlet_1, destination=m.fs.hotwell.condensate) m.fs.COND_02 = Arc(source=m.fs.hotwell.outlet, destination=m.fs.cond_pump.inlet) ############################################################################ # Low pressure FWHs # ############################################################################ m.fs.EXTR_LP11 = Arc(source=m.fs.turb.lp_split[11].outlet_2, destination=m.fs.fwh1.drain_mix.steam) m.fs.COND_03 = Arc(source=m.fs.cond_pump.outlet, destination=m.fs.fwh1.condense.inlet_2) m.fs.FWH1_DRN1 = Arc(source=m.fs.fwh1.condense.outlet_1, destination=m.fs.fwh1_pump.inlet) m.fs.FWH1_DRN2 = Arc(source=m.fs.fwh1_pump.outlet, destination=m.fs.fwh1_return.fwh1_drain) m.fs.FW01A = Arc(source=m.fs.fwh1.condense.outlet_2, destination=m.fs.fwh1_return.feedwater) # fwh2 m.fs.FW01B = Arc(source=m.fs.fwh1_return.outlet, destination=m.fs.fwh2.cooling.inlet_2) m.fs.FWH2_DRN = Arc(source=m.fs.fwh2.cooling.outlet_1, destination=m.fs.fwh1.drain_mix.drain) m.fs.EXTR_LP10 = Arc( source=m.fs.turb.lp_split[10].outlet_2, destination=m.fs.fwh2.desuperheat.inlet_1, ) # fwh3 m.fs.FW02 = Arc(source=m.fs.fwh2.desuperheat.outlet_2, destination=m.fs.fwh3.cooling.inlet_2) m.fs.FWH3_DRN = Arc(source=m.fs.fwh3.cooling.outlet_1, destination=m.fs.fwh2.drain_mix.drain) m.fs.EXTR_LP8 = Arc(source=m.fs.turb.lp_split[8].outlet_2, destination=m.fs.fwh3.desuperheat.inlet_1) # fwh4 m.fs.FW03 = Arc(source=m.fs.fwh3.desuperheat.outlet_2, destination=m.fs.fwh4.cooling.inlet_2) m.fs.FWH4_DRN = Arc(source=m.fs.fwh4.cooling.outlet_1, destination=m.fs.fwh3.drain_mix.drain) m.fs.EXTR_LP4 = Arc(source=m.fs.turb.lp_split[4].outlet_2, destination=m.fs.fwh4.desuperheat.inlet_1) ############################################################################ # FWH5 (Deaerator) and boiler feed pump (BFP) # ############################################################################ m.fs.FW04 = Arc(source=m.fs.fwh4.desuperheat.outlet_2, destination=m.fs.fwh5_da.feedwater) m.fs.EXTR_IP10 = Arc(source=m.fs.turb.ip_split[10].outlet_2, destination=m.fs.fwh5_da.steam) m.fs.FW05A = Arc(source=m.fs.fwh5_da.outlet, destination=m.fs.bfp.inlet) m.fs.EXTR_BFPT_A = Arc(source=m.fs.turb.ip_split[10].outlet_3, destination=m.fs.bfpt.inlet) m.fs.EXHST_BFPT = Arc(source=m.fs.bfpt.outlet, destination=m.fs.condenser_mix.bfpt) ############################################################################ # High-pressure feedwater heaters # ############################################################################ # fwh6 m.fs.FW05B = Arc(source=m.fs.bfp.outlet, destination=m.fs.fwh6.cooling.inlet_2) m.fs.FWH6_DRN = Arc(source=m.fs.fwh6.cooling.outlet_1, destination=m.fs.fwh5_da.drain) m.fs.EXTR_IP5 = Arc(source=m.fs.turb.ip_split[5].outlet_2, destination=m.fs.fwh6.desuperheat.inlet_1) # fwh7 m.fs.FW06 = Arc(source=m.fs.fwh6.desuperheat.outlet_2, destination=m.fs.fwh7.cooling.inlet_2) m.fs.FWH7_DRN = Arc(source=m.fs.fwh7.cooling.outlet_1, destination=m.fs.fwh6.drain_mix.drain) m.fs.EXTR_HP7 = Arc(source=m.fs.turb.hp_split[7].outlet_2, destination=m.fs.fwh7.desuperheat.inlet_1) # fwh8 m.fs.FW07 = Arc(source=m.fs.fwh7.desuperheat.outlet_2, destination=m.fs.fwh8.cooling.inlet_2) m.fs.FWH8_DRN = Arc(source=m.fs.fwh8.cooling.outlet_1, destination=m.fs.fwh7.drain_mix.drain) m.fs.EXTR_HP4 = Arc(source=m.fs.turb.hp_split[4].outlet_2, destination=m.fs.fwh8.desuperheat.inlet_1) ############################################################################ # Turn the Arcs into constraints and return the model # ############################################################################ pyo.TransformationFactory("network.expand_arcs").apply_to(m.fs) return m
def DC_OPF_RO_TruthTable_ConstraintCheck(Elecdata, Cur_TruthTable): Gen = Elecdata.Gen Branch = Elecdata.Branch Bus = Elecdata.Bus GasGenerators = Gen[Gen.FuelType == 'Gas'].index.tolist() m = pm.ConcreteModel() m.gen_set = pm.Set(initialize=Gen.index.tolist()) m.branch_set = pm.Set(initialize=Branch.index.tolist()) m.bus_set = pm.Set(initialize=Bus.index.tolist()) m.gasgen_set = pm.Set(initialize=GasGenerators, within=m.gen_set, ordered=True) m.TruthTable = pm.Param(m.gasgen_set, mutable=True, initialize=lambda m, i: (Cur_TruthTable[i])) m.P_UB = pm.Param(m.gasgen_set, mutable=False, initialize=lambda m, i: (Gen.DC_RO_OPF_P_UB[i])) m.P_LB = pm.Param(m.gasgen_set, mutable=False, initialize=lambda m, i: (Gen.DC_RO_OPF_P_LB[i])) #m.P_UB = pm.Var(m.gasgen_set,bounds= lambda m,i : (Gen.DC_RO_OPF_P_UB[i],Gen.DC_RO_OPF_P_UB[i]),initialize=lambda m,i : Gen.DC_OPF_RES[i] ) #m.P_LB = pm.Var(m.gasgen_set,bounds= lambda m,i : (Gen.Pmin_MW[i],Gen.DC_OPF_RES[i]),initialize=lambda m,i : Gen.DC_OPF_RES[i] ) m.P = pm.Var(m.gen_set, bounds=lambda m, i: (Gen.Pmin_MW[i], Gen.Pmax_MW[i]), initialize=1) m.Pij = pm.Var(m.branch_set, bounds=lambda m, i: (-Branch.RateA_MVA[i], Branch.RateA_MVA[i]), initialize=1) m.th = pm.Var(m.bus_set, bounds=(-np.pi, np.pi), initialize=0) m.P_Shed = pm.Var(m.gasgen_set, bounds=(0, None)) m.P_Add = pm.Var(m.gasgen_set, bounds=(0, None)) def PowerBal_constr(model, i): PowerBal = -Bus.PD_MW[i] \ + sum(m.P[k] for k in Gen[Gen.Gen_Bus==i].index.tolist()) \ - sum(m.Pij[k] for k in Branch[Branch.From_Bus==i].index.tolist()) \ + sum(m.Pij[k] for k in Branch[Branch.To_Bus==i].index.tolist()) \ ==0 return PowerBal m.PowerBal_constr = pm.Constraint(m.bus_set, rule=PowerBal_constr) def Branch_Flow(model, i): From_ix = Branch.From_Bus[i] To_ix = Branch.To_Bus[i] B = Elecdata.Params.BaseMVA / Branch.BR_X_PU[i] return m.Pij[i] == (B) * (m.th[From_ix] - m.th[To_ix]) m.branchflow = pm.Constraint(m.branch_set, rule=Branch_Flow) def SlackNode(model, i): if Bus.Bus_Type[i] == 3: return m.th[i] == 0 else: return pm.Constraint.Skip m.slack = pm.Constraint(m.bus_set, rule=SlackNode) def Power_UB_LB(model, i): Binary_LB = m.TruthTable[i] # Truth table index Binary_UB = 1 - Binary_LB return m.P[i] == Binary_UB * m.P_UB[i] + Binary_LB * m.P_LB[i] m.Power_UB_LB = pm.Constraint(m.gasgen_set, rule=Power_UB_LB) def Costs(model): return sum(Gen.CostCoeff_1[k] * m.P[k] for k in m.gen_set) <= Elecdata.Params.C_max_RO m.Costs = pm.Constraint(rule=Costs) def Shed(model, i): return m.P_Shed[i] == Gen.DC_OPF_RES[i] - m.P_LB[i] m.shed = pm.Constraint(m.gasgen_set, rule=Shed) def Add(model, i): return m.P_Add[i] == m.P_UB[i] - Gen.DC_OPF_RES[i] m.add = pm.Constraint(m.gasgen_set, rule=Add) def DC_OPF_Obj(model): return sum(Gen.AddWeight[i] * m.P_Add[i] + Gen.ShedWeight[i] * m.P_Shed[i] for i in m.gasgen_set) m.objective = pm.Objective(rule=DC_OPF_Obj, sense=pm.maximize, doc='Define objective function') return m
import pyomo.environ as pe ####################### ABSTRACT MODEL ################################# TL = pe.AbstractModel(name='Truck loading') #pallet set TL.pallet = pe.Set() #pallet volume (cubic meters) TL.vol = pe.Param(TL.pallet) #pallets' demand TL.qta = pe.Param(TL.pallet) #tir set TL.tir = pe.Set() #trucks' maximum capacity (cubic meters) TL.maxcap = pe.Param() #0-1 variable for trucks (used/not used) TL.y = pe.Var(TL.tir, within=pe.Binary) #quantity matrix of i pallet in j truck, pallets being non partitionable TL.x = pe.Var(TL.pallet, TL.tir, within=pe.NonNegativeIntegers) #objective function, minimize trucks def mintir_rule(m): return sum(m.y[j] for j in m.tir) TL.mintir = pe.Objective(rule=mintir_rule) #capacity constraint, each truck cannot be filled more than a given number. def maxload_rule(m, j): return sum(m.x[i, j] * m.vol[i] for i in m.pallet) <= m.y[j] * m.maxcap
def DC_OPF_RO_TruthTable(Elecdata, TruthTable): Gen = Elecdata.Gen Branch = Elecdata.Branch Bus = Elecdata.Bus GasGenerators = Gen[Gen.FuelType == 'Gas'].index.tolist() nscen = len(TruthTable) m = pm.ConcreteModel() m.gen_set = pm.Set(initialize=Gen.index.tolist()) m.branch_set = pm.Set(initialize=Branch.index.tolist()) m.bus_set = pm.Set(initialize=Bus.index.tolist()) m.scen_set = pm.RangeSet(0, nscen - 1) m.gasgen_set = pm.Set(initialize=GasGenerators, within=m.gen_set, ordered=True) m.P_UB = pm.Var(m.gasgen_set, bounds=lambda m, i: (Gen.DC_OPF_RES[i], Gen.Pmax_MW[i]), initialize=lambda m, i: Gen.DC_OPF_RES[i]) m.P_LB = pm.Var(m.gasgen_set, bounds=lambda m, i: (Gen.Pmin_MW[i], Gen.DC_OPF_RES[i]), initialize=lambda m, i: Gen.DC_OPF_RES[i]) m.P = pm.Var(m.gen_set, m.scen_set, bounds=lambda m, i, s: (Gen.Pmin_MW[i], Gen.Pmax_MW[i]), initialize=1) m.Pij = pm.Var(m.branch_set, m.scen_set, bounds=lambda m, i, s: (-Branch.RateA_MVA[i], Branch.RateA_MVA[i]), initialize=1) m.th = pm.Var(m.bus_set, m.scen_set, bounds=(-np.pi, np.pi), initialize=0) m.P_Shed = pm.Var(m.gasgen_set, bounds=(0, None)) m.P_Add = pm.Var(m.gasgen_set, bounds=(0, None)) def PowerBal_constr(model, i, s): PowerBal = -Bus.PD_MW[i] \ + sum(m.P[k,s] for k in Gen[Gen.Gen_Bus==i].index.tolist()) \ - sum(m.Pij[k,s] for k in Branch[Branch.From_Bus==i].index.tolist()) \ + sum(m.Pij[k,s] for k in Branch[Branch.To_Bus==i].index.tolist()) \ ==0 return PowerBal m.PowerBal_constr = pm.Constraint(m.bus_set, m.scen_set, rule=PowerBal_constr) def Branch_Flow(model, i, s): From_ix = Branch.From_Bus[i] To_ix = Branch.To_Bus[i] B = Elecdata.Params.BaseMVA / Branch.BR_X_PU[i] return m.Pij[i, s] == (B) * (m.th[From_ix, s] - m.th[To_ix, s]) m.branchflow = pm.Constraint(m.branch_set, m.scen_set, rule=Branch_Flow) def SlackNode(model, i, s): if Bus.Bus_Type[i] == 3: return m.th[i, s] == 0 else: return pm.Constraint.Skip m.slack = pm.Constraint(m.bus_set, m.scen_set, rule=SlackNode) def Power_UB_LB(model, i, s): Binary_LB = TruthTable[i][s] # Truth table index Binary_UB = 1 - Binary_LB return m.P[i, s] == Binary_UB * m.P_UB[i] + Binary_LB * m.P_LB[i] m.Power_UB_LB = pm.Constraint(m.gasgen_set, m.scen_set, rule=Power_UB_LB) def Costs(model, s): return sum(Gen.CostCoeff_1[k] * m.P[k, s] for k in m.gen_set) <= Elecdata.Params.C_max_RO m.Costs = pm.Constraint(m.scen_set, rule=Costs) def Shed(model, i): return m.P_Shed[i] == Gen.DC_OPF_RES[i] - m.P_LB[i] m.shed = pm.Constraint(m.gasgen_set, rule=Shed) def Add(model, i): return m.P_Add[i] == m.P_UB[i] - Gen.DC_OPF_RES[i] m.add = pm.Constraint(m.gasgen_set, rule=Add) def DC_OPF_Obj(model): return sum(Gen.AddWeight[i] * m.P_Add[i] + Gen.ShedWeight[i] * m.P_Shed[i] for i in m.gasgen_set) m.objective = pm.Objective(rule=DC_OPF_Obj, sense=pm.maximize, doc='Define objective function') opt = pm.SolverFactory('ipopt') opt.options['print_level'] = 5 # Optimize print('Starting Optimization') results = opt.solve(m, tee=True) status = 0 if (results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal): print('Model Solved to Optimality') status = 1 # Do something when the solution in optimal and feasible elif (results.solver.termination_condition == TerminationCondition.infeasible): print('Model is infeasible') # Do something when model in infeasible else: print('Solver Status: ', results.solver.status) UB_Res = dict([[i, np.round(m.P_UB[i].value, decimals=8)] for i in m.P_UB]) LB_Res = dict([[i, np.round(m.P_LB[i].value, decimals=8)] for i in m.P_LB]) P_Shed = dict([[i, np.round(m.P_Shed[i].value, decimals=8)] for i in m.P_Shed]) P_Add = dict([[i, np.round(m.P_Add[i].value, decimals=8)] for i in m.P_Add]) Elecdata.Gen = Elecdata.Gen.assign(DC_RO_OPF_P_UB=pd.Series(UB_Res)) Elecdata.Gen = Elecdata.Gen.assign(DC_RO_OPF_P_LB=pd.Series(LB_Res)) Elecdata.Gen = Elecdata.Gen.assign(P_Shed=pd.Series(P_Shed)) Elecdata.Gen = Elecdata.Gen.assign(P_Add=pd.Series(P_Add)) #Elecdata.Gen = Elecdata.Gen.combine(pd.DataFrame.from_dict(LB_Res,orient='index',columns=['DC_RO_OPF_P_LB'])) # Unload results Pc = pd.DataFrame(columns=[Gen.index.tolist() + ['Cost', 'Max Cost']], index=TruthTable.index.tolist()) for s in m.scen_set: for g in m.gen_set: Pc.loc[s][g] = m.P[g, s].value Pc.loc[s]['Cost'] = m.Costs[s].body() Pc.loc[s]['Max Cost'] = m.Costs[s].upper() Pc.index = ['Case' + str(x) for x in range(0, len(Pc))] Pc = Pc.astype(float).round(decimals=1) Elecdata.Params.Cases = Pc Elecdata.Params.RO_OPF_Obj = m.objective() Elecdata.Params.status = status
def test_PP_costing(): # Create a Concrete Model as the top level object m = pyo.ConcreteModel() # Add a flowsheet object to the model m.fs = FlowsheetBlock(default={"dynamic": False}) m.fs.get_costing(year='2018') # check that the model solved properly and has 0 degrees of freedom assert (degrees_of_freedom(m) == 0) ########################################################################### # Create costing constraints # ########################################################################### # coal flow rate # accounts 1.x and 2.x are coal handling, preparation and feed # accounts 4.x are for boiler BOP and foundations coal_accounts = [ '1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '4.11', '4.15', '4.16' ] m.fs.boiler = pyo.Block() m.fs.boiler.coal_mass_flow = pyo.Var(initialize=7238.95) # tpd m.fs.boiler.coal_mass_flow.fix() get_PP_costing(m.fs.boiler, coal_accounts, m.fs.boiler.coal_mass_flow, 'tpd', 2) # total fuel feed # accounts 3.x are for start up systems and miscellaneous plant equipment # accounts 7.x are for ductwork and stack foundations fuel_accounts = ['3.6', '3.9', '7.3', '7.5'] m.fs.fuel_feed = pyo.Block() m.fs.fuel_feed.total_fuel_feed = pyo.Var(initialize=603246) # lb/hr m.fs.fuel_feed.total_fuel_feed.fix() get_PP_costing(m.fs.fuel_feed, fuel_accounts, m.fs.fuel_feed.total_fuel_feed, 'lb/hr', 2) # HP BFW flow rate # accounts 3.x are for feedwater systems # account 4.9 is for the boiler # account 8.4 is steam piping BFW_accounts = ['3.1', '3.3', '3.5', '4.9', '8.4'] m.fs.bfp = pyo.Block() m.fs.bfp.BFW_mass_flow = pyo.Var(initialize=5316158) # lb/hr m.fs.bfp.BFW_mass_flow.fix() get_PP_costing(m.fs.bfp, BFW_accounts, m.fs.bfp.BFW_mass_flow, 'lb/hr', 2) # Steam turbine power # accounts 8.x are for the steam turbine and its foundations power_accounts = ['8.1'] m.fs.turb = pyo.Block() m.fs.turb.power = pyo.Var(initialize=769600) # kW m.fs.turb.power.fix() get_PP_costing(m.fs.turb, power_accounts, m.fs.turb.power, 'kW', 2) # Condernser duty cond_accounts = ['8.3'] m.fs.condenser = pyo.Block() m.fs.condenser.duty_MMBtu = pyo.Var(initialize=2016) # MMBtu/hr m.fs.condenser.duty_MMBtu.fix() get_PP_costing(m.fs.condenser, cond_accounts, m.fs.condenser.duty_MMBtu, "MMBtu/hr", 2) # Circulating water flow rate # accounts 9.x are for circulating water systems # account 14.5 is for the pumphouse circ_accounts = ['9.2', '9.3', '9.4', '9.6', '9.7', '14.5'] m.fs.circulating_water = pyo.Block() m.fs.circulating_water.vol_flow = pyo.Var(initialize=463371) # gpm m.fs.circulating_water.vol_flow.fix() get_PP_costing(m.fs.circulating_water, circ_accounts, m.fs.circulating_water.vol_flow, 'gpm', 2) # Ash flow rate # accounts are for ash storage and handling ash_accounts = ['10.6', '10.7', '10.9'] m.fs.ash_handling = pyo.Block() m.fs.ash_handling.ash_mass_flow = pyo.Var(initialize=66903) # lb/hr m.fs.ash_handling.ash_mass_flow.fix() get_PP_costing(m.fs.ash_handling, ash_accounts, m.fs.ash_handling.ash_mass_flow, 'lb/hr', 2) # add total cost build_flowsheet_cost_constraint(m) # add initialize costing_initialization(m.fs) # try solving solver = get_solver() results = solver.solve(m, tee=True) assert results.solver.termination_condition == \ pyo.TerminationCondition.optimal # all numbers come from the NETL excel file: # "201.001.001_BBR4 COE Spreadsheet_Rev0U_20190919_njk.xlsm" assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['1.1']), abs=1e-1) == 2306 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['1.2']), abs=1e-1) == 6385 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['1.3']), abs=1e-1) == 59527 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['1.4']), abs=1e-1) == 8086 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['2.1']), abs=1e-1) == 4073 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['2.2']), abs=1e-1) == 13976 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['4.11']), abs=1e-1) == 3751 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['4.15']), abs=1e-1) == 197 / 1e3 assert pytest.approx(pyo.value( m.fs.boiler.costing.total_plant_cost['4.16']), abs=1e-1) == 1014 / 1e3 assert pytest.approx(pyo.value( m.fs.fuel_feed.costing.total_plant_cost['3.6']), abs=1e-1) == 4864 / 1e3 assert pytest.approx(pyo.value( m.fs.fuel_feed.costing.total_plant_cost['3.9']), abs=1e-1) == 522 / 1e3 assert pytest.approx(pyo.value( m.fs.fuel_feed.costing.total_plant_cost['7.3']), abs=1e-1) == 1710 / 1e3 assert pytest.approx(pyo.value( m.fs.fuel_feed.costing.total_plant_cost['7.5']), abs=1e-1) == 647 / 1e3 assert pytest.approx(pyo.value(m.fs.bfp.costing.total_plant_cost['3.1']), abs=1e-1) == 19233 / 1e3 assert pytest.approx(pyo.value(m.fs.bfp.costing.total_plant_cost['3.3']), abs=1e-1) == 6897 / 1e3 assert pytest.approx(pyo.value(m.fs.bfp.costing.total_plant_cost['3.5']), abs=1e-1) == 2366 / 1e3 assert pytest.approx(pyo.value(m.fs.bfp.costing.total_plant_cost['4.9']), abs=1e-1) == 570418 / 1e3 assert pytest.approx(pyo.value(m.fs.bfp.costing.total_plant_cost['8.4']), abs=1e-1) == 81916 / 1e3 assert pytest.approx(pyo.value(m.fs.turb.costing.total_plant_cost['8.1']), abs=1e-1) == 110166 / 1e3 assert pytest.approx(pyo.value( m.fs.condenser.costing.total_plant_cost['8.3']), abs=1e-1) == 20447 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['9.2']), abs=1e-1) == 4133 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['9.3']), abs=1e-1) == 25518 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['9.4']), abs=1e-1) == 19859 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['9.6']), abs=1e-1) == 2870 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['9.7']), abs=1e-1) == 2690 / 1e3 assert pytest.approx(pyo.value( m.fs.circulating_water.costing.total_plant_cost['14.5']), abs=1e-1) == 464 / 1e3 assert pytest.approx(pyo.value( m.fs.ash_handling.costing.total_plant_cost['10.6']), abs=1e-1) == 6429 / 1e3 assert pytest.approx(pyo.value( m.fs.ash_handling.costing.total_plant_cost['10.7']), abs=1e-1) == 10725 / 1e3 assert pytest.approx(pyo.value( m.fs.ash_handling.costing.total_plant_cost['10.9']), abs=1e-1) == 2564 / 1e3 assert pytest.approx(pyo.value(m.fs.flowsheet_cost), abs=1e-1) == 993753 / 1e3 return m
def build_time_block(t0: int, delta_t: int, num_finite_elements: int, constant_control_duration: int, time_scale: float) -> _BlockData: """ Parameters ---------- t0: int start time delta_t: float end time num_finite_elements: number of finite elements constant_control_duration: number of finite elements over which the control (p) is constant time_scale: float coefficient of t within the sin function Returns ------- m: _BlockData The Pyomo model """ assert constant_control_duration >= delta_t assert constant_control_duration % delta_t == 0 assert (num_finite_elements * delta_t) % constant_control_duration == 0 def finite_element_ndx_to_start_t_x(ndx): return t0 + ndx * delta_t def finite_element_ndx_to_end_t_x(ndx): return t0 + (ndx + 1) * delta_t def finite_element_ndx_to_start_t_p(ndx): return t0 + (math.floor( ndx / (constant_control_duration / delta_t))) * constant_control_duration m = pe.Block(concrete=True) m.x_time_points = pe.Set(initialize=[ t for t in range(t0, t0 + delta_t * (num_finite_elements + 1), delta_t) ]) m.x = pe.Var(m.x_time_points) num_p_elements = int( (num_finite_elements * delta_t) / constant_control_duration) m.p_time_points = pe.Set(initialize=[ t for t in range(t0, t0 + constant_control_duration * num_p_elements, constant_control_duration) ]) bnds = (None, 2) m.p = pe.Var(m.p_time_points, bounds=bnds) obj_expr = 0 for fe_ndx in range(num_finite_elements): start_t_x = finite_element_ndx_to_start_t_x(fe_ndx) end_t_x = finite_element_ndx_to_end_t_x(fe_ndx) obj_expr += 0.5 * delta_t * ( (m.x[start_t_x] - (math.sin(time_scale * start_t_x) + 1))**2 + (m.x[end_t_x] - (math.sin(time_scale * end_t_x) + 1))**2) m.obj = pe.Objective(expr=obj_expr) m.con_indices = pe.Set(initialize=[t for t in m.x_time_points if t > t0]) m.cons = pe.Constraint(m.con_indices) for fe_ndx in range(num_finite_elements): start_t_x = finite_element_ndx_to_start_t_x(fe_ndx) end_t_x = finite_element_ndx_to_end_t_x(fe_ndx) start_t_p = finite_element_ndx_to_start_t_p(fe_ndx) m.cons[end_t_x] = m.x[end_t_x] - (m.x[start_t_x] + delta_t * (m.p[start_t_p] - m.x[end_t_x])) == 0 return m
def build(self): """ Add model equations to the unit model. This is called by a default block construnction rule when the unit model is created. """ super().build() # Basic unit model build/read config config = self.config # shorter config pointer # The thermodynamic expression writer object, te, writes expressions # including external function calls to calculate thermodynamic quantities # from a set of state variables. _assert_properties(config.property_package) te = ThermoExpr(blk=self, parameters=config.property_package) eff = self.efficiency_isentropic = pyo.Var( self.flowsheet().config.time, initialize=0.9, doc="Isentropic efficiency" ) eff.fix() pratio = self.ratioP = pyo.Var( self.flowsheet().config.time, initialize=0.7, doc="Ratio of outlet to inlet pressure" ) # Some shorter refernces to property blocks properties_in = self.control_volume.properties_in properties_out = self.control_volume.properties_out @self.Expression( self.flowsheet().config.time, doc="Outlet isentropic enthalpy" ) def h_is(b, t): return te.h(s=properties_in[t].entr_mol, p=properties_out[t].pressure) @self.Expression( self.flowsheet().config.time, doc="Isentropic enthalpy change" ) def delta_enth_isentropic(b, t): return self.h_is[t] - properties_in[t].enth_mol @self.Expression( self.flowsheet().config.time, doc="Isentropic work" ) def work_isentropic(b, t): return properties_in[t].flow_mol*( properties_in[t].enth_mol - self.h_is[t]) @self.Expression( self.flowsheet().config.time, doc="Outlet enthalpy" ) def h_o(b, t): # Early access to the outlet enthalpy and work return properties_in[t].enth_mol - eff[t]*( properties_in[t].enth_mol - self.h_is[t]) @self.Constraint(self.flowsheet().config.time) def eq_work(b, t): # Work from energy balance return properties_out[t].enth_mol == self.h_o[t] @self.Constraint(self.flowsheet().config.time) def eq_pressure_ratio(b, t): return (pratio[t]*properties_in[t].pressure == properties_out[t].pressure) @self.Expression(self.flowsheet().config.time) def work_mechanical(b, t): return b.control_volume.work[t]
def condenser_stage_rule(block): #-----------------------------------SETS----------------------------------- # local sets that will only be used in reactive stage block.inlet = pe.Set(initialize=['in']) block.outlet = pe.Set(initialize=['out','P']) block.stream = block.inlet | block.outlet block.COMP_WATER = pe.Set(initialize=['H2O']) #---------------------------------VARIABLES--------------------------------- block.T = pe.Var(within=pe.NonNegativeReals,bounds=(20+273.15,40+273.15)) # K block.T_F = pe.Var(within=pe.NonNegativeReals) # K block.P = pe.Var(within=pe.NonNegativeReals,bounds=(10,30)) # Bar block.Q_main = pe.Var(within=pe.Reals) # MW # Tray Inlet/Outlet Variable block.x_ = pe.Var(block.inlet,m.COMP_TOTAL,within=pe.NonNegativeReals,bounds=(0,1)) block.y_ = pe.Var(block.inlet,m.COMP_TOTAL,within=pe.NonNegativeReals,bounds=(0,1)) block.x = pe.Var(m.COMP_TOTAL,within=pe.NonNegativeReals,bounds=(0,1)) block.y = pe.Var(m.COMP_TOTAL,within=pe.NonNegativeReals,bounds=(0,1)) block.z = pe.Var(m.COMP_FEED,within=pe.NonNegativeReals,bounds=(0,1)) block.L = pe.Var(block.stream,within=pe.NonNegativeReals) block.W = pe.Var(within=pe.NonNegativeReals) block.V = pe.Var(block.stream,within=pe.NonNegativeReals) block.F = pe.Var(within=pe.NonNegativeReals) block.H_L_ = pe.Var(block.inlet,within=pe.Reals) block.H_V_ = pe.Var(block.inlet,within=pe.Reals) block.H_L = pe.Var(within=pe.Reals) block.H_V = pe.Var(within=pe.Reals) block.H_F = pe.Var(within=pe.Reals) block.f_V = pe.Var(m.COMP_TOTAL,within=pe.NonNegativeReals,initialize=1e-20) block.f_L = pe.Var(m.COMP_TOTAL,within=pe.NonNegativeReals,initialize=1e-20) block.PR_L = pe.Var(within=pe.NonNegativeReals,bounds=(0,1)) print('|','Importing Condenser Stage......') print('|','Adding the following local variable:') print('-'*36) for i in block.component_objects(pe.Var,active=True): print('|',i) print('-'*36) print('') #---------------------------------Equations--------------------------------- # Energy Block block.energy_block = pe.Block(rule=energy_block_rule) # VLE block block.VLE_block = pe.Block(rule=VLLE_block_rule) # Mass Balance def mass_balance_main_rule(block,i): if i in m.COMP_FEED: return block.F*block.z[i] + sum(block.L[s]*block.x_[s,i] + block.V[s]*block.y_[s,i] for s in block.inlet)\ - sum(block.L[s]*block.x[i] + block.V[s]*block.y[i] for s in block.outlet) == 0 elif i in block.COMP_WATER: return sum(block.L[s]*block.x_[s,i] + block.V[s]*block.y_[s,i] for s in block.inlet)\ - sum(block.L[s]*block.x[i] + block.V[s]*block.y[i] for s in block.outlet) - block.W == 0 else: return sum(block.L[s]*block.x_[s,i] + block.V[s]*block.y_[s,i] for s in block.inlet)\ - sum(block.L[s]*block.x[i] + block.V[s]*block.y[i] for s in block.outlet) == 0 block.mass_balance_main_con = pe.Constraint(m.COMP_TOTAL,rule=mass_balance_main_rule) # Equilibrium def VL_equil_rule(block,i): return block.f_V[i] == block.f_L[i] block.VL_equil_con = pe.Constraint(m.COMP_TOTAL-block.COMP_WATER,rule=VL_equil_rule) # Water phase def L_water_rule(block,i): return block.x[i] == pe.exp(-0.66037 - 7.1130*(539.1/block.T) - 0.67885*(1-block.T/539.1)**(1/3) -1.43381*(1-block.T/539.1)) block.L_water_con = pe.Constraint(block.COMP_WATER,rule=L_water_rule) def V_water_rule(block,i): return block.y[i]*block.P == pe.exp(2.30258509299*(5.20389 - 1733.926/(block.T-39.485))) block.V_water_con = pe.Constraint(block.COMP_WATER,rule=V_water_rule) # add bounds specifically for water block.x['H2O'].setub(water_x[1]+abs(water_x[1])*0.1) block.x['H2O'].setlb(water_x[0]-abs(water_x[0])*0.1) block.y['H2O'].setub(water_yp[1]/block.P.lb) block.y['H2O'].setlb(water_yp[0]/block.P.ub) # Summation def summation_x_main_rule(block): return sum(block.x[i] for i in m.COMP_TOTAL) == 1 block.summation_x_main_con = pe.Constraint(rule=summation_x_main_rule) def summation_y_main_rule(block): return sum(block.y[i] for i in m.COMP_TOTAL) == 1 block.summation_y_main_con = pe.Constraint(rule=summation_y_main_rule) # Heat Balance def heat_balance_main_rule(block): return block.F*block.H_F + sum(block.L[s]*block.H_L_[s] + block.V[s]*block.H_V_[s] for s in block.inlet) \ + block.Q_main - sum(block.L[s]*block.H_L + block.V[s]*block.H_V for s in block.outlet) \ - block.W*block.energy_block.dH_L['H2O'] == 0 block.heat_balance_main_con = pe.Constraint(rule=heat_balance_main_rule) # product / out ratio def PR_L_ratio_rule(block): return block.L['P'] == block.PR_L * sum(block.L[s] for s in block.outlet) block.PR_L_con = pe.Constraint(rule=PR_L_ratio_rule)
def test_scaling_without_rename(self): m = pyo.ConcreteModel() m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) m.v1 = pyo.Var(initialize=10) m.v2 = pyo.Var(initialize=20) m.v3 = pyo.Var(initialize=30) def c1_rule(m): return m.v1 == 1e6 m.c1 = pyo.Constraint(rule=c1_rule) def c2_rule(m): return m.v2 == 1e-4 m.c2 = pyo.Constraint(rule=c2_rule) m.scaling_factor[m.v1] = 1.0 m.scaling_factor[m.v2] = 0.5 m.scaling_factor[m.v3] = 0.25 m.scaling_factor[m.c1] = 1e-5 m.scaling_factor[m.c2] = 1e5 values = {} values[id(m.v1)] = (m.v1.value, m.scaling_factor[m.v1]) values[id(m.v2)] = (m.v2.value, m.scaling_factor[m.v2]) values[id(m.v3)] = (m.v3.value, m.scaling_factor[m.v3]) values[id(m.c1)] = (pyo.value(m.c1.body), m.scaling_factor[m.c1]) values[id(m.c2)] = (pyo.value(m.c2.body), m.scaling_factor[m.c2]) m.c2_ref = pyo.Reference(m.c2) m.v3_ref = pyo.Reference(m.v3) scale = pyo.TransformationFactory('core.scale_model') scale.apply_to(m, rename=False) self.assertTrue(hasattr(m, 'v1')) self.assertTrue(hasattr(m, 'v2')) self.assertTrue(hasattr(m, 'c1')) self.assertTrue(hasattr(m, 'c2')) orig_val, factor = values[id(m.v1)] self.assertAlmostEqual( m.v1.value, orig_val*factor, ) orig_val, factor = values[id(m.v2)] self.assertAlmostEqual( m.v2.value, orig_val*factor, ) orig_val, factor = values[id(m.c1)] self.assertAlmostEqual( pyo.value(m.c1.body), orig_val*factor, ) orig_val, factor = values[id(m.c2)] self.assertAlmostEqual( pyo.value(m.c2.body), orig_val*factor, ) orig_val, factor = values[id(m.v3)] self.assertAlmostEqual( m.v3_ref[None].value, orig_val*factor, ) # Note that because the model was not renamed, # v3_ref is still intact. lhs = m.c2.body monom_factor = lhs.arg(0) scale_factor = (m.scaling_factor[m.c2]/ m.scaling_factor[m.v2]) self.assertAlmostEqual( monom_factor, scale_factor, )
# ___________________________________________________________________________ import pyomo.environ as pyo from pyomo.contrib.interior_point.interior_point import InteriorPointSolver from pyomo.contrib.interior_point.interface import InteriorPointInterface from pyomo.contrib.interior_point.linalg.mumps_interface import MumpsInterface import logging logging.basicConfig(level=logging.INFO) # Supposedly this sets the root logger's level to INFO. # But when linear_solver.logger logs with debug, # it gets propagated to a mysterious root logger with # level NOTSET... m = pyo.ConcreteModel() m.x = pyo.Var() m.y = pyo.Var() m.obj = pyo.Objective(expr=m.x**2 + m.y**2) m.c1 = pyo.Constraint(expr=m.y == pyo.exp(m.x)) m.c2 = pyo.Constraint(expr=m.y >= (m.x - 1)**2) interface = InteriorPointInterface(m) linear_solver = MumpsInterface( # log_filename='lin_sol.log', icntl_options={11: 1}, # Set error level to 1 (most detailed) ) ip_solver = InteriorPointSolver(linear_solver) x, duals_eq, duals_ineq = ip_solver.solve(interface) print(x, duals_eq, duals_ineq)
def pysp_instance_creation_callback(scenario_name, use_integer=False, sense=pyo.minimize, crops_multiplier=1): # long function to create the entire model # scenario_name is a string (e.g. AboveAverageScenario0) # # Returns a concrete model for the specified scenario # scenarios come in groups of three scengroupnum = sputils.extract_num(scenario_name) scenario_base_name = scenario_name.rstrip("0123456789") model = pyo.ConcreteModel() def crops_init(m): retval = [] for i in range(crops_multiplier): retval.append("WHEAT" + str(i)) retval.append("CORN" + str(i)) retval.append("SUGAR_BEETS" + str(i)) return retval model.CROPS = pyo.Set(initialize=crops_init) # # Parameters # model.TOTAL_ACREAGE = 500.0 * crops_multiplier def _scale_up_data(indict): outdict = {} for i in range(crops_multiplier): for crop in ['WHEAT', 'CORN', 'SUGAR_BEETS']: outdict[crop + str(i)] = indict[crop] return outdict model.PriceQuota = _scale_up_data({ 'WHEAT': 100000.0, 'CORN': 100000.0, 'SUGAR_BEETS': 6000.0 }) model.SubQuotaSellingPrice = _scale_up_data({ 'WHEAT': 170.0, 'CORN': 150.0, 'SUGAR_BEETS': 36.0 }) model.SuperQuotaSellingPrice = _scale_up_data({ 'WHEAT': 0.0, 'CORN': 0.0, 'SUGAR_BEETS': 10.0 }) model.CattleFeedRequirement = _scale_up_data({ 'WHEAT': 200.0, 'CORN': 240.0, 'SUGAR_BEETS': 0.0 }) model.PurchasePrice = _scale_up_data({ 'WHEAT': 238.0, 'CORN': 210.0, 'SUGAR_BEETS': 100000.0 }) model.PlantingCostPerAcre = _scale_up_data({ 'WHEAT': 150.0, 'CORN': 230.0, 'SUGAR_BEETS': 260.0 }) # # Stochastic Data # Yield = {} Yield['BelowAverageScenario'] = \ {'WHEAT':2.0,'CORN':2.4,'SUGAR_BEETS':16.0} Yield['AverageScenario'] = \ {'WHEAT':2.5,'CORN':3.0,'SUGAR_BEETS':20.0} Yield['AboveAverageScenario'] = \ {'WHEAT':3.0,'CORN':3.6,'SUGAR_BEETS':24.0} def Yield_init(m, cropname): # yield as in "crop yield" crop_base_name = cropname.rstrip("0123456789") if scengroupnum != 0: return Yield[scenario_base_name][ crop_base_name] + farmerstream.rand() else: return Yield[scenario_base_name][crop_base_name] model.Yield = pyo.Param(model.CROPS, within=pyo.NonNegativeReals, initialize=Yield_init, mutable=True) # # Variables # if (use_integer): model.DevotedAcreage = pyo.Var(model.CROPS, within=pyo.NonNegativeIntegers, bounds=(0.0, model.TOTAL_ACREAGE)) else: model.DevotedAcreage = pyo.Var(model.CROPS, bounds=(0.0, model.TOTAL_ACREAGE)) model.QuantitySubQuotaSold = pyo.Var(model.CROPS, bounds=(0.0, None)) model.QuantitySuperQuotaSold = pyo.Var(model.CROPS, bounds=(0.0, None)) model.QuantityPurchased = pyo.Var(model.CROPS, bounds=(0.0, None)) # # Constraints # def ConstrainTotalAcreage_rule(model): return pyo.sum_product(model.DevotedAcreage) <= model.TOTAL_ACREAGE model.ConstrainTotalAcreage = pyo.Constraint( rule=ConstrainTotalAcreage_rule) def EnforceCattleFeedRequirement_rule(model, i): return model.CattleFeedRequirement[i] <= ( model.Yield[i] * model.DevotedAcreage[i] ) + model.QuantityPurchased[i] - model.QuantitySubQuotaSold[ i] - model.QuantitySuperQuotaSold[i] model.EnforceCattleFeedRequirement = pyo.Constraint( model.CROPS, rule=EnforceCattleFeedRequirement_rule) def LimitAmountSold_rule(model, i): return model.QuantitySubQuotaSold[i] + model.QuantitySuperQuotaSold[ i] - (model.Yield[i] * model.DevotedAcreage[i]) <= 0.0 model.LimitAmountSold = pyo.Constraint(model.CROPS, rule=LimitAmountSold_rule) def EnforceQuotas_rule(model, i): return (0.0, model.QuantitySubQuotaSold[i], model.PriceQuota[i]) model.EnforceQuotas = pyo.Constraint(model.CROPS, rule=EnforceQuotas_rule) # Stage-specific cost computations; def ComputeFirstStageCost_rule(model): return pyo.sum_product(model.PlantingCostPerAcre, model.DevotedAcreage) model.FirstStageCost = pyo.Expression(rule=ComputeFirstStageCost_rule) def ComputeSecondStageCost_rule(model): expr = pyo.sum_product(model.PurchasePrice, model.QuantityPurchased) expr -= pyo.sum_product(model.SubQuotaSellingPrice, model.QuantitySubQuotaSold) expr -= pyo.sum_product(model.SuperQuotaSellingPrice, model.QuantitySuperQuotaSold) return expr model.SecondStageCost = pyo.Expression(rule=ComputeSecondStageCost_rule) def total_cost_rule(model): if (sense == pyo.minimize): return model.FirstStageCost + model.SecondStageCost return -model.FirstStageCost - model.SecondStageCost model.Total_Cost_Objective = pyo.Objective(rule=total_cost_rule, sense=sense) return model
def prep_cs_cuts(self): # create a map scenario -> index, this index is used for various lists containing scenario dependent info. self.scenario_to_index = { scen: indx for indx, scen in enumerate(self.opt.all_scenario_names) } # create concrete model to use as pseudo-master self.opt.master = pyo.ConcreteModel() ##get the nonants off an arbitrary scenario arb_scen = self.opt.local_scenarios[self.opt.local_scenario_names[0]] non_ants = arb_scen._PySPnode_list[0].nonant_vardata_list # add copies of the nonanticipatory variables to the master problem # NOTE: the LShaped code expects the nonant vars to be in a particular # order and with a particular *name*. # We're also creating an index for reference against later nonant_vid_to_copy_map = dict() master_vars = list() for v in non_ants: non_ant_copy = pyo.Var(name=v.name) self.opt.master.add_component(v.name, non_ant_copy) master_vars.append(non_ant_copy) nonant_vid_to_copy_map[id(v)] = non_ant_copy self.opt.master_vars = master_vars # create an index of these non_ant_copies to be in the same # order as PH, used below nonants = dict() for ndn_i, nonant in arb_scen._mpisppy_data.nonant_indices.items(): vid = id(nonant) nonants[ndn_i] = nonant_vid_to_copy_map[vid] self.master_nonants = nonants self.opt.master.eta = pyo.Var(self.opt.all_scenario_names) self.opt.master.bender = LShapedCutGenerator() self.opt.master.bender.set_input(master_vars=self.opt.master_vars, tol=1e-4, comm=self.cylinder_comm) self.opt.master.bender.set_ls(self.opt) ## the below for loop can take some time, ## so return early if we get a kill signal, ## but only after a barrier self.cylinder_comm.Barrier() if self.got_kill_signal(): return # add the subproblems for all for scen in self.opt.all_scenario_names: subproblem_fn_kwargs = dict() # need to modify this to accept in user kwargs as well subproblem_fn_kwargs['scenario_name'] = scen self.opt.master.bender.add_subproblem( subproblem_fn=self.opt.create_subproblem, subproblem_fn_kwargs=subproblem_fn_kwargs, master_eta=self.opt.master.eta[scen], subproblem_solver=self.opt.options["sp_solver"], subproblem_solver_options=self.opt.options["sp_solver_options"] ) ## the above for loop can take some time, ## so return early if we get a kill signal, ## but only after a barrier self.cylinder_comm.Barrier() if self.got_kill_signal(): return ## This call is blocking, depending on the ## configuration. This necessitates the barrier ## above. self.opt.set_eta_bounds() self._eta_lb_array = np.fromiter( (self.opt.valid_eta_lb[s] for s in self.opt.all_scenario_names), dtype='d', count=len(self.opt.all_scenario_names)) self.make_eta_lb_cut()