separations#

This module contains functions for modeling separations in unit operations.

mix_and_split(ins, top, bottom, split)[source]#

Run splitter mass and energy balance with mixing all input streams.

Parameters:
  • ins (Iterable[Stream]) – All inlet fluids.

  • top (Stream) – Top inlet fluid.

  • bottom (Stream) – Bottom inlet fluid

  • split (array_like) – Component-wise split of feed to the top stream.

Examples

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol'], cache=True)
>>> feed_a = tmo.Stream(Water=20, Ethanol=5)
>>> feed_b = tmo.Stream(Water=15, Ethanol=5)
>>> split = 0.8
>>> effluent_a = tmo.Stream('effluent_a')
>>> effluent_b = tmo.Stream('effluent_b')
>>> tmo.separations.mix_and_split([feed_a, feed_b], effluent_a, effluent_b, split)
>>> effluent_a.show()
Stream: effluent_a
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    28
                Ethanol  8
>>> effluent_b.show()
Stream: effluent_b
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    7
                Ethanol  2
adjust_moisture_content(retentate, permeate, moisture_content, ID=None, strict=None)[source]#

Remove water from permate to adjust retentate moisture content.

Parameters:
  • retentate (Stream)

  • permeate (Stream)

  • moisture_content (float) – Fraction of water in retentate.

Examples

>>> import thermosteam as tmo
>>> Solids = tmo.Chemical('Solids', default=True, search_db=False, phase='s')
>>> tmo.settings.set_thermo(['Water', Solids])
>>> retentate = tmo.Stream('retentate', Solids=20, units='kg/hr')
>>> permeate = tmo.Stream('permeate', Water=50, Solids=0.1, units='kg/hr')
>>> moisture_content = 0.5
>>> tmo.separations.adjust_moisture_content(retentate, permeate, moisture_content)
>>> retentate.show(flow='kg/hr')
Stream: retentate
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kg/hr): Water   20
              Solids  20
>>> permeate.show(flow='kg/hr')
Stream: permeate
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kg/hr): Water   30
              Solids  0.1

Note that if not enough water is available, an InfeasibleRegion error is raised:

>>> retentate.imol['Water'] = permeate.imol['Water'] = 0
>>> tmo.separations.adjust_moisture_content(retentate, permeate, moisture_content)
Traceback (most recent call last):
InfeasibleRegion: not enough water; permeate moisture content is infeasible
mix_and_split_with_moisture_content(ins, retentate, permeate, split, moisture_content, ID=None, strict=None)[source]#

Run splitter mass and energy balance with mixing all input streams and and ensuring retentate moisture content.

Parameters:
  • ins (Iterable[Stream]) – Inlet fluids with solids.

  • retentate (Stream)

  • permeate (Stream)

  • split (array_like) – Component splits to the retentate.

  • moisture_content (float) – Fraction of water in retentate.

Examples

>>> import thermosteam as tmo
>>> Solids = tmo.Chemical('Solids', default=True, search_db=False, phase='s')
>>> tmo.settings.set_thermo(['Water', Solids])
>>> feed = tmo.Stream('feed', Water=100, Solids=10, units='kg/hr')
>>> wash_water = tmo.Stream('wash_water', Water=10, units='kg/hr')
>>> retentate = tmo.Stream('retentate')
>>> permeate = tmo.Stream('permeate')
>>> split = [0., 1.]
>>> moisture_content = 0.5
>>> tmo.separations.mix_and_split_with_moisture_content(
...     [feed, wash_water], retentate, permeate, split, moisture_content
... )
>>> retentate.show(flow='kg/hr')
Stream: retentate
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kg/hr): Water   10
              Solids  10
>>> permeate.show(flow='kg/hr')
Stream: permeate
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kg/hr): Water  100
partition_coefficients(IDs, top, bottom)[source]#

Return partition coefficients given streams in equilibrium.

Parameters:
  • top (Stream) – Vapor fluid.

  • bottom (Stream) – Liquid fluid.

  • IDs (tuple[str]) – IDs of chemicals in equilibrium.

Returns:

K – Patition coefficients in mol fraction in top stream over mol fraction in bottom stream.

Return type:

1d array

Examples

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', tmo.Chemical('O2', phase='g')], cache=True)
>>> s = tmo.Stream('s', Water=20, Ethanol=20, O2=0.1)
>>> s.vle(V=0.5, P=101325)
>>> tmo.separations.partition_coefficients(('Water', 'Ethanol'), s['g'], s['l'])
array([0.632, 1.582])
vle_partition_coefficients(top, bottom)[source]#

