Beispiel #1
0
    def get_parameter_value(comp_name, param):
        """
        Retrieve tuples of (value, units) for the specified property and
        component for the critical state section of the CoolProp json format.

        Args:
            comp_name: name of componet for which to retirve parameters
            param: name of parameter to return value and units

        Returns:
            tuple of parameter (value, units)
        """
        map_tuple = name_map.get(param, None)
        if map_tuple is None:
            raise BurntToast(
                "Unrecognized property name in CoolProp wrapper. Please "
                "contact the IDAES developers with this bug.")

        if map_tuple[1] == "CRITICAL":
            ptuple = CoolPropWrapper._get_critical_property(
                comp_name, map_tuple[0])
        elif map_tuple[1] == "EOS":
            ptuple = CoolPropWrapper._get_eos_property(comp_name, map_tuple[0])
        else:
            raise BurntToast(
                "Unrecognized argument in CoolProp wrapper. Please contact "
                "the IDAES developers with this bug.")

        return ptuple
Beispiel #2
0
    def build_parameters(rblock, config):
        parent = rblock.parent_block()
        units = parent.get_metadata().derived_units

        rbasis = parent.config.reaction_basis
        if rbasis == MaterialFlowBasis.molar:
            r_base = units["amount"]
        elif rbasis == MaterialFlowBasis.mass:
            r_base = units["mass"]
        else:
            raise BurntToast(
                "{} for unexpected reaction basis {}. This should not happen "
                "so please contact the IDAES developers with this bug.".format(
                    rblock.name, rbasis))

        c_form = config.concentration_form
        if c_form is None:
            raise ConfigurationError(
                "{} concentration_form configuration argument was not set. "
                "Please ensure that this argument is included in your "
                "configuration dict.".format(rblock.name))
        elif (c_form == ConcentrationForm.moleFraction
              or c_form == ConcentrationForm.massFraction):
            r_units = r_base * units["volume"]**-1 * units["time"]**-1
        else:
            order = 0
            for p, j in parent.config.property_package._phase_component_set:
                order += -rblock.reaction_order[p, j].value

            if (c_form == ConcentrationForm.molarity
                    or c_form == ConcentrationForm.activity):
                c_units = units["density_mole"]
            elif c_form == ConcentrationForm.molality:
                c_units = units["amount"] * units["mass"]**-1
            elif c_form == ConcentrationForm.partialPressure:
                c_units = units["pressure"]
            else:
                raise BurntToast(
                    "{} get_concentration_term received unrecognised "
                    "ConcentrationForm ({}). This should not happen - please "
                    "contact the IDAES developers with this bug.".format(
                        rblock.name, c_form))

            r_units = (r_base * units["length"]**-3 * units["time"]**-1 *
                       c_units**order)

        rblock.arrhenius_const = Var(
            doc="Arrhenius constant (pre-exponential factor)", units=r_units)
        set_param_value(rblock,
                        param="arrhenius_const",
                        units=r_units,
                        config=config)

        rblock.energy_activation = Var(doc="Activation energy",
                                       units=units["energy_mole"])

        set_param_value(rblock,
                        param="energy_activation",
                        units=units["energy_mole"],
                        config=config)
Beispiel #3
0
    def log_fug_phase_comp_Tdew(blk, p, j, pp):
        pobj = blk.params.get_phase(p)
        ctype = pobj._cubic_type
        cname = pobj.config.equation_of_state_options["type"].name

        if pobj.is_liquid_phase():
            x = blk._mole_frac_tdew
            xidx = pp
        elif pobj.is_vapor_phase():
            x = blk.mole_frac_comp
            xidx = ()
        else:
            raise BurntToast("{} non-vapor or liquid phase called for bubble "
                             "temperature calculation. This should never "
                             "happen, so please contact the IDAES developers "
                             "with this bug.".format(blk.name))

        def a(k):
            cobj = blk.params.get_component(k)
            fw = getattr(blk, cname + "_fw")[k]
            return (
                EoS_param[ctype]['omegaA'] *
                ((Cubic.gas_constant(blk) * cobj.temperature_crit)**2 /
                 cobj.pressure_crit) *
                ((1 + fw *
                  (1 - sqrt(blk.temperature_dew[pp] / cobj.temperature_crit)))
                 **2))

        kappa = getattr(blk.params, cname + "_kappa")
        am = sum(
            sum(x[xidx, i] * x[xidx, j] * sqrt(a(i) * a(j)) * (1 - kappa[i, j])
                for j in blk.component_list) for i in blk.component_list)

        b = getattr(blk, cname + "_b")
        bm = sum(x[xidx, i] * b[i] for i in blk.component_list)

        A = am * blk.pressure / (Cubic.gas_constant(blk) *
                                 blk.temperature_dew[pp])**2
        B = bm * blk.pressure / (Cubic.gas_constant(blk) *
                                 blk.temperature_dew[pp])

        delta = (2 * sqrt(a(j)) / am * sum(x[xidx, i] * sqrt(a(i)) *
                                           (1 - kappa[j, i])
                                           for i in blk.component_list))

        f = getattr(blk, "_" + cname + "_ext_func_param")
        if pobj.is_vapor_phase():
            proc = getattr(blk, "_" + cname + "_proc_Z_vap")
        elif pobj.is_liquid_phase():
            proc = getattr(blk, "_" + cname + "_proc_Z_liq")

        Z = proc(f, A, B)

        if pobj.is_vapor_phase():
            mole_frac = blk.mole_frac_comp[j]
        else:
            mole_frac = blk._mole_frac_tdew[pp[0], pp[1], j]

        return (_log_fug_coeff_method(A, b[j], bm, B, delta, Z, ctype) +
                log(mole_frac) + log(blk.pressure / blk.pressure._units))
    def cost_pump(blk, pump_type=PumpType.high_pressure):
        """
        Pump costing method

        TODO: describe equations

        Args:
            pump_type - PumpType Enum indicating pump type,
                        default = pumptype.high_pressure
        """
        if pump_type not in PumpType:
            raise ConfigurationError(
                f"{blk.unit_model.name} received invalid argument for pump_type:"
                f" {pump_type}. Argument must be a member of the PumpType Enum.")

        if pump_type == PumpType.high_pressure:
            WaterTAPCostingData.cost_high_pressure_pump(blk)
        elif pump_type == PumpType.low_pressure:
            WaterTAPCostingData.cost_low_pressure_pump(blk)
        elif pump_type == PumpType.pressure_exchanger:
            WaterTAPCostingData.cost_pressure_exchanger_pump(blk)
        elif pump_type == PumpType.energy_recovery_device:
            WaterTAPCostingData.cost_energy_recovery_device_pump(blk)
        else:
            raise BurntToast(f"Unrecognized pump_type: {pump_type}")
