Skip to content

Pump Error with Pseudo-Biomass Chemical: liquid viscosity method 'NEGLECT_P' is not valid #232

@iilard

Description

@iilard

In my process simulation, after fermentation the broth containing different solutes and biomass (which is defined as a pseudo-compound) go through a decanter (assumed using gravity). After the decanter, the concentrated biomass has to be filtered. For this I need a pump (e.g. assuming a pressure filter) beforehand.
However, it seems the pump in BioSTEAM has an issue with the viscosity method 'NEGLECT_P' due to a component with CASRN 'None'. Likely this is the Biomass.
I would only need the pump for it's electricity usage, for my LCA afterwards.

_settings.py:

import thermosteam as tmo
import biosteam as bst
from _chemicals import chemicals 
from thermosteam import settings

#Activate chemicals list & register them in bio/thermosteam
tmo.settings.set_thermo(chemicals) 
bst.settings.set_thermo(chemicals)

_chemicals.py:

import thermosteam as tmo
import biosteam as bst
from thermosteam import Chemicals, Chemical

def create_chemicals():
    chemicals = Chemicals([])
    def add_chemical(ID, ref=None, **data):
        chemical = Chemical(ID, **data) if ref is None else ref.copy(ID, **data)
        chemicals.append(chemical)
        return chemical
    
    Water = add_chemical('H2O')
    Glucose = add_chemical('Glucose', phase='l', rho=1560, Cp=1.213)
    CaCO3 = add_chemical('CaCO3', phase='s', rho=2710, Cp=0.834, default=True)
    CalciumLactate = add_chemical('CalciumLactate', phase='l', Hf=-1686100, rho=1494) # NOTE: Cp value not available (yet), filler could be ca. 1.4, Hf value probably off as for dissociated aq. species... makes whole fermentor endothermic...
    lla = add_chemical('L-LacticAcid', phase='l', Hf=-686300, Cp=2.109, rho=1206)
    SulfuricAcid = add_chemical('H2SO4', phase='l', rho=1840, Cp=1.38)
    CarbonDioxide = add_chemical('CO2', phase='g')
    Gypsum = add_chemical('CaSO4_2H2O', phase='s', rho=2320, MW=172.18, Hf=-2023000, Cp=1.081, CAS='10101-41-4', mu = 0.00095, search_db=False) # NOTE: Einstein-Equation gives 0.00095 sPa for Gypsum viscosity
    Biomass_s = add_chemical('Biomass_s', phase='s', formula='CH1.8O0.5N0.2', MW=24.626, rho=1093, Hf=-130412, Cp=1.25, search_db=False) 
    nutrient_N = add_chemical('NH3', phase='l')
    Octanol = add_chemical('Octanol', phase='l')
    Trioctylamine = add_chemical('Trioctylamine', phase='l')
    SodiumHydroxide = add_chemical('NaOH', phase='l') 
    SodiumSulfate = add_chemical('Na2SO4', phase='l', rho=1200, Cp=3.5, mu = 0.001, search_db=True)

    chemicals.compile()
    return chemicals
chemicals = create_chemicals()

_reactions.py:

import thermosteam as tmo

def fermentation_reaction():
    fermentation = tmo.Reaction(reaction='Glucose -> 1.84 L-LacticAcid + 0.585 Biomass_s', reactant='Glucose', X=1, basis='mol', correct_atomic_balance=False) 
    return fermentation

def neutralization_reaction():
    neutralization = tmo.Reaction(reaction='2 L-LacticAcid + CaCO3 -> CalciumLactate + CO2 + H2O', reactant='L-LacticAcid', X=1, basis='mol')
    return neutralization

def acidification_reaction():
    acidification = tmo.Reaction(reaction='CalciumLactate + H2SO4 -> 2 L-LacticAcid + CaSO4_2H2O', reactant='CalciumLactate', X=1, basis='mol')
    return acidification

def acidification_2_reaction():
    acidification_2 = tmo.Reaction(reaction='CaCO3 + H2SO4 -> CaSO4_2H2O + CO2 + H2O', reactant='CaCO3', X=1, basis='mol')
    return acidification_2

