Skip to content

AdsorbX

AdsorbX

Fast 1D adsorption–regeneration bed simulator

PACKED-BED ADSORPTION + REGEN SIM (1D) — PROJECT NOTES (TXT FRIENDLY)
1) Purpose

We built a 1D packed-bed simulator for gas-phase adsorption + regeneration with:

Correct flow direction (up/down)

Adsorption/desorption kinetics (LDF)

3 axial layers (different properties per layer)

“Fast run” solving (run as fast as possible) + replay later

Visualization: bed colormap + concentration profile plot

Ability to chain adsorption -> regeneration using the ACTUAL bed state at the end of the previous run

This is NOT CFD.
It’s an engineering dynamic model for:

breakthrough behavior

regeneration effectiveness

front movement intuition

basic TSA/PSA-style cycle thinking

2) Modeling assumptions

2.1 One-dimensional approximation:

Ignore radial gradients

Bed is “uniform” radially

Properties vary only along the axial coordinate z

Coordinate system is PHYSICAL and FIXED:

z = 0 : top of bed

z = L : bottom of bed

IMPORTANT RULE:

The bed drawing does NOT flip when flow reverses.

Only the physics changes (inlet location, velocity sign, etc).

3) Governing physics

3.1 Gas-phase balance (per axial cell):
We use a standard 1D convection–dispersion balance with sink from adsorption:

eps * dC/dt = - u * dC/dz + Dax * d2C/dz2 - rhob * dq/dt

Where:

C = gas concentration (mol/m^3)

q = adsorbed loading (mol/kg)

eps = void fraction (-)

u = superficial velocity (m/s) SIGNED by flow direction

Dax = axial dispersion (m^2/s)

rhob = bulk density (kg/m^3)

3.2 Solid-phase kinetics (LDF):
dq/dt = kLDF * (qStar - q)

This is common/industry standard for packed-bed dynamics.

3.3 Equilibrium (linear isotherm + temperature effect):
qStar = K(T) * C

K(T) = Kref * exp( -alpha * (T - Tref) )

Meaning:

At higher T (regen), capacity drops (K smaller)

At lower T (ads), capacity increases (K larger)

4) Numerical implementation

4.1 Spatial discretization:

Bed is discretized into N ~ 140 axial cells

Uniform spacing: dz = L/(N-1)

Each cell stores:

C[i] (gas conc)

q[i] (solid loading)

4.2 Time integration:

Explicit time stepping

Convection: upwind differencing (depends on sign of u)

Dispersion: central differencing

LDF: explicit update

Time step dt is automatically chosen using stability limits:

convection (CFL)

diffusion number

LDF stiffness

We always pick the smallest safe dt.

5) Flow direction (CRITICAL)

We define flow direction with EXACT strings:

"down" = top -> bottom

"up" = bottom -> top

Bed coordinate never changes.

Implementation rules:
If flowDir = "down":

uSigned > 0

inletIndex = 0

outletIndex = N-1

If flowDir = "up":

uSigned < 0

inletIndex = N-1

outletIndex = 0

Why this matters:

Convection term uses uSigned

Inlet boundary condition depends on inletIndex

During regeneration with upflow, the desorption front must move upward physically

Important design choice:

We refused “visual hacks” like flipping arrays or just swapping labels.

The simulation and visualization must represent the same physical bed.

6) Boundary conditions

Inlet (Dirichlet):

C[inletIndex] = Cin

Outlet (approx Neumann / zero-gradient):

C[outletIndex] = C[neighbor]

Cin comes from converting ppmv to mol/m^3 via ideal gas:

Ctot = P/(R*T)

Cin = Ctot * (ppmv * 1e-6)

7) 3-layer adsorbent bed

The bed is divided into 3 axial sections (L1, L2, L3).
Each layer has different:

Kref (equilibrium strength)

kLDF (kinetics)

Layer assignment is by z position (physical), not by “flow direction”.
That allows guard beds / polishing beds / mixed media.

8) Adsorption vs regeneration

Adsorption run (typical):

higher inlet ppmv

lower temperature

often downflow

bed loads up (q increases)

Regeneration run (typical):

purge ppmv ~ 0 (or low)

higher temperature

often upflow

bed unloads (q decreases)

9) Bed state continuity (very important)

Principle:

Regeneration must run on the CURRENT bed condition if it exists.

No “reset” unless user explicitly chooses it.

Implementation:
After any run finishes:
currentBed = {
C(z) array,
q(z) array,
timestamp,
note
}

Next runs can start from:

Initial Bed State (user-defined)
OR

Current Bed State (from last run)

This enables:

realistic cycles

partial regenerations

operator-like workflow

10) Fast-run + replay philosophy

Real-time solving + drawing is slow and CPU heavy.
So we separate:

Solve fast (no drawing, no DOM updates)

Save snapshots (time series of C profiles)

Replay later with:

play/pause

speed control

time slider

This is how many process simulators feel: solve first, inspect later.

11) Visualization rules

Physical bed view:

index i=0 is always top on screen

index i=N-1 always bottom on screen

NO flipping of the colormap

What changes when flow reverses:

inlet/outlet marker positions

flow arrow direction

which end is the boundary condition

which end is reported as “outlet”

So if regen is upflow:

you should see changes start at the bottom and move upward (physically)

12) What this model IS / IS NOT

IS:

1D dynamic packed-bed model (convection + dispersion + LDF)

Good for breakthrough/regeneration intuition

Layered bed support

Honest and consistent flow direction behavior

IS NOT:

CFD

vendor design tool

full thermal TSA with energy balance (not yet)

multi-component competitive adsorption (not yet)

pressure drop model (not yet)

13) Why the architecture matters

We intentionally kept it:

transparent

debuggable

extendable

Future add-ons (easy to plug in):

energy balance / thermal front

multicomponent adsorption

pressure drop (Ergun)

multi-cycle convergence to cyclic steady state

time-varying boundary conditions (ramps, steps)

14) Key lesson from debugging

The main bug class we fought was:
“solver is correct but visualization flips the bed / labels”

Final rule:

No visual tricks.

Physical coordinate system stays fixed.

Flow direction is handled in the math via signed velocity + inlet index.