Beispiel #5
0
def get_concentration_term(blk, r_idx):
    # conc_form = rblock.config.concentration_form
    try:
        conc_form = blk.params.config.rate_reactions[r_idx].concentration_form
    except KeyError:
        conc_form = blk.params.config.equilibrium_reactions[
            r_idx].concentration_form

    if conc_form is None:
        raise ConfigurationError(
            "{} concentration_form configuration argument was not set. "
            "Please ensure that this argument is included in your "
            "configuration dict.".format(blk.name))
    elif conc_form == ConcentrationForm.molarity:
        conc_term = getattr(blk.state_ref, "conc_mole_phase_comp")
    elif conc_form == ConcentrationForm.activity:
        conc_term = getattr(blk.state_ref, "act_phase_comp")
    elif conc_form == ConcentrationForm.molality:
        conc_term = getattr(blk.state_ref, "molality_phase_comp")
    elif conc_form == ConcentrationForm.moleFraction:
        conc_term = getattr(blk.state_ref, "mole_frac_phase_comp")
    elif conc_form == ConcentrationForm.massFraction:
        conc_term = getattr(blk.state_ref, "mass_frac_phase_comp")
    elif conc_form == ConcentrationForm.partialPressure:
        conc_term = (getattr(blk.state_ref, "mole_frac_phase_comp") *
                     getattr(blk.state_ref, "pressure"))
    else:
        raise BurntToast(
            "{} get_concentration_term received unrecognised "
            "ConcentrationForm ({}). This should not happen - please contact "
            "the IDAES developers with this bug.".format(blk.name, conc_form))

    return conc_term
Beispiel #6
0
    def get_mixed_state_block(self):
        """
        Validates StateBlock provided in user arguments for mixed stream.

        Returns:
            The user-provided StateBlock or an Exception
        """
        # Sanity check to make sure method is not called when arg missing
        if self.config.mixed_state_block is None:
            raise BurntToast(
                "{} get_mixed_state_block method called when "
                "mixed_state_block argument is None. This should "
                "not happen.".format(self.name)
            )

        # Check that the user-provided StateBlock uses the same prop pack
        if (
            self.config.mixed_state_block[
                self.flowsheet().config.time.first()
            ].config.parameters
            != self.config.property_package
        ):
            raise ConfigurationError(
                "{} StateBlock provided in mixed_state_block argument "
                "does not come from the same property package as "
                "provided in the property_package argument. All "
                "StateBlocks within a Mixer must use the same "
                "property package.".format(self.name)
            )

        return self.config.mixed_state_block
Beispiel #7
0
                        def e_rule(b, t, j):
                            try:
                                mfp = mb[t].component("{0}_phase{1}".format(
                                    l_name[:-5], s[-5:]))
                            except AttributeError:
                                raise AttributeError(
                                    "{} Cannot use ideal splitting with this "
                                    "property package. Package uses indexed "
                                    "port member {} which does not have the "
                                    "correct indexing sets, and an equivalent "
                                    "variable with correct indexing sets is "
                                    "not available.".format(self.name, s))

                            for p in self.config.property_package.phase_list:
                                if self.config.split_basis == \
                                        SplittingType.phaseFlow:
                                    s_check = split_map[p]
                                elif self.config.split_basis == \
                                        SplittingType.phaseComponentFlow:
                                    s_check = split_map[p, j]
                                else:
                                    raise BurntToast(
                                        "{} This should not happen. Please"
                                        " report this bug to the IDAES "
                                        "developers.".format(self.name))

                                if s_check == o:
                                    return mfp[p, j]
                            # else:
                            return self.eps
Beispiel #8
0
def log_henry_pressure(b, p, j, T=None):
    henry_def = b.params.get_component(j).config.henry_component[p]

    # TODO: Should use a log henry var/expression
    if T is None:
        henry = b.henry[p, j]
    else:
        henry = henry_def["method"].return_expression(b, p, j, T)

    # Need to get the appropriate concentration term
    # TODO: Add support for true and apparent bases

    h_conc = get_henry_concentration_term(b, henry_def, log=True)[p, j]

    if henry_def["type"].value <= 50:
        # H = c/P type
        log_h_press = h_conc - log(henry)
    elif henry_def["type"].value <= 100:
        # K = P/c type
        log_h_press = h_conc + log(henry)
    else:
        raise BurntToast(
            f"Unrecognized value for HenryType Enum {henry_def['type']}.")

    return log_h_press
