CADET with multicomponent colloidal isotherm

Expected behavior.

Forward simulation using colloidal isotherm - load for given time, then wash for given time. Expected binding behavior for protein

Actual behavior

I’m getting an issue

CADETProcessError: CADET Error: Simulation failed with b’ERROR: No protein component present\n’, not sure where it’s coming from

How to produce bug (including a minimal reproducible example)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
# import cadet packages
from CADETProcess.processModel import ComponentSystem
from CADETProcess.processModel import Langmuir, MultiComponentColloidal
from CADETProcess.processModel import (Inlet, LumpedRateModelWithoutPores, Outlet)
from CADETProcess.processModel import FlowSheet
from CADETProcess.processModel import Process
from CADETProcess.reference import ReferenceIO
from CADETProcess.comparison import Comparator
from CADETProcess.optimization import OptimizationProblem
from CADETProcess.optimization import U_NSGA3
#
# conversion
mLmin_to_m3s = 60*1e6 
# input parameters
exp_flow = 8. # MV/min (mL/min)
Q = exp_flow/(mLmin_to_m3s) # convert to m^3/s
# load and wash 
loadVol = 42.3 # mL
washVol = 33.9 # mL
total_vol = loadVol + washVol # mL
exp_time = total_vol/exp_flow*60 # seconds
sample_vol = loadVol
# system tubing
tubeIDmm = 0.75 # mm
tubeID = tubeIDmm/1000 # m
tubeLenmm = 2.7*1000 # mm
tubeLen = tubeLenmm/1000
tubeDisp = 0.0055315109901466425
# load time
pulse_time_s = loadVol/(exp_flow/60.)
# device parameters
device_Dax = 5.413e-9 # m^2/s
# protein size
size_kDa = 150
#
# create component system
component_system = ComponentSystem()
component_system.add_component('device')
# binding model 
binding_model = MultiComponentColloidal(component_system, name='MultiComponentColloidal')
binding_model.is_kinetic = True
binding_model.phase_ratio = 4.5e6
binding_model.kappa_exponential = 0
binding_model.kappa_factor = 0
binding_model.kappa_constant = 4e-9
binding_model.kinetic_rate_constant = 100000000
#---
binding_model.logkeq_exponent_factor = [100.0]
binding_model.logkeq_exponent_multiplier = 0
binding_model.logkeq_power_factor = 0
binding_model.logkeq_ph_exponent = 0
binding_model.logkeq_power_exponent = 0
#---
binding_model.bpp_power_factor = 0
binding_model.bpp_exponent_factor = [0.1]
binding_model.bpp_exponent_multiplier = 0
binding_model.bpp_ph_exponent = 0
binding_model.bpp_power_exponent = 0
#---
binding_model.coordination_number = 6
binding_model.protein_radius = 4.509263378751285e-09
binding_model.bound_states=[0,]
# feed (mAb05)
feed = Inlet(component_system, name='feed')
feed.c = [3.6/size_kDa,]
# eluent (washout buffer)
eluent = Inlet(component_system, name='eluent')
eluent.c = [0,]
# ----------------------
# device 
# ----------------------
device = LumpedRateModelWithoutPores(component_system, name='device')
device.length = 3.305/1000 # from keyence imaging
device.diameter = 2.85/100 # fron Natrix team
device.total_porosity = 0.804
device.axial_dispersion = 4.2507694650704883e-07
device.binding_model = binding_model
# ----------------------
# outlet
# ----------------------
outlet = Outlet(component_system, name='outlet')
outlet.solution_recorder.write_solution_bulk = True
# ------------------------------------------------
# now we create the Flow Sheet
# ------------------------------------------------
flow_sheet = FlowSheet(component_system)
flow_sheet.add_unit(feed, feed_inlet=True)
flow_sheet.add_unit(eluent, eluent_inlet=True)
flow_sheet.add_unit(device)
flow_sheet.add_unit(outlet, product_outlet=True)
flow_sheet.add_connection(feed, device)
flow_sheet.add_connection(eluent, device)
flow_sheet.add_connection(device, outlet)
# --------------------------------------------------------------
# now we create the process, i.e., turning feed on, swap to wash buffer, same flow rate
# --------------------------------------------------------------
process = Process(flow_sheet, 'batch elution')
process.add_event('eluent_on', 'flow_sheet.eluent.flow_rate', Q, 0)
process.add_event('eluent_off', 'flow_sheet.eluent.flow_rate', 0.0)
process.add_event('feed_on', 'flow_sheet.feed.flow_rate', Q)
process.add_event('feed_off', 'flow_sheet.feed.flow_rate', 0.0)
process.add_duration('feed_duration')
process.add_event_dependency('eluent_on', ['feed_off'])
process.add_event_dependency('eluent_off', ['feed_on'])
process.add_event_dependency('feed_off', ['feed_on', 'feed_duration'], [1, 1])
# cycle time
process.cycle_time = exp_time # seconds
# load time
process.feed_duration.time = pulse_time_s
#
# -----------------------------
# simluate the process
# -----------------------------
if __name__ == '__main__':
    from CADETProcess.simulator import Cadet
    process_simulator = Cadet()
    process_simulator.time_resolution = 0.1
    simulation_results = process_simulator.simulate(process)

Hello Rosario,

Thanks for your post!

You define your component system as

component_system = ComponentSystem()
component_system.add_component('device')

However, the MultiComponentColloidal model expects at least two components:

  • A non-binding salt component (index 0).
  • At least one protein component (index 1 or higher).

So your component system should include those, e.g.:

component_system = ComponentSystem(['salt', 'protein'])

Additionally to this change, all component-specific information, such as bound states, feed and eluent concentrations, and concentration changes in events, have to be provided for both components as a list then.

Also, be careful with naming—“device” was used both as a component name and for the binding model later.

For more information in the ComponentSytem() class, also see the CADET-Process documentation. Additionally, find the explanation of the non-binding pseudo component and it’s use in the CADET-core documentation of the MultiComponentColloidal.

Hope that already sheds some light on the issue!

Best,
Hannah

2 Likes