Resources Numerical accuracy Calculation policy

Power line tables, quantities and GIS export

Turns a line study into stringing tables, a support table, material quantities and GIS features in the project's units.

How the precision is kept

Results in meters and newtons are divided by exact unit factors from units.js to get project units, decanewtons or pounds-force. The rows keep full 64-bit values; the CSV, spreadsheet and TakeoffCraft outputs round them to 1 or 2 decimals. The GIS features carry unrounded values.

Open item. Give TakeoffCraft the unrounded quantities and leave rounding to the display.

Calculations under this policy

7 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.

powerline/outputs.js CivilCraft 0.8.1 64-bit 7 calculations Reviewed

Tests bin/test-powerline-out.mjs

Cubic meters per cubic yard

Exact

The factor used to give volumes in cubic yards in a feet project.

1 yd³ = 0.764554857984 m³

0.764554857984 is 0.9144³ written out in full, the exact definition.

  • powerline/outputs.js
  • line 51
  • CivilCraft 0.8.1
const CY_M3 = 0.764554857984;

Stringing table rows

64-bit

Tension, span length, sag and conductor length per section and temperature, in project units.

length ÷ m per unit; force ÷ 10 for daN or ÷ 4.4482216152605 for lbf

One division per value by factors defined in units.js; nothing is rounded in the rows.

  • powerline/outputs.js
  • lines 74 to 84
  • CivilCraft 0.8.1
// ---------------------------------------------------------------------------------------------------------- stringing
export function stringingRows(analysis, { units = 'm' } = {}) {
  const um = unitM(units), out = [];
  for (const [si, S] of (analysis?.sections || []).entries()) {
    for (const row of S.stringing || []) {
      out.push({ section: si + 1, from: S.from, to: S.to, Lr: fin(S.Lr) ? S.Lr / um : null, temp: row.temp, H: toForce(row.H, units), Tmax: toForce(row.Tmax, units),
        spans: row.spans.map((sp, k) => ({ span: S.spans[k]?.i ?? k, L: sp.L / um, sag: sp.sag / um, length: sp.length / um })) });
    }
  }
  return out;
}

Stringing table CSV

64-bit

The stringing table as CSV text.

lengths and sags round(v, 2); tension round(v, 1)

Values are rounded with Math.round(v × 10ᵈ) ÷ 10ᵈ before they are written: 2 decimals for lengths and sags and 1 for tensions, so the file does not carry the full values.

  • powerline/outputs.js
  • lines 86 to 102
  • CivilCraft 0.8.1
export function stringingCsv(analysis, line, { units = 'm' } = {}) {
  const lu = units === 'm' ? 'm' : 'ft', fu = forceUnit(units);
  const rows = stringingRows(analysis, { units });
  const out = [];
  for (const [si, S] of (analysis?.sections || []).entries()) {
    const mine = rows.filter((x) => x.section === si + 1);
    if (!mine.length) continue;
    if (out.length) out.push([]);
    const spans = mine[0].spans;
    out.push([t('pl.out.str.section', { n: si + 1, from: nm(line, S.from), to: nm(line, S.to) }), t('pl.out.str.ruling', { unit: lu }), r(mine[0].Lr)]);
    out.push([t('pl.out.str.span'), '', ...spans.map((sp) => nm(line, sp.span) + ' – ' + nm(line, sp.span + 1))]);
    out.push([t('pl.out.str.length', { unit: lu }), '', ...spans.map((sp) => r(sp.L))]);
    out.push([t('pl.out.str.temp'), t('pl.out.str.h', { unit: fu }), ...spans.map(() => t('pl.out.str.sag', { unit: lu }))]);
    for (const x of mine) out.push([x.temp, r(x.H, 1), ...x.spans.map((sp) => r(sp.sag))]);
  }
  return toCsv(out);
}

Support table rows

64-bit

Ground, attachment, load, nominal load, foundation and swing per support, in project units.

attach height = attach z − ground z; load = worst F_eq in daN or lbf

The attachment height is a difference of two elevations. Values are unrounded here; the CSV writer rounds them to 2 decimals, or 1 for loads and angles.

  • powerline/outputs.js
  • lines 104 to 121
  • CivilCraft 0.8.1
// ---------------------------------------------------------------------------------------------------------- supports
export function supportRows(analysis, line, lib, { units = 'm', cases = null } = {}) {
  const um = unitM(units);
  const caseName = (id) => (id == null ? '' : cases?.find((c) => c.id === id)?.name || id);
  return (analysis?.supports || []).map((s, i) => {
    const p = line?.supports?.[i] || {};
    const it = getItem(lib, s.item), sug = getItem(lib, s.suggest);
    return {
      i, id: s.id, name: nm(line, i), role: s.role, item: it?.name ?? (p.support || ''), itemKey: s.item ?? null,
      ground: s.groundZ ?? null, attach: s.attachZ ?? null, attachH: fin(s.attachZ) && fin(s.groundZ) ? s.attachZ - s.groundZ : null,
      angle: analysis.angles?.[i] ?? 0, load: toForce(s.worstFeq || 0, units), loadCase: s.worstCase ?? null, loadCaseName: caseName(s.worstCase),
      nominal: fin(it?.nominal_N) ? toForce(it.nominal_N, units) : null, ok: s.sizeOk ?? null,
      suggest: sug?.name ?? '', suggestKey: s.suggest ?? null,
      fdDepth: s.foundation ? s.foundation.depth_m / um : null, fdDia: s.foundation ? s.foundation.dia_m / um : null,
      swing: s.swing_deg ?? null, uplift: !!s.uplift, existing: !!p.existing,
    };
  });
}

Conductor length to buy

64-bit

The conductor length of a line with an allowance for jumpers and waste.

length = Σ catenary lengths × phases × circuits × 1.02 ÷ m per unit

Catenary lengths in the everyday case are added in span order. A span without a solved catenary adds √(L² + dz²), its chord. The 2 percent allowance is a constant in the code.

  • powerline/outputs.js
  • lines 153 to 159
  • CivilCraft 0.8.1
let m = 0, est = 0;
for (const sp of a.spans) {
  const cat = catOf(a, sp.i);
  if (cat) m += cat.length; else { m += Math.hypot(sp.L_m || 0, sp.dz_m || 0); est++; }
}
const phases = line.phases || 1, circuits = line.circuits || 1, n = phases * circuits;
const x = add('conductor:' + line.id, 'conductor', `${c.name} (${line.name})`, LU, len(m * n * (1 + JUMPER_PCT / 100)), line.conductor);

Lengths and volumes in project units

64-bit

Converts meters and cubic meters to feet and cubic yards for the quantity rows.

LF = m ÷ 0.3048; CY = m³ ÷ 0.764554857984

Both factors are exact definitions; each conversion is one division.

  • powerline/outputs.js
  • line 138
  • CivilCraft 0.8.1
const len = (m) => m / um, vol = (m3) => (feet ? m3 / CY_M3 : m3);

Quantities file for TakeoffCraft

64-bit

The quantity rows in the format TakeoffCraft reads.

qty = round(qty, 2)

Every quantity is rounded to 2 decimals before it is written to the file, so TakeoffCraft does not receive the full value.

  • powerline/outputs.js
  • lines 204 to 211
  • CivilCraft 0.8.1
/** TakeoffCraft's quantities format (fixedqty.js reads it), as planting.js scheduleQuantities writes it. */
export function takeoffQuantities(rows, { project = '', made = new Date().toISOString().replace(/\.\d+Z$/, 'Z') } = {}) {
  return {
    format: 'cadcraft-quantities', version: 1, app: 'CivilCraft', project: String(project).slice(0, 200), made,   // i18n-ignore: the app's name in the file
    items: rows.map((x) => ({ key: ('pl:' + x.key).slice(0, 80), name: String(x.name).slice(0, 200), unit: x.unit, qty: round(x.qty, 2), note: String(x.note || '').slice(0, 400) }))
      .filter((x) => QKEY.test(x.key) && x.qty >= 0),
  };
}

In detail

Method
Conductor quantity = Σ catenary lengths in the everyday case × conductors × 1.02 for jumpers and waste, as the file header says. Underground quantities from trenchQuantities in underground.js.
Number types
64-bit numbers. Cubic yards with 0.764554857984 m³ per cubic yard, which is 0.9144³ exactly.
Tolerances
CSV and spreadsheet values rounded to 2 decimals, forces and angles to 1.
Limits
A span whose section did not solve counts its chord, and the row's note says so. Existing supports are left out of the count.

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.

Every policy that takes this approach