def calculate_electrolyte_scaling(b):
    if b.params.config.state_components == StateIndex.true:
        _true_species_scaling(b)
    elif b.params.config.state_components == StateIndex.apparent:
        _apparent_species_scaling(b)
    else:
        raise BurntToast("{} - unrecognized value for state_components "
                         "argument - this should never happen. Please "
                         "contact the IDAES developers".format(b.name))
    def _rxn_rate_conv(b, t, x, j, has_rate_reactions):
        """
        Method to determine conversion term for reaction rate terms in material
        balance equations. This method gets the basis of the material flow
        and reaction rate terms and determines the correct conversion factor.
        """
        # If rate reactions are not required, skip the rest and return 1
        if not has_rate_reactions:
            return 1

        if x is None:
            # 0D control volume
            flow_basis = b.properties_out[t].get_material_flow_basis()
            prop = b.properties_out[t]
            rxn_basis = b.reactions[t].get_reaction_rate_basis()
        else:
            # 1D control volume
            flow_basis = b.properties[t, x].get_material_flow_basis()
            prop = b.properties[t, x]
            rxn_basis = b.reactions[t, x].get_reaction_rate_basis()

        # Check for undefined basis
        if flow_basis == MaterialFlowBasis.other:
            raise ConfigurationError(
                "{} contains reaction terms, but the property package "
                "used an undefined basis (MaterialFlowBasis.other). "
                "Rate based reaction terms require the property "
                "package to define the basis of the material flow "
                "terms.".format(b.name))
        if rxn_basis == MaterialFlowBasis.other:
            raise ConfigurationError(
                "{} contains reaction terms, but the reaction package "
                "used an undefined basis (MaterialFlowBasis.other). "
                "Rate based reaction terms require the reaction "
                "package to define the basis of the reaction rate "
                "terms.".format(b.name))

        try:
            if flow_basis == rxn_basis:
                return 1
            elif (flow_basis == MaterialFlowBasis.mass
                  and rxn_basis == MaterialFlowBasis.molar):
                return prop.mw[j]
            elif (flow_basis == MaterialFlowBasis.molar
                  and rxn_basis == MaterialFlowBasis.mass):
                return 1 / prop.mw[j]
            else:
                raise BurntToast(
                    "{} encountered unrecognsied combination of bases "
                    "for reaction rate terms. Please contact the IDAES"
                    " developers with this bug.".format(b.name))
        except AttributeError:
            raise PropertyNotSupportedError(
                "{} property package does not support "
                "molecular weight (mw), which is required for "
                "using property and reaction packages with "
                "different bases.".format(b.name))
Beispiel #11
0
    def build(self):
        """Callable method for Block construction."""
        super(IdealStateBlockData, self).build()

        # Check for valid phase indicator and consistent flags
        if self.config.has_phase_equilibrium and \
                self._params.config.valid_phase in ['Vap', 'Liq']:
            raise ConfigurationError("Inconsistent inputs. Valid phase"
                                     " flag not set to VL for the state"
                                     " block but has_phase_equilibrium"
                                     " is set to True.")

        # Add state variables
        self.flow_mol = Var(initialize=1.0,
                            domain=NonNegativeReals,
                            doc='Component molar flowrate [mol/s]')
        self.mole_frac_comp = Var(self._params.component_list,
                                  bounds=(0, 1),
                                  initialize=1 /
                                  len(self._params.component_list),
                                  doc='Mixture mole fractions [-]')
        self.pressure = Var(initialize=101325,
                            domain=NonNegativeReals,
                            doc='State pressure [Pa]')
        self.temperature = Var(initialize=298.15,
                               domain=NonNegativeReals,
                               doc='State temperature [K]')

        # Add supporting variables
        self.flow_mol_phase = Var(self._params.phase_list,
                                  initialize=0.5,
                                  doc='Phase molar flow rates [mol/s]')

        self.mole_frac_phase_comp = Var(self._params.phase_list,
                                        self._params.component_list,
                                        initialize=1 /
                                        len(self._params.component_list),
                                        bounds=(0, 1),
                                        doc='Phase mole fractions [-]')

        if not self.config.has_phase_equilibrium and \
                self._params.config.valid_phase == "Liq":
            self._make_liq_phase_eq()
        elif not self.config.has_phase_equilibrium and \
                self._params.config.valid_phase == "Vap":
            self._make_vap_phase_eq()
        elif (self.config.has_phase_equilibrium) or \
                (self._params.config.valid_phase ==
                    ('Liq', 'Vap')) or \
                (self._params.config.valid_phase ==
                    ('Vap', 'Liq')):
            self._make_flash_eq()
        else:
            raise BurntToast("{} found unexpected combination of valid_phases "
                             "and has_phase_equilibrium. Please contact the "
                             "IDAES developers with this bug.".format(
                                 self.name))
Beispiel #12
0
 def return_expression(b, pobj, i, j, T):
     if (i, j) in pobj.alpha:
         return pobj.alpha[i, j]
     elif (j, i) in pobj.alpha:
         return pobj.alpha[j, i]
     else:
         raise BurntToast(
             "{} alpha rule encountered unexpected index {}. Please contact"
             "the IDAES Developers with this bug.".format(b.name, (i, j)))
Beispiel #13
0
 def rule_alpha_expr(b, i, j):
     Y = getattr(b, pname+"_Y")
     if ((pname, i) not in b.params.true_phase_component_set or
             (pname, j) not in b.params.true_phase_component_set):
         return Expression.Skip
     elif ((i in molecular_set) and
             (j in molecular_set)):
         # alpha equal user provided parameters
         return alpha_rule(b, pobj, i, j, b.temperature)
     elif (i in b.params.cation_set and j in molecular_set):
         # Eqn 32
         return sum(
             Y[k] *
             alpha_rule(
                 b, pobj, (i+", "+k), j, b.temperature)
             for k in b.params.anion_set)
     elif (j in b.params.cation_set and i in molecular_set):
         # Eqn 32
         return sum(
             Y[k] *
             alpha_rule(
                 b, pobj, (j+", "+k), i, b.temperature)
             for k in b.params.anion_set)
     elif (i in b.params.anion_set and j in molecular_set):
         # Eqn 33
         return sum(
             Y[k] *
             alpha_rule(
                 b, pobj, (k+", "+i), j, b.temperature)
             for k in b.params.cation_set)
     elif (j in b.params.anion_set and i in molecular_set):
         # Eqn 33
         return sum(
             Y[k] *
             alpha_rule(
                 b, pobj, (k+", "+j), i, b.temperature)
             for k in b.params.cation_set)
     elif (i in b.params.cation_set and j in b.params.anion_set):
         # Eqn 34
         return sum(Y[k]*alpha_rule(
                        b, pobj, (i+", "+j), (k+", "+j), b.temperature)
                    for k in b.params.cation_set
                    if i != k)
     elif (i in b.params.anion_set and j in b.params.cation_set):
         # Eqn 35
         return sum(Y[k]*alpha_rule(
                        b, pobj, (j+", "+i), (j+", "+k), b.temperature)
                    for k in b.params.anion_set
                    if i != k)
     elif ((i in b.params.cation_set and j in b.params.cation_set) or
           (i in b.params.anion_set and j in b.params.anion_set)):
         # No like-ion interactions
         return Expression.Skip
     else:
         raise BurntToast(
             "{} eNRTL model encountered unexpected component pair {}."
             .format(b.name, (i, j)))
