Tutorial about Mass transfer in membrane processes

Tutorial about Mass transfer in membrane processes#

Assuming total retention, what is the polarization module inside a tubular membrane (1[m] long), and a flat sheet membrane at the following two conditions:

  • \(d_h\) = 10 [cm], \(\mu\) = 0.001 [Pa s], \(\rho\) = 1000 [kg/m\(^3\)], \(U\) = 0.5 [m/s], \(D\) = 1.5E-9 [m\(^2\)/s]

  • \(d_h\) = 10 [cm], \(\mu\) = 10 [Pa s], \(\rho\) = 1400 [kg/m\(^3\)], \(U\) = 0.5 [m/s], \(D\) = 1.5E-9 [m\(^2\)/s]

transmembrane flux of 0.01 [mm/s]

Extra question: how sensitive is the polarzation to the retention ? Hint: include \(C_p\) in the model equations and assume a retention of 70%, meaning \(\frac{C_p}{C_b} = 0.3\)

import numpy as np
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 1
----> 1 import numpy as np

ModuleNotFoundError: No module named 'numpy'
# Start by defining the parameters
d_h = 0.1   # m
mu = 0.001  # Pa s
# mu = 10     # Pa s
rho = 1000  # kg/m^3
# rho = 1400  # kg/m^3
U = 0.1     # m/s
D = 1.5*(10**-9)   # m^2/s
L = 1       # m
J_v = 1e-5   # m/s

# Determine the Reynolds Number and the Schmidt Number
Re = U*d_h*rho/mu
Sc = mu/(rho*D)
print(f'Reynolds Number: {Re:.1f}')
print(f'Schmidt Number: {Sc:.0f}')
Reynolds Number: 10000.0
Schmidt Number: 667
# Based on the above result, use the appropriate correlation for the Sherwood Number

## Laminar flow, Tubular membrane:
# Sh = 1.62*(Re*Sc*(d_h/L))**0.33
## Laminar flow, Flat sheet membrane:
Sh = 0.664*(Re**0.5)*(Sc**0.33)
## Turbulent flow, Tubular membrane:
# Sh = 0.023*(Re**0.88)*(Sc**0.33)
## Turbulent flow, Flat sheet membrane:
# Sh = 0.036*(Re**0.8)*(Sc**0.33)
print(f'Sherwood Number: {Sh:.0f}')
Sherwood Number: 568
# Now we can calculate the mass transfer coefficient using the definition of the Sherwood number
K_m = Sh*D/d_h
print(f'Mass transfer coefficient: {K_m} [m/s]')
Mass transfer coefficient: 8.514305382094568e-06 [m/s]
# Finally, the polarization module can calculated
print(J_v/K_m)
PM = np.exp(J_v/K_m)
print(f'Polarization module: {PM}')
1.1744939312406883
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 3
      1 # Finally, the polarization module can calculated
      2 print(J_v/K_m)
----> 3 PM = np.exp(J_v/K_m)
      4 print(f'Polarization module: {PM}')

NameError: name 'np' is not defined

#Extra tutorial about mass transfer in bioreactors

This is a simple monod type kinetic model describing the growth of a microorganism, the substrate consumption and product formation, coupled with an interfacial mass transfer model describing the oxygen mass transfer rate, in a batch reactor.

from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import numpy as np
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[6], line 1
----> 1 from scipy.integrate import solve_ivp
      2 import matplotlib.pyplot as plt
      3 import numpy as np

ModuleNotFoundError: No module named 'scipy'
# Define initial conditions of the simulation
x0 = 0.025 # g/L   Biomass concentration
s0 = 70    # g/L   Substrate concentration
p0 = 0     # g/L   Product concentration
o0 = 0     # g/L   Dissolved oxygen concentration
ini = [x0, s0, p0, o0]
# Define model parameters
Ks = 0.79     # g/L Substrate limitation constant
Yxs = 0.0857  # g/g Substrate to biomass yield
alpha = 5.59  # g/g Biomass to product yield
Osat = 0.007  # g/L Oxygen saturation at 25C, 1atm (from Henrys law)
kla = 300     # h^-1 Gas-Liquid mass transfer coefficient
Yso = 0.2266  # gO2/gGlucose Oxygen to substrate yield
mu_max = 2.16 # h^-1 Maximum growth rate of the microorganism
Ko = 0.001    # g/L Oxygen limitation constant
par = (Ks, Yxs, alpha, Osat, kla, Yso, mu_max, Ko)

