Skip to content

Commit

Permalink
2.50.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
yoelcortes committed Jan 9, 2025
1 parent 1301905 commit b751f3c
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 30 deletions.
2 changes: 1 addition & 1 deletion biosteam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""
from __future__ import annotations
__version__ = '2.49.4'
__version__ = '2.50.0'

#: Chemical engineering plant cost index (defaults to 567.5 at 2017).
CE: float = 567.5
Expand Down
153 changes: 127 additions & 26 deletions biosteam/process_tools/process_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@
"""
"""
from dataclasses import dataclass
__all__ = ('ProcessModel', 'scenario')
from thermosteam.utils import AbstractMethod
import biosteam as bst

def display_scenario(scenario):
__all__ = ('ProcessModel', 'ScenarioComparison')

def copy(scenario, **kwargs):
for i in scenario.__slots__:
if i not in kwargs: kwargs[i] = getattr(scenario, i)
return scenario.__class__(**kwargs)

def scenario_info(scenario):
slots = scenario.__slots__
units_of_measure = scenario.units_of_measure
arguments = []
Expand All @@ -27,7 +35,7 @@ def display_scenario(scenario):
N_spaces = len(clsname) +1
if N_spaces > 4:
arguments = '\n '.join(arguments)
print(
return (
f"{clsname}("
f"\n {arguments}\n"
")"
Expand All @@ -36,29 +44,119 @@ def display_scenario(scenario):
else:
spaces = N_spaces * ' '
arguments = f'\n{spaces}'.join(arguments)
print(
return (
f"{clsname}({arguments})"

)
else:
print(
return (
f"{type(scenario).__name__}()"
)

def scenario(cls=None, **units_of_measure):
if cls is None: return lambda cls: scenario(cls, **units_of_measure)
cls = dataclass(cls,
init=True, repr=True, eq=True, unsafe_hash=False, frozen=True,
match_args=True, slots=True,
)
cls.units_of_measure = units_of_measure
cls._ipython_display_ = cls.show = display_scenario
return cls
def display_scenario(scenario):
print(scenario_info(scenario))

def iterate_scenario_data(scenario):
for i in scenario.__slots__:
yield (i, getattr(scenario, i))

def scenario_comparison(left, right):
return ScenarioComparison(left, right)

@dataclass(
init=True, repr=True, eq=True,
unsafe_hash=False, frozen=True,
match_args=True, slots=True,
)
class ScenarioComparison:
left: object
right: object

def show(self):
parameters = ',\n'.join(['left=' + scenario_info(self.left), 'right=' + scenario_info(self.right)])
parameters = parameters.replace('\n', '\n ')
return print(
f'{type(self).__name__}(\n'
f' {parameters}\n'
f')'
)
_ipython_display_ = show


class ProcessModel:
"""Abstract class for setting up an all-in-one handle with easy
access to all objects related to a process model, including chemicals,
streams, units, systems, and model parameters and metrics."""
default_scenario = AbstractMethod
create_system = AbstractMethod
create_model = AbstractMethod
as_scenario = AbstractMethod

@classmethod
def scenario_hook(cls, scenario, kwargs):
if scenario is None:
if cls.default_scenario:
scenario = cls.default_scenario()
else:
try:
return cls.Scenario(**kwargs)
except:
raise NotImplementedError('missing class method `default_scenario`')
elif not isinstance(scenario, cls.Scenario):
if cls.as_scenario:
scenario = cls.as_scenario(scenario)
else:
raise NotImplementedError('missing class method `as_scenario`')
if kwargs: scenario = scenario.copy(**kwargs)
return scenario

def __init_subclass__(cls):
cls.cache = {}
if '__new__' in cls.__dict__: return
if not hasattr(cls, 'Scenario'):
raise NotImplementedError(
"ProcessModel sublcass missing a 'Scenario' attribute"
)
if 'Scenario' in cls.__dict__:
Scenario = cls.Scenario
Scenario.units_of_measure = units_of_measure = {}
for i, j in tuple(Scenario.__dict__.items()):
if i.startswith('__'): continue
if isinstance(j, str):
if j[0] == '[' and j[-1] == ']':
delattr(Scenario, i)
units_of_measure[i] = j
if isinstance(j, tuple) and len(j) == 2 and isinstance(j[-1], str):
value, units = j
if units[0] == '[' and units[-1] == ']':
setattr(Scenario, i, value)
units_of_measure[i] = units

# TODO: get defaults and separate units of measure
cls.Scenario = Scenario = dataclass(cls.Scenario,
init=True, repr=True, eq=True, unsafe_hash=False, frozen=True,
match_args=True, slots=True,
)
Scenario.items = iterate_scenario_data
Scenario._ipython_display_ = Scenario.show = display_scenario
Scenario.copy = copy
Scenario.__sub__ = scenario_comparison


def __new__(cls, *, simulate=True, scenario=None, **kwargs):
scenario = cls.scenario_hook(scenario, kwargs)
if scenario in cls.cache: return cls.cache[scenario]
self = super().__new__(cls)
self.scenario = scenario
self.flowsheet = bst.Flowsheet(repr(self))
bst.main_flowsheet.set_flowsheet(self.flowsheet)
system = self.create_system()
self.load_system(system)
model = self.create_model()
self.load_model(model)
if simulate: system.simulate()
cls.cache[scenario] = self
return self

def baseline(self):
sample = self.model.get_baseline_sample()
Expand All @@ -76,9 +174,10 @@ def load_model(self, model):
self.model = model
for i in model.parameters:
setattr(self, i.setter.__name__, i)
if i.baseline is not None: i.setter(i.baseline)
for i in model.metrics:
setattr(self, i.getter.__name__, i)

@property
def parameters(self):
return self.model._parameters
Expand All @@ -87,19 +186,21 @@ def parameters(self):
def metrics(self):
return self.model._metrics

def _repr(self, m):
clsname = type(self).__name__
newline = "\n" + " "*(len(clsname)+2)
return f'{clsname}: {newline.join([i.describe() for i in self.metrics])}'

def __repr__(self):
return f'<{type(self).__name__}: {len(self.parameters)}-parameters, {len(self.metrics)}-metrics>'
scenario = self.scenario
scenario_name = type(scenario).__name__
process_name = type(self).__name__
N = len(scenario_name)
return process_name + repr(self.scenario)[N:]

def _info(self, p, m):
return 'Process' + self.model._info(p, m)
def show(self):
"""Print representation of process model."""
scenario = self.scenario
scenario_name = type(scenario).__name__
process_name = type(self).__name__
N = len(scenario_name)
info = scenario_info(scenario)
print(process_name + info[N:])

def show(self, p=None, m=None):
"""Return information on p-parameters and m-metrics."""
print(self._info(p, m))
_ipython_display_ = show

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
name='biosteam',
packages=['biosteam'],
license='MIT',
version='2.49.4',
version='2.50.0',
description='The Biorefinery Simulation and Techno-Economic Analysis Modules',
long_description=open('README.rst', encoding='utf-8').read(),
author='Yoel Cortes-Pena',
Expand Down
2 changes: 1 addition & 1 deletion thermosteam

0 comments on commit b751f3c

Please sign in to comment.