or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

batch-running.mdcore.mddata-collection.mdexperimental.mdindex.mdspatial.md

spatial.mddocs/

0

# Spatial Systems

1

2

Mesa provides two comprehensive spatial modeling approaches: the traditional `mesa.space` module (in maintenance mode) and the modern `mesa.discrete_space` module (under active development). Both systems enable positioning agents in space and managing spatial relationships, but with different design philosophies and capabilities.

3

4

## Imports

5

6

```python { .api }

7

# Traditional spatial system (maintenance mode)

8

from mesa.space import (

9

MultiGrid, SingleGrid, HexSingleGrid, HexMultiGrid,

10

ContinuousSpace, NetworkGrid, PropertyLayer

11

)

12

13

# Modern discrete spatial system (active development)

14

from mesa.discrete_space import (

15

Grid, HexGrid, Network, VoronoiGrid,

16

OrthogonalMooreGrid, OrthogonalVonNeumannGrid,

17

Cell, CellAgent, FixedAgent, Grid2DMovingAgent, CellCollection,

18

PropertyLayer, DiscreteSpace

19

)

20

21

# Type definitions

22

from typing import Tuple, List, Union, Optional, Iterable

23

import numpy as np

24

25

# Spatial coordinate types

26

Coordinate = tuple[int, int]

27

FloatCoordinate = tuple[float, float] | np.ndarray

28

NetworkCoordinate = int

29

Position = Coordinate | FloatCoordinate | NetworkCoordinate

30

GridContent = Agent | None

31

MultiGridContent = list[Agent]

32

```

33

34

## Traditional Spatial System (mesa.space)

35

36

The traditional spatial system provides well-established grid and space classes that have been the foundation of Mesa models. These classes are now in maintenance mode with new development focused on `mesa.discrete_space`.

37

38

### Grid Classes

39

40

#### SingleGrid

41

42

Grid where each cell contains at most one agent.

43

44

```python { .api }

45

from mesa.space import SingleGrid

46

47

class SingleGrid:

48

"""

49

Grid where each cell contains at most one agent.

50

51

Provides basic grid functionality with unique occupancy constraint.

52

"""

53

54

def __init__(self, width: int, height: int, torus: bool = False):

55

"""

56

Initialize a SingleGrid.

57

58

Parameters:

59

width: Grid width in cells

60

height: Grid height in cells

61

torus: Whether the grid wraps around at edges

62

"""

63

...

64

65

def place_agent(self, agent, pos: Coordinate):

66

"""

67

Place an agent at a specific position.

68

69

Parameters:

70

agent: Agent to place

71

pos: (x, y) coordinate tuple

72

"""

73

...

74

75

def move_agent(self, agent, pos: Coordinate):

76

"""

77

Move an agent to a new position.

78

79

Parameters:

80

agent: Agent to move

81

pos: New (x, y) coordinate tuple

82

"""

83

...

84

85

def remove_agent(self, agent):

86

"""Remove an agent from the grid."""

87

...

88

89

def get_cell_list_contents(self, cell_list: List[Coordinate]) -> List[Agent]:

90

"""Get all agents in specified cells."""

91

...

92

93

def get_neighbors(self, pos: Coordinate, moore: bool = True,

94

include_center: bool = False, radius: int = 1) -> List[Coordinate]:

95

"""

96

Get neighboring positions.

97

98

Parameters:

99

pos: Center position

100

moore: If True, use Moore neighborhood (8-connected), else Von Neumann (4-connected)

101

include_center: Whether to include the center position

102

radius: Neighborhood radius

103

"""

104

...

105

106

def get_neighborhood(self, pos: Coordinate, moore: bool = True,

107

include_center: bool = False, radius: int = 1) -> List[Coordinate]:

108

"""Get neighborhood positions (alias for get_neighbors)."""

109

...

110

111

def iter_neighbors(self, pos: Coordinate, moore: bool = True,

112

include_center: bool = False, radius: int = 1) -> Iterator[Agent]:

113

"""Iterate over agents in neighboring cells."""

114

...

115

116

def find_empty(self) -> Coordinate | None:

117

"""Find a random empty cell, or None if grid is full."""

118

...

119

120

def exists_empty_cells(self) -> bool:

121

"""Check if any empty cells exist."""

122

...

123

```

