You have been commissioned to assess the hydraulic behavior of a reactor built for a continuous mix process. To this end you have performed a tracer test for the reactor, injecting an instantaneous pulse of rodamine with mass \(M_p\) at the inlet of the reactor, and measuring the concentration \(c_p\) at the outlet. The measured results are available in the file tracer_results.txt.
Plot the tracer concentration at the reactor outlet as a function of time.
Plot the the exit age distribution \(E(t)\), and the cumulative exit age distribution \(F(t)\):
Before solving this problem, it is always instructive to first check the data we will be working on. Most plain text files store valuable information in the header, and we often need to check this information to properly read the file.
File checking is often performed directly reading the file with a competent program such as Notepad++. However here we will be reading the first six lines of the file directly with python:
fname ='tracer_results.txt'n =6withopen(fname) as fpath:for i inrange(n): line = fpath.readline().strip()print(line)
Following our initial check, we recognize that our file contains 2 columns, one for time and one for the tracer concentration. We also note that the header information is identified with the ‘#’ character. Additional valuable information is also provided, so we store it for later use:
# storing header informationMp =1e6# mgQ =150# L/sV =5058# L
Plot of tracer concentration
Once we know the contents of our file, our first task is straightforward: first we read and store the data in the text file, and then we plot it.
We will be reading our data with the loadtxt function of numpy. Since we already know that our file contains 2 columns, we will be unpacking them in 2 arrays. Note that we also pass the ‘#’ character to loadtxt to identify comment lines.
from numpy import loadtxtimport matplotlib.pyplot as plt# loading data and plotting figuretime, conc = loadtxt(fname, comments='#', unpack=True)fig, ax = plt.subplots(figsize=(6,5))ax.plot(time, conc, lw=2, color='limegreen')# axes decorationsax.set_title('Rodamine concentration at the outlet')ax.set_xlabel('time [s]')ax.set_ylabel('Concentration [mg/L]')plt.show()
We note that the outlet concentration is not symmetric with respect to time, exhibiting a concentration tail for later times. This is an expected behavior for well mixed reactors.
Plot of distribution metrics
Following Equation 1, computation of the exit age distribution is straightforward. For the case of the cumulative exit age distribution, we need to proceed by numerical integration, in this case using the cumulative integration function cumulative_trapezoid:
from scipy.integrate import cumulative_trapezoid# computing residence time distribution metricsead = Q *conc / Mpfad = cumulative_trapezoid(ead, time,initial=0)# plotting figuresfig, axes = plt.subplots(1,2, figsize=(12,5))axes[0].plot(time, ead, lw=2)axes[1].plot(time, fad, lw=2)# decoratorsaxes[0].set_title('Exit age distribution')axes[0].set_ylabel('E(t) [min$^{-1}$]')axes[1].set_title('Cumulative exit age distribution')axes[1].set_ylabel('F(t) [adim]')for ax in axes: ax.set_xlabel('time [min]')plt.show()
Morril index
To compute \(T_{90}\) and \(T_{10}\) we need to use the cumulative exit age distribution. Here we will create an interpolated function for time as a function of \(F(t)\), in order to precisely compute the required time values.
from scipy.interpolate import interp1d# computing residence metricsf = interp1d(fad, time, kind='cubic')fracs = [0.1, 0.5, 0.9]times = f(fracs)#printing resultsfor i, val inenumerate(times):print('T{0:2.0f} = {1:>4.1f} min'.format(fracs[i]*100, val) )print('Morril index = {0:1.3f}'.format(times[2]/times[0]) )
T10 = 7.9 min
T50 = 25.5 min
T90 = 56.7 min
Morril index = 7.225
Follow up
Compute the total tracer mass that has leaved the reactor. Compare it with the total pulse mass \(M_p\).
Compute the experimental residence time \(\hat{\tau}\). Compare it with the theoretical residence time \(\tau\).