Skip to content

Commit

Permalink
add boxed-system/facilities feature
Browse files Browse the repository at this point in the history
  • Loading branch information
yoelcortes committed Aug 5, 2024
1 parent d446cb8 commit 02c4062
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 81 deletions.
7 changes: 2 additions & 5 deletions benchmark/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ class Tracker:
__slots__ = ('system', 'run', 'streams',
'adiabatic_stages', 'stages',
'profile_time', 'rtol', 'atol')
cutoff_time = 200
cutoff_time = 300

def __init__(self, name, algorithm, rtol=1e-16, atol=1e-6):
sys = all_systems[name](algorithm)
Expand Down Expand Up @@ -291,7 +291,6 @@ def benchmark(self):
time.tic()
f()
net_time += time.toc()
print('material error', sum([abs(i.mass_balance_error()) for i in stages]))
new_temperatures = np.array([i.T for i in streams])
new_flows = np.array([i.mol for i in streams])
dF = np.abs(flows - new_flows)
Expand Down Expand Up @@ -453,11 +452,9 @@ def plot_benchmark(systems=None, exclude=None, N=3, load=True, save=True):
file = os.path.join(simulations_folder, po_name)
with open(file, 'wb') as f: pickle.dump(pos, f)
else:
sm = Tracker(sys, 'sequential modular').benchmark()
for i in range(N):
sm = Tracker(sys, 'sequential modular').benchmark()
sms.append(sm)
po = Tracker(sys, 'phenomena oriented').benchmark()
for i in range(N):
po = Tracker(sys, 'phenomena oriented').benchmark()
pos.append(po)
Expand All @@ -468,7 +465,7 @@ def plot_benchmark(systems=None, exclude=None, N=3, load=True, save=True):
po_name = f'po_{time}_{sys}_benchmark_{N}.npy'
file = os.path.join(simulations_folder, po_name)
with open(file, 'wb') as f: pickle.dump(pos, f)
data = np.array([j['Time'] / i['Time'] for i, j in zip(sms, pos)])[1:]
data = np.array([j['Time'] / i['Time'] for i, j in zip(sms, pos)])
mean = np.mean(data)
sm_better = mean > 1
if sm_better:
Expand Down
4 changes: 3 additions & 1 deletion biosteam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def njit(*args, **kwargs):
from . import units
from .units import *
from ._facility import Facility
from . import _module
from ._module import *
from . import facilities
from .facilities import *
from . import wastewater
Expand All @@ -82,7 +84,7 @@ def njit(*args, **kwargs):
'MultiStream', 'settings', 'exceptions', 'report',
'process_tools', 'preferences', *_system.__all__, *_flowsheet.__all__,
*_tea.__all__, *units.__all__, *facilities.__all__, *wastewater.__all__,
*evaluation.__all__, *process_tools.__all__,
*evaluation.__all__, *process_tools.__all__, *_module.__all__,
)

def nbtutorial():
Expand Down
2 changes: 1 addition & 1 deletion biosteam/_facility.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ def __init__(self, ID='', ins=None, outs=(), thermo=None, **kwargs):
@property
def other_units(self):
return self._other_units


41 changes: 19 additions & 22 deletions biosteam/_flowsheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,6 @@

# %% Flowsheet search

class TemporaryFlowsheet:
__slots__ = ('original', 'temporary')

def __init__(self, temporary):
self.temporary = temporary

def __enter__(self):
self.original = main_flowsheet.get_flowsheet()
main_flowsheet.set_flowsheet(self.temporary)
return self.temporary

def __exit__(self, type, exception, traceback):
main_flowsheet.set_flowsheet(self.original)
if exception: raise exception


class FlowsheetRegistry:
__getitem__ = object.__getattribute__

Expand Down Expand Up @@ -87,8 +71,9 @@ class Flowsheet:
#: All flowsheets.
flowsheet: FlowsheetRegistry = FlowsheetRegistry()

def __new__(cls, ID):
def __new__(cls, ID=None):
self = super().__new__(cls)
if ID is None: ID = ''

#: Contains all System objects as attributes.
self.system: Registry = Registry()
Expand All @@ -101,29 +86,40 @@ def __new__(cls, ID):

#: ID of flowsheet.
self._ID: str = ID

#: Temporary flowsheet stack.
self._temporary_stack = []

self.flowsheet.__dict__[ID] = self
return self

def temporary(self):
def __enter__(self):
"""
Return a TemporaryFlowsheet object that, through context management,
will temporarily register all objects in this flowsheet instead of
Temporarily register all objects in this flowsheet instead of
the main flowsheet.
Examples
--------
>>> import biosteam as bst
>>> bst.settings.set_thermo(['Water'], cache=True)
>>> f = bst.Flowsheet('f')
>>> with f.temporary():
>>> with f:
... M1 = bst.Mixer('M1')
>>> M1 in bst.main_flowsheet.unit
False
>>> M1 in f.unit
True
"""
return TemporaryFlowsheet(self)
self._temporary_stack.append(main_flowsheet.get_flowsheet())
main_flowsheet.set_flowsheet(self)
return self

