Git Product home page Git Product logo

thermosteam's Introduction

ThermoSTEAM: BioSTEAM's Premier Thermodynamic Engine

Version_status

Documentation

Supported_versions

license

Coverage

image

Workshops

Join us on Friday, Jan 20, 9:15-10:15am CST, for a BioSTEAM workshop! Email [email protected] for details.

What is ThermoSTEAM?

ThermoSTEAM is a standalone thermodynamic engine capable of estimating mixture properties, solving thermodynamic phase equilibria, and modeling stoichiometric reactions. ThermoSTEAM builds upon chemicals, the chemical properties component of the Chemical Engineering Design Library, with a robust and flexible framework that facilitates the creation of property packages. The Biorefinery Simulation and Techno-Economic Analysis Modules (BioSTEAM) is dependent on ThermoSTEAM for the simulation of unit operations.

Installation

Get the latest version of ThermoSTEAM from PyPI. If you have an installation of Python with pip, simple install it with:

$ pip install thermosteam

To get the git version and install it, run:

$ git clone --depth 100 git://github.com/BioSTEAMDevelopmentGroup/thermosteam
$ cd thermosteam
$ pip install .

We use the depth option to clone only the last 100 commits. Thermosteam has a long history, so cloning the whole repository (without using the depth option) may take over 30 min.

If you would like to clone all branches, add the "--no-single-branch" flag as such:

$ git clone --depth 100 --no-single-branch git://github.com/BioSTEAMDevelopmentGroup/thermosteam

Documentation

ThermoSTEAM's documentation is available on the web:

https://biosteam.readthedocs.io/en/latest/API/thermosteam/index.html

Bug reports

To report bugs, please use the ThermoSTEAM's Bug Tracker at:

https://github.com/BioSTEAMDevelopmentGroup/thermosteam

License information

See LICENSE.txt for information on the terms & conditions for usage of this software, and a DISCLAIMER OF ALL WARRANTIES.

Although not required by the ThermoSTEAM license, if it is convenient for you, please cite ThermoSTEAM if used in your work. Please also consider contributing any changes you make back, and benefit the community.

Citation

To cite ThermoSTEAM in publications use:

Cortes-Pena, Y., (2020). ThermoSTEAM: BioSTEAM's Premier Thermodynamic Engine. 
Journal of Open Source Software, 5(56), 2814. doi.org/10.21105/joss.02814

thermosteam's People

Contributors

fwitte avatar lkudli2 avatar sarangbhagwat avatar yalinli2 avatar yoelcortes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

thermosteam's Issues

Very large download size of the git repo

Cloning the repo took quite a while as some files in the git history seem to be very large (total repo size > 1.5 GB, which seems excessive to me).
This is not part of the JOSS paper review, but I think, it could be a good idea, to see what one can do about this. Unfortunately, I cannot help you here as I have no experience on this topic, other than that history manipulation usually is considered bad. Basically, just wanted to let you know.
Best regards
Francesco

Extras_require missing sympy

Hi,
running the tests of an installation with pip install -e .[dev] is not able to run some tests in thermosteam/eos.py due to the package sympy missing. After installation the tests run successfully, but you could add sympy to extras_require, I suggest.
Best

Stream.proxy

Hey @yoelcortes When creating a proxy (through either proxy or flow_proxy) of one stream, I noticed that the _link of the new stream is set to the original stream, while the _link of the original stream is still not changed, is this intentional?

new._link = self

One counter-argument could be that what if the original stream itself is a proxy? I guess in that case you could set the new stream's link to the original stream's link?

Also, I think it'll be beneficial to note in the doc of proxy that the price will not be kept as the same of the time, I couldn't think of a way to keep the price synced as all times without invoking a while loop to check the _link of all streams

Happy to discuss, thanks!

Load chemicals/streams from YAML/JSON

Currently we define all chemicals and streams in a module, but it would also be nice to load them from a json or yaml file. For example, let's say we have a yaml file that looks like this:

Chemicals:
  Water:
  Ethanol:
  O2:
    phase: gas
  Cellulose:
    search_db: False
    phase: solid
    formula: C6H10O5
    Hf: -975708.8
    default: True
  Octane:
Synonyms: 
  Water: H2O
  Ethanol: 
    - CH3CH2OH
    - EthylAlcohol
Streams:
  process_water:
    Water: 500
    units: kg/hr
    price: 0.00035
  gasoline:
    Octane: 400
    units: kg/hr
    price: 0.756

We could then load the file with thermosteam in just one line, and create a ThermoData object to help creating only the objects we want. The new ThermoData object would enable users to extend the chemicals before creating streams, or choose which chemicals or streams to create. For example, we could create chemicals as follows:

>>> import thermosteam as tmo
>>> thermo_data = tmo.ThermoData.from_yaml(file_path) # A from_json method would also be available
>>> thermo_data.create_chemicals() 
CompiledChemicals([Water, Ethanol, O2, Cellulose])
>>> # Or alternatively, just load a few
>>>  thermo_data.create_chemicals(['Water', 'Cellulose']) 
CompiledChemicals([Water, Cellulose])

This would make creating thermodynamic property package much cleaner. For comparison, in a python module, we would have created these chemicals as follows:

import thermosteam as tmo
chemicals = tmo.Chemicals(['Water', 'Ethanol', 'O2'])
Cellulose = Chemical.blank('Cellulose', phase='s', formula='C6H10O5', Hf=-975708.8)
Cellulose.default()
chemicals.append(Cellulose)
chemicals.O2.at_state('g')
chemicals.compile()
chemicals.set_synonym('Water', 'H2O')

As for streams, we could create them in a similar fashion:

>>> # To start creating streams; we need to set the thermo package
>>> tmo.settings.set_thermo(chemicals)
>>> # Now we can create all the streams
>>> streams = thermo_data.create_streams()
>>> streams
[<Stream: process_water>, <Stream: gasoline>]
>>> # Alternatively, choose to create the streams we want
>>> gasoline = thermo_data.create_streams('gasoline')
>>> gasoline.show(flow='kg/hr')
Stream: gasoline
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kg/hr): Octane  400

Hopefully this can be done within the next week or two. I think it would be very helpful for sharing the data we use in thermosteam (instead of sharing python modules), and would make collaboration even easier. Thumbs up if you feel this would be a good addition :-) @Scheideba @yalinli2 @sarangbhagwat

Solubility of solid does not follow consistent direction of temperature dependency

Describe the bug
The solubility of a triacetic acid lactone in water, calculated through SLE, first decreases and then increases with increase in temperature. Additionally, the changes in solubility are very sharp at certain points.

To Reproduce

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 23 12:11:15 2020

Modified from the cornstover biorefinery constructed in Cortes-Peña et al., 2020,
with modification of fermentation system for 2,3-Butanediol instead of the original ethanol

[1] Cortes-Peña et al., BioSTEAM: A Fast and Flexible Platform for the Design, 
    Simulation, and Techno-Economic Analysis of Biorefineries under Uncertainty. 
    ACS Sustainable Chem. Eng. 2020, 8 (8), 3302–3310. 
    https://doi.org/10.1021/acssuschemeng.9b07040.

All units are explicitly defined here for transparency and easy reference

