Giter VIP home page Giter VIP logo

biosteamdevelopmentgroup / biosteam Goto Github PK

View Code? Open in Web Editor NEW
170.0 15.0 33.0 1.44 GB

The Biorefinery Simulation and Techno-Economic Analysis Modules; Life Cycle Assessment; Chemical Process Simulation Under Uncertainty

License: Other

Python 99.51% CSS 0.01% JavaScript 0.01% HTML 0.47%
distillation flash biorefinery bioprocess fermentation thermodynamics pump monte-carlo heat-exchanger techno-economic-analysis

biosteam's Introduction

BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules

Version_status

Documentation

license

Supported_versions

image

image

Join the chat at https://gitter.im/BioSTEAM-users/community

Read in: Español

Workshops

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

What is BioSTEAM?

BioSTEAM is a fast and flexible package for the design, simulation, techno-economic analysis, and life cycle assessment of biorefineries under uncertainty1. BioSTEAM is built to streamline and automate early-stage technology evaluations and to enable rigorous sensitivity and uncertainty analyses. Complete biorefinery configurations are available at the Bioindustrial-Park GitHub repository, BioSTEAM's premier repository for biorefinery models and results. The long-term growth and maintenance of BioSTEAM is supported through both community-led development and the research institutions invested in BioSTEAM, including the Center for Advanced Bioenergy and Bioproducts Innovations (CABBI). Through its open-source and community-lead platform, BioSTEAM aims to foster communication and transparency within the biorefinery research community for an integrated effort to expedite the evaluation of candidate biofuels and bioproducts.

Data on chemicals and algorithms to estimate thermodynamic properties are imported from chemicals and thermo, community-driven open-source libraries developed by Caleb Bell. BioSTEAM's premire thermodynamic engine, ThermoSTEAM, builds upon these libraries to facilitate the creation of thermodynamic property packages.

Installation

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

$ pip install biosteam

To get the git version, run:

$ git clone git://github.com/BioSTEAMDevelopmentGroup/biosteam

For help on common installation issues, please visit the documentation.

Documentation

BioSTEAM's documentation is available on the web:

http://biosteam.readthedocs.io/

Bug reports

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

https://github.com/BioSTEAMDevelopmentGroup/biosteam

Contributing

For guidelines on how to contribute, visit:

https://biosteam.readthedocs.io/en/latest/contributing/index.html

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 BioSTEAM license, if it is convenient for you, please cite BioSTEAM if used in your work. Please also consider contributing any changes you make back, and benefit the community.

About the authors

BioSTEAM was created and developed by Yoel Cortés-Peña as part of the Guest Group and the Center for Advanced Bioenergy and Bioproducts Innovation (CABBI) at the University of Illinois at Urbana-Champaign (UIUC).

References


  1. Cortés-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..

biosteam's People

Contributors

akprasad avatar benportner avatar cansinobl avatar ggmirandac avatar joyxyz1994 avatar lkudli2 avatar rupert-br 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

biosteam's Issues

Feature Request: continue model evaluation when system runs into simulation error for certain parameters

Description
Currently, if we are doing Monte Carlo evaluation for 5000 scenarios and unfortunately the system runs into some error during the last simulation, BioSTEAM will reports an error and won't return results, I've run into this a couple of times... 😢

So I'm wondering if it is possible to:

  1. Let BioSTEAM continue the evaluation (since the next set of scenario parameters might not lead to error).
  2. When generating the result table, save 'NA' or similar for the failed simulation.
  3. Save the error message somewhere for later debugging.

I think it's doable using try:... except:..., but I'm not sure whether using try:... except:...is good. This change might require some tweaks for spearman evaluation as well. However the change can be very helpful if the designed system is not very robust, or some combination of parameters are just bad.

Thanks in advance for considering!

Back-calculating key unit parameters to achieve TEA targets

Is your feature request related to a problem? Please describe.
BioSTEAM is great on calculating TEA metrics like stream price/IRR with a given system design, I'm wondering, would it be possible to/do you see value in letting BioSTEAM automate the design so that a certain price/IRR target can be reached?

For example, let's say for the cornstover biorefinery, we assume a 95% glucose-to-ethanol conversion that gives us an MESP of ~$0.69/kg, so can BioSTEAM calculate how low this conversion can be to give an MESP of $1/kg or less?

Describe the solution you'd like
The fundamental question I'd like to address is: what would it take to reach a TEA target? I consider this question in three different levels based on what I know about BioSTEAM (easiest to hardest to implement), it's kind of similar to Monte Carlo:

  • Easiest: as I described in the example. We can BioSTEAM calculate what the MESP will be from 0-100% conversion, then select the cutoff conversion to reach the target.

  • Medium (this might not be a robust solution): we provide a list of parameters with their probable ranges, BioSTEAM runs sensitivity analysis to rank these parameters from most to least impactful, then BioSTEAM changes the most impactful parameter to see if the target can be reached. If not, BioSTEAM will include the second most impactful parameter, run Monte Carlo within the given ranges, then see if the target can be reached, so on and so forth.

    • Note, I considered whether it'd be better to let BioSTEAM set the most impactful parameter at the value that would be closest to the target and only manipulate the second most impactful parameter to save some computation power, but realized this might result in local optima
  • Hardest: we provide a list of parameters with their probable ranges (or even better, BioSTEAM loads some default parameters like the biosteam.evaluation.State. load_default_parameters function), and BioSTEAM calculates the probability at a given value with the distribution of a certain parameter (i.e., the cumulative distribution function). Then BioSTEAM will do Monte Carlo for all the parameters we provided and give each simulation scenario the probability score (maybe by multiplying all probabilities?) and rank these scenarios from most to least probable. This way, we can clearly see:

    • How probable to achieve a TEA target?
    • What assumptions we need to make to achieve that target?

Look forward to your thoughts, thanks!!!

Commit history

Looks like the commit history has been reset (and it has been done so in the past), is there a reason for this? Doing so will cause some problem when I try to git pull upstream. Thanks!

Mismatching info for `HeatUtility` objects

Describe the bug
Sometimes, HeatUtility.flow and HeatUtility.inlet_utility_stream / HeatUtility.outlet_utility_stream flow information aren't matching, but this doesn't appear to be affecting results, only causing confusion in display.

To Reproduce
This is most easily seen in the cornstover biorefinery. See the screenshot, the flow of low_pressure_steam should be negative, BT.heat_utilities[1].flow gives the correct flow, but BT.heat_utilities[1] gives the opposite.

I think this is because the inlet_utility_stream and outlet_utility_stream flows are not correct, which stems from the reverse function. In that function, inlet_utility_stream and outlet_utility_stream are swapped, probably their flow should multiply by -1 instead?

However, I'm not sure whether there are other cache problems with the display

Screenshots
Output
Screen Shot 2020-04-29 at 5 44 49 PM

Code for reverse function
Screen Shot 2020-04-29 at 5 48 49 PM