Return VLE partition coefficients given vapor and liquid streams in equilibrium.

Parameters:
  • top (Stream) – Vapor fluid.

  • bottom (Stream) – Liquid fluid.

Returns:

  • IDs (tuple[str]) – IDs for chemicals in vapor-liquid equilibrium.

  • K (1d array) – Patition coefficients in mol fraction in vapor over mol fraction in liquid.

Examples

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', tmo.Chemical('O2', phase='g')], cache=True)
>>> s = tmo.Stream('s', Water=20, Ethanol=20, O2=0.1)
>>> s.vle(V=0.5, P=101325)
>>> IDs, K = tmo.separations.vle_partition_coefficients(s['g'], s['l'])
>>> IDs
('Water', 'Ethanol')
>>> K
array([0.632, 1.582])
lle_partition_coefficients(top, bottom)[source]#

Return LLE partition coefficients given two liquid streams in equilibrium.

Parameters:
  • top (Stream) – Liquid fluid.

  • bottom (Stream) – Other liquid fluid.

Returns:

  • IDs (tuple[str]) – IDs for chemicals in liquid-liquid equilibrium.

  • K (1d array) – Patition coefficients in mol fraction in top liquid over mol fraction in bottom liquid.

Examples

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', 'Octanol'], cache=True)
>>> s = tmo.Stream('s', Water=20, Octanol=20, Ethanol=1)
>>> s.lle(T=298.15, P=101325, top_chemical='Octanol') # Top phase is L
>>> IDs, K = tmo.separations.lle_partition_coefficients(s['L'], s['l'])
>>> IDs
('Water', 'Ethanol', 'Octanol')
>>> round(K[2], -1) # Octanol
3330.0
partition(feed, top, bottom, IDs, K, phi=None, top_chemicals=None, bottom_chemicals=None, strict=False, stacklevel=1)[source]#

Run equilibrium of feed to top and bottom streams given partition coeffiecients and return the phase fraction.

Parameters:
  • feed (Stream) – Mixed feed.

  • top (Stream) – Top fluid.

  • bottom (Stream) – Bottom fluid.

  • IDs (tuple[str]) – IDs of chemicals in equilibrium.

  • K (1d array) – Partition coefficeints corresponding to IDs.

  • phi (float, optional) – Guess phase fraction in top phase.

  • top_chemicals (tuple[str], optional) – Chemicals that remain in the top fluid.

  • bottom_chemicals (tuple[str], optional) – Chemicals that remain in the bottom fluid.

  • strict (bool, optional) – Whether to raise an InfeasibleRegion exception when solution results in negative flow rates or to remove negative flows and issue a warning. Defaults to False.

Returns:

phi – Phase fraction in top phase.

Return type:

float

Notes

Chemicals not in equilibrium end up in the top phase.

Examples

>>> import numpy as np
>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', tmo.Chemical('NaCl', default=True),
...                          tmo.Chemical('O2', phase='g')], cache=True)
>>> IDs = ('Water', 'Ethanol')
>>> K = np.array([0.629, 1.59])
>>> feed = tmo.Stream('feed', Water=20, Ethanol=20, O2=0.1)
>>> top = tmo.Stream('top')
>>> bottom = tmo.Stream('bottom')
>>> tmo.separations.partition(feed, top, bottom, IDs, K)
0.500
>>> top.show()
Stream: top
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    7.73
                Ethanol  12.3
                O2       0.1
>>> bottom.show()
Stream: bottom
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    12.3
                Ethanol  7.72
>>> feed = tmo.Stream('feed', Water=20, Ethanol=20, NaCl=0.1, O2=0.1)
>>> tmo.separations.partition(feed, top, bottom, IDs, K,
...                           top_chemicals=('O2',),
...                           bottom_chemicals=('NaCl'))
0.500
>>> top.show()
Stream: top
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    7.73
                Ethanol  12.3
                O2       0.1
>>> bottom.show()
Stream: bottom
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    12.3
                Ethanol  7.72
                NaCl     0.1
lle(feed, top, bottom, top_chemical=None, efficiency=1.0, multi_stream=None)[source]#

Run LLE mass and energy balance.

Parameters:
  • feed (Stream) – Mixed feed.

  • top (Stream) – Top fluid.

  • bottom (Stream) – Bottom fluid.

  • top_chemical (str, optional) – Identifier of chemical that will be favored in the top fluid.

  • efficiency=1. (float,) – Fraction of feed in liquid-liquid equilibrium. The rest of the feed is divided equally between phases

  • multi_stream (MultiStream, optional) – Data from feed is passed to this stream to perform liquid-liquid equilibrium.

