Steady-State Recycling Process#
In addition to the recycled fraction, fresh feed can also be injected in each cycle, resulting in the formation of a cyclic steady-state. This process, called closed-loop steady-state recycling (CL-SSR), can achieve higher productivity compared to CLR. However, due to additional dispersion in the system periphery, maintaining the separation of components generated during the passage of the column is difficult to realize. Hence, determining the optimal time at which to add new feed is therefore complex. To overcome this problem, a tank can be inserted in which the recycling fraction and new feed are mixed. The recycling fraction and new feed are then injected together in a process called mixed-recycle steady-state recycling (MR-SSR). A schematic flow diagram of the MR-SSR process is shown below.
For this demonstration, consider a two-component system with a Langmuir isotherm.
Component System#
from CADETProcess.processModel import ComponentSystem
component_system = ComponentSystem(['A', 'B'])
Binding Model#
from CADETProcess.processModel import Langmuir
binding_model = Langmuir(component_system, name='langmuir')
binding_model.is_kinetic = False
binding_model.adsorption_rate = [0.02, 0.03]
binding_model.desorption_rate = [1, 1]
binding_model.capacity = [100, 100]
Unit Operations#
from CADETProcess.processModel import (
Inlet, Cstr, LumpedRateModelWithoutPores, Outlet
)
feed = Inlet(component_system, name='feed')
feed.c = [10, 10]
eluent = Inlet(component_system, name='eluent')
eluent.c = [0, 0]
tank = Cstr(component_system, name='tank')
tank.V = 0.001
tank.c = [10, 10]
column = LumpedRateModelWithoutPores(component_system, name='column')
column.binding_model = binding_model
column.length = 0.6
column.diameter = 0.024
column.axial_dispersion = 4.7e-7
column.total_porosity = 0.7
column.solution_recorder.write_solution_bulk = True
outlet = Outlet(component_system, name='outlet')
Flow Sheet#
from CADETProcess.processModel import FlowSheet
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(tank)
flow_sheet.add_unit(column)
flow_sheet.add_unit(outlet, product_outlet=True)
flow_sheet.add_connection(feed, tank)
flow_sheet.add_connection(tank, column)
flow_sheet.add_connection(eluent, column)
flow_sheet.add_connection(column, tank)
flow_sheet.add_connection(column, outlet)
Process#
To realize the recycling, the output_state
of the column needs to be modified.
To reduce the number of event times that need to be specified, event dependencies are specified which enforce that always either feed or eluent are being pumped through the column.
from CADETProcess.processModel import Process
process = Process(flow_sheet, 'mrssr')
Q = 60/(60*1e6)
# Create Events and Durations
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('inject_on', 'flow_sheet.tank.flow_rate', Q)
process.add_event('inject_off', 'flow_sheet.tank.flow_rate', 0.0)
process.add_event('eluent_on', 'flow_sheet.eluent.flow_rate', Q)
process.add_event_dependency('eluent_on', ['inject_off'])
process.add_event('eluent_off', 'flow_sheet.eluent.flow_rate', 0.0)
process.add_event_dependency('eluent_off', ['inject_on'])
process.add_event('recycle_on', 'flow_sheet.output_states.column', 0)
process.add_event('recycle_off', 'flow_sheet.output_states.column', 1)
process.add_event_dependency('feed_on', ['inject_off'])
process.add_event_dependency('feed_off', ['feed_on', 'feed_duration'], [1, 1])
process.add_event_dependency(
'inject_off', ['inject_on', 'feed_duration', 'recycle_off', 'recycle_on'],
[1, 1, 1, -1]
)
Now, the cycle time is set to \(10~min\) and the feed_duration
to \(1~min\) and the recycling times are specified.
process.cycle_time = 600
process.feed_duration.time = 60
process.recycle_on.time = 360
process.recycle_off.time = 420
Simulate Process#
Since the process shows a startup behavior before reaching steady state, multiple cycles need to be simulated.
For this purpose, a StationarityEvaluator
is used (see Cyclic Stationarity).
if __name__ == '__main__':
from CADETProcess.simulator import Cadet
process_simulator = Cadet()
process_simulator.evaluate_stationarity = True
simulation_results = process_simulator.simulate(process)
simulation_results.solution.column.outlet.plot()
simulation_results.solution_cycles.column.outlet[-1].plot()
Optimize Fractionation Times#
To evaluate the process performance, fractionation times can determined to maximize yield and ensure purity constraints.
if __name__ == '__main__':
from CADETProcess.fractionation import FractionationOptimizer
fractionation_optimizer = FractionationOptimizer()
fractionator = fractionation_optimizer.optimize_fractionation(
simulation_results, purity_required=[0.95, 0.95]
)
print(fractionator.performance)
_ = fractionator.plot_fraction_signal()
Performance(mass=array([0.00049825, 0.00042765]), concentration=array([6.34049584, 2.05717073]), purity=array([0.95 , 0.94941365]), recovery=array([0.83041423, 0.7127434 ]), productivity=array([0.01019788, 0.00875283]), eluent_consumption=array([1.03801779, 0.89092925]) mass_balance_difference=array([1.31944574e-06, 1.61649770e-06]))