コード例 #1
0
def build_valve_liquid():
    m = ConcreteModel()
    m.fs = FlowsheetBlock(default={"dynamic": False})
    m.fs.properties = iapws95.Iapws95ParameterBlock()
    m.fs.valve = SteamValve(default={"property_package": m.fs.properties,
                                     "phase": "Liq"})
    return m
コード例 #2
0
def create_model(
    steady_state=True,
    time_set=[0,3],
    nfe=5,
    calc_integ=True,
    form=PIDForm.standard
):
    """ Create a test model and solver

    Args:
        steady_state (bool): If True, create a steady state model, otherwise
            create a dynamic model
        time_set (list): The begining and end point of the time domain
        nfe (int): Number of finite elements argument for the DAE transformation
        calc_integ (bool): If Ture, calculate in the initial condition for
            the integral term, else use a fixed variable (fs.ctrl.err_i0), flase
            is the better option if you have a value from a previous time period
        form: whether the equations are written in the standard or velocity form

    Returns
        (tuple): (ConcreteModel, Solver)
    """
    if steady_state:
        fs_cfg = {"dynamic":False}
        model_name = "Steam Tank, Steady State"
    else:
        fs_cfg = {"dynamic":True, "time_set":time_set}
        model_name = "Steam Tank, Dynamic"

    m = pyo.ConcreteModel(name=model_name)
    m.fs = FlowsheetBlock(default=fs_cfg)
    # Create a property parameter block
    m.fs.prop_water = iapws95.Iapws95ParameterBlock(
        default={"phase_presentation":iapws95.PhaseType.LG})
    # Create the valve and tank models
    m.fs.valve_1 = SteamValve(default={
        "dynamic":False,
        "has_holdup":False,
        "material_balance_type":MaterialBalanceType.componentTotal,
        "property_package":m.fs.prop_water})
    m.fs.tank = Heater(default={
        "has_holdup":True,
        "material_balance_type":MaterialBalanceType.componentTotal,
        "property_package":m.fs.prop_water})
    m.fs.valve_2 = SteamValve(default={
        "dynamic":False,
        "has_holdup":False,
        "material_balance_type":MaterialBalanceType.componentTotal,
        "property_package":m.fs.prop_water})

    # Connect the models
    m.fs.v1_to_t = Arc(source=m.fs.valve_1.outlet, destination=m.fs.tank.inlet)
    m.fs.t_to_v2 = Arc(source=m.fs.tank.outlet, destination=m.fs.valve_2.inlet)

    # The control volume block doesn't assume the two phases are in equilibrium
    # by default, so I'll make that assumption here, I don't actually expect
    # liquid to form but who knows. The phase_fraction in the control volume is
    # volumetric phase fraction hence the densities.
    @m.fs.tank.Constraint(m.fs.time)
    def vol_frac_vap(b, t):
        return b.control_volume.properties_out[t].phase_frac["Vap"]\
            *b.control_volume.properties_out[t].dens_mol\
            /b.control_volume.properties_out[t].dens_mol_phase["Vap"]\
            == b.control_volume.phase_fraction[t, "Vap"]

    # Add the stream constraints and do the DAE transformation
    pyo.TransformationFactory('network.expand_arcs').apply_to(m.fs)
    if not steady_state:
        pyo.TransformationFactory('dae.finite_difference').apply_to(
            m.fs, nfe=nfe, wrt=m.fs.time, scheme='BACKWARD')

    # Fix the derivative variables to zero at time 0 (steady state assumption)
    m.fs.fix_initial_conditions()

    # A tank pressure reference that's directly time-indexed
    m.fs.tank_pressure = pyo.Reference(
        m.fs.tank.control_volume.properties_out[:].pressure)

    # Add a controller
    m.fs.ctrl = PIDBlock(default={"pv":m.fs.tank_pressure,
                                  "output":m.fs.valve_1.valve_opening,
                                  "upper":1.0,
                                  "lower":0.0,
                                  "calculate_initial_integral":calc_integ,
                                  "pid_form":form})
    m.fs.ctrl.deactivate() # Don't want controller turned on by default

    # Fix the input variables
    m.fs.valve_1.inlet.enth_mol.fix(50000)
    m.fs.valve_1.inlet.pressure.fix(5e5)
    m.fs.valve_2.outlet.pressure.fix(101325)
    m.fs.valve_1.Cv.fix(0.001)
    m.fs.valve_2.Cv.fix(0.001)
    m.fs.valve_1.valve_opening.fix(1)
    m.fs.valve_2.valve_opening.fix(1)
    m.fs.tank.heat_duty.fix(0)
    m.fs.tank.control_volume.volume.fix(2.0)
    m.fs.ctrl.gain.fix(1e-6)
    m.fs.ctrl.time_i.fix(0.1)
    m.fs.ctrl.time_d.fix(0.1)
    m.fs.ctrl.setpoint.fix(3e5)

    # Initialize the model
    solver = pyo.SolverFactory("ipopt")
    solver.options = {'tol': 1e-6,
                      'linear_solver': "ma27",
                      'max_iter': 100}
    for t in m.fs.time:
        m.fs.valve_1.inlet.flow_mol = 100 # initial guess on flow
    # simple initialize
    m.fs.valve_1.initialize(outlvl=1)
    _set_port(m.fs.tank.inlet, m.fs.valve_1.outlet)
    m.fs.tank.initialize(outlvl=1)
    _set_port(m.fs.valve_2.inlet, m.fs.tank.outlet)
    m.fs.valve_2.initialize(outlvl=1)
    solver.solve(m, tee=True)

    # Return the model and solver
    return m, solver
