Skip to content

cnsp/ems-optimization

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

104 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

EMS Readiness Optimization for Manhattan

Python 3.11+ License: MIT

A simulation-based approach to optimizing ambulance staging locations in Manhattan, reducing EMS response times by 19% over a spatially-stratified baseline through demand-weighted allocation with capacity=2 constraints.


Key Findings

Metric P0 (Baseline) P2 (Optimized) Improvement
Mean Response Time 3.17 min 2.57 min βˆ’18.9%
P95 Response Time 6.26 min 4.66 min βˆ’25.6%
8-min Coverage 99.7% 99.7% +0.0 pp
Mean Utilization 7.6% 7.4% βˆ’0.2 pp

Results based on 2,700+ simulation experiments with 30 replications each (p < 0.001). Capacity sensitivity analysis (cap 1–5) confirms cap=2 as operationally optimal default. Alternative analyses confirm stability under distance metric choice and geographic focus.


Project Overview

This project evaluates three ambulance allocation policies for Manhattan's 48 FDNY firehouses:

  • P0 (Spatially-Stratified Uniform): Latitude-based geographic stratification providing even north–south coverage
  • P1 (Demand-Proportional): Units allocated based on nearby demand
  • P2 (Demand-Weighted Optimized): MIP-optimized allocation minimizing expected response time

The analysis combines:

  1. NHPP Demand Modeling β€” Calibrated from 2.24M historical motor vehicle collision records
  2. Mixed-Integer Programming β€” Three optimization formulations with capacity=2 constraint (PuLP/CBC solver)
  3. Discrete-Event Simulation β€” SimPy-based DES with 2,700+ production runs across 9 experiment sets
  4. Statistical Analysis β€” ANOVA, Tukey HSD, confidence intervals, effect sizes
  5. Capacity Sensitivity Analysis β€” Full spectrum (cap 1–5) across 9 fleet sizes
  6. Alternative Analyses β€” Manhattan distance metric comparison + CBD-focused optimization evaluation

Start Here

If you're new to the project:

  1. Read the Technical Report Β§1 (Executive Summary)
  2. Browse results/baseline/ β€” this is the canonical source of truth
  3. Run the baseline pipeline (optional β€” results are pre-committed):
    python scripts/run_production_v2.py
  4. Understand the tier system: Canonical β†’ Supporting β†’ Historical (see below)

For supporting analyses: results/analysis/ and docs/analysis/
For architecture deep-dive: docs/core/ARCHITECTURAL_MAP.md
For artifact governance: docs/core/artifact_governance_map.md

Source of Truth

All results and documentation follow a three-tier governance system:

Tier Label Location Status
Canonical βœ… Current results/baseline/, docs/core/ Authoritative β€” cited in technical report and README
Supporting πŸ”¬ Analysis results/analysis/, docs/analysis/ Robustness and sensitivity analyses that strengthen canonical findings
Historical ⚠️ Archive results/archive/, docs/archive/, scripts/archive/ Superseded artifacts (e.g., cap=5 era). Retained for audit trail only

Canonical values used throughout this project:

Parameter Value Source
Base arrival rate (Ξ»β‚€) 3.48 crashes/hour configs/demand.yaml
Firehouse capacity (C) 2 units/firehouse configs/optimization.yaml, DEC-010
Simulation horizon 168 hours (1 week) configs/simulation.yaml
Replications per scenario 30 configs/simulation.yaml
Master seed 42 configs/demand.yaml
Coverage threshold 8 minutes (NFPA) / 6 minutes (NYC) configs/simulation.yaml
Average EMS speed 20 mph configs/service.yaml
Service time mean 25 min (LogNormal) configs/service.yaml
Canonical K set {10, 15, 20, 25, 30, 35, 40, 45, 48} configs/optimization.yaml

Full governance details: docs/core/artifact_governance_map.md Figure/table provenance: docs/core/figure_table_traceability.md

Experimental Scope

The study comprises 4 core experiments and 4 supporting analyses, totalling 2,700+ simulation runs:

