The axial force and moment a rectangular reinforced concrete section carries for a given neutral axis depth, and the depth that balances a given axial force.
How the precision is kept
Forces are plain 64-bit sums over the concrete block and the bar layers. The neutral axis is found by bisection, so its accuracy is the stopping tolerance: the bracket is narrowed to 1e-12 of its upper end. Bar depths are rounded to 1e-6 mm so bars at one depth merge into a layer.
Calculations under this policy
5 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.
Places the top, bottom and side bars of a beam section from their count and cover.
y_i = −b ÷ 2 + c + (b − 2c) × i ÷ (n − 1); side bars evenly between the rows
Each position is computed from the face, not by adding a spacing repeatedly. A single bar in a row sits on the center line.
frame/rc-section.js
lines 27 to 41
CadCraft 0.8.2
export function rectBars({ b, h, top, bottom, side }) {
const bars = [];
const row = (r, z) => {
if (!r || !(r.n >= 1) || !(r.A > 0)) return;
const c = r.cover;
for (let i = 0; i < r.n; i++) bars.push({ y: r.n === 1 ? 0 : -b / 2 + c + (b - 2 * c) * i / (r.n - 1), z, A: r.A });
};
if (top) row(top, h / 2 - top.cover);
if (bottom) row(bottom, -h / 2 + bottom.cover);
if (side && side.n >= 1 && side.A > 0) {
const z0 = bottom ? -h / 2 + bottom.cover : -h / 2 + side.cover, z1 = top ? h / 2 - top.cover : h / 2 - side.cover;
for (let i = 1; i <= side.n; i++) { const z = z0 + (z1 - z0) * i / (side.n + 1); bars.push({ y: -b / 2 + side.cover, z, A: side.A }, { y: b / 2 - side.cover, z, A: side.A }); }
}
return bars;
}
Bar positions of a column cage
64-bit
Places the bars around the perimeter of a column, corners counted once.
y_i = −y_c + 2 y_c × i ÷ (n − 1); y_c = b ÷ 2 − cover
A corner bar reached from both faces is recognized by its coordinates rounded to 1e-6 mm; the bar keeps its unrounded position.
frame/rc-section.js
lines 43 to 50
CadCraft 0.8.2
export function perimeterBars({ b, h, cover, ny, nz, A }) {
const bars = [], seen = new Set();
const put = (y, z) => { const k = Math.round(y * 1e6) + ',' + Math.round(z * 1e6); if (!seen.has(k)) { seen.add(k); bars.push({ y, z, A }); } };
const yc = b / 2 - cover, zc = h / 2 - cover;
for (let i = 0; i < ny; i++) { const y = ny === 1 ? 0 : -yc + 2 * yc * i / (ny - 1); put(y, zc); put(y, -zc); }
for (let i = 0; i < nz; i++) { const z = nz === 1 ? 0 : -zc + 2 * zc * i / (nz - 1); put(yc, z); put(-yc, z); }
return bars;
}
Bars gathered into layers
64-bit
The depth of each bar from the compression face, with bars at one depth added into one layer.
d = depth ÷ 2 − sign × position, rounded to 1e-6 mm
The rounded depth is the one the strength calculation uses, so each layer's depth is changed by up to 5e-7 mm. Areas at one rounded depth are added in bar order.
The section's axial force and its moment about mid-depth when the neutral axis is at depth c.
a = min(β₁ c, h); C_c = α₁ f′c b a; ε_s = ε_cu (c − d) ÷ c; f_s = clamp(E_s ε_s, ±f_y); P = C_c + Σ F; M = Σ F × (h ÷ 2 − d)
A plain sum over the layers in order of depth. Compression and tension forces of similar size are added, so near zero axial force the sum is a small difference of large terms. The division by c is not guarded in this function; the neutral axis search and the strength diagram call it only with c above 0.
frame/rc-section.js
lines 64 to 79
CadCraft 0.8.2
export function forcesAt(L, c, m) {
const a = Math.min(m.beta1 * c, L.depth);
const Cc = m.alpha1 * m.fc * L.width * a;
let P = Cc, M = Cc * (L.depth / 2 - a / 2);
const steel = [];
let dt = 0, epsT = -Infinity;
for (const { d, As } of L.layers) {
const eps = m.epsCu * (c - d) / c; // compression +
const fs = Math.max(-m.fy, Math.min(m.fy, m.Es * eps));
const F = As * (fs - (d < a ? m.alpha1 * m.fc : 0));
P += F; M += F * (L.depth / 2 - d);
steel.push({ d, As, eps, fs, F });
if (d >= dt) { dt = d; epsT = -eps; }
}
return { P, M, a, c, Cc, steel, epsT, dt };
}
Neutral axis for a given axial force
64-bit
The neutral axis depth at which the section's axial force equals the target.
bisection on c between 1e-6 × depth and 1000 × depth
The first 60 steps halve the bracket on a logarithmic scale (the geometric mean), later ones on a linear scale. The search stops when the bracket is narrower than 1e-12 of its upper end, or after 200 steps, and returns the bracket's middle.
frame/rc-section.js
lines 81 to 91
CadCraft 0.8.2
export function neutralAxisForP(L, Ptarget, m) {
// P(c) rises with c; bracket between a tiny c (all steel in tension) and a deep one
let lo = 1e-6 * L.depth, hi = 1e3 * L.depth;
if (forcesAt(L, hi, m).P < Ptarget || forcesAt(L, lo, m).P > Ptarget) return null;
for (let k = 0; k < 200; k++) {
const mid = Math.sqrt(lo * hi) > 0 && k < 60 ? Math.sqrt(lo * hi) : (lo + hi) / 2;
if (forcesAt(L, mid, m).P > Ptarget) hi = mid; else lo = mid;
if (hi - lo <= 1e-12 * hi) break;
}
return (lo + hi) / 2;
}
In detail
Method
Plane sections; rectangular stress block; elastic–perfectly plastic steel; bars inside the block give back the concrete they displace; bisection on the neutral axis depth.
Number types
64-bit numbers.
Tolerances
Bisection stops when hi − lo ≤ 1e-12 × hi (relative), after 200 steps at most. Bar positions are matched to 1e-6 mm (absolute).
Limits
Rectangular sections; bars as points; concrete carries no tension.
Approach: 64-bit, stated limit
The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.