Optimize time-varying chamber pressure during primary drying
We need a few imports:
import matplotlib.pyplot as plt
from matplotlib import rc as matplotlibrc
from ruamel.yaml import YAML
yaml = YAML()
from lyopronto import opt_Pch, plot_styling
Then, we provide all the necessary simulation parameters.
# Set up the simulation settings
# This needs to be a dict with string keys, which can be expressed in YAML as well
sim = yaml.load("""
tool: Optimizer
Kv_known: true
Rp_known: true
Variable_Pch: true
Variable_Tsh: false
""")
# Or, equivalently:
sim = {
'tool': 'Optimizer',
'Kv_known': True,
'Rp_known': True,
'Variable_Pch': True,
'Variable_Tsh': False}
# Vial and fill properties
vial = {
# Av = Vial area in cm^2
'Av': 3.80,
# Ap = Product Area in cm^2
'Ap': 3.14,
# Vfill = Fill volume in mL
'Vfill': 2.0
}
# Product properties
product = {
# cSolid = Fractional concentration of solute in the frozen solution
'cSolid': 0.05,
# Product Resistance Parameters
'R0': 1.4, # cm^2-hr-Torr/g
'A1': 16.0, # cm-hr-Torr/g
'A2': 0.0, # 1/cm
# Critical product temperature
# At least 2 to 3 deg C below collapse or glass transition temperature
'T_pr_crit': -5 # in deg C
}
# Vial Heat Transfer Parameters
ht = {
'KC': 2.75e-4, # cal/s-cm^2-K
'KP': 8.93e-4, # cal/s-cm^2-K-Torr
'KD': 0.46 # 1/Torr
}
# Chamber Pressure bounds (optimizer will search within these)
Pchamber = {
# min = Minimum chamber pressure in Torr
'min': 0.05,
# max = Maximum chamber pressure in Torr
'max': 1000.0
}
# Shelf Temperature
Tshelf = {
# init = Initial shelf temperature in C
'init': -40.0,
# setpt = Shelf temperature set points in C
'setpt': [10.0],
# dt_setpt = Time for which shelf temperature set points are held in min
'dt_setpt': [1800.0],
# ramp_rate = Shelf temperature ramping rate in C/min
'ramp_rate': 1.0
}
# Equipment capability line
eq_cap = {
'a': -0.182, # slope
'b': 11.7 # intercept
}
# Number of vials (used for equipment capability calculation)
nVial = 398
# Time step
dt = 0.01 # hr
Now, we are ready to run the optimization, which is lyopronto.opt_Pch.dry.
documentation
Here is the docstring for opt_Pch.dry:
Find optimal chamber pressures for a given lyophilization process, with fixed shelf temperatures.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vial
|
dict
|
Vial properties, including 'Vfill', 'Ap', and 'Av'.. |
required |
product
|
dict
|
Product properties, including 'cSolid', 'T_pr_crit', and Rp parameters 'R0', 'A1', and 'A2'. |
required |
ht
|
dict
|
Heat transfer properties, including 'KC', 'KP', and 'KD'. |
required |
Pchamber
|
dict
|
Chamber pressure bounds between which optimizer will search. |
required |
Tshelf
|
dict
|
Shelf temperature set points and time (see docs). |
required |
dt
|
float
|
Fixed time step for output [hours] |
required |
eq_cap
|
dict
|
Equipment capability line, with 'a' slope and 'b' intercept. |
required |
nVial
|
int
|
Number of vials in the load, used for equipment capability calculation. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
output_table |
ndarray
|
Simulation output table with columns for: 0. Time [hr], 1. Sublimation front temperature [°C], 2. Vial bottom temperature [°C], 3. Shelf temperature [°C], 4. Chamber pressure [mTorr], 5. Sublimation flux [kg/hr/m²], 6. Drying percent [%] |
Source code in lyopronto/opt_Pch.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
It's a good idea, particularly when you are exploring interactively, to write the simulation input and output to disk together so that as you do later analysis, you have a record of what you did. Uncommenting the following code will do so.
Previously the inputs were recorded as a two-column CSV, which records info but isn't very readable (and frankly not very useful to a machine either). YAML provides a somewhat more sensible format, and fortunately a Python dictionary can be readily represented in YAML.
sim_setup = {
'sim': sim,
'vial': vial,
'product': product,
'ht': ht,
'Pchamber': Pchamber,
'Tshelf': Tshelf,
'eq_cap': eq_cap,
'nVial': nVial,
'dt': dt,
}
# # Write input data to disk as YAML
# import time
# current_time = time.strftime("%Y%m%d_%H%M%S")
# save_inputs(sim_setup, current_time)
# # Write simulation data to disk as CSV
# try:
# csvfile = open('lyopronto_output_'+current_time+'.csv', 'w')
# writer = csv.writer(csvfile)
# writer.writerow(['Time [hr]','Sublimation Temperature [C]','Vial Bottom Temperature [C]', 'Shelf Temperature [C]','Chamber Pressure [mTorr]','Sublimation Flux [kg/hr/m^2]','Percent Dried'])
# for i in range(0,len(output_table)):
# writer.writerow(output_table[i])
# finally:
# csvfile.close()
Finally, it's a good idea to plot everything. Here, again, you could save plots to disk by uncommenting the lines below.
Some default styling of the axes is carried out with lyopronto.plot_styling.axis_style_*, like turning on minor ticks and setting tick labels to be bold. However, this is entirely up to aesthetic taste and leaving it out does not affect the basic logic of the plots.
matplotlibrc('text.latex', preamble=r'\usepackage{color}')
matplotlibrc('text', usetex=False)
plt.rcParams['font.family'] = 'Arial'
figwidth = 30
figheight = 20
lineWidth = 5
markerSize = 20
fig = plt.figure(0, figsize=(figwidth, figheight))
ax1 = fig.add_subplot(1, 1, 1)
ax2 = ax1.twinx()
ax1.plot(output_table[:, 0], output_table[:, 4], '-', color='b', linewidth=lineWidth, markersize=markerSize, label="Chamber Pressure")
ax2.plot(output_table[:, 0], output_table[:, 5], '-', color=[0, 0.7, 0.3], linewidth=lineWidth, label="Sublimation Flux")
plot_styling.axis_style_pressure(ax1)
plot_styling.axis_style_subflux(ax2)
plt.tight_layout()
# figure_name = 'lyopronto_pressure_subflux_'+current_time+'.pdf'
# plt.savefig(figure_name)
# plt.close()

