Resources Numerical accuracy Calculation policy

Concrete section by strain compatibility

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.

frame/rc-section.js CadCraft 0.8.2 64-bit 5 calculations Reviewed

Tests tools/frame/test-rc.mjs

Bar positions of a beam

64-bit

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.

  • frame/rc-section.js
  • lines 52 to 62
  • CadCraft 0.8.2
export function layersFor(sec, axis = 'y', sign = 1) {
  const depth = axis === 'y' ? sec.h : sec.b, width = axis === 'y' ? sec.b : sec.h;
  const map = new Map();
  for (const bar of sec.bars) {
    const s = axis === 'y' ? bar.z : bar.y;
    const d = Math.round((depth / 2 - sign * s) * 1e6) / 1e6;
    map.set(d, (map.get(d) || 0) + bar.A);
  }
  const layers = [...map].map(([d, As]) => ({ d, As })).sort((a, b) => a.d - b.d);
  return { depth, width, layers };
}

Axial force and moment for a neutral axis depth

64-bit

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.

Every policy that takes this approach