Experiment Factors Scope Generator
Exp1 β€” Policy Comparison P0, P1, P2 at K=20 3 Γ— 30 reps = 90 runs scripts/run_production_v2.py
Exp2 β€” Fleet Sensitivity 9 K-values Γ— 3 policies 27 Γ— 30 reps = 810 runs scripts/run_production_v2.py
Exp3 β€” Demand Sensitivity 6 demand multipliers Γ— 2 policies 12 Γ— 30 reps = 360 runs scripts/run_production_v2.py
Exp4 β€” Service Robustness 3 service means Γ— 2 policies 6 Γ— 30 reps = 180 runs scripts/run_production_v2.py
Capacity β€” Cap Sensitivity cap {1–5} Γ— K {20,40} Γ— 3 policies Supporting scripts/analysis/capacity_sensitivity_*.py
CBD β€” CBD Robustness CBD surge scenarios 330 runs scripts/analysis/run_cbd_experiment.py
Distance β€” Metric Comparison Haversine vs Manhattan Supporting scripts/analysis/run_distance_comparison_experiment.py
Queue/Seasonal β€” Derived Post-hoc analysis of Exp1–4 Supporting scripts/analysis/analyze_queue_metrics.py, analyze_seasonal_patterns.py

All experiments use Common Random Numbers (CRN) for variance reduction. Verification (4 tests) and validation (3 pilots) are documented in the technical report Β§4.4.3.

Full experimental design: docs/core/experimental_design.md

Repository Architecture

The repository is organized to eliminate ambiguity:

Results:

  • Baseline β†’ canonical results (results/baseline/)
  • Analysis β†’ robustness & sensitivity (results/analysis/)
  • Archive β†’ historical artifacts (results/archive/)

Scripts:

  • Scripts (root) β†’ production entry points
  • scripts/analysis/ β†’ supporting analyses
  • scripts/archive/ β†’ legacy utilities

Documentation:

  • docs/core/ β†’ primary documentation
  • docs/analysis/ β†’ supporting analyses
  • docs/archive/ β†’ historical records

Repository Structure

