Skip to content

OpenMagnetics/MagneticAdviser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MagneticAdviser WASM

Standalone WebAssembly module exposing the OpenMagnetics MagneticAdviser (core + coil selection, simulation, scoring) to JavaScript through a single Emscripten artifact (libMagneticAdviser.wasm.js + .wasm, ~26 MB with all MAS catalogs embedded).

Contents

Bindings at a glance

Function Purpose State
adviser(...) Rank designs for a complete MAS Inputs document. reads g_constraints, g_libraryContext, Settings
topologyAdviser(...) Same, but builds the Inputs from a per-topology spec via the matching MKF converter. same
setAdviserConstraints(json) Set wire / shape / material filters + onlyStock. writes g_constraints (+ Settings for onlyStock)
clearAdviserConstraints() Drop all filters and reset g_onlyStock. writes g_constraints
getAdviserConstraints() Inspect the current filter state. read-only
setLibraryFromJson(text, mode) Install a per-process user catalog. writes g_libraryContext
clearLibrary() Drop the user catalog. writes g_libraryContext
simulate(masJson, fastMode) Run MagneticSimulator on a complete MAS. independent of stateful config
simulateMagnetic(inputsJson, magneticJson, fastMode) Same, with Inputs+Magnetic split. same
list_topologies() JSON array of topology keys for topologyAdviser. read-only
list_settings() JSON array of valid keys for the settingsJson arg. read-only

Authoritative source: EMSCRIPTEN_BINDINGS block at the bottom of src/libMagneticAdviser.cpp.

Build

See BUILD.md for the full sequence (one-time ngspice staging, MAS header generation order, debug flags). Quick recap once emsdk is sourced:

mkdir -p build && cd build
emcmake cmake .. -G Ninja                                        \
    -DEMBED_MAS_CORES=ON -DEMBED_MAS_CORE_SHAPES=ON              \
    -DEMBED_MAS_CORE_MATERIALS=ON -DEMBED_MAS_WIRES=ON           \
    -DEMBED_MAS_WIRE_MATERIALS=ON -DEMBED_MAS_BOBBINS=ON         \
    -DEMBED_MAS_INSULATION_MATERIALS=ON
ninja CAS MAS                       # generate headers first (CAS.hpp race)
ninja libMagneticAdviser -j4

Outputs land in build/: a 108 KB ES6 module and a 26 MB .wasm.

Loading the module

The build produces an ES module — serve the .js and .wasm from the same directory:

<script type="module">
  import Module from './libMagneticAdviser.wasm.js';
  const m = await Module();         // instantiates the WASM
  const r = JSON.parse(m.adviser(JSON.stringify(inputs), 5, '{}', false, '', '{}'));
</script>

In Node ≥ 18:

import Module from './build/libMagneticAdviser.wasm.js';
const m = await Module({
    locateFile: (p) => './build/' + p,    // tell Emscripten where the .wasm sits
    print:    () => {},                   // silence MAS catalog load chatter
    printErr: () => {},
});

Quick start

import Module from './build/libMagneticAdviser.wasm.js';
const m = await Module({ locateFile: p => './build/' + p });

// 1. Optionally narrow the candidate space. Filters persist across calls
//    until cleared.
m.setAdviserConstraints(JSON.stringify({
    allowedCoreShapeFamilies: ['ETD', 'PQ'],
    allowedWireTypes:         ['ROUND', 'LITZ'],
    onlyStock:                true,
}));

// 2. Run the topology adviser — caller has a Buck spec, library builds the
//    MAS Inputs (process_design_requirements + ngspice).
const buck = {
    inputVoltage:       { minimum: 10, maximum: 12 },
    diodeVoltageDrop:   0.7,
    efficiency:         0.85,
    currentRippleRatio: 0.4,
    operatingPoints: [{
        outputVoltages: [5], outputCurrents: [2],
        switchingFrequency: 100000, ambientTemperature: 25,
    }],
};
const r = JSON.parse(m.topologyAdviser('buck', JSON.stringify(buck),
                                       3, '{}', /*fastMode*/ false, '', '{}'));
console.log(r.data[0].mas.magnetic.core.name, r.data[0].weightedTotalScoring);

// 3. Re-simulate the top pick with a different model toggle, etc.
const sim = JSON.parse(m.simulate(JSON.stringify(r.data[0].mas), /*fastMode*/ false));
console.log(sim.outputs[0].coreLosses.coreLosses);

API reference

Adviser entry points

adviser(inputsJson, maxResults, weightsJson, fastMode, coreModeString, settingsJson) -> string

Direct path: takes a MAS Inputs document and returns ranked magnetic designs.

Param Type Description
inputsJson string Stringified MAS Inputs ({designRequirements, operatingPoints}).
maxResults number Maximum designs to return.
weightsJson string Stringified {<MagneticFilter>: weight} map. Pass '{}' for default weights. Ignored when fastMode=true.
fastMode boolean true = analytical fast path (no ngspice/CoilAdviser, weights & CMC flow ignored). false = full path.
coreModeString string '' (default = STANDARD_CORES), "STANDARD_CORES", or "AVAILABLE_CORES".
settingsJson string Stringified {<setting-key>: <value>} map applied to MKF Settings before the run. '{}' to leave Settings alone. See list_settings() for valid keys.

topologyAdviser(topology, topologyInputsJson, maxResults, weightsJson, fastMode, coreModeString, settingsJson) -> string

Converter path: builds the MAS Inputs from a per-topology spec by running process_design_requirements() + ngspice waveform extraction internally, then runs the adviser.

Param Type Description
topology string Topology key — see list_topologies(). Case-insensitive; _/-/spaces are stripped ("flyback", "PHASE_SHIFTED_FULL_BRIDGE", "advancedLlc" all work).
topologyInputsJson string Stringified per-topology inputs (constructor argument of the matching MKF converter class).
Others Same as adviser().

Topology coverage. 44 topologies registered as of this build: Buck, Boost, Cuk, Sepic, Zeta, FourSwitchBuckBoost, Flyback, IsolatedBuck/BuckBoost, SingleSwitch/TwoSwitch/ActiveClampForward, PushPull, AsymmetricHalfBridge, PhaseShiftedFullBridge, PhaseShiftedHalfBridge, LLC, CLLC, DAB — each with an advanced… variant. AdvancedFlyback skips ngspice and routes through converter.process() directly (smaller payload, faster).

Constraints and library context (stateful)

Both pieces of state persist across calls until cleared. They're applied inside every adviser branch via the MKF MagneticAdviser::get_advised_magnetic(..., ctx, constraints) overloads that wrap each call in a DatabaseFilterScope (RAII swap of the global coreDatabase / wireDatabase). Simulation calls do not honor them.

setAdviserConstraints(constraintsJson) -> string

Returns "{}" on success, {"error": "..."} on a parse/validation error (unknown key, non-array value, non-bool onlyStock, etc.). All keys optional; missing key = no filter on that dimension. A value passes a dimension iff (allowed.empty() || allowed∋v) && !blocked∋v — i.e. allow-list is opt-in; block-list is always active.

{
    "allowedWireTypes":         ["ROUND", "LITZ"],
    "blockedWireTypes":         ["FOIL"],
    "allowedCoreShapeFamilies": ["ETD", "PQ", "E"],
    "blockedCoreShapeFamilies": ["T"],
    "allowedCoreMaterialTypes": ["ferrite"],
    "blockedCoreMaterialTypes": [],
    "onlyStock":                true
}

String matching is case-insensitive against the magic_enum name of the corresponding MAS enum ("ETD" / "etd" / "Etd" all match CoreShapeFamily::ETD).

onlyStock is a thin convenience over Settings::set_use_only_cores_in_stock. Toggling it forces a core-catalog reload on the next call because the stock vs full catalog (cores_stock.ndjson vs cores.ndjson) is chosen at load_cores() time and cached.

Pass "{}" to clear, or use clearAdviserConstraints() for the same effect.

clearAdviserConstraints() -> void

Resets all filter sets to empty and drops the onlyStock preference. Does not revert the MKF Settings value for use_only_cores_in_stock — if you previously flipped it via onlyStock, call setAdviserConstraints({onlyStock: <previous>}) to flip it back explicitly.

getAdviserConstraints() -> string

Returns the current state for debugging:

{
    "wireType":         { "allowed": [...], "blocked": [...] },
    "shapeFamily":      { "allowed": [...], "blocked": [...] },
    "coreMaterialType": { "allowed": [...], "blocked": [...] },
    "onlyStock":        true
}

onlyStock is omitted from the response when unset.

setLibraryFromJson(jsonText, mode) -> string

Installs a per-process user catalog (LibraryContext) consumed by every subsequent adviser call. Returns "{}" on success or {"error": "..."}. Schema is anything LibraryContext::loadFromString accepts — keys include coreShapes, coreMaterials, cores, wires, wireMaterials, bobbins, insulationMaterials (each may be a {name: entry} map or an array of entries with a name field).

mode Effect
"merge" Supplements the built-in catalog (default if mode is empty).
"replace" The override fully replaces the corresponding built-in map.

Repeated calls accumulate into the same context; clearLibrary() drops it entirely.

clearLibrary() -> void

