Phase 2 brings Models to life. ADR-0004: behaviour comes from a small fixed vocabulary of rules (Constant / Proportional / Gap) read over the inbound Information Links, not free-form formulas — valid by construction. - types: Rule union, SimSpec, initialValue; replaces the unused equation field - simulation: forward-Euler engine, dependency-ordered evaluation, algebraic-loop detection, non-negative stocks, and a divergence guard - io: validate and round-trip the new fields (F8)
4.5 KiB
Behaviour comes from rules over Information Links, not free-form formulas
Part of meadows · see DESIGN.md.
Numeric simulation (phase 2) makes a Model alive: Stocks accumulate over time, Flows and Converters recompute each instant. Two coupled decisions shape how a Model carries the numbers, and both trade expressive power for valid by construction — the right trade for a tool that exists to popularise systems thinking, not to compete with Vensim.
Decision
1. The Information Link is the declared dependency. A Flow's or Converter's inputs are exactly the elements that link into it. There is no separate "equation references" namespace to keep in sync — the wiring you draw is the wiring the simulator reads. The same signed graph the loop detector walks (ADR-0001) is the graph the simulator integrates, so the loops you see classified R/B are the loops you run. They can never disagree.
2. A Flow/Converter computes from a small fixed vocabulary of Rules, not a
typed-in formula. Each instantaneous element picks one rule and a plain number
or two — never an expression:
| Rule | Value | Reads (via Information Links) | Emergent behaviour |
|---|---|---|---|
| Constant | a fixed number | nothing | linear Stock change |
| Proportional | factor × (its + inputs) |
the +-polarity inputs |
exponential growth / decay |
| Gap | factor × (level − target) |
the + input is level, − is target |
goal-seeking / asymptotic |
The famous curves are compositions of these over the structure — a logistic S-curve is Proportional growth meeting a Gap-driven ceiling (limits-to-growth); goal-seeking decay is a lone Gap (coffee cooling). The user sets up a local rule; the global shape emerges. That emergence is the lesson.
Polarity does double duty. The +/− already captured for loop
classification (ADR-0001) also selects each operand's role: Proportional reads
its + inputs; Gap reads its + input as the level and its − input as the
target. One gesture, two payoffs — no new per-link data.
Considered Options
- Free-form expression strings (
birth rate = Population × fertility) — maximally expressive, and whatequation?: stringoriginally anticipated. Rejected: needs a parser + a sandbox (nevereval/new Function), invites broken-formula and name-resolution errors (auto-names contain spaces), and lets a learner paint a curve instead of discovering it from structure. - Pick the output curve (label a Stock "exponential" / "logarithmic") — rejected: it is the answer, not the cause, and it breaks the moment feedback decides the shape. "Logarithmic" in particular has no honest local rule; what people mean by it is asymptotic approach — which is the Gap rule.
- Rules over Information Links (chosen) — no parser, valid by construction, and it teaches structure → behaviour.
Consequences
- The domain types gain a
Ruleunion on Flow/Converter (replacing the unusedequation?: string), an optionalinitialValueon Stock, and an optionalSimSpec(start/stop/dt) on the Model. All optional and additive, so existing saved Models still load (F8); they are simply not simulatable until equipped. - Algebraic loops are an error. A cycle in the wiring is legitimate feedback iff it passes through a Stock — the Stock supplies last-step state and so breaks the within-step dependency. A cycle among only Flows/Converters has no Stock to break it: the simulator cannot order it and rejects it. The simulator reuses the cycle machinery to detect this; it is a new sim-readiness check, distinct from structural validity (validation.ts).
- A reader seeing
{ kind: "gap", factor }on a Flow and wondering where its operands come from should look here: they are the Flow's inbound Information Links, picked by Polarity. - The vocabulary starts deliberately small (Constant / Proportional / Gap —
enough for linear, exponential, and goal-seeking, and for the coffee and
savings samples). Growing it is additive: a new
kindin the union plus a case in the evaluator. Multi-input products (e.g.Population × fertility) are a later increment, not a phase-2 blocker.