Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ missing, unclear, or inconsistent with the actual code/data? -->
- [ ] Add/update docstrings in `path/to/module.py`
- [ ] Write or refine a documentation page in `docs/`
- [ ] Update code examples in `examples/` to reflect current API behavior
- [ ] Update provenance notes in `benchmark/README.md` or
`docs/validation.md`
- [ ] Update `docs/validation.md` (it points to disslucc-benchmark)

### References & Context
<!-- Link to relevant code, the original LuccME scripts (kept in
LambdaGeo/terrame-docker, benchmark/references/), papers, or related
LambdaGeo/terrame-docker, benchmark/references/; results in
LambdaGeo/disslucc-benchmark), papers, or related
issues, if applicable. -->
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/onboarding_task.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ assignees: ""
2. Follow the setup in `README.md` (`pip install -e ".[examples,dev]"`) and
activate the virtual environment.
3. Make the specified adjustments.
4. Run `pytest tests/ -v` -- expect `19 passed, 2 xfailed` unmodified,
unless your task changes that on purpose.
4. Run `pytest tests/ -v` -- expect everything to pass
unmodified.
5. Run `mypy src/disslucc` and fix any new warnings it reports.

### Expected Deliverable
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Publish to PyPI

# PyPI Trusted Publishing (no token): the publisher on pypi.org is
# owner DisSModel, repository disslucc, workflow publish.yml.
on:
release:
types: [published]
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Build
run: |
python -m pip install --upgrade pip build
python -m build
- uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/

publish:
runs-on: ubuntu-latest
needs: build
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
39 changes: 30 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,41 @@

All notable changes to `disslucc` are documented here.

## [Unreleased]
## [0.5.0] -- 2026-10-08

Clean-up release: the numerical comparison with TerraME/LuccME moved to
[LambdaGeo/disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark),
and this repository keeps the package and examples to learn it with.
Everything removed is in the `v0.4.0` tag.

### Removed
- `benchmark/goldens/` and `benchmark/README.md` (reference results now live in
luccme-goldens and are consumed by disslucc-benchmark).
- Golden, per-year, discriminance, validation and Lua differential tests and their
helpers; the `lupa` dev dependency; `examples/run_lab1_validation.py` and
`examples/run_lab15_validation.py`.

### Changed
- Examples renamed without "lab": `run_continuous_real_data.py`,
`run_continuous_executor.py`, `run_discrete_executor.py`.
- TOML headers explain how to run each file locally with the CLI.
- `docs/validation.md` is a pointer to disslucc-benchmark; README, quickstart,
CLAUDE.md and CONTRIBUTING updated accordingly.
- `CITATION.cff`: repository is `DisSModel/disslucc`.

### Added
- LuccME-BR components: `PotentialSpatialLagRegression` (spatial-lag regression
potential), `AllocationClueLikeSaturation` (continuous allocation with a saturation
limit), and `LuccSaturationExecutor` (`lucc_continuous_saturation`) with
`examples/dissmodel-configs/lucc_saturation.toml`; `data/input/csAC_2009.zip` (dynamic
drivers). (Merged after 0.4.0; checked against LuccME labs 03 and 06 in disslucc-benchmark.)
- `tests/test_executor_toml.py`: each example TOML run through the CLI gives the
same output file (SHA-256) as the equivalent hand-built experiment.
- `.github/workflows/publish.yml`: PyPI Trusted Publishing on GitHub release.
- `DemandComputeTwoDates` and `DemandComputeThreeDates`: demand computed from the land-use
layers (start-year areas and one or two later reference years, interpolated linearly, rounded
as LuccME does). They are what LuccME labs 04, 05, 16 and 17 need on top of components that
already existed; with them those four labs reproduce the TerraME goldens
([disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark)).
as LuccME does), what LuccME labs 04, 05, 16 and 17 need.

### Changed
- `benchmark/goldens/` updated to terrame-docker v0.1.1: same results (CSVs within
1e-12), with `terrame.log` and `manifest.json` in English; `benchmark/README.md`
translated to English.
- `CONTRIBUTING.md` and the onboarding issue template expect `19 passed, 2 xfailed`.

## [0.4.0] -- 2026-09-23

Expand Down
19 changes: 10 additions & 9 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
title: "disslucc"
title: "disslucc: Land Use and Cover Change Modeling on top of DisSModel"
abstract: >-
Land use and land cover change (LUCC) modeling, raster-only,
script-first, on top of dissmodel. Continuous (CLUE) and discrete
(CLUE-S) allocation, validated against the original TerraME
reference on real data.
script-first, on top of dissmodel. LuccME's components
(demand, potential, allocation) in Python; agreement with
TerraME/LuccME is checked in LambdaGeo/disslucc-benchmark.
authors:
- family-names: "Souza Costa"
given-names: "Sérgio"
- family-names: "Costa"
given-names: "Sérgio Souza"
orcid: "https://orcid.org/0000-0002-0232-4549"
affiliation: "Universidade Federal do Maranhão (UFMA)"
version: 0.4.0
date-released: "2026-09-23"
version: 0.5.0
date-released: "2026-10-08"
license: MIT
repository-code: "https://github.com/LambdaGeo/disslucc"
url: "https://github.com/DisSModel/disslucc"
repository-code: "https://github.com/DisSModel/disslucc"
keywords:
- land use and land cover change
- LUCC
Expand Down
66 changes: 24 additions & 42 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ src/disslucc/
validation/ # continuous/discrete; potential + allocation are not
executors/ # ModelExecutor (base.py, continuous.py, discrete.py)
data/input/ # vendored real input data (shapefiles + demand CSVs)
benchmark/goldens/ # TerraME reference results, year by year (copy from LambdaGeo/terrame-docker)
examples/ # runnable scripts: synthetic, real data, via Executor
tests/ # pytest -- validation (exact numbers) + discriminance (does the
# benchmark actually constrain the implementation?)
examples/ # runnable scripts: synthetic, real data, executor, TOML configs
tests/ # pytest -- demand components, TOML == executor (same output SHA-256)
docs/
validation.md # the numbers -- cited in dissmodel's JOSS paper, see below
validation.md # pointer to LambdaGeo/disslucc-benchmark (no numbers live here)
architecture.md # what's faithful to the original vs. simplified vs. new
decisions.md # running decision log ("why"), append here, don't rewrite history
```
Expand All @@ -54,54 +52,38 @@ repo was fixed twice for the same class of bug (initial validation
scripts, then three more `examples/*.py` files found later) -- grep for
`/home/`, `/tmp/` before assuming a script is portable.

**Data provenance is not "whatever GitHub script has a matching name."**
The reference results live in `benchmark/goldens/`, a copy of the goldens
generated in [LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker)
v0.1.1, which keeps the generating scripts, the original TerraME outputs, the
generator and goldens for all 21 LuccME labs. This repository keeps only the
goldens its tests use (`lab01`, `lab15`, `lab03`, `lab06`: the LuccME package's labs;
`lab01_md1643`, `lab15_md10`); add one together with the component and test
that need it, never ahead of time. `lab01_md1643` and `lab15_md10` are the scenarios behind `docs/validation.md`
(same coefficients and demand as `lab01`/`lab15`, but `maxDifference` 1643
and 10 instead of 5000 and 300). **Read terrame-docker's
`benchmark/references/README.md` before citing or changing any
`max_difference`/`maxDifference` value.** Never edit a golden by hand --
regenerate it there and copy it (see `benchmark/README.md`).
A empirical validation number that stops matching (e.g. MAE jumping from
0.0036 to above 0.01) is stronger evidence of a wrong parameter than a
GitHub script that merely looks similar.

**`docs/validation.md`'s numbers are cited outside this repository** --
in `dissmodel`'s `paper.md`/`paper.bib` (JOSS submission). If a change
alters a Lab1 or Lab15 result, update `docs/validation.md` in the same
PR/commit and say so explicitly -- don't let it drift silently.
**Agreement with TerraME lives in
[LambdaGeo/disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark)**,
not here (since 0.5.0; the goldens, Lua differential and discriminance tests are
in the `v0.4.0` tag if you need to look at them). The benchmark pins a disslucc
release. If you change numerical behavior in `src/`, run the benchmark against
your branch (`pip install -e ../disslucc` there, `make benchmark`) before
merging, and say in the PR what changed. A number that stops matching is stronger
evidence of a wrong parameter than a script that merely looks similar.

**Before changing a `max_difference`/convergence value, a demand table, or a
regression coefficient** in `src/` or `examples/`, check the reference scripts
in terrame-docker's `benchmark/references/` (and its README) first. If the
change isn't traceable to those `.lua` files, it's very likely wrong even if it
looks locally reasonable.

**Published papers cite benchmark ids, not this repository's numbers**
(`lab01_md1643`, `lab15_md10`, in dissmodel's JOSS paper). Keep those ids
stable in the benchmark.

## Working here

```bash
python -m venv venv && source venv/bin/activate
pip install -e ".[examples,dev]"
pytest tests/ -v # expect: 73 passed, 2 xfailed (38 passed, 1 skipped without lupa)
pytest tests/ -v # expect: all passed
mypy src/disslucc # expect: clean
```

`AllocationClueLike(cell_correction=True)` (the default) deliberately differs
from TerraME: LuccME's `correctCellChange` never runs (a `regionregionAloc`
typo). Tests that compare against the continuous goldens use
`cell_correction=False`; don't "fix" the default to make them pass.

The 2 `xfail`s in `tests/test_benchmark_discriminance_lab15.py` are
intentional and documented (the Lab15 scenario is near-non-discriminative
by design -- a trivial static ranking reproduces the same output). Don't
"fix" them by loosening assertions; the fix, if one ever lands, is a
dynamic-covariate scenario (see `docs/decisions.md`).

Before changing a `max_difference`/convergence value, a demand table, or
a regression coefficient anywhere in `src/`, `tests/_lab*_helpers.py`, or
`examples/`: check the golden's `manifest.json` and the scripts in
terrame-docker's `benchmark/references/` first. If the change isn't
traceable to those `.lua` files, it's very likely wrong even if it looks
locally reasonable.
typo). `cell_correction=False` follows the reference run; the benchmark uses it
and also reports the default. Don't "fix" the default to make a comparison pass.

`CONTRIBUTING.md` has the full contribution workflow (branching,
PR/issue templates, coding standards). `docs/decisions.md` is the
Expand Down
40 changes: 12 additions & 28 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,8 @@ pip install -e ".[examples,dev]"
pytest tests/ -v
```

Expect `19 passed, 2 xfailed` on a clean checkout (the two `xfail` cases are
documented, known limitations of the Lab15 discriminance test -- see
`tests/test_benchmark_discriminance_lab15.py`). If your change makes that
number change unexpectedly, treat it as a regression until proven otherwise.
All tests should pass on a clean checkout. If your change makes a test fail
unexpectedly, treat it as a regression until proven otherwise.

---

Expand All @@ -107,32 +105,18 @@ number change unexpectedly, treat it as a regression until proven otherwise.

---

## Validation data and provenance

Two things make this package different from a typical model port, and both
have their own conventions:
## Data and agreement with TerraME

- **Vendored input data** (`data/input/`) is real data from
`disslucc-continuous`/`disslucc-discrete`, not synthetic. Don't regenerate
or "clean up" these files without understanding `docs/validation.md` first
-- the numbers they produce are cited in `dissmodel`'s JOSS paper.
- **Reference results** (`benchmark/goldens/`) are a copy of the goldens this
repository's tests use, generated in [LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker)
v0.1.1 (which has goldens for all 21 LuccME labs; copy one here only with
the component and test that use it),
which also keeps the original LuccME "Model Configurator" scripts, the
original TerraME outputs and the generator. Never edit a golden by hand:
regenerate it there and copy the folder (see `benchmark/README.md`). If
you're unsure which script is "the" source for a scenario, check
terrame-docker's `benchmark/references/README.md` -- coefficients/demand
matching is not sufficient evidence, since TerraME's own public test suite
contains look-alike scenarios with different convergence parameters.

If your change affects a Lab1 or Lab15 validation number, update
`docs/validation.md` in the same PR and say so explicitly in the PR
description -- these numbers are cited outside this repository.

---
`disslucc-continuous`/`disslucc-discrete`, used by the examples and tests.
Don't regenerate or "clean up" these files without checking the examples
and the benchmark that read them.
- **Agreement with TerraME/LuccME** is checked in
[LambdaGeo/disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark)
(reference results from [luccme-goldens](https://github.com/LambdaGeo/luccme-goldens),
generated in [terrame-docker](https://github.com/profsergiocosta/terrame-docker)).
If your change can alter numerical results, run the benchmark against your branch
and describe the outcome in the PR. Don't copy goldens into this repository.

## Documentation & Docstrings

Expand Down
56 changes: 29 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

Land use and land cover change (LUCC) modeling, raster-only,
script-first, on top of [`dissmodel`](https://github.com/DisSModel/dissmodel).
Continuous (CLUE) and discrete (CLUE-S) allocation, validated against
the original TerraME reference on real data for both: Lab1
(continuous) within the official 0.01 MAE tolerance, Lab15 (discrete)
at exact, 100% cell-by-cell agreement — see
[`docs/validation.md`](docs/validation.md) for both results and what
each one does and doesn't prove. Both are also checked year by year,
iteration counts included, against the goldens of
[LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker)
(`benchmark/goldens/`), which cover the 21 labs of the LuccME package; this
repository keeps the ones its tests use.
LuccME's components -- demand, potential, allocation -- in Python:
continuous (CLUE-like), discrete (CLUE-S-like), spatial-lag potential,
saturation, and demand computed from land-use layers. It is an alternative
that you can run next to TerraME/LuccME, not a replacement for them.

**Agreement with TerraME/LuccME** (iteration counts, per-year error, timings,
10 of the 21 LuccME labs so far) is checked in
[LambdaGeo/disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark),
which pins the release of disslucc it ran against. This repository is the
package and the examples to learn it with.

## Migration status: becoming the single successor repository

Expand Down Expand Up @@ -99,7 +99,7 @@ curious).
`examples/dissmodel-configs/` has a TOML config per executor
([continuous](examples/dissmodel-configs/lucc_continuous.toml),
[discrete](examples/dissmodel-configs/lucc_discrete.toml)), each
encoding the same coefficients as its `examples/run_*_via_executor.py`
encoding the same coefficients as its `examples/run_*_executor.py`
counterpart — see [`api.md`](docs/api.md#registering-with-dissmodel-configs-toml)
for the full explanation.

Expand All @@ -109,9 +109,8 @@ for the full explanation.
- **[api.md](docs/api.md)** — reference for every class and parameter
- **[architecture.md](docs/architecture.md)** — what's faithful to the
original repositories, what was simplified, what's new
- **[validation.md](docs/validation.md)** — validation results against
TerraME (Lab1 continuous, Lab15 discrete) and the engineering vs.
scientific validation distinction
- **[validation.md](docs/validation.md)** — where the comparison with
TerraME lives: [disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark)
- **[decisions.md](docs/decisions.md)** — full history of decisions and
tests throughout development

Expand All @@ -128,12 +127,18 @@ mypy src/disslucc
pytest tests/ -v
```

CI (`.github/workflows/tests.yml`) runs the same test suite on every push
and pull request. `tests/` includes the Lab1/Lab15 validation numbers as
real assertions, plus the discriminance suites ported from
`disslucc-continuous`/`disslucc-discrete` -- these check whether the
benchmark itself can tell a correct implementation from a wrong one, not
just whether it reproduces the reference.
CI (`.github/workflows/tests.yml`) runs ruff, mypy and the tests on every push
and pull request. `tests/` covers the demand components and checks that each
example TOML, run through the CLI, produces the same output file as the
equivalent hand-built experiment. **Agreement with TerraME/LuccME is not tested
here**: it is the job of
[disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark), which pins
the disslucc release it ran against.

## Citing

Use the DOI of the release you used (Zenodo, badge above once published) and
`CITATION.cff`.

## Structure

Expand All @@ -145,13 +150,10 @@ disslucc/
│ │ ├── demand/ # shared continuous + discrete
│ │ ├── potential/ # linear.py (continuous) + logistic.py (discrete)
│ │ └── allocation/ # clue.py (continuous) + clue_s.py (discrete)
│ ├── validation/ # shared continuous + discrete (outside components/, see architecture.md)
│ │ └── naive_baseline.py # discriminance baseline for Lab15
│ ├── validation/ # Pontius & Millones metrics, naive baseline (outside components/, see architecture.md)
│ └── executors/ # ModelExecutor -- automatic provenance, second entry point
├── examples/ # ready-made scripts, synthetic, real data, and via Executor
├── data/input/ # vendored Lab1 + Lab15 input shapefiles and demand CSVs
├── benchmark/
│ └── goldens/ # TerraME reference results, year by year (from LambdaGeo/terrame-docker)
├── tests/ # pytest suite -- validation + discriminance, run by CI
├── examples/ # ready-made scripts: synthetic, real data, executor, TOML configs
├── data/input/ # vendored example inputs (csAC, cs_moju) and demand CSVs
├── tests/ # pytest suite: demand components, TOML == executor
└── docs/
```
Loading
Loading