Resources Numerical accuracy Calculation policy

Process diagram entities

Lays out the process flow diagram, node boundaries and live value labels that the P&ID add-on and the Plant tab add to a drawing.

How the precision is kept

The layout is simple 64-bit arithmetic on fixed box sizes and gaps. Line, circle and text coordinates are rounded to 3 decimals of a drawing unit before they are handed to the host; polygon corners are not. Stream labels show temperature and pressure rounded to 3 decimals. The diagram is schematic, so the rounding does not affect any engineering value.

Calculations under this policy

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

process/draw.js CadCraft 0.8.2 64-bit 4 calculations Reviewed

Tests tools/process-test/test-draw4.mjs

Coordinate rounding

64-bit

A coordinate or label value rounded for the drawing.

f(v) = +v.toFixed(3); 0 when v is not finite

Three decimals of a drawing unit. A value that is not finite becomes 0 without a flag.

  • process/draw.js
  • lines 2 to 14
  • CadCraft 0.8.2
/**
 * draw.js — what the P&ID add-on and the Plant tab draw from the process engines (0.8 chem-draw_PART_4; DOM-free, no imports). Everything returns plain entities
 * ({ type: 'line'|'circle'|'poly'|'text', layer, … } in the units asked for) and plain tables, so the hosts only have to add them.
 *
 *   pfdEntities(def, res?, opts)        CH.2   the block flow / process flow diagram of a flowsheet: units in columns by flow, streams as arrows with their names, recycles over the top
 *   nodeInside(node-rect, items)        CH.14  which tags and line numbers lie inside a hazop node's region
 *   regionEntities(node, opts)          CH.14  the node boundary and its label as entities on layer HAZOP-NODE
 *   markerEntities(markers, at, opts)   CH.27 / XE.9  value labels beside tagged symbols, coloured by state
 *   markerLevels(markers)               XE.9   0..1 level per tag for tinting 3D elements
 *   loopTable(loops, bindings, spec)    CH.5   the loop binding screen's rows and what is unbound
 *   faceplate(ctrl, trend)              CH.5   the numbers a controller faceplate shows from a simulation
 */
const f = (v) => (Number.isFinite(v) ? +Number(v).toFixed(3) : 0);

Stream label

64-bit

The text under a stream arrow: its name, temperature in degrees Celsius and pressure in bar.

T (°C) = T − 273.15; P (bar) = P ÷ 1e5; both to 3 decimals

The subtraction and division are done on the solved values and only the label text is rounded.

  • process/draw.js
  • lines 40 to 41
  • CadCraft 0.8.2
const streamLabel = (id) => { const s = res && res.streams && res.streams[id]; return s && Number.isFinite(s.T) ? `${id}  ${f(s.T - 273.15)} C  ${f(s.P / 1e5)} bar` : id; };
const consumed = new Set();

Point inside a node boundary

64-bit

Whether a tag or line label lies inside a hazard study node's region.

count the sides whose ends straddle the point's height and cross to its right; inside when the count is odd

One division per straddling side and no tolerance, so a point exactly on the boundary can fall either way.

  • process/draw.js
  • line 71
  • CadCraft 0.8.2
const inPoly = (p, poly) => { let c = false; for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { const a = poly[i], b = poly[j]; if ((a[1] > p.y) !== (b[1] > p.y) && p.x < ((b[0] - a[0]) * (p.y - a[1])) / (b[1] - a[1]) + a[0]) c = !c; } return c; };

Controller faceplate numbers

64-bit

The set point, measurement, output and error a controller faceplate shows from a simulation.

error = set point − last measurement

The last values of the trend are passed on unrounded and the error is one subtraction.

  • process/draw.js
  • lines 107 to 113
  • CadCraft 0.8.2
/** A faceplate from a simulation result: the controller record, the trend of its pv and output, and the numbers the plate shows. */
export function faceplate(ctrl, trend = null) {
  const last = (a) => (Array.isArray(a) && a.length ? a[a.length - 1] : null);
  const pv = trend && trend.pv, out = trend && trend.out;
  return { tag: ctrl.id, mode: ctrl.auto === false ? 'MAN' : 'AUTO', sp: ctrl.sp, pv: last(pv), out: ctrl.auto === false ? ctrl.out ?? null : last(out), action: ctrl.action, Kc: ctrl.Kc, Ti: ctrl.Ti ?? null, Td: ctrl.Td ?? null, span: ctrl.span || null,
    error: Number.isFinite(last(pv)) && Number.isFinite(ctrl.sp) ? ctrl.sp - last(pv) : null };
}

In detail

Method
Units in columns by flow order, streams as polylines with arrow heads; a point-in-polygon test for what lies inside a hazard study node.
Number types
64-bit numbers; coordinates rounded with toFixed(3).
Tolerances
Two heights closer than 1e-6 draw as one straight arrow.
Limits
The point-in-polygon test has no tolerance on the boundary.

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