On this page

SDMain API reference

Manual contents | SDSir tutorial | Stocks, flows, and integration | Stock and Store reference

Source: sdmain.js. Inheritance: SDMain extends SDRoot extends APIRoot. Use this page to look up a contract; use the concepts chapter for a guided explanation and the tutorial for a complete worked model.

Scope: the instance members declared by SDMain, its model-facing runtime fields, and the inherited helpers a model author actually uses. Members that exist to serve the hosting page's message protocol are classified as runtime rather than recommended. NuML parameter values are noted where they enter the API; the notation itself is documented in the NuML User Manual.

1. Import, subclass, and availability

import {SDMain} from "@nwk/api/sdmain.js";
import {Stock} from "@nwk/api/stock.js";

class Model extends SDMain {
  constructor() {super();}

  init() {
    super.init();
    this.x = Stock.create(this.X0, this);
    this.x.flow = () => -this.K * this.x.get();
    this.setup();
  }

  setup() {
    super.setup();
    this.show("OUT", this.x.get());
  }

  update() {
    super.update();
    this.show("OUT", this.x.get());
  }

  get K()  {return this.getParam("K");}
  get X0() {return this.getParam("X0");}
}

export {Model}

The constructor wires the runtime pieces: a Clock, the stocks set, a ParamManager, and a DisplayManager. dt defaults to 1.0 until the Project's value is installed. Always call super() and, in every hook you override, the corresponding super.hook().

2. The Studio template regions

Studio generates SD Main files with numbered marker comments delimiting the regions it round-trips. The shipped apps keep them:

Markers Region Typical content
//1s … //1e Imports and globals import {Stock} from "@nwk/api/stock.js"
//2s … //2e Initializations (inside init) Stock.create, flow assignments, optional trailing this.setup()
//3s … //3e Local step (inside step) Discrete per-tick logic
//4s … //4e Local reset (inside reset) Clearing custom model state
//5s … //5e Local setup (inside setup) stock.initialize(...), initial show
//6s … //6e Local update (inside update) show calls
//7s … //7e Additional methods Auxiliaries and helpers
//8s … //8e User exports Extra exports

Below the regions, Studio emits one parameter getter per Project input. If you edit a generated file by hand, keep your code inside the marked regions so a round trip through Studio preserves it.

3. Lifecycle hooks

Hook Called when Put here
init() Once at app launch, from the runtime's initialize Stock.create, flow assignments, stopWhenTrue; optionally end with this.setup() so launch and Reset share one path
setup() From reset() — and at launch only if init() calls it Param-dependent stock.initialize(...), the initial show
step() Once per tick, before flows are evaluated Discrete per-tick logic; always call super.step() first
coordinate() From the base step() — Euler mode only, never under RK4 Stochastic draws and discrete transitions on Stores (pattern)
update() Once per tick, after commitment and the time advance Read stocks, show, log
reset() UI Reset Local reset of custom state; the base method reinitializes every registered store and stock, restarts the clock, then calls setup()
close() Declared no-op hook Teardown if a host calls it

Exact tick ordering, including the RK4 variant, is specified in the simulation cycle.

initialize(msg, delay, ws) is the runtime entry point, invoked by the dispatcher when the hosting page sends initSD: it installs parameters, display codes, dt, maxTime, and the RK4 flag, then calls your init(). Models override init(), not initialize.

4. Time, execution, and stopping

Member Kind Contract
currentTime getter The clock's current simulation time; in update() it is the just-completed time
dt field Integration step, installed from the Project; live dt changes are re-installed into every stock
maxTime field Stop time from the Project; the run halts once time reaches it (within dt/2)
stopWhenTrue setter A predicate checked after every tick; returning true halts the run. Example: this.stopWhenTrue = () => this.i.get() < 0.5;
halt() method Stops the clock immediately
delay getter/setter Milliseconds between ticks; driven by the UI speed control
run(ws) / tick(ws) runtime Continuous run / single step; invoked by the dispatcher for the UI's Run and Step buttons

Batch mode. With Project batch: true (or the UI's batch toggle) the clock runs bursts of 250 ticks back-to-back instead of waiting delay between ticks — the mode the Lotka app ships with. The clock also guards against runaway models: if simulation time becomes non-finite or stops advancing for 250 consecutive ticks, it reports a runtime error and halts.

5. Parameters

Member Kind Contract
getParam(icode) method Current value of the Project input with that code. Read it through a getter inside flows so live slider changes take effect immediately
getBParam(icode) method true when the parameter equals 1; for checkbox-style inputs
setParam(icode, param) runtime Applied by the dispatcher when a widget changes; "dt" is special-cased to re-install stock parameters and recompute the stop time
initParams(icodes, params) runtime Bulk installation at launch

A parameter whose string value begins with "numl" is parsed at installation into a compiled form ({code, ast}) rather than used as a plain value; parse errors are reported to the host as Numl Error(s). Authoring NuML programs is the subject of the NuML User Manual.

6. Displays and logging

Display codes come from the Project's displays; the DisplayManager buffers whatever you show and the runtime drains the buffer into the hosting page after every tick (and at launch, for anything shown during initialization).

Member Contract
show(dcode, ...values) Buffer one tuple for the display dcode; value order matches the display's legend
showArray(dcode, array) Same, passing an already-built array
log(dcode, ...msg) Append messages to a text display's pending buffer instead of replacing it
clear(dcode) Send the clear control token to a text display
createLogger(code) Returns a small Logger bound to one display code, with log(txt), clear(), and scrollToTop()

Call show from setup() for the initial point and from update() for the per-tick points, as in the tutorial. A show issued between ticks (for example from a flow) violates the purity contract and may be sampled multiple times under RK4.

7. Stocks and synchronized stores

Member Kind Contract
stocks field (Set) Every Stock.create(…, this) registers here; the clock integrates exactly this set
synchronize(...stores) method Enrolls stores in end-of-tick commitment: after this, assigning store.next marks the store touched, and all touched stores commit together. Stock.create and Store.create already call it; call it yourself only for stores you construct with new
touchedStores field (Set) The stores awaiting commitment this tick; drained by the runtime
shift() runtime Commits all touched stores; called by the clock each tick
installStockParams() runtime Pushes dt and the integration method into every stock at launch and on dt changes
isRK4() method true when the model runs RK4 (visitMode: "SDRK4")

The two-phase semantics of the stores themselves — get, next, shift, initial values — are specified in the Stock and Store reference.

8. Inherited utilities

From SDRoot: the stores set (plain Store.create registers here so Reset can reinitialize them) and the base reset() behavior.

From APIRoot, the static utilities — call them as APIRoot.name(...) (import APIRoot from @nwk/api/apiroot.js), not as methods on this:

Static Contract
random(x, y) Uniform in [0, x) with one argument, [x, y) with two
irandom(x, y) Integer uniform draw
normal(m, s) Normal variate, mean m, standard deviation s
bernoulli(p) / flip(p) 1 with probability p, else 0
poisson(lambda) Poisson variate
binomial(n, p) Binomial variate; exact for small n, approximated for large n
choose(...weights) Index drawn proportionally to weights
select(scale, ...probs) Index by cumulative probabilities
multinomial(N, ...probs) Vector of category counts
shuffle(array) In-place Fisher-Yates shuffle
distance(x, y, z, w) Euclidean distance
normalize(theta) Angle normalized to (-PI, PI]
degToRad / radToDeg, PI, PI2, … Angle conversion helpers and constants

These are shared with the agent-based runtime and are documented in more depth in the NL manual's NLRoot and APIRoot utilities chapter. Draws use the runtime's random sources; reproducible seeding is not part of this API.

9. Runtime protocol and internal inventory

These members exist so the hosting page can drive the model. They are listed so the inventory is complete, not because models should call them.

Member Classification
initialize(msg, delay, ws) Runtime entry for the initSD message; calls your init()
statusToJson(time) Builds the per-tick status message (display data, time, quit flag) and applies the stop checks
reply(ws) Sends the initial display data (sdinit) after launch
reset(ws) Runtime form of Reset; also the hook you override
installStockParams(), initParams, setParam Installation plumbing (sections above)
bumpMemoEpoch() Invalidates memoized NuML evaluations when state or parameters change
checkNuml(pval, ws) (static) Parses "numl"-prefixed parameter strings
getClock(), getParamManager(), getDisplayManager(), getTouchedStores() Accessors for runtime components
cws The connection the runtime replies on
close() Declared teardown hook (no-op)

For reference, the dispatcher messages that drive these members: initSD (launch), step, toggle (Run/Stop, batch flag), reset, params (widget change), simspeed, and history. See simdispatcher.js.

10. Verification

Contracts on this page are read directly from sdmain.js, clock.js, and simdispatcher.js at the time of writing, and cross-checked against the shipped sdsir and lotka applications. The guide tests execute this page's subclass example, verify the lifecycle, stopping, and display contracts, and check that this page inventories every declared member; see Publishing this guide for the verification commands.