Works out head losses through screens and filters, settling removal, disinfection contact time, sludge age, and the size of an activated-sludge tank and clarifier.
How the precision is kept
Each result is one closed-form 64-bit expression of the inputs a person gives. Inputs are checked for sign before use, and nothing is rounded before it is returned.
Calculations under this policy
10 calculations in 2 source files. 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.
One 64-bit expression with g = 9.80665 and the angle converted with Math.PI ÷ 180.
process/env/water.js
lines 148 to 149
CadCraft 0.8.2
/** Bar-screen head loss (m) by Kirschmer: beta bar shape factor (the person's), w bar width, b clear spacing, v velocity through the bars (m/s), theta degrees from horizontal. */
export function screenLoss({ beta, w, b, v, theta = 60 }) { if (!(beta > 0 && w > 0 && b > 0 && v > 0)) throw new Error('Screen loss needs beta, bar width, clear spacing and velocity.'); return beta * Math.pow(w / b, 4 / 3) * v * v / (2 * 9.80665) * Math.sin(theta * Math.PI / 180); }
Ideal settling basin
64-bit
The overflow rate and the fraction of particles removed.
v₀ = Q ÷ A; removal = min(1, vs ÷ v₀)
Two divisions; the removal is capped at 1.
process/env/water.js
lines 150 to 151
CadCraft 0.8.2
/** Ideal settling basin: removal fraction of particles settling at vs (m/d) in a basin of area A (m2) at flow Q (m3/d). */
export function idealClarifier({ Q, A, vs }) { if (!(Q > 0 && A > 0 && vs >= 0)) throw new Error('Clarifier needs Q, area and a settling velocity.'); const vo = Q / A; return { overflowRate: vo, removal: Math.min(1, vs / vo) }; }
Granular filter head loss
64-bit
The head lost through a clean filter bed in laminar flow.
h = 180 × L × ν × (1 − ε)² × v ÷ (g × ε³ × d²)
One 64-bit expression; products and one division, so nothing cancels. Porosity must be strictly between 0 and 1.
process/env/water.js
lines 152 to 153
CadCraft 0.8.2
/** Laminar granular-filter head loss (m of water) through depth L (m) at approach velocity v (m/s), grain diameter d (m), porosity eps, kinematic viscosity nu (m2/s). */
export function filterLoss({ L, v, d, eps, nu }) { if (!(L > 0 && v >= 0 && d > 0 && eps > 0 && eps < 1 && nu > 0)) throw new Error('Filter loss needs depth, velocity, grain size, porosity and viscosity.'); return L * 180 * nu * (1 - eps) ** 2 * v / (9.80665 * eps ** 3 * d * d); }
Disinfection survival
64-bit
The fraction of organisms surviving a contact time, and the log removal.
s = exp(−k × Cⁿ × t); log removal = −log₁₀(s)
The log removal is taken from the computed survival, not from the exponent. When the exponent is large enough for exp to underflow to 0 the log removal becomes Infinity.
process/env/water.js
lines 154 to 155
CadCraft 0.8.2
/** Chick-Watson: fraction surviving after contact time t with concentration C. → { surviving, log10 } (log10 removal). */
export function chickWatson({ k, n = 1, C, t }) { if (!(k >= 0 && C >= 0 && t >= 0)) throw new Error('Disinfection needs k, a concentration and a contact time.'); const s = Math.exp(-k * Math.pow(C, n) * t); return { surviving: s, log10: -Math.log10(s) }; }
Contact time for a log removal
64-bit
The contact time needed to reach a wanted log removal.
t = log₁₀ removal × ln 10 ÷ (k × Cⁿ)
One expression using Math.LN10.
process/env/water.js
lines 156 to 157
CadCraft 0.8.2
/** Contact time needed for a log10 removal at concentration C. */
export function ctFor({ k, n = 1, C, log10 }) { if (!(k > 0 && C > 0 && log10 > 0)) throw new Error('Needs k, C and a log removal.'); return log10 * Math.LN10 / (k * Math.pow(C, n)); }
Sludge retention time
64-bit
The mass of sludge in the system divided by the mass leaving per day.
SRT = V × X ÷ (Qw × Xw + Qe × Xe)
One expression; returns Infinity when nothing leaves.
process/env/water.js
lines 158 to 159
CadCraft 0.8.2
/** Sludge retention time (d): mass in the system over mass leaving. */
export function srtOf({ V, X, Qw, Xw, Qe = 0, Xe = 0 }) { const out = Qw * Xw + Qe * Xe; return out > 0 ? V * X / out : Infinity; }
Tank footprints for the site plan
64-bit
The plan area and the side of a square footprint for each tank.
area = V ÷ depth; side = √area
One division and one square root per tank, unrounded. The depth defaults to 4 m when none is given.
process/env/water.js
lines 166 to 169
CadCraft 0.8.2
/** Plan footprints for the site (CivilCraft): each tank as an area from volume and water depth. depths { tankId: m } or one number. */
export function siteLinks(plant, depths = 4) {
return (plant.tanks || []).map((t) => { const d = typeof depths === 'number' ? depths : (depths[t.id] ?? 4), area = t.V / d; return { id: t.id, kind: 'basin', volume: t.V, depth: d, area, side: Math.sqrt(area), aerated: !!t.aerated, note: 'Square footprint as a placeholder; the shape is the designer\'s.' }; });
}
64-bit closed forms. Unit changes are a division and a multiplication by 1000; the oxygen demand subtracts 1.42 × Px from the substrate removed. Every input except S and kd must be above 0.
process/env/design.js
lines 2 to 17
CadCraft 0.8.2
/**
* design.js — activated-sludge and clarifier sizing by the classic design equations (0.8 Track CH, CH.20; DOM-free). The kinetic constants (Y, kd) and the
* design targets are the PERSON'S inputs (no default: they depend on the wastewater); the equations are those of the textbooks (Metcalf and Eddy, Wastewater
* Engineering; Grady, Daigger and Lim). A drafting and study aid, not an engineering approval.
* completeMix({ Q (m3/d), S0, S (mg/L soluble BOD or COD in and out), Y (g VSS/g), kd (1/d), thetaC (d), X (mg VSS/L in the tank) })
* Px = Y Q (S0 - S)/(1 + kd thetaC) (kg VSS/d); V = thetaC Px / X (m3); tau = V/Q; F/M = Q S0 / (V X); oxygen = Q (S0 - S) - 1.42 Px (kg/d).
* returnRatio({ X, Xr, Xe = 0 }) solids balance on the reactor and clarifier: Qr/Q = (X - Xe...) / (Xr - X) (Xe the effluent solids, small)
* clarifier({ Q, Qr, X, A }) surface overflow rate Q/A (m/d) and solids loading (Q+Qr) X / A (kg/m2/h)
*/
export function completeMix({ Q, S0, S, Y, kd, thetaC, X }) {
for (const [k, v] of Object.entries({ Q, S0, Y, thetaC, X })) if (!(v > 0)) throw new Error('activated sludge: give ' + k + ' (it has no default)');
if (!(S >= 0 && S < S0)) throw new Error('the effluent S must be 0 or more and below the influent S0');
if (!(kd >= 0)) throw new Error('give kd (1/d)');
const dS = Q * (S0 - S) / 1000, Px = Y * dS / (1 + kd * thetaC), V = thetaC * Px * 1000 / X; // kg/d, m3 (X in g/m3)
return { Px, V, tau: V / Q, tauHours: 24 * V / Q, FM: Q * S0 / (V * X), oxygen: dS - 1.42 * Px, thetaC, source: 'Metcalf and Eddy, complete-mix activated sludge design equations' };
}
Return sludge ratio
64-bit
The return flow as a fraction of the plant flow.
Qr ÷ Q = (X − Xe) ÷ (Xr − X)
The denominator is a difference; the function refuses Xr at or below X, but a return concentration only slightly above X makes the ratio sensitive to both inputs.
process/env/design.js
lines 18 to 21
CadCraft 0.8.2
export function returnRatio({ X, Xr, Xe = 0 }) {
if (!(Xr > X && X > 0)) throw new Error('the return sludge must be thicker than the tank: Xr > X > 0');
return { ratio: (X - Xe) / (Xr - X), note: 'neglects the solids lost in the effluent beyond Xe' };
}
Clarifier loadings
64-bit
Surface overflow rate and solids loading of a clarifier.
overflow = Q ÷ A; solids = (Q + Qr) × X ÷ 1000 ÷ A ÷ 24
Divisions applied in sequence in 64-bit; no rounding.
process/env/design.js
lines 22 to 25
CadCraft 0.8.2
export function clarifier({ Q, Qr, X, A }) {
if (!(A > 0)) throw new Error('give the clarifier area A (m2)');
return { overflowRate: Q / A, solidsLoading: (Q + Qr) * X / 1000 / A / 24, units: { overflowRate: 'm/d', solidsLoading: 'kg/m2/h' } };
}
In detail
Method
Kirschmer bar-screen loss, ideal settling basin, laminar granular-filter loss with coefficient 180, Chick-Watson disinfection, and the complete-mix activated-sludge design equations the file attributes to Metcalf and Eddy.
Number types
64-bit numbers. g = 9.80665 m/s².
Limits
The oxygen equivalent of biomass is the constant 1.42. Kinetic constants are inputs with no default.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.