# Revegetation (Cover Transform) NoDb Mod

> Manages post-fire revegetation “cover transform” CSVs that rescale RAP fractional cover time series before WEPP (and related) runs.

> **See also:** [AGENTS.md](../../AGENTS.md) for NoDb locking/cache expectations and debugging hooks.

## Overview

The `Revegetation` mod is a small NoDb-backed controller used to apply scenario-based vegetation recovery assumptions after a disturbance (for example, wildfire).

When enabled alongside `disturbed` and `rap_ts`, the model workflow supports two modeling paradigms:
- **Historic wildfire (observed recovery):** use observed RAP cover time series directly and assume RAP captures post-fire recovery dynamics.
- **Hypothetical historic fire (scenario recovery):** apply a cover-transform curve so post-fire RAP cover is adjusted from the fire-year baseline to represent assumed disturbance/recovery trajectories.

Current scope limit:
- Revegetation is constrained to the years available in RAP/climate inputs for the run. Selecting a `20-yr_*` scenario does not extend simulation years beyond available observed RAP years.

Core workflow capabilities:
- Select a built-in cover-transform scenario (for example, a 20-year recovery curve), or accept a user-uploaded transform CSV.
- Persist the selected transform into the run working directory for reproducibility.
- Provide a parsed `cover_transform` mapping that `RAP_TS` uses to generate transformed `.cov` time series used by WEPP preparation.

This mod does **not** directly run WEPP/RHEM; it supplies metadata and transforms used by other controllers during prep.

Important semantics:
- The revegetation scenario does **not** change simulation length. WEPP still runs for the years present in the climate inputs.
- At prep time, observed RAP vs transformed RAP is a mutually exclusive branch (`prep_cover` chooses one writer path).
- In the WEPP engine, RAP/curve cover series are used as canopy constraints in growth logic (cap behavior), not as unconditional canopy replacement on every day.

## Workflow

1. **User selects a scenario (or uploads a CSV).**
   - UI sends `reveg_scenario` as one of:
     - `""` (Observed / no transform)
     - `"20-yr_Recovery.csv"` (built-in)
     - `"20-yr_PartialRecovery.csv"` (built-in)
     - `"user_cover_transform"` (user upload mode)
2. **Controller stages the transform file under the run working directory.**
   - Built-ins are copied from `data/cover_transforms/` into `<wd>/revegetation/`.
   - User uploads are saved into `<wd>/revegetation/` and then “activated” by recording the filename.
3. **`RAP_TS.prep_cover()` decides whether to transform.**
   - If both `Disturbed.fire_date` and `Revegetation.cover_transform` are present, RAP cover is rescaled relative to the fire-year cover and written to `p*.cov` via `RAP_TS._prep_transformed_cover()`.
   - Otherwise, RAP cover is written without transformation.
   - Only years represented in RAP/climate for that run are written and consumed by WEPP (`nyear`/`numyrs`), even when the selected curve file contains 20 years.

## Core API

Primary entry point: `wepppy.nodb.mods.revegetation.revegetation.Revegetation`.

| Member | Type | Purpose |
|---|---|---|
| `Revegetation.getInstance(wd)` | classmethod | Returns the run-scoped singleton (hydrated from `<wd>/revegetation.nodb`). |
| `load_cover_transform(reveg_scenario)` | method | Activates a built-in scenario by copying it into `<wd>/revegetation/` and setting `cover_transform_fn`. No-op for `"user_cover_transform"`. Clears selection when passed `""`. |
| `validate_user_defined_cover_transform(fn)` | method | Marks an already-saved file in `<wd>/revegetation/` as the active transform and sets `user_defined_cover_transform=True`. |
| `cover_transform_fn` | `str` property | Filename of the active CSV in `<wd>/revegetation/` (empty string means “Observed”). |
| `user_defined_cover_transform` | `bool` property | Indicates whether the active transform was user-supplied. |
| `cover_transform` | `Optional[CoverTransform]` property | Parsed scale-factor mapping, or `None` when no valid CSV is active. |
| `revegetation_dir` | `str` property | `<wd>/revegetation`. |
| `clean()` | method | Deletes and recreates `<wd>/revegetation/`. Called during controller initialization. |

### `CoverTransform` shape

`CoverTransform` is a mapping:

```python
CoverTransform = dict[tuple[str, str], numpy.ndarray[numpy.float32]]
```

Keys are `(burn_class, vegetation_label)` pairs (for example, `("High", "Tree")`), and values are 1-D arrays of per-year multiplicative scale factors.

## Cover Transform CSV format

The controller expects a headerless CSV where:
- **Row 0**: soil burn severity labels (repeated across columns)
- **Row 1**: vegetation labels (one per column)
- **Rows 2..**: scale factors for year 0, year 1, … relative to the **fire year**

Example (two header rows plus year-0 scale factors):

```text
High,High,Moderate,Moderate
Tree,Shrub,Tree,Shrub
0.30,0.30,0.50,0.50
```

Notes:
- `RAP_TS` currently looks up transform keys for the labels: `Tree`, `Shrub`, `Perennial`, `Annual`, `Litter`, `Bare`. If a key is missing, that band is left untransformed.
- If the RAP time series extends beyond the number of scale-factor rows, `RAP_TS` uses the **last** scale factor.
- Years *before* the fire year are not transformed.
- For keys present in the transform, post-fire values are derived from the RAP fire-year baseline multiplied by the curve scale factor (not from each year's observed RAP value).

## Outputs

This mod writes (or causes downstream prep to write) the following run-scoped artifacts:

- **NoDb state**: `<wd>/revegetation.nodb` (managed by `NoDbBase`)
- **Staged transform CSV**: `<wd>/revegetation/<scenario>.csv` (copied from the library or uploaded by a user)
- **Downstream WEPP inputs (via `RAP_TS`)**:
  - `<wd>/runs/*/p*.cov` (time-varying cover)
  - `<wd>/runs/*/simfire.txt` and `<wd>/runs/*/firedate.txt` (fire timing metadata)

## Integration points

- **Upstream / prerequisites**
  - `Disturbed.fire_date` enables “post-fire” timing used by `RAP_TS` transforms.
  - `Landuse.identify_burn_class(...)` provides burn class labels used as transform keys.
- **Downstream consumers**
  - `wepppy.nodb.mods.rap.rap_ts.RAP_TS.prep_cover()` reads `Revegetation.cover_transform` when present.
  - `wepppy.nodb.core.wepp.Wepp._prep_revegetation()` triggers `RAP_TS.prep_cover()` and writes fire metadata.
- **WEPPcloud / rq-engine plumbing**
  - `wepppy/microservices/rq_engine/wepp_routes.py` reads `reveg_scenario` and calls `Revegetation.load_cover_transform(...)`.
  - `wepppy/microservices/rq_engine/upload_disturbed_routes.py` handles user CSV upload and calls `Revegetation.validate_user_defined_cover_transform(...)`.
  - `wepppy/weppcloud/templates/controls/wepp_pure_advanced_options/revegetation.htm` renders the UI selector and upload control.

## Quick start / examples

### Load a built-in scenario

```python
from wepppy.nodb.mods.revegetation import Revegetation

reveg = Revegetation.getInstance("/path/to/run-wd")
reveg.load_cover_transform("20-yr_Recovery.csv")

transform = reveg.cover_transform
assert transform is not None
print(transform[("High", "Tree")][:5])  # year-0..year-4 scale factors
```

### Activate a user-uploaded scenario

```python
from pathlib import Path
from wepppy.nodb.mods.revegetation import Revegetation

wd = "/path/to/run-wd"
reveg = Revegetation.getInstance(wd)

# Save a CSV under <wd>/revegetation/ (the rq-engine upload route does this step).
csv_name = "my_transform.csv"
Path(reveg.revegetation_dir, csv_name).write_text("High\\nTree\\n0.3\\n")

reveg.validate_user_defined_cover_transform(csv_name)
assert reveg.user_defined_cover_transform is True
```

## Data assets in this directory

- `data/cover_transforms/*.csv`: Built-in cover-transform scenarios.
  - `20-yr_Recovery.csv` and `20-yr_PartialRecovery.csv` are also duplicated at the package root for convenience (identical content).
  - `data/cover_transforms/cover_transform.xlsx` is the spreadsheet source for those CSVs.
- `data/revegetation_land_soil_lookup.csv`: Landuse/soil-texture lookup used by revegetation-oriented run configs (for example, `wepppy/nodb/configs/reveg*.cfg`) to select parameter sets.

## Developer notes / caveats

- `Revegetation.clean()` deletes the entire `<wd>/revegetation/` directory; it is called during controller initialization.
- `validate_user_defined_cover_transform(...)` only checks that the file exists under `<wd>/revegetation/` and records its name; it does not parse/validate the CSV schema beyond what `cover_transform` will later attempt to load.
- `cover_transform` treats the first two rows as headers and does not enforce numeric types until converting values to `float32`.
- Naming a scenario `20-yr_*` describes curve length, not guaranteed simulation duration. The run length still comes from climate years and matching RAP years.
- In WEPP Fortran RAP mode, the `p*.cov` series is applied as a canopy upper bound in the relevant growth branches (`if(cancov > threshold) cancov = threshold`). This is why transformed scenarios may show little or no sediment difference when modeled canopy rarely exceeds that bound.

## Further reading

- `wepppy/nodb/mods/revegetation/revegetation.py`
- `wepppy/nodb/mods/rap/rap_ts.py`
- `wepppy/nodb/core/wepp.py`
- `wepppy/microservices/rq_engine/wepp_routes.py`
- `wepppy/microservices/rq_engine/upload_disturbed_routes.py`
- `wepppy/weppcloud/templates/controls/wepp_pure_advanced_options/revegetation.htm`