Examples

Perform liquid-liquid equilibrium around water and octanol and split the phases:

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', 'Octanol'], cache=True)
>>> feed = tmo.Stream('feed', Water=20, Octanol=20, Ethanol=1)
>>> top = tmo.Stream('top')
>>> bottom = tmo.Stream('bottom')
>>> tmo.separations.lle(feed, top, bottom, top_chemical='Octanol')
>>> top.show()
Stream: top
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    3.55
                Ethanol  0.861
                Octanol  20
>>> bottom.show()
Stream: bottom
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water    16.5
                Ethanol  0.139
                Octanol  0.00409

Assume that 1% of the feed is not in equilibrium (possibly due to poor mixing):

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', 'Octanol'], cache=True)
>>> feed = tmo.Stream('feed', Water=20, Octanol=20, Ethanol=1)
>>> top = tmo.Stream('top')
>>> bottom = tmo.Stream('bottom')
>>> ms = tmo.MultiStream('ms', phases='lL') # Store flow rate data here as well
>>> tmo.separations.lle(feed, top, bottom, efficiency=0.99, multi_stream=ms, top_chemical='Octanol')
>>> ms.show()
MultiStream: ms
phases: ('L', 'l'), T: 298.15 K, P: 101325 Pa
flow (kmol/hr): (L) Water    3.55
                    Ethanol  0.861
                    Octanol  20
                (l) Water    16.5
                    Ethanol  0.139
                    Octanol  0.00409
vle(feed, vap, liq, T=None, P=None, V=None, Q=None, x=None, y=None, multi_stream=None)[source]#

Run VLE mass and energy balance.

Parameters:
  • feed (Stream) – Mixed feed.

  • vap (Stream) – Vapor fluid.

  • liq (Stream) – Liquid fluid.

  • P=None (float) – Operating pressure [Pa].

  • Q=None (float) – Duty [kJ/hr].

  • T=None (float) – Operating temperature [K].

  • V=None (float) – Molar vapor fraction.

  • x=None (float) – Molar composition of liquid (for binary mixtures).

  • y=None (float) – Molar composition of vapor (for binary mixtures).

  • multi_stream (MultiStream, optional) – Data from feed is passed to this stream to perform vapor-liquid equilibrium.

Examples

Perform vapor-liquid equilibrium on water and ethanol and split phases to vapor and liquid streams:

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol'], cache=True)
>>> feed = tmo.Stream('feed', Water=20, Ethanol=20)
>>> vapor = tmo.Stream('top')
>>> liquid = tmo.Stream('bottom')
>>> tmo.separations.vle(feed, vapor, liquid, V=0.5, P=101325)
>>> vapor.show()
Stream: top
phase: 'g', T: 353.94 K, P: 101325 Pa
flow (kmol/hr): Water    7.75
                Ethanol  12.3
>>> liquid.show()
Stream: bottom
phase: 'l', T: 353.94 K, P: 101325 Pa
flow (kmol/hr): Water    12.3
                Ethanol  7.75

It is also possible to save flow rate data in a multi-stream as well:

>>> ms = tmo.MultiStream('ms', phases='lg')
>>> tmo.separations.vle(feed, vapor, liquid, V=0.5, P=101325, multi_stream=ms)
>>> ms.show()
MultiStream: ms
phases: ('g', 'l'), T: 353.94 K, P: 101325 Pa
flow (kmol/hr): (g) Water    7.75
                    Ethanol  12.3
                (l) Water    12.3
                    Ethanol  7.75
material_balance(chemical_IDs, variable_inlets, constant_inlets=(), constant_outlets=(), is_exact=True, balance='flow')[source]#

Solve stream mass balance by iteration.

Parameters:
  • chemical_IDs (tuple[str]) – Chemicals that will be used to solve mass balance linear equations. The number of chemicals must be same as the number of input streams varied.

  • variable_inlets (Iterable[Stream]) – Inlet streams that can vary in net flow rate to accomodate for the mass balance.

  • constant_inlets (Iterable[Stream], optional) – Inlet streams that cannot vary in flow rates.

  • constant_outlets (Iterable[Stream], optional) – Outlet streams that cannot vary in flow rates.

  • is_exact=True (bool, optional) – True if exact flow rate solution is required for the specified IDs.

  • balance='flow' ({'flow', 'composition'}, optional) –

    • ‘flow’: Satisfy output flow rates

    • ’composition’: Satisfy net output molar composition

Examples

Vary inlet flow rates to satisfy outlet flow rates:

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol'], cache=True)
>>> in_a = tmo.Stream('in_a', Water=1)
>>> in_b = tmo.Stream('in_b', Ethanol=1)
>>> variable_inlets = [in_a, in_b]
>>> in_c = tmo.Stream('in_c', Water=100)
>>> constant_inlets = [in_c]
>>> out_a = tmo.Stream('out_a', Water=200, Ethanol=2)
>>> out_b = tmo.Stream('out_b', Ethanol=100)
>>> constant_outlets = [out_a, out_b]
>>> chemical_IDs = ('Water', 'Ethanol')
>>> tmo.separations.material_balance(chemical_IDs, variable_inlets, constant_inlets, constant_outlets)
>>> tmo.Stream.sum([in_a, in_b, in_c]).mol - tmo.Stream.sum([out_a, out_b]).mol # Molar flow rates entering and leaving are equal
sparse([0., 0.])

Vary inlet flow rates to satisfy outlet composition:

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol'], cache=True)
>>> in_a = tmo.Stream('in_a', Water=1)
>>> in_b = tmo.Stream('in_b', Ethanol=1)
>>> variable_inlets = [in_a, in_b]
>>> in_c = tmo.Stream('in_c', Water=100)
>>> constant_inlets = [in_c]
>>> out_a = tmo.Stream('out_a', Water=200, Ethanol=2)
>>> out_b = tmo.Stream('out_b', Ethanol=100)
>>> constant_outlets = [out_a, out_b]
>>> chemical_IDs = ('Water', 'Ethanol')
>>> tmo.separations.material_balance(chemical_IDs, variable_inlets, constant_inlets, constant_outlets, balance='composition')
>>> tmo.Stream.sum([in_a, in_b, in_c]).z_mol - tmo.Stream.sum([out_a, out_b]).z_mol # Molar composition entering and leaving are equal
array([0., 0.])
chemical_splits(a, b=None, mixed=None)[source]#

Return a ChemicalIndexer with splits for all chemicals to stream a.

Examples

>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol'], cache=True)
>>> stream = tmo.Stream('stream', Water=10, Ethanol=10)
>>> stream.vle(V=0.5, P=101325)
>>> isplits = tmo.separations.chemical_splits(stream['g'], stream['l'])
>>> isplits.show()
ChemicalIndexer:
 Water    0.387
 Ethanol  0.613
>>> isplits = tmo.separations.chemical_splits(stream['g'], mixed=stream)
>>> isplits.show()
ChemicalIndexer:
 Water    0.387
 Ethanol  0.613
phase_fraction(feed, IDs, K, phi=None, top_chemicals=None, bottom_chemicals=None, strict=False, stacklevel=1)[source]#

Return the phase fraction given a stream and partition coeffiecients.

Parameters:
  • feed (Stream) – Mixed feed.

  • IDs (tuple[str]) – IDs of chemicals in equilibrium.

  • K (1d array) – Partition coefficeints corresponding to IDs.

  • phi (float, optional) – Guess phase fraction in top phase.

  • top_chemicals (tuple[str], optional) – Chemicals that remain in the top fluid.

  • bottom_chemicals (tuple[str], optional) – Chemicals that remain in the bottom fluid.

  • strict (bool, optional) – Whether to raise an InfeasibleRegion exception when solution results in negative flow rates or to remove negative flows and issue a warning. Defaults to False.

Returns:

phi – Phase fraction in top phase.

Return type:

float

Notes

Chemicals not in equilibrium end up in the top phase.

Examples

>>> import numpy as np
>>> import thermosteam as tmo
>>> tmo.settings.set_thermo(['Water', 'Ethanol', tmo.Chemical('NaCl', default=True),
...                          tmo.Chemical('O2', phase='g')], cache=True)
>>> IDs = ('Water', 'Ethanol')
>>> K = np.array([0.629, 1.59])
>>> feed = tmo.Stream('feed', Water=20, Ethanol=20, O2=0.1)
>>> tmo.separations.phase_fraction(feed, IDs, K)
0.500
>>> feed = tmo.Stream('feed', Water=20, Ethanol=20, NaCl=0.1, O2=0.1)
>>> tmo.separations.phase_fraction(feed, IDs, K,
...                                top_chemicals=('O2',),
...                                bottom_chemicals=('NaCl'))
0.500