예제 #1
0
class WaterStateBlockData(StateBlockData):
    """
    General purpose StateBlock for Zero-Order unit models.
    """
    def build(self):
        super().build()

        # Create state variables
        self.flow_mass_comp = Var(self.component_list,
                                  initialize=1,
                                  domain=PositiveReals,
                                  doc='Mass flowrate of each component',
                                  units=pyunits.kg / pyunits.s)

    # -------------------------------------------------------------------------
    # Other properties
    def _conc_mass_comp(self):
        def rule_cmc(blk, j):
            return (blk.flow_mass_comp[j] / sum(self.flow_mass_comp[k]
                                                for k in self.component_list) *
                    blk.dens_mass)

        self.conc_mass_comp = Expression(self.component_list, rule=rule_cmc)

    def _dens_mass(self):
        self.dens_mass = Param(initialize=self.params.dens_mass_default,
                               units=pyunits.kg / pyunits.m**3,
                               mutable=True,
                               doc="Mass density of flow")

    def _flow_vol(self):
        self.flow_vol = Expression(expr=sum(self.flow_mass_comp[j]
                                            for j in self.component_list) /
                                   self.dens_mass)

    def _visc_d(self):
        self.visc_d = Param(initialize=self.params.visc_d_default,
                            units=pyunits.kg / pyunits.m / pyunits.s,
                            mutable=True,
                            doc="Dynamic viscosity of solution")

    def get_material_flow_terms(blk, p, j):
        return blk.flow_mass_comp[j]

    def get_enthalpy_flow_terms(blk, p):
        raise NotImplementedError

    def get_material_density_terms(blk, p, j):
        return blk.conc_mass_comp[j]

    def get_energy_density_terms(blk, p):
        raise NotImplementedError

    def default_material_balance_type(self):
        return MaterialBalanceType.componentTotal

    def default_energy_balance_type(self):
        return EnergyBalanceType.none

    def define_state_vars(blk):
        return {"flow_mass_comp": blk.flow_mass_comp}

    def define_display_vars(blk):
        return {
            "Volumetric Flowrate": blk.flow_vol,
            "Mass Concentration": blk.conc_mass_comp
        }

    def get_material_flow_basis(blk):
        return MaterialFlowBasis.mass

    def calculate_scaling_factors(self):
        # Get default scale factors and do calculations from base classes
        super().calculate_scaling_factors()

        d_sf_Q = self.params.default_scaling_factor["flow_vol"]
        d_sf_c = self.params.default_scaling_factor["conc_mass_comp"]

        for j, v in self.flow_mass_comp.items():
            if iscale.get_scaling_factor(v) is None:
                iscale.set_scaling_factor(v, d_sf_Q * d_sf_c)

        if self.is_property_constructed("flow_vol"):
            if iscale.get_scaling_factor(self.flow_vol) is None:
                iscale.set_scaling_factor(self.flow_vol, d_sf_Q)

        if self.is_property_constructed("conc_mass_comp"):
            for j, v in self.conc_mass_comp.items():
                sf_c = iscale.get_scaling_factor(self.conc_mass_comp[j])
                if sf_c is None:
                    try:
                        sf_c = self.params.default_scaling_factor[(
                            "conc_mass_comp", j)]
                    except KeyError:
                        sf_c = d_sf_c
                    iscale.set_scaling_factor(self.conc_mass_comp[j], sf_c)