コード例 #3
0
ファイル: heater.py プロジェクト: sodanetworks/idaes-pse
def create_model_dynamic(p_in=5e5,
                         p_out=101325,
                         h=5e4,
                         time_set=[0, 5],
                         nfe=10,
                         dae_transform='dae.collocation',
                         dae_scheme='LAGRANGE-RADAU'):
    """ Create a test dynamic heater model and solver.  For dynamic testing
    a valve is added to the heater outlet to set up pressure driven flow.

    Args:
        time_set (list): The begining and end point of the time domain
        nfe (int): Number of finite elements argument for the DAE transformation
    Returns:
        (tuple): (ConcreteModel, Solver)
    """
    m = pyo.ConcreteModel(name="Dynamic Heater Test")
    m.fs = FlowsheetBlock(default={"dynamic": True, "time_set": time_set})
    # Create a property parameter block
    m.fs.prop_water = iapws95.Iapws95ParameterBlock(
        default={"phase_presentation": iapws95.PhaseType.MIX})
    # Create the valve and heater models
    m.fs.pipe = SteamValve(
        default={
            "dynamic": False,
            "has_holdup": False,
            "material_balance_type": MaterialBalanceType.componentTotal,
            "property_package": m.fs.prop_water
        })
    m.fs.heater = Heater(
        default={
            "has_holdup": True,
            "material_balance_type": MaterialBalanceType.componentTotal,
            "property_package": m.fs.prop_water
        })
    m.fs.valve = SteamValve(
        default={
            "dynamic": False,
            "has_holdup": False,
            "material_balance_type": MaterialBalanceType.componentTotal,
            "property_package": m.fs.prop_water
        })

    # Connect the models
    m.fs.v1_to_t = Arc(source=m.fs.pipe.outlet, destination=m.fs.heater.inlet)
    m.fs.t_to_v2 = Arc(source=m.fs.heater.outlet, destination=m.fs.valve.inlet)

    # Add the stream constraints and do the DAE transformation
    pyo.TransformationFactory('network.expand_arcs').apply_to(m.fs)
    pyo.TransformationFactory('dae.finite_difference').apply_to(
        m.fs, nfe=nfe, wrt=m.fs.time, scheme='BACKWARD')

    # Fix the derivative variables to zero at time 0 (steady state assumption)
    m.fs.fix_initial_conditions()

    # A heater pressure reference that's directly time-indexed
    m.fs.heater_pressure = pyo.Reference(
        m.fs.heater.control_volume.properties_out[:].pressure)

    # Fix the input variables
    m.fs.pipe.inlet.enth_mol.fix(h)
    m.fs.pipe.inlet.pressure.fix(p_in)
    m.fs.valve.outlet.pressure.fix(p_out)
    m.fs.pipe.Cv.fix(0.001)
    m.fs.valve.Cv.fix(0.001)
    m.fs.pipe.valve_opening.fix(1)
    m.fs.valve.valve_opening.fix(1)
    m.fs.heater.heat_duty.fix(0)
    m.fs.heater.control_volume.volume.fix(2.0)

    # Initialize the model
    solver = pyo.SolverFactory("ipopt")
    solver.options = {'tol': 1e-6, 'linear_solver': "ma27", 'max_iter': 25}
    for t in m.fs.time:
        m.fs.pipe.inlet.flow_mol = 250  # initial guess on flow
    # simple initialize
    m.fs.pipe.initialize()
    _set_port(m.fs.heater.inlet, m.fs.pipe.outlet)
    m.fs.heater.initialize()
    _set_port(m.fs.valve.inlet, m.fs.heater.outlet)
    m.fs.valve.initialize()
    solver.solve(m, tee=True)

    # Return the model and solver
    return m, solver
コード例 #4
0
    def build(self):
        super(TurbineMultistageData, self).build()
        config = self.config
        unit_cfg = {  # general unit model config
            "dynamic": config.dynamic,
            "has_holdup": config.has_holdup,
            "has_phase_equilibrium": config.has_phase_equilibrium,
            "material_balance_type": config.material_balance_type,
            "property_package": config.property_package,
            "property_package_args": config.property_package_args,
        }
        ni = self.config.num_parallel_inlet_stages
        inlet_idx = self.inlet_stage_idx = RangeSet(ni)

        # Adding unit models
        # ------------------------

        # Splitter to inlet that splits main flow into parallel flows for
        # paritial arc admission to the turbine
        self.inlet_split = Separator(default=self._split_cfg(unit_cfg, ni))
        self.throttle_valve = SteamValve(inlet_idx, default=unit_cfg)
        self.inlet_stage = TurbineInletStage(inlet_idx, default=unit_cfg)
        # mixer to combine the parallel flows back together
        self.inlet_mix = Mixer(default=self._mix_cfg(unit_cfg, ni))
        # add turbine sections.
        # inlet stage -> hp stages -> ip stages -> lp stages -> outlet stage
        self.hp_stages = TurbineStage(
            RangeSet(config.num_hp), default=unit_cfg)
        self.ip_stages = TurbineStage(
            RangeSet(config.num_ip), default=unit_cfg)
        self.lp_stages = TurbineStage(
            RangeSet(config.num_lp), default=unit_cfg)
        self.outlet_stage = TurbineOutletStage(default=unit_cfg)

        for i in self.hp_stages:
            self.hp_stages[i].ratioP.fix()
            self.hp_stages[i].efficiency_isentropic[:].fix()
        for i in self.ip_stages:
            self.ip_stages[i].ratioP.fix()
            self.ip_stages[i].efficiency_isentropic[:].fix()
        for i in self.lp_stages:
            self.lp_stages[i].ratioP.fix()
            self.lp_stages[i].efficiency_isentropic[:].fix()

        # Then make splitter config.  If number of outlets is specified
        # make a specific config, otherwise use default with 2 outlets
        s_sfg_default = self._split_cfg(unit_cfg, 2)
        hp_splt_cfg = {}
        ip_splt_cfg = {}
        lp_splt_cfg = {}
        # Now to finish up if there are more than two outlets, set that
        for i, v in config.hp_split_num_outlets.items():
            hp_splt_cfg[i] = self._split_cfg(unit_cfg, v)
        for i, v in config.ip_split_num_outlets.items():
            ip_splt_cfg[i] = self._split_cfg(unit_cfg, v)
        for i, v in config.lp_split_num_outlets.items():
            lp_splt_cfg[i] = self._split_cfg(unit_cfg, v)
        # put in splitters for turbine steam extractions
        if config.hp_split_locations:
            self.hp_split = Separator(
                config.hp_split_locations,
                default=s_sfg_default,
                initialize=hp_splt_cfg
            )
        if config.ip_split_locations:
            self.ip_split = Separator(
                config.ip_split_locations,
                default=s_sfg_default,
                initialize=ip_splt_cfg
            )
        if config.lp_split_locations:
            self.lp_split = Separator(
                config.lp_split_locations,
                default=s_sfg_default,
                initialize=lp_splt_cfg
            )

        # Done with unit models.  Adding Arcs (streams).
        # ------------------------------------------------

        # First up add streams in the inlet section
        def _split_to_rule(b, i):
            return {
                "source": getattr(self.inlet_split, "outlet_{}".format(i)),
                "destination": self.throttle_valve[i].inlet,
            }

        def _valve_to_rule(b, i):
            return {
                "source": self.throttle_valve[i].outlet,
                "destination": self.inlet_stage[i].inlet,
            }

        def _inlet_to_rule(b, i):
            return {
                "source": self.inlet_stage[i].outlet,
                "destination": getattr(self.inlet_mix, "inlet_{}".format(i)),
            }

        self.split_to_valve_stream = Arc(inlet_idx, rule=_split_to_rule)
        self.valve_to_inlet_stage_stream = Arc(inlet_idx, rule=_valve_to_rule)
        self.inlet_stage_to_mix = Arc(inlet_idx, rule=_inlet_to_rule)

        # There are three sections HP, IP, and LP which all have the same sort
        # of internal connctions, so the functions below provide some generic
        # capcbilities for adding the internal Arcs (streams).
        def _arc_indexes(nstages, index_set, discon, splits):
            """
            This takes the index set of all possible streams in a turbine
            section and throws out arc indexes for stages that are disconnected
            and arc indexes that are not needed because there is no splitter
            after a stage.

            Args:
                nstages (int): Number of stages in section
                index_set (Set): Index set for arcs in the section
                discon (list): Disconnected stages in the section
                splits (list): Spliter locations
            """
            sr = set()  # set of things to remove from the Arc index set
            for i in index_set:
                if (i[0] in discon or i[0] == nstages) and i[0] in splits:
                    # don't connect stage i to next remove stream after split
                    sr.add((i[0], 2))
                elif ((i[0] in discon or i[0] == nstages) and
                      i[0] not in splits):
                    # no splitter and disconnect so remove both streams
                    sr.add((i[0], 1))
                    sr.add((i[0], 2))
                elif i[0] not in splits:
                    # no splitter and not disconnected so just second stream
                    sr.add((i[0], 2))
                else:
                    # has splitter so need both streams don't remove anything
                    pass
            for i in sr:  # remove the unneeded Arc indexes
                index_set.remove(i)

        def _arc_rule(turbines, splitters):
            """
            This creates a rule function for arcs in a turbine section. When
            this is used the indexes for nonexistant stream will have already
            been removed, so any indexes the rule will get should have a stream
            associated.

            Args:
                turbines (TurbineStage): Indexed block with turbine section
                stages splitters (Separator): Indexed block of splitters
            """

            def _rule(b, i, j):
                if i in splitters and j == 1:
                    return {
                        "source": turbines[i].outlet,
                        "destination": splitters[i].inlet,
                    }
                elif j == 2:
                    return {
                        "source": splitters[i].outlet_1,
                        "destination": turbines[i + 1].inlet,
                    }
                else:
                    return {
                        "source": turbines[i].outlet,
                        "destination": turbines[i + 1].inlet,
                    }

            return _rule

        # Create initial arcs index sets with all possible streams
        self.hp_stream_idx = Set(
            initialize=self.hp_stages.index_set() * [1, 2])
        self.ip_stream_idx = Set(
            initialize=self.ip_stages.index_set() * [1, 2])
        self.lp_stream_idx = Set(
            initialize=self.lp_stages.index_set() * [1, 2])

        # Throw out unneeded streams
        _arc_indexes(
            config.num_hp,
            self.hp_stream_idx,
            config.hp_disconnect,
            config.hp_split_locations,
        )
        _arc_indexes(
            config.num_ip,
            self.ip_stream_idx,
            config.ip_disconnect,
            config.ip_split_locations,
        )
        _arc_indexes(
            config.num_lp,
            self.lp_stream_idx,
            config.lp_disconnect,
            config.lp_split_locations,
        )

        # Create connections internal to each turbine section (hp, ip, and lp)
        self.hp_stream = Arc(
            self.hp_stream_idx, rule=_arc_rule(self.hp_stages, self.hp_split)
        )
        self.ip_stream = Arc(
            self.ip_stream_idx, rule=_arc_rule(self.ip_stages, self.ip_split)
        )
        self.lp_stream = Arc(
            self.lp_stream_idx, rule=_arc_rule(self.lp_stages, self.lp_split)
        )

        # Connect hp section to ip section unless its a disconnect location
        last_hp = config.num_hp
        if (0 not in config.ip_disconnect and
                last_hp not in config.hp_disconnect):
            if last_hp in config.hp_split_locations:  # connect splitter to ip
                self.hp_to_ip_stream = Arc(
                    source=self.hp_split[last_hp].outlet_1,
                    destination=self.ip_stages[1].inlet,
                )
            else:  # connect last hp to ip
                self.hp_to_ip_stream = Arc(
                    source=self.hp_stages[last_hp].outlet,
                    destination=self.ip_stages[1].inlet,
                )
        # Connect ip section to lp section unless its a disconnect location
        last_ip = config.num_ip
        if (0 not in config.lp_disconnect and
                last_ip not in config.ip_disconnect):
            if last_ip in config.ip_split_locations:  # connect splitter to ip
                self.ip_to_lp_stream = Arc(
                    source=self.ip_split[last_ip].outlet_1,
                    destination=self.lp_stages[1].inlet,
                )
            else:  # connect last hp to ip
                self.ip_to_lp_stream = Arc(
                    source=self.ip_stages[last_ip].outlet,
                    destination=self.lp_stages[1].inlet,
                )
        # Connect inlet stage to hp section
        #   not allowing disconnection of inlet and first regular hp stage
        if 0 in config.hp_split_locations:
            # connect inlet mix to splitter and splitter to hp section
            self.inlet_to_splitter_stream = Arc(
                source=self.inlet_mix.outlet,
                destination=self.hp_split[0].inlet
            )
            self.splitter_to_hp_stream = Arc(
                source=self.hp_split[0].outlet_1,
                destination=self.hp_stages[1].inlet
            )
        else:  # connect mixer to first hp turbine stage
            self.inlet_to_hp_stream = Arc(
                source=self.inlet_mix.outlet,
                destination=self.hp_stages[1].inlet
            )

        @self.Expression(self.flowsheet().config.time)
        def power(b, t):
            return (
                sum(b.inlet_stage[i].power_shaft[t] for i in b.inlet_stage)
                + b.outlet_stage.power_shaft[t]
                + sum(b.hp_stages[i].power_shaft[t] for i in b.hp_stages)
                + sum(b.ip_stages[i].power_shaft[t] for i in b.ip_stages)
                + sum(b.lp_stages[i].power_shaft[t] for i in b.lp_stages)
            )

        # Connect inlet stage to hp section
        #   not allowing disconnection of inlet and first regular hp stage
        last_lp = config.num_lp
        if last_lp in config.lp_split_locations:  # connect splitter to outlet
            self.lp_to_outlet_stream = Arc(
                source=self.lp_split[last_lp].outlet_1,
                destination=self.outlet_stage.inlet,
            )
        else:  # connect last lpstage to outlet
            self.lp_to_outlet_stream = Arc(
                source=self.lp_stages[last_lp].outlet,
                destination=self.outlet_stage.inlet,
            )
        TransformationFactory("network.expand_arcs").apply_to(self)