Python API Reference
This page exposes the current PHIDS Python API using mkdocstrings.
Schemas and ingress
phids.api.schemas.base
Shared base model and annotated species-id aliases.
All schema submodules import StrictBaseModel, SpeciesId, HerbivoreId,
and SubstanceId from here to avoid repetition and ensure consistent Rule-of-16
bound application across the entire ingress boundary.
StrictBaseModel
phids.api.schemas.ecs
Pydantic schemata mirroring ECS component state for REST and WebSocket serialisation.
These models support inspection endpoints and state queries. They are never used
to parameterise engine construction - see phids.api.schemas.simulation for that.
PlantComponentSchema
Bases: StrictBaseModel
Pydantic schema for the Plant ECS component.
Source code in src/phids/api/schemas/ecs.py
SubstanceComponentSchema
Bases: StrictBaseModel
Pydantic schema for a Substance (signal or toxin) ECS component.
Source code in src/phids/api/schemas/ecs.py
SwarmComponentSchema
Bases: StrictBaseModel
Pydantic schema for the Herbivore Swarm ECS component.
Source code in src/phids/api/schemas/ecs.py
phids.api.schemas.conditions
Activation condition schemas for the compound chemical-defense trigger tree.
This module defines a recursive algebraic type tree composed of five node types,
discriminated by their kind literal field. The ConditionNode union is a
PEP 695 type alias that references both AllOfConditionSchema and
AnyOfConditionSchema - both of which in turn reference ConditionNode in
their conditions fields. This forward-reference cycle requires all five schemas
and the alias to live in the same module, and the two composite schemas must call
model_rebuild() after the alias is defined.
AllOfConditionSchema
Bases: StrictBaseModel
Boolean AND over nested activation predicates.
Source code in src/phids/api/schemas/conditions.py
AnyOfConditionSchema
Bases: StrictBaseModel
Boolean OR over nested activation predicates.
Source code in src/phids/api/schemas/conditions.py
EnvironmentalSignalConditionSchema
Bases: StrictBaseModel
Leaf predicate requiring a minimum ambient signal concentration at the owner's cell.
Source code in src/phids/api/schemas/conditions.py
HerbivorePresenceConditionSchema
Bases: StrictBaseModel
Leaf predicate requiring a herbivore species at the owner's cell.
Source code in src/phids/api/schemas/conditions.py
SubstanceActiveConditionSchema
Bases: StrictBaseModel
Leaf predicate requiring another substance to already be active.
Source code in src/phids/api/schemas/conditions.py
phids.api.schemas.triggers
Trigger action schemas and the TriggerConditionSchema interaction-matrix entry.
TriggerAction is a discriminated union of two concrete action types:
SynthesizeSubstanceAction (active chemical defense) and
ResourceWithdrawalAction (apparent nutrition reduction / stress response).
TriggerConditionSchema maps a (plant, herbivore) species pair to an action
and an optional compound activation-condition predicate tree.
EnvironmentalSignalInitiator
Bases: StrictBaseModel
Initiator that triggers when an environmental signal reaches a concentration threshold.
Source code in src/phids/api/schemas/triggers.py
HerbivoreAttackInitiator
Bases: StrictBaseModel
Initiator that triggers when a herbivore population reaches a threshold.
Source code in src/phids/api/schemas/triggers.py
PassiveDefensesSchema
Bases: StrictBaseModel
Morphological (passive) defenses of a flora species.
Source code in src/phids/api/schemas/triggers.py
ResourceWithdrawalAction
Bases: StrictBaseModel
Action to trigger apparent nutrition withdrawal (stress response).
Source code in src/phids/api/schemas/triggers.py
SynthesizeSubstanceAction
Bases: StrictBaseModel
Action to synthesize a specific chemical substance.
Source code in src/phids/api/schemas/triggers.py
TriggerConditionSchema
Bases: StrictBaseModel
Trigger condition for defense actions (Interaction Matrix entry).
Maps an initiator (e.g. herbivore presence, environmental signal) to an action that should be executed when the trigger conditions are met.
Source code in src/phids/api/schemas/triggers.py
phids.api.schemas.species
Per-species parameter schemas for flora and herbivore species, plus the diet matrix.
FloraSpeciesParams and HerbivoreSpeciesParams parameterise SimulationLoop
construction. DietCompatibilityMatrix enforces Rule-of-16 shape bounds on both
dimensions of the herbivore-flora edibility matrix.
DietCompatibilityMatrix
Bases: StrictBaseModel
Boolean matrix [herbivore_species, flora_species] indicating edibility.
Source code in src/phids/api/schemas/species.py
FloraSpeciesParams
Bases: StrictBaseModel
Per-species parameters for flora.
Source code in src/phids/api/schemas/species.py
HerbivoreResistancesSchema
Bases: StrictBaseModel
Herbivore resistances to passive plant defenses.
Source code in src/phids/api/schemas/species.py
HerbivoreSpeciesParams
Bases: StrictBaseModel
Per-species parameters for herbivore swarms.
Source code in src/phids/api/schemas/species.py
phids.api.schemas.placement
Initial placement and procedural placement strategy schemas.
InitialPlantPlacement and InitialSwarmPlacement define manually positioned
entities at simulation start. PlacementStrategy is a discriminated union of
three procedural seeding algorithms used when placement_mode = "procedural".
BandedPlacement
Bases: StrictBaseModel
Entities placed in dense lines/stripes.
Source code in src/phids/api/schemas/placement.py
ClusteredPlacement
Bases: StrictBaseModel
Groups of entities clustered around random centroids.
Source code in src/phids/api/schemas/placement.py
InitialPlantPlacement
Bases: StrictBaseModel
Single plant to place at simulation start.
Source code in src/phids/api/schemas/placement.py
InitialSwarmPlacement
Bases: StrictBaseModel
Single swarm to place at simulation start.
Source code in src/phids/api/schemas/placement.py
phids.api.schemas.simulation
Authoritative simulation configuration schema.
SimulationConfig is the single validated ingress container for all data that
parameterises SimulationLoop construction. Its model_validator enforces
cross-field reference integrity between placement species identifiers and the
declared species lists before any engine state is allocated.
SimulationConfig
Bases: StrictBaseModel
Complete simulation configuration payload (REST /api/scenario/load body).
Source code in src/phids/api/schemas/simulation.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
phids.api.schemas.responses
REST API response and request payload schemas.
These models are used exclusively at the HTTP boundary and carry no engine-internal
state. They are never passed into SimulationLoop construction.
BatchJobState
Bases: StrictBaseModel
Runtime state record for a single Monte Carlo batch simulation job.
Each batch job corresponds to N independent simulation runs dispatched
to a :class:concurrent.futures.ProcessPoolExecutor. Progress is tracked as
completed run count relative to the total, and the final aggregate summary is
persisted to disk for retrieval via the ledger and view endpoints.
Attributes:
| Name | Type | Description |
|---|---|---|
job_id |
str
|
Universally unique identifier assigned at job creation. |
status |
Literal['queued', 'running', 'done', 'failed']
|
Lifecycle state of the job. |
completed |
int
|
Number of runs that have completed (successfully or not). |
total |
int
|
Total number of runs requested. |
scenario_name |
str
|
Display label derived from the source scenario config. |
started_at |
str
|
ISO-8601 timestamp of job creation. |
finished_at |
str | None
|
ISO-8601 timestamp of completion, or |
max_ticks |
int
|
Maximum tick count per individual run. |
Source code in src/phids/api/schemas/responses.py
BatchStartPayload
Bases: StrictBaseModel
HTTP request payload for initiating a Monte Carlo batch simulation job.
Encapsulates the simulation scenario and batch execution parameters
submitted via POST /api/batch/start. The scenario field accepts a
complete :class:SimulationConfig that overrides the current server-side
draft, enabling fully reproducible parameterized batch studies.
Attributes:
| Name | Type | Description |
|---|---|---|
runs |
int
|
Number of independent simulation runs to execute in parallel. |
max_ticks |
int
|
Maximum simulation tick count per run. |
scenario_name |
str
|
Optional display label for the ledger. |
Source code in src/phids/api/schemas/responses.py
SimulationStatusResponse
Bases: StrictBaseModel
Response model for simulation state queries.
Source code in src/phids/api/schemas/responses.py
TickRateUpdatePayload
Bases: StrictBaseModel
REST payload for dynamically updating live simulation tick speed.
Source code in src/phids/api/schemas/responses.py
phids.io.scenario
Scenario I/O helpers for loading, validating, and serialising SimulationConfig instances.
This module provides three convenience functions that bridge external JSON representations of
simulation scenarios and the Pydantic-validated SimulationConfig model. Validation is
performed by :func:load_scenario_from_dict via SimulationConfig.model_validate, which
enforces all Rule-of-16 cardinality bounds, species-placement reference integrity, diet-matrix
shape constraints, and termination-threshold range checks before any engine state is allocated.
Any violation raises a pydantic.ValidationError, preventing malformed configuration data from
reaching the GridEnvironment or ECSWorld constructors.
:func:load_scenario_from_json reads a UTF-8 encoded JSON file, decodes it with the standard
library json module, and delegates to :func:load_scenario_from_dict. :func:scenario_to_json
serialises a validated configuration back to canonical JSON using Pydantic's model_dump_json
method, which preserves all default values and respects custom field serialisers defined in the
schema layer. Optionally, the output may be written to a file path, enabling scenario export from
the REST API and from command-line scripting workflows.
load_scenario_from_dict(data: JSONMapping) -> SimulationConfig
Parse and validate a simulation configuration from a mapping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
JSONMapping
|
Raw configuration mapping (typically decoded from JSON). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SimulationConfig |
SimulationConfig
|
Validated Pydantic configuration instance. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If the configuration is invalid. |
Source code in src/phids/io/scenario.py
load_scenario_from_json(path: str | Path) -> SimulationConfig
Load and validate a simulation configuration from a JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to the JSON scenario file. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SimulationConfig |
SimulationConfig
|
Validated Pydantic configuration instance. |
Source code in src/phids/io/scenario.py
scenario_to_json(config: SimulationConfig, path: str | Path | None = None) -> str
Serialise a SimulationConfig to a JSON string or file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
SimulationConfig
|
Configuration to serialise. |
required |
path
|
str | Path | None
|
Optional file path to write the JSON to. If |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
JSON representation of the configuration. |
Source code in src/phids/io/scenario.py
API and UI state surfaces
phids.api.main
FastAPI composition root governing live runtime state and API boundary coordination.
This module defines the canonical PHIDS application object and the mutable singleton references that
bind operator actions to a live SimulationLoop instance. The implementation mediates the
transition between draft-state configuration and executable simulation state, and wires router modules
that partition control, telemetry, and builder responsibilities. WebSocket transport loops are delegated to
dedicated manager classes, while this module retains ownership of endpoint registration and runtime
state access.
The architecture enforces the central methodological invariant of the PHIDS interface layer: editable draft configuration remains distinct from live ecological state until explicit load operations occur. This separation preserves deterministic replayability of trophic interaction, signal propagation, and metabolic attrition trajectories.
log_http_requests(request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response
async
Record request-latency diagnostics for interactive API and UI surfaces.
The middleware emits lightweight observability events for HTMX and REST traffic so operator workflows can be correlated with backend latency and error rates. The policy is asymmetric by design: client/server failures are elevated to warning level, whereas successful paths are kept at debug level to avoid telemetry inflation under nominal workloads.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Request
|
Incoming HTTP request object. |
required |
call_next
|
Callable[[Request], Awaitable[Response]]
|
Downstream ASGI callable that resolves the response. |
required |
Returns:
| Type | Description |
|---|---|
Response
|
Response returned by downstream middleware and route handling. |
Source code in src/phids/api/main.py
simulation_stream(websocket: WebSocket) -> None
async
Delegate binary simulation streaming to the simulation stream manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
Connected client socket endpoint. |
required |
Notes
The manager enforces msgpack+zlib encoding, tick-synchronous emission, and policy-close semantics when no live scenario is loaded.
Source code in src/phids/api/main.py
ui_cell_details(x: int, y: int, expected_tick: int | None = None) -> JSONResponse
async
Return rich grid-cell details for dashboard tooltips.
When a live simulation exists, data is sourced from the current ECS world and environment layers. Otherwise the endpoint returns draft-preview payloads so the placement editor can expose deterministic pre-runtime inspection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
expected_tick
|
int | None
|
Optional optimistic-concurrency marker from UI polling state. |
None
|
Returns:
| Type | Description |
|---|---|
JSONResponse
|
JSON response containing either live cell diagnostics or draft-preview details. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
Upstream presenter validation rejects out-of-bounds coordinates. |
Source code in src/phids/api/main.py
ui_status_badge() -> HTMLResponse
async
Return a small status <span> for HTMX outerHTML swap.
Returns:
| Type | Description |
|---|---|
HTMLResponse
|
Styled |
Source code in src/phids/api/main.py
ui_stream(websocket: WebSocket) -> None
async
Delegate live UI JSON streaming to the UI stream manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
Connected client socket endpoint. |
required |
Notes
The manager polls live-loop availability, emits payloads only on state-signature change, and applies tick-rate cadence constraints.
Source code in src/phids/api/main.py
ui_tick() -> Response
async
Return the current tick as plain text for HTMX innerHTML swap.
Returns:
| Type | Description |
|---|---|
Response
|
Plain-text tick content for lightweight polling updates. |
Source code in src/phids/api/main.py
phids.api.ui_state
UI state data models and draft configuration.
phids.api.websockets.manager
Connection managers for deterministic PHIDS WebSocket transport.
This module isolates asynchronous WebSocket stream orchestration from the FastAPI composition root.
The managers preserve protocol-specific invariants while reducing route-level control flow in
phids.api.main: the simulation stream emits msgpack+zlib binary snapshots keyed to simulation
ticks, and the UI stream emits compact JSON payloads keyed to rendered state signatures. By keeping
loop progression checks, payload encoding, sleep cadence, and disconnect handling in dedicated
classes, the runtime maintains strict draft-versus-live boundaries while exposing low-latency
observability of ecological dynamics.
DSEStreamManager
Connection manager for real-time Design Space Exploration UI updates.
Broadcasts NSGA-II Pareto Front generation payloads directly to the active
HTMX client connection over /ws/dse/stream.
Source code in src/phids/api/websockets/manager.py
__init__() -> None
broadcast_dse(payload: dict[str, Any]) -> None
async
Broadcast a JSON DSE payload to all connected clients.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload
|
dict[str, Any]
|
JSON-serializable dictionary containing generation metrics. |
required |
Source code in src/phids/api/websockets/manager.py
connect_dse(websocket: WebSocket) -> None
async
Accept and register a new DSE websocket connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
The WebSocket connection to establish. |
required |
Source code in src/phids/api/websockets/manager.py
disconnect_dse(websocket: WebSocket) -> None
Remove a DSE websocket connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
The WebSocket connection to terminate. |
required |
Source code in src/phids/api/websockets/manager.py
SimulationStreamManager
Manage binary simulation-state stream connections.
The manager enforces the machine-facing stream contract for
/ws/simulation/stream: payloads are derived from SimulationLoop snapshots,
serialized with json, compressed with zlib, and sent only when the tick changes.
A single cached compressed payload is retained per (loop identity, tick) pair to
avoid redundant encoding work under multiple subscribers.
Attributes:
| Name | Type | Description |
|---|---|---|
_cache_loop_id |
Identity of the loop instance used for the current cache entry. |
|
_cache_tick |
Tick number represented by the cached payload. |
|
_cache_payload |
Compressed binary frame for the cached loop/tick pair. |
Source code in src/phids/api/websockets/manager.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
__init__() -> None
handle_connection(websocket: WebSocket, loop: SimulationLoop | None) -> None
async
Handle one client connection for the binary simulation stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
Accepted socket client. |
required |
loop
|
SimulationLoop | None
|
Active simulation loop at connection time. |
required |
Notes
The stream is intentionally rejected when no live simulation exists. This prevents clients from observing ambiguous placeholder transport states and keeps runtime semantics explicit.
Source code in src/phids/api/websockets/manager.py
UIStreamManager
Manage lightweight UI dashboard stream connections.
The manager enforces rendering-oriented stream behavior for /ws/ui/stream. It polls the
active loop provider, emits JSON payloads only when a visible state signature changes, and uses
the configured tick rate to regulate cadence. This preserves responsive visual telemetry while
avoiding redundant frame emission during static states.
Attributes:
| Name | Type | Description |
|---|---|---|
_payload_builder |
Callable that assembles dashboard payload dictionaries. |
Source code in src/phids/api/websockets/manager.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
__init__(payload_builder: Callable[[dict[str, Any]], dict[str, Any]], snapshot_extractor: Callable[[SimulationLoop], dict[str, Any]] | None = None) -> None
Initialize the UI stream manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload_builder
|
Callable[[dict[str, Any]], dict[str, Any]]
|
Function mapping a snapshot to a JSON dictionary. |
required |
snapshot_extractor
|
Callable[[SimulationLoop], dict[str, Any]] | None
|
Function mapping a loop to a snapshot dictionary. |
None
|
Source code in src/phids/api/websockets/manager.py
handle_connection(websocket: WebSocket, get_loop: Callable[[], SimulationLoop | None]) -> None
async
Handle one client connection for the UI JSON stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket
|
WebSocket
|
Accepted socket client. |
required |
get_loop
|
Callable[[], SimulationLoop | None]
|
Callable returning the currently active simulation loop. |
required |
Notes
The connection remains open while no loop is loaded. This allows browser clients to subscribe once and begin receiving payloads immediately after a scenario is loaded.
Source code in src/phids/api/websockets/manager.py
API Presenters
phids.api.presenters.dashboard.shared
Pure structural utility helpers for the UI dashboard presenters.
Provides deterministic coercion, coordinate validation, and fallback rendering logic used across multiple presenter domains (mycorrhizal, substances, cell details).
validate_cell_coordinates(x: int, y: int, width: int, height: int) -> None
Assert that a pair of cell coordinates falls within the simulation grid bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
Column index to validate. |
required |
y
|
int
|
Row index to validate. |
required |
width
|
int
|
Total grid width. |
required |
height
|
int
|
Total grid height. |
required |
Raises:
| Type | Description |
|---|---|
HTTPException
|
Raises HTTP 404 with a descriptive detail message if the coordinates are out of bounds. |
Source code in src/phids/api/presenters/dashboard/shared.py
phids.api.presenters.dashboard.payloads
Dashboard presenter for full telemetry/UI payload.
Assembles and serializes the complete live dashboard state (flora/swarm populations, environmental layers, mycorrhizal root connections) streamed to the UI client.
build_live_dashboard_payload(snapshot: dict[str, Any], *, substance_names: dict[int, str]) -> dict[str, object]
Assemble the full JSON payload streamed to the browser canvas over the UI WebSocket.
This function constructs the authoritative rendering payload consumed by
/ws/ui/stream. It collects and serialises data from a pre-extracted snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snapshot
|
dict[str, Any]
|
The extracted thread-safe dictionary snapshot of the loop state. |
required |
substance_names
|
dict[int, str]
|
Mapping from substance identifier to display name. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, object]
|
A dictionary conforming to the full canvas payload schema. |
Source code in src/phids/api/presenters/dashboard/payloads.py
extract_ui_snapshot(loop: SimulationLoop) -> dict[str, Any]
Extract a fast, thread-safe shallow copy of UI-required state.
This function runs synchronously while holding the simulation lock. It returns a dictionary containing primitive values, copied NumPy arrays, and lightweight dicts representing the components needed for UI streaming.
Source code in src/phids/api/presenters/dashboard/payloads.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | |
phids.api.presenters.diagnostics.model
Diagnostics model context presenters.
EnergyDeficitSwarmRow
Bases: TypedDict
One leaderboard row describing a swarm with positive metabolic energy deficit.
Source code in src/phids/api/presenters/diagnostics/model.py
LiveSummary
Bases: TypedDict
Structured live-runtime counters for diagnostics and status rendering.
Source code in src/phids/api/presenters/diagnostics/model.py
build_energy_deficit_swarms(sim_loop: SimulationLoop | None) -> list[EnergyDeficitSwarmRow]
Rank live swarms by metabolic energy deficit severity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sim_loop
|
SimulationLoop | None
|
Active simulation loop instance, or None if draft mode. |
required |
Returns:
| Type | Description |
|---|---|
list[EnergyDeficitSwarmRow]
|
Sorted stress records for swarm entities with positive energy deficits. |
Source code in src/phids/api/presenters/diagnostics/model.py
build_live_summary(sim_loop: SimulationLoop | None) -> LiveSummary | None
Aggregate coarse live-model counters for diagnostics surfaces.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sim_loop
|
SimulationLoop | None
|
Active simulation loop instance, or None if draft mode. |
required |
Returns:
| Type | Description |
|---|---|
LiveSummary | None
|
Summary counters when a live loop exists, otherwise |
Source code in src/phids/api/presenters/diagnostics/model.py
phids.api.presenters.diagnostics.badge
Simulation status badge presenter.
render_status_badge_html(sim_loop: SimulationLoop | None) -> str
Render the HTMX-polled simulation status badge fragment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sim_loop
|
SimulationLoop | None
|
Active simulation loop instance, or None if draft mode. |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML fragment encoding current lifecycle state with semantic coloring. |
Source code in src/phids/api/presenters/diagnostics/badge.py
phids.api.presenters.telemetry.svg
Telemetry SVG chart presenter.
build_telemetry_svg(df: object) -> str
Generate an inline SVG line chart from telemetry data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
object
|
Tabular telemetry object with columns |
required |
Returns:
| Type | Description |
|---|---|
str
|
SVG markup suitable for |
Notes
The chart intentionally overlays flora population, herbivore population, and aggregate flora energy on a shared temporal axis to support rapid diagnosis of trophic oscillation and metabolic collapse onset.
Source code in src/phids/api/presenters/telemetry/svg.py
phids.api.presenters.trigger_rules.context
Trigger rules context presentation logic.
trigger_rules_template_context(draft: DraftState) -> dict[str, object]
Assemble the canonical template context for trigger-rule partial rendering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Active draft scenario state used as the authoritative builder source. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, object]
|
Template context dictionary containing species registries, trigger rows, condition summaries, |
dict[str, object]
|
and condition-node editing metadata. |
Source code in src/phids/api/presenters/trigger_rules/context.py
API Services
phids.api.services.draft.biotope
Draft-state mutation module for biotope parameters.
This module provides pure functions for updating the global biotope settings of a draft scenario, including grid dimensions, termination thresholds, and atmospheric factors.
update_biotope(draft: DraftState, *, grid_width: int, grid_height: int, max_ticks: int, tick_rate_hz: float, wind_x: float, wind_y: float, num_signals: int, num_toxins: int, z2_flora_species_extinction: int, z4_herbivore_species_extinction: int, z6_max_total_flora_energy: float, z7_max_total_herbivore_population: int, mycorrhizal_inter_species: bool, mycorrhizal_connection_cost: float, mycorrhizal_growth_interval_ticks: int, mycorrhizal_signal_velocity: int, signal_decay_factor: float = 0.85, substance_emit_rate: float = 0.1) -> bool
Normalize and persist global biotope parameters into the draft.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
grid_width
|
int
|
Requested biotope width. |
required |
grid_height
|
int
|
Requested biotope height. |
required |
max_ticks
|
int
|
Requested simulation tick horizon. |
required |
tick_rate_hz
|
float
|
Requested UI stream rate. |
required |
wind_x
|
float
|
Requested uniform wind x-component. |
required |
wind_y
|
float
|
Requested uniform wind y-component. |
required |
num_signals
|
int
|
Requested number of signal layers. |
required |
num_toxins
|
int
|
Requested number of toxin layers. |
required |
z2_flora_species_extinction
|
int
|
Requested species-specific flora-extinction termination rule. |
required |
z4_herbivore_species_extinction
|
int
|
Requested species-specific herbivore-extinction rule. |
required |
z6_max_total_flora_energy
|
float
|
Requested upper bound for total flora energy termination. |
required |
z7_max_total_herbivore_population
|
int
|
Requested upper bound for herbivore population termination. |
required |
mycorrhizal_inter_species
|
bool
|
Requested root-link species policy. |
required |
mycorrhizal_connection_cost
|
float
|
Requested root-link establishment cost. |
required |
mycorrhizal_growth_interval_ticks
|
int
|
Requested root-growth interval. |
required |
mycorrhizal_signal_velocity
|
int
|
Requested root-network signal velocity. |
required |
signal_decay_factor
|
float
|
Requested per-tick airborne signal retention (0.0-1.0). |
0.85
|
substance_emit_rate
|
float
|
Requested concentration increment per active emit tick (0.0-1.0). |
0.1
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in src/phids/api/services/draft/biotope.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
phids.api.services.draft.diet
Draft-state mutation module for diet compatibility matrix.
Provides pure functions to mutate herbivore-flora interaction policies within the draft matrix representation.
set_diet_compatibility(draft: DraftState, herbivore_idx: int, flora_idx: int, compatible: str = 'toggle') -> bool | None
Toggle or assign one herbivore-flora edibility matrix cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
herbivore_idx
|
int
|
Herbivore row index. |
required |
flora_idx
|
int
|
The integer column index representing the specific flora species. |
required |
compatible
|
str
|
Requested boolean state or the literal |
'toggle'
|
Returns:
| Type | Description |
|---|---|
bool | None
|
The updated boolean cell value, or |
Source code in src/phids/api/services/draft/diet.py
phids.api.services.draft.helpers
Shared helper functions for draft state mutations.
These pure functions provide utility operations for the draft-state services, including truthy flag interpretation, substance lookup, diet matrix resizing, and species index compaction. They are decoupled from the routing layer to support deterministic scenario editing.
find_substance_index(draft: DraftState, substance_id: int) -> int | None
Locate the list index for one substance identifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state whose substance registry is searched. |
required |
substance_id
|
int
|
Substance identifier to resolve. |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
The list index of the matching substance definition, or |
Source code in src/phids/api/services/draft/helpers.py
is_truthy_flag(value: str | bool) -> bool
Interpret HTML-form boolean payloads as deterministic Python truth values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
str | bool
|
Raw route payload representing a checkbox or toggle state. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True when the submitted value represents the affirmative state. |
Source code in src/phids/api/services/draft/helpers.py
rebuild_species_ids(draft: DraftState) -> None
Reassign sequential species identifiers after species-list mutations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state whose species collections require index compaction. |
required |
Source code in src/phids/api/services/draft/helpers.py
resize_diet_matrix(draft: DraftState) -> None
Resize the diet matrix to match current herbivore and flora list lengths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state whose matrix dimensions are compacted or extended. |
required |
Source code in src/phids/api/services/draft/helpers.py
phids.api.services.draft.placements
Draft-state mutation module for initial spatial placements.
Provides pure functions to add, remove, and clear initial plant and swarm placements within the draft scenario grid.
add_plant_placement(draft: DraftState, species_id: int, x: int, y: int, energy: float) -> None
Append one plant placement to the draft placement ledger.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
species_id
|
int
|
Flora species identifier. |
required |
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
energy
|
float
|
Initial plant energy reserve. |
required |
Source code in src/phids/api/services/draft/placements.py
add_swarm_placement(draft: DraftState, species_id: int, x: int, y: int, population: int, energy: float) -> None
Append one swarm placement to the draft placement ledger.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
species_id
|
int
|
Herbivore species identifier. |
required |
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
population
|
int
|
Initial swarm population. |
required |
energy
|
float
|
Initial swarm energy reserve. |
required |
Source code in src/phids/api/services/draft/placements.py
clear_placements(draft: DraftState) -> None
Clear all plant and swarm placements from the draft.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
Source code in src/phids/api/services/draft/placements.py
clear_plant_placements(draft: DraftState) -> None
Clear all plant placements from the draft.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
Source code in src/phids/api/services/draft/placements.py
clear_swarm_placements(draft: DraftState) -> None
Clear all swarm placements from the draft.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
Source code in src/phids/api/services/draft/placements.py
remove_plant_placement(draft: DraftState, index: int) -> None
Remove one plant placement by list index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Placement index to remove. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The plant placement index is out of range. |
Source code in src/phids/api/services/draft/placements.py
remove_swarm_placement(draft: DraftState, index: int) -> None
Remove one swarm placement by list index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Placement index to remove. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The swarm placement index is out of range. |
Source code in src/phids/api/services/draft/placements.py
phids.api.services.draft.species
Draft-state mutation module for species configurations.
Provides pure functions to add or remove flora and herbivore species definitions within the draft. Species removal automatically compacts dependencies such as the diet matrix, trigger rules, and initial placements to maintain a consistent state.
add_flora(draft: DraftState, params: FloraSpeciesParams) -> None
Append one flora species and expand dependent matrix state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
params
|
FloraSpeciesParams
|
Flora species parameter object. |
required |
Source code in src/phids/api/services/draft/species.py
add_herbivore(draft: DraftState, params: HerbivoreSpeciesParams) -> None
Append one herbivore species and expand dependent matrix state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
params
|
HerbivoreSpeciesParams
|
Herbivore species parameter object. |
required |
Source code in src/phids/api/services/draft/species.py
remove_flora(draft: DraftState, species_id: int) -> None
Remove one flora species and compact all dependent references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
species_id
|
int
|
Flora species identifier to remove. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
No flora species with the requested identifier exists. |
Source code in src/phids/api/services/draft/species.py
remove_herbivore(draft: DraftState, species_id: int) -> None
Remove one herbivore species and compact all dependent references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
species_id
|
int
|
Herbivore species identifier to remove. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
No herbivore species with the requested identifier exists. |
Source code in src/phids/api/services/draft/species.py
phids.api.services.draft.substances
Draft-state mutation module for substance definitions.
Provides pure functions to add, update, and remove substance definitions within the draft. Removing a substance correctly updates remaining definitions and purges associated trigger rules to maintain state integrity.
add_substance(draft: DraftState, *, name: str, is_toxin: str | bool = False, lethal: str | bool = False, repellent: str | bool = False, synthesis_duration: int = 3, aftereffect_ticks: int = 0, lethality_rate: float = 0.0, repellent_walk_ticks: int = 3, energy_cost_per_tick: float = 1.0, irreversible: str | bool = False) -> SubstanceDefinition
Append one substance definition to the bounded registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
name
|
str
|
Operator-facing substance label. |
required |
is_toxin
|
str | bool
|
Substance class toggle. |
False
|
lethal
|
str | bool
|
Lethal-toxin toggle. |
False
|
repellent
|
str | bool
|
Repellent-toxin toggle. |
False
|
synthesis_duration
|
int
|
Requested synthesis latency. |
3
|
aftereffect_ticks
|
int
|
Requested persistence duration after deactivation. |
0
|
lethality_rate
|
float
|
Requested lethal damage rate. |
0.0
|
repellent_walk_ticks
|
int
|
Requested repel walk duration. |
3
|
energy_cost_per_tick
|
float
|
Requested per-tick maintenance cost. |
1.0
|
irreversible
|
str | bool
|
Irreversible activation toggle. |
False
|
Returns:
| Type | Description |
|---|---|
SubstanceDefinition
|
The created |
Raises:
| Type | Description |
|---|---|
ValueError
|
The Rule of 16 ceiling for substances has been reached. |
Source code in src/phids/api/services/draft/substances.py
remove_substance(draft: DraftState, substance_id: int) -> None
Remove one substance definition and compact all dependent references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
substance_id
|
int
|
Substance identifier to remove. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
No substance with the requested identifier exists. |
Source code in src/phids/api/services/draft/substances.py
update_substance(draft: DraftState, substance_id: int, *, name: str | None = None, type_label: str | None = None, synthesis_duration: int | None = None, aftereffect_ticks: int | None = None, lethality_rate: float | None = None, repellent_walk_ticks: int | None = None, energy_cost_per_tick: float | None = None, irreversible: str | bool | None = None) -> SubstanceDefinition
Patch one substance definition in place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
substance_id
|
int
|
Substance identifier to modify. |
required |
name
|
str | None
|
Optional replacement name. |
None
|
type_label
|
str | None
|
Optional UI type label controlling toxin flags. |
None
|
synthesis_duration
|
int | None
|
Optional replacement synthesis latency. |
None
|
aftereffect_ticks
|
int | None
|
Optional replacement persistence duration. |
None
|
lethality_rate
|
float | None
|
Optional replacement lethal damage rate. |
None
|
repellent_walk_ticks
|
int | None
|
Optional replacement repel walk duration. |
None
|
energy_cost_per_tick
|
float | None
|
Optional replacement maintenance cost. |
None
|
irreversible
|
str | bool | None
|
Optional replacement irreversible flag. |
None
|
Returns:
| Type | Description |
|---|---|
SubstanceDefinition
|
The mutated |
Raises:
| Type | Description |
|---|---|
ValueError
|
No substance with the requested identifier exists. |
Source code in src/phids/api/services/draft/substances.py
phids.api.services.draft.trigger_rules
Draft-state mutation module for trigger rules and conditions.
Provides pure functions to add, remove, and update trigger rules, as well as complex
tree-manipulation functions for editing the hierarchical activation condition nodes.
Also exposes read-only query helpers (e.g. get_condition_node) so callers never
need to import private ui_state path-resolution utilities directly.
add_trigger_rule(draft: DraftState, flora_species_id: int, herbivore_species_id: int = 0, substance_id: int = 0, action_type: Literal['synthesize_substance', 'resource_withdrawal'] = 'synthesize_substance', apparent_nutrition_factor: float = 0.2, withdrawal_duration: int = 10, aftereffect_ticks: int = 10, min_herbivore_population: int = 5, activation_condition: ActivationConditionNode | None = None, initiator_type: Literal['herbivore_attack', 'environmental_signal'] = 'herbivore_attack', initiator_signal_id: int = 0, initiator_min_concentration: float = 0.01) -> None
Append one trigger rule to the draft trigger ledger.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
flora_species_id
|
int
|
Flora species identifier. |
required |
herbivore_species_id
|
int
|
Herbivore species identifier. |
0
|
substance_id
|
int
|
Substance identifier synthesized by the rule. |
0
|
action_type
|
Literal['synthesize_substance', 'resource_withdrawal']
|
"synthesize_substance" or "resource_withdrawal". |
'synthesize_substance'
|
apparent_nutrition_factor
|
float
|
Factor for resource_withdrawal. |
0.2
|
withdrawal_duration
|
int
|
Duration of the nutrition withdrawal. |
10
|
aftereffect_ticks
|
int
|
Duration of aftereffect. |
10
|
min_herbivore_population
|
int
|
Minimum herbivore population threshold. |
5
|
activation_condition
|
ActivationConditionNode | None
|
Optional nested activation-condition tree. |
None
|
initiator_type
|
Literal['herbivore_attack', 'environmental_signal']
|
The type of trigger initiator. |
'herbivore_attack'
|
initiator_signal_id
|
int
|
The ID of the environmental signal. |
0
|
initiator_min_concentration
|
float
|
Minimum signal concentration. |
0.01
|
Source code in src/phids/api/services/draft/trigger_rules.py
append_trigger_rule_condition_child(draft: DraftState, index: int, parent_path: str, condition: ActivationConditionNode) -> None
Append one child condition to a group node in a trigger tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
parent_path
|
str
|
Dotted path to the parent group node. |
required |
condition
|
ActivationConditionNode
|
Child node payload to append. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The parent node is missing or is not a valid group node. |
Source code in src/phids/api/services/draft/trigger_rules.py
default_activation_condition_for_rule(draft: DraftState, rule: TriggerRule, node_kind: str) -> ActivationConditionNode
Construct a default activation-condition node compatible with a trigger rule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Active draft state containing species and substance registries. |
required |
rule
|
TriggerRule
|
Trigger rule being edited. |
required |
node_kind
|
str
|
Requested node discriminator. |
required |
Returns:
| Type | Description |
|---|---|
ActivationConditionNode
|
Default node payload suitable for insertion into a condition tree. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
|
Source code in src/phids/api/services/draft/trigger_rules.py
delete_trigger_rule_condition_node(draft: DraftState, index: int, path: str) -> None
Delete one condition node by dotted path and prune empty groups.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
path
|
str
|
Dotted child index path to remove. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The path or parent node does not resolve to a removable child slot. |
Source code in src/phids/api/services/draft/trigger_rules.py
get_condition_node(rule_condition: ActivationConditionNode, path: str) -> ActivationConditionNode
Resolve one condition node by a dotted path string.
This is the public surface for condition-tree reads so that callers never
need to import the private _condition_node_at_path / _parse_condition_path
helpers from ui_state directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rule_condition
|
ActivationConditionNode
|
Root condition node of a trigger rule. |
required |
path
|
str
|
Dotted integer-index path (e.g. |
required |
Returns:
| Type | Description |
|---|---|
ActivationConditionNode
|
The |
Raises:
| Type | Description |
|---|---|
IndexError
|
The path does not resolve to a valid node. |
Source code in src/phids/api/services/draft/trigger_rules.py
parse_activation_condition_json(raw: str | None) -> ActivationConditionNode | None
Parse and validate a serialized activation-condition tree from builder input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
str | None
|
Raw JSON text submitted from trigger-rule editing controls. |
required |
Returns:
| Type | Description |
|---|---|
ActivationConditionNode | None
|
Normalized condition dictionary, or |
Raises:
| Type | Description |
|---|---|
HTTPException
|
Condition JSON is syntactically invalid or violates schema constraints. |
Source code in src/phids/api/services/draft/trigger_rules.py
remove_trigger_rule(draft: DraftState, index: int) -> None
Remove one trigger rule by list index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The requested trigger-rule index is out of range. |
Source code in src/phids/api/services/draft/trigger_rules.py
replace_trigger_rule_condition_node(draft: DraftState, index: int, path: str, condition: ActivationConditionNode) -> None
Replace one condition node addressed by a dotted path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
path
|
str
|
Dotted child index path identifying the node to replace. |
required |
condition
|
ActivationConditionNode
|
Replacement node payload. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
The path or parent node does not resolve to a mutable child slot. |
Source code in src/phids/api/services/draft/trigger_rules.py
set_trigger_rule_activation_condition(draft: DraftState, index: int, condition: ActivationConditionNode | None) -> None
Replace the full activation-condition tree for one trigger rule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
condition
|
ActivationConditionNode | None
|
Full replacement condition tree. |
required |
Source code in src/phids/api/services/draft/trigger_rules.py
trigger_rule_by_index(draft: DraftState, index: int) -> TriggerRule
Return one trigger rule from draft state with HTTP-oriented bounds checking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Active draft state containing trigger rules. |
required |
index
|
int
|
Positional index requested by route handlers. |
required |
Returns:
| Type | Description |
|---|---|
TriggerRule
|
Trigger rule at the requested index. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
Index is outside the current trigger-rule list bounds. |
Source code in src/phids/api/services/draft/trigger_rules.py
update_trigger_rule(draft: DraftState, index: int, *, flora_species_id: int | None = None, herbivore_species_id: int | None = None, initiator_type: Literal['herbivore_attack', 'environmental_signal'] | None = None, initiator_signal_id: int | None = None, initiator_min_concentration: float | None = None, substance_id: int | None = None, action_type: Literal['synthesize_substance', 'resource_withdrawal'] | None = None, apparent_nutrition_factor: float | None = None, withdrawal_duration: int | None = None, aftereffect_ticks: int | None = None, min_herbivore_population: int | None = None, activation_condition: ActivationConditionNode | None = None) -> None
Patch selected fields on one trigger rule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
flora_species_id
|
int | None
|
Optional replacement flora species identifier. |
None
|
herbivore_species_id
|
int | None
|
Optional replacement herbivore species identifier. |
None
|
initiator_type
|
Literal['herbivore_attack', 'environmental_signal'] | None
|
Optional replacement initiator type. |
None
|
initiator_signal_id
|
int | None
|
Optional replacement signal identifier. |
None
|
initiator_min_concentration
|
float | None
|
Optional replacement minimum concentration. |
None
|
substance_id
|
int | None
|
Optional replacement substance identifier. |
None
|
action_type
|
Literal['synthesize_substance', 'resource_withdrawal'] | None
|
Optional replacement action type. |
None
|
apparent_nutrition_factor
|
float | None
|
Optional replacement nutrition factor. |
None
|
withdrawal_duration
|
int | None
|
Optional replacement nutrition withdrawal duration. |
None
|
aftereffect_ticks
|
int | None
|
Optional replacement aftereffect ticks. |
None
|
min_herbivore_population
|
int | None
|
Optional replacement threshold. |
None
|
activation_condition
|
ActivationConditionNode | None
|
Optional replacement condition tree. |
None
|
Raises:
| Type | Description |
|---|---|
IndexError
|
The requested trigger-rule index is out of range. |
Source code in src/phids/api/services/draft/trigger_rules.py
update_trigger_rule_condition_node(draft: DraftState, index: int, path: str, **fields: ConditionValue) -> None
Patch selected key-value fields on one condition node.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
draft
|
DraftState
|
Draft state mutated in place. |
required |
index
|
int
|
Trigger-rule index in the draft list. |
required |
path
|
str
|
Dotted path to the condition node. |
required |
**fields
|
ConditionValue
|
Replacement key-value fields merged into the node. |
{}
|
Raises:
| Type | Description |
|---|---|
IndexError
|
The trigger rule has no condition tree or path resolution fails. |
Source code in src/phids/api/services/draft/trigger_rules.py
phids.api.services.dse.task_manager
DSE background task manager.
Manages running, cancelling, and streaming live progress metrics from the Design Space Exploration (DSE) genetic algorithm task in a non-blocking background worker thread.
DSETaskManager
Manages the background execution of the DSE Optimizer.
Attributes:
| Name | Type | Description |
|---|---|---|
websocket_manager |
The websocket manager instance used to broadcast generation progress. |
|
pareto_cache |
list[SimulationConfig]
|
Cache of the current Pareto front candidate configs. |
Source code in src/phids/api/services/dse/task_manager.py
__init__(websocket_manager: DSEStreamManager) -> None
Initialize the DSE Task Manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
websocket_manager
|
DSEStreamManager
|
WS stream manager for DSE metrics. |
required |
Source code in src/phids/api/services/dse/task_manager.py
start_dse_task(config: SimulationConfig) -> None
Start the DSE optimization in a background thread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
SimulationConfig
|
Base simulation config blueprint. |
required |
Source code in src/phids/api/services/dse/task_manager.py
stop_dse_task() -> None
Gracefully stop the DSE optimization.
Source code in src/phids/api/services/dse/task_manager.py
get_dse_manager(ws_manager: DSEStreamManager) -> DSETaskManager
Return the global DSE Task Manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ws_manager
|
DSEStreamManager
|
The websocket manager to initialize with. |
required |
Returns:
| Type | Description |
|---|---|
DSETaskManager
|
The singleton DSETaskManager instance. |
Source code in src/phids/api/services/dse/task_manager.py
Engine orchestration
phids.engine.loop
Simulation loop orchestration for deterministic, double-buffered ecosystem advancement.
This module implements the principal simulation driver for PHIDS, responsible for advancing the
grid environment and ECS world through a rigorously ordered sequence of systems: flow field,
lifecycle, interaction, signaling, and telemetry/termination. The simulation loop enforces
deterministic update ordering via an asyncio.Lock, ensuring reproducibility and scientific
validity. Double-buffering is employed to maintain a strict separation between read and write
states, preventing race conditions and guaranteeing the integrity of biological phenomena such as
systemic acquired resistance, metabolic attrition, and mitosis. Per-tick snapshots are captured
for replay and telemetry, supporting comprehensive analysis of emergent behaviours and ecological
dynamics. The architectural design reflects the project's commitment to data-oriented modelling,
O(1) spatial hash lookups, and the Rule of 16 for memory allocation, thereby simulating complex
plant-herbivore interactions with maximal computational efficiency and biological fidelity.
SimulationLoop
Orchestrate deterministic, double-buffered simulation ticks.
Double-buffering is achieved by keeping a read-copy of the grid state
while writing results to live objects; these writes become the read
state for the next tick. Concurrent access is protected by
asyncio.Lock.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
SimulationConfig
|
Validated :class: |
required |
Source code in src/phids/engine/loop.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | |
state_revision: int
property
Return a monotonic token for non-tick state mutations relevant to stream payloads.
__init__(config: SimulationConfig, *, disable_replay: bool = False) -> None
Initialise the SimulationLoop with the provided configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
SimulationConfig
|
Validated SimulationConfig instance from the API payload. |
required |
disable_replay
|
bool
|
If True, disables Zarr replay recording to disk. |
False
|
Source code in src/phids/engine/loop.py
get_state_snapshot() -> ReplayState
Return a serialisable snapshot of the current grid state.
Returns:
| Name | Type | Description |
|---|---|---|
ReplayState |
ReplayState
|
Snapshot containing tick, termination state and |
ReplayState
|
environment dictionary (from :meth: |
Source code in src/phids/engine/loop.py
pause() -> None
Toggle the paused state.
Flips the paused boolean.
run() -> None
async
Run the simulation loop until termination at configured tick rate.
The loop respects paused and sleeps to maintain tick_rate_hz.
Source code in src/phids/engine/loop.py
start() -> None
Mark the simulation as running.
Sets running state to True and clears the paused flag.
step() -> TerminationResult
async
Execute one deterministic simulation tick.
The method performs the ordered phases of the simulation (flow-field update, lifecycle, interaction, signaling, telemetry) while holding an asyncio lock to ensure async-safety. After processing it evaluates termination conditions.
Returns:
| Name | Type | Description |
|---|---|---|
TerminationResult |
TerminationResult
|
Termination state after the tick. |
Source code in src/phids/engine/loop.py
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | |
stop() -> None
update_tick_rate(tick_rate_hz: float) -> float
Update live simulation tick speed while preserving safe lower bounds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tick_rate_hz
|
float
|
Requested simulation ticks per second. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Applied tick-rate value after clamping. |
Source code in src/phids/engine/loop.py
update_wind(vx: float, vy: float) -> None
Update the environment uniform wind vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vx
|
float
|
The horizontal vector component of the globally applied wind force. |
required |
vy
|
float
|
The vertical vector component of the globally applied wind force. |
required |
Source code in src/phids/engine/loop.py
phids.engine.batch
Headless Monte Carlo batch processing engine for PHIDS ecosystem simulations.
This module implements the :class:BatchRunner, which executes a configurable number of
deterministic simulation runs in parallel using a :class:concurrent.futures.ProcessPoolExecutor
isolated from the FastAPI event loop. Each run is seeded with a unique integer so that,
while the per-tick mechanics remain deterministic for a given seed, population-level
stochasticity (e.g., probabilistic flow-field navigation, natal dispersal) produces
meaningfully varied trajectories across the ensemble, enabling Monte Carlo estimation of
extinction probabilities and Lotka-Volterra orbital stability.
The module-level function :func:_run_single_headless is intentionally not a class
method. Module-level callability is a strict requirement for :mod:multiprocessing
serialisation via pickle when using the spawn start method, which is mandatory on
platforms where forking a process that has loaded Numba JIT-compiled functions would cause
undefined behaviour. Each worker process independently JIT-compiles the flow-field kernel
on first invocation; subsequent runs within the same worker process reuse the cached
native code.
Statistical aggregation is performed by :func:aggregate_batch_telemetry, which aligns
the per-run telemetry row lists to the minimum observed tick count, stacks them into NumPy
arrays, and computes per-tick mean and standard deviation for flora population, herbivore
population, and per-species sub-populations. The resulting aggregate dictionary is
serialised to {output_dir}/{job_id}_summary.json for persistent retrieval and
Chart.js rendering of confidence bands in the batch dashboard.
BatchResult
dataclass
Aggregated result of a completed batch simulation run.
Attributes:
| Name | Type | Description |
|---|---|---|
job_id |
str
|
Unique identifier for the batch job. |
runs |
int
|
Number of individual simulation runs completed. |
per_run_telemetry |
TelemetryRuns
|
Nested list of raw telemetry row dicts per run. |
aggregate |
BatchAggregate
|
Statistical summary produced by
:func: |
Source code in src/phids/engine/batch.py
BatchRunner
Orchestrate parallel Monte Carlo simulation runs using ProcessPoolExecutor.
The :class:BatchRunner dispatches :func:_run_and_save to a
ProcessPoolExecutor configured with the spawn multiprocessing
context to avoid Numba/asyncio fork conflicts. Progress is reported via
an optional on_progress callback invoked in the main process after
each completed future, enabling the FastAPI background task to update the
BatchJobState without blocking the event loop.
Aggregate results are written to {output_dir}/{job_id}_summary.json
upon completion of all runs, making them available for retrieval via the
GET /api/batch/view/{job_id} endpoint.
Source code in src/phids/engine/batch.py
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | |
execute_batch(scenario_dict: dict[str, object], runs: int, max_ticks: int, job_id: str, output_dir: Path | None = None, on_progress: Callable[[int], None] | None = None, scenario_name: str | None = None) -> BatchResult
Execute runs independent simulation trajectories in parallel.
Dispatches all runs to a :class:concurrent.futures.ProcessPoolExecutor
using the spawn start method, collects telemetry as futures complete,
and computes statistical aggregates. The summary JSON is written to
{output_dir}/{job_id}_summary.json.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scenario_dict
|
dict[str, object]
|
JSON-serialisable |
required |
runs
|
int
|
Number of independent simulation runs to execute. |
required |
max_ticks
|
int
|
Maximum tick count per run. |
required |
job_id
|
str
|
Unique batch job identifier for file naming. |
required |
output_dir
|
Path | None
|
Directory for output files; defaults to |
None
|
on_progress
|
Callable[[int], None] | None
|
Optional callback invoked with completed count as each future resolves. |
None
|
scenario_name
|
str | None
|
Optional display label persisted into the summary so restored ledgers can retain operator-selected names. |
None
|
Returns:
| Type | Description |
|---|---|
BatchResult
|
Completed result with all per-run telemetry and aggregate. |
Source code in src/phids/engine/batch.py
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | |
aggregate_batch_telemetry(per_run: TelemetryRuns) -> BatchAggregate
Compute per-tick statistical summaries across an ensemble of simulation runs.
Aligns all runs to the minimum tick count observed in the ensemble (to handle early-termination runs without padding), then stacks scalar population and energy metrics into NumPy arrays for vectorised mean and standard deviation computation. Per-species populations are similarly aggregated where the union of all species identifiers seen across all runs is used as the index.
The extinction probability is estimated as the fraction of runs in which the total flora population reached zero at any tick, providing a coarse measure of ecosystem collapse risk under the configured parameter regime. A per-tick survival curve is also computed as the fraction of runs that retain strictly positive flora population at each aligned tick.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
per_run
|
TelemetryRuns
|
List of per-run row lists, each produced by :func: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
BatchAggregate |
BatchAggregate
|
Aggregate summary containing mean, std dev, and extinction metrics. |
Source code in src/phids/engine/batch.py
ECS components
phids.engine.components.plant
Plant ECS component dataclass encoding per-entity flora runtime state.
This module defines :class:PlantComponent, the data container attached to every flora entity in
the PHIDS Entity-Component-System world. Each plant entity carries its own independent energy
reserve, spatial grid coordinates, species-level growth and reproduction parameters, camouflage
properties, and the set of identifiers of currently connected mycorrhizal partners. The strict
separation between species-level parameters (which reside in the scenario configuration) and
per-entity mutable state (which resides in PlantComponent) is central to the data-oriented
design: the lifecycle and signaling systems iterate over PlantComponent instances via the ECS
query interface without requiring access to the configuration layer.
The energy field encodes the biological fitness proxy E_i,j(t); its dynamics are governed by
the growth term applied each lifecycle tick, the seed dispersal cost deducted at reproduction,
the connection cost subtracted when a new mycorrhizal link is established, the herbivory loss
inflicted by co-located swarms in the interaction phase, and the defense maintenance cost imposed
by active SubstanceComponent entities in the signaling phase. A plant entity is culled when
energy < survival_threshold, with the cause of terminal energy loss attributed via
last_energy_loss_cause for per-category death diagnostics.
PlantComponent
dataclass
Holds runtime state for a single plant entity.
Attributes:
| Name | Type | Description |
|---|---|---|
entity_id |
int
|
ECS entity identifier. |
species_id |
int
|
Flora species index. |
x, |
y
|
Current grid coordinates. |
energy |
float
|
Current energy reserve E_i,j(t). |
max_energy |
float
|
Species-specific energy capacity E_max. |
base_energy |
float
|
Initial energy used by growth formula. |
growth_rate |
float
|
Per-tick growth rate in percent. |
survival_threshold |
float
|
Energy threshold below which the plant dies. |
reproduction_interval |
int
|
Ticks between reproduction attempts. |
seed_min_dist |
float
|
Minimum seed dispersal distance. |
seed_max_dist |
float
|
Maximum seed dispersal distance. |
seed_energy_cost |
float
|
Energy cost paid for reproduction. |
seed_drop_height |
float
|
Effective release height used to estimate airborne seed flight time. |
seed_terminal_velocity |
float
|
Effective terminal velocity used in wind-shift estimation. |
camouflage |
bool
|
Whether constitutive camouflage is active. |
camouflage_factor |
float
|
Gradient multiplier when camouflaged. |
last_reproduction_tick |
int
|
Tick of the most recent reproduction. |
last_energy_loss_cause |
str | None
|
Most recent energetically relevant action label used for death diagnostics attribution. |
mycorrhizal_connections |
set[int]
|
Set of connected plant entity ids. |
apparent_nutrition_factor |
float
|
Stress-induced nutrient discount modifier. |
withdrawal_ticks_remaining |
int
|
Ticks until nutrition factor reverts to 1.0. |
Source code in src/phids/engine/components/plant.py
phids.engine.components.swarm
Herbivore swarm ECS component dataclass encoding per-entity herbivore runtime state.
This module defines :class:SwarmComponent, the data container attached to every herbivore
swarm entity in the PHIDS Entity-Component-System world. Each swarm entity represents a
spatially co-located cohort of individual herbivores sharing a common species identity, energy
pool, and movement state. The population field tracks the integer head-count of the cohort;
it is decremented by metabolic attrition when the swarm's energy reserve falls below the
individual-level minimum (energy_min) and incremented by reproduction when sufficient surplus
energy accumulates. When population reaches the split_population_threshold, the swarm
undergoes mitosis: the cohort is divided into two halves and a new entity carrying the offspring
half is registered in the ECS world and spatial hash.
The velocity field encodes the movement period in ticks between grid-cell relocations;
together with move_cooldown, it implements a discrete movement-frequency mechanism that
decouples slow-moving species from the per-tick grid update cycle. Movement decisions are
mediated by the scalar flow-field gradient, which encodes plant energy attractors and toxin
repellers computed by the Numba-accelerated flow-field kernel. When a swarm encounters a
repellent toxin, repelled is set and repelled_ticks_remaining governs the duration of the
subsequent random-walk dispersal phase that overrides gradient navigation.
SwarmComponent
dataclass
Holds runtime state for a single herbivore swarm entity.
Attributes:
| Name | Type | Description |
|---|---|---|
entity_id |
int
|
ECS entity identifier. |
species_id |
int
|
Herbivore species index. |
x, |
y
|
Current grid coordinates. |
population |
int
|
Current swarm head-count. |
initial_population |
int
|
Head-count at spawn; used for mitosis checks. |
energy |
float
|
Current energy reserve. |
energy_min |
float
|
Minimum energy per individual. |
velocity |
int
|
Movement period in ticks between moves. |
consumption_rate |
float
|
Per-tick consumption scalar. |
reproduction_energy_divisor |
float
|
Species-level growth throttle. |
energy_upkeep_per_individual |
float
|
Metabolic upkeep scalar applied each tick. |
split_population_threshold |
int
|
Explicit population threshold for mitosis. |
repelled |
bool
|
Whether the swarm is currently repelled by toxin. |
repelled_ticks_remaining |
int
|
Remaining ticks of repelled behavior. |
move_cooldown |
int
|
Ticks remaining until the next movement. |
last_dx |
int
|
Last movement delta on the x-axis (-1, 0, 1). |
last_dy |
int
|
Last movement delta on the y-axis (-1, 0, 1). |
Source code in src/phids/engine/components/swarm.py
phids.engine.components.substances
Substance ECS component dataclass encoding chemical-defense synthesis and activation state.
This module defines :class:SubstanceComponent, the data container attached to substance
entities in the PHIDS ECS world. A substance entity is associated with a single owner plant
entity and represents either a volatile organic compound (VOC) signal emitted into airborne
signal layers for detection by neighbouring plants, or a defensive toxin emitted into local toxin
layers to deter, repel, or kill co-located herbivore swarms. The substance lifecycle consists of
a configurable synthesis delay (synthesis_duration ticks during which the plant invests
metabolic resources), followed by an active emission phase, and an optional aftereffect window
(aftereffect_ticks) during which emission persists after the triggering herbivore has
departed.
Substances are dynamically created by the signaling system when a TriggerConditionSchema
rule is satisfied, and are garbage-collected when their owner plant dies or when the aftereffect
window expires and the trigger condition is no longer met. The irreversible flag encodes
constitutive systemic acquired resistance: once activated, the substance remains permanently
emitting until owner death, regardless of subsequent herbivore presence. The nested
activation_condition predicate tree enables compound chemical-defense cascades, such as
alarm-chain scenarios in which a secondary toxin activates only after a primary VOC signal is
already present, modelling coordinated multi-species defense networks.
SubstanceComponent
dataclass
Holds runtime state for a single substance entity.
A substance represents either a volatile signal (VOC) or a toxin.
Attributes:
| Name | Type | Description |
|---|---|---|
entity_id |
int
|
ECS entity identifier. |
substance_id |
int
|
Layer index into signal or toxin layers. |
owner_plant_id |
int
|
Entity id of the producing plant. |
is_toxin |
bool
|
True for toxins, False for signals. |
synthesis_duration |
int
|
Configured synthesis duration in ticks. |
synthesis_remaining |
int
|
Ticks remaining before activation. |
active |
bool
|
Whether the substance is currently active. |
aftereffect_ticks |
int
|
Configured aftereffect duration after trigger removal. |
aftereffect_remaining_ticks |
int
|
Remaining aftereffect duration at runtime. |
lethal |
bool
|
Whether the toxin is lethal. |
lethality_rate |
float
|
Individuals eliminated per tick when lethal. |
repellent |
bool
|
Whether the toxin repels swarms. |
repellent_walk_ticks |
int
|
Duration of repelled random-walk in ticks. |
activation_condition |
dict[str, object] | None
|
Optional nested activation predicate tree stored in JSON-serialisable form for runtime evaluation and tooltip display. |
energy_cost_per_tick |
float
|
Energy drained from the owner plant per active tick. |
irreversible |
bool
|
Whether activation remains permanently on once active. |
triggered_this_tick |
bool
|
Whether the trigger condition was satisfied in the current signaling pass. |
Source code in src/phids/engine/components/substances.py
Core runtime structures
phids.engine.core.biotope
GridEnvironment: NumPy-backed biotope with 2-D convolution diffusion and explicit double-buffering.
This module manages the continuous environmental state (e.g., plant energy layers, VOC gradients,
wind fields, and apparent nutrition). It enforces strict read/write double-buffering across all layers
to prevent race conditions and maintain exact tick-level determinism during concurrent simulation phases.
No allocations are made during the diffusion loop. All layers are pre-allocated according to the Rule of
16, ensuring fixed memory allocation and avoiding dynamic resizing during simulation. The
environment employs explicit read/write double-buffering to prevent race conditions and guarantee
deterministic simulation of biological phenomena such as Gaussian diffusion, systemic acquired
resistance, and metabolic attrition. The convolution kernel is pre-computed and its tails are
truncated to eliminate subnormal floats below SIGNAL_EPSILON, maintaining computational
efficiency and scientific accuracy. The architectural design is tightly coupled to the
:class:~phids.engine.core.ecs.ECSWorld and flow-field systems, supporting O(1) spatial hash
lookups and reproducible ecological dynamics.
GridEnvironment
Manage vectorised biotope layers and diffusion helpers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
Grid width W (1 ≤ W ≤ GRID_W_MAX). |
40
|
height
|
int
|
Grid height H (1 ≤ H ≤ GRID_H_MAX). |
40
|
num_signals
|
int
|
Number of signal substance layers (1 ≤ n ≤ MAX_SUBSTANCE_TYPES). |
4
|
num_toxins
|
int
|
Number of toxin substance layers (1 ≤ n ≤ MAX_SUBSTANCE_TYPES). |
4
|
Source code in src/phids/engine/core/biotope.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
__init__(width: int = 40, height: int = 40, num_signals: int = 4, num_toxins: int = 4) -> None
Initialise grid layers and double-buffered storage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
Grid width in cells. |
40
|
height
|
int
|
Grid height in cells. |
40
|
num_signals
|
int
|
Number of airborne signal layers. |
4
|
num_toxins
|
int
|
Number of toxin layers. |
4
|
Source code in src/phids/engine/core/biotope.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | |
clear_plant_energy(x: int, y: int, species_id: int) -> None
Clear a species-specific energy contribution in the write buffer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
species_id
|
int
|
The integer index representing the specific phylogenetic species associated with this operation. |
required |
Source code in src/phids/engine/core/biotope.py
diffuse_signals(signal_decay_factor: float = 0.85) -> None
Compute one diffusion tick for all signal layers.
This applies local semi-Lagrangian advection using per-cell wind vectors, followed by isotropic Gaussian diffusion and decay. The transport update respects heterogeneous wind fields across the grid and avoids global-mean wind averaging artefacts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal_decay_factor
|
float
|
Per-tick airborne signal retention (0.0-1.0).
Defaults to the |
0.85
|
Source code in src/phids/engine/core/biotope.py
rebuild_energy_layer() -> None
Recompute aggregate plant energy layer and swap buffers.
Aggregates per-species write buffers into the global write buffer, then swaps read/write buffers so that subsequent reads observe the newly-written values.
Source code in src/phids/engine/core/biotope.py
set_apparent_nutrition(x: int, y: int, value: float) -> None
Set apparent nutrition factor in the write buffer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
value
|
float
|
The apparent nutrition value to store. |
required |
Source code in src/phids/engine/core/biotope.py
set_plant_energy(x: int, y: int, species_id: int, value: float) -> None
Set a species-specific energy contribution in the write buffer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
species_id
|
int
|
The integer index representing the specific phylogenetic species associated with this operation. |
required |
value
|
float
|
Energy contribution (clamped to >= 0). |
required |
Source code in src/phids/engine/core/biotope.py
set_uniform_wind(vx: float, vy: float) -> None
Fill wind layers with a spatially uniform vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vx
|
float
|
X component of the wind. |
required |
vy
|
float
|
Y component of the wind. |
required |
Source code in src/phids/engine/core/biotope.py
to_dict() -> dict[str, object]
Returns a dict representation of the biotope state suitable for serialization.
This is used by the streaming interface to serialize the current state of the biotope to a dictionary.
Returns:
| Type | Description |
|---|---|
dict[str, object]
|
Mapping containing numpy arrays converted to nested lists. |
Source code in src/phids/engine/core/biotope.py
update_wind_at(x: int, y: int, vx: float, vy: float) -> None
Update the wind vector at a single grid cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
vx
|
float
|
X component of the wind. |
required |
vy
|
float
|
Y component of the wind. |
required |
Source code in src/phids/engine/core/biotope.py
phids.engine.core.ecs
Entity-Component-System (ECS) registry with O(1) spatial hash support for deterministic ecosystem simulation.
components and pre-allocated buffers (Rule of 16). The spatial hash is central to the simulation's ability to model emergent ecological phenomena with deterministic reproducibility and scientific rigour.
ECSWorld
Central ECS registry managing entities, components and spatial hash.
The world provides helpers for entity lifecycle, component indexing and a spatial hash grid for efficient cell membership queries.
Source code in src/phids/engine/core/ecs.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
__init__() -> None
Initialise the ECS world and its internal indices.
Attributes initialized
_next_id: Next entity id to allocate. _entities: Mapping of entity id to Entity. _component_index: Index mapping component types to entity id sets. _spatial_hash: Grid cell roster mapping (x, y) to entity id sets. _entity_positions: Reverse index mapping entity ids to their current cell.
Source code in src/phids/engine/core/ecs.py
add_component(entity_id: int, component: object) -> None
Attach a component to an entity and update the component index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
component
|
object
|
Component instance to attach. |
required |
Source code in src/phids/engine/core/ecs.py
collect_garbage(dead_entity_ids: list[int]) -> None
Bulk destroy a list of dead entities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dead_entity_ids
|
list[int]
|
List of entity ids to remove. |
required |
create_entity() -> Entity
Allocate and register a new entity.
Returns:
| Name | Type | Description |
|---|---|---|
Entity |
Entity
|
Newly created entity object. |
Source code in src/phids/engine/core/ecs.py
destroy_entity(entity_id: int) -> None
Remove an entity and clean up index and spatial hash references.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
Identifier of the entity to destroy. |
required |
Source code in src/phids/engine/core/ecs.py
entities_at(x: int, y: int) -> set[int]
Return the set of entity ids occupying a cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
Returns:
| Type | Description |
|---|---|
set[int]
|
set[int]: Entity ids occupying the cell. |
Source code in src/phids/engine/core/ecs.py
get_entity(entity_id: int) -> Entity
Return the entity instance for the given id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Entity |
Entity
|
Matching entity. |
Source code in src/phids/engine/core/ecs.py
has_entity(entity_id: int) -> bool
Return True if the entity exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if present. |
Source code in src/phids/engine/core/ecs.py
move_entity(entity_id: int, old_x: int, old_y: int, new_x: int, new_y: int) -> None
Atomically update spatial hash when an entity moves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
old_x
|
int
|
Previous X coordinate. |
required |
old_y
|
int
|
Previous Y coordinate. |
required |
new_x
|
int
|
The updated X-axis grid coordinate for the entity. |
required |
new_y
|
int
|
The updated Y-axis grid coordinate for the entity. |
required |
Source code in src/phids/engine/core/ecs.py
query(*component_types: type[object]) -> list[Entity]
Return a list of all entities that possess all listed component types.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*component_types
|
type[object]
|
Component classes/types to require. |
()
|
Returns:
| Type | Description |
|---|---|
list[Entity]
|
list[Entity]: Materialized list of entities matching the component set. |
Source code in src/phids/engine/core/ecs.py
register_position(entity_id: int, x: int, y: int) -> None
Register an entity at grid cell (x, y).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
x
|
int
|
X coordinate of the cell. |
required |
y
|
int
|
Y coordinate of the cell. |
required |
Source code in src/phids/engine/core/ecs.py
remove_component(entity_id: int, component_type: type[object]) -> None
Detach a component of the specified type from an entity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
component_type
|
type[object]
|
Component class/type to remove. |
required |
Source code in src/phids/engine/core/ecs.py
unregister_position(entity_id: int, x: int, y: int) -> None
Remove an entity from a grid cell.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_id
|
int
|
The unique integer identifier of the target entity within the ECS world registry. |
required |
x
|
int
|
X coordinate of the cell. |
required |
y
|
int
|
Y coordinate of the cell. |
required |
Source code in src/phids/engine/core/ecs.py
Entity
dataclass
Lightweight wrapper holding an entity id and attached components.
Source code in src/phids/engine/core/ecs.py
add_component(component: object) -> None
Attach a component instance keyed by its type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component
|
object
|
Component instance to attach. |
required |
get_component(component_type: type[C]) -> C
Return attached component of the given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
type[C]
|
The component class/type to retrieve. |
required |
Returns:
| Type | Description |
|---|---|
C
|
The component instance for the entity. |
Source code in src/phids/engine/core/ecs.py
has_component(component_type: type[object]) -> bool
Return True if the entity has a component of the given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
type[object]
|
Component class/type to check for. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if present, False otherwise. |
Source code in src/phids/engine/core/ecs.py
remove_component(component_type: type[object]) -> None
Detach a component of the given type (no-op if absent).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
type[object]
|
Component class/type to remove. |
required |
phids.engine.core.flow_field
Flow-field gradient generation accelerated with Numba @njit for deterministic ecological simulation.
This module provides the Jacobi iteration solver for pathfinding. It strictly adheres to Numba compilation
constraints: no Python dictionaries, lists, or custom classes are used inside @njit kernels. All array
operations rely on pre-allocated buffers and contiguous layouts to prevent memory allocation latency
during the hot-path evaluation phase. The global attraction gradient is
computed by combining plant attraction and toxin repulsion base values, then propagating them
across the grid via a multi-iteration neighbourhood averaging pass with configurable decay. The
resulting scalar field is intended to populate GridEnvironment.flow_field, supporting O(1)
spatial hash-mediated swarm navigation and deterministic simulation of emergent plant-herbivore
dynamics. The design strictly adheres to data-oriented principles, using pre-allocated NumPy
arrays and truncating subnormal floats (values with absolute magnitude below 1e-4) to zero after
propagation to maintain computational efficiency. Camouflage is applied post-computation via
apply_camouflage, which attenuates the gradient at specific plant-occupied cells to model
constitutive gradient masking.
apply_camouflage(flow_field: npt.NDArray[np.float64], x: int, y: int, factor: float) -> None
Attenuate the flow-field gradient at cell (x, y) in-place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flow_field
|
NDArray[float64]
|
Mutable gradient array |
required |
x
|
int
|
The X-axis spatial grid coordinate. |
required |
y
|
int
|
The Y-axis spatial grid coordinate. |
required |
factor
|
float
|
Multiplier in [0, 1]; 0 = invisible, 1 = no attenuation. |
required |
Source code in src/phids/engine/core/flow_field.py
compute_flow_field(plant_energy: npt.NDArray[np.float64], apparent_nutrition_layer: npt.NDArray[np.float64], toxin_layers: npt.NDArray[np.float64], width: int, height: int, base: npt.NDArray[np.float64] | None = None, current: npt.NDArray[np.float64] | None = None, nxt: npt.NDArray[np.float64] | None = None, alpha: float = 1.0, beta: float = 1.0, decay: float = 0.6, truncate_threshold: float = 0.0001) -> npt.NDArray[np.float64]
Public wrapper: sum toxin layers and delegate to the Numba kernel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
plant_energy
|
NDArray[float64]
|
Shape |
required |
apparent_nutrition_layer
|
NDArray[float64]
|
Shape |
required |
toxin_layers
|
NDArray[float64]
|
Shape |
required |
width
|
int
|
The horizontal bounds of the simulation grid environment. |
required |
height
|
int
|
The vertical bounds of the simulation grid environment. |
required |
base
|
NDArray[float64] | None
|
Pre-allocated 2-D scratch array. |
None
|
current
|
NDArray[float64] | None
|
Pre-allocated 2-D scratch array. |
None
|
nxt
|
NDArray[float64] | None
|
Pre-allocated 2-D scratch array. |
None
|
alpha
|
float
|
Attractant weight. |
1.0
|
beta
|
float
|
Repellent weight. |
1.0
|
decay
|
float
|
Decay factor. |
0.6
|
truncate_threshold
|
float
|
Truncation threshold. |
0.0001
|
Returns:
| Type | Description |
|---|---|
NDArray[float64]
|
npt.NDArray[np.float64]: Flow-field gradient of shape |
Source code in src/phids/engine/core/flow_field.py
phids.engine.core.placement
Spatial placement generation helpers.
Provides functions to compute coordinate vectors for uniform, clustered, and banded initial entity distributions on the biotope grid.
generate_banded(width: int, height: int, band_count: int, orientation: str) -> list[tuple[int, int]]
Place entities in dense lines/stripes across the grid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
The horizontal bounds of the simulation grid environment. |
required |
height
|
int
|
The vertical bounds of the simulation grid environment. |
required |
band_count
|
int
|
Number of bands to split the grid into. |
required |
orientation
|
str
|
The orientation direction ('horizontal' or 'vertical'). |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int]]
|
A list of generated (x, y) coordinates. |
Source code in src/phids/engine/core/placement.py
generate_clustered(width: int, height: int, cluster_count: int, variance: float) -> list[tuple[int, int]]
Create clusters of entities using a simple Gaussian spread.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
The horizontal bounds of the simulation grid environment. |
required |
height
|
int
|
The vertical bounds of the simulation grid environment. |
required |
cluster_count
|
int
|
Number of clusters to generate. |
required |
variance
|
float
|
The spread variance scale around each cluster center. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int]]
|
A list of unique generated (x, y) coordinates. |
Source code in src/phids/engine/core/placement.py
generate_uniform(width: int, height: int, density: float) -> list[tuple[int, int]]
Randomly scatter entities across the grid based on density.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
The horizontal bounds of the simulation grid environment. |
required |
height
|
int
|
The vertical bounds of the simulation grid environment. |
required |
density
|
float
|
Target density ratio. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int]]
|
A list of generated (x, y) coordinates. |
Source code in src/phids/engine/core/placement.py
Engine systems
phids.engine.systems.lifecycle
Lifecycle system: plant growth, mycorrhizal network formation, reproduction, and death.
This module implements the first of three ordered per-tick simulation phases executed by the
PHIDS SimulationLoop. The lifecycle phase applies deterministic physiological dynamics to all
registered plant entities before any herbivore interactions are resolved, ensuring that the energy
state observed by the interaction and signaling phases reflects the current-tick growth outcome.
Per-tick growth increments the energy reserve of each plant by base_energy * (growth_rate / 100),
clamped to max_energy. Reproduction is attempted on each tick that satisfies the
reproduction_interval constraint and leaves sufficient energy surplus above seed_energy_cost;
the seed is dispersed to a randomly sampled polar coordinate within [seed_min_dist, seed_max_dist]
from the parent, and germination is rejected if the target cell is already occupied by any plant
entity registered in the spatial hash, preventing overcrowding without requiring dense distance
scans. Mycorrhizal root-network formation occurs at configurable intervals (mycorrhizal_growth_interval_ticks),
pairing adjacent plants (Manhattan distance 1) that share sufficient energy surplus above their
respective survival thresholds; each connection costs both participants connection_cost energy
units and is bidirectionally recorded in their PlantComponent.mycorrhizal_connections sets.
Plants whose energy falls below survival_threshold are unregistered from the spatial hash,
removed from the energy layer write buffer, and queued for bulk entity destruction via
ECSWorld.collect_garbage. Per-cause death counts are accumulated in the plant_death_causes
dict for telemetry attribution.
run_lifecycle(world: ECSWorld, env: GridEnvironment, tick: int, flora_species_params: dict[int, object], mycorrhizal_connection_cost: float = 1.0, mycorrhizal_growth_interval_ticks: int = 8, mycorrhizal_inter_species: bool = False, plant_death_causes: dict[str, int] | None = None) -> None
Execute one lifecycle tick: grow, connect, reproduce, and cull.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
ECSWorld
|
The ECS world registry. |
required |
env
|
GridEnvironment
|
The GridEnvironment instance. |
required |
tick
|
int
|
Current simulation tick index. |
required |
flora_species_params
|
dict[int, object]
|
Mapping of species_id to species parameters. |
required |
mycorrhizal_connection_cost
|
float
|
Energy cost per new root connection. |
1.0
|
mycorrhizal_growth_interval_ticks
|
int
|
Ticks between new root-growth attempts. At most one new link is created per attempt. |
8
|
mycorrhizal_inter_species
|
bool
|
Allow inter-species root connections. |
False
|
plant_death_causes
|
dict[str, int] | None
|
Mapping of death causes to their respective counts. |
None
|
Source code in src/phids/engine/systems/lifecycle.py
phids.engine.systems.interaction.feeding
Herbivory logic for swarms feeding on flora in the interaction system.
phids.engine.systems.interaction.metabolism
Metabolism, reproduction, and mitosis logic for swarms in the interaction system.
phids.engine.systems.interaction.movement
Movement and pathfinding logic for swarms in the interaction system.
phids.engine.systems.interaction.population
Population utilities for interaction system.
phids.engine.systems.signaling
Signaling system: substance synthesis, activation, emission, diffusion, and toxin effects.
This module implements the third and final per-tick simulation phase of the PHIDS engine, governing the full lifecycle of volatile organic compound (VOC) signals and defensive toxins. The signaling phase is executed after both the lifecycle and interaction phases have committed their energy mutations, ensuring that plant survival status and herbivore co-location data reflect the current tick's resolved state before chemical-defense decisions are made.
The phase proceeds through six ordered sub-steps. First, orphaned substance entities whose owner
plants were destroyed in earlier phases are garbage-collected. Second, trigger-condition trees are
evaluated for each living plant against the per-cell herbivore census index
(_build_swarm_population_index): direct herbivore co-presence (herbivore_presence nodes) or
indirect conditions (substance_active, environmental_signal, all_of, any_of
composites) can independently satisfy a trigger. Third, synthesis countdown timers are decremented
for triggered substances; substances with zero remaining countdown and satisfied activation
conditions are transitioned to active state. Fourth, active substances emit concentration
increments (SUBSTANCE_EMIT_RATE) into signal or toxin environment layers, deduct
energy_cost_per_tick from the owner plant, relay VOC signals through mycorrhizal root
networks, and record toxin property aggregates for batch application. Fifth, toxin effects
(lethality and repellency) are applied to all co-located swarms via _apply_toxin_to_swarms,
with immediate spatial-hash deregistration and garbage collection of swarms annihilated by
chemical defense. Sixth, Gaussian diffusion is delegated to GridEnvironment.diffuse_signals,
which convolves each airborne signal layer with the pre-computed kernel and applies the
SIGNAL_EPSILON sparsity threshold to eliminate subnormal tail values.
run_signaling(world: ECSWorld, env: GridEnvironment, trigger_conditions: dict[int, list[TriggerConditionSchema]], mycorrhizal_inter_species: bool, signal_velocity: int, tick: int, plant_death_causes: dict[str, int] | None = None, substance_emit_rate: float = 0.1, signal_decay_factor: float = 0.85) -> None
Execute one signaling tick, handling synthesis, emission and diffusion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
ECSWorld
|
The central ECSWorld instance containing all entity component mappings and active systems. |
required |
env
|
GridEnvironment
|
Grid environment holding signal/toxin layers. |
required |
trigger_conditions
|
dict[int, list[TriggerConditionSchema]]
|
Mapping of flora species_id to trigger schemas. |
required |
mycorrhizal_inter_species
|
bool
|
Whether inter-species mycorrhizal signaling is permitted. |
required |
signal_velocity
|
int
|
Ticks per hop for root-network relays. |
required |
tick
|
int
|
Current simulation tick. |
required |
plant_death_causes
|
dict[str, int] | None
|
Mapping of death causes to their respective counts. |
None
|
substance_emit_rate
|
float
|
Concentration increment added per tick when an active SubstanceComponent emits. Defaults to 0.1 (module-level constant value). |
0.1
|
signal_decay_factor
|
float
|
Per-tick airborne signal retention after Gaussian diffusion (0.0-1.0). Defaults to 0.85 (module-level constant value). |
0.85
|
Source code in src/phids/engine/systems/signaling/__init__.py
Design Space Exploration (DSE)
phids.analytics.bio_database
Biological database component for matching traits and parameters.
Provides Pydantic models for flora and herbivore profiles, and the main BioDatabase service for Mode A matching and Mode B bounds query logic.
BioDatabase
Provides Generative (Mode A) and Constrained (Mode B) database queries.
Attributes:
| Name | Type | Description |
|---|---|---|
data |
The validated BioDatabaseModel payload loaded from JSON. |
Source code in src/phids/analytics/bio_database.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
__init__(db_path: str = 'src/phids/analytics/bio_database.json', data: BioDatabaseModel | None = None)
Initialise the bio database from the given JSON file path or model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
str
|
Path to the bio_database.json file. |
'src/phids/analytics/bio_database.json'
|
data
|
BioDatabaseModel | None
|
Explicit BioDatabaseModel instance (overrides db_path). |
None
|
Source code in src/phids/analytics/bio_database.py
from_duckdb(db_path: str = 'src/phids/analytics/bio_database.duckdb') -> BioDatabase
classmethod
Initialise the bio database directly from the DuckDB file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
str
|
Path to the bio_database.duckdb file. |
'src/phids/analytics/bio_database.duckdb'
|
Returns:
| Type | Description |
|---|---|
BioDatabase
|
A new BioDatabase instance populated from DuckDB. |
Raises:
| Type | Description |
|---|---|
ImportError
|
If duckdb is not installed. |
Source code in src/phids/analytics/bio_database.py
mode_a_match_flora(target_vector: list[float]) -> str
Matches a target vector to the closest database flora species name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_vector
|
list[float]
|
A list of floats containing [growth_rate, max_energy, seed_cost]. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name of the closest flora species found in the database. |
Source code in src/phids/analytics/bio_database.py
mode_a_match_herbivore(target_vector: list[float]) -> str
Matches a target vector to the closest database herbivore species name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_vector
|
list[float]
|
A list of floats containing [metabolism_upkeep, mitosis_threshold]. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name of the closest herbivore species found in the database. |
Source code in src/phids/analytics/bio_database.py
mode_b_get_bounds_flora(species_name: str) -> dict[str, tuple[float, float]]
Returns ±20% mutation bounds for a specific flora species.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
species_name
|
str
|
Name of the flora species to lookup. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, tuple[float, float]]
|
A dictionary mapping trait keys to (min_bound, max_bound) tuples. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the species_name is not found in the database. |
Source code in src/phids/analytics/bio_database.py
mode_b_get_bounds_herbivore(species_name: str) -> dict[str, tuple[float, float]]
Returns ±20% mutation bounds for a specific herbivore species.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
species_name
|
str
|
Name of the herbivore species to lookup. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, tuple[float, float]]
|
A dictionary mapping trait keys to (min_bound, max_bound) tuples. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the species_name is not found in the database. |
Source code in src/phids/analytics/bio_database.py
BioDatabaseModel
Bases: BaseModel
Container model matching the JSON structure of the biological database.
Attributes:
| Name | Type | Description |
|---|---|---|
flora |
dict[str, FloraProfile]
|
Dictionary mapping flora species names to their profiles. |
herbivores |
dict[str, HerbivoreProfile]
|
Dictionary mapping herbivore species names to their profiles. |
Source code in src/phids/analytics/bio_database.py
FloraProfile
Bases: BaseModel
Profile parameters representing a specific flora species.
Attributes:
| Name | Type | Description |
|---|---|---|
growth_rate |
float
|
Photosynthetic growth rate percentage per tick. |
max_energy |
float
|
Maximum physiological energy capacity. |
survival_threshold |
float
|
Energy reserve threshold below which the plant dies. |
seed_cost |
float
|
Caloric cost to reproduce/drop a seed. |
seed_dispersion_radius |
float
|
Maximum radius for seed dispersal. |
passive_defenses |
dict[str, float]
|
Morphological defense configuration. |
Source code in src/phids/analytics/bio_database.py
HerbivoreProfile
Bases: BaseModel
Profile parameters representing a specific herbivore species.
Attributes:
| Name | Type | Description |
|---|---|---|
metabolism_upkeep |
float
|
Tick-by-tick base metabolic energy cost. |
consumption_rate |
float
|
Feeding consumption rate per tick. |
mitosis_threshold |
float
|
Energy threshold required to undergo mitosis. |
split_ratio |
float
|
Energy and population allocation ratio on split. |
resistances |
dict[str, float]
|
Herbivore resistances to passive plant defenses. |
Source code in src/phids/analytics/bio_database.py
phids.analytics.dse_genotype
Genotype definitions representing the ecosystem design space.
Defines structural and parametric genes representing discrete choices and continuous variables of the Mixed-Integer Non-Linear Programming (MINLP) genotype.
DSEGenotype
Bases: BaseModel
The complete Hierarchical MINLP Genotype representation.
Attributes:
| Name | Type | Description |
|---|---|---|
scenario_name |
str
|
Name of the candidate scenario. |
structural |
StructuralGenes
|
The structural/discrete genes component. |
parametric |
ParametricGenes
|
The parametric/continuous genes component. |
Source code in src/phids/analytics/dse_genotype.py
ParametricGenes
Bases: BaseModel
Continuous, tuneable float values representing biological traits.
Attributes:
| Name | Type | Description |
|---|---|---|
flora_traits |
dict[str, FloraProfile]
|
Dictionary mapping flora species to their trait profiles. |
herbivore_traits |
dict[str, HerbivoreProfile]
|
Dictionary mapping herbivore species to their trait profiles. |
Source code in src/phids/analytics/dse_genotype.py
StructuralGenes
Bases: BaseModel
Discrete, structural choices of the ecosystem.
Attributes:
| Name | Type | Description |
|---|---|---|
flora_placement |
PlacementStrategy
|
Spatial distribution strategy for flora. |
herbivore_placement |
PlacementStrategy
|
Spatial distribution strategy for herbivores. |
diet_matrix |
list[list[bool]]
|
A 16x16 boolean matrix defining diet compatibility. |
trigger_matrix |
list[list[int]]
|
A 16x16 integer mapping of defensive trigger rules. |
Source code in src/phids/analytics/dse_genotype.py
validate_rule_of_16(matrix: list[list[bool]] | list[list[int]]) -> list[list[bool]] | list[list[int]]
classmethod
Ensure matrix dimensions do not exceed 16x16.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
matrix
|
list[list[bool]] | list[list[int]]
|
The nested list matrix to validate. |
required |
Returns:
| Type | Description |
|---|---|
list[list[bool]] | list[list[int]]
|
The validated matrix. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the matrix violates the 16x16 dimension limits. |
Source code in src/phids/analytics/dse_genotype.py
phids.analytics.dse_optimizer
NSGA-II multi-objective optimizer for Design Space Exploration (DSE).
Contains class and methods to run a genetic algorithm over the MINLP genotype to find stable, high-biomass, and diverse plant-herbivore configurations.
DSEOptimizer
Multi-objective NSGA-II optimizer for ecosystem exploration.
Attributes:
| Name | Type | Description |
|---|---|---|
base_config |
Base template simulation configuration. |
|
pop_size |
Size of the genetic algorithm population. |
|
generations |
Number of generations to iterate. |
|
toolbox |
DEAP toolbox for genetic operators registration. |
Source code in src/phids/analytics/dse_optimizer.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | |
__init__(base_config: SimulationConfig, pop_size: int = 50, generations: int = 20)
Initialize the optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_config
|
SimulationConfig
|
The template simulation configuration schema. |
required |
pop_size
|
int
|
The number of individuals in the population. Defaults to 50. |
50
|
generations
|
int
|
Number of evolutionary generations to run. Defaults to 20. |
20
|
Source code in src/phids/analytics/dse_optimizer.py
evaluate_candidate(individual: creator.Individual) -> tuple[float, float, float]
async
Headless evaluation of a single MINLP Genotype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
individual
|
Individual
|
The DEAP individual holding a candidate genotype. |
required |
Returns:
| Type | Description |
|---|---|
tuple[float, float, float]
|
A tuple of float fitnesses: (longevity, stability, dispersion). |
Source code in src/phids/analytics/dse_optimizer.py
evaluate_candidate_sync(individual: creator.Individual) -> tuple[float, float, float]
Synchronous wrapper for DEAP compatibility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
individual
|
Individual
|
The DEAP individual to evaluate. |
required |
Returns:
| Type | Description |
|---|---|
tuple[float, float, float]
|
A tuple of float fitnesses: (longevity, stability, dispersion). |
Source code in src/phids/analytics/dse_optimizer.py
run(sync_callback: Callable[[dict[str, Any], list[SimulationConfig]], None] | None = None, cancel_event: Any = None) -> list[creator.Individual]
Run the NSGA-II optimization loop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sync_callback
|
Callable[[dict[str, Any], list[SimulationConfig]], None] | None
|
Optional callable callback dispatched with Pareto front telemetry. |
None
|
cancel_event
|
Any
|
Optional asyncio/multiprocessing event to trigger early cancellation. |
None
|
Returns:
| Type | Description |
|---|---|
list[Individual]
|
The final evaluated population list of individuals. |
Source code in src/phids/analytics/dse_optimizer.py
phids.analytics.dse_pruning
Analytical pre-pruning system for Design Space Exploration (DSE).
Contains validators to filter out structurally and thermodynamically unviable genotypes before running computationally expensive simulations.
AnalyticalPruner
Executes Stage 1 of the DSE: Pre-Exploration Pruning via Analytical Bounds.
Eliminates infeasible MINLP configurations instantly to save CPU cycles.
Source code in src/phids/analytics/dse_pruning.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
evaluate_feasibility(genotype: DSEGenotype) -> bool
staticmethod
Returns True if the genotype is mathematically viable, False if doomed.
Performs: 1. Structural diet checks (ensures no herbivore starves by design). 2. Individual caloric conservation checks. 3. Global thermodynamic bounds checks. 4. Flora self-termination (seed cost vs max yield) checks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
genotype
|
DSEGenotype
|
The candidate DSEGenotype to evaluate. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the genotype is mathematically viable, False otherwise. |
Source code in src/phids/analytics/dse_pruning.py
phids.analytics.tuning
Automated hyperparameter tuning pipeline using Differential Evolution.
This module provides the :class:TrophicOptimizer to autonomously search for
stable Lotka-Volterra parameters over complex multi-species scenarios.
TrophicOptimizer
Hyperparameter tuner utilizing Scipy's Differential Evolution.
Mutates a given scenario blueprint to find a parameter regime that maximizes ecosystem survival over a large number of ticks, using population CV as a secondary stability metric.
Attributes:
| Name | Type | Description |
|---|---|---|
blueprint |
The baseline scenario configuration. |
|
runs_per_eval |
Number of concurrent stochastic runs per evaluation. |
|
max_ticks |
Target simulation duration per run. |
|
bounds |
list[tuple[float, float]]
|
List of min/max boundary value tuples for each parameter. |
param_mapping |
list[tuple[str, int, str]]
|
Mapping of float indices to blueprint keys. |
Source code in src/phids/analytics/tuning.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
__init__(blueprint: dict[str, Any], runs_per_eval: int = 20, max_ticks: int = 2500) -> None
Initialize the optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blueprint
|
dict[str, Any]
|
The baseline scenario configuration. |
required |
runs_per_eval
|
int
|
Number of concurrent stochastic runs per evaluation. |
20
|
max_ticks
|
int
|
Target simulation duration per run. |
2500
|
Source code in src/phids/analytics/tuning.py
optimize() -> dict[str, Any]
Run the Differential Evolution optimization loop.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The optimized configuration blueprint as a dictionary. |
Source code in src/phids/analytics/tuning.py
Telemetry and replay
phids.telemetry.analytics
Telemetry analytics: accumulate per-tick Lotka-Volterra metrics into a Polars DataFrame.
The :class:TelemetryRecorder accumulates per-tick population and energy metrics into an
in-memory row buffer and exposes a lazily-constructed :class:polars.DataFrame for
downstream export, Chart.js serialisation, and statistical aggregation. Each recorded tick
captures both aggregate scalars (total flora energy, total herbivore population) and
granular per-species dictionaries (population and aggregate energy keyed by
species_id), thereby enabling precise Lotka-Volterra phase-space visualisation and
Monte Carlo batch evaluation.
The per-species data is accumulated via defaultdict accumulators inside
:meth:TelemetryRecorder.record so that sparse or absent species naturally resolve to
zero without requiring sentinel guards. Active defense-maintenance costs are also
attributed per flora species_id by querying
:class:~phids.engine.components.substances.SubstanceComponent entities whose active
flag is set, summing their energy_cost_per_tick contribution. This diagnostic
facilitates identification of runaway defense-maintenance scenarios in which an entire
connected mycorrhizal network commits metabolic resources to sustained chemical defense
under persistent herbivore pressure.
The :attr:TelemetryRecorder.dataframe property materialises a fully rectangular Polars
DataFrame that preserves per-species breakdowns as typed scalar columns
(plant_{id}_pop, plant_{id}_energy, defense_cost_{id},
swarm_{id}_pop). This columnar representation exposes the per-species data through
the primary CSV and NDJSON export routes without requiring callers to reach into the raw
_rows buffer or invoke the auxiliary
:func:~phids.telemetry.export.core.telemetry_to_dataframe pandas-conversion helper. Species
identifiers observed across the accumulated session are unioned and sorted before columns
are written, guaranteeing a consistent column order even when individual ticks contain
sparse species sets.
TelemetryRecorder
Accumulate per-tick Lotka-Volterra metrics into a Polars DataFrame.
The recorder appends one row per tick and materialises a lazily-built Polars
DataFrame containing aggregate scalars together with per-species flat columns.
Aggregate fields comprise tick, total_flora_energy, flora_population,
herbivore_clusters, herbivore_population, and the five per-tick plant death
cause counts (death_reproduction, death_mycorrhiza,
death_defense_maintenance, death_herbivore_feeding,
death_background_deficit). Per-species breakdowns are exposed as typed Polars
scalar columns following the naming convention plant_{id}_pop,
plant_{id}_energy, swarm_{id}_pop, and defense_cost_{id}, where
{id} denotes the integer species_id. Missing species in a given tick are
zero-filled to guarantee a fully rectangular DataFrame suitable for vectorised
statistical operations and direct CSV or NDJSON export.
Source code in src/phids/telemetry/analytics.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | |
dataframe: pl.DataFrame
property
Return recorded metrics as a Polars DataFrame with per-species flat columns (lazily built).
Per-species dictionary accumulators stored in each row's
plant_pop_by_species, plant_energy_by_species,
swarm_pop_by_species, and defense_cost_by_species fields are
flattened into typed Polars scalar columns named plant_{id}_pop
(Int64), plant_{id}_energy (Float64), swarm_{id}_pop
(Int64), and defense_cost_{id} (Float64) respectively.
Missing species values for a given tick are zero-filled, ensuring the
resulting DataFrame is fully rectangular and free of null entries.
All species identifiers observed across the full retention window are unioned and sorted prior to column construction, so that the column layout is deterministic and consistent even when individual ticks contain sparse species sets due to extinction or delayed colonisation events.
The empty-state DataFrame (no recorded ticks) retains only the stable aggregate schema; per-species columns are added dynamically once at least one tick has been recorded and at least one species has been observed, reflecting the inherently dynamic cardinality of the species pool across independent simulation sessions.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pl.DataFrame: DataFrame containing aggregate and per-species flat |
DataFrame
|
telemetry columns for all accumulated ticks. |
__init__(max_rows: int = MAX_TELEMETRY_TICKS) -> None
Create a TelemetryRecorder with empty in-memory buffers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_rows
|
int
|
Maximum in-memory tick rows retained in the rolling window. |
MAX_TELEMETRY_TICKS
|
Source code in src/phids/telemetry/analytics.py
get_latest_metrics() -> TelemetryRow | None
Return the latest recorded telemetry row, if available.
Returns:
| Type | Description |
|---|---|
TelemetryRow | None
|
TelemetryRow | None: Most recent metrics row or |
Source code in src/phids/telemetry/analytics.py
get_species_ids() -> dict[str, list[int]]
Return the union of all flora and herbivore species ids seen so far.
Scans all accumulated rows to collect every species id that has appeared at least once in the simulation history, enabling Chart.js dataset generation to create series for species that may have gone extinct mid-simulation.
Returns:
| Type | Description |
|---|---|
dict[str, list[int]]
|
dict[str, list[int]]: Keys |
dict[str, list[int]]
|
each mapping to a sorted list of integer species identifiers. |
Source code in src/phids/telemetry/analytics.py
record(world: ECSWorld, tick: int, plant_death_causes: dict[str, int] | None = None, tick_metrics: TickMetrics | None = None) -> None
Snapshot current ECS metrics and append to the internal buffer.
Iterates over all :class:~phids.engine.components.plant.PlantComponent,
:class:~phids.engine.components.swarm.SwarmComponent, and active
:class:~phids.engine.components.substances.SubstanceComponent entities
to build aggregate and per-species counters. All per-species keys are
written unconditionally (with zero defaults) so that downstream pandas
and Polars operations encounter a fully rectangular schema without null
values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
ECSWorld
|
The ECS world to sample entity components from. |
required |
tick
|
int
|
Current simulation tick index. |
required |
plant_death_causes
|
dict[str, int] | None
|
Per-tick plant death diagnostics keyed by cause. |
None
|
tick_metrics
|
TickMetrics | None
|
Optional pre-collected tick metrics; if omitted, they are gathered from the world. |
None
|
Source code in src/phids/telemetry/analytics.py
reset() -> None
Clear accumulated telemetry and reset internal cache.
phids.telemetry.conditions
Termination condition evaluators (Z1-Z7) for deterministic simulation halting.
This module implements the rule-based termination logic that determines when a PHIDS simulation run should end. Seven named termination conditions are supported: Z1 halts when the configured maximum tick count is reached; Z2 halts when a specified flora species goes extinct (zero remaining plant entities with that species identifier); Z3 halts when all flora entities are extinct; Z4 and Z5 apply the analogous extinction conditions to herbivore species; Z6 halts when total aggregate flora energy exceeds a configured upper bound (modelling uncontrolled biomass expansion); and Z7 halts when total aggregate herbivore population exceeds a configured upper bound (modelling herbivore outbreak conditions).
All conditions are evaluated by a single pass over live ECS component queries, keeping the
computational cost proportional to the number of living entities rather than to any grid
dimension. Conditions with negative threshold values are disabled by convention, enabling
selective activation of any subset of the seven rules for a given scenario. The
:class:TerminationResult dataclass encodes both the terminated flag and a human-readable
reason string, which is logged and surfaced through the REST API /api/simulation/status
endpoint when the simulation halts.
TerminationResult
dataclass
Result returned by :func:check_termination.
Attributes:
| Name | Type | Description |
|---|---|---|
terminated |
bool
|
True when a termination condition has been met. |
reason |
str
|
Human-readable explanation for termination. |
Source code in src/phids/telemetry/conditions.py
check_termination(world: ECSWorld, tick: int, max_ticks: int, z2_flora_species: int = -1, z3_check_all_flora: bool = True, z4_herbivore_species: int = -1, z5_check_all_herbivores: bool = True, z6_max_flora_energy: float = -1.0, z7_max_total_herbivore_population: int = -1, tick_metrics: TickMetrics | None = None) -> TerminationResult
Evaluate termination conditions and return the first triggered one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
ECSWorld
|
The central ECSWorld instance containing all entity component mappings and active systems. |
required |
tick
|
int
|
Current simulation tick. |
required |
max_ticks
|
int
|
Z1 - maximum allowed ticks (halt when reached). |
required |
z2_flora_species
|
int
|
Species id that triggers Z2 on extinction (-1 disables). |
-1
|
z3_check_all_flora
|
bool
|
If True, halt when all flora are extinct (Z3). |
True
|
z4_herbivore_species
|
int
|
Species id that triggers Z4 on extinction (-1 disables). |
-1
|
z5_check_all_herbivores
|
bool
|
If True, halt when all herbivores are extinct (Z5). |
True
|
z6_max_flora_energy
|
float
|
Aggregate flora energy threshold for Z6 (-1 disables). |
-1.0
|
z7_max_total_herbivore_population
|
int
|
Aggregate herbivore population threshold for Z7 (-1 disables). |
-1
|
tick_metrics
|
TickMetrics | None
|
Optional pre-computed tick metrics. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
TerminationResult |
TerminationResult
|
Object indicating whether termination occurred and why. |
Source code in src/phids/telemetry/conditions.py
phids.telemetry.tick_metrics
Tick-level ECS aggregation structures for shared telemetry and termination evaluation.
This module defines the :class:TickMetrics dataclass and the corresponding
:func:collect_tick_metrics helper, which execute a single deterministic pass
over live ECS components to materialize scalar and per-species aggregates used by
both telemetry recording and termination-condition evaluation. The explicit
shared-aggregation contract eliminates duplicated component scans in the hot
simulation loop while preserving strict data-oriented semantics.
The collector accumulates flora and herbivore populations, aggregate energies, species-presence sets, and active defense-maintenance costs attributed to flora species through owner-linked substance components. These metrics encode both the biological observables (population size, energetic state, active chemical maintenance burden) and the computational invariants required by PHIDS phase ordering, thereby allowing telemetry and termination logic to observe an identical post-system world snapshot without divergent sampling artifacts.
TickMetrics
dataclass
Shared per-tick aggregate metrics for telemetry and termination consumers.
Attributes:
| Name | Type | Description |
|---|---|---|
flora_population |
int
|
Number of live flora entities. |
herbivore_clusters |
int
|
Number of live herbivore swarm entities. |
herbivore_population |
int
|
Total herbivore individuals across all swarms. |
total_flora_energy |
float
|
Sum of flora energy across all live plants. |
total_herbivore_population |
int
|
Alias for termination readability. |
flora_alive |
bool
|
Whether any flora entities are alive. |
herbivores_alive |
bool
|
Whether any herbivore swarms are alive. |
flora_species_alive |
set[int]
|
Set of live flora species IDs. |
herbivore_species_alive |
set[int]
|
Set of live herbivore species IDs. |
plant_pop_by_species |
dict[int, int]
|
Flora population counts keyed by species ID. |
plant_energy_by_species |
dict[int, float]
|
Flora aggregate energy keyed by species ID. |
swarm_pop_by_species |
dict[int, int]
|
Herbivore population keyed by species ID. |
defense_cost_by_species |
dict[int, float]
|
Active defense-maintenance costs keyed by flora species ID. |
Source code in src/phids/telemetry/tick_metrics.py
collect_tick_metrics(world: ECSWorld) -> TickMetrics
Aggregate one shared snapshot of live ECS metrics from the current world.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
ECSWorld
|
ECS world sampled after ordered system execution for the tick. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
TickMetrics |
TickMetrics
|
Shared aggregate metrics suitable for telemetry and termination. |
Source code in src/phids/telemetry/tick_metrics.py
phids.telemetry.export.core
Telemetry export core helpers.
Provides common data transformation utilities, color palettes, and filtering logic for exporting telemetry rows to LaTeX, TikZ, CSV, JSON, and PNG formats.
aggregate_to_dataframe(aggregate: Mapping[str, object], *, flora_names: dict[int, str] | None = None, herbivore_names: dict[int, str] | None = None) -> pd.DataFrame
Convert a batch aggregate summary dict to a wide pandas DataFrame.
Constructs a per-tick DataFrame from the mean and standard deviation arrays
stored inside the aggregate summary produced by
:func:~phids.engine.batch.aggregate_batch_telemetry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
aggregate
|
Mapping[str, object]
|
Dict with keys |
required |
flora_names
|
dict[int, str] | None
|
Optional display name mapping for flora species. |
None
|
herbivore_names
|
dict[int, str] | None
|
Optional display name mapping for herbivore species. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Wide-format DataFrame ready for export. |
Source code in src/phids/telemetry/export/core.py
decimate_dataframe(df: pd.DataFrame, tick_interval: int) -> pd.DataFrame
Return a tick-decimated DataFrame using stride semantics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
The structured Polars DataFrame constructed from recorded telemetry row objects. |
required |
tick_interval
|
int
|
Row stride; values below 1 are treated as 1. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Decimated DataFrame. |
Source code in src/phids/telemetry/export/core.py
filter_dataframe_columns(df: pd.DataFrame, columns: str | None) -> pd.DataFrame
Return a DataFrame restricted to requested columns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Input pandas DataFrame. |
required |
columns
|
str | None
|
Optional CSV column list. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Filtered DataFrame containing only existing columns. |
Source code in src/phids/telemetry/export/core.py
filter_telemetry_rows(rows: TelemetryRows, *, flora_ids: str | None = None, herbivore_ids: str | None = None) -> TelemetryRows
Filter per-species nested telemetry dictionaries by id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
TelemetryRows
|
A list of recorded telemetry frame dictionaries sequentially captured during the simulation execution. |
required |
flora_ids
|
str | None
|
Optional CSV flora species-id list. |
None
|
herbivore_ids
|
str | None
|
Optional CSV herbivore species-id list. |
None
|
Returns:
| Type | Description |
|---|---|
TelemetryRows
|
Row list with filtered species dictionaries. |
Source code in src/phids/telemetry/export/core.py
telemetry_to_dataframe(rows: TelemetryRows) -> pd.DataFrame
Flatten per-species nested dicts from raw telemetry rows into a pandas DataFrame.
Converts the list of row dicts accumulated by
:class:~phids.telemetry.analytics.TelemetryRecorder into a wide-format
pandas DataFrame. Each per-species nested dictionary (plant_pop_by_species,
plant_energy_by_species, swarm_pop_by_species, defense_cost_by_species)
is exploded into individual columns named plant_{id}_pop, plant_{id}_energy,
swarm_{id}_pop, and defense_cost_{id} respectively. Missing species in a
given tick are filled with zero, ensuring a fully rectangular output suitable for
vectorised statistical operations and LaTeX table generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
TelemetryRows
|
Raw row list from |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Wide-format DataFrame with one row per tick and one column per |
DataFrame
|
scalar metric or per-species measurement. |
Source code in src/phids/telemetry/export/core.py
phids.telemetry.export.latex
Telemetry export to LaTeX tabular environment.
Formats telemetry dataframes into booktabs LaTeX tabular tables suitable for academic papers.
export_bytes_tex_table(rows: TelemetryRows, *, columns: str | None = None, include_flora_ids: str | None = None, include_herbivore_ids: str | None = None, tick_interval: int = 1) -> bytes
Render the telemetry rows as a booktabs LaTeX tabular environment.
Flattens per-species dicts into a wide pandas DataFrame via
:func:telemetry_to_dataframe, then serialises to LaTeX using
DataFrame.to_latex(index=False), which emits
\\toprule, \\midrule, and \\bottomrule rules consistent with
the booktabs LaTeX package conventions expected in peer-reviewed journals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
TelemetryRows
|
Raw telemetry rows from |
required |
columns
|
str | None
|
Optional comma-separated list of columns to include. |
None
|
include_flora_ids
|
str | None
|
Optional comma-separated list of flora species IDs to filter. |
None
|
include_herbivore_ids
|
str | None
|
Optional comma-separated list of herbivore species IDs to filter. |
None
|
tick_interval
|
int
|
Integer tick interval to decimate rows. |
1
|
Returns:
| Name | Type | Description |
|---|---|---|
bytes |
bytes
|
UTF-8 encoded LaTeX |
Source code in src/phids/telemetry/export/latex.py
phids.telemetry.export.png
Telemetry export to PNG images.
Renders publication-quality charts (time series, phase space, defense economy, biomass stacks, and survival probability) using Matplotlib headless Agg backend to raw PNG bytes.
generate_png_bytes(rows: TelemetryRows, plot_type: str = 'timeseries', *, flora_names: dict[int, str] | None = None, herbivore_names: dict[int, str] | None = None, plant_species_id: int = 0, herbivore_species_id: int = 0, include_flora_ids: str | None = None, include_herbivore_ids: str | None = None, title: str | None = None, x_label: str | None = None, y_label: str | None = None, x_max: float | None = None, y_max: float | None = None, dpi: int = 150) -> bytes
Render a matplotlib chart to PNG bytes using the headless Agg backend.
Supports five plot_type modes:
"timeseries"- Overlaid line chart with one series per flora and herbivore species, sharing a common tick x-axis and a left y-axis for population counts."phasespace"- Lotka-Volterra phase-space scatter withshowLine=Truesemantics."defense_economy"- Line chart plotting defense cost divided by total energy capacity per flora species."biomass_stack"- Stacked area chart approximating carrying capacity share."survival_probability"- Aggregate batch survival probability (requires ensemble rows).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
TelemetryRows
|
A list of recorded telemetry frame dictionaries sequentially captured during the simulation execution. |
required |
plot_type
|
str
|
Output chart type ( |
'timeseries'
|
flora_names
|
dict[int, str] | None
|
Optional dictionary mapping flora species ids to display names. |
None
|
herbivore_names
|
dict[int, str] | None
|
Optional dictionary mapping herbivore ids to display names. |
None
|
plant_species_id
|
int
|
Flora species id to use for the x-axis in phasespace mode. |
0
|
herbivore_species_id
|
int
|
Herbivore species id to use for the y-axis in phasespace. |
0
|
include_flora_ids
|
str | None
|
Optional CSV list of flora ids to keep (filters out others). |
None
|
include_herbivore_ids
|
str | None
|
Optional CSV list of herbivore ids to keep. |
None
|
title
|
str | None
|
Optional override for the matplotlib title. |
None
|
x_label
|
str | None
|
Optional override for the matplotlib x-axis label. |
None
|
y_label
|
str | None
|
Optional override for the matplotlib y-axis label. |
None
|
x_max
|
float | None
|
Optional upper bound for the x-axis (ignored if zero or None). |
None
|
y_max
|
float | None
|
Optional upper bound for the y-axis (ignored if zero or None). |
None
|
dpi
|
int
|
Dots-per-inch scaling factor for rasterization. |
150
|
Returns:
| Type | Description |
|---|---|
bytes
|
Raw PNG-encoded bytes of the rendered figure. |
Source code in src/phids/telemetry/export/png.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
phids.telemetry.export.structured
Telemetry export to structured formats.
Exports telemetry dataframes into structured formats such as CSV and NDJSON.
export_bytes_csv(df: pl.DataFrame) -> bytes
Return the telemetry DataFrame serialized as CSV bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Polars DataFrame to serialize. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bytes |
bytes
|
CSV-encoded bytes. |
Source code in src/phids/telemetry/export/structured.py
export_bytes_json(df: pl.DataFrame) -> bytes
Return the telemetry DataFrame serialized as NDJSON bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Polars DataFrame to serialize. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bytes |
bytes
|
NDJSON-encoded bytes. |
Source code in src/phids/telemetry/export/structured.py
export_csv(df: pl.DataFrame, path: str | Path) -> None
Write the telemetry DataFrame to a CSV file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Polars DataFrame produced by the telemetry recorder. |
required |
path
|
str | Path
|
Destination file path. |
required |
Source code in src/phids/telemetry/export/structured.py
export_json(df: pl.DataFrame, path: str | Path) -> None
Write the telemetry DataFrame to a newline-delimited JSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Polars DataFrame produced by the telemetry recorder. |
required |
path
|
str | Path
|
Destination file path. |
required |
Source code in src/phids/telemetry/export/structured.py
phids.telemetry.export.tikz
Telemetry export to PGFPlots TikZ code.
Generates self-contained tikzpicture environments containing PGFPlots chart specifications for LaTeX papers (time series, phase spaces, defense economy ratio, biomass stack, survival probability).
generate_tikz_str(rows: TelemetryRows, plot_type: str = 'timeseries', *, flora_names: dict[int, str] | None = None, herbivore_names: dict[int, str] | None = None, plant_species_id: int = 0, herbivore_species_id: int = 0, include_flora_ids: str | None = None, include_herbivore_ids: str | None = None, title: str | None = None, x_label: str | None = None, y_label: str | None = None, x_max: float | None = None, y_max: float | None = None) -> str
Generate a PGFPlots LaTeX source string for publication-quality figures.
Produces a self-contained tikzpicture environment using the pgfplots
package. The output does not require the tikzplotlib library; instead,
coordinates are injected directly into \\addplot commands via a
\\pgfplotstable-compatible inline coordinate format. This approach ensures
compatibility with any LaTeX installation providing pgfplots >= 1.16.
The generated code is intended for compilation with pdflatex, xelatex,
or lualatex after pasting into a document preamble that includes
\\usepackage{pgfplots} and \\pgfplotsset{compat=1.18}.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
TelemetryRows
|
Raw telemetry rows from |
required |
plot_type
|
str
|
Chart mode - |
'timeseries'
|
flora_names
|
dict[int, str] | None
|
Optional display names keyed by flora species id. |
None
|
herbivore_names
|
dict[int, str] | None
|
Optional display names keyed by herbivore species id. |
None
|
plant_species_id
|
int
|
Flora species id for phase-space x-axis. |
0
|
herbivore_species_id
|
int
|
Herbivore species id for phase-space y-axis. |
0
|
include_flora_ids
|
str | None
|
Optional comma-separated list of flora species IDs to filter. |
None
|
include_herbivore_ids
|
str | None
|
Optional comma-separated list of herbivore species IDs to filter. |
None
|
title
|
str | None
|
Optional custom chart title. |
None
|
x_label
|
str | None
|
Optional custom x-axis label. |
None
|
x_max
|
float | None
|
Optional custom x-axis maximum value. |
None
|
y_label
|
str | None
|
Optional custom y-axis label. |
None
|
y_max
|
float | None
|
Optional custom y-axis maximum value. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
LaTeX source code for a complete |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/phids/telemetry/export/tikz.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
phids.io.zarr_replay
High-performance chunked replay storage using Zarr for PHIDS simulation snapshots.
This module implements a Zarr-based replay backend that provides identical ReplayBuffer
API semantics while achieving superior memory efficiency and I/O performance for large-scale
simulations. Zarr's chunked columnar storage model naturally maps to PHIDS field layers
(plant energy per species, signal concentrations, toxin fields, flow-field gradients),
enabling selective decompression and random access without materializing entire snapshots
into Python memory.
The Zarr schema employs fixed-depth chunking across the spatial and temporal dimensions, with metadata groups for tick counters and termination state. Metadata (tick index, termination flags) is stored in a separate consolidated JSON array, eliminating the need to read field chunks for iteration or seeking. Field chunks are compressed using Zstd, offering superior compression ratio and speed compared to default Zarr Blosc profiles when applied to dense floating-point arrays. Subnormal tails (values < 1e-4) in signal layers are omitted during serialization via a custom codec or post-hoc masking, further reducing storage overhead without loss of ecological fidelity.
The design aligns with the project's strict data-oriented paradigm: snapshots are decomposed into structured field arrays during checkpoint and reassembled into the dictionary format for re-simulation compatibility. All serialization and field codec logic is stateless, ensuring deterministic round-trip fidelity.
NoOpReplayBuffer
A no-op replay buffer that does not store or write any frames, preventing disk usage during tuning.
Source code in src/phids/io/zarr_replay.py
__init__(*args: Any, **kwargs: Any) -> None
Initialize the NoOpReplayBuffer (does nothing).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
Any
|
Positional arguments (ignored). |
()
|
kwargs
|
Any
|
Keyword arguments (ignored). |
{}
|
__len__() -> int
append(state: object) -> None
Append a state snapshot to the buffer (no-op).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
object
|
The simulation state object to append. |
required |
append_raw_arrays(*args: Any, **kwargs: Any) -> None
Append raw environment arrays to the buffer (no-op).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
Any
|
Positional arguments (ignored). |
()
|
kwargs
|
Any
|
Keyword arguments (ignored). |
{}
|
ReplayBuffer
Append-only Zarr-backed buffer of chunked tick frames.
Provides backwards-compatible ReplayBuffer API semantics over a Zarr store.
The schema is laid out as:
zarr.root['_metadata']: JSON array of tick/termination metadatazarr.root['fields/{field_name}/data']: Chunked field array
Source code in src/phids/io/zarr_replay.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | |
__init__(max_frames: int | None = None, *, spill_to_disk: bool = False, spill_path: str | Path | None = None) -> None
Create or open a Zarr replay buffer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_frames
|
int | None
|
Optional upper bound on retained frames. When set and greater
than zero, only the most recent |
None
|
spill_to_disk
|
bool
|
Accepted for API compatibility but has no effect; Zarr storage is always disk-backed. |
False
|
spill_path
|
str | Path | None
|
Optional explicit path for the Zarr store. If omitted, a temporary directory is allocated lazily. |
None
|
Source code in src/phids/io/zarr_replay.py
__len__() -> int
Return total number of retained frames.
Returns:
| Type | Description |
|---|---|
int
|
Total number of retained frames. |
append(state: ReplayState) -> None
Serialize and append a tick state to the buffer.
Decomposes the state dict into field arrays, storing each as a chunked Zarr dataset. Metadata (tick, termination) is stored separately.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
ReplayState
|
Tick state mapping (e.g., from |
required |
Source code in src/phids/io/zarr_replay.py
append_raw_arrays(*, tick: int, env: _ReplayEnvLike, termination_state: tuple[bool, str | None]) -> None
Append replay frame directly from environment NumPy arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tick
|
int
|
Current simulation tick. |
required |
env
|
_ReplayEnvLike
|
Grid environment exposing replay layer arrays. |
required |
termination_state
|
tuple[bool, str | None]
|
Tuple |
required |
Source code in src/phids/io/zarr_replay.py
get_frame(tick: int) -> ReplayState
Return the deserialized state for the specified frame index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tick
|
int
|
Index of the frame to retrieve (0-based). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ReplayState |
ReplayState
|
Reconstructed state mapping. |
Raises:
| Type | Description |
|---|---|
IndexError
|
If the tick is out of range. |
Source code in src/phids/io/zarr_replay.py
load(path: str | Path) -> ReplayBuffer
classmethod
Load a Zarr replay store.
If the path is a .zarr directory, opens it directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to the Zarr directory. |
required |
Returns:
| Type | Description |
|---|---|
ReplayBuffer
|
Zarr replay buffer attached to the loaded directory. |
Source code in src/phids/io/zarr_replay.py
save(path: str | Path) -> None
Export the Zarr store to a standalone file for external storage.
This creates a snapshot of the current Zarr store by consolidating metadata and copying the store directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Destination file path (or directory for full export). |
required |
Source code in src/phids/io/zarr_replay.py
Integration
phids.mcp_server
Model Context Protocol surface for autonomous PHIDS orchestration.
Exposes read-only simulation states as structural resources and provides agentic tools for system validation and telemetry inspection without violating the engine's single-writer architecture.
Architecture overview
- Resources - Declarative, passively-read context feeds that consuming agents can cache and reference without spending a tool-call budget.
- Tools - Targeted execution primitives for read-only inspection, validation, and diagnostics.
- Prompts - Pre-baked guidance fragments that wire the above surfaces into coherent agentic workflows.
The MCP server runs as a headless stdio process completely decoupled from the
FastAPI HTTP layer. It may be launched independently via just mcp or
programmatically via :func:run_mcp_server. No write paths into the engine
state are exposed.
active_draft_resource() -> str
Provide the full, untruncated JSON layout of the active configuration draft.
Agents can read this resource directly to digest species mappings, substance
definitions, trigger-rule trees, diet matrices, and termination thresholds
without spending a tool-call budget on runtime_snapshot.
Returns:
| Type | Description |
|---|---|
str
|
Indented JSON string of the current :class: |
Source code in src/phids/mcp_server.py
analyze_simulation_drift() -> str
Pre-configured prompt mapping to guide debugging agents through drift triage.
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Structured step-by-step investigation guide for stochastic drift |
str
|
anomalies inside the PHIDS engine. |
Source code in src/phids/mcp_server.py
inspect_telemetry_schema(zarr_store_path: str) -> dict[str, Any]
Expose Zarr replay store structure to the agent without loading field arrays.
Allows autonomous MLOps operators to inspect frame counts, top-level tree keys, and store-level metadata before initiating a heavy Polars lazy-frame extraction. The store is opened read-only; no data is mutated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
zarr_store_path
|
str
|
Filesystem path to a PHIDS |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
dict[str, Any]: On success - |
dict[str, Any]
|
|
dict[str, Any]
|
|
Source code in src/phids/mcp_server.py
query_batch_jobs() -> dict[str, Any]
Return a summary of active and completed batch jobs from the draft state.
Provides visibility into long-running exploration tasks or evolutionary exploration results.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
dict[str, Any]: Dictionary mapping job IDs to their state representations. |
Source code in src/phids/mcp_server.py
query_diagnostic_logs(limit: int = 80) -> list[dict[str, str]]
Return the newest structured diagnostic entries recorded by PHIDS.
Entries are emitted by all engine, API, and telemetry loggers via the
:class:~phids.shared.logging_config.InMemoryLogHandler ring buffer.
Ordered most-recent-first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of log rows to return (clamped to >= 1 internally). |
80
|
Returns:
| Type | Description |
|---|---|
list[dict[str, str]]
|
list[dict[str, str]]: Structured entries with |
list[dict[str, str]]
|
|
Source code in src/phids/mcp_server.py
read_batch_summary(job_id: str) -> dict[str, Any]
Read the aggregated metrics inside a batch job's summary JSON file.
Allows agents to digest batch job metric summaries without manually loading JSON artifacts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
job_id
|
str
|
The ID of the batch job to read. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
dict[str, Any]: Dictionary containing the aggregated metrics on success, |
dict[str, Any]
|
or an error message on failure. |
Source code in src/phids/mcp_server.py
run_mcp_server() -> None
runtime_snapshot() -> dict[str, Any]
Return a compact performance-and-counts summary of the active draft state.
Useful as a lightweight sanity check before heavier resource reads or batch operations. All counts reflect the in-memory singleton draft; no simulation loop is touched.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
dict[str, Any]: Compact read-only summary including scenario metadata, |
dict[str, Any]
|
grid dimensions, entity counts, and active termination thresholds |
dict[str, Any]
|
(Z-codes). |
Source code in src/phids/mcp_server.py
validate_okf_compliance() -> dict[str, Any]
Run the OKF knowledge-graph validation suite against the docs/ and .agents/ trees.
Invokes scripts/validate_okf.py via uv run from the project root,
mirroring the pre-commit hook execution environment exactly. Essential for
self-evolving agent loops to verify that documentation mutations remain
structurally valid before opening a PR.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
dict[str, Any]: |
dict[str, Any]
|
error lines), and |
Source code in src/phids/mcp_server.py
Shared utilities
phids.shared.constants
Shared compile-time constants for the PHIDS simulation engine.
This module centralises all numeric sentinels, hard upper limits, and physical simulation
parameters that must remain consistent across the engine core, API schemas, and telemetry
sub-packages. The Rule-of-16 caps (MAX_FLORA_SPECIES, MAX_HERBIVORE_SPECIES,
MAX_SUBSTANCE_TYPES) govern the maximum cardinality of pre-allocated NumPy matrices in the
GridEnvironment and ECS world; exceeding these limits during scenario construction is
intercepted by Pydantic validation at the API ingress boundary and is never permitted to reach
the engine simulation loop. Grid dimension bounds (GRID_W_MAX, GRID_H_MAX) define the
maximum spatial extent of the biotope, constraining convolution and Jacobi propagation cost.
The diffusion constant SIGNAL_EPSILON is a performance invariant: after each
Gaussian diffusion step, values below SIGNAL_EPSILON are zeroed to maintain
matrix sparsity and avoid accumulation of subnormal floating-point values that would
degrade Numba JIT-compiled kernel throughput.
Note: SIGNAL_DECAY_FACTOR and SUBSTANCE_EMIT_RATE were intentionally moved to
SimulationConfig to allow dynamic parameter tuning during Design Space Exploration.
phids.shared.logging_config
Central logging configuration for PHIDS.
The package uses a single idempotent logging bootstrap so API, UI, engine, telemetry, and I/O modules share consistent formatting and levels. Configuration is environment-driven to keep detailed debugging available without paying for verbose logs by default.
InMemoryLogHandler
Bases: Handler
Capture recent structured log entries for the diagnostics UI.
Source code in src/phids/shared/logging_config.py
emit(record: logging.LogRecord) -> None
Append one formatted record to the in-memory diagnostics buffer.
Args: record: The log record object to emit.
Source code in src/phids/shared/logging_config.py
configure_logging(*, force: bool = False) -> None
Configure PHIDS package logging.
Environment variables
PHIDS_LOG_LEVEL: Package log level (default INFO).
PHIDS_LOG_FILE: Optional file path for a rotating debug log.
PHIDS_LOG_FILE_LEVEL: File handler level (default DEBUG).
PHIDS_LOG_SIM_DEBUG_INTERVAL: Tick interval for engine summaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
force
|
bool
|
Reconfigure logging even if already configured. |
False
|
Source code in src/phids/shared/logging_config.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | |
get_recent_logs(*, limit: int = 80) -> list[dict[str, str]]
Return the newest structured PHIDS log entries first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of entries to return. |
80
|
Returns:
| Type | Description |
|---|---|
list[dict[str, str]]
|
list[dict[str, str]]: Structured log entries for diagnostics panels. |
Source code in src/phids/shared/logging_config.py
get_simulation_debug_interval() -> int
Return the interval used for periodic simulation debug summaries.
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Tick interval for DEBUG summaries. |