Custom Roles#
Roles are the data layer of BAM Engine’s ECS architecture. Each role is a dataclass-like container where every field is a NumPy array indexed by agent ID. Custom roles let you add new state variables to agents without modifying the core framework.
Quick Example#
from bamengine import role
from bamengine.typing import Float, Int
@role
class Inventory:
"""Track physical inventory for firms."""
goods_on_hand: Float
reorder_point: Float
days_until_delivery: Int
The @role Decorator#
The @role decorator transforms a class definition into a role:
from bamengine import role
from bamengine.typing import Float
@role
class RnDCapability:
rd_intensity: Float
patents_held: Float
The decorator:
Converts the class into a dataclass with
slots=TrueRegisters it in the global role registry
Derives the role name from the class name (
"RnDCapability")
You can override the name:
@role(name="R&D")
class RnDCapability:
rd_intensity: Float
Type Aliases#
Role fields must use BAM Engine’s type aliases, which map to NumPy dtypes:
Alias |
NumPy dtype |
Typical Use |
|---|---|---|
|
|
Prices, wages, production, net worth (any continuous value) |
|
|
Contract duration, vacancy count (discrete quantities) |
|
|
Employment status, bankruptcy flags (binary state) |
|
|
Employer ID, bank ID (references to other agents) |
Import from bamengine.typing:
from bamengine.typing import Float, Int, Bool, AgentId
Initializing Custom Roles#
After creating a simulation, attach your role with
use_role():
import bamengine as bam
sim = bam.Simulation.init(seed=42)
# Firm-level role (default: n_agents = n_firms)
sim.use_role(RnDCapability)
# Household-level role (specify agent count)
sim.use_role(BufferStock, n_agents=sim.n_households)
# Bank-level role
sim.use_role(BankRegulation, n_agents=sim.n_banks)
All fields are initialized to zero by default.
Accessing Role Data#
Retrieve a role by name and access its fields as NumPy arrays:
rnd = sim.get_role("RnDCapability")
rnd.rd_intensity # shape (n_firms,) -- array of floats
rnd.patents_held # shape (n_firms,) -- array of floats
rnd.rd_intensity[0] # scalar -- first firm's R&D intensity
Built-in roles have shortcut attributes (see Running Simulations):
sim.prod.price # equivalent to sim.get_role("Producer").price
sim.wrk.employer # equivalent to sim.get_role("Worker").employer
Built-in Roles#
BAM Engine provides 7 built-in roles across the three agent types:
Firm Roles
Role |
Key Fields |
|---|---|
|
|
|
|
|
Household Roles
Role |
Key Fields |
|---|---|
|
|
|
|
|
Bank Roles
Role |
Key Fields |
|---|---|
|
Tips#
Fields must be annotated: Every field needs a type annotation (
Float,Int,Bool, orAgentId). Unannotated attributes are ignored.Don’t subclass existing roles: Create new roles instead. The ECS pattern favors composition over inheritance.
Roles are dataclasses: Under the hood, roles use
@dataclass(slots=True). This means you get efficient attribute access and memory layout.Zero initialization: All fields start as zero-filled arrays. If you need non-zero defaults, set them in a custom event that runs at the start of the pipeline.
See also
Overview for the ECS architecture
Custom Events for using role data in events
Model Extensions for examples of roles in built-in extensions