Single unit operation - PCCΒΆ

"""Example use of PCC unit operation.

`pcc` instance is taken from (and generated by) `pcc_template`.

In this example we simulate propagation of constant inlet flow rate
and concentration profiles throughout the pcc. Afterwards we create
a bunch of plots.

"""

import numpy as np

from bokeh.io import show
from bokeh.layouts import gridplot
from bokeh.models import LinearAxis, Range1d
from bokeh.plotting import figure

from bio_rtd.logger import DataStoringLogger
from examples.templates.sc_uo.pcc_template import t, dt, pcc

"""Basic use."""
# Define inlet flow rate. Same shape at `t`.
f = np.ones_like(t) * 3.5
# Inlet concentration profile. Shape = (n_components, t.size).
c = np.ones([1, t.size]) * 4
# Set logger that stored data
pcc.log = DataStoringLogger()
# Simulate.
f_out, c_out = pcc.evaluate(f, c)

"""Plot inlet and outlet profiles."""
p1 = figure(plot_width=695, plot_height=350, title="PCC",
            x_axis_label="t [min]", y_axis_label="c [mg/mL]")
# Add new axis for flow rate to the right.
p1.extra_y_ranges = {'f': Range1d(0, max(f.max(), f_out.max()) * 1.1)}
p1.add_layout(LinearAxis(y_range_name='f'), 'right')
p1.yaxis[1].axis_label = "f [mL/min]"
# Set scale to CV.
x = np.cumsum(f) / pcc.cv * t[1]
p1.line(x, f, y_range_name='f',
        line_width=2, color='red', line_dash='dashed', legend_label='f_in')
p1.line(x, c[0],
        line_width=2, color='navy', line_dash='dashed', legend_label='c_in')
p1.line(x, f_out, y_range_name='f',
        line_width=2, color='red', legend_label='f_out')
p1.line(x, c_out[0],
        line_width=2, color='navy', legend_label='c_out')
# show(p1)

"""Print data from log."""
# get data tree from log
data_tree = pcc.log.get_data_tree(pcc.uo_id)
# print data
print(data_tree["t_cycle_optimization_loop_iter"])

"""Plot elution step profiles from log.

Notes
-----
Model stores parameters in log if
    `pcc.log.log_data == True` and
    `pcc.log.log_level_data <= pcc.log.INFO`.
Model stores parameters and time profiles in log if:
    `pcc.log.log_data == True` and
    `pcc.log.log_level_data <= pcc.log.DEBUG`.

"""
# Elution peak shape.
y = data_tree['p_elution_peak']
p2 = figure(plot_width=695, plot_height=350, title="Elution peak shape",
            x_axis_label="t [min]", y_axis_label="p []")
p2.line(t[:y.size], y, line_width=2, legend_label='elution peak shape')
# Elution peak collection interval.
y_mask = data_tree['elution_peak_interval']
p2.line(t[:y_mask.size], y_mask * y.max() * 0.1, line_width=2, color="black",
        alpha=0.5, legend_label='elution peak collection range')
# Duration of elution step.
p2.line(data_tree['elution_t'] * np.ones(2), np.ones(2) * y.max(),
        line_width=2, line_dash='dotdash', line_color="black", line_alpha=0.7,
        legend_label='elution step end')
# show(p2)

# Plot breakthrough profile for 2 cycles worth of inlet material.
p3 = figure(plot_width=695, plot_height=350, title='Binding capacity',
            x_axis_label="t [min]", y_axis_label="c / c_load")
i_cycle = int(round(data_tree["cycle_t"] / dt))
p3.line(t[:i_cycle * 2], c[0, :i_cycle * 2],
        line_width=2, color="blue", legend_label='load material')
p3.line(t[:i_cycle * 2], pcc.load_bt.calc_c_bound(f, c)[0, :i_cycle * 2],
        line_width=2, color="black", legend_label='bound material')
p3.line((t[i_cycle], t[i_cycle]), (0, c.max()), line_width=2,
        line_dash='dotdash', color="black", alpha=0.7, legend_label='cycle')
p3.line((t[0], t[0]), (0, c.max()), line_width=2, line_dash='dotdash',
        color="black", alpha=0.7, legend_label='cycle')
# show(p3)

# Plot profiles for each cycle of load.
p4_group = []
for i, cycle in enumerate(data_tree['cycles']):
    # When column starts seeing recycled material.
    i_s = cycle['i_cycle_load_start']
    # When column starts seeing load material (beginning of load step).
    i_ss = cycle['i_cycle_load_step_start']
    # When the load step ends.
    i_e = cycle['i_cycle_load_end']

    y_max = 8.5  # Just so all plots have same y range.
    pc = figure(plot_width=695, plot_height=350, title='Cycle ' + str(i),
                x_axis_label="t [min]", y_axis_label="c [mg/mL]")
    # Profiles.
    pc.line(t[i_s:i_e], cycle['f_load'],
            line_width=2, color="blue", legend_label='load flow rate')
    pc.line(t[i_s:i_e], cycle['c_load'][0],
            line_width=2, color="black", alpha=0.5, legend_label='load conc')
    pc.line(t[i_s:i_e], cycle['c_bound'][0],
            line_width=2, color="green", alpha=0.5, line_dash='solid',
            legend_label='bound part of load conc')
    pc.line(t[i_s:i_e], cycle['c_unbound'][0],
            line_width=2, color="green", alpha=0.5, line_dash='dashed',
            legend_label='unbound part of load conc')
    pc.line(t[i_s:i_e], cycle['c_out_load'][0],
            line_width=2, color="red", alpha=1, legend_label='overload conc')
    # Vertical markers.
    pc.line(t[i_s] * np.ones(2), (0, y_max),
            line_width=2, line_dash='dotted', color="black", alpha=0.7,
            legend_label='previous load start')
    if i > 1:
        i_wash_recycle = data_tree["cycles"][i - 1]["c_wash_recycle"].shape[1]
        pc.line(t[i_s + i_wash_recycle] * np.ones(2), (0, y_max),
                line_width=2, line_dash='dashed', color="black", alpha=0.4,
                legend_label='wash recycle end')
    pc.line(t[i_ss] * np.ones(2), (0, y_max),
            line_width=2, line_dash='dotdash', color="black", alpha=0.7,
            legend_label='load start')
    pc.line(t[i_e] * np.ones(2), (0, y_max),
            line_width=2, line_dash='dashdot', color="black", alpha=0.7,
            legend_label='load end')
    t_wash = data_tree["wash_t"]
    pc.line(t[i_e] * np.ones(2) + t_wash, (0, y_max),
            line_width=2, line_dash='dashed', color="black", alpha=0.7,
            legend_label='wash end')
    # ## WASH ##
    cvd = cycle['c_wash_desorbed']
    if cvd is not None and cvd.size > 0:
        pc.line(
            t[i_e] + t[:cvd.shape[1]],
            cvd[0],
            line_width=2, color="blue", line_dash='dotdash',
            legend_label='wash desorbed'
        )
    cow = cycle['c_out_wash']
    if cow is not None and cow.size > 0:
        pc.line(
            t[i_e] + t[:cow.shape[1]],
            cow[0],
            line_width=2, color="blue",
            legend_label='wash outlet'
        )
    pc.legend.location = "top_center"
    if i > 0:
        pc.legend.visible = False
    p4_group.append([pc])
# show(gridplot(p4_group))

show(gridplot([[p1], [p2], [p3], *p4_group]))