def __exit__(self, type, exception, traceback):
main_flowsheet.set_flowsheet(self._temporary_stack.pop())
if exception: raise exception

def temporary(self): return self # for backwards compatibility

def __reduce__(self):
return self.from_registries, self.registries
Expand Down Expand Up @@ -152,6 +148,7 @@ def from_registries(cls, ID, stream, unit, system):
flowsheet.unit = unit
flowsheet.system = system
flowsheet._ID = ID
flowsheet._temporary_stack = []
flowsheet.flowsheet.__dict__[ID] = flowsheet
return flowsheet

Expand Down
36 changes: 36 additions & 0 deletions biosteam/_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020-2024, Yoel Cortes-Pena <yoelcortes@gmail.com>
#
# This module is under the UIUC open-source license. See
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
# for license details.
"""
"""
from ._unit import Unit
from ._facility import Facility

__all__ = ('Module', 'FacilityModule')

class Module(Unit):
_N_ins = 0
_N_outs = 0
_ins_size_is_fixed = False
_outs_size_is_fixed = False

def _assert_compatible_property_package(self): pass

def _setup(self):
self.auxiliary_system._setup(load_configuration=False)
super()._setup()

def _run(self):
self.auxiliary_system.converge()

def _design(self):
for i in self.auxiliary_system.units: i._design()

def _cost(self):
for i in self.auxiliary_system.units: i._cost()

class FacilityModule(Module, Facility): network_priority = None
20 changes: 3 additions & 17 deletions biosteam/_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -2297,7 +2297,7 @@ def _setup_units(self):
if u.prioritize: self.prioritize_unit(u)
prioritized_units.add(u)

def _setup(self, update_configuration=False, units=None):
def _setup(self, update_configuration=False, units=None, load_configuration=True):
"""Setup each element of the system."""
if units is None: units = self.units
if update_configuration:
Expand All @@ -2310,7 +2310,7 @@ def _setup(self, update_configuration=False, units=None):
self._save_configuration()
self._load_stream_links()
else:
self._load_configuration()
if load_configuration: self._load_configuration()
self._create_temporary_connections()
if temporary_units_dump:
self._update_configuration(units=[*units, *temporary_units_dump])
Expand Down Expand Up @@ -2398,20 +2398,6 @@ def run_phenomena(self):
raise error
except:
for i in path: i.run()

# try:
# with self.stage_configuration(aggregated=True) as conf:
# conf.solve_nonlinearities()
# conf.solve_energy_departures()
# conf.solve_material_flows()
# except:
# for i in path: i.run()
# else:
# with self.stage_configuration(aggregated=False) as conf:
# for n, i in enumerate(path):
# i.run()
# conf.solve_energy_departures()
# conf.solve_material_flows()

