Add unit tests, refactor simulation code, improve Makefile, and add documentation
This commit is contained in:
@@ -1,7 +1,121 @@
|
|||||||
main: board.hpp forces.cpp forces.hpp main.cpp pedestrian.hpp vector2d.hpp constants.hpp
|
# Makefile for the project.
|
||||||
g++ -O3 -march=native -std=c++23 -S main.cpp -o main.s
|
# Provides targets for building, running, benchmarking, testing, and visualization.
|
||||||
g++ -O3 -march=native -std=c++23 -S forces.cpp -o force.s
|
#
|
||||||
g++ -O3 -march=native -std=c++23 main.cpp forces.cpp -o main
|
# Main commands:
|
||||||
|
# make run run the simulation, producing a CSV file
|
||||||
|
# containing pedestrian states at each iteration
|
||||||
|
# make video generate a video of the simulation
|
||||||
|
# make run-bench run benchmark measurements
|
||||||
|
# make test run all unit tests
|
||||||
|
#
|
||||||
|
# Default parameters can be overridden from the shell, as follows:
|
||||||
|
# (parameters can be provided selectively and in any order)
|
||||||
|
#
|
||||||
|
# make run N_P=<num_ped> N_ITER=<num_iter> CSV=<out.csv>
|
||||||
|
# make video N_P=<num_ped> N_ITER=<num_iter> CSV=<out.csv> MP4=<video.mp4>
|
||||||
|
# make run-bench N_P=<num_ped> N_ITER=<num_iter>
|
||||||
|
|
||||||
sizes: sizes.cpp
|
|
||||||
g++ -O3 -march=native -std=c++23 sizes.cpp -o sizes
|
# Select the C++ compiler.
|
||||||
|
# Prefer g++ when available, otherwise fall back to clang++.
|
||||||
|
CXX := $(shell command -v g++ || command -v clang++)
|
||||||
|
|
||||||
|
ifeq ($(CXX),)
|
||||||
|
$(error No C++ compiler found. Please install g++ or clang++)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ARCH := $(shell uname -m)
|
||||||
|
|
||||||
|
# Prepend sudo on ARM machines when benchmarking.
|
||||||
|
# This is needed to access the PMU performance counters.
|
||||||
|
ifeq ($(ARCH),arm64)
|
||||||
|
SUDO_BENCH := sudo
|
||||||
|
else ifeq ($(ARCH),aarch64)
|
||||||
|
SUDO_BENCH := sudo
|
||||||
|
else
|
||||||
|
SUDO_BENCH :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Compiler flags:
|
||||||
|
# -O3 enable high-level optimization
|
||||||
|
# -march=native optimize for the current machine
|
||||||
|
# -std=c++23 use the C++23 standard
|
||||||
|
# -Wall -Wextra enable useful warnings
|
||||||
|
# -flto enable link-time optimization across .cpp files, allowing
|
||||||
|
# cross-file optimizations such as function inlining
|
||||||
|
CXXFLAGS := -O3 -march=native -std=c++23 -Wall -Wextra -flto
|
||||||
|
|
||||||
|
SRC := main.cpp forces.cpp social_force_baseline.cpp
|
||||||
|
HEADERS := board.hpp forces.hpp pedestrian.hpp vector2d.hpp constants.hpp social_force.hpp
|
||||||
|
|
||||||
|
TEST_SRC := test_vector2d.cpp test_board.cpp test_forces.cpp forces.cpp
|
||||||
|
TEST_TARGET := run_tests
|
||||||
|
TEST_LIBS := -lgtest -lgtest_main -pthread
|
||||||
|
|
||||||
|
TARGET := main
|
||||||
|
SIZES_TARGET := sizes
|
||||||
|
|
||||||
|
# Default simulation parameters and output files.
|
||||||
|
# These can be overridden from the make command line; see examples above.
|
||||||
|
N_P ?= 60 # Default number of pedestrians.
|
||||||
|
N_ITER ?= 4000 # Default number of simulation iterations.
|
||||||
|
CSV ?= positions.csv # Default CSV output file.
|
||||||
|
MP4 ?= simulation.mp4 # Default visualization video output file.
|
||||||
|
|
||||||
|
# Enable/disable non-reproducible random seeding.
|
||||||
|
# Default: RANDOM_SEED=0 uses the fixed seed for reproducibility.
|
||||||
|
# Use RANDOM_SEED=1 to seed from std::random_device.
|
||||||
|
RANDOM_SEED ?= 0
|
||||||
|
|
||||||
|
# Pass random-seeding mode to the C++ code.
|
||||||
|
CXXFLAGS += -DRANDOM_SEED=$(RANDOM_SEED)
|
||||||
|
|
||||||
|
.PHONY: all main run bench run-bench video sizes asm test clean clean-all
|
||||||
|
|
||||||
|
all: main
|
||||||
|
|
||||||
|
# make main : compile the main executable with benchmarking disabled.
|
||||||
|
main: clean
|
||||||
|
$(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET)
|
||||||
|
|
||||||
|
# make run : compile the code and run the simulation without generating a video.
|
||||||
|
run: main
|
||||||
|
./$(TARGET) $(N_P) $(N_ITER) $(CSV)
|
||||||
|
|
||||||
|
# make bench : compile the main executable with benchmarking enabled.
|
||||||
|
bench: CXXFLAGS += -DBENCHMARK=1
|
||||||
|
bench: clean
|
||||||
|
$(SUDO_BENCH) $(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET)
|
||||||
|
|
||||||
|
# make run-bench : compile and run benchmark measurements.
|
||||||
|
run-bench: bench
|
||||||
|
$(SUDO_BENCH) ./$(TARGET) $(N_P) $(N_ITER)
|
||||||
|
|
||||||
|
# make video : compile the code, run the simulation, and generate the video.
|
||||||
|
video: run
|
||||||
|
python visualization.py $(CSV) $(MP4)
|
||||||
|
|
||||||
|
# make sizes : compile the size-check helper.
|
||||||
|
sizes: clean
|
||||||
|
$(CXX) $(CXXFLAGS) sizes.cpp -o $(SIZES_TARGET)
|
||||||
|
|
||||||
|
# make test : compile and run all unit tests.
|
||||||
|
test: clean
|
||||||
|
$(CXX) $(CXXFLAGS) $(TEST_SRC) $(TEST_LIBS) -o $(TEST_TARGET)
|
||||||
|
./$(TEST_TARGET)
|
||||||
|
|
||||||
|
# make asm : compile and generate assembly files.
|
||||||
|
asm: clean main.s forces.s social_force_baseline.s
|
||||||
|
|
||||||
|
%.s: %.cpp $(HEADERS)
|
||||||
|
$(CXX) -O3 -march=native -std=c++23 -Wall -Wextra -DRANDOM_SEED=$(RANDOM_SEED) -S $< -o $@
|
||||||
|
|
||||||
|
# make clean : delete generated files from previous builds.
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET) $(SIZES_TARGET) $(TEST_TARGET) *.o *.s
|
||||||
|
|
||||||
|
# make clean-all : delete all generated files from previous builds and runs.
|
||||||
|
# WARNING: this deletes all *.csv and *.mp4 files in this directory.
|
||||||
|
# Move any output files you want to keep to another directory before running it.
|
||||||
|
clean-all: clean
|
||||||
|
rm -f *.csv *.mp4
|
||||||
@@ -1,44 +1,456 @@
|
|||||||
# Advanced Systems Lab - Spring 2026
|
# Advanced Systems Lab - Spring 2026
|
||||||
This is the GitLab repository of group 70.
|
|
||||||
Project topic: Social Force Model for Pedestrian Dynamics
|
|
||||||
|
|
||||||
The goal of this project was to implement a fast version of Helbing's social force model. The final report includes an analysis and results of the work.
|
This is the GitLab repository of **Group 70**.
|
||||||
|
|
||||||
This repository contains all C++ and Python code that was used to generate and analyze the data.
|
**Project topic:** Social Force Model for Pedestrian Dynamics
|
||||||
|
|
||||||
|
The goal of this project was to implement and optimize Helbing's social force model for pedestrian dynamics. The final report includes the implementation details, performance analysis, and results of the work.
|
||||||
|
|
||||||
## Compilation
|
This repository contains the C++ simulation code and Python visualization code used to generate and analyze the data.
|
||||||
Linux
|
|
||||||
|
|
||||||
``
|
---
|
||||||
|
|
||||||
Windows
|
## Requirements
|
||||||
|
|
||||||
``
|
The project uses a `Makefile` to simplify building, running, benchmarking, testing, and visualization.
|
||||||
|
|
||||||
Apple
|
Make sure `make` is installed:
|
||||||
|
|
||||||
`clang++ -O3 -march=native -o main_test_visualisation main.cpp forces.cpp`
|
```bash
|
||||||
|
make --version
|
||||||
|
```
|
||||||
|
|
||||||
Compile the code with running the bash script 'src/main/run_simulation.sh'. After compilation was succesfull the user has 3 different option:
|
You also need a C++ compiler. The Makefile automatically uses `g++` if available, otherwise it falls back to `clang++`.
|
||||||
1. Benchmark
|
|
||||||
2. Test
|
|
||||||
3. Visualization
|
|
||||||
|
|
||||||
The flags used are:
|
Check your compiler with:
|
||||||
`gcc -lm -O3 -mavx2 -mfma -ffast-math -g`
|
|
||||||
|
|
||||||
## Storing coordinates in a csv
|
```bash
|
||||||
`./main_test_visualisation <number of pedestrians> <number of iterations> <csv file>`
|
g++ --version
|
||||||
|
clang++ --version
|
||||||
|
```
|
||||||
|
|
||||||
|
The visualization script requires Python and the following packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m pip install numpy pandas matplotlib
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate MP4 videos, `ffmpeg` must also be installed.
|
||||||
|
|
||||||
|
On Ubuntu/Debian:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install make g++ clang python3 python3-pip ffmpeg libgtest-dev
|
||||||
|
python3 -m pip install numpy pandas matplotlib
|
||||||
|
```
|
||||||
|
|
||||||
|
On macOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcode-select --install
|
||||||
|
brew install ffmpeg googletest
|
||||||
|
python3 -m pip install numpy pandas matplotlib
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Makefile commands
|
||||||
|
|
||||||
|
The main commands are:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs the simulation and writes pedestrian states to a CSV file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make video
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs the simulation and generates a visualization video.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run-bench
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiles and runs benchmark measurements.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiles and runs all unit tests.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make asm
|
||||||
|
```
|
||||||
|
|
||||||
|
Generates assembly files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make sizes
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiles the helper executable used to inspect data-structure sizes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes generated build files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes generated build files and all `.csv` / `.mp4` outputs in the current directory.
|
||||||
|
|
||||||
|
**Warning:** `make clean-all` removes all CSV and MP4 files in the directory. Move any output files you want to keep before running it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Default parameters
|
||||||
|
|
||||||
|
The default simulation parameters are:
|
||||||
|
|
||||||
|
```text
|
||||||
|
N_P=60
|
||||||
|
N_ITER=4000
|
||||||
|
CSV=positions.csv
|
||||||
|
MP4=simulation.mp4
|
||||||
|
RANDOM_SEED=0
|
||||||
|
```
|
||||||
|
|
||||||
|
They can be overridden from the shell. Parameters can be provided selectively and in any order.
|
||||||
|
|
||||||
|
Run the simulation with custom parameters:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run N_P=100 N_ITER=5000 CSV=my_positions.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a video with custom output names:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make video N_P=100 N_ITER=5000 CSV=my_positions.csv MP4=my_simulation.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
Run benchmark measurements with custom parameters:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run-bench N_P=100 N_ITER=5000
|
||||||
|
```
|
||||||
|
|
||||||
|
Use non-reproducible random initialization:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run RANDOM_SEED=1
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make video RANDOM_SEED=1
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, `RANDOM_SEED=0` uses a fixed seed for reproducible simulations and benchmarks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To compile the main executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make main
|
||||||
|
```
|
||||||
|
|
||||||
|
This produces the executable:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main
|
||||||
|
```
|
||||||
|
|
||||||
|
The Makefile uses the following compiler flags:
|
||||||
|
|
||||||
|
```text
|
||||||
|
-O3 -march=native -std=c++23 -Wall -Wextra -flto
|
||||||
|
```
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
- `-O3` enables high-level optimization.
|
||||||
|
- `-march=native` optimizes for the current machine.
|
||||||
|
- `-std=c++23` uses the C++23 standard.
|
||||||
|
- `-Wall -Wextra` enables useful compiler warnings.
|
||||||
|
- `-flto` enables link-time optimization across `.cpp` files, allowing cross-file optimizations such as function inlining.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the simulation
|
||||||
|
|
||||||
|
The recommended way to run the simulation is:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
|
This compiles the code and runs the simulation with the default parameters. It produces a CSV file containing pedestrian states at each saved time step.
|
||||||
|
|
||||||
|
You can also run the executable manually after compiling:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./main <number_of_pedestrians> <number_of_iterations> [csv_output_path]
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./main 60 4000 positions.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
The third argument is optional. If it is provided, simulation states are written to the given CSV file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CSV output
|
||||||
|
|
||||||
|
The CSV file contains the pedestrian states used for visualization.
|
||||||
|
|
||||||
|
The output columns are:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time,id,x,y,p.e.x,p.e.y,color
|
||||||
|
```
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
- `time` is the simulation time step.
|
||||||
|
- `id` is the pedestrian index.
|
||||||
|
- `x`, `y` are the pedestrian position coordinates.
|
||||||
|
- `p.e.x`, `p.e.y` are the components of the desired direction.
|
||||||
|
- `color` indicates the pedestrian group / walking direction.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Visualization
|
## Visualization
|
||||||
`python3 visualization.py <csv file> <mp4 file>`
|
|
||||||
|
|
||||||
|
To run the simulation and directly generate a video:
|
||||||
|
|
||||||
## Benchmark
|
```bash
|
||||||
|
make video
|
||||||
|
```
|
||||||
|
|
||||||
|
This produces an MP4 file, by default:
|
||||||
|
|
||||||
|
```text
|
||||||
|
simulation.mp4
|
||||||
|
```
|
||||||
|
|
||||||
## Test
|
To visualize an existing CSV file manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 visualization.py <csv_file> <mp4_file>
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 visualization.py positions.csv simulation.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated video shows the pedestrian dynamics produced by the social force simulation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benchmarking
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run-bench
|
||||||
|
```
|
||||||
|
|
||||||
|
This compiles the executable with benchmarking enabled and measures the simulation performance.
|
||||||
|
|
||||||
|
On ARM/AArch64 machines, the Makefile automatically prepends `sudo` for benchmark compilation and execution because access to PMU performance counters may require elevated privileges.
|
||||||
|
|
||||||
|
For reproducible benchmark results, keep:
|
||||||
|
|
||||||
|
```text
|
||||||
|
RANDOM_SEED=0
|
||||||
|
```
|
||||||
|
|
||||||
|
which is the default.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run-bench N_P=100 N_ITER=5000
|
||||||
|
```
|
||||||
|
|
||||||
|
The benchmark output includes estimated work, operational intensity, measured cycles, and estimated performance in flops per cycle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Run all unit tests with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
The tests use GoogleTest, so `libgtest-dev` or an equivalent GoogleTest installation must be available.
|
||||||
|
|
||||||
|
The test target compiles and runs the unit tests for the simulation components.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Assembly generation
|
||||||
|
|
||||||
|
To generate assembly files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make asm
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates assembly output for the main source files, such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main.s
|
||||||
|
forces.s
|
||||||
|
social_force_baseline.s
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be useful for inspecting compiler optimizations and generated instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structure-size helper
|
||||||
|
|
||||||
|
To compile the helper executable used to inspect data-structure sizes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make sizes
|
||||||
|
```
|
||||||
|
|
||||||
|
This produces:
|
||||||
|
|
||||||
|
```text
|
||||||
|
sizes
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./sizes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cleaning generated files
|
||||||
|
|
||||||
|
Remove generated build files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
This deletes files such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main
|
||||||
|
sizes
|
||||||
|
run_tests
|
||||||
|
*.o
|
||||||
|
*.s
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove build files and generated CSV/MP4 outputs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean-all
|
||||||
|
```
|
||||||
|
|
||||||
|
This deletes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main
|
||||||
|
sizes
|
||||||
|
run_tests
|
||||||
|
*.o
|
||||||
|
*.s
|
||||||
|
*.csv
|
||||||
|
*.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warning:** `make clean-all` deletes all `.csv` and `.mp4` files in the current directory. Move any files you want to keep before running it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes on reproducibility
|
||||||
|
|
||||||
|
By default, the simulation uses a fixed random seed:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SEED=42
|
||||||
|
```
|
||||||
|
|
||||||
|
This makes simulation runs reproducible.
|
||||||
|
|
||||||
|
To use non-reproducible initialization, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run RANDOM_SEED=1
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make video RANDOM_SEED=1
|
||||||
|
```
|
||||||
|
|
||||||
|
For benchmarking, reproducible initialization is recommended, so the default `RANDOM_SEED=0` should normally be kept.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example workflow
|
||||||
|
|
||||||
|
Compile and run the simulation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a video:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make video
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run benchmark measurements:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run-bench
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean generated build files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean everything generated, including CSV and MP4 outputs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make clean-all
|
||||||
|
```
|
||||||
@@ -2,22 +2,44 @@
|
|||||||
#include "vector2d.hpp"
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
struct Obstacle {
|
struct Obstacle {
|
||||||
Vector2d start;
|
Vector2d start; // First endpoint of the obstacle segment.
|
||||||
Vector2d end;
|
Vector2d end; // Second endpoint of the obstacle segment.
|
||||||
Vector2d obstacle_v;
|
Vector2d obstacle_v; // Direction vector of the segment: end - start.
|
||||||
|
double length_sq_inv; // Inverse squared length of the segment, used for projection.
|
||||||
double length_sq_inv;
|
|
||||||
|
|
||||||
|
// Default constructor: creates a degenerate obstacle at the origin.
|
||||||
|
// This is mainly used for array allocation before assigning real obstacles.
|
||||||
Obstacle() : start(0, 0), end(0, 0), obstacle_v(0, 0), length_sq_inv(0.0) {}
|
Obstacle() : start(0, 0), end(0, 0), obstacle_v(0, 0), length_sq_inv(0.0) {}
|
||||||
|
|
||||||
|
// Constructs an obstacle as a finite line segment from s to e.
|
||||||
|
//
|
||||||
|
// The segment direction and inverse squared length are precomputed and cached because
|
||||||
|
// they are reused when projecting pedestrian positions onto the obstacle.
|
||||||
|
//
|
||||||
|
// @param s The first endpoint of the obstacle segment.
|
||||||
|
// @param e The second endpoint of the obstacle segment.
|
||||||
|
//
|
||||||
Obstacle(Vector2d s, Vector2d e) : start(s), end(e) {
|
Obstacle(Vector2d s, Vector2d e) : start(s), end(e) {
|
||||||
obstacle_v = end - start;
|
obstacle_v = end - start;
|
||||||
double length_sq = obstacle_v.dot(obstacle_v);
|
double length_sq = obstacle_v.dot(obstacle_v);
|
||||||
length_sq_inv = length_sq != 0 ? 1.0 / length_sq : 0.0;
|
length_sq_inv = length_sq != 0.0 ? 1.0 / length_sq : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the point of the obstacle that is closest to p
|
// Flop count: 5 ADDs, 5 MULTs
|
||||||
// # 5 ADDs, 5 MULTs
|
//
|
||||||
Vector2d find_nearest_obstacle_point(Vector2d p) {
|
// Finds the point on this finite obstacle segment that is closest to p.
|
||||||
|
//
|
||||||
|
// The method projects the vector (p - start) onto the obstacle direction
|
||||||
|
// vector, then clamps the projection factor to [0, 1] so that the returned
|
||||||
|
// point lies on the finite segment.
|
||||||
|
//
|
||||||
|
// Note : If the obstacle is a single point, meaning start == end, length_sq_inv is zero
|
||||||
|
// and the method returns start.
|
||||||
|
//
|
||||||
|
// @param p The point for which the closest obstacle point is computed.
|
||||||
|
// @return The closest point on the obstacle segment to p.
|
||||||
|
//
|
||||||
|
Vector2d find_nearest_obstacle_point(const Vector2d &p) const {
|
||||||
|
|
||||||
// # 3 ADDs, 3 MULTs
|
// # 3 ADDs, 3 MULTs
|
||||||
double projection = (p - start).dot(obstacle_v) * length_sq_inv;
|
double projection = (p - start).dot(obstacle_v) * length_sq_inv;
|
||||||
@@ -33,22 +55,43 @@ struct Obstacle {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Board {
|
struct Board {
|
||||||
double length, width;
|
double length; // Board length.
|
||||||
Obstacle *obstacles; // Array of obstacles, represented as lines on the board
|
double width; // Board width.
|
||||||
int n_obstacles; // Number of obstacles
|
Obstacle *obstacles; // Array of obstacles contained in the board.
|
||||||
|
int n_obstacles; // Total number of obstacles
|
||||||
|
|
||||||
|
// Constructs a rectangular board of size length x width.
|
||||||
|
//
|
||||||
|
// The input array o contains n user-defined obstacles. The constructor copies
|
||||||
|
// those obstacles and then appends the two horizontal borders of the board (bottom and top) as additional
|
||||||
|
// obstacle segments:
|
||||||
|
//
|
||||||
|
// bottom: (0, 0) -> (length, 0)
|
||||||
|
// top: (0, width) -> (length, width)
|
||||||
|
//
|
||||||
|
// Note: only the horizontal borders are modeled as obstacles. The vertical
|
||||||
|
// borders are left open so pedestrians can enter and leave the walkway without
|
||||||
|
// being affected by repulsive forces from those borders. This is consistent
|
||||||
|
// with Fig. 2 in the paper, where the horizontal walkway boundaries are
|
||||||
|
// emphasized but the vertical ends are not shown as walls.
|
||||||
|
//
|
||||||
|
// @param l The board length.
|
||||||
|
// @param w The board width.
|
||||||
|
// @param n The number of user-defined obstacles in o.
|
||||||
|
// @param o Array of user-defined obstacles to copy into the board obstacles array.
|
||||||
|
//
|
||||||
|
Board(double l, double w, int n, Obstacle *o) : length(l), width(w), n_obstacles(n + 2) {
|
||||||
|
obstacles = new Obstacle[n + 2];
|
||||||
|
|
||||||
Board(double l, double w, int n, Obstacle *o) : length(l), width(w), n_obstacles(n + 4) {
|
|
||||||
obstacles = new Obstacle[n + 4];
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
obstacles[i] = o[i];
|
obstacles[i] = o[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// borders of the board: bottom, right, top, left
|
// Board borders: bottom, top.
|
||||||
obstacles[n] = Obstacle(Vector2d(0, 0), Vector2d(l, 0));
|
obstacles[n] = Obstacle(Vector2d(0, 0), Vector2d(l, 0));
|
||||||
obstacles[n + 1] = Obstacle(Vector2d(l, 0), Vector2d(l, w));
|
obstacles[n + 1] = Obstacle(Vector2d(0, w), Vector2d(l, w));
|
||||||
obstacles[n + 2] = Obstacle(Vector2d(l, w), Vector2d(0, w));
|
|
||||||
obstacles[n + 3] = Obstacle(Vector2d(0, w), Vector2d(0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Releases the dynamically allocated obstacle array.
|
||||||
~Board() { delete[] obstacles; }
|
~Board() { delete[] obstacles; }
|
||||||
};
|
};
|
||||||
+58
-55
@@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
// $\tau_{\alpha}$
|
//
|
||||||
const double TAU_ALPHA = 0.5;
|
// $\frac{1}{\tau_{\alpha}}$; $\tau_{\alpha} = 0.5s$
|
||||||
|
//
|
||||||
|
const double tau_alpha_inv = 2.0;
|
||||||
|
|
||||||
// $V^0_{\alpha \beta}$
|
// $V^0_{\alpha \beta}$
|
||||||
const double V0_AB = 2.1;
|
const double V0_AB = 2.1;
|
||||||
@@ -10,79 +12,80 @@ const double V0_AB = 2.1;
|
|||||||
// $\sigma$
|
// $\sigma$
|
||||||
const double SIGMA = 0.3;
|
const double SIGMA = 0.3;
|
||||||
|
|
||||||
|
//
|
||||||
|
// $\frac{-1}{2\sigma}$
|
||||||
|
//
|
||||||
|
const double NEG_TWO_SIGMA_INV = -1.0 / (2 * SIGMA);
|
||||||
|
|
||||||
|
//
|
||||||
|
// $\frac{-V^0_{\alpha\beta}}{2\sigma}$
|
||||||
|
//
|
||||||
|
const double NEG_V0_AB_div_TWO_SIGMA = V0_AB * NEG_TWO_SIGMA_INV;
|
||||||
|
|
||||||
// $U^0_{\alpha B}$
|
// $U^0_{\alpha B}$
|
||||||
const double U0_AB = 10.0;
|
const double U0_AB = 10.0;
|
||||||
|
|
||||||
// $R$
|
// $R$
|
||||||
const double R = 0.2;
|
const double R = 0.2;
|
||||||
|
|
||||||
// $c$
|
|
||||||
const double C = 0.5;
|
|
||||||
|
|
||||||
// $\varphi$
|
|
||||||
const double PHI = 100.0 / 180.0 * M_PI;
|
|
||||||
|
|
||||||
// $<v^0>$
|
|
||||||
const double V0_MEAN = 1.34;
|
|
||||||
|
|
||||||
// $\sqrt{\theta}$
|
|
||||||
const double V0_STD_DEV = 0.26;
|
|
||||||
|
|
||||||
// $\varDelta_{t}$
|
|
||||||
const double DELTA_T = 2;
|
|
||||||
|
|
||||||
// $dt$
|
|
||||||
const double DT = 0.02;
|
|
||||||
|
|
||||||
// # ##############################################################################################
|
|
||||||
|
|
||||||
//
|
|
||||||
// $\frac{-1}{2\sigma}$
|
|
||||||
//
|
|
||||||
const double NEG_ONE_DIV_TWO_SIGMA = -1.0 / (2 * SIGMA);
|
|
||||||
|
|
||||||
//
|
|
||||||
// $\frac{-V^0_{\alpha\beta}}{2\sigma}$
|
|
||||||
//
|
|
||||||
const double NEG_V0_AB_DIV_TWO_SIGMA = -V0_AB / (2 * SIGMA);
|
|
||||||
|
|
||||||
// $cos(\varphi)$
|
|
||||||
const double COS_PHI = cos(PHI);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// $\frac{-1}{R}$
|
// $\frac{-1}{R}$
|
||||||
//
|
//
|
||||||
const double NEG_R_INV = -1 / R;
|
const double NEG_R_INV = -1.0 / R;
|
||||||
|
|
||||||
//
|
//
|
||||||
// $\frac{-U^0_{\alpha B}}{R}$
|
// $\frac{-U^0_{\alpha B}}{R}$
|
||||||
//
|
//
|
||||||
const double NEG_U0_AB_DIV_R = -U0_AB / R;
|
const double NEG_U0_AB_div_R = -U0_AB / R;
|
||||||
|
|
||||||
// $c * dt$
|
// $-c$
|
||||||
const double C_TIMES_DT = C * DT;
|
const double c_neg = -0.5;
|
||||||
|
|
||||||
// ${(\varDelta_{t})}^2$
|
// $\varphi$
|
||||||
const double DELTA_T_SQ = DELTA_T * DELTA_T;
|
const double phi = 100.0 / 180.0 * M_PI;
|
||||||
|
|
||||||
|
// $cos(\varphi)$
|
||||||
|
const double cos_phi = cos(phi);
|
||||||
|
|
||||||
|
// $<v^0>$
|
||||||
|
const double v0_mean = 1.34;
|
||||||
|
|
||||||
|
// $\sqrt{\theta}$
|
||||||
|
const double v0_std_dev = 0.26;
|
||||||
|
|
||||||
|
// $\varDelta_{t}$
|
||||||
|
const double delta_t = 2;
|
||||||
|
|
||||||
|
// $\varDelta_{t}^2$
|
||||||
|
const double delta_t_sq = delta_t * delta_t;
|
||||||
|
|
||||||
|
// $dt$
|
||||||
|
const double dt = 0.02;
|
||||||
|
|
||||||
|
// $-dt$
|
||||||
|
const double dt_neg = -dt;
|
||||||
|
|
||||||
|
// $-c \cdot dt$
|
||||||
|
const double c_neg_times_dt = c_neg * dt;
|
||||||
|
|
||||||
//
|
//
|
||||||
// $\frac{1}{\tau_{\alpha}}$
|
// $\frac{1}{\tau_{\alpha}} \cdot dt$
|
||||||
//
|
//
|
||||||
const double TAU_ALPHA_INV = 1 / TAU_ALPHA;
|
const double dt_times_tau_alpha_inv = dt * tau_alpha_inv;
|
||||||
|
|
||||||
//
|
const double cost_add = 1;
|
||||||
// $\frac{dt}{\tau_{\alpha}}$
|
const double cost_mult = 1;
|
||||||
//
|
const double cost_div = 1;
|
||||||
const double DT_TIMES_TAU_ALPHA_INV = DT * TAU_ALPHA_INV;
|
|
||||||
|
|
||||||
// # ##############################################################################################
|
|
||||||
|
|
||||||
const double COST_ADD = 1;
|
|
||||||
const double COST_MULT = 1;
|
|
||||||
const double COST_DIV = 1;
|
|
||||||
|
|
||||||
// https://acl.inf.ethz.ch/teaching/fastcode/knowledgebase/#:~:text=How%20do%20exp(x)%2C%20sin(x)%2C%20etc.%20count%20in%20the%20flopcount%20of%20an%20algorithm%3F
|
// https://acl.inf.ethz.ch/teaching/fastcode/knowledgebase/#:~:text=How%20do%20exp(x)%2C%20sin(x)%2C%20etc.%20count%20in%20the%20flopcount%20of%20an%20algorithm%3F
|
||||||
const double COST_EXP = 30;
|
const double cost_exp = 30;
|
||||||
|
|
||||||
// -O3 maps each sqrt() call to an instruction
|
// -O3 maps each sqrt() call to an instruction
|
||||||
const double COST_SQRT = 1;
|
const double cost_sqrt = 1;
|
||||||
|
|
||||||
|
// Numerical tolerance used to avoid division by zero and unstable operations
|
||||||
|
// when distances or norms are extremely small.
|
||||||
|
const double EPS = 1e-9;
|
||||||
|
|
||||||
|
// Squared numerical tolerance
|
||||||
|
const double EPS_SQ = EPS * EPS;
|
||||||
+76
-60
@@ -2,123 +2,139 @@
|
|||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// # 2 ADDs, 5 MULTs, 1 SQRT
|
// Flop count: 2 ADDs, 5 MULTs, 1 SQRT
|
||||||
double w(Vector2d e, Vector2d f) {
|
double w(Vector2d e, Vector2d f) {
|
||||||
if (e.dot(f) >= f.norm() * COS_PHI)
|
if (e.dot(f) >= f.norm() * cos_phi)
|
||||||
return DT;
|
return dt_neg;
|
||||||
else
|
else
|
||||||
return C_TIMES_DT;
|
return c_neg_times_dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// # 4 ADDs, 4 MULTs
|
// Flop count: 2 ADDs, 4 MULTs
|
||||||
void add_acceleration_term(Pedestrian *p) {
|
Vector2d acceleration_term_dt(const Pedestrian &alpha) {
|
||||||
|
|
||||||
// # 2 MULTs
|
// # 2 MULTs
|
||||||
Vector2d desired_velocity = p->e * p->desired_v0;
|
Vector2d desired_velocity = alpha.e * alpha.desired_v0;
|
||||||
|
|
||||||
Vector2d actual_velocity = p->curr_vel;
|
Vector2d actual_velocity = alpha.curr_vel;
|
||||||
|
|
||||||
// # 4 ADDs, 2 MULTs
|
// # 2 ADDs, 2 MULTs
|
||||||
p->f_i += (desired_velocity - actual_velocity) * DT_TIMES_TAU_ALPHA_INV;
|
Vector2d f_alpha_0_dt = (desired_velocity - actual_velocity) * dt_times_tau_alpha_inv;
|
||||||
|
|
||||||
|
return f_alpha_0_dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// # (N - 1) * (14 ADDs, 23 MULTs, 1 DIV, 4 SQRTs, 1 EXP)
|
|
||||||
//
|
|
||||||
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{\beta})$
|
|
||||||
//
|
|
||||||
void add_repulsive_effects_from_pedestrians(Pedestrian *pedestrians, Pedestrian *p, int N) {
|
|
||||||
|
|
||||||
for (int i = 0; i < N; i++) {
|
// Flop count: 12 ADDs, 23 MULTs, 1 DIV, 4 SQRTs, 1 EXP
|
||||||
|
Vector2d pedestrian_repulsion_dt(const Pedestrian &alpha, const Pedestrian &beta) {
|
||||||
if (pedestrians + i == p)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// # 2 ADDs
|
// # 2 ADDs
|
||||||
// $\vec{r}_{\alpha\beta}$
|
// $\vec{r}_{\alpha\beta}$
|
||||||
Vector2d r_ab = p->curr_pos - pedestrians[i].curr_pos;
|
Vector2d r_ab = alpha.curr_pos - beta.curr_pos;
|
||||||
|
|
||||||
// # 1 ADD, 2 MULTs, 1 SQRT
|
// # 1 ADD, 2 MULTs, 1 SQRT
|
||||||
// $\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert$
|
// $\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert$
|
||||||
double r_ab_norm = r_ab.norm();
|
double r_ab_norm = r_ab.norm();
|
||||||
|
r_ab_norm = r_ab_norm > EPS ? r_ab_norm : EPS; // Avoid division by zero
|
||||||
|
|
||||||
|
// $v_\beta \Delta t \vec{e}_\beta$
|
||||||
|
Vector2d s_b_e = beta.s_b_e;
|
||||||
|
|
||||||
// # 2 ADDs
|
// # 2 ADDs
|
||||||
// $\vec{r}_{\alpha\beta} - \vec{v}_{\beta}\varDelta_{t}\vec{e}_{\beta}$
|
// $\vec{r}_{\alpha\beta} - \vec{v}_{\beta}\varDelta_{t}\vec{e}_{\beta}$
|
||||||
Vector2d r_ab2 = r_ab - pedestrians[i].s_b_e_b;
|
Vector2d r_ab2 = r_ab - s_b_e;
|
||||||
|
|
||||||
// # 1 ADD, 2 MULTs, 1 SQRT
|
// # 1 ADD, 2 MULTs, 1 SQRT
|
||||||
// $\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert$
|
// $\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert$
|
||||||
double r_ab2_norm = r_ab2.norm();
|
double r_ab2_norm = r_ab2.norm();
|
||||||
|
r_ab2_norm = r_ab2_norm > EPS ? r_ab2_norm : EPS; // Avoid division by zero
|
||||||
|
|
||||||
// # 1 ADD
|
// # 1 ADD
|
||||||
// $\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert$
|
// $\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert$
|
||||||
double r_ab_norm_sum = r_ab_norm + r_ab2_norm;
|
double r_ab_norm_sum = r_ab_norm + r_ab2_norm;
|
||||||
|
|
||||||
// # 1 ADD, 1 MULT, 1 SQRT
|
// # 1 ADD, 1 MULTs, 1 SQRT
|
||||||
//
|
//
|
||||||
// $2b = \sqrt{(\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert)^2 - (v_\beta \Delta t)^2}$
|
// $2b = \sqrt{(\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert)^2 - (v_\beta \Delta t)^2}$
|
||||||
//
|
//
|
||||||
double two_b = sqrt(r_ab_norm_sum * r_ab_norm_sum - pedestrians[i].s_b_sq);
|
double two_b_sq = r_ab_norm_sum * r_ab_norm_sum - beta.s_b_sq;
|
||||||
|
two_b_sq = two_b_sq > EPS_SQ ? two_b_sq : EPS_SQ; // Avoid sqrt of negative and avoid division by zero
|
||||||
|
double two_b = sqrt(two_b_sq);
|
||||||
|
|
||||||
// # 3 MULTs, 1 DIV
|
// # 5 MULTs, 1 DIV, 1 EXP
|
||||||
//
|
//
|
||||||
// $\frac{-V^0_{\alpha\beta}}{\sigma} \frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b \Arrowvert \vec{r}_{\alpha \beta} \Arrowvert \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}$
|
// $\frac{-V^0_{\alpha\beta}}{2\sigma}\,e^{-b/\sigma}\,\frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert\,\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}$
|
||||||
//
|
//
|
||||||
double common_factor = NEG_V0_AB_DIV_TWO_SIGMA * r_ab_norm_sum / (two_b * r_ab_norm * r_ab2_norm);
|
double scalar_mult_common = (r_ab_norm_sum / (two_b * r_ab_norm * r_ab2_norm)) * (NEG_V0_AB_div_TWO_SIGMA * exp(two_b * NEG_TWO_SIGMA_INV));
|
||||||
|
|
||||||
|
// # 1 MULT
|
||||||
|
//
|
||||||
|
// $\frac{-V^0_{\alpha\beta}}{2\sigma}\,e^{-b/\sigma}\,\frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert}$
|
||||||
|
//
|
||||||
|
double scalar_mult_r_ab = scalar_mult_common * r_ab2_norm;
|
||||||
|
|
||||||
|
// # 1 MULT
|
||||||
|
//
|
||||||
|
// $\frac{-V^0_{\alpha\beta}}{2\sigma}\,e^{-b/\sigma}\,\frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}$
|
||||||
|
//
|
||||||
|
double scalar_mult_r_ab2 = scalar_mult_common * r_ab_norm;
|
||||||
|
|
||||||
// # 2 ADDs, 4 MULTs
|
// # 2 ADDs, 4 MULTs
|
||||||
// $2\grad_{\vec{r}_{\alpha \beta}}b$
|
//
|
||||||
// See: derivations.jpg
|
// $\vec{-f}_{\alpha \beta}(\vec{r}_{\alpha \beta}) = \frac{-V^0_{\alpha \beta}}{2\sigma}e^{-b / \sigma} 2\grad_{\vec{r}_{\alpha \beta}}b $
|
||||||
Vector2d A = (r_ab * r_ab2_norm + r_ab2 * r_ab_norm) * common_factor;
|
//
|
||||||
|
//
|
||||||
|
// $= \frac{-V^0_{\alpha\beta}}{2\sigma}\,e^{-b/\sigma}\,\frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert}\,\vec{r}_{\alpha \beta} + \frac{-V^0_{\alpha\beta}}{2\sigma}\,e^{-b/\sigma}\,\frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}\,(\vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta)$
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// $2\grad_{\vec{r}_{\alpha \beta}}b = \frac{(\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert)(\frac{\vec{r}_{\alpha \beta}}{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert} + \frac{\vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta}{\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert})}{2b}$
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// $= \frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert}\,\vec{r}_{\alpha \beta} + \frac{\Arrowvert \vec{r}_{\alpha \beta} \Arrowvert + \Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}{2b\,\Arrowvert \vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta \Arrowvert}\,(\vec{r}_{\alpha \beta} - v_\beta \Delta t \vec{e}_\beta)$
|
||||||
|
//
|
||||||
|
//
|
||||||
|
Vector2d f_alpha_beta_neg = r_ab * scalar_mult_r_ab + r_ab2 * scalar_mult_r_ab2;
|
||||||
|
|
||||||
// # 4 MULTs, 1 EXP
|
// # 2 ADDs, 7 MULTs, 1 SQRT
|
||||||
// $-\vec{f}_{\alpha \beta}(\vec{r}_{\alpha \beta})$
|
|
||||||
//
|
//
|
||||||
// $= \frac{-V^0_{\alpha \beta}}{\sigma}e^{-b / \sigma} \grad_{\vec{r}_{\alpha \beta}}b$
|
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}).dt = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta})).\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}).dt$
|
||||||
//
|
//
|
||||||
//
|
Vector2d f_alpha_beta_dt = f_alpha_beta_neg * w(alpha.e, f_alpha_beta_neg);
|
||||||
// $= \frac{-V^0_{\alpha \beta}}{2\sigma}e^{-b / \sigma} 2\grad_{\vec{r}_{\alpha \beta}}b$
|
|
||||||
//
|
|
||||||
Vector2d neg_f_ped = A * exp(two_b * NEG_ONE_DIV_TWO_SIGMA);
|
|
||||||
|
|
||||||
// # 4 ADDs, 7 MULTs, 1 SQRT
|
return f_alpha_beta_dt;
|
||||||
//
|
|
||||||
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{\beta}) = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta})\vec{f}_{\alpha\beta}(\vec{r}_{\alpha} - \vec{r}_{\beta})$
|
|
||||||
//
|
|
||||||
p->f_i -= neg_f_ped * w(p->e, neg_f_ped);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// # board.n_obstacles * (12 ADDs, 18 MULTs, 1 DIV, 2 SQRTs, 1 EXP)
|
|
||||||
//
|
|
||||||
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{B})$
|
|
||||||
//
|
|
||||||
void add_repulsive_effects_from_borders(Board &board, Pedestrian *p) {
|
|
||||||
|
|
||||||
Obstacle *obstacles = board.obstacles;
|
// Flop count: 10 ADDs, 18 MULTs, 1 DIVs, 2 SQRTs, 1 EXP
|
||||||
int n_borders = board.n_obstacles;
|
Vector2d obstacle_repulsion_dt(const Pedestrian &alpha, const Obstacle &B) {
|
||||||
|
|
||||||
for (int i = 0; i < n_borders; i++) {
|
|
||||||
|
|
||||||
Obstacle obstacle = obstacles[i];
|
|
||||||
|
|
||||||
// # 7 ADDs, 5 MULTs
|
// # 7 ADDs, 5 MULTs
|
||||||
// $\vec{r}_{\alpha B}$
|
// $\vec{r}_{\alpha B}$
|
||||||
Vector2d r_aB = p->curr_pos - obstacle.find_nearest_obstacle_point(p->curr_pos);
|
Vector2d r_aB = alpha.curr_pos - B.find_nearest_obstacle_point(alpha.curr_pos);
|
||||||
|
|
||||||
// # 1 ADD, 2 MULTs, 1 SQRT
|
// # 1 ADD, 2 MULTs, 1 SQRT
|
||||||
// $\Arrowvert \vec{r}_{\alpha B} \Arrowvert$
|
// $\Arrowvert \vec{r}_{\alpha B} \Arrowvert$
|
||||||
double r_aB_norm = r_aB.norm();
|
double r_aB_norm = r_aB.norm();
|
||||||
|
r_aB_norm = r_aB_norm > EPS ? r_aB_norm : EPS; // Avoid division by zero
|
||||||
|
|
||||||
// # 4 MULTs, 1 DIV, 1 EXP
|
// # 2 MULTs, 1 DIV, 1 EXP
|
||||||
|
//
|
||||||
|
// $\frac{-U^0_{\alpha B}}{R} e^{- \Arrowvert \vec{r}_{\alpha B} \Arrowvert / R}$
|
||||||
|
//
|
||||||
|
double scalar_mult = (NEG_U0_AB_div_R * exp(r_aB_norm * NEG_R_INV)) / r_aB_norm;
|
||||||
|
|
||||||
|
// # 2 MULTs
|
||||||
//
|
//
|
||||||
// $-\vec{F}_{\alpha B}(\vec{r}_{\alpha B}) = \frac{-U^0_{\alpha B}}{R} e^{- \Arrowvert \vec{r}_{\alpha B} \Arrowvert / R} \frac{\vec{r}_{\alpha B}}{\Arrowvert \vec{r}_{\alpha B} \Arrowvert}$
|
// $-\vec{F}_{\alpha B}(\vec{r}_{\alpha B}) = \frac{-U^0_{\alpha B}}{R} e^{- \Arrowvert \vec{r}_{\alpha B} \Arrowvert / R} \frac{\vec{r}_{\alpha B}}{\Arrowvert \vec{r}_{\alpha B} \Arrowvert}$
|
||||||
//
|
//
|
||||||
Vector2d neg_f_obst = r_aB * (NEG_U0_AB_DIV_R * exp(r_aB_norm * NEG_R_INV) / r_aB_norm);
|
Vector2d f_alpha_B_neg = r_aB * scalar_mult;
|
||||||
|
|
||||||
// # 4 ADDs, 7 MULTs, 1 SQRT
|
// # 2 ADDs, 7 MULTs, 1 SQRT
|
||||||
//
|
//
|
||||||
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{ B}) = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B})\vec{F}_{\alpha B}(\vec{r}_{\alpha} - \vec{r}_{ B})$
|
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}).dt = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B})).\vec{F}_{\alpha B}(\vec{r}_{\alpha B}).dt$
|
||||||
//
|
//
|
||||||
p->f_i -= neg_f_obst * w(p->e, neg_f_obst);
|
Vector2d f_alpha_B_dt = f_alpha_B_neg * w(alpha.e, f_alpha_B_neg);
|
||||||
}
|
|
||||||
|
return f_alpha_B_dt;
|
||||||
}
|
}
|
||||||
|
|||||||
+102
-5
@@ -1,13 +1,110 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "board.hpp"
|
#include "board.hpp"
|
||||||
#include "pedestrian.hpp"
|
#include "pedestrian.hpp"
|
||||||
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
|
// Note: force contributions are multiplied by dt inside each function because
|
||||||
|
// this allows us to bake dt into precomputed constants and thus save two
|
||||||
|
// multiplications in the main loop. Conceptually, each returned term is the
|
||||||
|
// explicit-Euler approximation of the time integral of one force contribution
|
||||||
|
// over a single simulation step.
|
||||||
|
//
|
||||||
|
// This is valid because integration is linear: the integral of a sum is the sum
|
||||||
|
// of the integrals. Thus, summing F_i * dt for each contribution is equivalent
|
||||||
|
// to computing dt * sum(F_i) after all force contributions have been accumulated.
|
||||||
|
|
||||||
|
// Computes the direction-dependent perception weight from Eq. (7) in the paper:
|
||||||
|
//
|
||||||
|
// $w(\vec{e}_{\alpha}, \vec{f}) = \begin{cases} 1.0, & \text{if } \vec{e}_{\alpha} \cdot \vec{f} \geq \|\vec{f}\| \cos(\varphi), \\ c, & \text{otherwise}. \end{cases}$
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
// - $\vec{e}_{\alpha}$ is the desired direction of motion of the pedestrian,
|
||||||
|
// - $\vec{f}$ is the force under consideration (e.g., the repulsive force from another pedestrian or an obstacle),
|
||||||
|
// - $\varphi$ is half of the effective angle of sight,
|
||||||
|
// - $c$ is the reduced weight factor for situations outside the field of view.
|
||||||
|
//
|
||||||
|
// The function returns:
|
||||||
|
//
|
||||||
|
// $w(\vec{e}_{\alpha}, \vec{f}).(-dt)$
|
||||||
|
//
|
||||||
|
// Note: the reason we multiply the perception weight by -dt is:
|
||||||
|
// (1) the factor dt saves a multiplication by dt, in pedestrian_repulsion_dt and obstacle_repulsion_dt;
|
||||||
|
// (2) the minus sign is applied because the repulsion routines (pedestrian_repulsion_dt and obstacle_repulsion_dt) compute and
|
||||||
|
// pass the negated force direction to w(), matching the paper's w(e_alpha, -f) term. Returning -w() then converts that internally
|
||||||
|
// negated force back to the correct signed result vector without needing to perform any vector negation, in order to meet the paper's formula.
|
||||||
|
//
|
||||||
|
// @param e : The desired direction of motion of the pedestrian.
|
||||||
|
// @param f : The force under consideration (e.g., the repulsive force from another pedestrian or an obstacle).
|
||||||
|
// @return The direction-dependent perception weight multiplied by -dt.
|
||||||
|
//
|
||||||
double w(Vector2d e, Vector2d f);
|
double w(Vector2d e, Vector2d f);
|
||||||
|
|
||||||
double g(double v_max_alpha, double w_alpha_norm);
|
// Computes the acceleration term of the social force model
|
||||||
|
// for pedestrian alpha (Eq. (2) in the paper):
|
||||||
|
//
|
||||||
|
// $\vec{F}^0_\alpha = \frac{1}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
// - $v^0_\alpha$ is the desired speed,
|
||||||
|
// - $\vec{e}_\alpha$ is the desired direction,
|
||||||
|
// - $\vec{v}_\alpha$ is the current velocity,
|
||||||
|
// - $\tau_\alpha$ is the relaxation time.
|
||||||
|
//
|
||||||
|
// This function returns $\vec{F}^0_\alpha.dt$, i.e. the velocity increment caused by
|
||||||
|
// the acceleration term during one simulation time step:
|
||||||
|
//
|
||||||
|
// $\vec{F}^0_\alpha.dt = \frac{dt}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
|
||||||
|
//
|
||||||
|
// @param alpha : The pedestrian for which the acceleration contribution is computed.
|
||||||
|
// @return The velocity increment caused by the acceleration term during one simulation time step.
|
||||||
|
//
|
||||||
|
Vector2d acceleration_term_dt(const Pedestrian &alpha);
|
||||||
|
|
||||||
void add_acceleration_term(Pedestrian *p);
|
// Computes the repulsive pedestrian-pedestrian effect
|
||||||
|
// exerted by pedestrian beta on pedestrian alpha (Eq. (3) in the paper),
|
||||||
|
// weighted by the direction-dependent perception factor from Eq. (7)-(8):
|
||||||
|
//
|
||||||
|
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}) = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta})$
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
// - $\vec{r}_{\alpha \beta} = \vec{r}_\alpha - \vec{r}_\beta$ is the vector from beta's position to alpha's position,
|
||||||
|
// - $\vec{e}_\alpha$ is the desired direction of pedestrian alpha,
|
||||||
|
//
|
||||||
|
// - $\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}) = -\nabla_{\vec{r}_{\alpha\beta}} V_{\alpha\beta}\left[b(\vec{r}_{\alpha\beta})\right]$ is the repulsive effect from beta on alpha (The expanded formula details are given in the code comments given inside the function's implementation in forces.cpp),
|
||||||
|
//
|
||||||
|
// - $w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))$ the direction-dependent perception weighting function, for which the formula is detailed in the w() function above.
|
||||||
|
//
|
||||||
|
// This function returns $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}).dt$, i.e. the velocity increment caused by the repulsive pedestrian-pedestrian effect during one simulation time step:
|
||||||
|
//
|
||||||
|
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}).dt = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}).dt$
|
||||||
|
//
|
||||||
|
// @param alpha : The pedestrian receiving the repulsive effect.
|
||||||
|
// @param beta : The pedestrian producing the repulsive effect.
|
||||||
|
// @return The velocity increment caused by the repulsive effect from pedestrian beta on alpha during one
|
||||||
|
// simulation time step.
|
||||||
|
//
|
||||||
|
Vector2d pedestrian_repulsion_dt(const Pedestrian &alpha, const Pedestrian &beta);
|
||||||
|
|
||||||
void add_repulsive_effects_from_pedestrians(Pedestrian *P, Pedestrian *p, int N);
|
// Computes the repulsive effect exerted by obstacle B
|
||||||
|
// on pedestrian alpha (Eq. (5) in the paper), weighted by the direction-dependent perception factor from Eq. (7)-(8]:
|
||||||
void add_repulsive_effects_from_borders(Board &B, Pedestrian *p);
|
//
|
||||||
|
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}) = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))\vec{F}_{\alpha B}(\vec{r}_{\alpha B})$
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
// - $\vec{r}_{\alpha B} = \vec{r}_\alpha - \vec{r}^{\,\alpha}_B$ is the vector from the closest point on obstacle B (i.e., $\vec{r}^{\,\alpha}_B$) to pedestrian alpha's position,
|
||||||
|
// - $\vec{e}_\alpha$ is the desired direction of pedestrian alpha,
|
||||||
|
//
|
||||||
|
// - $\vec{F}_{\alpha B}(\vec{r}_{\alpha B}) = -\nabla_{\vec{r}_{\alpha B}} U_{\alpha B}(||\vec{r}_{\alpha B}||)$ is the repulsive effect from obstacle B on alpha (The expanded formula details are given in the code comments given inside the function's implementation in forces.cpp),
|
||||||
|
//
|
||||||
|
// - $w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))$ the direction-dependent perception weighting function, for which the formula is detailed in the w() function above.
|
||||||
|
//
|
||||||
|
// This function returns $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}).dt$, i.e. the velocity increment caused by the repulsive obstacle effect during one simulation time step:
|
||||||
|
//
|
||||||
|
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}).dt = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))\vec{F}_{\alpha B}(\vec{r}_{\alpha B}).dt$
|
||||||
|
//
|
||||||
|
// @param alpha : The pedestrian receiving the obstacle repulsive effect.
|
||||||
|
// @param B : The obstacle producing the repulsive effect.
|
||||||
|
// @return The velocity increment caused by the repulsive effect from obstacle B on alpha during one
|
||||||
|
// simulation time step.
|
||||||
|
//
|
||||||
|
Vector2d obstacle_repulsion_dt(const Pedestrian &alpha, const Obstacle &B);
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
|
|
||||||
#ifndef KPERF_H
|
#ifndef KPERF_H
|
||||||
#define KPERF_H
|
#define KPERF_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// #include <unistd.h> // for usleep()
|
// #include <unistd.h> // for usleep()
|
||||||
#include <dlfcn.h> // for dlopen() and dlsym()
|
#include <dlfcn.h> // for dlopen() and dlsym()
|
||||||
#include <mach/mach_time.h> // for mach_absolute_time()
|
|
||||||
#include <sys/kdebug.h> // for kdebug trace decode
|
|
||||||
#include <sys/sysctl.h> // for sysctl()
|
#include <sys/sysctl.h> // for sysctl()
|
||||||
|
#include <sys/kdebug.h> // for kdebug trace decode
|
||||||
|
#include <mach/mach_time.h> // for mach_absolute_time()
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// <kperf.framework> header (reverse engineered)
|
// <kperf.framework> header (reverse engineered)
|
||||||
@@ -253,7 +254,8 @@ static uint64_t (*kperf_ticks_to_ns)(uint64_t ticks);
|
|||||||
static uint64_t (*kperf_tick_frequency)(void);
|
static uint64_t (*kperf_tick_frequency)(void);
|
||||||
|
|
||||||
/// Get lightweight PET mode (not in kperf.framework).
|
/// Get lightweight PET mode (not in kperf.framework).
|
||||||
static int kperf_lightweight_pet_get(uint32_t *enabled) {
|
static int kperf_lightweight_pet_get(uint32_t *enabled)
|
||||||
|
{
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return -1;
|
return -1;
|
||||||
size_t size = 4;
|
size_t size = 4;
|
||||||
@@ -261,7 +263,10 @@ static int kperf_lightweight_pet_get(uint32_t *enabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set lightweight PET mode (not in kperf.framework).
|
/// Set lightweight PET mode (not in kperf.framework).
|
||||||
static int kperf_lightweight_pet_set(uint32_t enabled) { return sysctlbyname("kperf.lightweight_pet", NULL, NULL, &enabled, 4); }
|
static int kperf_lightweight_pet_set(uint32_t enabled)
|
||||||
|
{
|
||||||
|
return sysctlbyname("kperf.lightweight_pet", NULL, NULL, &enabled, 4);
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// <kperfdata.framework> header (reverse engineered)
|
// <kperfdata.framework> header (reverse engineered)
|
||||||
@@ -276,7 +281,8 @@ static int kperf_lightweight_pet_set(uint32_t enabled) { return sysctlbyname("kp
|
|||||||
#define KPEP_ARCH_ARM64 3
|
#define KPEP_ARCH_ARM64 3
|
||||||
|
|
||||||
/// KPEP event (size: 48/28 bytes on 64/32 bit OS)
|
/// KPEP event (size: 48/28 bytes on 64/32 bit OS)
|
||||||
typedef struct kpep_event {
|
typedef struct kpep_event
|
||||||
|
{
|
||||||
const char *name; ///< Unique name of a event, such as "INST_RETIRED.ANY".
|
const char *name; ///< Unique name of a event, such as "INST_RETIRED.ANY".
|
||||||
const char *description; ///< Description for this event.
|
const char *description; ///< Description for this event.
|
||||||
const char *errata; ///< Errata, currently NULL.
|
const char *errata; ///< Errata, currently NULL.
|
||||||
@@ -290,7 +296,8 @@ typedef struct kpep_event {
|
|||||||
} kpep_event;
|
} kpep_event;
|
||||||
|
|
||||||
/// KPEP database (size: 144/80 bytes on 64/32 bit OS)
|
/// KPEP database (size: 144/80 bytes on 64/32 bit OS)
|
||||||
typedef struct kpep_db {
|
typedef struct kpep_db
|
||||||
|
{
|
||||||
const char *name; ///< Database name, such as "haswell".
|
const char *name; ///< Database name, such as "haswell".
|
||||||
const char *cpu_id; ///< Plist name, such as "cpu_7_8_10b282dc".
|
const char *cpu_id; ///< Plist name, such as "cpu_7_8_10b282dc".
|
||||||
const char *marketing_name; ///< Marketing name, such as "Intel Haswell".
|
const char *marketing_name; ///< Marketing name, such as "Intel Haswell".
|
||||||
@@ -314,7 +321,8 @@ typedef struct kpep_db {
|
|||||||
} kpep_db;
|
} kpep_db;
|
||||||
|
|
||||||
/// KPEP config (size: 80/44 bytes on 64/32 bit OS)
|
/// KPEP config (size: 80/44 bytes on 64/32 bit OS)
|
||||||
typedef struct kpep_config {
|
typedef struct kpep_config
|
||||||
|
{
|
||||||
kpep_db *db;
|
kpep_db *db;
|
||||||
kpep_event **ev_arr; ///< (sizeof(kpep_event *) * counter_count), init NULL
|
kpep_event **ev_arr; ///< (sizeof(kpep_event *) * counter_count), init NULL
|
||||||
size_t *ev_map; ///< (sizeof(size_t *) * counter_count), init 0
|
size_t *ev_map; ///< (sizeof(size_t *) * counter_count), init 0
|
||||||
@@ -330,7 +338,8 @@ typedef struct kpep_config {
|
|||||||
} kpep_config;
|
} kpep_config;
|
||||||
|
|
||||||
/// Error code for kpep_config_xxx() and kpep_db_xxx() functions.
|
/// Error code for kpep_config_xxx() and kpep_db_xxx() functions.
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
KPEP_CONFIG_ERROR_NONE = 0,
|
KPEP_CONFIG_ERROR_NONE = 0,
|
||||||
KPEP_CONFIG_ERROR_INVALID_ARGUMENT = 1,
|
KPEP_CONFIG_ERROR_INVALID_ARGUMENT = 1,
|
||||||
KPEP_CONFIG_ERROR_OUT_OF_MEMORY = 2,
|
KPEP_CONFIG_ERROR_OUT_OF_MEMORY = 2,
|
||||||
@@ -351,11 +360,29 @@ typedef enum {
|
|||||||
} kpep_config_error_code;
|
} kpep_config_error_code;
|
||||||
|
|
||||||
/// Error description for kpep_config_error_code.
|
/// Error description for kpep_config_error_code.
|
||||||
static const char *kpep_config_error_names[KPEP_CONFIG_ERROR_MAX] = {"none", "invalid argument", "out of memory", "I/O", "buffer too small", "current system unknown", "database path invalid", "database not found", "database architecture unsupported", "database version unsupported", "database corrupt", "event not found", "conflicting events", "all counters must be forced", "event unavailable", "check errno"};
|
static const char *kpep_config_error_names[KPEP_CONFIG_ERROR_MAX] = {
|
||||||
|
"none",
|
||||||
|
"invalid argument",
|
||||||
|
"out of memory",
|
||||||
|
"I/O",
|
||||||
|
"buffer too small",
|
||||||
|
"current system unknown",
|
||||||
|
"database path invalid",
|
||||||
|
"database not found",
|
||||||
|
"database architecture unsupported",
|
||||||
|
"database version unsupported",
|
||||||
|
"database corrupt",
|
||||||
|
"event not found",
|
||||||
|
"conflicting events",
|
||||||
|
"all counters must be forced",
|
||||||
|
"event unavailable",
|
||||||
|
"check errno"};
|
||||||
|
|
||||||
/// Error description.
|
/// Error description.
|
||||||
static const char *kpep_config_error_desc(int code) {
|
static const char *kpep_config_error_desc(int code)
|
||||||
if (0 <= code && code < KPEP_CONFIG_ERROR_MAX) {
|
{
|
||||||
|
if (0 <= code && code < KPEP_CONFIG_ERROR_MAX)
|
||||||
|
{
|
||||||
return kpep_config_error_names[code];
|
return kpep_config_error_names[code];
|
||||||
}
|
}
|
||||||
return "unknown error";
|
return "unknown error";
|
||||||
@@ -482,13 +509,16 @@ static int (*kpep_event_description)(kpep_event *ev, const char **str_ptr);
|
|||||||
// load kperf/kperfdata dynamic library
|
// load kperf/kperfdata dynamic library
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
void **impl;
|
void **impl;
|
||||||
} lib_symbol;
|
} lib_symbol;
|
||||||
|
|
||||||
#define lib_nelems(x) (sizeof(x) / sizeof((x)[0]))
|
#define lib_nelems(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
#define lib_symbol_def(name) {#name, (void **)&name}
|
#define lib_symbol_def(name) \
|
||||||
|
{ \
|
||||||
|
#name, (void **)&name}
|
||||||
|
|
||||||
static const lib_symbol lib_symbols_kperf[] = {
|
static const lib_symbol lib_symbols_kperf[] = {
|
||||||
lib_symbol_def(kpc_pmu_version),
|
lib_symbol_def(kpc_pmu_version),
|
||||||
@@ -528,12 +558,34 @@ static const lib_symbol lib_symbols_kperf[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const lib_symbol lib_symbols_kperfdata[] = {
|
static const lib_symbol lib_symbols_kperfdata[] = {
|
||||||
lib_symbol_def(kpep_config_create), lib_symbol_def(kpep_config_free), lib_symbol_def(kpep_config_add_event), lib_symbol_def(kpep_config_remove_event), lib_symbol_def(kpep_config_force_counters), lib_symbol_def(kpep_config_events_count), lib_symbol_def(kpep_config_events), lib_symbol_def(kpep_config_kpc), lib_symbol_def(kpep_config_kpc_count), lib_symbol_def(kpep_config_kpc_classes), lib_symbol_def(kpep_config_kpc_map), lib_symbol_def(kpep_db_create),
|
lib_symbol_def(kpep_config_create),
|
||||||
lib_symbol_def(kpep_db_free), lib_symbol_def(kpep_db_name), lib_symbol_def(kpep_db_aliases_count), lib_symbol_def(kpep_db_aliases), lib_symbol_def(kpep_db_counters_count), lib_symbol_def(kpep_db_events_count), lib_symbol_def(kpep_db_events), lib_symbol_def(kpep_db_event), lib_symbol_def(kpep_event_name), lib_symbol_def(kpep_event_alias), lib_symbol_def(kpep_event_description),
|
lib_symbol_def(kpep_config_free),
|
||||||
|
lib_symbol_def(kpep_config_add_event),
|
||||||
|
lib_symbol_def(kpep_config_remove_event),
|
||||||
|
lib_symbol_def(kpep_config_force_counters),
|
||||||
|
lib_symbol_def(kpep_config_events_count),
|
||||||
|
lib_symbol_def(kpep_config_events),
|
||||||
|
lib_symbol_def(kpep_config_kpc),
|
||||||
|
lib_symbol_def(kpep_config_kpc_count),
|
||||||
|
lib_symbol_def(kpep_config_kpc_classes),
|
||||||
|
lib_symbol_def(kpep_config_kpc_map),
|
||||||
|
lib_symbol_def(kpep_db_create),
|
||||||
|
lib_symbol_def(kpep_db_free),
|
||||||
|
lib_symbol_def(kpep_db_name),
|
||||||
|
lib_symbol_def(kpep_db_aliases_count),
|
||||||
|
lib_symbol_def(kpep_db_aliases),
|
||||||
|
lib_symbol_def(kpep_db_counters_count),
|
||||||
|
lib_symbol_def(kpep_db_events_count),
|
||||||
|
lib_symbol_def(kpep_db_events),
|
||||||
|
lib_symbol_def(kpep_db_event),
|
||||||
|
lib_symbol_def(kpep_event_name),
|
||||||
|
lib_symbol_def(kpep_event_alias),
|
||||||
|
lib_symbol_def(kpep_event_description),
|
||||||
};
|
};
|
||||||
|
|
||||||
#define lib_path_kperf "/System/Library/PrivateFrameworks/kperf.framework/kperf"
|
#define lib_path_kperf "/System/Library/PrivateFrameworks/kperf.framework/kperf"
|
||||||
#define lib_path_kperfdata "/System/Library/PrivateFrameworks/kperfdata.framework/kperfdata"
|
#define lib_path_kperfdata \
|
||||||
|
"/System/Library/PrivateFrameworks/kperfdata.framework/kperfdata"
|
||||||
|
|
||||||
static bool lib_inited = false;
|
static bool lib_inited = false;
|
||||||
static bool lib_has_err = false;
|
static bool lib_has_err = false;
|
||||||
@@ -542,7 +594,8 @@ static char lib_err_msg[256];
|
|||||||
static void *lib_handle_kperf = NULL;
|
static void *lib_handle_kperf = NULL;
|
||||||
static void *lib_handle_kperfdata = NULL;
|
static void *lib_handle_kperfdata = NULL;
|
||||||
|
|
||||||
static void lib_deinit(void) {
|
static void lib_deinit(void)
|
||||||
|
{
|
||||||
lib_inited = false;
|
lib_inited = false;
|
||||||
lib_has_err = false;
|
lib_has_err = false;
|
||||||
if (lib_handle_kperf)
|
if (lib_handle_kperf)
|
||||||
@@ -551,19 +604,23 @@ static void lib_deinit(void) {
|
|||||||
dlclose(lib_handle_kperfdata);
|
dlclose(lib_handle_kperfdata);
|
||||||
lib_handle_kperf = NULL;
|
lib_handle_kperf = NULL;
|
||||||
lib_handle_kperfdata = NULL;
|
lib_handle_kperfdata = NULL;
|
||||||
for (size_t i = 0; i < lib_nelems(lib_symbols_kperf); i++) {
|
for (size_t i = 0; i < lib_nelems(lib_symbols_kperf); i++)
|
||||||
|
{
|
||||||
const lib_symbol *symbol = &lib_symbols_kperf[i];
|
const lib_symbol *symbol = &lib_symbols_kperf[i];
|
||||||
*symbol->impl = NULL;
|
*symbol->impl = NULL;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) {
|
for (size_t i = 0; i < lib_nelems(lib_symbols_kperfdata); i++)
|
||||||
|
{
|
||||||
const lib_symbol *symbol = &lib_symbols_kperfdata[i];
|
const lib_symbol *symbol = &lib_symbols_kperfdata[i];
|
||||||
*symbol->impl = NULL;
|
*symbol->impl = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool lib_init(void) {
|
static bool lib_init(void)
|
||||||
|
{
|
||||||
#define return_err() \
|
#define return_err() \
|
||||||
do { \
|
do \
|
||||||
|
{ \
|
||||||
lib_deinit(); \
|
lib_deinit(); \
|
||||||
lib_inited = true; \
|
lib_inited = true; \
|
||||||
lib_has_err = true; \
|
lib_has_err = true; \
|
||||||
@@ -575,30 +632,40 @@ static bool lib_init(void) {
|
|||||||
|
|
||||||
// load dynamic library
|
// load dynamic library
|
||||||
lib_handle_kperf = dlopen(lib_path_kperf, RTLD_LAZY);
|
lib_handle_kperf = dlopen(lib_path_kperf, RTLD_LAZY);
|
||||||
if (!lib_handle_kperf) {
|
if (!lib_handle_kperf)
|
||||||
snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperf.framework, message: %s.", dlerror());
|
{
|
||||||
|
snprintf(lib_err_msg, sizeof(lib_err_msg),
|
||||||
|
"Failed to load kperf.framework, message: %s.", dlerror());
|
||||||
return_err();
|
return_err();
|
||||||
}
|
}
|
||||||
lib_handle_kperfdata = dlopen(lib_path_kperfdata, RTLD_LAZY);
|
lib_handle_kperfdata = dlopen(lib_path_kperfdata, RTLD_LAZY);
|
||||||
if (!lib_handle_kperfdata) {
|
if (!lib_handle_kperfdata)
|
||||||
snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperfdata.framework, message: %s.", dlerror());
|
{
|
||||||
|
snprintf(lib_err_msg, sizeof(lib_err_msg),
|
||||||
|
"Failed to load kperfdata.framework, message: %s.", dlerror());
|
||||||
return_err();
|
return_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
// load symbol address from dynamic library
|
// load symbol address from dynamic library
|
||||||
for (size_t i = 0; i < lib_nelems(lib_symbols_kperf); i++) {
|
for (size_t i = 0; i < lib_nelems(lib_symbols_kperf); i++)
|
||||||
|
{
|
||||||
const lib_symbol *symbol = &lib_symbols_kperf[i];
|
const lib_symbol *symbol = &lib_symbols_kperf[i];
|
||||||
*symbol->impl = dlsym(lib_handle_kperf, symbol->name);
|
*symbol->impl = dlsym(lib_handle_kperf, symbol->name);
|
||||||
if (!*symbol->impl) {
|
if (!*symbol->impl)
|
||||||
snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperf function: %s.", symbol->name);
|
{
|
||||||
|
snprintf(lib_err_msg, sizeof(lib_err_msg),
|
||||||
|
"Failed to load kperf function: %s.", symbol->name);
|
||||||
return_err();
|
return_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) {
|
for (size_t i = 0; i < lib_nelems(lib_symbols_kperfdata); i++)
|
||||||
|
{
|
||||||
const lib_symbol *symbol = &lib_symbols_kperfdata[i];
|
const lib_symbol *symbol = &lib_symbols_kperfdata[i];
|
||||||
*symbol->impl = dlsym(lib_handle_kperfdata, symbol->name);
|
*symbol->impl = dlsym(lib_handle_kperfdata, symbol->name);
|
||||||
if (!*symbol->impl) {
|
if (!*symbol->impl)
|
||||||
snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperfdata function: %s.", symbol->name);
|
{
|
||||||
|
snprintf(lib_err_msg, sizeof(lib_err_msg),
|
||||||
|
"Failed to load kperfdata function: %s.", symbol->name);
|
||||||
return_err();
|
return_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -625,7 +692,8 @@ typedef uint64_t kd_buf_argtype;
|
|||||||
typedef uintptr_t kd_buf_argtype;
|
typedef uintptr_t kd_buf_argtype;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
kd_buf_argtype arg1;
|
kd_buf_argtype arg1;
|
||||||
kd_buf_argtype arg2;
|
kd_buf_argtype arg2;
|
||||||
@@ -654,7 +722,8 @@ typedef struct {
|
|||||||
/* only trace at most 4 types of events, at the code granularity */
|
/* only trace at most 4 types of events, at the code granularity */
|
||||||
#define KDBG_VALCHECK 0x00200000U
|
#define KDBG_VALCHECK 0x00200000U
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned int value1;
|
unsigned int value1;
|
||||||
unsigned int value2;
|
unsigned int value2;
|
||||||
@@ -662,7 +731,8 @@ typedef struct {
|
|||||||
unsigned int value4;
|
unsigned int value4;
|
||||||
} kd_regtype;
|
} kd_regtype;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
/* number of events that can fit in the buffers */
|
/* number of events that can fit in the buffers */
|
||||||
int nkdbufs;
|
int nkdbufs;
|
||||||
/* set if trace is disabled */
|
/* set if trace is disabled */
|
||||||
@@ -681,20 +751,23 @@ typedef struct {
|
|||||||
|
|
||||||
/// Clean up trace buffers and reset ktrace/kdebug/kperf.
|
/// Clean up trace buffers and reset ktrace/kdebug/kperf.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_reset(void) {
|
static int kdebug_reset(void)
|
||||||
|
{
|
||||||
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREMOVE};
|
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREMOVE};
|
||||||
return sysctl(mib, 3, NULL, NULL, NULL, 0);
|
return sysctl(mib, 3, NULL, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable and reinitialize the trace buffers.
|
/// Disable and reinitialize the trace buffers.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_reinit(void) {
|
static int kdebug_reinit(void)
|
||||||
|
{
|
||||||
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETUP};
|
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETUP};
|
||||||
return sysctl(mib, 3, NULL, NULL, NULL, 0);
|
return sysctl(mib, 3, NULL, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set debug filter.
|
/// Set debug filter.
|
||||||
static int kdebug_setreg(kd_regtype *kdr) {
|
static int kdebug_setreg(kd_regtype *kdr)
|
||||||
|
{
|
||||||
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETREG};
|
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETREG};
|
||||||
size_t size = sizeof(kd_regtype);
|
size_t size = sizeof(kd_regtype);
|
||||||
return sysctl(mib, 3, kdr, &size, NULL, 0);
|
return sysctl(mib, 3, kdr, &size, NULL, 0);
|
||||||
@@ -703,7 +776,8 @@ static int kdebug_setreg(kd_regtype *kdr) {
|
|||||||
/// Set maximum number of trace entries (kd_buf).
|
/// Set maximum number of trace entries (kd_buf).
|
||||||
/// Only allow allocation up to half the available memory (sane_size).
|
/// Only allow allocation up to half the available memory (sane_size).
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_trace_setbuf(int nbufs) {
|
static int kdebug_trace_setbuf(int nbufs)
|
||||||
|
{
|
||||||
int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETBUF, nbufs};
|
int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETBUF, nbufs};
|
||||||
return sysctl(mib, 4, NULL, NULL, NULL, 0);
|
return sysctl(mib, 4, NULL, NULL, NULL, 0);
|
||||||
}
|
}
|
||||||
@@ -711,14 +785,16 @@ static int kdebug_trace_setbuf(int nbufs) {
|
|||||||
/// Enable or disable kdebug trace.
|
/// Enable or disable kdebug trace.
|
||||||
/// Trace buffer must already be initialized.
|
/// Trace buffer must already be initialized.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_trace_enable(bool enable) {
|
static int kdebug_trace_enable(bool enable)
|
||||||
|
{
|
||||||
int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDENABLE, enable};
|
int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDENABLE, enable};
|
||||||
return sysctl(mib, 4, NULL, 0, NULL, 0);
|
return sysctl(mib, 4, NULL, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve trace buffer information from kernel.
|
/// Retrieve trace buffer information from kernel.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_get_bufinfo(kbufinfo_t *info) {
|
static int kdebug_get_bufinfo(kbufinfo_t *info)
|
||||||
|
{
|
||||||
if (!info)
|
if (!info)
|
||||||
return -1;
|
return -1;
|
||||||
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDGETBUF};
|
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDGETBUF};
|
||||||
@@ -731,7 +807,8 @@ static int kdebug_get_bufinfo(kbufinfo_t *info) {
|
|||||||
/// @param len Length of `buf` in bytes.
|
/// @param len Length of `buf` in bytes.
|
||||||
/// @param count Number of trace entries (kd_buf) obtained.
|
/// @param count Number of trace entries (kd_buf) obtained.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_trace_read(void *buf, size_t len, size_t *count) {
|
static int kdebug_trace_read(void *buf, size_t len, size_t *count)
|
||||||
|
{
|
||||||
if (count)
|
if (count)
|
||||||
*count = 0;
|
*count = 0;
|
||||||
if (!buf || !len)
|
if (!buf || !len)
|
||||||
@@ -752,7 +829,8 @@ static int kdebug_trace_read(void *buf, size_t len, size_t *count) {
|
|||||||
/// @param timeout_ms timeout milliseconds, 0 means wait forever.
|
/// @param timeout_ms timeout milliseconds, 0 means wait forever.
|
||||||
/// @param suc set true if new buffers filled.
|
/// @param suc set true if new buffers filled.
|
||||||
/// @return 0 on success.
|
/// @return 0 on success.
|
||||||
static int kdebug_wait(size_t timeout_ms, bool *suc) {
|
static int kdebug_wait(size_t timeout_ms, bool *suc)
|
||||||
|
{
|
||||||
if (timeout_ms == 0)
|
if (timeout_ms == 0)
|
||||||
return -1;
|
return -1;
|
||||||
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDBUFWAIT};
|
int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDBUFWAIT};
|
||||||
@@ -764,28 +842,42 @@ static int kdebug_wait(size_t timeout_ms, bool *suc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define EVENT_NAME_MAX 8
|
#define EVENT_NAME_MAX 8
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
const char *alias; /// name for print
|
const char *alias; /// name for print
|
||||||
const char *names[EVENT_NAME_MAX]; /// name from pmc db
|
const char *names[EVENT_NAME_MAX]; /// name from pmc db
|
||||||
} event_alias;
|
} event_alias;
|
||||||
|
|
||||||
/// Event names from /usr/share/kpep/<name>.plist
|
/// Event names from /usr/share/kpep/<name>.plist
|
||||||
static const event_alias profile_events[] = {{"cycles", {"FIXED_CYCLES"}}, {"instructions", {"FIXED_INSTRUCTIONS"}}, {"branches", {"INST_BRANCH"}}, {"branch-misses", {"BRANCH_MISPRED_NONSPEC"}}, {"retired_uops", {"RETIRE_UOP"}}, {"int_uops", {"MAP_INT_UOP"}}, {"simdfp_uops", {"MAP_SIMD_UOP"}}, {"loadstore_uops", {"MAP_LDST_UOP"}}};
|
static const event_alias profile_events[] = {
|
||||||
|
{"cycles", {"FIXED_CYCLES"}},
|
||||||
|
{"instructions", {"FIXED_INSTRUCTIONS"}},
|
||||||
|
{"branches", {"INST_BRANCH"}},
|
||||||
|
{"branch-misses", {"BRANCH_MISPRED_NONSPEC"}},
|
||||||
|
{"retired_uops", {"RETIRE_UOP"}},
|
||||||
|
{"int_uops", {"MAP_INT_UOP"}},
|
||||||
|
{"simdfp_uops", {"MAP_SIMD_UOP"}},
|
||||||
|
{"loadstore_uops", {"MAP_LDST_UOP"}}
|
||||||
|
};
|
||||||
|
|
||||||
static kpep_event *get_event(kpep_db *db, const event_alias *alias) {
|
static kpep_event *get_event(kpep_db *db, const event_alias *alias)
|
||||||
for (size_t j = 0; j < EVENT_NAME_MAX; j++) {
|
{
|
||||||
|
for (size_t j = 0; j < EVENT_NAME_MAX; j++)
|
||||||
|
{
|
||||||
const char *name = alias->names[j];
|
const char *name = alias->names[j];
|
||||||
if (!name)
|
if (!name)
|
||||||
break;
|
break;
|
||||||
kpep_event *ev = NULL;
|
kpep_event *ev = NULL;
|
||||||
if (kpep_db_event(db, name, &ev) == 0) {
|
if (kpep_db_event(db, name, &ev) == 0)
|
||||||
|
{
|
||||||
return ev;
|
return ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Events {
|
struct Events
|
||||||
|
{
|
||||||
kpc_config_t regs[KPC_MAX_COUNTERS];
|
kpc_config_t regs[KPC_MAX_COUNTERS];
|
||||||
size_t counter_map[KPC_MAX_COUNTERS];
|
size_t counter_map[KPC_MAX_COUNTERS];
|
||||||
uint64_t counters_0[KPC_MAX_COUNTERS];
|
uint64_t counters_0[KPC_MAX_COUNTERS];
|
||||||
@@ -799,14 +891,17 @@ struct Events {
|
|||||||
// #define EVENT_INIT {{0}, {0}, {0}, {0}, sizeof(profile_events) / sizeof(event_alias), false, false}
|
// #define EVENT_INIT {{0}, {0}, {0}, {0}, sizeof(profile_events) / sizeof(event_alias), false, false}
|
||||||
static struct Events events;
|
static struct Events events;
|
||||||
|
|
||||||
static bool kperf_init(void) {
|
static bool kperf_init(void)
|
||||||
if (events.init) {
|
{
|
||||||
|
if (events.init)
|
||||||
|
{
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
// events.ev_count = sizeof(profile_events) / sizeof(event_alias);
|
// events.ev_count = sizeof(profile_events) / sizeof(event_alias);
|
||||||
events.ev_count = 8;
|
events.ev_count = 8;
|
||||||
// load dylib
|
// load dylib
|
||||||
if (!lib_init()) {
|
if (!lib_init())
|
||||||
|
{
|
||||||
printf("Error: %s\n", lib_err_msg);
|
printf("Error: %s\n", lib_err_msg);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -814,7 +909,8 @@ static bool kperf_init(void) {
|
|||||||
|
|
||||||
// check permission
|
// check permission
|
||||||
int force_ctrs = 0;
|
int force_ctrs = 0;
|
||||||
if (kpc_force_all_ctrs_get(&force_ctrs)) {
|
if (kpc_force_all_ctrs_get(&force_ctrs))
|
||||||
|
{
|
||||||
printf("Permission denied, xnu/kpc requires root privileges.\n");
|
printf("Permission denied, xnu/kpc requires root privileges.\n");
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -822,7 +918,8 @@ static bool kperf_init(void) {
|
|||||||
int ret;
|
int ret;
|
||||||
// load pmc db
|
// load pmc db
|
||||||
kpep_db *db = NULL;
|
kpep_db *db = NULL;
|
||||||
if ((ret = kpep_db_create(NULL, &db))) {
|
if ((ret = kpep_db_create(NULL, &db)))
|
||||||
|
{
|
||||||
printf("Error: cannot load pmc database: %d.\n", ret);
|
printf("Error: cannot load pmc database: %d.\n", ret);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -833,13 +930,17 @@ static bool kperf_init(void) {
|
|||||||
// db->config_counter_count);
|
// db->config_counter_count);
|
||||||
// create a config
|
// create a config
|
||||||
kpep_config *cfg = NULL;
|
kpep_config *cfg = NULL;
|
||||||
if ((ret = kpep_config_create(db, &cfg))) {
|
if ((ret = kpep_config_create(db, &cfg)))
|
||||||
printf("Failed to create kpep config: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed to create kpep config: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
if ((ret = kpep_config_force_counters(cfg))) {
|
if ((ret = kpep_config_force_counters(cfg)))
|
||||||
printf("Failed to force counters: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed to force counters: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
@@ -847,10 +948,12 @@ static bool kperf_init(void) {
|
|||||||
// get events
|
// get events
|
||||||
kpep_event *ev_arr[events.ev_count];
|
kpep_event *ev_arr[events.ev_count];
|
||||||
memset(ev_arr, 0, events.ev_count * sizeof(ev_arr[0]));
|
memset(ev_arr, 0, events.ev_count * sizeof(ev_arr[0]));
|
||||||
for (size_t i = 0; i < events.ev_count; i++) {
|
for (size_t i = 0; i < events.ev_count; i++)
|
||||||
|
{
|
||||||
const event_alias *alias = profile_events + i;
|
const event_alias *alias = profile_events + i;
|
||||||
ev_arr[i] = get_event(db, alias);
|
ev_arr[i] = get_event(db, alias);
|
||||||
if (!ev_arr[i]) {
|
if (!ev_arr[i])
|
||||||
|
{
|
||||||
printf("Cannot find event: %s.\n", alias->alias);
|
printf("Cannot find event: %s.\n", alias->alias);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -858,10 +961,13 @@ static bool kperf_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add event to config
|
// add event to config
|
||||||
for (size_t i = 0; i < events.ev_count; i++) {
|
for (size_t i = 0; i < events.ev_count; i++)
|
||||||
|
{
|
||||||
kpep_event *ev = ev_arr[i];
|
kpep_event *ev = ev_arr[i];
|
||||||
if ((ret = kpep_config_add_event(cfg, &ev, 0, NULL))) {
|
if ((ret = kpep_config_add_event(cfg, &ev, 0, NULL)))
|
||||||
printf("Failed to add event: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed to add event: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
@@ -870,35 +976,46 @@ static bool kperf_init(void) {
|
|||||||
// prepare buffer and config
|
// prepare buffer and config
|
||||||
// uint32_t classes = 0;
|
// uint32_t classes = 0;
|
||||||
size_t reg_count = 0;
|
size_t reg_count = 0;
|
||||||
if ((ret = kpep_config_kpc_classes(cfg, &events.classes))) {
|
if ((ret = kpep_config_kpc_classes(cfg, &events.classes)))
|
||||||
printf("Failed get kpc classes: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed get kpc classes: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
if ((ret = kpep_config_kpc_count(cfg, ®_count))) {
|
if ((ret = kpep_config_kpc_count(cfg, ®_count)))
|
||||||
printf("Failed get kpc count: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed get kpc count: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
if ((ret = kpep_config_kpc_map(cfg, events.counter_map, sizeof(events.counter_map)))) {
|
if ((ret = kpep_config_kpc_map(cfg, events.counter_map, sizeof(events.counter_map))))
|
||||||
printf("Failed get kpc map: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed get kpc map: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
if ((ret = kpep_config_kpc(cfg, events.regs, sizeof(events.regs)))) {
|
if ((ret = kpep_config_kpc(cfg, events.regs, sizeof(events.regs))))
|
||||||
printf("Failed get kpc registers: %d (%s).\n", ret, kpep_config_error_desc(ret));
|
{
|
||||||
|
printf("Failed get kpc registers: %d (%s).\n", ret,
|
||||||
|
kpep_config_error_desc(ret));
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set config to kernel
|
// set config to kernel
|
||||||
if ((ret = kpc_force_all_ctrs_set(1))) {
|
if ((ret = kpc_force_all_ctrs_set(1)))
|
||||||
|
{
|
||||||
printf("Failed force all ctrs: %d.\n", ret);
|
printf("Failed force all ctrs: %d.\n", ret);
|
||||||
// events.worked = false;
|
// events.worked = false;
|
||||||
// return events.worked;
|
// return events.worked;
|
||||||
}
|
}
|
||||||
if ((events.classes & KPC_CLASS_CONFIGURABLE_MASK) && reg_count) {
|
if ((events.classes & KPC_CLASS_CONFIGURABLE_MASK) && reg_count)
|
||||||
if ((ret = kpc_set_config(events.classes, events.regs))) {
|
{
|
||||||
|
if ((ret = kpc_set_config(events.classes, events.regs)))
|
||||||
|
{
|
||||||
printf("Failed set kpc config: %d.\n", ret);
|
printf("Failed set kpc config: %d.\n", ret);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -906,12 +1023,14 @@ static bool kperf_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start counting
|
// start counting
|
||||||
if ((ret = kpc_set_counting(events.classes))) {
|
if ((ret = kpc_set_counting(events.classes)))
|
||||||
|
{
|
||||||
printf("Failed set counting: %d.\n", ret);
|
printf("Failed set counting: %d.\n", ret);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
}
|
}
|
||||||
if ((ret = kpc_set_thread_counting(events.classes))) {
|
if ((ret = kpc_set_thread_counting(events.classes)))
|
||||||
|
{
|
||||||
printf("Failed set thread counting: %d.\n", ret);
|
printf("Failed set thread counting: %d.\n", ret);
|
||||||
events.worked = false;
|
events.worked = false;
|
||||||
return events.worked;
|
return events.worked;
|
||||||
@@ -924,7 +1043,8 @@ static bool kperf_init(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct performance_counters {
|
struct performance_counters
|
||||||
|
{
|
||||||
double cycles;
|
double cycles;
|
||||||
double instructions;
|
double instructions;
|
||||||
double branches;
|
double branches;
|
||||||
@@ -935,22 +1055,35 @@ struct performance_counters {
|
|||||||
double loadstore_uops;
|
double loadstore_uops;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct performance_counters kperf_get_counters() {
|
static struct performance_counters kperf_get_counters()
|
||||||
|
{
|
||||||
static bool warned = false;
|
static bool warned = false;
|
||||||
int ret;
|
int ret;
|
||||||
// get counters before
|
// get counters before
|
||||||
if ((ret = kpc_get_thread_counters(0, KPC_MAX_COUNTERS, events.counters_0))) {
|
if ((ret = kpc_get_thread_counters(0, KPC_MAX_COUNTERS, events.counters_0)))
|
||||||
if (!warned) {
|
{
|
||||||
|
if (!warned)
|
||||||
|
{
|
||||||
printf("Failed get thread counters\n");
|
printf("Failed get thread counters\n");
|
||||||
warned = true;
|
warned = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct performance_counters counters = {(double)events.counters_0[events.counter_map[0]], (double)events.counters_0[events.counter_map[1]], (double)events.counters_0[events.counter_map[2]], (double)events.counters_0[events.counter_map[3]], (double)events.counters_0[events.counter_map[4]], (double)events.counters_0[events.counter_map[5]], (double)events.counters_0[events.counter_map[6]], (double)events.counters_0[events.counter_map[7]]};
|
struct performance_counters counters = {
|
||||||
|
(double)events.counters_0[events.counter_map[0]],
|
||||||
|
(double)events.counters_0[events.counter_map[1]],
|
||||||
|
(double)events.counters_0[events.counter_map[2]],
|
||||||
|
(double)events.counters_0[events.counter_map[3]],
|
||||||
|
(double)events.counters_0[events.counter_map[4]],
|
||||||
|
(double)events.counters_0[events.counter_map[5]],
|
||||||
|
(double)events.counters_0[events.counter_map[6]],
|
||||||
|
(double)events.counters_0[events.counter_map[7]]
|
||||||
|
};
|
||||||
|
|
||||||
return counters;
|
return counters;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_cur_cpu() {
|
static int get_cur_cpu()
|
||||||
|
{
|
||||||
int curcpu = -1;
|
int curcpu = -1;
|
||||||
uint64_t buf[32];
|
uint64_t buf[32];
|
||||||
kpc_get_cpu_counters(false, events.classes, &curcpu, buf);
|
kpc_get_cpu_counters(false, events.classes, &curcpu, buf);
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
#include "board.hpp"
|
#include "board.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "forces.hpp"
|
|
||||||
#include "math.h"
|
|
||||||
#include "pedestrian.hpp"
|
#include "pedestrian.hpp"
|
||||||
#include "vector2d.hpp"
|
#include "social_force.hpp"
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
#include <algorithm>
|
||||||
#include <random>
|
#include <cstdio>
|
||||||
#include <tuple>
|
#include <cstdlib>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef BENCHMARK
|
||||||
|
// Benchmarking is disabled by default.
|
||||||
|
#define BENCHMARK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
#include "tsc_x86.h"
|
#include "tsc_x86.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -18,98 +21,25 @@
|
|||||||
#include "kperf.h"
|
#include "kperf.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SEED 42
|
#define N_OBSTACLES 2
|
||||||
|
#define WARMUP_ITERS 5
|
||||||
#define BOARD_WIDTH 10.0
|
#define NUM_REPS 11
|
||||||
#define BOARD_LENGTH 50.0
|
|
||||||
|
|
||||||
#define BOARD_LENGTH_MINUS_ONE BOARD_LENGTH - 1
|
|
||||||
#define BOARD_LENGTH_MINUS_ONE_POINT_FIVE BOARD_LENGTH - 1.5
|
|
||||||
#define WALL_EPS 1e-3
|
|
||||||
#define BOARD_WIDTH_MINUS_WALL_EPS BOARD_WIDTH - WALL_EPS
|
|
||||||
#define BOARD_LENGTH_HALF BOARD_LENGTH / 2
|
|
||||||
#define BOARD_LENGTH_MINUS_TWO BOARD_LENGTH - 2
|
|
||||||
#define BOARD_WIDTH_MINUS_ZERO_POINT_THREE BOARD_WIDTH - 0.3
|
|
||||||
#define BOARD_LENGTH_PLUS_THIRTY BOARD_LENGTH + 30
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
Pedestrian *init_pedestrians(int n_p) {
|
|
||||||
|
|
||||||
Pedestrian *P = new Pedestrian[n_p];
|
|
||||||
|
|
||||||
// Platform-specific non-deterministic random number generator (if available)
|
|
||||||
// random_device rd;
|
|
||||||
// mt19937 gen(rd()); // Use rd for true randomness
|
|
||||||
|
|
||||||
mt19937 gen(SEED); // Use fixed seed for reproducibility
|
|
||||||
|
|
||||||
normal_distribution<double> velocity_gen(V0_MEAN, V0_STD_DEV);
|
|
||||||
uniform_real_distribution<double> x_gen_left(2, BOARD_LENGTH_HALF);
|
|
||||||
uniform_real_distribution<double> x_gen_right(BOARD_LENGTH_HALF, BOARD_LENGTH_MINUS_TWO);
|
|
||||||
uniform_real_distribution<double> y_gen(0.3, BOARD_WIDTH_MINUS_ZERO_POINT_THREE);
|
|
||||||
|
|
||||||
const int n_p_half = n_p / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < n_p_half; i++) {
|
|
||||||
|
|
||||||
double x = x_gen_left(gen);
|
|
||||||
double y = y_gen(gen);
|
|
||||||
|
|
||||||
Vector2d start_pos = Vector2d(x, y);
|
|
||||||
Vector2d destination = Vector2d(BOARD_LENGTH_PLUS_THIRTY, y);
|
|
||||||
|
|
||||||
Vector2d init_e = destination - start_pos;
|
|
||||||
double init_e_norm = init_e.norm();
|
|
||||||
Vector2d init_e_unit = init_e / init_e_norm;
|
|
||||||
|
|
||||||
double desired_v0;
|
|
||||||
|
|
||||||
// Avoid values are too small since they would make the pedestrians walk backwards
|
|
||||||
do {
|
|
||||||
desired_v0 = velocity_gen(gen);
|
|
||||||
} while (desired_v0 <= 0.1);
|
|
||||||
|
|
||||||
double v_max = 1.3 * desired_v0;
|
|
||||||
|
|
||||||
P[i] = Pedestrian(WHITE, Vector2d(), start_pos, init_e_unit, v_max, desired_v0, destination, Vector2d());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = n_p_half; i < n_p; i++) {
|
|
||||||
|
|
||||||
double x = x_gen_right(gen);
|
|
||||||
double y = y_gen(gen);
|
|
||||||
|
|
||||||
Vector2d start_pos = Vector2d(x, y);
|
|
||||||
Vector2d destination = Vector2d(-30, y);
|
|
||||||
|
|
||||||
Vector2d init_e = destination - start_pos;
|
|
||||||
double init_e_norm = init_e.norm();
|
|
||||||
Vector2d init_e_unit = init_e / init_e_norm;
|
|
||||||
|
|
||||||
double desired_v0;
|
|
||||||
do {
|
|
||||||
desired_v0 = velocity_gen(gen);
|
|
||||||
} while (desired_v0 <= 0.1);
|
|
||||||
|
|
||||||
double v_max = 1.3 * desired_v0;
|
|
||||||
|
|
||||||
P[i] = Pedestrian(BLACK, Vector2d(), start_pos, init_e_unit, v_max, desired_v0, destination, Vector2d());
|
|
||||||
}
|
|
||||||
|
|
||||||
return P;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
|
// Check that the correct number of arguments has been passed.
|
||||||
// Ensure that arguments are passed
|
//
|
||||||
|
// Expected usage:
|
||||||
|
//
|
||||||
|
// ./main <number_of_pedestrians> <number_of_iterations> [csv_output_path]
|
||||||
|
//
|
||||||
|
// The optional third argument enables CSV dumping for visualization.
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
printf("Usage: ./sim <number_of_pedestrians> <number_of_iterations>\n");
|
printf("Usage: ./main <number_of_pedestrians> <number_of_iterations> [csv_output_path]\n");
|
||||||
printf("Each iteration takes %.2f seconds in the simulation.\n", DT);
|
printf("Each iteration takes %.2f seconds in the simulation.\n", dt);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command-line arguments
|
// Parse command-line arguments.
|
||||||
int n_p = atoi(argv[1]);
|
int n_p = atoi(argv[1]);
|
||||||
int n_iter = atoi(argv[2]);
|
int n_iter = atoi(argv[2]);
|
||||||
|
|
||||||
@@ -123,167 +53,118 @@ int main(int argc, const char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Obstacle obs[] = {};
|
// If provided, dump_path is used to write simulation states to CSV.
|
||||||
Board board = Board(BOARD_LENGTH, BOARD_WIDTH, 0, obs);
|
// In benchmark mode, this path is intentionally ignored to avoid measuring
|
||||||
|
// file I/O overhead.
|
||||||
// Logic for importing/exporting the simulation data
|
|
||||||
const int dump_interval = 1;
|
|
||||||
const char *dump_path = (argc >= 4) ? argv[3] : nullptr;
|
const char *dump_path = (argc >= 4) ? argv[3] : nullptr;
|
||||||
|
|
||||||
vector<tuple<int, int, double, double, double, double, int>> samples; // t, id, x, y, e.x, e.y, color
|
#if BENCHMARK
|
||||||
if (dump_path) {
|
|
||||||
samples.reserve((size_t)n_iter / dump_interval * n_p);
|
#ifdef __aarch64__
|
||||||
|
// Initialize ARM performance counters.
|
||||||
|
kperf_init();
|
||||||
|
struct performance_counters startperf, endperf;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Warmup runs are not measured.
|
||||||
|
// Each run gets a fresh initialized state because run_simulation mutates the
|
||||||
|
// pedestrians and board state.
|
||||||
|
for (int i = 0; i < WARMUP_ITERS; i++) {
|
||||||
|
Pedestrian *P = init_pedestrians(n_p);
|
||||||
|
Board board = init_board_without_obstacles();
|
||||||
|
|
||||||
|
run_simulation(P, board, n_p, n_iter, nullptr);
|
||||||
|
|
||||||
|
delete[] P;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pedestrian *pedestrians = init_pedestrians(n_p);
|
// Store the cycle count of each measured repetition.
|
||||||
|
// The median is then used as the final timing result.
|
||||||
|
std::vector<long double> cycles_v;
|
||||||
|
cycles_v.reserve(NUM_REPS);
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_REPS; i++) {
|
||||||
|
Pedestrian *P = init_pedestrians(n_p);
|
||||||
|
Board board = init_board_without_obstacles();
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
uint64_t start = start_tsc();
|
myInt64 start = start_tsc();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
kperf_init();
|
|
||||||
struct performance_counters startperf, endperf;
|
|
||||||
startperf = kperf_get_counters();
|
startperf = kperf_get_counters();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int t = 0; t < n_iter; t++) {
|
// Disable CSV dumping during benchmarking to avoid measuring file I/O
|
||||||
|
// overhead. Initialization is also excluded from the measured region.
|
||||||
// Pass 1: Conmpute all the forces given the current state
|
run_simulation(P, board, n_p, n_iter, nullptr);
|
||||||
for (int i = 0; i < n_p; i++) {
|
|
||||||
|
|
||||||
Pedestrian &p = pedestrians[i];
|
|
||||||
|
|
||||||
p.f_i.reset();
|
|
||||||
|
|
||||||
// # 4 ADDs, 4 MULTs
|
|
||||||
//
|
|
||||||
// $\vec{F}_\alpha(\vec{v}_\alpha, v^0_\alpha \vec{e}_\alpha) = \frac{1}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
|
|
||||||
//
|
|
||||||
add_acceleration_term(&p);
|
|
||||||
|
|
||||||
// # (n_p - 1) * (14 ADDs, 23 MULTs, 1 DIV, 4 SQRTs, 1 EXP)
|
|
||||||
//
|
|
||||||
// $\sum_{\beta} \vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{\beta})$
|
|
||||||
//
|
|
||||||
add_repulsive_effects_from_pedestrians(pedestrians, &p, n_p);
|
|
||||||
|
|
||||||
// # board.n_obstacles * (12 ADDs, 18 MULTs, 1 DIV, 2 SQRTs, 1 EXP)
|
|
||||||
//
|
|
||||||
// $\sum_{B} \vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{B})$
|
|
||||||
//
|
|
||||||
add_repulsive_effects_from_borders(board, &p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass 2: Integrate the forces and update the current state
|
|
||||||
for (int i = 0; i < n_p; i++) {
|
|
||||||
|
|
||||||
Pedestrian &p = pedestrians[i];
|
|
||||||
|
|
||||||
// # 2 ADDs
|
|
||||||
//
|
|
||||||
// $\frac{d\vec{w_\alpha}}{dt} = \vec{F}_{\alpha}(t)$
|
|
||||||
//
|
|
||||||
//
|
|
||||||
p.w = p.w + p.f_i;
|
|
||||||
|
|
||||||
// # 1 ADD, 2 MULTs, 1 SQRT
|
|
||||||
double w_norm = p.w.norm();
|
|
||||||
|
|
||||||
p.curr_vel = p.w;
|
|
||||||
|
|
||||||
// # Assumption for calculating W(n): The "if" statement always evaluates to true.
|
|
||||||
// # This is not true in practice, but we assume the worst-case scenario for the flop count calculation.
|
|
||||||
if (w_norm > p.v_max) {
|
|
||||||
|
|
||||||
// # 2 MULTs, 1 DIV
|
|
||||||
p.curr_vel = p.curr_vel * (p.v_max / w_norm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # 2 ADDs, 2 MULTs
|
|
||||||
//
|
|
||||||
// $\vec{r}_i^{t+1} = \vec{r}^t_i + v^t_idt$
|
|
||||||
//
|
|
||||||
p.curr_pos = p.curr_pos + p.curr_vel * DT;
|
|
||||||
|
|
||||||
// When a pedestrian reaches the end of the hallway we simply "teleport" the person back to the beginning.
|
|
||||||
// This way, we have a constant flow of pedestrians, and it is easier to see lanes forming.
|
|
||||||
if (p.color == WHITE && p.curr_pos.x > BOARD_LENGTH_MINUS_ONE_POINT_FIVE) {
|
|
||||||
|
|
||||||
// keep y, reset x
|
|
||||||
p.curr_pos.x = 1.0;
|
|
||||||
p.destination.y = p.curr_pos.y;
|
|
||||||
|
|
||||||
p.w = Vector2d(p.desired_v0, 0);
|
|
||||||
p.curr_vel = p.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.color == BLACK && p.curr_pos.x < 1.5) {
|
|
||||||
p.curr_pos.x = BOARD_LENGTH_MINUS_ONE;
|
|
||||||
p.destination.y = p.curr_pos.y;
|
|
||||||
p.w = Vector2d(-p.desired_v0, 0);
|
|
||||||
p.curr_vel = p.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.curr_pos.y <= 0.0) {
|
|
||||||
p.curr_pos.y = WALL_EPS;
|
|
||||||
if (p.curr_vel.y < 0)
|
|
||||||
p.curr_vel.y = 0;
|
|
||||||
if (p.w.y < 0)
|
|
||||||
p.w.y = 0;
|
|
||||||
}
|
|
||||||
if (p.curr_pos.y >= BOARD_WIDTH) {
|
|
||||||
p.curr_pos.y = BOARD_WIDTH_MINUS_WALL_EPS;
|
|
||||||
if (p.curr_vel.y > 0)
|
|
||||||
p.curr_vel.y = 0;
|
|
||||||
if (p.w.y > 0)
|
|
||||||
p.w.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// # 2 ADDs
|
|
||||||
Vector2d e = p.destination - p.curr_pos;
|
|
||||||
|
|
||||||
// # 1 ADD, 2 MULTs, 1 DIV, 1 SQRT
|
|
||||||
double e_norm_inv = 1.0 / e.norm();
|
|
||||||
|
|
||||||
// # 2 MULTs
|
|
||||||
p.e = e * e_norm_inv;
|
|
||||||
|
|
||||||
// # 1 ADD, 3 MULTs, 1 SQRT
|
|
||||||
double s_b = p.curr_vel.norm() * DELTA_T;
|
|
||||||
|
|
||||||
// # 2 MULTs
|
|
||||||
p.s_b_e_b = p.e * s_b;
|
|
||||||
|
|
||||||
// # 1 MULT
|
|
||||||
p.s_b_sq = s_b * s_b;
|
|
||||||
|
|
||||||
// Save the data for visualization
|
|
||||||
if (dump_path && (t % dump_interval == 0)) {
|
|
||||||
samples.emplace_back(t, i, p.curr_pos.x, p.curr_pos.y, p.e.x, p.e.y, p.color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
long double T = stop_tsc(start);
|
long double cycles = stop_tsc(start);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
endperf = kperf_get_counters();
|
endperf = kperf_get_counters();
|
||||||
long double T = endperf.cycles - startperf.cycles;
|
long double cycles = endperf.cycles - startperf.cycles;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
long long add_count_per_iter = n_p * (4 + 14 * (n_p - 1) + 12 * board.n_obstacles) + n_p * 9;
|
delete[] P;
|
||||||
long long mult_count_per_iter = n_p * (4 + 23 * (n_p - 1) + 18 * board.n_obstacles) + n_p * 16;
|
|
||||||
long long div_count_per_iter = n_p * (n_p - 1 + board.n_obstacles) + n_p * 2;
|
|
||||||
long long sqrt_count_per_iter = n_p * (4 * (n_p - 1) + 2 * board.n_obstacles) + n_p * 3;
|
|
||||||
long long exp_count_per_iter = n_p * (n_p - 1 + board.n_obstacles);
|
|
||||||
|
|
||||||
long double W_per_iter = add_count_per_iter * COST_ADD + mult_count_per_iter * COST_MULT + div_count_per_iter * COST_DIV + sqrt_count_per_iter * COST_SQRT + exp_count_per_iter * COST_EXP;
|
cycles_v.push_back(cycles);
|
||||||
long long Q_per_iter = sizeof(Pedestrian) * n_p + sizeof(Obstacle) * board.n_obstacles; // TODO: Refine this
|
}
|
||||||
|
|
||||||
|
// Use the median cycle count to reduce the effect of noisy measurements.
|
||||||
|
std::sort(cycles_v.begin(), cycles_v.end());
|
||||||
|
long double T = cycles_v[NUM_REPS / 2];
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Normal execution mode. If dump_path is provided, the simulation writes a CSV
|
||||||
|
// file that can later be used to generate a video of the simulation.
|
||||||
|
Pedestrian *P = init_pedestrians(n_p);
|
||||||
|
Board board = init_board_without_obstacles();
|
||||||
|
run_simulation(P, board, n_p, n_iter, dump_path);
|
||||||
|
|
||||||
|
delete[] P;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BENCHMARK
|
||||||
|
|
||||||
|
// Flop-count per simulation iteration.
|
||||||
|
//
|
||||||
|
// The total work W(n) is estimated from the number of additions,
|
||||||
|
// multiplications, divisions, square roots, and exponentials performed by the
|
||||||
|
// simulation kernels.
|
||||||
|
long long add_count_per_iter = n_p * (4 + (n_p - 1) * 14 + N_OBSTACLES * 12) + n_p * 7;
|
||||||
|
|
||||||
|
long long mult_count_per_iter = n_p * (4 + (n_p - 1) * 23 + N_OBSTACLES * 18) + n_p * 16;
|
||||||
|
|
||||||
|
long long div_count_per_iter = n_p * ((n_p - 1) * 1 + N_OBSTACLES * 1) + n_p * 2;
|
||||||
|
|
||||||
|
long long sqrt_count_per_iter = n_p * ((n_p - 1) * 4 + N_OBSTACLES * 2) + n_p * 3;
|
||||||
|
|
||||||
|
long long exp_count_per_iter = n_p * ((n_p - 1) * 1 + N_OBSTACLES * 1);
|
||||||
|
|
||||||
|
long double W_per_iter = add_count_per_iter * cost_add + mult_count_per_iter * cost_mult + div_count_per_iter * cost_div + sqrt_count_per_iter * cost_sqrt + exp_count_per_iter * cost_exp;
|
||||||
|
|
||||||
|
// Estimated memory traffic per iteration.
|
||||||
|
// TODO: refine this model to account for actual memory accesses, cache reuse,
|
||||||
|
// and the full interaction loop.
|
||||||
|
long long Q_per_iter = sizeof(Pedestrian) * n_p + sizeof(Obstacle) * N_OBSTACLES;
|
||||||
|
|
||||||
|
// Operational intensity:
|
||||||
|
//
|
||||||
|
// I(n) = W(n) / Q(n)
|
||||||
|
//
|
||||||
|
// where W is estimated work in flops and Q is estimated memory traffic in
|
||||||
|
// bytes.
|
||||||
long double I = W_per_iter / Q_per_iter;
|
long double I = W_per_iter / Q_per_iter;
|
||||||
|
|
||||||
|
// Total estimated work over all iterations.
|
||||||
long double W = W_per_iter * n_iter;
|
long double W = W_per_iter * n_iter;
|
||||||
|
|
||||||
|
// Performance in estimated flops per cycle.
|
||||||
long double P = W / T;
|
long double P = W / T;
|
||||||
|
|
||||||
printf("W(n) per iteration: %.2Lf flops\n", W_per_iter);
|
printf("W(n) per iteration: %.2Lf flops\n", W_per_iter);
|
||||||
@@ -293,15 +174,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
printf("T(n): %.2Lf cycles\n", T);
|
printf("T(n): %.2Lf cycles\n", T);
|
||||||
printf("P(n): %.2Lf flops/cycle\n", P);
|
printf("P(n): %.2Lf flops/cycle\n", P);
|
||||||
|
|
||||||
// Export the data to a .csv file
|
#endif
|
||||||
if (dump_path) {
|
|
||||||
ofstream out(dump_path);
|
|
||||||
out << "time,id,x,y,p.e.x,p.e.y,color\n";
|
|
||||||
for (auto &[t, id, x, y, e_x, e_y, c] : samples) {
|
|
||||||
out << t << ',' << id << ',' << x << ',' << y << ',' << e_x << ',' << e_y << ',' << c << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] pedestrians;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
+31
-13
@@ -2,10 +2,10 @@
|
|||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "vector2d.hpp"
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
// For visual simulation.
|
// Needed for visual simulation, in order to distinguish two groups of pedestrians:
|
||||||
// Black pedestrians move from left to right;
|
// - White pedestrians move from left to right
|
||||||
// White pedestrians move from right to left.
|
// - Black pedestrians move from right to left
|
||||||
enum Color { BLACK, WHITE };
|
enum Color { WHITE, BLACK};
|
||||||
|
|
||||||
struct Pedestrian {
|
struct Pedestrian {
|
||||||
|
|
||||||
@@ -31,17 +31,35 @@ struct Pedestrian {
|
|||||||
|
|
||||||
// $\vec{r}^0_\alpha$
|
// $\vec{r}^0_\alpha$
|
||||||
Vector2d destination;
|
Vector2d destination;
|
||||||
|
|
||||||
// social force
|
|
||||||
Vector2d f_i;
|
|
||||||
|
|
||||||
// $s_\beta^2 = (v_\beta \Delta t)^2$
|
// $s_\beta^2 = (v_\beta \Delta t)^2$
|
||||||
double s_b_sq;
|
double s_b_sq;
|
||||||
|
// $s_\beta \vec{e}_\beta$
|
||||||
|
Vector2d s_b_e;
|
||||||
|
|
||||||
// $s_\beta \vec{e}_\beta = v_{\beta} \Delta t \vec{e}_{\beta}$
|
// Default constructor: initializes a pedestrian with color WHITE and all vector/scalar fields set to zero.
|
||||||
Vector2d s_b_e_b;
|
Pedestrian() : color(WHITE), curr_vel(0.0, 0.0), w(0.0, 0.0), curr_pos(0.0, 0.0), e(0.0, 0.0), v_max(0.0), desired_v0(0.0), destination(0.0, 0.0), s_b_sq(0.0), s_b_e(0.0, 0.0) {}
|
||||||
|
|
||||||
Pedestrian() : color(WHITE), curr_vel(0.0, 0.0), w(0.0, 0.0), curr_pos(0.0, 0.0), e(0.0, 0.0), v_max(0.0), desired_v0(0.0), destination(0.0, 0.0), f_i(0.0, 0.0), s_b_sq(0.0), s_b_e_b(0.0, 0.0) {}
|
// Constructs a pedestrian with the given initial state and precomputes the
|
||||||
|
// quantities used in the pedestrian-pedestrian repulsion term.
|
||||||
Pedestrian(Color clr, Vector2d c_v, Vector2d c_p, Vector2d e, double v_max, double desired_v0, Vector2d d, Vector2d f) : color(clr), curr_vel(c_v), w(c_v), curr_pos(c_p), e(e), v_max(v_max), desired_v0(desired_v0), destination(d), f_i(f), s_b_sq(c_v.dot(c_v) * DELTA_T_SQ), s_b_e_b(e * c_v.norm() * DELTA_T) {}
|
//
|
||||||
|
// The preferred velocity w is initialized to the current velocity c_v. The
|
||||||
|
// cached quantities s_b_sq and s_b_e correspond to the predicted step of this
|
||||||
|
// pedestrian over $\Delta t$:
|
||||||
|
//
|
||||||
|
// s_b = ||c_v|| * dt
|
||||||
|
// s_b_sq = s_b^2
|
||||||
|
// s_b_e = e * s_b
|
||||||
|
//
|
||||||
|
// These values are used when another pedestrian computes the elliptical
|
||||||
|
// interaction term involving this pedestrian.
|
||||||
|
//
|
||||||
|
// @param clr The display/color category of the pedestrian.
|
||||||
|
// @param c_v The initial current velocity.
|
||||||
|
// @param c_p The initial current position.
|
||||||
|
// @param e The initial desired direction of motion.
|
||||||
|
// @param v_max The maximum speed that the pedestrian can reach.
|
||||||
|
// @param desired_v0 The desired walking speed.
|
||||||
|
// @param d The pedestrian's target destination point.
|
||||||
|
//
|
||||||
|
Pedestrian(Color clr, Vector2d c_v, Vector2d c_p, Vector2d e, double v_max, double desired_v0, Vector2d d) : color(clr), curr_vel(c_v), w(c_v), curr_pos(c_p), e(e), v_max(v_max), desired_v0(desired_v0), destination(d), s_b_sq(c_v.dot(c_v) * delta_t_sq), s_b_e(e * c_v.norm() * delta_t) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "board.hpp"
|
||||||
|
#include "pedestrian.hpp"
|
||||||
|
|
||||||
|
// Initializes the pedestrians used in the simulation.
|
||||||
|
//
|
||||||
|
// The returned array contains n_p pedestrians split into two groups:
|
||||||
|
// one group walking from left to right and one group walking from right to left.
|
||||||
|
// Pedestrian positions, desired speeds, destinations, and initial directions are
|
||||||
|
// initialized according to the simulation setup.
|
||||||
|
//
|
||||||
|
// @param n_p : Number of pedestrians to initialize.
|
||||||
|
// @return Pointer to a dynamically allocated array of pedestrians.
|
||||||
|
Pedestrian *init_pedestrians(int n_p);
|
||||||
|
|
||||||
|
// Initializes a simulation board with no obstacles inside.
|
||||||
|
//
|
||||||
|
// @return Initialized Board instance.
|
||||||
|
Board init_board_without_obstacles();
|
||||||
|
|
||||||
|
// Runs the social force model with n_p pedestrians for n_iter iterations,
|
||||||
|
// i.e., n_iter * dt simulated seconds.
|
||||||
|
// If dump_path is not nullptr, the simulation states are written to that CSV
|
||||||
|
// file, which can be used to generate a visualization video. If dump_path is
|
||||||
|
// nullptr, no output file is generated.
|
||||||
|
//
|
||||||
|
// @param pedestrians : Array of pedestrians to simulate. The array is updated in place.
|
||||||
|
// @param board : Board containing the simulation dimensions and obstacles.
|
||||||
|
// @param n_p : Number of pedestrians.
|
||||||
|
// @param n_iter : Number of simulation iterations.
|
||||||
|
// @param dump_path : Optional path to the CSV output file.
|
||||||
|
// @return 0 on success.
|
||||||
|
int run_simulation(Pedestrian *pedestrians, Board &board, int n_p, int n_iter, const char *dump_path);
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
#include "board.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "forces.hpp"
|
||||||
|
#include "pedestrian.hpp"
|
||||||
|
#include "social_force.hpp"
|
||||||
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define SEED 42
|
||||||
|
|
||||||
|
#define BOARD_WIDTH 10.0
|
||||||
|
#define BOARD_LENGTH 50.0
|
||||||
|
#define WALL_EPS 1e-3
|
||||||
|
#define BOARD_WIDTH_MINUS_WALL_EPS 9.999
|
||||||
|
|
||||||
|
#ifndef RANDOM_SEED
|
||||||
|
// Fixed seeding is used by default to make simulations reproducible.
|
||||||
|
#define RANDOM_SEED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BENCHMARK
|
||||||
|
// Benchmarking is disabled by default.
|
||||||
|
#define BENCHMARK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
Pedestrian *init_pedestrians(int n_p) {
|
||||||
|
#if RANDOM_SEED
|
||||||
|
random_device rd; // Platform-provided random seed source.
|
||||||
|
mt19937 gen(rd()); // Non-reproducible initialization.
|
||||||
|
#else
|
||||||
|
mt19937 gen(SEED); // Reproducible initialization.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
normal_distribution<double> velocity_gen(v0_mean, v0_std_dev);
|
||||||
|
uniform_real_distribution<double> y_gen(WALL_EPS, BOARD_WIDTH_MINUS_WALL_EPS);
|
||||||
|
|
||||||
|
Pedestrian *P = new Pedestrian[n_p];
|
||||||
|
|
||||||
|
int n_p_half = n_p / 2;
|
||||||
|
|
||||||
|
// Group of pedestrians walking from left to right.
|
||||||
|
for (int i = 0; i < n_p_half; i++) {
|
||||||
|
double x = 0.0;
|
||||||
|
double y = y_gen(gen);
|
||||||
|
|
||||||
|
Vector2d start_pos = Vector2d(x, y);
|
||||||
|
Vector2d destination = Vector2d(BOARD_LENGTH, y);
|
||||||
|
|
||||||
|
Vector2d init_e = destination - start_pos;
|
||||||
|
double init_e_norm = init_e.norm();
|
||||||
|
Vector2d init_e_unit = init_e / init_e_norm;
|
||||||
|
|
||||||
|
// Reject near-zero desired speeds to avoid pedestrians that barely move,
|
||||||
|
// and reject negative speeds because they would reverse the intended
|
||||||
|
// walking direction.
|
||||||
|
double desired_v0;
|
||||||
|
do {
|
||||||
|
desired_v0 = velocity_gen(gen);
|
||||||
|
} while (desired_v0 <= 0.1);
|
||||||
|
|
||||||
|
double v_max = 1.3 * desired_v0;
|
||||||
|
|
||||||
|
P[i] = Pedestrian(WHITE, Vector2d(), start_pos, init_e_unit, v_max, desired_v0, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group of pedestrians walking from right to left.
|
||||||
|
for (int i = n_p_half; i < n_p; i++) {
|
||||||
|
double x = BOARD_LENGTH;
|
||||||
|
double y = y_gen(gen);
|
||||||
|
|
||||||
|
Vector2d start_pos = Vector2d(x, y);
|
||||||
|
Vector2d destination = Vector2d(0.0, y);
|
||||||
|
|
||||||
|
Vector2d init_e = destination - start_pos;
|
||||||
|
double init_e_norm = init_e.norm();
|
||||||
|
Vector2d init_e_unit = init_e / init_e_norm;
|
||||||
|
|
||||||
|
// Reject near-zero desired speeds to avoid pedestrians that barely move,
|
||||||
|
// and reject negative speeds because they would reverse the intended
|
||||||
|
// walking direction.
|
||||||
|
double desired_v0;
|
||||||
|
do {
|
||||||
|
desired_v0 = velocity_gen(gen);
|
||||||
|
} while (desired_v0 <= 0.1);
|
||||||
|
|
||||||
|
double v_max = 1.3 * desired_v0;
|
||||||
|
|
||||||
|
P[i] = Pedestrian(BLACK, Vector2d(), start_pos, init_e_unit, v_max, desired_v0, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
return P;
|
||||||
|
}
|
||||||
|
|
||||||
|
Board init_board_without_obstacles() { return Board(BOARD_LENGTH, BOARD_WIDTH, 0, nullptr); }
|
||||||
|
|
||||||
|
int run_simulation(Pedestrian *pedestrians, Board &board, int n_p, int n_iter, const char *dump_path) {
|
||||||
|
|
||||||
|
#if BENCHMARK
|
||||||
|
// Avoid compiler unused parameter warning
|
||||||
|
(void)dump_path;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !BENCHMARK
|
||||||
|
const int dump_interval = 1;
|
||||||
|
|
||||||
|
// Each sample stores: time step, pedestrian id, x, y, e.x, e.y, color.
|
||||||
|
vector<tuple<int, int, double, double, double, double, int>> samples;
|
||||||
|
|
||||||
|
if (dump_path) {
|
||||||
|
samples.reserve(static_cast<size_t>(n_iter / dump_interval + 1) * n_p);
|
||||||
|
|
||||||
|
// Store the initial state before the first simulation step.
|
||||||
|
for (int i = 0; i < n_p; i++) {
|
||||||
|
Pedestrian &p = pedestrians[i];
|
||||||
|
samples.emplace_back(0, i, p.curr_pos.x, p.curr_pos.y, p.e.x, p.e.y, p.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int t = 0; t < n_iter; t++) {
|
||||||
|
// Pass 1: Compute all forces from the current state.
|
||||||
|
for (int i = 0; i < n_p; i++) {
|
||||||
|
Pedestrian &p = pedestrians[i];
|
||||||
|
|
||||||
|
// # 2 ADDs, 4 MULTs
|
||||||
|
//
|
||||||
|
// $\vec{F}_\alpha(\vec{v}_\alpha, v^0_\alpha \vec{e}_\alpha) = \frac{1}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
|
||||||
|
//
|
||||||
|
Vector2d f_alpha_dt = acceleration_term_dt(p);
|
||||||
|
|
||||||
|
// # (N-1) * (12 ADDs, 23 MULTs, 1 DIV, 4 SQRTs, 1 EXP)
|
||||||
|
// + (N-1) * 2 ADDs
|
||||||
|
//
|
||||||
|
// $\sum_{\beta} \vec{F}_{\alpha\beta}(\vec{e}_{\alpha},\vec{r}_{\alpha} - \vec{r}_{\beta})$
|
||||||
|
//
|
||||||
|
for (int j = 0; j < n_p; j++) {
|
||||||
|
if (i == j)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
f_alpha_dt += pedestrian_repulsion_dt(p, pedestrians[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// # board.n_obstacles * (10 ADDs, 18 MULTs, 1 DIV, 2 SQRTs, 1 EXP)
|
||||||
|
// + board.n_obstacles * 2 ADDs
|
||||||
|
//
|
||||||
|
// $\sum_{B} \vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha} - \vec{r}_{B})$
|
||||||
|
//
|
||||||
|
for (int k = 0; k < board.n_obstacles; k++) {
|
||||||
|
f_alpha_dt += obstacle_repulsion_dt(p, board.obstacles[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// # 2 ADDs
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// $\frac{d\vec{w}_\alpha}{dt} = \vec{F}_{\alpha}(t)$
|
||||||
|
//
|
||||||
|
p.w += f_alpha_dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: Update pedestrian states after all forces have been computed.
|
||||||
|
// # (7 ADDs, 16 MULTs, 2 DIVs, 3 SQRTs) * n_p
|
||||||
|
for (int i = 0; i < n_p; i++) {
|
||||||
|
Pedestrian &p = pedestrians[i];
|
||||||
|
|
||||||
|
// # 1 ADD, 2 MULTs, 1 SQRT
|
||||||
|
double w_norm = p.w.norm();
|
||||||
|
double w_norm2 = w_norm > EPS ? w_norm : EPS; // Avoid division by zero.
|
||||||
|
|
||||||
|
// # 1 DIV
|
||||||
|
double correction_factor = std::min(1.0, p.v_max / w_norm2);
|
||||||
|
|
||||||
|
// # 2 MULTs
|
||||||
|
p.curr_vel = p.w * correction_factor;
|
||||||
|
|
||||||
|
// # 2 ADDs, 2 MULTs
|
||||||
|
//
|
||||||
|
// $\vec{r}_i^{t+1} = \vec{r}_i^t + \vec{v}_i^t dt$
|
||||||
|
//
|
||||||
|
p.curr_pos = p.curr_pos + p.curr_vel * dt;
|
||||||
|
|
||||||
|
// When a pedestrian reaches the end of the corridor, teleport them back
|
||||||
|
// to the opposite entrance while keeping the same y-coordinate. This
|
||||||
|
// maintains a constant pedestrian flow and makes lane formation easier to
|
||||||
|
// observe.
|
||||||
|
if (p.color == WHITE && p.curr_pos.x >= BOARD_LENGTH) {
|
||||||
|
p.curr_pos.x = 0.0;
|
||||||
|
p.w = Vector2d();
|
||||||
|
p.curr_vel = Vector2d();
|
||||||
|
}
|
||||||
|
if (p.color == BLACK && p.curr_pos.x <= 0.0) {
|
||||||
|
p.curr_pos.x = BOARD_LENGTH;
|
||||||
|
p.w = Vector2d();
|
||||||
|
p.curr_vel = Vector2d();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep pedestrians inside the board boundaries.
|
||||||
|
p.curr_pos.y = clamp(p.curr_pos.y, WALL_EPS, BOARD_WIDTH_MINUS_WALL_EPS);
|
||||||
|
|
||||||
|
// # 2 ADDs
|
||||||
|
Vector2d e = p.destination - p.curr_pos;
|
||||||
|
|
||||||
|
// # 1 ADD, 2 MULTs, 1 DIV, 1 SQRT
|
||||||
|
double e_norm = e.norm();
|
||||||
|
double e_norm2 = e_norm > EPS ? e_norm : EPS; // Avoid division by zero.
|
||||||
|
double e_norm_inv = 1.0 / e_norm2;
|
||||||
|
|
||||||
|
// # 2 MULTs
|
||||||
|
p.e = e * e_norm_inv;
|
||||||
|
|
||||||
|
// # 1 ADD, 2 MULTs
|
||||||
|
double velocity_norm_sq = p.curr_vel.dot(p.curr_vel);
|
||||||
|
|
||||||
|
// # 1 SQRT
|
||||||
|
double velocity_norm = std::sqrt(velocity_norm_sq);
|
||||||
|
|
||||||
|
// # 1 MULT
|
||||||
|
p.s_b_sq = velocity_norm_sq * delta_t_sq;
|
||||||
|
|
||||||
|
// # 3 MULTs
|
||||||
|
p.s_b_e = p.e * (velocity_norm * delta_t);
|
||||||
|
|
||||||
|
#if !BENCHMARK
|
||||||
|
// Store the current pedestrian state for visualization.
|
||||||
|
if (dump_path && (t % dump_interval == 0)) {
|
||||||
|
samples.emplace_back(t + 1, i, p.curr_pos.x, p.curr_pos.y, p.e.x, p.e.y, p.color);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BENCHMARK
|
||||||
|
// Export recorded simulation states to CSV.
|
||||||
|
if (dump_path) {
|
||||||
|
ofstream out(dump_path);
|
||||||
|
out << "time,id,x,y,p.e.x,p.e.y,color\n";
|
||||||
|
|
||||||
|
for (auto &[t, id, x, y, e_x, e_y, c] : samples) {
|
||||||
|
out << t << ',' << id << ',' << x << ',' << y << ',' << e_x << ',' << e_y << ',' << c << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "board.hpp"
|
||||||
|
#include "test_helpers.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, ProjectionInsideSegment) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(10.0, 0.0));
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(Vector2d(5.0, 3.0));
|
||||||
|
EXPECT_VEC2_NEAR(nearest, Vector2d(5.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, ProjectionBeforeStartIsClampedToStart) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(10.0, 0.0));
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(Vector2d(-5.0, 3.0));
|
||||||
|
EXPECT_VEC2_NEAR(nearest, Vector2d(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, ProjectionAfterEndIsClampedToEnd) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(10.0, 0.0));
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(Vector2d(15.0, 3.0));
|
||||||
|
EXPECT_VEC2_NEAR(nearest, Vector2d(10.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, QueryEqualToStartReturnsStart) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(10.0, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(o.find_nearest_obstacle_point(Vector2d(0.0, 0.0)), Vector2d(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, QueryEqualToEndReturnsEnd) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(10.0, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(o.find_nearest_obstacle_point(Vector2d(10.0, 0.0)), Vector2d(10.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, DiagonalSegment) {
|
||||||
|
Obstacle o(Vector2d(0.0, 0.0), Vector2d(4.0, 4.0));
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(Vector2d(4.0, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(nearest, Vector2d(2.0, 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, PointOnSegmentReturnsItself) {
|
||||||
|
Obstacle o(Vector2d(1.0, 1.0), Vector2d(5.0, 9.0));
|
||||||
|
Vector2d on_segment(3.0, 5.0);
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(on_segment);
|
||||||
|
EXPECT_VEC2_NEAR(nearest, on_segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstacleNearest, PointOnSinglePointObstacleReturnsStart) {
|
||||||
|
Obstacle o(Vector2d(7.0, 8.0), Vector2d(7.0, 8.0));
|
||||||
|
Vector2d nearest = o.find_nearest_obstacle_point(Vector2d(100.0, -50.0));
|
||||||
|
EXPECT_VEC2_NEAR(nearest, Vector2d(7.0, 8.0));
|
||||||
|
EXPECT_TRUE(std::isfinite(nearest.x));
|
||||||
|
EXPECT_TRUE(std::isfinite(nearest.y));
|
||||||
|
}
|
||||||
+419
@@ -0,0 +1,419 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// Tests for forces.cpp
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#include "board.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "forces.hpp"
|
||||||
|
#include "pedestrian.hpp"
|
||||||
|
#include "test_helpers.hpp"
|
||||||
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Build a pedestrian with default testing values so each test only has to override what it actually cares about.
|
||||||
|
Pedestrian MakePed(Vector2d pos = Vector2d(0.0, 0.0), Vector2d vel = Vector2d(1.0, 0.0), Vector2d e = Vector2d(1.0, 0.0), double v_max = 1.5, double desired_v0 = 1.15, Vector2d dest = Vector2d(100.0, 0.0)) { return Pedestrian(WHITE, vel, pos, e, v_max, desired_v0, dest); }
|
||||||
|
|
||||||
|
// One Euler step, mirroring social_force_baseline.cpp:
|
||||||
|
// w_new = w + total_force_dt
|
||||||
|
// v_new = w_new * min(1, v_max / ||w_new||)
|
||||||
|
// pos_new = pos + v_new * dt
|
||||||
|
struct StepResult {
|
||||||
|
Vector2d w, v, pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
StepResult IntegrateOneStep(const Pedestrian &p, Vector2d total_force_dt) {
|
||||||
|
Vector2d w_new = p.w + total_force_dt;
|
||||||
|
double w_norm = w_new.norm();
|
||||||
|
double cap = std::min(1.0, p.v_max / (w_norm > EPS ? w_norm : EPS));
|
||||||
|
Vector2d v_new = w_new * cap;
|
||||||
|
Vector2d pos_new = p.curr_pos + v_new * dt;
|
||||||
|
return {w_new, v_new, pos_new};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Tests for w(e, f)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST(WeightFn, ForceAlignedWithDirectionIsInView) { EXPECT_DOUBLE_EQ(w(Vector2d(1.0, 0.0), Vector2d(1.0, 0.0)), dt_neg); }
|
||||||
|
|
||||||
|
TEST(WeightFn, ForceOppositeToDirectionIsOutOfView) { EXPECT_DOUBLE_EQ(w(Vector2d(1.0, 0.0), Vector2d(-1.0, 0.0)), c_neg_times_dt); }
|
||||||
|
|
||||||
|
TEST(WeightFn, PerpendicularForceIsInView) { EXPECT_DOUBLE_EQ(w(Vector2d(1.0, 1.0), Vector2d(-1.0, 1.0)), dt_neg); }
|
||||||
|
|
||||||
|
TEST(WeightFn, ZeroForceIsInView) { EXPECT_DOUBLE_EQ(w(Vector2d(1.0, 0.0), Vector2d(0.0, 0.0)), dt_neg); }
|
||||||
|
|
||||||
|
TEST(WeightFn, AngleSlightlyInsidePhiIsInView) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
const double angle = 99.0 / 180.0 * M_PI;
|
||||||
|
Vector2d f(std::cos(angle), std::sin(angle));
|
||||||
|
EXPECT_DOUBLE_EQ(w(e, f), dt_neg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(WeightFn, AngleSlightlyOutsidePhiIsOutOfView) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
const double angle = 101.0 / 180.0 * M_PI;
|
||||||
|
Vector2d f(std::cos(angle), std::sin(angle));
|
||||||
|
EXPECT_DOUBLE_EQ(w(e, f), c_neg_times_dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(WeightFn, ScalingForceMagnitudeDoesNotChangeView) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
Vector2d f(0.7, 0.7);
|
||||||
|
EXPECT_DOUBLE_EQ(w(e, f), w(e, f * 1000.0));
|
||||||
|
EXPECT_DOUBLE_EQ(w(e, f), w(e, f * 0.001));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Tests for acceleration_term_dt(alpha)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, AtDesiredVelocityProducesNoForce) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
double v0 = 1.5;
|
||||||
|
Pedestrian p = MakePed(Vector2d(), e * v0, e, 2.0, v0);
|
||||||
|
EXPECT_VEC2_NEAR_TOL(acceleration_term_dt(p), Vector2d(0.0, 0.0), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, BelowDesiredVelocityAccelerates) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
double v0 = 1.5;
|
||||||
|
Pedestrian p = MakePed(Vector2d(), Vector2d(0.5, 0.0), e, 2.0, v0);
|
||||||
|
Vector2d expected = Vector2d(2.0, 0.0) * dt;
|
||||||
|
EXPECT_VEC2_NEAR(acceleration_term_dt(p), expected);
|
||||||
|
EXPECT_GT(acceleration_term_dt(p).x, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, AboveDesiredVelocityDecelerates) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
double v0 = 1.5;
|
||||||
|
Pedestrian p = MakePed(Vector2d(), Vector2d(1.75, 0.0), e, 2.0, v0);
|
||||||
|
Vector2d expected = Vector2d(-0.5, 0.0) * dt;
|
||||||
|
EXPECT_VEC2_NEAR(acceleration_term_dt(p), expected);
|
||||||
|
EXPECT_LT(acceleration_term_dt(p).x, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, MovingBackwardProducesLargerForwardForceThanMovingForward) {
|
||||||
|
Vector2d e(1.0, 0.0);
|
||||||
|
Pedestrian forward = MakePed(Vector2d(3.0, 0.0), Vector2d(0.5, 0.0), e, 2.0, 1.5);
|
||||||
|
Pedestrian backward = MakePed(Vector2d(3.0, 0.0), Vector2d(-1.0, 0.0), e, 2.0, 1.5);
|
||||||
|
EXPECT_GT(acceleration_term_dt(backward).x, acceleration_term_dt(forward).x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, DiagonalWithVelocity) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(), Vector2d(0.5, -0.25), Vector2d(1.0, 1.0), 5.0, 2.0);
|
||||||
|
EXPECT_VEC2_NEAR(acceleration_term_dt(p), Vector2d(3.0, 4.5) * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, ZeroDesiredSpeed) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(), Vector2d(0.7, -0.3), Vector2d(1.0, 0.0), 2.0, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(acceleration_term_dt(p), Vector2d(-1.4, 0.6) * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AccelerationTerm, SidewaysVelocityProducesPerpendicularCorrection) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(), Vector2d(1.0, 0.5), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(acceleration_term_dt(p).x, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(acceleration_term_dt(p), Vector2d(0.0, -1.0 * dt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Tests for pedestrian_repulsion_dt(alpha, beta)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST(PedRepulsion, DistantPedestriansProduceNegligibleForce) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0.0, 0.0), Vector2d(1.0, 0.0), Vector2d(1.0, 0.0));
|
||||||
|
Pedestrian b = MakePed(Vector2d(100.0, 0.0), Vector2d(-1.2, 0.0), Vector2d(-1.0, 0.0));
|
||||||
|
Vector2d f = pedestrian_repulsion_dt(a, b);
|
||||||
|
EXPECT_LT(f.norm(), 1e-30);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, CloseIsStrongerThanFar) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0.0, 0.0), Vector2d(1.0, 0.0), Vector2d(1.0, 0.0));
|
||||||
|
Pedestrian close = MakePed(Vector2d(2.0, 0.0), Vector2d(-1.0, 0.0), Vector2d(-1.0, 0.0));
|
||||||
|
Pedestrian far = MakePed(Vector2d(8.0, 0.0), Vector2d(-1.0, 0.0), Vector2d(-1.0, 0.0));
|
||||||
|
EXPECT_GT(pedestrian_repulsion_dt(a, close).norm(), pedestrian_repulsion_dt(a, far).norm());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, StationaryBetaInFront) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b = MakePed(Vector2d(1, 0), Vector2d(0, 0), Vector2d(-1, 0), 1.3, 1.0, Vector2d(-100, 0));
|
||||||
|
Vector2d expected(-0.24971795 * dt, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(pedestrian_repulsion_dt(a, b), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, StationaryBetaToTheSide) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b = MakePed(Vector2d(0, 1), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(0, -100));
|
||||||
|
Vector2d expected(0.0, -0.24971795 * dt);
|
||||||
|
EXPECT_VEC2_NEAR(pedestrian_repulsion_dt(a, b), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, StationaryBetaDiagonal) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b = MakePed(Vector2d(1, 1), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(-100, -100));
|
||||||
|
Vector2d expected(-0.04439144 * dt, -0.04439144 * dt);
|
||||||
|
EXPECT_VEC2_NEAR(pedestrian_repulsion_dt(a, b), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, HeadOnWithMovingBeta) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0, 0), Vector2d(1, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b = MakePed(Vector2d(3, 0), Vector2d(-1, 0), Vector2d(-1, 0), 1.3, 1.0, Vector2d(-100, 0));
|
||||||
|
EXPECT_VEC2_NEAR(pedestrian_repulsion_dt(a, b), Vector2d((-0.02512853) * dt, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, OutOfViewAttenuatedByFactorC) {
|
||||||
|
Pedestrian a_front = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b_front = MakePed(Vector2d(1, 0), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0);
|
||||||
|
Pedestrian a_back = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b_back = MakePed(Vector2d(-1, 0), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0);
|
||||||
|
|
||||||
|
Vector2d f_in = pedestrian_repulsion_dt(a_front, b_front);
|
||||||
|
Vector2d f_out = pedestrian_repulsion_dt(a_back, b_back);
|
||||||
|
|
||||||
|
EXPECT_NEAR(f_in.norm() / f_out.norm(), 1.0 / -c_neg, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, ForceOnAlphaPointsAwayFromBeta) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 1), 1.3, 1.0, Vector2d(100, 100));
|
||||||
|
Pedestrian b = MakePed(Vector2d(1, 1), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0);
|
||||||
|
Vector2d f = pedestrian_repulsion_dt(a, b);
|
||||||
|
Vector2d to_b(1.0, 1.0);
|
||||||
|
EXPECT_LT(f.dot(to_b), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PedRepulsion, OverlappingPedestriansProduceFiniteOutput) {
|
||||||
|
Pedestrian a = MakePed(Vector2d(2, 2), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian b = MakePed(Vector2d(2, 2), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(0, 0));
|
||||||
|
Vector2d f = pedestrian_repulsion_dt(a, b);
|
||||||
|
EXPECT_TRUE(std::isfinite(f.x));
|
||||||
|
EXPECT_TRUE(std::isfinite(f.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Tests for obstacle_repulsion_dt(alpha, B)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, DistantObstacleProducesNegligibleForce) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(0.0, 0.0), Vector2d(1.0, 0.0), Vector2d(1.0, 0.0));
|
||||||
|
Obstacle o(Vector2d(100.0, -50.0), Vector2d(100.0, 50.0));
|
||||||
|
EXPECT_LT(obstacle_repulsion_dt(p, o).norm(), 1e-30);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, PerpendicularWallBelow) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Obstacle wall(Vector2d(0, -1), Vector2d(10, -1));
|
||||||
|
Vector2d expected(0.0, 0.33689735 * dt);
|
||||||
|
EXPECT_VEC2_NEAR(obstacle_repulsion_dt(p, wall), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, ClampedToEndpoint) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(12, 0), Vector2d(0, 0), Vector2d(-1, 0), 1.3, 1.0, Vector2d(0, 0));
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Vector2d expected(0.0022699965 * dt, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(obstacle_repulsion_dt(p, wall), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, ClampedToStart) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(-3, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Vector2d expected(-0.0000152951 * dt, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(obstacle_repulsion_dt(p, wall), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, OutOfViewAttenuatedByFactorC) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(5, 1), Vector2d(0, 0), Vector2d(0, 1), 1.3, 1.0, Vector2d(5, 100));
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Vector2d expected(0.0, 0.33689735 * dt * -c_neg);
|
||||||
|
EXPECT_VEC2_NEAR(obstacle_repulsion_dt(p, wall), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, SinglePointObstacle) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(2, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Obstacle pt(Vector2d(5, 0), Vector2d(5, 0));
|
||||||
|
Vector2d expected(-0.0000152951 * dt, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(obstacle_repulsion_dt(p, pt), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, ForceOnAlphaPointsAwayFromObstacle) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(5, 2), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 2));
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Vector2d f = obstacle_repulsion_dt(p, wall);
|
||||||
|
EXPECT_GT(f.y, 0.0);
|
||||||
|
EXPECT_NEAR(f.x, 0.0, 1e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ObstRepulsion, AlphaOnObstacleProducesFiniteOutput) {
|
||||||
|
Pedestrian p = MakePed(Vector2d(5, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Vector2d f = obstacle_repulsion_dt(p, wall);
|
||||||
|
EXPECT_TRUE(std::isfinite(f.x));
|
||||||
|
EXPECT_TRUE(std::isfinite(f.y));
|
||||||
|
EXPECT_VEC2_NEAR(f, Vector2d(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Integration tests
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST(Integration, OnePedNoInteractions_AccelerationOnly) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(2, 3), Vector2d(0.5, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha);
|
||||||
|
|
||||||
|
Vector2d expected_total(dt, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, Vector2d(0.52, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(step.v, Vector2d(0.52, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(2.0 + 0.52 * dt, 3.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, OnePedOneStationaryBeta) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian beta = MakePed(Vector2d(1, 0), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(-100, 0));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + pedestrian_repulsion_dt(alpha, beta);
|
||||||
|
|
||||||
|
double Fa0_x = dt_times_tau_alpha_inv;
|
||||||
|
double Fab_x = -(V0_AB / SIGMA) * std::exp(-1.0 / SIGMA) * dt;
|
||||||
|
Vector2d expected_total(Fa0_x + Fab_x, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_total);
|
||||||
|
EXPECT_LT(step.w.norm(), alpha.v_max);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, expected_total * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, OnePedOneWall) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0.5), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Obstacle wall(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + obstacle_repulsion_dt(alpha, wall);
|
||||||
|
|
||||||
|
double Fa0_x = dt_times_tau_alpha_inv;
|
||||||
|
double FaB_y = (U0_AB / R) * std::exp(-0.5 / R) * dt;
|
||||||
|
Vector2d expected_total(Fa0_x, FaB_y);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_total);
|
||||||
|
EXPECT_LT(step.w.norm(), alpha.v_max);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(0, 0.5) + expected_total * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, AllThreeForcesCombined) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian beta = MakePed(Vector2d(1, 0), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(-100, 0));
|
||||||
|
Obstacle wall(Vector2d(0, -1), Vector2d(10, -1));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + pedestrian_repulsion_dt(alpha, beta) + obstacle_repulsion_dt(alpha, wall);
|
||||||
|
|
||||||
|
double Fa0_x = dt_times_tau_alpha_inv;
|
||||||
|
double Fab_x = -(V0_AB / SIGMA) * std::exp(-1.0 / SIGMA) * dt;
|
||||||
|
double FaB_y = (U0_AB / R) * std::exp(-1.0 / R) * dt;
|
||||||
|
Vector2d expected_total(Fa0_x + Fab_x, FaB_y);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_total);
|
||||||
|
EXPECT_LT(step.w.norm(), alpha.v_max);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, expected_total * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, SymmetricStationaryBetas_VerticalForcesCancel) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(5, 5), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 5));
|
||||||
|
Pedestrian b_north = MakePed(Vector2d(5, 6), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(5, 100));
|
||||||
|
Pedestrian b_south = MakePed(Vector2d(5, 4), Vector2d(0, 0), Vector2d(0, 0), 1.3, 1.0, Vector2d(5, -100));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + pedestrian_repulsion_dt(alpha, b_north) + pedestrian_repulsion_dt(alpha, b_south);
|
||||||
|
|
||||||
|
Vector2d expected_total(dt_times_tau_alpha_inv, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(5, 5) + expected_total * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, PedBetweenTwoParallelWalls_VerticalForcesCancel) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(5, 1), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 1));
|
||||||
|
Obstacle bottom(Vector2d(0, 0), Vector2d(10, 0));
|
||||||
|
Obstacle top(Vector2d(0, 2), Vector2d(10, 2));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + obstacle_repulsion_dt(alpha, bottom) + obstacle_repulsion_dt(alpha, top);
|
||||||
|
|
||||||
|
Vector2d expected_total(dt_times_tau_alpha_inv, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_total);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(5, 1) + expected_total * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, HeadOnEncounter) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0), Vector2d(1, 0), Vector2d(1, 0), 1.3, 1.0);
|
||||||
|
Pedestrian beta = MakePed(Vector2d(3, 0), Vector2d(-1, 0), Vector2d(-1, 0), 1.3, 1.0, Vector2d(-100, 0));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + pedestrian_repulsion_dt(alpha, beta);
|
||||||
|
|
||||||
|
double sqrt3 = std::sqrt(3.0);
|
||||||
|
double Fab_x = -(2.0 * V0_AB / SIGMA / sqrt3) * std::exp(-sqrt3 / SIGMA) * dt;
|
||||||
|
Vector2d expected_total(Fab_x, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
Vector2d expected_w(1.0 + Fab_x, 0.0);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_w);
|
||||||
|
EXPECT_LT(step.w.norm(), alpha.v_max);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_w);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(0.0, 0.0) + expected_w * dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, SpeedCapAppliesWhenPreferredExceedsVMax) {
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 100.0, Vector2d(1000, 0));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha);
|
||||||
|
EXPECT_VEC2_NEAR(total, Vector2d(4.0, 0.0));
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, Vector2d(4.0, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(step.v, Vector2d(alpha.v_max, 0.0));
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, Vector2d(alpha.v_max * dt, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Integration, FourPedestriansNoWalls_AsymmetricCrowd) {
|
||||||
|
|
||||||
|
Pedestrian alpha = MakePed(Vector2d(0, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Pedestrian b_front = MakePed(Vector2d(1, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Pedestrian b_behind = MakePed(Vector2d(-1, 0), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 0));
|
||||||
|
Pedestrian b_north = MakePed(Vector2d(0, 1), Vector2d(0, 0), Vector2d(1, 0), 1.3, 1.0, Vector2d(100, 1));
|
||||||
|
|
||||||
|
Vector2d total = acceleration_term_dt(alpha) + pedestrian_repulsion_dt(alpha, b_front) + pedestrian_repulsion_dt(alpha, b_behind) + pedestrian_repulsion_dt(alpha, b_north);
|
||||||
|
const double A = (V0_AB / SIGMA) * std::exp(-1.0 / SIGMA) * dt;
|
||||||
|
Vector2d expected_total(dt_times_tau_alpha_inv - A + -c_neg * A, -A);
|
||||||
|
|
||||||
|
EXPECT_VEC2_NEAR(total, expected_total);
|
||||||
|
|
||||||
|
StepResult step = IntegrateOneStep(alpha, total);
|
||||||
|
|
||||||
|
Vector2d expected_w = expected_total;
|
||||||
|
Vector2d expected_v = expected_w;
|
||||||
|
Vector2d expected_pos = alpha.curr_pos + expected_v * dt;
|
||||||
|
|
||||||
|
EXPECT_LT(expected_w.norm(), alpha.v_max);
|
||||||
|
EXPECT_VEC2_NEAR(step.w, expected_w);
|
||||||
|
EXPECT_VEC2_NEAR(step.v, expected_v);
|
||||||
|
EXPECT_VEC2_NEAR(step.pos, expected_pos);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vector2d.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
// default tolerance value
|
||||||
|
constexpr double tolerance = 1e-9;
|
||||||
|
|
||||||
|
// Compare two Vector2d component-wise, with tolerance, and assert if difference between expected and obtained value is below tolerance
|
||||||
|
inline ::testing::AssertionResult Vec2Near(const Vector2d &a, const Vector2d &b, double tol = tolerance) {
|
||||||
|
if (std::fabs(a.x - b.x) <= tol && std::fabs(a.y - b.y) <= tol) {
|
||||||
|
return ::testing::AssertionSuccess();
|
||||||
|
}
|
||||||
|
return ::testing::AssertionFailure() << "expected (" << b.x << ", " << b.y << "), got (" << a.x << ", " << a.y << "), diff (" << (a.x - b.x) << ", " << (a.y - b.y) << ")" << ", exceeds tolerance : " << tol;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXPECT_VEC2_NEAR(actual, expected) EXPECT_TRUE(Vec2Near((actual), (expected)))
|
||||||
|
|
||||||
|
#define EXPECT_VEC2_NEAR_TOL(actual, expected, tol) EXPECT_TRUE(Vec2Near((actual), (expected), (tol)))
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "test_helpers.hpp"
|
||||||
|
#include "vector2d.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
TEST(Vector2dArith, VectorAddition) {
|
||||||
|
Vector2d a(1.0, 2.0), b(3.0, 4.0);
|
||||||
|
EXPECT_VEC2_NEAR(a + b, Vector2d(4.0, 6.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, AdditionIsCommutative) {
|
||||||
|
Vector2d a(1.5, -3.2), b(-0.7, 4.1);
|
||||||
|
EXPECT_VEC2_NEAR(a + b, b + a);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, AdditionDoesNotMutateOperands) {
|
||||||
|
Vector2d a(1.0, 2.0), b(3.0, 4.0);
|
||||||
|
(void)(a + b);
|
||||||
|
EXPECT_VEC2_NEAR(a, Vector2d(1.0, 2.0));
|
||||||
|
EXPECT_VEC2_NEAR(b, Vector2d(3.0, 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, VectorSubtraction) {
|
||||||
|
Vector2d a(5.0, 7.0), b(2.0, 4.0);
|
||||||
|
EXPECT_VEC2_NEAR(a - b, Vector2d(3.0, 3.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, SubtractionIsAntiCommutative) {
|
||||||
|
Vector2d a(5.0, 7.0), b(2.0, 4.0);
|
||||||
|
Vector2d ab = a - b;
|
||||||
|
Vector2d ba = b - a;
|
||||||
|
EXPECT_VEC2_NEAR(ab, Vector2d(-ba.x, -ba.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarSubtractionAppliesToBothComponents) {
|
||||||
|
Vector2d a(5.0, 7.0);
|
||||||
|
EXPECT_VEC2_NEAR(a - 2.0, Vector2d(3.0, 5.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarAdditionAppliesToBothComponents) {
|
||||||
|
Vector2d a(5.0, 7.0);
|
||||||
|
EXPECT_VEC2_NEAR(a + 2.0, Vector2d(7.0, 9.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarMultiplication) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
EXPECT_VEC2_NEAR(a * 2.0, Vector2d(6.0, -8.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarMultiplicationByZero) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
EXPECT_VEC2_NEAR(a * 0.0, Vector2d(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarMultiplicationByNegative) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
EXPECT_VEC2_NEAR(a * -1.0, Vector2d(-3.0, 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, ScalarDivision) {
|
||||||
|
Vector2d a(6.0, -8.0);
|
||||||
|
EXPECT_VEC2_NEAR(a / 2.0, Vector2d(3.0, -4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, CompoundAdditionMutatesAndReturns) {
|
||||||
|
Vector2d a(1.0, 2.0);
|
||||||
|
Vector2d b(3.0, 4.0);
|
||||||
|
Vector2d returned = (a += b);
|
||||||
|
EXPECT_VEC2_NEAR(a, Vector2d(4.0, 6.0));
|
||||||
|
EXPECT_VEC2_NEAR(returned, Vector2d(4.0, 6.0));
|
||||||
|
EXPECT_VEC2_NEAR(b, Vector2d(3.0, 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dArith, CompoundAdditionAccumulates) {
|
||||||
|
Vector2d a(0.0, 0.0);
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
a += Vector2d(1.0, 2.0);
|
||||||
|
EXPECT_VEC2_NEAR(a, Vector2d(5.0, 10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dMutation, ReverseSignReturnsNegated) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
Vector2d r = a.reverse_sign();
|
||||||
|
EXPECT_VEC2_NEAR(r, Vector2d(-3.0, 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dMutation, ReverseSignDoesNotMutateOriginal) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
(void)a.reverse_sign();
|
||||||
|
EXPECT_VEC2_NEAR(a, Vector2d(3.0, -4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dMutation, ResetZerosBothComponents) {
|
||||||
|
Vector2d a(3.0, -4.0);
|
||||||
|
a.reset();
|
||||||
|
EXPECT_DOUBLE_EQ(a.x, 0.0);
|
||||||
|
EXPECT_DOUBLE_EQ(a.y, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dNorm, ZeroVectorHasZeroNorm) {
|
||||||
|
Vector2d v;
|
||||||
|
EXPECT_DOUBLE_EQ(v.norm(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dNorm, AxisAlignedNorm) {
|
||||||
|
EXPECT_DOUBLE_EQ(Vector2d(3.0, 0.0).norm(), 3.0);
|
||||||
|
EXPECT_DOUBLE_EQ(Vector2d(0.0, -4.0).norm(), 4.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dNorm, ThreeFourFiveTriangle) {
|
||||||
|
Vector2d v(3.0, 4.0);
|
||||||
|
EXPECT_NEAR(v.norm(), 5.0, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dNorm, NormIsAlwaysNonNegative) {
|
||||||
|
EXPECT_GE(Vector2d(-1.0, -2.0).norm(), 0.0);
|
||||||
|
EXPECT_GE(Vector2d(1e-30, 1e-30).norm(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dDot, OrthogonalVectorsDotIsZero) {
|
||||||
|
Vector2d a(1.0, 0.0), b(0.0, 1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(a.dot(b), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dDot, ParallelVectorsDotIsProductOfNorms) {
|
||||||
|
Vector2d a(2.0, 0.0), b(3.0, 0.0);
|
||||||
|
EXPECT_DOUBLE_EQ(a.dot(b), 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dDot, AntiParallelDotIsNegative) {
|
||||||
|
Vector2d a(1.0, 1.0), b(-1.0, -1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(a.dot(b), -2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dDot, DotIsCommutative) {
|
||||||
|
Vector2d a(1.5, -2.5), b(3.0, 4.0);
|
||||||
|
EXPECT_DOUBLE_EQ(a.dot(b), b.dot(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Vector2dDot, DotWithSelfEqualsNormSquared) {
|
||||||
|
Vector2d a(3.0, 4.0);
|
||||||
|
EXPECT_NEAR(a.dot(a), a.norm() * a.norm(), tolerance);
|
||||||
|
}
|
||||||
+47
-18
@@ -5,7 +5,9 @@ import matplotlib.pyplot as plt
|
|||||||
from matplotlib.animation import FuncAnimation
|
from matplotlib.animation import FuncAnimation
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
sys.exit("Use it like the following: python visualization.py positions.csv (out.mp4)")
|
sys.exit(
|
||||||
|
"Use it like the following: python visualization.py positions.csv (out.mp4)"
|
||||||
|
)
|
||||||
|
|
||||||
df = pd.read_csv(sys.argv[1])
|
df = pd.read_csv(sys.argv[1])
|
||||||
frames = sorted(df["time"].unique())
|
frames = sorted(df["time"].unique())
|
||||||
@@ -16,7 +18,9 @@ df = df.sort_values(["id", "time"]).reset_index(drop=True)
|
|||||||
df["dx"] = df.groupby("id")["x"].shift(-1) - df["x"]
|
df["dx"] = df.groupby("id")["x"].shift(-1) - df["x"]
|
||||||
df["dy"] = df.groupby("id")["y"].shift(-1) - df["y"]
|
df["dy"] = df.groupby("id")["y"].shift(-1) - df["y"]
|
||||||
mag = np.sqrt(df["dx"] ** 2 + df["dy"] ** 2)
|
mag = np.sqrt(df["dx"] ** 2 + df["dy"] ** 2)
|
||||||
TELEPORT_THRESHOLD = 5.0 # any per-step jump larger than this is the teleport, not motion
|
TELEPORT_THRESHOLD = (
|
||||||
|
5.0 # any per-step jump larger than this is the teleport, not motion
|
||||||
|
)
|
||||||
fallback = (mag > TELEPORT_THRESHOLD) | mag.isna() | (mag < 1e-9)
|
fallback = (mag > TELEPORT_THRESHOLD) | mag.isna() | (mag < 1e-9)
|
||||||
df.loc[fallback, "dx"] = df.loc[fallback, "p.e.x"]
|
df.loc[fallback, "dx"] = df.loc[fallback, "p.e.x"]
|
||||||
df.loc[fallback, "dy"] = df.loc[fallback, "p.e.y"]
|
df.loc[fallback, "dy"] = df.loc[fallback, "p.e.y"]
|
||||||
@@ -25,23 +29,27 @@ df["dx"] = df["dx"] / mag
|
|||||||
df["dy"] = df["dy"] / mag
|
df["dy"] = df["dy"] / mag
|
||||||
|
|
||||||
fig, ax = plt.subplots(figsize=(10, 2.2))
|
fig, ax = plt.subplots(figsize=(10, 2.2))
|
||||||
ax.set_xlim(0, 50); ax.set_ylim(0, 10)
|
ax.set_xlim(0, 50)
|
||||||
ax.set_aspect("equal"); ax.set_facecolor("white")
|
ax.set_ylim(0, 10)
|
||||||
ax.set_xticks([]); ax.set_yticks([])
|
ax.set_aspect("equal")
|
||||||
|
ax.set_facecolor("white")
|
||||||
|
ax.set_xticks([])
|
||||||
|
ax.set_yticks([])
|
||||||
for s in ("top", "right", "left", "bottom"):
|
for s in ("top", "right", "left", "bottom"):
|
||||||
ax.spines[s].set_color("gray")
|
ax.spines[s].set_color("gray")
|
||||||
|
|
||||||
s_white = ax.scatter([], [], s=30, facecolors="black", edgecolor="black", zorder=2)
|
s_white = ax.scatter([], [], s=30, facecolors="gray", edgecolor="gray", zorder=2)
|
||||||
s_black = ax.scatter([], [], s=30, facecolors="white", edgecolor="black", zorder=2)
|
s_black = ax.scatter([], [], s=30, facecolors="black", edgecolor="black", zorder=2)
|
||||||
title = ax.set_title("")
|
title = ax.set_title("")
|
||||||
|
|
||||||
# Keep a reference to quiver objects so we can remove them each frame
|
# Keep a reference to quiver objects so we can remove them each frame
|
||||||
quivers = []
|
quivers = []
|
||||||
|
|
||||||
|
|
||||||
def update(t):
|
def update(t):
|
||||||
sub = df[df["time"] == t]
|
sub = df[df["time"] == t]
|
||||||
w = sub[sub["color"] == 1]
|
w = sub[sub["color"] == 0]
|
||||||
b = sub[sub["color"] == 0]
|
b = sub[sub["color"] == 1]
|
||||||
|
|
||||||
s_white.set_offsets(w[["x", "y"]].values)
|
s_white.set_offsets(w[["x", "y"]].values)
|
||||||
s_black.set_offsets(b[["x", "y"]].values)
|
s_black.set_offsets(b[["x", "y"]].values)
|
||||||
@@ -54,25 +62,46 @@ def update(t):
|
|||||||
# Draw fresh arrows each frame
|
# Draw fresh arrows each frame
|
||||||
arrow_scale = 1.5 # Tune this to change arrow length visually
|
arrow_scale = 1.5 # Tune this to change arrow length visually
|
||||||
if len(w):
|
if len(w):
|
||||||
q_white = ax.quiver(w["x"], w["y"], w["dx"], w["dy"],
|
q_white = ax.quiver(
|
||||||
color="black", angles="xy", scale_units="xy",
|
w["x"],
|
||||||
scale=1/arrow_scale, width=0.003,
|
w["y"],
|
||||||
headwidth=4, headlength=5, zorder=3)
|
w["dx"],
|
||||||
|
w["dy"],
|
||||||
|
color="gray",
|
||||||
|
angles="xy",
|
||||||
|
scale_units="xy",
|
||||||
|
scale=1 / arrow_scale,
|
||||||
|
width=0.003,
|
||||||
|
headwidth=4,
|
||||||
|
headlength=5,
|
||||||
|
zorder=3,
|
||||||
|
)
|
||||||
quivers.append(q_white)
|
quivers.append(q_white)
|
||||||
if len(b):
|
if len(b):
|
||||||
q_black = ax.quiver(b["x"], b["y"], b["dx"], b["dy"],
|
q_black = ax.quiver(
|
||||||
color="gray", angles="xy", scale_units="xy",
|
b["x"],
|
||||||
scale=1/arrow_scale, width=0.003,
|
b["y"],
|
||||||
headwidth=4, headlength=5, zorder=3)
|
b["dx"],
|
||||||
|
b["dy"],
|
||||||
|
color="black",
|
||||||
|
angles="xy",
|
||||||
|
scale_units="xy",
|
||||||
|
scale=1 / arrow_scale,
|
||||||
|
width=0.003,
|
||||||
|
headwidth=4,
|
||||||
|
headlength=5,
|
||||||
|
zorder=3,
|
||||||
|
)
|
||||||
quivers.append(q_black)
|
quivers.append(q_black)
|
||||||
|
|
||||||
title.set_text(f"t = {t}")
|
title.set_text(f"t = {t}")
|
||||||
return s_white, s_black, title
|
return s_white, s_black, title
|
||||||
|
|
||||||
|
|
||||||
anim = FuncAnimation(fig, update, frames=frames, interval=50, blit=False)
|
anim = FuncAnimation(fig, update, frames=frames, interval=50, blit=False)
|
||||||
|
|
||||||
if len(sys.argv) >= 3:
|
if len(sys.argv) >= 3:
|
||||||
anim.save(sys.argv[2], writer="ffmpeg", fps=5)
|
anim.save(sys.argv[2], writer="ffmpeg", fps=25)
|
||||||
print(f"Saved {sys.argv[2]}")
|
print(f"Saved {sys.argv[2]}")
|
||||||
else:
|
else:
|
||||||
plt.show()
|
plt.show()
|
||||||
Reference in New Issue
Block a user