# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020-2023, 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.
"""
"""
import thermosteam as tmo
from thermosteam.units_of_measure import parse_units_notation
import yaml
import os
__all__ = ('preferences', 'TemporaryPreferences')
[docs]
class DisplayPreferences:
"""
All preferences for BioSTEAM diagram and results display.
Examples
--------
>>> from biosteam import preferences
>>> preferences.show()
DisplayPreferences:
label_streams: True
autodisplay: True
minimal_nodes: False
number_path: False
profile: False
raise_exception: False
background_color: 'transparent'
stream_color: '#90918e'
label_color: '#90918e'
depth_colors: ['#f98f609f']
stream_width: 'F_mass'
unit_color: '#555f69'
unit_label_color: 'white'
unit_periphery_color: '#90918e'
fill_cluster: False
graphviz_format: 'svg'
tooltips_full_results: False
graphviz_html_height: {'big-system': ('600px', '900px'), 'system': ('400px', '600px'), 'unit': ('225px', '400px')}
show_all_streams: True
flow: 'kmol/hr:.3g'
T: 'K:.5g'
P: 'Pa:.6g'
composition: False
N: 7
sort: False
"""
__slots__ = ('label_streams', 'autodisplay', 'minimal_nodes', 'number_path',
'profile', 'raise_exception', 'background_color', 'stream_color',
'label_color', 'label_color', 'depth_colors', 'stream_width',
'unit_color', 'unit_label_color', 'unit_periphery_color',
'fill_cluster', 'graphviz_format', 'tooltips_full_results',
'graphviz_html_height', 'show_all_streams')
def __init__(self):
#: Whether to label the ID of streams with sources and sinks in process
#: flow diagrams.
self.label_streams: bool = True
#: Whether to automatically generate diagrams when displaying an object in the
#: IPython console.
self.autodisplay: bool = True
#: Whether to ignore unit graphics and display unit nodes as dots in process
#: flow diagrams.
self.minimal_nodes: bool = False
#: Whether to number unit operations according to their order in the system path.
self.number_path: bool = False
#: Whether to clock the simulation time of unit operations in diagrams.
self.profile: bool = False
#: Whether to raise exception regarding problems displaying graphviz diagrams.
self.raise_exception: bool = False
#: Background color in graphviz diagrams.
self.background_color: str = 'transparent'
#: Color of streams in graphviz diagrams.
self.stream_color: str = '#90918e'
#: Color of stream labels in graphviz diagrams.
self.label_color: str = '#90918e'
#: Color of subsystem clusters in BioSTEAM graphviz diagrams.
self.depth_colors: list[str] = ['#f98f609f']
#: Property to scale stream widths in BioSTEAM graphviz diagrams.
self.stream_width: str = 'F_mass'
#: Unit node fill color in BioSTEAM graphviz diagrams.
self.unit_color: str = '#555f69'
#: Unit node label color in BioSTEAM graphviz diagrams.
self.unit_label_color: str = 'white'
#: Unit node periphery color in BioSTEAM graphviz diagrams.
self.unit_periphery_color: str = '#90918e'
#: Whether to fill subsystem boxes in BioSTEAM 'cluster' diagrams.
self.fill_cluster: bool = False
#: Image format of BioSTEAM graphviz diagrams.
self.graphviz_format: str = 'svg'
#: Whether to add full results in tooltips by inserting java script into graphviz html outputs.
self.tooltips_full_results: bool = False
#: Displayed height of graphviz html diagrams without and with full results.
self.graphviz_html_height: dict[str, tuple[str, str]] = {
'big-system': ('600px', '900px'),
'system': ('400px', '600px'),
'unit': ('225px', '400px'),
}
#: Whether to show all streams, including empty feeds and products.
self.show_all_streams = True
[docs]
def temporary(self):
"""Return a TemporaryPreferences object that will revert back to original
preferences after context management."""
return TemporaryPreferences()
[docs]
def reset(self, save=False):
"""Reset to BioSTEAM defaults."""
self.__init__()
self.flow = 'kmol/hr'
self.T = 'K'
self.P = 'Pa'
self.composition = False
self.N = 7
self.sort = False
if save: self.save()
@property
def flow(self) -> str:
"""Flow rate units and notation."""
return ":".join([tmo.Stream.display_units.flow, tmo.Stream.display_notation.flow])
@flow.setter
def flow(self, units_notation):
units, notation = parse_units_notation(units_notation)
if units is not None:
tmo.Stream.display_units.flow = units
if notation is not None:
tmo.Stream.display_notation.flow = notation
@property
def T(self) -> str:
"""Temperature units and notation."""
return ":".join([tmo.Stream.display_units.T, tmo.Stream.display_notation.T])
@T.setter
def T(self, units_notation):
units, notation = parse_units_notation(units_notation)
if units is not None:
tmo.Stream.display_units.T = units
if notation is not None:
tmo.Stream.display_notation.T = notation
@property
def P(self) -> str:
"""Pressure units and notation."""
return ":".join([tmo.Stream.display_units.P, tmo.Stream.display_notation.P])
@P.setter
def P(self, units_notation):
units, notation = parse_units_notation(units_notation)
if units is not None:
tmo.Stream.display_units.P = units
if notation is not None:
tmo.Stream.display_notation.P = notation
@property
def composition(self) -> bool:
"""Whether to show composition."""
return tmo.Stream.display_units.composition
@composition.setter
def composition(self, composition):
tmo.Stream.display_units.composition = composition
@property
def sort(self) -> bool:
"""Whether to sort flows in decreasing order."""
try: return tmo.Stream.display_units.sort
except: return False
@sort.setter
def sort(self, sort):
try: tmo.Stream.display_units.sort = sort
except: pass
@property
def N(self) -> int:
"""Number of compounds to display."""
return tmo.Stream.display_units.N
@N.setter
def N(self, N):
tmo.Stream.display_units.N = N
def update(self, *, save=False, **kwargs):
for i, j in kwargs.items(): setattr(self, i, j)
if save: self.save()
def _set_mode(self, stream, label, bg, cluster, unit_color,
unit_label_color, unit_periphery_color, fill_cluster, save):
self.background_color = bg
self.stream_color = stream
self.label_color = label
self.depth_colors = cluster
self.unit_color = unit_color
self.unit_label_color = unit_label_color
self.unit_periphery_color = unit_periphery_color
self.fill_cluster = fill_cluster
if save: self.save()
[docs]
def classic_mode(self,
stream='#90918e',
label='#90918e',
bg='transparent',
cluster=('#f98f609f',),
unit_color='#555f69',
unit_label_color='white',
unit_periphery_color='none',
fill_cluster=False,
save=False):
"""Set diagram display colors to classic mode."""
self._set_mode(stream, label, bg, cluster, unit_color,
unit_label_color, unit_periphery_color,
fill_cluster, save)
[docs]
def dark_mode(self, stream='#98a2ad', label='#e5e5e5', bg='transparent',
cluster=['#5172512f', '#1111112f'], unit_color='#555f69',
unit_label_color='white', unit_periphery_color='none',
fill_cluster=True, save=False):
"""Set diagram display colors to dark mode."""
self._set_mode(stream, label, bg, cluster, unit_color, unit_label_color,
unit_periphery_color, fill_cluster, save)
[docs]
def light_mode(self, stream='#4e4e4e', label='#4e4e4e', bg='#ffffffff',
cluster=['#d5edf02f', '#ffffffdf'], unit_color='white:#CDCDCD',
unit_label_color='black', unit_periphery_color='#4e4e4e',
fill_cluster=True, save=False):
"""Set diagram display colors to light mode."""
self._set_mode(stream, label, bg, cluster, unit_color, unit_label_color,
unit_periphery_color, fill_cluster, save)
night_mode = dark_mode
day_mode = light_mode
def autoload(self):
folder = os.path.dirname(__file__)
file = os.path.join(folder, 'preferences.yaml')
with open(file, 'r') as stream:
data = yaml.full_load(stream)
assert isinstance(data, dict), 'yaml file must return a dict'
self.update(**data)
[docs]
def to_dict(self):
"""Return dictionary of all preferences."""
dct = {i: getattr(self, i) for i in preferences.__slots__}
dct['flow'] = self.flow
dct['T'] = self.T
dct['P'] = self.P
dct['composition'] = self.composition
dct['N'] = self.N
dct['sort'] = self.sort
return dct
[docs]
def save(self):
"""Save preferences."""
folder = os.path.dirname(__file__)
file = os.path.join(folder, 'preferences.yaml')
with open(file, 'w') as file:
dct = self.to_dict()
yaml.dump(dct, file)
[docs]
def show(self):
"""Print all specifications."""
dct = self.to_dict()
print(f'{type(self).__name__}:\n' + '\n'.join([f"{i}: {repr(j)}" for i, j in dct.items()]))
_ipython_display_ = show
class TemporaryPreferences:
def __enter__(self):
dct = self.__dict__
dct.update({i: getattr(preferences, i) for i in preferences.__slots__})
dct['flow'] = preferences.flow
dct['T'] = preferences.T
dct['P'] = preferences.P
dct['composition'] = preferences.composition
dct['N'] = preferences.N
dct['sort'] = preferences.sort
return preferences
def __exit__(self, type, exception, traceback):
preferences.update(**self.__dict__)
if exception: raise exception
#:
preferences: DisplayPreferences = DisplayPreferences()
if os.environ.get("FILTER_WARNINGS"):
from warnings import filterwarnings; filterwarnings('ignore')
if not os.environ.get("DISABLE_PREFERENCES") == "1":
try: preferences.autoload()
except: pass