Example #1
0
    def test_no_state_bounds_4(self, frame):
        frame.params.config.state_bounds = {
            "test_state": (1, 2, 3, pyunits.km)}
        bounds, value = get_bounds_from_config(frame, "test_state", pyunits.m)

        assert bounds == (1000, 3000)
        assert value == 2000
Example #2
0
    def test_no_state_bounds_3(self, frame):
        frame.params.config.state_bounds = {
            "test_state": (1, 2, 3)}
        bounds, value = get_bounds_from_config(frame, "test_state", "bar")

        assert bounds == (1, 3)
        assert value == 2
Example #3
0
def define_state(b):
    # FTPx formulation always requires a flash, so set flag to True
    # TODO: should have some checking to make sure developers implement this properly
    b.always_flash = True

    units = b.params.get_metadata().derived_units

    # Check that only necessary state_bounds are defined
    expected_keys = ["flow_mol", "temperature", "pressure"]
    if (b.params.config.state_bounds is not None
            and any(b.params.config.state_bounds.keys()) not in expected_keys):
        for k in b.params.config.state_bounds.keys():
            if "mole_frac" in k:
                _log.warning("{} - found state_bounds argument for {}."
                             " Mole fraction bounds are set automatically and "
                             "this argument will be ignored.".format(
                                 b.name, k))
            elif k not in expected_keys:
                raise ConfigurationError(
                    "{} - found unexpected state_bounds key {}. Please ensure "
                    "bounds are provided only for expected state variables "
                    "and that you have typed the variable names correctly.".
                    format(b.name, k))

    # Get bounds and initial values from config args
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol",
                                              units["flow_mole"])
    t_bounds, t_init = get_bounds_from_config(b, "temperature",
                                              units["temperature"])
    p_bounds, p_init = get_bounds_from_config(b, "pressure", units["pressure"])

    # Add state variables
    b.flow_mol = Var(initialize=f_init,
                     domain=NonNegativeReals,
                     bounds=f_bounds,
                     doc=' Total molar flowrate',
                     units=units["flow_mole"])
    b.mole_frac_comp = Var(b.component_list,
                           bounds=(0, None),
                           initialize=1 / len(b.component_list),
                           doc='Mixture mole fractions',
                           units=None)
    b.pressure = Var(initialize=p_init,
                     domain=NonNegativeReals,
                     bounds=p_bounds,
                     doc='State pressure',
                     units=units["pressure"])
    b.temperature = Var(initialize=t_init,
                        domain=NonNegativeReals,
                        bounds=t_bounds,
                        doc='State temperature',
                        units=units["temperature"])

    # Add supporting variables
    if f_init is None:
        fp_init = None
    else:
        fp_init = f_init / len(b.phase_list)

    b.flow_mol_phase = Var(b.phase_list,
                           initialize=fp_init,
                           domain=NonNegativeReals,
                           bounds=f_bounds,
                           doc='Phase molar flow rates',
                           units=units["flow_mole"])

    b.mole_frac_phase_comp = Var(b.phase_component_set,
                                 initialize=1 / len(b.component_list),
                                 bounds=(0, None),
                                 doc='Phase mole fractions',
                                 units=None)

    def Fpc_expr(b, p, j):
        return b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, j]

    b.flow_mol_phase_comp = Expression(b.phase_component_set,
                                       rule=Fpc_expr,
                                       doc='Phase-component molar flowrates')

    b.phase_frac = Var(b.phase_list,
                       initialize=1 / len(b.phase_list),
                       bounds=(0, None),
                       doc='Phase fractions',
                       units=None)

    # Add electrolye state vars if required
    if b.params._electrolyte:
        define_electrolyte_state(b)

    # Add supporting constraints
    if b.config.defined_state is False:
        # applied at outlet only
        b.sum_mole_frac_out = Constraint(expr=1 == sum(
            b.mole_frac_comp[i] for i in b.component_list))

    if len(b.phase_list) == 1:

        def rule_total_mass_balance(b):
            return b.flow_mol_phase[b.phase_list[1]] == b.flow_mol

        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return b.mole_frac_comp[i] == \
                b.mole_frac_phase_comp[b.phase_list[1], i]

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] == 1

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    elif len(b.phase_list) == 2:
        # For two phase, use Rachford-Rice formulation
        def rule_total_mass_balance(b):
            return sum(b.flow_mol_phase[p] for p in b.phase_list) == \
                b.flow_mol

        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return b.flow_mol * b.mole_frac_comp[i] == sum(
                b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i]
                for p in b.phase_list if (p, i) in b.phase_component_set)

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b):
            return sum(b.mole_frac_phase_comp[b.phase_list[1], i]
                       for i in b.component_list
                       if (b.phase_list[1], i) in b.phase_component_set) -\
                sum(b.mole_frac_phase_comp[b.phase_list[2], i]
                    for i in b.component_list
                    if (b.phase_list[2], i) in b.phase_component_set) == 0

        b.sum_mole_frac = Constraint(rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p]

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    else:
        # Otherwise use a general formulation
        def rule_comp_mass_balance(b, i):
            return b.flow_mol * b.mole_frac_comp[i] == sum(
                b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i]
                for p in b.phase_list if (p, i) in b.phase_component_set)

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b, p):
            return sum(b.mole_frac_phase_comp[p, i] for i in b.component_list
                       if (p, i) in b.phase_component_set) == 1

        b.sum_mole_frac = Constraint(b.phase_list, rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p]

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    # -------------------------------------------------------------------------
    # General Methods
    def get_material_flow_terms_FTPx(p, j):
        """Create material flow terms for control volume."""
        return b.flow_mol_phase_comp[p, j]

    b.get_material_flow_terms = get_material_flow_terms_FTPx

    def get_enthalpy_flow_terms_FTPx(p):
        """Create enthalpy flow terms."""
        # enth_mol_phase probably does not exist when this is created
        # Use try/except to build flow term if not present
        try:
            eflow = b._enthalpy_flow_term
        except AttributeError:

            def rule_eflow(b, p):
                return b.flow_mol_phase[p] * b.enth_mol_phase[p]

            eflow = b._enthalpy_flow_term = Expression(b.phase_list,
                                                       rule=rule_eflow)
        return eflow[p]

    b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx

    def get_material_density_terms_FTPx(p, j):
        """Create material density terms."""
        # dens_mol_phase probably does not exist when this is created
        # Use try/except to build term if not present
        try:
            mdens = b._material_density_term
        except AttributeError:

            def rule_mdens(b, p, j):
                return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j]

            mdens = b._material_density_term = Expression(
                b.phase_component_set, rule=rule_mdens)
        return mdens[p, j]

    b.get_material_density_terms = get_material_density_terms_FTPx

    def get_energy_density_terms_FTPx(p):
        """Create energy density terms."""
        # Density and energy terms probably do not exist when this is created
        # Use try/except to build term if not present
        try:
            edens = b._energy_density_term
        except AttributeError:

            def rule_edens(b, p):
                return b.dens_mol_phase[p] * b.energy_internal_mol_phase[p]

            edens = b._energy_density_term = Expression(b.phase_list,
                                                        rule=rule_edens)
        return edens[p]

    b.get_energy_density_terms = get_energy_density_terms_FTPx

    def default_material_balance_type_FTPx():
        return MaterialBalanceType.componentTotal

    b.default_material_balance_type = default_material_balance_type_FTPx

    def default_energy_balance_type_FTPx():
        return EnergyBalanceType.enthalpyTotal

    b.default_energy_balance_type = default_energy_balance_type_FTPx

    def get_material_flow_basis_FTPx():
        return MaterialFlowBasis.molar

    b.get_material_flow_basis = get_material_flow_basis_FTPx

    def define_state_vars_FTPx():
        """Define state vars."""
        return {
            "flow_mol": b.flow_mol,
            "mole_frac_comp": b.mole_frac_comp,
            "temperature": b.temperature,
            "pressure": b.pressure
        }

    b.define_state_vars = define_state_vars_FTPx

    def define_display_vars_FTPx():
        """Define display vars."""
        return {
            "Total Molar Flowrate": b.flow_mol,
            "Total Mole Fraction": b.mole_frac_comp,
            "Temperature": b.temperature,
            "Pressure": b.pressure
        }

    b.define_display_vars = define_display_vars_FTPx
def _apparent_species_state(b):
    # Create references to base state vars
    add_object_reference(b, "flow_mol_apparent", b.flow_mol)
    b.flow_mol_phase_apparent = Reference(b.flow_mol_phase)
    b.flow_mol_phase_comp_apparent = Reference(b.flow_mol_phase_comp)
    b.mole_frac_phase_comp_apparent = Reference(b.mole_frac_phase_comp)

    # Get units and bounds for true species state
    units = b.params.get_metadata().derived_units
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol",
                                              units["flow_mole"])

    # Create true species state vars
    b.flow_mol_phase_comp_true = Var(
        b.params.true_phase_component_set,
        initialize=f_init,
        domain=NonNegativeReals,
        bounds=f_bounds,
        doc="Phase-component molar flowrates of true species",
        units=units["flow_mole"])

    b.mole_frac_phase_comp_true = Var(
        b.params.true_phase_component_set,
        initialize=1 / len(b.params.true_species_set),
        bounds=(1e-20, 1.001),
        doc="Phase-component molar fractions of true species",
        units=pyunits.dimensionless)

    # Check for inherent reactions and add apparent extent terms if required
    if b.has_inherent_reactions:
        b.apparent_inherent_reaction_extent = Var(
            b.params.inherent_reaction_idx,
            initialize=0,
            units=units["flow_mole"],
            doc="Apparent extent of inherent reactions")

    def appr_to_true_species(b, p, j):
        pobj = b.params.get_phase(p)
        cobj = b.params.get_component(j)
        if pobj.is_aqueous_phase:
            if isinstance(cobj, IonData):
                e = 0
                for a in b.params._apparent_set:
                    aobj = b.params.get_component(a)
                    if j in aobj.config.dissociation_species:
                        e += (aobj.config.dissociation_species[j] *
                              b.flow_mol_phase_comp_apparent[p, a])
            else:
                e = b.flow_mol_phase_comp_apparent[p, j]

            # Next, check for inherent reactions
            if b.has_inherent_reactions:
                for r in b.params.inherent_reaction_idx:
                    # Get stoichiometric coeffiicient for inherent reactions
                    gamma = b.params.inherent_reaction_stoichiometry[r, p, j]

                    if gamma != 0:
                        e += gamma * b.apparent_inherent_reaction_extent[r]

            return b.flow_mol_phase_comp_true[p, j] == e
        else:
            return b.flow_mol_phase_comp_apparent[p, j] == \
                b.flow_mol_phase_comp_true[p, j]

    b.appr_to_true_species = Constraint(
        b.params.true_phase_component_set,
        rule=appr_to_true_species,
        doc="Apparent to true species conversion")

    def true_species_mole_fractions(b, p, j):
        return (b.mole_frac_phase_comp_true[p, j] *
                sum(b.flow_mol_phase_comp_true[p, k]
                    for k in b.params.true_species_set
                    if (p, k) in b.params.true_phase_component_set) ==
                b.flow_mol_phase_comp_true[p, j])

    b.true_mole_frac_constraint = Constraint(
        b.params.true_phase_component_set,
        rule=true_species_mole_fractions,
        doc="Calculation of true species mole fractions")
def _true_species_state(b):
    # Create references to base state vars
    add_object_reference(b, "flow_mol_true", b.flow_mol)
    b.flow_mol_phase_true = Reference(b.flow_mol_phase)
    b.flow_mol_phase_comp_true = Reference(b.flow_mol_phase_comp)
    b.mole_frac_phase_comp_true = Reference(b.mole_frac_phase_comp)

    # Get units and bounds for apparent species state
    units = b.params.get_metadata().derived_units
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol",
                                              units["flow_mole"])

    # Create apparent species state vars
    b.flow_mol_phase_comp_apparent = Var(
        b.params.apparent_phase_component_set,
        initialize=f_init,
        domain=NonNegativeReals,
        bounds=f_bounds,
        doc="Phase-component molar flowrates of apparent species",
        units=units["flow_mole"])

    b.mole_frac_phase_comp_apparent = Var(
        b.params.apparent_phase_component_set,
        initialize=1 / len(b.params.apparent_species_set),
        bounds=(1e-20, 1.001),
        doc="Phase-component molar fractions of apparent species",
        units=pyunits.dimensionless)

    def true_to_appr_species(b, p, j):
        pobj = b.params.get_phase(p)
        cobj = b.params.get_component(j)
        if pobj.is_aqueous_phase():
            total_charge = sum(b.flow_mol_phase_comp_true[p, c] *
                               b.params.get_component(c).config.charge
                               for c in b.params.cation_set)

            if isinstance(cobj, ApparentData):
                # Need to recompose composition
                ions = list(cobj.config.dissociation_species.keys())

                e = (
                    b.flow_mol_phase_comp_true[p, ions[0]] *
                    abs(b.params.get_component(ions[0]).config.charge) *
                    b.flow_mol_phase_comp_true[p, ions[1]] /
                    (total_charge * cobj.config.dissociation_species[ions[1]]))
            elif j == "H2O":
                # Special case for water
                # Need to generalise to cover other cases with weak acids
                e = b.flow_mol_phase_comp_true[p, j]

                if "H+" in b.params.cation_set and "OH-" in b.params.anion_set:
                    e += (b.flow_mol_phase_comp_true[p, "H+"] *
                          b.flow_mol_phase_comp_true[p, "OH-"] / total_charge)
                elif ("H3O+" in b.params.cation_set
                      and "OH-" in b.params.anion_set):
                    # Assume H3O+
                    e += (2 * b.flow_mol_phase_comp_true[p, "H3O+"] *
                          b.flow_mol_phase_comp_true[p, "OH-"] / total_charge)
            else:
                e = b.flow_mol_phase_comp_true[p, j]

            return b.flow_mol_phase_comp_apparent[p, j] == e
        else:
            return b.flow_mol_phase_comp_apparent[p, j] == \
                b.flow_mol_phase_comp_true[p, j]

    b.true_to_appr_species = Constraint(
        b.params.apparent_phase_component_set,
        rule=true_to_appr_species,
        doc="True to apparent species conversion")

    def apparent_species_mole_fractions(b, p, j):
        return (b.mole_frac_phase_comp_apparent[p, j] *
                sum(b.flow_mol_phase_comp_apparent[p, k]
                    for k in b.params.apparent_species_set
                    if (p, k) in b.params.apparent_phase_component_set) ==
                b.flow_mol_phase_comp_apparent[p, j])

    b.appr_mole_frac_constraint = Constraint(
        b.params.apparent_phase_component_set,
        rule=apparent_species_mole_fractions,
        doc="Calculation of apparent species mole fractions")
Example #6
0
def define_state(b):
    # FcTP formulation always requires a flash, so set flag to True
    # TODO: should have some checking to make sure developers implement this properly
    b.always_flash = True

    # Check that only necessary state_bounds are defined
    expected_keys = ["flow_mol_comp", "temperature", "pressure"]
    if (b.params.config.state_bounds is not None
            and any(b.params.config.state_bounds.keys()) not in expected_keys):
        for k in b.params.config.state_bounds.keys():
            if k not in expected_keys:
                raise ConfigurationError(
                    "{} - found unexpected state_bounds key {}. Please ensure "
                    "bounds are provided only for expected state variables "
                    "and that you have typed the variable names correctly.".
                    format(b.name, k))

    units = b.params.get_metadata().derived_units
    # Get bounds and initial values from config args
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol_comp",
                                              units["flow_mole"])
    t_bounds, t_init = get_bounds_from_config(b, "temperature",
                                              units["temperature"])
    p_bounds, p_init = get_bounds_from_config(b, "pressure", units["pressure"])

    # Add state variables
    b.flow_mol_comp = Var(b.component_list,
                          initialize=f_init,
                          domain=NonNegativeReals,
                          bounds=f_bounds,
                          doc=' Component molar flowrate',
                          units=units["flow_mole"])
    b.pressure = Var(initialize=p_init,
                     domain=NonNegativeReals,
                     bounds=p_bounds,
                     doc='State pressure',
                     units=units["pressure"])
    b.temperature = Var(initialize=t_init,
                        domain=NonNegativeReals,
                        bounds=t_bounds,
                        doc='State temperature',
                        units=units["temperature"])

    # Add supporting variables
    b.flow_mol = Expression(expr=sum(b.flow_mol_comp[j]
                                     for j in b.component_list),
                            doc="Total molar flowrate")

    if f_init is None:
        fp_init = None
    else:
        fp_init = f_init / len(b.phase_list)

    b.flow_mol_phase = Var(b.phase_list,
                           initialize=fp_init,
                           domain=NonNegativeReals,
                           bounds=f_bounds,
                           doc='Phase molar flow rates',
                           units=units["flow_mole"])

    b.mole_frac_comp = Var(b.component_list,
                           bounds=(0, None),
                           initialize=1 / len(b.component_list),
                           doc='Mixture mole fractions',
                           units=None)

    b.mole_frac_phase_comp = Var(b.phase_component_set,
                                 initialize=1 / len(b.component_list),
                                 bounds=(0, None),
                                 doc='Phase mole fractions',
                                 units=None)

    b.phase_frac = Var(b.phase_list,
                       initialize=1 / len(b.phase_list),
                       bounds=(0, None),
                       doc='Phase fractions',
                       units=None)

    # Add supporting constraints
    def rule_mole_frac_comp(b, j):
        if len(b.component_list) > 1:
            return b.flow_mol_comp[j] == b.mole_frac_comp[j] * sum(
                b.flow_mol_comp[k] for k in b.component_list)
        else:
            return b.mole_frac_comp[j] == 1

    b.mole_frac_comp_eq = Constraint(b.component_list,
                                     rule=rule_mole_frac_comp)

    if len(b.phase_list) == 1:

        def rule_total_mass_balance(b):
            return b.flow_mol_phase[b.phase_list[1]] == b.flow_mol

        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return b.mole_frac_comp[i]*1e3 == \
                1e3*b.mole_frac_phase_comp[b.phase_list[1], i]

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] == 1

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    elif len(b.phase_list) == 2:
        # For two phase, use Rachford-Rice formulation
        def rule_total_mass_balance(b):
            return sum(b.flow_mol_phase[p] for p in b.phase_list) == \
                b.flow_mol

        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return b.flow_mol_comp[i] == sum(
                b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i]
                for p in b.phase_list if (p, i) in b.phase_component_set)

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b):
            return 1e3*sum(b.mole_frac_phase_comp[b.phase_list[1], i]
                           for i in b.component_list
                           if (b.phase_list[1], i)
                           in b.phase_component_set) -\
                1e3*sum(b.mole_frac_phase_comp[b.phase_list[2], i]
                        for i in b.component_list
                        if (b.phase_list[2], i)
                        in b.phase_component_set) == 0

        b.sum_mole_frac = Constraint(rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p]

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    else:
        # Otherwise use a general formulation
        def rule_comp_mass_balance(b, i):
            return b.flow_mol_comp[i] == sum(
                b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, i]
                for p in b.phase_list if (p, i) in b.phase_component_set)

        b.component_flow_balances = Constraint(b.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b, p):
            return 1e3 * sum(b.mole_frac_phase_comp[p, i]
                             for i in b.component_list
                             if (p, i) in b.phase_component_set) == 1e3

        b.sum_mole_frac = Constraint(b.phase_list, rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] * b.flow_mol == b.flow_mol_phase[p]

        b.phase_fraction_constraint = Constraint(b.phase_list,
                                                 rule=rule_phase_frac)

    # -------------------------------------------------------------------------
    # General Methods
    def get_material_flow_terms_FTPx(p, j):
        """Create material flow terms for control volume."""
        if j in b.component_list:
            return b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, j]
        else:
            return 0

    b.get_material_flow_terms = get_material_flow_terms_FTPx

    def get_enthalpy_flow_terms_FTPx(p):
        """Create enthalpy flow terms."""
        return b.flow_mol_phase[p] * b.enth_mol_phase[p]

    b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx

    def get_material_density_terms_FTPx(p, j):
        """Create material density terms."""
        if j in b.component_list:
            return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j]
        else:
            return 0

    b.get_material_density_terms = get_material_density_terms_FTPx

    def get_energy_density_terms_FTPx(p):
        """Create energy density terms."""
        return b.dens_mol_phase[p] * b.enth_mol_phase[p]

    b.get_energy_density_terms = get_energy_density_terms_FTPx

    def default_material_balance_type_FTPx():
        return MaterialBalanceType.componentTotal

    b.default_material_balance_type = default_material_balance_type_FTPx

    def default_energy_balance_type_FTPx():
        return EnergyBalanceType.enthalpyTotal

    b.default_energy_balance_type = default_energy_balance_type_FTPx

    def get_material_flow_basis_FTPx():
        return MaterialFlowBasis.molar

    b.get_material_flow_basis = get_material_flow_basis_FTPx

    def define_state_vars_FTPx():
        """Define state vars."""
        return {
            "flow_mol_comp": b.flow_mol_comp,
            "temperature": b.temperature,
            "pressure": b.pressure
        }

    b.define_state_vars = define_state_vars_FTPx

    def define_display_vars_FTPx():
        """Define display vars."""
        return {
            "Molar Flowrate": b.flow_mol_comp,
            "Temperature": b.temperature,
            "Pressure": b.pressure
        }

    b.define_display_vars = define_display_vars_FTPx
Example #7
0
def define_state(b):
    # FpcTP contains full information on the phase equilibrium, so flash
    # calculations re not always needed
    b.always_flash = False

    # Check that only necessary state_bounds are defined
    expected_keys = [
        "flow_mol_phase_comp", "enth_mol", "temperature", "pressure"
    ]
    if (b.params.config.state_bounds is not None
            and any(b.params.config.state_bounds.keys()) not in expected_keys):
        for k in b.params.config.state_bounds.keys():
            if k not in expected_keys:
                raise ConfigurationError(
                    "{} - found unexpected state_bounds key {}. Please ensure "
                    "bounds are provided only for expected state variables "
                    "and that you have typed the variable names correctly.".
                    format(b.name, k))

    units = b.params.get_metadata().derived_units
    # Get bounds and initial values from config args
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol_phase_comp",
                                              units["flow_mole"])
    t_bounds, t_init = get_bounds_from_config(b, "temperature",
                                              units["temperature"])
    p_bounds, p_init = get_bounds_from_config(b, "pressure", units["pressure"])

    # Add state variables
    b.flow_mol_phase_comp = Var(b.phase_component_set,
                                initialize=f_init,
                                domain=NonNegativeReals,
                                bounds=f_bounds,
                                doc='Phase-component molar flowrate',
                                units=units["flow_mole"])
    b.pressure = Var(initialize=p_init,
                     domain=NonNegativeReals,
                     bounds=p_bounds,
                     doc='State pressure',
                     units=units["pressure"])
    b.temperature = Var(initialize=t_init,
                        domain=NonNegativeReals,
                        bounds=t_bounds,
                        doc='State temperature',
                        units=units["temperature"])

    # Add supporting variables
    b.flow_mol = Expression(expr=sum(b.flow_mol_phase_comp[i]
                                     for i in b.phase_component_set),
                            doc="Total molar flowrate")

    def flow_mol_phase(b, p):
        return sum(b.flow_mol_phase_comp[p, j] for j in b.component_list
                   if (p, j) in b.phase_component_set)

    b.flow_mol_phase = Expression(b.phase_list,
                                  rule=flow_mol_phase,
                                  doc='Phase molar flow rates')

    def rule_flow_mol_comp(b, j):
        return sum(b.flow_mol_phase_comp[p, j] for p in b.phase_list
                   if (p, j) in b.phase_component_set)

    b.flow_mol_comp = Expression(b.component_list,
                                 rule=rule_flow_mol_comp,
                                 doc='Component molar flow rates')

    def mole_frac_comp(b, j):
        return (sum(b.flow_mol_phase_comp[p, j]
                    for p in b.phase_list if (p, j) in b.phase_component_set) /
                b.flow_mol)

    b.mole_frac_comp = Expression(b.component_list,
                                  rule=mole_frac_comp,
                                  doc='Mixture mole fractions')

    b.mole_frac_phase_comp = Var(b.phase_component_set,
                                 initialize=1 / len(b.component_list),
                                 doc='Phase mole fractions',
                                 units=None)

    def rule_mole_frac_phase_comp(b, p, j):
        return b.mole_frac_phase_comp[p, j] * b.flow_mol_phase[p] == \
            b.flow_mol_phase_comp[p, j]

    b.mole_frac_phase_comp_eq = Constraint(b.phase_component_set,
                                           rule=rule_mole_frac_phase_comp)

    def rule_phase_frac(b, p):
        if len(b.phase_list) == 1:
            return 1
        else:
            return b.flow_mol_phase[p] / b.flow_mol

    b.phase_frac = Expression(b.phase_list,
                              rule=rule_phase_frac,
                              doc='Phase fractions')

    # Add electrolye state vars if required
    if b.params._electrolyte:
        define_electrolyte_state(b)

    # -------------------------------------------------------------------------
    # General Methods
    def get_material_flow_terms_FpcTP(p, j):
        """Create material flow terms for control volume."""
        return b.flow_mol_phase_comp[p, j]

    b.get_material_flow_terms = get_material_flow_terms_FpcTP

    def get_enthalpy_flow_terms_FpcTP(p):
        """Create enthalpy flow terms."""
        # enth_mol_phase probably does not exist when this is created
        # Use try/except to build flow term if not present
        try:
            eflow = b._enthalpy_flow_term
        except AttributeError:

            def rule_eflow(b, p):
                return b.flow_mol_phase[p] * b.enth_mol_phase[p]

            eflow = b._enthalpy_flow_term = Expression(b.phase_list,
                                                       rule=rule_eflow)
        return eflow[p]

    b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FpcTP

    def get_material_density_terms_FpcTP(p, j):
        """Create material density terms."""
        # dens_mol_phase probably does not exist when this is created
        # Use try/except to build term if not present
        try:
            mdens = b._material_density_term
        except AttributeError:

            def rule_mdens(b, p, j):
                return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j]

            mdens = b._material_density_term = Expression(
                b.phase_component_set, rule=rule_mdens)
        return mdens[p, j]

    b.get_material_density_terms = get_material_density_terms_FpcTP

    def get_energy_density_terms_FpcTP(p):
        """Create energy density terms."""
        # Density and energy terms probably do not exist when this is created
        # Use try/except to build term if not present
        try:
            edens = b._energy_density_term
        except AttributeError:

            def rule_edens(b, p):
                return b.dens_mol_phase[p] * b.energy_internal_mol_phase[p]

            edens = b._energy_density_term = Expression(b.phase_list,
                                                        rule=rule_edens)
        return edens[p]

    b.get_energy_density_terms = get_energy_density_terms_FpcTP

    def default_material_balance_type_FpcTP():
        return MaterialBalanceType.componentTotal

    b.default_material_balance_type = default_material_balance_type_FpcTP

    def default_energy_balance_type_FpcTP():
        return EnergyBalanceType.enthalpyTotal

    b.default_energy_balance_type = default_energy_balance_type_FpcTP

    def get_material_flow_basis_FpcTP():
        return MaterialFlowBasis.molar

    b.get_material_flow_basis = get_material_flow_basis_FpcTP

    def define_state_vars_FpcTP():
        """Define state vars."""
        return {
            "flow_mol_phase_comp": b.flow_mol_phase_comp,
            "temperature": b.temperature,
            "pressure": b.pressure
        }

    b.define_state_vars = define_state_vars_FpcTP

    def define_display_vars_FpcTP():
        """Define display vars."""
        return {
            "Molar Flowrate": b.flow_mol_phase_comp,
            "Temperature": b.temperature,
            "Pressure": b.pressure
        }

    b.define_display_vars = define_display_vars_FpcTP
Example #8
0
def define_state(b):
    # FpcTP contains full information on the phase equilibrium, so flash
    # calculations re not always needed
    b.always_flash = False

    units = b.params.get_metadata().derived_units
    # Get bounds and initial values from config args
    f_bounds, f_init = get_bounds_from_config(b, "flow_mol_phase_comp",
                                              units["flow_mole"])
    t_bounds, t_init = get_bounds_from_config(b, "temperature",
                                              units["temperature"])
    p_bounds, p_init = get_bounds_from_config(b, "pressure", units["pressure"])

    # Add state variables
    b.flow_mol_phase_comp = Var(b.params._phase_component_set,
                                initialize=f_init,
                                domain=NonNegativeReals,
                                bounds=f_bounds,
                                doc='Phase-component molar flowrate',
                                units=units["flow_mole"])
    b.pressure = Var(initialize=p_init,
                     domain=NonNegativeReals,
                     bounds=p_bounds,
                     doc='State pressure',
                     units=units["pressure"])
    b.temperature = Var(initialize=t_init,
                        domain=NonNegativeReals,
                        bounds=t_bounds,
                        doc='State temperature',
                        units=units["temperature"])

    # Add supporting variables
    b.flow_mol = Expression(expr=sum(b.flow_mol_phase_comp[i]
                                     for i in b.params._phase_component_set),
                            doc="Total molar flowrate")

    def flow_mol_phase(b, p):
        return sum(b.flow_mol_phase_comp[p, j] for j in b.params.component_list
                   if (p, j) in b.params._phase_component_set)

    b.flow_mol_phase = Expression(b.params.phase_list,
                                  rule=flow_mol_phase,
                                  doc='Phase molar flow rates')

    def rule_flow_mol_comp(b, j):
        return sum(b.flow_mol_phase_comp[p, j] for p in b.params.phase_list
                   if (p, j) in b.params._phase_component_set)

    b.flow_mol_comp = Expression(b.params.component_list,
                                 rule=rule_flow_mol_comp,
                                 doc='Component molar flow rates')

    def mole_frac_comp(b, j):
        return (sum(b.flow_mol_phase_comp[p, j] for p in b.params.phase_list
                    if (p, j) in b.params._phase_component_set) / b.flow_mol)

    b.mole_frac_comp = Expression(b.params.component_list,
                                  rule=mole_frac_comp,
                                  doc='Mixture mole fractions')

    b.mole_frac_phase_comp = Var(b.params._phase_component_set,
                                 initialize=1 / len(b.params.component_list),
                                 doc='Phase mole fractions',
                                 units=None)

    def rule_mole_frac_phase_comp(b, p, j):
        return b.mole_frac_phase_comp[p, j] * b.flow_mol_phase[p] == \
            b.flow_mol_phase_comp[p, j]

    b.mole_frac_phase_comp_eq = Constraint(b.params._phase_component_set,
                                           rule=rule_mole_frac_phase_comp)

    def rule_phase_frac(b, p):
        if len(b.params.phase_list) == 1:
            return 1
        else:
            return b.flow_mol_phase[p] / b.flow_mol

    b.phase_frac = Expression(b.params.phase_list,
                              rule=rule_phase_frac,
                              doc='Phase fractions')

    # -------------------------------------------------------------------------
    # General Methods
    def get_material_flow_terms_FpcTP(p, j):
        """Create material flow terms for control volume."""
        return b.flow_mol_phase_comp[p, j]

    b.get_material_flow_terms = get_material_flow_terms_FpcTP

    def get_enthalpy_flow_terms_FpcTP(p):
        """Create enthalpy flow terms."""
        return b.flow_mol_phase[p] * b.enth_mol_phase[p]

    b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FpcTP

    def get_material_density_terms_FpcTP(p, j):
        """Create material density terms."""
        if j in b.params.component_list:
            return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j]
        else:
            return 0

    b.get_material_density_terms = get_material_density_terms_FpcTP

    def get_energy_density_terms_FpcTP(p):
        """Create energy density terms."""
        return b.dens_mol_phase[p] * b.enth_mol_phase[p]

    b.get_energy_density_terms = get_energy_density_terms_FpcTP

    def default_material_balance_type_FpcTP():
        return MaterialBalanceType.componentTotal

    b.default_material_balance_type = default_material_balance_type_FpcTP

    def default_energy_balance_type_FpcTP():
        return EnergyBalanceType.enthalpyTotal

    b.default_energy_balance_type = default_energy_balance_type_FpcTP

    def get_material_flow_basis_FpcTP():
        return MaterialFlowBasis.molar

    b.get_material_flow_basis = get_material_flow_basis_FpcTP

    def define_state_vars_FpcTP():
        """Define state vars."""
        return {
            "flow_mol_phase_comp": b.flow_mol_phase_comp,
            "temperature": b.temperature,
            "pressure": b.pressure
        }

    b.define_state_vars = define_state_vars_FpcTP

    def define_display_vars_FpcTP():
        """Define display vars."""
        return {
            "Molar Flowrate": b.flow_mol_phase_comp,
            "Temperature": b.temperature,
            "Pressure": b.pressure
        }

    b.define_display_vars = define_display_vars_FpcTP
Example #9
0
    def test_no_state_bounds(self, frame):
        bounds, value = get_bounds_from_config(frame, "foo", "bar")

        assert bounds == (None, None)
        assert value is None
Example #10
0
def define_state(b):
    # FTPx formulation always requires a flash, so set flag to True
    # TODO: should have some checking to make sure developers implement this properly
    b.always_flash = True

    units = b.params.get_metadata().derived_units
    # Get bounds and initial values from config args
    f_bounds, f_init = get_bounds_from_config(
        b, "flow_mol", units["flow_mole"])
    t_bounds, t_init = get_bounds_from_config(
        b, "temperature", units["temperature"])
    p_bounds, p_init = get_bounds_from_config(
        b, "pressure", units["pressure"])

    # Add state variables
    b.flow_mol = Var(initialize=f_init,
                     domain=NonNegativeReals,
                     bounds=f_bounds,
                     doc=' Total molar flowrate',
                     units=units["flow_mole"])
    b.mole_frac_comp = Var(b.params.component_list,
                           bounds=(0, None),
                           initialize=1 / len(b.params.component_list),
                           doc='Mixture mole fractions',
                           units=None)
    b.pressure = Var(initialize=p_init,
                     domain=NonNegativeReals,
                     bounds=p_bounds,
                     doc='State pressure',
                     units=units["pressure"])
    b.temperature = Var(initialize=t_init,
                        domain=NonNegativeReals,
                        bounds=t_bounds,
                        doc='State temperature',
                        units=units["temperature"])

    # Add supporting variables
    if f_init is None:
        fp_init = None
    else:
        fp_init = f_init / len(b.params.phase_list)

    b.flow_mol_phase = Var(b.params.phase_list,
                           initialize=fp_init,
                           domain=NonNegativeReals,
                           bounds=f_bounds,
                           doc='Phase molar flow rates',
                           units=units["flow_mole"])

    b.mole_frac_phase_comp = Var(
        b.params._phase_component_set,
        initialize=1/len(b.params.component_list),
        bounds=(0, None),
        doc='Phase mole fractions',
        units=None)

    b.phase_frac = Var(
        b.params.phase_list,
        initialize=1/len(b.params.phase_list),
        bounds=(0, None),
        doc='Phase fractions',
        units=None)

    # Add supporting constraints
    if b.config.defined_state is False:
        # applied at outlet only
        b.sum_mole_frac_out = Constraint(
            expr=1e3 == 1e3*sum(b.mole_frac_comp[i]
                                for i in b.params.component_list))

    if len(b.params.phase_list) == 1:
        def rule_total_mass_balance(b):
            return b.flow_mol_phase[b.params.phase_list[1]] == b.flow_mol
        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return 1e3*b.mole_frac_comp[i] == \
                1e3*b.mole_frac_phase_comp[b.params.phase_list[1], i]
        b.component_flow_balances = Constraint(b.params.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_phase_frac(b, p):
            return b.phase_frac[p] == 1
        b.phase_fraction_constraint = Constraint(b.params.phase_list,
                                                 rule=rule_phase_frac)

    elif len(b.params.phase_list) == 2:
        # For two phase, use Rachford-Rice formulation
        def rule_total_mass_balance(b):
            return sum(b.flow_mol_phase[p] for p in b.params.phase_list) == \
                b.flow_mol
        b.total_flow_balance = Constraint(rule=rule_total_mass_balance)

        def rule_comp_mass_balance(b, i):
            return b.flow_mol*b.mole_frac_comp[i] == sum(
                b.flow_mol_phase[p]*b.mole_frac_phase_comp[p, i]
                for p in b.params.phase_list
                if (p, i) in b.params._phase_component_set)
        b.component_flow_balances = Constraint(b.params.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b):
            return 1e3*sum(b.mole_frac_phase_comp[b.params.phase_list[1], i]
                           for i in b.params.component_list
                           if (b.params.phase_list[1], i)
                           in b.params._phase_component_set) -\
                1e3*sum(b.mole_frac_phase_comp[b.params.phase_list[2], i]
                        for i in b.params.component_list
                        if (b.params.phase_list[2], i)
                        in b.params._phase_component_set) == 0
        b.sum_mole_frac = Constraint(rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p]*b.flow_mol == b.flow_mol_phase[p]
        b.phase_fraction_constraint = Constraint(b.params.phase_list,
                                                 rule=rule_phase_frac)

    else:
        # Otherwise use a general formulation
        def rule_comp_mass_balance(b, i):
            return b.flow_mol*b.mole_frac_comp[i] == sum(
                b.flow_mol_phase[p]*b.mole_frac_phase_comp[p, i]
                for p in b.params.phase_list
                if (p, i) in b.params._phase_component_set)
        b.component_flow_balances = Constraint(b.params.component_list,
                                               rule=rule_comp_mass_balance)

        def rule_mole_frac(b, p):
            return 1e3*sum(b.mole_frac_phase_comp[p, i]
                           for i in b.params.component_list
                           if (p, i) in b.params._phase_component_set) == 1e3
        b.sum_mole_frac = Constraint(b.params.phase_list,
                                     rule=rule_mole_frac)

        def rule_phase_frac(b, p):
            return b.phase_frac[p]*b.flow_mol == b.flow_mol_phase[p]
        b.phase_fraction_constraint = Constraint(b.params.phase_list,
                                                 rule=rule_phase_frac)

    # -------------------------------------------------------------------------
    # General Methods
    def get_material_flow_terms_FTPx(p, j):
        """Create material flow terms for control volume."""
        if j in b.params.component_list:
            return b.flow_mol_phase[p] * b.mole_frac_phase_comp[p, j]
        else:
            return 0
    b.get_material_flow_terms = get_material_flow_terms_FTPx

    def get_enthalpy_flow_terms_FTPx(p):
        """Create enthalpy flow terms."""
        return b.flow_mol_phase[p] * b.enth_mol_phase[p]
    b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx

    def get_material_density_terms_FTPx(p, j):
        """Create material density terms."""
        if j in b.params.component_list:
            return b.dens_mol_phase[p] * b.mole_frac_phase_comp[p, j]
        else:
            return 0
    b.get_material_density_terms = get_material_density_terms_FTPx

    def get_energy_density_terms_FTPx(p):
        """Create energy density terms."""
        return b.dens_mol_phase[p] * b.enth_mol_phase[p]
    b.get_energy_density_terms = get_energy_density_terms_FTPx

    def default_material_balance_type_FTPx():
        return MaterialBalanceType.componentTotal
    b.default_material_balance_type = default_material_balance_type_FTPx

    def default_energy_balance_type_FTPx():
        return EnergyBalanceType.enthalpyTotal
    b.default_energy_balance_type = default_energy_balance_type_FTPx

    def get_material_flow_basis_FTPx():
        return MaterialFlowBasis.molar
    b.get_material_flow_basis = get_material_flow_basis_FTPx

    def define_state_vars_FTPx():
        """Define state vars."""
        return {"flow_mol": b.flow_mol,
                "mole_frac_comp": b.mole_frac_comp,
                "temperature": b.temperature,
                "pressure": b.pressure}
    b.define_state_vars = define_state_vars_FTPx

    def define_display_vars_FTPx():
        """Define display vars."""
        return {"Total Molar Flowrate": b.flow_mol,
                "Total Mole Fraction": b.mole_frac_comp,
                "Temperature": b.temperature,
                "Pressure": b.pressure}
    b.define_display_vars = define_display_vars_FTPx