Drops the user library context. Subsequent adviser calls revert to the built-in catalogs.

Simulation

Both entry points run MagneticSimulator and return the fully-populated MAS (with outputs[] filled in: core losses, winding losses, magnetizing inductance, leakage, temperature, etc.). Neither honors setAdviserConstraints (those filter candidates; simulation operates on a chosen design) or setLibraryFromJson (the MAS is self-contained — core, material, and wires are embedded in magnetic).

simulate(masJson, fastMode) -> string

Param Type Description
masJson string Stringified MAS ({inputs, magnetic} at minimum). Coil must be fully processed (turn descriptions present).
fastMode boolean true = analytical estimates only; false = full simulation with iterative loss/temperature convergence.

A MAS produced by adviser(..., fastMode=false, ...) qualifies. A fast-mode MAS does not and will throw COIL_NOT_PROCESSED ... Missing turns description (see Gotchas).

simulateMagnetic(inputsJson, magneticJson, fastMode) -> string

Same simulation with Inputs and Magnetic supplied separately. Matches MagneticSimulator::simulate(Inputs, Magnetic, bool) 1:1. Useful when the caller already has the two pieces split (e.g. resimulating a chosen core against a new operating point).

Introspection

list_topologies() -> string

Returns a stringified JSON array of all topology keys accepted by topologyAdviser(). Useful for populating UI dropdowns without hard-coding the list.

list_settings() -> string

Returns a stringified JSON array of all setting keys accepted in the settingsJson argument of adviser() / topologyAdviser(). As of this build, 64 settings are registered.

Return shape

Adviser entry points return a stringified JSON object. On success:

{
    "data": [
        {
            "mas":                  { ... full MAS object ... },
            "weightedTotalScoring": 4.0,
            "scoringPerFilter":     { "COST": 1.0, "LOSSES": 0.95, ... }
        },
        ...
    ]
}

Sorted by weightedTotalScoring descending. On error:

{ "error": "<message>" }

simulate() / simulateMagnetic() return the simulated MAS directly (no data wrapper) on success, same {error} shape on failure.

The library never throws into JS — C++ exceptions are caught at the JS boundary (try/catch in every entry point) and converted to the error payload above. The boundary handler is a translator, not a safety net: do not extend the pattern deeper into the code to swallow logic errors.

Fast mode vs full mode

Aspect fastMode=true fastMode=false
Pipeline get_advised_magnetic_fast() only full MagneticAdviser flow
Coil processing Skipped — no turn placement Full CoilAdviser + MagneticSimulator
ngspice waveform path Skipped Used by topologyAdviser only
Weights Ignored — ranks by total analytical losses Honored (or default COST+LOSSES+DIMENSIONS)
CMC (interference) flow Ignored Honored via the dedicated CMC filter flow
Typical wall-clock 1–2 s per call (small N) 8–30 s per call (varies wildly with N & spec)
simulate() input? No — fast-mode MAS will throw Yes
Constraints honored? Yes Yes

Use fast mode for interactive UI ranking, panel sweeps, or first-pass exploration. Use full mode whenever the downstream caller needs a fully-processed coil (simulation, loss breakdown, plotting) or whenever custom weights / CMC filter flow matter.

Settings

The settingsJson argument is a {key: value} map applied to MKF Settings before each adviser call. Settings persist (Settings is a singleton) — passing them in settingsJson makes the change explicit per call, but it does not roll back afterward. Get the full list at runtime via list_settings().

Selected high-impact keys:

Key Type Notes
use_only_cores_in_stock bool Loads cores_stock.ndjson instead of cores.ndjson. Also reachable via setAdviserConstraints({onlyStock: ...}).
use_toroidal_cores bool Include / exclude toroidal cores.
use_concentric_cores bool Include / exclude two-piece-set / piece-and-plate cores.
use_powder_cores bool Include / exclude powder materials.
core_adviser_include_stacks bool Expand stackable shapes (E, T, U, C, PLANAR_E) to multi-stack variants.
core_adviser_include_distributed_gaps bool Allow cores with one gap per column.
core_adviser_saturation_margin double Bpeak × margin > Bsat ⇒ reject. Default 1.2. Set to 1.0 for legacy behavior.
coil_adviser_maximum_number_wires size_t Cap on wires evaluated per winding.

Mutating Settings has the same caching caveat as onlyStock — flags consulted at load_cores() time (use_toroidal_cores, use_concentric_cores, use_only_cores_in_stock) only take effect after the catalog reloads. Toggling those keys from settingsJson without invalidating the catalog will look like a no-op.

Demo page (demo/)

A single-file static page that exercises every binding through buttons + textareas. To use:

