import type { BackendInfo, MethodDefinition, PlateResult } from "@plate-dyn/core"; import type { SolveState } from "../hooks/useSolve"; export interface RunStatusProps { state: SolveState; result?: PlateResult | null; method?: MethodDefinition; backend?: BackendInfo | null; /** Predicted wall time for the next run [ms], shown while idle */ estimateMs?: number; onCancel?: () => void; } /** Makes the execution visible: what ran, where, how big it was and how long it took. */ export function RunStatus({ state, result, method, backend, estimateMs, onCancel, }: RunStatusProps) { const be = state.status === "done" || state.status === "error" ? state.run.backend : backend; const wallMs = state.status === "done" || state.status === "error" ? state.run.wallMs : null; const compute = state.status === "done" || state.status === "error" ? state.run.compute : undefined; const onGpu = compute?.loop === "webgpu"; return (
{state.status === "idle" && ready} {state.status === "running" && ( <> running {onCancel && ( )} )} {state.status === "done" && finished} {state.status === "error" && ( {state.run.cancelled ? "cancelled" : "error"} )} {result && !result.info.stable && ( diverged at step {result.info.diverged_step} )} {method && {method.name}}
{state.status === "running" && (
)} {state.status === "error" &&
{state.run.error}
} {compute && ( )} {result && ( )} {result && ( )}
Backend {be ? `${be.engine} ${be.version}, ${be.optimization === "1" ? "JS JIT (opt 1)" : "interpreter (opt 0)"}, ${be.host === "web-worker" ? "Web Worker in this browser" : "Node.js"}` : "numbl (not yet started)"}
Wall time {wallMs !== null ? ( onGpu ? ( <> {fmtMs(wallMs)} in total: {fmtMs(compute.gpuMs ?? 0)} for the time loop on the GPU, {fmtMs(compute.numblMs ?? 0)} in numbl for setup and output ) : ( <> {fmtMs(wallMs)} in numbl {result ? ` (${fmtMs(result.info.wall_s * 1000)} inside the solver by tic/toc)` : ""} ) ) : state.status === "running" ? ( "…" ) : estimateMs !== undefined ? ( `about ${fmtMs(estimateMs)} expected` ) : ( "–" )}
Time loop {onGpu ? `WebGPU port of plate_fd_loop.m on ${compute.adapter ?? "the GPU"}, single precision` : `numbl${compute.note ? `: ${compute.note}` : ""}`}
Problem size {problemSize(result)}
Time step {fmtTime(result.info.dt)}, {result.info.nsteps.toLocaleString()} steps,{" "} {result.t.length.toLocaleString()} samples in the series
{result && result.info.notes.length > 0 && (
{result.info.notes.map((n, i) => (
{n}
))}
)} {(state.status === "running" || state.status === "done" || state.status === "error") && state.output.length > 0 && (
Script output ({state.output.length} lines)
{state.output.join("")}
)}
); } function problemSize(r: PlateResult): string { const x = r.info.extra; return `${x.nx} × ${x.ny} = ${r.info.ncells.toLocaleString()} nodes, ${Number(x.nunknowns).toLocaleString()} unknowns, ${r.rigid.nr} rigid-body coordinates, ${Number(x.cell_steps).toLocaleString()} cell-steps, ${r.frames.t.length} frames`; } export function fmtMs(ms: number): string { if (ms < 1000) return `${ms.toFixed(0)} ms`; return `${(ms / 1000).toFixed(2)} s`; } export function fmtTime(s: number): string { if (s >= 1) return `${s.toPrecision(3)} s`; if (s >= 1e-3) return `${(s * 1e3).toPrecision(3)} ms`; return `${(s * 1e6).toPrecision(3)} µs`; }