Repository navigation
Add avoidance and intrusion number computation - #279
schroedtert wants to merge 31 commits into
Conversation
4ea73ee to
6bfa513
Compare
|
@chraibi can you take over and add the documentation for the functions? |
Due to Python operator precedence, v_i - v_j / ||v_i - v_j|| was computing v_i - (v_j / ||v_i - v_j||) instead of the intended (v_i - v_j) / ||v_i - v_j||. Split into two steps to make the order of operations explicit.
cos_alpha should be the dot product of the unit position difference (e_v) and the unit relative velocity (v_rel_hat). Previously, it was divided by (distance * v_rel_norm), which introduced a spurious 1/d factor since e_v was already a unit vector. Also rename v_rel_norm to delta_v_norm to clarify it is the magnitude of the actual relative velocity (used in the TTC denominator), not the norm of the unit vector.
Per Cordes2024 Eq. 1, the neighbor set N_i is restricted to agents within r_ij <= 3*r_soc. Without this filter, distant agents contribute small but accumulating spurious values to the intrusion number.
Also update the API docs section header from "Other" to "Dimensionless Numbers".
Module docstring with paper reference, and per-function docstrings with the mathematical definitions (Eqs. 1 and 2 from Cordes et al. 2024), parameter descriptions, and return types. Follows the existing rst/math style used in other PedPy modules.
- Intrusion: two-agent known distance, neighbor cutoff exclusion, three-agent sum, and MAX aggregation method - Avoidance: head-on collision with analytically known TTC, diverging agents with zero avoidance
Demonstrates compute_intrusion and compute_avoidance using the existing bottleneck demo data. Shows per-agent results and temporal evolution plots of the frame-averaged dimensionless numbers.
a5af5bb to
2e990de
Compare
- Use column constants (ID_COL, FRAME_COL, etc.) instead of string literals in _compute_individual_distances - Use inner join instead of outer join for self-merge (same DataFrame) - Make compute_individual_distances public (used across modules) - Guard division by zero: filter distance > l_min in intrusion, skip distance == 0 and delta_v_norm == 0 pairs in avoidance - Fix SettingWithCopyWarning: use .loc[] and .copy() - Simplify cos_alpha computation: use np.sum directly instead of converting to list and back - Compute v_rel_hat only for valid pairs (nonzero relative velocity) - Fix methods.rst section adornment level for Dimensionless Numbers
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Change radius default from 0.2 to 0.4 to match paper (ℓ_soc = 0.4m) - Export INTRUSION_COL and AVOIDANCE_COL from pedpy.__init__ - Document that agents at exactly l_min distance are excluded
ThoChat
left a comment
There was a problem hiding this comment.
The whole method has to be implemented alongside the "Pair Distribution Function" in the Spatial_Analysis section of the code.
| "The **Avoidance number** quantifies the imminence of collisions via the time-to-collision (TTC):\n", | ||
| "\n", | ||
| "$$\n", | ||
| "\\mathcal{A}v_i = \\max_{j \\in \\mathcal{N}'_i}\n", |
There was a problem hiding this comment.
There, why is it a "max" and not a "sum"? In the API the Avoidance number is defined with a sum but then it says "implemented by taking the max". This is confusing. The equation (2) of the paper is with a sum though.
It would be also nice to explain why we can use IntrusionMethod with "SUM" and "MAX" and how it affects the computation/ when we need which and why.
There was a problem hiding this comment.
Eq. 2 of the paper is written as a sum, but the main text restricts N'_i to the single neighbour with the shortest TTC, so the sum has one term and equals the maximum of tau_0/tau_ij over the frame. SI Appendix B explains why: replacing that max by a sum over all neighbours "blurs the delineation of regimes to a large extent". The docstring and the user guide now say this instead of "implemented by taking the max".
For the intrusion number, SUM is Eq. 1 and MAX is Eq. S6 of the SI, a variant the authors tested and found to leave the regime delineation unchanged. Both are now explained in the docs with those references.
Since your review the branch also moved the functions into spatial_analysis next to the pair distribution function, as you asked, and I checked the implementation against the paper's own supplementary trajectories (the six Waiting runs): the intrusion and avoidance numbers reproduce the six Waiting points of Fig. 1B within the reading accuracy of the figure. Getting there required two changes that are now in: the paper's per-pair caps (400 and 60) applied by default, and pairs whose discs already overlap counted as no collision ahead, as in the paper. Every pedestrian is now present in both results, with 0 where nothing contributes.
# Conflicts: # pedpy/__init__.py
Move IntrusionMethod, compute_intrusion and compute_avoidance from the separate dimensionless_number_calculator module into spatial_analysis, next to the pair distribution function, as requested in review. The API reference lists them under Spatial Analysis; the user guide section moves below the pair-distribution function. Rewrite the avoidance docs: Eq. 2 of Cordes et al. is a sum, but the paper restricts the neighbour set to the single neighbour with the shortest time-to-collision, so the sum has one term and equals the maximum of tau_0 / tau_ij over the frame. Say so instead of "implemented by taking the max". Explain IntrusionMethod.SUM (the paper's definition) against MAX (strongest single encroachment).
The default was 0.4 m, the l_soc the paper uses only in its simulated models. For recorded trajectories the paper computes the TTC with a disc diameter of 0.2 m, which is what PedPy analyses. The parameter is a diameter (the centre distance at contact), so rename it from radius to l_min, the name compute_intrusion already uses for the same 0.2 m. Note the paper's per-pair cap in the docstring.
Cordes et al. bound In_ij at 400 and Av_ij at 60 to limit artifacts from apparent overlaps of the discs in noisy trajectories. Apply the same caps by default, keep touching and overlapping pairs at the cap instead of dropping or zeroing them, and let cap=None disable them.
Both results now have one row per pedestrian and frame. Agents with no neighbor within the cutoff, on no collision course or alone in a frame get 0, as the paper says the intrusion vanishes for isolated pedestrians. The two frames then merge on id and frame without loss.
Name the velocity window, the Butterworth smoothing, the missing epsilon term and the paper's averaging rules, so that a user who wants the published numbers knows what to do outside the functions.
Use a 1 s velocity window, average the avoidance number only over pedestrians on a collision course, mention the caps and the missing smoothing, and fix the sentence that described MAX backwards.
Tighten the head-on tolerance to what the formula delivers and cover touching, overlapping and oblique pairs, the caps, and agents alone in a frame, with hand-computed expectations.
Pairs already inside the contact distance were given a TTC of 0 and took the cap. On the paper's own Waiting trajectories that gives avoidance numbers three times the published ones. Counting such pairs as no anticipated collision, TTC infinite, reproduces the six Waiting points of Fig. 1B within the reading accuracy of the figure, while the intrusion number already matched. Keep the cap for near-contact approaches.
IntrusionMethod.MAX is Eq. S6 of the paper's SI Appendix B, which the authors tested and found to leave the regime delineation unchanged. The docs called it a definition foreign to the paper.
The simulation code regularises Eq. 1 at contact, not at the cutoff, and treats contact as a collision; the data analysis code treats it as no anticipated collision. Say which convention this module follows and that it reproduces the authors' analysis and Fig. 1B.
Codecov Report✅ All modified and coverable lines are covered by tests. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Implementation of Dimensionless numbers reveal distinct regimes in the structure and dynamics of pedestrian crowds 10.1093/pnasnexus/pgae120