On this page

Stock and Store API reference

Manual contents | SDSir tutorial | Stocks, flows, and integration | SDMain reference

Sources: stock.js, store.js, integrator.js. Inheritance: Stock extends Store.

A Store is NuWebKit's two-phase value: a committed current value and a pending next value that the runtime commits in a synchronized pass at the end of each tick. A Stock is a store with an attached flow and an integrator — the code form of an SD stock. The concepts chapter explains when to use which; this page specifies the contracts.

1. Import and creation

import {Stock} from "@nwk/api/stock.js";
import {Store} from "@nwk/api/store.js";

// inside init() of an SDMain subclass:
this.s = Stock.create(this.N - this.IO, this);        // integrated stock
this.q = Stock.create(this.Q0, this, true);           // sequence stock
this.S = Store.create(() => this.N - this.I0, this);  // plain two-phase store

Always create through the static create(initial, main) factories inside init(). They register the object with the main class — stocks into main.stocks (the set the clock integrates and Reset reinitializes), plain stores into main.stores (reinitialized on Reset) — and enroll it in synchronized commitment via main.synchronize. A bare new Stock(...) is not integrated and a bare new Store(...) is not committed.

The initial argument may be a value or a function. A function is evaluated now and re-evaluated on every Reset, so Store.create(() => this.N - this.I0, this) automatically restarts from the current slider positions without any code in setup().

2. Store members

Member Kind Contract
Store.create(initial, main) static Construct, register with main.stores, synchronize
get() method The committed current value — what every flow and auxiliary should read
next setter/getter Assign the pending value; the assignment marks the store touched so it commits at the end of the tick
shift() runtime Commit: current = next, then notify listeners. Called by the runtime's synchronized pass
set(val) method Immediate assignment of both current and pending value, outside the two-phase cycle; notifies listeners. For host-driven overrides, not for use inside a tick
initialize(value) method Install a new committed value, pending value, and remembered initial (re-evaluating a function argument); notifies listeners
reinitialize() method initialize from the remembered initial; what Reset applies to every registered store
setUpdater(f) / autoNext() method Optional updater function; autoNext assigns next = f() when an updater is present
addListener(fn) method fn(store, oldValue, newValue) fires on commitment, set, and initialization
toString() method "<current, next>" for debugging

The two-phase discipline is what makes SD updates synchronous: within one tick, every get() returns the pre-tick value no matter how many stores have already computed their next. Assigning next twice in one tick simply overwrites the pending value; only the last assignment commits.

3. Stock members

Member Kind Contract
Stock.create(initial, main, isseq) static Construct, register with main.stocks, synchronize. isseq: true makes a sequence stock
flow setter/getter The stock's net-rate function — inflows minus outflows, the right-hand side of its ODE. setFlow(f) is the method form
get() inherited The committed level, safe to read inside any flow
initialize(value) method As Store's, and also clears any pending change
getInitial() method The value most recently installed by initialize
step() / integrate() runtime Evaluate the flow into the pending change (and advance the RK4 stage); called by the clock
shift(val) runtime Commit one step (see the mode table below); with an argument, installs a provisional RK4 stage value
installParams(dt, isRK4, stocks) runtime Receives dt and the integration method from SDMain.installStockParams
reset() / reinit() runtime Reset-time reinitialization; note reset() reinitializes every stock registered with the main class, not only this one — treat it as runtime plumbing and let SDMain.reset() drive it

A stock with no flow assigned holds its value: its pending change evaluates to zero (sequence stocks hold their current value instead). Exceptions thrown inside a flow are reported to the host as Numl Error(s).

4. Initialization and reset

Three mechanisms set a stock's level, in order of precedence during a run:

  1. Creation. Stock.create(initial, this) installs the launch value — evaluated once at init() time (or re-evaluated on Reset if you passed a function).
  2. setup(). Calling stock.initialize(expr) in setup() re-installs the level from current parameters. Because the base SDMain.reset() calls setup() after reinitializing the registered stores, this is the conventional place to make Reset honor the sliders — the Lotka model does exactly this.
  3. Function initials. Passing a closure to create achieves the same effect declaratively, as in the stochastic SIR pattern.

On Reset the runtime reinitializes every registered store and stock from its remembered initial, restarts the clock at time zero, and then calls your setup(). Values assigned mid-run with set() or next do not survive it.

5. Commitment semantics by mode

What shift() commits depends on how the stock was created and which integrator the model runs:

Mode Pending change (step) Committed value (shift)
Euler stock (visitMode: "SD") next = dt * flow() current + next — forward Euler
RK4 stock (visitMode: "SDRK4") next = flow(), staged k1…k4 current + k1/6 + k2/3 + k3/3 + k4/6 after the fourth stage; the intermediate shifts install provisional stage values
Sequence stock (isseq: true) next = flow() next — replaced, not accumulated, never scaled by dt
Plain Store whatever you assign to next next — replaced

Sequence stocks implement difference equations x(t+1) = f(…) with the same registration, synchronization, and reset behavior as integrated stocks.

6. Integrator internals

Each stock owns an Integrator that runs the classic RK4 stages when visitMode: "SDRK4":

Stage Flow evaluated at Produces
K0 committed state, time t k1 = dt * rate; provisional state x + k1/2
K1 provisional state, t + dt/2 k2; provisional state x + k2/2
K2 provisional state, t + dt/2 k3; provisional state x + k3
K3 provisional state, t + dt k4; commit x + k1/6 + k2/3 + k3/3 + k4/6

The clock drives all stocks through the stages together, so cross-stock flow expressions see a consistent provisional system state at every stage. This is internal machinery: models interact with it only through flow purity (see Euler and RK4 integration).

7. Verification

Contracts on this page are read directly from stock.js, store.js, and integrator.js at the time of writing, and cross-checked against the shipped sdsir and lotka applications and the Studio model library's StocSir.