Beispiel #14
0
    def build_parameters(rblock, config):
        parent = rblock.parent_block()
        units = parent.get_metadata().derived_units

        c_form = config.concentration_form
        if c_form is None:
            raise ConfigurationError(
                "{} concentration_form configuration argument was not set. "
                "Please ensure that this argument is included in your "
                "configuration dict.".format(rblock.name))
        elif (c_form == ConcentrationForm.moleFraction
              or c_form == ConcentrationForm.massFraction
              or c_form == ConcentrationForm.activity):
            e_units = pyunits.dimensionless
        else:
            order = 0

            try:
                # This will work for Reaction Packages
                pc_set = parent.config.property_package._phase_component_set
            except AttributeError:
                # Need to allow for inherent reactions in Property Packages
                if not parent._electrolyte:
                    # In most cases ,should have _phase_component_set
                    pc_set = parent._phase_component_set
                else:
                    # However, for electrolytes need true species set
                    pc_set = parent.true_phase_component_set

            for p, j in pc_set:
                order += rblock.reaction_order[p, j].value

            if c_form == ConcentrationForm.molarity:
                c_units = units["density_mole"]
            elif c_form == ConcentrationForm.molality:
                c_units = units["amount"] * units["mass"]**-1
            elif c_form == ConcentrationForm.partialPressure:
                c_units = units["pressure"]
            else:
                raise BurntToast(
                    "{} get_concentration_term received unrecognised "
                    "ConcentrationForm ({}). This should not happen - please "
                    "contact the IDAES developers with this bug.".format(
                        rblock.name, c_form))

            e_units = c_units**order

        rblock.k_eq_ref = Var(doc="Equilibrium constant at reference state",
                              units=e_units)
        set_param_from_config(rblock, param="k_eq_ref", config=config)

        rblock.T_eq_ref = Var(
            doc="Reference temperature for equilibrium constant",
            units=units["temperature"])
        set_param_from_config(rblock, param="T_eq_ref", config=config)
Beispiel #15
0
 def func_fw(m, j):
     cobj = m.params.get_component(j)
     if ctype == CubicType.PR:
         return 0.37464 + 1.54226*cobj.omega - \
                0.26992*cobj.omega**2
     elif ctype == CubicType.SRK:
         return 0.48 + 1.574*cobj.omega - \
                0.176*cobj.omega**2
     else:
         raise BurntToast(
             "{} received unrecognized cubic type. This should "
             "never happen, so please contact the IDAES developers "
             "with this bug.".format(b.name))
Beispiel #16
0
def henry_units(henry_type, units):
    if henry_type == HenryType.Hcp:
        h_units = units["density_mole"] / units["pressure"]
    elif henry_type == HenryType.Kpc:
        h_units = units["pressure"] / units["density_mole"]
    elif henry_type == HenryType.Hxp:
        h_units = units["pressure"]**-1
    elif henry_type == HenryType.Kpx:
        h_units = units["pressure"]
    else:
        raise BurntToast(f"Unrecognized value for HenryType {henry_type}")

    return h_units
Beispiel #17
0
        def rule_G_expr(b, i, j):
            Y = getattr(b, pname + "_Y")

            def _G_appr(b, pobj, i, j, T):  # Eqn 23
                return exp(-alpha_rule(b, pobj, i, j, T) *
                           tau_rule(b, pobj, i, j, T))

            if ((pname, i) not in b.params.true_phase_component_set
                    or (pname, j) not in b.params.true_phase_component_set):
                return Expression.Skip
            elif ((i in molecular_set) and (j in molecular_set)):
                # G comes directly from parameters
                return _G_appr(b, pobj, i, j, b.temperature)
            elif (i in b.params.cation_set and j in molecular_set):
                # Eqn 38
                return sum(Y[k] *
                           _G_appr(b, pobj, (i + ", " + k), j, b.temperature)
                           for k in b.params.anion_set)
            elif (j in b.params.cation_set and i in molecular_set):
                # Eqn 40
                return sum(Y[k] *
                           _G_appr(b, pobj, (j + ", " + k), i, b.temperature)
                           for k in b.params.anion_set)
            elif (i in b.params.anion_set and j in molecular_set):
                # Eqn 39
                return sum(Y[k] *
                           _G_appr(b, pobj, (k + ", " + i), j, b.temperature)
                           for k in b.params.cation_set)
            elif (j in b.params.anion_set and i in molecular_set):
                # Eqn 41
                return sum(Y[k] *
                           _G_appr(b, pobj, (k + ", " + j), i, b.temperature)
                           for k in b.params.cation_set)
            elif (i in b.params.cation_set and j in b.params.anion_set):
                # Eqn 42
                return sum(Y[k] * _G_appr(b, pobj, (i + ", " + j),
                                          (k + ", " + j), b.temperature)
                           for k in b.params.cation_set if i != k)
            elif (i in b.params.anion_set and j in b.params.cation_set):
                # Eqn 43
                return sum(Y[k] * _G_appr(b, pobj, (j + ", " + i),
                                          (j + ", " + k), b.temperature)
                           for k in b.params.anion_set if i != k)
            elif ((i in b.params.cation_set and j in b.params.cation_set)
                  or (i in b.params.anion_set and j in b.params.anion_set)):
                # No like-ion interactions
                return Expression.Skip
            else:
                raise BurntToast(
                    "{} eNRTL model encountered unexpected component pair {}.".
                    format(b.name, (i, j)))
Beispiel #18
0
    def compress_fact_phase(b, p):
        pobj = b.params.get_phase(p)
        cname = pobj._cubic_type.name
        A = getattr(b, cname + "_A")
        B = getattr(b, cname + "_B")

        expr_write = CubicThermoExpressions(b)
        if pobj.is_vapor_phase():
            return expr_write.z_vap(eos=pobj._cubic_type, A=A[p], B=B[p])
        elif pobj.is_liquid_phase():
            return expr_write.z_liq(eos=pobj._cubic_type, A=A[p], B=B[p])
        raise BurntToast("{} non-vapor or liquid phase called for cubic "
                         "EoS compressability factor. This should never "
                         "happen, so please contact the IDAES developers "
                         "with this bug.".format(b.name))