Environment

  • Python: v3.7.6
  • OS: macOS Catalina v.10.15.4
  • BioSTEAM: v.2.15.3
  • thermosteam: v.0.15.6
  • biorefineries: v2.10.1

Thanks!!!

Bug: unit design decorator

Describe the bug
The script was not updated to work with the new thermosteam functions, leading to a TypeError.
I believe the origin code in biosteam.units.decorators._design of:

moisture = sum([i.get_flow(units, IDs='7732-18-5') for i in ins])

should be replaced with

moisture = sum([i.get_flow(units, key='7732-18-5') for i in ins])

I tried this and it fixed the problem

To Reproduce
I found this when trying to import the cornstover biorefinery:

from biorefineries.cornstover.system import cornstover

Screenshots
Screenshot for error:
Error

Fixing the code:
Fix

Fixed

Environment

  • Python: v3.7.6
  • OS: macOS Catalina 10.15.3
  • BioSTEAM: v2.6.7
  • thermosteam: v0.6.8
  • biorefineries: v2.6.2

How to batch-create new unit classes through excel?

I noticed some really cool codes the cornstover biorefinery used to batch-create units using the factories module of biosteam and an Excel spreadsheet:
Batch-create units

Can you explain how this works? I couldn't really understand the raw codes in the factories module and additionally, I was baffled by the fact that/I wanted to know:

  • Seems like not all units in the spreadsheet were used
  • Only some units were created this way, was this intended to be a quick way for simple units?
  • Is there anyway for me to check what units were created this way in the console?

Sorry I have a ton of questions... I tried to look at the tutorial but still couldn't figure these out. Let me know if the answers were somewhere in the tutorial , thanks!!!

Originally posted by @yalinli2 in #6 (comment)

Allow user to set split for Splitter

Currently split in bst.Splitter is just a property without setter:

@property
def split(self):
    """[Array] Componentwise split of feed to 0th outlet stream."""
    return self._isplit._data

but can we add a setter function to it? Like:

@property
def split(self):
    """[Array] Componentwise split of feed to 0th outlet stream."""
    return self._isplit._data
@split.setter
def split(self, split):
    self._isplit = self.thermo.chemicals.isplit(split)

Or we can add a set_split function to take in the order arg as in the __init__, thanks!

bst.[UnitName] vs bst.units.[UnitName]

Description
It appears that we can access biosteam native unit operations (e.g., Flash) by using either bst.Flash or bst.units.Flash, are there any differences and what is the preferred way?

Additionally, I can access more units in the dropdown menu when I hit tab in Spyder after bst.units. than after bst., but not showing in the dropdown menu in bst. is not preventing me from calling these units, i.e., bst.Centrifuge_LLE works as well as bst.units.Centrifuge_LLE even if Centrifuge_LLE is only shown in the dropdown menu in bst.units.

I'm not sure whether the dropdown menu thing is a Sypder bug...

Screenshots
Note that the dropdown menu for bst. only contains Flash but not Centrifuge_LLE, and both of them appear in the dropdown menu for bst.units

1

2

3

bst.Junction failing to copy flow information

Describe the bug
I noticed that under certain conditions, the Junction unit may fail to copy the upstream flow information into downstream flow, but I have not figure out what might be the case.

To reproduce
This can be seen in the cornstover system in biorefineries package. I'll use M601 as an example. Note that in the original code, no ID was assigned so the default ID (U1) was used, there are several other instances like this, I'll compile them and submit another issue under Bioindustrial-Park.