@author: sarangbhagwat
"""

# %%  

# =============================================================================
# Setup
# =============================================================================

import thermosteam as tmo
from thermosteam import functional as fn

__all__ = ('TAL_chemicals', 'chemical_groups', 'soluble_organics', 'combustibles')

# chems is the object containing all chemicals used in this biorefinery
chems = TAL_chemicals = tmo.Chemicals([])

# To keep track of which chemicals are available in the database and which
# are created from scratch
database_chemicals_dict = {}
copied_chemicals_dict = {}
defined_chemicals_dict = {}

def chemical_database(ID, phase=None, **kwargs):
    chemical = tmo.Chemical(ID, **kwargs)
    if phase:
        chemical.at_state(phase)
        chemical.phase_ref = phase
    chems.append(chemical)
    database_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

def chemical_copied(ID, ref_chemical, **data):
    chemical = ref_chemical.copy(ID)
    chems.append(chemical)
    for i, j in data.items(): setattr(chemical, i, j)
    copied_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

def chemical_defined(ID, **kwargs):
    chemical = tmo.Chemical.blank(ID, **kwargs)
    chems.append(chemical)
    defined_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

_cal2joule = 4.184


# %% 

# =============================================================================
# Create chemical objects available in database
# Some common names might not be pointing to the correct chemical,
# therefore more accurate ones were used (e.g. NitricOxide was used instead of NO),
# data from Humbird et al. unless otherwise noted
# =============================================================================

H2O = chemical_database('H2O')

# =============================================================================
# Gases
# =============================================================================

O2 = chemical_database('O2', phase='g', Hf=0)
N2 = chemical_database('N2', phase='g', Hf=0)
H2 = chemical_database('H2', phase='g')
CH4 = chemical_database('CH4', phase='g')
CarbonMonoxide = chemical_database('CarbonMonoxide', phase='g', 
                                        Hf=-26400*_cal2joule)
CO2 = chemical_database('CO2', phase='g')
NH3 = chemical_database('NH3', phase='g', Hf=-10963*_cal2joule)
NitricOxide = chemical_database('NitricOxide', phase='g')
NO2 = chemical_database('NO2', phase='g')
H2S = chemical_database('H2S', phase='g', Hf=-4927*_cal2joule)
SO2 = chemical_database('SO2', phase='g')

# =============================================================================
# Soluble inorganics
# =============================================================================

H2SO4 = chemical_database('H2SO4', phase='l')
HCl = chemical_copied('HCl', H2SO4) # HCl giving errors; doesn't change things much for TAL SA biorefinery
HNO3 = chemical_database('HNO3', phase='l', Hf=-41406*_cal2joule)
NaOH = chemical_database('NaOH', phase='l')
KOH = chemical_database('KOH', phase = 's')

KCl = chemical_database('KCl', phase = 's')
# Arggone National Lab active thermochemical tables, accessed 04/07/2020
# https://atct.anl.gov/Thermochemical%20Data/version%201.118/species/?species_number=928
AmmoniumHydroxide = chemical_database('AmmoniumHydroxide', phase='l', Hf=-336.719e3)
CalciumDihydroxide = chemical_database('CalciumDihydroxide',
                                        phase='s', Hf=-235522*_cal2joule)
AmmoniumSulfate = chemical_database('AmmoniumSulfate', phase='l',
                                    Hf=-288994*_cal2joule)
NaNO3 = chemical_database('NaNO3', phase='l', Hf=-118756*_cal2joule)
# NIST https://webbook.nist.gov/cgi/cbook.cgi?ID=C7757826&Mask=2, accessed 04/07/2020
Na2SO4 = chemical_database('Na2SO4', phase='l', Hf=-1356.38e3)
# CaSO4 = chemical_database('CaSO4', phase='s', Hf=-342531*_cal2joule)
# The default Perry 151 model has a crazy value, use another model instead
# CaSO4.Cn.move_up_model_priority('Constant', 0)
# 

# =============================================================================
# Soluble organic salts
# =============================================================================

Ethanol = chemical_database('Ethanol')
Acetate = chemical_database('Acetate', phase='l', Hf=-108992*_cal2joule)
AmmoniumAcetate = chemical_database('AmmoniumAcetate', phase='l', 
                                         Hf=-154701*_cal2joule)

# Hf from a Ph.D. dissertation (Lactic Acid Production from Agribusiness Waste Starch
# Fermentation with Lactobacillus Amylophilus and Its Cradle-To-Gate Life 
# Cycle Assessment as A Precursor to Poly-L-Lactide, by Andréanne Harbec)
# The dissertation cited Cable, P., & Sitnai, O. (1971). The Manufacture of 
# Lactic Acid by the Fermentation of Whey: a Design and Cost Study. 
# Commonwealth Scientific and Industrial Research Organization, Australia, 
# which was also cited by other studies, but the origianl source cannot be found online
CalciumLactate = chemical_database('CalciumLactate', phase='l',
                                   Hf=-1686.1e3)
# Hf from Lange's Handbook of Chemistry, 15th edn., Table 6.3, PDF page 631
CalciumAcetate = chemical_database('CalciumAcetate', phase='l', Hf=-1514.73e3)

# Solubility of CalciumSuccinate is 3.2 g/L in water as Ca2+ based on 
# Burgess and Drasdo, Polyhedron 1993, 12 (24), 2905–2911, which is 12.5 g/L as CaSA
# Baseline CalciumSuccinate is ~14 g/L in fermentation broth, thus assumes all 
# CalciumSuccinate in liquid phase
CalciumSuccinate = chemical_database('CalciumSuccinate', phase='l')

# =============================================================================
# Soluble organics
# =============================================================================

AceticAcid = chemical_database('AceticAcid')
Glucose = chemical_database('Glucose', phase = 'l')

# TAL = chemical_database('Triacetic acid lactone')

# SA = chemical_database('Sorbic acid')
# KSA = chemical_database('Potassium sorbate')
# BSA = chemical_database('Butyl sorbate')

IBA = chemical_database('Isobutyraldehyde')
DPHP = chemical_database('Dipotassium hydrogen phosphate', phase = 'l')

# This one is more consistent with others
# try: Glucose.Cn.l.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
# except: Glucose.Cn.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
GlucoseOligomer = chemical_defined('GlucoseOligomer', phase='l', formula='C6H10O5',
                                   Hf=-233200*_cal2joule)
GlucoseOligomer.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn', 'mu', 'kappa'])
Extract = chemical_copied('Extract', Glucose)

Xylose = chemical_database('Xylose')
Xylose.copy_models_from(Glucose, ['Hvap', 'Psat', 'mu'])
XyloseOligomer = chemical_defined('XyloseOligomer', phase='l', formula='C5H8O4',
                                  Hf=-182100*_cal2joule)
XyloseOligomer.copy_models_from(Xylose, ['Hvap', 'Psat', 'Cn', 'mu'])

Sucrose = chemical_database('Sucrose', phase='l')
Sucrose.Cn.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
Cellobiose = chemical_database('Cellobiose', phase='l', Hf=-480900*_cal2joule)

Mannose = chemical_database('Mannose', phase='l', Hf=Glucose.Hf)
Mannose.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn', 'mu'])
MannoseOligomer = chemical_copied('MannoseOligomer', GlucoseOligomer)

Galactose = chemical_database('Galactose', phase='l', Hf=Glucose.Hf)
Galactose.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn','mu'])
GalactoseOligomer = chemical_copied('GalactoseOligomer', GlucoseOligomer)

Arabinose = chemical_database('Arabinose', phase='l', Hf=Xylose.Hf)
Arabinose.copy_models_from(Xylose, ['Hvap', 'Psat', 'mu'])
ArabinoseOligomer = chemical_copied('ArabinoseOligomer', XyloseOligomer)

SolubleLignin = chemical_database('SolubleLignin', search_ID='Vanillin', 
                                  phase='l', Hf=-108248*_cal2joule)
Protein = chemical_defined('Protein', phase='l', 
                           formula='CH1.57O0.31N0.29S0.007', 
                           Hf=-17618*_cal2joule)
Enzyme = chemical_defined('Enzyme', phase='l', 
                           formula='CH1.59O0.42N0.24S0.01', 
                           Hf=-17618*_cal2joule)
# Properties of fermentation microbes copied from Z_mobilis as in Humbird et al.
FermMicrobe = chemical_defined('FermMicrobe', phase='l',
                      formula='CH1.8O0.5N0.2', Hf=-31169.39*_cal2joule)
WWTsludge = chemical_defined('WWTsludge', phase='s', 
                             formula='CH1.64O0.39N0.23S0.0035', 
                             Hf=-23200.01*_cal2joule)

Furfural = chemical_database('Furfural')


Acetoin = chemical_database('3-Hydroxybutanone', phase = None, Hvap = 44.56*1000) # , V = 89.5e-6
Acetoin.copy_models_from(Furfural, ['Psat', 'Cn', 'mu', 'kappa', 'V'])
Acetoin.Tb = 145.4 + 273.15


Hexanol = chemical_database('Hexanol')
Heptane = chemical_database('Heptane')
Toluene = chemical_database('Toluene')
# Tb from chemspider(chemenu database)
# http://www.chemspider.com/Chemical-Structure.207215.html, accessed 04/07/2020
# https://www.chemenu.com/products/CM196167, accessed 04/07/2020
# Using Millipore Sigma's Pressure-Temperature Nomograph Interactive Tool at
# https://www.sigmaaldrich.com/chemistry/solvents/learning-center/nomograph.html,
# will give ~300°C at 760 mmHg if using the 115°C Tb at 1 mmHg (accessed 04/07/2020)
# Hfus from NIST, accessed 04/24/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C67470&Mask=4
HMF = chemical_database('HMF', Hf=-99677*_cal2joule, Tb=291.5+273.15, Hfus=19800)
HMF.copy_models_from(Furfural, ['V', 'Hvap', 'Psat', 'mu', 'kappa'])
HMF.Dortmund.update(chems.Furfural.Dortmund)

# TAL = chemical_copied('Triacetic acid lactone', HMF)
# SA = chemical_copied('Sorbic acid', HMF)
# KSA = chemical_copied('Potassium sorbate', HMF)
# BSA = chemical_copied('Butyl sorbate', HMF)

TAL = Triaceticacidlactone = chemical_database('Triacetic acid lactone')
TAL.copy_models_from(Furfural, ['Psat', 'Hvap']) # doesn't matter, since we never boil TAL
SA = Sorbicacid =  chemical_database('Sorbic acid')
KSA = Potassiumsorbate = chemical_database('Potassium sorbate')
BSA = Butylsorbate = chemical_database('Butyl sorbate')
HMTHP = chemical_copied('HMTHP', TAL)

TAL.Hfus = Furfural.Hfus/2.4 # !!! matters for solubility; update 
TAL.Tm = KSA.Tm = 185 + 273.15
TAL.Tb = KSA.Tb =  239.1 + 273.15

# https://pubchem.ncbi.nlm.nih.gov/compound/Sorbic-acid#section=Stability-Shelf-Life
SA.Tb = 228 + 273.15

BSA.Tm = 130 + 273.15
BSA.Tb = 226.5 + 273.15

VitaminA = chemical_database('VitaminA')
VitaminD2 = chemical_database('VitaminD2')
# Hfus from NIST, condensed phase, accessed 04/07/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C87990&Mask=4
Xylitol = chemical_database('Xylitol', phase='l', Hf=-243145*_cal2joule, Hfus=-1118.6e3)

# Hfus from NIST, accessed 04/07/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C50215&Mask=4
# LacticAcid = chemical_database('LacticAcid', Hfus=11.34e3)
LacticAcid = chemical_database('LacticAcid')
LacticAcid.Hfus = 11.34e3

SuccinicAcid = chemical_database('SuccinicAcid', phase_ref='s')

EthylAcetate = chemical_database('EthylAcetate')
# Hf from DIPPR value in Table 3 of Vatani et al., Int J Mol Sci 2007, 8 (5), 407–432
EthylLactate = chemical_database('EthylLactate', Hf=-695.08e3)

EthylSuccinate = chemical_database('EthylSuccinate')
# Cannot find data on Hf of CalciumSuccinate, estimate here assuming
# Hrxn for Ca(OH)2 and SA and Ca(OH)2 and LA are the same 
CalciumSuccinate.Hf = CalciumLactate.Hf + (SuccinicAcid.Hf-2*LacticAcid.Hf)


# =============================================================================
# Insoluble organics
# =============================================================================

Glucan = chemical_defined('Glucan', phase='s', formula='C6H10O5', Hf=-233200*_cal2joule)
Glucan.copy_models_from(Glucose, ['Cn'])
Mannan = chemical_copied('Mannan', Glucan)
Galactan = chemical_copied('Galactan', Glucan)

Xylan = chemical_defined('Xylan', phase='s', formula='C5H8O4', Hf=-182100*_cal2joule)
Xylan.copy_models_from(Xylose, ['Cn'])
Arabinan = chemical_copied('Arabinan', Xylan)

Lignin = chemical_database('Lignin', phase='s')
# Hf scaled based on vanillin
Lignin.Hf = -108248*_cal2joule/tmo.Chemical('Vanillin').MW*Lignin.MW

# =============================================================================
# Insoluble inorganics
# =============================================================================

# Holmes, Trans. Faraday Soc. 1962, 58 (0), 1916–1925, abstract
# This is for auto-population of combustion reactions
P4O10 = chemical_database('P4O10', phase='s', Hf=-713.2*_cal2joule)
Ash = chemical_database('Ash', search_ID='CaO', phase='s', Hf=-151688*_cal2joule,
                        HHV=0, LHV=0)
# This is to copy the solid state of Xylose,
# cannot directly use Xylose as Xylose is locked at liquid state now
Tar = chemical_copied('Tar', Xylose, phase_ref='s')

# =============================================================================
# Mixtures
# =============================================================================

# CSL is modeled as 50% water, 25% protein, and 25% lactic acid in Humbird et al.,
# did not model separately as only one price is given
CSL = chemical_defined('CSL', phase='l', formula='CH2.8925O1.3275N0.0725S0.00175', 
                      Hf=Protein.Hf/4+H2O.Hf/2+LacticAcid.Hf/4)

# Boiler chemicals includes amine, ammonia, and phosphate,
# did not model separately as composition unavailable and only one price is given
BoilerChems = chemical_database('BoilerChems', search_ID='DiammoniumPhosphate',
                                phase='l', Hf=0, HHV=0, LHV=0)

# =============================================================================
# Filler
# =============================================================================

BaghouseBag = chemical_defined('BaghouseBag', phase='s', MW=1, Hf=0, HHV=0, LHV=0)
BaghouseBag.Cn.add_model(0)
CoolingTowerChems = chemical_copied('CoolingTowerChems', BaghouseBag)

# =============================================================================
# Not currently in use
# =============================================================================

DAP = chemical_database('DAP', search_ID='DiammoniumPhosphate',
                             phase='l', Hf= -283996*_cal2joule)
Methanol = chemical_database('Methanol')
MethylAcetate = chemical_database('MethylAcetate')
Denaturant = chemical_database('Denaturant', search_ID='n-Heptane')
DenaturedEnzyme = chemical_copied('DenaturedEnzyme', Enzyme)

# Hf from DIPPR value in Table 3 of Vatani et al., Int J Mol Sci 2007, 8 (5), 407–432
MethylLactate = chemical_database('MethylLactate', Hf=-643.1e3)
FermMicrobeXyl = chemical_copied('FermMicrobeXyl', FermMicrobe)


# %% 

# =============================================================================
# Group chemicals
# =============================================================================

#!!! Sarang please review and update this dict, it affects simulation
chemical_groups = dict(
    OtherSugars = ('Arabinose', 'Mannose', 'Galactose', 'Cellobiose', 'Sucrose'),
    SugarOligomers = ('GlucoseOligomer', 'XyloseOligomer', 'GalactoseOligomer',
                      'ArabinoseOligomer', 'MannoseOligomer'),
    OrganicSolubleSolids = ('AmmoniumAcetate', 'SolubleLignin', 'Extract', 'CSL',
                            # 'Triacetic acid lactone',
                            'Sorbic acid', 'HMTHP',
                            'Potassium sorbate', 'Butyl sorbate', 'VitaminA', 'VitaminD2'),
                            # 'LacticAcid', 'CalciumLactate', 'CalciumAcetate',
                            # 'EthylLactate', 'EthylAcetate', 'SuccinicAcid',
                            # 'CalciumSuccinate', 'EthylSuccinate', 
                            # 'Methanol', 'MethylLactate', 'MethylAcetate'),
    InorganicSolubleSolids = ('AmmoniumSulfate', 'NaOH', 'HNO3', 'NaNO3',
                              # 'DAP',
                              'BoilerChems', 'Na2SO4', 'AmmoniumHydroxide'),
    Furfurals = ('Furfural', 'HMF'),
    #!!! I suspect you want to add some chemicals here
    OtherOrganics = ('Denaturant', 'Xylitol'),
    COSOxNOxH2S = ('NitricOxide', 'NO2', 'SO2', 'CarbonMonoxide', 'H2S'),
    Proteins = ('Protein', 'Enzyme', 'DenaturedEnzyme'),
    CellMass = ('WWTsludge', 'FermMicrobe'),
                # 'FermMicrobeXyl'),
    # Theoretically P4O10 should be soluble, but it's the product of the
    # auto-populated combusion reactions so should in solid phase, however no
    # P4O10 will be generated in the system as no P-containing chemicals 
    # are included in "combustibles"
    OtherInsolubleSolids = ('Tar', 'Ash', 'CalciumDihydroxide', 'P4O10',
                            'BaghouseBag', 'CoolingTowerChems'),
    OtherStructuralCarbohydrates = ('Glucan', 'Xylan', 'Lignin', 'Arabinan', 
                                    'Mannan', 'Galactan'),
    SeparatelyListedOrganics = ('Ethanol', 'Glucose', 'Xylose', 'AceticAcid',
                                'Acetate', 'Lignin'),
    SpearatedlyListedOthers = ('H2O', 'NH3', 'H2SO4', 'CO2', 'CH4', 'O2', 'N2')
    )

# This group is needed in the system.py module
soluble_groups = ('OtherSugars', 'SugarOligomers', 'OrganicSolubleSolids',
                  'Furfurals', 'OtherOrganics', 'Proteins', 'CellMass',
                  'SeparatelyListedOrganics')
soluble_organics = list(sum([chemical_groups[i] for i in soluble_groups], ()))
soluble_organics.remove('WWTsludge')

solubles = tuple(soluble_organics) + chemical_groups['InorganicSolubleSolids'] + ('H2SO4',)

insoluble_groups = ('OtherInsolubleSolids', 'OtherStructuralCarbohydrates')
insolubles = sum([chemical_groups[i] for i in insoluble_groups], ('WWTsludge',))

# This group is needed in the system.py module
combustibles = soluble_organics + list(chemical_groups['OtherStructuralCarbohydrates'])
# combustibles.remove('CalciumLactate')
# combustibles.remove('CalciumAcetate')
combustibles.extend(['WWTsludge','NH3', 'NitricOxide', 'CarbonMonoxide', 'H2S', 'CH4'])

# Chemicals that will be modeled in Distallation/Flash units,
# list is in ascending order of Tb,
# Xylitol is not included due to high Tm and Tb thus will stay in liquid phase


# phase_change_chemicals = ['Methanol', 'Ethanol', 'H2O', 'EthylAcetate', 'Denaturant',
#                           'AceticAcid', 'MethylAcetate', 'MethylLactate',
#                           'EthylLactate', 'Furfural', 'SuccinicAcid', 'LacticAcid', 'HMF']

#!!! Sarang please review and update this, I'm not sure what chemicals are used
# in the biorefinery, getting rid of unused chemicals (i.e., exclude them from chems)
# should help reduce simulation time
phase_change_chemicals = ['Methanol', 'Ethanol', 'H2O', 'EthylAcetate', 'Denaturant',
                          'AceticAcid', 'MethylAcetate', 'MethylLactate',
                          'EthylLactate', 'Furfural', 'EthylSuccinate',
                          'SuccinicAcid', 'LacticAcid', 'HMF']

for chem in chems:
    if chem.ID in phase_change_chemicals: pass
    elif chem.locked_state: pass
    else: 
        # Set phase_ref to avoid missing model errors
        if chem.phase_ref == 'g':
            chem.at_state('g')
        if chem.ID in solubles:
            chem.phase_ref = 'l'
            chem.at_state('l')
        if chem.ID in insolubles:
            chem.phase_ref = 's'
            chem.at_state('s')


# %% 

# =============================================================================
# Set assumptions/estimations for missing properties
# =============================================================================

# Set chemical heat capacity
# Cp of biomass (1.25 J/g/K) from Leow et al., Green Chemistry 2015, 17 (6), 3584–3599
for chemical in (CSL, Protein, Enzyme, WWTsludge, 
                 DenaturedEnzyme, FermMicrobe, FermMicrobeXyl):
    chemical.Cn.add_model(1.25*chemical.MW)

# Set chemical molar volume following assumptions in lipidcane biorefinery,
# assume densities for solulables and insolubles to be 1e5 and 1540 kg/m3, respectively
# !!! This has significant impacts on results, need to double-check accuracy
def set_rho(chemical, rho):       
    V = fn.rho_to_V(rho, chemical.MW)
    chemical.V.add_model(V, top_priority=True)

for chemical in chems:
    if chemical.ID in phase_change_chemicals: pass
    elif chemical.ID in solubles: set_rho(chemical, 1e5)
    elif chemical.ID in insolubles: set_rho(chemical, 1540)

# The Lakshmi Prasad model gives negative kappa values for some chemicals
for chemical in chems:
    if chemical.locked_state:
        try: chemical.kappa.move_up_model_priority('Lakshmi Prasad', -1)
        except: pass
        
# Default missing properties of chemicals to those of water,
for chemical in chems: chemical.default()


# %%

# Though set_thermo will first compile the Chemicals object,
# compile beforehand is easier to debug because of the helpful error message
chems.compile()
tmo.settings.set_thermo(chems)
chems.set_synonym('CalciumDihydroxide', 'Lime')
chems.set_synonym('3-Hydroxybutanone', 'Acetoin')
chems.set_synonym('Triacetic acid lactone', 'TAL')
chems.set_synonym('Triacetic acid lactone', 'Triaceticacidlactone')
chems.set_synonym('Sorbic acid', 'SA')
chems.set_synonym('Sorbic acid', 'Sorbicacid')
chems.set_synonym('Potassium sorbate', 'KSA')
chems.set_synonym('Potassium sorbate', 'Potassiumsorbate')
chems.set_synonym('Butyl sorbate', 'BSA')
chems.set_synonym('Butyl sorbate', 'Butylsorbate')
chems.set_synonym('Dipotassium hydrogen phosphate', 'DPHP')
chems.set_synonym('H2O', 'Water')
chems.set_synonym('H2SO4', 'SulfuricAcid')
chems.set_synonym('NH3', 'Ammonia')
chems.set_synonym('AmmoniumSulfate', 'NH4SO4')
chems.set_synonym('Denaturant', 'Octane')
chems.set_synonym('CO2', 'CarbonDioxide')
chems.set_synonym('CarbonMonoxide', 'CO')
chems.set_synonym('NitricOxide', 'NO')
# chems.set_synonym('CaSO4', 'Gypsum')
chems.set_synonym('P4O10', 'PhosphorusPentoxide')
chems.set_synonym('Na2SO4', 'SodiumSulfate')
chems.set_synonym('AmmoniumHydroxide', 'NH4OH')
chems.set_synonym('Isobutyraldehyde', 'IBA')

# %%

# from TAL.utils import get_chemical_properties	
# get_chemical_properties(chems, 400, 101325, output=True)


>>> from thermosteam import indexer, equilibrium, settings
>>> imass = indexer.MolarFlowIndexer(l=[('Triacetic acid lactone', 304), ('Water', 1000)], phases=('s', 'l'))
>>> sle = equilibrium.SLE(imass)
>>> sle('Triacetic acid lactone', T=193.15)
>>> sle
>>> sle('Triacetic acid lactone', T=293.15)
>>> sle
>>> sle('Triacetic acid lactone', T=298.15)
>>> sle
>>> sle('Triacetic acid lactone', T=450)
>>> sle

Expected behavior
If the heat given off in the dissolving reaction is less than the heat required to break apart the solid, the net dissolving reaction is endothermic and solubility increases with temperature; if the opposite is true, solubility decreases with temperature. I don't think the trend changes at different temperature ranges.

Actual behavior
SLE(imol=MolarFlowIndexer(phases=('l', 's'),
l=[('H2O', 1000), ('Triacetic acid lactone', 304)]),
thermal_condition=ThermalCondition(T=193.15, P=101325))

SLE(imol=MolarFlowIndexer(phases=('l', 's'),
l=[('H2O', 1000), ('Triacetic acid lactone', 304)]),
thermal_condition=ThermalCondition(T=293.15, P=101325))

SLE(imol=MolarFlowIndexer(
l=[('H2O', 1000), ('Triacetic acid lactone', 8.457)],
s=[('Triacetic acid lactone', 295.5)]),
thermal_condition=ThermalCondition(T=298.15, P=101325))

SLE(imol=MolarFlowIndexer(phases=('l', 's'),
l=[('H2O', 1000), ('Triacetic acid lactone', 304)]),
thermal_condition=ThermalCondition(T=450.00, P=101325))

Version
Latest github master version as of 8:56 PM, 11/7/2020.

Creating a custom component

Can one explain how to create a custom component and their properties in thermosteam? Just a basic example will suffice.

Enthalpy calculation

Description
For the following code, I'm not sure why having HMF in the stream raises error when calculating enthalpy, especially when I used copy_missing_slots_from function

import thermosteam as tmo

chems = tmo.Chemicals(['Water', 'Ethanol', 'CH4', 'HMF', 'Furfural'])
tmo.settings.set_thermo(chems)

chems.HMF.Hfus = 19.8
chems.Furfural.Hfus = 14370
chems.HMF.copy_missing_slots_from(chems.Furfural)

ms_no_HMF = tmo.Stream('ms_no_HMF', units='kmol/hr', T=300, P=101325,
                       Water=4.649e+04, Ethanol=50, CH4=800, Furfural=0.5)
ms_with_HMF = tmo.Stream('ms_with_HMF', units='kmol/hr', T=300, P=101325,
                         Water=4.649e+04, Ethanol=50, CH4=800, Furfural=0.5,
                         HMF=0.5)

print('No HMF: '+str(ms_no_HMF.H))
print('With HMF: '+str(ms_with_HMF.H))

Screen Shot 2020-04-21 at 4 58 55 PM

What confuses me most, is that in return H_ref + H_int_T_ref_to_Tm_s + Hfus + Cn_l.integrate_by_T(Tm, T), seems like Hfus is None (I let the function print H_ref, H_int_T_ref_to_Tm_s, Hfus, and Cn_l.integrate_by_T(Tm, T)), but:

for chem in chems:
    print(str(chem.ID)+': '+str(chem.Hfus))

All chemicals have Hfus
Screen Shot 2020-04-21 at 5 04 22 PM

Environment:
OS: macOS Catalina 10.15.4
biosteam: v2.12.8
thermosteam: v0.12.16

Thanks!!!

Index error for sle involving chemicals that used copy_models_from

For SLE, using a chemical that uses the copy_models_from function -- in this case, setting the Psat and Hvap of triacetic acid lactone to those of water -- gives an index error as described below (after the two relevant blocks of code):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 23 12:11:15 2020

Modified from the cornstover biorefinery constructed in Cortes-Peña et al., 2020,
with modification of fermentation system for 2,3-Butanediol instead of the original ethanol

[1] Cortes-Peña et al., BioSTEAM: A Fast and Flexible Platform for the Design, 
    Simulation, and Techno-Economic Analysis of Biorefineries under Uncertainty. 
    ACS Sustainable Chem. Eng. 2020, 8 (8), 3302–3310. 
    https://doi.org/10.1021/acssuschemeng.9b07040.

All units are explicitly defined here for transparency and easy reference

@author: sarangbhagwat
"""