def _solve(self):
"""Solve the system recycle iteratively."""
Expand Down Expand Up @@ -2723,7 +2709,7 @@ def simulate(self, update_configuration: Optional[bool]=None, units=None,
the system will be replaced.
"""
with self.flowsheet.temporary():
with self.flowsheet:
specifications = self._specifications
if specifications and not self._running_specifications:
self._running_specifications = True
Expand Down
30 changes: 8 additions & 22 deletions biosteam/_unit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020-2023, Yoel Cortes-Pena <yoelcortes@gmail.com>
# Copyright (C) 2020-2024, Yoel Cortes-Pena <yoelcortes@gmail.com>
#
# This module is under the UIUC open-source license. See
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
Expand Down Expand Up @@ -826,10 +826,6 @@ def _load_costs(self):
heat_utilities.extend(N * unit.heat_utilities)
power_utility.consumption += N * unit.power_utility.consumption
power_utility.production += N * unit.power_utility.production
F_BM_auxiliary = unit.F_BM
F_D_auxiliary = unit.F_D
F_P_auxiliary = unit.F_P
F_M_auxiliary = unit.F_M
bpc_auxiliary = unit.baseline_purchase_costs
pc_auxiliary = unit.purchase_costs
ic_auxiliary = unit.installed_costs
Expand All @@ -840,24 +836,14 @@ def _load_costs(self):
f"'{j}' already in `baseline_purchase_cost` "
f"dictionary of {repr(self)}; try using a different key"
)
elif N == 1:
baseline_purchase_costs[j] = bpc_auxiliary[i]
purchase_costs[j] = pc_auxiliary[i]
installed_costs[j] = ic_auxiliary[i]
else:
F_D[j] = fd = F_D_auxiliary.get(i, 1.)
F_P[j] = fp = F_P_auxiliary.get(i, 1.)
F_M[j] = fm = F_M_auxiliary.get(i, 1.)
if N == 1:
baseline_purchase_costs[j] = Cpb = bpc_auxiliary[i]
purchase_costs[j] = pc_auxiliary[i]
installed_costs[j] = Cbm = ic_auxiliary[i]
else:
baseline_purchase_costs[j] = Cpb = N * bpc_auxiliary[i]
purchase_costs[j] = N * pc_auxiliary[i]
installed_costs[j] = Cbm = N * ic_auxiliary[i]
try:
F_BM[j] = F_BM_auxiliary[i]
except KeyError:
# Assume costs already added elsewhere using another method.
# Calculate BM as an estimate.
F_BM[j] = Cbm / Cpb + 1 - fd * fp * fm
baseline_purchase_costs[j] = N * bpc_auxiliary[i]
purchase_costs[j] = N * pc_auxiliary[i]
installed_costs[j] = N * ic_auxiliary[i]

self._costs_loaded = True

Expand Down
25 changes: 22 additions & 3 deletions biosteam/process_tools/system_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .._unit import streams
from biosteam.utils import as_stream, MissingStream
from biosteam.process_tools import utils
from biosteam import Unit
from typing import Optional
from inspect import signature

Expand Down Expand Up @@ -225,6 +226,7 @@ def __call__(self, ID=None, ins=None, outs=None,
relative_molar_tolerance=None,
temperature_tolerance=None,
relative_temperature_tolerance=None,
box=False, priority=None,
**kwargs
):
fthermo = self.fthermo
Expand All @@ -251,12 +253,29 @@ def __call__(self, ID=None, ins=None, outs=None,
temperature_tolerance=temperature_tolerance,
relative_temperature_tolerance=relative_temperature_tolerance,
)
with (bst.MockSystem() if mockup else bst.System(**options)) as system:
if priority is not None: box = True
if box:
if rename:
unit_registry = system.flowsheet.unit
unit_registry = bst.main_flowsheet.unit
irrelevant_units = tuple(unit_registry)
unit_registry.untrack(irrelevant_units)
self.f(ins, outs, **kwargs)
if priority is None:
module = bst.Module(ins=ins, outs=outs)
else:
module = bst.FacilityModule(ins=ins, outs=outs)
module.network_priority = priority
ins = tuple([module.auxin(i) for i in module.ins])
outs = tuple([module.auxout(i) for i in module.outs])
with bst.Flowsheet(ID), (bst.MockSystem() if mockup else bst.System(**options)) as system:
self.f(ins, outs, **kwargs)
module.register_auxiliary(system, 'auxiliary_system')
else:
with (bst.MockSystem() if mockup else bst.System(**options)) as system:
if rename:
unit_registry = system.flowsheet.unit
irrelevant_units = tuple(unit_registry)
unit_registry.untrack(irrelevant_units)
self.f(ins, outs, **kwargs)
system.load_inlet_ports(ins, {k: i for i, j in enumerate(self.ins) if (k:=get_name(j)) is not None})
system.load_outlet_ports(outs, {k: i for i, j in enumerate(self.outs) if (k:=get_name(j)) is not None})
if autorename is not None: tmo.utils.Registry.AUTORENAME = original_autorename
Expand Down
19 changes: 11 additions & 8 deletions biosteam/units/aerated_bioreactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,10 @@ def _init(self,
theta=0.5, Q_consumption=None,
optimize_power=None,
mixins=None,
fed_gas_hook=None,
**kwargs,
):
self.fed_gas_hook = fed_gas_hook
self.reactions = reactions
self.backward_reactions = backward_reactions
self.theta = theta # Average concentration of gas substrate in the liquid as a fraction of saturation.
Expand Down Expand Up @@ -488,7 +490,7 @@ def vent(self):

@property
def variable_gas_feeds(self):
return [self.ins[i] for i in self.feed_gas_compositions]
return [(i if isinstance(i, bst.Stream) else self.ins[i]) for i in self.feed_gas_compositions]

@property
def normal_gas_feeds(self):
Expand Down Expand Up @@ -540,6 +542,13 @@ def get_SURs(self, effluent):
self.backward_reactions.force_reaction(consumed)
return consumed.get_flow('mol/s', self.gas_substrates), consumed, produced

def _load_gas_feeds(self):
if self.fed_gas_hook is not None: self.fed_gas_hook()
for i in self.mixers: i.simulate()
for i in self.compressors: i.simulate()
for i in self.gas_coolers: i.simulate()
self.sparger.simulate()

def _run(self):
variable_gas_feeds = self.variable_gas_feeds
for i in variable_gas_feeds: i.phase = 'g'
Expand Down Expand Up @@ -567,17 +576,11 @@ def _run(self):
x_substrates.append(gas.get_molar_fraction(ID))
index = range(len(self.gas_substrates))

def run_auxiliaries():
for i in self.mixers: i.simulate()
for i in self.compressors: i.simulate()
for i in self.gas_coolers: i.simulate()
self.sparger.simulate()

def load_flow_rates(F_feeds):
for i in index:
gas = variable_gas_feeds[i]
gas.set_total_flow(F_feeds[i], 'mol/s')
run_auxiliaries()
self._load_gas_feeds()
effluent.set_data(effluent_liquid_data)
effluent.mix_from([self.sparged_gas, -s_consumed, s_produced, *liquid_feeds], energy_balance=False)
if (effluent.mol < 0).any(): breakpoint()
Expand Down
2 changes: 1 addition & 1 deletion thermosteam

0 comments on commit 02c4062

Please sign in to comment.