import biosteam as bst
from biorefineries.cornstover import system
M601 = bst.find.unit.U1 
M601
# Note that d56 and d57 are empty streams, re-simulate won't fix it
(Screenshot #1)

M601.diagram(radius=1)
# Note that d56 and d57 are rejected_water_and_blowdown and evaporation_and_blowdown, 
# and they are not empty
(Screenshot #2)

bst.find.stream.rejected_water_and_blowdown
(Screenshot #3)

bst.find.stream.evaporation_and_blowdown
(Screenshot #4)

Screenshots
#1: M601_flow
M601_flow

#2: M601_diagram
M601_diagram

#3: rejected_water_and_blowdown
rejected_water_and_blowndown

#4: evaporation_and_blowdown
evaporation_and_blowndown

Environment

  • Python: v3.7.1
  • OS: iOS Catalina
  • BioSTEAM: v1.0.6
  • biorefineries: v0.1.1

Additional context
Unlike Stream and Unit, which have name that we can easily using find function to locate, Junction does not have IDs and so far I can only use import to get the Junction unit I want. Is it possible to find them using find function?

Automatic creation recycle systems

Recycle systems are currently created by specifying the tear stream and a network of unit operations to run sequentially. This effort takes a ton of time in the biorefinery development process. Automatic creation of a recycle system given a set of unit operations would not only save time but also prevent human error. Any help in spearheading this enhancement is appreciated!

Cannot compile chemicals: Na2HPO4 and KH2PO4.

After making a list of chemical objects, I am trying to compile them.

chemical_list = ('Na2HPO4', 'KH2PO4')
chemical_objects = tmo.Chemicals([])
    for chemical in chemical_list:
        try:
            chemical_objects.append(tmo.Chemical(chemical))
        except LookupError:
            chemical_objects.append(create_fake_chemical(chemical))

chemical_objects.compile()

However, Na2HPO4 and KH2PO4 show the next error:

Traceback (most recent call last):
  File "/Users/dim/.local/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3326, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-4df983869c2e>", line 1, in <module>
    runfile('/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom/initial.py', wdir='/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom')
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom/initial.py", line 77, in <module>
    chemicals.compile()
  File "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/_chemicals.py", line 159, in compile
    CompiledChemicals._compile(self)
  File "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/_chemicals.py", line 304, in _compile
    f"{chemical} is missing key thermodynamic properties ({missing}); "
RuntimeError: Na2HPO4 is missing key thermodynamic properties (Psat, Tb and Hvap); use the `<Chemical>.get_missing_properties()` to check all missing properties

The same I get for KH2PO4.
chemical.default() doesn't help

Environment

Python: anaconda, 3.7
OS: macOS Catalina version 10.15.2
BioSTEAM: v 2.19.15
biorefineries v 2.14.15

Tutorial examples for Splitter and Centrifuge_LLE units

Describe the bug
I think there is a typo in the example for Splitter, and the Centrifuge_LLE example is not working.

To reproduce

  • Splitter
    I think the split should be (0.99, 0.1) instead of (0.99, 0.01) to give the results shown in screenshot 1

  • Centrifuge_LLE
    An error would occur as in screenshot 2

Screenshots

  • Screenshot 1
    Splitter

  • Screenshot 2
    Centrifuge_LLE

Environment

  • Python: v3.7.0
  • OS: macOS 10.15.2
    you might want to change the iOS in the template to macOS, I think iOS is for iPhone, not Mac
  • BioSTEAM: v1.0.7

Missing essential unit operation models

Any help in developing the following essential unit operations would be greatly appreciated:

  • Compressor (added 8/6/2022)
  • Turbine
  • Fenske-Gilliland-Underwood shortcut column (added 3/22/2020)
  • Rigorous heat exchanger for heat integration (added 6/6/2020)
  • Multi-component distillation models (preferably MESH equation based; Completed 12/17/2023)
  • Kinetic fermentation models (because of the large number of models out there, it is out of the scope of BioSTEAM; but models may be contributed to the bioindustrial-park)
  • Centrifuge modeled by liquid-liquid equilibrium (added 3/27/2020)
  • Settler modeled by liquid-liquid equilibrium (added 6/6/2020)

Please visit the "Inheriting from Unit" section in the docs for how to create a Unit operation. Make sure to read the "Developer's guide" before making any pull request. Briefly, concise and thorough documentation with full references, and passing biorefinery and unit operation tests are required for every contribution.

Minor bug

Ran into this when I was debugging something else...I think in the following line, it should be not '{type(stream).__name__}' instead of not '{type(item).__name__}' Thanks!

f"'Stream' objects; not '{type(item).__name__}'"

import from biorefineries library leads to the attribute error.

Describe the bug
if I try to run:
from biorefineries.lipidcane import system
or
from biorefineries.cornstover import system
The output in jupyter notebook is:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-68-5aca82ca079b> in <module>
----> 1 from biorefineries.lipidcane import system

~/opt/anaconda3/lib/python3.7/site-packages/biorefineries/lipidcane/system.py in <module>
     11 from biosteam import units
     12 from biorefineries.lipidcane.tea import LipidcaneTEA
---> 13 from biorefineries.lipidcane.chemicals import (pretreatment_chemicals,
     14                                   ethanol_chemicals,
     15                                   biodiesel_chemicals)

~/opt/anaconda3/lib/python3.7/site-packages/biorefineries/lipidcane/chemicals.py in <module>
     42                                 formula="C6H10O5", # Glucose monomer
     43                                 MW=162.14,
---> 44                                 Hf=-975708.8)
     45 Hemicellulose = create_new_chemical('Hemicellulose',
     46                                     formula="C5H8O5", # Xylose monomer

~/opt/anaconda3/lib/python3.7/site-packages/biorefineries/lipidcane/chemicals.py in create_new_chemical(ID, phase, **constants)
     34 
     35 def create_new_chemical(ID, phase='s', **constants):
---> 36     solid = tmo.Chemical.blank(ID, phase=phase, **constants)
     37     lipidcane_chemicals.append(solid)
     38     return solid

~/.local/lib/python3.7/site-packages/thermosteam/_chemical.py in blank(cls, ID, CAS, phase_ref, phase, **data)
   1039         self.phase_ref = phase_ref or phase
   1040         self._CAS = CAS or ID
-> 1041         for i,j in data.items(): setfield(self, i , j)
   1042         return self
   1043 

AttributeError: 'Chemical' object has no attribute 'formula'

and in Pycharm Python console it is like this:

In[27]: from biorefineries.cornstover import system
> /Users/dim/.local/lib/python3.7/site-packages/thermosteam/base/thermo_model_handle.py(132)integrate_by_T()
-> for model in self.models:
(Pdb) >?

To Reproduce
run the
if I try to run:
from biorefineries.lipidcane import system
or
from biorefineries.cornstover import system
in jupyter notebook or in the PyCharm Python console

Expected behavior
The one which is described in the documentation, for example, here.

Environment

  • Python: anaconda, 3.7
  • OS: MacOS Catalina version 10.15.2
  • BioSTEAM: v 2.2.4
  • biorefineries v 2.2.5
  • jupyter notebook v 6.0.1

Problems with outflow after running the simulation

I have to in-streams to the mixer. One contains water and solid stuff, other contains "solid" bacteria.

After running the simulation, I am trying to show F_vol of the out-stream. It causes crash.

If I run the media with only liquid compounds (another stream "bacteria_stream" contains only "solid" bacteria), then I have no problems.

The code:

import thermosteam as tmo
import biosteam as bst


def create_fake_chemical(chem_id):
    """
    made mostly for bacteria and biomass
    :param chem_id:
    :return:
    """
    chemical = tmo.Chemical.blank(chem_id, MW=1., phase='s')
    chemical.default()
    return chemical


def introducing_chemicals(chemical_list):
    """
    creating chemical objects related to compounds which are a part of bio process
    :param chemical_list:
    :return:
    """
    solid_chemicals_with_problems = ('Na2HPO4', 'KH2PO4', "CaCl2")
    chemical_objects = tmo.Chemicals([])
    for chemical in chemical_list:
        if chemical in solid_chemicals_with_problems:
            chemical_objects.append(tmo.Chemical(chemical, phase='s', default=True))
            continue
        try:
            chemical_objects.append(tmo.Chemical(chemical))
        except LookupError:
            chemical_objects.append(create_fake_chemical(chemical))
    return chemical_objects


media = {
    'CaCl2': 0.0111,
    'Glucose': 20.0,
    'MgSO4': 0.012,
    'NH4Cl': 1.0,
    'NaCl': 0.5,
    'Water': 1000,
    'thiamine': 6e-06,
    # 'Na2HPO4': 7.0,
    # "KH2PO4": 3.0
}
other_chemicals = ("Vanillin", "O2", "CO2", "Ecoli")
chemicals = introducing_chemicals([*other_chemicals, *[c for c, _ in media.items()]])

bst.settings.set_thermo(chemicals)

media_stream = bst.Stream('media', T=295, units='kg/hr', **media)
bacteria_stream = bst.Stream('Bacteria', T=295, units='kg/hr', Water=1000)
media_mix = bst.units.Mixer('Mixer')
[media_stream, bacteria_stream] - media_mix
s1 = media_mix.outs[0]
media_mix.simulate()
print(s1.F_vol)

The output:

Traceback (most recent call last):
  File "/Users/dim/.local/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3326, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-ef0394a537f0>", line 1, in <module>
    runfile('/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom/upstream.py', wdir='/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom')
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/dim/PycharmProjects/bioprocess_modelling/Bellossom/upstream.py", line 57, in <module>
    print(s1.F_vol)
  File "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/_stream.py", line 580, in F_vol
    return 1000. * self.mixture.V(self.phase, self.mol, *self._thermal_condition)
  File "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/base/phase_handle.py", line 114, in __call__
    return getattr(self, phase)(z, T, P)
  File "/Users/dim/opt/anaconda3/lib/python3.7/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 "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/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 "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/base/thermo_model_handle.py", line 370, in __call__
    Tmin=self.Tmin, Tmax=self.Tmax,
  File "/Users/dim/opt/anaconda3/lib/python3.7/site-packages/thermosteam/base/thermo_model_handle.py", line 275, in Tmin
    return min([i.Tmin for i in self._models])
ValueError: min() arg is an empty sequence

Environment

Python: anaconda, 3.7
OS: macOS Catalina version 10.15.2
BioSTEAM: v 2.19.15
biorefineries v 2.14.15

Addition

I have the same output if I try to run media_stream.F_vol in the same code even before simulation.

Modify heat_exchanger_utilities_from_units

In the following code:

return [i for i in heat_utilities if i.heat_exchanger]

Will it be better to change to:

return [i for i in heat_utilities if i.heat_exchanger and i.duty != 0]

The original one will cause problem in HXN if there's no duties associated with a HeatUtility, because this line:

if abs(hu.heat_exchanger.ins[0].T - hu.heat_exchanger.outs[0].T)>0.01]

will raise an error since hu.heat_exchanger.ins[0] is a MissingStream, it doesn't have T

Thanks!

Feature Request: MultiEffectEvaporator with number of effects adjusted automatically to achieve given vapor fraction

With MultiEffectEvaporator units, some of you may have noticed that at low input values for vapor fraction (V) we get an infeasible region error. Since this is not true for Flash units, I reasoned that this was not a Thermosteam issue and that the problem could be addressed within the MultiEffectEvaporator class. After discussing this with Yoel, we came to the conclusion that the infeasible region errors at low values of V are due to lower pressure effect(s) being unable to achieve that V at a temperature that is within 5-10 degrees C (hard and fast rule) of the effect immediately preceding it. This is a feature request to address that issue by solving by iteratively removing the effect with the lowest pressure if an infeasible region error occurs with all effects.

E.g., with a MultiEffectEvaporator unit having 5 effects with pressure decreasing from 1st to 5th effect, if an infeasible region error occurs with all 5 effects, a solution is attempted again with the last effect taken off. Repeated if an infeasible region occurs with 4 effects, and so forth.

Best practice for evaluating TEA parameters

Description
Assume I want to evaluate how minimum product selling price (MPSP) changes with internal rate of return (IRR) under uncertainty, and I want to depict this using a figure where the x-axis is IRR, y-axis is MPSP, and the IRR vs. MPSP correlation is a curve with error band to express uncertainty (similar to Figure 4 in the paper Cortes-Peña et al., ACS Sustainable Chem. Eng. 2020, 8 (8), 3302–3310).

I can use the evaluate_across_coordinate function for Model object, but I'm wondering if this is the best practice? I ask because unlike Figure 4 in the paper where the change of feedstock lipid content affects the system, change of TEA parameters like IRR won't affect the system. So for each of the Monte Carlo scenario, there's no need to re-simulate the system to get the MPSP, re-calculating the cashflow should be enough.

My impression from reading the document is that BioSTEAM creates Block objects that only simulate the system downstream of the change, so I'm wondering if for my question, this means that only the cashflow will be re-calculated when change IRR (which is what we want)?

Thanks!!!

problems with display function

Using Jupyter labs I have had issues with displaying units and systems. Even when I copy example text directly from the website there is an error message even with basic units that work fine with the .show() function.
biosteam unit diagram error

Clarification on using find function

I really like the find function in biosteam, but only want to ask something related to its usage (as instructed in the tutorial on managing flowsheets):

Let's assume I create a very simple stream in biosteam and try to find it:
import biosteam as bst
species = bst.Species('Water', 'Ethanol')
bst.Stream.species = species
s0 = bst.Stream('s0', Water=1, Ethanol=1)

When I try to use find, I can do:
lost_stream = bst.find('s0')

But I cannot use:
lost_stream = bst.find.stream('s0')

which will trigger an error:
Error

Instead, I should use:
lost_stream = bst.find.stream.s0

Can you explain on the difference between find and stream and why this is the case?

Thanks!!!

Reusage of the equipment question.

I am trying to replicate the PHA production bioprocess on biosteam.
The process is below.
image

What concerns me is the Homogenisation(H301) and Centrifugation (C401). According to the process, they are doing 3 passes on each of these two pieces of the equipment.

I have tried to find out if it is possible to implement it on biosteam. The only way which I see is to make the new objects with zero purchase costs. Is there more elegant way?

System.save_report raises error for systems without heat utilities

Describe the bug
As described in the title, the save_report function will raise an error for systems without heat utilities, I think that's because how the heat utility table is created by the heat_utilities_table function in biosteam/report/table. Please make it possible for those systems to have reports. Thank you!

To Reproduce
Any simple systems will do, for example:

import biosteam as bst

bst.settings.set_thermo(bst.Chemicals(('Water', 'Ethanol')))

s1 = bst.Stream(Water=100, Ethanol=50)
s2 = bst.Stream(Water=100, Ethanol=10)

M1 = bst.Mixer(ins=(s1, s2))
S1 = bst.Splitter(ins=M1-0, split=0.1)

sys = bst.System('sys', path=(M1, S1))

sys.save_report('sys.xlsx')

Expected behavior
Excel file as system report

Actual behavior
Note: I think you had an extra space between "**" and "Actual behavior" in your .github/ISSUE_TEMPLTE/bug_report.md, it was not rendered correctly so I changed it myself.
Screen Shot 2020-12-05 at 7 49 10 AM

Version

  • biosteam v2.21.6

Extend rigorous testing (pytests and doctests)

Currently, version testing in BioSTEAM is limited to doctests for all essential unit operations and design methods, but some less-widely used unit operations do not have tests. Several important classes in BioSTEAM do not yet have doctests, including the TEA, System, and Model objects. In addition, system-wide tests of biorefineries are limited to verifying the minimum product selling price and IRR. Other important biorefinery results, including electricity consumption, cooling and heating duty, and excess electricity production are not rigorously tested. This issue introduces two milestones to address these problem:

  • Add doctests to all unit operations and classes in BioSTEAM.
  • Add pytests to all system-wide results of featured biorefineries, including capital cost, electricity consumption, cooling and heating duty, and excess electricity production.

Feature Request: An argument that lets clustered PFDs know which unit should be first in each cluster

The new option to "cluster" units that are in the same recycle systems in a process flow diagram is really cool! I think it would be even more useful if we could pass an argument for which unit should be first (on the left-most part) in each cluster. That way, e.g., in SYS3 of the sugarcane to 3-HP biorefinery flowsheet below, we could have Cofermentation be the “first” unit in the SYS3 cluster to make the process clearer at first glance.

deleteme_sugarcane_HP

The split function has disappeared after one of the updates

Describe the bug
I didn't update biosteam for a while, but now I have downloaded the last version. For one of my units I used the split function which was also used in other virtual equipment.

To Reproduce

from biosteam.units.design_tools.separations import split

Expected behavior
just usage of the split function

Actual behavior

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3418, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-ad53f2ea018f>", line 1, in <module>
    runfile('/Users/dmitrybachin/PycharmProjects/bioprocess_modelling/tea.py', wdir='/Users/dmitrybachin/PycharmProjects/bioprocess_modelling')
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/dmitrybachin/PycharmProjects/bioprocess_modelling/tea.py", line 9, in <module>
    from general.garage import UniversalFermenter
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Users/dmitrybachin/PycharmProjects/bioprocess_modelling/general/garage.py", line 8, in <module>
    from biosteam.units.design_tools.separations import split
  File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
    module = self._system_import(name, *args, **kwargs)
ModuleNotFoundError: No module named 'biosteam.units.design_tools.separations'

Version

  • Python 3.8
  • biosteam 2.22.7
  • macOS Catalina 10.15.6

Inhereting from unit doesn't overwrite some parameters

Read it first :-)

I have almost finished writing the issue and then I have realised how to fix it. You might want to add a more specific error about it.
I have left the issue like it was written in the first place. However, I have added the fix in the end.

Description

I have a weird issue related to the overwriting parameters _N_ins and _N_outs when I am inheriting from a Unit.

Debugging has shown that they both are equal 1 in the moment of the object initiation.

Also, if I use:

    def __init__(self, ID, ins=('', ''), outs=('', '')):
        self._N_outs = 2
        self._N_ins = 2

then everything is OK, but if specify them when I declare the class it crashes.
(because _N_ins==1, _N_outs==1 for some reasons as I already said)

   class SprayDryer(bst.Unit):
       _N_ins = 2
       _N_outs = 2

However, everything is ok, with other parameters like _graphics
also I have created other objects with 1 inlet 2 outs it was usually ok. Yesterday, I had the same, but then it has disappeared...

The code:

(importing chemicals is not shown but I can add it if it is relevant)

class SprayDryer(bst.Unit):
    _N_ins = 2
    _N_outs = 2
    node = {'shape': 'invhouse',
            'fillcolor': '#90949c',
            'style': 'filled',
            'gradientangle': '0',
            'color': 'black',
            'peripheries': '1',
            'margin': 'default',
            }
    single_edge_in = ({'headport': 'c'},)
    single_edge_out = ({'tailport': 'c'},)
    _graphics = UnitGraphics(single_edge_in, single_edge_out, node)

    def __init__(self, ID, ins=('', ''), outs=('', '')):
        bst.Unit.__init__(self, ID=ID, ins=ins, outs=outs)



sd = SprayDryer('SD001')
sd.diagram()

The output:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-2-52916b9384b7> in <module>
      1 from garage import *
----> 2 sd = SprayDryer('SD001')
      3 sd.diagram()

~/PycharmProjects/bioprocess_modelling/Bellossom/garage.py in __init__(self, ID, ins, outs, x)
    285 
    286     def __init__(self, ID, ins=('', ''), outs=('', ''), x=11):
--> 287         bst.Unit.__init__(self, ID=ID, ins=ins, outs=outs)

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_unit.py in __init__(self, ID, ins, outs, thermo)
    170         self._specification = None
    171         self._load_thermo(thermo)
--> 172         self._init_ins(ins)
    173         self._init_outs(outs)
    174         self._init_utils()

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_unit.py in _init_ins(self, ins)
    179     def _init_ins(self, ins):
    180         # Ins[:class:`~thermosteam.Stream`] Input streams
--> 181         self._ins = Ins(self, self._N_ins, ins, self._thermo, self._ins_size_is_fixed)
    182 
    183     def _init_outs(self, outs):

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/utils/piping.py in __init__(self, sink, size, streams, thermo, fixed_size)
    307     def __init__(self, sink, size, streams, thermo, fixed_size=True):
    308         self._sink = sink
--> 309         super().__init__(size, streams, thermo, fixed_size)
    310 
    311     @property

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/utils/piping.py in __init__(self, size, streams, thermo, fixed_size)
    174                     else:
    175                         N = len(streams)
--> 176                         n_missing(size, N) # Assert size is not too big
    177                         self._streams[:N] = [redock(i) if isa(i, Stream)
    178                                              else dock(Stream(i, thermo=thermo)) for i in streams]

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/utils/piping.py in n_missing(ub, N)
    139 
    140 def n_missing(ub, N):
--> 141     assert ub >= N, f"size of streams exceeds {ub}"
    142     return ub - N
    143 

AssertionError: size of streams exceeds 1

Fix

I have fixed just adding to the class the _run method. Usually, I add it immediately but this time I wanted to play with _graphics first. So after I added this,

    def _run(self):
        pass

There was no problem and the object was successfully created.
It might be useful to specify the error about this.

Environment

Python: anaconda, 3.7
OS: macOS Catalina version 10.15.2
BioSTEAM: v 2.20.5

Best practice to work around tiny errors

I'm not exactly sure whether it's the problem of Python of numpy, but sometimes numbers such as 0.8 will be expressed as 0.8000000001 (I heard because it's recorded as one number divided by another one?). So this sometimes cause problems in reactions because of the material infeasible check. I'm now adding a "1e-6" to some reaction conversions to work around this, but wondering if there are better ways? Thanks!

How to implement "_run" method in Unit subclasses?

I was trying to create a very simple unit class using biosteam but ran into a lot of questions:

Let's assume I just want to make a tank (name=FakeTank), where there is only 1 ins and 1 outs and cost=0, here are my codes:
FakeTank

Then I try to create a FakeTank unit U101:
U101

When I try to simulate U101, I got an error:
Error

When I check the streams, the ins is what I want, but the outs is empty:
Streams

How should I modify the code to make this work?

Thanks!

Error generating biorefinery diagram from documentation

Describe the bug
I am trying to reproduce this part of the documentation
However neither for cornstover nor for lipidcane chemicals are not specified in the documentation. I have tried to take chemicals from the objects inside the packages, but it did not help.

The output is below:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-27-eb6ddd6d0ca2> in <module>
      1 cornstover_sys = bst.find.system.cornstover_sys
----> 2 cornstover_sys.diagram()

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_system.py in diagram(self, kind, file, format, **graph_attrs)
    454             return self._thorough_diagram(file, format, **graph_attrs)
    455         elif kind == 'surface':
--> 456             return self._surface_diagram(file, format, **graph_attrs)
    457         elif kind == 'minimal':
    458             return self._minimal_diagram(file, format, **graph_attrs)

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_system.py in _surface_diagram(self, file, format, **graph_attrs)
    421                 else: outs += products
    422 
--> 423                 subsystem_unit = _systemUnit(i.ID, ins, outs)
    424                 units.add(subsystem_unit)
    425 

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_unit.py in __init__(self, ID, ins, outs, thermo)
    168         self._init_utils()
    169         self._init_results()
--> 170         self._assert_compatible_property_package()
    171         self._register(ID)
    172 

~/opt/anaconda3/lib/python3.7/site-packages/biosteam/_unit.py in _assert_compatible_property_package(self)
    201         streams = self.ins + self.outs
    202         assert all([s.chemicals.IDs == chemical_IDs for s in streams if s]), (
--> 203             "unit operation chemicals are incompatible with inlet and outlet streams; "
    204             "try using the `thermo` keyword argument to initialize unit operation "
    205             "with a compatible thermodynamic property package")

AssertionError: unit operation chemicals are incompatible with inlet and outlet streams; try using the `thermo` keyword argument to initialize unit operation with a compatible thermodynamic property package

To Reproduce

import biosteam as bst
from biorefineries.cornstover import system
cornstover_sys = bst.find.system.cornstover_sys
cornstover_sys.diagram()

Expected behavior
The step 4 in the 8th chapter of the documentation

Environment

  • Python: anaconda, 3.7
  • OS: MacOS Catalina version 10.15.2
  • BioSTEAM: v 2.2.5
  • biorefineries v 2.2.5
  • jupyter notebook v 6.0.1

(Help wanted) Best practice to change one unit in a biorefinery

If I just want to change one unit in an established biorefinery (e.g., change fermentation in the Humbird cornstover biorefinery), should I remake the whole biorefinery, or is there an easy way to swap out the original fermentation with the new one?

MEE design

In MEE design, the following line won't work if there is only one evaporator associated with the MEE.

F_mass=0, F_vol=0, P_suction=evap.outs[0].P,

As evap is defined previously in for evap in evaporators[1:]

Related to this, I have a questions regarding the new feature (#45). BioSTEAM now automatically adjusts the number of effects to achieve a given vapor fraction if the set number of effects won't work. But assume I make an MEE with 5 evaporators at V=0.5. In uncertainty analysis, BioSTEAM finds that 5 evaporators won't work for one simulation and reduce the number to 4, then later, assume that 5 evaporators can work for another simulation, will BioSTEAM reverses the number back to 5?

Thanks!

Document CombinedTEA Class

The CombinedTEA class is undocumented. Additionally, it would be nice to include the corn stover biorefinery's CombinedTEA as example implementation.

Adding CostItem to designed cost

Description
When using the cost decorators, the added scaled cost will overwrite the original _cost functions in the super class, this is normally not a problem (and should be done this way for most cases.

However I ran into this problem: assume I want to design a Flash tank with three items: a tank, an agitator, and a pump. I want to use BioSTEAM's design and cost algorithms for the tank, but since BioSTEAM's Flash doesn't have agitators and pumps, I want to use cost decorators to add them (scaled based on inlet mass flow rate).

I kind of figured this out using the current cost decorator as below, but not sure if there's a better way to do this, thanks!

import biosteam as bst
from math import ceil
from biosteam.units import Flash
from biosteam.units.decorators import CostItem

# This is the same as the _cost function in biosteam.units.decorators,
# but I cannot import it directly 
def add_scaled_costs(self):
    D = self.design_results
    C = self.purchase_costs
    kW = 0
    for i, x in self.cost_items.items():
        S = D[x._basis]
        if x.ub:
            D[x.N or '#' + i] = N = ceil(S/x.ub)
            q = S/x.S
            F = q/N
            C[i] = N*bst.CE/x.CE*x.cost*F**x.n
            kW += x.kW*q
        elif x.N:
            N = getattr(self, x.N)
            F = S/x.S
            C[i] = N*bst.CE/x.CE*x.cost*F**x.n
            kW += N*x.kW*F
        else:
            F = S/x.S
            C[i] = bst.CE/x.CE*x.cost*F**x.n
            kW += x.kW*F
    if kW: self.power_utility(kW)

def add_cost_item(unit, basis, ID=None, *, CE, cost, n, S=1, ub=0, kW=0, BM=1, 
                   units=None, fsize=None, N=None):
    if hasattr(unit, 'cost_items'):
        cost_items = unit.cost_items
    else: cost_items = unit.cost_items = {}
    cost_items[ID] = CostItem(basis, units, S, ub, CE, cost, n, kW, BM, N)

class NewFlash(Flash):
    _units = Flash._units
    _units['Flow rate'] = 'kg/hr'
    
    def _design(self):
        Flash._design(self)
        self.design_results['Flow rate'] = self.ins[0].F_mass
    
    def _cost(self):
        # To avoid adding scaled power_utility repeatedly during simulation
        self.power_utility(0)
        Flash._cost(self)
        designed_kW = self.power_utility.rate
        # Adding CostItem will overwrite designed power_utility
        add_cost_item(self, ID='Agitator', basis='Flow rate', units='kg/hr',
                       kW=170, cost=90000, S=252891, CE=521.9, n=0.5, BM=1.5)
        add_cost_item(self, ID='Pump', basis='Flow rate', units='kg/hr',
                       kW=55.9275, cost=30000, S=204390, CE=521.9, n=0.8, BM=2.3)
        add_scaled_costs(self)
        scaled_kW = self.power_utility.rate
        self.power_utility(designed_kW+scaled_kW)

Class for system-wide process results

System-wide process results for biorefineries such as electricity, heating and cooling is not trivial to generate in biosteam. This issue proposes implementing a UnitGroup class for generating such process results with data table and plotting capabilities. The outline for the class would look as follows:

class UnitGroup:
    __slots__ = ('name', 'units', 'heat_utilities')
    
    def __init__(self, name, units):
        self.name = name
        self.units = units
        self.heat_utilities = get_heat_utilities(units)

    def get_cooling_duty(self): ...
    
    def get_heating_duty(self): ...
    
    def get_installed_cost(self): ...
    
    def get_electricity_consumption(self): ...

    def get_electricity_production(self): ...

    def to_dict(self):
        return {'Installed equipment cost [million USD]': self.get_installed_cost(),
                'Cooling duty [GJ/hr]': self.get_cooling_duty(),
                'Heating duty [GJ/hr]': self.get_heating_duty(),
                'Electricity consumption [MW]': self.get_electricity_consumption()}
                'Electricity production [MW]': self.get_electricity_production()}

    def to_series(self):
        return pd.Series(self.to_dict(), name=self.name)

    @staticmethod
    def create_df(unit_subgroups): ...

    @staticmethod
    def plot_unit_subgroups(units_subgroups, fraction=True): ...

    def __repr__(self):
        return f"{type(self).__name__}({self.name}, {self.units})"

This would facilitate generating testing functions for biorefineries, as well as evaluating metrics for scenario grids and Monte Carlo.

GUI interface

Hi
Biosteam appears to be a very good platform for my research work but I am more comfortable in in a GUI environment (eg Thermoflex) to build models based on Python scripted unit models. I see that this is work in progress and wonder if you could advise an expected timeframe?

Kind regards

Allan

How to consider the lifetime of equipment if it's different from the biorefinery lifetime?

I'm not sure if we have run into this problem in any of the biorefineries. But suppose we have a biorefinery with a lifetime of 30 years, and we have a piece of equipment that will only last for 15 years. So we'll need to buy another piece of the equipment half way through the project. How can we account for this in TEA?

I don't think we can just multiply the cost by 2 since we need to consider discount, so we probably need to do something to the cashflow table.

Thanks!

Improve Model.evaluate function

Hope you aren't mad at me bringing this up again haha, but let's consider two scenarios:

  1. I ran 1,000 Monte Carlo, only 1 or 2 failed. In this case, I don't care too much about the failed scenarios, so now how biosteam handles this, which resets the system and goes on with the evaluation works perfect.

  2. I ran 1,000 Monte Carlo, but all of them failed. In this case, there probably was something wrong with my Model setup, so I would actually want to see the error message.

So to address both scenarios, I propose:

  1. Include an "error_log" attribute to Model or Model.table to record the error messages.

  2. Include a kwarg for the evaluate function to allow users to choose whether they want the evaluation to be interrupted by errors.

Glad to discuss and thanks for considering!

Select a liquid/gas stream of a rigorously simulated HXutility unit

Description
Suppose I want to simulate a HXutility as follows:

import thermosteam as tmo
from biosteam.units import StorageTank, HXutility

chemicals = tmo.Chemicals(['Water', 'Ethanol'])
tmo.settings.set_thermo(chemicals)

s1 = Stream('s1', Water=100, Ethanol=50)
H1 = HXutility('H1', ins=s1, V=0, rigorous=True)
H1.show() # before simulation
H1.simulate()
H1.show() # after simulation

Screen Shot 2020-05-29 at 7 53 51 AM

My question is, how can I select the liquid phase of H1 outs as the ins for a downstream unit? Note that before simulation, H1.outs[0] is a Stream, not a MultiStream, so doing:

s1 = tmo.Stream('s1', Water=100, Ethanol=50)
H1 = HXutility('H1', ins=s1, V=0, rigorous=True)
H1_liquid = tmo.Stream('H1_liquid', phase='l')
H1_liquid.mol = H1.outs[0].imol['l']
T1 = StorageTank('T1', ins=H1_liquid)

from biosteam import System
system = System('system', path=(H1, T1))

raises error:
Screen Shot 2020-05-29 at 7 59 54 AM

Also, it is strange to me that the outs of H1 after simulation is at 354.39K - I thought it should remain at 298.15K as the ins?

Thanks!!!

Environment
OS: macOS Catalina 10.15.4
Python: v3.7.6
biosteam: v2.17.7
thermosteam: v.0.17.15

bst._system warning

The line below will trigger an error - is warning imported from somewhere? I was using biosteam v 2.20.5, but this isn't urgent, you can patch this up in the next update.

from warning import warn

BTW, thanks for considering adding deprecation warning! It'll help a lot!

Mass Balance unit for one chemical

Description
I'm trying to use the Mass Balance unit, but ran into problem when trying to modify the example provided for two chemicals, Iet's assume I just want the mass balance for ethanol.

To Reproduce
Modified codes below:

from biosteam import System
from biosteam.units import Mixer, Splitter, StorageTank, Pump, Flash, MassBalance
from thermosteam import Chemicals, Stream, settings
chemicals = Chemicals(['Water', 'Ethanol'])
settings.set_thermo(chemicals)
# water = Stream('water',
#                 Water=40,
#                 units='lb/s',
#                 T=350, P=101325)
ethanol = Stream('ethanol',
                 Ethanol=190,
                 T=300, P=101325)
target = Stream('target', flow=50)
# T1 = StorageTank('T1')
T2 = StorageTank('T2')
# P1 = Pump('P1', P=101325)
P2 = Pump('P2', P=101325)
M1 = Mixer('M1', outs='s1')
S1 = Splitter('S1', outs=('s2', 's3'), split=0.5)
F1 = Flash('F1', outs=('s4', 's5'), V=0.5, P =101325)
# Connect units
# water-T1-P1
ethanol-T2-P2
[P2-0, S1-0]-M1-F1-1-S1

MB1 = MassBalance('MB1', streams=[0],
                  chemical_IDs=['Ethanol'],
                  outs=target,
                  ins=(ethanol, S1-0))
mixSys = System('mixSys',
                 recycle=S1-0,
                 network=(MB1, T2, P2, M1, F1, S1))
# Make diagram to view system
mixSys.diagram()
mixSys.simulate()
MB1.show()

Screenshot
Error

Environment
OS: macOS 10.15.3
biosteam: 2.2.1
thermosteam: 0.2.13

Thanks!!!

Bug report: bst.units.ConveyingBelt warning

Description
I believe there's a small bug with the warning message for ConveyingBelt, currently there's no attribute _lb_warning defined.

from .decorators import cost
from .._unit import Unit

__all__ = ('ConveyingBelt',)

@cost('Flow rate', CE=567, cost=813, ub=2000, n=0.38, N='Number of conveyors')
class ConveyingBelt(Unit):
    length = 40 #: ft
    height = 20 #: ft
    _N_outs = 1
    _has_power_utility = True
    _minimum_flow = 120
    _units = {'Flow rate': 'ft^3/hr'}
    
    def _design(self):
        feed = self.ins[0]
        self.design_results['Flow rate'] = F_vol = feed.F_vol*35.315 # ft3/hr
        if F_vol < self._minimum_flow:
            self._lb_warning('Flow rate', F_vol, self._minimum_flow)
        F_mass = feed.F_mass*0.0006124 #lb/s
        self.power_utility(0.00058*F_mass**0.82*self.length
                           + self.height*0.00182*F_mass * 0.7457) # kW

Proposed fix
This can be easily fixed by importing lb_warning from bst.utils:

from .decorators import cost
from .._unit import Unit
from ..utils import lb_warning

__all__ = ('ConveyingBelt',)

@cost('Flow rate', CE=567, cost=813, ub=2000, n=0.38, N='Number of conveyors')
class ConveyingBelt(Unit):
    length = 40 #: ft
    height = 20 #: ft
    _N_outs = 1
    _has_power_utility = True
    _minimum_flow = 120
    _units = {'Flow rate': 'ft^3/hr'}
    
    def _design(self):
        feed = self.ins[0]
        self.design_results['Flow rate'] = F_vol = feed.F_vol*35.315 # ft3/hr
        if F_vol < self._minimum_flow:
            lb_warning(source=self, key='Flow rate', value=F_vol, 
                       units='kg/hr', stacklevel=3, lb=self._minimum_flow)
        F_mass = feed.F_mass*0.0006124 #lb/s
        self.power_utility(0.00058*F_mass**0.82*self.length
                           + self.height*0.00182*F_mass * 0.7457) # kW

And it worked for a small test:
Screen Shot 2020-05-29 at 1 58 59 PM

A side note: kwargs are aligned in different orders for lb_warning/ub_warning (source is the last) and bounds_warning (source is the first), not sure if this is intentional/matters, thanks!!!

Environment
OS: macOS Catalina 10.15.4
Python: v3.7.6
biosteam: v2.18.0
thermosteam: v0.18.0

FeatureRequest: Clear cache and retry for failed evaluation

Description
First, thanks for adding the _failed_evaluation function!

I found that doing evaluation for my system, there's a certain Monte Carlo sample that won't trigger any error if I just run this sample, but the system won't be able to converge if this sample is run in evaluation, and I suspect it's because of the cache of other samples run before this one, as my system includes multiple distillation columns.

For the following codes for bst.evaluation._model:

try:
self._update(sample, self._specification)
return [i() for i in self._getters]
except:
return self._failed_evaluation()

I changed to:

try:
    self._system.simulate()
    return [i() for i in self._getters]
except:
    try:
        self._system.empty_recycles()
        self._system.empty_process_streams()
        for i in self._system.units:
            if hasattr(i, '_McCabeThiele_args'):
                i._McCabeThiele_args[:] = 0 # clear the cache for distillation columns 
        self._system.simulate()
        return [i() for i in self._getters]
    except:
        return self._failed_evaluation()

And it solved the problem. Though because this clears the cache, results for samples after this one changed 1-2%. However I don't think this is a big deal.

Proposed Feature
So I'll propose for all units that use cache to speed up simulation, we add a clear_cache function. Then for the system, we also add a clear_system_cache function to call the clear_clear function of those units. In evaluation, if it fails, we first check if clearing cache and process streams/recycles can solve this.

Additional Notes
Since BioSTEAM now allows failed evaluation, another feature that'll be good is to print a summary at the end of evaluation to show how many simulations are good and how many fail - I didn't notice the failed evaluation at first.

Also, for the failed metrics, now they are recorded as nan, so spearman results can't be calculated. However for my particular case, I ran 100 samples and only 1 failed, so I think calculating Spearman's rho for the 99 good samples still make sense. So I'm wondering if we can exclude the failed samples in Spearman calculation?

Thanks a lot!!!

Environment
Python: v 3.7.6
BioSTEAM: v 2.20.9

AttributeError: inlet_utility_stream

Describe the bug
I found this bug when designing HXs, I wasn't sure if it was due to my codes or biosteam, but then I found the same bug existed in the cornstover biorefinery. The heat_utilities attribute works for some units but not for others. I'm not sure whether it's from biosteam or biorefineries.

To reproduce

from biorefineries.cornstover.system import *
# This works
R301.heat_utilities

# But this won't work
BT.heat_utilities

Screenshots
Screen Shot 2020-04-06 at 3 51 24 PM

Environment

  • Python: v3.7.6
  • OS: macOS Catalina 10.15.3
  • BioSTEAM: v2.11.0
  • thermosteam: v0.9.0
  • biorefineries: v2.8.0

Centrifuge LLE is giving attribute error.

from biosteam import Stream
from biosteam.units import Centrifuge_LLE
from biorefineries.lipidcane import species

Set up stream

Stream.species = species.biodiesel_species
feed = Stream(Lipid=1, Methanol=51, Glycerol= 9, Biodiesel=27, T=333.15)

Set up centrifuge

C1 = Centrifuge_LLE(ID='C1',ins = feed, outs = ('light', 'heavy'), species_IDs=('Lipid', 'Methanol', 'Biodiesel'), split=(1, 0.5, 1), solvents=('Glycerol',),solvent_split=(0.05))

Run all methods

C1.simulate()

See all results

C1.diagram()
C1.show(T='degC', P='atm', fraction=True)

Error -

Traceback (most recent call last):
File "", line 1, in
File "C:\Users\Abhishek D\source\repos\Biorefinery Simulation\env_2_new\lib\site-packages\biosteam\units_centrifuge_LLE.py", line 64, in init
Unit.init(ID, ins, outs)
File "C:\Users\Abhishek D\source\repos\Biorefinery Simulation\env_2_new\lib\site-packages\biosteam_unit.py", line 189, in init
self._init_ins(ins, species)
AttributeError: 'str' object has no attribute '_init_ins'

Number of ins and outs for BoilerTurbogenerator

Describe the bug
In biosteam v2.0.1, the numbers of ins and outs for BoilerTurbogenerator are both 3 (in v1.0.8, there are only 2 ins and 2 outs). This seems to be creating empty ins and outs streams.

However, I'm not sure if this unit is currently under development

Environment

  • Python: v3.7.0
  • OS: macOS Catalina 10.15.3
  • BioSTEAM: v2.0.1

ModuleNotFoundError and TypeError in the TEA

Describe the bug
The attempt to reproduce documentation section TEA tea let to this output

---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
~/.local/lib/python3.7/site-packages/lazypkg/__init__.py in __getattr__(self, name)
     52         try:
---> 53             attr = import_module('.'+name, self.__package__)
     54         except ModuleNotFoundError as Error:

~/opt/anaconda3/lib/python3.7/importlib/__init__.py in import_module(name, package)
    126             level += 1
--> 127     return _bootstrap._gcd_import(name[level:], package, level)
    128 

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _gcd_import(name, package, level)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _find_and_load(name, import_)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _find_and_load_unlocked(name, import_)

ModuleNotFoundError: No module named 'biorefineries.lipidcane.lipidcane_sys'

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
<ipython-input-4-a199e55e7f85> in <module>
----> 1 lipidcane_tea = LipidcaneTEA(system=lc.lipidcane_sys,
      2                              IRR=0.15,
      3                              duration=(2018, 2038),
      4                              depreciation='MACRS7',
      5                              income_tax=0.35,

~/.local/lib/python3.7/site-packages/lazypkg/__init__.py in __getattr__(self, name)
     60             unsearchable = self.__unsearchable
     61             for i in self.__all__:
---> 62                 module = getattr(self, i)
     63                 if (isinstance(module, ModuleType)
     64                     and i not in unsearchable

~/.local/lib/python3.7/site-packages/lazypkg/__init__.py in __getattr__(self, name)
     51         attempts = self.__import_attempts
     52         try:
---> 53             attr = import_module('.'+name, self.__package__)
     54         except ModuleNotFoundError as Error:
     55             if name in attempts:

~/opt/anaconda3/lib/python3.7/importlib/__init__.py in import_module(name, package)
    125                 break
    126             level += 1
--> 127     return _bootstrap._gcd_import(name[level:], package, level)
    128 
    129 

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _gcd_import(name, package, level)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _find_and_load(name, import_)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _find_and_load_unlocked(name, import_)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _load_unlocked(spec)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap_external.py in exec_module(self, module)

~/opt/anaconda3/lib/python3.7/importlib/_bootstrap.py in _call_with_frames_removed(f, *args, **kwds)

~/opt/anaconda3/lib/python3.7/site-packages/biorefineries/lipidcane/process_settings.py in <module>
     42          'Gasoline': 0.756} # 2 USD/gal
     43 
---> 44 set_lipidcane_process_settings()

~/opt/anaconda3/lib/python3.7/site-packages/biorefineries/lipidcane/process_settings.py in set_lipidcane_process_settings()
     15     bst.PowerUtility.price = 0.065
     16     HeatUtility = bst.HeatUtility
---> 17     steam_utility = HeatUtility.heating_agents['Low pressure steam']
     18     steam_utility.efficiency = 0.85
     19     steam_utility.T = 529.2

TypeError: list indices must be integers or slices, not str

To Reproduce
First two steps from the TEA documentation (9.1 and the first step of 9.2)

Environment

  • Python: anaconda, 3.7
  • OS: MacOS Catalina version 10.15.2
  • BioSTEAM: v 2.3.4
  • biorefineries v 2.2.5
  • jupyter notebook v 6.0.1

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.