Skip to content

AndreKnoesen/distillation-simulator

Repository files navigation

Distillation Column Simulator

A rigorous multi-component distillation column simulator, originally written in Fortran in the mid-1990s by Roger Boulton, Emeritus Distinguished Professor of Enology and of Chemical Engineering, University of California, Davis.

The code has been converted to Python and wrapped in a modern FastAPI + React web application.


Background

The original Fortran program (Fortran Source/MULTST.FOR) solved the full MESH equations (Material balances, Equilibrium relationships, Summation, and Heat/enthalpy balances) simultaneously across all stages using Newton–Raphson iteration on a block-tridiagonal Jacobian. It was demonstrated on a 10-component brandy distillate system — ethanol, water, and eight congeners — but applies to any distillate if the component set and concentrations are changed.

"This from the mid 1990s … I never published it but is far beyond anything I have seen that is relevant to brandy, but all distillates if the components and concentrations are varied." — Roger Boulton


Project Structure

.
├── distillation.py              # Core solver (Python/NumPy port of MULTST.FOR)
├── backend/
│   ├── main.py                  # FastAPI REST API
│   ├── requirements.txt         # Python dependencies
│   └── test_api.py              # Quick smoke test
├── frontend/
│   ├── src/
│   │   ├── App.jsx              # Main layout and state
│   │   ├── App.css              # Styles
│   │   ├── api.js               # HTTP client
│   │   └── components/
│   │       ├── ConfigPanel.jsx  # Input form
│   │       ├── ResultsPanel.jsx # Charts and table
│   │       └── ProfileChart.jsx # Recharts wrapper
│   ├── package.json
│   └── vite.config.js           # Dev proxy → backend
├── Fortran Source/
│   ├── MULTST.FOR               # Original Fortran source
│   ├── TEN.DAT                  # 10-component input file
│   └── 10CPTPR.OUT              # Reference output
├── distillation_output.txt      # Python solver output (matches Fortran exactly)
└── start.ps1                    # One-command launcher (Windows PowerShell)

Quick Start

Prerequisites

  • Python 3.11+ (numpy, fastapi, uvicorn — see backend/requirements.txt)
  • Node.js 18+

Install

# Python backend
pip install -r backend/requirements.txt

# Node frontend
cd frontend
npm install
cd ..

Run

Option A — single script (Windows):

.\start.ps1

Opens two terminal windows (backend on port 8000, frontend on port 5173).

Option B — manually in two terminals:

# Terminal 1 — backend
cd backend
python -m uvicorn main:app --reload --port 8000

# Terminal 2 — frontend
cd frontend
npm run dev

Then open http://localhost:5173 in your browser.

The interactive API documentation is at http://localhost:8000/docs.


Using the App

The UI pre-loads the 10-component brandy system from TEN.DAT. Hit Run Simulation to solve immediately. All inputs are editable:

Section What you can change
Column Specification Stages, distillate rate, reflux ratio, pressure, temperature bounds
Murphree Efficiency Uniform stage efficiency (0–1)
Feed Stage, temperature, vapour fraction, all 10 component flow rates
Liquid Sidestreams Stage and flow rate
Components Antoine constants, enthalpy parameters, K-factor polynomial coefficients (advanced)

Results are shown as:

  • Summary cards — convergence status, top proof, reboiler/condenser duties
  • Convergence history — residual at each Newton–Raphson iteration
  • Charts — temperature, proof, ethanol mole fraction, congener profiles, K-factors
  • Data table — full per-stage numerical output

Solver Details

Algorithm

  1. Initial guess — constant-molar-flow approximation per column section
  2. Newton–Raphson loop — iterates until sum-of-squares of corrections < 0.1
  3. Block-tridiagonal Jacobian — exploits the stage-to-stage coupling structure; each stage couples only to its neighbours, reducing the linear solve from O(N³) to O(N)
  4. Gaussian elimination with partial pivoting (_gausl) — in double precision for numerical stability

Thermodynamic models

Component pair Model
Ethanol – water Van Laar activity coefficients (A₁₂ = 0.7715, A₂₁ = 0.3848)
Congeners (3–10) Polynomial K-factor as a function of ethanol mole fraction (7 coefficients per component)
All vapour pressures Antoine equation — input in log₁₀/mmHg, converted internally to ln/atm
Enthalpies Linear: H = H₀ + Cₚ·T
Stage efficiency Murphree (per-stage or uniform)

10-component brandy system (TEN.DAT)

# Component Role
1 Ethanol Primary alcohol
2 Water Co-solvent
3 Acetic acid Volatile acid
4 Acetaldehyde Aldehyde (heads fraction)
5 Isobutanol Fusel alcohol
6 Isoamyl alcohol Fusel alcohol
7 Propanol Fusel alcohol
8 Isoamyl acetate Ester
9 Ethyl acetate Ester
10 Furfural Aldehyde

Verified output

The Python solver replicates the original Fortran output to 4 significant figures for all 26 stages and 10 components. The default case converges in 7 Newton–Raphson iterations.

Reference values (26-stage column, reflux = 70, feed at stage 6):

Stage T (°C) FL total Ethanol FL
1 (reboiler) 99.98 46.65 0.002
6 (feed) 96.11 70.92 0.965
26 (condenser) 78.63 24.50 17.993

API Reference

GET /api/default

Returns the default 10-component configuration as JSON (suitable for direct use as a POST /api/simulate body).

POST /api/simulate

Request body schema:

{
  "components": [
    {
      "name": "Ethanol",
      "antoine": [8.04494, 1554.30, 222.65],
      "enthalpy": [0.0, 28.0, 9200.0, 28.0],
      "kconst": [1111.11, 1111.11, 1111.11, 1111.11, 1111.11, 1111.11, 1111.11]
    }
  ],
  "column": { "nst": 26, "dest": 0.35, "rflx": 70.0, "p": 1.0, "tt": 78.3, "tb": 100.0 },
  "murphree": { "uniform": true, "value": 0.5 },
  "feeds": [
    { "stage": 6, "tf": 90.0, "fkv": 0.0, "flows": [2.011, 47.983, ...] }
  ],
  "liquid_sidestreams": [{ "stage": 22, "flow": 3.0 }],
  "vapor_sidestreams": []
}

Response includes converged, n_iterations, residuals, stages (full per-stage profiles), heat_duties, and component_names.

Full schema available at /docs.


Python API

The solver can also be used directly without the web layer:

from distillation import simulate

result = simulate(config_dict)   # same schema as the POST body

for stage in result["stages"]:
    print(stage["stage"], stage["temperature"], stage["proof_liquid"])

For file-based use (reads Fortran fixed-format input, writes formatted output):

from distillation import run_from_file

run_from_file("Fortran Source/TEN.DAT", "output.txt")

Extending the Model

  • More components — add entries to the components list; increase nk accordingly. The solver is dimensioned for up to 10 components by the current array sizes in distillation.py (trivially extended).
  • Different solvents — replace the Van Laar constants in _mquac() or generalise to a full UNIQUAC/NRTL model.
  • Non-uniform efficiencies — pass murphree.per_stage as an array.
  • Multiple feeds — add entries to feeds; each specifies its own stage, temperature, vapour fraction, and composition.

Credits

Original Fortran code: Roger Boulton, Emeritus Distinguished Professor of Enology and of Chemical Engineering, Department of Viticulture and Enology, University of California, Davis.

Python conversion and web application: developed in collaboration with Claude (Anthropic).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors