// worker_threads worker: generate + simulate one experiment, write per-run files, return a compact summary const { parentPort, workerData } = require('worker_threads'); const fs = require('fs'), path = require('path'), os = require('os'); const { loadCore } = require('./load_core.js'); const HFL = loadCore(); function writeFrames(id, res, outDir) { const fr = res.frames; if (!fr || !fr.length) return null; const N = fr[0].ux.length, E = fr[0].r.length; const per = 2 * N + 2 * E; const buf = new Float32Array(per * fr.length); const index = []; fr.forEach((f, k) => { buf.set(f.ux, k * per); buf.set(f.uy, k * per + N); buf.set(f.r, k * per + 2 * N); buf.set(f.Nax, k * per + 2 * N + E); index.push({ d: f.d, F: f.F, nEvents: f.nEvents }); }); fs.writeFileSync(path.join(outDir, id + '_frames.bin'), Buffer.from(buf.buffer)); fs.writeFileSync(path.join(outDir, id + '_frames.json'), JSON.stringify({ nFrames: fr.length, N, E, layout: 'per frame: ux[N], uy[N], r[E], Nax[E] float32 little-endian', frames: index })); return id + '_frames.bin'; } parentPort.on('message', (job) => { const t0 = Date.now(); try { const g = HFL.generate(job.params); const res = HFL.simulate(g, { frames: job.saveFrames ? 'both' : 'none', maxFrames: job.maxFrames || 240 }); const b = HFL.exportBundle(res, g); const rec = { id: job.id, stage: job.stage, reason: job.reason, hypothesis: job.hypothesis, tags: job.tags || [], date: new Date().toISOString(), appVersion: HFL.VERSION, params: res.params, seed: res.params.seed, branchingList: HFL.branchingList(res.params), structureStats: g.stats, W: g.W, H: g.H, t: g.t, N: res.N, E: res.E, status: res.status, mode: res.mode, metrics: res.metrics, diagnostics: b.summary.diagnostics, curve: res.curve, events: res.events, grid: res.grid, finalActive: Array.from(res.finalActive), geometry: b.geometry, runtimeMs: Date.now() - t0, host: os.hostname(), predictions: job.predictions || null, }; if (job.saveFrames) rec.framesFile = writeFrames(job.id, res, job.outDir); fs.writeFileSync(path.join(job.outDir, job.id + '.json'), JSON.stringify(rec)); const m = res.metrics; parentPort.postMessage({ ok: true, id: job.id, summary: { id: job.id, stage: job.stage, status: res.status, converged: m.converged, N: res.N, E: res.E, events: m.nEvents, k0: m.k0, Fpeak: m.Fpeak, Wf: m.workToFailure, loc: m.localization, drop: m.maxLoadDropNorm, ppw: m.postPeakWorkFraction, mode: m.fractureMode, resid: res.diagnostics.maxResidual, ms: Date.now() - t0 } }); } catch (e) { parentPort.postMessage({ ok: false, id: job.id, error: String(e && e.stack || e), ms: Date.now() - t0 }); } });