Preset Schema
RODENT experiment presets are JSON files that describe the complete configuration of an experiment.
Preset files are stored in:
presets/
The current preset format is:
schema_version: 2
The application contains migration and validation support for the preset format.
Top-Level Structure
A preset has the following structure:
{
"schema_version": 2,
"metadata": {},
"simulation": {},
"arena": {},
"stimuli": [],
"protocol": [],
"treatment": {},
"controller": {},
"rodent": {},
"ui": {}
}
The required top-level sections are:
schema_version
metadata
simulation
arena
stimuli
protocol
treatment
rodent
controller
The ui section is supported by the current presets but is not required by the Python preset validator.
schema_version
The current schema version is:
"schema_version": 2
Custom presets should be authored directly against version 2.
The application also contains migration support for older preset data.
metadata
The metadata section identifies and describes the preset.
Required fields:
"metadata": {
"id": "example_arena",
"name": "Example Arena",
"version": "1.0",
"description": "Example arena configuration"
}
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique preset identifier |
name | string | Yes | Human-readable preset name |
version | string | Yes | Preset version |
description | string | Yes | Description of the preset |
simulation
The simulation section defines deterministic simulation settings.
Example:
"simulation": {
"fixed_delta": 0.02,
"seed": 42
}
| Field | Type | Required | Description |
|---|---|---|---|
fixed_delta | number | Yes | Fixed simulation timestep |
seed | integer | Yes | Random seed used by the simulation |
The fixed timestep must be greater than zero.
The seed must be an integer.
These values are also used by the project's determinism checks.
arena
The arena section contains the physical and visual environment.
Required fields:
"arena": {
"id": "example_arena",
"dimensions": {},
"materials": {},
"regions": []
}
Required arena fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Arena identifier |
dimensions | object | Yes | Arena width and height |
materials | object | Yes | Material definitions |
regions | array | Yes | Arena regions |
Optional arena fields include:
inner_walls
doors
treatment_chamber
show_labels
outer_wall_height
use_outer_walls
outer_wall_material_id
structures
Arena Dimensions
Example:
"dimensions": {
"width": 100,
"height": 100
}
Width and height must be positive.
Preset coordinates use this two-dimensional arena space.
arena.materials
Materials are stored as an object keyed by material ID.
Example:
"materials": {
"solid_floor": {
"display_name": "Solid Floor",
"categories": ["floor"],
"visual": {
"color": "#64748b",
"roughness": 0.75,
"metallic": 0.0,
"transparency": 0.0
},
"physical": {
"speed_multiplier": 1.0
}
}
}
A material contains:
display_name
categories
visual
physical
The visual definition contains:
color
roughness
metallic
transparency
The physical definition currently contains:
speed_multiplier
Standard Material IDs
The application defines the following standard materials:
| ID | Description | Speed multiplier |
|---|---|---|
solid_floor | Solid floor | 1.00 |
rubber | Rubber surface | 0.95 |
wire_mesh | Wire mesh | 0.85 |
wood_chip | Wood-chip bedding | 0.80 |
painted_steel | Painted steel | 1.00 |
clear_acrylic | Clear acrylic | 1.00 |
The renderer uses material properties such as roughness, metallic appearance and transparency when constructing the 3D scene.
arena.regions
Regions define areas of the arena.
A region must contain:
{
"id": "open_area",
"label": "Open Area",
"semantic_type": "open",
"material_id": "solid_floor",
"rect": [0, 0, 100, 100],
"color": "#64748b",
"object_type": "region",
"enabled": true
}
Required fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique region ID |
label | string | Yes | Display name |
semantic_type | string | Yes | Functional region type |
material_id | string | Yes | Material reference |
rect | array | Yes | [x, y, width, height] |
color | string | Yes | Region colour |
object_type | string | Yes | Region object type |
enabled | boolean | Yes | Whether the region is active |
The region ID must be unique within the preset.
The rect array must contain exactly four numeric values.
Standard Semantic Types
The standard semantic types are:
open
corridor
shelter
risk
exploration
transition
Other semantic types may produce validator warnings.
Optional Region Fields
The renderer supports additional fields such as:
shape
radius
surface
rotation_y
elevation
height_mode
height_target_region_id
covered
Shape
A region can use:
"shape": "rect"
or:
"shape": "circle"
Circular regions can specify:
"radius": 10
Rotation
Example:
"rotation_y": 45
Elevation
Example:
"elevation": 6
Elevation is represented in the preset coordinate system and converted to the Godot 3D Y axis by the renderer.
Covered Regions
Example:
"covered": true
Covered regions can receive the DCR roof treatment in the renderer.
arena.inner_walls
Inner walls divide regions and create physical boundaries.
Example:
"inner_walls": [
{
"id": "divider",
"rect": [48, 20, 4, 60],
"object_type": "wall",
"enabled": true,
"height": 20,
"material_id": "painted_steel"
}
]
Required fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique wall ID |
rect | array | Yes | [x, y, width, height] |
object_type | string | Yes | Wall object type |
enabled | boolean | Yes | Whether the wall is enabled |
height | number | Yes | Wall height |
material_id | string | Yes | Material reference |
Optional field:
rotation_y
Wall IDs must be unique.
The wall height must be greater than zero.
Very large wall heights may produce validator warnings.
arena.doors
Doors create controlled openings through walls.
Example:
"doors": [
{
"id": "door_1",
"label": "Main Door",
"type": "gap",
"wall": "divider",
"position": [50, 50],
"width": 8,
"default_state": "closed",
"material_id": "painted_steel"
}
]
Required fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Door ID |
label | string | Yes | Display label |
type | string | Yes | Door type |
wall | string | Yes | Referenced wall ID |
position | array | Yes | Two-dimensional position |
width | number | Yes | Door width |
default_state | string | Yes | Initial door state |
material_id | string | Yes | Material reference |
Supported door types:
gap
circular
Supported default states:
open
closed
locked
The referenced wall must exist.
The door position must contain exactly two numeric values.
The door width must be greater than zero.
Optional Door Fields
The current renderer also supports:
open
state
enabled
object_type
rotation_y
render_visual
Door state changes can occur while an experiment is running.
arena.treatment_chamber
A treatment chamber is optional.
Example:
"treatment_chamber": {
"id": "treatment_chamber",
"object_type": "treatment_chamber",
"label": "Treatment Chamber",
"position": [50, 50],
"size": [20, 20],
"rotation_y": 0,
"material_id": "clear_acrylic",
"enabled": true,
"collidable": true
}
The treatment chamber can define:
id
object_type
label
position
size
rotation_y
material_id
enabled
collidable
The treatment section must reference the appropriate chamber ID.
arena.structures
The renderer supports additional structures.
Currently supported structure types include:
bridge
ramp
Bridge
A bridge can specify:
{
"id": "bridge_1",
"type": "bridge",
"region_id": "bridge",
"entry_region_id": "entry",
"exit_region_id": "exit",
"elevation": 6.0
}
The bridge renderer creates an elevated base with entry and exit ramps.
Ramp
Ramp structures provide sloped visual geometry.
The bridge and ramp renderer should not be assumed to provide generic collision geometry for every structure.
stimuli
Stimuli are stored as an array.
Example:
"stimuli": [
{
"id": "field_light",
"type": "light",
"position": [50, 50],
"radius": 10,
"initially_active": true,
"enabled": true,
"object_type": "stimulus",
"rotation_y": 0,
"properties": {}
}
]
Required stimulus fields:
id
type
position
The position must contain two numeric values.
Additional fields can describe:
radius
initially_active
enabled
object_type
rotation_y
properties
The renderer uses stimulus data to create visual markers and, for light stimuli, actual light sources.
protocol
Protocol events are stored as an array.
Example:
"protocol": [
{
"step": 30,
"source_id": "field_light",
"active": true
},
{
"step": 60,
"source_id": "field_light",
"active": false
}
]
Each protocol event requires:
| Field | Type | Required |
|---|---|---|
step | integer/number | Yes |
source_id | string | Yes |
active | boolean | Yes |
source_id identifies the stimulus or source affected by the protocol event.
treatment
The treatment section is required.
Example:
"treatment": {
"id": "control",
"name": "Control",
"category": "control",
"condition": "none",
"dose": 0,
"unit": "mg",
"administration_step": 0,
"chamber_id": "treatment_chamber"
}
Required fields:
| Field | Type | Required |
|---|---|---|
id | string | Yes |
name | string | Yes |
category | string | Yes |
condition | string | Yes |
dose | number | Yes |
unit | string | Yes |
administration_step | number | Yes |
chamber_id | string | Yes |
The chamber ID should correspond to the configured treatment chamber when a treatment chamber is present.
rodent
The rodent section defines the simulated animal parameters.
Example:
"rodent": {
"weight_grams": 300,
"size_scale": 1.0,
"base_speed": 1.0,
"turn_tendency": 0.5,
"activity": 0.5,
"risk_tolerance": 0.5
}
Required fields:
| Field | Type | Required |
|---|---|---|
weight_grams | number | Yes |
size_scale | number | Yes |
base_speed | number | Yes |
turn_tendency | number | Yes |
activity | number | Yes |
risk_tolerance | number | Yes |
These values form part of the simulated rodent configuration.
controller
The controller defines the starting state and movement parameters.
Example:
"controller": {
"type": "default",
"start_position": [50, 50],
"start_heading": 0,
"speed": 1.0,
"turn_noise": 0.1
}
Required fields:
| Field | Type | Required |
|---|---|---|
type | string | Yes |
start_position | array | Yes |
start_heading | number | Yes |
speed | number | Yes |
turn_noise | number | Yes |
The start position must contain two values.
Presets may also include a controller behaviour preset where supported by the application.
ui
The UI section contains user-interface preferences.
Current presets can contain camera configuration such as:
"ui": {
"camera": {
"sensitivity": 0.4,
"invert_horizontal": false,
"invert_vertical": false,
"follow_distance": 1.6
}
}
These values control camera behaviour rather than the physical arena itself.
The Python preset validator does not require the ui section.
Coordinate System
Preset arena positions use two-dimensional coordinates.
JSON plane
y
│
│
│
└────────── x
The Godot renderer maps them to:
JSON x → Godot X
JSON y → Godot Z
elevation → Godot Y
The renderer uses a scale factor of:
0.1
Therefore:
Godot X = JSON x × 0.1
Godot Z = JSON y × 0.1
Godot Y = elevation × 0.1
IDs and References
Object IDs should be unique within their respective collections.
For example:
region IDs → unique
wall IDs → unique
door IDs → unique
stimulus IDs → unique
References must point to existing objects.
For example:
"wall": "divider"
requires a wall with:
"id": "divider"
Material references must point to IDs defined in:
arena.materials
Minimal Complete Preset
The following is a complete minimal preset structure suitable as a starting point for a custom configuration:
{
"schema_version": 2,
"metadata": {
"id": "custom_arena",
"name": "Custom Arena",
"version": "1.0",
"description": "Custom RODENT arena"
},
"simulation": {
"fixed_delta": 0.02,
"seed": 42
},
"arena": {
"id": "custom_arena",
"dimensions": {
"width": 100,
"height": 100
},
"materials": {
"solid_floor": {
"display_name": "Solid Floor",
"categories": [
"floor"
],
"visual": {
"color": "#64748b",
"roughness": 0.75,
"metallic": 0.0,
"transparency": 0.0
},
"physical": {
"speed_multiplier": 1.0
}
}
},
"regions": [
{
"id": "open_area",
"label": "Open Area",
"semantic_type": "open",
"material_id": "solid_floor",
"rect": [
0,
0,
100,
100
],
"color": "#64748b",
"object_type": "region",
"enabled": true
}
],
"inner_walls": [],
"doors": [],
"use_outer_walls": true,
"outer_wall_height": 28,
"outer_wall_material_id": "solid_floor"
},
"stimuli": [],
"protocol": [],
"treatment": {
"id": "control",
"name": "Control",
"category": "control",
"condition": "none",
"dose": 0,
"unit": "mg",
"administration_step": 0,
"chamber_id": "treatment_chamber"
},
"rodent": {
"weight_grams": 300,
"size_scale": 1.0,
"base_speed": 1.0,
"turn_tendency": 0.5,
"activity": 0.5,
"risk_tolerance": 0.5
},
"controller": {
"type": "default",
"start_position": [
50,
50
],
"start_heading": 0,
"speed": 1.0,
"turn_noise": 0.1
},
"ui": {
"camera": {
"sensitivity": 0.4,
"invert_horizontal": false,
"invert_vertical": false,
"follow_distance": 1.6
}
}
}
Validation
The Python validator is located at:
tests/validate_presets.py
Run:
python tests/validate_presets.py
The validator checks all JSON files under:
presets/
It checks:
- schema version
- metadata
- simulation settings
- arena dimensions
- materials
- regions
- walls
- doors
- stimuli
- protocol events
- treatment
- rodent configuration
- controller configuration
- references between objects
The deterministic configuration check is located at:
tests/test_determinism_ci.py
Run:
python tests/test_determinism_ci.py
The determinism check verifies that the determinism-relevant configuration produces a stable configuration hash. It does not execute two complete simulations.
Creating a Custom Preset
To create a new preset:
- Create a JSON file under:
presets/
- Set:
"schema_version": 2
-
Add all required top-level sections.
-
Define the arena dimensions.
-
Define the materials used by regions, walls and doors.
-
Define the regions.
-
Add walls and doors where required.
-
Add stimuli and protocol events if required.
-
Configure treatment, rodent and controller settings.
-
Run:
python tests/validate_presets.py
- Run:
python tests/test_determinism_ci.py
A preset should pass these checks before being committed to the repository.