Beispiel #19
0
 def compress_fact_phase(b, p):
     pobj = b.params.get_phase(p)
     cname = pobj._cubic_type.name
     A = getattr(b, cname + "_A")
     B = getattr(b, cname + "_B")
     f = getattr(b, "_" + cname + "_ext_func_param")
     if pobj.is_vapor_phase():
         proc = getattr(b, "_" + cname + "_proc_Z_vap")
     elif pobj.is_liquid_phase():
         proc = getattr(b, "_" + cname + "_proc_Z_liq")
     else:
         raise BurntToast("{} non-vapor or liquid phase called for cubic "
                          "EoS compressability factor. This should never "
                          "happen, so please contact the IDAES developers "
                          "with this bug.".format(b.name))
     return proc(f, A[p], B[p])
Beispiel #20
0
def get_concentration_term(blk, r_idx, log=False):
    cfg = blk.params.config
    if "rate_reactions" in cfg:
        try:
            conc_form = cfg.rate_reactions[r_idx].concentration_form
        except KeyError:
            conc_form = cfg.equilibrium_reactions[r_idx].concentration_form
        state = blk.state_ref
    else:
        conc_form = cfg.inherent_reactions[r_idx].concentration_form
        state = blk

    if hasattr(state.params, "_electrolyte") and state.params._electrolyte:
        sub = "_true"
    else:
        sub = ""

    if log:
        pre = "log_"
    else:
        pre = ""

    if conc_form is None:
        raise ConfigurationError(
            "{} concentration_form configuration argument was not set. "
            "Please ensure that this argument is included in your "
            "configuration dict.".format(blk.name))
    elif conc_form == ConcentrationForm.molarity:
        conc_term = getattr(state, pre + "conc_mol_phase_comp" + sub)
    elif conc_form == ConcentrationForm.activity:
        conc_term = getattr(state, pre + "act_phase_comp" + sub)
    elif conc_form == ConcentrationForm.molality:
        conc_term = getattr(state, pre + "molality_phase_comp" + sub)
    elif conc_form == ConcentrationForm.moleFraction:
        conc_term = getattr(state, pre + "mole_frac_phase_comp" + sub)
    elif conc_form == ConcentrationForm.massFraction:
        conc_term = getattr(state, pre + "mass_frac_phase_comp" + sub)
    elif conc_form == ConcentrationForm.partialPressure:
        conc_term = (getattr(state, pre + "pressure_phase_comp" + sub))
    else:
        raise BurntToast(
            "{} get_concentration_term received unrecognised "
            "ConcentrationForm ({}). This should not happen - please contact "
            "the IDAES developers with this bug.".format(blk.name, conc_form))

    return conc_term
Beispiel #21
0
    def build_parameters(rblock, config):
        parent = rblock.parent_block()
        units = parent.get_metadata().derived_units

        c_form = config.concentration_form
        if c_form is None:
            raise ConfigurationError(
                "{} concentration_form configuration argument was not set. "
                "Please ensure that this argument is included in your "
                "configuration dict.".format(rblock.name))
        elif (c_form == ConcentrationForm.moleFraction
              or c_form == ConcentrationForm.massFraction):
            e_units = None
        else:
            order = 0
            for p, j in parent.config.property_package._phase_component_set:
                order += rblock.reaction_order[p, j].value

            if (c_form == ConcentrationForm.molarity
                    or c_form == ConcentrationForm.activity):
                c_units = units["density_mole"]
            elif c_form == ConcentrationForm.molality:
                c_units = units["amount"] * units["mass"]**-1
            elif c_form == ConcentrationForm.partialPressure:
                c_units = units["pressure"]
            else:
                raise BurntToast(
                    "{} get_concentration_term received unrecognised "
                    "ConcentrationForm ({}). This should not happen - please "
                    "contact the IDAES developers with this bug.".format(
                        rblock.name, c_form))

            e_units = c_units**order

        rblock.k_eq_ref = Var(doc="Equilibrium constant at reference state",
                              units=e_units)
        set_param_value(rblock, param="k_eq_ref", units=e_units, config=config)

        rblock.T_eq_ref = Var(
            doc="Reference temperature for equilibrium constant",
            units=units["temperature"])
        set_param_value(rblock,
                        param="T_eq_ref",
                        units=units["temperature"],
                        config=config)
Beispiel #22
0
    def log_fug_coeff_phase_comp_Pdew(blk, p, j, pp):
        pobj = blk.params.get_phase(p)
        ctype = pobj._cubic_type
        cname = pobj.config.equation_of_state_options["type"].name

        if pobj.is_liquid_phase():
            x = blk._mole_frac_pdew
            xidx = pp
        elif pobj.is_vapor_phase():
            x = blk.mole_frac_comp
            xidx = ()
        else:
            raise BurntToast("{} non-vapor or liquid phase called for bubble "
                             "temperature calculation. This should never "
                             "happen, so please contact the IDAES developers "
                             "with this bug.".format(blk.name))

        a = getattr(blk, cname + "_a")
        kappa = getattr(blk.params, cname + "_kappa")
        am = sum(
            sum(x[xidx, i] * x[xidx, j] * sqrt(a[i] * a[j]) * (1 - kappa[i, j])
                for j in blk.params.component_list)
            for i in blk.params.component_list)

        b = getattr(blk, cname + "_b")
        bm = sum(x[xidx, i] * b[i] for i in blk.params.component_list)

        A = am * blk.pressure_dew[pp] / (Cubic.gas_constant(blk) *
                                         blk.temperature)**2
        B = bm * blk.pressure_dew[pp] / (Cubic.gas_constant(blk) *
                                         blk.temperature)

        delta = (2 * sqrt(a[j]) / am * sum(x[xidx, i] * sqrt(a[i]) *
                                           (1 - kappa[j, i])
                                           for i in blk.params.component_list))

        f = getattr(blk, "_" + cname + "_ext_func_param")
        if pobj.is_vapor_phase():
            proc = getattr(blk, "_" + cname + "_proc_Z_vap")
        elif pobj.is_liquid_phase():
            proc = getattr(blk, "_" + cname + "_proc_Z_liq")

        Z = proc(f, A, B)

        return _log_fug_coeff_method(A, b[j], bm, B, delta, Z, ctype)