# %%  

# =============================================================================
# Setup
# =============================================================================

import thermosteam as tmo
from thermosteam import functional as fn

__all__ = ('TAL_chemicals', 'chemical_groups', 'soluble_organics', 'combustibles')

# chems is the object containing all chemicals used in this biorefinery
chems = TAL_chemicals = tmo.Chemicals([])

# To keep track of which chemicals are available in the database and which
# are created from scratch
database_chemicals_dict = {}
copied_chemicals_dict = {}
defined_chemicals_dict = {}

def chemical_database(ID, phase=None, **kwargs):
    chemical = tmo.Chemical(ID, **kwargs)
    if phase:
        chemical.at_state(phase)
        chemical.phase_ref = phase
    chems.append(chemical)
    database_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

def chemical_copied(ID, ref_chemical, **data):
    chemical = ref_chemical.copy(ID)
    chems.append(chemical)
    for i, j in data.items(): setattr(chemical, i, j)
    copied_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

def chemical_defined(ID, **kwargs):
    chemical = tmo.Chemical.blank(ID, **kwargs)
    chems.append(chemical)
    defined_chemicals_dict[ID] = f'{ID}: {chemical.formula}/{chemical.MW}'
    return chemical

_cal2joule = 4.184


# %% 

# =============================================================================
# Create chemical objects available in database
# Some common names might not be pointing to the correct chemical,
# therefore more accurate ones were used (e.g. NitricOxide was used instead of NO),
# data from Humbird et al. unless otherwise noted
# =============================================================================

H2O = chemical_database('H2O')

# =============================================================================
# Gases
# =============================================================================

