# Dry Clutter Runtime Notes

## Current Status
SignalReach now has a first-class dry clutter synthesis layer inside the
propagation service:
- vegetation penalties
- structure penalties
- legacy local clutter penalty
- dormant moisture modifiers

This is intentionally **backend-first** and **default-off**.

It now supports the first local data-fed path as well as presets. For now it supports:
- `disabled`
- `manual`
- `preset`
- `sampled`

The next sampler-facing modules now also exist as stable stubs:
- [clutter_sampler.py](/C:/dev/rfplanner/signalreach/propagation/clutter_sampler.py)
- [vegetation.py](/C:/dev/rfplanner/signalreach/propagation/vegetation.py)
- [structures.py](/C:/dev/rfplanner/signalreach/propagation/structures.py)
- [environment.py](/C:/dev/rfplanner/signalreach/propagation/environment.py)

Those modules are now wired into the live runtime. If no data source is
configured they behave as dry fallbacks. If a layer is placed in `sampled`
mode, the sampler can read local GeoJSON or CSV clutter sources without
changing the terrain backends again.

The main value is architectural:
- non-terrain losses are no longer one opaque `clutter_loss_db`
- lite and ITM backends both synthesize the same named loss buckets
- future raster/vector clutter samplers can plug into the same result contract

## Local Source Support
Vegetation and structure layers now support:
- `mode: sampled`
- `source_mode: geojson | csv`
- `source_path: <local file>`
- `sample_radius_m: <meters>`

This is intentionally local/offline-first. The current sampler is meant for
small to moderate operator-provided clutter layers, not large national raster
products yet.

## Files
- [mission.py](/C:/dev/rfplanner/signalreach/mission.py)
- [contracts.py](/C:/dev/rfplanner/signalreach/propagation/contracts.py)
- [context.py](/C:/dev/rfplanner/signalreach/propagation/context.py)
- [clutter.py](/C:/dev/rfplanner/signalreach/propagation/clutter.py)
- [lite.py](/C:/dev/rfplanner/signalreach/propagation/lite.py)
- [terrain_itm.py](/C:/dev/rfplanner/signalreach/propagation/terrain_itm.py)

## Runtime Model
The current synthesis equation is:

`L_total = L_terrain + L_system + L_veg + L_struct + L_local + L_env`

Where:
- `L_terrain` comes from the active terrain backbone (`fspl`, `lr_lite`, or `itm`)
- `L_system` is the explicit radio chain/system loss already in the mission
- `L_veg` comes from manual or preset vegetation penalties
- `L_struct` comes from manual or preset structure penalties
- `L_local` is the legacy `rf.clutter_loss_db`, treated as a local/terminal penalty
- `L_env` is the dormant moisture/environment modifier term

## Moisture Status
Moisture is now schema-ready but still intentionally dormant by default.

Supported modes:
- `disabled`
- `preset`

Supported presets:
- `dry`
- `normal`
- `humid`
- `post_rain`
- `saturated_worst_case`

This is **not** meant to imply calibrated weather realism yet. It simply
creates the correct contract seam so moisture later modifies vegetation/local
loss instead of replacing the terrain backbone.

## Example Mission JSON
These knobs are currently easiest to use through raw mission JSON:

```json
{
  "clutter": {
    "enabled": true,
    "mode": "preset",
    "vegetation": {
      "enabled": true,
      "mode": "preset",
      "preset": "mixed_forest"
    },
    "structure": {
      "enabled": true,
      "mode": "preset",
      "preset": "suburban_edge"
    }
  },
  "moisture": {
    "mode": "preset",
    "preset": "humid"
  }
}
```

Example sampled-source configuration:

```json
{
  "clutter": {
    "enabled": true,
    "vegetation": {
      "enabled": true,
      "mode": "sampled",
      "source_mode": "geojson",
      "source_path": "C:/data/veg_polys.geojson",
      "sample_radius_m": 25
    },
    "structure": {
      "enabled": true,
      "mode": "sampled",
      "source_mode": "csv",
      "source_path": "C:/data/buildings.csv",
      "sample_radius_m": 20
    }
  }
}
```

## Intended Use Right Now
Use this layer for:
- architecture stabilization
- controlled manual sensitivity studies
- debug/audit visibility into non-terrain penalties

Do **not** treat it as:
- a data-backed vegetation model
- a calibrated structure model
- a production moisture engine
- a scalable national clutter ingestion path

## Next Implementation Targets
1. Improve the local sampled heuristics with better path-length and penetration modeling.
2. Add scalable raster/vector ingestion beyond small local GeoJSON/CSV inputs.
3. Add moisture confidence into final uncertainty/confidence more explicitly.
4. Expose the clutter/moisture controls in the Advanced UI once the schema
   settles enough to avoid churn.
