Skip to content

Add avoidance and intrusion number computation - #279

Open
schroedtert wants to merge 31 commits into
mainfrom
avoidance-intrusion
Open

schroedtert wants to merge 31 commits into
mainfrom
avoidance-intrusion

Conversation

@schroedtert

@schroedtert schroedtert commented Feb 23, 2024 •

Copy link
Copy Markdown
Collaborator

Implementation of Dimensionless numbers reveal distinct regimes in the structure and dynamics of pedestrian crowds 10.1093/pnasnexus/pgae120

@schroedtert
schroedtert force-pushed the avoidance-intrusion branch from 4ea73ee to 6bfa513 Compare March 25, 2024 14:10
@schroedtert

Copy link
Copy Markdown
Collaborator Author

@chraibi can you take over and add the documentation for the functions?

@schroedtert schroedtert added this to the v1.4.0 milestone Feb 22, 2025
@chraibi chraibi removed this from the v1.4.0 milestone Sep 1, 2025
schroedtert and others added 13 commits April 7, 2026 20:41
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.
@chraibi
chraibi force-pushed the avoidance-intrusion branch from a5af5bb to 2e990de Compare April 7, 2026 18:44
@chraibi
chraibi marked this pull request as ready for review April 7, 2026 19:52
Copilot AI review requested due to automatic review settings April 7, 2026 19:52

This comment was marked as outdated.

@chraibi
chraibi requested review from ThoChat April 7, 2026 20:07
- 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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pedpy/methods/method_utils.py
- 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 ThoChat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole method has to be implemented alongside the "Pair Distribution Function" in the Spatial_Analysis section of the code.

Comment thread notebooks/user_guide.ipynb Outdated
"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",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@chraibi chraibi Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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

codecov Bot commented Oct 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.25%. Comparing base (a862ef1) to head (d271ce8).

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants