“Wave Tracker” for Pressure Swing Adsorption
Introduction
Pressure Swing Adsorption (PSA) is a widely adopted process design for gas separation. A foundational analytical framework for PSA is the equilibrium theory, first introduced by works such as Knaebel and Hill (1985). Equilibrium theory simplifies PSA analysis by neglecting mass transfer resistances, dispersive effects, and other dissipative phenomena. Under this framework, several assumptions are typically made:
- Linear adsorption isotherms.
- Instantaneous equilibrium between gas and adsorbed phases.
- Isothermal operation.
- Ideal gas behavior.
With these assumptions, the governing equations simplify significantly, facilitating quick numerical or even analytical solutions. Furthermore, the complex dynamics within adsorption columns can usually be adequately represented by a few concentration (or partial pressure) fronts and characteristic lines between these fronts, enhancing interpretability and visualization. Consequently, equilibrium theory provides a computationally inexpensive yet insightful approach to elucidate key characteristics of PSA processes across various boundary conditions, without resorting to computationally intensive PDE-based simulations. Equilibrium theory is particularly beneficial for:
- Guiding cycle designs and determining step sequences.
- Providing fast approximations, initial guesses, or surrogate models for more rigorous PDE-based analyses.
- Enabling simplified optimization and parametric sensitivity studies.
- Estimating process-level performance metrics for large-scale adsorbent screening and optimization.
In this blog post, I first provide an overview of the equilibrium theory as applied to PSA. Next, I demonstrate a Python code for wave tracking based on equilibrium theory. Lastly, I present several illustrative examples, showcasing the practical application of the code and interpreting the resulting wave diagrams.
Equilibrium Theory for PSA
Basic Setting
Consider a packed bed of length
- Component
(the heavy or more strongly adsorbed species), - Component
(the light or less strongly adsorbed species).
The total pressure is denoted by
Key assumptions:
- Linear isotherms of the form:
where and are the adsorbed amounts of and .
- Instantaneous equilibrium between the gas and adsorbed phases (no mass-transfer resistance or kinetic limitations).
- Ideal gas behavior and isothermal operation (temperature
is constant).
- The adsorbent bed has an interstitial void fraction
, meaning a fraction of the bed volume is accessible to gas flow, and is occupied by the solid adsorbent.
- Under these assumptions, the evolution of gas composition and pressure along the bed can be described by a system of hyperbolic PDEs (or derived characteristic equations) which are simplified by the equilibrium and linear isotherm assumptions.
Dimensionless Groups
To simplify the governing equations, it is helpful to define:
Shocks and Waves
In a typical PSA cycle, the column may experience phases of constant pressure (e.g., during a high-pressure feed step) and phases of changing pressure (e.g., blowdown or pressurization steps). During these steps, characteristic curves in the
Constant-Pressure Operation
Suppose the gas enters the bed at one end at a fixed total pressure
- Shock waves form when a gas richer in the heavy component
pushes into a region with lower concentration. In this case, characteristics merge, and the shock speed (in dimensionless form) is given by where and are the velocity and composition just ahead of and behind the shock, respectively. - Simple waves (or expansion waves) appear when a more dilute gas (lower
) contacts a region of higher . In this case, the characteristics diverge. Mathematically, the velocity and concentration along the characteristic can be determined by integrating the relevant PDE or by directly applying the method of characteristics, which yields a continuous fan of solutions rather than a jump (shock).
Changing-Pressure Steps
In other steps (e.g., blowdown or pressurization), the total pressure
Under these linear and equilibrium assumptions, the bed dynamics during a PSA cycle can be understood via piecewise integration of hyperbolic PDEs, supplemented by shock or simple-wave conditions. Each step—whether at constant pressure or during pressurization/depressurization—admits characteristic equations that are integrable under suitable boundary conditions (e.g., a feed at one end, product at the other, or a closed end).
This equilibrium theory has proven highly useful in analyzing PSA processes because it provides:
- Exact or semi-analytical wave speeds and concentration profiles,
- Simple criteria for identifying shock fronts vs. expansion regions,
- A framework for understanding how separation performance depends on key dimensionless parameters
, , and .
More detailed calculations can incorporate boundary conditions for each PSA step, track how the shock or wave front moves through the bed, and predict when and where the heavy or light components break through. Although the theory is based on idealized assumptions, it offers valuable insights for process design, performance limits, and optimization of PSA cycles.
Code Overview
The Python implementation follows the equilibrium theory equations described by Knaebel & Hill for simulating wave propagation in a PSA (Pressure Swing Adsorption) column. The core data structures (Region
, WaveFront
, Characteristic
) capture the state of the bed and any traveling fronts or characteristics. The main computation logic resides in the WaveTracker
class, which calculates wave speeds at both constant and changing pressures. A higher-level controller, WaveDiagram
, orchestrates time-stepping (or pressure-stepping) through multiple PSA steps, updates the column profile, and manages plotting.
Region
A Region
stores a constant concentration of the heavy component (y_value
) for a segment of the column between z_left
and z_right
. Multiple Region
objects strung together represent the piecewise-constant concentration profile along the column length.
WaveFront
A WaveFront
tracks a discontinuity moving through the bed. It has a wave_type
that may be "shock"
(when a discontinuity steepens) or "simple"
(when it spreads out). The attributes z_position
, y_ahead
, and y_behind
indicate the current position of the wavefront and the compositions it separates.
Characteristic
A Characteristic
represents a path in the ((z,t)) or ((z,P)) plane corresponding to a particular composition. These paths are stored in arrays (z_values
, y_values
, t_values
, and p_values
) for post-processing or plotting. Characteristics are typically generated inside the column and can merge with (or be terminated by) wavefronts.
WaveTracker
The WaveTracker
class implements the theoretical equations needed to determine front speeds and characteristic velocities under different operating conditions. It contains methods for:
- Constant-Pressure Wave Speeds. Functions such as
get_characteristic_speed_constant_p()
andget_shock_speed_constant_p()
implement Equations (9)-(10) and (15) of Knaebel & Hill, giving the velocities of simple waves or shocks. - Changing-Pressure Wave Speeds. Additional methods like
get_u_changing_p()
andget_shock_speed_changing_p()
capture Equations (7) and (16) for situations where the column is undergoing pressurization or depressurization.
By calling these methods, the code can determine how a wavefront or characteristic should move over a small time (or pressure) interval.
WaveDiagram
This class coordinates the overall simulation. It breaks the total PSA process into discrete “steps” (e.g., a constant-pressure feed step, a blowdown step with decreasing pressure, etc.) and handles:
- Detecting discontinuities in the bed and spawning new wavefronts.
- Moving wavefronts and characteristics incrementally through time or pressure changes.
- Rebuilding the piecewise-constant column profile based on where wavefronts have moved.
- Storing intermediate snapshots and final states for visualization.
When WaveDiagram
completes a step, it has an updated column profile plus a record of all wavefronts and characteristics for plotting.
Examples and Usage
Below are simplified examples illustrating how to set up and run PSA steps. The bed length, inlet compositions, pressures, and velocities are all adjustable, making it easy to experiment with different cycle designs.
2-Bed 4-Step Cycle (product for pressurization)
The following code creates a bed of length
Click to expand the code
# Initialize the WaveTracker and WaveDiagram
= WaveTracker(betaA=0.3, betaB=0.7, beta=0.5)
wave_tracker = WaveDiagram(wave_tracker)
wave_diagram
# Define the bed and step parameters
= 1.0
L = [Region(y_value=0.0, z_left=0.0, z_right=L)]
initial_profile = 2.0
T_pressurization = 2.0
T_feed = 4.0
T_blowdown = 2.0
T_purge = 1.0
P_low = 5.0
P_high = 0.8
y_feed = 0.0 # pure light
y_product
# Pressurization step: increases pressure from P_low to P_high,
# with flow entering from the 'reverse' end (z=L).
wave_diagram.run_changing_pressure_step(="Pressurization",
step_name=y_product,
y_in=P_low,
p_start=P_high,
p_end=T_pressurization,
t_step=initial_profile,
z_profile_in="reverse",
flow_dir
)
# Feed step: keeps pressure constant, injects heavier component from z=0
wave_diagram.run_constant_pressure_step(="Feed",
step_name=1.8,
u_in=y_feed,
y_in=T_feed,
t_step="default",
flow_dir
)
# Blowdown step: decreases pressure from P_high to P_low
wave_diagram.run_changing_pressure_step(="Blowdown",
step_name=None,
y_in=P_high,
p_start=P_low,
p_end=T_blowdown,
t_step="default",
flow_dir
)
# Purge step: at low pressure, flow enters from z=L to desorb heavy component
wave_diagram.run_constant_pressure_step(="Purge",
step_name=2.0,
u_in=0.0,
y_in=T_purge,
t_step="reverse",
flow_dir
)
# Generate and display the wave diagram
"Pressurization","Feed","Blowdown","Purge"]) wave_diagram.plot_steps([
In this cycle:
- Pressurization lifts the column from low to high pressure. The code checks whether a wavefront forms at
depending on the composition mismatch.
- Feed introduces a heavier component at the inlet
under a constant, now high, pressure.
- Blowdown vents the column from the other end, decreasing pressure from
to .
- Purge at low pressure pushes a light component backward through the column to remove heavy species.
When the script finishes, the result is a stitched wave diagram showing how composition fronts move step by step.
2-Bed 4-Step Cycle (feed for pressurization)
A similar 4-step sequence can be constructed but this time the feed is used for pressurization. This allows you to see how slight changes in step sequences can lead to very different wavefronts.
4-Bed 9-Step Industrial Cycle
The third example demonstrates a typical industrial multi-step PSA, including multiple depressurizations, equalizations, and repressurizations. The code is longer, but follows the same pattern:
- A series of
run_constant_pressure_step
andrun_changing_pressure_step
calls for each sub-step (e.g., Adsorption, partial depressurization, purge). - Each step specifies relevant inlet compositions, target pressures, and durations.
- After finishing all steps,
plot_steps
creates a consolidated wave diagram.
In this scenario, you can explore the effects of partial equalizations (PEQ
) and depressurizations (DEQ
) to see how multi-step cycles shift bed compositions.
How to Run and Adapt
- Dependencies and Setup. You need Python 3, NumPy, Scipy, and Matplotlib. Save the code as
wave_tracking.py
(or any name) and run it directly (python wave_tracking.py
).
- Modifying the Bed. Adjust or extend
initial_profile
to reflect different initial loading conditions (multipleRegion
objects with variousy_value
s).
- Adjusting Equilibrium Parameters. The isotherm parameters
kA
,kB
, and bed porosityepsilon
determine the values ofbetaA
,betaB
, andbeta
. Changing these inputs reflects different adsorbent materials.
- Defining Custom Steps. In your own PSA script, call
run_constant_pressure_step
orrun_changing_pressure_step
with the times, pressures, flows, and compositions you want. The code automatically spawns shocks or simple waves where discontinuities appear.
- Plotting and Analysis. The
plot_steps
method creates a stitched diagram of composition fronts across multiple steps. It can be customized or replaced with your own post-processing routines if desired.
By experimenting with different inlet conditions, velocities, and pressure profiles, you can simulate and visualize how composition waves and shocks evolve in a PSA system under various operating strategies.
Citation
@online{yin2025,
author = {Yin, Xiangyu},
title = {“{Wave} {Tracker}” for {Pressure} {Swing} {Adsorption}},
date = {2025-03-02},
url = {https://xiangyu-yin.com/content/post_wave_tracking.html},
langid = {en}
}