Beispiel #23
0
                    def e_rule(b, t, p, j):
                        if self.config.split_basis == \
                                SplittingType.phaseFlow:
                            s_check = split_map[p]
                        elif self.config.split_basis == \
                                SplittingType.componentFlow:
                            s_check = split_map[j]
                        elif self.config.split_basis == \
                                SplittingType.phaseComponentFlow:
                            s_check = split_map[p, j]
                        else:
                            raise BurntToast(
                                "{} This should not happen. Please"
                                " report this bug to the IDAES "
                                "developers.".format(self.name))

                        if s_check == o:
                            return mb[t].component(l_name)[p, j]
                        else:
                            return self.eps
    def _get_representative_property_block(self):
        try:
            t_ref = self.flowsheet().time.first()
        except AttributeError:
            raise ConfigurationError(
                "{} control volume does not appear to be part of a "
                "flowsheet (could not find a time attribute).".format(
                    self.name))

        try:
            rep_blk = self.properties_out[t_ref]
        except AttributeError:
            try:
                d_ref = self.length_domain.first()
                rep_blk = self.properties[t_ref, d_ref]
            except AttributeError:
                raise BurntToast("{} Something went wrong when trying to find "
                                 "a representative StateBlock. Please contact "
                                 "the IDAES developers with this bug.".format(
                                     self.name))
        return rep_blk
Beispiel #25
0
 def rule_log_gamma_pdh(b, j):
     A = getattr(b, pname+"_A_DH")
     Ix = getattr(b, pname+"_ionic_strength")
     I0 = getattr(b, pname+"_ionic_strength_ref")
     rho = ClosestApproach
     if j in molecular_set:
         # Eqn 69
         # Note typo in original paper. Correct power for I is (3/2)
         return (2*A*Ix**(3/2)/(1+rho*Ix**(1/2)))
     elif j in b.params.ion_set:
         # Eqn 70
         z = abs(cobj(b, j).config.charge)
         return (-A*((2*z**2/rho) *
                     log((1+rho*Ix**0.5)/(1+rho*I0**0.5)) +
                     (z**2*Ix**0.5 - 2*Ix**(3/2)) / (1+rho*Ix**0.5) -
                     (2*Ix*I0**-0.5) / (1+rho*I0**0.5) *
                     ref_state.ndIdn(b, pname, j)))
     else:
         raise BurntToast(
             "{} eNRTL model encountered unexpected component."
             .format(b.name))
Beispiel #26
0
def get_henry_concentration_term(blk, henry_dict, log=False):
    if log:
        pre = "log_"
    else:
        pre = ""

    if hasattr(blk.params, "_electrolyte") and blk.params._electrolyte:
        if henry_dict["basis"] == StateIndex.true:
            sub = "_true"
        else:
            sub = "_apparent"
    else:
        sub = ""

    henry_type = henry_dict["type"]
    if henry_type == HenryType.Hcp or henry_type == HenryType.Kpc:
        conc_type = "conc_mol_phase_comp"
    elif henry_type == HenryType.Hxp or henry_type == HenryType.Kpx:
        conc_type = "mole_frac_phase_comp"
    else:
        raise BurntToast(f"Unrecognized value for HenryType {henry_type}")

    return getattr(blk, pre + conc_type + sub)
    def cost_mixer(blk, mixer_type=MixerType.default):
        """
        Mixer costing method

        TODO: describe equations

        args:
            mixer_type - MixerType Enum indicating mixer type,
                        default = MixerType.default
        """
        if mixer_type not in MixerType:
            raise ConfigurationError(
                f"{blk.unit_model.name} received invalid argument for mixer_type:"
                f" {mixer_type}. Argument must be a member of the MixerType Enum.")

        if mixer_type==MixerType.default:
            WaterTAPCostingData.cost_default_mixer(blk)
        elif mixer_type==MixerType.NaOCl:
            WaterTAPCostingData.cost_naocl_mixer(blk)
        elif mixer_type==MixerType.CaOH2:
            WaterTAPCostingData.cost_caoh2_mixer(blk)
        else:
            raise BurntToast(f"Unrecognized mixer_type: {mixer_type}")
    def cost_reverse_osmosis(blk, ro_type=ROType.standard):
        """
        Reverse osmosis costing method

        TODO: describe equations

        Args:
            ro_type - ROType Enum indicating reverse osmosis type,
                      default = ROType.standard
        """
        # Validate arguments
        if ro_type not in ROType:
            raise ConfigurationError(
                f"{blk.unit_model.name} received invalid argument for ro_type:"
                f" {ro_type}. Argument must be a member of the ROType Enum.")

        if ro_type == ROType.standard:
            membrane_cost = blk.costing_package.reverse_osmosis_membrane_cost
        elif ro_type == ROType.high_pressure:
            membrane_cost = blk.costing_package.reverse_osmosis_high_pressure_membrane_cost
        else:
            raise BurntToast(f"Unrecognized ro_type: {ro_type}")
        cost_membrane(blk, membrane_cost, blk.costing_package.factor_membrane_replacement)
