---
jupytext:
  formats: md:myst,py:percent
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.17.1
kernelspec:
  display_name: Python 3
  language: python
  name: python3
---

(clr_process)=
# Closed Loop Recycling Process
In closed-loop recycling (CLR), the stock mixture is pumped over the column several times until the desired purity is achieved.
The general structure of a CLR is shown below.

```{figure} ./figures/clr_flow_sheet.svg
:name: clr_flow_sheet

Flow sheet for closed-loop recycling process.
```

To realize the recycling, the {attr}`~CADETProcess.processModel.FlowSheet.output_state` of the column needs to be modified, leading to the following event structure:

```{figure} ./figures/clr_events.svg
:name: clr_events

Events for closed-loop recycling process.
```

For this example, consider a two-component system with a Langmuir isotherm.

## Component System

```{code-cell}
from CADETProcess.processModel import ComponentSystem

component_system = ComponentSystem(['A', 'B'])
```

## Binding Model

```{code-cell}
from CADETProcess.processModel import Langmuir

binding_model = Langmuir(component_system, name='langmuir')
binding_model.adsorption_rate = [0.04, 0.05]
binding_model.desorption_rate = [1, 1]
binding_model.capacity = [100, 100]
```

## Unit Operations

```{code-cell}
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]

pump = Cstr(component_system, name='pump')
pump.V = 1e-9

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

outlet = Outlet(component_system, name='outlet')
```

## Flow Sheet

```{code-cell}
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(pump)
flow_sheet.add_unit(column)
flow_sheet.add_unit(outlet, product_outlet=True)

flow_sheet.add_connection(feed, column)
flow_sheet.add_connection(eluent, column)
flow_sheet.add_connection(column, outlet)
flow_sheet.add_connection(column, pump)
flow_sheet.add_connection(pump, column)
```

## Process

```{code-cell}
from CADETProcess.processModel import Process
process = Process(flow_sheet, 'clr')
```

### Create Events and Durations

```{code-cell}
Q = 60 / (60 * 1e6)

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_event('eluent_off', 'flow_sheet.eluent.flow_rate', 0.0)
process.add_event('eluent_on', 'flow_sheet.eluent.flow_rate', Q)

process.add_event('recycle_on_state', 'flow_sheet.output_states.column', {'pump': 1})
process.add_event('recycle_on_pump', 'flow_sheet.pump.flow_rate', Q)
process.add_event('recycle_off_state', 'flow_sheet.output_states.column', {'outlet': 1})
process.add_event('recycle_off_pump', 'flow_sheet.pump.flow_rate', 0)
```

### Event dependencies
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.

```{code-cell}
process.add_event_dependency('eluent_off', ['feed_on'])

process.add_event_dependency('recycle_on_state', ['feed_off'])
process.add_event_dependency('recycle_on_pump', ['feed_off'])

process.add_event_dependency('recycle_off_pump', ['recycle_off_state'])
process.add_event_dependency('eluent_on', ['recycle_off_state'])
```

### Event Times
Now, the cycle time is set to $10~min$ and the `feed_duration` to $1~min$.

```{code-cell}
process.cycle_time = 2000
process.feed_off.time = 40
process.recycle_off_state.time = 1280
```

## Simulate Process

Here, the first plot shows the concentration profile at the column outlet.
It is important to note that since part of this profile is recycled, the concentration profile at the system outlet must be considered (second plot) to evaluate the process performance.

```{code-cell}
if __name__ == '__main__':
    from CADETProcess.simulator import Cadet
    process_simulator = Cadet()

    simulation_results = process_simulator.simulate(process)
    simulation_results.solution.column.outlet.plot()
    simulation_results.solution.outlet.inlet.plot()
```

## Optimize Fractionation Times

```{code-cell}
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()
```

## Peak Shaving
The disadvantage of the CLR process is an increased dispersion due to multiple passes through the pump and additional piping.

To improve the overall process performance, the CLR process is often combined with peak shaving.
In this process, the initial and final regions of the chromatogram with sufficient purity are "shaved off" during each cycle.
Peak shaving can reduce the number of recycling cycles required, since a decreasing amount of components must be pumped across the column.

```{figure} ./figures/clr_peak_shaving_events.svg
:name: clr_peak_shaving_events

Events for closed-loop recycling process with peak shaving.
```
