This commit is contained in:
Varyngoth
2026-01-28 13:31:49 -04:00
commit 676659e5b9
239 changed files with 7509 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
"""
E.Drake - ENGN 2220
Jan 21, 2026
TESTING GEOMETRY
"""
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
#print(str(config.GEO_DIR))
sys.path.insert(0,str(config.GEO_DIR))
sys.path.insert(0,str(config.PATH_DIR))
from geo_current import geo_current
import jupedsim as jps
from matplotlib.patches import Circle
def main_loop():
geometry,doors = geo_current(full_plot = True)
dk_3, d4_6, d7_12, d_exit = doors
model = jps.CollisionFreeSpeedModel()
sim = jps.Simulation(model=model,geometry=geometry)
exit_id = sim.add_exit_stage(d_exit)
journey = jps.JourneyDescription([exit_id])
journey_id = sim.add_journey(journey)
total_sim_time = 60.0
doorways = {
0: dk_3,
1: d4_6,
2: d7_12,
}
# Spawn times for each door (seconds)
spawn_schedule = {
"door_1": [0.0, 5.0, 10.0], # Agents at t=0, 5, 10
"door_2": [2.0, 7.0],
"door_3": [3.0],
}
events = []
for door_name, times in spawn_schedule.items():
for t in times:
events.append((t, doors[door_name]))
events.sort(key=lambda x: x[0])
event_index = 0
while sim.elapsed_time() < total_sim_time:
current_time = sim.elapsed_time()
# Process all events whose time has come
while event_index < len(events) and events[event_index][0] <= current_time:
_, door_pos = events[event_index]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=door_pos,
journey_id=journey_id,
stage_id=exit_id,
radius=0.2,
)
sim.add_agent(agent_params)
event_index += 1
sim.iterate()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_loop()
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,386 @@
import jupedsim as jps
import shapely
from dataclasses import dataclass
from typing import List, Dict, Tuple
import numpy as np
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
sys.path.insert(0,str(config.GEO_DIR))
from geo_current import geo_current
@dataclass
class AgentSetup:
id:int
grade:str
door:int
speed:float
radius:float
spawn:Tuple[float,float]
@dataclass
class SimSetup:
doorways:Dict[int,shapely.Polygon]
grades:Dict[str,Dict]
min_spacing:float=0.6
total_sim_time:float=200.0
door_capacity:int=10
walkable_area:shapely.Polygon
exit_area:shapely.Polygon
class EvacSim:
def __init__(self,setup:SimSetup):
self.setup = setup
self.all_agents = []
self.all_spawn_events = []
self.simulation = None
self.exit_id = None
self.doorway_system = {}
def run(self):
self.all_agents = self.agent_params()
self.setup_sim_env()
self.spawn_events = self.get_spawn_events()
self.run_sim()
def agent_params(self)->List[AgentSetup]:
agent_id = 1
rng = np.random.default_rng(seed=42)
all_agents = []
for grade in self.setup.grades.keys():
spawn_time = rng.uniform(0.0,115.0)
self.setup.grades[grade]["Spawn Time"] = spawn_time
gr_agent_num = int(self.setup.grades[grade]["Pop Current"])
door = int(self.setup.grades[grade]["Door"])
current_agent = 0
for num in range(gr_agent_num):
speed = rng.normal(
loc=self.setup.grades[grade]["Speed Mean"],
scale=self.setup.grades[grade]["Speed Std Dev"],
size=1)
radius = self.setup.grades[grade]["Radius"]
new_agent = AgentSetup(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius = radius,
)
all_agents.append(new_agent)
agent_id += 1
current_agent += 1
return all_agents
def setup_sim_env(self):
walkable_area = self.setup.walkable_area
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(
model=model,geometry=walkable_area)
self.exit_id = self.simulation.add_exit_stage(
self.setup.exit_polygon)
def doorway_system(self, door_id: int, door_polygon: shapely.Polygon):
def get_waiting_area(self,door_polygon:shapely.Polygon)->shapely.Polygon:
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
waiting_area = get_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
self.doorway_info[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
for door_id, door_polygon in self.setup.doorways.items():
self.doorway_system(door_id, door_polygon)
def get_spawn_events(self)->List[Dict]:
events = []
agents_by_grade = {}
def get_spawn_point(self,door:int,num_points:int)->List[Tuple[float,float]]:
polygon = self.setup.doorways[door]
min_x,min_y,max_x,max_y = polygon.bounds
points = []
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.setup.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
for agent in self.all_agents:
for grade_name, grade_info in self.setup.grades.items():
if agent.door == grade_info["Door"]:
if grade_name not in agents_by_grade:
agents_by_grade[grade_name] = []
agents_by_grade[grade_name].append(agent)
break
for grade_name, grade_info in self.setup.grades.items():
door_id = grade_info["Door"]
spawn_time = grade_info["Spawn Time"]
grade_agents = agents_by_group.get(grade_name, [])
if not grade_agents:
continue
door_polygon = self.setup.doorways[door_id]
spawn_positions = self.get_spawn_point(
door_polygon,
len(group_agents))
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent": agent,
"position": position,
"grade": grade_name,
"door": door_id
})
events.sort(key=lambda x: x["time"])
return events
def run_sim(self):
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
print("\nStarting Simulation Loop...")
print(f"Total Simulation Time: {self.config.total_simulation_time}s")
print(f"Door Capacity: {self.config.door_capacity} agents per door")
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
self._process_spawn_events(
current_time,
event_index,
spawned_event_indices,
agents_in_door_area)
while (event_index < len(self.spawn_events) and \
self.spawn_events[event_index]["time"] <= current_time and \
event_index in spawned_event_indices):
event_index += 1
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def process_spawn_events(
self,
current_time: float,
event_index: int,
spawned_events: set,
agents_in_door_area: Dict
):
while (event_idx < len(self.spawn_events) and \
self.spawn_events[event_idx]["time"] <= current_time and \
event_idx not in spawned_events):
event = self.spawn_events[event_idx]
door_id = event["door"]
agent = event["agent"]
if agents_in_door_area[door_id] < self.setup.door_capacity:
self.spawn_agent(event,door_id,agent)
agents_in_door_area[door_id] += 1
spawned_events.add(event_idx)
event_index += 1
def spawn_agent(self,event:Dict,door_id:int,agent:AgentSetup):
journey_id = self.doorway_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.doorway_system[door_id]["waiting_set_id"],
radius=agent.radius,
v0=agent.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
def start_sim_run():
print("Evacuation Simulation")
print("-" * 40)
geometry,[door0,door1,door2,exit_door] = geo_current(full_plot = True)
door_polygons = {
1: door0,
2: door1,
3: door2
}
grade_data = {
"Kindergarden":{
"Door":0,
"Pop Current":34,
"Pop Mean":31.43,
"Pop Std Dev":5.65,
"Speed Mean":1.21,
"Speed Std Dev":0.24,
"Radius":0.407,
"Spawn Time":None
},
"Grade 1":{
"Door":0,
"Pop Current":26,
"Pop Mean":32.57,
"Pop Std Dev":6.27,
"Speed Mean":1.35,
"Speed Std Dev":0.26,
"Radius":0.407,
"Spawn Time":None
},
"Grade 2":{
"Door":0,
"Pop Current":42,
"Pop Mean":34.43,
"Pop Std Dev":6.80,
"Speed Mean":1.42,
"Speed Std Dev":0.28,
"Radius":0.407,
"Spawn Time":None
},
"Grade 3":{
"Door":0,
"Pop Current":39,
"Pop Mean":35.43,
"Pop Std Dev":5.19,
"Speed Mean":1.48,
"Speed Std Dev":0.23,
"Radius":0.407,
"Spawn Time":None
},
"Grade 4":{
"Door":1,
"Pop Current":30,
"Pop Mean":34.86,
"Pop Std Dev":6.77,
"Speed Mean":1.58,
"Speed Std Dev":0.26,
"Radius":0.417,
"Spawn Time":None
},
"Grade 5":{
"Door":1,
"Pop Current":43,
"Pop Mean":36.71,
"Pop Std Dev":7.09,
"Speed Mean":1.59,
"Speed Std Dev":0.24,
"Radius":0.434,
"Spawn Time":None
},
"Grade 6":{
"Door":1,
"Pop Current":29,
"Pop Mean":37.71,
"Pop Std Dev":6.99,
"Speed Mean":1.65,
"Speed Std Dev":0.24,
"Radius":0.454,
"Spawn Time":None
},
"Grade 7":{
"Door":2,
"Pop Current":45,
"Pop Mean":40.43,
"Pop Std Dev":6.02,
"Speed Mean":1.61,
"Speed Std Dev":0.25,
"Radius":0.471,
"Spawn Time":None
},
"Grade 8":{
"Door":2,
"Pop Current":36,
"Pop Mean":40.43,
"Pop Std Dev":5.50,
"Speed Mean":1.66,
"Speed Std Dev":0.24,
"Radius":0.488,
"Spawn Time":None
},
"Grade 9":{
"Door":2,
"Pop Current":44,
"Pop Mean":44.14,
"Pop Std Dev":4.85,
"Speed Mean":1.60,
"Speed Std Dev":0.24,
"Radius":0.500,
"Spawn Time":None
},
"Grade 10":{
"Door":2,
"Pop Current":36,
"Pop Mean":46.29,
"Pop Std Dev":6.29,
"Speed Mean":1.57,
"Speed Std Dev":0.23,
"Radius":0.507,
"Spawn Time":None
},
"Grade 11":{
"Door":2,
"Pop Current":54,
"Pop Mean":48.29,
"Pop Std Dev":3.30,
"Speed Mean":1.51,
"Speed Std Dev":0.22,
"Radius":0.515,
"Spawn Time":None
},
"Grade 12":{
"Door":2,
"Pop Current":46,
"Pop Mean":43.71,
"Pop Std Dev":6.02,
"Speed Mean":1.54,
"Speed Std Dev":0.23,
"Radius":0.520,
"Spawn Time":None
}}
config = SimSetup(
doorways=door_polygons,
grades=grade_data,
total_simulation_time=180.0,
door_capacity=10,
walkable_area=geometry,
exit_area=exit_door
)
sim = EvacSim(config)
return sim.run()
if __name__ == "__main__":
simulation = start_sim_run()
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,388 @@
import jupedsim as jps
import shapely
import random
from dataclasses import dataclass
from typing import List, Dict, Tuple, Optional
import numpy as np
@dataclass
class AgentConfig:
id: int
grade: str
door: int
speed: float
radius: float
@dataclass
class SimulationConfig:
door_polygons: Dict[int, shapely.Polygon]
groups: Dict[str, Dict]
total_simulation_time: float = 300.0
door_capacity: int = 10
min_spacing: float = 0.6
"""
def __post_init__(self):
'''Set default walkable area and exit polygon if not provided.'''
if self.walkable_area_coords is None:
self.walkable_area_coords = [(0, 0), (50, 0), (50, 30), (0, 30)]
if self.exit_polygon_coords is None:
self.exit_polygon_coords = [(45, 10), (48, 10), (48, 20), (45, 20)]
"""
class PedestrianSimulation:
"""
Main class for running pedestrian simulations with door queues and scheduled spawning.
Architecture Flow:
1. Configuration Setup
2. Agent Configuration Generation
3. Geometry Preparation (Walkable area, Doors, Exit)
4. Door System Setup (Waiting areas, Queues, Journeys)
5. Spawn Event Precomputation
6. Simulation Execution with Dynamic Spawning
7. Results Analysis/Visualization
"""
def __init__(self, config: SimulationConfig):
self.config = config
self.simulation = None
self.door_systems = {}
self.all_agents = []
self.spawn_events = []
self.exit_id = None
def run(self) -> jps.Simulation:
"""
Main orchestrator: Run the complete simulation.
Steps:
1. Create agent configurations
2. Setup simulation environment
3. Precompute spawn events
4. Execute simulation with dynamic spawning
5. Return completed simulation object
"""
print("=" * 60)
print("PEDESTRIAN SIMULATION STARTING")
print("=" * 60)
self.all_agents = self._create_agent_configurations()
print(f"Created {len(self.all_agents)} agent configurations")
self._setup_simulation_environment()
print("Simulation environment setup complete")
self.spawn_events = self._precompute_spawn_events()
print(f"Precomputed {len(self.spawn_events)} spawn events")
self._execute_simulation()
print("Simulation execution complete")
return self.simulation
def _create_agent_configurations(self) -> List[AgentConfig]:
"""Create AgentConfig objects for all agents in all groups."""
all_agents = []
agent_id = 0
for group_name, group_info in self.config.groups.items():
door = group_info["door"]
size = group_info["size"]
for i in range(size):
grade = random.choice(["A", "B", "C", "D", "F"])
speed = random.uniform(1.0, 1.5) # m/s
radius = random.uniform(0.2, 0.3) # meters
all_agents.append(AgentConfig(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius=radius
))
agent_id += 1
return all_agents
# includes id, grade, door, speed, and radius
def _generate_spawn_points(self, polygon: shapely.Polygon,num_points: int) -> List[Tuple[float, float]]:
"""Generate non-overlapping spawn points within a polygon."""
points = []
min_x, min_y, max_x, max_y = polygon.bounds
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.config.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
# get list of spawn point tuples to provide to agents
def _create_waiting_area(self, door_polygon: shapely.Polygon) -> shapely.Polygon:
"""Create a waiting area adjacent to the door polygon."""
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
def _setup_simulation_environment(self):
"""Setup the simulation with door queues and waiting areas."""
# Create walkable area geometry
walkable_area = shapely.Polygon(self.config.walkable_area_coords)
# Create model and simulation
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(model=model, geometry=walkable_area)
# Define exit zone
exit_polygon = shapely.Polygon(self.config.exit_polygon_coords)
self.exit_id = self.simulation.add_exit_stage(exit_polygon)
# Create door systems
for door_id, door_polygon in self.config.door_polygons.items():
self._setup_door_system(door_id, door_polygon)
def _setup_door_system(self, door_id: int, door_polygon: shapely.Polygon):
"""Setup queue system for a specific door."""
# Create waiting area
waiting_area = self._create_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
# Create queue waypoints
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
# Create journey
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
# Store door system
self.door_systems[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
def _precompute_spawn_events(self) -> List[Dict]:
"""Precompute all spawn events with positions and agent configurations."""
events = []
# Group agents by their assigned group
agents_by_group = {}
for agent in self.all_agents:
for group_name, group_info in self.config.groups.items():
if agent.door == group_info["door"]:
if group_name not in agents_by_group:
agents_by_group[group_name] = []
agents_by_group[group_name].append(agent)
break
# Create events for each group
for group_name, group_info in self.config.groups.items():
door_id = group_info["door"]
spawn_time = group_info["spawn_time"]
group_agents = agents_by_group.get(group_name, [])
if not group_agents:
continue
# Generate spawn positions
door_polygon = self.config.door_polygons[door_id]
spawn_positions = self._generate_spawn_points(
door_polygon,
len(group_agents)
)
# Create events
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent_config": agent,
"position": position,
"group": group_name,
"door": door_id
})
# Sort events by time
events.sort(key=lambda x: x["time"])
return events
def _execute_simulation(self):
"""Execute the simulation with dynamic spawning."""
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
'''
print("\nStarting simulation loop...")
print(f"Total simulation time: {self.config.total_simulation_time}s")
print(f"Door capacity: {self.config.door_capacity} agents per door")
'''
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
# Process spawn events
self._process_spawn_events(current_time, event_index, spawned_event_indices,
agents_in_door_area)
# Update event index
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index in spawned_event_indices):
event_index += 1
# Iterate simulation
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def _process_spawn_events(self, current_time: float, event_index: int,
spawned_event_indices: set, agents_in_door_area: Dict):
"""Process all spawn events that should occur at the current time."""
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index not in spawned_event_indices):
event = self.spawn_events[event_index]
door_id = event["door"]
agent_config = event["agent_config"]
# Check door capacity
if agents_in_door_area[door_id] < self.config.door_capacity:
self._spawn_agent(event, door_id, agent_config)
agents_in_door_area[door_id] += 1
spawned_event_indices.add(event_index)
# Move to next event
event_index += 1
def _spawn_agent(self, event: Dict, door_id: int, agent_config: AgentConfig):
"""Spawn a single agent into the simulation."""
journey_id = self.door_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.door_systems[door_id]["waiting_set_id"],
radius=agent_config.radius,
v0=agent_config.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
# Example usage function
def create_and_run_simulation() -> PedestrianSimulation:
"""
Example function to create and run a complete simulation.
Returns:
PedestrianSimulation: The completed simulation object
"""
# Define door polygons
door_polygons = {
1: shapely.Polygon([(5, 5), (10, 5), (10, 15), (5, 15)]),
2: shapely.Polygon([(20, 5), (25, 5), (25, 15), (20, 15)]),
3: shapely.Polygon([(35, 5), (40, 5), (40, 15), (35, 15)]),
}
# Define groups (example with 4 groups, extend to 13 as needed)
groups = {
"group_1": {"door": 1, "spawn_time": 0.0, "size": 40},
"group_2": {"door": 2, "spawn_time": 5.0, "size": 35},
"group_3": {"door": 3, "spawn_time": 10.0, "size": 30},
"group_4": {"door": 1, "spawn_time": 15.0, "size": 25},
# Add 9 more groups to reach 13 total
}
# Create simulation configuration
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=200.0, # Adjust as needed
door_capacity=10,
min_spacing=0.6
)
# Create and run simulation
sim_runner = PedestrianSimulation(config)
simulation = sim_runner.run()
return sim_runner
# Quick execution function
def run_simulation_quickstart():
"""Quickstart function for running a basic simulation."""
print("Pedestrian Simulation Quickstart")
print("-" * 40)
# You can modify these parameters
door_polygons = {
1: shapely.Polygon([(2, 2), (6, 2), (6, 8), (2, 8)]),
2: shapely.Polygon([(10, 2), (14, 2), (14, 8), (10, 8)]),
3: shapely.Polygon([(18, 2), (22, 2), (22, 8), (18, 8)]),
}
groups = {
"class_a": {"door": 1, "spawn_time": 0.0, "size": 30},
"class_b": {"door": 2, "spawn_time": 10.0, "size": 25},
"class_c": {"door": 3, "spawn_time": 20.0, "size": 20},
}
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=100.0,
door_capacity=8
)
sim = PedestrianSimulation(config)
return sim.run()
if __name__ == "__main__":
# Option 1: Use the example function
# sim_runner = create_and_run_simulation()
# Option 2: Use quickstart for testing
simulation = run_simulation_quickstart()
# You can now analyze the simulation results
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")
# Additional analysis can be added here

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"