- Python 66.7%
- R 23%
- Shell 4.8%
- Makefile 3.1%
- Dockerfile 1.3%
- Other 1.1%
Cache the minute-level SUMO emission field and road geometry, render the busy/calm CO2 animation in notebook 7, and prepare the inert reconstruction sweep with CI wiring. |
||
|---|---|---|
| .forgejo/workflows | ||
| cache | ||
| doc | ||
| graphics | ||
| lib | ||
| out | ||
| selection | ||
| src | ||
| tabpfn | ||
| .1_session-info.txt | ||
| .2_session-info.txt | ||
| .3_session-info.txt | ||
| .4_session-info.txt | ||
| .5_session-info.txt | ||
| .6_session-info.txt | ||
| .7_session-info.txt | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| .lfsconfig | ||
| 1-AoI_Raster.html | ||
| 1-AoI_Raster.Rmd | ||
| 2-Per-Cell_AoI.html | ||
| 2-Per-Cell_AoI.Rmd | ||
| 3-Parameter_Study.Rmd | ||
| 4-Required_Penetration.html | ||
| 4-Required_Penetration.Rmd | ||
| 5-Cell_Reachability.html | ||
| 5-Cell_Reachability.Rmd | ||
| 6-No_AoI.html | ||
| 6-No_AoI.Rmd | ||
| 7-Field_Reconstruction.html | ||
| 7-Field_Reconstruction.Rmd | ||
| AGENTS.md | ||
| Containerfile | ||
| envmon.emissions.add.xml | ||
| envmon.sif | ||
| envmon.sumocfg | ||
| Makefile | ||
| parallel.sbatch | ||
| README.md | ||
| reconstruct.sbatch | ||
| VERSIONS | ||
envmon
R-based geospatial analysis of sensing coverage in vehicular sensor networks. Uses SUMO traffic simulation traces from the Luxembourg LuST scenario to study how well moving vehicles can keep grid cells supplied with sensor readings (e.g. CO2 pollution). The core question: what penetration rate of sensor-equipped vehicles is needed to guarantee at least N visited time slots per hour for at least X percent of the cells of a city grid?
Metric. Each observation hour is divided into twelve 5-minute slots; a cell counts as visited in a slot if at least one equipped vehicle records a position inside it (notebook 6). The project initially measured freshness as Age of Information (AoI), but AoI was dropped as the primary metric because a cell's peak AoI is censored by the observation window: its value depends on when the last vehicle leaves the cell within the hour (see the discussion in 6-No_AoI.Rmd). Notebooks 1-4 document the earlier AoI line and are retained for reference.
Prerequisites
| Dependency | Notes |
|---|---|
| R 4.x | The Containerfile pins ghcr.io/rocker-org/geospatial:4.5.1 for local container builds; local installs need the packages listed there |
| Podman (or Docker) | For containerised builds; skip if using LOCAL_R=1 |
| Git LFS | Default hydration is about 8 GiB after excluding envmon.sif; notebooks use the full LuST trace and precomputed result Parquet files |
| csv2parquet | From domoritz/arrow-tools — used in the data pipeline |
| SUMO 1.8.0 | Only needed to regenerate the raw trace; lib/sumo is a source submodule and must be built before lib/sumo/bin/sumo exists |
Getting Started
Committed *.html files are the fastest review path. Rendering notebooks is the reproducibility check.
# Clone the public repository
git clone https://code.fabimi.net/luna/envmon.git
cd envmon
# Pull LFS objects
git lfs install
git lfs pull
# Optional: retrieve the Singularity image for SLURM/HPC use
git lfs pull -I envmon.sif
Optional submodules
Only needed for the Data Pipeline; not needed to render notebooks from committed LFS data.
git submodule update --init lib/LuSTScenario lib/sumo
Container (recommended)
make container.exists # build the Podman image
make PODMAN=docker container.exists # build with Docker
make 1-AoI_Raster.html # render a notebook inside the container
Local R
Install the R packages listed in the Containerfile, then prefix commands with LOCAL_R=1:
LOCAL_R=1 make 6-No_AoI.html
Data Pipeline
Optional: cache/envmon.parquet is committed via LFS, so reviewers do not need to run SUMO before rendering notebooks.
The trace data is produced in three sequential steps (each target depends on the previous):
SUMO simulation ──► FCD XML ──► CSV ──► Parquet
make out/envmon.fcd.xml # 1. Run SUMO → Floating Car Data XML
make out/envmon.csv # 2. xml2csv.py → semicolon-delimited CSV
make cache/envmon.parquet # 3. csv2parquet → Parquet (used by notebooks)
make out/envmon.fcd.xml requires a built SUMO binary at lib/sumo/bin/sumo; make out/envmon.csv uses SUMO's tools/xml/xml2csv.py; make cache/envmon.parquet requires csv2parquet.
Analysis Notebooks
Notebooks 1-4 use the superseded AoI metric; notebook 5 (reachability) supports both metrics; notebook 6 defines the current visit-based metric.
| # | File | Purpose | Key Parameters |
|---|---|---|---|
| 1 | 1-AoI_Raster.Rmd |
Single-vehicle AoI visualisation on a raster grid | frame_duration |
| 2 | 2-Per-Cell_AoI.Rmd |
Per-cell AoI for sampled vehicle subsets | penetration_rate |
| 3 | 3-Parameter_Study.Rmd |
Compute-heavy sweep under default params (seq_len(10) × c(0.1,0.3,0.7,0.9) × 10 workers) |
repetitions, penetration_rates, no_workers |
| 4 | 4-Required_Penetration.Rmd |
Determine the required penetration rate | (loads pre-computed results) |
| 5 | 5-Cell_Reachability.Rmd |
Grid cell reachability analysis | sample_size, time windows |
| 6 | 6-No_AoI.Rmd |
Visit-based slot-coverage metric (current) | slot_width, penetration_rate |
3-Parameter_Study.html is intentionally not committed because the full default render is a parameter sweep; CI and smoke runs use reduced parameters.
Rendering
make 3-Parameter_Study.html # full default sweep in the container
LOCAL_R=1 make 3-Parameter_Study.html # full default sweep with local R
# Recommended smoke render for notebook 3
R --vanilla -e 'rmarkdown::render("3-Parameter_Study.Rmd", params=list(repetitions=seq(1), penetration_rates=c(0.001)))'
# Custom parameters
R --vanilla -e 'rmarkdown::render("2-Per-Cell_AoI.Rmd", params=list(penetration_rate=0.001))'
Extracting a standalone R script
make 3-Parameter_Study.R # runs knitr::purl() to extract R code
Batch Processing (SLURM)
Batch runs for the current visit metric use the standalone script extracted from notebook 6 (make 6-No_AoI.R); it accepts the notebook parameters via optparse (including --slot_width) and writes one visits.parquet per run — the committed results under out/cell-visits/ were produced this way.
src/run-aoi.R is the equivalent CLI for the superseded AoI metric:
Rscript src/run-aoi.R \
--penetration_rate 0.01 \
--repetition 1 \
--cell_size 200 \
--crs 32632 \
--busy_start 28800 --busy_end 32400 \
--calm_start 57600 --calm_end 61200
Only --penetration_rate is required; the rest have sensible defaults. Output goes to out/{SLURM_ARRAY_JOB_ID}/{SLURM_ARRAY_TASK_ID}/ on a cluster, or out/local/{UUID}/ for local runs. Each run produces aoi.parquet, grid.shp, and session-info.txt.
See parallel.sbatch for the SLURM array job template; as committed it invokes src/run-aoi.R, so substitute the purled 6-No_AoI.R for visit-metric runs.
Project Structure
.
├── 1-AoI_Raster.Rmd … 6-No_AoI.Rmd # analysis notebooks
├── Makefile # build automation
├── Containerfile # Podman/Docker image definition
├── VERSIONS # pinned tool versions
├── envmon.sumocfg # SUMO simulation config
├── parallel.sbatch # SLURM batch script
├── src/
│ ├── helpers.R # shared functions (loaders, grid, binning, AoI)
│ └── run-aoi.R # standalone CLI for batch AoI computation (superseded metric)
├── cache/ # cached data (Parquet, RData, shapefiles)
├── out/ # pipeline outputs and batch results
├── lib/
│ ├── sumo/ # SUMO v1_8_0 (git submodule)
│ └── LuSTScenario/ # Luxembourg traffic scenario (git submodule)
├── graphics/ # generated figures plus tracked graphics/6/aoi-problem.png input illustration
└── doc/ # presentations
CI
Forgejo Actions renders all six notebooks with reduced parameters on every pull request and uploads the HTML output as artifacts.
Future Directions
Candidate extensions toward publication, ranked by payoff:
| Direction | Core question | Why it is strong | Effort |
|---|---|---|---|
| Field reconstruction | Does slot coverage guarantee accurate pollution maps? | SUMO's emission model yields a ground-truth CO2 field generated by the very traffic being sampled; kriging/GP reconstruction error vs. penetration derives principled values for the ad-hoc constants (slot width 300 s, N = 6, X = 75 %) and is free of the window-censoring artifact that ruled out peak AoI | Medium |
| Analytic thinning model | Can required penetration be computed in closed form? | Random equipping at rate p thins the full visit process, so P(cell c covered in slot s) = 1 − (1 − p)^m with m the number of distinct vehicles visiting (c, s) in the full trace — exact, replaces the repetition-based sweeps of notebooks 3/4/6, and the shared-vehicle dependence explains why ranking vehicles independently fails | Low |
| Vehicle selection | How few vehicles suffice if chosen well? | Large oracle-vs-random headroom; the per-cell indicator 1{≥ N slots} is not submodular, but its truncation min(#slots, N) is, so lazy greedy carries a (1 − 1/e) guarantee on the surrogate; a learner should predict visit sets and marginal gain, not per-pair hits | Medium |
| Multi-day generalization | Does a selection made today still cover tomorrow? | The selection objective is an expectation over future days, but LuST is a single synthetic day; perturbed SUMO demand seeds or real multi-day taxi traces make the claim testable | Medium |
| Cross-city scaling | Does required penetration transfer across cities? | MoST, TAPASCologne, InTAS, or real taxi traces defuse the single-city objection; a scaling law against road density or urban form would be citable on its own | High |
| Metric design | How sensitive is the metric to its constants? | Sweeps over slot width, N, X, and cell size (MAUP) plus a slot phase-shift check turn ad-hoc choices into defended ones | Low |
| Fleet heterogeneity | Which fleet mix buys coverage cheapest? | Bus lines are repeatable, hence have predictable visit sets — ideal input for selection; budgeted equipping matches how real deployments work | Medium |
| Micro-detour steering | Can bounded detours rescue never-visited cells? | Covering empty (cell, slot) pairs under a detour budget is budgeted set cover; motivated by the censored tail in notebook 5 | High |
| Weighted coverage | Coverage where it matters most | Cell weights (population exposure) and slot weights (time-of-day exposure) drop directly into the coverage objective | Low |
Communication realism is deliberately out of scope: a 300 s slot tolerates minutes of upload delay by construction, so the abstraction is justified in prose rather than modeled (e.g. an ns-3/Veins coupling).