O2 = chemical_database('O2', phase='g', Hf=0)
N2 = chemical_database('N2', phase='g', Hf=0)
H2 = chemical_database('H2', phase='g')
CH4 = chemical_database('CH4', phase='g')
CarbonMonoxide = chemical_database('CarbonMonoxide', phase='g', 
                                        Hf=-26400*_cal2joule)
CO2 = chemical_database('CO2', phase='g')
NH3 = chemical_database('NH3', phase='g', Hf=-10963*_cal2joule)
NitricOxide = chemical_database('NitricOxide', phase='g')
NO2 = chemical_database('NO2', phase='g')
H2S = chemical_database('H2S', phase='g', Hf=-4927*_cal2joule)
SO2 = chemical_database('SO2', phase='g')

# =============================================================================
# Soluble inorganics
# =============================================================================

H2SO4 = chemical_database('H2SO4', phase='l')
HCl = chemical_copied('HCl', H2SO4) # HCl giving errors; doesn't change things much for TAL SA biorefinery
HNO3 = chemical_database('HNO3', phase='l', Hf=-41406*_cal2joule)
NaOH = chemical_database('NaOH', phase='l')
KOH = chemical_database('KOH', phase = 's')

KCl = chemical_database('KCl', phase = 's')
# Arggone National Lab active thermochemical tables, accessed 04/07/2020
# https://atct.anl.gov/Thermochemical%20Data/version%201.118/species/?species_number=928
AmmoniumHydroxide = chemical_database('AmmoniumHydroxide', phase='l', Hf=-336.719e3)
CalciumDihydroxide = chemical_database('CalciumDihydroxide',
                                        phase='s', Hf=-235522*_cal2joule)
AmmoniumSulfate = chemical_database('AmmoniumSulfate', phase='l',
                                    Hf=-288994*_cal2joule)
NaNO3 = chemical_database('NaNO3', phase='l', Hf=-118756*_cal2joule)
# NIST https://webbook.nist.gov/cgi/cbook.cgi?ID=C7757826&Mask=2, accessed 04/07/2020
Na2SO4 = chemical_database('Na2SO4', phase='l', Hf=-1356.38e3)
# CaSO4 = chemical_database('CaSO4', phase='s', Hf=-342531*_cal2joule)
# The default Perry 151 model has a crazy value, use another model instead
# CaSO4.Cn.move_up_model_priority('Constant', 0)
# 

# =============================================================================
# Soluble organic salts
# =============================================================================

Ethanol = chemical_database('Ethanol')
Acetate = chemical_database('Acetate', phase='l', Hf=-108992*_cal2joule)
AmmoniumAcetate = chemical_database('AmmoniumAcetate', phase='l', 
                                         Hf=-154701*_cal2joule)

# Hf from a Ph.D. dissertation (Lactic Acid Production from Agribusiness Waste Starch
# Fermentation with Lactobacillus Amylophilus and Its Cradle-To-Gate Life 
# Cycle Assessment as A Precursor to Poly-L-Lactide, by Andréanne Harbec)
# The dissertation cited Cable, P., & Sitnai, O. (1971). The Manufacture of 
# Lactic Acid by the Fermentation of Whey: a Design and Cost Study. 
# Commonwealth Scientific and Industrial Research Organization, Australia, 
# which was also cited by other studies, but the origianl source cannot be found online
CalciumLactate = chemical_database('CalciumLactate', phase='l',
                                   Hf=-1686.1e3)
# Hf from Lange's Handbook of Chemistry, 15th edn., Table 6.3, PDF page 631
CalciumAcetate = chemical_database('CalciumAcetate', phase='l', Hf=-1514.73e3)

# Solubility of CalciumSuccinate is 3.2 g/L in water as Ca2+ based on 
# Burgess and Drasdo, Polyhedron 1993, 12 (24), 2905–2911, which is 12.5 g/L as CaSA
# Baseline CalciumSuccinate is ~14 g/L in fermentation broth, thus assumes all 
# CalciumSuccinate in liquid phase
CalciumSuccinate = chemical_database('CalciumSuccinate', phase='l')

# =============================================================================
# Soluble organics
# =============================================================================

AceticAcid = chemical_database('AceticAcid')
Glucose = chemical_database('Glucose', phase = 'l')

# TAL = chemical_database('Triacetic acid lactone')

# SA = chemical_database('Sorbic acid')
# KSA = chemical_database('Potassium sorbate')
# BSA = chemical_database('Butyl sorbate')

IBA = chemical_database('Isobutyraldehyde')
DPHP = chemical_database('Dipotassium hydrogen phosphate', phase = 'l')

# This one is more consistent with others
# try: Glucose.Cn.l.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
# except: Glucose.Cn.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
GlucoseOligomer = chemical_defined('GlucoseOligomer', phase='l', formula='C6H10O5',
                                   Hf=-233200*_cal2joule)
GlucoseOligomer.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn', 'mu', 'kappa'])
Extract = chemical_copied('Extract', Glucose)

Xylose = chemical_database('Xylose')
Xylose.copy_models_from(Glucose, ['Hvap', 'Psat', 'mu'])
XyloseOligomer = chemical_defined('XyloseOligomer', phase='l', formula='C5H8O4',
                                  Hf=-182100*_cal2joule)
XyloseOligomer.copy_models_from(Xylose, ['Hvap', 'Psat', 'Cn', 'mu'])

Sucrose = chemical_database('Sucrose', phase='l')
Sucrose.Cn.move_up_model_priority('Dadgostar and Shaw (2011)', 0)
Cellobiose = chemical_database('Cellobiose', phase='l', Hf=-480900*_cal2joule)

Mannose = chemical_database('Mannose', phase='l', Hf=Glucose.Hf)
Mannose.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn', 'mu'])
MannoseOligomer = chemical_copied('MannoseOligomer', GlucoseOligomer)

Galactose = chemical_database('Galactose', phase='l', Hf=Glucose.Hf)
Galactose.copy_models_from(Glucose, ['Hvap', 'Psat', 'Cn','mu'])
GalactoseOligomer = chemical_copied('GalactoseOligomer', GlucoseOligomer)

Arabinose = chemical_database('Arabinose', phase='l', Hf=Xylose.Hf)
Arabinose.copy_models_from(Xylose, ['Hvap', 'Psat', 'mu'])
ArabinoseOligomer = chemical_copied('ArabinoseOligomer', XyloseOligomer)

SolubleLignin = chemical_database('SolubleLignin', search_ID='Vanillin', 
                                  phase='l', Hf=-108248*_cal2joule)
Protein = chemical_defined('Protein', phase='l', 
                           formula='CH1.57O0.31N0.29S0.007', 
                           Hf=-17618*_cal2joule)
Enzyme = chemical_defined('Enzyme', phase='l', 
                           formula='CH1.59O0.42N0.24S0.01', 
                           Hf=-17618*_cal2joule)
# Properties of fermentation microbes copied from Z_mobilis as in Humbird et al.
FermMicrobe = chemical_defined('FermMicrobe', phase='l',
                      formula='CH1.8O0.5N0.2', Hf=-31169.39*_cal2joule)
WWTsludge = chemical_defined('WWTsludge', phase='s', 
                             formula='CH1.64O0.39N0.23S0.0035', 
                             Hf=-23200.01*_cal2joule)

Furfural = chemical_database('Furfural')


Acetoin = chemical_database('3-Hydroxybutanone', phase = None, Hvap = 44.56*1000) # , V = 89.5e-6
Acetoin.copy_models_from(Furfural, ['Psat', 'Cn', 'mu', 'kappa', 'V'])
Acetoin.Tb = 145.4 + 273.15


Hexanol = chemical_database('Hexanol')
Heptane = chemical_database('Heptane')
Toluene = chemical_database('Toluene')
# Tb from chemspider(chemenu database)
# http://www.chemspider.com/Chemical-Structure.207215.html, accessed 04/07/2020
# https://www.chemenu.com/products/CM196167, accessed 04/07/2020
# Using Millipore Sigma's Pressure-Temperature Nomograph Interactive Tool at
# https://www.sigmaaldrich.com/chemistry/solvents/learning-center/nomograph.html,
# will give ~300°C at 760 mmHg if using the 115°C Tb at 1 mmHg (accessed 04/07/2020)
# Hfus from NIST, accessed 04/24/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C67470&Mask=4
HMF = chemical_database('HMF', Hf=-99677*_cal2joule, Tb=291.5+273.15, Hfus=19800)
HMF.copy_models_from(Furfural, ['V', 'Hvap', 'Psat', 'mu', 'kappa'])
HMF.Dortmund.update(chems.Furfural.Dortmund)

# TAL = chemical_copied('Triacetic acid lactone', HMF)
# SA = chemical_copied('Sorbic acid', HMF)
# KSA = chemical_copied('Potassium sorbate', HMF)
# BSA = chemical_copied('Butyl sorbate', HMF)

TAL = Triaceticacidlactone = chemical_database('Triacetic acid lactone')
TAL.copy_models_from(Furfural, ['Psat', 'Hvap'])
SA = Sorbicacid =  chemical_database('Sorbic acid')
KSA = Potassiumsorbate = chemical_database('Potassium sorbate')
BSA = Butylsorbate = chemical_database('Butyl sorbate')
HMTHP = chemical_copied('HMTHP', TAL)

TAL.Tm = KSA.Tm = 185 + 273.15
TAL.Tb = KSA.Tb =  239.1 + 273.15

# https://pubchem.ncbi.nlm.nih.gov/compound/Sorbic-acid#section=Stability-Shelf-Life
SA.Tb = 228 + 273.15

BSA.Tm = 130 + 273.15
BSA.Tb = 226.5 + 273.15

VitaminA = chemical_database('VitaminA')
VitaminD2 = chemical_database('VitaminD2')
# Hfus from NIST, condensed phase, accessed 04/07/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C87990&Mask=4
Xylitol = chemical_database('Xylitol', phase='l', Hf=-243145*_cal2joule, Hfus=-1118.6e3)

# Hfus from NIST, accessed 04/07/2020
# https://webbook.nist.gov/cgi/cbook.cgi?ID=C50215&Mask=4
# LacticAcid = chemical_database('LacticAcid', Hfus=11.34e3)
LacticAcid = chemical_database('LacticAcid')
LacticAcid.Hfus = 11.34e3

SuccinicAcid = chemical_database('SuccinicAcid', phase_ref='s')

EthylAcetate = chemical_database('EthylAcetate')
# Hf from DIPPR value in Table 3 of Vatani et al., Int J Mol Sci 2007, 8 (5), 407–432
EthylLactate = chemical_database('EthylLactate', Hf=-695.08e3)

EthylSuccinate = chemical_database('EthylSuccinate')
# Cannot find data on Hf of CalciumSuccinate, estimate here assuming
# Hrxn for Ca(OH)2 and SA and Ca(OH)2 and LA are the same 
CalciumSuccinate.Hf = CalciumLactate.Hf + (SuccinicAcid.Hf-2*LacticAcid.Hf)


# =============================================================================
# Insoluble organics
# =============================================================================

Glucan = chemical_defined('Glucan', phase='s', formula='C6H10O5', Hf=-233200*_cal2joule)
Glucan.copy_models_from(Glucose, ['Cn'])
Mannan = chemical_copied('Mannan', Glucan)
Galactan = chemical_copied('Galactan', Glucan)

Xylan = chemical_defined('Xylan', phase='s', formula='C5H8O4', Hf=-182100*_cal2joule)
Xylan.copy_models_from(Xylose, ['Cn'])
Arabinan = chemical_copied('Arabinan', Xylan)

Lignin = chemical_database('Lignin', phase='s')
# Hf scaled based on vanillin
Lignin.Hf = -108248*_cal2joule/tmo.Chemical('Vanillin').MW*Lignin.MW

# =============================================================================
# Insoluble inorganics
# =============================================================================

# Holmes, Trans. Faraday Soc. 1962, 58 (0), 1916–1925, abstract
# This is for auto-population of combustion reactions
P4O10 = chemical_database('P4O10', phase='s', Hf=-713.2*_cal2joule)
Ash = chemical_database('Ash', search_ID='CaO', phase='s', Hf=-151688*_cal2joule,
                        HHV=0, LHV=0)
# This is to copy the solid state of Xylose,
# cannot directly use Xylose as Xylose is locked at liquid state now
Tar = chemical_copied('Tar', Xylose, phase_ref='s')

# =============================================================================
# Mixtures
# =============================================================================