def caustic_wash_reaction():
    caustic_wash = tmo.Reaction(reaction='2 NaOH + H2SO4 -> Na2SO4 + 2 H2O', reactant='H2SO4', X=1, basis='mol')
    return caustic_wash

_units.py:

import biosteam as bst
import thermosteam as tmo
from biosteam import CSTR
import numpy as np
from _reactions import fermentation_reaction, neutralization_reaction, acidification_reaction, acidification_2_reaction

# Custom Fermentor based on CSTR for cont. ferm.
class Fermentor(bst.CSTR):
    _N_ins = 1
    _N_outs = 2
    def __init__(self, ID='Fermentor', ins=None, outs=(), thermo=None, *, T=323.15, **kwargs):
        if outs == ():
            outs = [None, None]  # liquid, gas
        super().__init__(ID, ins, outs, thermo=thermo, **kwargs)
        self.T=T
        self.fermentation_rxn = fermentation_reaction()
        self.neutralization_rxn = neutralization_reaction()

    def _run(self):
        feed = self.ins[0]
        liquid_effluent, gas_vent = self.outs
        liquid_effluent.copy_like(feed)
        # Apply reactions using the class-defined reactions, first parallel fermentation, then neutralization
        self.fermentation_rxn(liquid_effluent.mol)
        self.neutralization_rxn(liquid_effluent.mol)
        # Handle gas separation - remove CO2 from liquid and send to gas vent
        gas_vent.copy_flow(liquid_effluent, 'CO2', remove=True)
        liquid_effluent.phase = 'l'
        gas_vent.phase = 'g'
        liquid_effluent.T = gas_vent.T = self.T


# Custom Acidification Reactor
class AcidificationReactor(bst.CSTR):
    _N_ins = 2
    _N_outs = 2

    def __init__(self, ID='', ins=None, outs=(), P=101325, T=298.15, tau=1.0, V=1.0): # check assumptions
        if outs == ():
            outs = [None, None]
        super().__init__(ID, ins, outs, tau=tau, T=T, P=P)

        self.acidification_rxn = acidification_reaction()
        self.acidification_2_rxn = acidification_2_reaction()
        
    def _run(self):
        feed, acid = self.ins
        effluent, vent = self.outs

        # Acid dosing
        n_CL = feed.imol['CalciumLactate']
        acid.imol['H2SO4'] = n_CL * 1.1
        acid.imass['H2O'] = acid.imass['H2SO4'] / 0.93 * 0.07

        # Mix and pre‑instantiate phases
        effluent.mix_from([feed, acid])
        effluent.phases = ('l', 's')
        effluent.T = self.T
        effluent.P = self.P

        # Reactions
        self.acidification_rxn(effluent)
        self.acidification_2_rxn(effluent)

        # CO2 Vent
        vent.copy_flow(effluent, 'CO2', remove=True)
        vent.phase = 'g'
        vent.T = effluent.T
        vent.P = effluent.P

        # Force gypsum into solid phase (safety net)
        n_total = effluent.imol['CaSO4_2H2O']
        if n_total:
            effluent.imol['l', 'CaSO4_2H2O'] = 0
            effluent.imol['s', 'CaSO4_2H2O'] = n_total

        # Keep outlet as slurry/liquid
        effluent.phase = 'l'

_system.py:

import biosteam as bst
from _units import Fermentor, AcidificationReactor  # custom classes
from _reactions import fermentation_reaction, neutralization_reaction, acidification_reaction, caustic_wash_reaction  # reaction functions
from _settings import chemicals  # compiled thermo package

# -------------------- FEED STREAMS FOR ALL UNITS --------------------
glucose_feed = bst.Stream('glucose_feed', Glucose=2476, Water=28483, units='kg/hr') # 80 g/L
CaCO3_feed = bst.Stream('CaCO3_feed', CaCO3=1277.8, Water=2373, units='kg/hr') # 0.92mol CaCO3 per 1mol Glucose (reactions) --> + excess 1% // 35wt% CaCO3 slurry
H2SO4_feed = bst.Stream('H2SO4_feed', units='kg/hr')
Organic_Solvent = bst.Stream('Organic_Solvent', Octanol=5340.6, Trioctylamine=5340.6, units='kg/hr') # 1.5 : 1 ratio -> org : aq phase
Extr_Water_1 = bst.Stream('Extraction_water_1', H2O=5494.6, units='kg/hr') # Patent Ratio 0.4 : 1 (Water kg : Extract kg)
Extr_Water_2 = bst.Stream('Extraction_water_2', H2O=4695.1, units='kg/hr') # Patent Ratio 0.4 : 1 (Water kg : Extract kg)
Caustic_Sol = bst.Stream('Caustic_Solution', NaOH=123.84, H2O=1548, units='kg/hr')
OrgSolvent_Makeup = bst.Stream('Solvent_Makeup', Octanol=53.2512, Trioctylamine=53.5608, units='kg/hr') # Add the 1% of solvent lost in caustic wash
Recycled_Solvent = bst.Stream('Recycled_Solvent')

# -------------------- SUBSYSTEM 1: CONVERSION --------------------
# Unit and stream definitions
with bst.System('conversion_sys') as conversion_sys:
    feedstock_mixer = bst.Mixer('feedstock_mixer', ins=[glucose_feed, CaCO3_feed], outs=('crude_feed'))
    preheater = bst.HXutility('preheater', feedstock_mixer.outs[0], outs=('heated_feed'), T=323.15) # NOTE: Placeholder 50°C
    fermentor = Fermentor('fermentor', preheater.outs[0], outs=['fermented_broth', 'CO2'], P=101325, T=323.15, tau=72, V_wf=0.8, kW_per_m3=0.05, dT_hx_loop=35)    # NOTE find source for possible kW_per_m3 value for anaerobic ferm.
    ferm_cooler = bst.HXutility('fermentor_cooler', fermentor.outs[0], outs=('cooled_broth'), T=298.15)


# -------------------- SUBSYSTEM 2.1: SEPARATION --------------------
# Biomass Removal --> NOTE: Fine tuning of splits, realism vs. idealism
# Acidification & Gypsum Filtration

with bst.System('separation_sys') as separation_sys:
    gravity_decanter = bst.units.SolidsSeparator('GravityDecanter',
                                            ins=ferm_cooler.outs[0], outs=('decanter_underflow', 'decanter_overflow'),
                                            split={'Biomass_s': 1, 'CaCO3': 0.85, 'CalciumLactate': 0.05}, # NOTE: FINE TUNING, HOW MUCH PRODUCT LOST?
                                            moisture_content=0.35)
 
    slurry_p = bst.Pump('slurry_pump', gravity_decanter.outs[0], outs=('pressurized_decanted_cake'), P=200000, pump_type='Default')

    bm_Filter = bst.units.SolidsSeparator('BM_Filter',
                                          ins=slurry_p.outs[0], outs=('BM_cake', 'BM_filtrate'),
                                          split={'Biomass_s': 1, 'CaCO3': 0.75},
                                          moisture_content=0.08)
    filtrate_mixer = bst.Mixer('filtrate_mixer', ins=(gravity_decanter.outs[1], bm_Filter.outs[1]), outs='clarified_broth')
    acidification_reactor = AcidificationReactor('AcidificationReactor', ins=[filtrate_mixer.outs[0], 'H2SO4_feed'], outs=('acidified_slurry', 'CO2'))
    gypsum_filter = bst.units.SolidsSeparator('GypsumFilter', ins=acidification_reactor.outs[0], outs=('gypsum_cake', 'lactic_acid_solution'), 
                                          split={'CaSO4_2H2O': 0.99}, # Fine particles stay, removed by extraction
                                          moisture_content=0.2)


# -------------------- SUBSYSTEM 2.2A: PURIFICATION --------------------
# MEE 1 to conc. extraction feed
# Extraction with organic amine solvent
# 2 Stage Back-Extraction into fresh water
# MEE 2 to 88wt% lla

with bst.System('purification_sys') as purification_sys:
    Evaporator_1 = bst.units.MultiEffectEvaporator(ID='MEE_pre_extr', ins=[gypsum_filter.outs[1]], outs=('conc_la_sol', 'condensate'), 
                                                   V=0.85, V_definition='Overall', # NOTE adjust to 30wt% lactic acid conc. before extraction for eff.
                                                   P=(101325, 90000, 70000)) 


    ExtrMixer1 = bst.LiquidsMixingTank(ID='Extraction_M1', ins=[Evaporator_1.outs[0], Organic_Solvent], outs=['mixed_phase_1'])
    PreExtr_Cooler = bst.HXutility('extraction_cooler', ins=ExtrMixer1.outs[0], T=313.15)  # 40°C extr feed for optimal eff.
    ExtrSettler1 = bst.LiquidsSplitSettler(ID='Extraction_S1', ins=[PreExtr_Cooler.outs[0]], outs=['organic_phase_1', 'aq_phase_1'],
                                    split={'L-LacticAcid': 0.99, # Simulate two stage extraction in one
                                            'H2SO4': 0.98,
                                            'Octanol': 1,
                                            'Trioctylamine': 1,
                                            'H2O': 0.01, # some water carry-over
                                            'CaCO3': 0,
                                            'CaSO4_2H2O': 0,
                                            'NH3' : 0}) # NOTE "realistic" assumptions of split

    BackExtrMixer1 = bst.LiquidsMixingTank('BackExtr_Mixer1', ins=[ExtrSettler1.outs[0], Extr_Water_1])
    PreBackExtr_Heater1 = bst.HXutility('backextr_heater1', ins=BackExtrMixer1.outs[0], T=348.15) # ca. 20-30°C higher T for Back Extraction
    BackExtrSettler1 = bst.LiquidsSplitSettler('BackExtr_Settler1',
                                               ins=PreBackExtr_Heater1.outs[0],
                                               outs=('back_extr_aq1', 'back_extr_org1'),
                                               split={'L-LacticAcid': 0.85,
                                                   'H2SO4': 0,
                                                   'H2O': 0.99,
                                                   'Octanol': 0,
                                                   'Trioctylamine': 0})

    BackExtrMixer2 = bst.LiquidsMixingTank('BackExtr_Mixer2', ins=[BackExtrSettler1.outs[1], Extr_Water_2])
    PreBackExtr_Heater2 = bst.HXutility('backextr_heater2', ins=BackExtrMixer2.outs[0], T=348.15) # ca. 20-30°C higher T for Back Extraction
    BackExtrSettler2 = bst.LiquidsSplitSettler('BackExtr_Settler2',
                                               ins=PreBackExtr_Heater2.outs[0],
                                               outs=('back_extr_aq2', 'back_extr_org2'),
                                               split={'L-LacticAcid': 0.8, # 80% of remaining LLA (total 97%)
                                                   'H2SO4': 0,
                                                   'H2O': 0.99,
                                                   'Octanol': 0,
                                                   'Trioctylamine': 0})
    AqPhaseMixer = bst.MixTank('PostExtraction_AqPhase', ins=[BackExtrSettler1.outs[0], BackExtrSettler2.outs[0]], outs='pure_lactic_acid_solution')

# -------------------- SUBSYSTEM 2.2B: SOLVENT RECOVERY & RECYCLING --------------------
#Organic solvent washed (neutralized) with aq. NaOH solution

    Caustic_Mixer = bst.LiquidsMixingTank('Caustic_Mixer', ins=[BackExtrSettler2.outs[1], Caustic_Sol], outs=('caustic_mix'))
    CausticWash_Reactor = bst.SinglePhaseReactor('Caustic_Wash', ins=Caustic_Mixer.outs[0], outs=('neutralized_solvent'),
                                                 T=323.15, P=101325, V_wf=0.8,
                                                 tau=0.5, 
                                                 reaction=caustic_wash_reaction())
    Caustic_Settler = bst.LiquidsSplitSettler('Caustic_Settler', 
                                              ins=CausticWash_Reactor.outs[0], 
                                              outs=('caustic_w_waste_brine', 'recovered_solvent'),
                                              split={
                                                 'Octanol': 0.01,
                                                 'Trioctylamine': 0.01,
                                                 'NaOH': 1,
                                                 'Na2SO4': 1,
                                                 'H2O': 1,
                                                 'L-LacticAcid': 1})
    OrgSolvent_Recycler = bst.Mixer('Solvent_Mixer', ins=[Caustic_Settler.outs[1], OrgSolvent_Makeup], outs=Organic_Solvent)