Beispiel #29
0
    def build(self):
        '''
        Callable method for Block construction.
        '''
        # Call super.build() to initialize Block
        # In this case we are replicating the super.build to get around a
        # chicken-and-egg problem
        # The super.build tries to validate units, but they have not been set
        # and cannot be set until the config block is created by super.build
        super(ReactionParameterBlock, self).build()
        self.default_scaling_factor = {}

        # Set base units of measurement
        self.get_metadata().add_default_units(self.config.base_units)

        # TODO: Need way to tie reaction package to a specfic property package
        self._validate_property_parameter_units()
        self._validate_property_parameter_properties()

        # Call configure method to set construction arguments
        self.configure()

        # Build core components
        self._reaction_block_class = GenericReactionBlock

        # Alias associated property package to keep line length down
        ppack = self.config.property_package

        if not hasattr(ppack, "_electrolyte") or not ppack._electrolyte:
            pc_set = ppack._phase_component_set
        elif ppack.config.state_components.name == "true":
            pc_set = ppack.true_phase_component_set
        elif ppack.config.state_components.name == "apparent":
            pc_set = ppack.apparent_phase_component_set
        else:
            raise BurntToast()

        # Construct rate reaction attributes if required
        if len(self.config.rate_reactions) > 0:
            # Construct rate reaction index
            self.rate_reaction_idx = Set(
                initialize=self.config.rate_reactions.keys())

            # Construct rate reaction stoichiometry dict
            self.rate_reaction_stoichiometry = {}
            for r, rxn in self.config.rate_reactions.items():
                for p, j in pc_set:
                    self.rate_reaction_stoichiometry[(r, p, j)] = 0

                if rxn.stoichiometry is None:
                    raise ConfigurationError(
                        "{} rate reaction {} was not provided with a "
                        "stoichiometry configuration argument.".format(
                            self.name, r))
                else:
                    for k, v in rxn.stoichiometry.items():
                        if k[0] not in ppack.phase_list:
                            raise ConfigurationError(
                                "{} stoichiometry for rate reaction {} "
                                "included unrecognised phase {}.".format(
                                    self.name, r, k[0]))
                        if k[1] not in ppack.component_list:
                            raise ConfigurationError(
                                "{} stoichiometry for rate reaction {} "
                                "included unrecognised component {}.".format(
                                    self.name, r, k[1]))
                        self.rate_reaction_stoichiometry[(r, k[0], k[1])] = v

                # Check that a method was provided for the rate form
                if rxn.rate_form is None:
                    _log.debug(
                        "{} rate reaction {} was not provided with a "
                        "rate_form configuration argument. This is suitable "
                        "for processes using stoichiometric reactors, but not "
                        "for those using unit operations which rely on "
                        "reaction rate.".format(self.name, r))

        # Construct equilibrium reaction attributes if required
        if len(self.config.equilibrium_reactions) > 0:
            # Construct equilibrium reaction index
            self.equilibrium_reaction_idx = Set(
                initialize=self.config.equilibrium_reactions.keys())

            # Construct equilibrium reaction stoichiometry dict
            self.equilibrium_reaction_stoichiometry = {}
            for r, rxn in self.config.equilibrium_reactions.items():
                for p, j in pc_set:
                    self.equilibrium_reaction_stoichiometry[(r, p, j)] = 0

                if rxn.stoichiometry is None:
                    raise ConfigurationError(
                        "{} equilibrium reaction {} was not provided with a "
                        "stoichiometry configuration argument.".format(
                            self.name, r))
                else:
                    for k, v in rxn.stoichiometry.items():
                        if k[0] not in ppack.phase_list:
                            raise ConfigurationError(
                                "{} stoichiometry for equilibrium reaction {} "
                                "included unrecognised phase {}.".format(
                                    self.name, r, k[0]))
                        if k[1] not in ppack.component_list:
                            raise ConfigurationError(
                                "{} stoichiometry for equilibrium reaction {} "
                                "included unrecognised component {}.".format(
                                    self.name, r, k[1]))
                        self.equilibrium_reaction_stoichiometry[(r, k[0],
                                                                 k[1])] = v

                # Check that a method was provided for the equilibrium form
                if rxn.equilibrium_form is None:
                    raise ConfigurationError(
                        "{} equilibrium reaction {} was not provided with a "
                        "equilibrium_form configuration argument.".format(
                            self.name, r))

        # Add a master reaction index which includes both types of reactions
        if (len(self.config.rate_reactions) > 0
                and len(self.config.equilibrium_reactions) > 0):
            self.reaction_idx = Set(
                initialize=(self.rate_reaction_idx
                            | self.equilibrium_reaction_idx))
        elif len(self.config.rate_reactions) > 0:
            self.reaction_idx = Set(initialize=self.rate_reaction_idx)
        elif len(self.config.equilibrium_reactions) > 0:
            self.reaction_idx = Set(initialize=self.equilibrium_reaction_idx)
        else:
            raise BurntToast("{} Generic property package failed to construct "
                             "master reaction Set. This should not happen. "
                             "Please contact the IDAES developers with this "
                             "bug".format(self.name))

        # Construct blocks to contain parameters for each reaction
        for r in self.reaction_idx:
            self.add_component("reaction_" + str(r), Block())

        # Build parameters
        if len(self.config.rate_reactions) > 0:
            for r in self.rate_reaction_idx:
                rblock = getattr(self, "reaction_" + r)
                r_config = self.config.rate_reactions[r]

                order_init = {}
                for p, j in pc_set:
                    if "reaction_order" in r_config.parameter_data:
                        try:
                            order_init[p, j] = r_config.parameter_data[
                                "reaction_order"][p, j]
                        except KeyError:
                            order_init[p, j] = 0
                    else:
                        # Assume elementary reaction and use stoichiometry
                        try:
                            if r_config.stoichiometry[p, j] < 0:
                                # These are reactants, but order is -ve stoic
                                order_init[p,
                                           j] = -r_config.stoichiometry[p, j]
                            else:
                                # Anything else is a product, not be included
                                order_init[p, j] = 0
                        except KeyError:
                            order_init[p, j] = 0

                rblock.reaction_order = Var(pc_set,
                                            initialize=order_init,
                                            doc="Reaction order",
                                            units=None)

                for val in self.config.rate_reactions[r].values():
                    try:
                        val.build_parameters(rblock,
                                             self.config.rate_reactions[r])
                    except AttributeError:
                        pass

        if len(self.config.equilibrium_reactions) > 0:
            for r in self.equilibrium_reaction_idx:
                rblock = getattr(self, "reaction_" + r)
                r_config = self.config.equilibrium_reactions[r]

                order_init = {}
                for p, j in pc_set:
                    if "reaction_order" in r_config.parameter_data:
                        try:
                            order_init[p, j] = r_config.parameter_data[
                                "reaction_order"][p, j]
                        except KeyError:
                            order_init[p, j] = 0
                    else:
                        # Assume elementary reaction and use stoichiometry
                        try:
                            # Here we use the stoic. coeff. directly
                            # However, solids should be excluded as they
                            # normally do not appear in the equilibrium
                            # relationship
                            pobj = ppack.get_phase(p)
                            if not pobj.is_solid_phase():
                                order_init[p, j] = r_config.stoichiometry[p, j]
                            else:
                                order_init[p, j] = 0
                        except KeyError:
                            order_init[p, j] = 0

                rblock.reaction_order = Var(pc_set,
                                            initialize=order_init,
                                            doc="Reaction order",
                                            units=None)

                for val in self.config.equilibrium_reactions[r].values():
                    try:
                        val.build_parameters(
                            rblock, self.config.equilibrium_reactions[r])
                    except AttributeError:
                        pass
                    except KeyError as err:
                        # This likely arises from mismatched true and apparent
                        # species sets. Reaction packages must use the same
                        # basis as the associated thermo properties
                        # Raise an exception to inform the user
                        raise PropertyPackageError(
                            "{} KeyError encountered whilst constructing "
                            "reaction parameters. This may be due to "
                            "mismatched state_components between the "
                            "Reaction Package and the associated Physical "
                            "Property Package - Reaction Packages must use the"
                            "same basis (true or apparent species) as the "
                            "Physical Property Package.".format(self.name),
                            err)

        # As a safety check, make sure all Vars in reaction blocks are fixed
        for v in self.component_objects(Var, descend_into=True):
            for i in v:
                if v[i].value is None:
                    raise ConfigurationError(
                        "{} parameter {} was not assigned"
                        " a value. Please check your configuration "
                        "arguments.".format(self.name, v.local_name))
                v[i].fix()

        # Set default scaling factors
        if self.config.default_scaling_factors is not None:
            self.default_scaling_factor.update(
                self.config.default_scaling_factors)
        # Finally, call populate_default_scaling_factors method to fill blanks
        iscale.populate_default_scaling_factors(self)