# CSL is modeled as 50% water, 25% protein, and 25% lactic acid in Humbird et al.,
# did not model separately as only one price is given
CSL = chemical_defined('CSL', phase='l', formula='CH2.8925O1.3275N0.0725S0.00175', 
                      Hf=Protein.Hf/4+H2O.Hf/2+LacticAcid.Hf/4)

# Boiler chemicals includes amine, ammonia, and phosphate,
# did not model separately as composition unavailable and only one price is given
BoilerChems = chemical_database('BoilerChems', search_ID='DiammoniumPhosphate',
                                phase='l', Hf=0, HHV=0, LHV=0)

# =============================================================================
# Filler
# =============================================================================

BaghouseBag = chemical_defined('BaghouseBag', phase='s', MW=1, Hf=0, HHV=0, LHV=0)
BaghouseBag.Cn.add_model(0)
CoolingTowerChems = chemical_copied('CoolingTowerChems', BaghouseBag)

# =============================================================================
# Not currently in use
# =============================================================================

DAP = chemical_database('DAP', search_ID='DiammoniumPhosphate',
                             phase='l', Hf= -283996*_cal2joule)
Methanol = chemical_database('Methanol')
MethylAcetate = chemical_database('MethylAcetate')
Denaturant = chemical_database('Denaturant', search_ID='n-Heptane')
DenaturedEnzyme = chemical_copied('DenaturedEnzyme', Enzyme)

# Hf from DIPPR value in Table 3 of Vatani et al., Int J Mol Sci 2007, 8 (5), 407–432
MethylLactate = chemical_database('MethylLactate', Hf=-643.1e3)
FermMicrobeXyl = chemical_copied('FermMicrobeXyl', FermMicrobe)


# %% 

# =============================================================================
# Group chemicals
# =============================================================================

#!!! Sarang please review and update this dict, it affects simulation
chemical_groups = dict(
    OtherSugars = ('Arabinose', 'Mannose', 'Galactose', 'Cellobiose', 'Sucrose'),
    SugarOligomers = ('GlucoseOligomer', 'XyloseOligomer', 'GalactoseOligomer',
                      'ArabinoseOligomer', 'MannoseOligomer'),
    OrganicSolubleSolids = ('AmmoniumAcetate', 'SolubleLignin', 'Extract', 'CSL',
                            # 'Triacetic acid lactone',
                            'Sorbic acid', 'HMTHP',
                            'Potassium sorbate', 'Butyl sorbate', 'VitaminA', 'VitaminD2'),
                            # 'LacticAcid', 'CalciumLactate', 'CalciumAcetate',
                            # 'EthylLactate', 'EthylAcetate', 'SuccinicAcid',
                            # 'CalciumSuccinate', 'EthylSuccinate', 
                            # 'Methanol', 'MethylLactate', 'MethylAcetate'),
    InorganicSolubleSolids = ('AmmoniumSulfate', 'NaOH', 'HNO3', 'NaNO3',
                              # 'DAP',
                              'BoilerChems', 'Na2SO4', 'AmmoniumHydroxide'),
    Furfurals = ('Furfural', 'HMF'),
    #!!! I suspect you want to add some chemicals here
    OtherOrganics = ('Denaturant', 'Xylitol'),
    COSOxNOxH2S = ('NitricOxide', 'NO2', 'SO2', 'CarbonMonoxide', 'H2S'),
    Proteins = ('Protein', 'Enzyme', 'DenaturedEnzyme'),
    CellMass = ('WWTsludge', 'FermMicrobe'),
                # 'FermMicrobeXyl'),
    # Theoretically P4O10 should be soluble, but it's the product of the
    # auto-populated combusion reactions so should in solid phase, however no
    # P4O10 will be generated in the system as no P-containing chemicals 
    # are included in "combustibles"
    OtherInsolubleSolids = ('Tar', 'Ash', 'CalciumDihydroxide', 'P4O10',
                            'BaghouseBag', 'CoolingTowerChems'),
    OtherStructuralCarbohydrates = ('Glucan', 'Xylan', 'Lignin', 'Arabinan', 
                                    'Mannan', 'Galactan'),
    SeparatelyListedOrganics = ('Ethanol', 'Glucose', 'Xylose', 'AceticAcid',
                                'Acetate', 'Lignin'),
    SpearatedlyListedOthers = ('H2O', 'NH3', 'H2SO4', 'CO2', 'CH4', 'O2', 'N2')
    )

# This group is needed in the system.py module
soluble_groups = ('OtherSugars', 'SugarOligomers', 'OrganicSolubleSolids',
                  'Furfurals', 'OtherOrganics', 'Proteins', 'CellMass',
                  'SeparatelyListedOrganics')
soluble_organics = list(sum([chemical_groups[i] for i in soluble_groups], ()))
soluble_organics.remove('WWTsludge')

solubles = tuple(soluble_organics) + chemical_groups['InorganicSolubleSolids'] + ('H2SO4',)

insoluble_groups = ('OtherInsolubleSolids', 'OtherStructuralCarbohydrates')
insolubles = sum([chemical_groups[i] for i in insoluble_groups], ('WWTsludge',))

# This group is needed in the system.py module
combustibles = soluble_organics + list(chemical_groups['OtherStructuralCarbohydrates'])
# combustibles.remove('CalciumLactate')
# combustibles.remove('CalciumAcetate')
combustibles.extend(['WWTsludge','NH3', 'NitricOxide', 'CarbonMonoxide', 'H2S', 'CH4'])

# Chemicals that will be modeled in Distallation/Flash units,
# list is in ascending order of Tb,
# Xylitol is not included due to high Tm and Tb thus will stay in liquid phase


# phase_change_chemicals = ['Methanol', 'Ethanol', 'H2O', 'EthylAcetate', 'Denaturant',
#                           'AceticAcid', 'MethylAcetate', 'MethylLactate',
#                           'EthylLactate', 'Furfural', 'SuccinicAcid', 'LacticAcid', 'HMF']

#!!! Sarang please review and update this, I'm not sure what chemicals are used
# in the biorefinery, getting rid of unused chemicals (i.e., exclude them from chems)
# should help reduce simulation time
phase_change_chemicals = ['Methanol', 'Ethanol', 'H2O', 'EthylAcetate', 'Denaturant',
                          'AceticAcid', 'MethylAcetate', 'MethylLactate',
                          'EthylLactate', 'Furfural', 'EthylSuccinate',
                          'SuccinicAcid', 'LacticAcid', 'HMF']

for chem in chems:
    if chem.ID in phase_change_chemicals: pass
    elif chem.locked_state: pass
    else: 
        # Set phase_ref to avoid missing model errors
        if chem.phase_ref == 'g':
            chem.at_state('g')
        if chem.ID in solubles:
            chem.phase_ref = 'l'
            chem.at_state('l')
        if chem.ID in insolubles:
            chem.phase_ref = 's'
            chem.at_state('s')


# %% 

# =============================================================================
# Set assumptions/estimations for missing properties
# =============================================================================

# Set chemical heat capacity
# Cp of biomass (1.25 J/g/K) from Leow et al., Green Chemistry 2015, 17 (6), 3584–3599
for chemical in (CSL, Protein, Enzyme, WWTsludge, 
                 DenaturedEnzyme, FermMicrobe, FermMicrobeXyl):
    chemical.Cn.add_model(1.25*chemical.MW)

# Set chemical molar volume following assumptions in lipidcane biorefinery,
# assume densities for solulables and insolubles to be 1e5 and 1540 kg/m3, respectively
# !!! This has significant impacts on results, need to double-check accuracy
def set_rho(chemical, rho):       
    V = fn.rho_to_V(rho, chemical.MW)
    chemical.V.add_model(V, top_priority=True)

for chemical in chems:
    if chemical.ID in phase_change_chemicals: pass
    elif chemical.ID in solubles: set_rho(chemical, 1e5)
    elif chemical.ID in insolubles: set_rho(chemical, 1540)

# The Lakshmi Prasad model gives negative kappa values for some chemicals
for chemical in chems:
    if chemical.locked_state:
        try: chemical.kappa.move_up_model_priority('Lakshmi Prasad', -1)
        except: pass
        
# Default missing properties of chemicals to those of water,
for chemical in chems: chemical.default()


# %%

# Though set_thermo will first compile the Chemicals object,
# compile beforehand is easier to debug because of the helpful error message
chems.compile()
tmo.settings.set_thermo(chems)
chems.set_synonym('CalciumDihydroxide', 'Lime')
chems.set_synonym('3-Hydroxybutanone', 'Acetoin')
chems.set_synonym('Triacetic acid lactone', 'TAL')
chems.set_synonym('Triacetic acid lactone', 'Triaceticacidlactone')
chems.set_synonym('Sorbic acid', 'SA')
chems.set_synonym('Sorbic acid', 'Sorbicacid')
chems.set_synonym('Potassium sorbate', 'KSA')
chems.set_synonym('Potassium sorbate', 'Potassiumsorbate')
chems.set_synonym('Butyl sorbate', 'BSA')
chems.set_synonym('Butyl sorbate', 'Butylsorbate')
chems.set_synonym('Dipotassium hydrogen phosphate', 'DPHP')
chems.set_synonym('H2O', 'Water')
chems.set_synonym('H2SO4', 'SulfuricAcid')
chems.set_synonym('NH3', 'Ammonia')
chems.set_synonym('AmmoniumSulfate', 'NH4SO4')
chems.set_synonym('Denaturant', 'Octane')
chems.set_synonym('CO2', 'CarbonDioxide')
chems.set_synonym('CarbonMonoxide', 'CO')
chems.set_synonym('NitricOxide', 'NO')
# chems.set_synonym('CaSO4', 'Gypsum')
chems.set_synonym('P4O10', 'PhosphorusPentoxide')
chems.set_synonym('Na2SO4', 'SodiumSulfate')
chems.set_synonym('AmmoniumHydroxide', 'NH4OH')
chems.set_synonym('Isobutyraldehyde', 'IBA')

# %%

# from TAL.utils import get_chemical_properties	
# get_chemical_properties(chems, 400, 101325, output=True)
>>> from thermosteam import indexer, equilibrium, settings
>>> imol = indexer.MolarFlowIndexer(l=[('Triacetic acid lactone', 304), ('Water', 30)], phases=('s', 'l'))
>>> sle = equilibrium.SLE(imol)
>>> sle('Triacetic acid lactone', T=300)
>>> sle
  File "<ipython-input-3-fa0329f2a71f>", line 4, in <module>
    sle('Triacetic acid lactone', T=300)

  File "C:\Users\saran\Documents\Academia\Spring 2020\BioSTEAM\thermosteam\thermosteam\equilibrium\sle.py", line 136, in __call__
    x = self._solve_x(T)

  File "C:\Users\saran\Documents\Academia\Spring 2020\BioSTEAM\thermosteam\thermosteam\equilibrium\sle.py", line 164, in _solve_x
    return flx.wegstein(self._x_iter, x, xtol=1e-6, args=args)

  File "C:\Users\saran\Anaconda3\envs\bioenv\lib\site-packages\flexsolve\iterative_solvers.py", line 66, in wegstein
    x1 = g0 = f(x0, *args)

  File "C:\Users\saran\Documents\Academia\Spring 2020\BioSTEAM\thermosteam\thermosteam\equilibrium\sle.py", line 172, in _x_iter
    return solubility_eutectic(T, Tm, Hm, Cpl, Cps, gamma[self._solute_index])

IndexError: index 56 is out of bounds for axis 0 with size 2

Add utf-8 encoding setting in setup.py

Hi @yoelcortes, we were helping @GaYeongKim installing qsdsan and she ran into this error:

Picture1

In case that the error message is hard to read, it's about the installation of thermosteam, where this error appeared:
UnicodeDecodeError: 'cp949' codec can't decode byte 0xe2 in position 3426: illegal multibyte sequence