# MEE 2
    Evaporator_2 = bst.MultiEffectEvaporator(ID='MEE_post_extr', ins=[AqPhaseMixer.outs[0]], outs=('product', 'condensate'),
    V=0.95, V_definition='Overall', P=(101325, 80000, 60000, 30000)) # NOTE Fine tuning
    Evaporator_2.split={
        'H2O': 1,
        'L-LacticAcid': 0}
    
# Finisher Flash for 88wt%
    Evaporator_3 = bst.Flash(ID='FlashEvaporator', ins=Evaporator_2.outs[0], outs=('condensate', '88wt_product'), P=30000, V=0.414) # Tuned to give 88wt% LLA


# -------------------- MAIN SYSTEM --------------------
# Combine subsystems into the main system
lactic_acid_system = bst.System('lactic_acid_system',
                                path=[conversion_sys, separation_sys, purification_sys],
                                recycle=[Organic_Solvent])

# SIMULATION 
if __name__ == '__main__':
    lactic_acid_system.simulate()
    print("Simulation completed!")

gravity_decanter.outs[0].show()
gravity_decanter.outs[1].show()
bm_Filter.outs[0].show()
bm_Filter.outs[1].show()

ERROR:

Traceback (most recent call last):
  File "c:\Users\ivano\Desktop\Masterarbeit\Code\_system.py", line 136, in <module>
    lactic_acid_system.simulate()
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_system.py", line 3105, in simulate
    with self.flowsheet:
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_flowsheet.py", line 120, in __exit__
    if exception: raise exception
                  ^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_system.py", line 3166, in simulate
    raise error
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_system.py", line 3154, in simulate
    if design_and_cost: self._summary()
                        ^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_system.py", line 2862, in _summary
    f(i, i._summary)
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 94, in try_method_with_object_stamp
    raise_error_with_object_stamp(object, error)
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 84, in raise_error_with_object_stamp
    raise error
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 88, in try_method_with_object_stamp
    return method(*args)
           ^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_system.py", line 2862, in _summary
    f(i, i._summary)
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 94, in try_method_with_object_stamp
    raise_error_with_object_stamp(object, error)
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 84, in raise_error_with_object_stamp
    raise error
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\exceptions.py", line 88, in try_method_with_object_stamp
    return method(*args)
           ^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\_unit.py", line 1191, in _summary
    self._design(**design_kwargs) if design_kwargs else self._design()
                                                        ^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\biosteam\units\_pump.py", line 175, in _design
    nu = si.nu
         ^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\_stream.py", line 1450, in nu
    mu = self.mu
         ^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\_stream.py", line 1415, in mu
    return self._get_property('mu')
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\_stream.py", line 1351, in _get_property
    property_cache[name] = value = calculate(
                                   ^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\mixture\ideal_mixture_model.py", line 62, in __call__
    return sum([j * models[i](phase, T, P) for i, j in mol.dct.items()])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\mixture\ideal_mixture_model.py", line 62, in <listcomp>
    return sum([j * models[i](phase, T, P) for i, j in mol.dct.items()])
                    ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\base\phase_handle.py", line 131, in __call__
    return self.model(T, P)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermosteam\thermo\tp_dependent_property.py", line 44, in __call__
    return self.TP_dependent_property(T, P)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ivano\miniconda3\envs\glukose-env\Lib\site-packages\thermo\utils\tp_dependent_property.py", line 288, in TP_dependent_property
    raise RuntimeError(f"{self.name} method '{method_P}' is not valid at T={T} K and P={P} Pa for component with CASRN '{self.CASRN}'")
RuntimeError: <System: separation_sys> <Pump: slurry_pump> liquid viscosity method 'NEGLECT_P' is not valid at T=298.15 K and P=101325.0 Pa for component with CASRN 'None'

What should happen is that the pump increases the pressure of the fermentation broth to 200 kPa for the filter to work. Obviously a centrifugal pump wouldn't make sense here as the biomass is very dense (low moisture and solute content).

  • Is there a way to make the pump work or would you recommend another approach for such dense mixtures (and pseudo compound)?

Alternatively I will have to use a ScrewPress, but then I will neglect the electricity usage in my LCA and it won't be accurate in terms of representing the process description I'm using.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions