# -*- 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.
"""
"""
from collections.abc import Iterable
import thermosteam as tmo
__all__ = ('ThermoData',)
# %% Utilities
def as_IDs(IDs):
isa = isinstance
if isa(IDs, Iterable):
for i in IDs:
if not isa(i, str):
raise ValueError('IDs must be an iterable of strings')
else:
raise ValueError('IDs must be an iterable of strings')
return IDs
def chemicals_from_data(data, all_data):
new_chemicals = dict(data)
chemical_copies = {}
for ID, kwargs in data.items():
if kwargs:
if 'Copy of' in kwargs:
chemical_copies[ID] = kwargs
else:
new_chemicals[ID] = tmo.Chemical(ID, **kwargs)
else:
new_chemicals[ID] = tmo.Chemical(ID)
for ID, kwargs in chemical_copies.items():
kwargs = kwargs.copy()
copied_ID = kwargs.pop('Copy of')
try:
copied_chemical = new_chemicals[copied_ID]
except KeyError:
new_chemicals[copied_ID] = copied_chemical = tmo.Chemical(copied_ID, **all_data[copied_ID])
new_chemicals[ID] = copied_chemical.copy(ID, **kwargs)
return tmo.Chemicals([new_chemicals[i] for i in data])
# %%
[docs]
class ThermoData:
"""
Create a ThermoData object for creating thermodynamic property packages
and streams.
Parameters
----------
data : dict
Examples
--------
>>> import thermosteam as tmo
>>> data = {
... 'Chemicals': {
... 'Water': {},
... 'Ethanol': {},
... 'O2': {'phase': 'gas'},
... 'Cellulose': {
... 'search_db': False,
... 'phase': 'solid',
... 'formula': 'C6H10O5',
... 'Hf': -975708.8,
... 'default': True
... },
... 'Octane': {}
... },
... 'Streams': {
... 'process_water': {
... 'Water': 500,
... 'units': 'kg/hr',
... 'price': 0.00035,
... },
... 'gasoline': {
... 'Octane': 400,
... 'units': 'kg/hr',
... 'price': 0.756,
... },
... }
... }
>>> thermo_data = tmo.ThermoData(data)
>>> chemicals = thermo_data.create_chemicals()
>>> chemicals
Chemicals([Water, Ethanol, O2, Cellulose, Octane])
>>> tmo.settings.set_thermo(chemicals)
>>> thermo_data.create_streams()
[<Stream: process_water>, <Stream: gasoline>]
It is also possible to create a ThermoData object from json or yaml files
For example, lets say we have a yaml file that looks like this:
.. code-block:: yaml
# File name: example_chemicals.yaml
Chemicals:
Water:
Ethanol:
O2:
phase: gas
Cellulose:
search_db: False
phase: solid
formula: C6H10O5
Hf: -975708.8
default: True
Octane:
Then we could create the chemicals in just a few lines:
>>> # thermo_data = tmo.ThermoData.from_yaml('example_chemicals.yaml')
>>> # thermo_data.create_chemicals()
>>> # Chemicals([Water, Ethanol, O2, Cellulose, Octane])
"""
__slots__ = ('data',)
def __init__(self, data: dict):
data_fields = ('Chemicals', 'Streams', 'Synonyms')
self.data = {i: data.get(i, {})
for i in data_fields}
[docs]
@classmethod
def from_yaml(cls, file):
"""Create a ThermoData object from a yaml file given its directory."""
import yaml
with open(file, 'r') as stream:
data = yaml.full_load(stream)
assert isinstance(data, dict), 'yaml file must return a dict'
return ThermoData(data)
[docs]
@classmethod
def from_json(cls, file):
"""Create a ThermoData object from a json file given its directory."""
import json
with open(file, 'r') as stream:
data = json.load(stream)
assert isinstance(data, dict), 'json file must return a dict'
return ThermoData(data)
@property
def chemical_data(self):
try: data = self.data['Chemicals']
except KeyError: raise AttributeError('no chemical data available')
return data
@property
def stream_data(self):
try: data = self.data['Streams']
except KeyError: raise AttributeError('no stream data available')
return data
@property
def synonym_data(self):
try: data = self.data['Synonyms']
except KeyError: raise AttributeError('no synonym data available')
return data
[docs]
def create_stream(self, ID):
"""
Create stream from data.
Parameters
----------
ID=None : str
ID of stream to create.
"""
data = self.stream_data[ID]
return tmo.Stream(ID, **data) if data else tmo.Stream(ID)
[docs]
def create_streams(self, IDs=None):
"""
Create streams from data.
Parameters
----------
IDs=None : Iterable[str], optional
IDs of streams to create. Defaults to all streams.
"""
data = self.stream_data
if IDs: data = {i:data[i] for i in as_IDs(IDs)}
return [tmo.Stream(i, **(j or {})) for i, j in data.items()]
[docs]
def create_chemicals(self, IDs=None):
"""
Create chemicals from data.
Parameters
----------
IDs=None : Iterable[str] or str, optional
IDs of chemicals to create. Defaults to all chemicals.
"""
all_data = self.chemical_data
data = all_data if IDs is None else {i: all_data.get(i) for i in as_IDs(IDs)}
return chemicals_from_data(data, all_data)