Batch operation equations:

# Define the model equations
def batch(t, y, par):
  # Call parameters
  x, s, p, o = y
  Ks, Yxs, alpha, Osat, kla, Yso, mu_max, Ko = par
  # Emperical Monod equation
  mu = mu_max * (s/(Ks+s)) * (o/(Ko+o))
  # system of ODE to be solved
  dxdt = mu * x
  dsdt = -dxdt / Yxs
  dpdt = alpha * dxdt
  dodt = (kla * (Osat - o)) - (Yso * (-dsdt))
  return [dxdt, dsdt, dpdt, dodt]
# Run the simulation
tspan = (0, 10) # hours
sol = solve_ivp(batch, tspan, ini, method='Radau', args=(par,))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 3
      1 # Run the simulation
      2 tspan = (0, 10) # hours
----> 3 sol = solve_ivp(batch, tspan, ini, method='Radau', args=(par,))

NameError: name 'solve_ivp' is not defined
# Plotting the results using matplotlib
plt.figure()
plt.plot(sol.t, sol.y[0], 'k-', label='[X]')
plt.plot(sol.t, sol.y[1], 'r-', label='[S]')
plt.plot(sol.t, sol.y[2], 'g-', label='[P]')
plt.xlabel('Time [h]')
plt.ylabel('Concentration [g/L]')
plt.xlim(tspan)
plt.ylim (0,)
plt.legend()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[11], line 2
      1 # Plotting the results using matplotlib
----> 2 plt.figure()
      3 plt.plot(sol.t, sol.y[0], 'k-', label='[X]')
      4 plt.plot(sol.t, sol.y[1], 'r-', label='[S]')

NameError: name 'plt' is not defined
# Plotting the dissolved oxygen concentration
plt.figure()
plt.plot(sol.t, (sol.y[3]*100/Osat),'k-',label='O2 saturation')
plt.xlabel('time [hours]')
plt.ylabel('O2 saturation [sat%]')
plt.xlim(tspan)
plt.ylim(-10,110)
plt.legend()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[12], line 2
      1 # Plotting the dissolved oxygen concentration
----> 2 plt.figure()
      3 plt.plot(sol.t, (sol.y[3]*100/Osat),'k-',label='O2 saturation')
      4 plt.xlabel('time [hours]')

NameError: name 'plt' is not defined

Exercise: modify the model equations to simulate a fedbatch fermentation and a continuous fermentation. Parameters for the fedbatch reactor:

  • V\(_{initial}\) = 200 [L]

  • F = 0.1 [m\(^3\)/h]

  • Sf = 70 [g/L]

  • S0 = 10 [g/L]

  • Max working volume = 500 [L]

Parameters for the continuous reactor

  • D = 0.1 [h\(^{-1}\)]

  • Sf = 70 [g/L]

  • S0 = 70 [g/L]

SPOILER!! the solution to the fedbatch reactors is shown bellow!

Extra exercise: use an optimizer to optimize the final product concentration by changing the feeding rate (max feeding rate is 200 [L/h]).
HINT: use the Scipy.optimise.minimize package and define constraints.

##FEDBATCH MODEL

# Define initial conditions of the simulation
x0 = 0.025*10 # g/L   Biomass concentration
s0 = 10    # g/L   Substrate concentration
p0 = 0     # g/L   Product concentration
o0 = 0     # g/L   Dissolved oxygen concentration
V0 = 200   # L
ini = [x0, s0, p0, o0, V0]
# Define model parameters, including the fedbatch parameters
Ks = 0.79     # g/L Substrate limitation constant
Yxs = 0.0857  # g/g Substrate to biomass yield
alpha = 5.59  # g/g Biomass to product yield
Osat = 0.007  # g/L Oxygen saturation at 25C, 1atm (from Henrys law)
kla = 300     # h^-1 Gas-Liquid mass transfer coefficient
Yso = 0.2266  # gO2/gGlucose Oxygen to substrate yield
mu_max = 2.16 # h^-1 Maximum growth rate of the microorganism
Ko = 0.001    # g/L Oxygen limitation constant
F = 50        # L/h Feeding rate
Sf = 70       # g/L Substrate concentration in the feed
par = (Ks, Yxs, alpha, Osat, kla, Yso, mu_max, Ko, F, Sf)
def fedbatch(t, y, par):
  # Call parameters
  x, s, p, o, V = y
  Ks, Yxs, alpha, Osat, kla, Yso, mu_max, Ko, F, Sf = par
  # Limiting the feeding to the max volume with an "if" statement
  if V > 700:
    F=0
  # Emperical Monod equation
  mu = mu_max * (s/(Ks+s)) * (o/(Ko+o))
  # system of ODE to be solved
  dxdt = (mu * x) - x*(F/V)
  dsdt = -((mu * x) / Yxs) + ((Sf-s)*(F/V))
  dpdt = alpha * dxdt - p*(F/V)
  dodt = (kla * (Osat - o)) - (Yso * ((mu * x) / Yxs)) - o*(F/V)
  dVdt = F
  return [dxdt, dsdt, dpdt, dodt, dVdt]
# Run the simulation
tspan = (0, 10) # hours
sol = solve_ivp(fedbatch, tspan, ini, method='RK45', args=(par,))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[16], line 3
      1 # Run the simulation
      2 tspan = (0, 10) # hours
----> 3 sol = solve_ivp(fedbatch, tspan, ini, method='RK45', args=(par,))

NameError: name 'solve_ivp' is not defined
# Plotting the results using matplotlib
plt.figure()
plt.plot(sol.t, sol.y[0], 'k-', label='[X]')
plt.plot(sol.t, sol.y[1], 'r-', label='[S]')
plt.plot(sol.t, sol.y[2], 'g-', label='[P]')
plt.xlabel('Time [h]')
plt.ylabel('Concentration [g/L]')
plt.xlim(tspan)
plt.ylim (0,)
plt.legend()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 2
      1 # Plotting the results using matplotlib
----> 2 plt.figure()
      3 plt.plot(sol.t, sol.y[0], 'k-', label='[X]')
      4 plt.plot(sol.t, sol.y[1], 'r-', label='[S]')

NameError: name 'plt' is not defined
# Plotting the dissolved oxygen concentration
plt.figure()
plt.plot(sol.t, (sol.y[3]*100/Osat),'k-',label='O2 saturation')
plt.xlabel('time [hours]')
plt.ylabel('O2 saturation [sat%]')
plt.xlim(tspan)
plt.ylim(-10,110)
plt.legend()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[18], line 2
      1 # Plotting the dissolved oxygen concentration
----> 2 plt.figure()
      3 plt.plot(sol.t, (sol.y[3]*100/Osat),'k-',label='O2 saturation')
      4 plt.xlabel('time [hours]')

NameError: name 'plt' is not defined
# Plotting the dissolved oxygen concentration
plt.figure()
plt.plot(sol.t, (sol.y[4]),'k-',label='liquid volume')
plt.xlabel('time [hours]')
plt.ylabel('Liquid volume [L]')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[19], line 2
      1 # Plotting the dissolved oxygen concentration
----> 2 plt.figure()
      3 plt.plot(sol.t, (sol.y[4]),'k-',label='liquid volume')
      4 plt.xlabel('time [hours]')

NameError: name 'plt' is not defined

Continuous reactor model#

## Now your turn to implement the continuous reactor with the given parameters