# from the repo root, after building build/libMagneticAdviser.wasm.{js,wasm}
python3 -m http.server 8765
# open http://localhost:8765/demo/

Panels:

# Panel What it covers
1 setAdviserConstraints / clear / get Checkboxes for wire types, shape families, material types; onlyStock toggle; live getAdviserConstraints round-trip view.
2 setLibraryFromJson / clearLibrary Paste a LibraryContext JSON, pick merge/replace.
3 topologyAdviser Topology dropdown (populated from list_topologies()), JSON textarea pre-seeded with the buck fixture, fastMode/coreMode controls.
4 adviser Direct MAS Inputs. "Load flyback fixture" button pulls test/fixtures/inputs_flyback_65W.json.
5 simulate / simulateMagnetic "Use last result" button pipes the most recent adviser output. Gated: refuses to populate from a fast-mode result with a clear message.
6 list_topologies / list_settings One-click introspection.
7 Runtime benchmark Sweep maxResults across fast / full modes; pivoted table with per-row timing and an optional speedup column.

The page exposes the WASM module as window.M, so the browser console is also a usable REPL.

Common patterns

Compare fast vs full on the same input — handy when calibrating expectations:

const fastR = JSON.parse(m.topologyAdviser('buck', payload, 5, '{}', true,  '', '{}'));
const fullR = JSON.parse(m.topologyAdviser('buck', payload, 5, '{}', false, '', '{}'));

Resimulate the top pick under different Settings — adviser cached the magnetic; only the simulator inputs change:

const top = fullR.data[0].mas;
// Bump the saturation margin and rerun
const sim = JSON.parse(m.simulate(JSON.stringify(top), /*fastMode*/ false));

Drop one MAS database to shrink the artifact — pass -DEMBED_MAS_WIRES=OFF (and friends) to CMake. Be aware that without the wire catalog any non-fast adviser call will fail to wind the coil.

Drive the demo programmatically — every button delegates to a window.* function. Useful for E2E tests:

M.setAdviserConstraints(JSON.stringify({ allowedCoreShapeFamilies: ['PQ'] }));
document.querySelector('button[onclick="runTopology()"]').click();

Gotchas

  • COIL_NOT_PROCESSED ... Missing turns descriptionsimulate() on a fast-mode MAS. Fast mode skips CoilAdviser, so magnetic.coil has no turnsDescription. Re-run the adviser with fastMode=false and feed that MAS to simulate().
  • onlyStock toggle "doesn't take effect" — the core catalog is cached. The binding handles this for you by calling clear_loaded_cores() whenever the flag flips. If you mutate use_only_cores_in_stock (or use_toroidal_cores, use_concentric_cores) through settingsJson instead, you must flip the flag through a path that invalidates the cache or restart the process.
  • clearAdviserConstraints does not undo onlyStock — it drops the WASM-side preference, but the MKF Settings value persists. Pass {onlyStock: <previous>} explicitly if you need to revert.
  • list_topologies() returns 44 keys but my topology isn't there — some MKF converter models are stubs (e.g. raw CurrentTransformer, Cllllc skeleton) and are intentionally not registered. See the include list in src/libMagneticAdviser.cpp for the authoritative set.
  • Custom weights with fastMode=true — silently ignored (the fast path ranks by analytical losses internally). Use full mode if weights matter.
  • CMC / interference suppression inputs — full mode auto-detects Application::INTERFERENCE_SUPPRESSION and switches to the CMC filter flow (toroidal-only, impedance / leakage scoring). Fast mode does not.
  • ngspice 404 at build time — the MKF CMake tries to download ngspice from a dead sourceforge mirror. Copy a working _deps/ngspice/install from another WebLib build into build/_deps/ngspice/install/ to skip the download. See BUILD.md §2.
  • Settings mutations persist across callsSettings is a singleton, and settingsJson doesn't roll back. Track what you changed and reset it explicitly if you need a clean baseline.

Tests

node test/run.mjs              # 11-case smoke suite (default — all sections)
node test/run.mjs adviser      # only the direct adviser cases
node test/run.mjs topology     # only the topologyAdviser cases
node test/run.mjs constraints  # only the constraints+filter cases
node test/run.mjs simulate     # only the simulate / simulateMagnetic cases
node test/run_matrix.mjs       # full topologies × sizes sweep (long-running)

Exit code 0 means all selected cases returned valid {data: [...]} (or a populated MAS for simulate). Fixtures live in test/fixtures/; the matrix runner writes per-case CSV to test/results_fast.log.

Open bugs / triage notes from the matrix runs are tracked in HANDOFF.md (WASM-side) and MKF_ISSUES.md (MKF-side). Read these before chasing a failing topology — the bug is often already documented.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors