Advanced simulation add-on: result summary and color bands
Lists the headline numbers of a nonlinear or contact result and cuts the deformed plot mesh into color bands with a legend.
How the precision is kept
The numbers come from the solver's result object; this file only combines and formats them. Values handed to the panel are rounded to 4 (in places 3) significant digits with toPrecision; the result object keeps the full values. Band limits are evenly spaced between the least and largest finite value.
Calculations under this policy
3 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.
Rounds a result to a few significant digits before the panel words it.
toPrecision(p), then back to a number: the value given to the panel is rounded to p significant digits (4 by default). The result object is not changed.
addons/advanced-sim/lib/results.js
line 25
PartCraft 0.8.1
const sig = (v, p = 4) => (Number.isFinite(v) ? +(+v).toPrecision(p) : v);
Summary rows
64-bit
The load reached, largest displacement and stress, plastic strain, contact force and penetration of a result.
The load is Math.hypot of the three load totals times the load factor reached. Each number passes through sig, so the panel receives 4 significant digits (3 for plastic strain, penetration and time). The penetration percent divides by h only when h > 0.
Splits the plot mesh into bands of equal value range and gives each band's limits.
band = floor((v − lo) ÷ (hi − lo) × n), clamped to 0 … n − 1; limits: lo + (hi − lo) × i ÷ n
lo and hi are the least and largest finite value; a zero range is widened first, so the division is safe. A triangle's band comes from the mean of its three corner values. The shape scale is 5 % of the part's diagonal ÷ the largest displacement, rounded to 2 significant digits and never below 1.
addons/advanced-sim/lib/results.js
lines 55 to 88
PartCraft 0.8.1
export function bands(r, field = 'vm', { n = 8, scale = null, beside = true } = {}) {
const pm = r && r.plotMesh, val = r && r.vertex && r.vertex[field], U = r && r.vertex && r.vertex.u;
if (!pm || !val) return null;
const P = pm.positions, I = pm.indices, nv = P.length / 3, nt = I.length / 3;
let lo = Infinity, hi = -Infinity;
for (let i = 0; i < val.length; i++) { const v = val[i]; if (Number.isFinite(v)) { if (v < lo) lo = v; if (v > hi) hi = v; } }
if (!(hi >= lo)) return null;
if (hi - lo < 1e-12 * Math.max(1, Math.abs(hi))) hi = lo + 1e-12 * Math.max(1, Math.abs(lo)) + 1e-30;
// the size of the part, and the shape's scale: the largest displacement drawn as 5 % of the part's size unless given
const mn = [Infinity, Infinity, Infinity], mx = [-Infinity, -Infinity, -Infinity];
for (let i = 0; i < nv; i++) for (let k = 0; k < 3; k++) { const x = P[3 * i + k]; if (x < mn[k]) mn[k] = x; if (x > mx[k]) mx[k] = x; }
const size = Math.hypot(mx[0] - mn[0], mx[1] - mn[1], mx[2] - mn[2]) || 1;
let umax = 0; if (U) for (let i = 0; i < nv; i++) umax = Math.max(umax, Math.hypot(U[3 * i], U[3 * i + 1], U[3 * i + 2]));
const k = scale !== null ? scale : (umax > 0 ? Math.max(1, +((0.05 * size) / umax).toPrecision(2)) : 1);
const offset = beside ? [(mx[0] - mn[0]) * 1.25 + 0.1 * size, 0, 0] : [0, 0, 0];
const band = (v) => Math.min(n - 1, Math.max(0, Math.floor((v - lo) / (hi - lo) * n)));
const groups = Array.from({ length: n }, () => ({ pos: [], idx: [], map: new Map() }));
for (let t = 0; t < nt; t++) {
const a = I[3 * t], b = I[3 * t + 1], c = I[3 * t + 2];
const g = groups[band((val[a] + val[b] + val[c]) / 3)];
for (const q of [a, b, c]) {
let id = g.map.get(q);
if (id === undefined) {
id = g.map.size; g.map.set(q, id);
for (let d = 0; d < 3; d++) g.pos.push(P[3 * q + d] + (U ? k * U[3 * q + d] : 0) + offset[d]);
}
g.idx.push(id);
}
}
const legend = groups.map((_, i) => ({ lo: lo + (hi - lo) * i / n, hi: lo + (hi - lo) * (i + 1) / n, color: rampColor((i + 0.5) / n) }));
const bodies = [];
groups.forEach((g, i) => { if (g.idx.length) bodies.push({ name: 'band ' + (i + 1), color: legend[i].color, mesh: { positions: g.pos, indices: g.idx } }); });
return { bodies, legend, scale: k, offset, lo, hi, triangles: nt };
}
In detail
Method
summary: load = |load total| × load factor reached; penetration as a percent of the element size. bands: band index = floor((v − lo) ÷ (hi − lo) × n); a triangle takes the band of the mean of its three corner values.
Number types
64-bit arithmetic on the solver's arrays (their type is set by the worker that made them); band positions in plain arrays.
Tolerances
A range narrower than 1e-12 × max(1, |hi|) is widened by 1e-12 × max(1, |lo|) + 1e-30. Penetration is flagged above PEN_LIMIT × h (PEN_LIMIT is imported from nlstudy.js).
Limits
Band bodies pass through addon-bodies.js, which rounds their coordinates to 0.001 mm.
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.