124

125

#### MultiGrid

126

127

Grid where each cell can contain multiple agents.

128

129

```python { .api }

130

from mesa.space import MultiGrid

131

132

class MultiGrid:

133

"""

134

Grid where each cell can contain multiple agents.

135

136

Extends SingleGrid functionality to support multiple agents per cell.

137

"""

138

139

def __init__(self, width: int, height: int, torus: bool = False):

140

"""

141

Initialize a MultiGrid.

142

143

Parameters:

144

width: Grid width in cells

145

height: Grid height in cells

146

torus: Whether the grid wraps around at edges

147

"""

148

...

149

150

def place_agent(self, agent, pos: Coordinate):

151

"""Add an agent to a cell (multiple agents allowed)."""

152

...

153

154

def move_agent(self, agent, pos: Coordinate):

155

"""Move an agent to a new position."""

156

...

157

158

def remove_agent(self, agent):

159

"""Remove an agent from the grid."""

160

...

161

162

def get_cell_list_contents(self, cell_list: List[Coordinate]) -> List[Agent]:

163

"""Get all agents in specified cells."""

164

...

165

166

# Inherits all SingleGrid methods with multi-agent behavior

167

```

168

169

### Hexagonal Grids

170

171

#### HexSingleGrid and HexMultiGrid

172

173

Hexagonal grid variants providing 6-neighbor connectivity.

174

175

```python { .api }

176

from mesa.space import HexSingleGrid, HexMultiGrid

177

178

class HexSingleGrid(SingleGrid):

179

"""Hexagonal grid with single agent per cell."""

180

181

def get_neighbors(self, pos: Coordinate, include_center: bool = False,

182

radius: int = 1) -> List[Coordinate]:

183

"""Get hexagonal neighbors (6-connected)."""

184

...

185

186

class HexMultiGrid(MultiGrid):

187

"""Hexagonal grid with multiple agents per cell."""

188

189

def get_neighbors(self, pos: Coordinate, include_center: bool = False,

190

radius: int = 1) -> List[Coordinate]:

191

"""Get hexagonal neighbors (6-connected)."""

192

...

193

```

194

195

### ContinuousSpace

196

197

Two-dimensional continuous space with real-valued coordinates.

198

199

```python { .api }

200

from mesa.space import ContinuousSpace

201

202

class ContinuousSpace:

203

"""

204

Two-dimensional continuous space with real-valued coordinates.

205

206

Enables positioning agents at any (x, y) coordinate within defined bounds.

207

"""

208

209

def __init__(self, x_max: float, y_max: float, torus: bool = False,

210

x_min: float = 0, y_min: float = 0):

211

"""

212

Initialize continuous space.

213

214

Parameters:

215

x_max: Maximum x coordinate

216

y_max: Maximum y coordinate

217

torus: Whether space wraps around at boundaries

218

x_min: Minimum x coordinate

219

y_min: Minimum y coordinate

220

"""

221

...

222

223

def place_agent(self, agent, pos: FloatCoordinate):

224

"""

225

Place an agent at continuous coordinates.

226

227

Parameters:

228

agent: Agent to place

229

pos: (x, y) coordinate tuple with float values

230

"""

231

...

232

233

def move_agent(self, agent, pos: FloatCoordinate):

234

"""Move an agent to new continuous coordinates."""

235

...

236

237

def remove_agent(self, agent):

238

"""Remove an agent from the space."""

239

...

240

241

def get_neighbors(self, pos: FloatCoordinate, radius: float,

242

include_center: bool = False) -> List[Agent]:

243

"""

244

Get agents within radius of position.

245

246

Parameters:

247

pos: Center position

248

radius: Search radius

249

include_center: Whether to include agent at exact center

250

"""

251

...

252

253

def get_heading(self, pos_1: FloatCoordinate, pos_2: FloatCoordinate) -> float:

254

"""Get heading from pos_1 to pos_2 in radians."""

255

...

256

257

def get_distance(self, pos_1: FloatCoordinate, pos_2: FloatCoordinate) -> float:

258

"""Get Euclidean distance between positions."""

259

...

260

261

def move_by(self, agent, dx: float, dy: float):

262

"""Move agent by relative displacement."""

263

...

264

265

def torus_adj(self, pos: FloatCoordinate) -> FloatCoordinate:

266

"""Adjust position for torus topology."""

267

...

268

```

269

270

### NetworkGrid

271

272

Network-based space where agents are positioned on graph nodes.

273

274

```python { .api }

275

from mesa.space import NetworkGrid

276

import networkx as nx

277

278

class NetworkGrid:

279

"""

280

Network-based space where agents are positioned on nodes.

281

282

Uses NetworkX graphs to define spatial structure and connectivity.

283

"""

284

285

def __init__(self, G: nx.Graph):

286

"""

287

Initialize network grid.

288

289

Parameters:

290

G: NetworkX graph defining the network structure

291

"""

292

...

293

294

def place_agent(self, agent, node_id: NetworkCoordinate):

295

"""

296

Place agent on a network node.

297

298

Parameters:

299

agent: Agent to place

300

node_id: Network node identifier

301

"""

302

...

303

304

def move_agent(self, agent, node_id: NetworkCoordinate):

305

"""Move agent to a different node."""

306

...

307

308

def remove_agent(self, agent):

309

"""Remove agent from the network."""

310

...

311

312

def get_neighbors(self, node_id: NetworkCoordinate) -> List[NetworkCoordinate]:

313

"""Get neighboring nodes in the network."""

314

...

315

316

def get_all_cell_contents(self) -> List[Agent]:

317

"""Get all agents in the network."""

318

...

319

320

def get_cell_list_contents(self, cell_list: List[NetworkCoordinate]) -> List[Agent]:

321

"""Get agents on specified nodes."""

322

...

323

324

def iter_neighbors(self, node_id: NetworkCoordinate) -> Iterator[Agent]:

325

"""Iterate over agents on neighboring nodes."""

326

...

327

```

328

329

## Modern Discrete Spatial System (mesa.discrete_space)

330

331

The modern discrete spatial system introduces a cell-centric approach with enhanced capabilities for property-rich spatial modeling. This system is under active development and represents the future of Mesa's spatial functionality.

332

333

### Core Classes

334

335

#### Cell

336

337

Active positions that can have properties and contain agents.

338

339

```python { .api }

340

from mesa.discrete_space import Cell

341

342

class Cell:

343

"""

344

Active positions that can have properties and contain agents.

345

346

Cells are first-class objects that can store properties, execute behaviors,

347

and manage their agent occupants.

348

"""

349

350

def __init__(self, coordinate: Coordinate, capacity: int | None = None):

351

"""

352

Initialize a cell.

353

354

Parameters:

355

coordinate: The cell's position coordinate

356

capacity: Maximum number of agents (None for unlimited)

357

"""

358

...

359

360

def add_agent(self, agent):

361

"""Add an agent to this cell."""

362

...

363

364

def remove_agent(self, agent):

365

"""Remove an agent from this cell."""

366

...

367

368

@property

369

def agents(self) -> List[Agent]:

370

"""Get all agents in this cell."""

371

...

372

373

@property

374

def coordinate(self) -> Coordinate:

375

"""Get the cell's coordinate."""

376

...

377

378

@property

379

def is_empty(self) -> bool:

380

"""Check if cell contains no agents."""

381

...

382

383

@property

384

def is_full(self) -> bool:

385

"""Check if cell is at capacity."""

386

...

387

388

def get_neighborhood(self, radius: int = 1) -> 'CellCollection':

389

"""Get neighboring cells within radius."""

390

...

391

```

392

393

#### CellAgent

394

395

Base agent class that understands cell-based spatial interactions.

396

397

```python { .api }

398

from mesa.discrete_space import CellAgent

399

400

class CellAgent(Agent):

401

"""

402

Base agent class that understands cell-based spatial interactions.

403

404

Extends the basic Agent class with cell-aware spatial behavior

405

and movement capabilities.

406

"""

407

408

def __init__(self, model, cell: Cell | None = None):

409

"""

410

Initialize a CellAgent.

411

412

Parameters:

413

model: The model instance

414

cell: Initial cell position (optional)

415

"""

416

super().__init__(model)

417

...

418

419

@property

420

def cell(self) -> Cell | None:

421

"""Get the cell this agent occupies."""

422

...

423

424

@property

425

def coordinate(self) -> Coordinate | None:

426

"""Get the agent's coordinate position."""

427

...

428

429

def move_to(self, cell: Cell):

430

"""

431

Move this agent to a different cell.

432

433

Parameters:

434

cell: Target cell to move to

435

"""

436

...

437

438

def get_neighbors(self, radius: int = 1, include_center: bool = False) -> List['CellAgent']:

439

"""

440

Get neighboring agents within radius.

441

442

Parameters:

443

radius: Search radius in cells

444

include_center: Whether to include agents in same cell

445

"""

446

...

447

448

def get_neighborhood_cells(self, radius: int = 1) -> 'CellCollection':

449

"""Get neighboring cells within radius."""

450

...

451

```

452

453

#### FixedAgent

454

455

Specialized agent class for immobile entities permanently fixed to cells.

456

457

```python { .api }

458

from mesa.discrete_space import FixedAgent

459

460

class FixedAgent(Agent):

461

"""

462

Agent permanently fixed to a cell position.

463

464

FixedAgent represents patches or immobile entities that are bound to

465

specific cells and cannot move. Useful for terrain features, buildings,

466

or other static environmental elements.

467

"""

468

469

def remove(self):

470

"""Remove the agent from the model and cell."""

471

...

472

```

473

474

#### Grid2DMovingAgent

475

476

Specialized agent class with enhanced 2D grid movement capabilities.

477

478

```python { .api }

479

from mesa.discrete_space import Grid2DMovingAgent

480

481

class Grid2DMovingAgent(CellAgent):

482

"""

483

Mixin for agents with directional movement in 2D grids.

484

485

Provides convenient movement methods using cardinal and ordinal directions,

486

with support for string-based direction commands like 'north', 'southeast', etc.

487

"""

488

489

DIRECTION_MAP = {

490

"n": (-1, 0), "north": (-1, 0), "up": (-1, 0),

491

"s": (1, 0), "south": (1, 0), "down": (1, 0),

492

"e": (0, 1), "east": (0, 1), "right": (0, 1),

493

"w": (0, -1), "west": (0, -1), "left": (0, -1),

494

"ne": (-1, 1), "northeast": (-1, 1), "upright": (-1, 1),

495

"nw": (-1, -1), "northwest": (-1, -1), "upleft": (-1, -1),

496

"se": (1, 1), "southeast": (1, 1), "downright": (1, 1),

497

"sw": (1, -1), "southwest": (1, -1), "downleft": (1, -1)

498

}

499

500

def move_by_direction(self, direction: str | tuple[int, int]):

501

"""

502

Move agent by direction string or coordinate offset.

503

504

Parameters:

505

direction: Direction string (e.g., 'north') or coordinate tuple

506

"""

507

...

508

```

509

510

### Grid Implementations

511

512

#### Grid

513

514

Base grid implementation for cell spaces.

515

516

```python { .api }

517

from mesa.discrete_space import Grid

518

519

class Grid(DiscreteSpace):

520

"""

521

Base grid implementation for cell spaces.

522

523

Provides orthogonal grid structure with configurable neighborhood types

524

and boundary conditions.

525

"""

526

527

def __init__(self, width: int, height: int, torus: bool = False,

528

capacity: int | None = None):

529

"""

530

Initialize a grid.

531

532

Parameters:

533

width: Grid width in cells

534

height: Grid height in cells

535

torus: Whether grid wraps at boundaries

536

capacity: Default cell capacity (None for unlimited)

537

"""

538

...

539

540

def place_agent(self, agent: CellAgent, cell: Cell):

541

"""Place an agent in a specific cell."""

542

...

543

544

def move_agent(self, agent: CellAgent, cell: Cell):

545

"""Move an agent to a different cell."""

546

...

547

548

def remove_agent(self, agent: CellAgent):

549

"""Remove an agent from the grid."""

550

...

551

552

def select_random_empty_cell(self) -> Cell:

553

"""Select a random empty cell."""

554

...

555

556

def get_cell(self, coordinate: Coordinate) -> Cell:

557

"""Get cell at specific coordinate."""

558

...

559

560

def get_cells(self) -> CellCollection:

561

"""Get all cells in the grid."""

562

...

563

564

def get_empty_cells(self) -> CellCollection:

565

"""Get all empty cells."""

566

...

567

568

@property

569

def width(self) -> int:

570

"""Grid width."""

571

...

572

573

@property

574

def height(self) -> int:

575

"""Grid height."""

576

...

577

```

578

579

#### OrthogonalMooreGrid

580

581

Grid with 8-neighbor (Moore) connectivity pattern.

582

583

```python { .api }

584

from mesa.discrete_space import OrthogonalMooreGrid

585

586

class OrthogonalMooreGrid(Grid):

587

"""

588

Grid with 8-neighbor (Moore) connectivity.

589

590

Provides Moore neighborhood where each cell has up to 8 neighbors

591

(including diagonal connections). In n-dimensional space, provides

592

(3^n)-1 neighbors per cell.

593

"""

594

595

def __init__(self, width: int, height: int, torus: bool = False, capacity: int | None = None):

596

"""

597

Initialize Moore grid.

598

599

Parameters:

600

width: Grid width

601

height: Grid height

602

torus: Whether grid wraps around edges

603

capacity: Maximum agents per cell (None for unlimited)

604

"""

605

...

606

```

607

608

#### OrthogonalVonNeumannGrid

609

610

Grid with 4-neighbor (Von Neumann) connectivity pattern.

611

612

```python { .api }

613

from mesa.discrete_space import OrthogonalVonNeumannGrid

614

615

class OrthogonalVonNeumannGrid(Grid):

616

"""

617

Grid with 4-neighbor (Von Neumann) connectivity.

618

619

Provides Von Neumann neighborhood where each cell has up to 4 neighbors

620

(only orthogonal connections, no diagonals). In n-dimensional space,

621

provides 2n neighbors per cell.

622

"""

623

624

def __init__(self, width: int, height: int, torus: bool = False, capacity: int | None = None):

625

"""

626

Initialize Von Neumann grid.

627

628

Parameters:

629

width: Grid width

630

height: Grid height

631

torus: Whether grid wraps around edges

632

capacity: Maximum agents per cell (None for unlimited)

633

"""

634

...

635

```

636

637

#### HexGrid

638

639

Hexagonal grid arrangement with 6-neighbor connectivity.

640

641

```python { .api }

642

from mesa.discrete_space import HexGrid

643

644

class HexGrid(DiscreteSpace):

645

"""

646

Hexagonal grid arrangement with 6-neighbor connectivity.

647

648

Provides hexagonal tessellation for models requiring 6-neighbor

649

spatial relationships.

650

"""

651

652

def __init__(self, width: int, height: int, torus: bool = False):

653

"""

654

Initialize hexagonal grid.

655

656

Parameters:

657

width: Grid width in hexagonal cells

658

height: Grid height in hexagonal cells

659

torus: Whether grid wraps at boundaries

660

"""

661

...

662

663

# Inherits base grid methods with hexagonal geometry

664

```

665

666

#### Network

667

668

Network-based cell arrangement using graph structures.

669

670

```python { .api }

671

from mesa.discrete_space import Network

672

import networkx as nx

673

674

class Network(DiscreteSpace):

675

"""

676

Network-based cell arrangement using graph structures.

677

678

Enables spatial modeling on arbitrary network topologies

679

using NetworkX graphs.

680

"""

681

682

def __init__(self, graph: nx.Graph):

683

"""

684

Initialize network space.

685

686

Parameters:

687

graph: NetworkX graph defining connectivity

688

"""

689

...

690

691

def get_cell(self, node_id) -> Cell:

692

"""Get cell corresponding to network node."""

693

...

694

695

def get_neighbors(self, cell: Cell) -> CellCollection:

696

"""Get neighboring cells in the network."""

697

...

698

```

699

700

### Property Layers

701

702

Property layers provide efficient storage and manipulation of spatial properties across cells.

703

704

```python { .api }

705

from mesa.discrete_space import PropertyLayer

706

707

class PropertyLayer:

708

"""

709

Efficient property storage and manipulation for spatial models.

710

711

Enables storing and updating scalar or array properties across

712

spatial cells with optimized operations.

713

"""

714

715

def __init__(self, name: str, width: int, height: int,

716

default_value: Any = 0, dtype=None):

717

"""

718

Initialize property layer.

719

720

Parameters:

721

name: Layer name identifier

722

width: Layer width

723

height: Layer height

724

default_value: Default cell value

725

dtype: NumPy data type

726

"""

727

...

728

729

def set_cell(self, coordinate: Coordinate, value: Any):

730

"""Set value at specific coordinate."""

731

...

732

733

def get_cell(self, coordinate: Coordinate) -> Any:

734

"""Get value at specific coordinate."""

735

...

736

737

def set_cells(self, coordinates: List[Coordinate], values: Any):

738

"""Set values at multiple coordinates."""

739

...

740

741

def get_cells(self, coordinates: List[Coordinate]) -> List[Any]:

742

"""Get values at multiple coordinates."""

743

...

744

745

def modify_cells(self, coordinates: List[Coordinate],

746

operation: Callable, *args, **kwargs):

747

"""Apply operation to modify cell values."""

748

...

749

750

def apply_operation(self, operation: Callable, *args, **kwargs):

751

"""Apply operation to all cells."""

752

...

753

754

@property

755

def data(self) -> np.ndarray:

756

"""Access underlying NumPy array."""

757

...

758

```

759

760

## Usage Examples

761

762

### Traditional Grid Usage

763

764

```python { .api }

765

from mesa import Agent, Model

766

from mesa.space import MultiGrid

767

from mesa.datacollection import DataCollector

768

769

class TraditionalAgent(Agent):

770

def __init__(self, model, energy=100):

771

super().__init__(model)

772

self.energy = energy

773

774

def step(self):

775

# Move to random neighboring cell

776

neighbors = self.model.grid.get_neighborhood(

777

self.pos, moore=True, include_center=False

778

)

779

new_pos = self.random.choice(neighbors)

780

self.model.grid.move_agent(self, new_pos)

781

782

# Interact with agents in same cell

783

cellmates = self.model.grid.get_cell_list_contents([self.pos])

784

for other in cellmates:

785

if other != self and isinstance(other, TraditionalAgent):

786

# Simple energy transfer

787

transfer = min(5, self.energy // 2)

788

self.energy -= transfer

789

other.energy += transfer

790

791

class TraditionalModel(Model):

792

def __init__(self, n_agents=50, width=20, height=20):

793

super().__init__()

794

795

# Create traditional grid

796

self.grid = MultiGrid(width, height, torus=True)

797

798

# Create agents and place randomly

799

for i in range(n_agents):

800

agent = TraditionalAgent(self, energy=100)

801

x = self.random.randrange(width)

802

y = self.random.randrange(height)

803

self.grid.place_agent(agent, (x, y))

804

805

self.running = True

806

807

def step(self):

808

self.agents.shuffle_do("step")

809

810

# Usage

811

model = TraditionalModel(n_agents=30, width=15, height=15)

812

for i in range(50):

813

model.step()

814

```

815

816

### Modern Cell-Space Usage

817

818

```python { .api }

819

from mesa import Model

820

from mesa.discrete_space import Grid, CellAgent, PropertyLayer

821

822

class ModernAgent(CellAgent):

823

def __init__(self, model, cell=None, energy=100):

824

super().__init__(model, cell)

825

self.energy = energy

826

827

def step(self):

828

# Get neighboring cells

829

neighbors = self.get_neighborhood_cells(radius=1)

830

empty_neighbors = neighbors.select_cells(lambda c: c.is_empty)

831

832

if empty_neighbors:

833

# Move to random empty neighboring cell

834

new_cell = self.random.choice(empty_neighbors)

835

self.move_to(new_cell)

836

837

# Interact with nearby agents

838

nearby_agents = self.get_neighbors(radius=1, include_center=True)

839

for other in nearby_agents:

840

if other != self:

841

# Energy sharing

842

avg_energy = (self.energy + other.energy) / 2

843

self.energy = avg_energy

844

other.energy = avg_energy

845

846

# Update cell property

847

if hasattr(self.model, 'temperature_layer'):

848

current_temp = self.model.temperature_layer.get_cell(self.coordinate)

849

# Agents increase local temperature

850

self.model.temperature_layer.set_cell(self.coordinate, current_temp + 0.1)

851

852

class ModernModel(Model):

853

def __init__(self, n_agents=50, width=20, height=20):

854

super().__init__()

855

856

# Create modern grid

857

self.space = Grid(width, height, torus=True)

858

859

# Create property layer for temperature

860

self.temperature_layer = PropertyLayer(

861

"temperature", width, height, default_value=20.0

862

)

863

864

# Create agents and place randomly

865

for i in range(n_agents):

866

agent = ModernAgent(self, energy=100)

867

empty_cell = self.space.select_random_empty_cell()

868

self.space.place_agent(agent, empty_cell)

869

870

self.running = True

871

872

def step(self):

873

# Execute agent behaviors

874

self.agents.shuffle_do("step")

875

876

# Update environment - cool down temperature

877

self.temperature_layer.apply_operation(

878

lambda temp: max(20.0, temp * 0.95) # Cool towards base temperature

879

)

880

881

# Usage

882

model = ModernModel(n_agents=30, width=15, height=15)

883

for i in range(50):

884

model.step()

885

886

# Access spatial data

887

hot_cells = []

888

for cell in model.space.get_cells():

889

temp = model.temperature_layer.get_cell(cell.coordinate)

890

if temp > 25.0:

891

hot_cells.append(cell)

892

893

print(f"Hot spots: {len(hot_cells)} cells above 25°C")

894

```

895

896

### Continuous Space Usage

897

898

```python { .api }

899

from mesa import Agent, Model

900

from mesa.space import ContinuousSpace

901

import math

902

903

class ContinuousAgent(Agent):

904

def __init__(self, model, pos, speed=1.0):

905

super().__init__(model)

906

self.pos = pos

907

self.speed = speed

908

self.heading = self.random.uniform(0, 2 * math.pi)

909

910

def step(self):

911

# Move in current direction

912

dx = math.cos(self.heading) * self.speed

913

dy = math.sin(self.heading) * self.speed

914

self.model.space.move_by(self, dx, dy)

915

916

# Avoid crowding - turn away from nearby agents

917

neighbors = self.model.space.get_neighbors(self.pos, radius=3.0)

918

if len(neighbors) > 3:

919

# Calculate average position of neighbors

920

avg_x = sum(n.pos[0] for n in neighbors) / len(neighbors)

921

avg_y = sum(n.pos[1] for n in neighbors) / len(neighbors)

922

923

# Turn away from crowd

924

away_heading = self.model.space.get_heading(

925

(avg_x, avg_y), self.pos

926

)

927

self.heading = away_heading + self.random.uniform(-0.5, 0.5)

928

929

# Random walk component

930

self.heading += self.random.uniform(-0.1, 0.1)

931

932

class ContinuousModel(Model):

933

def __init__(self, n_agents=100, width=50.0, height=50.0):

934

super().__init__()

935

936

# Create continuous space

937

self.space = ContinuousSpace(width, height, torus=True)

938

939

# Create agents at random positions

940

for i in range(n_agents):

941

x = self.random.uniform(0, width)

942

y = self.random.uniform(0, height)

943

agent = ContinuousAgent(self, (x, y), speed=self.random.uniform(0.5, 2.0))

944

self.space.place_agent(agent, (x, y))

945

946

self.running = True

947

948

def step(self):

949

self.agents.shuffle_do("step")

950

951

# Usage

952

model = ContinuousModel(n_agents=50, width=30.0, height=30.0)

953

for i in range(100):

954

model.step()

955

```

956

957

### Network Space Usage

958

959

```python { .api }

960

from mesa import Agent, Model

961

from mesa.space import NetworkGrid

962

import networkx as nx

963

964

class NetworkAgent(Agent):

965

def __init__(self, model, node_id):

966

super().__init__(model)

967

self.node_id = node_id

968

self.infection_status = "susceptible"

969

970

def step(self):

971

if self.infection_status == "infected":

972

# Spread infection to neighbors

973

neighbors = self.model.grid.get_neighbors(self.pos)

974

for neighbor_node in neighbors:

975

neighbor_agents = self.model.grid.get_cell_list_contents([neighbor_node])

976

for agent in neighbor_agents:

977

if (agent.infection_status == "susceptible" and

978

self.random.random() < 0.1): # 10% transmission rate

979

agent.infection_status = "infected"

980

981

elif self.infection_status == "susceptible":

982

# Random movement

983

neighbors = self.model.grid.get_neighbors(self.pos)

984

if neighbors:

985

new_node = self.random.choice(neighbors)

986

self.model.grid.move_agent(self, new_node)

987

988

class NetworkModel(Model):

989

def __init__(self, network_type="small_world", n_nodes=100):

990

super().__init__()

991

992

# Create network

993

if network_type == "small_world":

994

G = nx.watts_strogatz_graph(n_nodes, 6, 0.3)

995

elif network_type == "scale_free":

996

G = nx.barabasi_albert_graph(n_nodes, 3)

997

else:

998

G = nx.erdos_renyi_graph(n_nodes, 0.1)

999

1000

self.grid = NetworkGrid(G)

1001

1002

# Create agents on random nodes

1003

nodes = list(G.nodes())

1004

for i, node in enumerate(nodes):

1005

agent = NetworkAgent(self, node)

1006

self.grid.place_agent(agent, node)

1007

1008

# Patient zero

1009

if self.agents:

1010

patient_zero = self.random.choice(self.agents)

1011

patient_zero.infection_status = "infected"

1012

1013

self.running = True

1014

1015

def step(self):

1016

self.agents.shuffle_do("step")

1017

1018

# Check if infection has spread to everyone or died out

1019

infected = len([a for a in self.agents if a.infection_status == "infected"])

1020

if infected == 0 or infected == len(self.agents):

1021

self.running = False

1022

1023

# Usage

1024

model = NetworkModel("small_world", n_nodes=50)

1025

for i in range(100):

1026

model.step()

1027

if not model.running:

1028

break

1029

1030

infected_count = len([a for a in model.agents if a.infection_status == "infected"])

1031

print(f"Final infected count: {infected_count}/{len(model.agents)}")

1032

```

1033

1034

## Choosing Between Spatial Systems

1035

1036

### Use Traditional mesa.space When:

1037

- Working with existing Mesa models that use the traditional system

1038

- Need well-tested, stable spatial functionality

1039

- Building simple grid-based models without complex spatial properties

1040

- Prioritize compatibility with Mesa 2.x models

1041

1042

### Use Modern mesa.discrete_space When:

1043

- Building new models from scratch

1044

- Need property-rich spatial environments

1045

- Want cell-centric spatial modeling

1046

- Require advanced spatial operations and queries

1047

- Planning to use experimental Mesa features

1048

1049

### Migration Considerations

1050

1051

```python { .api }

1052

# Traditional approach

1053

from mesa.space import MultiGrid

1054

1055

class OldModel(Model):

1056

def __init__(self):

1057

self.grid = MultiGrid(10, 10, True)

1058

# Traditional grid operations...

1059

1060

# Modern approach - similar functionality with enhanced capabilities

1061

from mesa.discrete_space import Grid

1062

1063

class NewModel(Model):

1064

def __init__(self):

1065

self.space = Grid(10, 10, torus=True)

1066

# Modern grid operations with cells and property layers...

1067

```