Skip to content

Biotope and Double-Buffering

GridEnvironment is the canonical owner of PHIDS’s grid-aligned environmental state. It is where continuous fields, diffusion behavior, wind transport, and the most explicit double-buffering mechanics of the current engine are implemented.

This chapter documents the current implementation in src/phids/engine/core/biotope.py.

Role in the Engine

GridEnvironment provides the field side of the hybrid ECS + cellular-automata runtime. While ECSWorld stores discrete entities, GridEnvironment stores the continuous or grid-aggregated quantities that those entities read from and write to.

In current implementation it owns:

  • plant_energy_layer
  • _plant_energy_layer_write
  • plant_energy_by_species
  • _plant_energy_by_species_write
  • signal_layers
  • _signal_layers_write
  • toxin_layers
  • _toxin_layers_write
  • wind_vector_x
  • wind_vector_y
  • flow_field

Architectural Significance

PHIDS’s design rules repeatedly refer to double-buffering. In the current codebase, that principle is most concretely embodied in GridEnvironment.

This means GridEnvironment is not just a container for NumPy arrays. It is the subsystem that makes the engine’s buffered environmental semantics real.

Bounded Memory Discipline

The biotope enforces several hard constraints at initialization time:

  • 1 <= width <= GRID_W_MAX
  • 1 <= height <= GRID_H_MAX
  • 1 <= num_signals <= MAX_SUBSTANCE_TYPES
  • 1 <= num_toxins <= MAX_SUBSTANCE_TYPES

It also pre-allocates the species-specific plant-energy tensor with shape:

  • (MAX_FLORA_SPECIES, width, height)

This is the environmental expression of the Rule of 16. The engine does not dynamically resize these core buffers during a simulation tick.

State Layout

Aggregate plant-energy layer

plant_energy_layer is the read-visible aggregate plant-energy field consumed by later phases such as flow-field generation.

Per-species plant-energy tensor

plant_energy_by_species stores species-specific contributions, allowing the environment to retain species granularity while also exposing an aggregate field.

Signal and toxin layers

Signals and toxins are each stored as stacked layers of shape:

  • (num_signals, width, height)
  • (num_toxins, width, height)

This allows PHIDS to represent multiple airborne signals and multiple toxin fields in a vectorized way.

Wind fields

Wind is represented as two NumPy layers:

  • wind_vector_x
  • wind_vector_y

In the current implementation these are typically filled uniformly, but the abstraction also supports per-cell updates.

Flow field

flow_field is stored in the environment as a scalar guidance surface of shape (width, height). It is written by the flow-field phase and read by the interaction phase.

Double-Buffering Mechanics

Plant-energy writes

Plant-energy writes are not applied directly to the read-visible aggregate field.

Instead:

  • species-specific writes go to _plant_energy_by_species_write,
  • rebuild_energy_layer() aggregates those writes into _plant_energy_layer_write,
  • read and write buffers are swapped,
  • the write buffers are refreshed from the new read-visible values.

This is one of the clearest examples of PHIDS’s buffered-state discipline.

Signal diffusion writes

Signal diffusion reads from signal_layers, writes into _signal_layers_write, and then swaps the buffers.

Toxin diffusion writes

Toxin diffusion uses the same read/write swap pattern via toxin_layers and _toxin_layers_write.

Diffusion Model

The environment defines a precomputed Gaussian kernel through DIFFUSION_KERNEL, built by _make_gaussian_kernel().

Current signal diffusion procedure

diffuse_signals() currently performs, for each signal layer:

  1. compute the mean wind vector,
  2. convolve the layer with DIFFUSION_KERNEL using scipy.signal.convolve2d,
  3. advect by integer cell shifts using np.roll,
  4. zero values below SIGNAL_EPSILON,
  5. write into the signal write buffer,
  6. swap read and write buffers.

Current toxin diffusion procedure

diffuse_toxins() currently mirrors the same procedure for toxin layers.

This is a significant current-state fact: in the present implementation, toxins still pass through a convolution-and-roll diffusion helper, even though higher-level design intentions may treat toxins as more localized defenses.

Subnormal Float Mitigation

After convolution and wind shifting, both signal and toxin diffusion zero out values below SIGNAL_EPSILON.

This is not cosmetic cleanup. It is a performance invariant motivated by the cost of processing subnormal floating-point tails. In PHIDS, sparsity preservation is part of the computational model.

Wind Semantics

The environment currently supports two wind update modes:

  • set_uniform_wind(vx, vy) for full-field updates,
  • update_wind_at(x, y, vx, vy) for cell-local mutation.

The live REST interface primarily exercises the uniform update pathway.

Plant-Energy API

The current plant-energy helper methods are:

  • set_plant_energy(x, y, species_id, value)
  • clear_plant_energy(x, y, species_id)
  • rebuild_energy_layer()

A notable invariant is that set_plant_energy() clamps values to >= 0.0 before storing them in write buffers.

Serialization Role

GridEnvironment.to_dict() converts the current environment layers into list-backed structures suitable for msgpack serialization and WebSocket transport.

This makes GridEnvironment the backbone of the replay and streaming snapshot formats.

Read/Write Boundary in Practice

The current engine does not implement a universally duplicated whole-world state. Instead, it uses field-level double-buffering for the environment combined with deterministic phase ordering.

This is the most precise description of the present runtime model:

  • buffered environmental layers,
  • ordered ECS mutation passes,
  • synchronization at phase boundaries through rebuild/swap operations.

Evidence from Tests

The current test suite verifies key environmental properties.

Diffusion thresholding

tests/test_biotope_diffusion.py verifies that tiny signal concentrations are truncated to zero after diffusion.

Buffer-swap and invariants

tests/test_schemas_and_invariants.py verifies that:

  • invalid grid dimensions raise errors,
  • plant-energy writes become visible only after rebuild_energy_layer(),
  • clearing plant energy is reflected after a rebuild,
  • negative energy writes are clamped to zero.

Methodological Limits of the Current Biotope

The current environment model should be described precisely:

  • wind advection is represented by integer rolls of the convolved field,
  • diffusion is layer-based rather than particle-based,
  • toxins currently still use the diffusion helper,
  • double-buffering is strongest at the field level rather than as a full read/write universe clone.

These are important characteristics of the current scientific and computational model.

Verified Current-State Evidence

  • src/phids/engine/core/biotope.py
  • src/phids/engine/loop.py
  • tests/test_biotope_diffusion.py
  • tests/test_schemas_and_invariants.py