Workbench¶
The workbench is a browser app for looking at runs: a dock of panels that shows solved fields in 3D and 2D, browses the run and artifact indexes, and follows runs and sweeps while they solve. It runs as a local trame server that you start from a script or from the femforge-workbench command, and it reads the same telemetry root and run bundles as the rest of femforge. You can also build your own dock from the same panels and list it beside the built-in ones.
Install¶
The workbench is a separate package, femforge-workbench, that the workbench extra installs:
ff.workbench raises an ImportError that names this command when the extra is missing. The package has extras of its own, listed on the install page, among them app for the --webview window and pty for an interactive terminal on Windows.
Open the field viewer from a script¶
import femforge as ff
display = ff.DisplaySpec(
field="magnetic flux density",
mode=ff.DisplayMode.MAGNITUDE,
colormap="plasma",
hidden_bodies=("air",),
body_styles=(ff.BodyStyle(body="mover", paint=ff.BodyPaint.SOLID, color="#c0c0c0", opacity=0.5),),
streamlines=ff.FieldLineOptions(seed_placement=ff.SeedPlacement.CENTER_PLANE, line_count=16),
)
viewer = ff.workbench.field_viewer(display=display, telemetry="telemetry")
ff.workbench.show(viewer, title="Field viewer")
field_viewer builds the viewer dock. Its run picker lists the indexed runs of the telemetry root, FEMFORGE_TELEMETRY when you pass none. Pass run= a Result, a Run, a run id or a bundle directory to open that run once the server starts. The DisplaySpec sets where the controls start; Results covers it.
show serves a workbench and returns when its window closes:
show(workbench) |
Opens |
|---|---|
a dock Split |
that dock, as built |
a module-level function that returns a Split or a Renderable |
its result, rebuilt on every save of a .py file under its source roots |
a WorkbenchSpec |
that catalogue card, as the command opens it by name |
| nothing | the launcher |
window= |
Opens in |
|---|---|
Window.APP, the default |
a Chrome or Edge app window; closing it stops the server |
Window.BROWSER |
a tab in the default browser |
Window.WEBVIEW |
pywebview, from the app extra |
show also takes port, telemetry, persist and watch, which match the command's flags below.
The femforge-workbench command¶
femforge-workbench # the launcher
femforge-workbench field-viewer --telemetry telemetry
femforge-workbench -m lab.workbenches:gap_study --app
| Argument | Does |
|---|---|
name |
Open the catalogue card with this name, such as demo or field-viewer. Without a name or -m, open the launcher. |
-m pkg.module:attr |
Open the module attribute, a function that returns a dock Split or a Renderable. |
-m pkg.module |
Open the module's main(), which returns a Renderable. |
--app |
Open a Chrome or Edge app window; closing it stops the server. |
--webview |
Open in pywebview, from the app extra. |
--dev |
Run the server as a child that restarts after every save to the shell's own modules; the page reloads itself. |
--port N |
Serve on port N, 8080 by default. |
--watch DIR |
Also rebuild the open workbench when a .py file under DIR is saved. Repeatable. |
--telemetry DIR |
The telemetry root the panels read. It also sets FEMFORGE_TELEMETRY for the process, so a card opened later reads the same root. |
--persist DIR |
Where layouts and choices are saved, the per-user config directory by default. |
Without --app or --webview, the command opens a browser tab. An unknown name, or the browsers or monitors card without a telemetry root, exits with code 2 and a message. CLI lists the command beside femforge.
The built-in cards¶
| Card | Opens |
|---|---|
launcher |
Every catalogue card: open and close docks, open previews, run scripts and browse what they produced. |
demo |
A sphere, a line figure and a grouped table, to try the shell without data. |
browsers |
The run browser over the run detail, beside the artifact browser over the artifact detail. |
field-viewer |
A solved run's fields: the 3D view, the 2D plot, colormaps, bodies and field lines. |
monitors |
A run's heartbeat, stages, convergence and logs; a sweep's progress and failures; the log browser; a terminal. |
The browsers and monitors cards read the telemetry root; the Telemetry page covers what it holds. The artifact panels read the store at FEMFORGE_ARTIFACTS when it is set, else the store the index rows name.
The field viewer¶
The field viewer puts its controls beside two tabs, a 3D view and a 2D plot. The app bar holds the run picker, which lists the 200 newest indexed runs that still have fields, and a field for a bundle path. A run that is still solving opens once its solve ends. A 2D run under 400,000 cells draws as a plotly figure in the 2D tab; every other run draws in the 3D view. The camera stays put when the next run frames alike.
The controls come in three sections:
| Section | Controls |
|---|---|
| Field | Quantity, Mode, Colormap, Invert colormap, Log scale, Outline, Auto range with the color range slider, Opacity mapping |
| Field Lines | Show field lines, Seeds, Integrate within, the seed count, Length |
| Bodies | An All Bodies row with the body colormap and the overall opacity, then one row per body and per material that owns several bodies, each with a visibility checkbox, a color and an opacity |
The Mode picker offers what the quantity supports:
| DisplayMode | Colors by | Offered for |
|---|---|---|
MAGNITUDE |
the vector magnitude | vectors, the default |
X, Y, Z |
one component | vectors |
POLAR_XY |
the in-plane direction as hue, brightened by the in-plane magnitude | vectors |
VALUE |
the value | scalars, the default |
ABSVALUE |
the absolute value | scalars |
Each body's color comes from one of three sources, its BodyPaint:
| Paint | Color | In the body row |
|---|---|---|
FIELD |
the field colormap, by the displayed value | a swatch of the field colormap; click it for the body colormap |
COLORMAP |
one color of the body colormap, spread evenly over every body that takes it | a color chip; pick a color to fix it |
SOLID |
the body's own color | a color chip; the cross returns the body to the field colormap |
The automatic color range spans every body. With Auto range on, the range is the union of each body's own range, over every body whether it is shown, hidden or painted another way, so hiding a body never rescales the rest. Each body is clamped to its 2nd to 98th percentile, so a small body keeps its range beside a large one. Log scale keeps only the positive values and starts at their 1st percentile. Turn Auto range off to pin the range with the slider.
Field lines integrate the magnetic flux density, so the switch stays off for a run without one. Seeds sit on a grid in the domain's bounding box: a volume, the center plane or a line along X through the center. A 2D run moves volume seeds to the center plane. Length is the fraction of the domain diagonal a line may run, and each line ends at its first sharp reversal. Integrate within limits the lines to one body.
Runs and figures¶
| Panel | Shows |
|---|---|
| RunBrowserPanel | The run index as a table, refreshed every 5 s, with text, status, tag and physics filters and the runs claimed right now marked. A row click selects the run. |
| RunDetailPanel | The selected run: its manifest summary, stage timeline, results, artifact inputs, files and retention history, re-read every second while it solves. Fields opens the run in the field viewer, Folder opens its directory, Rehydrate fetches its store inputs back and Purge opens a dialog that removes bundle files by a retention plan. |
| FigurePanel | One plotly figure; update() swaps it, a figure with a uirevision keeps its zoom and selections, and bind() routes plotly events to a handler. |
| TablePanel | A polars frame that the browser sorts, filters, groups and hides columns of, with its layout saved per panel. |
The dock¶
A dock is a tree of three nodes:
| Node | Holds |
|---|---|
| Panel | One tab: a title, a builder that makes the component once the shell exists, a weight and an optional id. |
| Tabs | Panels sharing one tab group, the first one shown. |
| Split | Panels, tab groups and splits along one axis, shared by weight. The root is horizontal and the axis alternates with depth. |
A panel's id is its id, else its title in lower case with dashes. The builder receives the shell, the id and the title.
The workbench saves its layout and its open cards when they change, and restores the layout on the next start when it names exactly the panels the dock defines; a changed dock starts from its authored layout. Popped-out and floating groups come back docked. The reset button in the app bar rebuilds the authored layout. Each table saves its column layout and each 3D view its render mode.
The documents live in the per-user config directory, such as %LOCALAPPDATA%\femforge-workbench on Windows or ~/.config/femforge-workbench on Linux, as <user>/<workbench>/<key>.json. Pass --persist DIR to keep them elsewhere.
Write your own workbench¶
A workbench is a module-level function that returns a Split. Register it as a card through the femforge.workbenches entry point group:
import femforge as ff
import plotly.graph_objects as go
import polars as pl
from femforge_workbench.registry import WorkbenchSpec, panel
def force_curve(app: ff.workbench.Workbench, id: str, title: str) -> ff.workbench.FigurePanel:
"""The force against the gap."""
figure = go.Figure(go.Scatter(x=[1.0, 2.0, 3.0], y=[8.7, 6.1, 4.4], name="force"))
return ff.workbench.FigurePanel(app, id, title, figure)
def points(app: ff.workbench.Workbench, id: str, title: str) -> ff.workbench.TablePanel:
"""The solved points."""
frame = pl.DataFrame({"gap": [1.0, 2.0, 3.0], "force": [8.7, 6.1, 4.4]})
return ff.workbench.TablePanel(app, id, title, frame, filterable=True)
def gap_study() -> ff.workbench.Split:
"""The force curve beside its points."""
return ff.workbench.Split([ff.workbench.Panel("Force", force_curve, weight=2), ff.workbench.Panel("Points", points)])
WORKBENCHES: list[WorkbenchSpec] = [
panel("gap-study", "Gap Study", target=gap_study, description="Force against air gap.", group="Lab"),
]
if __name__ == "__main__":
ff.workbench.show(gap_study)
An entry point provides one WorkbenchSpec or a sequence of them. Three functions in femforge_workbench.registry declare one:
| Function | Card | Opens |
|---|---|---|
panel |
dock | the dock its target returns |
renderable |
viewer | the Renderable its target returns, in one 3D panel |
script |
script | its module, run with python -m in a subprocess; the launcher shows the files it lists in outputs |
A target is the function itself or its pkg.module:attr name, and its module is imported only when the card opens, so listing the catalogue imports nothing heavy. Two different specs under one name raise an error. A dock or viewer card rebuilds in place when you save a .py file under its source roots or a --watch directory, keeping the layout; a builder that raises leaves the old panels and names the error in the status line.
Limits¶
- One VTK.wasm mirror per page. The newest 3D panel mirrors its scene in the browser; older ones stream rendered frames from the server and say so in their status line.
- A scene whose geometry exceeds 64 MB streams from the server instead of mirroring.
--appandWindow.APPlook for Chrome and Edge at their default Windows install paths. Without either, the workbench opens a browser tab, and closing it leaves the server running.- The terminal panel is interactive on Windows with the
ptyextra. Elsewhere it shows the live log stream, read-only. - A
Splitpassed toshowis served as built. Pass the function that builds it to rebuild on save. - The
browsersandmonitorscards need a telemetry root.