After research Ga-Yeong found one way to fix it (and potential other users who don't use English as the primary language in their OS) is to add the encoding system in setup.py, essentially change this:

long_description=open('README.rst').read(),

to this:

long_description=open('README.rst', encoding='utf-8').read(),

Can you update this in the setup.py for thermosteam (not sure if you want to do it for biosteam as well)? Thanks!

PS, the users can also fix this by updating their system setting, but I feel updating thermosteam will make it easier for the users.

Minor issues on Chemical and README

Some minor things I found that might be good to fix:

  • Should here be new._locked_state = self._locked_state instead?
    new._locked_state = new._locked_state
  • The shield badge for build (Travis CI) hasn't been updated for months, maybe ask Travis for more credits, remove it, or replace with AppVeyor/GitHub one?
  • I just realized the other day that if you use the depth flag in cloning, by default it wouldn't include any other branches unless --no-single-branch flag is given, so might be good to add in the README

Thanks!

Add enthalpy calculation in thermosteam.reaction?

Description
I'm wondering whether it makes sense to automatically calculate reaction enthalpy in stoichiometric reactions? Basically calculate the Hrxn based on Hf of reactants/products and reaction conversions, then when performing the reaction, have the Hrxn added to the H of product streams (this can be optional), thanks!

Entropy can not be calculated in SLE

Hi Yoel,

with python3.8 and the current state of the master branch in my fork (only change numpy dependency) installed, I tried the following solid liquid equilibrium calculation:

from thermosteam import Chemical, indexer, equilibrium, settings
settings.set_thermo(['Water', 'KOH'], cache=True)
imol = indexer.MolarFlowIndexer(l=[('Water', 10), ('KOH', 3)], phases=('s', 'l'))
sle = equilibrium.SLE(imol)
sle('KOH', T=298.15, P=1e5)

sle.mixture.xH(imol, *sle.thermal_condition)
sle.mixture.xmu(imol, *sle.thermal_condition)
sle.mixture.xV(imol, *sle.thermal_condition)
sle.mixture.xS(imol, *sle.thermal_condition)

I do get results for enthalpy, viscosity and specific volume, but for entropy it seems that some value is can not be calculated properly:

Traceback (most recent call last):
  File "fiddle.py", line 15, in <module>
    print(sle.mixture.xS(imol, *sle.thermal_condition))
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/mixture/mixture.py", line 356, in xS
    S_total = sum([S(phase, mol, T, P) for phase, mol in phase_mol])
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/mixture/mixture.py", line 356, in <listcomp>
    S_total = sum([S(phase, mol, T, P) for phase, mol in phase_mol])
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/base/phase_handle.py", line 84, in __call__
    return getattr(self, phase)(z, T, P)
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/mixture/ideal_mixture_model.py", line 56, in __call__
    return sum([j * i(T, P) for i, j in zip(self.models, mol) if j])
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/mixture/ideal_mixture_model.py", line 56, in <listcomp>
    return sum([j * i(T, P) for i, j in zip(self.models, mol) if j])
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/base/functor.py", line 270, in __call__
    return self.function(T, **self.__dict__)
  File "/home/witte/nextcloud/Documents/Hochschule/Dissertation/Kraftwerkssimulation/thermosteam/thermosteam/free_energy.py", line 91, in Liquid_Entropy_Ref_Solid
    return S0 + S_int_T_ref_to_Tm_s + Sfus + Cn_l.T_dependent_property_integral_over_T(Tm, T)
TypeError: unsupported operand type(s) for +: 'float' and 'NoneType'

Can you help me here?

Thanks a lot!

Retrieve properties for component mixtures

Hi,

In issue #33 I have asked about retrieving properties for pure components. I would also need to retrieve properties for arbitrary mixtures of components. Of course, calculating that is relatively trivial in most cases, but I am wondering whether this is possible in thermostream relatively simple, or do I need to create a stream?

Heat of reaction does not account for state of water (liquid/gaseous) after reaction

Hi Yoel,
again looking at methane combustion. Feeding a stream of Methane and O2 into a reaction at a temperature of T = 100 °C (could be less, I wanted to make sure after reaction temperature is larger than saturation temperature at partial pressure of the water) the outlet temperature is the same and therefore water should be gaseous.

Is there a way to calculate the actual heat of reaction in respect to the before/after reaction state? Or is the heat of reaction not supposed to be calculated on this basis?

import thermosteam as tmo
from thermosteam import reaction as rxn

chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'Glucose', 'Ethanol'])
tmo.settings.set_thermo(chemicals)
combustion = rxn.Reaction('Methane + 2O2 -> 2Water + CO2', reactant='Methane', X=1)

heat_of_combustion = combustion.dH / chemicals['Methane'].MW

feed = tmo.Stream(Methane=1, O2=2, T=373.15, phase='g')
print('BEFORE REACTION')
feed.show(N=100)

# React feed molar flow rate
combustion(feed)

print('AFTER REACTION')
feed.show(N=100)
print('This result is zero, if the state of the fluids is not considered in the heat of reaction:')
print(heat_of_combustion - combustion.dH / chemicals['Methane'].MW)

Thank you very much and best regards
Francesco

CoolProp compatibility bug using liquid-vapour-equilibrium (vle)

Describe the bug
Running a Thermosteam code in an environment with a clean thermosteam installation (pip install thermosteam) works fine. As soon as I install CoolProp (pip install coolprop) running the same code raises errors.

To Reproduce

import thermosteam as tmo
from thermo import electrochem as ec

def Vl(mol, T, P=None):  # m3
    MWs = mixture.MWs[H2O_index:KOH_index+1]
    wt = MWs * mol[H2O_index:KOH_index+1]
    wt_no_water = list(wt)
    wt_no_water.pop(0)
    rho = ec.Laliberte_density(T, wt_no_water / wt.sum(), CASs_no_water)  # kg / m3
    MW = wt.sum() / mol[H2O_index:KOH_index+1].sum()
    return tmo.functional.rho_to_V(rho, MW)


def mul(mol, T, P=None):  # Pa*s
    MWs = mixture.MWs[H2O_index:KOH_index+1]
    wt = MWs * mol[H2O_index:KOH_index+1]
    wt_no_water = list(wt)
    wt_no_water.pop(0)
    mu = ec.Laliberte_viscosity(T, wt_no_water / wt.sum(), CASs_no_water)
    return mu


def Cnl(mol, T, P=None):  # kJ / kg*K
    MWs = mixture.MWs[H2O_index:KOH_index+1]
    wt = MWs * mol[H2O_index:KOH_index+1]
    wt_no_water = list(wt)
    wt_no_water.pop(0)
    Cp = ec.Laliberte_heat_capacity(
        T, wt_no_water / wt.sum(), CASs_no_water
    )  # J / kg / K
    MW = wt.sum() / mol.sum()
    return (
        Cp * MW / 1000.0 * mol[H2O_index:KOH_index+1].sum()
    )  # Special case, needs to return the total capacity


# according to Gilliam et al., 2006, A review  of specific conductivities of
# potassium hydroxide solutions for various concentrations and temperatures
def electrical_conductivity_KOH(V, mol, T):
    n_KOH = mol[chemicals.index("KOH")]
    molar_concentration = n_KOH / V
    temperature = T

    A = -2.041
    B = -0.0028
    C = 0.005332
    D = 207.2
    E = 0.001043
    F = -0.0000003

    # [Sm/cm]
    kappa = (
        A * molar_concentration
        + B * pow(molar_concentration, 2)
        + C * molar_concentration * temperature
        + D * (molar_concentration / temperature)
        + E * pow(molar_concentration, 3)
        + F * (pow(molar_concentration, 2) * pow(temperature, 2))
    )

    return kappa * 100 # [Sm/m]

"""stream definition"""

chemicals = tmo.Chemicals([])
chemicals.append(tmo.Chemical('H2', phase="g"))
chemicals.append(tmo.Chemical('H2O'))
chemicals.append(tmo.Chemical('KOH', phase="l"))
chemicals.append(tmo.Chemical('O2', phase="g"))


mixture = tmo.Mixture.from_chemicals(chemicals)
thermo = tmo.Thermo(chemicals, mixture, cache=False)

H2O_index = chemicals.index("H2O")
KOH_index = chemicals.index("KOH")
CASs_no_water = list(chemicals.CASs[H2O_index+1:KOH_index+1])

"""implementing new mixture rules and functions"""
mixture.rule = "electrolyte"
tmo.settings.set_thermo(thermo)

replace = object.__setattr__
replace(mixture.V, "l", Vl)
replace(mixture.mu, "l", mul)
replace(mixture.Cn, "l", Cnl)

stream = tmo.MultiStream()

stream.set_flow(1, "kg/hr", ("g", "H2"))
stream.set_flow(0, "kg/hr", ("g", "O2"))
stream.set_flow(3, "kg/hr", ("l", "KOH"))
stream.set_flow(7, "kg/hr", ("l", "H2O"))

stream.set_property("T", 25, "degC")
stream.set_property("P", 1, "bar")

stream.vle(T=stream.T, P=stream.P)

stream.show(flow='kg/hr')

viscosity_fluid = stream.mu #[Pa/s]
specific_enthalpy = (stream.H / stream.F_mass)*1000 #[J/kg]
specific_volume = stream.F_vol / stream.F_mass #[m3/kg]
density = stream.rho #[m3/kg]
specific_entropy = (stream.S / stream.F_mass)*1000 #[J/(K*kg)]


print("thermosteam_testing specific enthalpy:", specific_enthalpy)
print("thermosteam_testing specific volume:", specific_volume)
print("thermosteam_testing density:", density)
print("thermosteam_testing specific entropy:", specific_entropy)
print("thermosteam_testing viscosity:", viscosity_fluid)

Expected behavior
no runtime error