ems-optimization/
β”œβ”€β”€ configs/ # YAML configuration files
β”‚ β”œβ”€β”€ demand.yaml # NHPP demand model parameters
β”‚ β”œβ”€β”€ optimization.yaml # MIP solver settings (capacity=2 default)
β”‚ β”œβ”€β”€ service.yaml # Travel & service time config
β”‚ β”œβ”€β”€ simulation.yaml # DES engine settings
β”‚ └── cbd_scenario.yaml # CBD robustness experiment config
β”œβ”€β”€ data/
β”‚ β”œβ”€β”€ raw/ # Original data files (NYC Open Data)
β”‚ β”œβ”€β”€ processed/ # Generated data (not in Git β€” run `make data`)
β”‚ └── manifests/ # Data audit records
β”œβ”€β”€ docs/ # Project documentation (62+ .md files, see docs/core/DOCUMENTATION_INDEX.md)
β”‚ β”œβ”€β”€ core/ # Key deliverables, specs, guides
β”‚ β”‚ β”œβ”€β”€ technical_report.md # Full final report (v2.1.0)
β”‚ β”‚ β”œβ”€β”€ executive_presentation.md # Stakeholder slide deck
β”‚ β”‚ └── ... # Additional core docs
β”‚ β”œβ”€β”€ analysis/ # Robustness & sensitivity analyses
β”‚ β”‚ β”œβ”€β”€ capacity_sensitivity_analysis.md
β”‚ β”‚ └── ... # Additional analysis docs
β”‚ └── archive/ # Historical/superseded docs
β”œβ”€β”€ notebooks/ # Jupyter analysis notebooks
β”‚ β”œβ”€β”€ 02_eda_spatiotemporal.ipynb
β”‚ β”œβ”€β”€ 03_input_modeling.ipynb
β”‚ β”œβ”€β”€ 04_service_travel_proxy.ipynb
β”‚ β”œβ”€β”€ 05_optimization.ipynb
β”‚ β”œβ”€β”€ 06_simulation_debug.ipynb
β”‚ β”œβ”€β”€ 07_production_results.ipynb
β”‚ β”œβ”€β”€ 08_statistical_analysis.ipynb
β”‚ β”œβ”€β”€ 09_cbd_analysis.ipynb
β”‚ └── colab_standalone/ # Google Colab standalone notebooks
β”‚     β”œβ”€β”€ individual/ # 8 phase notebooks (00–07)
β”‚     β”œβ”€β”€ EMS_Optimization_Complete_Pipeline.ipynb
β”‚     └── README.md
β”œβ”€β”€ results/
β”‚ β”œβ”€β”€ baseline/ # Canonical production results (cap=2)
β”‚ β”‚ β”œβ”€β”€ allocations/ # Policy allocation CSVs
β”‚ β”‚ β”œβ”€β”€ figures/ # Publication-ready figures
β”‚ β”‚ β”œβ”€β”€ simulation/ # Verification & validation output
β”‚ β”‚ └── tables/ # Statistical summary tables
β”‚ β”œβ”€β”€ analysis/ # Supporting robustness & sensitivity
β”‚ β”‚ β”œβ”€β”€ capacity_comparison/ # Capacity sensitivity (cap 1–5) results
β”‚ β”‚ β”œβ”€β”€ cbd_focused_comparison/ # CBD-focused vs Manhattan-wide results
β”‚ β”‚ β”œβ”€β”€ distance_comparison/ # Haversine vs Manhattan metric results
β”‚ β”‚ β”œβ”€β”€ heatmaps/ # Allocation heatmaps (K Γ— cap Γ— policy)
β”‚ β”‚ β”œβ”€β”€ maps/ # Allocation map visualizations
β”‚ β”‚ β”œβ”€β”€ simulation/ # CBD & production DES output
β”‚ β”‚ └── tables/ # Analysis summary tables
β”‚ β”œβ”€β”€ archive/ # Legacy/historical artifacts (cap=5 era)
β”‚ β”‚ β”œβ”€β”€ figures/ # Superseded figures
β”‚ β”‚ β”œβ”€β”€ optimization/ # Phase 3 historical optimization results
β”‚ β”‚ └── tables/ # Superseded tables
β”‚ β”œβ”€β”€ figures/ # Top-level visualization PNGs
β”‚ β”œβ”€β”€ tables/ # Top-level statistical tables (CSV + LaTeX)
β”‚ └── simulation/ # Top-level simulation output data
β”œβ”€β”€ scripts/ # Automation & analysis scripts (23+)
β”œβ”€β”€ src/ems_readiness/ # Core Python package (v0.6.0)
β”‚ β”œβ”€β”€ demand/ # NHPP arrival generator
β”‚ β”œβ”€β”€ service/ # Travel time & service time models
β”‚ β”œβ”€β”€ optimization/ # MIP formulations, policies & allocator
β”‚ β”œβ”€β”€ simulation/ # SimPy DES engine
β”‚ └── utils/ # Distance calculations
β”œβ”€β”€ tests/ # 176 unit tests (pytest)
β”œβ”€β”€ requirements.txt # Python dependencies
β”œβ”€β”€ Makefile # Build automation
└── LICENSE # MIT License

Installation & Setup

Prerequisites

  • Python 3.11+
  • pip

Quick Start

# Clone the repository
git clone https://github.com/cnsp/ems-optimization.git
cd ems-optimization

# Create virtual environment and install dependencies
make setup
# Or manually:
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Install project package in development mode
pip install -e .

Data Setup

Large raw data files are tracked via Git LFS. Ensure Git LFS is installed before cloning:

git lfs install   # one-time setup
git clone https://github.com/cnsp/ems-optimization.git

The following files are pulled automatically via LFS on clone:

  • data/raw/Motor_Vehicle_Collisions_-_Crashes_20260223.csv (536 MB)
  • data/raw/Police_Precincts_20260223.csv (3.6 MB)

All other raw files (boundary GeoJSONs, firehouse listings, data dictionaries) and key processed files (distance matrices, demand lambdas, firehouse lists) are tracked directly in Git.

If LFS files were not pulled (e.g., shallow clone), run:

git lfs pull

See data/raw/README.md for original NYC Open Data download links.

