First
This commit is contained in:
BIN
SRS_evac_2025-11-23_23-03-09.sqlite
Normal file
BIN
SRS_evac_2025-11-23_23-03-09.sqlite
Normal file
Binary file not shown.
BIN
SRS_evac_2025-11-24_01-03-06.sqlite
Normal file
BIN
SRS_evac_2025-11-24_01-03-06.sqlite
Normal file
Binary file not shown.
BIN
SRS_evac_2025-11-30_19-43-15.sqlite
Normal file
BIN
SRS_evac_2025-11-30_19-43-15.sqlite
Normal file
Binary file not shown.
14
_0_next-steps.txt
Normal file
14
_0_next-steps.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
The next steps to implement, ordered by level of urgency
|
||||||
|
will be the following:
|
||||||
|
|
||||||
|
(a) ensuring agents do not get stuck on geometry
|
||||||
|
during the simulation
|
||||||
|
|
||||||
|
(b) ensuring agents are properly spawning during the
|
||||||
|
simulation after the first time step
|
||||||
|
|
||||||
|
(c) making agents in visualization have the correct
|
||||||
|
size based on their assigned radius
|
||||||
|
|
||||||
|
(d) colouring agents in the visualization based on
|
||||||
|
which grade they are assigned
|
||||||
BIN
_SRS_sim_010542_.sqlite
Normal file
BIN
_SRS_sim_010542_.sqlite
Normal file
Binary file not shown.
BIN
_SRS_sim_013834_.sqlite
Normal file
BIN
_SRS_sim_013834_.sqlite
Normal file
Binary file not shown.
BIN
_SRS_sim_231233_.sqlite
Normal file
BIN
_SRS_sim_231233_.sqlite
Normal file
Binary file not shown.
BIN
_SRS_sim_231809_.sqlite
Normal file
BIN
_SRS_sim_231809_.sqlite
Normal file
Binary file not shown.
BIN
_SRS_sim_232009_.sqlite
Normal file
BIN
_SRS_sim_232009_.sqlite
Normal file
Binary file not shown.
0
_source_/SRS_evac_2025-11-30_19-43-15.sqlite
Normal file
0
_source_/SRS_evac_2025-11-30_19-43-15.sqlite
Normal file
132
_source_/agent_data.py
Normal file
132
_source_/agent_data.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
}}
|
||||||
35
_source_/agent_setup.py
Normal file
35
_source_/agent_setup.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from agent_data import grade_data
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import numpy as np
|
||||||
|
from typing import List,Tuple,Dict
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AgentSetup:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
|
||||||
|
def AgentConfig()->Tuple[List[AgentSetup],Dict[str,int]]:
|
||||||
|
agent_id = 1
|
||||||
|
pop_count = {}
|
||||||
|
all_agents = []
|
||||||
|
for key in grade_data.keys():
|
||||||
|
grade = grade_data[key]
|
||||||
|
#pop_count.append(grade["Pop Current"])
|
||||||
|
pop_count[key] = grade["Pop Current"]
|
||||||
|
for _ in range(grade["Pop Current"]):
|
||||||
|
rng = np.random.default_rng()
|
||||||
|
agent = AgentSetup(
|
||||||
|
id=agent_id,
|
||||||
|
grade=key,
|
||||||
|
speed=max(0.1,(rng.normal(
|
||||||
|
grade["Speed Mean"],
|
||||||
|
grade["Speed Std Dev"]
|
||||||
|
))),
|
||||||
|
radius=grade["Radius"]
|
||||||
|
)
|
||||||
|
all_agents.append(agent)
|
||||||
|
agent_id +=1
|
||||||
|
return all_agents,pop_count
|
||||||
|
|
||||||
17
_source_/config.py
Normal file
17
_source_/config.py
Normal 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
|
||||||
|
SOURCE_DIR = ROOT/"_source_"
|
||||||
|
ARCHIVE_DIR = ROOT/"archive"
|
||||||
|
PATH_DIR = ROOT/"path"
|
||||||
|
AGENTS_DIR = ROOT/SOURCE_DIR/"agents"
|
||||||
|
#GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
|
||||||
|
#TEST_DIR = ROOT/SOURCE_DIR/"test"
|
||||||
|
|
||||||
|
|
||||||
153
_source_/crosswalk_setup.py
Normal file
153
_source_/crosswalk_setup.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
from dataclasses import dataclass,field
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List,Dict,Tuple,Set
|
||||||
|
from shapely import Polygon,Point
|
||||||
|
from geometry_setup import GeometrySetup
|
||||||
|
import shapely
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class CrosswalkStatus(Enum):
|
||||||
|
INACTIVE = "inactive"
|
||||||
|
ACTIVE = "active"
|
||||||
|
COOLDOWN = "cooldown"
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CrosswalkConfig:
|
||||||
|
trigger_dist:float
|
||||||
|
activation_time:float
|
||||||
|
cooldown_time:float
|
||||||
|
min_agents_activation:int
|
||||||
|
activation_delay:float
|
||||||
|
crosswalk_area:Polygon
|
||||||
|
trigger_area:Polygon
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CrosswalkState:
|
||||||
|
status:CrosswalkStatus
|
||||||
|
is_active:bool
|
||||||
|
time_active:float
|
||||||
|
time_remaining:float
|
||||||
|
agents_in_trigger:int
|
||||||
|
agents_waiting:int
|
||||||
|
activation_count:int
|
||||||
|
|
||||||
|
class CrosswalkController:
|
||||||
|
def __init__(self,config:CrosswalkConfig,current_setup:bool):
|
||||||
|
self.config = config
|
||||||
|
self.crosswalk_area = config.crosswalk_area
|
||||||
|
self.trigger_area = config.trigger_area
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = 0.0
|
||||||
|
self.current_time = 0.0
|
||||||
|
self.activation_count = 0
|
||||||
|
self.agents_in_trigger: Set[int] = set()
|
||||||
|
self.agents_waiting: Set[int] = set()
|
||||||
|
self.total_active_time = 0.0
|
||||||
|
self.agents_served = 0
|
||||||
|
|
||||||
|
def update(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]],
|
||||||
|
time_step:float,
|
||||||
|
current_time:float=None
|
||||||
|
)->CrosswalkState:
|
||||||
|
if current_time is not None:
|
||||||
|
self.current_time = current_time
|
||||||
|
else:
|
||||||
|
self.current_time +=time_step
|
||||||
|
self.update_agents_in_trigger(agent_pos)
|
||||||
|
self.update_state(time_step)
|
||||||
|
self.update_statistics(time_step)
|
||||||
|
return self.get_state()
|
||||||
|
|
||||||
|
def update_agents_in_trigger(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]]
|
||||||
|
)->None:
|
||||||
|
self.agents_in_trigger.clear()
|
||||||
|
if not agent_pos:
|
||||||
|
return
|
||||||
|
agent_ids = list(agent_pos.keys())
|
||||||
|
positions = np.array(list(agent_pos.values()),dtype=np.float32)
|
||||||
|
in_trigger = self.points_in_area(positions,self.trigger_area)
|
||||||
|
for i, agent_id in enumerate(agent_ids):
|
||||||
|
if in_trigger[i]:
|
||||||
|
self.agents_in_trigger.add(agent_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
|
||||||
|
if len(points) ==0:
|
||||||
|
return np.array([],dtype=bool)
|
||||||
|
buffered_area = polygon.buffer(1e-9)
|
||||||
|
inside = np.zeros(len(points),dtype=bool)
|
||||||
|
for i,(x,y) in enumerate(points):
|
||||||
|
point = Point(x,y)
|
||||||
|
inside[i] = buffered_area.contains(point)
|
||||||
|
return inside
|
||||||
|
|
||||||
|
def update_state(self,time_step:float)->None:
|
||||||
|
elapsed = self.current_time - self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
if elapsed >= self.config.activation_time:
|
||||||
|
self.deactivate()
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
if elapsed >= self.config.cooldown_time:
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
elif self.status ==CrosswalkStatus.INACTIVE:
|
||||||
|
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
|
||||||
|
elapsed >= self.config.activation_delay):
|
||||||
|
self.activate()
|
||||||
|
|
||||||
|
def activate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.ACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
self.activation_count +=1
|
||||||
|
self.agents_served += len(self.agents_waiting)
|
||||||
|
self.agents_waiting.clear()
|
||||||
|
|
||||||
|
def deactivate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.COOLDOWN
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
|
||||||
|
def update_statistics(self,time_step:float)->None:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
self.total_active_time += time_step
|
||||||
|
|
||||||
|
def get_state(self)->CrosswalkState:
|
||||||
|
elapsed = self.current_time-self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
time_remaining = max(0.0,self.config.activation_time-elapsed)
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
time_remaining = max(0.0,self.config.cooldown_time-elapsed)
|
||||||
|
else:
|
||||||
|
time_remaining = 0.0
|
||||||
|
return CrosswalkState(
|
||||||
|
status=self.status,
|
||||||
|
is_active=(self.status == CrosswalkStatus.ACTIVE),
|
||||||
|
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
|
||||||
|
time_remaining=time_remaining,
|
||||||
|
agents_in_trigger=len(self.agents_in_trigger),
|
||||||
|
agents_waiting=len(self.agents_waiting),
|
||||||
|
activation_count = self.activation_count
|
||||||
|
)
|
||||||
|
def can_agent_cross(self,agent_id:int)->bool:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if agent_id in self.agents_in_trigger:
|
||||||
|
self.agents_waiting.add(agent_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self)->bool:
|
||||||
|
return self.status == CrosswalkStatus.ACTIVE
|
||||||
|
|
||||||
|
def get_statistics(self)->Dict[str,float]:
|
||||||
|
return {
|
||||||
|
"total_activations":self.activation_count,
|
||||||
|
"total_active_time":self.total_active_time,
|
||||||
|
"agents_served":self.agents_served,
|
||||||
|
"current_agents_in_trigger":len(self.agents_in_trigger),
|
||||||
|
"current_agents_waiting":len(self.agents_waiting)
|
||||||
|
}
|
||||||
279
_source_/geometry_setup.py
Normal file
279
_source_/geometry_setup.py
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
from matplotlib.axes import Axes
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
class GeometrySetup:
|
||||||
|
def __init__(self,current_setup:bool):
|
||||||
|
self.current_setup = current_setup
|
||||||
|
self.spawn_area = self.spawn()
|
||||||
|
self.queue_area = self.queue()
|
||||||
|
self.grass_area = self.grass()
|
||||||
|
self.crosswalk_area = self.crosswalk()
|
||||||
|
self.entry_area = self.entry_polygon()
|
||||||
|
self.exit_area = self.exit_polygon()
|
||||||
|
|
||||||
|
def spawn(self)->Polygon:
|
||||||
|
spawn_area = Polygon([
|
||||||
|
(25.0,16.823),
|
||||||
|
(25.0,4.569),
|
||||||
|
(26.163,4.569),
|
||||||
|
(28.214,5.07),
|
||||||
|
(28.214,17.761),
|
||||||
|
(25.787,17.761),
|
||||||
|
])
|
||||||
|
return spawn_area
|
||||||
|
|
||||||
|
def queue(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(18.767,9.395),
|
||||||
|
(16.924,9.395),
|
||||||
|
(15.214,9.395),
|
||||||
|
(15.214,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(19.531,10.306),
|
||||||
|
(15.214,10.306),
|
||||||
|
(13.88,10.306),
|
||||||
|
(12.98,9.896),
|
||||||
|
(12.98,0),
|
||||||
|
(15.214,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
return queue_area
|
||||||
|
|
||||||
|
def grass(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(4,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(6.23,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761),
|
||||||
|
(4,10.306),
|
||||||
|
(5.33,10.306),
|
||||||
|
(6.23,9.896)
|
||||||
|
])
|
||||||
|
return grass_area
|
||||||
|
|
||||||
|
def crosswalk(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(4,6.068),
|
||||||
|
(4,7.896),
|
||||||
|
(15.214,7.896),
|
||||||
|
(15.214,6.068)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(6.23,4.982),
|
||||||
|
(6.23,8.982),
|
||||||
|
(12.98,8.982),
|
||||||
|
(12.98,4.982)
|
||||||
|
])
|
||||||
|
return crosswalk_area
|
||||||
|
|
||||||
|
def entry_polygon(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
entry_area = Polygon([ # x: 2.9m, y: 3.428m
|
||||||
|
(15.314,5.268), # dx: 0.1m, dy: 0.8m
|
||||||
|
(15.314,8.696),
|
||||||
|
(18.214,8.696),
|
||||||
|
(18.214,5.268)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
entry_area = Polygon([ # x: 2.9m, y: 5.6m
|
||||||
|
(15.98,9.782),
|
||||||
|
(15.98,4.182),
|
||||||
|
(13.08,4.182),
|
||||||
|
(13.08,9.782)
|
||||||
|
])
|
||||||
|
return entry_area
|
||||||
|
|
||||||
|
def exit_polygon(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
exit_area = Polygon([ # x: 2.9m, y: 3.428m
|
||||||
|
(1,5.268),
|
||||||
|
(1,8.696),
|
||||||
|
(3.9,8.696),
|
||||||
|
(3.9,5.268)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
exit_area = Polygon([ # x: 2.9m, y: 5.6m
|
||||||
|
(3.23,4.182),
|
||||||
|
(3.23,9.782),
|
||||||
|
(6.13,9.782),
|
||||||
|
(6.13,4.182)
|
||||||
|
])
|
||||||
|
return exit_area
|
||||||
|
|
||||||
|
def plot_all(self)->Tuple[Figure,Axes]:
|
||||||
|
fig,ax = plt.subplots(figsize=(12,8))
|
||||||
|
plot_polygon(
|
||||||
|
self.spawn_area,
|
||||||
|
color="lightblue",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plot_polygon(
|
||||||
|
self.queue_area,
|
||||||
|
color="blue",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plot_polygon(
|
||||||
|
self.grass_area,
|
||||||
|
color="blue",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plot_polygon(
|
||||||
|
self.crosswalk_area,
|
||||||
|
color="red",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plot_polygon(
|
||||||
|
self.entry_area,
|
||||||
|
color="green",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plot_polygon(
|
||||||
|
self.exit_area,
|
||||||
|
color="green",
|
||||||
|
add_points=False,
|
||||||
|
ax=ax,
|
||||||
|
alpha=0.4
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.spawn_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.queue_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.grass_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.crosswalk_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.entry_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
*self.exit_area.exterior.xy,
|
||||||
|
color='black',
|
||||||
|
alpha=0.7,
|
||||||
|
linewidth=1
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
self.spawn_area.centroid.x,
|
||||||
|
self.spawn_area.centroid.y,
|
||||||
|
'Spawn\nArea',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontweight='bold'
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
self.queue_area.centroid.x,
|
||||||
|
self.queue_area.centroid.y,
|
||||||
|
'Queue',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontweight='bold'
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
self.crosswalk_area.centroid.x,
|
||||||
|
self.crosswalk_area.centroid.y,
|
||||||
|
'Crosswalk',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontweight='bold',
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
self.entry_area.centroid.x,
|
||||||
|
self.entry_area.centroid.y,
|
||||||
|
'Crosswalk\nEntry',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontsize=9,
|
||||||
|
fontweight='bold',
|
||||||
|
#color="white"
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
self.exit_area.centroid.x,
|
||||||
|
self.exit_area.centroid.y,
|
||||||
|
'Crosswalk\nExit',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontsize=9,
|
||||||
|
fontweight='bold',
|
||||||
|
#color="white"
|
||||||
|
)
|
||||||
|
ax.text(
|
||||||
|
(self.grass_area.centroid.x-0.9 if not\
|
||||||
|
self.current_setup else self.grass_area.centroid.x),
|
||||||
|
self.grass_area.centroid.y+2,
|
||||||
|
'Grass',
|
||||||
|
ha='center',
|
||||||
|
va='center',
|
||||||
|
fontweight='bold'
|
||||||
|
)
|
||||||
|
ax.set_xlabel('X (m)')
|
||||||
|
ax.set_ylabel('Y (m)')
|
||||||
|
ax.set_title(
|
||||||
|
f'{"Current" if self.current_setup else "Smart"} Crosswalk Design',
|
||||||
|
fontweight='bold',
|
||||||
|
fontsize=15
|
||||||
|
)
|
||||||
|
ax.set_aspect('equal')
|
||||||
|
ax.grid(True, alpha=0.3)
|
||||||
|
return fig,ax
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from PyQt6 import QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
fig,ax = GeometrySetup(True).plot_all()
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
9
_source_/requirements.txt
Normal file
9
_source_/requirements.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
matplotlib
|
||||||
|
PyQt5
|
||||||
|
numpy
|
||||||
|
scipy
|
||||||
|
shapely
|
||||||
|
pedpy
|
||||||
|
jupedsim
|
||||||
|
pandas
|
||||||
|
plotly
|
||||||
253
_source_/simulation.py
Normal file
253
_source_/simulation.py
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
from typing import Tuple,List,Dict
|
||||||
|
import sys
|
||||||
|
from datetime import datetime as dt
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from shapely import Polygon,Point,GeometryCollection,get_coordinates
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
import jupedsim as jps
|
||||||
|
import numpy as np
|
||||||
|
import pathlib
|
||||||
|
from geometry_setup import GeometrySetup
|
||||||
|
from spawning import SpawnManager
|
||||||
|
from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig
|
||||||
|
from agent_setup import AgentConfig
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SimulationSetup:
|
||||||
|
simulation_time:float=200.0
|
||||||
|
time_step:float=0.01
|
||||||
|
max_iterations:int=50000
|
||||||
|
trajectory_file:str="crosswalk_sim.sqlite"
|
||||||
|
current_setup:bool=True
|
||||||
|
trigger_dist:float=1.0
|
||||||
|
activation_time:float=30.0
|
||||||
|
cooldown_time:float=10.0
|
||||||
|
min_agents_activation:int=1
|
||||||
|
activation_delay:float=2.0
|
||||||
|
|
||||||
|
class CrosswalkSimulation:
|
||||||
|
def __init__(self,config:SimulationSetup):
|
||||||
|
self.config = config
|
||||||
|
self.RED = "\033[1;31m"
|
||||||
|
self.GREEN = "\033[1;32m"
|
||||||
|
self.YELLOW = "\033[1;33m"
|
||||||
|
self.BLUE = "\033[1;34m"
|
||||||
|
self.PURPLE = "\033[1;35m"
|
||||||
|
self.CYAN = "\033[1;36m"
|
||||||
|
self.RESET = "\033[0m"
|
||||||
|
|
||||||
|
print(f"{self.YELLOW}\nInitializing Geometry...")
|
||||||
|
self.geo = GeometrySetup(self.config.current_setup)
|
||||||
|
self.spawn_area = self.geo.spawn()
|
||||||
|
self.queue_area = self.geo.queue()
|
||||||
|
self.grass_area = self.geo.grass()
|
||||||
|
self.crosswalk_area = self.geo.crosswalk()
|
||||||
|
self.entry_area = self.geo.entry_polygon()
|
||||||
|
self.exit_area = self.geo.exit_polygon()
|
||||||
|
self.walkable_area = GeometryCollection([
|
||||||
|
self.spawn_area,
|
||||||
|
self.queue_area,
|
||||||
|
self.grass_area,
|
||||||
|
self.crosswalk_area,
|
||||||
|
])
|
||||||
|
print(f"{self.GREEN}Geometry Configuration Complete!")
|
||||||
|
|
||||||
|
print(f"{self.YELLOW}\nInitializing Agent Setup...")
|
||||||
|
self.all_agents, pop_count = AgentConfig()
|
||||||
|
self.total_agents = len(self.all_agents)
|
||||||
|
self.req_spacing = (max([agent.radius for agent in self.all_agents])*2.2)
|
||||||
|
print(f"{self.GREEN}Agent Setup Complete!")
|
||||||
|
print(f"{self.RESET}Generated {self.total_agents} Agents")
|
||||||
|
print(f"Population Breakdown:\n"+"".join(f"\t{k}: {v}\n" for k,v in pop_count.items()))
|
||||||
|
|
||||||
|
print(f"{self.YELLOW}Initializing Spawn Manager...")
|
||||||
|
self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=self.req_spacing)
|
||||||
|
self.all_coords = self.spawn_manager.generate_coords()
|
||||||
|
print(f"{self.GREEN}Spawn Manager Setup Complete!")
|
||||||
|
print(f"{self.RESET}Generated {len(self.all_coords)} Spawn Coordinates")
|
||||||
|
|
||||||
|
print(f"{self.YELLOW}\nInitializing Crosswalk Controller...")
|
||||||
|
self.crosswalk_controller = CrosswalkController(
|
||||||
|
CrosswalkConfig(
|
||||||
|
trigger_dist=self.config.trigger_dist,
|
||||||
|
activation_time=self.config.activation_time,
|
||||||
|
cooldown_time=self.config.cooldown_time,
|
||||||
|
min_agents_activation=self.config.min_agents_activation,
|
||||||
|
activation_delay=self.config.activation_delay,
|
||||||
|
crosswalk_area=self.crosswalk_area,
|
||||||
|
trigger_area=self.entry_area
|
||||||
|
),
|
||||||
|
self.config.current_setup
|
||||||
|
)
|
||||||
|
print(f"{self.GREEN}Crosswalk Controller Setup Complete!")
|
||||||
|
|
||||||
|
print(f"{self.YELLOW}\nInitializing JuPedSim Model...")
|
||||||
|
self.trajectory_file = self.config.trajectory_file
|
||||||
|
self.simulation = jps.Simulation(
|
||||||
|
model=jps.CollisionFreeSpeedModel(),
|
||||||
|
geometry=self.walkable_area,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(self.trajectory_file)
|
||||||
|
),
|
||||||
|
dt=self.config.time_step
|
||||||
|
)
|
||||||
|
self.journey_setup()
|
||||||
|
self.jps_agent_ids:Dict[int,int] = {}
|
||||||
|
self.iteration_count = 0
|
||||||
|
self.current_time = 0.0
|
||||||
|
print(f"{self.GREEN}JuPedSim Model Initialized{self.RESET}")
|
||||||
|
print(f"\tTime Step: {self.config.time_step}s")
|
||||||
|
print(f"\tMax Iterations: {self.config.max_iterations}")
|
||||||
|
print(f"\tCrosswalk Activation Length: {self.config.activation_time}s")
|
||||||
|
print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time}s")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def journey_setup(self):
|
||||||
|
self.queue_waiting_coords = self.waiting_coords(
|
||||||
|
self.queue_area,
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.queue_stage_id = self.simulation.add_waiting_set_stage(
|
||||||
|
self.queue_waiting_coords
|
||||||
|
)
|
||||||
|
self.queue_stage = self.simulation.get_stage(self.queue_stage_id)
|
||||||
|
self.queue_stage.state = jps.WaitingSetState.ACTIVE
|
||||||
|
self.crosswalk_entry_id = self.simulation.add_waypoint_stage((
|
||||||
|
self.entry_area.centroid.x,
|
||||||
|
self.entry_area.centroid.y),
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.crosswalk_exit_id = self.simulation.add_waypoint_stage((
|
||||||
|
self.exit_area.centroid.x,
|
||||||
|
self.exit_area.centroid.y),
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.exit_stage_id = self.simulation.add_waypoint_stage((
|
||||||
|
self.grass_area.centroid.x,
|
||||||
|
self.grass_area.centroid.y),
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.journey = jps.JourneyDescription([
|
||||||
|
self.queue_stage_id,
|
||||||
|
self.crosswalk_entry_id,
|
||||||
|
self.crosswalk_exit_id,
|
||||||
|
self.exit_stage_id
|
||||||
|
])
|
||||||
|
self.journey.set_transition_for_stage(
|
||||||
|
self.queue_stage_id,
|
||||||
|
jps.Transition.create_fixed_transition(
|
||||||
|
self.crosswalk_entry_id
|
||||||
|
))
|
||||||
|
self.journey.set_transition_for_stage(
|
||||||
|
self.crosswalk_entry_id,
|
||||||
|
jps.Transition.create_fixed_transition(
|
||||||
|
self.crosswalk_exit_id
|
||||||
|
))
|
||||||
|
self.journey.set_transition_for_stage(
|
||||||
|
self.crosswalk_exit_id,
|
||||||
|
jps.Transition.create_fixed_transition(
|
||||||
|
self.exit_stage_id
|
||||||
|
))
|
||||||
|
self.journey_id = self.simulation.add_journey(
|
||||||
|
self.journey)
|
||||||
|
|
||||||
|
def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]:
|
||||||
|
min_x,min_y,max_x,max_y = area.bounds
|
||||||
|
points = []
|
||||||
|
x = (min_x+spacing)/2
|
||||||
|
while x<max_x:
|
||||||
|
y = (min_y+spacing)/2
|
||||||
|
while y < max_y:
|
||||||
|
if area.contains(Point(x, y)):
|
||||||
|
points.append((x,y))
|
||||||
|
y += spacing
|
||||||
|
x += spacing
|
||||||
|
return points if points else [(min_x+0.5, min_y+0.5)]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
print(f"{self.CYAN}\nStarting Simulation")
|
||||||
|
print(f"Target Agents: {len(self.all_agents)}\n{self.RESET}")
|
||||||
|
while self.iteration_count < self.config.max_iterations:
|
||||||
|
if self.spawn_manager.check_spawn_complete(self.all_agents):
|
||||||
|
new_agent = self.spawn_manager.spawn_agent(self.all_agents)
|
||||||
|
if new_agent:
|
||||||
|
jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters(
|
||||||
|
journey_id=self.journey_id,
|
||||||
|
stage_id=self.queue_stage_id,
|
||||||
|
position=self.spawn_manager.get_agent_pos(new_agent.id),
|
||||||
|
desired_speed=new_agent.speed,
|
||||||
|
radius=new_agent.radius
|
||||||
|
)
|
||||||
|
jps_agent_id = self.simulation.add_agent(jps_agent_params)
|
||||||
|
self.jps_agent_ids[new_agent.id] = jps_agent_id
|
||||||
|
#if self.iteration_count % 100 ==0:
|
||||||
|
# print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Total Agents Spawned {len(self.jps_agent_ids)}")
|
||||||
|
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Total Agents Spawned {len(self.jps_agent_ids)}")
|
||||||
|
current_agent_pos = {}
|
||||||
|
for spawn_id,jps_id in self.jps_agent_ids.items():
|
||||||
|
try:
|
||||||
|
agent = self.simulation.agent(jps_id)
|
||||||
|
current_agent_pos[spawn_id] = (agent.position[0],agent.position[1])
|
||||||
|
except RuntimeError:
|
||||||
|
continue
|
||||||
|
crosswalk_state = self.crosswalk_controller.update(
|
||||||
|
current_agent_pos,
|
||||||
|
self.config.time_step,
|
||||||
|
self.current_time
|
||||||
|
)
|
||||||
|
if crosswalk_state.is_active:
|
||||||
|
if self.queue_stage.state != jps.WaitingSetState.INACTIVE:
|
||||||
|
self.queue_stage.state = jps.WaitingSetState.INACTIVE
|
||||||
|
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Crosswalk ACTIVATED")
|
||||||
|
else:
|
||||||
|
if self.queue_stage.state != jps.WaitingSetState.ACTIVE:
|
||||||
|
self.queue_stage.state = jps.WaitingSetState.ACTIVE
|
||||||
|
self.simulation.iterate()
|
||||||
|
self.current_time += self.config.time_step
|
||||||
|
self.iteration_count +=1
|
||||||
|
agents_to_remove = []
|
||||||
|
for spawn_id, jps_id in list(self.jps_agent_ids.items()):
|
||||||
|
try:
|
||||||
|
_ = self.simulation.agent(jps_id)
|
||||||
|
except RuntimeError:
|
||||||
|
agents_to_remove.append(spawn_id)
|
||||||
|
|
||||||
|
for spawn_id in agents_to_remove:
|
||||||
|
self.spawn_manager.unfill_coords(spawn_id)
|
||||||
|
del self.jps_agent_ids[spawn_id]
|
||||||
|
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} JOURNEY COMPLETE: Agent {spawn_id}")
|
||||||
|
if (len(self.jps_agent_ids) ==0 and not self.spawn_manager.check_spawn_complete(self.all_agents)):
|
||||||
|
print(f"{self.GREEN}\nSimulation Completed at Iteration: {self.iteration_count}")
|
||||||
|
print(f"Total Simulation Time: {self.current_time:.2f} s")
|
||||||
|
break
|
||||||
|
print("\n")
|
||||||
|
print(f"{self.GREEN}=== SIMULATION COMPLETE ===")
|
||||||
|
stats = self.crosswalk_controller.get_statistics()
|
||||||
|
print(f"{self.YELLOW}Crosswalk Activations: {self.RESET}{stats['total_activations']}")
|
||||||
|
print(f"{self.YELLOW}Agents Served: {self.RESET}{stats['agents_served']}")
|
||||||
|
print(f"{self.PURPLE}Trajectory File:\n{self.trajectory_file}{self.RESET}\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
config = SimulationSetup(
|
||||||
|
simulation_time=200.0,
|
||||||
|
time_step=0.01,
|
||||||
|
max_iterations=50000,
|
||||||
|
trajectory_file=f"_SRS_sim_{dt.now().strftime("%H%M%S")}_.sqlite",
|
||||||
|
current_setup=True,
|
||||||
|
trigger_dist=1.0,
|
||||||
|
activation_time=30.0,
|
||||||
|
cooldown_time=10.0,
|
||||||
|
min_agents_activation=1,
|
||||||
|
activation_delay=2.0,
|
||||||
|
)
|
||||||
|
sim = CrosswalkSimulation(config)
|
||||||
|
sim.run()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
102
_source_/spawning.py
Normal file
102
_source_/spawning.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from shapely.geometry import Polygon,Point
|
||||||
|
from typing import Tuple, List, Dict, Set
|
||||||
|
from scipy.spatial import cKDTree
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
from agent_setup import AgentSetup
|
||||||
|
|
||||||
|
|
||||||
|
class SpawnManager:
|
||||||
|
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
|
||||||
|
"""
|
||||||
|
self.spawn_area: geometry where agents may spawn
|
||||||
|
self.min_spacing: minimum spacing for spawn_points
|
||||||
|
self.spawn_coords: all spawn points available
|
||||||
|
self.filled_coords: spawn points currently filled by agents
|
||||||
|
self.spawned_agents: all agents already spawned in sim
|
||||||
|
self.agent_pos: connects agent_id to spawn_index
|
||||||
|
self.rng: random number generator object
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.spawn_area = spawn_area
|
||||||
|
self.min_spacing = min_spacing
|
||||||
|
self.spawn_coords: np.ndarray = np.array([])
|
||||||
|
self.filled_coords: np.ndarray = np.array([])
|
||||||
|
self.spawned_agents:Set[int] = set()
|
||||||
|
self.agent_pos:Dict[int,int] = {}
|
||||||
|
self.rng = np.random.default_rng()
|
||||||
|
|
||||||
|
def generate_coords(self,max_samples:int=1000)->None:
|
||||||
|
buffered_spawn = self.spawn_area.buffer(-self.min_spacing)
|
||||||
|
min_x,min_y,max_x,max_y = buffered_spawn.bounds
|
||||||
|
points = []
|
||||||
|
while len(points) ==0:
|
||||||
|
x = self.rng.uniform(min_x,max_x)
|
||||||
|
y = self.rng.uniform(min_y,max_y)
|
||||||
|
if buffered_spawn.contains(Point(x,y)):
|
||||||
|
points.append([x,y])
|
||||||
|
for _ in range(max_samples):
|
||||||
|
idx = self.rng.integers(0,len(points))
|
||||||
|
base = points[idx]
|
||||||
|
for _ in range(25):
|
||||||
|
angle = self.rng.uniform(0,2*np.pi)
|
||||||
|
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
|
||||||
|
x = base[0]+radius*np.cos(angle)
|
||||||
|
y = base[1]+radius*np.sin(angle)
|
||||||
|
if not buffered_spawn.contains(Point(x,y)):
|
||||||
|
continue
|
||||||
|
if len(points)>0:
|
||||||
|
tree = cKDTree(points)
|
||||||
|
distance,_ = tree.query([[x,y]],k=1)
|
||||||
|
if distance[0]<self.min_spacing:
|
||||||
|
continue
|
||||||
|
points.append([x,y])
|
||||||
|
break
|
||||||
|
self.spawn_coords = np.array(points)
|
||||||
|
self.filled_coords = np.zeros(len(points),dtype=bool)
|
||||||
|
return self.spawn_coords
|
||||||
|
|
||||||
|
def get_coords(self)->Tuple[float,float]|None:
|
||||||
|
free_idx = np.where(~self.filled_coords)[0]
|
||||||
|
if len(free_idx) == 0:
|
||||||
|
return None
|
||||||
|
idx = self.rng.choice(free_idx)
|
||||||
|
return tuple(self.spawn_coords[idx])
|
||||||
|
|
||||||
|
|
||||||
|
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
|
||||||
|
if len(self.spawned_agents) >= len(all_agents):
|
||||||
|
return None
|
||||||
|
spawn_point = self.get_coords()
|
||||||
|
if not spawn_point:
|
||||||
|
return None
|
||||||
|
|
||||||
|
free_agents = [agent for agent in all_agents \
|
||||||
|
if agent.id not in self.spawned_agents
|
||||||
|
]
|
||||||
|
if not free_agents:
|
||||||
|
return None
|
||||||
|
agent = self.rng.choice(free_agents)
|
||||||
|
self.spawned_agents.add(agent.id)
|
||||||
|
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
|
||||||
|
spawn_idx = np.argmin(distances)
|
||||||
|
self.filled_coords[spawn_idx] = True
|
||||||
|
self.agent_pos[agent.id] = spawn_idx
|
||||||
|
return agent
|
||||||
|
|
||||||
|
def unfill_coords(self,agent_id:int)->None:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
self.filled_coords[spawn_idx] = False
|
||||||
|
del self.agent_pos[agent_id]
|
||||||
|
self.spawned_agents.discard(agent_id)
|
||||||
|
|
||||||
|
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
return tuple(self.spawn_coords[spawn_idx])
|
||||||
|
return (0,0)
|
||||||
|
|
||||||
|
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
|
||||||
|
return (len(self.spawned_agents)<len(all_agents) and\
|
||||||
|
np.sum(~self.filled_coords) > 0)
|
||||||
47
_source_/visualizer.py
Normal file
47
_source_/visualizer.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.animation import FuncAnimation
|
||||||
|
import numpy as np
|
||||||
|
from jupedsim.internal.notebook_utils import read_sqlite_file
|
||||||
|
|
||||||
|
def visualize(trajectory_file):
|
||||||
|
traj_data,walkable_area = read_sqlite_file(trajectory_file)
|
||||||
|
df = traj_data.data
|
||||||
|
fig,ax = plt.subplots(figsize=(12,8))
|
||||||
|
if hasattr(walkable_area,"polygon"):
|
||||||
|
x,y = walkable_area.polygon.exterior.xy
|
||||||
|
ax.plot(x,y,'k-',alpha=0.3)
|
||||||
|
scatter = ax.scatter([],[],s=20,c="blue",alpha=0.7)
|
||||||
|
frame_text = ax.text(0.02,0.98,'',transform=ax.transAxes)
|
||||||
|
frames=sorted(df["frame"].unique())
|
||||||
|
frames=frames[::5]
|
||||||
|
|
||||||
|
def update(frame):
|
||||||
|
frame_data = df[df["frame"]==frame]
|
||||||
|
if len(frame_data)>0:
|
||||||
|
positions=np.column_stack(
|
||||||
|
[frame_data["x"].values,frame_data["y"].values])
|
||||||
|
scatter.set_offsets(positions)
|
||||||
|
frame_text.set_text(f"Frame: {frame}, Agents: {len(frame_data)}")
|
||||||
|
return scatter, frame_text
|
||||||
|
|
||||||
|
anim = FuncAnimation(fig,update,frames=frames,blit=False,interval=1)
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
file_1 = "SRS_evac_2025-11-23_23-03-09.sqlite"
|
||||||
|
file_2 = "SRS_evac_2025-11-24_01-03-06.sqlite"
|
||||||
|
file_3 = "SRS_evac_2025-11-30_19-43-15.sqlite"
|
||||||
|
file_4 = "crosswalk_sim.sqlite"
|
||||||
|
file_5 = "_SRS_sim_232009_.sqlite"
|
||||||
|
file_6 = "_SRS_sim_231809_.sqlite"
|
||||||
|
file_7 = "_SRS_sim_231233_.sqlite"
|
||||||
|
file_8 = "_SRS_sim_010542_.sqlite"
|
||||||
|
file_9 = "_SRS_sim_013834_.sqlite"
|
||||||
|
visualize(file_3)
|
||||||
BIN
archive/SRS_evac_path/SRS_evac.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-23-22.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-23-22.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-41-16.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-41-16.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-42-15.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-42-15.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-45-35.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-45-35.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-51-43.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-51-43.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-52-52.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-52-52.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-54-40.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_00-54-40.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_01-01-57.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-24_01-01-57.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-30_19-35-45.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-30_19-35-45.sqlite
Normal file
Binary file not shown.
BIN
archive/SRS_evac_path/SRS_evac_2025-11-30_19-37-01.sqlite
Normal file
BIN
archive/SRS_evac_path/SRS_evac_2025-11-30_19-37-01.sqlite
Normal file
Binary file not shown.
286
archive/SRS_modeling_2025-11-09.py
Normal file
286
archive/SRS_modeling_2025-11-09.py
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sat Nov 8 22:59:23 2025
|
||||||
|
|
||||||
|
@author: ethan
|
||||||
|
"""
|
||||||
|
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import pathlib
|
||||||
|
import jupedsim as jps
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
def main():
|
||||||
|
grades = {
|
||||||
|
# mean and standard deviation for
|
||||||
|
# average population size
|
||||||
|
# and average speed by grade levels
|
||||||
|
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
|
||||||
|
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
|
||||||
|
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
|
||||||
|
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
|
||||||
|
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
|
||||||
|
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
|
||||||
|
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
|
||||||
|
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
|
||||||
|
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
|
||||||
|
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
|
||||||
|
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
|
||||||
|
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
|
||||||
|
"Grade 12":np.array([43.71,6.02,1.54,0.23])
|
||||||
|
}
|
||||||
|
df=pd.DataFrame({
|
||||||
|
"Grade Level":(
|
||||||
|
list(grades.keys())),
|
||||||
|
"Pop Mean":[
|
||||||
|
grades[j][0] for j in grades],
|
||||||
|
"Pop Std Dev":[
|
||||||
|
grades[j][1] for j in grades],
|
||||||
|
"Speed Mean":[
|
||||||
|
grades[j][2] for j in grades],
|
||||||
|
"Speed Std Dev":[
|
||||||
|
grades[j][3] for j in grades]})
|
||||||
|
rng = np.random.default_rng(seed=42)
|
||||||
|
A_crosswalk=Polygon([
|
||||||
|
(-1,1.499),(-1,3.327),
|
||||||
|
(0,3.327),(11.214, 3.327),
|
||||||
|
(11.214,1.499),(0,1.499)])
|
||||||
|
B_queue=Polygon([
|
||||||
|
(11.214, 0),(22.163, 0),
|
||||||
|
(22.163, 4.826),(11.214, 4.826)])
|
||||||
|
C_road_adj_path=Polygon([
|
||||||
|
(21.787,4.826),(24.214,4.826),
|
||||||
|
(24.214,40.431),(29.179,40.431),
|
||||||
|
(29.179,42.511),(24.214,42.511),
|
||||||
|
(21.787,42.511)])
|
||||||
|
D_path_k_3=Polygon([
|
||||||
|
(26.45,42.511),(26.45,52.84),
|
||||||
|
(26.45,53.84),(29.179,53.84),
|
||||||
|
(29.179,52.84),(29.179,42.511)])
|
||||||
|
E_path_4_6=Polygon([
|
||||||
|
(29.179,42.511),(54.351,42.511),
|
||||||
|
(60.406,48.842),(60.406,51.22),
|
||||||
|
(60.406,52.22),(63.49,52.22),
|
||||||
|
(63.49,51.22),(63.49,47.866),
|
||||||
|
(56.381,40.431),(29.179,40.431)])
|
||||||
|
F_path_7_12=Polygon([
|
||||||
|
(22.163, 0),(39.227, 5.516),
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935),
|
||||||
|
(43.169,10.954),(24.214,4.826),
|
||||||
|
(22.163,4.826)])
|
||||||
|
G_extended_queue=Polygon([
|
||||||
|
(11.214,0),(12.924,0),
|
||||||
|
(12.924,-4.569),(11.214,-4.569)])
|
||||||
|
H_angled_path=Polygon([
|
||||||
|
(21.787,13.192),(21.787,10.527),
|
||||||
|
(17,4.826),(14.767,4.826)])
|
||||||
|
enter_k_3=Polygon([
|
||||||
|
(26.45,52.84),(29.179,52.84),
|
||||||
|
(29.179,53.84),(26.45,53.84)])
|
||||||
|
enter_4_6=Polygon([
|
||||||
|
(60.406,51.22),(60.406,52.22),
|
||||||
|
(63.49,52.22),(63.49,51.22)])
|
||||||
|
enter_7_12=Polygon([
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935)])
|
||||||
|
exit_polygon=Polygon([
|
||||||
|
(0,1.499),(0,3.327),
|
||||||
|
(-1,3.327),(-1,1.499)])
|
||||||
|
plot_polygon(
|
||||||
|
A_crosswalk,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
B_queue,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
C_road_adj_path, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
D_path_k_3, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
E_path_4_6, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
F_path_7_12, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
G_extended_queue, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
H_angled_path, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_k_3, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_4_6, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_7_12, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
exit_polygon, color="orangered",add_points=False)
|
||||||
|
geometry = (A_crosswalk.union(
|
||||||
|
B_queue).union(
|
||||||
|
C_road_adj_path).union(
|
||||||
|
D_path_k_3).union(
|
||||||
|
E_path_4_6).union(
|
||||||
|
F_path_7_12).union(
|
||||||
|
G_extended_queue).union(
|
||||||
|
H_angled_path))
|
||||||
|
trajectory_file = "SRS_evac.sqlite"
|
||||||
|
simulation = jps.Simulation(
|
||||||
|
model=jps.AnticipationVelocityModel(),
|
||||||
|
geometry=geometry,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(trajectory_file)))
|
||||||
|
exit_id = simulation.add_exit_stage(exit_polygon)
|
||||||
|
journey = jps.JourneyDescription([exit_id])
|
||||||
|
journey_id = simulation.add_journey(journey)
|
||||||
|
grade_pop = {}
|
||||||
|
door = {}
|
||||||
|
door_polygon = {
|
||||||
|
"K-3":enter_k_3,
|
||||||
|
"4-6":enter_4_6,
|
||||||
|
"7-12":enter_7_12,
|
||||||
|
}
|
||||||
|
platoon_agents = {}
|
||||||
|
for i, grade in enumerate(df["Grade Level"]):
|
||||||
|
grade_sample=rng.normal(
|
||||||
|
loc=df["Pop Mean"][i],
|
||||||
|
scale=df["Pop Std Dev"][i],size=1)
|
||||||
|
grade_pop[grade] = int(np.ceil(grade_sample[0]))
|
||||||
|
x = grade_pop[grade]
|
||||||
|
if i < 4:
|
||||||
|
door[grade] = "K-3"
|
||||||
|
elif i <7:
|
||||||
|
door[grade] = "4-6"
|
||||||
|
else:
|
||||||
|
door[grade] = "7-12"
|
||||||
|
platoon_a_size = int(x/2)
|
||||||
|
platoon_b_size = x - platoon_a_size
|
||||||
|
platoon_a_id = (2*(i+1))-1
|
||||||
|
platoon_b_id = (2*(i+1))
|
||||||
|
platoon_agents[platoon_a_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_a_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
platoon_agents[platoon_b_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_b_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
spawned_total=0
|
||||||
|
#============================================================
|
||||||
|
agent_set = []
|
||||||
|
for platoon_id, platoon_data in platoon_agents.items():
|
||||||
|
spawn_time=float(
|
||||||
|
rng.uniform(5,15)+rng.uniform(0,120))
|
||||||
|
spawn_time=min(spawn_time,120)
|
||||||
|
remaining=int(platoon_data["Platoon Size"])
|
||||||
|
attempts = 0
|
||||||
|
max_attempts = 50
|
||||||
|
time_delay = 1.0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
while remaining>0 and attempts<max_attempts:
|
||||||
|
n_try = min(batch_size,remaining)
|
||||||
|
try:
|
||||||
|
positions = jps.distribute_by_number(
|
||||||
|
polygon=platoon_data["Entry Door"],
|
||||||
|
number_of_agents=n_try,
|
||||||
|
distance_to_agents=0.45,
|
||||||
|
distance_to_polygon=0.3,
|
||||||
|
max_iterations=1500)
|
||||||
|
placed_count = len(positions)
|
||||||
|
if placed_count ==0:
|
||||||
|
attempts +=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
continue
|
||||||
|
offset=0.1
|
||||||
|
for k, pos in enumerate(positions):
|
||||||
|
speed=float(rng.normal(
|
||||||
|
loc=df["Speed Mean"][i],
|
||||||
|
scale=df["Speed Std Dev"][i],
|
||||||
|
size=1)[0])
|
||||||
|
agent = {
|
||||||
|
"Grade Level":
|
||||||
|
platoon_data["Grade Level"],
|
||||||
|
"Entry Point":
|
||||||
|
(door[platoon_data["Grade Level"]]),
|
||||||
|
"Platoon":platoon_id,
|
||||||
|
"Position":(
|
||||||
|
float(pos[0]),float(pos[1])),
|
||||||
|
"Speed":speed,
|
||||||
|
"Spawn Time":float(
|
||||||
|
spawn_time+(k*offset))}
|
||||||
|
agent_set.append(agent)
|
||||||
|
remaining-=placed_count
|
||||||
|
if remaining>0:
|
||||||
|
attempts+=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
attempts=0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"Error placing platoon {platoon_id}: {e}")
|
||||||
|
print("Reducing batch and retrying")
|
||||||
|
attempts+=1
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
spawn_time+=time_delay
|
||||||
|
spawned_total+=placed_count
|
||||||
|
#=========================================
|
||||||
|
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
|
||||||
|
pending=deque(pending)
|
||||||
|
max_iterations=20000
|
||||||
|
max_agents_per_step=50
|
||||||
|
spawn_tolerance=1e-8
|
||||||
|
while ((simulation.agent_count()>0 or len(pending)>0)
|
||||||
|
and simulation.iteration_count()<max_iterations):
|
||||||
|
current_time=simulation.elapsed_time()
|
||||||
|
agents_this_step=0
|
||||||
|
while (pending and (pending[0]["Spawn Time"]<=(
|
||||||
|
current_time+spawn_tolerance))
|
||||||
|
and agents_this_step<max_agents_per_step):
|
||||||
|
a = pending.popleft()
|
||||||
|
pos=tuple(a["Position"])
|
||||||
|
v0 = float(a["Speed"])
|
||||||
|
v0 = float(np.clip(v0,0.2,2.5))
|
||||||
|
agent_params = (
|
||||||
|
jps.AnticipationVelocityModelAgentParameters(
|
||||||
|
journey_id=journey_id,
|
||||||
|
stage_id=exit_id,
|
||||||
|
position=pos,
|
||||||
|
radius=0.25,
|
||||||
|
desired_speed=v0,
|
||||||
|
anticipation_time=0.5,
|
||||||
|
reaction_time=0.3,
|
||||||
|
wall_buffer_distance=0.08))
|
||||||
|
retry=0
|
||||||
|
max_retry=25
|
||||||
|
while retry < max_retry:
|
||||||
|
try:
|
||||||
|
simulation.add_agent(agent_params)
|
||||||
|
spawned_total +=1
|
||||||
|
agents_this_step+=1
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed: add_agent")
|
||||||
|
print(f"For: pos={pos}, speed={v0}")
|
||||||
|
print(f"{e}: Rescheduling...")
|
||||||
|
retry +=1
|
||||||
|
if retry >= max_retry:
|
||||||
|
print(f"\n\nMax Retries:{max_retry}")
|
||||||
|
break
|
||||||
|
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
|
||||||
|
pos[1]+rng.uniform(-0.1,0.1))
|
||||||
|
agent_params.position =adj_pos
|
||||||
|
simulation.iterate()
|
||||||
|
iter_count = simulation.iteration_count()
|
||||||
|
print(f"\nTime Step: {iter_count}")
|
||||||
|
print(f"Total Agents: {spawned_total}")
|
||||||
|
print(f"Platoon: {platoon_agents.items()}")
|
||||||
|
print("\nSimulation Completed!")
|
||||||
|
print(f"Total Time Steps: {simulation.iteration_count()}")
|
||||||
|
print(f"Elapsed Time: {simulation.elapsed_time()}")
|
||||||
|
print(f"{trajectory_file = }")
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
349
archive/SRS_modeling_2025-11-10.py
Normal file
349
archive/SRS_modeling_2025-11-10.py
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Mon Nov 10 01:02:31 2025
|
||||||
|
|
||||||
|
@author: ethan
|
||||||
|
"""
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import pathlib
|
||||||
|
import jupedsim as jps
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
def main():
|
||||||
|
grades = {
|
||||||
|
# mean and standard deviation for
|
||||||
|
# average population size
|
||||||
|
# and average speed by grade levels
|
||||||
|
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
|
||||||
|
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
|
||||||
|
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
|
||||||
|
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
|
||||||
|
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
|
||||||
|
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
|
||||||
|
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
|
||||||
|
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
|
||||||
|
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
|
||||||
|
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
|
||||||
|
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
|
||||||
|
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
|
||||||
|
"Grade 12":np.array([43.71,6.02,1.54,0.23])
|
||||||
|
}
|
||||||
|
df=pd.DataFrame({
|
||||||
|
"Grade Level":(
|
||||||
|
list(grades.keys())),
|
||||||
|
"Pop Mean":[
|
||||||
|
grades[j][0] for j in grades],
|
||||||
|
"Pop Std Dev":[
|
||||||
|
grades[j][1] for j in grades],
|
||||||
|
"Speed Mean":[
|
||||||
|
grades[j][2] for j in grades],
|
||||||
|
"Speed Std Dev":[
|
||||||
|
grades[j][3] for j in grades]})
|
||||||
|
rng = np.random.default_rng(seed=42)
|
||||||
|
A_crosswalk=Polygon([
|
||||||
|
(-1,1.499),(-1,3.327),
|
||||||
|
(0,3.327),(11.214, 3.327),
|
||||||
|
(11.214,1.499),(0,1.499)])
|
||||||
|
B_queue=Polygon([
|
||||||
|
(11.214, 0),(22.163, 0),
|
||||||
|
(22.163, 4.826),(11.214, 4.826)])
|
||||||
|
C_road_adj_path=Polygon([
|
||||||
|
(21.787,4.826),(24.214,4.826),
|
||||||
|
(24.214,40.431),(29.179,40.431),
|
||||||
|
(29.179,42.511),(24.214,42.511),
|
||||||
|
(21.787,42.511)])
|
||||||
|
D_path_k_3=Polygon([
|
||||||
|
(26.45,42.511),(26.45,52.84),
|
||||||
|
(26.45,53.84),(29.179,53.84),
|
||||||
|
(29.179,52.84),(29.179,42.511)])
|
||||||
|
E_path_4_6=Polygon([
|
||||||
|
(29.179,42.511),(54.351,42.511),
|
||||||
|
(60.406,48.842),(60.406,51.22),
|
||||||
|
(60.406,52.22),(63.49,52.22),
|
||||||
|
(63.49,51.22),(63.49,47.866),
|
||||||
|
(56.381,40.431),(29.179,40.431)])
|
||||||
|
F_path_7_12=Polygon([
|
||||||
|
(22.163, 0),(39.227, 5.516),
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935),
|
||||||
|
(43.169,10.954),(24.214,4.826),
|
||||||
|
(22.163,4.826)])
|
||||||
|
G_extended_queue=Polygon([
|
||||||
|
(11.214,0),(12.924,0),
|
||||||
|
(12.924,-4.569),(11.214,-4.569)])
|
||||||
|
H_angled_path=Polygon([
|
||||||
|
(21.787,13.192),(21.787,10.527),
|
||||||
|
(17,4.826),(14.767,4.826)])
|
||||||
|
enter_k_3=Polygon([
|
||||||
|
(26.45,52.84),(29.179,52.84),
|
||||||
|
(29.179,53.84),(26.45,53.84)])
|
||||||
|
enter_4_6=Polygon([
|
||||||
|
(60.406,51.22),(60.406,52.22),
|
||||||
|
(63.49,52.22),(63.49,51.22)])
|
||||||
|
enter_7_12=Polygon([
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935)])
|
||||||
|
exit_polygon=Polygon([
|
||||||
|
(0,1.499),(0,3.327),
|
||||||
|
(-1,3.327),(-1,1.499)])
|
||||||
|
plot_polygon(
|
||||||
|
A_crosswalk,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
B_queue,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
C_road_adj_path, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
D_path_k_3, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
E_path_4_6, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
F_path_7_12, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
G_extended_queue, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
H_angled_path, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_k_3, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_4_6, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_7_12, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
exit_polygon, color="orangered",add_points=False)
|
||||||
|
geometry = (A_crosswalk.union(
|
||||||
|
B_queue).union(
|
||||||
|
C_road_adj_path).union(
|
||||||
|
D_path_k_3).union(
|
||||||
|
E_path_4_6).union(
|
||||||
|
F_path_7_12).union(
|
||||||
|
G_extended_queue).union(
|
||||||
|
H_angled_path))
|
||||||
|
trajectory_file = "SRS_evac.sqlite"
|
||||||
|
simulation = jps.Simulation(
|
||||||
|
model=jps.AnticipationVelocityModel(),
|
||||||
|
geometry=geometry,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(trajectory_file)))
|
||||||
|
exit_id = simulation.add_exit_stage(exit_polygon)
|
||||||
|
journey = jps.JourneyDescription([exit_id])
|
||||||
|
journey_id = simulation.add_journey(journey)
|
||||||
|
grade_pop = {}
|
||||||
|
door = {}
|
||||||
|
door_polygon = {
|
||||||
|
"K-3":enter_k_3,
|
||||||
|
"4-6":enter_4_6,
|
||||||
|
"7-12":enter_7_12,
|
||||||
|
}
|
||||||
|
platoon_agents = {}
|
||||||
|
for i, grade in enumerate(df["Grade Level"]):
|
||||||
|
grade_sample=rng.normal(
|
||||||
|
loc=df["Pop Mean"][i],
|
||||||
|
scale=df["Pop Std Dev"][i],size=1)
|
||||||
|
grade_pop[grade] = int(np.ceil(grade_sample[0]))
|
||||||
|
x = grade_pop[grade]
|
||||||
|
if i < 4:
|
||||||
|
door[grade] = "K-3"
|
||||||
|
elif i <7:
|
||||||
|
door[grade] = "4-6"
|
||||||
|
else:
|
||||||
|
door[grade] = "7-12"
|
||||||
|
platoon_a_size = int(x/2)
|
||||||
|
platoon_b_size = x - platoon_a_size
|
||||||
|
platoon_a_id = (2*(i+1))-1
|
||||||
|
platoon_b_id = (2*(i+1))
|
||||||
|
platoon_agents[platoon_a_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_a_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
platoon_agents[platoon_b_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_b_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
agent_set = []
|
||||||
|
for platoon_id, platoon_data in platoon_agents.items():
|
||||||
|
spawn_time=float(
|
||||||
|
rng.uniform(5,15)+rng.uniform(0,120))
|
||||||
|
spawn_time=min(spawn_time,120)
|
||||||
|
remaining=int(platoon_data["Platoon Size"])
|
||||||
|
attempts = 0
|
||||||
|
max_attempts = 10
|
||||||
|
time_delay = 1.0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
while remaining>0 and attempts<max_attempts:
|
||||||
|
n_try = min(batch_size,remaining)
|
||||||
|
try:
|
||||||
|
positions = jps.distribute_by_number(
|
||||||
|
polygon=platoon_data["Entry Door"],
|
||||||
|
number_of_agents=n_try,
|
||||||
|
distance_to_agents=0.45,
|
||||||
|
distance_to_polygon=0.3,
|
||||||
|
max_iterations=1500)
|
||||||
|
placed_count = len(positions)
|
||||||
|
if placed_count ==0:
|
||||||
|
attempts +=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
continue
|
||||||
|
offset=0.1
|
||||||
|
for k, pos in enumerate(positions):
|
||||||
|
speed=float(rng.normal(
|
||||||
|
loc=df["Speed Mean"][i],
|
||||||
|
scale=df["Speed Std Dev"][i],
|
||||||
|
size=1)[0])
|
||||||
|
agent = {
|
||||||
|
"Grade Level":
|
||||||
|
platoon_data["Grade Level"],
|
||||||
|
"Entry Point":
|
||||||
|
(door[platoon_data["Grade Level"]]),
|
||||||
|
"Platoon":platoon_id,
|
||||||
|
"Position":(
|
||||||
|
float(pos[0]),float(pos[1])),
|
||||||
|
"Speed":speed,
|
||||||
|
"Spawn Time":float(
|
||||||
|
spawn_time+(k*offset))}
|
||||||
|
agent_set.append(agent)
|
||||||
|
|
||||||
|
remaining-=placed_count
|
||||||
|
if remaining>0:
|
||||||
|
attempts+=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
attempts=0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"Error placing platoon {platoon_id}: {e}")
|
||||||
|
print("Reducing batch and retrying")
|
||||||
|
attempts+=1
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
spawn_time+=time_delay
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
# Group platoons by entry door first
|
||||||
|
door_platoons = {}
|
||||||
|
for platoon_id, platoon_data in platoon_agents.items():
|
||||||
|
door = platoon_data["Entry Door"]
|
||||||
|
if door not in door_platoons:
|
||||||
|
door_platoons[door] = []
|
||||||
|
door_platoons[door].append((platoon_id, platoon_data))
|
||||||
|
|
||||||
|
# Process each door sequentially
|
||||||
|
agent_set = []
|
||||||
|
for door_poly, platoon_list in door_platoons.items():
|
||||||
|
# Calculate total agents for this door
|
||||||
|
total_agents = sum(data["Platoon Size"] for _, data in platoon_list)
|
||||||
|
|
||||||
|
# Generate all positions for this door at once
|
||||||
|
try:
|
||||||
|
all_positions = jps.distribute_by_number(
|
||||||
|
polygon=door_poly,
|
||||||
|
number_of_agents=total_agents,
|
||||||
|
distance_to_agents=0.6, # Increased for safety
|
||||||
|
distance_to_polygon=0.3,
|
||||||
|
max_iterations=1500
|
||||||
|
)
|
||||||
|
|
||||||
|
# Distribute positions to platoons
|
||||||
|
position_index = 0
|
||||||
|
for platoon_id, platoon_data in platoon_list:
|
||||||
|
platoon_size = platoon_data["Platoon Size"]
|
||||||
|
platoon_positions = all_positions[position_index:position_index + platoon_size]
|
||||||
|
position_index += platoon_size
|
||||||
|
|
||||||
|
# Create agents for this platoon
|
||||||
|
spawn_time = float(rng.uniform(5,15) + rng.uniform(0,120))
|
||||||
|
spawn_time = min(spawn_time, 120)
|
||||||
|
offset = 0.1
|
||||||
|
|
||||||
|
for k, pos in enumerate(platoon_positions):
|
||||||
|
speed = float(rng.normal(
|
||||||
|
loc=df["Speed Mean"][platoon_data["Grade Index"]], # You'll need to store grade index
|
||||||
|
scale=df["Speed Std Dev"][platoon_data["Grade Index"]],
|
||||||
|
size=1
|
||||||
|
)[0])
|
||||||
|
|
||||||
|
agent = {
|
||||||
|
"Grade Level": platoon_data["Grade Level"],
|
||||||
|
"Entry Point": door_poly, # Or door name if you prefer
|
||||||
|
"Platoon": platoon_id,
|
||||||
|
"Position": (float(pos[0]), float(pos[1])),
|
||||||
|
"Speed": speed,
|
||||||
|
"Spawn Time": float(spawn_time + (k * offset))
|
||||||
|
}
|
||||||
|
agent_set.append(agent)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error generating positions for door: {e}")
|
||||||
|
# Fallback: use your original per-platoon approach for this door
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
|
||||||
|
pending=deque(pending)
|
||||||
|
max_iterations=1500
|
||||||
|
spawned_total=0
|
||||||
|
max_agents_per_step=25
|
||||||
|
spawn_tolerance=1e-8
|
||||||
|
while ((simulation.agent_count()>0 or len(pending)>0)
|
||||||
|
and simulation.iteration_count()<max_iterations):
|
||||||
|
current_time=simulation.elapsed_time()
|
||||||
|
agents_this_step=0
|
||||||
|
while (pending and (pending[0]["Spawn Time"]<=(
|
||||||
|
current_time+spawn_tolerance))
|
||||||
|
and agents_this_step<max_agents_per_step):
|
||||||
|
a = pending.popleft()
|
||||||
|
pos=tuple(a["Position"])
|
||||||
|
v0 = float(a["Speed"])
|
||||||
|
v0 = float(np.clip(v0,0.2,2.5))
|
||||||
|
agent_params = (
|
||||||
|
jps.AnticipationVelocityModelAgentParameters(
|
||||||
|
journey_id=journey_id,
|
||||||
|
stage_id=exit_id,
|
||||||
|
position=pos,
|
||||||
|
radius=0.25,
|
||||||
|
desired_speed=v0,
|
||||||
|
anticipation_time=0.5,
|
||||||
|
reaction_time=0.3,
|
||||||
|
wall_buffer_distance=0.08))
|
||||||
|
retry=0
|
||||||
|
max_retry=10
|
||||||
|
while retry < max_retry:
|
||||||
|
try:
|
||||||
|
simulation.add_agent(agent_params)
|
||||||
|
spawned_total +=1
|
||||||
|
agents_this_step+=1
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed: add_agent")
|
||||||
|
print(f"For: pos={pos}, speed={v0}")
|
||||||
|
print(f"{e}: Rescheduling...")
|
||||||
|
retry +=1
|
||||||
|
if retry >= max_retry:
|
||||||
|
print(f"\n\nMax Retries:{max_retry}")
|
||||||
|
break
|
||||||
|
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
|
||||||
|
pos[1]+rng.uniform(-0.1,0.1))
|
||||||
|
agent_params.position =adj_pos
|
||||||
|
simulation.iterate()
|
||||||
|
iter_count = simulation.iteration_count()
|
||||||
|
print(f"Iteration Complete: {iter_count}")
|
||||||
|
print("Simulation Completed!")
|
||||||
|
print(f"Iterations: {simulation.iteration_count()}")
|
||||||
|
print(f"Elapsed Time: {simulation.elapsed_time()}")
|
||||||
|
print(f"{trajectory_file = }")
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
314
archive/SRS_modeling_2025-11-22.py
Normal file
314
archive/SRS_modeling_2025-11-22.py
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
"""
|
||||||
|
Created on Sat Nov 22 14:42:45 2025
|
||||||
|
|
||||||
|
@author: ethan
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import pathlib
|
||||||
|
import jupedsim as jps
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def main():
|
||||||
|
grades = {
|
||||||
|
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
|
||||||
|
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
|
||||||
|
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
|
||||||
|
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
|
||||||
|
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
|
||||||
|
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
|
||||||
|
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
|
||||||
|
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
|
||||||
|
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
|
||||||
|
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
|
||||||
|
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
|
||||||
|
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
|
||||||
|
"Grade 12":np.array([43.71,6.02,1.54,0.23])
|
||||||
|
}
|
||||||
|
df=pd.DataFrame({
|
||||||
|
"Grade Level":(
|
||||||
|
list(grades.keys())),
|
||||||
|
"Pop Mean":[
|
||||||
|
grades[j][0] for j in grades],
|
||||||
|
"Pop Std Dev":[
|
||||||
|
grades[j][1] for j in grades],
|
||||||
|
"Speed Mean":[
|
||||||
|
grades[j][2] for j in grades],
|
||||||
|
"Speed Std Dev":[
|
||||||
|
grades[j][3] for j in grades]})
|
||||||
|
rng = np.random.default_rng(seed=42)
|
||||||
|
A_crosswalk=Polygon([
|
||||||
|
(-1,1.499),(-1,3.327),
|
||||||
|
(0,3.327),(11.214, 3.327),
|
||||||
|
(11.214,1.499),(0,1.499)])
|
||||||
|
B_queue=Polygon([
|
||||||
|
(11.214, 0),(22.163, 0),
|
||||||
|
(22.163, 4.826),(11.214, 4.826)])
|
||||||
|
C_road_adj_path=Polygon([
|
||||||
|
(21.787,4.826),(24.214,4.826),
|
||||||
|
(24.214,40.431),(29.179,40.431),
|
||||||
|
(29.179,42.511),(24.214,42.511),
|
||||||
|
(21.787,42.511)])
|
||||||
|
D_path_k_3=Polygon([
|
||||||
|
(26.45,42.511),(26.45,52.84),
|
||||||
|
(26.45,53.84),(29.179,53.84),
|
||||||
|
(29.179,52.84),(29.179,42.511)])
|
||||||
|
E_path_4_6=Polygon([
|
||||||
|
(29.179,42.511),(54.351,42.511),
|
||||||
|
(60.406,48.842),(60.406,51.22),
|
||||||
|
(60.406,52.22),(63.49,52.22),
|
||||||
|
(63.49,51.22),(63.49,47.866),
|
||||||
|
(56.381,40.431),(29.179,40.431)])
|
||||||
|
F_path_7_12=Polygon([
|
||||||
|
(22.163, 0),(39.227, 5.516),
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935),
|
||||||
|
(43.169,10.954),(24.214,4.826),
|
||||||
|
(22.163,4.826)])
|
||||||
|
G_extended_queue=Polygon([
|
||||||
|
(11.214,0),(12.924,0),
|
||||||
|
(12.924,-4.569),(11.214,-4.569)])
|
||||||
|
H_angled_path=Polygon([
|
||||||
|
(21.787,13.192),(21.787,10.527),
|
||||||
|
(17,4.826),(14.767,4.826)])
|
||||||
|
enter_k_3=Polygon([
|
||||||
|
(26.45,52.84),(29.179,52.84),
|
||||||
|
(29.179,53.84),(26.45,53.84)])
|
||||||
|
enter_4_6=Polygon([
|
||||||
|
(60.406,51.22),(60.406,52.22),
|
||||||
|
(63.49,52.22),(63.49,51.22)])
|
||||||
|
enter_7_12=Polygon([
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935)])
|
||||||
|
exit_polygon=Polygon([
|
||||||
|
(0,1.499),(0,3.327),
|
||||||
|
(-1,3.327),(-1,1.499)])
|
||||||
|
plot_polygon(
|
||||||
|
A_crosswalk,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
B_queue,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
C_road_adj_path, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
D_path_k_3, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
E_path_4_6, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
F_path_7_12, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
G_extended_queue, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
H_angled_path, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_k_3, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_4_6, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_7_12, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
exit_polygon, color="orangered",add_points=False)
|
||||||
|
geometry = (A_crosswalk.union(
|
||||||
|
B_queue).union(
|
||||||
|
C_road_adj_path).union(
|
||||||
|
D_path_k_3).union(
|
||||||
|
E_path_4_6).union(
|
||||||
|
F_path_7_12).union(
|
||||||
|
G_extended_queue).union(
|
||||||
|
H_angled_path))
|
||||||
|
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||||
|
trajectory_file = f"SRS_evac_{current_time}.sqlite"
|
||||||
|
simulation = jps.Simulation(
|
||||||
|
model=jps.AnticipationVelocityModel(),
|
||||||
|
geometry=geometry,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(trajectory_file)))
|
||||||
|
exit_id = simulation.add_exit_stage(exit_polygon)
|
||||||
|
journey = jps.JourneyDescription([exit_id])
|
||||||
|
journey_id = simulation.add_journey(journey)
|
||||||
|
grade_pop = {}
|
||||||
|
door = {}
|
||||||
|
door_polygon = {
|
||||||
|
"K-3":enter_k_3,
|
||||||
|
"4-6":enter_4_6,
|
||||||
|
"7-12":enter_7_12,
|
||||||
|
}
|
||||||
|
platoon_agents = {}
|
||||||
|
for i, grade in enumerate(df["Grade Level"]):
|
||||||
|
grade_sample=rng.normal(
|
||||||
|
loc=df["Pop Mean"][i],
|
||||||
|
scale=df["Pop Std Dev"][i],size=1)
|
||||||
|
grade_pop[grade] = int(np.ceil(grade_sample[0]))
|
||||||
|
x = grade_pop[grade]
|
||||||
|
if i < 4:
|
||||||
|
door[grade] = "K-3"
|
||||||
|
elif i <7:
|
||||||
|
door[grade] = "4-6"
|
||||||
|
else:
|
||||||
|
door[grade] = "7-12"
|
||||||
|
platoon_a_size = int(x/2)
|
||||||
|
platoon_b_size = x - platoon_a_size
|
||||||
|
platoon_a_id = (2*(i+1))-1
|
||||||
|
platoon_b_id = (2*(i+1))
|
||||||
|
platoon_agents[platoon_a_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_a_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
platoon_agents[platoon_b_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_b_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
interval = 50
|
||||||
|
try:
|
||||||
|
max_iterations = int(input("Number of steps?"))
|
||||||
|
if max_iterations<=0:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
print("Please enter a positive integer")
|
||||||
|
return
|
||||||
|
agent_set = []
|
||||||
|
next_planned_id = 0
|
||||||
|
for platoon_id, platoon_data in platoon_agents.items():
|
||||||
|
spawn_time=float(
|
||||||
|
rng.uniform(5,15)+rng.uniform(0,120))
|
||||||
|
spawn_time=min(spawn_time,120)
|
||||||
|
remaining=int(platoon_data["Platoon Size"])
|
||||||
|
attempts = 0
|
||||||
|
max_attempts = 10
|
||||||
|
time_delay = 1.0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
while remaining>0 and attempts<max_attempts:
|
||||||
|
n_try = min(batch_size,remaining)
|
||||||
|
try:
|
||||||
|
positions = jps.distribute_by_number(
|
||||||
|
polygon=platoon_data["Entry Door"],
|
||||||
|
number_of_agents=n_try,
|
||||||
|
distance_to_agents=0.45,
|
||||||
|
distance_to_polygon=0.3,
|
||||||
|
max_iterations=1500)
|
||||||
|
placed_count = len(positions)
|
||||||
|
if placed_count ==0:
|
||||||
|
attempts +=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
continue
|
||||||
|
offset=0.1
|
||||||
|
for k, pos in enumerate(positions):
|
||||||
|
speed=float(rng.normal(
|
||||||
|
loc=df["Speed Mean"][i],
|
||||||
|
scale=df["Speed Std Dev"][i],
|
||||||
|
size=1)[0])
|
||||||
|
agent = {
|
||||||
|
"Grade Level":
|
||||||
|
platoon_data["Grade Level"],
|
||||||
|
"Entry Point":
|
||||||
|
(door[platoon_data["Grade Level"]]),
|
||||||
|
"Platoon":platoon_id,
|
||||||
|
"Position":(
|
||||||
|
float(pos[0]),float(pos[1])),
|
||||||
|
"Speed":speed,
|
||||||
|
"Spawn Time":float(
|
||||||
|
spawn_time+(k*offset))}
|
||||||
|
next_planned_id +=1
|
||||||
|
agent['PlannedID'] = next_planned_id
|
||||||
|
agent_set.append(agent)
|
||||||
|
|
||||||
|
remaining-=placed_count
|
||||||
|
if remaining>0:
|
||||||
|
attempts+=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
attempts=0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"Error placing platoon {platoon_id}: {e}")
|
||||||
|
print("Reducing batch and retrying")
|
||||||
|
attempts+=1
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
spawn_time+=time_delay
|
||||||
|
total_agents = next_planned_id
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
|
||||||
|
pending=deque(pending)
|
||||||
|
sim_to_planned = {}
|
||||||
|
max_agents_per_step=25
|
||||||
|
spawned_total = 0
|
||||||
|
spawn_tolerance=1e-8
|
||||||
|
while ((simulation.agent_count()>0 or len(pending)>0)
|
||||||
|
and simulation.iteration_count()<max_iterations):
|
||||||
|
current_time=simulation.elapsed_time()
|
||||||
|
agents_this_step=0
|
||||||
|
while (pending and (pending[0]["Spawn Time"]<=(
|
||||||
|
current_time+spawn_tolerance))
|
||||||
|
and agents_this_step<max_agents_per_step):
|
||||||
|
a = pending.popleft()
|
||||||
|
pos=tuple(a["Position"])
|
||||||
|
v0 = float(a["Speed"])
|
||||||
|
v0 = float(np.clip(v0,0.2,2.5))
|
||||||
|
agent_params = (
|
||||||
|
jps.AnticipationVelocityModelAgentParameters(
|
||||||
|
journey_id=journey_id,
|
||||||
|
stage_id=exit_id,
|
||||||
|
position=pos,
|
||||||
|
radius=0.25,
|
||||||
|
desired_speed=v0,
|
||||||
|
anticipation_time=0.5,
|
||||||
|
reaction_time=0.3,
|
||||||
|
wall_buffer_distance=0.08))
|
||||||
|
try:
|
||||||
|
add_result = simulation.add_agent(agent_params)
|
||||||
|
internal_id = None
|
||||||
|
if add_result is not None:
|
||||||
|
try:
|
||||||
|
internal_id = int(add_result)
|
||||||
|
except Exception:
|
||||||
|
internal_id = None
|
||||||
|
if internal_id is not None:
|
||||||
|
sim_to_planned[internal_id] = a
|
||||||
|
print(f"Added planned={a['PlannedID']}")
|
||||||
|
print(f"internal={internal_id}")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
spawned_total +=1
|
||||||
|
else:
|
||||||
|
print(f"Added planned={a['PlannedID']}")
|
||||||
|
print("(no internal_id returned)")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
agents_this_step+=1
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
pending_for_door = sum(
|
||||||
|
1 for x in pending if (
|
||||||
|
x['Entry Point']==a["Entry Point"]))
|
||||||
|
print("\nadd_agent FAILED")
|
||||||
|
print(f"planned={a['PlannedID']}")
|
||||||
|
print(f"pos={pos}")
|
||||||
|
print(f"speed={v0:.2f}")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
print(f"pending_for_door={pending_for_door}")
|
||||||
|
print(f"\n\nerror={e}")
|
||||||
|
retry_delay = 0.1
|
||||||
|
a["Spawn Time"] = current_time+retry_delay
|
||||||
|
pending.appendleft(a)
|
||||||
|
break
|
||||||
|
simulation.iterate()
|
||||||
|
iter_count = simulation.iteration_count()
|
||||||
|
if iter_count % interval ==0:
|
||||||
|
print(f"\nIteration: {iter_count}")
|
||||||
|
print(f"time={simulation.elapsed_time():.2f}")
|
||||||
|
print(f"active={simulation.agent_count()}")
|
||||||
|
print(f"pending={len(pending)}")
|
||||||
|
print(f"spawned={spawned_total}/{total_agents}")
|
||||||
|
print("Simulation Completed!")
|
||||||
|
print(f"Iterations: {simulation.iteration_count()}")
|
||||||
|
print(f"Elapsed Time: {simulation.elapsed_time()}")
|
||||||
|
print(f"Total Agents: {total_agents}")
|
||||||
|
print(f"{trajectory_file = }")
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
323
archive/SRS_modeling_2025-11-30.py
Normal file
323
archive/SRS_modeling_2025-11-30.py
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sun Nov 30 18:48:13 2025
|
||||||
|
|
||||||
|
@author: ethan
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import pathlib
|
||||||
|
import jupedsim as jps
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
|
from jupedsim.internal.notebook_utils import animate, read_sqlite_file
|
||||||
|
import pedpy
|
||||||
|
|
||||||
|
def main():
|
||||||
|
grades = {
|
||||||
|
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
|
||||||
|
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
|
||||||
|
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
|
||||||
|
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
|
||||||
|
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
|
||||||
|
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
|
||||||
|
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
|
||||||
|
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
|
||||||
|
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
|
||||||
|
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
|
||||||
|
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
|
||||||
|
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
|
||||||
|
"Grade 12":np.array([43.71,6.02,1.54,0.23])
|
||||||
|
}
|
||||||
|
df=pd.DataFrame({
|
||||||
|
"Grade Level":(
|
||||||
|
list(grades.keys())),
|
||||||
|
"Pop Mean":[
|
||||||
|
grades[j][0] for j in grades],
|
||||||
|
"Pop Std Dev":[
|
||||||
|
grades[j][1] for j in grades],
|
||||||
|
"Speed Mean":[
|
||||||
|
grades[j][2] for j in grades],
|
||||||
|
"Speed Std Dev":[
|
||||||
|
grades[j][3] for j in grades]})
|
||||||
|
rng = np.random.default_rng(seed=42)
|
||||||
|
A_crosswalk=Polygon([
|
||||||
|
(-1,1.499),(-1,3.327),
|
||||||
|
(0,3.327),(11.214, 3.327),
|
||||||
|
(11.214,1.499),(0,1.499)])
|
||||||
|
B_queue=Polygon([
|
||||||
|
(11.214, 0),(22.163, 0),
|
||||||
|
(22.163, 4.826),(11.214, 4.826)])
|
||||||
|
C_road_adj_path=Polygon([
|
||||||
|
(21.787,4.826),(24.214,4.826),
|
||||||
|
(24.214,40.431),(29.179,40.431),
|
||||||
|
(29.179,42.511),(24.214,42.511),
|
||||||
|
(21.787,42.511)])
|
||||||
|
D_path_k_3=Polygon([
|
||||||
|
(26.45,42.511),(26.45,52.84),
|
||||||
|
(26.45,53.84),(29.179,53.84),
|
||||||
|
(29.179,52.84),(29.179,42.511)])
|
||||||
|
E_path_4_6=Polygon([
|
||||||
|
(29.179,42.511),(54.351,42.511),
|
||||||
|
(60.406,48.842),(60.406,51.22),
|
||||||
|
(60.406,52.22),(63.49,52.22),
|
||||||
|
(63.49,51.22),(63.49,47.866),
|
||||||
|
(56.381,40.431),(29.179,40.431)])
|
||||||
|
F_path_7_12=Polygon([
|
||||||
|
(22.163, 0),(39.227, 5.516),
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935),
|
||||||
|
(43.169,10.954),(24.214,4.826),
|
||||||
|
(22.163,4.826)])
|
||||||
|
G_extended_queue=Polygon([
|
||||||
|
(11.214,0),(12.924,0),
|
||||||
|
(12.924,-4.569),(11.214,-4.569)])
|
||||||
|
H_angled_path=Polygon([
|
||||||
|
(21.787,13.192),(21.787,10.527),
|
||||||
|
(17,4.826),(14.767,4.826)])
|
||||||
|
enter_k_3=Polygon([
|
||||||
|
(26.45,52.84),(29.179,52.84),
|
||||||
|
(29.179,53.84),(26.45,53.84)])
|
||||||
|
enter_4_6=Polygon([
|
||||||
|
(60.406,51.22),(60.406,52.22),
|
||||||
|
(63.49,52.22),(63.49,51.22)])
|
||||||
|
enter_7_12=Polygon([
|
||||||
|
(39.631, 4.267),(39.939,3.315),
|
||||||
|
(45.099,4.983),(44.792,5.935)])
|
||||||
|
exit_polygon=Polygon([
|
||||||
|
(0,1.499),(0,3.327),
|
||||||
|
(-1,3.327),(-1,1.499)])
|
||||||
|
plot_polygon(
|
||||||
|
A_crosswalk,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
B_queue,color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
C_road_adj_path, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
D_path_k_3, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
E_path_4_6, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
F_path_7_12, color="blue",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
G_extended_queue, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
H_angled_path, color="black",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_k_3, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_4_6, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
enter_7_12, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(
|
||||||
|
exit_polygon, color="orangered",add_points=False)
|
||||||
|
geometry = (A_crosswalk.union(
|
||||||
|
B_queue).union(
|
||||||
|
C_road_adj_path).union(
|
||||||
|
D_path_k_3).union(
|
||||||
|
E_path_4_6).union(
|
||||||
|
F_path_7_12).union(
|
||||||
|
G_extended_queue).union(
|
||||||
|
H_angled_path))
|
||||||
|
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
||||||
|
trajectory_file = f"SRS_evac_{current_time}.sqlite"
|
||||||
|
simulation = jps.Simulation(
|
||||||
|
model=jps.AnticipationVelocityModel(),
|
||||||
|
geometry=geometry,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(trajectory_file)))
|
||||||
|
exit_id = simulation.add_exit_stage(exit_polygon)
|
||||||
|
journey = jps.JourneyDescription([exit_id])
|
||||||
|
journey_id = simulation.add_journey(journey)
|
||||||
|
grade_pop = {}
|
||||||
|
door = {}
|
||||||
|
door_polygon = {
|
||||||
|
"K-3":enter_k_3,
|
||||||
|
"4-6":enter_4_6,
|
||||||
|
"7-12":enter_7_12,
|
||||||
|
}
|
||||||
|
platoon_agents = {}
|
||||||
|
for i, grade in enumerate(df["Grade Level"]):
|
||||||
|
grade_sample=rng.normal(
|
||||||
|
loc=df["Pop Mean"][i],
|
||||||
|
scale=df["Pop Std Dev"][i],size=1)
|
||||||
|
grade_pop[grade] = int(np.ceil(grade_sample[0]))
|
||||||
|
x = grade_pop[grade]
|
||||||
|
if i < 4:
|
||||||
|
door[grade] = "K-3"
|
||||||
|
elif i <7:
|
||||||
|
door[grade] = "4-6"
|
||||||
|
else:
|
||||||
|
door[grade] = "7-12"
|
||||||
|
platoon_a_size = int(x/2)
|
||||||
|
platoon_b_size = x - platoon_a_size
|
||||||
|
platoon_a_id = (2*(i+1))-1
|
||||||
|
platoon_b_id = (2*(i+1))
|
||||||
|
platoon_agents[platoon_a_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_a_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
platoon_agents[platoon_b_id] ={
|
||||||
|
"Grade Level": grade,
|
||||||
|
"Platoon Size": platoon_b_size,
|
||||||
|
"Entry Door":door_polygon[door[grade]]
|
||||||
|
}
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
interval = 50
|
||||||
|
try:
|
||||||
|
max_iterations = int(input("Number of steps?"))
|
||||||
|
if max_iterations<=0:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
print("Please enter a positive integer")
|
||||||
|
return
|
||||||
|
agent_set = []
|
||||||
|
next_planned_id = 0
|
||||||
|
for platoon_id, platoon_data in platoon_agents.items():
|
||||||
|
spawn_time=float(
|
||||||
|
rng.uniform(5,15)+rng.uniform(0,120))
|
||||||
|
spawn_time=min(spawn_time,120)
|
||||||
|
remaining=int(platoon_data["Platoon Size"])
|
||||||
|
attempts = 0
|
||||||
|
max_attempts = 10
|
||||||
|
time_delay = 1.0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
while remaining>0 and attempts<max_attempts:
|
||||||
|
n_try = min(batch_size,remaining)
|
||||||
|
try:
|
||||||
|
positions = jps.distribute_by_number(
|
||||||
|
polygon=platoon_data["Entry Door"],
|
||||||
|
number_of_agents=n_try,
|
||||||
|
distance_to_agents=0.45,
|
||||||
|
distance_to_polygon=0.3,
|
||||||
|
max_iterations=1500)
|
||||||
|
placed_count = len(positions)
|
||||||
|
if placed_count ==0:
|
||||||
|
attempts +=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
continue
|
||||||
|
offset=0.1
|
||||||
|
for k, pos in enumerate(positions):
|
||||||
|
speed=float(rng.normal(
|
||||||
|
loc=df["Speed Mean"][i],
|
||||||
|
scale=df["Speed Std Dev"][i],
|
||||||
|
size=1)[0])
|
||||||
|
agent = {
|
||||||
|
"Grade Level":
|
||||||
|
platoon_data["Grade Level"],
|
||||||
|
"Entry Point":
|
||||||
|
(door[platoon_data["Grade Level"]]),
|
||||||
|
"Platoon":platoon_id,
|
||||||
|
"Position":(
|
||||||
|
float(pos[0]),float(pos[1])),
|
||||||
|
"Speed":speed,
|
||||||
|
"Spawn Time":float(
|
||||||
|
spawn_time+(k*offset))}
|
||||||
|
next_planned_id +=1
|
||||||
|
agent['PlannedID'] = next_planned_id
|
||||||
|
agent_set.append(agent)
|
||||||
|
|
||||||
|
remaining-=placed_count
|
||||||
|
if remaining>0:
|
||||||
|
attempts+=1
|
||||||
|
spawn_time+=time_delay
|
||||||
|
attempts=0
|
||||||
|
batch_size=max(1,min(10,remaining))
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"Error placing platoon {platoon_id}: {e}")
|
||||||
|
print("Reducing batch and retrying")
|
||||||
|
attempts+=1
|
||||||
|
batch_size=max(1,batch_size//2)
|
||||||
|
spawn_time+=time_delay
|
||||||
|
total_agents = next_planned_id
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
|
||||||
|
pending=deque(pending)
|
||||||
|
sim_to_planned = {}
|
||||||
|
max_agents_per_step=25
|
||||||
|
spawned_total = 0
|
||||||
|
spawn_tolerance=1e-8
|
||||||
|
while ((simulation.agent_count()>0 or len(pending)>0)
|
||||||
|
and simulation.iteration_count()<max_iterations):
|
||||||
|
current_time=simulation.elapsed_time()
|
||||||
|
agents_this_step=0
|
||||||
|
while (pending and (pending[0]["Spawn Time"]<=(
|
||||||
|
current_time+spawn_tolerance))
|
||||||
|
and agents_this_step<max_agents_per_step):
|
||||||
|
a = pending.popleft()
|
||||||
|
pos=tuple(a["Position"])
|
||||||
|
v0 = float(a["Speed"])
|
||||||
|
v0 = float(np.clip(v0,0.2,2.5))
|
||||||
|
agent_params = (
|
||||||
|
jps.AnticipationVelocityModelAgentParameters(
|
||||||
|
journey_id=journey_id,
|
||||||
|
stage_id=exit_id,
|
||||||
|
position=pos,
|
||||||
|
radius=0.25,
|
||||||
|
desired_speed=v0,
|
||||||
|
anticipation_time=0.5,
|
||||||
|
reaction_time=0.3,
|
||||||
|
wall_buffer_distance=0.08))
|
||||||
|
try:
|
||||||
|
add_result = simulation.add_agent(agent_params)
|
||||||
|
internal_id = None
|
||||||
|
if add_result is not None:
|
||||||
|
try:
|
||||||
|
internal_id = int(add_result)
|
||||||
|
except Exception:
|
||||||
|
internal_id = None
|
||||||
|
if internal_id is not None:
|
||||||
|
sim_to_planned[internal_id] = a
|
||||||
|
print(f"Added planned={a['PlannedID']}")
|
||||||
|
print(f"internal={internal_id}")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
spawned_total +=1
|
||||||
|
else:
|
||||||
|
print(f"Added planned={a['PlannedID']}")
|
||||||
|
print("(no internal_id returned)")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
agents_this_step+=1
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
pending_for_door = sum(
|
||||||
|
1 for x in pending if (
|
||||||
|
x['Entry Point']==a["Entry Point"]))
|
||||||
|
print("\nadd_agent FAILED")
|
||||||
|
print(f"planned={a['PlannedID']}")
|
||||||
|
print(f"pos={pos}")
|
||||||
|
print(f"speed={v0:.2f}")
|
||||||
|
print(f"time={current_time:.2f}")
|
||||||
|
print(f"pending_for_door={pending_for_door}")
|
||||||
|
print(f"\n\nerror={e}")
|
||||||
|
retry_delay = 0.1
|
||||||
|
a["Spawn Time"] = current_time+retry_delay
|
||||||
|
pending.appendleft(a)
|
||||||
|
break
|
||||||
|
simulation.iterate()
|
||||||
|
iter_count = simulation.iteration_count()
|
||||||
|
if iter_count % interval ==0:
|
||||||
|
print(f"\nIteration: {iter_count}")
|
||||||
|
print(f"time={simulation.elapsed_time():.2f}")
|
||||||
|
print(f"active={simulation.agent_count()}")
|
||||||
|
print(f"pending={len(pending)}")
|
||||||
|
print(f"spawned={spawned_total}/{total_agents}")
|
||||||
|
print("Simulation Completed!")
|
||||||
|
print(f"Iterations: {simulation.iteration_count()}")
|
||||||
|
print(f"Elapsed Time: {simulation.elapsed_time()}")
|
||||||
|
print(f"Total Agents: {total_agents}")
|
||||||
|
print(f"{trajectory_file = }")
|
||||||
|
trajectory_data,walkable_area = read_sqlite_file(trajectory_file)
|
||||||
|
speed=pedpy.compute_individual_speed(traj_data=trajectory_data,frame_step=10)
|
||||||
|
speed=speed.merge(trajectory_data.data,on=["id","frame"],how="left")
|
||||||
|
animate(trajectory_data,walkable_area,title_note="Evac",every_nth_frame=5)
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
BIN
archive/full-geo-current.png
Normal file
BIN
archive/full-geo-current.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
archive/geo-setup.png
Normal file
BIN
archive/geo-setup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
96
archive/geometry.py
Normal file
96
archive/geometry.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
|
||||||
|
def current():
|
||||||
|
queue = Polygon([
|
||||||
|
(18.767,9.395),
|
||||||
|
(15.214,9.395),
|
||||||
|
(15.214,4.569),
|
||||||
|
(15.214,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823),
|
||||||
|
#(26.163,4.569),
|
||||||
|
#(28.214,5.07),
|
||||||
|
#(28.214,17.761),
|
||||||
|
#(25.787,17.761),
|
||||||
|
])
|
||||||
|
crosswalk = Polygon([
|
||||||
|
(4,6.068),
|
||||||
|
(4,7.896),
|
||||||
|
(4,7.896),
|
||||||
|
(15.214,7.896),
|
||||||
|
(15.214,6.068),
|
||||||
|
(4,6.068)
|
||||||
|
])
|
||||||
|
grass = Polygon([
|
||||||
|
(4,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761)
|
||||||
|
])
|
||||||
|
plot_polygon(queue,color="blue",add_points=False)
|
||||||
|
plot_polygon(crosswalk,color="red",add_points=False)
|
||||||
|
plot_polygon(grass,color="blue",add_points=False)
|
||||||
|
|
||||||
|
def new():
|
||||||
|
new_queue = Polygon([
|
||||||
|
(19.531,10.306),
|
||||||
|
(13.88,10.306),
|
||||||
|
(12.98,9.896),
|
||||||
|
(12.98,4.569),
|
||||||
|
(12.98,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823),
|
||||||
|
#(26.163,4.569),
|
||||||
|
#(28.214,5.07),
|
||||||
|
#(28.214,17.761),
|
||||||
|
#(25.787,17.761),
|
||||||
|
])
|
||||||
|
new_crosswalk = Polygon([
|
||||||
|
(6.23,4.982),
|
||||||
|
(6.23,8.982),
|
||||||
|
(12.98,8.982),
|
||||||
|
(12.98,4.982),
|
||||||
|
])
|
||||||
|
new_grass = Polygon([
|
||||||
|
(6.23,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761),
|
||||||
|
(4,10.306),
|
||||||
|
(5.33,10.306),
|
||||||
|
(6.23,9.896)
|
||||||
|
])
|
||||||
|
plot_polygon(new_queue,color="blue",add_points=False)
|
||||||
|
plot_polygon(new_crosswalk,color="red",add_points=False)
|
||||||
|
plot_polygon(new_grass,color="blue",add_points=False)
|
||||||
|
return
|
||||||
|
|
||||||
|
def spawn():
|
||||||
|
spawn_area = Polygon([
|
||||||
|
(25.0,16.823),
|
||||||
|
(25.0,4.569),
|
||||||
|
(26.163,4.569),
|
||||||
|
(28.214,5.07),
|
||||||
|
(28.214,17.761),
|
||||||
|
(25.787,17.761),
|
||||||
|
])
|
||||||
|
plot_polygon(spawn_area,color="green",add_points=False)
|
||||||
|
return spawn_area
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from PyQt6 import QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
new()
|
||||||
|
current()
|
||||||
|
spawn()
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
1728
archive/prompt_full-run-sim.txt
Normal file
1728
archive/prompt_full-run-sim.txt
Normal file
File diff suppressed because it is too large
Load Diff
557
archive/prompt_full-run-sim1.txt
Normal file
557
archive/prompt_full-run-sim1.txt
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
I'm creating a simulation using JuPedSim to see how pedestrians interact with a crosswalk system. The crosswalk system will have a user-defined "active" interval, and will be activated by agents being within a predefined distance. All agents will spawn in a specified spawn area, and are attempting to get from one side of the road to the other. They may only use the crosswalk to cross, and can only cross when the crosswalk is "active". Below are provided 4 separate files to create the JuPedSim simulation model:
|
||||||
|
|
||||||
|
============================
|
||||||
|
(1.1)
|
||||||
|
============================
|
||||||
|
=>geometry_setup.py(file):
|
||||||
|
class GeometrySetup(current_setup:bool)
|
||||||
|
spawn:defines the Polygon for the spawn_area
|
||||||
|
queue:defines the Polygon for the queue_area
|
||||||
|
trigger:defines the Polygon for the trigger_area
|
||||||
|
crosswalk:defines the Polygon for the crosswalk_area
|
||||||
|
grass:defines the Polygon for the grass_area
|
||||||
|
|
||||||
|
============================
|
||||||
|
(1.2)
|
||||||
|
============================
|
||||||
|
=>spawning.py(file):
|
||||||
|
class SpawnManager(spawn_area:Polygon,min_spacing:float):
|
||||||
|
__init__:
|
||||||
|
spawn_area: area for valid spawns
|
||||||
|
min_spacing: minimum spacing between spawn points
|
||||||
|
spawn_coords: all coordinates for spawning agents
|
||||||
|
filled_coords: spawn points currently occupied by agents
|
||||||
|
spawned_agents: set of agent ids already spawned
|
||||||
|
agent_pos: dictionary which connects agent ids to a spawn point
|
||||||
|
generate_coords:
|
||||||
|
uses Poisson disk method with CDTree from scipy.spatial for all spawn points
|
||||||
|
get_coords:
|
||||||
|
provides a tuple[float,float] or None based on if there is room to spawn agents
|
||||||
|
spawn_agent:
|
||||||
|
provides AgentSetup object or None depending on if there is room to spawn and there are agents left to spawn
|
||||||
|
unfill_coords:
|
||||||
|
returns None, used to indicate a spawn point has been made available by an agent moving
|
||||||
|
get_agent_pos;
|
||||||
|
returns (0,0) or the spawn coordinates of agents if they are in agent_pos
|
||||||
|
check_spawn_complete:
|
||||||
|
checks whether the number of agents spawned is the same as all_agents list, and if all spawn points are empty.
|
||||||
|
|
||||||
|
============================
|
||||||
|
(1.3)
|
||||||
|
============================
|
||||||
|
=>agents(folder):
|
||||||
|
|>agent_data.py(file):
|
||||||
|
| dictionary with group keys and values
|
||||||
|
|>agent_setup.py(file):
|
||||||
|
defines the AgentSetup class and computes list of all_agents:
|
||||||
|
"""
|
||||||
|
@dataclass
|
||||||
|
class AgentSetup:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
"""
|
||||||
|
|
||||||
|
============================
|
||||||
|
(1.4)
|
||||||
|
============================
|
||||||
|
=>crosswalk_setup.py(file):
|
||||||
|
class CrosswalkStatus(Enum):
|
||||||
|
INACTIVE
|
||||||
|
ACTIVE
|
||||||
|
COOLDOWN
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CrosswalkConfig:
|
||||||
|
trigger_dist:float
|
||||||
|
activation_time:float
|
||||||
|
cooldown_time:float
|
||||||
|
min_agents_activation:int
|
||||||
|
activation_delay:float
|
||||||
|
crosswalk_area:Polygon
|
||||||
|
trigger_area:Polygon
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CrosswalkState:
|
||||||
|
status:CrosswalkStatus
|
||||||
|
is_active:bool
|
||||||
|
time_active:float
|
||||||
|
time_remaining:float
|
||||||
|
agents_in_trigger:int
|
||||||
|
agents_waiting:int
|
||||||
|
activation_count:int
|
||||||
|
|
||||||
|
class CrosswalkController:
|
||||||
|
__init__(self,config:CrosswalkConfig,current_setup:bool):
|
||||||
|
update: calls the following methods, then returns get_state()
|
||||||
|
update_agents_in_trigger
|
||||||
|
update_state
|
||||||
|
update_statistics
|
||||||
|
update_agents_in_trigger: updates what agents are in the trigger_area for the crosswalk
|
||||||
|
@staticmethod
|
||||||
|
points_in_area:finding what points lie in a given area
|
||||||
|
update_state:updates crosswalk state to be COOLDOWN,ACTIVE or INACTIVE based on specified parameters
|
||||||
|
activate:changes the system to the ACTIVE state
|
||||||
|
deactivate:changes the system to the COOLDOWN state
|
||||||
|
update_statistics:updates info on crosswalks if status is ACTIVE
|
||||||
|
get_state: provides the state based on the current time in relation to the activation time, then updates the system
|
||||||
|
can_agent_cross: checks whether ACTIVE or INACTIVE to either let agent pass or add agent to waiting group
|
||||||
|
@property
|
||||||
|
is_active: provides a boolean response whether the system is ACTIVE or not
|
||||||
|
get_statistics:provides stats from the system at the current time
|
||||||
|
|
||||||
|
|
||||||
|
============
|
||||||
|
here is the code:
|
||||||
|
|
||||||
|
|
||||||
|
============================
|
||||||
|
(2.1) (geometry_setup.py)
|
||||||
|
============================
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
|
||||||
|
class GeometrySetup:
|
||||||
|
def __init__(self,current_setup:bool):
|
||||||
|
self.current_setup = current_setup
|
||||||
|
self.spawn()
|
||||||
|
self.queue()
|
||||||
|
self.trigger()
|
||||||
|
self.crosswalk()
|
||||||
|
self.grass()
|
||||||
|
|
||||||
|
def spawn(self)->Polygon:
|
||||||
|
spawn_area = Polygon([
|
||||||
|
(25.0,16.823),
|
||||||
|
(25.0,4.569),
|
||||||
|
(26.163,4.569),
|
||||||
|
(28.214,5.07),
|
||||||
|
(28.214,17.761),
|
||||||
|
(25.787,17.761),
|
||||||
|
])
|
||||||
|
plot_polygon(spawn_area,color="purple",add_points=False)
|
||||||
|
return spawn_area
|
||||||
|
|
||||||
|
def queue(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(18.767,9.395),
|
||||||
|
(16.924,9.395),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(19.531,10.306),
|
||||||
|
(15.214,10.306),
|
||||||
|
(15.214,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
plot_polygon(queue_area,color="blue",add_points=False)
|
||||||
|
return queue_area
|
||||||
|
|
||||||
|
def trigger(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
trigger_area = Polygon([
|
||||||
|
(15.214,0),
|
||||||
|
(15.214,9.395),
|
||||||
|
(16.924,9.395),
|
||||||
|
(16.924,0)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
trigger_area = Polygon([
|
||||||
|
(15.214,10.306),
|
||||||
|
(15.214,0),
|
||||||
|
(12.98,0),
|
||||||
|
(12.98,9.896),
|
||||||
|
(13.88,10.306)
|
||||||
|
])
|
||||||
|
plot_polygon(trigger_area,color="green",add_points=False)
|
||||||
|
return trigger_area
|
||||||
|
|
||||||
|
def crosswalk(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(4,6.068),
|
||||||
|
(4,7.896),
|
||||||
|
(4,7.896),
|
||||||
|
(15.214,7.896),
|
||||||
|
(15.214,6.068),
|
||||||
|
(4,6.068)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(6.23,4.982),
|
||||||
|
(6.23,8.982),
|
||||||
|
(12.98,8.982),
|
||||||
|
(12.98,4.982)
|
||||||
|
])
|
||||||
|
plot_polygon(crosswalk_area,color="red",add_points=False)
|
||||||
|
return crosswalk_area
|
||||||
|
|
||||||
|
def grass(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(4,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(6.23,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761),
|
||||||
|
(4,10.306),
|
||||||
|
(5.33,10.306),
|
||||||
|
(6.23,9.896)
|
||||||
|
])
|
||||||
|
plot_polygon(grass_area,color="blue",add_points=False)
|
||||||
|
return grass_area
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from PyQt6 import QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
geo_map = GeometrySetup(False)
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
============================
|
||||||
|
(2.2) (spawning.py)
|
||||||
|
============================
|
||||||
|
|
||||||
|
from shapely.geometry import Polygon,Point
|
||||||
|
from typing import Tuple, List, Dict, Set
|
||||||
|
from scipy.spatial import cKDTree
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
import config
|
||||||
|
sys.path.insert(0,str(config.AGENTS_DIR))
|
||||||
|
from agents.agent_setup import AgentSetup
|
||||||
|
|
||||||
|
|
||||||
|
class SpawnManager:
|
||||||
|
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
|
||||||
|
"""
|
||||||
|
self.spawn_area: geometry where agents may spawn
|
||||||
|
self.min_spacing: minimum spacing for spawn_points
|
||||||
|
self.spawn_coords: all spawn points available
|
||||||
|
self.filled_coords: spawn points currently filled by agents
|
||||||
|
self.spawned_agents: all agents already spawned in sim
|
||||||
|
self.agent_pos: connects agent_id to spawn_index
|
||||||
|
self.rng: random number generator object
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.spawn_area = spawn_area
|
||||||
|
self.min_spacing = min_spacing
|
||||||
|
self.spawn_coords: np.ndarray = np.array([])
|
||||||
|
self.filled_coords: np.ndarray = np.array([])
|
||||||
|
self.spawned_agents: set = set()
|
||||||
|
self.agent_pos:dict={}
|
||||||
|
self.rng = np.random.default_rng()
|
||||||
|
|
||||||
|
def generate_coords(self,max_samples:int=1000)->None:
|
||||||
|
min_x,min_y,max_x,max_y = self.spawn_area.bounds
|
||||||
|
points = []
|
||||||
|
while len(points) ==0:
|
||||||
|
x = self.rng.uniform(min_x,max_x)
|
||||||
|
y = self.rng.uniform(min_y,max_y)
|
||||||
|
if self.spawn_area.contains(Point(x,y)):
|
||||||
|
points.append([x,y])
|
||||||
|
for _ in range(max_samples):
|
||||||
|
idx = self.rng.integers(0,len(points),endpoint=True)
|
||||||
|
base = points[idx]
|
||||||
|
for _ in range(25):
|
||||||
|
angle = self.rng.uniform(0,2*np.pi)
|
||||||
|
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
|
||||||
|
x = base[0]+radius*np.cos(angle)
|
||||||
|
y = base[1]+radius*np.sin(angle)
|
||||||
|
if not self.spawn_area.contains(Point(x,y)):
|
||||||
|
continue
|
||||||
|
if len(points)>0:
|
||||||
|
tree = cKDTree(points)
|
||||||
|
distance,_ = tree.query([[x,y]],k=1)
|
||||||
|
if distance[0]<self.min_spacing:
|
||||||
|
continue
|
||||||
|
points.append([x,y])
|
||||||
|
break
|
||||||
|
self.spawn_coords = np.array(points)
|
||||||
|
self.filled_coords = np.zeros(len(points),dtype=bool)
|
||||||
|
|
||||||
|
|
||||||
|
def get_coords(self)->Tuple[float,float]|None:
|
||||||
|
'''
|
||||||
|
points = [
|
||||||
|
pt for pt in self.generate_coords if pt \
|
||||||
|
not in self.filled_coords.values()
|
||||||
|
]
|
||||||
|
return self.rng.choice(points) if points else None
|
||||||
|
'''
|
||||||
|
free_idx = np.where(~self.filled_coords)[0]
|
||||||
|
if len(free_idx) == 0:
|
||||||
|
return None
|
||||||
|
idx = self.rng.choice(free_idx)
|
||||||
|
return tuple(self.spawn_coords[idx])
|
||||||
|
|
||||||
|
|
||||||
|
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
|
||||||
|
if len(self.spawned_agents) >= len(all_agents):
|
||||||
|
return None
|
||||||
|
spawn_point = self.get_coords()
|
||||||
|
if not spawn_point:
|
||||||
|
return None
|
||||||
|
free_agents = [
|
||||||
|
agent for agent in all_agents \
|
||||||
|
if agent.id not in self.spawned_agents
|
||||||
|
]
|
||||||
|
if not free_agents:
|
||||||
|
return None
|
||||||
|
agent = self.rng.choice(free_agents)
|
||||||
|
self.spawned_agents.add(agent.id)
|
||||||
|
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
|
||||||
|
spawn_idx = np.argmin(distances)
|
||||||
|
self.filled_coords[spawn_idx] = True
|
||||||
|
self.agent_pos[agent.id] = spawn_idx
|
||||||
|
return agent
|
||||||
|
|
||||||
|
def unfill_coords(self,agent_id:int)->None:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
self.filled_coords[spawn_idx] = False
|
||||||
|
del self.agent_pos[agent_id]
|
||||||
|
self.spawned_agents.discard(agent_id)
|
||||||
|
|
||||||
|
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
return tuple(self.spawn_coords[spawn_idx])
|
||||||
|
return (0,0)
|
||||||
|
|
||||||
|
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
|
||||||
|
return (len(self.spawned_agents)<len(all_agents) and\
|
||||||
|
np.sum(~self.filled_coords) > 0)
|
||||||
|
|
||||||
|
|
||||||
|
============================
|
||||||
|
(2.3) (agent_setup.py)
|
||||||
|
============================
|
||||||
|
|
||||||
|
from agent_data import grade_data
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import numpy as np
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AgentSetup:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
|
||||||
|
def AgentConfig()->List[List[AgentSetup],List[int]]:
|
||||||
|
agent_id = 1
|
||||||
|
pop_list = []
|
||||||
|
all_agents = []
|
||||||
|
for idx,key in enumerate(grade_data.keys()):
|
||||||
|
grade = grade_data[key]
|
||||||
|
pop_list.append(grade["Pop Current"])
|
||||||
|
for i in range(pop_list[idx]):
|
||||||
|
rng = np.random.default_rng()
|
||||||
|
agent = AgentSetup(
|
||||||
|
id=agent_id,
|
||||||
|
grade=key,
|
||||||
|
speed=rng.normal(
|
||||||
|
grade["Speed Mean"],
|
||||||
|
grade["Speed Std Dev"]
|
||||||
|
),
|
||||||
|
radius=grade["Radius"]
|
||||||
|
)
|
||||||
|
all_agents.append(agent)
|
||||||
|
agent_id +=1
|
||||||
|
return all_agents, pop_list
|
||||||
|
|
||||||
|
|
||||||
|
============================
|
||||||
|
(2.4) (crosswalk_setup.py)
|
||||||
|
============================
|
||||||
|
|
||||||
|
from dataclasses import dataclass,field
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List,Dict,Tuple,Set
|
||||||
|
from shapely import Polygon,Point
|
||||||
|
from geometry_setup import GeometrySetup
|
||||||
|
import shapely
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class CrosswalkStatus(Enum):
|
||||||
|
INACTIVE = "inactive"
|
||||||
|
ACTIVE = "active"
|
||||||
|
COOLDOWN = "cooldown"
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CrosswalkConfig:
|
||||||
|
trigger_dist:float
|
||||||
|
activation_time:float
|
||||||
|
cooldown_time:float
|
||||||
|
min_agents_activation:int
|
||||||
|
activation_delay:float
|
||||||
|
crosswalk_area:Polygon
|
||||||
|
trigger_area:Polygon
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CrosswalkState:
|
||||||
|
status:CrosswalkStatus
|
||||||
|
is_active:bool
|
||||||
|
time_active:float
|
||||||
|
time_remaining:float
|
||||||
|
agents_in_trigger:int
|
||||||
|
agents_waiting:int
|
||||||
|
activation_count:int
|
||||||
|
|
||||||
|
class CrosswalkController:
|
||||||
|
def __init__(self,config:CrosswalkConfig,current_setup:bool):
|
||||||
|
self.config = config
|
||||||
|
self.crosswalk_area = config.crosswalk_area
|
||||||
|
self.trigger_area = config.trigger_area
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = 0.0
|
||||||
|
self.current_time = 0.0
|
||||||
|
self.activation_count = 0
|
||||||
|
self.agents_in_trigger: Set[int] = set()
|
||||||
|
self.agents_waiting: Set[int] = set()
|
||||||
|
self.total_active_time = 0.0
|
||||||
|
self.agents_served = 0
|
||||||
|
|
||||||
|
def update(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]],
|
||||||
|
time_step:float,
|
||||||
|
current_time:float=None
|
||||||
|
)->CrosswalkState:
|
||||||
|
if current_time is not None:
|
||||||
|
self.current_time = current_time
|
||||||
|
else:
|
||||||
|
self.current_time +=time_step
|
||||||
|
self.update_agents_in_trigger(agent_pos)
|
||||||
|
self.update_state(time_step)
|
||||||
|
self.update_statistics(time_step)
|
||||||
|
return self.get_state()
|
||||||
|
|
||||||
|
def update_agents_in_trigger(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]]
|
||||||
|
)->None:
|
||||||
|
self.agents_in_trigger.clear()
|
||||||
|
if not agent_pos:
|
||||||
|
return
|
||||||
|
agent_ids = list(agent_pos.keys())
|
||||||
|
positions = np.array(list(agent_pos.values()),dtype=np.float32)
|
||||||
|
in_trigger = self.points_in_area(positions,self.trigger_area)
|
||||||
|
for i, agent_id in enumerate(agent_ids):
|
||||||
|
if in_trigger[i]:
|
||||||
|
self.agents_in_trigger.add(agent_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
|
||||||
|
if len(points) ==0:
|
||||||
|
return np.array([],dtype=bool)
|
||||||
|
x,y = points[:,0],points[:,1]
|
||||||
|
vertices = shapely.get_coordinates(polygon)
|
||||||
|
n = len(vertices)
|
||||||
|
inside = np.zeros(len(points),dtype=bool)
|
||||||
|
j = n-1
|
||||||
|
for i in range(n):
|
||||||
|
xi, yi = vertices[i]
|
||||||
|
xj, yj = vertices[j]
|
||||||
|
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
|
||||||
|
inside ^= mask
|
||||||
|
j = i
|
||||||
|
return inside
|
||||||
|
|
||||||
|
def update_state(self,time_step:float)->None:
|
||||||
|
elapsed = self.current_time - self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
if elapsed >= self.config.activation_time:
|
||||||
|
self.deactivate()
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
if elapsed >= self.config.cooldown_time:
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
elif self.status ==CrosswalkStatus.INACTIVE:
|
||||||
|
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
|
||||||
|
elapsed >= self.config.activation_delay):
|
||||||
|
self.activate()
|
||||||
|
|
||||||
|
def activate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.ACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
self.activation_count +=1
|
||||||
|
self.agents_served += len(self.agents_waiting)
|
||||||
|
self.agents_waiting.clear()
|
||||||
|
|
||||||
|
def deactivate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.COOLDOWN
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
|
||||||
|
def update_statistics(self,time_step:float)->None:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
self.total_active_time += time_step
|
||||||
|
|
||||||
|
def get_state(self)->CrosswalkState:
|
||||||
|
elapsed = self.current_time-self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
time_remaining = max(0.0,self.config.activation_time-elapsed)
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
|
||||||
|
else:
|
||||||
|
time_remaining = 0.0
|
||||||
|
return CrosswalkState(
|
||||||
|
status=self.status,
|
||||||
|
is_active=(self.status == CrosswalkStatus.ACTIVE),
|
||||||
|
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
|
||||||
|
time_remaining=time_remaining,
|
||||||
|
agents_in_trigger=len(self.agents_in_trigger),
|
||||||
|
agents_waiting=len(self.agents_waiting),
|
||||||
|
activation_count = self.activation_count
|
||||||
|
)
|
||||||
|
def can_agent_cross(self,agent_id:int)->bool:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if agent_id in self.agents_in_trigger:
|
||||||
|
self.agents_waiting.add(agent_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self)->bool:
|
||||||
|
return self.status == CrosswalkStatus.ACTIVE
|
||||||
|
|
||||||
|
def get_statistics(self)->Dict[str,float]:
|
||||||
|
return {
|
||||||
|
"total_activations":self.activation_count,
|
||||||
|
"total_active_time":self.total_active_time,
|
||||||
|
"agents_served":self.agents_served,
|
||||||
|
"current_agents_in_trigger":len(self.agents_in_trigger),
|
||||||
|
"current_agents_waiting":len(self.agents_waiting)
|
||||||
|
}
|
||||||
|
|
||||||
|
=========================
|
||||||
|
The last part which needs to be done is using the JuPedSim module to create a full simulation using all these pieces;
|
||||||
|
|
||||||
690
archive/prompt_full-run-sim2.txt
Normal file
690
archive/prompt_full-run-sim2.txt
Normal file
@@ -0,0 +1,690 @@
|
|||||||
|
The text file includes all the files which makeup my simulation configuration. could you please run through them and fix any potential errors, delete any unused or unnecessary variables?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
===================
|
||||||
|
(geometry_setup.py)
|
||||||
|
===================
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
from matplotlib.axes import Axes
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
class GeometrySetup:
|
||||||
|
def __init__(self,current_setup:bool):
|
||||||
|
self.current_setup = current_setup
|
||||||
|
self.spawn_area = self.spawn()
|
||||||
|
self.queue_area = self.queue()
|
||||||
|
self.grass_area = self.grass()
|
||||||
|
self.crosswalk_area = self.crosswalk()
|
||||||
|
self.entry_area = self.entry_polygon()
|
||||||
|
self.exit_area = self.exit_polygon()
|
||||||
|
|
||||||
|
def spawn(self)->Polygon:
|
||||||
|
spawn_area = Polygon([
|
||||||
|
(25.0,16.823),
|
||||||
|
(25.0,4.569),
|
||||||
|
(26.163,4.569),
|
||||||
|
(28.214,5.07),
|
||||||
|
(28.214,17.761),
|
||||||
|
(25.787,17.761),
|
||||||
|
])
|
||||||
|
return spawn_area
|
||||||
|
|
||||||
|
def queue(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(18.767,9.395),
|
||||||
|
(16.924,9.395),
|
||||||
|
(15.214,9.395), #prev trigger area
|
||||||
|
(15.214,0), # ||
|
||||||
|
(16.924,0), # ||
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
queue_area = Polygon([
|
||||||
|
(19.531,10.306),
|
||||||
|
(15.214,10.306),
|
||||||
|
(13.88,10.306), #prev trigger area
|
||||||
|
(12.98,9.896), # ||
|
||||||
|
(12.98,0), # ||
|
||||||
|
(15.214,0), #
|
||||||
|
(15.214,0),
|
||||||
|
(16.924,0),
|
||||||
|
(16.924,4.569),
|
||||||
|
(25.0,4.569),
|
||||||
|
(25.0,16.823)
|
||||||
|
])
|
||||||
|
return queue_area
|
||||||
|
|
||||||
|
def grass(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(4,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
grass_area = Polygon([
|
||||||
|
(6.23,0),
|
||||||
|
(0,0),
|
||||||
|
(0,17.761),
|
||||||
|
(4,17.761),
|
||||||
|
(4,10.306),
|
||||||
|
(5.33,10.306),
|
||||||
|
(6.23,9.896)
|
||||||
|
])
|
||||||
|
return grass_area
|
||||||
|
|
||||||
|
def crosswalk(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(4,6.068),
|
||||||
|
(4,7.896),
|
||||||
|
(4,7.896),
|
||||||
|
(15.214,7.896),
|
||||||
|
(15.214,6.068),
|
||||||
|
(4,6.068)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
crosswalk_area = Polygon([
|
||||||
|
(6.23,4.982),
|
||||||
|
(6.23,8.982),
|
||||||
|
(12.98,8.982),
|
||||||
|
(12.98,4.982)
|
||||||
|
])
|
||||||
|
return crosswalk_area
|
||||||
|
|
||||||
|
def entry_polygon(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
entry_area = Polygon([ # x: 2.9m, y: 3.428m
|
||||||
|
(15.314,5.268), # dx: 0.1m, dy: 0.8m
|
||||||
|
(15.314,8.696),
|
||||||
|
(18.214,8.696),
|
||||||
|
(18.214,5.268)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
entry_area = Polygon([ # x: 2.9m, y: 5.6m
|
||||||
|
(15.98,9.782),
|
||||||
|
(15.98,4.182),
|
||||||
|
(13.08,4.182),
|
||||||
|
(13.08,9.782)
|
||||||
|
])
|
||||||
|
return entry_area
|
||||||
|
|
||||||
|
def exit_polygon(self)->Polygon:
|
||||||
|
if self.current_setup is True:
|
||||||
|
exit_area = Polygon([ # x: 2.9m, y: 3.428m
|
||||||
|
(1,5.268),
|
||||||
|
(1,8.696),
|
||||||
|
(3.9,8.696),
|
||||||
|
(3.9,5.268)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
exit_area = Polygon([ # x: 2.9m, y: 5.6m
|
||||||
|
(3.23,4.182),
|
||||||
|
(3.23,9.782),
|
||||||
|
(6.13,9.782),
|
||||||
|
(6.13,4.182)
|
||||||
|
])
|
||||||
|
return exit_area
|
||||||
|
|
||||||
|
def plot_all(self)->Tuple[Figure,Axes]:
|
||||||
|
plot_polygon(self.spawn_area,color="green",add_points=False)
|
||||||
|
plot_polygon(self.queue_area,color="blue",add_points=False)
|
||||||
|
plot_polygon(self.grass_area,color="blue",add_points=False)
|
||||||
|
plot_polygon(self.crosswalk_area,color="red",add_points=False)
|
||||||
|
plot_polygon(self.entry_area,color="black",add_points=False)
|
||||||
|
plot_polygon(self.exit_area,color="black",add_points=False)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from PyQt6 import QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
GeometrySetup(False).plot_all()
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
===================
|
||||||
|
(agent_setup.py)
|
||||||
|
===================
|
||||||
|
from agent_data import grade_data
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import numpy as np
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AgentSetup:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
|
||||||
|
def AgentConfig()->List[List[AgentSetup],List[int]]:
|
||||||
|
agent_id = 1
|
||||||
|
pop_list = []
|
||||||
|
all_agents = []
|
||||||
|
for idx,key in enumerate(grade_data.keys()):
|
||||||
|
grade = grade_data[key]
|
||||||
|
pop_list.append(grade["Pop Current"])
|
||||||
|
for i in range(pop_list[idx]):
|
||||||
|
rng = np.random.default_rng()
|
||||||
|
agent = AgentSetup(
|
||||||
|
id=agent_id,
|
||||||
|
grade=key,
|
||||||
|
speed=rng.normal(
|
||||||
|
grade["Speed Mean"],
|
||||||
|
grade["Speed Std Dev"]
|
||||||
|
),
|
||||||
|
radius=grade["Radius"]
|
||||||
|
)
|
||||||
|
all_agents.append(agent)
|
||||||
|
agent_id +=1
|
||||||
|
return all_agents, pop_list
|
||||||
|
|
||||||
|
|
||||||
|
===================
|
||||||
|
(crosswalk_setup.py)
|
||||||
|
===================
|
||||||
|
|
||||||
|
from dataclasses import dataclass,field
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List,Dict,Tuple,Set
|
||||||
|
from shapely import Polygon,Point
|
||||||
|
from geometry_setup import GeometrySetup
|
||||||
|
import shapely
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class CrosswalkStatus(Enum):
|
||||||
|
INACTIVE = "inactive"
|
||||||
|
ACTIVE = "active"
|
||||||
|
COOLDOWN = "cooldown"
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CrosswalkConfig:
|
||||||
|
trigger_dist:float
|
||||||
|
activation_time:float
|
||||||
|
cooldown_time:float
|
||||||
|
min_agents_activation:int
|
||||||
|
activation_delay:float
|
||||||
|
crosswalk_area:Polygon
|
||||||
|
trigger_area:Polygon
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CrosswalkState:
|
||||||
|
status:CrosswalkStatus
|
||||||
|
is_active:bool
|
||||||
|
time_active:float
|
||||||
|
time_remaining:float
|
||||||
|
agents_in_trigger:int
|
||||||
|
agents_waiting:int
|
||||||
|
activation_count:int
|
||||||
|
|
||||||
|
class CrosswalkController:
|
||||||
|
def __init__(self,config:CrosswalkConfig,current_setup:bool):
|
||||||
|
self.config = config
|
||||||
|
self.crosswalk_area = config.crosswalk_area
|
||||||
|
self.trigger_area = config.trigger_area
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = 0.0
|
||||||
|
self.current_time = 0.0
|
||||||
|
self.activation_count = 0
|
||||||
|
self.agents_in_trigger: Set[int] = set()
|
||||||
|
self.agents_waiting: Set[int] = set()
|
||||||
|
self.total_active_time = 0.0
|
||||||
|
self.agents_served = 0
|
||||||
|
|
||||||
|
def update(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]],
|
||||||
|
time_step:float,
|
||||||
|
current_time:float=None
|
||||||
|
)->CrosswalkState:
|
||||||
|
if current_time is not None:
|
||||||
|
self.current_time = current_time
|
||||||
|
else:
|
||||||
|
self.current_time +=time_step
|
||||||
|
self.update_agents_in_trigger(agent_pos)
|
||||||
|
self.update_state(time_step)
|
||||||
|
self.update_statistics(time_step)
|
||||||
|
return self.get_state()
|
||||||
|
|
||||||
|
def update_agents_in_trigger(
|
||||||
|
self,
|
||||||
|
agent_pos:Dict[int,Tuple[float,float]]
|
||||||
|
)->None:
|
||||||
|
self.agents_in_trigger.clear()
|
||||||
|
if not agent_pos:
|
||||||
|
return
|
||||||
|
agent_ids = list(agent_pos.keys())
|
||||||
|
positions = np.array(list(agent_pos.values()),dtype=np.float32)
|
||||||
|
in_trigger = self.points_in_area(positions,self.trigger_area)
|
||||||
|
for i, agent_id in enumerate(agent_ids):
|
||||||
|
if in_trigger[i]:
|
||||||
|
self.agents_in_trigger.add(agent_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
|
||||||
|
if len(points) ==0:
|
||||||
|
return np.array([],dtype=bool)
|
||||||
|
x,y = points[:,0],points[:,1]
|
||||||
|
vertices = shapely.get_coordinates(polygon)
|
||||||
|
n = len(vertices)
|
||||||
|
inside = np.zeros(len(points),dtype=bool)
|
||||||
|
j = n-1
|
||||||
|
for i in range(n):
|
||||||
|
xi, yi = vertices[i]
|
||||||
|
xj, yj = vertices[j]
|
||||||
|
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
|
||||||
|
inside ^= mask
|
||||||
|
j = i
|
||||||
|
return inside
|
||||||
|
|
||||||
|
def update_state(self,time_step:float)->None:
|
||||||
|
elapsed = self.current_time - self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
if elapsed >= self.config.activation_time:
|
||||||
|
self.deactivate()
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
if elapsed >= self.config.cooldown_time:
|
||||||
|
self.status = CrosswalkStatus.INACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
elif self.status ==CrosswalkStatus.INACTIVE:
|
||||||
|
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
|
||||||
|
elapsed >= self.config.activation_delay):
|
||||||
|
self.activate()
|
||||||
|
|
||||||
|
def activate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.ACTIVE
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
self.activation_count +=1
|
||||||
|
self.agents_served += len(self.agents_waiting)
|
||||||
|
self.agents_waiting.clear()
|
||||||
|
|
||||||
|
def deactivate(self)->None:
|
||||||
|
self.status = CrosswalkStatus.COOLDOWN
|
||||||
|
self.state_start_time = self.current_time
|
||||||
|
|
||||||
|
def update_statistics(self,time_step:float)->None:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
self.total_active_time += time_step
|
||||||
|
|
||||||
|
def get_state(self)->CrosswalkState:
|
||||||
|
elapsed = self.current_time-self.state_start_time
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
time_remaining = max(0.0,self.config.activation_time-elapsed)
|
||||||
|
elif self.status == CrosswalkStatus.COOLDOWN:
|
||||||
|
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
|
||||||
|
else:
|
||||||
|
time_remaining = 0.0
|
||||||
|
return CrosswalkState(
|
||||||
|
status=self.status,
|
||||||
|
is_active=(self.status == CrosswalkStatus.ACTIVE),
|
||||||
|
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
|
||||||
|
time_remaining=time_remaining,
|
||||||
|
agents_in_trigger=len(self.agents_in_trigger),
|
||||||
|
agents_waiting=len(self.agents_waiting),
|
||||||
|
activation_count = self.activation_count
|
||||||
|
)
|
||||||
|
def can_agent_cross(self,agent_id:int)->bool:
|
||||||
|
if self.status == CrosswalkStatus.ACTIVE:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if agent_id in self.agents_in_trigger:
|
||||||
|
self.agents_waiting.add(agent_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_active(self)->bool:
|
||||||
|
return self.status == CrosswalkStatus.ACTIVE
|
||||||
|
|
||||||
|
def get_statistics(self)->Dict[str,float]:
|
||||||
|
return {
|
||||||
|
"total_activations":self.activation_count,
|
||||||
|
"total_active_time":self.total_active_time,
|
||||||
|
"agents_served":self.agents_served,
|
||||||
|
"current_agents_in_trigger":len(self.agents_in_trigger),
|
||||||
|
"current_agents_waiting":len(self.agents_waiting)
|
||||||
|
}
|
||||||
|
|
||||||
|
===================
|
||||||
|
(spawning.py)
|
||||||
|
===================
|
||||||
|
from shapely.geometry import Polygon,Point
|
||||||
|
from typing import Tuple, List, Dict, Set
|
||||||
|
from scipy.spatial import cKDTree
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
import config
|
||||||
|
sys.path.insert(0,str(config.AGENTS_DIR))
|
||||||
|
from agents.agent_setup import AgentSetup
|
||||||
|
|
||||||
|
|
||||||
|
class SpawnManager:
|
||||||
|
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
|
||||||
|
"""
|
||||||
|
self.spawn_area: geometry where agents may spawn
|
||||||
|
self.min_spacing: minimum spacing for spawn_points
|
||||||
|
self.spawn_coords: all spawn points available
|
||||||
|
self.filled_coords: spawn points currently filled by agents
|
||||||
|
self.spawned_agents: all agents already spawned in sim
|
||||||
|
self.agent_pos: connects agent_id to spawn_index
|
||||||
|
self.rng: random number generator object
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.spawn_area = spawn_area
|
||||||
|
self.min_spacing = min_spacing
|
||||||
|
self.spawn_coords: np.ndarray = np.array([])
|
||||||
|
self.filled_coords: np.ndarray = np.array([])
|
||||||
|
self.spawned_agents: set = set()
|
||||||
|
self.agent_pos:dict={}
|
||||||
|
self.rng = np.random.default_rng()
|
||||||
|
|
||||||
|
def generate_coords(self,max_samples:int=1000)->None:
|
||||||
|
min_x,min_y,max_x,max_y = self.spawn_area.bounds
|
||||||
|
points = []
|
||||||
|
while len(points) ==0:
|
||||||
|
x = self.rng.uniform(min_x,max_x)
|
||||||
|
y = self.rng.uniform(min_y,max_y)
|
||||||
|
if self.spawn_area.contains(Point(x,y)):
|
||||||
|
points.append([x,y])
|
||||||
|
for _ in range(max_samples):
|
||||||
|
idx = self.rng.integers(0,len(points),endpoint=True)
|
||||||
|
base = points[idx]
|
||||||
|
for _ in range(25):
|
||||||
|
angle = self.rng.uniform(0,2*np.pi)
|
||||||
|
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
|
||||||
|
x = base[0]+radius*np.cos(angle)
|
||||||
|
y = base[1]+radius*np.sin(angle)
|
||||||
|
if not self.spawn_area.contains(Point(x,y)):
|
||||||
|
continue
|
||||||
|
if len(points)>0:
|
||||||
|
tree = cKDTree(points)
|
||||||
|
distance,_ = tree.query([[x,y]],k=1)
|
||||||
|
if distance[0]<self.min_spacing:
|
||||||
|
continue
|
||||||
|
points.append([x,y])
|
||||||
|
break
|
||||||
|
self.spawn_coords = np.array(points)
|
||||||
|
self.filled_coords = np.zeros(len(points),dtype=bool)
|
||||||
|
#return points
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_coords(self)->Tuple[float,float]|None:
|
||||||
|
'''
|
||||||
|
points = [
|
||||||
|
pt for pt in self.generate_coords if pt \
|
||||||
|
not in self.filled_coords.values()
|
||||||
|
]
|
||||||
|
return self.rng.choice(points) if points else None
|
||||||
|
'''
|
||||||
|
free_idx = np.where(~self.filled_coords)[0]
|
||||||
|
if len(free_idx) == 0:
|
||||||
|
return None
|
||||||
|
idx = self.rng.choice(free_idx)
|
||||||
|
return tuple(self.spawn_coords[idx])
|
||||||
|
|
||||||
|
|
||||||
|
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
|
||||||
|
if len(self.spawned_agents) >= len(all_agents):
|
||||||
|
return None
|
||||||
|
spawn_point = self.get_coords()
|
||||||
|
if not spawn_point:
|
||||||
|
return None
|
||||||
|
free_agents = [
|
||||||
|
agent for agent in all_agents \
|
||||||
|
if agent.id not in self.spawned_agents
|
||||||
|
]
|
||||||
|
if not free_agents:
|
||||||
|
return None
|
||||||
|
agent = self.rng.choice(free_agents)
|
||||||
|
self.spawned_agents.add(agent.id)
|
||||||
|
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
|
||||||
|
spawn_idx = np.argmin(distances)
|
||||||
|
self.filled_coords[spawn_idx] = True
|
||||||
|
self.agent_pos[agent.id] = spawn_idx
|
||||||
|
return agent
|
||||||
|
|
||||||
|
def unfill_coords(self,agent_id:int)->None:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
self.filled_coords[spawn_idx] = False
|
||||||
|
del self.agent_pos[agent_id]
|
||||||
|
self.spawned_agents.discard(agent_id)
|
||||||
|
|
||||||
|
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
|
||||||
|
if agent_id in self.agent_pos:
|
||||||
|
spawn_idx = self.agent_pos[agent_id]
|
||||||
|
return tuple(self.spawn_coords[spawn_idx])
|
||||||
|
return (0,0)
|
||||||
|
|
||||||
|
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
|
||||||
|
return (len(self.spawned_agents)<len(all_agents) and\
|
||||||
|
np.sum(~self.filled_coords) > 0)
|
||||||
|
|
||||||
|
|
||||||
|
===================
|
||||||
|
(simulation.py)
|
||||||
|
===================
|
||||||
|
|
||||||
|
from typing import Tuple,List,Dict
|
||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from shapely import Polygon,Point,GeometryCollection,get_coordinates
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
import jupedsim as jps
|
||||||
|
import numpy as np
|
||||||
|
import pathlib
|
||||||
|
from geometry_setup import GeometrySetup
|
||||||
|
from spawning import SpawnManager
|
||||||
|
from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig
|
||||||
|
from agents.agent_setup import AgentConfig
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SimulationSetup:
|
||||||
|
simulation_time:float=200.0
|
||||||
|
time_step:float=0.01
|
||||||
|
max_iterations:int=50000
|
||||||
|
trajectory_file:str="crosswalk_sim.sqlite"
|
||||||
|
current_setup:bool=True
|
||||||
|
trigger_dist:float=1.0
|
||||||
|
activation_time:float=30.0
|
||||||
|
cooldown_time:float=10.0
|
||||||
|
min_agents_activation:int=1
|
||||||
|
activation_delay:float=2.0
|
||||||
|
|
||||||
|
class CrosswalkSimulation:
|
||||||
|
def __init__(self,config:SimulationSetup):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
print("\nInitializing Geometry...")
|
||||||
|
self.geo = GeometrySetup(self.config.current_setup)
|
||||||
|
self.spawn_area = self.geo.spawn()
|
||||||
|
self.queue_area = self.geo.queue()
|
||||||
|
self.grass_area = self.geo.grass()
|
||||||
|
self.crosswalk_area = self.geo.crosswalk()
|
||||||
|
self.entry_area = self.geo.entry_polygon()
|
||||||
|
self.exit_area = self.geo.exit_polygon()
|
||||||
|
self.walkable_area = GeometryCollection([
|
||||||
|
self.spawn_area,
|
||||||
|
self.queue_area,
|
||||||
|
self.grass_area,
|
||||||
|
self.crosswalk_area,
|
||||||
|
]).unary_union
|
||||||
|
print("Geometry Configuration Complete!")
|
||||||
|
|
||||||
|
print("\nInitializing Spawn Manager...")
|
||||||
|
self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=0.55)
|
||||||
|
self.spawn_manager.generate_coords()
|
||||||
|
print("Spawn Manager Setup Complete!")
|
||||||
|
|
||||||
|
print("\nInitializing Crosswalk Controller...")
|
||||||
|
self.crosswalk_controller = CrosswalkController(
|
||||||
|
CrosswalkConfig(
|
||||||
|
trigger_dist=self.config.trigger_dist,
|
||||||
|
activation_time=self.config.activation_time,
|
||||||
|
cooldown_time=self.config.cooldown_time,
|
||||||
|
min_agents_activation=self.config.min_agents_activation,
|
||||||
|
activation_delay=self.config.activation_delay,
|
||||||
|
crosswalk_area=self.crosswalk_area,
|
||||||
|
trigger_area=self.entry_area
|
||||||
|
),
|
||||||
|
self.config.current_setup
|
||||||
|
)
|
||||||
|
print("Crosswalk Controller Setup Complete!")
|
||||||
|
|
||||||
|
print("\nInitializing Agent Setup...")
|
||||||
|
self.all_agents, pop_list = AgentConfig()
|
||||||
|
self.total_agents = len(self.all_agents)
|
||||||
|
print(f"Created {self.total_agents} Agents...")
|
||||||
|
print("Agent Setup Complete!")
|
||||||
|
|
||||||
|
print("\nInitializing JuPedSim Model...")
|
||||||
|
self.trajectory_file = self.config.trajectory_file
|
||||||
|
self.simulation = jps.Simulation(
|
||||||
|
model=jps.CollisionFreeSpeedModel(),
|
||||||
|
geometry=self.walkable_area,
|
||||||
|
trajectory_writer=jps.SqliteTrajectoryWriter(
|
||||||
|
output_file=pathlib.Path(self.trajectory_file)
|
||||||
|
),
|
||||||
|
dt=self.config.time_step
|
||||||
|
)
|
||||||
|
self.journey_setup()
|
||||||
|
self.jps_agent_ids = {}
|
||||||
|
# unsure whether these are necessary
|
||||||
|
#
|
||||||
|
#self.agent_targets = {}
|
||||||
|
#self.active_agents = {}
|
||||||
|
self.iteration_count = 0
|
||||||
|
self.current_time = 0.0
|
||||||
|
print(f"\nJuPedSim Model Initialized with:")
|
||||||
|
print(f"\tTime Step: {self.config.time_step} s")
|
||||||
|
print(f"\tMax Iterations: {self.config.max_iterations}")
|
||||||
|
print(f"\tCrosswalk Activation Length: {self.config.activation_time} s")
|
||||||
|
print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time} s")
|
||||||
|
|
||||||
|
|
||||||
|
def journey_setup(self):
|
||||||
|
self.queue_waiting_coords = self.waiting_coords(
|
||||||
|
self.queue_area,self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.queue_stage_id = self.simulation.add_waiting_set_stage(
|
||||||
|
self.queue_waiting_coords
|
||||||
|
)
|
||||||
|
self.queue_stage = self.simulation.get_stage(self.queue_stage_id)
|
||||||
|
self.queue_stage.state = jps.WaitingSetState.ACTIVE
|
||||||
|
self.crosswalk_entry_id = self.simulation.add_waypoint_stage(
|
||||||
|
self.entry_area,
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.crosswalk_exit_id = self.simulation.add_waypoint_stage(
|
||||||
|
self.exit_area,
|
||||||
|
self.spawn_manager.min_spacing
|
||||||
|
)
|
||||||
|
self.journey = jps.JourneyDescription([
|
||||||
|
self.queue_stage_id,
|
||||||
|
self.crosswalk_entry_id,
|
||||||
|
self.crosswalk_exit_id,
|
||||||
|
])
|
||||||
|
self.journey.set_transition_for_stage(
|
||||||
|
self.queue_stage_id,
|
||||||
|
jps.Transition.create_fixed_transition(
|
||||||
|
self.crosswalk_entry_id
|
||||||
|
))
|
||||||
|
self.journey.set_transition_for_stage(
|
||||||
|
self.crosswalk_entry_id,
|
||||||
|
jps.Transition.create_fixed_transition(
|
||||||
|
self.crosswalk_exit_id
|
||||||
|
))
|
||||||
|
self.journey_id = self.simulation.add_journey(
|
||||||
|
self.journey)
|
||||||
|
|
||||||
|
def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]:
|
||||||
|
min_x,min_y,max_x,max_y = area.bounds
|
||||||
|
points = []
|
||||||
|
x = (min_x+spacing)/2
|
||||||
|
while x<max_x:
|
||||||
|
y = (min_y+spacing)/2
|
||||||
|
while y < max_y:
|
||||||
|
if area.contains(Point(x, y)):
|
||||||
|
points.append((x,y))
|
||||||
|
y += spacing
|
||||||
|
x += spacing
|
||||||
|
return points if points else [(min_x+0.5, min_y+0.5)]
|
||||||
|
|
||||||
|
def run(self,max_iterations:int):
|
||||||
|
print("\n\nStarting Simulation")
|
||||||
|
print("Target Agents: {len(self.all_agents)}")
|
||||||
|
while self.iteration_count < max_iterations:
|
||||||
|
if self.spawn_manager.check_spawn_complete(self.all_agents):
|
||||||
|
new_agent = self.spawn_manager.spawn_agent(self.all_agents)
|
||||||
|
if new_agent:
|
||||||
|
jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters(
|
||||||
|
journey_id=self.journey_id,
|
||||||
|
stage_id=self.queue_stage_id,
|
||||||
|
position=self.spawn_manager.get_agent_pos(new_agent.id),
|
||||||
|
v0=new_agent.speed,
|
||||||
|
radius=new_agent.radius
|
||||||
|
)
|
||||||
|
jps_agent_id = self.simulation.add_agent(jps_agent_params)
|
||||||
|
self.jps_agent_ids[new_agent.id] = jps_agent_id
|
||||||
|
# unsure whether this is necessary
|
||||||
|
#
|
||||||
|
# self.agent_targets[new_agent.id] = None
|
||||||
|
print(f"Iter {self.iteration_count:05d}: Spawned agent {new_agent.id} (JPS-ID: {jps_agent_id})")
|
||||||
|
current_agent_pos = {}
|
||||||
|
for spawn_id,jps_id in self.jps_agent_ids.items():
|
||||||
|
agent = self.simulation.agent(jps_id)
|
||||||
|
current_agent_pos[spawn_id] = (agent.position[0],agent.position[1])
|
||||||
|
crosswalk_state = self.crosswalk_controller.update(
|
||||||
|
current_agent_pos,self.config.time_step,self.current_time
|
||||||
|
)
|
||||||
|
if crosswalk_state.is_active:
|
||||||
|
if self.queue_stage.stage != jps.WaitingSetState.INACTIVE:
|
||||||
|
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
|
||||||
|
print(f"Iter {self.iteration_count:05d}: Crosswalk ACTIVATED, releasing queue.")
|
||||||
|
else:
|
||||||
|
if self.queue_stage.stage != jps.WaitingSetState.ACTIVE:
|
||||||
|
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
|
||||||
|
print(f"Iter {self.iteration_count:05d}: Crosswalk INACTIVE, holding agents.")
|
||||||
|
self.simulation.iterate()
|
||||||
|
self.current_time += self.config.time_step
|
||||||
|
self.iteration_count +=1
|
||||||
|
agents_to_remove = []
|
||||||
|
for spawn_id, jps_id in list(self.jps_agent_ids.items()):
|
||||||
|
try:
|
||||||
|
_ = self.simulation.agent(jps_id)
|
||||||
|
except RuntimeError:
|
||||||
|
agents_to_remove.append(spawn_id)
|
||||||
|
self.spawn_manager.unfill_coords(spawn_id)
|
||||||
|
for spawn_id in agents_to_remove:
|
||||||
|
del self.jps_agent_ids[spawn_id]
|
||||||
|
print(f"Iter {self.iteration_count:05d}: Agent {spawn_id} reached exit and was removed.")
|
||||||
|
if (len(self.jps_agent_ids) ==0 and not self.spawn_manager.check_spawn_complete(self.all_agents)):
|
||||||
|
print(f"\nSimulation Completed at Iteration: {self.iteration_count}")
|
||||||
|
print(f"Total Simulation Time: {self.current_time:.2f} seconds.")
|
||||||
|
break
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("Simulation finished.")
|
||||||
|
stats = self.crosswalk_controller.get_statistics()
|
||||||
|
print(f"Crosswalk was activated {stats['total_activations']} times.")
|
||||||
|
print(f"Agents served by crosswalk: {stats['agents_served']}")
|
||||||
|
print(f"Trajectory saved to: {self.trajectory_file.absolute()}")
|
||||||
|
|
||||||
|
|
||||||
BIN
archive/source/__pycache__/config.cpython-313.pyc
Normal file
BIN
archive/source/__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
17
archive/source/sim_agents/config.py
Normal file
17
archive/source/sim_agents/config.py
Normal 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"
|
||||||
|
|
||||||
|
|
||||||
72
archive/source/sim_agents/traits.py
Normal file
72
archive/source/sim_agents/traits.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
grades = {
|
||||||
|
"Kindergarden":np.array(
|
||||||
|
[31.43,5.65,1.21,0.24]
|
||||||
|
),
|
||||||
|
"Grade 1":np.array(
|
||||||
|
[32.57,6.27,1.35,0.26]
|
||||||
|
),
|
||||||
|
"Grade 2":np.array(
|
||||||
|
[34.43,6.80,1.42,0.28]
|
||||||
|
),
|
||||||
|
"Grade 3":np.array(
|
||||||
|
[35.43,5.19,1.48,0.23]
|
||||||
|
),
|
||||||
|
"Grade 4":np.array(
|
||||||
|
[34.86,6.77,1.58,0.26]
|
||||||
|
),
|
||||||
|
"Grade 5":np.array(
|
||||||
|
[36.71,7.09,1.59,0.24]
|
||||||
|
),
|
||||||
|
"Grade 6":np.array(
|
||||||
|
[37.71,6.99,1.65,0.24]
|
||||||
|
),
|
||||||
|
"Grade 7":np.array(
|
||||||
|
[40.43,6.02,1.61,0.25]
|
||||||
|
),
|
||||||
|
"Grade 8":np.array(
|
||||||
|
[40.43,5.50,1.66,0.24]
|
||||||
|
),
|
||||||
|
"Grade 9":np.array(
|
||||||
|
[44.14,4.85,1.60,0.24]
|
||||||
|
),
|
||||||
|
"Grade 10":np.array(
|
||||||
|
[46.29,6.29,1.57,0.23]
|
||||||
|
),
|
||||||
|
"Grade 11":np.array(
|
||||||
|
[48.29,3.30,1.51,0.22]
|
||||||
|
),
|
||||||
|
"Grade 12":np.array(
|
||||||
|
[43.71,6.02,1.54,0.23]
|
||||||
|
)}
|
||||||
|
|
||||||
|
df_srs_data=pd.DataFrame({
|
||||||
|
"Grade Level":(
|
||||||
|
list(grades.keys())),
|
||||||
|
"Pop Mean":[
|
||||||
|
grades[j][0] for j in grades],
|
||||||
|
"Pop Std Dev":[
|
||||||
|
grades[j][1] for j in grades],
|
||||||
|
"Speed Mean":[
|
||||||
|
grades[j][2] for j in grades],
|
||||||
|
"Speed Std Dev":[
|
||||||
|
grades[j][3] for j in grades]})
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AgentConfig:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
door:str
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
91
archive/source/sim_agents/traits_current.py
Normal file
91
archive/source/sim_agents/traits_current.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import numpy as np
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Dict, Optional
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
##https://censusatschool.ca/data-results/2017-2018/average-height-by-age/
|
||||||
|
@dataclass
|
||||||
|
class AgentConfig:
|
||||||
|
id:int
|
||||||
|
grade:str
|
||||||
|
door:int
|
||||||
|
speed:float
|
||||||
|
radius:float
|
||||||
|
|
||||||
|
def agent_params():
|
||||||
|
gr_data = {
|
||||||
|
# [0]: number of students
|
||||||
|
# [1]: door number
|
||||||
|
# [2]: speed mean
|
||||||
|
# [3]: speed standard deviation
|
||||||
|
# [4]: radius mean
|
||||||
|
"Kindergarden":np.array(
|
||||||
|
[34,0,1.21,0.24,0.407]
|
||||||
|
),
|
||||||
|
"Grade 1":np.array(
|
||||||
|
[26,0,1.35,0.26,0.407]
|
||||||
|
),
|
||||||
|
"Grade 2":np.array(
|
||||||
|
[42,0,1.42,0.28,0.407]
|
||||||
|
),
|
||||||
|
"Grade 3":np.array(
|
||||||
|
[39,0,1.48,0.23,0.407]
|
||||||
|
),
|
||||||
|
"Grade 4":np.array(
|
||||||
|
[30,1,1.58,0.26,0.417]
|
||||||
|
),
|
||||||
|
"Grade 5":np.array(
|
||||||
|
[43,1,1.59,0.24,0.434]
|
||||||
|
),
|
||||||
|
"Grade 6":np.array(
|
||||||
|
[29,1,1.65,0.24,0.454]
|
||||||
|
),
|
||||||
|
"Grade 7":np.array(
|
||||||
|
[45,2,1.61,0.25,0.471]
|
||||||
|
),
|
||||||
|
"Grade 8":np.array(
|
||||||
|
[36,2,1.66,0.24,0.488]
|
||||||
|
),
|
||||||
|
"Grade 9":np.array(
|
||||||
|
[44,2,1.60,0.24,0.500]
|
||||||
|
),
|
||||||
|
"Grade 10":np.array(
|
||||||
|
[36,2,1.57,0.23,0.507]
|
||||||
|
),
|
||||||
|
"Grade 11":np.array(
|
||||||
|
[54,2,1.51,0.22,0.515]
|
||||||
|
),
|
||||||
|
"Grade 12":np.array(
|
||||||
|
[46,2,1.54,0.23,0.520]
|
||||||
|
)}
|
||||||
|
agent_id = 1
|
||||||
|
rng = np.random.default_rng(seed=42)
|
||||||
|
all_agents = []
|
||||||
|
gr_agents = []
|
||||||
|
for grade in gr_data:
|
||||||
|
for num in range(int(gr_data[grade][0])):
|
||||||
|
door = gr_data[grade][1]
|
||||||
|
speed = rng.normal(
|
||||||
|
loc=gr_data[grade][2],
|
||||||
|
scale=gr_data[grade][3],
|
||||||
|
size=1)
|
||||||
|
radius = gr_data[grade][4]
|
||||||
|
gr_agents.append(
|
||||||
|
AgentConfig(
|
||||||
|
id=agent_id,
|
||||||
|
grade=grade,
|
||||||
|
door=door,
|
||||||
|
speed=speed,
|
||||||
|
radius = radius
|
||||||
|
))
|
||||||
|
agent_id += 1
|
||||||
|
all_agents.append(gr_agents)
|
||||||
|
gr_agents = []
|
||||||
|
#for grade in all_agents:
|
||||||
|
# for agent in grade:
|
||||||
|
# print(agent)
|
||||||
|
return all_agents
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
archive/source/sim_geometry/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
archive/source/sim_geometry/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
17
archive/source/sim_geometry/config.py
Normal file
17
archive/source/sim_geometry/config.py
Normal 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"
|
||||||
|
|
||||||
|
|
||||||
147
archive/source/sim_geometry/geo_current.py
Normal file
147
archive/source/sim_geometry/geo_current.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import sys
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QtAgg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from shapely import Polygon
|
||||||
|
from shapely.plotting import plot_polygon
|
||||||
|
|
||||||
|
def geo_current(full_plot:bool=False):
|
||||||
|
A_crosswalk=Polygon([
|
||||||
|
(-1,1.499),
|
||||||
|
(-1,3.327),
|
||||||
|
(0,3.327),
|
||||||
|
(11.214, 3.327),
|
||||||
|
(11.214,1.499),
|
||||||
|
(0,1.499)
|
||||||
|
])
|
||||||
|
B_queue=Polygon([
|
||||||
|
(11.214, 0),
|
||||||
|
(22.163, 0),
|
||||||
|
(22.163, 4.826),
|
||||||
|
(11.214, 4.826)
|
||||||
|
])
|
||||||
|
C_road_adj_path=Polygon([
|
||||||
|
(21.787,4.826),
|
||||||
|
(24.214,4.826),
|
||||||
|
(24.214,40.431),
|
||||||
|
(29.179,40.431),
|
||||||
|
(29.179,42.511),
|
||||||
|
(24.214,42.511),
|
||||||
|
(21.787,42.511)
|
||||||
|
])
|
||||||
|
D_path_k_3=Polygon([
|
||||||
|
(26.45,42.511),
|
||||||
|
(26.45,52.84),
|
||||||
|
(26.45,53.84),
|
||||||
|
(29.179,53.84),
|
||||||
|
(29.179,52.84),
|
||||||
|
(29.179,42.511)
|
||||||
|
])
|
||||||
|
E_path_4_6=Polygon([
|
||||||
|
(29.179,42.511),
|
||||||
|
(54.351,42.511),
|
||||||
|
(60.406,48.842),
|
||||||
|
(60.406,51.22),
|
||||||
|
(60.406,52.22),
|
||||||
|
(63.49,52.22),
|
||||||
|
(63.49,51.22),
|
||||||
|
(63.49,47.866),
|
||||||
|
(56.381,40.431),
|
||||||
|
(29.179,40.431)
|
||||||
|
])
|
||||||
|
F_path_7_12=Polygon([
|
||||||
|
(22.163, 0),
|
||||||
|
(39.227, 5.516),
|
||||||
|
(39.631, 4.267),
|
||||||
|
(39.939,3.315),
|
||||||
|
(45.099,4.983),
|
||||||
|
(44.792,5.935),
|
||||||
|
(43.169,10.954),
|
||||||
|
(24.214,4.826),
|
||||||
|
(22.163,4.826)
|
||||||
|
])
|
||||||
|
G_extended_queue=Polygon([
|
||||||
|
(11.214,0),
|
||||||
|
(12.924,0),
|
||||||
|
(12.924,-4.569),
|
||||||
|
(11.214,-4.569)
|
||||||
|
])
|
||||||
|
H_angled_path=Polygon([
|
||||||
|
(21.787,13.192),
|
||||||
|
(21.787,10.527),
|
||||||
|
(17,4.826),
|
||||||
|
(14.767,4.826)
|
||||||
|
])
|
||||||
|
enter_k_3=Polygon([
|
||||||
|
(26.45,52.84),
|
||||||
|
(29.179,52.84),
|
||||||
|
(29.179,53.84),
|
||||||
|
(26.45,53.84)
|
||||||
|
])
|
||||||
|
enter_4_6=Polygon([
|
||||||
|
(60.406,51.22),
|
||||||
|
(60.406,52.22),
|
||||||
|
(63.49,52.22),
|
||||||
|
(63.49,51.22)
|
||||||
|
])
|
||||||
|
enter_7_12=Polygon([
|
||||||
|
(39.631, 4.267),
|
||||||
|
(39.939,3.315),
|
||||||
|
(45.099,4.983),
|
||||||
|
(44.792,5.935)
|
||||||
|
])
|
||||||
|
exit_polygon=Polygon([
|
||||||
|
(0,1.499),
|
||||||
|
(0,3.327),
|
||||||
|
(-1,3.327),
|
||||||
|
(-1,1.499)
|
||||||
|
])
|
||||||
|
|
||||||
|
geometry = (
|
||||||
|
A_crosswalk.union(
|
||||||
|
B_queue).union(
|
||||||
|
C_road_adj_path).union(
|
||||||
|
D_path_k_3).union(
|
||||||
|
E_path_4_6).union(
|
||||||
|
F_path_7_12).union(
|
||||||
|
G_extended_queue).union(
|
||||||
|
H_angled_path)
|
||||||
|
)
|
||||||
|
doors = [
|
||||||
|
enter_k_3,
|
||||||
|
enter_4_6,
|
||||||
|
enter_7_12,
|
||||||
|
exit_polygon
|
||||||
|
]
|
||||||
|
|
||||||
|
if full_plot is False:
|
||||||
|
plot_polygon(A_crosswalk,color="black",add_points=False)
|
||||||
|
plot_polygon(B_queue,color="black",add_points=False)
|
||||||
|
plot_polygon(C_road_adj_path, color="blue",add_points=False)
|
||||||
|
plot_polygon(D_path_k_3, color="blue",add_points=False)
|
||||||
|
plot_polygon(E_path_4_6, color="blue",add_points=False)
|
||||||
|
plot_polygon(F_path_7_12, color="blue",add_points=False)
|
||||||
|
plot_polygon(G_extended_queue, color="black",add_points=False)
|
||||||
|
plot_polygon(H_angled_path, color="black",add_points=False)
|
||||||
|
|
||||||
|
plot_polygon(enter_k_3, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(enter_4_6, color="darkgreen",add_points=False)
|
||||||
|
plot_polygon(enter_7_12, color="darkgreen",add_points=False)
|
||||||
|
|
||||||
|
plot_polygon(exit_polygon, color="orangered",add_points=False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
plot_polygon(geometry,color="blue",add_points=False)
|
||||||
|
plot_polygon(enter_k_3,color="red",add_points=False)
|
||||||
|
plot_polygon(enter_4_6,color="red",add_points=False)
|
||||||
|
plot_polygon(enter_7_12,color="red",add_points=False)
|
||||||
|
|
||||||
|
return geometry, doors
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from PyQt6 import QtWidgets
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
geometry,doors = geo_current(True)
|
||||||
|
plt.show(block=False)
|
||||||
|
sys.exit(app.exec())
|
||||||
71
archive/source/test/_1_testing.py
Normal file
71
archive/source/test/_1_testing.py
Normal 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())
|
||||||
386
archive/source/test/_2_testing.py
Normal file
386
archive/source/test/_2_testing.py
Normal 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")
|
||||||
|
|
||||||
BIN
archive/source/test/__pycache__/config.cpython-310.pyc
Normal file
BIN
archive/source/test/__pycache__/config.cpython-310.pyc
Normal file
Binary file not shown.
BIN
archive/source/test/__pycache__/config.cpython-312.pyc
Normal file
BIN
archive/source/test/__pycache__/config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
archive/source/test/__pycache__/config.cpython-313.pyc
Normal file
BIN
archive/source/test/__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
388
archive/source/test/_test_example_.py
Normal file
388
archive/source/test/_test_example_.py
Normal 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
|
||||||
17
archive/source/test/config.py
Normal file
17
archive/source/test/config.py
Normal 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"
|
||||||
|
|
||||||
|
|
||||||
6
config
Normal file
6
config
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = false
|
||||||
|
bare = true
|
||||||
|
symlinks = false
|
||||||
|
ignorecase = true
|
||||||
BIN
crosswalk_sim.sqlite
Normal file
BIN
crosswalk_sim.sqlite
Normal file
Binary file not shown.
1
description
Normal file
1
description
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
||||||
15
hooks/applypatch-msg.sample
Normal file
15
hooks/applypatch-msg.sample
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
|
||||||
|
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
|
||||||
|
:
|
||||||
24
hooks/commit-msg.sample
Normal file
24
hooks/commit-msg.sample
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message.
|
||||||
|
# Called by "git commit" with one argument, the name of the file
|
||||||
|
# that has the commit message. The hook should exit with non-zero
|
||||||
|
# status after issuing an appropriate message if it wants to stop the
|
||||||
|
# commit. The hook is allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "commit-msg".
|
||||||
|
|
||||||
|
# Uncomment the below to add a Signed-off-by line to the message.
|
||||||
|
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||||
|
# hook is more suited to it.
|
||||||
|
#
|
||||||
|
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||||
|
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||||
|
|
||||||
|
# This example catches duplicate Signed-off-by lines.
|
||||||
|
|
||||||
|
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||||
|
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||||
|
echo >&2 Duplicate Signed-off-by lines.
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
174
hooks/fsmonitor-watchman.sample
Normal file
174
hooks/fsmonitor-watchman.sample
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use IPC::Open2;
|
||||||
|
|
||||||
|
# An example hook script to integrate Watchman
|
||||||
|
# (https://facebook.github.io/watchman/) with git to speed up detecting
|
||||||
|
# new and modified files.
|
||||||
|
#
|
||||||
|
# The hook is passed a version (currently 2) and last update token
|
||||||
|
# formatted as a string and outputs to stdout a new update token and
|
||||||
|
# all files that have been modified since the update token. Paths must
|
||||||
|
# be relative to the root of the working tree and separated by a single NUL.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "query-watchman" and set
|
||||||
|
# 'git config core.fsmonitor .git/hooks/query-watchman'
|
||||||
|
#
|
||||||
|
my ($version, $last_update_token) = @ARGV;
|
||||||
|
|
||||||
|
# Uncomment for debugging
|
||||||
|
# print STDERR "$0 $version $last_update_token\n";
|
||||||
|
|
||||||
|
# Check the hook interface version
|
||||||
|
if ($version ne 2) {
|
||||||
|
die "Unsupported query-fsmonitor hook version '$version'.\n" .
|
||||||
|
"Falling back to scanning...\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $git_work_tree = get_working_dir();
|
||||||
|
|
||||||
|
my $retry = 1;
|
||||||
|
|
||||||
|
my $json_pkg;
|
||||||
|
eval {
|
||||||
|
require JSON::XS;
|
||||||
|
$json_pkg = "JSON::XS";
|
||||||
|
1;
|
||||||
|
} or do {
|
||||||
|
require JSON::PP;
|
||||||
|
$json_pkg = "JSON::PP";
|
||||||
|
};
|
||||||
|
|
||||||
|
launch_watchman();
|
||||||
|
|
||||||
|
sub launch_watchman {
|
||||||
|
my $o = watchman_query();
|
||||||
|
if (is_work_tree_watched($o)) {
|
||||||
|
output_result($o->{clock}, @{$o->{files}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub output_result {
|
||||||
|
my ($clockid, @files) = @_;
|
||||||
|
|
||||||
|
# Uncomment for debugging watchman output
|
||||||
|
# open (my $fh, ">", ".git/watchman-output.out");
|
||||||
|
# binmode $fh, ":utf8";
|
||||||
|
# print $fh "$clockid\n@files\n";
|
||||||
|
# close $fh;
|
||||||
|
|
||||||
|
binmode STDOUT, ":utf8";
|
||||||
|
print $clockid;
|
||||||
|
print "\0";
|
||||||
|
local $, = "\0";
|
||||||
|
print @files;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub watchman_clock {
|
||||||
|
my $response = qx/watchman clock "$git_work_tree"/;
|
||||||
|
die "Failed to get clock id on '$git_work_tree'.\n" .
|
||||||
|
"Falling back to scanning...\n" if $? != 0;
|
||||||
|
|
||||||
|
return $json_pkg->new->utf8->decode($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub watchman_query {
|
||||||
|
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
|
||||||
|
or die "open2() failed: $!\n" .
|
||||||
|
"Falling back to scanning...\n";
|
||||||
|
|
||||||
|
# In the query expression below we're asking for names of files that
|
||||||
|
# changed since $last_update_token but not from the .git folder.
|
||||||
|
#
|
||||||
|
# To accomplish this, we're using the "since" generator to use the
|
||||||
|
# recency index to select candidate nodes and "fields" to limit the
|
||||||
|
# output to file names only. Then we're using the "expression" term to
|
||||||
|
# further constrain the results.
|
||||||
|
my $last_update_line = "";
|
||||||
|
if (substr($last_update_token, 0, 1) eq "c") {
|
||||||
|
$last_update_token = "\"$last_update_token\"";
|
||||||
|
$last_update_line = qq[\n"since": $last_update_token,];
|
||||||
|
}
|
||||||
|
my $query = <<" END";
|
||||||
|
["query", "$git_work_tree", {$last_update_line
|
||||||
|
"fields": ["name"],
|
||||||
|
"expression": ["not", ["dirname", ".git"]]
|
||||||
|
}]
|
||||||
|
END
|
||||||
|
|
||||||
|
# Uncomment for debugging the watchman query
|
||||||
|
# open (my $fh, ">", ".git/watchman-query.json");
|
||||||
|
# print $fh $query;
|
||||||
|
# close $fh;
|
||||||
|
|
||||||
|
print CHLD_IN $query;
|
||||||
|
close CHLD_IN;
|
||||||
|
my $response = do {local $/; <CHLD_OUT>};
|
||||||
|
|
||||||
|
# Uncomment for debugging the watch response
|
||||||
|
# open ($fh, ">", ".git/watchman-response.json");
|
||||||
|
# print $fh $response;
|
||||||
|
# close $fh;
|
||||||
|
|
||||||
|
die "Watchman: command returned no output.\n" .
|
||||||
|
"Falling back to scanning...\n" if $response eq "";
|
||||||
|
die "Watchman: command returned invalid output: $response\n" .
|
||||||
|
"Falling back to scanning...\n" unless $response =~ /^\{/;
|
||||||
|
|
||||||
|
return $json_pkg->new->utf8->decode($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_work_tree_watched {
|
||||||
|
my ($output) = @_;
|
||||||
|
my $error = $output->{error};
|
||||||
|
if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
|
||||||
|
$retry--;
|
||||||
|
my $response = qx/watchman watch "$git_work_tree"/;
|
||||||
|
die "Failed to make watchman watch '$git_work_tree'.\n" .
|
||||||
|
"Falling back to scanning...\n" if $? != 0;
|
||||||
|
$output = $json_pkg->new->utf8->decode($response);
|
||||||
|
$error = $output->{error};
|
||||||
|
die "Watchman: $error.\n" .
|
||||||
|
"Falling back to scanning...\n" if $error;
|
||||||
|
|
||||||
|
# Uncomment for debugging watchman output
|
||||||
|
# open (my $fh, ">", ".git/watchman-output.out");
|
||||||
|
# close $fh;
|
||||||
|
|
||||||
|
# Watchman will always return all files on the first query so
|
||||||
|
# return the fast "everything is dirty" flag to git and do the
|
||||||
|
# Watchman query just to get it over with now so we won't pay
|
||||||
|
# the cost in git to look up each individual file.
|
||||||
|
my $o = watchman_clock();
|
||||||
|
$error = $output->{error};
|
||||||
|
|
||||||
|
die "Watchman: $error.\n" .
|
||||||
|
"Falling back to scanning...\n" if $error;
|
||||||
|
|
||||||
|
output_result($o->{clock}, ("/"));
|
||||||
|
$last_update_token = $o->{clock};
|
||||||
|
|
||||||
|
eval { launch_watchman() };
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
die "Watchman: $error.\n" .
|
||||||
|
"Falling back to scanning...\n" if $error;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_working_dir {
|
||||||
|
my $working_dir;
|
||||||
|
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
|
||||||
|
$working_dir = Win32::GetCwd();
|
||||||
|
$working_dir =~ tr/\\/\//;
|
||||||
|
} else {
|
||||||
|
require Cwd;
|
||||||
|
$working_dir = Cwd::cwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $working_dir;
|
||||||
|
}
|
||||||
8
hooks/post-update.sample
Normal file
8
hooks/post-update.sample
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to prepare a packed repository for use over
|
||||||
|
# dumb transports.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "post-update".
|
||||||
|
|
||||||
|
exec git update-server-info
|
||||||
14
hooks/pre-applypatch.sample
Normal file
14
hooks/pre-applypatch.sample
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to verify what is about to be committed
|
||||||
|
# by applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-applypatch".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
precommit="$(git rev-parse --git-path hooks/pre-commit)"
|
||||||
|
test -x "$precommit" && exec "$precommit" ${1+"$@"}
|
||||||
|
:
|
||||||
49
hooks/pre-commit.sample
Normal file
49
hooks/pre-commit.sample
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to verify what is about to be committed.
|
||||||
|
# Called by "git commit" with no arguments. The hook should
|
||||||
|
# exit with non-zero status after issuing an appropriate message if
|
||||||
|
# it wants to stop the commit.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-commit".
|
||||||
|
|
||||||
|
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
against=HEAD
|
||||||
|
else
|
||||||
|
# Initial commit: diff against an empty tree object
|
||||||
|
against=$(git hash-object -t tree /dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If you want to allow non-ASCII filenames set this variable to true.
|
||||||
|
allownonascii=$(git config --type=bool hooks.allownonascii)
|
||||||
|
|
||||||
|
# Redirect output to stderr.
|
||||||
|
exec 1>&2
|
||||||
|
|
||||||
|
# Cross platform projects tend to avoid non-ASCII filenames; prevent
|
||||||
|
# them from being added to the repository. We exploit the fact that the
|
||||||
|
# printable range starts at the space character and ends with tilde.
|
||||||
|
if [ "$allownonascii" != "true" ] &&
|
||||||
|
# Note that the use of brackets around a tr range is ok here, (it's
|
||||||
|
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||||
|
# the square bracket bytes happen to fall in the designated range.
|
||||||
|
test $(git diff --cached --name-only --diff-filter=A -z $against |
|
||||||
|
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
|
||||||
|
then
|
||||||
|
cat <<\EOF
|
||||||
|
Error: Attempt to add a non-ASCII file name.
|
||||||
|
|
||||||
|
This can cause problems if you want to work with people on other platforms.
|
||||||
|
|
||||||
|
To be portable it is advisable to rename the file.
|
||||||
|
|
||||||
|
If you know what you are doing you can disable this check using:
|
||||||
|
|
||||||
|
git config hooks.allownonascii true
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If there are whitespace errors, print the offending file names and fail.
|
||||||
|
exec git diff-index --check --cached $against --
|
||||||
13
hooks/pre-merge-commit.sample
Normal file
13
hooks/pre-merge-commit.sample
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to verify what is about to be committed.
|
||||||
|
# Called by "git merge" with no arguments. The hook should
|
||||||
|
# exit with non-zero status after issuing an appropriate message to
|
||||||
|
# stderr if it wants to stop the merge commit.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-merge-commit".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||||
|
exec "$GIT_DIR/hooks/pre-commit"
|
||||||
|
:
|
||||||
53
hooks/pre-push.sample
Normal file
53
hooks/pre-push.sample
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# An example hook script to verify what is about to be pushed. Called by "git
|
||||||
|
# push" after it has checked the remote status, but before anything has been
|
||||||
|
# pushed. If this script exits with a non-zero status nothing will be pushed.
|
||||||
|
#
|
||||||
|
# This hook is called with the following parameters:
|
||||||
|
#
|
||||||
|
# $1 -- Name of the remote to which the push is being done
|
||||||
|
# $2 -- URL to which the push is being done
|
||||||
|
#
|
||||||
|
# If pushing without using a named remote those arguments will be equal.
|
||||||
|
#
|
||||||
|
# Information about the commits which are being pushed is supplied as lines to
|
||||||
|
# the standard input in the form:
|
||||||
|
#
|
||||||
|
# <local ref> <local oid> <remote ref> <remote oid>
|
||||||
|
#
|
||||||
|
# This sample shows how to prevent push of commits where the log message starts
|
||||||
|
# with "WIP" (work in progress).
|
||||||
|
|
||||||
|
remote="$1"
|
||||||
|
url="$2"
|
||||||
|
|
||||||
|
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
|
||||||
|
|
||||||
|
while read local_ref local_oid remote_ref remote_oid
|
||||||
|
do
|
||||||
|
if test "$local_oid" = "$zero"
|
||||||
|
then
|
||||||
|
# Handle delete
|
||||||
|
:
|
||||||
|
else
|
||||||
|
if test "$remote_oid" = "$zero"
|
||||||
|
then
|
||||||
|
# New branch, examine all commits
|
||||||
|
range="$local_oid"
|
||||||
|
else
|
||||||
|
# Update to existing branch, examine new commits
|
||||||
|
range="$remote_oid..$local_oid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for WIP commit
|
||||||
|
commit=$(git rev-list -n 1 --grep '^WIP' "$range")
|
||||||
|
if test -n "$commit"
|
||||||
|
then
|
||||||
|
echo >&2 "Found WIP commit in $local_ref, not pushing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
169
hooks/pre-rebase.sample
Normal file
169
hooks/pre-rebase.sample
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||||
|
#
|
||||||
|
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||||
|
# its job, and can prevent the command from running by exiting with
|
||||||
|
# non-zero status.
|
||||||
|
#
|
||||||
|
# The hook is called with the following parameters:
|
||||||
|
#
|
||||||
|
# $1 -- the upstream the series was forked from.
|
||||||
|
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||||
|
#
|
||||||
|
# This sample shows how to prevent topic branches that are already
|
||||||
|
# merged to 'next' branch from getting rebased, because allowing it
|
||||||
|
# would result in rebasing already published history.
|
||||||
|
|
||||||
|
publish=next
|
||||||
|
basebranch="$1"
|
||||||
|
if test "$#" = 2
|
||||||
|
then
|
||||||
|
topic="refs/heads/$2"
|
||||||
|
else
|
||||||
|
topic=`git symbolic-ref HEAD` ||
|
||||||
|
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$topic" in
|
||||||
|
refs/heads/??/*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0 ;# we do not interrupt others.
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Now we are dealing with a topic branch being rebased
|
||||||
|
# on top of master. Is it OK to rebase it?
|
||||||
|
|
||||||
|
# Does the topic really exist?
|
||||||
|
git show-ref -q "$topic" || {
|
||||||
|
echo >&2 "No such branch $topic"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Is topic fully merged to master?
|
||||||
|
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||||
|
if test -z "$not_in_master"
|
||||||
|
then
|
||||||
|
echo >&2 "$topic is fully merged to master; better remove it."
|
||||||
|
exit 1 ;# we could allow it, but there is no point.
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||||
|
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||||
|
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||||
|
if test "$only_next_1" = "$only_next_2"
|
||||||
|
then
|
||||||
|
not_in_topic=`git rev-list "^$topic" master`
|
||||||
|
if test -z "$not_in_topic"
|
||||||
|
then
|
||||||
|
echo >&2 "$topic is already up to date with master"
|
||||||
|
exit 1 ;# we could allow it, but there is no point.
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||||
|
/usr/bin/perl -e '
|
||||||
|
my $topic = $ARGV[0];
|
||||||
|
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||||
|
my (%not_in_next) = map {
|
||||||
|
/^([0-9a-f]+) /;
|
||||||
|
($1 => 1);
|
||||||
|
} split(/\n/, $ARGV[1]);
|
||||||
|
for my $elem (map {
|
||||||
|
/^([0-9a-f]+) (.*)$/;
|
||||||
|
[$1 => $2];
|
||||||
|
} split(/\n/, $ARGV[2])) {
|
||||||
|
if (!exists $not_in_next{$elem->[0]}) {
|
||||||
|
if ($msg) {
|
||||||
|
print STDERR $msg;
|
||||||
|
undef $msg;
|
||||||
|
}
|
||||||
|
print STDERR " $elem->[1]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$topic" "$not_in_next" "$not_in_master"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
<<\DOC_END
|
||||||
|
|
||||||
|
This sample hook safeguards topic branches that have been
|
||||||
|
published from being rewound.
|
||||||
|
|
||||||
|
The workflow assumed here is:
|
||||||
|
|
||||||
|
* Once a topic branch forks from "master", "master" is never
|
||||||
|
merged into it again (either directly or indirectly).
|
||||||
|
|
||||||
|
* Once a topic branch is fully cooked and merged into "master",
|
||||||
|
it is deleted. If you need to build on top of it to correct
|
||||||
|
earlier mistakes, a new topic branch is created by forking at
|
||||||
|
the tip of the "master". This is not strictly necessary, but
|
||||||
|
it makes it easier to keep your history simple.
|
||||||
|
|
||||||
|
* Whenever you need to test or publish your changes to topic
|
||||||
|
branches, merge them into "next" branch.
|
||||||
|
|
||||||
|
The script, being an example, hardcodes the publish branch name
|
||||||
|
to be "next", but it is trivial to make it configurable via
|
||||||
|
$GIT_DIR/config mechanism.
|
||||||
|
|
||||||
|
With this workflow, you would want to know:
|
||||||
|
|
||||||
|
(1) ... if a topic branch has ever been merged to "next". Young
|
||||||
|
topic branches can have stupid mistakes you would rather
|
||||||
|
clean up before publishing, and things that have not been
|
||||||
|
merged into other branches can be easily rebased without
|
||||||
|
affecting other people. But once it is published, you would
|
||||||
|
not want to rewind it.
|
||||||
|
|
||||||
|
(2) ... if a topic branch has been fully merged to "master".
|
||||||
|
Then you can delete it. More importantly, you should not
|
||||||
|
build on top of it -- other people may already want to
|
||||||
|
change things related to the topic as patches against your
|
||||||
|
"master", so if you need further changes, it is better to
|
||||||
|
fork the topic (perhaps with the same name) afresh from the
|
||||||
|
tip of "master".
|
||||||
|
|
||||||
|
Let's look at this example:
|
||||||
|
|
||||||
|
o---o---o---o---o---o---o---o---o---o "next"
|
||||||
|
/ / / /
|
||||||
|
/ a---a---b A / /
|
||||||
|
/ / / /
|
||||||
|
/ / c---c---c---c B /
|
||||||
|
/ / / \ /
|
||||||
|
/ / / b---b C \ /
|
||||||
|
/ / / / \ /
|
||||||
|
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||||
|
|
||||||
|
|
||||||
|
A, B and C are topic branches.
|
||||||
|
|
||||||
|
* A has one fix since it was merged up to "next".
|
||||||
|
|
||||||
|
* B has finished. It has been fully merged up to "master" and "next",
|
||||||
|
and is ready to be deleted.
|
||||||
|
|
||||||
|
* C has not merged to "next" at all.
|
||||||
|
|
||||||
|
We would want to allow C to be rebased, refuse A, and encourage
|
||||||
|
B to be deleted.
|
||||||
|
|
||||||
|
To compute (1):
|
||||||
|
|
||||||
|
git rev-list ^master ^topic next
|
||||||
|
git rev-list ^master next
|
||||||
|
|
||||||
|
if these match, topic has not merged in next at all.
|
||||||
|
|
||||||
|
To compute (2):
|
||||||
|
|
||||||
|
git rev-list master..topic
|
||||||
|
|
||||||
|
if this is empty, it is fully merged to "master".
|
||||||
|
|
||||||
|
DOC_END
|
||||||
24
hooks/pre-receive.sample
Normal file
24
hooks/pre-receive.sample
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to make use of push options.
|
||||||
|
# The example simply echoes all push options that start with 'echoback='
|
||||||
|
# and rejects all pushes when the "reject" push option is used.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-receive".
|
||||||
|
|
||||||
|
if test -n "$GIT_PUSH_OPTION_COUNT"
|
||||||
|
then
|
||||||
|
i=0
|
||||||
|
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
|
||||||
|
do
|
||||||
|
eval "value=\$GIT_PUSH_OPTION_$i"
|
||||||
|
case "$value" in
|
||||||
|
echoback=*)
|
||||||
|
echo "echo from the pre-receive-hook: ${value#*=}" >&2
|
||||||
|
;;
|
||||||
|
reject)
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
fi
|
||||||
42
hooks/prepare-commit-msg.sample
Normal file
42
hooks/prepare-commit-msg.sample
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to prepare the commit log message.
|
||||||
|
# Called by "git commit" with the name of the file that has the
|
||||||
|
# commit message, followed by the description of the commit
|
||||||
|
# message's source. The hook's purpose is to edit the commit
|
||||||
|
# message file. If the hook fails with a non-zero status,
|
||||||
|
# the commit is aborted.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||||
|
|
||||||
|
# This hook includes three examples. The first one removes the
|
||||||
|
# "# Please enter the commit message..." help message.
|
||||||
|
#
|
||||||
|
# The second includes the output of "git diff --name-status -r"
|
||||||
|
# into the message, just before the "git status" output. It is
|
||||||
|
# commented because it doesn't cope with --amend or with squashed
|
||||||
|
# commits.
|
||||||
|
#
|
||||||
|
# The third example adds a Signed-off-by line to the message, that can
|
||||||
|
# still be edited. This is rarely a good idea.
|
||||||
|
|
||||||
|
COMMIT_MSG_FILE=$1
|
||||||
|
COMMIT_SOURCE=$2
|
||||||
|
SHA1=$3
|
||||||
|
|
||||||
|
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
|
||||||
|
|
||||||
|
# case "$COMMIT_SOURCE,$SHA1" in
|
||||||
|
# ,|template,)
|
||||||
|
# /usr/bin/perl -i.bak -pe '
|
||||||
|
# print "\n" . `git diff --cached --name-status -r`
|
||||||
|
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
|
||||||
|
# *) ;;
|
||||||
|
# esac
|
||||||
|
|
||||||
|
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||||
|
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
|
||||||
|
# if test -z "$COMMIT_SOURCE"
|
||||||
|
# then
|
||||||
|
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
|
||||||
|
# fi
|
||||||
78
hooks/push-to-checkout.sample
Normal file
78
hooks/push-to-checkout.sample
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# An example hook script to update a checked-out tree on a git push.
|
||||||
|
#
|
||||||
|
# This hook is invoked by git-receive-pack(1) when it reacts to git
|
||||||
|
# push and updates reference(s) in its repository, and when the push
|
||||||
|
# tries to update the branch that is currently checked out and the
|
||||||
|
# receive.denyCurrentBranch configuration variable is set to
|
||||||
|
# updateInstead.
|
||||||
|
#
|
||||||
|
# By default, such a push is refused if the working tree and the index
|
||||||
|
# of the remote repository has any difference from the currently
|
||||||
|
# checked out commit; when both the working tree and the index match
|
||||||
|
# the current commit, they are updated to match the newly pushed tip
|
||||||
|
# of the branch. This hook is to be used to override the default
|
||||||
|
# behaviour; however the code below reimplements the default behaviour
|
||||||
|
# as a starting point for convenient modification.
|
||||||
|
#
|
||||||
|
# The hook receives the commit with which the tip of the current
|
||||||
|
# branch is going to be updated:
|
||||||
|
commit=$1
|
||||||
|
|
||||||
|
# It can exit with a non-zero status to refuse the push (when it does
|
||||||
|
# so, it must not modify the index or the working tree).
|
||||||
|
die () {
|
||||||
|
echo >&2 "$*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Or it can make any necessary changes to the working tree and to the
|
||||||
|
# index to bring them to the desired state when the tip of the current
|
||||||
|
# branch is updated to the new commit, and exit with a zero status.
|
||||||
|
#
|
||||||
|
# For example, the hook can simply run git read-tree -u -m HEAD "$1"
|
||||||
|
# in order to emulate git fetch that is run in the reverse direction
|
||||||
|
# with git push, as the two-tree form of git read-tree -u -m is
|
||||||
|
# essentially the same as git switch or git checkout that switches
|
||||||
|
# branches while keeping the local changes in the working tree that do
|
||||||
|
# not interfere with the difference between the branches.
|
||||||
|
|
||||||
|
# The below is a more-or-less exact translation to shell of the C code
|
||||||
|
# for the default behaviour for git's push-to-checkout hook defined in
|
||||||
|
# the push_to_deploy() function in builtin/receive-pack.c.
|
||||||
|
#
|
||||||
|
# Note that the hook will be executed from the repository directory,
|
||||||
|
# not from the working tree, so if you want to perform operations on
|
||||||
|
# the working tree, you will have to adapt your code accordingly, e.g.
|
||||||
|
# by adding "cd .." or using relative paths.
|
||||||
|
|
||||||
|
if ! git update-index -q --ignore-submodules --refresh
|
||||||
|
then
|
||||||
|
die "Up-to-date check failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git diff-files --quiet --ignore-submodules --
|
||||||
|
then
|
||||||
|
die "Working directory has unstaged changes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This is a rough translation of:
|
||||||
|
#
|
||||||
|
# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
|
||||||
|
if git cat-file -e HEAD 2>/dev/null
|
||||||
|
then
|
||||||
|
head=HEAD
|
||||||
|
else
|
||||||
|
head=$(git hash-object -t tree --stdin </dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git diff-index --quiet --cached --ignore-submodules $head --
|
||||||
|
then
|
||||||
|
die "Working directory has staged changes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git read-tree -u -m "$commit"
|
||||||
|
then
|
||||||
|
die "Could not update working tree to new HEAD"
|
||||||
|
fi
|
||||||
77
hooks/sendemail-validate.sample
Normal file
77
hooks/sendemail-validate.sample
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# An example hook script to validate a patch (and/or patch series) before
|
||||||
|
# sending it via email.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an appropriate
|
||||||
|
# message if it wants to prevent the email(s) from being sent.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "sendemail-validate".
|
||||||
|
#
|
||||||
|
# By default, it will only check that the patch(es) can be applied on top of
|
||||||
|
# the default upstream branch without conflicts in a secondary worktree. After
|
||||||
|
# validation (successful or not) of the last patch of a series, the worktree
|
||||||
|
# will be deleted.
|
||||||
|
#
|
||||||
|
# The following config variables can be set to change the default remote and
|
||||||
|
# remote ref that are used to apply the patches against:
|
||||||
|
#
|
||||||
|
# sendemail.validateRemote (default: origin)
|
||||||
|
# sendemail.validateRemoteRef (default: HEAD)
|
||||||
|
#
|
||||||
|
# Replace the TODO placeholders with appropriate checks according to your
|
||||||
|
# needs.
|
||||||
|
|
||||||
|
validate_cover_letter () {
|
||||||
|
file="$1"
|
||||||
|
# TODO: Replace with appropriate checks (e.g. spell checking).
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_patch () {
|
||||||
|
file="$1"
|
||||||
|
# Ensure that the patch applies without conflicts.
|
||||||
|
git am -3 "$file" || return
|
||||||
|
# TODO: Replace with appropriate checks for this patch
|
||||||
|
# (e.g. checkpatch.pl).
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_series () {
|
||||||
|
# TODO: Replace with appropriate checks for the whole series
|
||||||
|
# (e.g. quick build, coding style checks, etc.).
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
# main -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
|
||||||
|
then
|
||||||
|
remote=$(git config --default origin --get sendemail.validateRemote) &&
|
||||||
|
ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
|
||||||
|
worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
|
||||||
|
git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
|
||||||
|
git config --replace-all sendemail.validateWorktree "$worktree"
|
||||||
|
else
|
||||||
|
worktree=$(git config --get sendemail.validateWorktree)
|
||||||
|
fi || {
|
||||||
|
echo "sendemail-validate: error: failed to prepare worktree" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unset GIT_DIR GIT_WORK_TREE
|
||||||
|
cd "$worktree" &&
|
||||||
|
|
||||||
|
if grep -q "^diff --git " "$1"
|
||||||
|
then
|
||||||
|
validate_patch "$1"
|
||||||
|
else
|
||||||
|
validate_cover_letter "$1"
|
||||||
|
fi &&
|
||||||
|
|
||||||
|
if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
|
||||||
|
then
|
||||||
|
git config --unset-all sendemail.validateWorktree &&
|
||||||
|
trap 'git worktree remove -ff "$worktree"' EXIT &&
|
||||||
|
validate_series
|
||||||
|
fi
|
||||||
128
hooks/update.sample
Normal file
128
hooks/update.sample
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to block unannotated tags from entering.
|
||||||
|
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "update".
|
||||||
|
#
|
||||||
|
# Config
|
||||||
|
# ------
|
||||||
|
# hooks.allowunannotated
|
||||||
|
# This boolean sets whether unannotated tags will be allowed into the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.allowdeletetag
|
||||||
|
# This boolean sets whether deleting tags will be allowed in the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.allowmodifytag
|
||||||
|
# This boolean sets whether a tag may be modified after creation. By default
|
||||||
|
# it won't be.
|
||||||
|
# hooks.allowdeletebranch
|
||||||
|
# This boolean sets whether deleting branches will be allowed in the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.denycreatebranch
|
||||||
|
# This boolean sets whether remotely creating branches will be denied
|
||||||
|
# in the repository. By default this is allowed.
|
||||||
|
#
|
||||||
|
|
||||||
|
# --- Command line
|
||||||
|
refname="$1"
|
||||||
|
oldrev="$2"
|
||||||
|
newrev="$3"
|
||||||
|
|
||||||
|
# --- Safety check
|
||||||
|
if [ -z "$GIT_DIR" ]; then
|
||||||
|
echo "Don't run this script from the command line." >&2
|
||||||
|
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||||
|
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||||
|
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Config
|
||||||
|
allowunannotated=$(git config --type=bool hooks.allowunannotated)
|
||||||
|
allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
|
||||||
|
denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
|
||||||
|
allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
|
||||||
|
allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
|
||||||
|
|
||||||
|
# check for no description
|
||||||
|
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||||
|
case "$projectdesc" in
|
||||||
|
"Unnamed repository"* | "")
|
||||||
|
echo "*** Project description file hasn't been set" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Check types
|
||||||
|
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||||
|
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
|
||||||
|
if [ "$newrev" = "$zero" ]; then
|
||||||
|
newrev_type=delete
|
||||||
|
else
|
||||||
|
newrev_type=$(git cat-file -t $newrev)
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$refname","$newrev_type" in
|
||||||
|
refs/tags/*,commit)
|
||||||
|
# un-annotated tag
|
||||||
|
short_refname=${refname##refs/tags/}
|
||||||
|
if [ "$allowunannotated" != "true" ]; then
|
||||||
|
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||||
|
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/tags/*,delete)
|
||||||
|
# delete tag
|
||||||
|
if [ "$allowdeletetag" != "true" ]; then
|
||||||
|
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/tags/*,tag)
|
||||||
|
# annotated tag
|
||||||
|
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||||
|
then
|
||||||
|
echo "*** Tag '$refname' already exists." >&2
|
||||||
|
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/heads/*,commit)
|
||||||
|
# branch
|
||||||
|
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||||
|
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/heads/*,delete)
|
||||||
|
# delete branch
|
||||||
|
if [ "$allowdeletebranch" != "true" ]; then
|
||||||
|
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/remotes/*,commit)
|
||||||
|
# tracking branch
|
||||||
|
;;
|
||||||
|
refs/remotes/*,delete)
|
||||||
|
# delete tracking branch
|
||||||
|
if [ "$allowdeletebranch" != "true" ]; then
|
||||||
|
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Anything else (is there anything else?)
|
||||||
|
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Finished
|
||||||
|
exit 0
|
||||||
6
info/exclude
Normal file
6
info/exclude
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
||||||
BIN
objects/00/18e012a737d33450a3866cc205f97ea6e1fb70
Normal file
BIN
objects/00/18e012a737d33450a3866cc205f97ea6e1fb70
Normal file
Binary file not shown.
BIN
objects/00/969a1c8a366694668216adb45f83d6f1e556d9
Normal file
BIN
objects/00/969a1c8a366694668216adb45f83d6f1e556d9
Normal file
Binary file not shown.
BIN
objects/01/97aecc21afa11827bd9f862b617914ddb6aa40
Normal file
BIN
objects/01/97aecc21afa11827bd9f862b617914ddb6aa40
Normal file
Binary file not shown.
BIN
objects/01/f107b4bbf1f7b0d8a908c3871ba09b5b5cb798
Normal file
BIN
objects/01/f107b4bbf1f7b0d8a908c3871ba09b5b5cb798
Normal file
Binary file not shown.
BIN
objects/03/6329b0c56e81c756c754babc7cc440f1a4008e
Normal file
BIN
objects/03/6329b0c56e81c756c754babc7cc440f1a4008e
Normal file
Binary file not shown.
BIN
objects/06/3b77deaa237dc8a50e9567b5cd69f30d9f0596
Normal file
BIN
objects/06/3b77deaa237dc8a50e9567b5cd69f30d9f0596
Normal file
Binary file not shown.
BIN
objects/08/d9f4304a05d54afbdfda83a3a94723a8b1e51d
Normal file
BIN
objects/08/d9f4304a05d54afbdfda83a3a94723a8b1e51d
Normal file
Binary file not shown.
BIN
objects/09/01725297a1bbfbf20f6836b3ec2bb41edea3ee
Normal file
BIN
objects/09/01725297a1bbfbf20f6836b3ec2bb41edea3ee
Normal file
Binary file not shown.
BIN
objects/09/1d49b980f8fc0d822c24fdf21e8cb5f685462c
Normal file
BIN
objects/09/1d49b980f8fc0d822c24fdf21e8cb5f685462c
Normal file
Binary file not shown.
BIN
objects/0b/f635618c37d2f23740e65873826cac3677e0d4
Normal file
BIN
objects/0b/f635618c37d2f23740e65873826cac3677e0d4
Normal file
Binary file not shown.
2
objects/0c/a3fdbe7aa61df2722044646b749fe9e01cf1d1
Normal file
2
objects/0c/a3fdbe7aa61df2722044646b749fe9e01cf1d1
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
x<01><>Mn<4D>0<0C><><EFBFBD>><3E>kEB UU7]<5D>&q %?O<>jO_x=AW<1E><>'<27><>1z<31>nTO\<5C>@<40>v<EFBFBD>f<EFBFBD><66>fջ۞<04>]<5D><><EFBFBD><EFBFBD>R;E<>:<3A><>;Jb<>f<EFBFBD>Hb<10>9<EFBFBD><39>$P<>$[i<>n<EFBFBD><6E>aTV<54><56><EFBFBD><EFBFBD><EFBFBD>\<5C><>WL<57>J<EFBFBD>Yl<59><6C>ޗ<EFBFBD>><3E><><1C>@zRreϭj<CFAD><6A>t<EFBFBD>yL<79><06>H<EFBFBD><48><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
<EFBFBD><1C>l>-0Lf<4C>A!<0C><>l\a5<61>N<EFBFBD><4E><EFBFBD><EFBFBD>#X<>~Ip`<05><02><>n<EFBFBD><6E><EFBFBD>'$<24><><0C><><EFBFBD>.<2E><>W~<7E><><EFBFBD>Z<1B>|9<><39>
|
||||||
BIN
objects/0d/8d05cf4b2731f31c3bc337cc5c4cd8d945b996
Normal file
BIN
objects/0d/8d05cf4b2731f31c3bc337cc5c4cd8d945b996
Normal file
Binary file not shown.
BIN
objects/0d/d875f2e70c9139168c120e1c74babad769420a
Normal file
BIN
objects/0d/d875f2e70c9139168c120e1c74babad769420a
Normal file
Binary file not shown.
BIN
objects/0e/63c79653bb2a39520506d503e710bcaffc1417
Normal file
BIN
objects/0e/63c79653bb2a39520506d503e710bcaffc1417
Normal file
Binary file not shown.
3
objects/11/b30845f2269c04acb3640877884df3a875bb1f
Normal file
3
objects/11/b30845f2269c04acb3640877884df3a875bb1f
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
x<01>W<EFBFBD>n<EFBFBD>0<10>9_A<5F>d!n<><15><>ni<6E><69><17> [<5B><>Z]ZF<5A><46>/<2F>X<EFBFBD><58>&q<11><>7<EFBFBD><37>o<EFBFBD>^<5E>r(<28><>%<1F><>!?<3F><><19>R<><52><EFBFBD>,<2C><><EFBFBD><EFBFBD>2e<32>Pʺ<50>U<EFBFBD><55><EFBFBD><EFBFBD>=;<3B>'<27>
|
||||||
|
<EFBFBD>[!<21><>I)Qճͩ,<2C><><EFBFBD><EFBFBD>J<EFBFBD>ry<72><79>G1<47><31><02><>1[+y<<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>4<EFBFBD><34>J<1A><>C<>X<EFBFBD>Ρ<EFBFBD>QDIbb<11> <20><11><0C>)<29><>7<EFBFBD>M
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>I<EFBFBD>DȽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10>c
|
||||||
BIN
objects/13/464dfc30a13d4eafe09eedae8274926753b14b
Normal file
BIN
objects/13/464dfc30a13d4eafe09eedae8274926753b14b
Normal file
Binary file not shown.
BIN
objects/13/e75caa45d5deb4e21d2596c54aa544328042c2
Normal file
BIN
objects/13/e75caa45d5deb4e21d2596c54aa544328042c2
Normal file
Binary file not shown.
BIN
objects/15/941e4765075416c9e4431b715c7efc7ce9121f
Normal file
BIN
objects/15/941e4765075416c9e4431b715c7efc7ce9121f
Normal file
Binary file not shown.
BIN
objects/15/abf09aeeb7da4d0901502db8dc0f7f59cda3d0
Normal file
BIN
objects/15/abf09aeeb7da4d0901502db8dc0f7f59cda3d0
Normal file
Binary file not shown.
BIN
objects/16/75dc905c352a0fa4baff8887bcbbac41e1d879
Normal file
BIN
objects/16/75dc905c352a0fa4baff8887bcbbac41e1d879
Normal file
Binary file not shown.
BIN
objects/16/bc9dc0e3c6d02d3bb278b8fed82abcfa018e53
Normal file
BIN
objects/16/bc9dc0e3c6d02d3bb278b8fed82abcfa018e53
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user