** Actual behavior**
MultiStream: s1
phases: ('g', 'l'), T: 298.15 K, P: 100000 Pa
flow (kg/hr): (g) H2 1
H2O 0.292
(l) H2O 6.71
KOH 3
Traceback (most recent call last):
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\IPython\core\interactiveshell.py", line 2632, in safe_execfile
py3compat.execfile(
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\IPython\utils\py3compat.py", line 55, in execfile
exec(compiler(f.read(), fname, "exec"), glob, loc)
File "C:\Users\nkawerau\Documents\python-scripts\ael-model\thermosteam_testing.py", line 102, in
viscosity_fluid = stream.mu #[Pa/s]
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam_multi_stream.py", line 534, in mu
self._property_cache['mu'] = mu = self.mixture.xmu(self._imol.iter_composition(), *self._thermal_condition)
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\mixture\mixture.py", line 368, in xmu
return sum([self.mu(phase, mol, T, P) for phase, mol in phase_mol])
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\mixture\mixture.py", line 368, in
return sum([self.mu(phase, mol, T, P) for phase, mol in phase_mol])
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\base\phase_handle.py", line 84, in call
return getattr(self, phase)(z, T, P)
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\mixture\ideal_mixture_model.py", line 56, in call
return sum([j * i(T, P) for i, j in zip(self.models, mol) if j])
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\mixture\ideal_mixture_model.py", line 56, in
return sum([j * i(T, P) for i, j in zip(self.models, mol) if j])
File "C:\Users\nkawerau\Anaconda3\envs\tespy_thermosteam_env\lib\site-packages\thermosteam\thermo\tp_dependent_property.py", line 21, in call
return self.TP_dependent_property(T, P)
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\thermo\utils\tp_dependent_property.py", line 245, in TP_dependent_property
raise RuntimeError("%s method '%s' is not valid at T=%s K and P=%s Pa for component with CASRN '%s'" %(self.name, method_P, T, P, self.CASRN))
RuntimeError: Gas viscosity method 'COOLPROP' is not valid at T=298.15 K and P=99999.99999999999 Pa for component with CASRN '7732-18-5'

Version
Python: 3.8.12
Thermosteam: 0.27.19
Thermo: 0.2.13
CoolProp: 6.4.1

Rerunning script raises error

Description
I have a script that works fine when running for the first time, but will trigger an error about a local variable in thermosteam from the second time on.

To Reproduce
Script attached here.
chemicals.py.zip

Screenshot
Error

Environment
OS: macOS 10.15.3
thermosteam: 0.3.0
python: 3.7.1

Thanks!!!

Temperature domain ignored without warning when calling a specific model

Hi @yoelcortes,
there are warning messages when the user calls Psat or sigmamethod with a temperature value out of bounds. But I do not get a warning or an error, if my input value is out of bounds for a specific model:

import thermosteam as tmo
import numpy as np

for fluid in ['Water', 'Ethanol', 'Methane', 'CO2', 'R134a']:
    fluid_data = tmo.Chemical(fluid)
    print('#' * 50)
    print('Properties of ' + fluid)
    print('#' * 50)
    for sometemperature in np.linspace(50, 1000, 20):
        print('*' * 25)
        print('Evaluation at T=' + str(sometemperature) + ' K')
        print('*' * 25)
        print('Saturation pressure')
        print('*' * 25)
        for model in fluid_data.Psat:
            try:
                print('Methematical model:', model.name)
                print('Temperature limits (min, max):', model.Tmin, model.Tmax)
                print('Saturation pressure at T=' + str(sometemperature) + ' K:', model(T=sometemperature))
                if sometemperature < model.Tmin:
                    print('There is no warning for minimum temperature!')
                elif sometemperature > model.Tmax:
                    print('There is no warning for maximum temperature!')
            except (ValueError, FloatingPointError, OverflowError) as e:
                print('ERROR: ' + str(e))

        print('*' * 25)
        print('Sufrace tension')
        print('*' * 25)
        for model in fluid_data.sigma:
            try:
                print('Methematical model:', model.name)
                print('Temperature limits (min, max):', model.Tmin, model.Tmax)
                print('Surface tension at T=' + str(sometemperature) + ' K:', model(T=sometemperature))
                if sometemperature < model.Tmin:
                    print('There is no warning for minimum temperature!')
                elif sometemperature > model.Tmax:
                    print('There is no warning for maximum temperature!')
            except (ValueError, FloatingPointError, OverflowError) as e:
                print('ERROR: ' + str(e))

While it may not make any sense to call saturation pressure of e.g. water at -100 °C or at T>Tcritical, there is always the chance for a typo to happen. So I feel, that it would be very beneficial for the user to get a warning message, that his input is out of bounds for this specific method, too. In some cases a different error is thrown (for T>Tmax), but there is no hint towards the upper temperature limit.
Best regards
Francesco

OS: Ubuntu 18.04 LTS
Python: 3.8.0
thermosteam: 0.21.1

Setting a MultiStream with mass units other than kg/hr produces wrong results

Describe the bug
On instanciation of a MultiStream, setting mass flow units other than 'kg/hr' (because factor is 1 in this case) produces wrong data on the MultiStream instance.

To Reproduce

from thermosteam import MultiStream, settings

settings.set_thermo(['Water', 'KOH', 'H2', 'O2'], cache=True)

s1 = MultiStream(ID='s1', l=[('Water', 1), ('KOH', 2)], g=[('H2', 3)], units='g/s')
s1.show(flow='kg/s')

** Actual behavior**

Data are incorrect:

MultiStream: s1
 phases: ('g', 'l'), T: 298.15 K, P: 101325 Pa
 flow (g/s): (g) H2     0.231
             (l) Water  0.0772
                 KOH    0.154

Additional context
Will be fixed by PR #50.

Retrieving density for a pure component

I would like to retrieve component properties for pure components (later to be expanded to mixtures). Below is a sample of the code I am using. For water I get pretty close, but for dextrose the value is way off. Anyone has a suggestion what I am doing wrong here?

import thermosteam as tmo

# define the chemicals object
chemicals = tmo.Chemicals(['Dextrose'])

# A Thermo object is built with an iterable of Chemicals or their IDs.
# Default mixture, thermodynamic equilibrium models are selected.
thermo = tmo.Thermo(chemicals)

# now set the thermo package
tmo.settings.set_thermo(thermo)

#now create a stream
s1 = tmo.Stream('feed', Dextrose=1, units='kg/hr')
s1.T = 273.15 + 25

s1.rho # Density [kg/m^3]

1088.3618978529837

Expected answer is 1600 according to https://www.chemsrc.com/en/cas/50-99-7_895097.html.

Am I correct to assume that thermosteam is using the chemicals package under the hood? Anyway for me to simplify this code? Having to create a stream to retrieve the density seems to be overkill.

Error: 'GroupCounts' object has no attribute 'subgroups'

Description
Seems like thermosteam.Chemicals can cause attribute errors as in the title for some chemicals

To Reproduce

import thermosteam as tmo
water, glycerol = tmo.Chemicals(['Water', 'Glycerol'])
water.show()
glycerol.show()

Screenshot
No error for water
Water

Attribute error for glycerol
Glycerol

Environment
OS: macOS 10.15.3
python: 3.7.0
thermosteam: 0.2.10

Thanks!!!

Include authorship/copyright notes and address code overlap

Most modules in the thermosteam.properties subpackage and a couple of modules elsewhere originated from thermo. Although most have been enhanced/altered, it is important to credit the original author. To address this issue, a comment header will be added to each copied/modified file noting the origin, authors and license. Also, all authorship, date, and license information will be removed from the module docstring and be placed as a comment block for both consistency and keeping the documentation concise.

Additionally, it may be possible to contribute back the enhancements made to these files to the thermo repository and use thermo as a third-party dependency for better code reuse and management. Alternatively, it is also possible to work with the original author of these files to create a lower-level package that can work as a better dependency for thermo, thermosteam, and other packages.

Error raised when setting properties

Description
I ran into errors when trying to set some properties of a chemical, for example:

import thermosteam as tmo
HMF = tmo.Chemical('HMF')
HMF.Tm = 564.65

Screen Shot 2020-04-24 at 8 51 33 AM

In addition to Tb, the same error will be raised for MW, Tm, Tt, Tc, Pt, Pc, and Vc. I don't think these properties are phase/T/P dependent?

Note
I think this error is new, I didn't run into this error when using thermosteam v0.12.17

Environment
OS: macOS Catalina 10.15.4
thermosteam: v0.13.2

Thanks!

AttributeError: 'CompiledChemicals' object has no attribute 'IDs'

Description
See the two sets of codes below, I'm not sure why the first one works fine but the second one would raise the error as in the title

To Reproduce
First define the function needed for both sets of codes:

import thermosteam as tmo

def append_new_single_phase_chemical(ID, chems, *sources, **data):
    chemical = tmo.Chemical.blank(ID, **data)
    chemical.copy_missing_slots_from(*sources)
    try: chemical.at_state(phase=chemical.phase_ref)
    except: pass
    chems.append(chemical)

Below codes work fine

chems_1 = tmo.Chemicals([])
append_new_single_phase_chemical('chemical_1', chems=chems_1)
chems_1.chemical_1.MW = 1
chems_1.chemical_1.phase_ref = 's'
tmo.settings.set_thermo(chems_1)

But below ones will raise the error

chems_2 = tmo.Chemicals([])
append_new_single_phase_chemical('chemical_2', chems=chems_2, MW=1, phase_ref='s')
tmo.settings.set_thermo(chems_2)

Screenshot
Error

Environment
OS: macOS Catalina 10.15.3
thermosteam: 0.3.0
python: 3.7.1

Thanks!!!

<Chemical>._other_names seems to be an obsolete attribute but still included in the .__slots__

import thermosteam as tmo
tmo.Chemical('H2O').__slots__

('_ID',
'_locked_state',
'_other_names',
'_phase_ref',
'_eos',
'_eos_1atm',
'_synonyms',
'_CAS',
'_InChI',
'_InChI_key',
'_common_name',
'_iupac_name',
'_pubchemid',
'_smiles',
'_formula',
'_Dortmund',
'_UNIFAC',
'_PSRK',
'_NIST',
'_Psat',
'_Hvap',
'_sigma',
'_epsilon',
'_kappa',
'_V',
'_Cn',
'_mu',
'_S_excess',
'_H_excess',
'_S',
'_H',
'_MW',
'_Tm',
'_Tb',
'_Tt',
'_Tc',
'_Pt',
'_Pc',
'_Vc',
'_Hf',
'_S0',
'_LHV',
'_HHV',
'_Hfus',
'_Sfus',
'_omega',
'_dipole',
'_similarity_variable',
'_iscyclic_aliphatic',
'_combustion',
'_N_solutes')

tmo.Chemical('H2O')._other_names

Traceback (most recent call last):

File "", line 1, in
tmo.Chemical('H2O')._other_names

AttributeError: _other_names

Full implementation of thermo as an optional dependency

Thermosteam Thermo, Mixture, and Chemicals objects may wrap around objects from the thermo library to provide support for some of their exciting features and not break support for native thermosteam objects.

This issue is meant to track progress on fully implementing the thermo library as a optional dependency.

Additionally, it would be interesting to contribute back to thermo features regarding self contained methods/models for easier implementation of pure chemical properties (in particular user-models and model management in general) and slowly depend more and more on thermo... However, the first step is to ensure full compatibility with thermo to begin to move dependencies around.

thermo already has well-tested features for user-defined models and model management. The best way to ensure compatibility with thermo may be to implement methods to convert thermosteam model handles to the appropriate counterparts in thermo (and possibly vice-versa while we're at it).

Avoid `x not in list` error in reloading

For lines 47-49 (not sure if it's my environment or other users get the same problem as well)

VolumeLiquid.ranked_methods.remove('EOS')

I often get the this error (since I use auto-reload):
Screen Shot 2021-09-09 at 9 39 25 PM

Can we change these to something similar to (or better solutions) so that auto-reloading would work?

for i in (
    VolumeLiquid.ranked_methods,
    VolumeLiquid.ranked_methods_P,
    VolumeGas.ranked_methods_P
        ):
    if 'EOS' in i:
        i.remove('EOS')

Thanks!!!

Aliases in `Chemical.copy`

The copy method basically copy everything from one chemical to another, but this could cause problem for aliases setting as it bypasses the duplicate alias check.

For example, in the corn biorefinery, Starch (and also Fiber, SolubleProtein, InsolubleProtein) is copied from Cellulose, and they end up having the same aliases
https://github.com/BioSTEAMDevelopmentGroup/Bioindustrial-Park/blob/1d79328e2cb60ef15392dce2ac0134c374980460/BioSTEAM%202.x.x/biorefineries/corn/_chemicals.py#L22

>>> from biorefineries import corn as cn
>>> chems = cn.create_chemicals()
>>> chems.Cellulose.aliases
{'C6H10O5'}
>>> chems.Starch.aliases
{'C6H10O5'}

I'm OK with just fixing it in the biorefinery setting like
https://github.com/BioSTEAMDevelopmentGroup/Bioindustrial-Park/blob/86fa9bc440d0e7d55a637bdcfb9a91c909928c07/BioSTEAM%202.x.x/biorefineries/corn/_chemicals.py#L22

but wondering if it's better to do something on the method, thanks!

Suggested changes for the documentation

General

  • I think combining mixture property estimation with the ability to model reactions may be a highlight of this package, too. Maybe consider adding it to the highlights section on the landing page.
  • For rather inexperienced users who want to use the git version, you may add
    $ cd thermosteam
    $ pip install -e .
    
    to your installation instructions.
  • Include a changelog, so the user is informed about the changes since the last (major) version. Maybe there are adjustments to perform in the code.
  • Add some comments on how to contribute to your project including instructions on testing.
  • Not a required change, but a hint: Sometimes an internal link is very useful, e.g. in the Note-box of the Chemical API documentation. You can establish a link to classes/subpackages/methods/... using :py:class:/:py:mod:/:py:meth:/..., see https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects.

Typos and rendering issues

Command-line execution of code instead of ipynb

  • I think you should mention to the users, that the tutorial shown is run in an ipynb and to have the same results in script based programs, some modifications are to be made:

    • Using the print() method instead in command-line execution of your code does not look beautiful in many instances, e.g.

      print(pretreatment_parallel_rxn[0:2])

      which prints as

      ParallelReaction(["ReactionItem('Water + Glucan -> Glucose', reactant='Glucan', X=0.099, basis=mol), ReactionItem('Water + Glucan -> GlucoseOligomer', reactant='Glucan', X=0.003, basis=mol)"])

      I suggest to give a hint to the user, that using

      pretreatment_parallel_rxn[0:2].show()

      behaves the same as the ipynb on readthedocs:

      ParallelReaction (by mol):
      index  stoichiometry                      reactant    X[%]
      [0]    Water + Glucan -> Glucose          Glucan      9.90
      [1]    Water + Glucan -> GlucoseOligomer  Glucan      0.30
    • To show plots the user must import matplotlib.pyplot, e.g.

      import thermosteam as tmo
      from matplotlib import pyplot as plt
      Water = tmo.Chemical('Water')
      Water.Psat.plot_vs_T([Water.Tm, Water.Tb], 'degC', 'atm', label="Water")
      plt.show()

`refresh_constants` bug

Describe the bug
Looks like the refresh_constants method is not working for Chemicals

To Reproduce

import thermosteam as tmo
chems = tmo.Chemicals([tmo.Chemical('H2O'), tmo.Chemical('Ethanol')])
chems.compile()
chems.refresh_constants()

** Actual behavior**
Screen Shot 2021-10-05 at 10 48 00 AM

Version

  • GitHub

No flx.InfeasibleRegion in solve_vle_composition exception

Description
Currently module flexsolve has no attribute InfeasibleRegion, can you check this out? Thanks!!!

raise flx.InfeasibleRegion('liquid phase composition')

Environment
OS: macOS Catalina 10.15.5
python: v 3.7.6
biosteam: v 2.20.4
thermosteam: v 0.20.6
flexsolve: v 0.3.28

Retrieving list of all components

I have searched but was unable to find a method that retrieved a full list of components in the database at runtime. I would also like to iterate through the available physical property models for each of the components, including the validity ranges (T & P). The reason for asking is that I am working on a graphical front end and would like to show this information to the user.

Enthalpy balance violated mixing liquid and gaseous phases

E.g. mixing two streams of water (one liquid and one gaseous), the enthalpy of the mixed stream is not equal to the sum of the enthalpy of the incoming streams. I checked within the method mix_from, the enthalpy value assigned to the mixed Stream seems to be correct at that point (https://github.com/BioSTEAMDevelopmentGroup/thermosteam/blob/master/thermosteam/_stream.py#L896).

Maybe there is something wrong in my code?

import thermosteam as tmo

chemicals = tmo.Chemicals(['Water', 'Ethanol'])
thermo = tmo.Thermo(chemicals)
tmo.settings.set_thermo(thermo)
s3 = tmo.Stream('s3', T=300, P=1e5, Water=10, units='kg/hr')
s4 = tmo.Stream('s4', phase='g', T=400, P=1e5, Water=10, units='kg/hr')
s_eq = tmo.Stream('s34_mixture')
s_eq.mix_from([s3, s4])

print('MultiStream with enthalpy added by mix_from')
s_eq.vle(H=s_eq.H, P=1e5)
s_eq.show(composition=True)
print('Absolute deviation in enthalpy:', s_eq.H - (s3.H + s4.H))

print('MultiStream with Enthalpy summed individually')
s_eq.vle(H=s3.H + s4.H, P=1e5)
s_eq.show(composition=True)

yields

MultiStream with enthalpy added by mix_from
MultiStream: s34_mixture
 phases: ('g', 'l'), T: 372.79 K, P: 100000 Pa
 composition: (g) Water  1
                  -----  0.538 kmol/hr
              (l) Water  1
                  -----  0.572 kmol/hr
Absolute deviation in enthalpy: 1802.331630182045
MultiStream with Enthalpy summed individually
MultiStream: s34_mixture
 phases: ('g', 'l'), T: 372.79 K, P: 100000 Pa
 composition: (g) Water  1
                  -----  0.497 kmol/hr
              (l) Water  1
                  -----  0.614 kmol/hr

Mixed phase for mixture calculations

Extending on bug #34, please consider the example mentioned below:

from thermosteam import Mixture
mixture = Mixture.from_chemicals(['Water', 'Dextrose'])
mixture.get_property('rho', 'g/L', 'l', [0.2, 0.8], 350, 101325)

When using the phase 'l', Dextrose defaults to the "wrong" density as mentioned before. How to manage this in the best way? Ideally, I would like to calculate with water 'l' and dextrose 'd'. Or is, in this case, the correlation for liquid Dextrose just at fault, and should that be fixed instead?

Feature request: Add function to estimate Hc based on molecular formula

Is your feature request related to a problem? Please describe.
I was using the boilerturbogenerator unit, and I realized it was pulling Hc of chemicals to calculate the amount of steam/electricity produced. But many chemicals in the database do not have a default Hc.

Describe the solution you'd like
I'm wondering if we can add an option to implement functions to estimate Hc. In the past I've used Dulong's formula (empirical correlation initially developed to estimate energy value of coal, I used that to calculate Hc of biomass, which is a common approach in literature) to estimate Hc based on the molecular formula.

Additional context
I'll see if I can get this work. I think it'll be helpful to add molecular formula an attribute for of the chemicals, this can make it possible to automatically populate ParallelReaction for combustion that can be used in boilerturbogenerator, molecular formula should be easily extracted from InChI.

Volume calculation

Description
I think there might be problems with how thermosteam calculates stream volumes. I found this when trying to calculate concentration of certain compounds in kg/L, then realize that volume/density is off.

For example, run the following codes:

from biorefineries.cornstover.system import H301
stream = H301.outs[0]
print('Temperature of H301 output is ' + str(stream.T) + ' K \n')

stream_density_kgL = round(stream.F_mass/(stream.F_vol*1000), 3)
print('Density of H301 output at ' + str(stream.T) + ' K is ' + str(stream_density_kgL) + ' kg/L \n')
stream.show(N=100, composition=True, flow='kg/hr')

import thermosteam as tmo
thermo = tmo.Chemicals(['Water'])
tmo.settings.set_thermo(thermo)
s1 = tmo.Stream('s1', Water=1000, units='kg/hr', T=298.15)
s2 = s1.copy()
s2.T = 321.15

s1_density_kgL = round(s1.F_mass/(s1.F_vol*1000), 3)
s2_density_kgL = round(s2.F_mass/(s2.F_vol*1000), 3)
print('\n')
print('Density of water at ' + str(s1.T) + ' K is ' + str(s1_density_kgL) + ' kg/L \n')
print('Density of water at ' + str(s2.T) + ' K is ' + str(s2_density_kgL) + ' kg/L')

My output:
Screen Shot 2020-04-06 at 8 35 00 PM

My questions:

  1. The stream (output of H301) is ~70% water in liquid phase with the rest being solids (i.e., minimum organic solvents), why the density is only 0.153 kg/L?
  2. In the case of water density at 298.15 and 321.15 K, why density of water increases? Shouldn't it decrease when it's above 4°C?

Environment
python: v3.7.6
OS: macOS Catalina 10.15.3
biosteam: v2.11.0
thermosteam: v.0.9.0
biorefineries: v2.8.0

Thanks!!!

Correct_atomic_balance does not work on methane combustion equation

Hi Yoel,
I always like to try and fiddle with new code/software and wanted to create something I am more familiar with than biodiesel reactions :), a combustion reaction of methane with auto correcting the atomic balance. Unfortunately, I do not really know what I am doing wrong here, can you help me? I would expect, that the atomic balance can be solved here.

import thermosteam as tmo
from thermosteam import reaction as rxn

chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2'])
tmo.settings.set_thermo(chemicals)
combustion = rxn.Reaction('Methane + O2 -> Water + CO2',
                                   reactant='Methane', X=1,
                                   correct_atomic_balance=True)

Thanks and best regards
Francesco

copy_models_from arguments not accepted

import thermosteam as tmo
Tetradecanol = tmo.Chemical('Tetradecanol')
Octanol = tmo.Chemical('Octanol')
Octanol.copy_models_from(Tetradecanol, 'Psat')

ValueError: P is not a valid model name; names must be a subset of ('Psat', 'Hvap', 'sigma', 'epsilon', 'kappa', 'V', 'Cn', 'mu')

Mixture equations of state

Mixture equations of state are not yet implemented. Any help in building functions/classes to evaluate and solve mixture equations of state would be appreciated!

Unlock chemical state

In thermosteam, we can lock the phase of a chemical by using the at_state function. Assume that I first want to set the chemical Glucose at gas phase then liquid phase, looks like I can use at_state(phase, copy=True) to let me change the phase later, but since the default for copy is False, if I forgot to change it, I would have to redefine this chemical. There is a lock_phase function, is there a one for unlocking?

Thanks!

Minor tweak to allow multiple Indexer

Hey @yoelcortes, the way that currently thermo data is cached, as in below where the thermo condition is used as the key to the _data_cache dict, assumes that the user is always retrieving volume info from the cache dict

vol = self._data_cache[TP]

Would it be better to change it into a nested dict like

    try:
        vol = self._data_cache['vol'][TP]
    except:
        chemicals = self.chemicals
        mol = self.data
        vol = np.zeros_like(mol, dtype=object)
        for i, chem in enumerate(chemicals):
            vol[i] = VolumetricFlowProperty(chem.ID, mol, i, chem.V,
                                            TP, None, self._phase)
        self._data_cache['vol'] = {}
        self._data_cache['vol'][TP] = \
        vol = ChemicalVolumetricFlowIndexer.from_data(property_array(vol),
                                                      self._phase, chemicals,
                                                      False)

I noticed this because in QSDsan, we added a ConcentrationIndexer to calculate concentration, which also relies on TP, now I'm using the nested dict for conc and it works fine, so it's up to you whether to keep vol as the default, thanks!

Using Thermosteam and CoolProp in the same environment leads to errors

Describe the bug
Running a Thermosteam code in an environment with a clean thermosteam installation (pip install thermosteam) works fine. As soon as I install CoolProp (pip install coolprop) running the same code raises errors.

To Reproduce

import thermosteam as tmo

"""stream definition"""
H2O = tmo.Chemical("H2O")

chemicals = tmo.CompiledChemicals([H2O])
mixture = tmo.Mixture.from_chemicals(chemicals)
thermo = tmo.Thermo(chemicals, mixture, cache=False)

tmo.settings.set_thermo(thermo)

stream = tmo.MultiStream(ID="stream", l=[("H2O", 1)], units="m3/hr")

stream.set_property("T", 20, "degC")
stream.set_property("P", 1, "bar")

viscosity_H2O = stream.mu

print("thermosteam viscosity of H2O:", viscosity_H2O)

Expected behavior
thermosteam viscosity of H2O: 0.0010216146329967753

I do not expect the CoolProp installation to affect code execution.

** Actual behavior**
Traceback (most recent call last):
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\IPython\core\interactiveshell.py", line 2632, in safe_execfile
py3compat.execfile(
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\IPython\utils\py3compat.py", line 55, in execfile
exec(compiler(f.read(), fname, "exec"), glob, loc)
File "C:\Users\nkawerau\Documents\python-scripts\ael-model\thermosteam_testing.py", line 12, in
stream = tmo.MultiStream(ID="stream", l=[("H2O", 1)], units="m3/hr")
File "C:\Users\nkawerau\Anaconda3\envs\thermosteam_env\lib\site-packages\thermosteam_multi_stream.py", line 224, in init
flow._data[:] = material_data
File "C:\Users\nkawerau\AppData\Roaming\Python\Python38\site-packages\free_properties_property_array.py", line 184, in setitem
i.item().value = v
File "C:\Users\nkawerau\Anaconda3\envs\thermosteam_env\lib\site-packages\thermosteam\indexer.py", line 1032, in VolumetricFlowProperty
self.mol[self.index] = value / V(*self.TP) / 1000.
File "C:\Users\nkawerau\Anaconda3\envs\thermosteam_env\lib\site-packages\thermosteam\thermo\tp_dependent_property.py", line 21, in call
return self.TP_dependent_property(T, P)
File "C:\Users\nkawerau\Anaconda3\envs\thermosteam_env\lib\site-packages\thermo\utils\tp_dependent_property.py", line 245, in TP_dependent_property
raise RuntimeError("%s method '%s' is not valid at T=%s K and P=%s Pa for component with CASRN '%s'" %(self.name, method_P, T, P, self.CASRN))
RuntimeError: Liquid molar volume method 'COOLPROP' is not valid at T=298.15 K and P=101325.0 Pa for component with CASRN '7732-18-5 (H2O)'

Version
Python: 3.8.12
Thermosteam: 0.27.18
Thermo: 0.2.13
CoolProp: 6.4.1

NOTICE: Upgrading to Python 3.8

Thermosteam will be moving to 3.8 with the next updates.

Reasons

  • Python 3.8 offers huge, but not backward compatible, advantages such as the walrus operator (assignment expressions), positional-only arguments, the f'{expr=}' syntax for debugging, and much more. These improvements would accelerate development in Thermosteam.
  • It has been over a year since Python 3.8 has been released, and all major libraries already support to Python 3.8.

Latest numba version does not work with latest numpy version

Describe the bug
numba requires numpy<=1.21 (ImportError: Numba needs NumPy 1.21 or less).

To Reproduce
Install latest version of thermosteam via pip in a fresh python 3.8 environment.

Additional context
Suggest to limit numpy version (see respective PR). I however wonder, why this dependency is not dealt with on the numba side.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.