Note: The Colab notebooks (notebooks/colab_standalone/) include self-contained data processing cells that generate crashes_manhattan.csv from the raw crash CSV on first run. All other processed files are tracked in Git and require no extra setup.

Generate all processed data (single command):

make data
# Or: python scripts/generate_all_data.py
  1. Verify data:
make verify-data

Pipeline CLI Options

The data pipeline (scripts/generate_all_data.py) supports several options:

Flag Description
--force Regenerate all files even if they exist
--no-validate Skip raw data validation checks
--no-cache Disable smart caching
--jobs N / -j N Parallel workers (0 = auto-detect CPU count)
--tier {1,2,3} Run only a specific tier
--version Show current data version info
--seed N Master seed for reproducibility (default: 42)
--verify Check data existence without generating
# Force-regenerate with 4 parallel workers
python scripts/generate_all_data.py --force --jobs 4

# Show data version/lineage info
python scripts/generate_all_data.py --version

# Skip validation (faster startup)
python scripts/generate_all_data.py --no-validate

Note: Notebooks auto-detect and regenerate missing processed data on first run, so you can also just open any notebook and it will trigger the pipeline if needed.

How to Reproduce Results

Run the Full Pipeline

# 1. Process data (if raw data available)
make data

# 2. Run optimization comparison
python scripts/analysis/run_optimization_comparison.py

# 3. Run simulation verification
python scripts/run_verification.py

# 4. Run validation pilots
python scripts/run_validation_pilots.py

# 5. Run production experiments (1,440 simulations β€” takes ~2-4 hours)
python scripts/run_production_v2.py

# 6. Analyze results and generate figures
python scripts/analysis/analyze_production_results.py
python scripts/analysis/generate_publication_figures.py

# 7. Run CBD robustness experiment (330 simulations)
python scripts/analysis/run_cbd_experiment.py --reps 30

# 8. Run gap closure analyses
python scripts/analysis/analyze_queue_metrics.py
python scripts/analysis/analyze_seasonal_patterns.py

# 9. Run alternative analyses (Phase 8)
python scripts/analysis/generate_manhattan_distance_matrix.py
python scripts/analysis/run_distance_comparison_experiment.py --reps 10
python scripts/analysis/run_cbd_focused_optimization.py --reps 10

# 10. Run capacity sensitivity analysis (Phase 9)
python scripts/analysis/capacity_sensitivity_full_spectrum.py

# 11. Run P0 spatial stratification analysis
python scripts/analysis/p0_spatial_analysis.py

# 12. Run extended fleet analysis experiments (810 simulations β€” cap=2)
python scripts/run_production_v2.py

Run Specific Experiments

from ems_readiness.simulation import EMSSimulation, BatchRunner
from ems_readiness.optimization import EMSAllocator

# Initialize allocator
allocator = EMSAllocator.from_project(".")

# Get P2 allocation for K=20 units (capacity=2 default)
result = allocator.solve(model="demand_weighted", K=20)
print(result.allocation)

# Run simulation
sim = EMSSimulation.from_config("configs/simulation.yaml", allocation=result.allocation)
metrics = sim.run(duration_hours=168)
print(f"Mean RT: {metrics['mean_response_time']:.2f} min")

How to Run Tests

# Run all 176 tests
pytest tests/ -v

# Run specific test modules
pytest tests/test_simulation_core.py -v
pytest tests/test_dispatch_logic.py -v
pytest tests/test_extreme_cases.py -v
pytest tests/test_reproducibility.py -v

Documentation Index

πŸ“– Full index: docs/core/DOCUMENTATION_INDEX.md β€” master list of all 62 docs with status badges (βœ… Current / πŸ”„ Historical / πŸ“‹ Reference)

Key Documents

Document Description
Technical Report Full final report (v2.1.0) with all findings
Executive Summary One-page executive summary
Executive Presentation 10-slide stakeholder presentation
Data Usage Guide Which data/results files to use and why
Reproducibility Guide How to reproduce all results

Model & Methods

Document Description
Conceptual Model DES model specification
Optimization Formulation MIP mathematical formulations
Experimental Design Factorial experiment specification
Optimization Results Current optimization results (cap=2)

Analysis & Robustness

Document Description
Capacity Sensitivity Full-spectrum capacity (cap 1–5) analysis
CBD Robustness CBD-specific DES experiment findings
Distance Metric Comparison Haversine vs. Manhattan distance analysis
Research Questions Evaluation of all 5 research questions

Guides

Document Description
Code Documentation Architecture and API guide
Notebook Guide Notebook descriptions and execution order
Testing Guide Testing framework and how to run tests
Figure Trace Guide Figure-to-source traceability
Visualization Index Complete catalog of all generated figures

Navigating Results

The results/ folder contains output from multiple project phases. Each subfolder has its own README.md explaining what's there and whether it's current or legacy.

Folder Label Status What's Inside
results/baseline/ Canonical βœ… Current Production allocations, figures, simulation verification/validation, tables
results/baseline/simulation/ Canonical βœ… Current Verification tests, validation pilots
results/analysis/ Supporting βœ… Current All robustness & sensitivity analyses
results/analysis/capacity_comparison/ Supporting βœ… Current Full capacity sweep (cap 1–5)
results/analysis/cbd_focused_comparison/ Supporting βœ… Current CBD-focused optimization experiment
results/analysis/distance_comparison/ Supporting βœ… Current Manhattan vs Haversine comparison
results/analysis/heatmaps/ Supporting βœ… Current Allocation heatmaps across K Γ— cap Γ— policy
results/analysis/maps/ Supporting βœ… Current Allocation maps at K=40
results/archive/ Legacy ⚠️ Historical Phase 3 historical artifacts (cap=5 era)
results/archive/optimization/ Legacy ⚠️ Historical Superseded optimization results
results/figures/ β€” βœ… Mostly Current Publication figures (pub_fig*) + technical plots
results/tables/ β€” βœ… Mostly Current Publication tables + experiment pivots (see README)
results/simulation/ β€” βœ… Current Top-level simulation output data

Quick reference: results/WHICH_FILES_TO_USE.md
Full guide: docs/core/data_usage_guide.md

Google Colab Notebooks

For external verification and reproduction, standalone Google Colab notebooks are available in notebooks/colab_standalone/. These require no local setup β€” just open in Colab and run.

Notebook Phase Runtime
00_colab_setup_and_data Environment setup, data verification ~2 min
01_colab_eda_spatiotemporal Exploratory data analysis ~5 min
02_colab_demand_modeling NHPP demand model ~5 min
03_colab_service_modeling Travel/service time models ~3 min
04_colab_optimization P0/P1/P2 allocation policies ~5 min
05_colab_simulation SimPy DES (demo or full-scale) 5 min – 8 hr
06_colab_statistical_analysis ANOVA, effect sizes, CIs ~3 min
07_colab_visualization_reporting Publication figures, summary report ~3 min
All-in-One Pipeline Complete end-to-end analysis 5–10 hr

See notebooks/colab_standalone/README.md for detailed instructions.

Research Questions

  1. RQ1: How does demand for EMS services vary spatially and temporally across Manhattan?
  2. RQ2: What is the optimal allocation of K ambulances to minimize expected response time?
  3. RQ3: How do optimized allocations compare to the spatially-stratified uniform baseline under realistic conditions?
  4. RQ4: How sensitive are policy rankings to fleet size, demand intensity, service time, and firehouse capacity?
  5. RQ5: What fleet size achieves β‰₯95% coverage within 8 minutes?

Citation

@misc{ems_optimization_2026,
 title={EMS Readiness Optimization for Manhattan: A Simulation-Based Approach to Ambulance Staging},
 author={EMS Optimization Research Team},
 year={2026},
 howpublished={\url{https://github.com/cnsp/ems-optimization}},
 note={Version 1.3.0}
}

License

This project is licensed under the MIT License β€” see LICENSE for details.

Contact


Built with Python, SimPy, PuLP, pandas, and matplotlib. 8,500+ lines of code across 14 modules, tested with 176 unit tests and 2,700+ simulation experiments. Covers capacity sensitivity (cap 1–5), spatially-stratified P0 baseline, and alternative distance metric and CBD-focused optimization analyses.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages