On this page

Checking and troubleshooting a model

Manual contents | Previous: Grazers and food | Next: Quick reference and glossary | Compatibility and known limitations

Start with one small, predictable case. A realistic-looking animation is not evidence that the rules, timing, or conservation arithmetic are correct. All advice here assumes lifecycle: "aligned".

Find the first failing stage

Work down this list. Stop at the first mismatch rather than changing several rules at once.

  1. Does the app load? If not, save all four definitions and fix the first syntax/import/runtime exception. A later error may only be a consequence.
  2. Is time-zero state correct? Check Project, constructors, defaults, init, and setup. Do not investigate movement until the starting world is right.
  3. Does one Step advance time? If not, inspect the first exception and clock configuration. Do not add a timer or manually call step/shift hooks.
  4. Does the intended rule execute? Check visit mode and patch activation. One temporary log at the rule entrance distinguishes "not scheduled" from "scheduled but calculating the wrong result."
  5. Is the proposed result correct? Inspect input values, current state, pending state, units, and assignment order.
  6. Is the committed result correct? Custom Stores need synchronization; immediate and pending writes have different meanings.
  7. Is only the picture/chart wrong? Compare numeric state with appearance and published series before changing the scientific rule.
  8. Does it fail only after Reset? Check reset ownership, cached references, repeated registrations, and whether setup reconstructs derived state.

Use the quick-reference tables while checking each stage. Pause before editing; save and relaunch after code or Project changes. A live slider change is not the same operation as loading new code.

A short verification routine

  1. Startup: inspect time zero, population, colors, input defaults, chart values, and one representative turtle/patch.
  2. One tick: write down what it should read, propose, and commit. Step once. Compare the result, not only its appearance.
  3. Boundary: move/query near each edge, and check a corner or tiny wrapped world where duplicate neighbors would matter.
  4. Inputs: change each live input; also test zero/min/max where supported. Distinguish changes taking effect now, next tick, reset, or relaunch.
  5. Reset twice: run, change inputs, reset, step, and reset again. Check custom state, current slider values, initial outputs, and listener counts.
  6. Population changes: if used, test zero agents, one birth/death, setup of newborns, and eventual removal from local queries.
  7. Randomness: check a deterministic case first. Reset restores saved starts, not the random stream; matching every future random path is not a reset test.

The fire, Walkers, and Grazers tutorials include small expected-result tables. Keep such a table with your model's own tests.

Common symptoms

Symptom Likely checks
App never connects or has a JavaScript error Save All; inspect the browser console; check import/export names and generated shell wiring; solve the first exception before changing rules
Empty/incorrect initial state but Reset helps Confirm aligned flag, remove constructor init/setup calls, move population-dependent work to setup; do not make a reset-before-run ritual
Only background appears Check Project agent, exported Turtle, Main initAgents, count/start array, and agent palette/size
Patch rules do nothing Check visitMode includes C; register active patches using visitAlways in setup; do not confuse drawing with activation
Turtle rules do nothing Check visitMode includes A and Main initialized the population
Turtle steps but never moves Check movement mode, live speed, and super.turtleStep; Next_Location requires a destination proposal
Turtle turns but movement seems delayed Base heading updater runs after scheduling movement; proposal order determines which move uses it
Movement is too large or points the wrong way Coordinates are not pixels; theta is radians, zero east, y grows downward
Updater never runs Assign a function, keep aligned base-step call; evaluate custom Store updaters explicitly
Updater runs twice Remove old manual built-in autoNext calls; check duplicate patch activation paths
Current value does not change immediately next assignments commit at shift; inspect current after a full Step
Update order changes the answer Look for ordinary fields, immediate setters, or intentional pending-state claims; distinguish them from synchronous rules
Resource becomes negative or is created Specify growth/claim order; cap consumption by remaining pending availability; check accounting, not just color
Query unexpectedly includes center block includes center; use blockX/ring when appropriate; turtle nhood queries exclude the calling turtle separately
Query misses turtles Use committed occupancy with a working agent index; dying/newborn-hidden turtles are excluded; inspect the query's selected patches
Radius result differs from a grid ring Euclidean radius and square/hex graph distance are different; see the neighborhood reference
Neighborhood call throws RangeError Grid distances must be nonnegative safe integers; radii finite/nonnegative; nhood4 requires square topology
Chart is empty or wrong shape Match exact code/site, legend length, scalar/series array; use showList for a prebuilt series array; publish in setup and update
Slider moves but model ignores it Match code/case; read it in model code; distinguish live/initial/reset-only input policies
Population count slider does not resize Initial count configuration is not a live resizing command; use designed birth/death rules or relaunch
Speed default 0 becomes 1 Legacy numeric default fallback remains; the tutorials explicitly apply current speed in setup and pending speed each step
Custom state survives Reset unexpectedly Give each owner an explicit local reset policy; synchronize only commits, it does not define reset
Listeners accumulate after Reset Wire once in init/constructor; setup repeats; clean registrations owned by discarded turtles
Saved turtle references stop working World reset recreates turtles; rebuild references in setup rather than using old objects
Colored/invisible turtle still exists Color/visibility is not death; use an explicit rule and die when removal is intended
Birth does not immediately change the count Birth insertion/setup is deferred until shift; local visibility can begin later

