Source code for biosteam.evaluation._feature
# -*- 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.
"""
"""
__all__ = ('Feature', 'MockFeature', 'Variable', 'MockVariable')
from ._name import element_name
from ..utils import format_title
[docs]
class Feature:
"""
Abstract class for a feature in BioSTEAM.
Attributes
----------
name : str
Name of feature.
units : str
Units of measure.
element : object
Element corresponding to feature.
"""
__slots__ = ('name', 'units', 'element')
include_units_in_index = True
def __init__(self, name, units, element):
self.name = format_title(name)
self.units = units
self.element = element
def mockup(self):
return MockFeature(self.name, self.units, self.element)
@classmethod
def check_index_unique(cls, feature, features):
key = (feature.element_name, feature.name)
keys = {(i.element_name, i.name) for i in features}
if key in keys:
raise ValueError(
"each feature must have a unique element and name; "
f"feature with element {repr(feature.element)} "
f"and name {repr(feature.name)} already present"
)
@classmethod
def check_indices_unique(cls, features):
keys = set()
for i in features:
key = (i.element, i.name)
if key in keys:
kind = cls.__name__.lower()
raise ValueError(
f"each {kind} must have a unique element and name; "
f"more than one {kind} with element {repr(i.element)} "
f"and name {repr(i.name)} are present"
)
keys.add(key)
@property
def element_name(self):
return element_name(self.element)
@property
def name_with_units(self):
units = self.units
name = self.name
if units: name += f" [{units}]"
return name
@property
def index(self):
name = self.name
if self.include_units_in_index:
units = self.units
if units: name += f" [{units}]"
return (self.element_name, name)
@property
def short_description(self):
element, name = self.index
name, *_ = name.split(' [')
if element not in name:
name = ' '.join([element, name])
if len(name) > 31:
words = name.split(' ')
words = [(i[:4]+'.' if len(i) > 5 else i) for i in words]
name = ' '.join(words)
name = name.strip(' ')
if len(name) > 31: name = name[:31]
return name
[docs]
def describe(self, number_format='.3g', distribution=True, bounds=True) -> str:
"""Return description of feature."""
name = self.name
if self.element:
name = self.element_name + ' - ' + name
description = name
if distribution:
if getattr(self, 'distribution', None):
dist_name = type(self.distribution).__name__
distribution_values = self.distribution._repr.values()
distribution = ', '.join([format(j, number_format)
for j in distribution_values])
distribution = f' ({dist_name}; {distribution})'
description += distribution
elif bounds:
baseline = getattr(self, 'baseline', None)
bounds = getattr(self, 'bounds', None)
if bounds and baseline:
lb, ub = bounds
values = ', '.join([format(i, number_format)
for i in (lb, baseline, ub)])
if self.units:
description += f' ({values} {str(self.units)})'
else:
description += f' ({values})'
if description:
first_letter = description[0]
if first_letter.islower():
description = first_letter.upper() + description[1:]
if not bounds and self.units:
units = (' [' + str(self.units) + ']')
description += units
return description
def __repr__(self):
units = f" ({self.units})" if self.units else ""
element = f" [{self.element_name}]" if self.element else ""
return f'<{type(self).__name__}:{element} {self.name}{units}>'
def show(self):
print(self._info())
class MockFeature(Feature):
__slots__ = ()
def __init__(self, name, units, element):
super().__init__(name, units, element_name(element))
def __repr__(self):
return f"{type(self).__name__}('{self.name}', '{self.units}', '{self.element}')"
Variable = Feature
MockVariable = MockFeature