Works out liquid activity coefficients by NRTL, Wilson and UNIQUAC and Henry's constants for dissolved gases.
How the precision is kept
Each model is its published closed form, evaluated in 64-bit from the interaction parameters the person gives; no parameters of a pair are shipped. Sums run over the components in order. The only data in the file is a table of Henry's constants for gases in water, which its own comment calls approximate to about 5 %.
Calculations under this policy
6 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
The interaction parameter of each pair at a temperature.
pᵢⱼ = aᵢⱼ + bᵢⱼ ÷ T; 0 on the diagonal
One division and one addition per pair. A missing entry counts as 0.
process/activity.js
lines 2 to 14
CadCraft 0.8.2
/**
* activity.js — liquid activity-coefficient models NRTL, UNIQUAC and Wilson, and Henry's law (0.8 Track CH, CH.1; DOM-free).
* Equations as published: NRTL Renon & Prausnitz, AIChE J. 14 (1968) 135; UNIQUAC Abrams & Prausnitz, AIChE J. 21 (1975) 116;
* Wilson, J. Am. Chem. Soc. 86 (1964) 127 (all three in SVN7 ch. 12 and Reid–Prausnitz–Poling ch. 8).
*
* Parameters are the person's (or a pack's) with their own source; none are shipped as "the" parameters of a pair. Each parameter matrix
* is given as { a: [[..]], b: [[..]] } with the temperature form p_ij = a_ij + b_ij / T (T in K). Diagonals are ignored.
* NRTL tau_ij = a_ij + b_ij/T, G_ij = exp(-alpha_ij tau_ij); alpha is a matrix or one number (default 0.3).
* Wilson ln Lambda_ij = a_ij + b_ij/T (Lambda_ii = 1).
* UNIQUAC ln tau_ij = a_ij + b_ij/T (tau_ii = 1); pure-component r (volume) and q (area) are given; coordination number z = 10.
* Every model returns ln gamma for a mole-fraction vector x.
*/
const mat = (p, n, T) => Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 0 : (p?.a?.[i]?.[j] || 0) + (p?.b?.[i]?.[j] || 0) / T)));
Two passes of sums over the components, in index order. The non-randomness α is 0.3 unless given.
process/activity.js
lines 16 to 28
CadCraft 0.8.2
export function lnGammaNRTL(x, T, par) {
const n = x.length, tau = mat(par, n, T), al = (i, j) => (Array.isArray(par.alpha) ? par.alpha[i][j] : (par.alpha ?? 0.3));
const G = tau.map((row, i) => row.map((t, j) => (i === j ? 1 : Math.exp(-al(i, j) * t))));
const S = new Array(n).fill(0), C = new Array(n).fill(0); // S_j = sum_k x_k G_kj, C_j = sum_m x_m tau_mj G_mj
for (let j = 0; j < n; j++) for (let k = 0; k < n; k++) { S[j] += x[k] * G[k][j]; C[j] += x[k] * tau[k][j] * G[k][j]; }
const out = [];
for (let i = 0; i < n; i++) {
let s = C[i] / S[i];
for (let j = 0; j < n; j++) s += x[j] * G[i][j] / S[j] * (tau[i][j] - C[j] / S[j]);
out.push(s);
}
return out;
}
Wilson
64-bit
ln γ of every component by the Wilson model.
ln γᵢ = 1 − ln(Σⱼ xⱼ Λᵢⱼ) − Σₖ xₖ Λₖᵢ ÷ Σⱼ xⱼ Λₖⱼ
One 64-bit expression per component. Near a pure component the three terms nearly cancel to give a ln γ close to zero.
process/activity.js
lines 29 to 35
CadCraft 0.8.2
export function lnGammaWilson(x, T, par) {
const n = x.length, ln = mat(par, n, T), L = ln.map((row, i) => row.map((v, j) => (i === j ? 1 : Math.exp(v))));
const S = new Array(n).fill(0); for (let i = 0; i < n; i++) for (let j = 0; j < n; j++) S[i] += x[j] * L[i][j];
const out = [];
for (let i = 0; i < n; i++) { let t = 0; for (let k = 0; k < n; k++) t += x[k] * L[k][i] / S[k]; out.push(1 - Math.log(S[i]) - t); }
return out;
}
One expression. The table of values at 298.15 K above it holds two or three significant digits and is described in its comment as approximate to about 5 %.