Skip to content

Commit 5bbacf2

Browse files
committed
add ID inference based on variable assignment
1 parent 403c470 commit 5bbacf2

File tree

8 files changed

+149
-32
lines changed

8 files changed

+149
-32
lines changed

thermosteam/_multi_stream.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"""
99
"""
1010
from __future__ import annotations
11+
import thermosteam as tmo
1112
from ._thermo import Thermo
1213
from ._stream import Stream
1314
from ._thermal_condition import ThermalCondition
@@ -248,6 +249,9 @@ def __init__(self,
248249
if total_flow: self._imol.data *= total_flow / self.F_mol
249250
self._sink = self._source = None
250251
self.reset_cache()
252+
253+
if tmo.preferences.ID_inference and ID == '': ID = tmo.utils.infer_variable_assignment(self.__class__)
254+
251255
self._register(ID)
252256
if vlle: self.vlle(T=T, P=P)
253257

thermosteam/_preferences.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class DisplayPreferences:
4242
tooltips_full_results: False
4343
graphviz_html_height: {'big-system': ('600px', '900px'), 'huge-system': ('800px', '1200px'), 'system': ('400px', '600px'), 'unit': ('225px', '400px')}
4444
show_all_streams: True
45+
ID_inference: False
4546
flow: 'kmol/hr:.3g'
4647
T: 'K:.5g'
4748
P: 'Pa:.6g'
@@ -55,7 +56,7 @@ class DisplayPreferences:
5556
'label_color', 'label_color', 'depth_colors', 'stream_width',
5657
'unit_color', 'unit_label_color', 'unit_periphery_color',
5758
'fill_cluster', 'graphviz_format', 'tooltips_full_results',
58-
'graphviz_html_height', 'show_all_streams')
59+
'graphviz_html_height', 'show_all_streams', 'ID_inference')
5960

6061
def __init__(self):
6162
#: Whether to label the ID of streams with sources and sinks in process
@@ -80,16 +81,16 @@ def __init__(self):
8081
self.raise_exception: bool = False
8182

8283
#: Background color in graphviz diagrams.
83-
self.background_color: str = 'transparent'
84+
self.background_color: str = '#000000'
8485

8586
#: Color of streams in graphviz diagrams.
86-
self.stream_color: str = '#90918e'
87+
self.stream_color: str = '#98a2ad'
8788

8889
#: Color of stream labels in graphviz diagrams.
89-
self.label_color: str = '#90918e'
90+
self.label_color: str = '#e5e5e5'
9091

9192
#: Color of subsystem clusters in BioSTEAM graphviz diagrams.
92-
self.depth_colors: list[str] = ['#f98f609f']
93+
self.depth_colors: list[str] = ['#5172512f', '#1111112f']
9394

9495
#: Property to scale stream widths in BioSTEAM graphviz diagrams.
9596
self.stream_width: str = 'F_mass'
@@ -101,7 +102,7 @@ def __init__(self):
101102
self.unit_label_color: str = 'white'
102103

103104
#: Unit node periphery color in BioSTEAM graphviz diagrams.
104-
self.unit_periphery_color: str = '#90918e'
105+
self.unit_periphery_color: str = 'none'
105106

106107
#: Whether to fill subsystem boxes in BioSTEAM 'cluster' diagrams.
107108
self.fill_cluster: bool = False
@@ -123,6 +124,9 @@ def __init__(self):
123124
#: Whether to show all streams, including empty feeds and products.
124125
self.show_all_streams = True
125126

127+
#: Whether to infer ID of unit operations, streams, and systems by variable name.
128+
self.ID_inference = True
129+
126130
def temporary(self):
127131
"""Return a TemporaryPreferences object that will revert back to original
128132
preferences after context management."""
@@ -217,22 +221,7 @@ def _set_mode(self, stream, label, bg, cluster, unit_color,
217221
self.fill_cluster = fill_cluster
218222
if save: self.save()
219223

220-
def classic_mode(self,
221-
stream='#90918e',
222-
label='#90918e',
223-
bg='transparent',
224-
cluster=('#f98f609f',),
225-
unit_color='#555f69',
226-
unit_label_color='white',
227-
unit_periphery_color='none',
228-
fill_cluster=False,
229-
save=False):
230-
"""Set diagram display colors to classic mode."""
231-
self._set_mode(stream, label, bg, cluster, unit_color,
232-
unit_label_color, unit_periphery_color,
233-
fill_cluster, save)
234-
235-
def dark_mode(self, stream='#98a2ad', label='#e5e5e5', bg='transparent',
224+
def dark_mode(self, stream='#98a2ad', label='#e5e5e5', bg='#000000',
236225
cluster=['#5172512f', '#1111112f'], unit_color='#555f69',
237226
unit_label_color='white', unit_periphery_color='none',
238227
fill_cluster=True, save=False):
@@ -268,7 +257,6 @@ def to_dict(self):
268257
dct['composition'] = self.composition
269258
dct['N'] = self.N
270259
dct['sort'] = self.sort
271-
272260
return dct
273261

274262
def save(self):

thermosteam/_stream.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ def __init__(self, ID: Optional[str] = '',
400400
mol *= total_flow / mol.sum()
401401
self._sink = self._source = None
402402
self.reset_cache()
403+
404+
if tmo.preferences.ID_inference and ID == '': ID = tmo.utils.infer_variable_assignment(self.__class__)
403405
self._register(ID)
404406
if vlle:
405407
self.vlle(T=T, P=P)

thermosteam/network.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,24 +298,33 @@ def __init__(self, size, streams, thermo, fixed_size, stacklevel):
298298
dock = self._dock
299299
redock = self._redock
300300
if streams == ():
301-
self._streams = [dock(Stream(thermo=thermo)) for i in range(size)]
301+
self._streams = [dock(Stream('-', thermo=thermo)) for i in range(size)]
302302
else:
303303
isa = isinstance
304304
if fixed_size:
305305
self._initialize_missing_streams()
306306
if streams is not None:
307307
if isa(streams, str):
308-
self._streams[0] = dock(Stream(streams, thermo=thermo))
308+
self._streams[0] = dock(Stream(streams or '-', thermo=thermo))
309309
elif isa(streams, stream_types):
310310
self._streams[0] = redock(streams, stacklevel)
311311
else:
312312
N = len(streams)
313313
n_missing(size, N) # Make sure size is not too big
314-
self._streams[:N] = [redock(i, stacklevel+1) if isa(i, Stream)
315-
else dock(Stream(i, thermo=thermo)) for i in streams]
314+
loaded_streams = []
315+
for i in streams:
316+
if isa(i, Stream):
317+
s = redock(i, stacklevel)
318+
elif i is None:
319+
s = self._create_missing_stream()
320+
else:
321+
s = Stream(i or '-', thermo=thermo)
322+
dock(s)
323+
loaded_streams.append(s)
324+
self._streams[:N] = loaded_streams
316325
elif streams is not None:
317326
if isa(streams, str):
318-
self._streams = [dock(Stream(streams, thermo=thermo))]
327+
self._streams = [dock(Stream(streams or '-', thermo=thermo))]
319328
elif isinstance(streams, stream_types):
320329
self._streams = [redock(streams, stacklevel)]
321330
else:
@@ -326,7 +335,7 @@ def __init__(self, size, streams, thermo, fixed_size, stacklevel):
326335
elif i is None:
327336
s = self._create_missing_stream()
328337
else:
329-
s = Stream(i, thermo=thermo)
338+
s = Stream(i or '-', thermo=thermo)
330339
dock(s)
331340
loaded_streams.append(s)
332341
else:

thermosteam/preferences.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
ID_inference: true
12
N: 7
23
P: Pa:.6g
34
T: K:.5g
45
autodisplay: true
5-
background_color: transparent
6+
background_color: '#000000'
67
composition: false
78
depth_colors:
89
- '#5172512f'

thermosteam/utils/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from . import plots
1818
from . import stream_filters
1919
from . import abstract_method
20+
from . import variable_assignment_inference
2021

2122
__all__ = (*pickle.__all__,
2223
*representation.__all__,
@@ -28,6 +29,7 @@
2829
*plots.__all__,
2930
*stream_filters.__all__,
3031
*abstract_method.__all__,
32+
*variable_assignment_inference.__all__,
3133
)
3234

3335
from .pickle import *
@@ -39,4 +41,5 @@
3941
from .colors import *
4042
from .plots import *
4143
from .stream_filters import *
42-
from .abstract_method import *
44+
from .abstract_method import *
45+
from .variable_assignment_inference import *

thermosteam/utils/decorators/registered.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _register(self, ID):
5353
if '.' in ID:
5454
if hasattr(self, '_ID') and data.get(ID_old:=self._ID) is self: del data[ID_old]
5555
self._ID = ID.lstrip('.')
56-
elif ID:
56+
elif ID and ID != '-':
5757
registry.register_safely(ID, self)
5858
elif self.autonumber:
5959
ID = self._take_ticket()
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# -*- coding: utf-8 -*-
2+
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
3+
# Copyright (C) 2020, Yoel Cortes-Pena <yoelcortes@gmail.com>
4+
#
5+
# This module is under the UIUC open-source license. See
6+
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
7+
# for license details.
8+
"""
9+
"""
10+
import inspect
11+
import os
12+
import sys
13+
import tokenize
14+
import linecache
15+
16+
__all__ = (
17+
'infer_variable_assignment',
18+
)
19+
20+
cache = {}
21+
max_filecache = 50
22+
def infer_variable_assignment(caller):
23+
# Get frame where item is assigned.
24+
# Stacklevel default to 2, assuming this method is used within `__init__`
25+
frame = inspect.currentframe().f_back.f_back
26+
filename = frame.f_code.co_filename
27+
28+
# This means no variable assignment.
29+
# Registered BioSTEAM objects will know how to handle this.
30+
default = '-'
31+
32+
# Get current line from frame file
33+
try:
34+
lines, variable_cache, crossout = cache[filename]
35+
except:
36+
# If the file fails to load, lines will be None and this is cached.
37+
lines = None
38+
# If using native cache, we replace the lines that failed with None
39+
# to save space and improve speed.
40+
crossout = True
41+
if filename in linecache.cache:
42+
try:
43+
# Must be ipython or notebook session
44+
lines = linecache.cache[filename][2]
45+
crossout = False
46+
except:
47+
lines = None
48+
if lines is None:
49+
if os.path.isabs(filename):
50+
fullname = filename
51+
else:
52+
# Try looking through the module search path, which is only useful
53+
# when handling a relative filename.
54+
for dirname in sys.path:
55+
try: fullname = os.path.join(dirname, filename)
56+
except (TypeError, AttributeError): continue
57+
else: break
58+
try:
59+
with tokenize.open(fullname) as fp: lines = fp.readlines()
60+
except:
61+
pass
62+
# We use variable_cache to cache the lines that we already tried
63+
# and not attempt them again; this improves speed.
64+
variable_cache = None if lines is None else {}
65+
cache[filename] = lines, variable_cache, crossout
66+
if cache.__len__() > max_filecache:
67+
cache.popitem(cache.__iter__().__next__())
68+
if lines is None: return default
69+
n = frame.f_lineno - 1
70+
if n in variable_cache:
71+
return variable_cache[n]
72+
else:
73+
line = lines[n]
74+
75+
# Attempt to infer name
76+
try:
77+
line = line.strip()
78+
if line.startswith('with '): # Context management
79+
callside, nameside = line.split(' as ', 1)
80+
callside = callside[5:].strip() # remove 'with ' and trailing spaces
81+
variable_name = nameside.split(':', 1)[0].strip() # remove ':' and trailing spaces
82+
valid_variable_name = variable_name.isidentifier()
83+
else: # Ordinary variable assignment
84+
nameside, callside = line.split('=', 1)
85+
names = nameside.strip().split('.')
86+
variable_name = names[-1]
87+
valid_variable_name = all([i.isidentifier() for i in names])
88+
if valid_variable_name:
89+
# Make sure not a nested call
90+
callside = callside.strip()
91+
name = callside.split('(', 1)[0]
92+
key, *other = name.split('.')
93+
if key in frame.f_locals:
94+
obj = frame.f_locals[key]
95+
elif key in frame.f_globals:
96+
obj = frame.f_globals[key]
97+
else:
98+
if crossout: lines[n] = None
99+
variable_cache[n] = default
100+
return default # NameError by user
101+
for key in other: obj = getattr(obj, key)
102+
if obj != caller: variable_name = default # No assignment
103+
else:
104+
variable_name = default # No assignment
105+
variable_cache[n] = variable_name
106+
return variable_name
107+
except:
108+
if crossout: lines[n] = None
109+
variable_cache[n] = default
110+
return default # Could not infer name

0 commit comments

Comments
 (0)