fig = plt.figure(0, figsize=(figwidth, figheight))
ax = fig.add_subplot(1, 1, 1)
plot_styling.axis_style_percdried(ax)
ax.plot(output_table[:, 0], output_table[:, -1], '-k', linewidth=lineWidth, label="Percent Dried")
plt.tight_layout()
# figure_name = 'lyopronto_percentdried_'+current_time+'.pdf'
# plt.savefig(figure_name)
# plt.close()

fig = plt.figure(0, figsize=(figwidth, figheight))
ax = fig.add_subplot(1, 1, 1)
plot_styling.axis_style_temperature(ax)
ax.plot(output_table[:, 0], output_table[:, 1], '-b', linewidth=lineWidth, label="Sublimation Front Temperature")
ax.plot(output_table[:, 0], output_table[:, 2], '-r', linewidth=lineWidth, label="Maximum Product Temperature")
ax.plot(output_table[:, 0], output_table[:, 3], '-k', linewidth=lineWidth, label="Shelf Temperature")
plt.legend(fontsize=40, loc='best')
ll, ul = ax.get_ylim()
ax.set_ylim([ll, ul + 5.0])
plt.tight_layout()
# figure_name = 'lyopronto_temperatures_'+current_time+'.pdf'
# plt.savefig(figure_name)
# plt.close()
