Multiple Phases Example
STEPS
Connect to the database
Create a ‘thermo’ base for Liquid-Vapor problem
Add components and check for ‘phase_equilibrium_form’ arg
Create a ‘thermo’ base for Liquid-Solid problem
Add components for water plus lime (i.e., calcium hydroxide)
Create a ‘reaction’ base and pull in the solubility product reactions
1. Connect to database
[1]:
from watertap.edb import ElectrolyteDB
print("connecting to " + str(ElectrolyteDB.DEFAULT_URL))
db = ElectrolyteDB()
connecting to mongodb://localhost:27017
2. Create ‘thermo’ base for Liquid-Vapor problem
This is the only major step that is different from the previous example. All subsequent steps from this point would be the same. Note here that the “thermo_Liq_Vap_FpcTP” base brings in some additional information that includes: (i) which phases are in equilibrium (‘phase_equilibrium_state’) and (ii) the type of’bubble_dew_method’ used. These arguments in the ‘base’ object will also have a down-stream impact on what information gets pulled in when grabbing components and adding them to the ‘base’.
[2]:
vap_liq_thermo_base = db.get_base("thermo_Liq_Vap_FpcTP")
vap_liq_thermo_base.idaes_config
[2]:
{'phases': {'Liq': {'type': idaes.core.phases.AqueousPhase,
'equation_of_state': idaes.generic_models.properties.core.eos.ideal.Ideal},
'Vap': {'type': idaes.core.phases.VaporPhase,
'equation_of_state': idaes.generic_models.properties.core.eos.ideal.Ideal}},
'state_definition': idaes.generic_models.properties.core.state_definitions.FpcTP.FpcTP,
'state_bounds': {'temperature': (273.15, 300, 650),
'pressure': (50000, 100000, 1000000)},
'pressure_ref': 100000,
'temperature_ref': 300,
'phases_in_equilibrium': [('Vap', 'Liq')],
'phase_equilibrium_state': {('Vap',
'Liq'): idaes.generic_models.properties.core.phase_equil.smooth_VLE.SmoothVLE},
'bubble_dew_method': idaes.generic_models.properties.core.phase_equil.bubble_dew.IdealBubbleDew,
'base_units': {'time': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d730>,
'length': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d700>,
'mass': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d250>,
'amount': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2db80>,
'temperature': <pyomo.core.base.units_container._PyomoUnit at 0x7fc708052880>}}
3. Add components to the Liquid-Vapor problem
In this demo, we will only add ‘H2O’ and ‘CO2’ to our ‘base’ object. Both of these components have valid parameter information in the database to resolve the phase equilibrium between vapor and liquid systems. After the components are added, there is now a ‘phase_equilibrium_form’ key in the IDAES config under each component. This key contains a dictionary defing what phases are in equilibrium and how that equilibrium is resolved (i.e., in this case the’fugacity’ method is used).
NOTE: You can also add other reactions to this system (not shown here).
[3]:
comp_list = ["H2O", "CO2"]
comps = db.get_components(component_names=comp_list)
for comp_obj in comps:
print("Adding " + str(comp_obj.name) + "" )
vap_liq_thermo_base.add(comp_obj)
vap_liq_thermo_base.idaes_config['components']['H2O']['phase_equilibrium_form']
Adding CO2
Adding H2O
[3]:
{('Vap',
'Liq'): idaes.generic_models.properties.core.phase_equil.forms.fugacity}
4. Create ‘thermo’ base for Liquid-Solid problem
Liquid-Solid equilibrium is resolved in IDAES using the ‘reaction’ configuration and defining a ‘solubility product’ reaction in that configuration dictionary. Thus, the ‘thermo’ base for the Liquid-Solid system will look remarkably similar to the Liquid only system. The only difference is that ‘Sol’ is now a valid phase in the ‘phases’ dictionary.
[4]:
sol_liq_thermo_base = db.get_base("thermo_Liq_Sol_FpcTP")
sol_liq_thermo_base.idaes_config
[4]:
{'phases': {'Liq': {'type': idaes.core.phases.AqueousPhase,
'equation_of_state': idaes.generic_models.properties.core.eos.ideal.Ideal},
'Sol': {'type': idaes.core.phases.SolidPhase,
'equation_of_state': idaes.generic_models.properties.core.eos.ideal.Ideal}},
'state_definition': idaes.generic_models.properties.core.state_definitions.FpcTP.FpcTP,
'state_bounds': {'temperature': (273.15, 300, 650),
'pressure': (50000, 100000, 1000000)},
'pressure_ref': 100000,
'temperature_ref': 300,
'base_units': {'time': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d730>,
'length': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d700>,
'mass': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d250>,
'amount': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2db80>,
'temperature': <pyomo.core.base.units_container._PyomoUnit at 0x7fc708052880>}}
5. Add the components for a water + lime system
Adding components is the same as before. In the database, the ‘lime’ component is denoted by ‘Ca[OH]2’.
[5]:
comp_list = ["H2O", "Ca[OH]2", "H_+", "OH_-", "Ca_2+"]
comps = db.get_components(component_names=comp_list)
for comp_obj in comps:
print("Adding " + str(comp_obj.name) + "" )
sol_liq_thermo_base.add(comp_obj)
Adding Ca[OH]2
Adding Ca_2+
Adding H_+
Adding OH_-
Adding H2O
6. Create ‘reaction’ base and add reactions
Solubility product reactions are stored in the database in the same way that acid-base reactions are stored. Thus, to include Liquid-Solid equilibrium constaints to our model, we only need to call the ‘get_reactions’ function to grab all reactions for our system of interest.
As an optional argument, you can pass a ‘phases’ list to the ‘get_reactions’ function. Reactions will be added only if they DO NOT contain an invalid phase in the list. In this example, we state that both “Liq” and “Sol” are valid phases. Thus, it will grab all reactions that contain either phase (and meet the other criteria based on the list of components).
[6]:
react_base = db.get_base("reaction")
phase_list = ["Liq", "Sol"]
react_obj = db.get_reactions(component_names=comp_list, phases=phase_list)
for r in react_obj:
print("Found reaction: " + str(r.name))
react_base.add(r)
react_base.idaes_config
Found reaction: H2O_Kw
Found reaction: CaOH2_Ksp
[6]:
{'base_units': {'time': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d730>,
'length': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d700>,
'mass': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2d250>,
'amount': <pyomo.core.base.units_container._PyomoUnit at 0x7fc62ff2db80>,
'temperature': <pyomo.core.base.units_container._PyomoUnit at 0x7fc708052880>},
'equilibrium_reactions': {'H2O_Kw': {'stoichiometry': {('Liq', 'H2O'): -1,
('Liq', 'H_+'): 1,
('Liq', 'OH_-'): 1},
'heat_of_reaction': idaes.generic_models.properties.core.reactions.dh_rxn.constant_dh_rxn,
'equilibrium_constant': idaes.generic_models.properties.core.reactions.equilibrium_constant.van_t_hoff,
'equilibrium_form': idaes.generic_models.properties.core.reactions.equilibrium_forms.log_power_law_equil,
'concentration_form': <ConcentrationForm.moleFraction: 4>,
'parameter_data': {'dh_rxn_ref': (55.83,
<pyomo.core.expr.numeric_expr.NPV_DivisionExpression at 0x7fc62fc41ca0>),
'ds_rxn_ref': (-80.7,
<pyomo.core.expr.numeric_expr.NPV_DivisionExpression at 0x7fc62fc419a0>),
'k_eq_ref': (3.281873555975635e-18,
<pyomo.core.base.units_container._PyomoUnit at 0x7fc62e3dbdc0>),
'T_eq_ref': (298,
<pyomo.core.base.units_container._PyomoUnit at 0x7fc708052880>),
'reaction_order': {('Liq', 'H2O'): -1,
('Liq', 'H_+'): 1,
('Liq', 'OH_-'): 1}}},
'CaOH2_Ksp': {'stoichiometry': {('Liq', 'Ca_2+'): 1,
('Liq', 'OH_-'): 2,
('Sol', 'Ca[OH]2'): -1},
'heat_of_reaction': idaes.generic_models.properties.core.reactions.dh_rxn.constant_dh_rxn,
'equilibrium_constant': idaes.generic_models.properties.core.reactions.equilibrium_constant.van_t_hoff,
'equilibrium_form': idaes.generic_models.properties.core.reactions.equilibrium_forms.log_solubility_product,
'concentration_form': <ConcentrationForm.moleFraction: 4>,
'parameter_data': {'dh_rxn_ref': (0,
<pyomo.core.expr.numeric_expr.NPV_DivisionExpression at 0x7fc62fc41280>),
'k_eq_ref': (3.26725301e-11,
<pyomo.core.base.units_container._PyomoUnit at 0x7fc62e3dbdc0>),
'T_eq_ref': (298,
<pyomo.core.base.units_container._PyomoUnit at 0x7fc708052880>),
'reaction_order': {('Liq', 'Ca_2+'): 1,
('Liq', 'OH_-'): 2,
('Sol', 'Ca[OH]2'): -1}}}}}
If instead of giving both “Liq” and “Sol” as valid phases we specify just “Liq”, then we will only find the ‘H2O_Kw’ reaction, because ‘CaOH2_Ksp’ contains a “Sol” phase component.
[7]:
react_obj = db.get_reactions(component_names=comp_list, phases=["Liq"])
for r in react_obj:
print("Found reaction: " + str(r.name))
Found reaction: H2O_Kw