예제 #2
0
class FlueGasStateBlockData(StateBlockData):
    """
    This is an example of a property package for calculating the thermophysical
    properties of flue gas using the ideal gas assumption.
    """
    def build(self):
        """
        Callable method for Block construction
        """
        super(FlueGasStateBlockData, self).build()
        comps = self.params.component_list
        # Add state variables
        self.flow_mol_comp = Var(comps,
                                 domain=Reals,
                                 initialize=1.0,
                                 bounds=(0, 1e6),
                                 doc='Component molar flowrate [mol/s]',
                                 units=pyunits.mol / pyunits.s)
        self.pressure = Var(domain=Reals,
                            initialize=1.01325e5,
                            bounds=(1, 5e7),
                            doc='State pressure [Pa]',
                            units=pyunits.Pa)
        self.temperature = Var(domain=Reals,
                               initialize=500,
                               bounds=(200, 1500),
                               doc='State temperature [K]',
                               units=pyunits.K)

        # Add expressions for some basic oft-used quantiies
        self.flow_mol = Expression(expr=sum(self.flow_mol_comp[j]
                                            for j in comps))

        def rule_mole_frac(b, c):
            return b.flow_mol_comp[c] / b.flow_mol

        self.mole_frac_comp = Expression(comps,
                                         rule=rule_mole_frac,
                                         doc='mole fraction of component i')

        self.flow_mass = Expression(expr=sum(
            self.flow_mol_comp[j] * self.params.mw_comp[j] for j in comps),
                                    doc='total mass flow')

        def rule_mw_comp(b, j):
            return b.params.mw_comp[j]

        self.mw_comp = Expression(comps, rule=rule_mw_comp)

        def rule_mw(b):
            return sum(b.mw_comp[j] * b.mole_frac_comp[j] for j in comps)

        self.mw = Expression(rule=rule_mw)

        self.pressure_crit = Expression(expr=sum(self.params.pressure_crit[j] *
                                                 self.mole_frac_comp[j]
                                                 for j in comps))
        self.temperature_crit = Expression(expr=sum(
            self.params.temperature_crit[j] * self.mole_frac_comp[j]
            for j in comps))
        self.pressure_red = Expression(expr=self.pressure / self.pressure_crit)
        self.temperature_red = Expression(expr=self.temperature /
                                          self.temperature_crit)

        self.compress_fact = Expression(expr=1.0,
                                        doc='Vapor Compressibility Factor')

        def rule_dens_mol_phase(b, p):
            return b.pressure / b.compress_fact / \
                constants.Constants.gas_constant / b.temperature

        self.dens_mol_phase = Expression(self.params.phase_list,
                                         rule=rule_dens_mol_phase,
                                         doc='Molar Density')

        self.flow_vol = Expression(doc='Volumetric Flowrate',
                                   expr=self.flow_mol /
                                   self.dens_mol_phase["Vap"])

    def _heat_cap_calc(self):
        # heat capacity J/mol-K
        self.cp_mol = Var(initialize=1000,
                          doc='heat capacity [J/mol-K]',
                          units=pyunits.J / pyunits.mol / pyunits.K)

        def rule_cp_phase(b, p):
            # This property module only has one phase
            return self.cp_mol

        self.cp_mol_phase = Expression(self.params.phase_list,
                                       rule=rule_cp_phase)

        try:
            ft = sum(self.flow_mol_comp[j] for j in self.params.component_list)
            t = pyunits.convert(self.temperature, to_units=pyunits.kK)
            self.heat_cap_correlation = Constraint(
                expr=(self.cp_mol *
                      ft == sum(self.flow_mol_comp[j] *
                                (self.params.cp_mol_ig_comp_coeff_A[j] +
                                 self.params.cp_mol_ig_comp_coeff_B[j] * t +
                                 self.params.cp_mol_ig_comp_coeff_C[j] * t**2 +
                                 self.params.cp_mol_ig_comp_coeff_D[j] * t**3 +
                                 self.params.cp_mol_ig_comp_coeff_E[j] / t**2)
                                for j in self.params.component_list)))
        except AttributeError:
            self.del_component(self.cp_mol)
            self.del_component(self.heat_cap_correlation)

    def _enthalpy_calc(self):
        self.enth_mol = Var(doc='Specific Enthalpy [J/mol]',
                            units=pyunits.J / pyunits.mol)

        def rule_enth_phase(b, p):
            # This property module only has one phase
            return self.enth_mol

        self.enth_mol_phase = Expression(self.params.phase_list,
                                         rule=rule_enth_phase)

        def enthalpy_correlation(b):
            ft = sum(self.flow_mol_comp[j] for j in self.params.component_list)
            t = pyunits.convert(self.temperature, to_units=pyunits.kK)
            return self.enth_mol * ft == sum(
                self.flow_mol_comp[j] * pyunits.convert(
                    self.params.cp_mol_ig_comp_coeff_A[j] * t +
                    self.params.cp_mol_ig_comp_coeff_B[j] * t**2 / 2 +
                    self.params.cp_mol_ig_comp_coeff_C[j] * t**3 / 3 +
                    self.params.cp_mol_ig_comp_coeff_D[j] * t**4 / 4 -
                    self.params.cp_mol_ig_comp_coeff_E[j] / t +
                    self.params.cp_mol_ig_comp_coeff_F[j],
                    to_units=pyunits.J / pyunits.mol)
                for j in self.params.component_list)
            # NOTE: the H term (from the Shomate Equation) is not
            # included here so that the reference state enthalpy is the
            # enthalpy of formation (not 0).

        try:
            self.enthalpy_correlation = Constraint(rule=enthalpy_correlation)
        except AttributeError:
            self.del_component(self.enth_mol_phase)
            self.del_component(self.enth_mol)
            self.del_component(self.enthalpy_correlation)

    def _entropy_calc(self):
        self.entr_mol = Var(doc='Specific Entropy [J/mol/K]',
                            units=pyunits.J / pyunits.mol / pyunits.K)

        # Specific Entropy

        def rule_entr_phase(b, p):
            # This property module only has one phase
            return self.entr_mol

        self.entr_mol_phase = Expression(self.params.phase_list,
                                         rule=rule_entr_phase)

        def entropy_correlation(b):
            ft = sum(self.flow_mol_comp[j] for j in self.params.component_list)
            t = pyunits.convert(self.temperature, to_units=pyunits.kK)
            n = self.flow_mol_comp
            x = self.mole_frac_comp
            p = self.pressure
            r_gas = constants.Constants.gas_constant
            return (self.entr_mol + r_gas * log(p/1e5)) * ft == \
                sum(n[j] * (
                    self.params.cp_mol_ig_comp_coeff_A[j]*log(t) +
                    self.params.cp_mol_ig_comp_coeff_B[j]*t +
                    self.params.cp_mol_ig_comp_coeff_C[j]*t**2 / 2 +
                    self.params.cp_mol_ig_comp_coeff_D[j]*t**3 / 3 -
                    self.params.cp_mol_ig_comp_coeff_E[j]/t**2 / 2 +
                    self.params.cp_mol_ig_comp_coeff_G[j] +
                    r_gas * log(x[j])) for j in self.params.component_list)

        try:
            self.entropy_correlation = Constraint(rule=entropy_correlation)
        except AttributeError:
            self.del_component(self.entr_mol_phase)
            self.del_component(self.entropy_correlation)

    def _therm_cond(self):
        comps = self.params.component_list
        self.therm_cond_comp = Var(comps,
                                   initialize=0.05,
                                   doc='thermal conductivity J/m-K-s',
                                   units=pyunits.J / pyunits.m / pyunits.K /
                                   pyunits.s)
        self.therm_cond = Var(
            initialize=0.05,
            doc='thermal conductivity of gas mixture J/m-K-s',
            units=pyunits.J / pyunits.m / pyunits.K / pyunits.s)
        self.visc_d_comp = Var(comps,
                               initialize=2e-5,
                               doc='dynamic viscocity of pure gas species',
                               units=pyunits.kg / pyunits.m / pyunits.s)
        self.visc_d = Var(initialize=2e-5,
                          doc='viscosity of gas mixture kg/m-s',
                          units=pyunits.kg / pyunits.m / pyunits.s)

        try:

            def rule_therm_cond(b, c):
                t = pyunits.convert(b.temperature, to_units=pyunits.kK)
                return b.therm_cond_comp[c] == (
                    ((b.params.cp_mol_ig_comp_coeff_A[c] +
                      b.params.cp_mol_ig_comp_coeff_B[c] * t +
                      b.params.cp_mol_ig_comp_coeff_C[c] * t**2 +
                      b.params.cp_mol_ig_comp_coeff_D[c] * t**3 +
                      b.params.cp_mol_ig_comp_coeff_E[c] / t**2) /
                     b.params.mw_comp[c]) + 1.25 *
                    (constants.Constants.gas_constant / b.params.mw_comp[c])
                ) * b.visc_d_comp[c]

            self.therm_cond_con = Constraint(comps, rule=rule_therm_cond)

            def rule_theta(b, c):
                return b.temperature / b.params.ep_Kappa[c]

            self.theta = Expression(comps, rule=rule_theta)

            def rule_omega(b, c):
                return (1.5794145 + 0.00635771 * b.theta[c] -
                        0.7314 * log(b.theta[c]) +
                        0.2417357 * log(b.theta[c])**2 -
                        0.0347045 * log(b.theta[c])**3)

            self.omega = Expression(comps, rule=rule_omega)

            # Pure gas viscocity - from Chapman-Enskog theory
            def rule_visc_d(b, c):
                return (pyunits.convert(b.visc_d_comp[c],
                                        to_units=pyunits.g / pyunits.cm /
                                        pyunits.s) * b.params.sigma[c]**2 *
                        b.omega[c] == b.params.ce_param * sqrt(
                            pyunits.convert(b.params.mw_comp[c],
                                            to_units=pyunits.g / pyunits.mol) *
                            b.temperature))

            self.visc_d_con = Constraint(comps, rule=rule_visc_d)

            # section to calculate viscosity of gas mixture
            def rule_phi(b, i, j):
                return (1 / 2.8284 *
                        (1 +
                         (b.params.mw_comp[i] / b.params.mw_comp[j]))**(-0.5) *
                        (1 + sqrt(b.visc_d_comp[i] / b.visc_d_comp[j]) *
                         (b.params.mw_comp[j] / b.params.mw_comp[i])**0.25)**2)

            self.phi_ij = Expression(comps, comps, rule=rule_phi)

            # viscosity of Gas mixture kg/m-s
            def rule_visc_d_mix(b):
                return b.visc_d == sum(
                    (b.mole_frac_comp[i] * b.visc_d_comp[i]) /
                    sum(b.mole_frac_comp[j] * b.phi_ij[i, j] for j in comps)
                    for i in comps)

            self.vis_d_mix_con = Constraint(rule=rule_visc_d_mix)

            # thermal conductivity of gas mixture in kg/m-s
            def rule_therm_mix(b):
                return b.therm_cond == sum(
                    (b.mole_frac_comp[i] * b.therm_cond_comp[i]) /
                    sum(b.mole_frac_comp[j] * b.phi_ij[i, j] for j in comps)
                    for i in comps)

            self.therm_mix_con = Constraint(rule=rule_therm_mix)

        except AttributeError:
            self.del_component(self.therm_cond_comp)
            self.del_component(self.therm_cond)
            self.del_component(self.visc_d_comp)
            self.del_component(self.visc_d)
            self.del_component(self.omega)
            self.del_component(self.theta)
            self.del_component(self.phi_ij)
            self.del_component(self.sigma)
            self.del_component(self.ep_Kappa)
            self.del_component(self.therm_cond_con)
            self.del_component(self.theta_con)
            self.del_component(self.omega_con)
            self.del_component(self.visc_d_con)
            self.del_component(self.phi_con)

    def default_material_balance_type(self):
        return MaterialBalanceType.componentTotal

    def default_energy_balance_type(self):
        return EnergyBalanceType.enthalpyTotal

    def get_material_flow_terms(self, p, j):
        return self.flow_mol_comp[j]

    def get_material_flow_basis(self):
        return MaterialFlowBasis.molar

    def get_enthalpy_flow_terms(self, p):
        if not self.is_property_constructed("enthalpy_flow_terms"):
            try:

                def rule_enthalpy_flow_terms(b, p):
                    return self.enth_mol_phase[p] * self.flow_mol

                self.enthalpy_flow_terms = Expression(
                    self.params.phase_list, rule=rule_enthalpy_flow_terms)
            except AttributeError:
                self.del_component(self.enthalpy_flow_terms)
        return self.enthalpy_flow_terms[p]

    def get_material_density_terms(self, p, j):
        return self.dens_mol_phase[p]

    def get_energy_density_terms(self, p):
        if not self.is_property_constructed("energy_density_terms"):
            try:

                def rule_energy_density_terms(b, p):
                    return self.enth_mol_phase[p] * \
                        self.dens_mol_phase[p] - self.pressure

                self.energy_density_terms = Expression(
                    self.params.phase_list, rule=rule_energy_density_terms)
            except AttributeError:
                self.del_component(self.energy_density_terms)
        return self.energy_density_terms[p]

    def define_state_vars(self):
        return {
            "flow_mol_comp": self.flow_mol_comp,
            "temperature": self.temperature,
            "pressure": self.pressure
        }

    def model_check(self):
        """
        Model checks for property block
        """
        # Check temperature bounds
        for v in self.compoent_object_data(Var, descend_into=True):
            if value(v) < v.lb:
                _log.error(f"{v} is below lower bound in {self.name}")
            if value(v) > v.ub:
                _log.error(f"{v} is above upper bound in {self.name}")

    def calculate_scaling_factors(self):
        super().calculate_scaling_factors()

        # Get some scale factors that are frequently used to calculate others
        sf_flow = iscale.get_scaling_factor(self.flow_mol)
        sf_mol_fraction = {}
        comps = self.params.component_list
        for i in comps:
            sf_mol_fraction[i] = iscale.get_scaling_factor(
                self.mole_frac_comp[i])
        # calculate flow_mol_comp scale factors
        for i, c in self.flow_mol_comp.items():
            iscale.set_scaling_factor(c, sf_flow * sf_mol_fraction[i])

        if self.is_property_constructed("energy_density_terms"):
            for i, c in self.energy_density_terms.items():
                sf1 = iscale.get_scaling_factor(self.enth_mol_phase[i])
                sf2 = iscale.get_scaling_factor(self.dens_mol_phase[i])
                iscale.set_scaling_factor(c, sf1 * sf2)

        if self.is_property_constructed("enthalpy_flow_terms"):
            for i, c in self.enthalpy_flow_terms.items():
                sf1 = iscale.get_scaling_factor(self.enth_mol_phase[i])
                sf2 = iscale.get_scaling_factor(self.flow_mol)
                iscale.set_scaling_factor(c, sf1 * sf2)

        if self.is_property_constructed("heat_cap_correlation"):
            iscale.constraint_scaling_transform(
                self.heat_cap_correlation,
                iscale.get_scaling_factor(self.cp_mol) *
                iscale.get_scaling_factor(self.flow_mol))
        if self.is_property_constructed("enthalpy_correlation"):
            for p, c in self.enthalpy_correlation.items():
                iscale.constraint_scaling_transform(
                    c,
                    iscale.get_scaling_factor(self.enth_mol) *
                    iscale.get_scaling_factor(self.flow_mol))
        if self.is_property_constructed("entropy_correlation"):
            iscale.constraint_scaling_transform(
                self.entropy_correlation,
                iscale.get_scaling_factor(self.entr_mol) *
                iscale.get_scaling_factor(self.flow_mol))
        if self.is_property_constructed("vapor_pressure_correlation"):
            iscale.constraint_scaling_transform(
                self.vapor_pressure_correlation,
                log(iscale.get_scaling_factor(self.pressure_sat)) *
                iscale.get_scaling_factor(self.flow_mol))
        if self.is_property_constructed("therm_cond_con"):
            for i, c in self.therm_cond_con.items():
                iscale.constraint_scaling_transform(
                    c, iscale.get_scaling_factor(self.therm_cond_comp[i]))
        if self.is_property_constructed("therm_mix_con"):
            iscale.constraint_scaling_transform(
                self.therm_mix_con, iscale.get_scaling_factor(self.therm_cond))
        if self.is_property_constructed("visc_d_con"):
            for i, c in self.visc_d_con.items():
                iscale.constraint_scaling_transform(
                    c, iscale.get_scaling_factor(self.visc_d_comp[i]))
        if self.is_property_constructed("visc_d_mix_con"):
            iscale.constraint_scaling_transform(
                self.visc_d_mix_con, iscale.get_scaling_factor(self.visc_d))