Coverage for physped/core/pedestrian_simulator.py: 84%
87 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-01 09:28 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-01 09:28 +0000
1import logging
2from pathlib import Path
4import numpy as np
5import pandas as pd
6from tqdm import tqdm
7from tqdm.contrib.logging import logging_redirect_tqdm
9from physped.core.langevin_model import LangevinModel
10from physped.core.parametrize_potential import get_grid_indices
11from physped.core.pedestrian_initializer import (
12 get_initial_dynamics,
13 initialize_pedestrians,
14)
15from physped.core.piecewise_potential import PiecewisePotential
16from physped.utils.functions import (
17 cartesian_to_polar_coordinates,
18 periodic_angular_conditions,
19)
21log = logging.getLogger(__name__)
24def simulate_pedestrians(
25 piecewise_potential: PiecewisePotential,
26 config: dict,
27) -> pd.DataFrame:
28 parameters = config.params
30 # if config.read.simulated_trajectories:
31 # return read_simulated_trajectories_from_file(config)
33 log.info("Simulate trajectories using the piecewise potential")
34 initial_dynamics = get_initial_dynamics(config)
35 origins = initialize_pedestrians(initial_dynamics)
37 # !Make sure that the potential is defined for the states we sample
38 # states_to_sample_from = potential_defined_at_slow_state(
39 # states_to_sample_from, piecewise_potential
40 # )
41 # states_to_sample_from = states_to_sample_from[
42 # states_to_sample_from["potential_defined"]
43 # ].copy()
45 evaluation_time = np.arange(
46 parameters.simulation.start,
47 parameters.simulation.end,
48 parameters.simulation.step,
49 )
50 trajectories = []
52 model = LangevinModel(piecewise_potential, parameters)
53 n_frames_back = config.params.fps // 2 # Go 0.5 second back
54 with logging_redirect_tqdm():
55 for starting_state in tqdm(
56 origins[:, :11],
57 desc="Simulating trajectories",
58 unit="trajs",
59 total=origins.shape[0],
60 miniters=1,
61 ):
62 first_trajectory_piece = simulate_trajectory_piece(
63 model, starting_state, evaluation_time, 0
64 )
65 pid = int(first_trajectory_piece.iloc[0]["Pid"])
66 trajectory_pieces = [first_trajectory_piece]
68 while check_restarting_conditions(
69 trajectory_pieces[-1], n_frames_back, piecewise_potential
70 ):
71 last_trajectory_piece = trajectory_pieces[-1]
72 no_last_trajectory_piece = int(
73 last_trajectory_piece.iloc[0]["piece_id"]
74 )
75 restarting_state = last_trajectory_piece.iloc[-n_frames_back]
76 restarting_time = restarting_state["t"]
77 log.info(
78 "Pid %s piece %s: Reverting %s frames -> t = %.2f.",
79 int(pid),
80 no_last_trajectory_piece,
81 n_frames_back,
82 restarting_time,
83 )
85 last_trajectory_piece = last_trajectory_piece.iloc[
86 : -n_frames_back - 1
87 ] # strip frames from last trajectory piece
88 trajectory_pieces[-1] = last_trajectory_piece
89 frame_to_restart_from = int(restarting_state["k"])
91 new_evaluation_time = evaluation_time[frame_to_restart_from:]
93 no_new_traj_piece = no_last_trajectory_piece + 1
94 new_trajectory_piece = simulate_trajectory_piece(
95 model,
96 restarting_state,
97 new_evaluation_time,
98 no_new_traj_piece,
99 )
100 trajectory_pieces.append(new_trajectory_piece)
102 trajectory_pieces = pd.concat(trajectory_pieces)
103 trajectories.append(trajectory_pieces)
105 trajectories = pd.concat(trajectories).dropna()
106 trajectories["rf"], trajectories["thetaf"] = (
107 cartesian_to_polar_coordinates(trajectories.uf, trajectories.vf)
108 )
109 trajectories["rs"], trajectories["thetas"] = (
110 cartesian_to_polar_coordinates(trajectories.us, trajectories.vs)
111 )
112 trajectories["thetaf"] = periodic_angular_conditions(
113 trajectories["thetaf"], config.params.grid.bins["theta"]
114 )
115 trajectories["thetas"] = periodic_angular_conditions(
116 trajectories["thetas"], config.params.grid.bins["theta"]
117 )
118 # if config.save.simulated_trajectories:
119 # log.debug(
120 # "Configuration 'save.simulated_trajectories' is set to True."
121 # )
122 # save_trajectories(
123 # trajectories,
124 # Path.cwd().parent,
125 # config.filename.simulated_trajectories,
126 # )
127 return trajectories
130# def sample_trajectory_origins_from_trajectories(piecewise_potential:
131# PiecewisePotential, parameters: dict) -> np.ndarray:
132# ntrajs = np.min([parameters.simulation.ntrajs, parameters.input_ntrajs])
133# sampled_origins = piecewise_potential.trajectory_origins.sample(n=ntrajs)
134# # Stacking to go from [x,y,u,v] to [x,y,u,v,xs,ys,us,vs]
135# sampled_origins = np.hstack((sampled_origins, sampled_origins))
136# log.info("Sampled %d origins from the input trajectories.", ntrajs)
137# return sampled_origins
140def read_simulated_trajectories_from_file(config):
141 log.debug("Configuration 'read.simulated_trajectories' is set to True.")
142 filepath = Path.cwd().parent / config.filename.simulated_trajectories
143 try:
144 simulated_trajectories = pd.read_csv(filepath)
145 log.warning("Simulated trajectories read from file")
146 # log.debug("Filepath %s", filepath.relative_to(config.root_dir))
147 return simulated_trajectories
148 except FileNotFoundError as e:
149 log.error("Preprocessed trajectories not found: %s", e)
152def heatmap_zero_at_slow_state(
153 piecewise_potential: PiecewisePotential, slow_state
154) -> bool:
155 """Check if the heatmap is zero at the given position.
157 If the heatmap is zero it indicates that the spatial position was never
158 visited by a pedestrian in the input data.
160 Parameters:
161 piecewise_potential: The piecewise potential object.
162 position: The position to check.
164 Returns:
165 True if the heatmap is zero at the given position, False otherwise.
166 """
167 heatmap = np.sum(piecewise_potential.histogram, axis=(2, 3, 4))
168 slow_state_index = get_grid_indices(piecewise_potential, slow_state)
169 return heatmap[slow_state_index[0], slow_state_index[1]] == 0
172def check_restarting_conditions(traj, n_frames_back, piecewise_potential):
173 # traj is the last trajectory_piece
174 last_trajectory_piece_too_short = len(traj) < n_frames_back
175 if last_trajectory_piece_too_short:
176 log.warning(
177 "Last trajectory piece has only %s frames. Not reverting.",
178 len(traj),
179 )
180 return False
182 trajectory_already_left_the_measurement_domain = (
183 heatmap_zero_at_slow_state(
184 piecewise_potential, traj.iloc[-1][["xs", "ys", "us", "vs", "k"]]
185 )
186 )
187 if trajectory_already_left_the_measurement_domain:
188 log.warning(
189 "Last trajectory piece already left the measurement domain. "
190 "Not reverting."
191 )
192 return False
194 particle_left_the_lattice = np.all(np.isinf(traj.iloc[-1]))
195 if particle_left_the_lattice:
196 log.warning(
197 "Last trajectory piece already left the lattice. Not reverting."
198 )
199 return False
201 max_restarts = 2
202 simulation_already_restarted_too_often = (
203 traj["piece_id"].iloc[0] > max_restarts
204 )
205 if simulation_already_restarted_too_often:
206 log.warning(
207 "Simulation already restarted %s times. Not reverting.",
208 max_restarts,
209 )
210 return False
212 return True
215def simulate_trajectory_piece(
216 model: LangevinModel,
217 starting_state: np.ndarray,
218 evaluation_time: np.ndarray,
219 no_traj_piece: int,
220) -> pd.DataFrame:
221 integration_output = model.simulate(starting_state[:11], evaluation_time)
222 columns = ["xf", "yf", "uf", "vf", "xs", "ys", "us", "vs", "t", "k", "Pid"]
223 trajectory_piece = pd.DataFrame(integration_output, columns=columns)
224 trajectory_piece.replace([np.inf, -np.inf], np.nan, inplace=True)
225 trajectory_piece.dropna(inplace=True)
226 trajectory_piece["piece_id"] = no_traj_piece
227 return trajectory_piece