/ proof-of-concept / plate-dynamics-lab
Sign in
proof-of-concept / plate-dynamics-lab
plate-dynamics-lab / packages / components / src / status / RunStatus.tsx
160 lines · 5.4 KBCodeBlameHistory
80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 1import type { BackendInfo, MethodDefinition, PlateResult } from "@plate-dyn/core";
2import type { SolveState } from "../hooks/useSolve";
4export interface RunStatusProps {
5 state: SolveState<unknown>;
6 result?: PlateResult | null;
7 method?: MethodDefinition;
8 backend?: BackendInfo | null;
9 /** Predicted wall time for the next run [ms], shown while idle */
10 estimateMs?: number;
11 onCancel?: () => void;
14/** Makes the execution visible: what ran, where, how big it was and how long it took. */
15export function RunStatus({
16 state,
17 result,
18 method,
19 backend,
20 estimateMs,
21 onCancel,
22}: RunStatusProps) {
23 const be = state.status === "done" || state.status === "error" ? state.run.backend : backend;
24 const wallMs = state.status === "done" || state.status === "error" ? state.run.wallMs : null;
25 const compute =
26 state.status === "done" || state.status === "error" ? state.run.compute : undefined;
27 const onGpu = compute?.loop === "webgpu";
28 return (
29 <div className="pl-status">
30 <div className="pl-row">
31 {state.status === "idle" && <span className="pl-badge pl-badge-info">ready</span>}
32 {state.status === "running" && (
33 <>
34 <span className="pl-badge pl-badge-info">running</span>
35 {onCancel && (
36 <button className="pl-btn pl-btn-danger" onClick={onCancel}>
37 Cancel
38 </button>
39 )}
40 </>
41 )}
42 {state.status === "done" && <span className="pl-badge pl-badge-ok">finished</span>}
43 {state.status === "error" && (
44 <span className="pl-badge pl-badge-error">
45 {state.run.cancelled ? "cancelled" : "error"}
46 </span>
47 )}
48 {result && !result.info.stable && (
49 <span className="pl-badge pl-badge-error">
50 diverged at step {result.info.diverged_step}
51 </span>
52 )}
53 {method && <span className="pl-muted">{method.name}</span>}
54 </div>
55 {state.status === "running" && (
56 <div className="pl-progress">
57 <div />
58 </div>
59 )}
60 {state.status === "error" && <div className="pl-error">{state.run.error}</div>}
61 <table>
62 <tbody>
63 <tr>
64 <td>Backend</td>
65 <td>
66 {be
67 ? `${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"}`
68 : "numbl (not yet started)"}
69 </td>
70 </tr>
71 <tr>
72 <td>Wall time</td>
73 <td>
74 {wallMs !== null ? (
75 onGpu ? (
76 <>
77 {fmtMs(wallMs)} in total: {fmtMs(compute.gpuMs ?? 0)} for the time loop on the
78 GPU, {fmtMs(compute.numblMs ?? 0)} in numbl for setup and output
79 </>
80 ) : (
81 <>
82 {fmtMs(wallMs)} in numbl
83 {result
84 ? ` (${fmtMs(result.info.wall_s * 1000)} inside the solver by tic/toc)`
85 : ""}
86 </>
87 )
88 ) : state.status === "running" ? (
89 "…"
90 ) : estimateMs !== undefined ? (
91 `about ${fmtMs(estimateMs)} expected`
92 ) : (
93 "–"
94 )}
95 </td>
96 </tr>
97 {compute && (
98 <tr>
99 <td>Time loop</td>
100 <td>
101 {onGpu
102 ? `WebGPU port of plate_fd_loop.m on ${compute.adapter ?? "the GPU"}, single precision`
103 : `numbl${compute.note ? `: ${compute.note}` : ""}`}
104 </td>
105 </tr>
106 )}
107 {result && (
108 <tr>
109 <td>Problem size</td>
110 <td>{problemSize(result)}</td>
111 </tr>
112 )}
113 {result && (
114 <tr>
115 <td>Time step</td>
116 <td>
117 {fmtTime(result.info.dt)}, {result.info.nsteps.toLocaleString()} steps,{" "}
118 {result.t.length.toLocaleString()} samples in the series
119 </td>
120 </tr>
121 )}
122 </tbody>
123 </table>
124 {result && result.info.notes.length > 0 && (
125 <div>
126 {result.info.notes.map((n, i) => (
127 <div className="pl-note" key={i}>
128 {n}
129 </div>
130 ))}
131 </div>
132 )}
133 {(state.status === "running" || state.status === "done" || state.status === "error") &&
134 state.output.length > 0 && (
135 <details>
136 <summary className="pl-small pl-muted">
137 Script output ({state.output.length} lines)
138 </summary>
139 <div className="pl-log">{state.output.join("")}</div>
140 </details>
141 )}
142 </div>
143 );
146function problemSize(r: PlateResult): string {
147 const x = r.info.extra;
148 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`;
151export function fmtMs(ms: number): string {
152 if (ms < 1000) return `${ms.toFixed(0)} ms`;
153 return `${(ms / 1000).toFixed(2)} s`;
156export function fmtTime(s: number): string {
157 if (s >= 1) return `${s.toPrecision(3)} s`;
158 if (s >= 1e-3) return `${(s * 1e3).toPrecision(3)} ms`;
159 return `${(s * 1e6).toPrecision(3)} µs`;
↑↓ move↵openescclose