Beispiel #30
0
    def add_material_mixing_equations(self, inlet_blocks, mixed_block,
                                      mb_type):
        """
        Add material mixing equations.
        """
        pp = self.config.property_package
        # Get phase component list(s)
        pc_set = mixed_block.phase_component_set

        # Get units metadata
        units = pp.get_metadata()

        flow_basis = mixed_block[
            self.flowsheet().time.first()].get_material_flow_basis()
        if flow_basis == MaterialFlowBasis.molar:
            flow_units = units.get_derived_units("flow_mole")
        elif flow_basis == MaterialFlowBasis.mass:
            flow_units = units.get_derived_units("flow_mass")
        else:
            # Let this pass for now with no units
            flow_units = None

        if mb_type == MaterialBalanceType.componentPhase:
            # Create equilibrium generation term and constraints if required
            if self.config.has_phase_equilibrium is True:
                try:
                    self.phase_equilibrium_generation = Var(
                        self.flowsheet().time,
                        pp.phase_equilibrium_idx,
                        domain=Reals,
                        doc="Amount of generation in unit by phase equilibria",
                        units=flow_units)
                except AttributeError:
                    raise PropertyNotSupportedError(
                        "{} Property package does not contain a list of phase "
                        "equilibrium reactions (phase_equilibrium_idx), "
                        "thus does not support phase equilibrium.".format(
                            self.name))

            # Define terms to use in mixing equation
            def phase_equilibrium_term(b, t, p, j):
                if self.config.has_phase_equilibrium:
                    sd = {}
                    for r in pp.phase_equilibrium_idx:
                        if pp.phase_equilibrium_list[r][0] == j:
                            if (pp.phase_equilibrium_list[r][1][0] == p):
                                sd[r] = 1
                            elif (pp.phase_equilibrium_list[r][1][1] == p):
                                sd[r] = -1
                            else:
                                sd[r] = 0
                        else:
                            sd[r] = 0

                    return sum(b.phase_equilibrium_generation[t, r] * sd[r]
                               for r in pp.phase_equilibrium_idx)
                else:
                    return 0

            # Write phase-component balances
            @self.Constraint(
                self.flowsheet().time,
                pc_set,
                doc="Material mixing equations",
            )
            def material_mixing_equations(b, t, p, j):
                return 0 == (
                    sum(inlet_blocks[i][t].get_material_flow_terms(p, j)
                        for i in range(len(inlet_blocks))) -
                    mixed_block[t].get_material_flow_terms(p, j) +
                    phase_equilibrium_term(b, t, p, j))

        elif mb_type == MaterialBalanceType.componentTotal:
            # Write phase-component balances
            @self.Constraint(
                self.flowsheet().time,
                mixed_block.component_list,
                doc="Material mixing equations",
            )
            def material_mixing_equations(b, t, j):
                return 0 == sum(
                    sum(inlet_blocks[i][t].get_material_flow_terms(p, j)
                        for i in range(len(inlet_blocks))) -
                    mixed_block[t].get_material_flow_terms(p, j)
                    for p in mixed_block.phase_list if (p, j) in pc_set)

        elif mb_type == MaterialBalanceType.total:
            # Write phase-component balances
            @self.Constraint(self.flowsheet().time,
                             doc="Material mixing equations")
            def material_mixing_equations(b, t):
                return 0 == sum(
                    sum(
                        sum(inlet_blocks[i][t].get_material_flow_terms(p, j)
                            for i in range(len(inlet_blocks))) -
                        mixed_block[t].get_material_flow_terms(p, j)
                        for j in mixed_block.component_list
                        if (p, j) in pc_set) for p in mixed_block.phase_list)

        elif mb_type == MaterialBalanceType.elementTotal:
            raise ConfigurationError("{} Mixers do not support elemental "
                                     "material balances.".format(self.name))
        elif mb_type == MaterialBalanceType.none:
            pass
        else:
            raise BurntToast(
                "{} Mixer received unrecognised value for "
                "material_balance_type. This should not happen, "
                "please report this bug to the IDAES developers.".format(
                    self.name))