#Copyright © 2024-Present, UChicago Argonne, LLC
import numpy as np
[docs]
class FluidizedBedND:
"""Model for batch fluidized bed reactor under plug flow approximations.
Implementation of a non-dimensional model for particle coating
by atomic layer deposition. It assumes a well mixed approximation for
particle mixing and plug flow approximation for precursor transport.
The model assumes a first-order irreversible Langmuir kinetics
with the sticking probability value contained in the Damkohler
number.
The normalized time in the model refers to the normalized dose time
during the precursor exposure step.
Parameters
----------
Da : float
Damkohler number, a dimensionless parameter representing the ratio
of reaction rate to transport rate. Higher values indicate faster
surface reactions relative to mass transport. Must be non-negative.
Attributes
----------
Da : float
The Damkohler number for the system.
Examples
--------
Create a FluidizedBedND model with a Damkohler number of 2.0:
>>> model = FluidizedBedND(Da=2.0)
>>> coverage = model.calc_coverage(t=1.0)
>>> print(f"Coverage: {coverage:.3f}")
Coverage: 0.797
Calculate saturation curve over normalized dose time:
>>> t, coverage = model.saturation_curve(tmax=3.0, dt=0.1)
>>> max_coverage = coverage[-1]
>>> print(f"Maximum coverage: {max_coverage:.3f}")
Maximum coverage: 0.950
Notes
-----
The Damkohler number (Da) is defined as:
Da = k * τ
where k is the first-order reaction rate constant and τ is the
characteristic time scale for precursor dosing.
"""
model_kwd = ["dose", "nondim"]
def __init__(self, Da=None):
self.Da = Da
[docs]
def calc_coverage(self, t=1, Da=None):
"""Calculate the surface coverage at a given normalized dose time
Computes the fractional surface coverage θ of particles by precursor
molecules in the batch fluidized bed reactor at a specified normalized
dose time. The calculation uses analytical solutions for the well-mixed
particle model with plug flow precursor transport and first-order
Langmuir kinetics.
Parameters
----------
t : float, optional
Normalized dose time (dimensionless), defined as the ratio
of actual dose time to the characteristic dosing time.
Default is 1.0 (dose duration equals the characteristic time).
Must be non-negative.
Da : float, optional
Damkohler number. If provided, updates the model's Da attribute
and uses this value for the calculation. If None (default),
uses the current model's Da value.
Returns
-------
float
Surface coverage θ (dimensionless), bounded between 0 and 1.
A value of 0 indicates no coverage, while 1 indicates complete
monolayer saturation.
Examples
--------
Calculate coverage at the end of dosing (t=1):
>>> model = FluidizedBedND(Da=2.0)
>>> coverage = model.calc_coverage(t=1.0)
>>> print(f"Coverage: {coverage:.3f}")
Coverage: 0.797
Override the Damkohler number for a specific calculation:
>>> coverage = model.calc_coverage(t=1.0, Da=5.0)
>>> print(f"Coverage with Da=5: {coverage:.3f}")
Coverage with Da=5: 0.959
"""
if Da is not None:
self.Da = Da
else:
Da = self.Da
b = Da*(1-t)
b_safe = np.minimum(b, 50)
av = np.where(b>50, 1-t, 1/Da*np.log(1+np.exp(b_safe)-np.exp(-Da*t)))
cov = 1 - av
return cov
[docs]
def saturation_curve(self, tmax=5, dt= 0.01):
"""Calculate the saturation curve of the ALD process
Computes the relationship between normalized dose time and
surface coverage, producing the characteristic saturation curve
for the fluidized bed ALD process. This curve shows how coverage
approaches saturation as dose time increases in a batch reactor
with well-mixed particles and plug flow precursor transport.
Parameters
----------
tmax : float, optional
Maximum normalized dose time for the curve. Default is 5.0.
Determines the extent of the curve. Larger values show
near-saturation behavior but may be unnecessary if saturation
is reached earlier.
dt : float, optional
Time step size (dimensionless). Default is 0.01.
Smaller values provide smoother curves but increase computation time.
Must be positive and smaller than tmax.
Returns
-------
t : ndarray
Array of normalized dose times, shape (n,), where n = tmax/dt.
Values range from 0 to (tmax - dt).
coverage : ndarray
Array of surface coverage values θ at each time point, shape (n,).
Each value is bounded between 0 and 1. The coverage increases
monotonically, approaching saturation at large dose times.
Examples
--------
Generate a saturation curve with default parameters:
>>> model = FluidizedBedND(Da=2.0)
>>> t, coverage = model.saturation_curve()
>>> print(f"Coverage at t=1: {coverage[100]:.3f}") # dt=0.01, so index 100 is t=1
Coverage at t=1: 0.797
Create a high-resolution saturation curve:
>>> t, coverage = model.saturation_curve(tmax=3.0, dt=0.001)
>>> import matplotlib.pyplot as plt
>>> plt.plot(t, coverage)
>>> plt.xlabel('Normalized dose time')
>>> plt.ylabel('Surface coverage θ')
>>> plt.title(f'Fluidized Bed ALD Saturation Curve (Da={model.Da})')
>>> plt.grid(True)
>>> plt.show()
See Also
--------
run : Complete simulation including precursor utilization
calc_coverage : Calculate coverage at a single time point
"""
t = np.arange(0, tmax, dt)
c = self.calc_coverage(t)
return t, c
[docs]
def run(self, tmax=5, dt=0.01):
"""Run complete simulation including coverage and precursor utilization
Executes the fluidized bed model simulation over a range of normalized
dose times, computing both surface coverage and precursor utilization
at each time step. This method provides comprehensive results for
analyzing both coating efficiency and precursor usage in a batch
reactor with well-mixed particles and plug flow precursor transport.
Parameters
----------
tmax : float, optional
Maximum normalized dose time for the simulation. Default is 5.0.
Should be greater than 0. Larger values allow observation of
near-saturation behavior.
dt : float, optional
Time step size for the simulation (dimensionless). Default is 0.01.
Smaller values provide higher resolution but increase computation time.
Must be positive and smaller than tmax.
Returns
-------
t : ndarray
Array of normalized dose times, shape (n,), where n = tmax/dt.
Values range from 0 to (tmax - dt).
coverage : ndarray
Array of surface coverage values θ at each time point, shape (n,).
Each value is bounded between 0 and 1, representing fractional
monolayer coverage.
precursor : ndarray
Array of precursor utilization factors at each time point, shape (n,).
Each value is bounded between 0 and 1, representing the fraction
of precursor that has reacted with particle surfaces. Values closer
to 0 indicate less efficient precursor usage.
Examples
--------
Run a basic simulation with default parameters:
>>> model = FluidizedBedND(Da=2.0)
>>> t, coverage, precursor = model.run()
>>> print(f"Final coverage: {coverage[-1]:.3f}")
Final coverage: 0.993
Notes
-----
This method combines the functionality of calc_coverage() and precursor
utilization calculations to provide a complete picture of the fluidized
bed ALD process.
See Also
--------
calc_coverage : Calculate coverage only
saturation_curve : Calculate coverage without precursor data
"""
t = np.arange(0, tmax, dt)
Da = self.Da
b = Da*(1-t)
b_safe = np.minimum(b, 50)
y = np.where(b>50, 1-t, 1/Da*np.log(1+np.exp(b_safe)-np.exp(-Da*t)))
c = 1-y
x = np.exp(-Da*y)
return t, c, x