Multiple Phases Example

STEPS

  1. Connect to the database

  2. Create a ‘thermo’ base for Liquid-Vapor problem

  3. Add components and check for ‘phase_equilibrium_form’ arg

  4. Create a ‘thermo’ base for Liquid-Solid problem

  5. Add components for water plus lime (i.e., calcium hydroxide)

  6. 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.base.phases.AqueousPhase,
   'equation_of_state': idaes.models.properties.modular_properties.eos.ideal.Ideal},
  'Vap': {'type': idaes.core.base.phases.VaporPhase,
   'equation_of_state': idaes.models.properties.modular_properties.eos.ideal.Ideal}},
 'state_definition': idaes.models.properties.modular_properties.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.models.properties.modular_properties.phase_equil.smooth_VLE.SmoothVLE},
 'bubble_dew_method': idaes.models.properties.modular_properties.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.models.properties.modular_properties.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.base.phases.AqueousPhase,
   'equation_of_state': idaes.models.properties.modular_properties.eos.ideal.Ideal},
  'Sol': {'type': idaes.core.base.phases.SolidPhase,
   'equation_of_state': idaes.models.properties.modular_properties.eos.ideal.Ideal}},
 'state_definition': idaes.models.properties.modular_properties.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.models.properties.modular_properties.reactions.dh_rxn.constant_dh_rxn,
   'equilibrium_constant': idaes.models.properties.modular_properties.reactions.equilibrium_constant.van_t_hoff,
   'equilibrium_form': idaes.models.properties.modular_properties.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.models.properties.modular_properties.reactions.dh_rxn.constant_dh_rxn,
   'equilibrium_constant': idaes.models.properties.modular_properties.reactions.equilibrium_constant.van_t_hoff,
   'equilibrium_form': idaes.models.properties.modular_properties.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