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:
- Creation.
Stock.create(initial, this)installs the launch value — evaluated once atinit()time (or re-evaluated on Reset if you passed a function). setup(). Callingstock.initialize(expr)insetup()re-installs the level from current parameters. Because the baseSDMain.reset()callssetup()after reinitializing the registered stores, this is the conventional place to make Reset honor the sliders — the Lotka model does exactly this.- Function initials. Passing a closure to
createachieves 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.