commit 676659e5b90e1a44bfa79dc9cda354b3bc9216ac Author: Varyngoth Date: Wed Jan 28 13:31:49 2026 -0400 First diff --git a/HEAD b/HEAD new file mode 100644 index 0000000..b870d82 --- /dev/null +++ b/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/SRS_evac_2025-11-23_23-03-09.sqlite b/SRS_evac_2025-11-23_23-03-09.sqlite new file mode 100644 index 0000000..5feca4a Binary files /dev/null and b/SRS_evac_2025-11-23_23-03-09.sqlite differ diff --git a/SRS_evac_2025-11-24_01-03-06.sqlite b/SRS_evac_2025-11-24_01-03-06.sqlite new file mode 100644 index 0000000..1d96b2e Binary files /dev/null and b/SRS_evac_2025-11-24_01-03-06.sqlite differ diff --git a/SRS_evac_2025-11-30_19-43-15.sqlite b/SRS_evac_2025-11-30_19-43-15.sqlite new file mode 100644 index 0000000..2b6c51c Binary files /dev/null and b/SRS_evac_2025-11-30_19-43-15.sqlite differ diff --git a/_0_next-steps.txt b/_0_next-steps.txt new file mode 100644 index 0000000..de33e48 --- /dev/null +++ b/_0_next-steps.txt @@ -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 \ No newline at end of file diff --git a/_SRS_sim_010542_.sqlite b/_SRS_sim_010542_.sqlite new file mode 100644 index 0000000..83eca43 Binary files /dev/null and b/_SRS_sim_010542_.sqlite differ diff --git a/_SRS_sim_013834_.sqlite b/_SRS_sim_013834_.sqlite new file mode 100644 index 0000000..2ed0a1e Binary files /dev/null and b/_SRS_sim_013834_.sqlite differ diff --git a/_SRS_sim_231233_.sqlite b/_SRS_sim_231233_.sqlite new file mode 100644 index 0000000..091d49b Binary files /dev/null and b/_SRS_sim_231233_.sqlite differ diff --git a/_SRS_sim_231809_.sqlite b/_SRS_sim_231809_.sqlite new file mode 100644 index 0000000..2db9133 Binary files /dev/null and b/_SRS_sim_231809_.sqlite differ diff --git a/_SRS_sim_232009_.sqlite b/_SRS_sim_232009_.sqlite new file mode 100644 index 0000000..8f20212 Binary files /dev/null and b/_SRS_sim_232009_.sqlite differ diff --git a/_source_/SRS_evac_2025-11-30_19-43-15.sqlite b/_source_/SRS_evac_2025-11-30_19-43-15.sqlite new file mode 100644 index 0000000..e69de29 diff --git a/_source_/agent_data.py b/_source_/agent_data.py new file mode 100644 index 0000000..deb673a --- /dev/null +++ b/_source_/agent_data.py @@ -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 + }} \ No newline at end of file diff --git a/_source_/agent_setup.py b/_source_/agent_setup.py new file mode 100644 index 0000000..0197aec --- /dev/null +++ b/_source_/agent_setup.py @@ -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 + diff --git a/_source_/config.py b/_source_/config.py new file mode 100644 index 0000000..98f5f64 --- /dev/null +++ b/_source_/config.py @@ -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" + + diff --git a/_source_/crosswalk_setup.py b/_source_/crosswalk_setup.py new file mode 100644 index 0000000..21e67a9 --- /dev/null +++ b/_source_/crosswalk_setup.py @@ -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) + } \ No newline at end of file diff --git a/_source_/geometry_setup.py b/_source_/geometry_setup.py new file mode 100644 index 0000000..c0ae039 --- /dev/null +++ b/_source_/geometry_setup.py @@ -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()) \ No newline at end of file diff --git a/_source_/requirements.txt b/_source_/requirements.txt new file mode 100644 index 0000000..e99683c --- /dev/null +++ b/_source_/requirements.txt @@ -0,0 +1,9 @@ +matplotlib +PyQt5 +numpy +scipy +shapely +pedpy +jupedsim +pandas +plotly \ No newline at end of file diff --git a/_source_/simulation.py b/_source_/simulation.py new file mode 100644 index 0000000..81f720c --- /dev/null +++ b/_source_/simulation.py @@ -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 xNone: + 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]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) 0) diff --git a/_source_/visualizer.py b/_source_/visualizer.py new file mode 100644 index 0000000..1127c9c --- /dev/null +++ b/_source_/visualizer.py @@ -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) \ No newline at end of file diff --git a/archive/SRS_evac_path/SRS_evac.sqlite b/archive/SRS_evac_path/SRS_evac.sqlite new file mode 100644 index 0000000..edc5850 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-23_23-02-22.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-23_23-02-22.sqlite new file mode 100644 index 0000000..e69de29 diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-23-22.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-23-22.sqlite new file mode 100644 index 0000000..2cb4833 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-23-22.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-41-16.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-41-16.sqlite new file mode 100644 index 0000000..16df689 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-41-16.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-42-15.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-42-15.sqlite new file mode 100644 index 0000000..404eded Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-42-15.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-45-35.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-45-35.sqlite new file mode 100644 index 0000000..404eded Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-45-35.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-49-17.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-49-17.sqlite new file mode 100644 index 0000000..e69de29 diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-50-42.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-50-42.sqlite new file mode 100644 index 0000000..e69de29 diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-51-43.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-51-43.sqlite new file mode 100644 index 0000000..f72270f Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-51-43.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-52-52.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-52-52.sqlite new file mode 100644 index 0000000..1926543 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-52-52.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_00-54-40.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-54-40.sqlite new file mode 100644 index 0000000..3403f18 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_00-54-40.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-24_01-01-57.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-24_01-01-57.sqlite new file mode 100644 index 0000000..27751dd Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-24_01-01-57.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-30_19-35-45.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-30_19-35-45.sqlite new file mode 100644 index 0000000..7449af9 Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-30_19-35-45.sqlite differ diff --git a/archive/SRS_evac_path/SRS_evac_2025-11-30_19-37-01.sqlite b/archive/SRS_evac_path/SRS_evac_2025-11-30_19-37-01.sqlite new file mode 100644 index 0000000..783b2fc Binary files /dev/null and b/archive/SRS_evac_path/SRS_evac_2025-11-30_19-37-01.sqlite differ diff --git a/archive/SRS_modeling_2025-11-09.py b/archive/SRS_modeling_2025-11-09.py new file mode 100644 index 0000000..def86c5 --- /dev/null +++ b/archive/SRS_modeling_2025-11-09.py @@ -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 attempts0: + 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_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() diff --git a/archive/SRS_modeling_2025-11-10.py b/archive/SRS_modeling_2025-11-10.py new file mode 100644 index 0000000..5401e5d --- /dev/null +++ b/archive/SRS_modeling_2025-11-10.py @@ -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 attempts0: + 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_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() diff --git a/archive/SRS_modeling_2025-11-22.py b/archive/SRS_modeling_2025-11-22.py new file mode 100644 index 0000000..9e98a9d --- /dev/null +++ b/archive/SRS_modeling_2025-11-22.py @@ -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 attempts0: + 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()0 and attempts0: + 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()None: + min_x,min_y,max_x,max_y = area.bounds + points = [] + x = (min_x+spacing)/2 + while x {spawn_time, stage_history} + self.agents_in_queue: set = set() + self.agents_in_crosswalk: set = set() + self.simulation_complete = False + + print(f"Simulation initialized with {self.total_agents} total agents.") + print(f"Crosswalk: trigger={crosswalk_config.trigger_dist}m, " + f"active={crosswalk_config.activation_time}s, " + f"cooldown={crosswalk_config.cooldown_time}s") + + def _setup_routing(self) -> None: + """ + Setup JuPedSim journeys and stages for agent routing [citation:2]. + Agents follow: Queue Area -> (Wait for Crosswalk) -> Crosswalk -> Grass (Exit) + """ + # 1. QUEUE AREA (Waiting Stage) + # Create a waypoint in the center of the queue area as a gathering point + queue_center = self.queue_area.centroid + self.queue_stage_id = self.simulation.add_waypoint_stage( + [queue_center.x, queue_center.y], + distance=0.5 # Acceptance radius + ) + + # 2. CROSSWALK (Conditional Stage) + # Create waypoints for crosswalk entry and exit + crosswalk_bounds = self.crosswalk_area.bounds + crosswalk_entry = ((crosswalk_bounds[0] + crosswalk_bounds[2]) / 2, + crosswalk_bounds[1]) # Bottom center + crosswalk_exit = ((crosswalk_bounds[0] + crosswalk_bounds[2]) / 2, + crosswalk_bounds[3]) # Top center + + self.crosswalk_entry_id = self.simulation.add_waypoint_stage( + crosswalk_entry, + distance=0.3 + ) + self.crosswalk_exit_id = self.simulation.add_waypoint_stage( + crosswalk_exit, + distance=0.3 + ) + + # 3. GRASS AREA (Exit Stage) + # Create exit in the grass area + grass_bounds = self.grass_area.bounds + exit_polygon = Polygon([ + (grass_bounds[0], grass_bounds[1]), + (grass_bounds[2], grass_bounds[1]), + (grass_bounds[2], grass_bounds[1] + 2.0), + (grass_bounds[0], grass_bounds[1] + 2.0) + ]) + self.exit_stage_id = self.simulation.add_exit_stage(exit_polygon) + + # 4. CREATE JOURNEYS [citation:2] + # Journey 1: For agents waiting to cross + self.waiting_journey = jps.JourneyDescription([ + self.queue_stage_id, + self.crosswalk_entry_id, + self.exit_stage_id + ]) + + # Set transitions: queue -> crosswalk entry -> exit + self.waiting_journey.set_transition_for_stage( + self.queue_stage_id, + jps.Transition.create_fixed_transition(self.crosswalk_entry_id) + ) + self.waiting_journey.set_transition_for_stage( + self.crosswalk_entry_id, + jps.Transition.create_fixed_transition(self.exit_stage_id) + ) + + self.waiting_journey_id = self.simulation.add_journey(self.waiting_journey) + + # Journey 2: For agents actively crossing (bypasses queue) + self.crossing_journey = jps.JourneyDescription([ + self.crosswalk_entry_id, + self.exit_stage_id + ]) + self.crossing_journey.set_transition_for_stage( + self.crosswalk_entry_id, + jps.Transition.create_fixed_transition(self.exit_stage_id) + ) + self.crossing_journey_id = self.simulation.add_journey(self.crossing_journey) + + def _spawn_new_agents(self) -> None: + """ + Spawn new agents if there are available spawn points and agents remaining. + """ + # Get current agent positions for crosswalk controller + current_positions = {} + for agent_id in self.active_agents: + agent = self.simulation.agent(agent_id) + if agent: + current_positions[agent_id] = (agent.position[0], agent.position[1]) + + # Update crosswalk state with current agent positions + crosswalk_state = self.crosswalk_controller.update( + agent_pos=current_positions, + time_step=self.time_step, + current_time=self.current_time + ) + + # Determine how many agents to spawn (respecting queue capacity) + agents_to_spawn = min( + 5, # Max spawn per iteration + self.total_agents - len(self.active_agents) + ) + + for _ in range(agents_to_spawn): + agent_data = self.spawn_manager.spawn_agent(self.all_agents) + if not agent_data: + break # No more agents or spawn points + + # Choose journey based on crosswalk state + if crosswalk_state.is_active: + # Crosswalk is active, go directly to crossing + journey_id = self.crossing_journey_id + stage_id = self.crosswalk_entry_id + initial_target = "crosswalk" + else: + # Crosswalk inactive, go to queue area + journey_id = self.waiting_journey_id + stage_id = self.queue_stage_id + initial_target = "queue" + self.agents_in_queue.add(agent_data.id) + + # Add agent to JuPedSim simulation [citation:1] + spawn_point = self.spawn_manager.get_agent_pos(agent_data.id) + agent_params = jps.CollisionFreeSpeedModelAgentParameters( + journey_id=journey_id, + stage_id=stage_id, + position=spawn_point, + desired_speed=agent_data.speed, + radius=agent_data.radius + ) + + jps_agent_id = self.simulation.add_agent(agent_params) + + # Track agent in our system + self.active_agents[jps_agent_id] = { + 'original_id': agent_data.id, + 'grade': agent_data.grade, + 'spawn_time': self.current_time, + 'current_stage': initial_target, + 'stage_history': [initial_target] + } + + print(f"Spawned agent {agent_data.id} (JuPedSim ID: {jps_agent_id}) " + f"at {spawn_point}, target: {initial_target}, " + f"speed: {agent_data.speed:.2f}m/s") + + def _update_agent_stages(self) -> None: + """ + Update agent stages based on crosswalk state and agent positions. + Move agents from queue to crosswalk when crosswalk becomes active. + """ + if not self.crosswalk_controller.is_active: + return + + # Crosswalk is active, move queued agents to crossing + agents_to_move = [] + for jps_id, agent_info in self.active_agents.items(): + if agent_info['current_stage'] == 'queue' and jps_id in self.agents_in_queue: + agents_to_move.append(jps_id) + + for jps_id in agents_to_move[:5]: # Move up to 5 agents per iteration + agent = self.simulation.agent(jps_id) + if agent: + # Check if agent is actually in queue area before moving + agent_point = Point(agent.position[0], agent.position[1]) + if self.queue_area.contains(agent_point): + # Change agent's journey to crossing journey + self.simulation.switch_agent_journey( + jps_id, + self.crossing_journey_id, + self.crosswalk_entry_id + ) + + self.active_agents[jps_id]['current_stage'] = 'crosswalk' + self.active_agents[jps_id]['stage_history'].append('crosswalk') + self.agents_in_queue.discard(jps_id) + self.agents_in_crosswalk.add(jps_id) + + print(f"Moved agent {self.active_agents[jps_id]['original_id']} " + f"from queue to crosswalk") + + def _check_agent_progress(self) -> None: + """ + Check agent progress through stages and handle completed agents. + """ + completed_agents = [] + + for jps_id, agent_info in list(self.active_agents.items()): + agent = self.simulation.agent(jps_id) + if not agent: + # Agent has left the simulation (reached exit) + completed_agents.append(jps_id) + + # Free up spawn point + original_id = agent_info['original_id'] + self.spawn_manager.unfill_coords(original_id) + + print(f"Agent {original_id} completed journey in " + f"{self.current_time - agent_info['spawn_time']:.1f}s, " + f"stages: {agent_info['stage_history']}") + + # Update crosswalk controller if agent was in trigger zone + if original_id in self.crosswalk_controller.agents_in_trigger: + self.crosswalk_controller.agents_in_trigger.discard(original_id) + + else: + # Update current stage based on agent's target + current_target = agent.stage_id + if current_target == self.crosswalk_entry_id and agent_info['current_stage'] != 'crosswalk': + agent_info['current_stage'] = 'crosswalk' + agent_info['stage_history'].append('crosswalk') + self.agents_in_queue.discard(jps_id) + self.agents_in_crosswalk.add(jps_id) + + elif current_target == self.exit_stage_id and agent_info['current_stage'] != 'exit': + agent_info['current_stage'] = 'exit' + agent_info['stage_history'].append('exit') + self.agents_in_crosswalk.discard(jps_id) + + # Remove completed agents from tracking + for jps_id in completed_agents: + original_id = self.active_agents[jps_id]['original_id'] + self.agents_in_queue.discard(jps_id) + self.agents_in_crosswalk.discard(jps_id) + del self.active_agents[jps_id] + + def _log_simulation_state(self, iteration: int) -> None: + """ + Log current simulation state. + """ + if iteration % 100 == 0: # Log every 10 seconds (100 iterations * 0.1s) + crosswalk_stats = self.crosswalk_controller.get_statistics() + status = self.crosswalk_controller.get_state() + + print(f"\n--- Time: {self.current_time:.1f}s, Iteration: {iteration} ---") + print(f"Active agents: {len(self.active_agents)}") + print(f" In queue: {len(self.agents_in_queue)}, " + f"In crosswalk: {len(self.agents_in_crosswalk)}") + print(f"Crosswalk: {status.status.value.upper()}, " + f"Active: {status.is_active}, " + f"Agents in trigger: {status.agents_in_trigger}") + print(f"Total activations: {crosswalk_stats['total_activations']}, " + f"Agents served: {crosswalk_stats['agents_served']}") + print("-" * 50) + + def run(self) -> Dict: + """ + Run the complete simulation. + + Returns: + Dictionary with simulation results and statistics + """ + print("\n" + "="*60) + print("STARTING CROSSWALK SIMULATION") + print("="*60) + + start_wall_time = time.time() + iteration = 0 + + try: + while self.current_time < self.simulation_time: + # 1. Spawn new agents if capacity allows + self._spawn_new_agents() + + # 2. Update agent stages based on crosswalk state + self._update_agent_stages() + + # 3. Execute one simulation iteration [citation:1] + self.simulation.iterate() + + # 4. Update simulation time + self.current_time += self.time_step + iteration += 1 + + # 5. Check agent progress + self._check_agent_progress() + + # 6. Log state periodically + self._log_simulation_state(iteration) + + # 7. Check for simulation completion + if (len(self.active_agents) == 0 and + len(self.spawn_manager.spawned_agents) >= self.total_agents): + print("\nAll agents have completed their journey.") + self.simulation_complete = True + break + + except KeyboardInterrupt: + print("\nSimulation interrupted by user.") + except Exception as e: + print(f"\nError during simulation: {e}") + import traceback + traceback.print_exc() + + # Calculate final statistics + wall_time_elapsed = time.time() - start_wall_time + crosswalk_stats = self.crosswalk_controller.get_statistics() + + results = { + 'simulation_time': self.current_time, + 'wall_time': wall_time_elapsed, + 'iterations': iteration, + 'total_agents': self.total_agents, + 'agents_spawned': len(self.spawn_manager.spawned_agents), + 'agents_completed': self.total_agents - len(self.active_agents), + 'crosswalk_activations': crosswalk_stats['total_activations'], + 'crosswalk_active_time': crosswalk_stats['total_active_time'], + 'agents_served': crosswalk_stats['agents_served'], + 'simulation_complete': self.simulation_complete, + 'trajectory_file': self.trajectory_file + } + + print("\n" + "="*60) + print("SIMULATION COMPLETE") + print("="*60) + print(f"Simulation time: {self.current_time:.1f}s") + print(f"Wall time: {wall_time_elapsed:.1f}s") + print(f"Iterations: {iteration}") + print(f"Agents: {results['agents_spawned']} spawned, " + f"{results['agents_completed']} completed") + print(f"Crosswalk: {results['crosswalk_activations']} activations, " + f"{results['crosswalk_active_time']:.1f}s total active time") + print(f"Trajectory data saved to: {self.trajectory_file}") + print("="*60) + + return results + + def visualize(self) -> None: + """ + Create a simple visualization of the simulation setup. + Requires matplotlib. + """ + try: + import matplotlib.pyplot as plt + from shapely.plotting import plot_polygon + + fig, ax = plt.subplots(figsize=(12, 8)) + + # Plot areas + plot_polygon(self.spawn_area, ax=ax, color='purple', alpha=0.3, label='Spawn Area') + plot_polygon(self.queue_area, ax=ax, color='blue', alpha=0.3, label='Queue Area') + plot_polygon(self.trigger_area, ax=ax, color='green', alpha=0.3, label='Trigger Area') + plot_polygon(self.crosswalk_area, ax=ax, color='red', alpha=0.5, label='Crosswalk') + plot_polygon(self.grass_area, ax=ax, color='lightgreen', alpha=0.3, label='Grass Area') + + # Plot spawn points + spawn_points = self.spawn_manager.spawn_coords + ax.scatter(spawn_points[:, 0], spawn_points[:, 1], + c='black', s=10, alpha=0.5, label='Spawn Points') + + # Add journey waypoints + queue_center = self.queue_area.centroid + ax.scatter(queue_center.x, queue_center.y, c='blue', s=100, + marker='s', label='Queue Waypoint') + + crosswalk_bounds = self.crosswalk_area.bounds + crosswalk_entry = ((crosswalk_bounds[0] + crosswalk_bounds[2]) / 2, + crosswalk_bounds[1]) + crosswalk_exit = ((crosswalk_bounds[0] + crosswalk_bounds[2]) / 2, + crosswalk_bounds[3]) + + ax.scatter(crosswalk_entry[0], crosswalk_entry[1], c='red', s=100, + marker='^', label='Crosswalk Entry') + ax.scatter(crosswalk_exit[0], crosswalk_exit[1], c='green', s=100, + marker='v', label='Crosswalk Exit') + + ax.set_xlabel('X (meters)') + ax.set_ylabel('Y (meters)') + ax.set_title('Crosswalk Simulation Setup') + ax.legend(loc='upper right') + ax.grid(True, alpha=0.3) + ax.set_aspect('equal') + + plt.tight_layout() + plt.show() + + except ImportError: + print("Visualization requires matplotlib. Install with: pip install matplotlib") + + +def main(): + """ + Main entry point for the simulation. + """ + # Parse command line arguments + import argparse + parser = argparse.ArgumentParser(description='Run crosswalk simulation') + parser.add_argument('--setup', type=str, default='new', + help='Geometry setup: "current" or "new" (default)') + parser.add_argument('--time', type=float, default=300.0, + help='Maximum simulation time in seconds (default: 300)') + parser.add_argument('--visualize', action='store_true', + help='Show visualization before running') + + args = parser.parse_args() + + # Convert setup string to boolean + current_setup = (args.setup.lower() == 'current') + + # Create and run simulation + sim = CrosswalkSimulation( + current_setup=current_setup, + simulation_time=args.time + ) + + # Show visualization if requested + if args.visualize: + sim.visualize() + input("Press Enter to start simulation...") + + # Run simulation + results = sim.run() + + # Save results to file + import json + results_file = f"simulation_results_{time.strftime('%Y%m%d_%H%M%S')}.json" + with open(results_file, 'w') as f: + json.dump(results, f, indent=2) + + print(f"\nResults saved to: {results_file}") + return results + + +if __name__ == "__main__": + main() + + + +====================================== +(example 2) +====================================== + +import sys +import pathlib +import numpy as np +from typing import Dict, Tuple + +# 1. Import JuPedSim and supporting libraries +import jupedsim as jps +from shapely import Polygon, GeometryCollection, get_coordinates +from shapely.plotting import plot_polygon +import matplotlib.pyplot as plt + +# 2. Import your custom modules +from geometry_setup import GeometrySetup +from spawning import SpawnManager +from agents.agent_setup import run_AgentSetup +from crosswalk_setup import CrosswalkController, CrosswalkConfig, CrosswalkStatus + +class CrosswalkSimulation: + """ + Main class to integrate custom components with JuPedSim. + Manages the simulation loop, agent lifecycle, and crosswalk-controlled routing. + """ + def __init__(self, current_setup: bool, sim_time_step: float = 0.01): + self.sim_time_step = sim_time_step + self.current_time = 0.0 + + # 3. Initialize Geometry and Derived Polygons + print("Initializing geometry...") + self.geo = GeometrySetup(current_setup) + self.spawn_area = self.geo.spawn() + self.queue_area = self.geo.queue() + self.trigger_area = self.geo.trigger() + self.crosswalk_area = self.geo.crosswalk() + self.grass_area = self.geo.grass() + + # 4. Create the unified walkable area for JuPedSim + # The walkable area is the union of all non-obstacle polygons. + self.walkable_area = GeometryCollection([ + self.spawn_area, + self.queue_area, + self.trigger_area, + self.crosswalk_area, + self.grass_area + ]).unary_union # Creates a single polygon[citation:7] + + # 5. Initialize your custom management systems + print("Setting up agent and spawn managers...") + self.all_agents, self.pop_list = run_AgentSetup() + self.spawn_manager = SpawnManager(self.spawn_area, min_spacing=0.55) + self.spawn_manager.generate_coords(max_samples=500) # Generate spawn points + + print("Configuring crosswalk controller...") + crosswalk_config = CrosswalkConfig( + trigger_dist=2.0, + activation_time=15.0, # seconds active + cooldown_time=5.0, # seconds between activations + min_agents_activation=1, + activation_delay=0.0, + crosswalk_area=self.crosswalk_area, + trigger_area=self.trigger_area + ) + self.crosswalk_controller = CrosswalkController(crosswalk_config, current_setup) + + # 6. Set up the JuPedSim simulation core[citation:4] + print("Creating JuPedSim simulation object...") + self.trajectory_file = pathlib.Path("crosswalk_simulation.sqlite") + self.simulation = jps.Simulation( + model=jps.CollisionFreeSpeedModel(), # Choose a pedestrian model[citation:1] + geometry=self.walkable_area, + trajectory_writer=jps.SqliteTrajectoryWriter( + output_file=self.trajectory_file + ), + dt=self.sim_time_step + ) + + # 7. Define JuPedSim Stages for routing[citation:3] + # Waiting Set: Agents wait here (in the queue area) for the crosswalk. + self.waiting_positions = self._generate_waiting_positions(self.queue_area, spacing=0.6) + self.waiting_stage_id = self.simulation.add_waiting_set_stage(self.waiting_positions) + self.waiting_stage = self.simulation.get_stage(self.waiting_stage_id) + self.waiting_stage.state = jps.WaitingSetState.INACTIVE # Start as a waypoint + + # Exit Stage: Agents finish here (in the grass area). + # Use the centroid of the grass area as the final target. + grass_centroid = list(self.grass_area.centroid.coords[0]) + self.exit_stage_id = self.simulation.add_exit_stage( + Polygon([(grass_centroid[0]-0.5, grass_centroid[1]-0.5), + (grass_centroid[0]+0.5, grass_centroid[1]-0.5), + (grass_centroid[0]+0.5, grass_centroid[1]+0.5), + (grass_centroid[0]-0.5, grass_centroid[1]+0.5)]) + ) + + # 8. Create the main Journey[citation:3] + # Journey: Waiting Set -> Exit. + self.journey = jps.JourneyDescription([self.waiting_stage_id, self.exit_stage_id]) + # Transition: Agents go from the waiting set directly to the exit. + self.journey.set_transition_for_stage( + self.waiting_stage_id, + jps.Transition.create_fixed_transition(self.exit_stage_id) + ) + self.journey_id = self.simulation.add_journey(self.journey) + + # 9. Tracking dictionaries + self.jps_agent_ids = {} # Maps your agent.id -> JuPedSim agent id + self.agent_targets = {} # Maps your agent.id -> current target (x, y) + + def _generate_waiting_positions(self, polygon: Polygon, spacing: float): + """Generate grid positions inside a polygon for waiting/queueing.""" + min_x, min_y, max_x, max_y = polygon.bounds + positions = [] + x = min_x + spacing / 2 + while x < max_x: + y = min_y + spacing / 2 + while y < max_y: + point = jps.Vector2D(x, y) + if polygon.contains(Polygon([(x, y)]).buffer(0.1)): + positions.append((point.x, point.y)) + y += spacing + x += spacing + return positions if positions else [(min_x+0.5, min_y+0.5)] + + def run(self, max_iterations: int = 50000): + """Main simulation loop.""" + print(f"\nStarting simulation. Target agents: {len(self.all_agents)}") + iteration = 0 + + while iteration < max_iterations: + # A. SPAWNING: Add new agents if there is space and agents left. + if self.spawn_manager.check_spawn_complete(self.all_agents): + new_agent = self.spawn_manager.spawn_agent(self.all_agents) + if new_agent: + # Add agent to JuPedSim targeting the waiting set. + jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters( + journey_id=self.journey_id, + stage_id=self.waiting_stage_id, + position=self.spawn_manager.get_agent_pos(new_agent.id), + v0=new_agent.speed, # Desired 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 + self.agent_targets[new_agent.id] = None + print(f"Iter {iteration:05d}: Spawned agent {new_agent.id} (JPS-ID: {jps_agent_id})") + + # B. CROSSWALK CONTROL & ROUTING + # Get positions of ALL agents currently in the JuPedSim simulation. + current_agent_positions = {} + for your_id, jps_id in self.jps_agent_ids.items(): + agent = self.simulation.agent(jps_id) + current_agent_positions[your_id] = (agent.position[0], agent.position[1]) + + # Update the crosswalk state based on agents in the trigger area. + crosswalk_state = self.crosswalk_controller.update( + current_agent_positions, self.sim_time_step, self.current_time + ) + + # C. CONTROL AGENT MOVEMENT BASED ON CROSSWALK STATE + if crosswalk_state.is_active: + # Crosswalk ACTIVE: Release agents from the waiting set. + if self.waiting_stage.state != jps.WaitingSetState.INACTIVE: + self.waiting_stage.state = jps.WaitingSetState.INACTIVE + print(f"Iter {iteration:05d}: Crosswalk ACTIVATED, releasing queue.") + else: + # Crosswalk INACTIVE: Hold agents in the waiting set. + if self.waiting_stage.state != jps.WaitingSetState.ACTIVE:""" +main_simulation.py +Main integration file for crosswalk simulation with JuPedSim +""" + +import sys +import pathlib +from typing import Dict, Tuple, List +import jupedsim as jps +from shapely import Polygon, Point +import numpy as np +from dataclasses import dataclass +import matplotlib.pyplot as plt + +# Import your existing modules +from geometry_setup import GeometrySetup +from spawning import SpawnManager +from agents.agent_setup import run_AgentSetup +from crosswalk_setup import CrosswalkController, CrosswalkConfig, CrosswalkStatus + +@dataclass +class SimulationConfig: + """Configuration for the entire simulation""" + time_step: float = 0.01 # Simulation time step in seconds + max_iterations: int = 5000 # Maximum simulation iterations + output_file: str = "crosswalk_simulation.sqlite" + current_setup: bool = True # Use current or alternative geometry + model_type: str = "collision_free" # Options: "collision_free", "anticipation" + + # Crosswalk parameters + trigger_distance: float = 1.0 + activation_time: float = 10.0 + cooldown_time: float = 5.0 + min_agents_activation: int = 1 + activation_delay: float = 2.0 + +class CrosswalkSimulation: + """Main simulation class integrating all components""" + + def __init__(self, config: SimulationConfig): + self.config = config + self.simulation = None + self.geometry = None + self.spawn_manager = None + self.crosswalk_controller = None + self.agents_data = [] + self.journey_id = None + self.exit_id = None + self.queue_stage_id = None + + # Statistics + self.iteration_count = 0 + self.active_agents = {} + + def setup(self): + """Setup all simulation components""" + print("Setting up simulation components...") + + # 1. Setup geometry + geo_setup = GeometrySetup(self.config.current_setup) + self.geometry = self._create_geometry_polygon(geo_setup) + + # 2. Setup crosswalk controller + crosswalk_config = CrosswalkConfig( + trigger_dist=self.config.trigger_distance, + 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=geo_setup.crosswalk(), + trigger_area=geo_setup.trigger() + ) + self.crosswalk_controller = CrosswalkController( + crosswalk_config, + self.config.current_setup + ) + + # 3. Setup spawn manager + spawn_area = geo_setup.spawn() + self.spawn_manager = SpawnManager(spawn_area, min_spacing=0.55) + self.spawn_manager.generate_coords() + + # 4. Get agent data + self.agents_data, pop_list = run_AgentSetup() + print(f"Created {len(self.agents_data)} agents") + + # 5. Setup JuPedSim simulation + self._setup_jupedsim_simulation() + + print("Setup complete!") + + def _create_geometry_polygon(self, geo_setup: GeometrySetup) -> Polygon: + """Create a single walkable area polygon from all geometry components""" + # Combine all non-walkable areas (obstacles) as holes + walkable_exterior = Polygon([ + (0, 0), + (30, 0), + (30, 20), + (0, 20) + ]) + + # Define obstacles (non-walkable areas) + obstacles = [ + geo_setup.grass(), # Grass area is non-walkable + ] + + return walkable_exterior + + def _setup_jupedsim_simulation(self): + """Setup JuPedSim simulation object with chosen model[citation:1]""" + + # Choose pedestrian model[citation:1] + if self.config.model_type == "collision_free": + model = jps.CollisionFreeSpeedModel() + elif self.config.model_type == "anticipation": + model = jps.AnticipationVelocityModel() + else: + raise ValueError(f"Unknown model type: {self.config.model_type}") + + # Create simulation object[citation:6] + self.simulation = jps.Simulation( + model=model, + geometry=self.geometry, + trajectory_writer=jps.SqliteTrajectoryWriter( + output_file=pathlib.Path(self.config.output_file) + ), + ) + + # Setup journey with stages[citation:3] + self._setup_journey() + + def _setup_journey(self): + """Setup agent journey with stages and transitions[citation:3]""" + + # Create exit stage (across the road) + exit_polygon = Polygon([ + (4, 17.761), + (0, 17.761), + (0, 20), + (4, 20) + ]) + self.exit_id = self.simulation.add_exit_stage(exit_polygon) + + # Create waiting positions before crosswalk (queue area) + queue_positions = self._generate_queue_positions() + self.queue_stage_id = self.simulation.add_queue_stage(queue_positions) + + # Create waypoint at crosswalk entrance + crosswalk_entrance = (15.0, 7.0) # Near trigger area + waypoint_dist = 0.5 + waypoint_id = self.simulation.add_waypoint_stage(crosswalk_entrance, waypoint_dist) + + # Create journey with stages[citation:3] + journey = jps.JourneyDescription([ + waypoint_id, # First go to crosswalk entrance + self.queue_stage_id, # Wait in queue + self.exit_id # Finally exit + ]) + + # Setup transitions between stages[citation:3] + journey.set_transition_for_stage( + waypoint_id, + jps.Transition.create_fixed_transition(self.queue_stage_id) + ) + + # Transition from queue to exit only when crosswalk is active + # We'll control this dynamically in the simulation loop + journey.set_transition_for_stage( + self.queue_stage_id, + jps.Transition.create_fixed_transition(self.exit_id) + ) + + self.journey_id = self.simulation.add_journey(journey) + + def _generate_queue_positions(self) -> List[Tuple[float, float]]: + """Generate waiting positions in the queue area""" + queue_positions = [] + # Create a grid of waiting positions in the queue area + for i in range(5): # 5 rows + for j in range(3): # 3 columns + x = 18.0 + j * 0.6 + y = 6.0 + i * 0.6 + queue_positions.append((x, y)) + return queue_positions + + def run(self): + """Run the main simulation loop""" + print("Starting simulation...") + + # Spawn initial agents + self._spawn_initial_agents() + + # Get queue object for controlling agent release[citation:2] + queue = self.simulation.get_stage(self.queue_stage_id) + + # Main simulation loop[citation:6] + while (self.simulation.agent_count() > 0 and + self.iteration_count < self.config.max_iterations): + + # Get current agent positions + agent_positions = self._get_agent_positions() + + # Update crosswalk controller + crosswalk_state = self.crosswalk_controller.update( + agent_positions, + self.config.time_step, + current_time=self.iteration_count * self.config.time_step + ) + + # Control queue based on crosswalk state[citation:2] + if crosswalk_state.is_active: + # Release agents from queue when crosswalk is active + agents_in_queue = self._count_agents_in_stage(self.queue_stage_id) + if agents_in_queue > 0: + # Release all agents that can cross + queue.pop(agents_in_queue) + + # Spawn new agents if needed + self._spawn_new_agents() + + # Run simulation iteration + self.simulation.iterate() + self.iteration_count += 1 + + # Print progress + if self.iteration_count % 100 == 0: + self._print_progress(crosswalk_state) + + print(f"Simulation completed after {self.iteration_count} iterations") + print(f"Agents remaining: {self.simulation.agent_count()}") + + # Print final statistics + stats = self.crosswalk_controller.get_statistics() + print("\nCrosswalk Statistics:") + for key, value in stats.items(): + print(f" {key}: {value}") + + def _spawn_initial_agents(self): + """Spawn initial batch of agents""" + num_initial_agents = min(20, len(self.agents_data)) + print(f"Spawning {num_initial_agents} initial agents...") + + for _ in range(num_initial_agents): + agent = self.spawn_manager.spawn_agent(self.agents_data) + if agent: + self._add_agent_to_simulation(agent) + + def _spawn_new_agents(self): + """Spawn new agents during simulation if needed""" + # Spawn new agent every 10 iterations if we have capacity + if self.iteration_count % 10 == 0: + agent = self.spawn_manager.spawn_agent(self.agents_data) + if agent: + self._add_agent_to_simulation(agent) + + def _add_agent_to_simulation(self, agent): + """Add an agent to the JuPedSim simulation[citation:6]""" + + # Get spawn position + spawn_pos = self.spawn_manager.get_agent_pos(agent.id) + + # Choose appropriate agent parameters based on model[citation:1] + if self.config.model_type == "collision_free": + agent_params = jps.CollisionFreeSpeedModelAgentParameters( + position=spawn_pos, + desired_speed=agent.speed, + radius=agent.radius, + journey_id=self.journey_id, + stage_id=self.queue_stage_id # Start in queue stage + ) + else: # anticipation model + agent_params = jps.AnticipationVelocityModelAgentParameters( + position=spawn_pos, + desired_speed=agent.speed, + radius=agent.radius, + journey_id=self.journey_id, + stage_id=self.queue_stage_id + ) + + # Add agent to simulation + agent_id = self.simulation.add_agent(agent_params) + self.active_agents[agent_id] = agent.id + + print(f"Spawned agent {agent.id} at position {spawn_pos}") + + def _get_agent_positions(self) -> Dict[int, Tuple[float, float]]: + """Get current positions of all agents in simulation""" + positions = {} + for agent in self.simulation.agents(): + positions[agent.id] = agent.position + return positions + + def _count_agents_in_stage(self, stage_id: int) -> int: + """Count agents currently targeting a specific stage""" + count = 0 + for agent in self.simulation.agents(): + if agent.stage_id == stage_id: + count += 1 + return count + + def _print_progress(self, crosswalk_state): + """Print simulation progress""" + print(f"Iteration: {self.iteration_count}, " + f"Agents: {self.simulation.agent_count()}, " + f"Crosswalk: {crosswalk_state.status.value}, " + f"Active time remaining: {crosswalk_state.time_remaining:.1f}s") + + def visualize(self): + """Visualize simulation results""" + try: + import pedpy + from jupedsim.internal.notebook_utils import read_sqlite_file + + # Read trajectory data + trajectory_data, walkable_area = read_sqlite_file(self.config.output_file) + + # Create visualization + fig, axes = plt.subplots(1, 2, figsize=(15, 6)) + + # Plot trajectories + pedpy.plot_trajectories( + traj=trajectory_data, + axes=axes[0], + walkable_area=walkable_area + ) + axes[0].set_title("Agent Trajectories") + + # Plot density heatmap + density = pedpy.compute_classic_density( + traj_data=trajectory_data, + measurement_area=walkable_area.polygon, + frame_step=10 + ) + pedpy.plot_density( + density=density, + axes=axes[1], + walkable_area=walkable_area + ) + axes[1].set_title("Density Heatmap") + + plt.tight_layout() + plt.show() + + except ImportError: + print("Visualization requires pedpy and matplotlib. Install with: pip install pedpy matplotlib") + +def main(): + """Main entry point for the simulation""" + + # Configuration + config = SimulationConfig( + current_setup=True, + time_step=0.01, + max_iterations=3000, + output_file="crosswalk_simulation_results.sqlite", + trigger_distance=1.5, + activation_time=15.0, + cooldown_time=3.0, + min_agents_activation=1, + activation_delay=1.0 + ) + + # Create and run simulation + simulation = CrosswalkSimulation(config) + simulation.setup() + simulation.run() + + # Optional: Visualize results + # simulation.visualize() + + print("\nSimulation completed successfully!") + +if __name__ == "__main__": + main() + self.waiting_stage.state = jps.WaitingSetState.ACTIVE + print(f"Iter {iteration:05d}: Crosswalk INACTIVE, holding agents.") + + # D. ITERATE: Advance the JuPedSim simulation by one step. + self.simulation.iterate() + self.current_time += self.sim_time_step + iteration += 1 + + # E. REMOVAL: Check for agents that have reached the exit. + # Their JPS id will no longer be found in the simulation. + agents_to_remove = [] + for your_id, jps_id in list(self.jps_agent_ids.items()): + try: + # This will fail if the agent has been removed (reached exit). + _ = self.simulation.agent(jps_id) + except RuntimeError: + agents_to_remove.append(your_id) + # Free up the spawn point for this agent. + self.spawn_manager.unfill_coords(your_id) + + for your_id in agents_to_remove: + del self.jps_agent_ids[your_id] + if your_id in self.agent_targets: + del self.agent_targets[your_id] + print(f"Iter {iteration:05d}: Agent {your_id} reached exit and was removed.") + + # F. COMPLETION CHECK: Stop if all agents spawned and exited. + if (len(self.jps_agent_ids) == 0 and + not self.spawn_manager.check_spawn_complete(self.all_agents)): + print(f"\nSimulation completed at iteration {iteration}.") + print(f"Total simulation time: {self.current_time:.2f} seconds.") + break + + # G. FINALIZATION + 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()}") + + def plot_final_setup(self): + """Visualize the final geometry setup.""" + fig, ax = plt.subplots(figsize=(10, 8)) + for poly, color, label in [ + (self.spawn_area, 'purple', 'Spawn Area'), + (self.queue_area, 'blue', 'Queue Area'), + (self.trigger_area, 'green', 'Trigger Area'), + (self.crosswalk_area, 'red', 'Crosswalk Area'), + (self.grass_area, 'lightblue', 'Grass Area') + ]: + plot_polygon(poly, ax=ax, color=color, alpha=0.5, label=label, add_points=False) + ax.legend() + ax.set_title("Simulation Geometry Setup") + ax.set_xlabel("x [m]") + ax.set_ylabel("y [m]") + ax.set_aspect('equal') + plt.show() + +if __name__ == "__main__": + # Run the simulation with the 'False' geometry setup. + sim = CrosswalkSimulation(current_setup=False, sim_time_step=0.05) + sim.plot_final_setup() # Optional: view geometry before running + sim.run(max_iterations=20000) + +========================== +(example 3) +========================== +""" +main_simulation.py +Main integration file for crosswalk simulation with JuPedSim +""" + +import sys +import pathlib +from typing import Dict, Tuple, List +import jupedsim as jps +from shapely import Polygon, Point +import numpy as np +from dataclasses import dataclass +import matplotlib.pyplot as plt + +# Import your existing modules +from geometry_setup import GeometrySetup +from spawning import SpawnManager +from agents.agent_setup import run_AgentSetup +from crosswalk_setup import CrosswalkController, CrosswalkConfig, CrosswalkStatus + +@dataclass +class SimulationConfig: + """Configuration for the entire simulation""" + time_step: float = 0.01 # Simulation time step in seconds + max_iterations: int = 5000 # Maximum simulation iterations + output_file: str = "crosswalk_simulation.sqlite" + current_setup: bool = True # Use current or alternative geometry + model_type: str = "collision_free" # Options: "collision_free", "anticipation" + + # Crosswalk parameters + trigger_distance: float = 1.0 + activation_time: float = 10.0 + cooldown_time: float = 5.0 + min_agents_activation: int = 1 + activation_delay: float = 2.0 + +class CrosswalkSimulation: + """Main simulation class integrating all components""" + + def __init__(self, config: SimulationConfig): + self.config = config + self.simulation = None + self.geometry = None + self.spawn_manager = None + self.crosswalk_controller = None + self.agents_data = [] + self.journey_id = None + self.exit_id = None + self.queue_stage_id = None + + # Statistics + self.iteration_count = 0 + self.active_agents = {} + + def setup(self): + """Setup all simulation components""" + print("Setting up simulation components...") + + # 1. Setup geometry + geo_setup = GeometrySetup(self.config.current_setup) + self.geometry = self._create_geometry_polygon(geo_setup) + + # 2. Setup crosswalk controller + crosswalk_config = CrosswalkConfig( + trigger_dist=self.config.trigger_distance, + 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=geo_setup.crosswalk(), + trigger_area=geo_setup.trigger() + ) + self.crosswalk_controller = CrosswalkController( + crosswalk_config, + self.config.current_setup + ) + + # 3. Setup spawn manager + spawn_area = geo_setup.spawn() + self.spawn_manager = SpawnManager(spawn_area, min_spacing=0.55) + self.spawn_manager.generate_coords() + + # 4. Get agent data + self.agents_data, pop_list = run_AgentSetup() + print(f"Created {len(self.agents_data)} agents") + + # 5. Setup JuPedSim simulation + self._setup_jupedsim_simulation() + + print("Setup complete!") + + def _create_geometry_polygon(self, geo_setup: GeometrySetup) -> Polygon: + """Create a single walkable area polygon from all geometry components""" + # Combine all non-walkable areas (obstacles) as holes + walkable_exterior = Polygon([ + (0, 0), + (30, 0), + (30, 20), + (0, 20) + ]) + + # Define obstacles (non-walkable areas) + obstacles = [ + geo_setup.grass(), # Grass area is non-walkable + ] + + return walkable_exterior + + def _setup_jupedsim_simulation(self): + """Setup JuPedSim simulation object with chosen model[citation:1]""" + + # Choose pedestrian model[citation:1] + if self.config.model_type == "collision_free": + model = jps.CollisionFreeSpeedModel() + elif self.config.model_type == "anticipation": + model = jps.AnticipationVelocityModel() + else: + raise ValueError(f"Unknown model type: {self.config.model_type}") + + # Create simulation object[citation:6] + self.simulation = jps.Simulation( + model=model, + geometry=self.geometry, + trajectory_writer=jps.SqliteTrajectoryWriter( + output_file=pathlib.Path(self.config.output_file) + ), + ) + + # Setup journey with stages[citation:3] + self._setup_journey() + + def _setup_journey(self): + """Setup agent journey with stages and transitions[citation:3]""" + + # Create exit stage (across the road) + exit_polygon = Polygon([ + (4, 17.761), + (0, 17.761), + (0, 20), + (4, 20) + ]) + self.exit_id = self.simulation.add_exit_stage(exit_polygon) + + # Create waiting positions before crosswalk (queue area) + queue_positions = self._generate_queue_positions() + self.queue_stage_id = self.simulation.add_queue_stage(queue_positions) + + # Create waypoint at crosswalk entrance + crosswalk_entrance = (15.0, 7.0) # Near trigger area + waypoint_dist = 0.5 + waypoint_id = self.simulation.add_waypoint_stage(crosswalk_entrance, waypoint_dist) + + # Create journey with stages[citation:3] + journey = jps.JourneyDescription([ + waypoint_id, # First go to crosswalk entrance + self.queue_stage_id, # Wait in queue + self.exit_id # Finally exit + ]) + + # Setup transitions between stages[citation:3] + journey.set_transition_for_stage( + waypoint_id, + jps.Transition.create_fixed_transition(self.queue_stage_id) + ) + + # Transition from queue to exit only when crosswalk is active + # We'll control this dynamically in the simulation loop + journey.set_transition_for_stage( + self.queue_stage_id, + jps.Transition.create_fixed_transition(self.exit_id) + ) + + self.journey_id = self.simulation.add_journey(journey) + + def _generate_queue_positions(self) -> List[Tuple[float, float]]: + """Generate waiting positions in the queue area""" + queue_positions = [] + # Create a grid of waiting positions in the queue area + for i in range(5): # 5 rows + for j in range(3): # 3 columns + x = 18.0 + j * 0.6 + y = 6.0 + i * 0.6 + queue_positions.append((x, y)) + return queue_positions + + def run(self): + """Run the main simulation loop""" + print("Starting simulation...") + + # Spawn initial agents + self._spawn_initial_agents() + + # Get queue object for controlling agent release[citation:2] + queue = self.simulation.get_stage(self.queue_stage_id) + + # Main simulation loop[citation:6] + while (self.simulation.agent_count() > 0 and + self.iteration_count < self.config.max_iterations): + + # Get current agent positions + agent_positions = self._get_agent_positions() + + # Update crosswalk controller + crosswalk_state = self.crosswalk_controller.update( + agent_positions, + self.config.time_step, + current_time=self.iteration_count * self.config.time_step + ) + + # Control queue based on crosswalk state[citation:2] + if crosswalk_state.is_active: + # Release agents from queue when crosswalk is active + agents_in_queue = self._count_agents_in_stage(self.queue_stage_id) + if agents_in_queue > 0: + # Release all agents that can cross + queue.pop(agents_in_queue) + + # Spawn new agents if needed + self._spawn_new_agents() + + # Run simulation iteration + self.simulation.iterate() + self.iteration_count += 1 + + # Print progress + if self.iteration_count % 100 == 0: + self._print_progress(crosswalk_state) + + print(f"Simulation completed after {self.iteration_count} iterations") + print(f"Agents remaining: {self.simulation.agent_count()}") + + # Print final statistics + stats = self.crosswalk_controller.get_statistics() + print("\nCrosswalk Statistics:") + for key, value in stats.items(): + print(f" {key}: {value}") + + def _spawn_initial_agents(self): + """Spawn initial batch of agents""" + num_initial_agents = min(20, len(self.agents_data)) + print(f"Spawning {num_initial_agents} initial agents...") + + for _ in range(num_initial_agents): + agent = self.spawn_manager.spawn_agent(self.agents_data) + if agent: + self._add_agent_to_simulation(agent) + + def _spawn_new_agents(self): + """Spawn new agents during simulation if needed""" + # Spawn new agent every 10 iterations if we have capacity + if self.iteration_count % 10 == 0: + agent = self.spawn_manager.spawn_agent(self.agents_data) + if agent: + self._add_agent_to_simulation(agent) + + def _add_agent_to_simulation(self, agent): + """Add an agent to the JuPedSim simulation[citation:6]""" + + # Get spawn position + spawn_pos = self.spawn_manager.get_agent_pos(agent.id) + + # Choose appropriate agent parameters based on model[citation:1] + if self.config.model_type == "collision_free": + agent_params = jps.CollisionFreeSpeedModelAgentParameters( + position=spawn_pos, + desired_speed=agent.speed, + radius=agent.radius, + journey_id=self.journey_id, + stage_id=self.queue_stage_id # Start in queue stage + ) + else: # anticipation model + agent_params = jps.AnticipationVelocityModelAgentParameters( + position=spawn_pos, + desired_speed=agent.speed, + radius=agent.radius, + journey_id=self.journey_id, + stage_id=self.queue_stage_id + ) + + # Add agent to simulation + agent_id = self.simulation.add_agent(agent_params) + self.active_agents[agent_id] = agent.id + + print(f"Spawned agent {agent.id} at position {spawn_pos}") + + def _get_agent_positions(self) -> Dict[int, Tuple[float, float]]: + """Get current positions of all agents in simulation""" + positions = {} + for agent in self.simulation.agents(): + positions[agent.id] = agent.position + return positions + + def _count_agents_in_stage(self, stage_id: int) -> int: + """Count agents currently targeting a specific stage""" + count = 0 + for agent in self.simulation.agents(): + if agent.stage_id == stage_id: + count += 1 + return count + + def _print_progress(self, crosswalk_state): + """Print simulation progress""" + print(f"Iteration: {self.iteration_count}, " + f"Agents: {self.simulation.agent_count()}, " + f"Crosswalk: {crosswalk_state.status.value}, " + f"Active time remaining: {crosswalk_state.time_remaining:.1f}s") + + def visualize(self): + """Visualize simulation results""" + try: + import pedpy + from jupedsim.internal.notebook_utils import read_sqlite_file + + # Read trajectory data + trajectory_data, walkable_area = read_sqlite_file(self.config.output_file) + + # Create visualization + fig, axes = plt.subplots(1, 2, figsize=(15, 6)) + + # Plot trajectories + pedpy.plot_trajectories( + traj=trajectory_data, + axes=axes[0], + walkable_area=walkable_area + ) + axes[0].set_title("Agent Trajectories") + + # Plot density heatmap + density = pedpy.compute_classic_density( + traj_data=trajectory_data, + measurement_area=walkable_area.polygon, + frame_step=10 + ) + pedpy.plot_density( + density=density, + axes=axes[1], + walkable_area=walkable_area + ) + axes[1].set_title("Density Heatmap") + + plt.tight_layout() + plt.show() + + except ImportError: + print("Visualization requires pedpy and matplotlib. Install with: pip install pedpy matplotlib") + +def main(): + """Main entry point for the simulation""" + + # Configuration + config = SimulationConfig( + current_setup=True, + time_step=0.01, + max_iterations=3000, + output_file="crosswalk_simulation_results.sqlite", + trigger_distance=1.5, + activation_time=15.0, + cooldown_time=3.0, + min_agents_activation=1, + activation_delay=1.0 + ) + + # Create and run simulation + simulation = CrosswalkSimulation(config) + simulation.setup() + simulation.run() + + # Optional: Visualize results + # simulation.visualize() + + print("\nSimulation completed successfully!") + +if __name__ == "__main__": + main() + + + diff --git a/archive/prompt_full-run-sim1.txt b/archive/prompt_full-run-sim1.txt new file mode 100644 index 0000000..d353721 --- /dev/null +++ b/archive/prompt_full-run-sim1.txt @@ -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]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) 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; + diff --git a/archive/prompt_full-run-sim2.txt b/archive/prompt_full-run-sim2.txt new file mode 100644 index 0000000..d4a86b0 --- /dev/null +++ b/archive/prompt_full-run-sim2.txt @@ -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]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) 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 xList[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") + diff --git a/archive/source/test/__pycache__/config.cpython-310.pyc b/archive/source/test/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..ccb81a7 Binary files /dev/null and b/archive/source/test/__pycache__/config.cpython-310.pyc differ diff --git a/archive/source/test/__pycache__/config.cpython-312.pyc b/archive/source/test/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..29a62c3 Binary files /dev/null and b/archive/source/test/__pycache__/config.cpython-312.pyc differ diff --git a/archive/source/test/__pycache__/config.cpython-313.pyc b/archive/source/test/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..a92f53d Binary files /dev/null and b/archive/source/test/__pycache__/config.cpython-313.pyc differ diff --git a/archive/source/test/_test_example_.py b/archive/source/test/_test_example_.py new file mode 100644 index 0000000..29fc12f --- /dev/null +++ b/archive/source/test/_test_example_.py @@ -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 \ No newline at end of file diff --git a/archive/source/test/config.py b/archive/source/test/config.py new file mode 100644 index 0000000..13e75ca --- /dev/null +++ b/archive/source/test/config.py @@ -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" + + diff --git a/config b/config new file mode 100644 index 0000000..64280b8 --- /dev/null +++ b/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true diff --git a/crosswalk_sim.sqlite b/crosswalk_sim.sqlite new file mode 100644 index 0000000..d3e4454 Binary files /dev/null and b/crosswalk_sim.sqlite differ diff --git a/description b/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/hooks/applypatch-msg.sample b/hooks/applypatch-msg.sample new file mode 100644 index 0000000..a5d7b84 --- /dev/null +++ b/hooks/applypatch-msg.sample @@ -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+"$@"} +: diff --git a/hooks/commit-msg.sample b/hooks/commit-msg.sample new file mode 100644 index 0000000..b58d118 --- /dev/null +++ b/hooks/commit-msg.sample @@ -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 +} diff --git a/hooks/fsmonitor-watchman.sample b/hooks/fsmonitor-watchman.sample new file mode 100644 index 0000000..23e856f --- /dev/null +++ b/hooks/fsmonitor-watchman.sample @@ -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 $/; }; + + # 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; +} diff --git a/hooks/post-update.sample b/hooks/post-update.sample new file mode 100644 index 0000000..ec17ec1 --- /dev/null +++ b/hooks/post-update.sample @@ -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 diff --git a/hooks/pre-applypatch.sample b/hooks/pre-applypatch.sample new file mode 100644 index 0000000..4142082 --- /dev/null +++ b/hooks/pre-applypatch.sample @@ -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+"$@"} +: diff --git a/hooks/pre-commit.sample b/hooks/pre-commit.sample new file mode 100644 index 0000000..e144712 --- /dev/null +++ b/hooks/pre-commit.sample @@ -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 -- diff --git a/hooks/pre-merge-commit.sample b/hooks/pre-merge-commit.sample new file mode 100644 index 0000000..399eab1 --- /dev/null +++ b/hooks/pre-merge-commit.sample @@ -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" +: diff --git a/hooks/pre-push.sample b/hooks/pre-push.sample new file mode 100644 index 0000000..4ce688d --- /dev/null +++ b/hooks/pre-push.sample @@ -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: +# +# +# +# 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 &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/hooks/pre-rebase.sample b/hooks/pre-rebase.sample new file mode 100644 index 0000000..6cbef5c --- /dev/null +++ b/hooks/pre-rebase.sample @@ -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 diff --git a/hooks/pre-receive.sample b/hooks/pre-receive.sample new file mode 100644 index 0000000..a1fd29e --- /dev/null +++ b/hooks/pre-receive.sample @@ -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 diff --git a/hooks/prepare-commit-msg.sample b/hooks/prepare-commit-msg.sample new file mode 100644 index 0000000..10fa14c --- /dev/null +++ b/hooks/prepare-commit-msg.sample @@ -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 diff --git a/hooks/push-to-checkout.sample b/hooks/push-to-checkout.sample new file mode 100644 index 0000000..af5a0c0 --- /dev/null +++ b/hooks/push-to-checkout.sample @@ -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 &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 diff --git a/hooks/update.sample b/hooks/update.sample new file mode 100644 index 0000000..c4d426b --- /dev/null +++ b/hooks/update.sample @@ -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 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&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 &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 diff --git a/info/exclude b/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/info/exclude @@ -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] +# *~ diff --git a/objects/00/18e012a737d33450a3866cc205f97ea6e1fb70 b/objects/00/18e012a737d33450a3866cc205f97ea6e1fb70 new file mode 100644 index 0000000..ae11a34 Binary files /dev/null and b/objects/00/18e012a737d33450a3866cc205f97ea6e1fb70 differ diff --git a/objects/00/969a1c8a366694668216adb45f83d6f1e556d9 b/objects/00/969a1c8a366694668216adb45f83d6f1e556d9 new file mode 100644 index 0000000..899abe5 Binary files /dev/null and b/objects/00/969a1c8a366694668216adb45f83d6f1e556d9 differ diff --git a/objects/01/97aecc21afa11827bd9f862b617914ddb6aa40 b/objects/01/97aecc21afa11827bd9f862b617914ddb6aa40 new file mode 100644 index 0000000..fde7260 Binary files /dev/null and b/objects/01/97aecc21afa11827bd9f862b617914ddb6aa40 differ diff --git a/objects/01/f107b4bbf1f7b0d8a908c3871ba09b5b5cb798 b/objects/01/f107b4bbf1f7b0d8a908c3871ba09b5b5cb798 new file mode 100644 index 0000000..e5f35ac Binary files /dev/null and b/objects/01/f107b4bbf1f7b0d8a908c3871ba09b5b5cb798 differ diff --git a/objects/03/6329b0c56e81c756c754babc7cc440f1a4008e b/objects/03/6329b0c56e81c756c754babc7cc440f1a4008e new file mode 100644 index 0000000..7461744 Binary files /dev/null and b/objects/03/6329b0c56e81c756c754babc7cc440f1a4008e differ diff --git a/objects/06/3b77deaa237dc8a50e9567b5cd69f30d9f0596 b/objects/06/3b77deaa237dc8a50e9567b5cd69f30d9f0596 new file mode 100644 index 0000000..2005cda Binary files /dev/null and b/objects/06/3b77deaa237dc8a50e9567b5cd69f30d9f0596 differ diff --git a/objects/08/d9f4304a05d54afbdfda83a3a94723a8b1e51d b/objects/08/d9f4304a05d54afbdfda83a3a94723a8b1e51d new file mode 100644 index 0000000..157a0fd Binary files /dev/null and b/objects/08/d9f4304a05d54afbdfda83a3a94723a8b1e51d differ diff --git a/objects/09/01725297a1bbfbf20f6836b3ec2bb41edea3ee b/objects/09/01725297a1bbfbf20f6836b3ec2bb41edea3ee new file mode 100644 index 0000000..18f4241 Binary files /dev/null and b/objects/09/01725297a1bbfbf20f6836b3ec2bb41edea3ee differ diff --git a/objects/09/1d49b980f8fc0d822c24fdf21e8cb5f685462c b/objects/09/1d49b980f8fc0d822c24fdf21e8cb5f685462c new file mode 100644 index 0000000..91f0390 Binary files /dev/null and b/objects/09/1d49b980f8fc0d822c24fdf21e8cb5f685462c differ diff --git a/objects/0b/f635618c37d2f23740e65873826cac3677e0d4 b/objects/0b/f635618c37d2f23740e65873826cac3677e0d4 new file mode 100644 index 0000000..4318fca Binary files /dev/null and b/objects/0b/f635618c37d2f23740e65873826cac3677e0d4 differ diff --git a/objects/0c/a3fdbe7aa61df2722044646b749fe9e01cf1d1 b/objects/0c/a3fdbe7aa61df2722044646b749fe9e01cf1d1 new file mode 100644 index 0000000..bb15a38 --- /dev/null +++ b/objects/0c/a3fdbe7aa61df2722044646b749fe9e01cf1d1 @@ -0,0 +1,2 @@ +xMn0 >kEB UU7]&q %?OjO_x=AW'1znTO\@vffջ۞]R;E:;J bfHb9$P$[inaTV\WLJYlޗ>@ zRreϭjtyLH +l>-0LfA! l\a5N#X~Ip`n'$ .W~Z |9 7{%* H`Q0 \ No newline at end of file diff --git a/objects/0d/8d05cf4b2731f31c3bc337cc5c4cd8d945b996 b/objects/0d/8d05cf4b2731f31c3bc337cc5c4cd8d945b996 new file mode 100644 index 0000000..2dca083 Binary files /dev/null and b/objects/0d/8d05cf4b2731f31c3bc337cc5c4cd8d945b996 differ diff --git a/objects/0d/d875f2e70c9139168c120e1c74babad769420a b/objects/0d/d875f2e70c9139168c120e1c74babad769420a new file mode 100644 index 0000000..1bfb4ba Binary files /dev/null and b/objects/0d/d875f2e70c9139168c120e1c74babad769420a differ diff --git a/objects/0e/63c79653bb2a39520506d503e710bcaffc1417 b/objects/0e/63c79653bb2a39520506d503e710bcaffc1417 new file mode 100644 index 0000000..e33559f Binary files /dev/null and b/objects/0e/63c79653bb2a39520506d503e710bcaffc1417 differ diff --git a/objects/11/b30845f2269c04acb3640877884df3a875bb1f b/objects/11/b30845f2269c04acb3640877884df3a875bb1f new file mode 100644 index 0000000..26ef827 --- /dev/null +++ b/objects/11/b30845f2269c04acb3640877884df3a875bb1f @@ -0,0 +1,3 @@ +xWn09_Ad !nni [Z]ZF/X&q7o^r(%!?R,2ePʺU=;' +[!I)Qճͩ,JryG11[+y<4JCXΡQDIbb  )7M IDȽc +AdhAbYE'pmΔ̋,/~e~@0J ?GQlzBRIA_&|AS'pƒ3'FClqNЂY^w0u.>{j O4eOd 'O;`Q4בRu5e`ҍBMQ!Sc}mp#hP 9c^fF ZR-We_üBCP}Txc/~˾72nZR _#\ֱK5)r*!WflMJ&24dH*E(VA&skc;գD}RUo/2 \ No newline at end of file diff --git a/objects/13/464dfc30a13d4eafe09eedae8274926753b14b b/objects/13/464dfc30a13d4eafe09eedae8274926753b14b new file mode 100644 index 0000000..5637258 Binary files /dev/null and b/objects/13/464dfc30a13d4eafe09eedae8274926753b14b differ diff --git a/objects/13/e75caa45d5deb4e21d2596c54aa544328042c2 b/objects/13/e75caa45d5deb4e21d2596c54aa544328042c2 new file mode 100644 index 0000000..18e4b08 Binary files /dev/null and b/objects/13/e75caa45d5deb4e21d2596c54aa544328042c2 differ diff --git a/objects/15/941e4765075416c9e4431b715c7efc7ce9121f b/objects/15/941e4765075416c9e4431b715c7efc7ce9121f new file mode 100644 index 0000000..008b334 Binary files /dev/null and b/objects/15/941e4765075416c9e4431b715c7efc7ce9121f differ diff --git a/objects/15/abf09aeeb7da4d0901502db8dc0f7f59cda3d0 b/objects/15/abf09aeeb7da4d0901502db8dc0f7f59cda3d0 new file mode 100644 index 0000000..10995d5 Binary files /dev/null and b/objects/15/abf09aeeb7da4d0901502db8dc0f7f59cda3d0 differ diff --git a/objects/16/75dc905c352a0fa4baff8887bcbbac41e1d879 b/objects/16/75dc905c352a0fa4baff8887bcbbac41e1d879 new file mode 100644 index 0000000..174bcec Binary files /dev/null and b/objects/16/75dc905c352a0fa4baff8887bcbbac41e1d879 differ diff --git a/objects/16/bc9dc0e3c6d02d3bb278b8fed82abcfa018e53 b/objects/16/bc9dc0e3c6d02d3bb278b8fed82abcfa018e53 new file mode 100644 index 0000000..1356b1d Binary files /dev/null and b/objects/16/bc9dc0e3c6d02d3bb278b8fed82abcfa018e53 differ diff --git a/objects/16/df6898f7184173ea9e9dfa275ebe7d0087b91a b/objects/16/df6898f7184173ea9e9dfa275ebe7d0087b91a new file mode 100644 index 0000000..fe9252d Binary files /dev/null and b/objects/16/df6898f7184173ea9e9dfa275ebe7d0087b91a differ diff --git a/objects/18/dc72ae1711ffd4d91a2be202d66046a784d619 b/objects/18/dc72ae1711ffd4d91a2be202d66046a784d619 new file mode 100644 index 0000000..c85ae52 --- /dev/null +++ b/objects/18/dc72ae1711ffd4d91a2be202d66046a784d619 @@ -0,0 +1,3 @@ +xKN0DYF$N,!8DL$e n@-JOU>GdГyJF2fմrҒ)iCJM:n +qYE͈ڮZbRvrObI3Z?ިKʕ\#o]|>AQ,W1 +1ơL 8iWhx#([LWzkPj.T}3: {3 + XۿJ|u \ No newline at end of file diff --git a/objects/19/26543fb620b83766dd263faaf7d0f6e98b8957 b/objects/19/26543fb620b83766dd263faaf7d0f6e98b8957 new file mode 100644 index 0000000..318f682 Binary files /dev/null and b/objects/19/26543fb620b83766dd263faaf7d0f6e98b8957 differ diff --git a/objects/19/7f2162a0df4d79e37acbd7501cc39b085384ab b/objects/19/7f2162a0df4d79e37acbd7501cc39b085384ab new file mode 100644 index 0000000..c82f4d0 Binary files /dev/null and b/objects/19/7f2162a0df4d79e37acbd7501cc39b085384ab differ diff --git a/objects/1a/411b781453f92ed80f42ecf811a42e38694799 b/objects/1a/411b781453f92ed80f42ecf811a42e38694799 new file mode 100644 index 0000000..a04d339 Binary files /dev/null and b/objects/1a/411b781453f92ed80f42ecf811a42e38694799 differ diff --git a/objects/1a/e5ab6360d1e3316a82fe709394f7a0680fda66 b/objects/1a/e5ab6360d1e3316a82fe709394f7a0680fda66 new file mode 100644 index 0000000..1594f90 Binary files /dev/null and b/objects/1a/e5ab6360d1e3316a82fe709394f7a0680fda66 differ diff --git a/objects/1b/35f2b0573849f953f61c839f55aebd451279a5 b/objects/1b/35f2b0573849f953f61c839f55aebd451279a5 new file mode 100644 index 0000000..572b50b Binary files /dev/null and b/objects/1b/35f2b0573849f953f61c839f55aebd451279a5 differ diff --git a/objects/1c/23f64b9fab072568e57d04e462d4952ababe4e b/objects/1c/23f64b9fab072568e57d04e462d4952ababe4e new file mode 100644 index 0000000..36ee091 Binary files /dev/null and b/objects/1c/23f64b9fab072568e57d04e462d4952ababe4e differ diff --git a/objects/1d/96b2eb16ac213ec95ce83c4ec5459ccaed9d8e b/objects/1d/96b2eb16ac213ec95ce83c4ec5459ccaed9d8e new file mode 100644 index 0000000..d8f266d Binary files /dev/null and b/objects/1d/96b2eb16ac213ec95ce83c4ec5459ccaed9d8e differ diff --git a/objects/1f/0a9520f570b0a71a891813068037722fb6ff2c b/objects/1f/0a9520f570b0a71a891813068037722fb6ff2c new file mode 100644 index 0000000..14a36ca Binary files /dev/null and b/objects/1f/0a9520f570b0a71a891813068037722fb6ff2c differ diff --git a/objects/20/d263f34c7b1f293a46f794e3ae4295eed8f940 b/objects/20/d263f34c7b1f293a46f794e3ae4295eed8f940 new file mode 100644 index 0000000..d01fef8 Binary files /dev/null and b/objects/20/d263f34c7b1f293a46f794e3ae4295eed8f940 differ diff --git a/objects/21/e67a9c70c5cd3581c23c20b2fe04400a4b568b b/objects/21/e67a9c70c5cd3581c23c20b2fe04400a4b568b new file mode 100644 index 0000000..f091840 Binary files /dev/null and b/objects/21/e67a9c70c5cd3581c23c20b2fe04400a4b568b differ diff --git a/objects/22/7a85c21d34194ffa28cbaa7f16549dc34dc55e b/objects/22/7a85c21d34194ffa28cbaa7f16549dc34dc55e new file mode 100644 index 0000000..23d7dda Binary files /dev/null and b/objects/22/7a85c21d34194ffa28cbaa7f16549dc34dc55e differ diff --git a/objects/26/6b0ad2439f5949f5815e4c8db24337625a0c2b b/objects/26/6b0ad2439f5949f5815e4c8db24337625a0c2b new file mode 100644 index 0000000..9281f71 Binary files /dev/null and b/objects/26/6b0ad2439f5949f5815e4c8db24337625a0c2b differ diff --git a/objects/27/751dd67773a2793fb8b5a2a0ce21e969f0e757 b/objects/27/751dd67773a2793fb8b5a2a0ce21e969f0e757 new file mode 100644 index 0000000..a712ac2 Binary files /dev/null and b/objects/27/751dd67773a2793fb8b5a2a0ce21e969f0e757 differ diff --git a/objects/28/55067bd8da862e55347bb2d753cac07bf85e19 b/objects/28/55067bd8da862e55347bb2d753cac07bf85e19 new file mode 100644 index 0000000..28d64b0 Binary files /dev/null and b/objects/28/55067bd8da862e55347bb2d753cac07bf85e19 differ diff --git a/objects/29/a62c323080e0ce0f8ac8b40aed26980134a861 b/objects/29/a62c323080e0ce0f8ac8b40aed26980134a861 new file mode 100644 index 0000000..cc96737 Binary files /dev/null and b/objects/29/a62c323080e0ce0f8ac8b40aed26980134a861 differ diff --git a/objects/29/fc12f4700ff32c93b3a27b91f745f71af3deee b/objects/29/fc12f4700ff32c93b3a27b91f745f71af3deee new file mode 100644 index 0000000..5173f2a Binary files /dev/null and b/objects/29/fc12f4700ff32c93b3a27b91f745f71af3deee differ diff --git a/objects/2b/6c51c1f9fd500d724ba3ebacb4f1342b1b33b4 b/objects/2b/6c51c1f9fd500d724ba3ebacb4f1342b1b33b4 new file mode 100644 index 0000000..7468997 Binary files /dev/null and b/objects/2b/6c51c1f9fd500d724ba3ebacb4f1342b1b33b4 differ diff --git a/objects/2c/b483349a381109051fc983477d4752103049cb b/objects/2c/b483349a381109051fc983477d4752103049cb new file mode 100644 index 0000000..a4891ed Binary files /dev/null and b/objects/2c/b483349a381109051fc983477d4752103049cb differ diff --git a/objects/2d/b913315707f6e3fa1a5034fde2aede28256b2e b/objects/2d/b913315707f6e3fa1a5034fde2aede28256b2e new file mode 100644 index 0000000..5f9020b Binary files /dev/null and b/objects/2d/b913315707f6e3fa1a5034fde2aede28256b2e differ diff --git a/objects/2e/d0a1ee8c643c2964b2612eb5da5fa22db0ea84 b/objects/2e/d0a1ee8c643c2964b2612eb5da5fa22db0ea84 new file mode 100644 index 0000000..417b691 Binary files /dev/null and b/objects/2e/d0a1ee8c643c2964b2612eb5da5fa22db0ea84 differ diff --git a/objects/34/03f187f89a649930a49b42ff44fe51703398db b/objects/34/03f187f89a649930a49b42ff44fe51703398db new file mode 100644 index 0000000..a89e385 Binary files /dev/null and b/objects/34/03f187f89a649930a49b42ff44fe51703398db differ diff --git a/objects/35/51aada5455647a0426eca3a3325f1469c9f6bd b/objects/35/51aada5455647a0426eca3a3325f1469c9f6bd new file mode 100644 index 0000000..5428435 Binary files /dev/null and b/objects/35/51aada5455647a0426eca3a3325f1469c9f6bd differ diff --git a/objects/37/543b61307e17747abbbd624201623736b838f4 b/objects/37/543b61307e17747abbbd624201623736b838f4 new file mode 100644 index 0000000..69146a4 Binary files /dev/null and b/objects/37/543b61307e17747abbbd624201623736b838f4 differ diff --git a/objects/38/d611a3f4b39abd16be5846e22c7ba58fb95753 b/objects/38/d611a3f4b39abd16be5846e22c7ba58fb95753 new file mode 100644 index 0000000..9733282 Binary files /dev/null and b/objects/38/d611a3f4b39abd16be5846e22c7ba58fb95753 differ diff --git a/objects/3b/d6058b5aed7b6bbb760ea4d8e417b329a42ff0 b/objects/3b/d6058b5aed7b6bbb760ea4d8e417b329a42ff0 new file mode 100644 index 0000000..39f44a1 Binary files /dev/null and b/objects/3b/d6058b5aed7b6bbb760ea4d8e417b329a42ff0 differ diff --git a/objects/3b/fc3a1497205d2f72203719568c7dd8ed839478 b/objects/3b/fc3a1497205d2f72203719568c7dd8ed839478 new file mode 100644 index 0000000..d441f7a Binary files /dev/null and b/objects/3b/fc3a1497205d2f72203719568c7dd8ed839478 differ diff --git a/objects/3c/5ba072f118f73896c26bbdc5236b5a99d5778a b/objects/3c/5ba072f118f73896c26bbdc5236b5a99d5778a new file mode 100644 index 0000000..aac16b0 Binary files /dev/null and b/objects/3c/5ba072f118f73896c26bbdc5236b5a99d5778a differ diff --git a/objects/3d/cf0e3080efb48d6168f4d978690fdf989671f5 b/objects/3d/cf0e3080efb48d6168f4d978690fdf989671f5 new file mode 100644 index 0000000..aaa3b7b --- /dev/null +++ b/objects/3d/cf0e3080efb48d6168f4d978690fdf989671f5 @@ -0,0 +1 @@ +xmmO0yOq%eH4P FB9%=2RP; rv^F+& 9Ak^L@!Pa ?O^,0Z!v| ϔSаG WoLM a)҆Cݨ3gZr8G'0%ov[RYY=0oA5+C\{DK!uּJ҂9CJ-]Ώ&ǿN$y"Ҟ?\&|zB <9VV|1lOu{m^ \ No newline at end of file diff --git a/objects/3f/89177b956b024da6428c7fec9babaaf7bbc7b8 b/objects/3f/89177b956b024da6428c7fec9babaaf7bbc7b8 new file mode 100644 index 0000000..7f377f5 Binary files /dev/null and b/objects/3f/89177b956b024da6428c7fec9babaaf7bbc7b8 differ diff --git a/objects/40/4eded8d6da00a375db39d5f5f1f957a32039a3 b/objects/40/4eded8d6da00a375db39d5f5f1f957a32039a3 new file mode 100644 index 0000000..7f9ee4c Binary files /dev/null and b/objects/40/4eded8d6da00a375db39d5f5f1f957a32039a3 differ diff --git a/objects/42/08c81ee0530c76f8eab13469158eca8082a4e3 b/objects/42/08c81ee0530c76f8eab13469158eca8082a4e3 new file mode 100644 index 0000000..2614d9a Binary files /dev/null and b/objects/42/08c81ee0530c76f8eab13469158eca8082a4e3 differ diff --git a/objects/47/a913c38fd3ecbd9f14c462bcb928182d6f3a76 b/objects/47/a913c38fd3ecbd9f14c462bcb928182d6f3a76 new file mode 100644 index 0000000..31031ec Binary files /dev/null and b/objects/47/a913c38fd3ecbd9f14c462bcb928182d6f3a76 differ diff --git a/objects/49/beb2bc2cc1b36d0d070797a46416ca8728ae6b b/objects/49/beb2bc2cc1b36d0d070797a46416ca8728ae6b new file mode 100644 index 0000000..0e7d452 Binary files /dev/null and b/objects/49/beb2bc2cc1b36d0d070797a46416ca8728ae6b differ diff --git a/objects/4b/cb36361cbbda6764861212ecc3bded488cf2d3 b/objects/4b/cb36361cbbda6764861212ecc3bded488cf2d3 new file mode 100644 index 0000000..d2e565a Binary files /dev/null and b/objects/4b/cb36361cbbda6764861212ecc3bded488cf2d3 differ diff --git a/objects/4c/0ccf494cdc0218fa5dd2d033d0b816afea31f1 b/objects/4c/0ccf494cdc0218fa5dd2d033d0b816afea31f1 new file mode 100644 index 0000000..b1e66b0 Binary files /dev/null and b/objects/4c/0ccf494cdc0218fa5dd2d033d0b816afea31f1 differ diff --git a/objects/51/f07c8ab1fe806d5c8a6bf862be52c5756cc62a b/objects/51/f07c8ab1fe806d5c8a6bf862be52c5756cc62a new file mode 100644 index 0000000..4d1adc6 Binary files /dev/null and b/objects/51/f07c8ab1fe806d5c8a6bf862be52c5756cc62a differ diff --git a/objects/53/97050f2613a864e79d30f9c00eb0f564c22f46 b/objects/53/97050f2613a864e79d30f9c00eb0f564c22f46 new file mode 100644 index 0000000..1fb4fcf Binary files /dev/null and b/objects/53/97050f2613a864e79d30f9c00eb0f564c22f46 differ diff --git a/objects/54/01e5d8c554ccab6de1e443092adde1f57b63b3 b/objects/54/01e5d8c554ccab6de1e443092adde1f57b63b3 new file mode 100644 index 0000000..711d45b Binary files /dev/null and b/objects/54/01e5d8c554ccab6de1e443092adde1f57b63b3 differ diff --git a/objects/57/b2ce90a2a48e3818e593384f0d4825c4b30f8d b/objects/57/b2ce90a2a48e3818e593384f0d4825c4b30f8d new file mode 100644 index 0000000..e590d3c Binary files /dev/null and b/objects/57/b2ce90a2a48e3818e593384f0d4825c4b30f8d differ diff --git a/objects/57/be5d1fa8f8afdbbac6caf4264e4b72d44e6205 b/objects/57/be5d1fa8f8afdbbac6caf4264e4b72d44e6205 new file mode 100644 index 0000000..8a02aaa Binary files /dev/null and b/objects/57/be5d1fa8f8afdbbac6caf4264e4b72d44e6205 differ diff --git a/objects/5d/f2f32a5277ea10c5480a180872da4af84313be b/objects/5d/f2f32a5277ea10c5480a180872da4af84313be new file mode 100644 index 0000000..eeda05f Binary files /dev/null and b/objects/5d/f2f32a5277ea10c5480a180872da4af84313be differ diff --git a/objects/5f/eca4a3265c65926ebd053cc0f232be12edf87e b/objects/5f/eca4a3265c65926ebd053cc0f232be12edf87e new file mode 100644 index 0000000..3cd74e4 Binary files /dev/null and b/objects/5f/eca4a3265c65926ebd053cc0f232be12edf87e differ diff --git a/objects/65/75045efa1a4606c369de86f8de4bf36dee374f b/objects/65/75045efa1a4606c369de86f8de4bf36dee374f new file mode 100644 index 0000000..72cdb7c Binary files /dev/null and b/objects/65/75045efa1a4606c369de86f8de4bf36dee374f differ diff --git a/objects/68/817893dd14ec130e949d721c31376e0ff1dfe4 b/objects/68/817893dd14ec130e949d721c31376e0ff1dfe4 new file mode 100644 index 0000000..8b1bbec Binary files /dev/null and b/objects/68/817893dd14ec130e949d721c31376e0ff1dfe4 differ diff --git a/objects/6a/87fd13a7e8e48369d698ff5c94b1a695bb2c79 b/objects/6a/87fd13a7e8e48369d698ff5c94b1a695bb2c79 new file mode 100644 index 0000000..299c72e Binary files /dev/null and b/objects/6a/87fd13a7e8e48369d698ff5c94b1a695bb2c79 differ diff --git a/objects/6b/d73bede4b45f5a05e1e835aa646626f4e1df14 b/objects/6b/d73bede4b45f5a05e1e835aa646626f4e1df14 new file mode 100644 index 0000000..d8143c3 Binary files /dev/null and b/objects/6b/d73bede4b45f5a05e1e835aa646626f4e1df14 differ diff --git a/objects/6c/5b0715b414797856658efed91d70afb0bf9242 b/objects/6c/5b0715b414797856658efed91d70afb0bf9242 new file mode 100644 index 0000000..9984329 Binary files /dev/null and b/objects/6c/5b0715b414797856658efed91d70afb0bf9242 differ diff --git a/objects/6d/99a5070b9bd41e654d5faa302de4eb80c3650b b/objects/6d/99a5070b9bd41e654d5faa302de4eb80c3650b new file mode 100644 index 0000000..82ac568 Binary files /dev/null and b/objects/6d/99a5070b9bd41e654d5faa302de4eb80c3650b differ diff --git a/objects/6f/500bc379396bc66ddf876744b228da9c25cf0a b/objects/6f/500bc379396bc66ddf876744b228da9c25cf0a new file mode 100644 index 0000000..053767c Binary files /dev/null and b/objects/6f/500bc379396bc66ddf876744b228da9c25cf0a differ diff --git a/objects/72/3f507028218df8e83bbac3de52a71b441c9b01 b/objects/72/3f507028218df8e83bbac3de52a71b441c9b01 new file mode 100644 index 0000000..22e040a --- /dev/null +++ b/objects/72/3f507028218df8e83bbac3de52a71b441c9b01 @@ -0,0 +1 @@ +x+)JMU05e040031QHO͏O.-*J+K.,564+Lfh4_U]u0 z\[) \ No newline at end of file diff --git a/objects/72/64b5b1d286d1cb476f531ccf42152caebe43a8 b/objects/72/64b5b1d286d1cb476f531ccf42152caebe43a8 new file mode 100644 index 0000000..3a4273a Binary files /dev/null and b/objects/72/64b5b1d286d1cb476f531ccf42152caebe43a8 differ diff --git a/objects/74/066b0085e97dea3830c0129618338c974df623 b/objects/74/066b0085e97dea3830c0129618338c974df623 new file mode 100644 index 0000000..fb32ee7 Binary files /dev/null and b/objects/74/066b0085e97dea3830c0129618338c974df623 differ diff --git a/objects/74/49af9499e9c72b669397f6d367c18927e14887 b/objects/74/49af9499e9c72b669397f6d367c18927e14887 new file mode 100644 index 0000000..85f91f9 Binary files /dev/null and b/objects/74/49af9499e9c72b669397f6d367c18927e14887 differ diff --git a/objects/78/3b2fcb4d15e002f8452bb3b3245498a9ad7b1b b/objects/78/3b2fcb4d15e002f8452bb3b3245498a9ad7b1b new file mode 100644 index 0000000..eb8dbad Binary files /dev/null and b/objects/78/3b2fcb4d15e002f8452bb3b3245498a9ad7b1b differ diff --git a/objects/78/f8319947ea18b2c0bf944c357f7bfbdaeb0775 b/objects/78/f8319947ea18b2c0bf944c357f7bfbdaeb0775 new file mode 100644 index 0000000..4904336 Binary files /dev/null and b/objects/78/f8319947ea18b2c0bf944c357f7bfbdaeb0775 differ diff --git a/objects/80/f26500d42f4bfefa3097db95aaab61568049e9 b/objects/80/f26500d42f4bfefa3097db95aaab61568049e9 new file mode 100644 index 0000000..fe3d2eb --- /dev/null +++ b/objects/80/f26500d42f4bfefa3097db95aaab61568049e9 @@ -0,0 +1,4 @@ +xRMOQ +B, B ZM!fhkKI+. cqƘbH\@Ə`\ԨVMPYy̼Q7isλ{}32q545 /fq̸"FU0Ht s Fy69[繐`Aj}18Wr2؇\* +Pu*@=4yofh~*ov8p1ߩ90Qt9n ^ƢPóU.8/~XxC(i>xv3N®X$"FFd)#OM,0kXO%3%E;kfzEZ! 6-^"J5.F)F5E}-B)2%|gxE]o;a5QoB5:}? ]{a(5Lx VnG7{(%)a[&:Uce[)Ԧflm|Xt4Oo=?~/>,:8Q [A111B({IgOԖ^.UPod03MLK_m)qcꊒGEP +q'ƃ G9E$] x9h䠉fZ8h堍Q7O Ie MI Wn&e<5&=G}^ii4XdTx$Z^6 AlB>Q? 5P\H؝Õ(d,k?&oGb=ֵÒs \ No newline at end of file diff --git a/objects/81/37a42f5b5a7b7ac168eed9b03547232e0ab533 b/objects/81/37a42f5b5a7b7ac168eed9b03547232e0ab533 new file mode 100644 index 0000000..8fa00c9 Binary files /dev/null and b/objects/81/37a42f5b5a7b7ac168eed9b03547232e0ab533 differ diff --git a/objects/81/f720cab71fa5f9192d5e11c64025d84dc5ab16 b/objects/81/f720cab71fa5f9192d5e11c64025d84dc5ab16 new file mode 100644 index 0000000..48054b1 Binary files /dev/null and b/objects/81/f720cab71fa5f9192d5e11c64025d84dc5ab16 differ diff --git a/objects/83/eca436a7df93c3b503826ff3236dee9faf3d5c b/objects/83/eca436a7df93c3b503826ff3236dee9faf3d5c new file mode 100644 index 0000000..9e906f2 Binary files /dev/null and b/objects/83/eca436a7df93c3b503826ff3236dee9faf3d5c differ diff --git a/objects/83/fdf4be28807af8f8602755b8bcab39cdddee8f b/objects/83/fdf4be28807af8f8602755b8bcab39cdddee8f new file mode 100644 index 0000000..0b908f4 Binary files /dev/null and b/objects/83/fdf4be28807af8f8602755b8bcab39cdddee8f differ diff --git a/objects/88/b6bd40f742d4dc7ab15710f4c2aa61f663d22e b/objects/88/b6bd40f742d4dc7ab15710f4c2aa61f663d22e new file mode 100644 index 0000000..de943b4 Binary files /dev/null and b/objects/88/b6bd40f742d4dc7ab15710f4c2aa61f663d22e differ diff --git a/objects/89/72f61b0bebb961e726aab26d76588bf4743734 b/objects/89/72f61b0bebb961e726aab26d76588bf4743734 new file mode 100644 index 0000000..1da5009 Binary files /dev/null and b/objects/89/72f61b0bebb961e726aab26d76588bf4743734 differ diff --git a/objects/8e/c661d02937bd14e83695f1b5af474abf420bbd b/objects/8e/c661d02937bd14e83695f1b5af474abf420bbd new file mode 100644 index 0000000..47c0907 Binary files /dev/null and b/objects/8e/c661d02937bd14e83695f1b5af474abf420bbd differ diff --git a/objects/8f/2021283addea5ef72568bb69cb0654b0f22bf4 b/objects/8f/2021283addea5ef72568bb69cb0654b0f22bf4 new file mode 100644 index 0000000..8eccc5c Binary files /dev/null and b/objects/8f/2021283addea5ef72568bb69cb0654b0f22bf4 differ diff --git a/objects/93/1d428f2075d22ebf77147f4fb8ca4dd2e9484b b/objects/93/1d428f2075d22ebf77147f4fb8ca4dd2e9484b new file mode 100644 index 0000000..f091db8 Binary files /dev/null and b/objects/93/1d428f2075d22ebf77147f4fb8ca4dd2e9484b differ diff --git a/objects/93/eff6946c442918fce2b6de35f3bc0a0f8eb549 b/objects/93/eff6946c442918fce2b6de35f3bc0a0f8eb549 new file mode 100644 index 0000000..50aa6d0 Binary files /dev/null and b/objects/93/eff6946c442918fce2b6de35f3bc0a0f8eb549 differ diff --git a/objects/94/0f5622d28d270ba8e6eaa0ed0c7ba0deea6bd5 b/objects/94/0f5622d28d270ba8e6eaa0ed0c7ba0deea6bd5 new file mode 100644 index 0000000..5c73bea Binary files /dev/null and b/objects/94/0f5622d28d270ba8e6eaa0ed0c7ba0deea6bd5 differ diff --git a/objects/97/2a66be458c8043a466a49bc7ab624d527e6f8e b/objects/97/2a66be458c8043a466a49bc7ab624d527e6f8e new file mode 100644 index 0000000..485e0d1 Binary files /dev/null and b/objects/97/2a66be458c8043a466a49bc7ab624d527e6f8e differ diff --git a/objects/98/f5f64c54a4a431d0cb29fe35bc68e8b7c04746 b/objects/98/f5f64c54a4a431d0cb29fe35bc68e8b7c04746 new file mode 100644 index 0000000..7f4afab Binary files /dev/null and b/objects/98/f5f64c54a4a431d0cb29fe35bc68e8b7c04746 differ diff --git a/objects/99/ded1393cfbea9ab9b258d8af53e8a32401ad0b b/objects/99/ded1393cfbea9ab9b258d8af53e8a32401ad0b new file mode 100644 index 0000000..e277a60 Binary files /dev/null and b/objects/99/ded1393cfbea9ab9b258d8af53e8a32401ad0b differ diff --git a/objects/9b/63a3e4e61e79325864ac2b19e3e5dd1614bb6c b/objects/9b/63a3e4e61e79325864ac2b19e3e5dd1614bb6c new file mode 100644 index 0000000..25f712f Binary files /dev/null and b/objects/9b/63a3e4e61e79325864ac2b19e3e5dd1614bb6c differ diff --git a/objects/9c/b002b280f99fa0430993a44b7017fd56659bcd b/objects/9c/b002b280f99fa0430993a44b7017fd56659bcd new file mode 100644 index 0000000..5b7f27d --- /dev/null +++ b/objects/9c/b002b280f99fa0430993a44b7017fd56659bcd @@ -0,0 +1 @@ +x}j0{ާXtjSCQ YUAIDjo_2|;S7Ʒ!x/R:9#"wMV ,T)8 >;ѵ7IH!ų{X_Ɲ Tb+sL%|U~{&a5,HLnkAZm'P0(v1|PR%#S_$ \ No newline at end of file diff --git a/objects/9d/10fd3bc1408128d27339fe54ca82a44781bc80 b/objects/9d/10fd3bc1408128d27339fe54ca82a44781bc80 new file mode 100644 index 0000000..3a40278 Binary files /dev/null and b/objects/9d/10fd3bc1408128d27339fe54ca82a44781bc80 differ diff --git a/objects/9d/97142b8a3687df8e51d38fcc76fd63ea36723d b/objects/9d/97142b8a3687df8e51d38fcc76fd63ea36723d new file mode 100644 index 0000000..6094479 Binary files /dev/null and b/objects/9d/97142b8a3687df8e51d38fcc76fd63ea36723d differ diff --git a/objects/9e/98a9d39aaf0cc2e97142e391ffab9e20ae43cc b/objects/9e/98a9d39aaf0cc2e97142e391ffab9e20ae43cc new file mode 100644 index 0000000..1be51fc Binary files /dev/null and b/objects/9e/98a9d39aaf0cc2e97142e391ffab9e20ae43cc differ diff --git a/objects/a1/3d0f22b051f29fd73bf95b4104eb3490c79c7c b/objects/a1/3d0f22b051f29fd73bf95b4104eb3490c79c7c new file mode 100644 index 0000000..e29ec6d --- /dev/null +++ b/objects/a1/3d0f22b051f29fd73bf95b4104eb3490c79c7c @@ -0,0 +1,2 @@ +xQN Ef@ th+3zM'9O܈i`r'}c^Ɓ8Fk4&*q!hQGeqȠ6x+;٧%E[gig{1SaOX=Z`'>-~MnP& +)o=Z= aku~f \ No newline at end of file diff --git a/objects/a3/1260695090139f6c86eaa31a91fb098e279716 b/objects/a3/1260695090139f6c86eaa31a91fb098e279716 new file mode 100644 index 0000000..15a4c9e Binary files /dev/null and b/objects/a3/1260695090139f6c86eaa31a91fb098e279716 differ diff --git a/objects/a3/644f2eb6be8f415f6762385627681e4693f6c0 b/objects/a3/644f2eb6be8f415f6762385627681e4693f6c0 new file mode 100644 index 0000000..7293913 --- /dev/null +++ b/objects/a3/644f2eb6be8f415f6762385627681e4693f6c0 @@ -0,0 +1,2 @@ +x] +0})rel"xmi-u x>fZ,ژ$;묉q] 4$" h祉bCWLlĞ92~Dct]>CQmձeZ#/~/QG! \ No newline at end of file diff --git a/objects/a4/20193db2ff4969ddf84cb2c0d6bf5ee4f65573 b/objects/a4/20193db2ff4969ddf84cb2c0d6bf5ee4f65573 new file mode 100644 index 0000000..2ca764e Binary files /dev/null and b/objects/a4/20193db2ff4969ddf84cb2c0d6bf5ee4f65573 differ diff --git a/objects/a5/e278bd874da96085ac0c21774ae8e900e8ce46 b/objects/a5/e278bd874da96085ac0c21774ae8e900e8ce46 new file mode 100644 index 0000000..0285e86 Binary files /dev/null and b/objects/a5/e278bd874da96085ac0c21774ae8e900e8ce46 differ diff --git a/objects/a6/025783d6cd5c4ca7711625d275baa8c8541aca b/objects/a6/025783d6cd5c4ca7711625d275baa8c8541aca new file mode 100644 index 0000000..c4bfcfb Binary files /dev/null and b/objects/a6/025783d6cd5c4ca7711625d275baa8c8541aca differ diff --git a/objects/a6/06ee845896b91917b7b0bd617ee3782c1afe1c b/objects/a6/06ee845896b91917b7b0bd617ee3782c1afe1c new file mode 100644 index 0000000..8c03af7 Binary files /dev/null and b/objects/a6/06ee845896b91917b7b0bd617ee3782c1afe1c differ diff --git a/objects/a7/8faddbd33f10498d7c73d465749a1cc9152c85 b/objects/a7/8faddbd33f10498d7c73d465749a1cc9152c85 new file mode 100644 index 0000000..1858baf Binary files /dev/null and b/objects/a7/8faddbd33f10498d7c73d465749a1cc9152c85 differ diff --git a/objects/a9/2f53dd794974b87173501f402b2f22807017f9 b/objects/a9/2f53dd794974b87173501f402b2f22807017f9 new file mode 100644 index 0000000..756a1ae Binary files /dev/null and b/objects/a9/2f53dd794974b87173501f402b2f22807017f9 differ diff --git a/objects/ac/6807e904bd8196a14e0972d6fa6d7410c76495 b/objects/ac/6807e904bd8196a14e0972d6fa6d7410c76495 new file mode 100644 index 0000000..015efff Binary files /dev/null and b/objects/ac/6807e904bd8196a14e0972d6fa6d7410c76495 differ diff --git a/objects/ac/7eb35c6bb466c3ddf0e5aeefda47acf0d4e200 b/objects/ac/7eb35c6bb466c3ddf0e5aeefda47acf0d4e200 new file mode 100644 index 0000000..e02f81a Binary files /dev/null and b/objects/ac/7eb35c6bb466c3ddf0e5aeefda47acf0d4e200 differ diff --git a/objects/b2/4fd143cf3deef1d9545ed909f923bbdb878004 b/objects/b2/4fd143cf3deef1d9545ed909f923bbdb878004 new file mode 100644 index 0000000..d5d0a42 Binary files /dev/null and b/objects/b2/4fd143cf3deef1d9545ed909f923bbdb878004 differ diff --git a/objects/b5/3dfb7021e7091a8a713827aa39f32abcfc9052 b/objects/b5/3dfb7021e7091a8a713827aa39f32abcfc9052 new file mode 100644 index 0000000..944fc57 --- /dev/null +++ b/objects/b5/3dfb7021e7091a8a713827aa39f32abcfc9052 @@ -0,0 +1,2 @@ +xAN0 EYM!Ėn8&U0錘MeVNVf nP[ϚgH3zBYq@ +Nu~!Ll FϭS* V9YPbbxn[7Z7^C.+/|r@e~vm3RN|_16V 3 IŔJJtPp<1槿p^o7@H{qCTf(/˙_G \ No newline at end of file diff --git a/objects/b6/5b21968b70dbcad6db8e6171a14ee91cf5819a b/objects/b6/5b21968b70dbcad6db8e6171a14ee91cf5819a new file mode 100644 index 0000000..b35d8d6 Binary files /dev/null and b/objects/b6/5b21968b70dbcad6db8e6171a14ee91cf5819a differ diff --git a/objects/bc/3f8297dde86a7115ebd11c86c59f0e0cb77922 b/objects/bc/3f8297dde86a7115ebd11c86c59f0e0cb77922 new file mode 100644 index 0000000..4f19af3 --- /dev/null +++ b/objects/bc/3f8297dde86a7115ebd11c86c59f0e0cb77922 @@ -0,0 +1,4 @@ +xVˎ0횯 Y<«RzꢪCmk#8@ff٬=e~<׌K g,k$-mA^>œ=DXJW\z}F=:ɲ*V֑@pN* oP i؁>vz IQ3C{U|(]w ,Bu"YF%~0 `f6}l,6:܋m=s'Dq;_&#L omQ2N4z:~g= ++8blYw~eu,SA&'g@qXVRc*Hǝ59xrrഖN"Aj=R}s<Q)/@I(MT7LlDBKXeEo2M5ܣK+}^H^'2x'OuL! +XmFIv.c>x}Fǵjr"^KJ0'xz~&5\{ezlDn" +NH6z&n^U@UL v`eg\VYY|\2Q "kE݈s]2DzdT_O78U` WDoӱ"K UF 9<g \ No newline at end of file diff --git a/objects/bc/467a78b7f09af6d7b6bc125b8e508ba236a491 b/objects/bc/467a78b7f09af6d7b6bc125b8e508ba236a491 new file mode 100644 index 0000000..06a1e6a Binary files /dev/null and b/objects/bc/467a78b7f09af6d7b6bc125b8e508ba236a491 differ diff --git a/objects/be/ea7951db0b01d282ae6271f062362bd32101c8 b/objects/be/ea7951db0b01d282ae6271f062362bd32101c8 new file mode 100644 index 0000000..e9a78cf Binary files /dev/null and b/objects/be/ea7951db0b01d282ae6271f062362bd32101c8 differ diff --git a/objects/c0/22ae2188b6e312c03307e320f8fbccc1149ef6 b/objects/c0/22ae2188b6e312c03307e320f8fbccc1149ef6 new file mode 100644 index 0000000..fd1bff5 Binary files /dev/null and b/objects/c0/22ae2188b6e312c03307e320f8fbccc1149ef6 differ diff --git a/objects/c0/27bb997f12cfa018c0a03c81d698aa16bbecac b/objects/c0/27bb997f12cfa018c0a03c81d698aa16bbecac new file mode 100644 index 0000000..772de9a Binary files /dev/null and b/objects/c0/27bb997f12cfa018c0a03c81d698aa16bbecac differ diff --git a/objects/c0/ae039dd924e7ca9205422eb6889724f765971f b/objects/c0/ae039dd924e7ca9205422eb6889724f765971f new file mode 100644 index 0000000..16b72e7 Binary files /dev/null and b/objects/c0/ae039dd924e7ca9205422eb6889724f765971f differ diff --git a/objects/c8/498969fb575c747ba49de4c0dec4ac4dead8bf b/objects/c8/498969fb575c747ba49de4c0dec4ac4dead8bf new file mode 100644 index 0000000..83042b1 Binary files /dev/null and b/objects/c8/498969fb575c747ba49de4c0dec4ac4dead8bf differ diff --git a/objects/c9/a10b20b67e26879e18033e81fd47d71c81aa1f b/objects/c9/a10b20b67e26879e18033e81fd47d71c81aa1f new file mode 100644 index 0000000..845c9de Binary files /dev/null and b/objects/c9/a10b20b67e26879e18033e81fd47d71c81aa1f differ diff --git a/objects/cb/cc586dbcc1cec3f0fca7e14e11eb56e4186248 b/objects/cb/cc586dbcc1cec3f0fca7e14e11eb56e4186248 new file mode 100644 index 0000000..d4a7720 Binary files /dev/null and b/objects/cb/cc586dbcc1cec3f0fca7e14e11eb56e4186248 differ diff --git a/objects/cc/b81a7ff1bc5c9c12866f6ec039865e8253ed79 b/objects/cc/b81a7ff1bc5c9c12866f6ec039865e8253ed79 new file mode 100644 index 0000000..3de5e92 Binary files /dev/null and b/objects/cc/b81a7ff1bc5c9c12866f6ec039865e8253ed79 differ diff --git a/objects/ce/51fdc78437199f2efc6b2590727b7105cea975 b/objects/ce/51fdc78437199f2efc6b2590727b7105cea975 new file mode 100644 index 0000000..27ccdfa Binary files /dev/null and b/objects/ce/51fdc78437199f2efc6b2590727b7105cea975 differ diff --git a/objects/cf/414e23b833274a9b681178dbc53bd96cb97d8e b/objects/cf/414e23b833274a9b681178dbc53bd96cb97d8e new file mode 100644 index 0000000..cafe3a3 Binary files /dev/null and b/objects/cf/414e23b833274a9b681178dbc53bd96cb97d8e differ diff --git a/objects/cf/994cef0dbc79c6db83917714f00b97ed78681e b/objects/cf/994cef0dbc79c6db83917714f00b97ed78681e new file mode 100644 index 0000000..93700c2 Binary files /dev/null and b/objects/cf/994cef0dbc79c6db83917714f00b97ed78681e differ diff --git a/objects/d1/c7d9424f0c3dd0afa7afd21301577d212b6c42 b/objects/d1/c7d9424f0c3dd0afa7afd21301577d212b6c42 new file mode 100644 index 0000000..9a6cfe2 Binary files /dev/null and b/objects/d1/c7d9424f0c3dd0afa7afd21301577d212b6c42 differ diff --git a/objects/d3/33490b585a159db05b0d769a1e3e50b232ad60 b/objects/d3/33490b585a159db05b0d769a1e3e50b232ad60 new file mode 100644 index 0000000..02fe72b Binary files /dev/null and b/objects/d3/33490b585a159db05b0d769a1e3e50b232ad60 differ diff --git a/objects/d3/537213cd458398cf6e8b0a1edfc43054a8aaef b/objects/d3/537213cd458398cf6e8b0a1edfc43054a8aaef new file mode 100644 index 0000000..ceca570 Binary files /dev/null and b/objects/d3/537213cd458398cf6e8b0a1edfc43054a8aaef differ diff --git a/objects/d3/8820b0f197ce3d5e32f71278990c505fe807f1 b/objects/d3/8820b0f197ce3d5e32f71278990c505fe807f1 new file mode 100644 index 0000000..8899af4 Binary files /dev/null and b/objects/d3/8820b0f197ce3d5e32f71278990c505fe807f1 differ diff --git a/objects/d3/e4454dc662621020b0ba3ce3c4e434c75ffeab b/objects/d3/e4454dc662621020b0ba3ce3c4e434c75ffeab new file mode 100644 index 0000000..635ff75 Binary files /dev/null and b/objects/d3/e4454dc662621020b0ba3ce3c4e434c75ffeab differ diff --git a/objects/d3/f570a855ce43e3fce6b432d7df955ec1329bf9 b/objects/d3/f570a855ce43e3fce6b432d7df955ec1329bf9 new file mode 100644 index 0000000..193b92b Binary files /dev/null and b/objects/d3/f570a855ce43e3fce6b432d7df955ec1329bf9 differ diff --git a/objects/d4/a86b0d0c7f20e5143567a17d6962ab8c9e2a77 b/objects/d4/a86b0d0c7f20e5143567a17d6962ab8c9e2a77 new file mode 100644 index 0000000..af2291c Binary files /dev/null and b/objects/d4/a86b0d0c7f20e5143567a17d6962ab8c9e2a77 differ diff --git a/objects/d6/7e22a0db4913037cfa846d6242b5eaec2725b9 b/objects/d6/7e22a0db4913037cfa846d6242b5eaec2725b9 new file mode 100644 index 0000000..848940d Binary files /dev/null and b/objects/d6/7e22a0db4913037cfa846d6242b5eaec2725b9 differ diff --git a/objects/d8/8054a1259bf0c8b3813beb4ce8845a9720b045 b/objects/d8/8054a1259bf0c8b3813beb4ce8845a9720b045 new file mode 100644 index 0000000..f8ddf1e Binary files /dev/null and b/objects/d8/8054a1259bf0c8b3813beb4ce8845a9720b045 differ diff --git a/objects/d9/603574337b2f89b3ae4b752df8da8031609112 b/objects/d9/603574337b2f89b3ae4b752df8da8031609112 new file mode 100644 index 0000000..0e5d0d5 Binary files /dev/null and b/objects/d9/603574337b2f89b3ae4b752df8da8031609112 differ diff --git a/objects/da/fd7f874da53f6db065ac39ea5de012c7da8d8a b/objects/da/fd7f874da53f6db065ac39ea5de012c7da8d8a new file mode 100644 index 0000000..10d39fb Binary files /dev/null and b/objects/da/fd7f874da53f6db065ac39ea5de012c7da8d8a differ diff --git a/objects/de/33e483b3cc5c4f8b5fe2d61b67de53fef5b84a b/objects/de/33e483b3cc5c4f8b5fe2d61b67de53fef5b84a new file mode 100644 index 0000000..4c43f65 --- /dev/null +++ b/objects/de/33e483b3cc5c4f8b5fe2d61b67de53fef5b84a @@ -0,0 +1 @@ +xeAr0 E)tɢY@+ IwT'y>}L?b41@> ڐRzEŤ帞N 5JJpN'y(Id1txd@޴56G,R!1z%ԅ7fSh9 )l!njkSofd +n{3Fx|Dk \ No newline at end of file diff --git a/objects/ec/58f763fedd4509d5f5846e9f18322dd9ac69d6 b/objects/ec/58f763fedd4509d5f5846e9f18322dd9ac69d6 new file mode 100644 index 0000000..7b08447 Binary files /dev/null and b/objects/ec/58f763fedd4509d5f5846e9f18322dd9ac69d6 differ diff --git a/objects/ed/c58504b3b6ab898fc0ccb3b8d5d5a3db92e263 b/objects/ed/c58504b3b6ab898fc0ccb3b8d5d5a3db92e263 new file mode 100644 index 0000000..bb6dbcc Binary files /dev/null and b/objects/ed/c58504b3b6ab898fc0ccb3b8d5d5a3db92e263 differ diff --git a/objects/f4/e0cfb79f2d893ece1e6701932cc2bf3018560d b/objects/f4/e0cfb79f2d893ece1e6701932cc2bf3018560d new file mode 100644 index 0000000..7163a24 --- /dev/null +++ b/objects/f4/e0cfb79f2d893ece1e6701932cc2bf3018560d @@ -0,0 +1,3 @@ +xQN!D} =0$96 .b<_TKUn\U!%9~MJ,^Ҫ2j\#+w=EfR\py)P,N :x_9KO};W/OW#ђf +|[ +w>rW3=NvSko}ZL \ No newline at end of file diff --git a/objects/f7/165c9c57cdb38471d9b020d3f01b15a0a5b713 b/objects/f7/165c9c57cdb38471d9b020d3f01b15a0a5b713 new file mode 100644 index 0000000..e85b149 Binary files /dev/null and b/objects/f7/165c9c57cdb38471d9b020d3f01b15a0a5b713 differ diff --git a/objects/f7/2270f42a2745971a9fd6f014f7139281546cb9 b/objects/f7/2270f42a2745971a9fd6f014f7139281546cb9 new file mode 100644 index 0000000..2306e21 Binary files /dev/null and b/objects/f7/2270f42a2745971a9fd6f014f7139281546cb9 differ diff --git a/objects/f8/c17a7d291314d28c75ed7f2bf6c637d269c3aa b/objects/f8/c17a7d291314d28c75ed7f2bf6c637d269c3aa new file mode 100644 index 0000000..07f6d93 Binary files /dev/null and b/objects/f8/c17a7d291314d28c75ed7f2bf6c637d269c3aa differ diff --git a/objects/fa/89134e633c0bcc14f578a7bf1332c71db5ad2b b/objects/fa/89134e633c0bcc14f578a7bf1332c71db5ad2b new file mode 100644 index 0000000..57f5e40 Binary files /dev/null and b/objects/fa/89134e633c0bcc14f578a7bf1332c71db5ad2b differ diff --git a/objects/fd/423acfdfdf9448bf089a7d4bbf621f5766ad68 b/objects/fd/423acfdfdf9448bf089a7d4bbf621f5766ad68 new file mode 100644 index 0000000..f24ad57 Binary files /dev/null and b/objects/fd/423acfdfdf9448bf089a7d4bbf621f5766ad68 differ diff --git a/refs/heads/full_A b/refs/heads/full_A new file mode 100644 index 0000000..77c7b58 --- /dev/null +++ b/refs/heads/full_A @@ -0,0 +1 @@ +197f2162a0df4d79e37acbd7501cc39b085384ab diff --git a/refs/heads/full_B b/refs/heads/full_B new file mode 100644 index 0000000..eed3029 --- /dev/null +++ b/refs/heads/full_B @@ -0,0 +1 @@ +ac6807e904bd8196a14e0972d6fa6d7410c76495 diff --git a/refs/heads/main b/refs/heads/main new file mode 100644 index 0000000..6c596e0 --- /dev/null +++ b/refs/heads/main @@ -0,0 +1 @@ +ea5acf6965202d6c5f7e9cb53026bd6c1b0b908e diff --git a/refs/heads/new_sim b/refs/heads/new_sim new file mode 100644 index 0000000..77c7b58 --- /dev/null +++ b/refs/heads/new_sim @@ -0,0 +1 @@ +197f2162a0df4d79e37acbd7501cc39b085384ab diff --git a/refs/heads/working b/refs/heads/working new file mode 100644 index 0000000..3602a28 --- /dev/null +++ b/refs/heads/working @@ -0,0 +1 @@ +0ca3fdbe7aa61df2722044646b749fe9e01cf1d1