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
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
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")
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
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
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
def test_no_state_bounds(self, frame): bounds, value = get_bounds_from_config(frame, "foo", "bar") assert bounds == (None, None) assert value is None
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