Resources Numerical accuracy Calculation policy

Material units, gravity and custom material limits

The unit factor from density to the solver's mass unit, the gravity constant, and the limits on a custom material.

How the precision is kept

The solver works in millimeters, tonnes and seconds, so N = t·mm/s². The density factor is exact. The gravity constant is a rounded value that the code comments call standard gravity.

Open item. Set G_MM to 9806.65, the standard gravity in mm/s², or change its comment to say it is the rounded 9.81 m/s².

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.

materials.js PartCraft 0.8.1 64-bit 3 calculations Reviewed

Tests None found by the review

Density to tonnes per cubic millimeter

Exact

Converts a density in kg/m³ to t/mm³ for the solver.

ρ(t/mm³) = ρ(kg/m³) × 10⁻¹²

1 kg/m³ is 10⁻³ t over 10⁹ mm³, so the factor 1e-12 is exact in decimal. It is one 64-bit multiply, and 1e-12 itself is the nearest 64-bit number to that value.

  • materials.js
  • line 43
  • PartCraft 0.8.1
export const tonnesPerMm3 = (kgPerM3) => kgPerM3 * 1e-12;

Gravity constant

64-bit

The default gravity used for self-weight, in millimeters per second squared.

g = 9810 mm/s²

The value is 9.81 m/s² rounded, labelled 'standard gravity' in the code. Standard gravity is 9.80665 m/s². fea.js and tetfea.js use it when a load gives no g of its own.

  • materials.js
  • line 44
  • PartCraft 0.8.1
export const G_MM = 9810;   // standard gravity, mm/s²

Custom material check

64-bit

Checks a material from a file or a person and fills in a library material's thermal values.

Each number must be a finite 64-bit number inside its range, else an error is thrown; values are copied as given, with no rounding.

  • materials.js
  • lines 46 to 62
  • PartCraft 0.8.1
/** A custom material from untrusted input → material or throws. */
export function readMaterial(m) {
  if (typeof m === 'string') { const k = materialById(m); if (!k) throw new Error(t('study.mat.unknown')); return k; }
  if (!m || typeof m !== 'object') throw new Error(t('study.mat.missing'));
  const num = (v, lo, hi, what) => { if (typeof v !== 'number' || !Number.isFinite(v) || v < lo || v > hi) throw new Error(t('study.mat.range.' + what)); return v; };
  const out = {
    id: 'custom', name: typeof m.name === 'string' ? m.name.slice(0, 60) : t('study.mat.custom'),
    E: num(m.E, 1, 2e6, 'e'), nu: num(m.nu, 0, 0.49, 'nu'), density: num(m.density, 1, 30000, 'density'), yield: num(m.yield, 0.01, 1e5, 'yield'),
  };
  // thermal properties are optional on a custom material (a thermal study asks for them)
  if (m.k !== undefined && m.k !== null) out.k = num(m.k, 1e-3, 5000, 'k');
  if (m.cp !== undefined && m.cp !== null) out.cp = num(m.cp, 1, 1e5, 'cp');
  if (m.alpha !== undefined && m.alpha !== null) out.alpha = num(m.alpha, 0, 1e-3, 'alpha');
  const lib = typeof m.id === 'string' ? materialById(m.id) : null;   // a library material passed as an object keeps its id
  if (lib && ['E', 'nu', 'density', 'yield'].every((k) => lib[k] === out[k])) { out.id = lib.id; out.name = lib.name; for (const k of ['k', 'cp', 'alpha']) if (out[k] === undefined) out[k] = lib[k]; }
  return out;
}

In detail

Method
kg/m³ × 1e-12 = t/mm³; gravity in mm/s².
Number types
64-bit numbers.
Tolerances
Custom material ranges: E 1 to 2e6 MPa, ν 0 to 0.49, density 1 to 30000 kg/m³, yield 0.01 to 1e5 MPa, k 1e-3 to 5000, cp 1 to 1e5, α 0 to 1e-3.
Limits
G_MM = 9810 mm/s² is the rounded 9.81 m/s². Standard gravity is 9806.65 mm/s², so self-weight and gravity loads that use the default are 0.034 percent high.

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