What belongs in the console?

Use temporary console.log statements for inspection, not a second model loop. For example, inside update:

console.log("tick", this.currentTime, "population", this.turtles.size);

This fragment assumes an agent world. For a patch-only world, inspect this.patches.toList().length instead. Log both current and pending custom Store values when diagnosing phase order. The browser console is distinct from a configured model logger display. Do not conceal an exception with a broad catch, return a success-shaped empty value, or silently change the intended rule just to keep the picture moving.

Inspect the right phase

Inspection point What it can tell you
Constructor Custom allocations exist; runtime defaults may not yet be assigned
Init Context/defaults and one-time registrations; Main populations are not ready
Setup Initial/reset world after populations are ready
Coordinate Committed state before this tick's individual rules
Inside an individual rule Current state and proposals made so far, not a completed tick
Update Committed results after the clock advances

Log primitive values such as patch.food.get(), not just a whole mutable object: browser consoles may expand an object using values it acquired later. Record the time and object id or patch coordinates alongside each number. Remove noisy diagnostic logs once the small case agrees with your prediction.

Worked diagnosis: food disappears, but the turtle does not gain energy

Use the stationary Grazers check, not a randomly moving population. Its first tick should change occupied-patch food from 4 to 2 and turtle energy from 4 to 5.

Trace these questions in order:

  1. Which patch was eaten? The rule uses myPatch() before movement. Inspecting only the destination patch can make a correct result look wrong.
  2. How much was granted? Record the eaten result from takeFood. BITE is a maximum request, not a promise of that much food.
  3. What energy was proposed? With energy 4 and eaten 2, 4 - 1 + 2 is 5, not 6: the tick has a cost.
  4. Was energy registered? The custom Store needs this.main.synchronize(this.energy) in turtle init. A correct energy.next with unchanged energy.get() after a completed tick suggests a commitment problem, not an eating problem.
  5. Was the chart published after commitment? Main update reads the new energy; coordinate reads the old energy.
  6. Does only color disagree? Grazers' color updater reads energy.next inside the step and needs the aligned base turtle step to evaluate it.

Do not "fix" the symptom by adding a second energy assignment in Main, calling Store shift yourself, or removing the cost. Repair the first broken link in the intended calculation.

Worked diagnosis: Reset works once, then behaves differently

Compare these checkpoints across startup and two resets:

Check Expected ownership
Main and patch objects Same objects survive
Custom patch Store Same Store, restored by patch reset
Turtle objects and their Stores New objects after each world reset
Persistent patch activation Same selected patches; no duplicate Set entries
Listeners on surviving objects No extra copies installed by repeated setup
Cached turtle references Rebuilt in setup, pointing to replacements
Sliders Current values retained, not restored to Project defaults
Initial chart values Published again by setup

If a model's starting state depends on an input, specify whether reset should use the current input or a saved baseline. Both policies can be meaningful, but they require different code. Reset is not a request to rewind randomness. See Main reset ownership and turtle lifecycle.

Keep a small evidence record

For each repair, record a few rows like these in your own test notes:

Stage Expected Actual
Startup Population and state from the configured start Measured values
One Step Hand-calculated state and time Measured values
Input change + Step Only the intended rule changes Measured values
Reset + Step Restored owner state with current input policy Measured values
Second Reset Same ownership/registration behavior Measured values

When randomness is not the subject of the check, use explicit starts and temporarily remove random turns, as in the moving Grazers experiment. Keep a known-good small case while you restore larger populations and randomness.

When asking for help

Include Project lifecycle, visit mode, topology/boundaries, the smallest reproducible start, relevant rule/reset hooks, expected versus actual values, the first exception with its stack, and whether the problem occurs initially, after a tick, or after reset. State whether you edited a standalone app or Studio model. Avoid including private data or credentials.

Separate compatibility from scientific rules

The flag does not migrate a legacy app by itself. Follow the freshening checklist for an explicitly selected migration. Remaining app migrations are deferred. Known movement helper caveats are listed in Programming turtles; they are not limitations of the repaired neighborhood operators.