Skip to content

Commit

Permalink
enhancements to auxiliary API and use it units
Browse files Browse the repository at this point in the history
  • Loading branch information
yoelcortes committed Sep 5, 2023
1 parent 85d6527 commit ffeb7d6
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 121 deletions.
39 changes: 18 additions & 21 deletions biosteam/_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1591,19 +1591,19 @@ def get_nested_auxiliary_units_with_names(self, depth=-1) -> list[Unit]:
)
return auxiliary_units

def _unit_auxlets(self, N_streams, streams):
def _unit_auxlets(self, N_streams, streams, thermo):
if streams is None:
return [self.auxlet(piping.MissingStream()) for i in range(N_streams)]
elif streams == ():
return [self.auxlet(piping.Stream(None)) for i in range(N_streams)]
elif isinstance(streams, piping.stream_types):
return self.auxlet(streams)
return [self.auxlet(piping.Stream(None, thermo=thermo)) for i in range(N_streams)]
elif isinstance(streams, (str, *piping.stream_types)):
return self.auxlet(streams, thermo=thermo)
else:
return [self.auxlet(i) for i in streams]
return [self.auxlet(i, thermo=thermo) for i in streams]

def auxiliary(
self, name, cls, ins=None, outs=(), thermo=None,
stack=False, **kwargs
**kwargs
):
"""
Create and register an auxiliary unit operation. Inlet and outlet
Expand All @@ -1614,29 +1614,24 @@ def auxiliary(
if thermo is None: thermo = self.thermo
if name not in self.auxiliary_unit_names:
raise RuntimeError(f'{name!r} not in auxiliary unit names')
if stack:
if hasattr(self, name):
lst = getattr(self, name)
else:
lst = []
setattr(self, name, lst)
name = f"{name}[{len(lst)}]"
auxunit = cls.__new__(cls)
stack = getattr(self, name, None)
if isinstance(stack, list):
name = f"{name}[{len(stack)}]"
stack.append(auxunit)
else:
setattr(self, name, auxunit)
auxunit.owner = self # Avoids property package checks
auxunit.__init__(
'.' + name,
self._unit_auxlets(cls._N_ins, ins),
self._unit_auxlets(cls._N_outs, outs),
self._unit_auxlets(cls._N_ins, ins, thermo),
self._unit_auxlets(cls._N_outs, outs, thermo),
thermo,
**kwargs
)
if stack:
lst.append(auxunit)
else:
setattr(self, name, auxunit)
return auxunit

def auxlet(self, stream: Stream):
def auxlet(self, stream: Stream, thermo=None):
"""
Define auxiliary unit inlet or outlet. This method has two
behaviors:
Expand All @@ -1652,7 +1647,9 @@ def auxlet(self, stream: Stream):
"""
if stream is None: stream = Stream(None)
if isinstance(stream, str):
stream = Stream('.' + stream, thermo=self.thermo)
if thermo is None: thermo = self.thermo
stream = Stream('.' + stream, thermo=thermo)
stream._source = stream._sink = self
if self is stream._source and stream in self._outs:
if isinstance(stream, tmo.MultiStream):
SuperpositionStream = piping.SuperpositionMultiOutlet
Expand Down
3 changes: 0 additions & 3 deletions biosteam/units/_multi_effect_evaporator.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,13 @@ def _load_components(self):
'evaporators', Flash,
ins=self.ins,
outs=(None, None), P=P[0],
stack=True,
)
else:
evaporator = self.auxiliary(
'evaporators', Evaporator,
ins=self.ins,
outs=(None, None, None), P=P[0],
chemical=self.chemical,
stack=True,
)
for i in range(1, n):
evaporator = self.auxiliary(
Expand All @@ -330,7 +328,6 @@ def _load_components(self):
ins=(evaporator.outs[1], evaporator.outs[0]),
outs=(None, self.outs[0] if i == n-1 else None, None),
P=P[i], chemical=self.chemical,
stack=True,
)
condenser = self.auxiliary(
'condenser', HXutility, ins=evaporator.outs[0], outs=[None], V=0
Expand Down
183 changes: 100 additions & 83 deletions biosteam/units/distillation.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,57 +343,43 @@ def _load_components(self, partial_condenser, condenser_thermo, reboiler_thermo,

#: [HXutility] Condenser.
if not condenser_thermo: condenser_thermo = thermo
distillate = self.auxlet(self.distillate)
self.condensate = condensate = self.auxlet('condensate')
condenser_inlet = self.auxlet(
tmo.Stream(None, phase='g', thermo=condenser_thermo)
)
if partial_condenser:
self.condenser = condenser = HXutility(
'.condenser',
ins=condenser_inlet,
outs=tmo.MultiStream(None, thermo=condenser_thermo),
self.auxiliary(
'condenser', HXutility,
ins='vapor',
thermo=condenser_thermo
)
self.reflux_drum = RefluxDrum(
'.reflux_drum',
ins=condenser.outs[0],
outs=(distillate, condensate)
self.condenser.outlet.phases = ('g', 'l')
self.auxiliary(
'reflux_drum', RefluxDrum,
ins=self.condenser-0,
outs=('distillate', 'condensate')
)
self.condensate = self.reflux_drum-1
else:
self.condenser = HXutility(
'.condenser',
ins=condenser_inlet,
outs=tmo.Stream(None, thermo=condenser_thermo),
self.auxiliary(
'condenser', HXutility,
ins='vapor',
thermo=condenser_thermo
)
self.top_split = bst.FakeSplitter(
'.top_split',
self.auxiliary(
'top_split', FakeSplitter,
ins = self.condenser-0,
outs=(distillate, condensate),
thermo=self.thermo
outs=('distillate', 'condensate'),
thermo=condenser_thermo
)
#: [HXutility] Reboiler.
self.condensate = self.top_split-1
self.condenser.inlet.phase = 'g'
if not reboiler_thermo: reboiler_thermo = thermo
pump_inlet = self.auxlet(
tmo.Stream(None, phase='g', thermo=reboiler_thermo)
)
self.pump = pump = bst.Pump(
'.Pump', pump_inlet, tmo.Stream(None, thermo=reboiler_thermo),
thermo=reboiler_thermo,
self.auxiliary('pump', bst.Pump,
'liquid', thermo=reboiler_thermo,
)
self.reboiler = HXutility(
'.Reboiler',
ins=pump-0,
outs=tmo.MultiStream(None, thermo=reboiler_thermo),
thermo=reboiler_thermo
self.auxiliary('reboiler', HXutility,
self.pump-0, thermo=reboiler_thermo
)
self.boilup = boilup = self.auxlet('boilup')
self.bottoms_split = bst.PhaseSplitter(
'.bottoms_split',
ins = self.reboiler-0,
outs=(boilup, self.auxlet(self.bottoms_product)),
thermo=reboiler_thermo,
self.reboiler.outs[0].phases = ('g', 'l')
self.auxiliary('bottoms_split', bst.PhaseSplitter,
self.reboiler-0, 'boilup', thermo=reboiler_thermo,
)
self.LHK = LHK
self.reset_cache() # Abstract method
Expand Down Expand Up @@ -1183,20 +1169,43 @@ class BinaryDistillation(Distillation, new_graphics=False):
Glycerol 23.9
-------- 105 kmol/hr
>>> D1.results()
Divided Distillation Column Units D1
Electricity Power kW 2.48
Cost USD/hr 0.194
Cooling water Duty kJ/hr -4.88e+06
Flow kmol/hr 3.33e+03
Cost USD/hr 1.63
... ... ...
Purchase cost Pump - Pump USD 4.32e+03
Pump - Motor USD 441
Reboiler - Floating head USD 2.71e+04
Total purchase cost USD 2.15e+05
Utility cost USD/hr 64.4
<BLANKLINE>
[36 rows x 2 columns]
Divided Distillation Column Units D1
Electricity Power kW 2.48
Cost USD/hr 0.194
Cooling water Duty kJ/hr -4.88e+06
Flow kmol/hr 3.33e+03
Cost USD/hr 1.63
Low pressure steam Duty kJ/hr 1.02e+07
Flow kmol/hr 263
Cost USD/hr 62.6
Design Theoretical feed stage 9
Theoretical stages 13
Minimum reflux Ratio 0.687
Reflux Ratio 1.37
Rectifier stages 15
Stripper stages 13
Rectifier height ft 34.7
Stripper height ft 31.7
Rectifier diameter ft 3.95
Stripper diameter ft 3.2
Rectifier wall thickness in 0.312
Stripper wall thickness in 0.312
Rectifier weight lb 6.03e+03
Stripper weight lb 4.44e+03
Purchase cost Rectifier trays USD 1.5e+04
Stripper trays USD 1.25e+04
Rectifier tower USD 4.58e+04
Stripper platform and ladders USD 1.4e+04
Stripper tower USD 3.84e+04
Rectifier platform and ladders USD 1.14e+04
Condenser - Floating head USD 3.33e+04
Reflux drum - Horizontal pressur... USD 1.02e+04
Reflux drum - Platform and ladders USD 3.02e+03
Pump - Pump USD 4.32e+03
Pump - Motor USD 441
Reboiler - Floating head USD 2.71e+04
Total purchase cost USD 2.15e+05
Utility cost USD/hr 64.4
Binary distillation with full-condenser
Expand All @@ -1214,7 +1223,7 @@ class BinaryDistillation(Distillation, new_graphics=False):
... is_divided=False)
>>> D1.simulate()
>>> # See all results
>>> D1.results()
>>> D1.results() # doctest: +SKIP
Distillation Column Units D1
Electricity Power kW 2.48
Cost USD/hr 0.194
Expand Down Expand Up @@ -1617,35 +1626,43 @@ class ShortcutColumn(Distillation, new_graphics=False):
Glycerol 23.9
-------- 105 kmol/hr
>>> D1.results()
Divided Distillation Column Units D1
Electricity Power kW 2.92
Cost USD/hr 0.228
Cooling water Duty kJ/hr -7.54e+06
Flow kmol/hr 5.15e+03
Cost USD/hr 2.51
... ... ...
Purchase cost Pump - Pump USD 4.32e+03
Pump - Motor USD 454
Reboiler - Floating head USD 2.98e+04
Total purchase cost USD 2.58e+05
Utility cost USD/hr 85.1
<BLANKLINE>
[36 rows x 2 columns]
>>> D1.results()
Divided Distillation Column Units D1
Electricity Power kW 2.92
Cost USD/hr 0.228
Cooling water Duty kJ/hr -7.54e+06
Flow kmol/hr 5.15e+03
Cost USD/hr 2.51
... ... ...
Purchase cost Pump - Pump USD 4.32e+03
Pump - Motor USD 454
Reboiler - Floating head USD 2.98e+04
Total purchase cost USD 2.58e+05
Utility cost USD/hr 85.1
<BLANKLINE>
[36 rows x 2 columns]
Divided Distillation Column Units D1
Electricity Power kW 2.92
Cost USD/hr 0.228
Cooling water Duty kJ/hr -7.54e+06
Flow kmol/hr 5.15e+03
Cost USD/hr 2.51
Low pressure steam Duty kJ/hr 1.34e+07
Flow kmol/hr 346
Cost USD/hr 82.4
Design Theoretical feed stage 8
Theoretical stages 16
Minimum reflux Ratio 1.06
Reflux Ratio 2.12
Rectifier stages 13
Stripper stages 26
Rectifier height ft 31.7
Stripper height ft 50.9
Rectifier diameter ft 4.54
Stripper diameter ft 3.65
Rectifier wall thickness in 0.312
Stripper wall thickness in 0.312
Rectifier weight lb 6.48e+03
Stripper weight lb 7.95e+03
Purchase cost Rectifier trays USD 1.52e+04
Stripper trays USD 2.01e+04
Rectifier tower USD 4.78e+04
Stripper platform and ladders USD 1.42e+04
Stripper tower USD 5.39e+04
Rectifier platform and ladders USD 1.81e+04
Condenser - Floating head USD 4.07e+04
Reflux drum - Horizontal pressur... USD 1.03e+04
Reflux drum - Platform and ladders USD 3.02e+03
Pump - Pump USD 4.32e+03
Pump - Motor USD 454
Reboiler - Floating head USD 2.98e+04
Total purchase cost USD 2.58e+05
Utility cost USD/hr 85.1
"""
line = 'Distillation'
Expand Down
11 changes: 4 additions & 7 deletions biosteam/units/heat_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,13 +453,9 @@ def simulate_as_auxiliary_exchanger(self,
outlet = self.outs[0]
if not inlet: inlet = inlet.materialize_connection(None)
if not outlet: outlet = outlet.materialize_connection(None)
if P is None:
inlet.mix_from(ins, vle=vle)
P = inlet.P
else:
inlet.mix_from(ins, energy_balance=False)
inlet.vle(H=sum([i.H for i in ins]), P=P)
inlet.reduce_phases()
idata = inlet.get_data()
inlet.mix_from(ins, energy_balance=False)
if vle: inlet.vle(H=sum([i.H for i in ins]), P=P)
if outs is None:
if duty is None: raise ValueError('must pass duty when no outlets are given')
outlet.copy_like(inlet)
Expand All @@ -485,6 +481,7 @@ def simulate_as_auxiliary_exchanger(self,
design_kwargs=dict(duty=duty),
)
for i in self.heat_utilities: i.hxn_ok = hxn_ok
inlet.set_data(idata)

def _run(self):
feed = self.ins[0]
Expand Down
1 change: 0 additions & 1 deletion biosteam/units/liquid_liquid_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,6 @@ def _setup(self):
args = (self.N_stages, self.feed_stages, self.extract_side_draws, self.use_cache,
*self._ins, self.raffinate_side_draws, self.solvent_ID, self.partition_data)
if args != self._last_args:
del self.stages
MultiStageEquilibrium.__init__(
self, self.ID, self.ins, self.outs, self.thermo,
N_stages=self.N_stages, feed_stages=self.feed_stages, phases=('l', 'L'), P=self.P,
Expand Down
13 changes: 7 additions & 6 deletions biosteam/units/phase_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,15 @@ def __init__(self, ID='', ins=None, outs=(), thermo=None, *,
self.splitters = []
if top_split:
self.auxiliary(
'splitters', bst.Splitter, ins=partition-0, outs=[self.outs[2], self.outs[0]],
split=top_split, stack=True
'splitters', bst.Splitter,
partition-0, [self.outs[2], self.outs[0]],
split=top_split,
)
if bottom_split:
self.auxiliary(
'splitters', bst.Splitter, ins=partition-1, outs=[self.outs[-1], self.outs[1]],
split=bottom_split, stack=True
'splitters', bst.Splitter,
partition-1, [self.outs[-1], self.outs[1]],
split=bottom_split,
)

def add_feed(self, stream):
Expand Down Expand Up @@ -397,6 +399,7 @@ def __init__(self, ID='', ins=None, outs=(), thermo=None, *,
last_stage = None
self._asplit = asplits = -np.ones(N_stages)
self._bsplit = bsplits = asplits.copy()
self.stages = stages = []
for i in range(N_stages):
if last_stage is None:
feed = ()
Expand Down Expand Up @@ -437,12 +440,10 @@ def __init__(self, ID='', ins=None, outs=(), thermo=None, *,
partition_data=partition_data,
top_split=top_split,
bottom_split=bottom_split,
stack=True
)
if last_stage:
last_stage.add_feed(new_stage-0)
last_stage = new_stage
stages = self.stages
for feed, stage in zip(self.ins, feed_stages):
stages[stage].add_feed(self.auxlet(feed))
self.solvent_ID = solvent
Expand Down

0 comments on commit ffeb7d6

Please sign in to comment.