Deterministic module ownership, runtime flow, worker boundaries, and separation between physics, UI, analysis, and rendering
Spinning Magnets Lab is structured as a scientific application rather than a monolithic graphics demo. Its architecture is built around one central promise: physics is computed through a deterministic pipeline, and every major stage has a clear owner. This is what allows the simulator to remain auditable as the project grows from simple induction visualization toward a broader generator analysis environment.
At a system level, the simulator follows the chain below:
state.project → project normalization → geometry placement → magnetic field evaluation → coil sampling and flux computation → induction and load response → electrical power and torque metrics → derived analysis products → UI panels and rendering
This runtime direction is deliberately one-way. Project state feeds the engine; the engine feeds the displays. The display layer is not allowed to repair schema, invent field values, or alter induction logic.
| Layer | Primary responsibility |
|---|---|
| Project / schema | Own defaults, normalization, import-export compatibility, and forward-safe project structure |
| Presets | Provide self-contained reference machine configurations and descriptive metadata |
| Geometry | Place magnets and coils in world coordinates |
| Field solving | Compute magnetic flux density from the active solver |
| Induction runtime | Track linkage, voltage, current, RMS, peaks, and load-aware electrical state |
| Mechanics | Compute shaft speed, torque-related quantities, and machine-side mechanical metrics |
| Analysis | Build rotor sweep products and other deterministic instrument-style summaries |
| Worker | Perform heavy deterministic calculations off the main UI thread |
| UI / rendering | Display existing results without changing the physics |
The project state is the authoritative machine description. It contains disc parameters, magnet parameters, coil configuration, field settings, performance settings, simulation settings, analysis configuration, probe information, and mechanics-related entries. Defaults live in the canonical default-project definition, while schema ownership belongs to the project schema file.
The architectural intent is that every project entry path must normalize data before the rest of the simulator consumes it. This keeps file imports, presets, and future format revisions stable and scientifically interpretable.
Project import and export are handled in the project I/O layer, which sanitizes incoming JSON, merges it with defaults, and ensures that imported machine states regain a valid internal structure before they are applied. That same principle is used when presets are loaded: preset definitions should be self-contained, but normalization still guarantees compatibility with the current schema.
Preset handling is intentionally separated from the core engine. A preset is not a physics solver; it is a curated machine definition. The registry layer stores preset identity, label, category, difficulty, description, and the associated project object.
This allows the UI to present documented machine families while still passing a normalized project into the engine. It also prevents display metadata from leaking into lower physics layers.
Owns preset identity and descriptive metadata without becoming the owner of magnetic or electrical calculations.
Ensures imported or preset-loaded machine data becomes schema-valid before the engine consumes it.
Loads presets, applies projects, updates UI descriptions, and orchestrates runtime without owning the solver mathematics.
The main application file is the orchestration layer. It wires together controls, worker messaging, simulation stepping, scope buffering, preset changes, toolbar behavior, overlay toggles, and rendering updates. It must coordinate the system without becoming the owner of magnetic field mathematics.
In practice, the main runtime loop advances rotor angle, schedules derived builds, applies authoritative induction outputs to the latest derived object, and updates panels and canvases from the resulting state. It also coordinates preset loading, project import/export, and instrument refresh.
Geometry modules translate the abstract project description into placed magnets and placed coils. Once this spatial structure exists, the field layer becomes responsible for evaluating the magnetic field generated by the placed magnets. This separation is essential: geometry decides where things are, while the field layer decides what magnetic field they produce.
The derived engine then consumes those placed objects and field contexts to build instrument-oriented outputs such as heatmap overlays, vector overlays, line scans, field probes, flux lines, and rotor-related diagnostics.
One of the strongest architectural decisions in the project is the separation between general derived data and authoritative induction. The derived path assembles placed geometry, overlays, and diagnostics, while the induction runtime owns the evolving coil linkage history and electrical state.
The induction runtime maintains sampling caches for coil surfaces, computes flux from those cached sample locations, tracks recent linkage history, derives instantaneous voltage, and propagates that into current, RMS, peak values, and load-aware measurement outputs. Those results are then injected back into the latest derived object so the UI can display them consistently.
This matters because it prevents the renderer or panel code from inventing physics values on demand. Instead, the measurement-style outputs come from one authoritative runtime path.
The mechanics layer extends the simulator from pure induction into electromechanical interpretation. It owns quantities such as shaft torque, angular velocity, mechanical power, centripetal and safety-related rotor metrics, and other machine-side results that depend on the rotating assembly rather than solely on field sampling.
Keeping mechanics explicit prevents those values from being improvised in the UI and makes it easier to compare electrical output against mechanical input in a disciplined way.
Rotor sweep is best understood as a dedicated analysis product, not as ordinary frame rendering. It evaluates the machine over many deterministic rotor angles and produces angle-resolved signatures such as linkage, voltage, current, power, reaction torque, shaft torque, and net torque.
Architecturally, this belongs to an analysis layer because it generates a structured data product from the underlying engine. That product can then feed charts, tables, export routines, and future efficiency-map tools without altering the core real-time runtime.
Heavy derived computations are performed inside a dedicated physics worker. The worker receives a build request, calls the derived builder, caches overlay products when appropriate, and posts the result back to the main thread through a defined message protocol.
This allows the application to remain interactive even when heatmaps, vector overlays, flux-line calculations, or larger analysis products are active. Just as importantly, it preserves the separation between orchestration on the main thread and deterministic numerical work in the worker path.
The documentation shell, control panels, preset descriptions, coil table, scope panel, rotor sweep panel, probe displays, and stage rendering all belong to the presentation side of the project. These components may format numbers, show diagnostics, and provide interaction, but they must not repair missing schema or compute missing physics.
The current interface reflects this instrument-oriented layout: a left control area, central stage, and right-side measurement panels built around coil prediction, scope, rotor state, field probe information, performance readout, and analysis panels.
The intended dependency direction is simple: UI and rendering sit above orchestration, orchestration depends on application and engine modules, and the lower layers handle schema, presets, field, geometry, mechanics, and utility logic. Forbidden crossovers include engine modules importing UI code, field modules depending on rendering, workers owning UI concerns, or UI-side schema repairs.
The architecture is designed to support growth without losing scientific clarity. That means new visualization layers can be added, new field solvers can be compared, new machine presets can be documented, and new instrument panels can be introduced, provided the one-way deterministic pipeline remains intact.
The simulator is therefore best understood as a structured instrument platform: project state defines the machine, normalization stabilizes the machine definition, geometry places the machine, field solvers compute the magnetic space, coil integration samples that space, induction runtime turns flux history into electrical observables, mechanics interprets the rotating system, analysis builds higher-level deterministic products, and rendering presents the result.