Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
#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 
Ejemplo n.º 7
0
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])
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
    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  #
Ejemplo n.º 13
0
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):
Ejemplo n.º 14
0
            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))
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
    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()
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
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]
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
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
Ejemplo n.º 21
0
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
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
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
Ejemplo n.º 24
0
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
Ejemplo n.º 25
0
    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)
Ejemplo n.º 27
0
    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,
                )
Ejemplo n.º 28
0
#  ___________________________________________________________________________

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)
Ejemplo n.º 29
0
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
Ejemplo n.º 30
0
    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()