diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index a47336a..2c5e67f 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -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 diff --git a/.github/ISSUE_TEMPLATE/onboarding_task.md b/.github/ISSUE_TEMPLATE/onboarding_task.md index 34bc37f..3fd429c 100644 --- a/.github/ISSUE_TEMPLATE/onboarding_task.md +++ b/.github/ISSUE_TEMPLATE/onboarding_task.md @@ -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 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1ee549c --- /dev/null +++ b/.github/workflows/publish.yml @@ -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/ diff --git a/CHANGELOG.md b/CHANGELOG.md index b451e26..6bb47bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CITATION.cff b/CITATION.cff index 8b8550a..bc4d908 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md index 55469e2..dfc487c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 ``` @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f687622..6f66214 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. --- @@ -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 diff --git a/README.md b/README.md index a485137..9307f0a 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. @@ -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 @@ -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 @@ -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/ ``` diff --git a/benchmark/README.md b/benchmark/README.md deleted file mode 100644 index 7956ac5..0000000 --- a/benchmark/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# TerraME/LuccME goldens - -`goldens/` is a copy of goldens generated in -[LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker) **v0.1.1** -(`benchmark/goldens/`), with TerraME 2.0.1 + LuccME `6244dd4`. disslucc is validated -against them, one algorithm at a time. - -The scripts, the original TerraME outputs and the generator live **only** in -terrame-docker; this repository keeps only the results, so the tests run without Docker. - -## Contents of `goldens//` - -| File | Contents | -|---|---| -| `.csv.gz` | state of every cell at the end of every year: `year,id,col,row`, `_out` and `_pot`, 12 decimal places | -| `terrame.log` | TerraME output (demand, allocated area, iterations per year) | -| `manifest.json` | source script and SHA-256, versions, years, columns, iterations per year (`iterations_per_year`) and the cross-check | - -`col`/`row` match `data/input/csAC.zip` (`col`, `row`) and `cs_moju.zip` (`col`, `lin`). - -## Which ones - -Only the goldens this repository's tests use. The others (the 21 labs of the LuccME -package) stay in terrame-docker and come here when the corresponding component is -implemented, together with the test that uses them. - -| Golden | Components | Used in | -|---|---|---| -| `lab01` | PreComputedValues + CLinearRegression + CClueLike (`maxDifference` 5000) | `test_goldens_per_year.py` | -| `lab01_md1643` | same, `maxDifference` 1643: iterates up to 26 times per year | `test_goldens_per_year.py`, `test_validation_lab1.py`, discriminance | -| `lab15` | PreComputedValues + DLogisticRegression + DClueSLike (`maxDifference` 300) | `test_goldens_per_year.py` | -| `lab15_md10` | same, `maxDifference` 10: iterates 56–67 times per year | `test_goldens_per_year.py`, `test_validation_lab15.py`, discriminance | -| `lab03` | PreComputedValues + CSpatialLagRegression + CClueLikeSaturation (`maxDifference` 1643) | `test_spatial_lag_golden.py`, `test_saturation_golden.py` | -| `lab06` | same as `lab03`, plus `updateYears = {2009}` (`ti` from `csAC_2009`, `data/input/csAC_2009.zip`) | `test_spatial_lag_golden.py`, `test_saturation_golden.py` | - -The last year of `lab01_md1643` and of `lab15_md10` is this repository's former reference -(`benchmark/data/*.zip`). - -## Before using them as evidence - -- In the package labs the allocation is accepted at the first pass every year; only the - `_md` variants test the convergence loop. -- In the labs with `CClueLike`, TerraME never runs `correctCellChange` (a - `regionregionAloc` typo). disslucc runs it by default; compare with - `cell_correction=False`. See `docs/validation.md`. -- Compare with a tolerance (1e-9 in the file; the tests use MAE < 1e-6), never by - SHA-256: from one generation to the next, a few cells change in the 12th decimal place. - -## Adding or updating - -Copy the golden's folder from terrame-docker's `benchmark/goldens//`, at the same -version (`v0.1.1`), to `benchmark/goldens//` here, in the same commit as the test -that starts using it. Never edit a golden by hand; to regenerate one, run -`benchmark/generate.sh ` in terrame-docker. diff --git a/benchmark/goldens/lab01/lab01.csv.gz b/benchmark/goldens/lab01/lab01.csv.gz deleted file mode 100644 index 65a82d2..0000000 Binary files a/benchmark/goldens/lab01/lab01.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab01/manifest.json b/benchmark/goldens/lab01/manifest.json deleted file mode 100644 index ff43be7..0000000 --- a/benchmark/goldens/lab01/manifest.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "lab": "lab01", - "source": { - "script": "luccme/tests/functional/lab01.lua", - "sha256": "d8e16d718c3654b0d7f89d743413f3ee781d6fb05574231fb843af895c238dfa" - }, - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 2008, - 2014 - ], - "n_cells": 6574, - "columns": [ - "f_out", - "d_out", - "outros_out", - "f_pot", - "d_pot", - "outros_pot" - ], - "file": { - "name": "lab01.csv.gz", - "rows": 46018, - "sha256": "c773f4b295c8c980898b320a460d29fedd1118260d1e7eacd7c7fc8cd51e59ed" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab01_2014.dbf:d_out": 5.000028169277471e-13 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": { - "2008": 0, - "2009": 0, - "2010": 0, - "2011": 0, - "2012": 0, - "2013": 0, - "2014": 0 - }, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab01/terrame.log b/benchmark/goldens/lab01/terrame.log deleted file mode 100644 index 5eb3a04..0000000 --- a/benchmark/goldens/lab01/terrame.log +++ /dev/null @@ -1,90 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2008 Step: 0 -f Area: 137878 Difference: 0 -d Area: 19982 Difference: 0 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2008. Number of iterations: 0 Maximum error: 2.7950358344242e-05 -[harness] lab01: Lab01, years 2008..2014, columns f_out,d_out,outros_out,f_pot,d_pot,outros_pot - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2009 Step: 0 -f Area: 136483 Difference: -1139 -d Area: 20953 Difference: 714 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2009. Number of iterations: 0 Maximum error: 1138.4125251856 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2010 Step: 0 -f Area: 135609 Difference: -1757 -d Area: 21826 Difference: 1331 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2010. Number of iterations: 0 Maximum error: 1756.4683416836 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2011 Step: 0 -f Area: 134822 Difference: -2288 -d Area: 22612 Difference: 1862 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2011. Number of iterations: 0 Maximum error: 2287.3741648977 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2012 Step: 0 -f Area: 134114 Difference: -2711 -d Area: 23320 Difference: 2284 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2012. Number of iterations: 0 Maximum error: 2710.5152184257 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2013 Step: 0 -f Area: 133475 Difference: -3064 -d Area: 23956 Difference: 2635 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2013. Number of iterations: 0 Maximum error: 3063.7042134609 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2014 Step: 0 -f Area: 132899 Difference: -3354 -d Area: 24530 Difference: 2922 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2014. Number of iterations: 0 Maximum error: 3353.6276049931 - -Saving Lab01_2014. -Elapsed time: 00:00:03 hh:mm:ss - -End of Simulation -[harness] lab01: 5 files of the lab's own output in /work/out/lab01 -[harness] lab01: CSV written to /work/out/lab01 diff --git a/benchmark/goldens/lab01_md1643/lab01_md1643.csv.gz b/benchmark/goldens/lab01_md1643/lab01_md1643.csv.gz deleted file mode 100644 index b674723..0000000 Binary files a/benchmark/goldens/lab01_md1643/lab01_md1643.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab01_md1643/manifest.json b/benchmark/goldens/lab01_md1643/manifest.json deleted file mode 100644 index bd39472..0000000 --- a/benchmark/goldens/lab01_md1643/manifest.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "lab": "lab01_md1643", - "source": [ - { - "script": "benchmark/references/lab01_md1643/lab1_main.lua", - "sha256": "48d3fa0891a01107ed7ed72d30f3dbd11cc98a33712126996376db1fdc3774bd" - }, - { - "script": "benchmark/references/lab01_md1643/lab1_submodel.lua", - "sha256": "f26699b3a9e63cc0c5119c52b9c32b9894b8f9ab5152fcef185755e9c51e041f" - } - ], - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 2008, - 2014 - ], - "n_cells": 6574, - "columns": [ - "f_out", - "d_out", - "outros_out", - "f_pot", - "d_pot", - "outros_pot" - ], - "file": { - "name": "lab01_md1643.csv.gz", - "rows": 46018, - "sha256": "979d63d14ebed6040ee3000b91939abdb050924be36385503cd1d74a2744759f" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab1_2014.dbf:d_out": 4.999993474807951e-13, - "original_Lab1_2014.dbf:d_out": 4.999993474807951e-13 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": { - "2008": 0, - "2009": 0, - "2010": 8, - "2011": 26, - "2012": 18, - "2013": 17, - "2014": 17 - }, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab01_md1643/terrame.log b/benchmark/goldens/lab01_md1643/terrame.log deleted file mode 100644 index 5711e5c..0000000 --- a/benchmark/goldens/lab01_md1643/terrame.log +++ /dev/null @@ -1,522 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2008 Step: 0 -f Area: 137878 Difference: 0 -d Area: 19982 Difference: 0 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2008. Number of iterations: 0 Maximum error: 2.7950358344242e-05 -[harness] lab01_md1643: Lab1, years 2008..2014, columns f_out,d_out,outros_out,f_pot,d_pot,outros_pot - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2009 Step: 0 -f Area: 136483 Difference: -1139 -d Area: 20953 Difference: 714 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2009. Number of iterations: 0 Maximum error: 1138.4125251856 -Cell 1: f_pot=0.0295, d_pot=-0.0398 -Cell 2: f_pot=-0.0227, d_pot=0.0000 -Cell 3: f_pot=-0.0171, d_pot=0.0000 -Cell 4: f_pot=-0.0223, d_pot=0.0000 -Cell 5: f_pot=-0.0353, d_pot=0.0000 -Mean d_pot: 0.0052 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2010 Step: 0 -f Area: 135609 Difference: -1757 -d Area: 21826 Difference: 1331 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 1 -f Area: 135626 Difference: -1740 -d Area: 21773 Difference: 1278 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 2 -f Area: 135642 Difference: -1724 -d Area: 21725 Difference: 1230 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 3 -f Area: 135658 Difference: -1708 -d Area: 21681 Difference: 1186 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 4 -f Area: 135674 Difference: -1693 -d Area: 21641 Difference: 1146 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 5 -f Area: 135689 Difference: -1678 -d Area: 21604 Difference: 1110 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 6 -f Area: 135704 Difference: -1663 -d Area: 21571 Difference: 1076 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 7 -f Area: 135718 Difference: -1648 -d Area: 21540 Difference: 1046 -outros Area: 6489 Difference: 0 - -Year: 2010 Step: 8 -f Area: 135732 Difference: -1634 -d Area: 21512 Difference: 1017 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2010. Number of iterations: 8 Maximum error: 1633.341418966 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2011 Step: 0 -f Area: 135106 Difference: -2005 -d Area: 22329 Difference: 1579 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 1 -f Area: 135124 Difference: -1986 -d Area: 22271 Difference: 1521 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 2 -f Area: 135142 Difference: -1969 -d Area: 22219 Difference: 1469 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 3 -f Area: 135159 Difference: -1951 -d Area: 22173 Difference: 1422 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 4 -f Area: 135176 Difference: -1934 -d Area: 22130 Difference: 1380 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 5 -f Area: 135192 Difference: -1918 -d Area: 22092 Difference: 1341 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 6 -f Area: 135209 Difference: -1902 -d Area: 22056 Difference: 1306 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 7 -f Area: 135224 Difference: -1886 -d Area: 22024 Difference: 1274 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 8 -f Area: 135240 Difference: -1870 -d Area: 21994 Difference: 1244 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 9 -f Area: 135255 Difference: -1855 -d Area: 21967 Difference: 1217 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 10 -f Area: 135270 Difference: -1841 -d Area: 21942 Difference: 1191 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 11 -f Area: 135284 Difference: -1826 -d Area: 21919 Difference: 1168 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 12 -f Area: 135298 Difference: -1812 -d Area: 21897 Difference: 1146 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 13 -f Area: 135312 Difference: -1798 -d Area: 21877 Difference: 1126 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 14 -f Area: 135326 Difference: -1784 -d Area: 21858 Difference: 1107 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 15 -f Area: 135339 Difference: -1771 -d Area: 21840 Difference: 1090 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 16 -f Area: 135352 Difference: -1758 -d Area: 21824 Difference: 1073 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 17 -f Area: 135365 Difference: -1745 -d Area: 21809 Difference: 1058 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 18 -f Area: 135377 Difference: -1733 -d Area: 21794 Difference: 1044 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 19 -f Area: 135390 Difference: -1721 -d Area: 21781 Difference: 1030 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 20 -f Area: 135402 Difference: -1709 -d Area: 21768 Difference: 1017 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 21 -f Area: 135414 Difference: -1697 -d Area: 21756 Difference: 1005 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 22 -f Area: 135425 Difference: -1685 -d Area: 21745 Difference: 994 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 23 -f Area: 135436 Difference: -1674 -d Area: 21734 Difference: 983 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 24 -f Area: 135448 Difference: -1663 -d Area: 21724 Difference: 973 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 25 -f Area: 135459 Difference: -1652 -d Area: 21714 Difference: 964 -outros Area: 6489 Difference: 0 - -Year: 2011 Step: 26 -f Area: 135469 Difference: -1641 -d Area: 21705 Difference: 955 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2011. Number of iterations: 26 Maximum error: 1640.6006249464 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2012 Step: 0 -f Area: 134931 Difference: -1893 -d Area: 22504 Difference: 1467 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 1 -f Area: 134948 Difference: -1877 -d Area: 22452 Difference: 1415 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 2 -f Area: 134965 Difference: -1860 -d Area: 22404 Difference: 1368 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 3 -f Area: 134981 Difference: -1844 -d Area: 22362 Difference: 1326 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 4 -f Area: 134997 Difference: -1828 -d Area: 22323 Difference: 1287 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 5 -f Area: 135012 Difference: -1813 -d Area: 22287 Difference: 1251 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 6 -f Area: 135027 Difference: -1797 -d Area: 22255 Difference: 1218 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 7 -f Area: 135042 Difference: -1783 -d Area: 22224 Difference: 1188 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 8 -f Area: 135057 Difference: -1768 -d Area: 22197 Difference: 1161 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 9 -f Area: 135071 Difference: -1754 -d Area: 22171 Difference: 1135 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 10 -f Area: 135085 Difference: -1740 -d Area: 22147 Difference: 1111 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 11 -f Area: 135098 Difference: -1726 -d Area: 22125 Difference: 1089 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 12 -f Area: 135112 Difference: -1713 -d Area: 22104 Difference: 1068 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 13 -f Area: 135125 Difference: -1700 -d Area: 22085 Difference: 1049 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 14 -f Area: 135137 Difference: -1687 -d Area: 22067 Difference: 1031 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 15 -f Area: 135150 Difference: -1675 -d Area: 22050 Difference: 1014 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 16 -f Area: 135162 Difference: -1662 -d Area: 22034 Difference: 998 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 17 -f Area: 135174 Difference: -1650 -d Area: 22019 Difference: 983 -outros Area: 6489 Difference: 0 - -Year: 2012 Step: 18 -f Area: 135186 Difference: -1639 -d Area: 22005 Difference: 969 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2012. Number of iterations: 18 Maximum error: 1638.1073367472 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2013 Step: 0 -f Area: 134661 Difference: -1878 -d Area: 22774 Difference: 1452 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 1 -f Area: 134677 Difference: -1862 -d Area: 22725 Difference: 1403 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 2 -f Area: 134693 Difference: -1846 -d Area: 22680 Difference: 1358 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 3 -f Area: 134709 Difference: -1830 -d Area: 22640 Difference: 1318 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 4 -f Area: 134724 Difference: -1815 -d Area: 22603 Difference: 1281 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 5 -f Area: 134739 Difference: -1800 -d Area: 22569 Difference: 1247 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 6 -f Area: 134754 Difference: -1785 -d Area: 22538 Difference: 1216 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 7 -f Area: 134768 Difference: -1771 -d Area: 22509 Difference: 1187 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 8 -f Area: 134783 Difference: -1757 -d Area: 22482 Difference: 1161 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 9 -f Area: 134796 Difference: -1743 -d Area: 22458 Difference: 1136 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 10 -f Area: 134810 Difference: -1729 -d Area: 22435 Difference: 1113 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 11 -f Area: 134823 Difference: -1716 -d Area: 22414 Difference: 1092 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 12 -f Area: 134836 Difference: -1703 -d Area: 22394 Difference: 1072 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 13 -f Area: 134849 Difference: -1690 -d Area: 22375 Difference: 1053 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 14 -f Area: 134861 Difference: -1678 -d Area: 22358 Difference: 1036 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 15 -f Area: 134873 Difference: -1666 -d Area: 22341 Difference: 1020 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 16 -f Area: 134885 Difference: -1654 -d Area: 22326 Difference: 1004 -outros Area: 6489 Difference: 0 - -Year: 2013 Step: 17 -f Area: 134897 Difference: -1642 -d Area: 22312 Difference: 990 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2013. Number of iterations: 17 Maximum error: 1641.4798002601 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2014 Step: 0 -f Area: 134385 Difference: -1869 -d Area: 23049 Difference: 1442 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 1 -f Area: 134401 Difference: -1853 -d Area: 23003 Difference: 1396 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 2 -f Area: 134416 Difference: -1837 -d Area: 22961 Difference: 1354 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 3 -f Area: 134432 Difference: -1822 -d Area: 22923 Difference: 1315 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 4 -f Area: 134447 Difference: -1807 -d Area: 22888 Difference: 1280 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 5 -f Area: 134461 Difference: -1792 -d Area: 22855 Difference: 1248 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 6 -f Area: 134475 Difference: -1778 -d Area: 22826 Difference: 1218 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 7 -f Area: 134489 Difference: -1764 -d Area: 22798 Difference: 1191 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 8 -f Area: 134503 Difference: -1750 -d Area: 22773 Difference: 1165 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 9 -f Area: 134517 Difference: -1737 -d Area: 22749 Difference: 1142 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 10 -f Area: 134530 Difference: -1724 -d Area: 22727 Difference: 1120 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 11 -f Area: 134543 Difference: -1711 -d Area: 22707 Difference: 1099 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 12 -f Area: 134555 Difference: -1698 -d Area: 22688 Difference: 1080 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 13 -f Area: 134568 Difference: -1686 -d Area: 22670 Difference: 1062 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 14 -f Area: 134580 Difference: -1674 -d Area: 22653 Difference: 1045 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 15 -f Area: 134592 Difference: -1662 -d Area: 22637 Difference: 1030 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 16 -f Area: 134603 Difference: -1650 -d Area: 22622 Difference: 1015 -outros Area: 6489 Difference: 0 - -Year: 2014 Step: 17 -f Area: 134615 Difference: -1639 -d Area: 22608 Difference: 1001 -outros Area: 6489 Difference: 0 - -Demand allocated correctly in 2014. Number of iterations: 17 Maximum error: 1638.220954259 - -Saving Lab1_2014. -[harness] lab01_md1643: CSV written to /work/out/lab01_md1643 diff --git a/benchmark/goldens/lab03/lab03.csv.gz b/benchmark/goldens/lab03/lab03.csv.gz deleted file mode 100644 index b316365..0000000 Binary files a/benchmark/goldens/lab03/lab03.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab03/manifest.json b/benchmark/goldens/lab03/manifest.json deleted file mode 100644 index 2946a8d..0000000 --- a/benchmark/goldens/lab03/manifest.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "lab": "lab03", - "source": { - "script": "luccme/tests/functional/lab03.lua", - "sha256": "f32e13c853c05b0ea3df9368086e3ccc4fab8d3ba8322f39f15a7f056160a027" - }, - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 2008, - 2014 - ], - "n_cells": 6574, - "columns": [ - "f_out", - "d_out", - "outros_out", - "f_pot", - "d_pot", - "outros_pot" - ], - "file": { - "name": "lab03.csv.gz", - "rows": 46018, - "sha256": "dba249299d94626100b97af14cc46734a17df2a957ed36bda9ff3881c6ebba29" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab03_2014.dbf:d_out": 5.000002148425331e-13 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": {}, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab03/terrame.log b/benchmark/goldens/lab03/terrame.log deleted file mode 100644 index 51ee38e..0000000 --- a/benchmark/goldens/lab03/terrame.log +++ /dev/null @@ -1,118 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2008 Step: 0 -f area: 137878 Difference: 0 -d area: 19982 Difference: 0 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.10000000002027 dir: -1 const: 0.05266679 -> 0.05266679 0 1 -d elas: 0.10000000001401 dir: -1 const: 0.01431553 -> 0.01431553 0 1 -outros elas: 0.10000016027163 dir: -1 const: 0 -> 0 0 1 - -Demand allocated correctly in 2008 Number of iterations: 0 Maximum error: 0.01040035000733 -[harness] lab03: Lab03, years 2008..2014, columns f_out,d_out,outros_out,f_pot,d_pot,outros_pot - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2009 Step: 0 -f area: 137418 Difference: -204 -d area: 20334 Difference: 96 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099851813713511 dir: -1 const: 0.052648226568119 -> 0.052648226568119 -1.8563431881255e-05 0.99998143656812 -d elas: 0.0995264862247 dir: 1 const: 0.014443615865131 -> 0.014443615865131 0.00012808586513093 1.0001280858651 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2009 Number of iterations: 0 Maximum error: 203.9372570531 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2010 Step: 0 -f area: 137079 Difference: -287 -d area: 20673 Difference: 179 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099791090223736 dir: -1 const: 0.052629628612048 -> 0.052629628612048 -1.8597956070319e-05 0.99998140204393 -d elas: 0.099132190192513 dir: 1 const: 0.014570081884318 -> 0.014570081884318 0.00012646601918755 1.0001264660192 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2010 Number of iterations: 0 Maximum error: 286.971568782 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2011 Step: 0 -f area: 136752 Difference: -358 -d area: 21000 Difference: 250 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099739024534728 dir: -1 const: 0.052610995995854 -> 0.052610995995854 -1.8632616194334e-05 0.99998136738381 -d elas: 0.0988079001206 dir: 1 const: 0.014694968507161 -> 0.014694968507161 0.00012488662284242 1.0001248866228 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2011 Number of iterations: 0 Maximum error: 357.82429921019 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2012 Step: 0 -f area: 136436 Difference: -388 -d area: 21316 Difference: 280 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099716648902286 dir: -1 const: 0.052590163421247 -> 0.052590163421247 -2.0832574607325e-05 0.99997916742539 -d elas: 0.098684256490536 dir: 1 const: 0.014832621312254 -> 0.014832621312254 0.00013765280509327 1.0001376528051 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2012 Number of iterations: 0 Maximum error: 387.69424774163 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2013 Step: 0 -f area: 136131 Difference: -408 -d area: 21622 Difference: 300 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099701765439119 dir: -1 const: 0.052569287356422 -> 0.052569287356422 -2.0876064825124e-05 0.99997912393517 -d elas: 0.098610855076548 dir: 1 const: 0.014968405021323 -> 0.014968405021323 0.00013578370906851 1.0001357837091 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2013 Number of iterations: 0 Maximum error: 407.20663381234 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2014 Step: 0 -f area: 135836 Difference: -418 -d area: 21918 Difference: 310 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.09969381618728 dir: -1 const: 0.052548367612094 -> 0.052548367612094 -2.0919744327618e-05 0.99997908025567 -d elas: 0.098582216155697 dir: 1 const: 0.015102369708185 -> 0.015102369708185 0.00013396468686188 1.0001339646869 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2014 Number of iterations: 0 Maximum error: 417.18589488399 - -Saving Lab03_2014. -Elapsed time: 00:00:04 hh:mm:ss - -End of Simulation -[harness] lab03: 5 files of the lab's own output in /work/out/lab03 -[harness] lab03: CSV written to /work/out/lab03 diff --git a/benchmark/goldens/lab06/lab06.csv.gz b/benchmark/goldens/lab06/lab06.csv.gz deleted file mode 100644 index 4175177..0000000 Binary files a/benchmark/goldens/lab06/lab06.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab06/manifest.json b/benchmark/goldens/lab06/manifest.json deleted file mode 100644 index a48e5e6..0000000 --- a/benchmark/goldens/lab06/manifest.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "lab": "lab06", - "source": { - "script": "luccme/tests/functional/lab06.lua", - "sha256": "1248ddfea1df91ef6bd07aea3ff325fc748e10107883c548d69ae35a20041b5b" - }, - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 2008, - 2014 - ], - "n_cells": 6574, - "columns": [ - "f_out", - "d_out", - "outros_out", - "f_pot", - "d_pot", - "outros_pot" - ], - "file": { - "name": "lab06.csv.gz", - "rows": 46018, - "sha256": "456b5346f631028b7dae3e2cad247760e59abc57de40d549e02f2331f163d1b6" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab06_2014.dbf:d_out": 5.000002148425331e-13 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": {}, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab06/terrame.log b/benchmark/goldens/lab06/terrame.log deleted file mode 100644 index 9c55404..0000000 --- a/benchmark/goldens/lab06/terrame.log +++ /dev/null @@ -1,121 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2008 Step: 0 -f area: 137878 Difference: 0 -d area: 19982 Difference: 0 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.10000000002027 dir: -1 const: 0.05266679 -> 0.05266679 0 1 -d elas: 0.10000000001401 dir: -1 const: 0.01431553 -> 0.01431553 0 1 -outros elas: 0.10000016027163 dir: -1 const: 0 -> 0 0 1 - -Demand allocated correctly in 2008 Number of iterations: 0 Maximum error: 0.01040035000733 -[harness] lab06: Lab06, years 2008..2014, columns f_out,d_out,outros_out,f_pot,d_pot,outros_pot - -Updating dynamic variables... -layer_2009 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2009 Step: 0 -f area: 137389 Difference: -233 -d area: 20331 Difference: 93 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.09983074185143 dir: -1 const: 0.052648226568119 -> 0.052648226568119 -1.8563431881255e-05 0.99998143656812 -d elas: 0.099542216292271 dir: 1 const: 0.014443615865131 -> 0.014443615865131 0.00012808586513093 1.0001280858651 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2009 Number of iterations: 0 Maximum error: 232.93682142338 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2010 Step: 0 -f area: 137053 Difference: -313 -d area: 20667 Difference: 173 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099772292812196 dir: -1 const: 0.052629628612048 -> 0.052629628612048 -1.8597956070319e-05 0.99998140204393 -d elas: 0.099162815645913 dir: 1 const: 0.014570081884318 -> 0.014570081884318 0.00012646601918755 1.0001264660192 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2010 Number of iterations: 0 Maximum error: 312.79287200209 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2011 Step: 0 -f area: 136729 Difference: -381 -d area: 20991 Difference: 240 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099722588869631 dir: -1 const: 0.052610995995854 -> 0.052610995995854 -1.8632616194334e-05 0.99998136738381 -d elas: 0.098851979284461 dir: 1 const: 0.014694968507161 -> 0.014694968507161 0.00012488662284242 1.0001248866228 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2011 Number of iterations: 0 Maximum error: 380.35929244847 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2012 Step: 0 -f area: 136417 Difference: -408 -d area: 21304 Difference: 268 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099702461272719 dir: -1 const: 0.052590163421247 -> 0.052590163421247 -2.0832574607325e-05 0.99997916742539 -d elas: 0.098740566525434 dir: 1 const: 0.014832621312254 -> 0.014832621312254 0.00013765280509327 1.0001376528051 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2012 Number of iterations: 0 Maximum error: 407.10642724726 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2013 Step: 0 -f area: 136115 Difference: -424 -d area: 21607 Difference: 285 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099689657048882 dir: -1 const: 0.052569287356422 -> 0.052569287356422 -2.0876064825124e-05 0.99997912393517 -d elas: 0.098678829724857 dir: 1 const: 0.014968405021323 -> 0.014968405021323 0.00013578370906851 1.0001357837091 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2013 Number of iterations: 0 Maximum error: 423.73931471611 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Year: 2014 Step: 0 -f area: 135822 Difference: -431 -d area: 21900 Difference: 293 -outros area: 6489 Difference: 0 -Region 1 -f elas: 0.099683763142858 dir: -1 const: 0.052548367612094 -> 0.052548367612094 -2.0919744327618e-05 0.99997908025567 -d elas: 0.098660722562989 dir: 1 const: 0.015102369708185 -> 0.015102369708185 0.00013396468686188 1.0001339646869 -outros elas: 0.10000016027163 dir: 0 const: 0.0 -> 0.0 0.0 1.0 - -Demand allocated correctly in 2014 Number of iterations: 0 Maximum error: 430.88351101935 - -Saving Lab06_2014. -Elapsed time: 00:00:05 hh:mm:ss - -End of Simulation -[harness] lab06: 5 files of the lab's own output in /work/out/lab06 -[harness] lab06: CSV written to /work/out/lab06 diff --git a/benchmark/goldens/lab15/lab15.csv.gz b/benchmark/goldens/lab15/lab15.csv.gz deleted file mode 100644 index de7f4fd..0000000 Binary files a/benchmark/goldens/lab15/lab15.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab15/manifest.json b/benchmark/goldens/lab15/manifest.json deleted file mode 100644 index 7502457..0000000 --- a/benchmark/goldens/lab15/manifest.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "lab": "lab15", - "source": { - "script": "luccme/tests/functional/lab15.lua", - "sha256": "bc9faf3a26aafa42c0075f114c0def46a1320e1f89ab52b7886b27ca72075bf7" - }, - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 1999, - 2004 - ], - "n_cells": 5914, - "columns": [ - "f_out", - "d_out", - "o_out", - "f_pot", - "d_pot", - "o_pot" - ], - "file": { - "name": "lab15.csv.gz", - "rows": 35484, - "sha256": "00c400fe3b1cd15883ee02e7ba2f195becfc4e3d95ddb4dc28dfeeef86108ad9" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab15_2004.dbf:d_out": 0.0 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": { - "1999": 0, - "2000": 0, - "2001": 0, - "2002": 0, - "2003": 0, - "2004": 0 - }, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab15/terrame.log b/benchmark/goldens/lab15/terrame.log deleted file mode 100644 index 1851d4f..0000000 --- a/benchmark/goldens/lab15/terrame.log +++ /dev/null @@ -1,139 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 1999 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 1999 Iteration -> 0 -f -> 5706 demand -> 5706 difference -> 0 -d -> 205 demand -> 205 difference -> 0 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 1999 -[harness] lab15: Lab15, years 1999..2004, columns f_out,d_out,o_out,f_pot,d_pot,o_pot - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2000 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2000 Iteration -> 0 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2000 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2001 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2001 Iteration -> 0 -f -> 5706 demand -> 5611 difference -> -95 -d -> 205 demand -> 300 difference -> 95 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2001 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2002 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2002 Iteration -> 0 -f -> 5706 demand -> 5563 difference -> -143 -d -> 205 demand -> 348 difference -> 143 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2002 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2003 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2003 Iteration -> 0 -f -> 5706 demand -> 5516 difference -> -190 -d -> 205 demand -> 395 difference -> 190 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2003 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2004 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 300 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2004 Iteration -> 0 -f -> 5706 demand -> 5468 difference -> -238 -d -> 205 demand -> 443 difference -> 238 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2004 - -Saving Lab15_2004. -Elapsed time: 00:00:02 hh:mm:ss - -End of Simulation -[harness] lab15: 5 files of the lab's own output in /work/out/lab15 -[harness] lab15: CSV written to /work/out/lab15 diff --git a/benchmark/goldens/lab15_md10/lab15_md10.csv.gz b/benchmark/goldens/lab15_md10/lab15_md10.csv.gz deleted file mode 100644 index 858686e..0000000 Binary files a/benchmark/goldens/lab15_md10/lab15_md10.csv.gz and /dev/null differ diff --git a/benchmark/goldens/lab15_md10/manifest.json b/benchmark/goldens/lab15_md10/manifest.json deleted file mode 100644 index 584499a..0000000 --- a/benchmark/goldens/lab15_md10/manifest.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "lab": "lab15_md10", - "source": [ - { - "script": "benchmark/references/lab15_md10/lab6_main.lua", - "sha256": "12b1174b8f2f71938eeb7e9aa328bc72ead02e3d3c096535f18a07a6837e1584" - }, - { - "script": "benchmark/references/lab15_md10/lab6_submodel.lua", - "sha256": "bf107bee8178039285989e736e22b9bfe068cf85d3ddb0a5c6c23579e417c9c6" - } - ], - "engine": { - "image": "terrame-luccme", - "terrame": "2.0.1", - "terralib": "5.5.1", - "luccme_commit": "6244dd461f94259efb6e1d2170d32fc7e28c033c" - }, - "generator": { - "script": "benchmark/harness.lua", - "sha256": "5f7504f11442d83981565676635ffd996034f721a5ad08ec0cab082d2e948a97" - }, - "status": "ok", - "years": [ - 1999, - 2004 - ], - "n_cells": 5914, - "columns": [ - "f_out", - "d_out", - "o_out", - "f_pot", - "d_pot", - "o_pot" - ], - "file": { - "name": "lab15_md10.csv.gz", - "rows": 35484, - "sha256": "aa1ee24ee75953c066e3713072aa4c2a4fac7fe7ee1b1f5d8bd0bafb202bebcc" - }, - "crosscheck_vs_original_output": { - "tolerance": 1e-09, - "max_abs_diff": { - "Lab6_2004.dbf:d_out": 0.0, - "original_Lab15_2004.dbf:d_out": 0.0 - }, - "note": "last year of the CSV compared with the .dbf files of the output folder: the script's own output and, for references, the original TerraME output" - }, - "iterations_per_year": { - "1999": 0, - "2000": 67, - "2001": 56, - "2002": 56, - "2003": 61, - "2004": 61 - }, - "iterations_note": "continuous: LuccME's 'Number of iterations'; discrete: largest n in 'Iteration -> n' (0 = first pass accepted)" -} diff --git a/benchmark/goldens/lab15_md10/terrame.log b/benchmark/goldens/lab15_md10/terrame.log deleted file mode 100644 index e147415..0000000 --- a/benchmark/goldens/lab15_md10/terrame.log +++ /dev/null @@ -1,1643 +0,0 @@ - -Verifying Model parameters -Verifying Demand parameters -Verifying Potential parameters -Verifying Allocation parameters - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 1999 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 1999 Iteration -> 0 -f -> 5706 demand -> 5706 difference -> 0 -d -> 205 demand -> 205 difference -> 0 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 1999 -[harness] lab15_md10: Lab6, years 1999..2004, columns f_out,d_out,o_out,f_pot,d_pot,o_pot - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2000 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5706 -Initial area for land use : d -> 205 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2000 Iteration -> 0 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 1 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 2 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 3 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 4 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 5 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 6 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 7 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 8 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 9 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 10 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 11 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 12 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 13 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 14 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 15 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 16 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 17 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 18 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 19 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 20 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 21 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 22 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 23 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 24 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 25 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 26 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 27 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 28 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 29 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 30 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 31 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 32 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 33 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 34 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 35 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 36 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 37 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 38 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 39 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 40 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 41 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 42 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 43 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 44 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 45 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 46 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 47 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 48 -f -> 5706 demand -> 5658 difference -> -48 -d -> 205 demand -> 253 difference -> 48 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 49 -f -> 5705 demand -> 5658 difference -> -47 -d -> 206 demand -> 253 difference -> 47 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 50 -f -> 5705 demand -> 5658 difference -> -47 -d -> 206 demand -> 253 difference -> 47 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 51 -f -> 5704 demand -> 5658 difference -> -46 -d -> 207 demand -> 253 difference -> 46 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 52 -f -> 5702 demand -> 5658 difference -> -44 -d -> 209 demand -> 253 difference -> 44 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 53 -f -> 5701 demand -> 5658 difference -> -43 -d -> 210 demand -> 253 difference -> 43 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 54 -f -> 5700 demand -> 5658 difference -> -42 -d -> 211 demand -> 253 difference -> 42 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 55 -f -> 5697 demand -> 5658 difference -> -39 -d -> 214 demand -> 253 difference -> 39 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 56 -f -> 5694 demand -> 5658 difference -> -36 -d -> 217 demand -> 253 difference -> 36 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 57 -f -> 5689 demand -> 5658 difference -> -31 -d -> 222 demand -> 253 difference -> 31 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 58 -f -> 5687 demand -> 5658 difference -> -29 -d -> 224 demand -> 253 difference -> 29 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 59 -f -> 5686 demand -> 5658 difference -> -28 -d -> 225 demand -> 253 difference -> 28 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 60 -f -> 5685 demand -> 5658 difference -> -27 -d -> 226 demand -> 253 difference -> 27 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 61 -f -> 5681 demand -> 5658 difference -> -23 -d -> 230 demand -> 253 difference -> 23 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 62 -f -> 5680 demand -> 5658 difference -> -22 -d -> 231 demand -> 253 difference -> 22 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 63 -f -> 5676 demand -> 5658 difference -> -18 -d -> 235 demand -> 253 difference -> 18 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 64 -f -> 5672 demand -> 5658 difference -> -14 -d -> 239 demand -> 253 difference -> 14 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 65 -f -> 5669 demand -> 5658 difference -> -11 -d -> 242 demand -> 253 difference -> 11 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 66 -f -> 5669 demand -> 5658 difference -> -11 -d -> 242 demand -> 253 difference -> 11 -o -> 3 demand -> 3 difference -> 0 - -Year: 2000 Iteration -> 67 -f -> 5667 demand -> 5658 difference -> -9 -d -> 244 demand -> 253 difference -> 9 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2000 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2001 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5667 -Initial area for land use : d -> 244 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2001 Iteration -> 0 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 1 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 2 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 3 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 4 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 5 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 6 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 7 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 8 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 9 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 10 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 11 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 12 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 13 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 14 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 15 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 16 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 17 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 18 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 19 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 20 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 21 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 22 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 23 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 24 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 25 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 26 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 27 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 28 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 29 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 30 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 31 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 32 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 33 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 34 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 35 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 36 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 37 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 38 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 39 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 40 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 41 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 42 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 43 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 44 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 45 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 46 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 47 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 48 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 49 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 50 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 51 -f -> 5667 demand -> 5611 difference -> -56 -d -> 244 demand -> 300 difference -> 56 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 52 -f -> 5666 demand -> 5611 difference -> -55 -d -> 245 demand -> 300 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 53 -f -> 5651 demand -> 5611 difference -> -40 -d -> 260 demand -> 300 difference -> 40 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 54 -f -> 5632 demand -> 5611 difference -> -21 -d -> 279 demand -> 300 difference -> 21 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 55 -f -> 5623 demand -> 5611 difference -> -12 -d -> 288 demand -> 300 difference -> 12 -o -> 3 demand -> 3 difference -> 0 - -Year: 2001 Iteration -> 56 -f -> 5620 demand -> 5611 difference -> -9 -d -> 291 demand -> 300 difference -> 9 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2001 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2002 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5620 -Initial area for land use : d -> 291 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2002 Iteration -> 0 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 1 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 2 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 3 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 4 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 5 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 6 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 7 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 8 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 9 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 10 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 11 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 12 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 13 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 14 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 15 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 16 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 17 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 18 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 19 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 20 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 21 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 22 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 23 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 24 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 25 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 26 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 27 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 28 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 29 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 30 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 31 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 32 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 33 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 34 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 35 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 36 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 37 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 38 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 39 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 40 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 41 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 42 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 43 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 44 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 45 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 46 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 47 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 48 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 49 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 50 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 51 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 52 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 53 -f -> 5620 demand -> 5563 difference -> -57 -d -> 291 demand -> 348 difference -> 57 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 54 -f -> 5609 demand -> 5563 difference -> -46 -d -> 302 demand -> 348 difference -> 46 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 55 -f -> 5586 demand -> 5563 difference -> -23 -d -> 325 demand -> 348 difference -> 23 -o -> 3 demand -> 3 difference -> 0 - -Year: 2002 Iteration -> 56 -f -> 5570 demand -> 5563 difference -> -7 -d -> 341 demand -> 348 difference -> 7 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2002 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2003 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5570 -Initial area for land use : d -> 341 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2003 Iteration -> 0 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 1 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 2 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 3 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 4 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 5 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 6 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 7 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 8 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 9 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 10 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 11 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 12 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 13 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 14 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 15 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 16 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 17 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 18 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 19 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 20 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 21 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 22 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 23 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 24 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 25 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 26 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 27 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 28 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 29 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 30 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 31 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 32 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 33 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 34 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 35 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 36 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 37 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 38 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 39 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 40 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 41 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 42 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 43 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 44 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 45 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 46 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 47 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 48 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 49 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 50 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 51 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 52 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 53 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 54 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 55 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 56 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 57 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 58 -f -> 5570 demand -> 5516 difference -> -54 -d -> 341 demand -> 395 difference -> 54 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 59 -f -> 5547 demand -> 5516 difference -> -31 -d -> 364 demand -> 395 difference -> 31 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 60 -f -> 5529 demand -> 5516 difference -> -13 -d -> 382 demand -> 395 difference -> 13 -o -> 3 demand -> 3 difference -> 0 - -Year: 2003 Iteration -> 61 -f -> 5523 demand -> 5516 difference -> -7 -d -> 388 demand -> 395 difference -> 7 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2003 - -Executing Demand component -Executing Potential component -Executing Allocation component - -Time: 2004 -------------------------------------------------------------------------------- -Cell Area 1 -Num of cells 5914 -Max diff area 10 -Initial area for land use : f -> 5523 -Initial area for land use : d -> 388 -Initial area for land use : o -> 3 -------------------------------------------------------------------------------- - -Year: 2004 Iteration -> 0 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 1 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 2 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 3 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 4 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 5 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 6 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 7 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 8 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 9 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 10 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 11 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 12 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 13 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 14 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 15 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 16 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 17 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 18 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 19 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 20 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 21 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 22 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 23 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 24 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 25 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 26 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 27 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 28 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 29 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 30 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 31 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 32 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 33 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 34 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 35 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 36 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 37 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 38 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 39 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 40 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 41 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 42 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 43 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 44 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 45 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 46 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 47 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 48 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 49 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 50 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 51 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 52 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 53 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 54 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 55 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 56 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 57 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 58 -f -> 5523 demand -> 5468 difference -> -55 -d -> 388 demand -> 443 difference -> 55 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 59 -f -> 5511 demand -> 5468 difference -> -43 -d -> 400 demand -> 443 difference -> 43 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 60 -f -> 5479 demand -> 5468 difference -> -11 -d -> 432 demand -> 443 difference -> 11 -o -> 3 demand -> 3 difference -> 0 - -Year: 2004 Iteration -> 61 -f -> 5469 demand -> 5468 difference -> -1 -d -> 442 demand -> 443 difference -> 1 -o -> 3 demand -> 3 difference -> 0 - -Demand allocated correctly in this time: 2004 - -Saving Lab6_2004. -Elapsed time: 00:00:07 hh:mm:ss - -End of Simulation -[harness] lab15_md10: CSV written to /work/out/lab15_md10 diff --git a/docs/decisions.md b/docs/decisions.md index 6310122..4883a90 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -1,5 +1,12 @@ # Decision history — disslucc +> **Note (0.5.0).** This is a historical log. References to `benchmark/goldens/`, +> `tests/_lab*_helpers.py`, the Lua differential test and the discriminance suites +> describe the state up to v0.4.0; those now live in +> [disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark) or in the +> `v0.4.0` tag. + + > This file is the "why": decisions made, tests already run, what was > tried and dropped. For "how to use it", see [`quickstart.md`](quickstart.md) diff --git a/docs/examples/notebooks/continuous_synthetic.ipynb b/docs/examples/notebooks/continuous_synthetic.ipynb index cbabceb..8427e95 100644 --- a/docs/examples/notebooks/continuous_synthetic.ipynb +++ b/docs/examples/notebooks/continuous_synthetic.ipynb @@ -13,7 +13,7 @@ "small enough to read and run cell by cell.\n", "\n", "For the real validated scenario (Lab1, against the original TerraME\n", - "reference), see [`examples/run_lab1_via_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_lab1_via_executor.py) and\n", + "reference), see [`examples/run_continuous_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_continuous_executor.py) and\n", "[Validation](../../validation.md) instead — this notebook is for\n", "understanding the mechanics, not for validation." ] @@ -410,7 +410,7 @@ " match demand exactly\n", "\n", "For the same model driven by real data and validated against TerraME,\n", - "see [`examples/run_lab1_via_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_lab1_via_executor.py) and\n", + "see [`examples/run_continuous_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_continuous_executor.py) and\n", "[Validation](../../validation.md). For automatic provenance\n", "(`ModelExecutor`/`ExperimentRecord`, a CLI), see\n", "[API Reference](../../api.md#executors-dissluccexecutors)." diff --git a/docs/examples/notebooks/discrete_synthetic.ipynb b/docs/examples/notebooks/discrete_synthetic.ipynb index 94506ce..52e6e3d 100644 --- a/docs/examples/notebooks/discrete_synthetic.ipynb +++ b/docs/examples/notebooks/discrete_synthetic.ipynb @@ -14,7 +14,7 @@ "\n", "For the real validated scenario (Lab15, at exact cell-by-cell agreement\n", "against the original TerraME reference), see\n", - "[`examples/run_lab15_via_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_lab15_via_executor.py) and\n", + "[`examples/run_discrete_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_discrete_executor.py) and\n", "[Validation](../../validation.md) instead -- this notebook is for\n", "understanding the mechanics, not for validation." ] @@ -409,7 +409,7 @@ "\n", "For the same model driven by real data and validated against TerraME\n", "at exact cell-by-cell agreement, see\n", - "[`examples/run_lab15_via_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_lab15_via_executor.py) and\n", + "[`examples/run_discrete_executor.py`](https://github.com/DisSModel/disslucc/blob/main/examples/run_discrete_executor.py) and\n", "[Validation](../../validation.md). For automatic provenance\n", "(`ModelExecutor`/`ExperimentRecord`, a CLI), see\n", "[API Reference](../../api.md#executors-dissluccexecutors)." diff --git a/docs/index.md b/docs/index.md index 42f937f..67b2290 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,11 +2,11 @@ **Land Use and Cover Change (LUCC) modeling, raster-only, on top of [`dissmodel`](https://dissmodel.github.io/dissmodel/)** -Continuous (CLUE) and discrete (CLUE-S) allocation, both validated -against the original TerraME reference on real data: Lab1 (continuous) -within the official 0.01 MAE tolerance, Lab15 (discrete) at exact, -100% cell-by-cell agreement -- see [Validation](validation.md) for -both results and what each one does and doesn't prove. +LuccME's components -- demand, potential and allocation -- in Python: +continuous (CLUE-like), discrete (CLUE-S-like), spatial-lag potential and +saturation. Agreement with the original TerraME/LuccME is checked in +[disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark); +see [Validation](validation.md). ```bash pip install -e ".[examples]" @@ -38,9 +38,8 @@ a second entry point, with a CLI, same math -- see - **[API Reference](api.md)** -- reference for every public class and parameter - **[Architecture](architecture.md)** -- what's faithful to the original repositories, what was simplified, what's new -- **[Validation](validation.md)** -- validation results against - TerraME (Lab1 continuous, Lab15 discrete) and the engineering vs. - scientific validation distinction +- **[Validation](validation.md)** -- where the comparison with TerraME + lives (disslucc-benchmark) and what is checked in this repository - **[Decisions](decisions.md)** -- full history of decisions and tests throughout development - **Notebooks** -- two small, fully synthetic (no shapefiles, no diff --git a/docs/quickstart.md b/docs/quickstart.md index 2f06551..ae63c2c 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -66,9 +66,10 @@ is the experiment, reproducible with | Script | What | Data | |---|---|---| | `examples/run_script.py` | full continuous model | synthetic | -| `examples/run_lab1_real.py` | continuous model | real (csAC.zip, Lab1) | -| `examples/run_lab1_validation.py` | validates against TerraME | real + TerraME reference | -| `examples/run_lab15_validation.py` | discrete model, validates against TerraME | real + TerraME reference | +| `examples/run_continuous_real_data.py` | continuous model | real (csAC.zip) | +| `examples/run_continuous_executor.py` | same, through the executor (provenance) | real (csAC.zip) | +| `examples/run_discrete_executor.py` | discrete model, through the executor | real (cs_moju.zip) | +| `examples/dissmodel-configs/*.toml` | the same experiments as TOML, run with the CLI | real | Run any of them from inside `examples/`: @@ -89,8 +90,8 @@ walk through the same mechanics one step at a time: - [`api.md`](api.md) -- reference for every class and parameter - [`architecture.md`](architecture.md) -- what's faithful to the original `disslucc-continuous`/`disslucc-discrete`, what was simplified, what's new -- [`validation.md`](validation.md) -- validation results against - TerraME (MAE, Pontius & Millones decomposition) +- [`validation.md`](validation.md) -- how disslucc is checked against + TerraME (the disslucc-benchmark repository) - [`decisions.md`](decisions.md) -- full history of decisions, tests, and findings throughout development @@ -102,8 +103,8 @@ instead of direct construction -- same components underneath, same math, identical result: ```bash -python3 run_lab1_via_executor.py # same Lab1, with ExperimentRecord -python3 run_lab15_via_executor.py # same Lab15, with ExperimentRecord +python3 run_continuous_executor.py # continuous, with ExperimentRecord +python3 run_discrete_executor.py # discrete, with ExperimentRecord ``` Details in [`api.md`](api.md#executors-dissluccexecutors). diff --git a/docs/validation.md b/docs/validation.md index 152c497..a1749ea 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -1,223 +1,48 @@ # Validation — disslucc vs TerraME -Two real scenarios, two numeric results against the original -TerraME/LuccME reference -- not just "runs without error". +The agreement between disslucc and the original TerraME/LuccME is checked in a +separate repository, +**[LambdaGeo/disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark)**, +which keeps the reference results, the comparison code and the numbers. This +repository holds the package and examples only, so that learning the model +and checking it against TerraME are two separate things. -## Lab1 (continuous) — Amazon deforestation, csAC region +## Where things are -Real data: `data/input/csAC.zip` (shapefile, 6,574 cells) + -`data/input/examples_demand_lab1.csv`, both originally from -`disslucc-continuous`. Real calibrated regression coefficients (not -synthetic). Reference: the last year (2014) of the golden -`benchmark/goldens/lab01_md1643` -- the same values as the former -`benchmark/data/LUCCME_Lab1_2014.zip` from `disslucc-continuous` (max -difference 5e-13), now kept together with its generating scripts in -[LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker) -(`benchmark/references/lab01_md1643/`). +| What | Where | +|---|---| +| Per-lab results (iterations, MAE, max error, timings), reproducible with `make benchmark` | [disslucc-benchmark](https://github.com/LambdaGeo/disslucc-benchmark) | +| TerraME/LuccME reference results (goldens, all 21 LuccME labs) and the container that generated them | [LambdaGeo/luccme-goldens](https://github.com/LambdaGeo/luccme-goldens), [terrame-docker](https://github.com/profsergiocosta/terrame-docker) | +| What is checked here | `tests/`: demand components, and the TOML executors against hand-built experiments (same output file, same SHA-256) | -Method: raster grid built via direct `row`/`col` mapping (each -shapefile polygon already knows its position in the original grid -- -not spatial resampling by resolution, which would introduce alignment -error). Same method as -`lucc_benchmark_executor.py::_build_mock_raster`. +disslucc-benchmark pins the disslucc release it was run with; the numbers it +reports belong to that pair of versions. -``` -Pontius & Millones -- disslucc vs TerraME, class 'd' (deforestation): - n: 6574 - mae: 0.003583 - quantity_disagreement: 0.003210 - allocation_disagreement: 0.000373 - total_disagreement: 0.003583 -``` +## Two scenarios worth knowing -**MAE = 0.0036, within the 0.01 tolerance** that -`disslucc-continuous/tests/test_benchmark_validation.py` uses to -consider something "equivalent to TerraME". `quantity_disagreement` -dominates over `allocation_disagreement` by nearly 10x -- the spatial -pattern matches almost perfectly; the small difference is in total -allocated quantity, not position. +The package's two original scenarios are kept in the benchmark under these ids: -Reproduce: `examples/run_lab1_validation.py`. +- **`lab01_md1643`** (continuous, Amazon deforestation, csAC region, 6,574 cells): + iteration counts identical to TerraME in every year. +- **`lab15_md10`** (discrete, Moju region, 5,914 cells): iteration counts + identical to TerraME in every year. -Provenance: the original LuccME scripts that generated this reference -(`Lab1`, `maxDifference=1643`) are kept in -terrame-docker's `benchmark/references/lab01_md1643/` (`lab1_main.lua` + -`lab1_submodel.lua`, with the original TerraME output zip). These are -**not** the same scripts as `terrame/luccme`'s public -`tests/functional/lab01.lua`, which shares this scenario's coefficients -and demand but declares a different `maxDifference` (5000) and did not -generate this data -- see `benchmark/references/README.md` there. +Their numbers are in the benchmark report; they are not copied here, so there +is one place to update when they change. -**Where this MAE comes from.** All of it is one deliberate deviation: -LuccME's `correctCellChange` never runs (its guard reads -`cell.regionregionAloc`, a typo, in `AllocationCClueLike.lua`), while -disslucc runs the correction by default (`cell_correction=True`, the -intended algorithm). With `cell_correction=False`, disslucc matches TerraME -in every year and every iteration count (MAE < 1e-7, float32 noise) -- see -"Year by year" below. +## One deliberate difference -## Lab15 (discrete) — deforestation, Moju region +LuccME's `correctCellChange` never runs: its guard reads `cell.regionregionAloc`, +a field name that no cell has (in `AllocationCClueLike.lua`). disslucc runs the +correction by default (`AllocationClueLike(cell_correction=True)`), the +algorithm as documented. With `cell_correction=False` disslucc follows the +behavior of the reference run. The benchmark reports both, and labels the +difference "by design". -Real data: `data/input/cs_moju.zip` (shapefile, 5,914 cells), originally -from `disslucc-discrete`. Real logistic regression coefficients, real -transition matrix (irreversible deforestation: forest can become -deforested, deforested doesn't go back to forest). Reference: -the last year (2004) of the golden `benchmark/goldens/lab15_md10` -- the -same values as the former `benchmark/data/Lab15_2004.zip` from -`disslucc-discrete` (identical), now kept with its generating scripts in -terrame-docker (`benchmark/references/lab15_md10/`). +## History -``` -Pontius & Millones -- disslucc vs TerraME, class 'd': - n: 5914 - mae: 0.0 - quantity_disagreement: 0.0 - allocation_disagreement: 0.0 - total_disagreement: 0.0 - -Accuracy: 100.0000% F1: 1.0000 (TP=442 TN=5472 FP=0 FN=0) -``` - -**MAE = 0, 100% cell-by-cell agreement** -- matches exactly the result -documented in `disslucc-discrete` itself -(`benchmark/validate_lab15.py`: "100% cell-by-cell agreement with -TerraME d_out"). - -Reproduce: `examples/run_lab15_validation.py`. - -Provenance: despite the "Lab15" label used throughout this project -(inherited from `disslucc-discrete`), the script that actually -generated this reference is named `Lab6` internally -(`outputTheme = "Lab6_"`, `maxDifference=10`), vendored unmodified at -terrame-docker's `benchmark/references/lab15_md10/` under their original -names `lab6_main.lua` + `lab6_submodel.lua` (this project used to keep -them as `lab15_main.lua`/`lab15_submodel.lua`, to match its "Lab15" -naming). This is **not** the same script as -`terrame/luccme`'s public `tests/functional/lab15.lua`, which shares -this scenario's coefficients and demand but declares a different -`maxDifference` (300) and did not generate this data -- see -`benchmark/references/README.md` in terrame-docker for the full trace. - -### Discriminance warning (inherited, not my own achievement) - -The original `disslucc-discrete` already documents this caveat in -`benchmark/validate_lab15.py`, and it applies equally here: the Lab15 -scenario is **nearly non-discriminative** -- a trivial static ranking -by `(prob_d - prob_f)`, with no CLUE-S, no iteration at all, no time -steps, already reproduces the same cell-by-cell output. - -This means the result above confirms that **the logistic regression -coefficients were transcribed correctly** (the deterministic part, -easy to verify) -- **it is not proof that the allocation algorithm** -(CLUE-S with iteration/convergence via `factor_iteration`) **is -faithful** in scenarios where competition between classes really -matters. Proving that requires comparing the iteration count per step -against the TerraME log -- done now, year by year, in the next section. - -## Year by year, with iteration counts - -`benchmark/goldens/` holds a copy of the goldens generated in -[LambdaGeo/terrame-docker](https://github.com/LambdaGeo/terrame-docker): -for every cell and every simulated year, `_out` and `_pot`, plus -TerraME's convergence-loop iteration count per year. terrame-docker has 23 -(the 21 functional labs of the LuccME package and the two scenarios above); -this repository keeps the ones its tests use (`lab01`, `lab01_md1643`, -`lab15`, `lab15_md10`, and `lab03`/`lab06` for the components of the next -section), and adds the others as their components are implemented. -`tests/test_goldens_per_year.py` checks the iteration count per year -(exact) and every class per year (MAE < 1e-6): - -| Golden | `maxDifference` | TerraME iterations per year | disslucc | -|---|---|---|---| -| `lab15_md10` (discrete) | 10 | 0, 67, 56, 56, 61, 61 | same; `d_out`/`d_pot` identical every year | -| `lab15` (package) | 300 | 0 every year | same; identical every year | -| `lab01_md1643` (continuous) | 1643 | 0, 0, 8, 26, 18, 17, 17 | same with `cell_correction=False` (MAE < 1e-7 every year) | -| `lab01` (package) | 5000 | 0 every year | same with `cell_correction=False` | - -With the default `cell_correction=True`, `lab01_md1643` gives 0, 0, 0, 14, -17, 16, 16 iterations and drifts from 2009 on (MAE 0.003583 in 2014, the -number in the Lab1 section above). - -This closes the gap left by the Lab15 discriminance warning: the final map -alone could not tell a correct CLUE-S from a static ranking, but the -iteration counts can, and they match in every year. - -## Spatial-lag potential and saturation allocation (lab03, lab06) - -`PotentialSpatialLagRegression` and `AllocationClueLikeSaturation` port the two -LuccME components LuccME-BR (Bezerra et al. 2022) is built from. `lab03` and -`lab06` are the two LuccME labs that combine exactly -`DemandPreComputedValues` + `PotentialCSpatialLagRegression` + -`AllocationCClueLikeSaturation` (csAC, 2008–2014); `lab06` also replaces the -driver `ti` in 2009 (`updateYears`). - -| Check | Result | Test | -|---|---|---| -| potential alone, each year from the golden's previous land use | every cell, year and class, \|Δ\| < 1e-10 (the goldens keep 12 decimals) | `test_spatial_lag_golden.py` | -| whole model (potential + allocation) run from 2008 on its own | every cell's `_out` and `_pot`, every year to 2014, \|Δ\| < 1e-9 | `test_saturation_golden.py` | -| TerraME log, per year | iterations (0 every year) and "Maximum error" identical (rel. 1e-9) | `test_saturation_golden.py` | - -What the goldens also tell apart: a non-cumulative constant adaptation (the -reading `PotentialLinearRegression` uses) misses from 2010 on; `lab06`'s -dynamic variables paired by `object_id0` instead of by position (LuccME's -`forEachCellPair`; `csAC_2009` is not in `csAC`'s order) misses by 0.045. - -What they do **not** reach: no cell ever needs `correctCellChange`, no cell is -saturated (`changeLimiarValue = 1`), and csAC has no isolated cell — the cells' -visiting order has no effect either. Those parts are checked against the -original Lua functions (`tests/lua/`, run with `lupa`) on synthetic cases that -reach every branch, within 1e-12 (`test_lua_differential.py`); ports broken on -purpose (`BACKP` reset per cell, the 3 × 3 window without the cell, an isolated -cell using its own share) fail them. Not tested at all: `modify_driver`, called -only after 500 iterations. - -## Summary - -| Scenario | Type | MAE | Match | What it proves | -|---|---|---|---|---| -| Lab1 | continuous | 0.0036 | -- | full model (Demand+Potential+Allocation), within the official tolerance; the whole MAE is the cell correction that TerraME skips | -| Lab15 | discrete | 0.0 | 100% | correct regression coefficients | -| Lab1, Lab15 year by year | both | < 1e-7 | iterations exact | CLUE-S convergence confirmed; CLUE identical to TerraME with `cell_correction=False` | -| lab03, lab06 year by year | continuous | < 1e-9 | iterations and max error exact | spatial-lag potential + saturation allocation identical to TerraME; the branches the labs don't reach, identical to the Lua | - -## What these numbers DON'T prove: engineering validation ≠ scientific validation - -A distinction from chapter 26 of the *Geospatial Modeling with Python* -book (LambdaGeo), worth naming explicitly here: the two results above -are **engineering validation** -- they confirm this Python port matches -a TerraME reference run. That's a different question from **scientific -validation**: fitting the model to real observed data, splitting a -calibration period (used to fit parameters) from a held-out validation -period, checked only after fitting. - -Neither scenario here does that calibration/validation split -- Lab1 -and Lab15 run with already-calibrated coefficients (inherited from the -original repositories), comparing output against output, not against -an independent observation. - -Also worth noting: cell-by-cell comparison is sometimes too strict a -standard even for scientific validation -- two reasonable runs of the -same model can disagree pixel by pixel and still be "equally good" at -the spatial-pattern level. The standard alternative is **multi-scale** -comparison (Costanza 1989) -- checking agreement over successively -larger windows (3×3, 5×5, 9×9, ...): two maps can disagree at the -finest resolution and agree well at a coarser one, real information a -strict pixel-by-pixel match would throw away. This metric is already -listed as implemented in the new LuccME specification spreadsheet -(`Multi-window (Costanza 86)`) but doesn't exist in this package yet. - -## References - -- Verburg, P. H. et al. (2002). "Modeling the spatial dynamics of - regional land use: the CLUE-S model." *Environmental Management*, - 30(3), 391-405 -- Verburg, P. H. et al. (2006). "Downscaling of land use change - scenarios to assess the dynamics of European landscapes." - *Agriculture, Ecosystems & Environment*, 114(1), 39-56 -- Costanza, R. (1989). "Model goodness of fit: a multiple resolution - procedure." *Ecological Modelling*, 47(3-4), 199-215 -- the - multi-scale comparison method cited above -- Pontius Jr., R. G. & Millones, M. (2011). "Death to Kappa: birth of - quantity disagreement and allocation disagreement for accuracy - assessment." *International Journal of Remote Sensing*, 32(15), - 4407-4429 -- the decomposition used in `validation/pontius.py` +Before 0.5.0 this repository carried its own goldens, a Lua differential test +and a discriminance suite. They moved to (or were superseded by) the benchmark. +The full set is in the `v0.4.0` tag +([browse](https://github.com/DisSModel/disslucc/tree/v0.4.0)), including the +earlier version of this page with the Lab 1 and Lab 15 analysis. diff --git a/examples/dissmodel-configs/lucc_continuous.toml b/examples/dissmodel-configs/lucc_continuous.toml index 2c0ebab..ba227a7 100644 --- a/examples/dissmodel-configs/lucc_continuous.toml +++ b/examples/dissmodel-configs/lucc_continuous.toml @@ -1,35 +1,20 @@ -# configs/models/lucc_continuous.toml +# LuccContinuousExecutor (CLUE-like, linear-regression potential) from a TOML file. # -# Registers disslucc's LuccContinuousExecutor with dissmodel-platform. -# This is the SECOND entry point (docs/decisions.md) -- the default, -# script-first path (examples/run_script.py) needs none of this. +# Run it locally (data/input/csAC.zip is the region used in LuccME's Lab 1): # -# Mirrors the convention in dissmodel-configs -# (https://github.com/DisSModel/dissmodel-configs/blob/main/models/ -# lucc_continuous_vector.toml from disslucc-continuous), adapted to -# this package: disslucc is raster-only (no vector/raster split), so -# there is one executor -- LuccContinuousExecutor, name="lucc_continuous" -# -- not "lucc_vector"/"lucc_raster". +# python -m disslucc.executors.continuous run \ +# --toml examples/dissmodel-configs/lucc_continuous.toml \ +# --input data/input/csAC.zip \ +# --param demand_csv=data/input/examples_demand_lab1.csv \ +# --output outputs/result.tif # -# Everything under [model] except [model.parameters] becomes part of -# record.resolved_spec on the platform and is merged into -# record.parameters before run() is called (see ModelExecutor.run()'s -# docstring in dissmodel/executor/model_executor.py: "Receives record -# with resolved_spec and parameters already merged"). LuccContinuousExecutor -# reads land_use_types/potential_data/static/complementar_lu/ -# allocation_data directly from record.parameters -- see -# src/disslucc/executors/continuous.py and the equivalent hand-built -# ExperimentRecord in examples/run_lab1_via_executor.py, which this -# file is the TOML-registered version of (same Lab1 coefficients). +# The same file registers the model in dissmodel-configs for dissmodel-platform. +# It is the declarative twin of examples/run_continuous_executor.py: same +# coefficients, and tests/test_executor_toml.py checks that both give the +# same output file (same SHA-256). # -# NOTE: dissmodel's own *local* CLI (dissmodel.executor.cli.run_cli, -# `python -m disslucc.executors.continuous run --toml ...`) only lifts -# [model.parameters] into record.parameters -- it does NOT perform the -# resolved_spec merge described above. That merge is platform-side. -# So this file, as structured, is for registration in dissmodel-configs -# and execution via dissmodel-platform; it is not yet a drop-in -# --toml argument for local CLI runs of this executor. See -# docs/decisions.md for the open item this leaves. +# Everything under [model] except [model.parameters] is merged into +# record.parameters before run(); [model.parameters] holds runtime values. [model] executor_module = "disslucc.executors" diff --git a/examples/dissmodel-configs/lucc_discrete.toml b/examples/dissmodel-configs/lucc_discrete.toml index 260c5a7..84e6176 100644 --- a/examples/dissmodel-configs/lucc_discrete.toml +++ b/examples/dissmodel-configs/lucc_discrete.toml @@ -1,14 +1,13 @@ -# configs/models/lucc_discrete.toml +# LuccDiscreteExecutor (CLUE-S-like, logistic-regression potential) from a TOML file. # -# Registers disslucc's LuccDiscreteExecutor with dissmodel-platform. -# See lucc_continuous.toml in this directory for the full explanation -# of the [model] vs [model.parameters] split and the open item this -# depends on (docs/decisions.md, "TOML config example added for the -# executors path"). +# python -m disslucc.executors.discrete run \ +# --toml examples/dissmodel-configs/lucc_discrete.toml \ +# --input data/input/cs_moju.zip \ +# --param demand_csv=data/input/demand_moju.csv \ +# --output outputs/result.tif # -# Encodes the same Lab15/Moju coefficients as -# examples/run_lab15_via_executor.py, so the two are directly -# comparable. +# Declarative twin of examples/run_discrete_executor.py (the Moju region). +# See lucc_continuous.toml for the [model] / [model.parameters] split. [model] executor_module = "disslucc.executors" @@ -32,11 +31,8 @@ transition_matrix = [[[1, 1, 0], [0, 1, 0], [0, 0, 1]]] # demand_csv must be supplied at experiment time -- no sensible default. [model.parameters] n_steps = 6 -max_difference = 100.0 # looser than the exact-validation tolerance (10.0) used in - # run_lab15_validation.py -- see that script's comment: - # resolution-based rasterization here doesn't reproduce - # TerraME's exact cell count the way direct row/col - # alignment does. +max_difference = 100.0 # the grid is rasterized from the resolution, so the cell + # count is close to, not exactly, the original layer's factor_iteration = 0.0001 [[model.potential_data]] diff --git a/examples/dissmodel-configs/lucc_saturation.toml b/examples/dissmodel-configs/lucc_saturation.toml index e13e540..c42c930 100644 --- a/examples/dissmodel-configs/lucc_saturation.toml +++ b/examples/dissmodel-configs/lucc_saturation.toml @@ -1,12 +1,10 @@ -# configs/models/lucc_saturation.toml +# LuccSaturationExecutor (name="lucc_continuous_saturation"): the components of +# LuccME-BR -- DemandPreComputedValues + PotentialSpatialLagRegression + +# AllocationClueLikeSaturation. # -# Registers disslucc's LuccSaturationExecutor (name="lucc_continuous_saturation"): -# DemandPreComputedValues + PotentialSpatialLagRegression + -# AllocationClueLikeSaturation, the components of LuccME-BR. -# -# This file is LuccME's lab03 (luccme/tests/functional/lab03.lua), the same -# coefficients, demand and allocation data; tests/test_executor_saturation.py -# runs it through the local CLI and checks the result against the lab03 golden: +# Coefficients, demand and allocation data are those of LuccME's lab03. The input +# is a GeoTIFF with named bands (land uses, drivers, "mask", "order"); the test +# in tests/test_executor_toml.py shows how to build one from csAC.zip: # # python -m disslucc.executors.saturation run \ # --toml examples/dissmodel-configs/lucc_saturation.toml \ @@ -15,6 +13,7 @@ # # Every [[model.potential_data]] / [[model.allocation_data]] entry names its # class (lu) and may name its region (default 1): one entry per class per region. +# Agreement with TerraME for this model: LambdaGeo/disslucc-benchmark. [model] executor_module = "disslucc.executors" diff --git a/examples/quicklook_continuous_real_data.png b/examples/quicklook_continuous_real_data.png new file mode 100644 index 0000000..dbd3a53 Binary files /dev/null and b/examples/quicklook_continuous_real_data.png differ diff --git a/examples/quicklook_lab1_real.png b/examples/quicklook_lab1_real.png deleted file mode 100644 index ea4a3c5..0000000 Binary files a/examples/quicklook_lab1_real.png and /dev/null differ diff --git a/examples/run_lab1_via_executor.py b/examples/run_continuous_executor.py similarity index 79% rename from examples/run_lab1_via_executor.py rename to examples/run_continuous_executor.py index ba0236e..f3f5385 100644 --- a/examples/run_lab1_via_executor.py +++ b/examples/run_continuous_executor.py @@ -1,8 +1,12 @@ """ -Same Lab1 scenario as run_lab1_real.py, but through the real -ModelExecutor lifecycle (validate -> load -> run -> save) instead of -direct construction -- proves that bringing the Executor back doesn't -change the result, it only adds provenance (ExperimentRecord). +Same scenario as run_continuous_real_data.py (csAC region, 6,574 cells), but +through the ModelExecutor lifecycle (validate -> load -> run -> save) instead +of direct construction: the result is the same, and it adds provenance +(ExperimentRecord, input checksum, timings). + +The same experiment as TOML: examples/dissmodel-configs/lucc_continuous.toml +(tests/test_executor_toml.py checks that both give the same output file). +Agreement with TerraME (LuccME's Lab 1): LambdaGeo/disslucc-benchmark. """ from __future__ import annotations diff --git a/examples/run_lab1_real.py b/examples/run_continuous_real_data.py similarity index 97% rename from examples/run_lab1_real.py rename to examples/run_continuous_real_data.py index 2d805d3..4b9d7d2 100644 --- a/examples/run_lab1_real.py +++ b/examples/run_continuous_real_data.py @@ -113,6 +113,6 @@ ax.axis("off") fig.colorbar(im, ax=axes, shrink=0.7, label="cell fraction") fig.suptitle(f"csAC (real data) — land use after {N_STEPS - 1} steps") -fig.savefig("quicklook_lab1_real.png", dpi=120) +fig.savefig("quicklook_continuous_real_data.png", dpi=120) plt.close(fig) -print("\nquicklook saved to: quicklook_lab1_real.png") +print("\nquicklook saved to: quicklook_continuous_real_data.png") diff --git a/examples/run_lab15_via_executor.py b/examples/run_discrete_executor.py similarity index 76% rename from examples/run_lab15_via_executor.py rename to examples/run_discrete_executor.py index fece19b..ec8d4bd 100644 --- a/examples/run_lab15_via_executor.py +++ b/examples/run_discrete_executor.py @@ -1,6 +1,10 @@ """ -Same Lab15 scenario as run_lab15_validation.py, through the real -ModelExecutor lifecycle. +Discrete (CLUE-S-like) model on the Moju region (data/input/cs_moju.zip), +through the ModelExecutor lifecycle (validate -> load -> run -> save), which +adds provenance (ExperimentRecord, checksums, timings). + +The same experiment as TOML: examples/dissmodel-configs/lucc_discrete.toml. +Agreement with TerraME (LuccME's Lab 15): LambdaGeo/disslucc-benchmark. """ from __future__ import annotations @@ -23,13 +27,8 @@ "cell_area": 1.0, "n_steps": 6, "resolution": 0.00455, # cs_moju is in EPSG:4618 (degrees); ~84x92 cells - "max_difference": 100.0, # looser tolerance than run_lab15_validation.py: the - # resolution-based rasterization (production, new - # dataset) doesn't reproduce TerraME's exact cell - # count (that only happens with direct row/col - # alignment -- see run_lab15_validation.py, which - # uses that method for exact validation instead of - # general use) + "max_difference": 100.0, # the grid is rasterized from the resolution, so the cell count + # is close to, not exactly, the original layer's "factor_iteration": 0.0001, "potential_data": [ {"const": -2.34187976925989, "elasticity": 0.0, "betas": { diff --git a/examples/run_lab15_validation.py b/examples/run_lab15_validation.py deleted file mode 100644 index 70d3d8a..0000000 --- a/examples/run_lab15_validation.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -Real validation of the (new, raster) discrete port against the real -TerraME reference for Lab15 (cs_moju, 1999-2004) -- same -coefficients/transition matrix as -disslucc_discrete.executors.lucc_validation_executor. - -DISCRIMINANCE WARNING (inherited from the original repository, -benchmark/validate_lab15.py): the Lab15 scenario is nearly -non-discriminative -- a trivial static ranking by (prob_d - prob_f), -with no CLUE-S, no iteration, no time steps, already reproduces the -same cell-by-cell output. This validation confirms the logistic -regression coefficients were transcribed correctly, NOT necessarily -that the allocation algorithm itself is faithful (see -disslucc-discrete/tests/test_benchmark_discriminance.py, not ported -here). -""" -from __future__ import annotations - -from pathlib import Path - -import geopandas as gpd -import numpy as np -import pandas as pd -from dissmodel.core import Environment -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import ( - AllocationDClueSLike, - DemandPreComputedValues, - PotentialDLogisticRegression, -) -from disslucc.schemas import LogisticRegressionSpec -from disslucc.validation.pontius import confusion_metrics, pontius_millones - -ROOT = Path(__file__).resolve().parent.parent -# The input zip is read directly -- GDAL opens a single-layer shapefile zip -# without manual extraction, same as the tests in disslucc-discrete do. -CS_MOJU_ZIP = ROOT / "data" / "input" / "cs_moju.zip" -# TerraME reference: golden lab15_md10, last year = 2004 (generated with -# LambdaGeo/terrame-docker; formerly benchmark/data/Lab15_2004.zip). -GOLDEN_CSV = ROOT / "benchmark" / "goldens" / "lab15_md10" / "lab15_md10.csv.gz" - -LAND_USE_TYPES = ["f", "d", "o"] -N_STEPS = 6 -CELL_AREA = 1.0 - -ANNUAL_DEMAND: list[list[float]] = [ - [5706, 205, 3], [5658, 253, 3], [5611, 300, 3], - [5563, 348, 3], [5516, 395, 3], [5468, 443, 3], -] - -POTENTIAL_DATA: list[list[LogisticRegressionSpec]] = [[ - LogisticRegressionSpec(const=-2.34187976925989, elasticity=0.0, betas={ - "media_decl": -0.0272710076327129, "dist_area_": 4.30977432375496, - "dist_br": 3.10319957497883, "dist_curua": 0.445414024051873, - "dist_rios_": 47.3556329553235, "dist_estra": 38.4966894254506, - }), - LogisticRegressionSpec(const=-0.100351497277102, elasticity=0.6, betas={ - "media_decl": 0.0581358851690861, "dist_area_": -0.974998890251365, - "dist_br": -2.51650696123426, "dist_curua": -1.26742746441679, - "dist_rios_": -40.3646901047482, "dist_estra": -23.0841140199094, - }), - LogisticRegressionSpec(const=0.01, elasticity=0.5, betas={}), -]] - -TRANSITION_MATRIX = [[[1, 1, 0], [0, 1, 0], [0, 0, 1]]] # irreversible deforestation - - -def build_backend_by_rowcol(gdf: gpd.GeoDataFrame) -> tuple[RasterBackend, np.ndarray, np.ndarray]: - rows = gdf["lin"].astype(int).values - cols = gdf["col"].astype(int).values - n_rows, n_cols = int(rows.max()) + 1, int(cols.max()) + 1 - - backend = RasterBackend(shape=(n_rows, n_cols)) - mask = np.zeros((n_rows, n_cols), dtype=np.float32) - mask[rows, cols] = 1.0 - backend.set("mask", mask) - - driver_cols = ["media_decl", "dist_area_", "dist_br", "dist_curua", "dist_rios_", "dist_estra"] - for col in LAND_USE_TYPES + driver_cols: - arr = np.zeros((n_rows, n_cols), dtype=np.float32) - arr[rows, cols] = gdf[col].astype(float).values - backend.set(col, arr) - - return backend, rows, cols - - -# ── 1. real data + grid aligned by (lin, col) ───────────────────────────────── - -gdf = gpd.read_file(CS_MOJU_ZIP) -backend, rows, cols = build_backend_by_rowcol(gdf) -print(f"Backend: shape={backend.shape}, {len(rows):,} valid cells") - -# ── 2. run our raster discrete port ─────────────────────────────────────────── - -env = Environment(end_time=N_STEPS - 1) - -demand = DemandPreComputedValues(annual_demand=ANNUAL_DEMAND, land_use_types=LAND_USE_TYPES) - -PotentialDLogisticRegression( - backend=backend, potential_data=POTENTIAL_DATA, land_use_types=LAND_USE_TYPES, -) - -AllocationDClueSLike( - backend=backend, demand=demand, land_use_types=LAND_USE_TYPES, - transition_matrix=TRANSITION_MATRIX, cell_area=CELL_AREA, - max_difference=10.0, max_iteration=1000, factor_iteration=0.0001, -) - -env.run() - -for i, lu in enumerate(LAND_USE_TYPES): - n = int((backend.get(lu)[backend.get("mask").astype(bool)] == 1).sum()) - dem = ANNUAL_DEMAND[-1][i] - print(f" {lu}: allocated={n} demand={dem} diff={n - dem:+d}") - -# ── 3. compare cell by cell against the real TerraME reference ─────────────── - -golden = pd.read_csv(GOLDEN_CSV) -terrame = golden[golden["year"] == golden["year"].max()].rename(columns={"row": "lin"}) - -ours_df = pd.DataFrame({ - "lin": rows, "col": cols, "d_ours": backend.get("d")[rows, cols], -}).set_index(["lin", "col"]) -terrame_df = pd.DataFrame({ - "lin": terrame["lin"].astype(int).values, - "col": terrame["col"].astype(int).values, - "d_terrame": terrame["d_out"].astype(float).values, -}).set_index(["lin", "col"]) -aligned = ours_df.join(terrame_df, how="inner") -print(f"\nAligned cells: {len(aligned):,}") - -m = pontius_millones(aligned["d_ours"].values, aligned["d_terrame"].values) -print("\nPontius & Millones -- disslucc discrete (new raster) vs real TerraME, class 'd':") -for k, v in m.items(): - print(f" {k:>24}: {v}") - -c = confusion_metrics(aligned["d_ours"].values, aligned["d_terrame"].values) -print(f"\nAccuracy: {c['accuracy']:.4f}% F1: {c['f1']:.4f} (TP={c['tp']} TN={c['tn']} FP={c['fp']} FN={c['fn']})") diff --git a/examples/run_lab1_validation.py b/examples/run_lab1_validation.py deleted file mode 100644 index 401990b..0000000 --- a/examples/run_lab1_validation.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Real validation: runs disslucc (ours, raster-only) over the real Lab1 -scenario and compares cell by cell against the TerraME reference -(benchmark/goldens/lab01_md1643, last year = 2014, generated with -LambdaGeo/terrame-docker; formerly benchmark/data/LUCCME_Lab1_2014.zip), using -the continuous Pontius & Millones decomposition -(disslucc.validation.pontius). - -Unlike run_lab1_real.py: here the raster grid is built via DIRECT -row/col mapping (the way csAC.shp already comes, each polygon already -has row/col from the original grid) -- not spatial resampling by -resolution. It's the same method -disslucc_continuous.executors.lucc_benchmark_executor._build_mock_raster -uses to compare against TerraME; resampling by resolution (as -run_lab1_real.py does) introduces alignment error that doesn't exist -here. -""" -from __future__ import annotations - -from pathlib import Path - -import geopandas as gpd -import numpy as np -import pandas as pd -from dissmodel.core import Environment -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import ( - AllocationClueLike, - DemandPreComputedValues, - PotentialLinearRegression, -) -from disslucc.components.demand import load_demand_csv -from disslucc.schemas import AllocationSpec, RegressionSpec -from disslucc.validation.pontius import pontius_millones - -ROOT = Path(__file__).resolve().parent.parent -CSAC_ZIP = ROOT / "data" / "input" / "csAC.zip" -GOLDEN_CSV = ROOT / "benchmark" / "goldens" / "lab01_md1643" / "lab01_md1643.csv.gz" -DEMAND_CSV = ROOT / "data" / "input" / "examples_demand_lab1.csv" - -LAND_USE_TYPES = ["f", "d", "outros"] -DRIVER_COLS = ["assentamen", "uc_us", "uc_pi", "ti", "dist_riobr", "fertilidad", "rodovias"] -N_STEPS = 7 -CELL_AREA = 25.0 - - -def build_backend_by_rowcol(gdf: gpd.GeoDataFrame) -> tuple[RasterBackend, np.ndarray, np.ndarray]: - """Same method as the real benchmark's _build_mock_raster: each - polygon already knows its (row, col) in the original grid -- use - that directly, no spatial resampling.""" - rows = gdf["row"].astype(int).values - cols = gdf["col"].astype(int).values - n_rows, n_cols = int(rows.max()) + 1, int(cols.max()) + 1 - - backend = RasterBackend(shape=(n_rows, n_cols)) - mask = np.zeros((n_rows, n_cols), dtype=np.float32) - mask[rows, cols] = 1.0 - backend.set("mask", mask) - - for col in LAND_USE_TYPES + DRIVER_COLS: - arr = np.zeros((n_rows, n_cols), dtype=np.float32) - arr[rows, cols] = gdf[col].astype(float).values - backend.set(col, arr) - - return backend, rows, cols - - -def load_terrame_reference(golden_csv: Path) -> pd.DataFrame: - """TerraME's final year (row, col, d_out) from the golden.""" - golden = pd.read_csv(golden_csv) - return golden[golden["year"] == golden["year"].max()] - - -# ── 1. real data + grid aligned by row/col ──────────────────────────────────── -# csAC.zip is read directly -- GDAL opens a single-layer shapefile zip -# without manual extraction, same as the tests in disslucc-continuous do. - -gdf = gpd.read_file(CSAC_ZIP) -backend, rows, cols = build_backend_by_rowcol(gdf) -print(f"Backend: shape={backend.shape}, {len(rows):,} valid cells") - -annual_demand = load_demand_csv(DEMAND_CSV.read_text(), LAND_USE_TYPES) - -# ── 2. run our disslucc, same coefficients as always ────────────────────────── - -env = Environment(end_time=N_STEPS - 1) - -demand = DemandPreComputedValues(annual_demand=annual_demand, land_use_types=LAND_USE_TYPES) - -potential = PotentialLinearRegression( - backend=backend, demand=demand, land_use_types=LAND_USE_TYPES, - land_use_no_data="outros", - potential_data=[[ - RegressionSpec(const=0.7392, betas={ - "assentamen": -0.2193, "uc_us": 0.1754, "uc_pi": 0.09708, - "ti": 0.1207, "dist_riobr": 0.0000002388, "fertilidad": -0.1313, - }), - RegressionSpec(const=0.267, betas={ - "rodovias": -0.0000009922, "assentamen": 0.2294, - "uc_us": -0.09867, "dist_riobr": -0.0000003216, "fertilidad": 0.1281, - }), - RegressionSpec(const=0.0), - ]], -) - -allocation = AllocationClueLike( - backend=backend, demand=demand, potential=potential, - land_use_types=LAND_USE_TYPES, - static={"f": -1, "d": -1, "outros": 1}, - complementar_lu="f", cell_area=CELL_AREA, - allocation_data=[ - AllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), - AllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), - AllocationSpec(static=1, min_value=0, max_value=1, min_change=0, max_change=1), - ], -) - -env.run() - -# ── 3. compare cell by cell against the real TerraME reference ─────────────── - -terrame = load_terrame_reference(GOLDEN_CSV) -terrame_row = terrame["row"].astype(int).values -terrame_col = terrame["col"].astype(int).values -terrame_d = terrame["d_out"].astype(float).values - -our_d = backend.get("d")[rows, cols] # our grid, same row order as csAC.shp - -# align by (row,col) -- csAC and the TerraME reference may not be in -# the same row order, so join by key, not by position - -ours_df = pd.DataFrame({"row": rows, "col": cols, "d_ours": our_d}).set_index(["row", "col"]) -terrame_df = pd.DataFrame({"row": terrame_row, "col": terrame_col, "d_terrame": terrame_d}).set_index(["row", "col"]) -aligned = ours_df.join(terrame_df, how="inner") -print(f"Aligned cells (common row,col): {len(aligned):,}") - -m = pontius_millones(aligned["d_ours"].values, aligned["d_terrame"].values) -print("\nPontius & Millones -- disslucc (ours) vs real TerraME, class 'd' (deforestation):") -for k, v in m.items(): - print(f" {k:>24}: {v}") diff --git a/pyproject.toml b/pyproject.toml index f526f8a..8430a4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "disslucc" -version = "0.4.0" +version = "0.5.0" description = "Land Use and Cover Change (LUCC) modeling — continuous and discrete allocation — on top of dissmodel." readme = "README.md" -authors = [{ name = "Sergio Souza Costa", email = "sergio@ufma.br" }] +authors = [{ name = "Sérgio Souza Costa" }] license = { text = "MIT" } requires-python = ">=3.11" dependencies = [ @@ -15,11 +15,15 @@ dependencies = [ "scipy", ] +[project.urls] +Homepage = "https://github.com/DisSModel/disslucc" +Repository = "https://github.com/DisSModel/disslucc" +Documentation = "https://dissmodel.github.io/disslucc/" +Issues = "https://github.com/DisSModel/disslucc/issues" + [project.optional-dependencies] examples = ["matplotlib"] -# lupa runs the original LuccME Lua in tests/test_lua_differential.py -# (skipped without it); see docs/decisions.md -dev = ["mypy>=1.10", "pytest>=7", "ruff>=0.16,<0.17", "lupa"] +dev = ["mypy>=1.10", "pytest>=7", "ruff>=0.16,<0.17"] [tool.mypy] # 3.12, not 3.11 (the floor in requires-python): numpy 2.5's stub uses diff --git a/src/disslucc/components/demand/dates.py b/src/disslucc/components/demand/dates.py index 2c43599..ee3e5b7 100644 --- a/src/disslucc/components/demand/dates.py +++ b/src/disslucc/components/demand/dates.py @@ -136,7 +136,7 @@ class DemandComputeTwoDates(DemandPreComputedValues): the demand must match a reference to the unit. """ - def setup( + def setup( # type: ignore[override] self, backend, land_use_types: list[str], @@ -158,7 +158,7 @@ def setup( end_year - start_year + 1, direction_for_interpolation, ) - super().setup(annual_demand=rows, land_use_types=land_use_types) + super().setup(annual_demand=[[float(v) for v in r] for r in rows], land_use_types=land_use_types) class DemandComputeThreeDates(DemandPreComputedValues): @@ -172,7 +172,7 @@ class DemandComputeThreeDates(DemandPreComputedValues): middle_year : year they describe; start_year < middle_year < final_year. """ - def setup( + def setup( # type: ignore[override] self, backend, land_use_types: list[str], @@ -199,4 +199,4 @@ def setup( end_year - start_year + 1, direction_for_interpolation, ) - super().setup(annual_demand=rows, land_use_types=land_use_types) + super().setup(annual_demand=[[float(v) for v in r] for r in rows], land_use_types=land_use_types) diff --git a/src/disslucc/executors/continuous.py b/src/disslucc/executors/continuous.py index ad5616b..76d3a8b 100644 --- a/src/disslucc/executors/continuous.py +++ b/src/disslucc/executors/continuous.py @@ -7,7 +7,7 @@ produce that. Coexists with script-first, doesn't replace it: `examples/run_script.py` -and `examples/run_lab1_real.py` keep working exactly as before -- this +and `examples/run_continuous_real_data.py` keep working exactly as before -- this executor is a SECOND entry point. Inherits from `LuccExecutorBase` (validate/load/save shared with diff --git a/tests/_golden_helpers.py b/tests/_golden_helpers.py deleted file mode 100644 index bbf7d02..0000000 --- a/tests/_golden_helpers.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -tests/_golden_helpers.py --------------------------- -Per-year comparison against the LuccME goldens in benchmark/goldens/ -(generated with LambdaGeo/terrame-docker; see benchmark/README.md). - -A golden holds, for every cell and every simulated year, `_out` and -`_pot`, plus the TerraME iteration count per year in manifest.json. -`YearRecorder` snapshots the same arrays from the disslucc backend at the end -of each step, so both sides can be compared year by year. -""" -from __future__ import annotations - -import json -from pathlib import Path - -import numpy as np -import pandas as pd -from dissmodel.core import Model - -ROOT = Path(__file__).resolve().parent.parent -GOLDENS = ROOT / "benchmark" / "goldens" - - -def golden_available(name: str) -> bool: - return (GOLDENS / name / f"{name}.csv.gz").exists() - - -def load_golden(name: str) -> tuple[pd.DataFrame, dict]: - """Golden table (year, id, col, row, _out, _pot) and its manifest.""" - df = pd.read_csv(GOLDENS / name / f"{name}.csv.gz") - manifest = json.loads((GOLDENS / name / "manifest.json").read_text()) - return df, manifest - - -def golden_iterations(manifest: dict) -> list[int]: - """TerraME iterations per year, in year order.""" - it = manifest["iterations_per_year"] - return [it[y] for y in sorted(it)] - - -class YearRecorder(Model): - """Snapshots `` and `_pot` at every step. Create it AFTER the - allocation component so it runs after it in each step.""" - - def setup(self, backend, rows: np.ndarray, cols: np.ndarray, land_use_types: list[str]) -> None: - self.backend = backend - self.rows, self.cols = rows, cols - self.land_use_types = land_use_types - self.snapshots: list[dict[str, np.ndarray]] = [] - - def execute(self) -> None: - snap = {} - for lu in self.land_use_types: - snap[f"{lu}_out"] = np.asarray(self.backend.get(lu), dtype=np.float64)[self.rows, self.cols].copy() - if f"{lu}_pot" in self.backend.arrays: - snap[f"{lu}_pot"] = np.asarray( - self.backend.get(f"{lu}_pot"), dtype=np.float64)[self.rows, self.cols].copy() - self.snapshots.append(snap) - - -def per_year_frame(recorder: YearRecorder, years: list[int]) -> pd.DataFrame: - """Recorder snapshots as a long table keyed by (year, row, col).""" - frames = [] - for year, snap in zip(years, recorder.snapshots): - f = pd.DataFrame(snap) - f["year"], f["row"], f["col"] = year, recorder.rows, recorder.cols - frames.append(f) - return pd.concat(frames).set_index(["year", "row", "col"]).sort_index() - - -def per_year_mae(ours: pd.DataFrame, golden: pd.DataFrame, column: str) -> dict[int, float]: - """MAE of `column` per year, cells aligned by (row, col).""" - ref = golden.set_index(["year", "row", "col"])[column] - joined = ours[[column]].join(ref.rename("ref"), how="inner") - return { - int(year): float(np.abs(g[column] - g["ref"]).mean()) - for year, g in joined.groupby(level="year") - } diff --git a/tests/_lab03_helpers.py b/tests/_lab03_helpers.py deleted file mode 100644 index 95fee13..0000000 --- a/tests/_lab03_helpers.py +++ /dev/null @@ -1,253 +0,0 @@ -""" -tests/_lab03_helpers.py ------------------------ -Shared by the tests of PotentialSpatialLagRegression and -AllocationClueLikeSaturation against the LuccME goldens of lab03 and lab06 -(benchmark/goldens/, from LambdaGeo/terrame-docker v0.1.1). - -Both labs: DemandPreComputedValues + PotentialCSpatialLagRegression + -AllocationCClueLikeSaturation on csAC (data/input/csAC.zip), 2008–2014, the same -coefficients, demand and allocation data (luccme/tests/functional/lab03.lua, -lab06.lua). lab06 also replaces `ti` from csAC_2009 in 2009 (`updateYears`). -""" - -from __future__ import annotations - -import json -import re -from pathlib import Path - -import geopandas as gpd -import numpy as np -import pandas as pd -from dissmodel.core import Environment, Model -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import ( - AllocationClueLikeSaturation, - DemandPreComputedValues, - PotentialSpatialLagRegression, - SaturationAllocationSpec, - SpatialLagRegressionSpec, -) -from disslucc.components.potential.spatial_lag import spatial_lag_regression - -ROOT = Path(__file__).resolve().parent.parent -GOLDENS = ROOT / "benchmark" / "goldens" -INPUT = ROOT / "data" / "input" -LAND_USES = ["f", "d", "outros"] -NO_DATA = "outros" -YEARS = list(range(2008, 2015)) -# luccme/tests/functional/lab03.lua and lab06.lua (identical in both) -DEMAND = [ - [137878.1691, 19982.62882, 6489.202049], - [137622.2199, 20238.57805, 6489.202049], - [137366.2707, 20494.52729, 6489.202049], - [137110.3214, 20750.47652, 6489.202049], - [136824.6853, 21036.11265, 6489.202049], - [136539.0492, 21321.74879, 6489.202049], - [136253.4130, 21607.38493, 6489.202049], -] -UPDATES = {"lab03": {}, "lab06": {2009: "csAC_2009.zip"}} - - -def specs() -> list[SpatialLagRegressionSpec]: - return [ - SpatialLagRegressionSpec( - const=0.05266679, - ro=0.9124615, - betas={"uc_us": 0.03789872, "uc_pi": 0.04141921, "ti": 0.04455667}, - ), - SpatialLagRegressionSpec( - const=0.01431553, - ro=0.9019253, - betas={ - "assentamen": 0.0443537, - "uc_us": -0.01454847, - "fertilidad": 0.01701601, - "dist_riobr": -0.00000002262071, - }, - ), - SpatialLagRegressionSpec(const=0, ro=0, betas={}), - ] - - -class Lab: - """csAC on a raster by (row, col), the golden, and the dynamic-variable updates.""" - - def __init__(self, name: str): - self.name = name - self.cells = gpd.read_file(INPUT / "csAC.zip") - self.rows = self.cells["row"].astype(int).values - self.cols = self.cells["col"].astype(int).values - self.shape = (self.rows.max() + 1, self.cols.max() + 1) - self.valid = self.grid(np.ones(len(self.cells))) > 0 - self.golden = pd.read_csv(GOLDENS / name / f"{name}.csv.gz") - self.manifest = json.loads((GOLDENS / name / "manifest.json").read_text()) - self.drivers_used = sorted({d for s in specs() for d in s.betas}) - - def grid(self, values) -> np.ndarray: - a = np.zeros(self.shape) - a[self.rows, self.cols] = np.asarray(values, dtype=np.float64) - return a - - def initial(self, column: str) -> np.ndarray: - return self.grid(self.cells[column].astype(float).values) - - def drivers(self, year: int, pair_by: str = "position") -> dict[str, np.ndarray]: - """Drivers in effect in `year`. LuccME's updateYears copies the columns - of the _ file into the cells *by position* (forEachCellPair), - although csAC_2009 is not in the same order as csAC; `pair_by="id"` - pairs by object_id0 instead (used to show the difference matters).""" - cells = self.cells.copy() - for upd_year, file in sorted(UPDATES[self.name].items()): - if year >= upd_year: - new = gpd.read_file(INPUT / file) - if pair_by == "id": - new = cells[["object_id0"]].merge( - new.drop(columns="geometry"), on="object_id0", how="left" - ) - for col in new.columns: - if col in cells.columns and col not in ("geometry", "object_id0"): - cells[col] = new[col].values - return {d: self.grid(cells[d].astype(float).values) for d in self.drivers_used} - - def golden_year(self, year: int) -> pd.DataFrame: - return self.golden[self.golden["year"] == year].set_index(["row", "col"]) - - def past(self, year: int) -> dict[str, np.ndarray]: - """Land use at the start of `year`: initial attributes, then the golden's previous year.""" - if year == YEARS[0]: - return {lu: self.initial(lu) for lu in LAND_USES} - prev = self.golden_year(year - 1).reindex(list(zip(self.rows, self.cols))) - return {lu: self.grid(prev[f"{lu}_out"].values) for lu in LAND_USES} - - -def adapted_consts(cumulative: bool = True) -> dict[int, list[float]]: - """const in effect each year. LuccME writes the adapted constant back into - `const`, so the adaptation accumulates; `cumulative=False` is the other - reading of the code, kept to show the golden tells them apart.""" - base = [s.const for s in specs()] - out, cur = {}, list(base) - for t, year in enumerate(YEARS): - if t > 0: - start = cur if cumulative else base - cur = [ - start[i] + 0.01 * (DEMAND[t][i] - DEMAND[t - 1][i]) / DEMAND[t - 1][i] - for i in range(len(LAND_USES)) - ] - out[year] = list(cur) - return out - - -def max_errors(lab: Lab, cumulative: bool = True, pair_by: str = "position") -> dict[tuple[int, str], float]: - consts = adapted_consts(cumulative) - errors = {} - for year in YEARS: - past = lab.past(year) - drivers = lab.drivers(year, pair_by) - ref = lab.golden_year(year) - r = ref.index.get_level_values("row").values - c = ref.index.get_level_values("col").values - for i, (lu, spec) in enumerate(zip(LAND_USES, specs(), strict=True)): - spec.const = consts[year][i] - _, pot = spatial_lag_regression(spec, past[lu], drivers, lab.valid, past[NO_DATA]) - errors[(year, lu)] = float(np.abs(pot[r, c] - ref[f"{lu}_pot"].values).max()) - return errors - - -CELL_AREA = 25 # lab03.lua / lab06.lua: cellArea = 25 -# lab03.lua / lab06.lua allocationData, region 1 -ALLOCATION = [ - SaturationAllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), # f - SaturationAllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), # d - SaturationAllocationSpec(static=1, min_value=0, max_value=1, min_change=0, max_change=1), # outros -] - - -def terrame_log(name: str) -> dict[int, tuple[int, float]]: - """year → (iterations, maximum error) from the golden's TerraME log.""" - text = (GOLDENS / name / "terrame.log").read_text() - pattern = ( - r"Demand allocated correctly in (\d+)\s+Number of iterations: (\d+)\s+Maximum error: ([\d.eE+-]+)" - ) - return {int(y): (int(n), float(e)) for y, n, e in re.findall(pattern, text)} - - -def run(lab: Lab, order: str = "file"): - """Run the model from 2008 to 2014; return per-year snapshots and the allocation.""" - backend = RasterBackend(shape=lab.shape) - for lu in LAND_USES: - backend.set(lu, lab.initial(lu)) - for name, arr in lab.drivers(YEARS[0]).items(): - backend.set(name, arr) - backend.set("mask", lab.valid.astype(np.float32)) - # the order in which TerraME visits the cells: the layer's (file) order - visit = ( - np.arange(len(lab.cells)) if order == "file" else np.random.default_rng(0).permutation(len(lab.cells)) - ) - backend.set("order", lab.grid(visit)) - - class Updates(Model): - """LuccME's updateYears: this year's drivers, before demand and potential.""" - - def execute(self): - year = YEARS[0] + int(self.env.now()) - for name, arr in lab.drivers(year).items(): - backend.set(name, arr) - - snaps: dict[int, dict[str, np.ndarray]] = {} - - class Record(Model): - def execute(self): - year = YEARS[0] + int(self.env.now()) - snaps[year] = { - **{f"{lu}_out": backend.get(lu).copy() for lu in LAND_USES}, - **{f"{lu}_pot": backend.get(f"{lu}_pot").copy() for lu in LAND_USES}, - } - - env = Environment(end_time=len(YEARS) - 1) - Updates() - demand = DemandPreComputedValues(annual_demand=DEMAND, land_use_types=LAND_USES) - potential = PotentialSpatialLagRegression( - backend=backend, - potential_data=[specs()], - demand=demand, - land_use_types=LAND_USES, - land_use_no_data=NO_DATA, - ) - allocation = AllocationClueLikeSaturation( - backend=backend, - demand=demand, - potential=potential, - land_use_types=LAND_USES, - allocation_data=[ALLOCATION], - complementar_lu="f", - cell_area=CELL_AREA, - land_use_no_data=NO_DATA, - attr_protection="uc_pi", - max_difference=1643, - max_iteration=1000, - initial_elasticity=0.1, - min_elasticity=0.001, - max_elasticity=1.5, - order_attr="order", - ) - Record() - env.run() - return snaps, allocation - - -def worst_per_column(lab: Lab, snaps) -> dict[str, tuple[int, float]]: - worst: dict[str, tuple[int, float]] = {} - for year in YEARS: - ref = lab.golden_year(year) - r = ref.index.get_level_values("row").values - c = ref.index.get_level_values("col").values - for col, arr in snaps[year].items(): - err = float(np.abs(arr[r, c] - ref[col].values).max()) - if col not in worst or err > worst[col][1]: - worst[col] = (year, err) - return worst - - diff --git a/tests/_lab15_helpers.py b/tests/_lab15_helpers.py deleted file mode 100644 index 8a3894d..0000000 --- a/tests/_lab15_helpers.py +++ /dev/null @@ -1,137 +0,0 @@ -""" -tests/_lab15_helpers.py -------------------------- -Shared scenario, run and metrics helpers for the Lab15 (discrete, -raster) tests. Factored out of examples/run_lab15_validation.py so the -same, already-verified pipeline backs both the reproducibility script -and the pytest suite. -""" -from __future__ import annotations - -from pathlib import Path - -import geopandas as gpd -import numpy as np -import pandas as pd -from dissmodel.core import Environment -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import ( - AllocationDClueSLike, - DemandPreComputedValues, - PotentialDLogisticRegression, -) -from disslucc.schemas import LogisticRegressionSpec -from disslucc.validation.pontius import confusion_metrics, pontius_millones - -ROOT = Path(__file__).resolve().parent.parent -CS_MOJU_ZIP = ROOT / "data" / "input" / "cs_moju.zip" -# TerraME reference: golden lab15_md10 (benchmark/goldens/, generated in -# LambdaGeo/terrame-docker). Its last year is the former -# benchmark/data/Lab15_2004.zip (identical). -GOLDEN_CSV = ROOT / "benchmark" / "goldens" / "lab15_md10" / "lab15_md10.csv.gz" - -LAND_USE_TYPES = ["f", "d", "o"] -N_STEPS = 6 -CELL_AREA = 1.0 - -ANNUAL_DEMAND: list[list[float]] = [ - [5706, 205, 3], [5658, 253, 3], [5611, 300, 3], - [5563, 348, 3], [5516, 395, 3], [5468, 443, 3], -] - -DEFAULT_POTENTIAL_DATA: list[list[LogisticRegressionSpec]] = [[ - LogisticRegressionSpec(const=-2.34187976925989, elasticity=0.0, betas={ - "media_decl": -0.0272710076327129, "dist_area_": 4.30977432375496, - "dist_br": 3.10319957497883, "dist_curua": 0.445414024051873, - "dist_rios_": 47.3556329553235, "dist_estra": 38.4966894254506, - }), - LogisticRegressionSpec(const=-0.100351497277102, elasticity=0.6, betas={ - "media_decl": 0.0581358851690861, "dist_area_": -0.974998890251365, - "dist_br": -2.51650696123426, "dist_curua": -1.26742746441679, - "dist_rios_": -40.3646901047482, "dist_estra": -23.0841140199094, - }), - LogisticRegressionSpec(const=0.01, elasticity=0.5, betas={}), -]] - -TRANSITION_MATRIX = [[[1, 1, 0], [0, 1, 0], [0, 0, 1]]] # irreversible deforestation - -data_available = CS_MOJU_ZIP.exists() and GOLDEN_CSV.exists() - - -def build_backend_by_rowcol(gdf: gpd.GeoDataFrame) -> tuple[RasterBackend, np.ndarray, np.ndarray]: - rows = gdf["lin"].astype(int).values - cols = gdf["col"].astype(int).values - n_rows, n_cols = int(rows.max()) + 1, int(cols.max()) + 1 - - backend = RasterBackend(shape=(n_rows, n_cols)) - mask = np.zeros((n_rows, n_cols), dtype=np.float32) - mask[rows, cols] = 1.0 - backend.set("mask", mask) - - driver_cols = ["media_decl", "dist_area_", "dist_br", "dist_curua", "dist_rios_", "dist_estra"] - for col in LAND_USE_TYPES + driver_cols: - arr = np.zeros((n_rows, n_cols), dtype=np.float32) - arr[rows, cols] = gdf[col].astype(float).values - backend.set(col, arr) - - return backend, rows, cols - - -def load_gdf_input() -> gpd.GeoDataFrame: - return gpd.read_file(CS_MOJU_ZIP) - - -def load_gdf_terrame() -> pd.DataFrame: - """TerraME's final year (2004) as a table (lin, col, d_out), in the same - row order as the input shapefile. The golden calls `lin` `row`.""" - golden = pd.read_csv(GOLDEN_CSV) - final = golden[golden["year"] == golden["year"].max()][["row", "col", "d_out"]] - final = final.rename(columns={"row": "lin"}) - cells = load_gdf_input()[["lin", "col"]].astype(int) - return cells.merge(final, on=["lin", "col"], how="left", validate="one_to_one") - - -def run_lab15_raster( - potential_data: list[list[LogisticRegressionSpec]] | None = None, - annual_demand: list[list[float]] | None = None, -) -> tuple[RasterBackend, np.ndarray, np.ndarray]: - """Run the raster Lab15 (discrete CLUE-S-like) scenario; return the - backend plus the (row, col) arrays used to build it.""" - gdf = load_gdf_input() - backend, rows, cols = build_backend_by_rowcol(gdf) - - demand_table = annual_demand or ANNUAL_DEMAND - env = Environment(end_time=len(demand_table) - 1) - - demand = DemandPreComputedValues(annual_demand=demand_table, land_use_types=LAND_USE_TYPES) - PotentialDLogisticRegression( - backend=backend, potential_data=potential_data or DEFAULT_POTENTIAL_DATA, - land_use_types=LAND_USE_TYPES, - ) - AllocationDClueSLike( - backend=backend, demand=demand, land_use_types=LAND_USE_TYPES, - transition_matrix=TRANSITION_MATRIX, cell_area=CELL_AREA, - max_difference=10.0, max_iteration=1000, factor_iteration=0.0001, - ) - env.run() - return backend, rows, cols - - -def metrics_vs_terrame(backend: RasterBackend, rows: np.ndarray, cols: np.ndarray) -> dict: - """Pontius & Millones decomposition plus confusion metrics for - class 'd', aligned against the real TerraME reference.""" - terrame = load_gdf_terrame() - ours_df = pd.DataFrame({ - "lin": rows, "col": cols, "d_ours": backend.get("d")[rows, cols], - }).set_index(["lin", "col"]) - terrame_df = pd.DataFrame({ - "lin": terrame["lin"].astype(int).values, - "col": terrame["col"].astype(int).values, - "d_terrame": terrame["d_out"].astype(float).values, - }).set_index(["lin", "col"]) - aligned = ours_df.join(terrame_df, how="inner") - - m = pontius_millones(aligned["d_ours"].values, aligned["d_terrame"].values) - m.update(confusion_metrics(aligned["d_ours"].values, aligned["d_terrame"].values)) - return m diff --git a/tests/_lab1_helpers.py b/tests/_lab1_helpers.py deleted file mode 100644 index 9c7abb3..0000000 --- a/tests/_lab1_helpers.py +++ /dev/null @@ -1,145 +0,0 @@ -""" -tests/_lab1_helpers.py ------------------------ -Shared scenario, run and metrics helpers for the Lab1 (continuous, -raster) tests. Factored out of examples/run_lab1_validation.py so the -same, already-verified pipeline backs both the reproducibility script -and the pytest suite. -""" -from __future__ import annotations - -from pathlib import Path - -import geopandas as gpd -import numpy as np -import pandas as pd -from dissmodel.core import Environment -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import ( - AllocationClueLike, - DemandPreComputedValues, - PotentialLinearRegression, -) -from disslucc.components.demand import load_demand_csv -from disslucc.schemas import AllocationSpec, RegressionSpec -from disslucc.validation.pontius import pontius_millones - -ROOT = Path(__file__).resolve().parent.parent -CSAC_ZIP = ROOT / "data" / "input" / "csAC.zip" -# TerraME reference: golden lab01_md1643 (benchmark/goldens/, generated in -# LambdaGeo/terrame-docker). Its last year is the former -# benchmark/data/LUCCME_Lab1_2014.zip (max difference 5e-13). -GOLDEN_CSV = ROOT / "benchmark" / "goldens" / "lab01_md1643" / "lab01_md1643.csv.gz" -DEMAND_CSV = ROOT / "data" / "input" / "examples_demand_lab1.csv" - -LAND_USE_TYPES = ["f", "d", "outros"] -DRIVER_COLS = ["assentamen", "uc_us", "uc_pi", "ti", "dist_riobr", "fertilidad", "rodovias"] -N_STEPS = 7 -CELL_AREA = 25.0 -TOLERANCE = 0.01 - -# Values transcribed from the original LuccME script (lab1_submodel.lua), -# same as disslucc-continuous/tests/test_benchmark_discriminance.py. -LUCCME_MAX_DIFFERENCE = 1643.0 # AllocationCClueLike, area units -LUCCME_DEMAND_D_2014 = 21607.38493 # D1.annualDemand, last row, class "d" - -# Official, calibrated regression coefficients (same as examples/run_lab1_validation.py). -DEFAULT_POTENTIAL_DATA: list[list[RegressionSpec]] = [[ - RegressionSpec(const=0.7392, betas={ - "assentamen": -0.2193, "uc_us": 0.1754, "uc_pi": 0.09708, - "ti": 0.1207, "dist_riobr": 0.0000002388, "fertilidad": -0.1313, - }), - RegressionSpec(const=0.267, betas={ - "rodovias": -0.0000009922, "assentamen": 0.2294, - "uc_us": -0.09867, "dist_riobr": -0.0000003216, "fertilidad": 0.1281, - }), - RegressionSpec(const=0.0), -]] - -DEFAULT_ALLOCATION_DATA: list[AllocationSpec] = [ - AllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), - AllocationSpec(static=-1, min_value=0, max_value=1, min_change=0, max_change=1), - AllocationSpec(static=1, min_value=0, max_value=1, min_change=0, max_change=1), -] - -data_available = CSAC_ZIP.exists() and GOLDEN_CSV.exists() and DEMAND_CSV.exists() - - -def build_backend_by_rowcol(gdf: gpd.GeoDataFrame) -> tuple[RasterBackend, np.ndarray, np.ndarray]: - """Each polygon already knows its (row, col) in the original grid -- - use that directly, no spatial resampling. Same method as - disslucc_continuous.executors.lucc_benchmark_executor._build_mock_raster.""" - rows = gdf["row"].astype(int).values - cols = gdf["col"].astype(int).values - n_rows, n_cols = int(rows.max()) + 1, int(cols.max()) + 1 - - backend = RasterBackend(shape=(n_rows, n_cols)) - mask = np.zeros((n_rows, n_cols), dtype=np.float32) - mask[rows, cols] = 1.0 - backend.set("mask", mask) - - for col in LAND_USE_TYPES + DRIVER_COLS: - arr = np.zeros((n_rows, n_cols), dtype=np.float32) - arr[rows, cols] = gdf[col].astype(float).values - backend.set(col, arr) - - return backend, rows, cols - - -def load_terrame_reference(golden_csv: Path = GOLDEN_CSV) -> pd.DataFrame: - """TerraME's final year (2014) as a table (row, col, d_out), in the same - row order as the input shapefile.""" - golden = pd.read_csv(golden_csv) - final = golden[golden["year"] == golden["year"].max()][["row", "col", "d_out"]] - cells = gpd.read_file(CSAC_ZIP)[["row", "col"]].astype(int) - return cells.merge(final, on=["row", "col"], how="left", validate="one_to_one") - - -def full_metrics(pred: np.ndarray, ref: np.ndarray, tolerance: float = TOLERANCE) -> dict: - """Pontius & Millones decomposition plus RMSE and match_pct, same - fields as disslucc_continuous's LUCCBenchmarkExecutor._metrics.""" - m = pontius_millones(pred, ref) - diff = np.abs(np.asarray(pred, dtype=np.float64) - np.asarray(ref, dtype=np.float64)) - m["rmse"] = float(np.sqrt((diff ** 2).mean())) - m["match_pct"] = float((diff <= tolerance).mean() * 100) - return m - - -def run_lab1(potential_data: list[list[RegressionSpec]] | None = None, n_steps: int = N_STEPS) -> dict: - """Run the raster Lab1 scenario and return the metrics dict for - class 'd' (deforestation) against the real TerraME reference.""" - gdf = gpd.read_file(CSAC_ZIP) - backend, rows, cols = build_backend_by_rowcol(gdf) - - annual_demand = load_demand_csv(DEMAND_CSV.read_text(), LAND_USE_TYPES) - env = Environment(end_time=n_steps - 1) - - demand = DemandPreComputedValues(annual_demand=annual_demand, land_use_types=LAND_USE_TYPES) - potential = PotentialLinearRegression( - backend=backend, demand=demand, land_use_types=LAND_USE_TYPES, - land_use_no_data="outros", - potential_data=potential_data or DEFAULT_POTENTIAL_DATA, - ) - AllocationClueLike( - backend=backend, demand=demand, potential=potential, - land_use_types=LAND_USE_TYPES, - static={"f": -1, "d": -1, "outros": 1}, - complementar_lu="f", cell_area=CELL_AREA, - allocation_data=DEFAULT_ALLOCATION_DATA, - ) - env.run() - - terrame = load_terrame_reference() - terrame_row = terrame["row"].astype(int).values - terrame_col = terrame["col"].astype(int).values - terrame_d = terrame["d_out"].astype(float).values - - our_d = backend.get("d")[rows, cols] - ours_df = pd.DataFrame({"row": rows, "col": cols, "d_ours": our_d}).set_index(["row", "col"]) - terrame_df = pd.DataFrame( - {"row": terrame_row, "col": terrame_col, "d_terrame": terrame_d} - ).set_index(["row", "col"]) - aligned = ours_df.join(terrame_df, how="inner") - - return full_metrics(aligned["d_ours"].values, aligned["d_terrame"].values) diff --git a/tests/lua/AllocationCClueLikeSaturation.lua b/tests/lua/AllocationCClueLikeSaturation.lua deleted file mode 100644 index 7132db8..0000000 --- a/tests/lua/AllocationCClueLikeSaturation.lua +++ /dev/null @@ -1,858 +0,0 @@ ---- Modification of the AllocationClueLike component. In this case the speed of change in each cell use a spatio-temporal variable, dynamically updated --- every year, that indicates if the cell is in a more consolidated or in a frontier area. The saturation threshold considers a 10x10 neighbourhood, --- discounting protected areas. --- @arg component.maxDifference Maximum allocation error allowed for each land use in area. --- @arg component.maxIteration Maximum number of iterations at each time step of the model. --- @arg component.initialElasticity Initial elasticity value (iterationFactor). --- @arg component.minElasticity Minimum elasticity value which controls the allocation interaction factor. --- @arg component.maxElasticity Maximum elasticity value which controls the allocation interaction factor. --- @arg component.complementarLU The land use which will be recomputed in the end to sum exactly 100%. --- @arg component.saturationIndicator Name of a attribute which will be dynamically updated (and can be saved for calibration purposes). --- @arg component.attrProtection Database attribute indicating the percentage of protected areas to be excluded from the saturation level computation. --- @arg component.allocationData A table with two allocation parameters for each land use. --- @arg component.allocationData.static Indicates if the variable can increase or decrease in each cell, or only change in the direction of the demand. --- @arg component.allocationData.minValue Minimum value allowed for the percentage of a given land use in a cell (as a result of new changes - the original --- value can be out of the limits). --- @arg component.allocationData.maxValue Maximum value allowed for the percentage of a given land use in a cell (as a result of new changes - the original --- value can be out of the limits). --- @arg component.allocationData.minChange Minimum change in a given land use in a cell in a time step until (saturation) threshold. --- @arg component.allocationData.maxChange Maximum change in a given land use allowed in a cell in a time step until (saturation) threshold. --- @arg component.allocationData.changeLimiarValue Threshold (or limier) refers to a given amount of the land use in each cell. After this limier, the speed --- of change of a given land use in the cell is modified. --- @arg component.allocationData.maxChangeAboveLimiar Maximum change in a given land use allowed in a cell in a time step after (saturation) threshold. --- @arg component.run Handles with the rules of the component execution. --- @arg component.verify Handles with the verify method of a AllocationCClueLikeSaturation component. --- @arg component.updateAllocationParameters Handles with the allocation parameters update. --- @arg component.initElasticity Handles with the elasticity initialize considering a single --- elasticity for each land use (all cells). --- @arg component.computeChange Compute the Allocation change based on the potential of the cell. --- @arg component.compareAllocationToDemand Compares the demand to the amount of allocated land --- use/cover, then adapts elasticity. --- @arg component.correctCellChange Corrects total land use/cover types to 100 percent. --- @arg component.countAllocatedLandUseArea Calculates total area allocated by the regression --- equations for each land use/cover type. --- @arg component.printAllocatedArea Calculates and prints the allocated by the regression --- equations for each land use/cover type. --- @usage --DONTRUN ---A1 = AllocationCClueLikeSaturation ---{ --- maxDifference = 1643, --- maxIteration = 1000, --- initialElasticity = 0.1, --- minElasticity = 0.001, --- maxElasticity = 1.5, --- complementarLU = "floresta", --- saturationIndicator = "saturationLimiar", --- attrProtection = "uc_pi", --- allocationData = --- { --- -- Region 1 --- { --- {static = -1, minValue = 0, maxValue = 1, minChange = 0, maxChange = 1, changeLimiarValue = 1, maxChangeAboveLimiar = 0}, -- floresta --- {static = -1, minValue = 0, maxValue = 1, minChange = 0, maxChange = 1, changeLimiarValue = 1, maxChangeAboveLimiar = 0}, -- desmatamento --- {static = 1, minValue = 0, maxValue = 1, minChange = 0, maxChange = 1, changeLimiarValue = 1, maxChangeAboveLimiar = 0}, -- outros --- } --- } ---} -function AllocationCClueLikeSaturation(component) - -- Handles with the rules of the component execution. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.run(event, model) - component.run = function(self,event,luccMEModel) - -- Synchronize cellular space in the first year - local luTypes = luccMEModel.landUseTypes - local cs = luccMEModel.cs - - -- saving the first year values - if (event:getTime() == luccMEModel.startTime) then - for k, cell in pairs (cs.cells) do - for luind, lu in pairs (luTypes) do - cell[lu.."_backup"] = cell[lu] - end - end - - -- restoring the data from a saved year - elseif (belong(event:getTime() - 1,luccMEModel.save.saveYears) and ((event:getTime() - 1) ~= luccMEModel.startTime)) then - for k, cell in pairs (cs.cells) do - for luind, lu in pairs (luTypes) do - cell[lu] = cell[lu.."_backupYear"] - end - end - end - - -- Initialize the demandDirection and elasticity(internal component variables) - self:initElasticity(luccMEModel, self.initialElasticity) - self:updateAllocationParameters(event, luccMEModel) - - -- Define iteration loop variables - local nIter = 0 - local allocation_ok = false - local maxAdjust = self.maxDifference - local maxdiff = self.maxDifference * 1000 -- Used to have a large number of iterations - local flagFlex = false - local regionsNumber = #self.allocationData - - -- Loop until maxdiff is achieved - repeat - for rNumber = 1, regionsNumber, 1 do - -- compute tentative allocation - if (event:getTime() ~= luccMEModel.startTime) then - self:computeChange(luccMEModel, rNumber) - self:correctCellChange(luccMEModel, rNumber) - end - end - - if luccMEModel.useLog == true then - self:printAllocatedArea(event, luccMEModel, nIter) - end - - -- verify if allocation reaches demand - maxdiff = self:compareAllocationToDemand(event, luccMEModel) - - if (maxdiff <= maxAdjust) then - allocation_ok = true - - if (luccMEModel.useLog == true) then - print("\nDemand allocated correctly in "..event:getTime().."\tNumber of iterations: "..nIter.."\tMaximum error: "..maxdiff) - end - else - nIter = nIter + 1 - - if (nIter > self.maxIteration * 0.50) and (flagFlex == false) then - maxAdjust = maxAdjust * 2 - flagFlex = true - luccMEModel.potential:modifyDriver(self.complementarLU, self.attrProtection, 0.5, event, luccMEModel) - end - end - until ((nIter >= self.maxIteration) or (allocation_ok == true)) - - if (nIter == self.maxIteration) then - print("\nDemand not allocated correctly in this time step: "..nIter.."\n") - os.exit() - end - - forEachCell(cs, function(cell) - local total = 0 - local biggerLuValue = 0 - local biggerLu = "" - - for i, lu in pairs (luTypes) do - if (lu ~= self.complementarLU) then - total = total + cell[lu] - if (lu ~= luccMEModel.landUseNoData) then - if (cell[lu] > biggerLuValue) then - biggerLuValue = cell[lu] - biggerLu = lu - end - end - end - end - if (self.complementarLU ~= nil) then - cell[self.complementarLU] = 1 - total - if (cell[self.complementarLU] < 0) then - cell[biggerLu] = cell[biggerLu] + cell[self.complementarLU] - cell[self.complementarLU] = 0 - end - end - end - ) - - -- calculating changes and outputs - for i, lu in pairs (luTypes) do - local out = lu.."_out" - local diff = lu.."_chpast" - local previous = lu.."_chtot" - - for k, cell in pairs (cs.cells) do - if (event:getTime() == luccMEModel.startTime) then - cell[previous] = 0 - else - cell[previous] = cell[lu] - cell[lu.."_backup"] - end - cell[out] = cell[lu] - cell[diff] = cell[lu] - cell.past[lu] - end - end - - -- doing save year backup - if (belong(event:getTime(),luccMEModel.save.saveYears) and (event:getTime() ~= luccMEModel.startTime)) then - for k, cell in pairs (cs.cells) do - for luind, lu in pairs (luTypes) do - cell[lu.."_backupYear"] = cell[lu] - cell[lu] = cell[lu.."_backup"] - end - end - end - - cs:synchronize() - end - - -- Handles with the parameters verification. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.verify(event, self) - component.verify = function(self, event, luccMEModel) - print("Verifying Allocation parameters") - - -- create a region if does not have one - local cs = luccMEModel.cs - - if (self.regionAttr == nil) then - self.regionAttr = "regionAloc" - end - - forEachCell(cs, function(cell) - cell["alternate_model"] = 0 - - if (cell[self.regionAttr] == nil) then - cell["regionAloc"] = 1 - else - cell["regionAloc"] = cell[self.regionAttr] - end - end - ) - - -- check maxDifference - if (self.maxDifference == nil) then - error("maxDifference variable is missing", 2) - end - - -- check maxIteration - if (self.maxIteration == nil) then - error("maxIteration variable is missing", 2) - end - - -- check initialElasticity - if (self.initialElasticity == nil) then - error("initialElasticity variable is missing", 2) - end - - -- check minElasticity - if (self.minElasticity == nil) then - error("minElasticity variable is missing", 2) - end - - -- check maxElasticity - if (self.maxElasticity == nil) then - error("maxElasticity variable is missing", 2) - end - - -- check complementarLU - if (self.complementarLU == nil) then - error("complementarLU variable is missing", 2) - end - - -- check saturationIndicator - if (self.saturationIndicator == nil) then - error("saturationIndicator variable is missing", 2) - end - - -- check attrProtection - if (self.attrProtection == nil) then - error("attrProtection variable is missing", 2) - end - - -- check allocationData - if (self.allocationData == nil) then - error("allocationData is missing", 2) - end - - local regionsNumber = #self.allocationData - local lutNumber = #luccMEModel.landUseTypes - - -- check number of Regions - if (regionsNumber == nil or regionsNumber == 0) then - error("The model must have at least One region") - else - for i = 1, regionsNumber, 1 do - local allocationNumber = #self.allocationData[i] - - -- check the number of allocations - if (allocationNumber ~= lutNumber) then - error("Invalid number of regressions on Region number "..i.." . Regressions: "..allocationNumber.." LandUseTypes: "..lutNumber) - end - - -- check data into allocationData - for j = 1, allocationNumber, 1 do - -- check static variable - if(self.allocationData[i][j].static == nil) then - error("static variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check minValue variable - if (self.allocationData[i][j].minValue == nil) then - error("minValue variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check maxValue variable - if (self.allocationData[i][j].maxValue == nil) then - error("maxValue variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check minChange variable - if (self.allocationData[i][j].minChange == nil) then - error("minChange variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check maxChange variable - if (self.allocationData[i][j].maxChange == nil) then - error("maxChange variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check changeLimiarValue variable - if (self.allocationData[i][j].changeLimiarValue == nil) then - error("changeLimiarValue variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check maxChangeAboveLimiar variable - if (self.allocationData[i][j].maxChangeAboveLimiar == nil) then - error("maxChangeAboveLimiar variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - end -- for j - end -- for i - end -- else - - -- check complementarLU within database - if (self.complementarLU ~= nil) then - local foundCLU = 0 - - for k = 1, lutNumber, 1 do - if (self.complementarLU == luccMEModel.landUseTypes[k]) then - foundCLU = 1 - break - end - end - - if (foundCLU == 0) then - error("complementarLU: "..self.complementarLU.." is not a landUseType defined on main file.", 2) - end - end - - -- check attrProtection within database - if (self.attrProtection ~= nil) then - if (luccMEModel.cs.cells[1][self.attrProtection] == nil) then - error("attrProtection: "..self.attrProtection.." not found within database", 2) - end - end - - luccMEModel.cs:createNeighborhood{ name = "11x11", - strategy = "mxn", - } - end - - -- Update the allocation parameters based on the saturation of the region. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.updateAllocationParameters(event, luccMEModel) - component.updateAllocationParameters = function(self, event, luccMEModel) - local cs = luccMEModel.cs - local currentTime = event:getTime() - - forEachCell(cs, function(cell) - local prot_t = 0 - local original = 1 - local perc_def_original = 0 - - if (self.attrProtection ~= nil) then - prot_t = cell[self.attrProtection] - end - - if (luccMEModel.landUseNoData ~= nil) then - original = 1 - cell[luccMEModel.landUseNoData] - end - - local available_forest = original - prot_t - - if (available_forest > 0) then - local total_perc = 0 - local count = 0 - - perc_def_original = (1 - cell[self.complementarLU]) / available_forest - - if (perc_def_original > 1) then - perc_def_original = 1 - end - - forEachNeighbor(cell, "11x11", function(neigh, _, cell) - local prot_t = 0 - local original = 1 - - if (self.attrProtection ~= nil) then - prot_t = neigh[self.attrProtection] - end - - if (luccMEModel.landUseNoData ~= nil) then - original = 1 - neigh[luccMEModel.landUseNoData] - end - - local neigh_available_forest = original - prot_t - - if (neigh_available_forest > 0) then - local neigh_perc = (1 - neigh[self.complementarLU]) / neigh_available_forest - if (neigh_perc > 1) then - neigh_perc = 1 - end - - total_perc = total_perc + neigh_perc - count = count + 1 - end - end - ) - - if (count > 0) then - perc_def_original = total_perc / count - end - else - perc_def_original = 1 - end - - cell[self.saturationIndicator] = perc_def_original - end - ) - end - - -- Handles with the elasticity initialize considering a single elasticity for each land use (all cells). - -- Similar to the coarse scale old clue. - -- @arg luccMEModel A LuccME model. - -- @arg value The elasticity value. - -- @usage --DONTRUN - -- component.initElasticity(luccMEModel, self.initialElasticity) - component.initElasticity = function(self, luccMEModel, value) - -- Init elasticity. In this version of the component, a single elasticity for each land use(all cells). - -- Similar to the coarse scale old clue - local luTypes = luccMEModel.landUseTypes - self.elasticity = {} - - for k = 1, #luTypes, 1 do - self.elasticity[k] = value - end - end - - -- Compute the allocation change. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.computeChange(luccMEModel) - component.computeChange = function(self, luccMEModel, rNumber) - local cs = luccMEModel.cs - local luTypes = luccMEModel.landUseTypes - local activeRegionNumber = 0 - - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local attr_pot = lu.."_pot" - local luDirect = luccMEModel.demand:getCurrentLuDirection(i) - - for k, cell in pairs (cs.cells) do - if (cell.regionAloc == rNumber) then - local pot = cell[attr_pot] - local luStatic = luAllocData.static - local change = pot * self.elasticity[i] - - activeRegionNumber = rNumber - - if (math.abs(change) < luAllocData.minChange) then - pot = 0 - change = 0 - end - - if (math.abs(change) >= luAllocData.maxChange) then - if (pot ~= 0) then - change = luAllocData.maxChange * (pot / math.abs(pot)) - end - end - - if (((pot >= 0) and (luDirect == 1) and (luStatic < 1) and (cell[self.saturationIndicator] > luAllocData.changeLimiarValue))) then - if (change >= luAllocData.maxChangeAboveLimiar) then - if ((change / 2) < luAllocData.maxChangeAboveLimiar) then - change = change / 2 - else - change = luAllocData.maxChangeAboveLimiar - end - end - end - - if ((pot <= 0) and (luDirect == -1) and (luStatic < 1) and (cell[self.saturationIndicator] > luAllocData.changeLimiarValue)) then - if (math.abs(change) >= luAllocData.maxChangeAboveLimiar) then - if (math.abs(change / 2) < luAllocData.maxChangeAboveLimiar) then - change = change / 2 - else - change = (-1) * luAllocData.maxChangeAboveLimiar - end - end - end - - - if (luStatic == 1) then --do not change - cell[lu] = cell.past[lu] - elseif (luStatic == 0) then -- change independent of demand direction (ANAP) - cell[lu] = cell.past[lu] + change - elseif (((pot >= 0) and (luDirect == 1) and (luStatic == -1)) or ((pot <= 0) and (luDirect == -1) and (luStatic == -1))) then - cell[lu] = cell.past[lu] + change -- change according to demand direction - else - cell[lu] = cell.past[lu] - end - - if (cell[lu] < 0) then - cell[lu] = 0 - end - - if (cell[lu] < luAllocData.minValue) then - if (cell.past[lu] >= luAllocData.minValue) then - cell[lu] = luAllocData.minValue - else - cell[lu] = cell.past[lu] - end - end - - if (cell[lu] > luAllocData.maxValue) then - if (cell.past[lu] <= luAllocData.maxValue) then - cell[lu] = luAllocData.maxValue - else - cell[lu] = cell.past[lu] - end - end - end -- if cell.region - end -- for k - - if (activeRegionNumber == 0) then - error("Region "..rNumber.." is not set into database.") - end - end -- for i, lu - end -- computeChange - - -- Compares the demand to the amount of allocated land use/cover, then adapts elasticity. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.compareAllocationToDemand (event, luccMEModel) - component.compareAllocationToDemand = function(self, event, luccMEModel, rNumber) - -- Compares the demand to the amount of allocated land use/cover, then adapts elasticity - local cs = luccMEModel.cs - local luTypes = luccMEModel.landUseTypes - local areas = self:countAllocatedLandUseArea(cs, luTypes) - local max = 0 - local tot = 0 - local nRegression = 0 - local regionsNumber = #self.allocationData - - for j = 1, #luccMEModel.potential.potentialData, 1 do - for i, lu in pairs (luTypes) do - local luDirect = luccMEModel.demand:getCurrentLuDirection(i) - local currentDemand = luccMEModel.demand:getCurrentLuDemand(i) - - if (luDirect == 0 and event:getTime() == luccMEModel.startTime) then - if (currentDemand >= areas[i]) then - luDirect = 1 - else - luDirect = -1 - end - end - - if (luDirect == 1) then - self.elasticity[i] = self.elasticity[i] * (currentDemand / areas[i]) - else - self.elasticity[i] = self.elasticity[i] * (areas[i] / currentDemand) - end - - if (self.elasticity[i] > self.maxElasticity) then - self.elasticity[i] = self.maxElasticity - luccMEModel.potential:modify(luccMEModel, j, i, luDirect, event) - end - - if (self.elasticity[i] < self.minElasticity) then - if (self.allocationData[1][i].static < 0) then - self.elasticity[i] = self.minElasticity - luccMEModel.potential:modify(luccMEModel, j, i, luDirect * (-1), event) -- Original clue does not modify in this case, but AMAZALERT results are like this - else - luccMEModel.demand:changeLuDirection(i) - end - end - - if (luccMEModel.useLog == true) then - if (j > nRegression) then - print("Region "..j) - nRegression = j - end - - if (luccMEModel.potential.potentialData[j][i].newminReg ~= nil) then - print(lu, "elas: ", self.elasticity[i], "dir: ", luDirect, "const: ", luccMEModel.potential.potentialData[j][i].const, "->", luccMEModel.potential.potentialData[j][i].newconst, luccMEModel.potential.potentialData[j][i].newminReg, luccMEModel.potential.potentialData[j][i].newmaxReg) - elseif (luccMEModel.potential.potentialData[j][i].const ~= nil) then - print(lu, "elas: ", self.elasticity[i], "dir: ", luDirect, "const: ", luccMEModel.potential.potentialData[j][i].const, "->", luccMEModel.potential.potentialData[j][i].newconst) - else - print(lu, "elas: ", self.elasticity[i], "dir: ", luDirect) - end - end - - local diff = math.abs((areas[i] - currentDemand)) - - if (diff > max) then - max = diff - end - - tot = tot + math.abs(areas[i] - currentDemand) - end -- for i - end -- for j - - return max - end - - -- Corrects total land use/cover types to 100 percent. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.correctCellChange(luccMEModel) - component.correctCellChange = function(self, luccMEModel, rNumber) - -- corrects total land use/cover types to 100 percent - local cs = luccMEModel.cs - local luTypes = luccMEModel.landUseTypes - local NCOV = #luTypes - local BACKP = 0.5 - - for k, cell in pairs (cs.cells) do - if (cell.regionAloc == rNumber) then - local nostatic = 0 - local decr = 0 - local incr = 0 - local totcov = 0 - local totchange = 0 - local totstatic = 0 - local amin = cell[luTypes[1]] - cell.past[luTypes[1]] - local amax = amin - local max = math.abs(amax) - local l = 0 - - -- checks if total land use/covers from 100 percent - for i, lu in pairs (luTypes) do - totcov = totcov + cell[lu] - end - - if (math.abs(totcov - 1) > 0.005) then - for i, lu in pairs (luTypes) do - local dif = cell[lu] - cell.past[lu] - totchange = totchange + math.abs(dif) - - if (math.abs(dif) > max) then - max = math.abs(dif) - end - - if (dif > amax) then - amax = dif - end - - if (dif < amin) then - amin = dif - end - end - - -- adapts land use/cover types if all of them change into the same direction - if (totchange > 0) then - if ((BACKP * totchange) > (max * 0.5)) then - BACKP =(max / (2 * totchange)) - end - end - - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or cell[lu] >= luAllocData.maxValue) then - luStatic = 1 - end - - if (luStatic < 1) then - local dif = cell[lu] - cell.past[lu] - - if (dif >(-1 * BACKP * totchange)) then - incr = incr + 1 - end - - if (dif <(BACKP * totchange)) then - decr = decr + 1 - end - else - nostatic = nostatic + 1 - end - end - - if ((decr == (NCOV - nostatic)) or (incr == (NCOV - nostatic))) then - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luDirect = luccMEModel.demand:getCurrentLuDirection(i) - - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or cell[lu] >= luAllocData.maxValue) then - luStatic = 1 - end - - if (luStatic < 1) then - if (incr == (NCOV - nostatic)) then - cell[lu] = cell[lu] - (amin +(BACKP * totchange)) - end - - if (decr == (NCOV - nostatic)) then - cell[lu] = cell[lu] + ((BACKP * totchange) - amax) - end - - if (cell[lu] < 0) then - cell[lu] = 0 - end - end - - if ((luDirect == 1) and (cell[lu] < cell.past[lu]) and (luAllocData.static == -1)) then - cell[lu] = cell.past[lu] - end - - if ((luDirect == -1) and (cell[lu] > cell.past[lu]) and (luAllocData.static == -1)) then - cell[lu] = cell.past[lu] - end - end - end - - -- perform the corrections - repeat - l = l + 1 - totcov = 0 - totchange = 0 - totstatic = 0 - - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or cell[lu] >= luAllocData.maxValue) then - luStatic = 1 - end - - if (luStatic < 1) then - totcov = totcov + cell[lu] - else - totstatic = totstatic + cell[lu] - end - - totchange = totchange + math.abs(cell[lu] - cell.past[lu]) - end - - if (math.abs(totcov - (1 - totstatic)) > 0.005) then - if (totchange == 0) then - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or cell[lu] >= luAllocData.maxValue) then - luStatic = 1 - end - - if (luStatic < 1) then - cell[lu] = cell[lu] * ((1 - totstatic) / totcov) - end - end - else - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local aux = cell[lu] - - cell[lu] = cell[lu] -(math.abs(cell[lu] - cell.past[lu]) * ((totcov - (1 - totstatic)) / totchange)) - - if (cell[lu] < 0) then - cell[lu] = 0 - end - end - end - end - until ((math.abs(totcov -(1-totstatic)) <= 0.005) or (l >= 25)) - - if (l == 25) then - totcov = 0 - totstatic = 0 - - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or (cell[lu] >= luAllocData.maxValue)) then - luStatic =1 - end - - if (luStatic < 1) then - totcov = totcov + cell[lu] - else - totstatic = totstatic + cell[lu] - end - end - - for i, luAllocData in pairs (self.allocationData[rNumber]) do - local lu = luTypes[i] - local luStatic = luAllocData.static - - if ((cell[lu] <= luAllocData.minValue) or (cell[lu] >= luAllocData.maxValue)) then - luStatic = 1 - end - - if (luStatic < 1) then - cell[lu] = cell[lu] * ((1 - totstatic) / totcov) - end - end - end -- if l - end -- totcov - end -- if cell.region - end -- for each cell - end - - -- Calculates total area allocated by the regression equations for each land use/cover type. - -- @arg cs A multivalued set of Cells (Cell Space). - -- @arg luTypes A set of land use types. - -- @usage --DONTRUN - -- component.countAllocatedLandUseArea(cs, luTypes) - component.countAllocatedLandUseArea = function(self, cs, luTypes) - -- Calculates total area allocated by the regression equations for each land use/cover type - local areas = {} - local cellarea = cs.cellArea - - for i, lu in pairs (luTypes) do - local area = 0 - - for k, cell in pairs (cs.cells) do - local temp = cell[lu] - - if (temp > 0) then - area = area + temp - end - end - - areas[i] = area * cellarea - end - - return areas - end - - -- Calculates and prints the allocated by the regression equations for each land use/cover type. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @arg nIter An iterator number. - -- @usage --DONTRUN - -- component.printAllocatedArea(event, luccMEModel, nIter) - component.printAllocatedArea = function(self, event, luccMEModel, nIter) - -- Calculates and prints the allocated by the regression equations for each land use/cover type - local cs = luccMEModel.cs - local luTypes = luccMEModel.landUseTypes - local areas = {} - - areas = self:countAllocatedLandUseArea(cs, luTypes) - - print("\nYear: "..event:getTime().."\tStep: "..nIter) - - for i, lu in pairs (luTypes) do - local currentDemand = luccMEModel.demand:getCurrentLuDemand(i) - print(lu.."\tarea: "..math.floor(areas[i]).."\tDifference: "..math.floor(areas[i] - currentDemand)) - end - - io.flush() - end - - collectgarbage("collect") - return component -end -- close Allocation Component diff --git a/tests/lua/PotentialCSpatialLagRegression.lua b/tests/lua/PotentialCSpatialLagRegression.lua deleted file mode 100644 index 6db3ca3..0000000 --- a/tests/lua/PotentialCSpatialLagRegression.lua +++ /dev/null @@ -1,411 +0,0 @@ ---- Similar to the LinearRegression approach, but relies spatial regression techniques to estimate the --- regression cover (considers the spatial dependence of the land use). --- @arg component A Spatial Lag Regression component. --- @arg component.potentialData A table with the regression parameters for each attribute. --- @arg component.potentialData.isLog Inform whether the model is part of a coupling model. --- @arg component.potentialData.const A linear regression constant. --- @arg component.potentialData.minReg A coefficient to minimize the regression value. --- @arg component.potentialData.maxReg A coefficient to potentiality the regression value. --- @arg component.potentialData.ro Auto regressive coefficient. --- @arg component.potentialData.betas A linear regression betas for land use drivers --- and the index of landUseDrivers to be used by the regression (attributes). --- @arg component.landUseDrivers The land use drivers fields in database. --- @arg component.run Handles with the execution method of a PotentialCSpatialLagRegression component. --- @arg component.verify Handles with the verify method of a PotentialCSpatialLagRegression component. --- @arg component.modify Handles with the modify method of a PotentialCSpatialLagRegression component. --- @arg component.adaptRegressionConstants Handles with the constants regression method of a --- PotentialCSpatialLagRegression component. --- @arg component.modifyDriver Modify potencial for an protected area. --- @arg component.computePotential Handles with the modify method of a PotentialCSpatialLagRegression component. --- @return The modified component. --- @usage --DONTRUN ---P1 = PotentialCSpatialLagRegression ---{ --- potentialData = --- { --- -- Region 1 --- { --- -- floresta --- { --- isLog = false, --- const = 0.05266679, --- minReg = 0, --- maxReg = 1, --- ro = 0.9124615, --- --- betas = --- { --- uc_us = 0.03789872, --- uc_pi = 0.04141921, --- ti = 0.04455667 --- } --- }, --- --- -- desmatamento --- { --- isLog = false, --- const = 0.01431553, --- minReg = 0, --- maxReg = 1, --- ro = 0.9019253, --- --- betas = --- { --- assentamentos = 0.0443537, --- uc_us = -0.01454847, --- dist_riobranco = -0.00000002262071, --- fertilidadealtaoumedia = 0.01701601 --- } --- }, --- --- -- outros --- { --- isLog = false, --- const = 0, --- minReg = 0, --- maxReg = 1, --- ro = 0, --- --- betas = --- { --- --- } --- } --- } --- } ---} -function PotentialCSpatialLagRegression(component) - -- Handles with the execution method of a PotentialCSpatialLagRegression component. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.run(event, model) - component.run = function(self, event, luccMEModel) - local luTypes = luccMEModel.landUseTypes - local demand = luccMEModel.demand - local regionsNumber = #self.potentialData - - -- Create an internal constant that can be modified during allocation - for rNumber = 1, regionsNumber, 1 do - for i, luData in pairs(self.potentialData[rNumber]) do - if (luData.const == nil) then - luData.const = 0 - end - - if (luData.minReg == nil) then - luData.minReg = 0 - end - - if (luData.maxReg == nil) then - luData.maxReg = 1 - end - - luData.newconst = luData.const - luData.newminReg = luData.minReg - luData.newmaxReg = luData.maxReg - end - - if (self.constChange == nil) then - self.constChange = 0.1 -- original clue value - end - - if (event:getTime() > luccMEModel.startTime) then - self:adaptRegressionConstants(demand, rNumber) - end - - for i = 1, #luTypes, 1 do - self:computePotential(luccMEModel, rNumber, i) - end - end - end -- function run - - -- Handles with the verify method of a PotentialCSpatialLagRegression component. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.verify(event, self) - component.verify = function(self, event, luccMEModel) - print("Verifying Potential parameters") - local cs = luccMEModel.cs - - if (self.regionAttr == nil) then - self.regionAttr = "region" - end - - forEachCell(cs, function(cell) - cell["alternate_model"] = 0 - - if (cell[self.regionAttr] == nil) then - cell["region"] = 1 - else - cell["region"] = cell[self.regionAttr] - end - end - ) - - -- check potentialData - if (self.potentialData == nil) then - error("potentialData is missing", 2) - end - - local regionsNumber = #self.potentialData - - -- check number of Regions - if (regionsNumber == nil or regionsNumber == 0) then - error("The model must have at least One region") - else - for i = 1, regionsNumber, 1 do - local regressionNumber = #self.potentialData[i] - local lutNumber = #luccMEModel.landUseTypes - - -- check the number of regressions - if (regressionNumber ~= lutNumber) then - error("Invalid number of regressions on Region number "..i.." . Regressions: "..regressionNumber.." LandUseTypes: "..lutNumber) - end - - for j = 1, regressionNumber, 1 do - -- check isLog variable - if(self.potentialData[i][j].isLog == nil) then - error("isLog variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check minReg variable - if(self.potentialData[i][j].minReg == nil) then - error("minReg variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check maxReg variable - if(self.potentialData[i][j].maxReg == nil) then - error("maxReg variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check ro variable - if(self.potentialData[i][j].ro == nil) then - error("ro variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check const variable - if (self.potentialData[i][j].const == nil) then - error("const variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check betas variable - if (self.potentialData[i][j].betas == nil) then - error("minReg variable is missing on Region "..i.." LandUseType: "..luccMEModel.landUseTypes[j], 2) - end - - -- check betas within database - for k, lu in pairs (self.potentialData[i][j].betas) do - if (luccMEModel.cs.cells[1][k] == nil) then - error("Beta "..k.." on Region "..i.." LandUseType "..luccMEModel.landUseTypes[j].." not found within database", 2) - end - end - end -- for j - end -- for i - end -- else - - local filename = self.filename - - if (filename ~= nil) then - loadGALNeighborhood(filename) - else - cs:createNeighborhood() - end - end -- verify - - -- Handles with the modify method of a PotentialCSpatialLagRegression component. - -- @arg luccMEModel A LuccME model. - -- @arg rNumber The potential region number. - -- @arg luIndex A land use index (an specific luIndex of a list of possible land uses). - -- @arg direction The direction for the regression. - -- @usage --DONTRUN - -- component.modify(luccMEModel, j, i, luDirect) - component.modify = function(self, luccMEModel, rNumber, luIndex, direction) - local cs = luccMEModel.cs - local luData = self.potentialData[rNumber][luIndex] - - if luData.newconst == nil then - luData.newconst = 0 - end - - if (luData.isLog) then - local const_unlog = (10 ^ luData.newconst) + self.constChange * direction - - if (const_unlog ~= 0) then - luData.newconst = math.log(const_unlog, 10) - end - else - luData.newconst = luData.newconst + self.constChange * direction - end - - self:computePotential (luccMEModel, rNumber, luIndex) - end -- function modifyPotential - - -- Handles with the constants regression method of a PotentialCSpatialLagRegression component. - -- @arg demand A demand to calculate the potential. - -- @arg rNumber The potential region number. - -- @usage --DONTRUN - -- component.adaptRegressionConstants(demand, rNumbers) - component.adaptRegressionConstants = function(self, demand, rNumber) - for i, luData in pairs (self.potentialData[rNumber]) do - local currentDemand = demand:getCurrentLuDemand(i) - local previousDemand = demand:getPreviousLuDemand(i) - local plus = 0.01 * ((currentDemand - previousDemand) / previousDemand) - - luData.newconst = luData.const - - if (luData.isLog) then - local const_unlog = (10 ^ luData.newconst) + plus - - if (const_unlog ~= 0) then - luData.newconst = math.log (const_unlog, 10) - end - else - luData.newconst = luData.newconst + plus - end - - luData.newminReg = luData.newminReg + plus - luData.newmaxReg = luData.newmaxReg + plus - luData.const = luData.newconst - end - end -- function adaptRegressionConstants - - -- Modify potencial for an protected area. - -- @arg complementarLU Land use name. - -- @arg attrProtection The protetion attribute name. - -- @arg rate A rate for potencial multiplier. - -- @arg event A representation of a time instant when the simulation engine must run. - -- @arg luccMEModel A LuccME model. - -- @usage --DONTRUN - -- component.modifyDriver(self.complementarLU, self.attrProtection, 0.5, event, luccMEModel) - component.modifyDriver = function(self, complementarLU, attrProtection, rate, event, luccMEModel) - local regionsNumber = #luccMEModel.potential.potentialData - local luTypes = luccMEModel.landUseTypes - local luIndex = 1 - - for i, complementarLU in pairs (luTypes) do - if (complementarLU == luTypes[i]) then - luIndex = i - break - end - end - - for i = 1, regionsNumber, 1 do - local regressionNumber = #luccMEModel.potential.potentialData[i] - - for j = 1, regressionNumber, 1 do - if (luccMEModel.potential.potentialData[i][j].betas[attrProtection] ~= nil) then - luccMEModel.potential.potentialData[i][j].betas[attrProtection] = luccMEModel.potential.potentialData[i][j].betas[attrProtection] * rate - end - end - - self:computePotential(luccMEModel, i, luIndex) - end - end - - -- Handles with the compute potential method of a PotentialCSpatialLagRegression component. - -- @arg luccMEModel A LuccME model. - -- @arg rNumber The pontencial region number. - -- @arg luIndex A land use index (an specific luIndex of a list of possible land uses). - -- @usage --DONTRUN - -- component.computePotential(luccMEModel, rNumber, luIndex) - component.computePotential = function(self, luccMEModel, rNumber, luIndex) - local cs = luccMEModel.cs - local luTypes = luccMEModel.landUseTypes - local lu = luTypes[luIndex] - local luData = self.potentialData[rNumber][luIndex] - local pot = lu.."_pot" - local reg = lu.."_reg" - local activeRegionNumber = 0 - - for k,cell in pairs (cs.cells) do - if (cell.region == rNumber) then - activeRegionNumber = rNumber - - local regressionX = 0 - - for var, beta in pairs (luData.betas) do - regressionX = regressionX + beta * cell[var] - end - - --regresDrivers = regressionX - local regresY = 0 - local neighSum = 0 - local count = 0 - local neighY = 0 - local Y = 0 - - forEachNeighbor(cell, function(neigh, _, cell) - Y = cell.past[lu] - neighY = neigh.past[lu] - - if (cell[luccMEModel.landUseNoData] ~= 1) then - Y = Y / (1 - cell[luccMEModel.landUseNoData]) - end - - if (neigh[luccMEModel.landUseNoData] ~= 1) then - neighY = neighY / (1 - neigh[luccMEModel.landUseNoData]) - end - - if (neigh[luccMEModel.landUseNoData] < 1) then - count = count + 1 - neighSum = neighSum + neighY - end - end - ) - - if (count > 0) then - regresY = (Y + neighSum) / (count + 1) - else - regresY = Y - end - - local oldRegress = regresY - - if (luData.isLog) then -- if the land use is log transformed - regresY = math.log(regresY + 0.0001, 10) -- ANAP - end - - - regresY = regresY*luData.ro - - local regression = luData.newconst + regressionX + regresY - local regressionLimit = luData.const+ regressionX + regresY - - if (luData.isLog) then -- if the land use is log transformed - regression = (10 ^ regression) - 0.0001 - regressionLimit = (10 ^ regressionLimit) - 0.0001 - end - - local oldReg = regressionLimit - - if (regressionLimit > luData.maxReg) then - regression = 1 - end - - if (regressionLimit < luData.minReg) then - regression = 0 - end - - --if (regression > 1) then -- ANAP - -- regression = 1 - --end - - --if (regression < 0) then - -- regression = 0 - --end - - regression = regression * (1 - cell[luccMEModel.landUseNoData]) - cell[reg] = regression - cell[pot] = regression - cell.past[lu] - end -- if region - end -- for k - - if (activeRegionNumber == 0) then - error("Region ".. rNumber.." is not set into database.") - end - end -- function computePotential - - collectgarbage("collect") - return component -end -- PotentialCSpatialLagRegression diff --git a/tests/test_benchmark_discriminance_lab1.py b/tests/test_benchmark_discriminance_lab1.py deleted file mode 100644 index 8ce5816..0000000 --- a/tests/test_benchmark_discriminance_lab1.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -tests/test_benchmark_discriminance_lab1.py -============================================= -Measures the **discriminative power** of the Lab1 benchmark. Ported -from disslucc-continuous/tests/test_benchmark_discriminance.py, -raster-only (disslucc has no vector substrate). - -The question here is not "does the model reproduce TerraME?" but "can the -benchmark tell a correct implementation from an incorrect one?". Reproducing the -reference is necessary but not sufficient: if the test still passes with wrong -coefficients, it does not constrain the implementation. - -Known state (inherited from disslucc-continuous, 2026-07-27) --------------------------------------------------------------- -**Lab1 is discriminative.** Every perturbation tested on the regression -coefficients breaks the tolerance criterion, including zeroing the drivers and -halving the betas. A do-nothing baseline fails too. Unlike the Lab15 scenario -in disslucc-discrete, there is no trivial shortcut. - -On the convergence tolerance ------------------------------- -The original LuccME script (``lab1.lua`` / ``lab1_submodel.lua``) declares -``maxDifference = 1643`` in ``AllocationCClueLike`` -- in area units, against a -2014 demand of 21607.38 for class ``d``. That is a **7.6% convergence band**, -and the Python defaults match it exactly. - -This band is wide enough that the reference itself stops 1001.45 area units -short of the demand it declares, which is legitimate rather than a defect. It -also means Python and TerraME can each halt at different, equally valid points -inside the band -- which is why the fit varies non-monotonically with the -number of steps. See ``test_reference_gap_within_luccme_tolerance``. -""" -from __future__ import annotations - -import copy - -import geopandas as gpd -import numpy as np -import pytest -from _lab1_helpers import ( - CELL_AREA, - CSAC_ZIP, - DEFAULT_POTENTIAL_DATA, - LUCCME_DEMAND_D_2014, - LUCCME_MAX_DIFFERENCE, - TOLERANCE, - data_available, - load_terrame_reference, - run_lab1, -) - -skip_if_no_data = pytest.mark.skipif(not data_available, reason="Lab1 data files not found") - - -def _scaled_potential_data(factor: float) -> list[list]: - """Regression coefficients with every beta multiplied by ``factor``.""" - perturbed = copy.deepcopy(DEFAULT_POTENTIAL_DATA) - for spec in perturbed[0]: - if spec.betas: - spec.betas = {c: v * factor for c, v in spec.betas.items()} - return perturbed - - -# ══════════════════════════════════════════════════════════════════════════════ -# 1. Discriminance -- perturbing the coefficients must break the test -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -@pytest.mark.parametrize( - "factor,description", - [ - (0.0, "betas zeroed (no drivers)"), - (0.5, "betas halved"), - (2.0, "betas doubled"), - (-1.0, "betas sign-flipped"), - ], -) -def test_perturbed_coefficients_fail_tolerance(factor, description): - """With wrong coefficients the benchmark must fail. - - If it passed, the tolerance would be too loose to constrain the - implementation -- and the reported parity would carry no evidential weight. - """ - m = run_lab1(potential_data=_scaled_potential_data(factor)) - - assert m["mae"] >= TOLERANCE or m["rmse"] >= TOLERANCE, ( - f"With {description}, the benchmark still passed " - f"(MAE={m['mae']:.6f}, RMSE={m['rmse']:.6f}). " - "The tolerance does not constrain the implementation." - ) - - -@skip_if_no_data -def test_do_nothing_baseline_fails(): - """Keeping `d` at its initial value must fail the tolerance criterion.""" - gdf = gpd.read_file(str(CSAC_ZIP)) - ref = load_terrame_reference() - - d_initial = gdf["d"].values.astype(float) - d_out = ref["d_out"].values.astype(float) - mae = float(np.abs(d_initial - d_out).mean()) - - assert mae >= TOLERANCE, ( - f"The do-nothing baseline has MAE={mae:.6f}, below the {TOLERANCE} " - "tolerance. The benchmark would be satisfied without simulating anything." - ) - - -# ══════════════════════════════════════════════════════════════════════════════ -# 2. Characterisation -- what the benchmark does NOT cover -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -def test_match_pct_is_reported_alongside_mae(): - """A low MAE coexists with a sizeable tail outside tolerance. - - This test imposes no floor on `match_pct` -- it records that the two numbers - tell different stories, and that reporting MAE alone overstates agreement. - """ - m = run_lab1() - - assert m["mae"] < TOLERANCE, "regression: the model no longer meets the criterion" - assert m["match_pct"] < 100.0, ( - "If match_pct reached 100%, cell-by-cell agreement can be claimed -- it " - "currently is not exact." - ) - - -@skip_if_no_data -def test_pontius_identity_holds(): - """quantity + allocation must reproduce MAE exactly.""" - m = run_lab1() - total = m["quantity_disagreement"] + m["allocation_disagreement"] - assert total == pytest.approx(m["mae"], abs=1e-12), ( - f"Pontius identity violated ({total:.9f} != {m['mae']:.9f})" - ) - assert m["allocation_disagreement"] >= -1e-12, "negative allocation disagreement -- impossible" - - -# ══════════════════════════════════════════════════════════════════════════════ -# 3. Convergence tolerance -- why the fit varies with the number of steps -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -def test_reference_gap_within_luccme_tolerance(): - """The reference falls short of its declared demand -- legitimately. - - ``AllocationCClueLike`` in the original script declares - ``maxDifference = 1643`` area units. The reference stops ~1001 units short - of the 2014 demand for ``d``, which is inside that band. This is correct - behaviour, not a defect in the reference. - """ - ref = load_terrame_reference() - ref_area = float(ref["d_out"].values.astype(float).sum()) * CELL_AREA - gap = abs(ref_area - LUCCME_DEMAND_D_2014) - - assert gap < LUCCME_MAX_DIFFERENCE, ( - f"The reference misses its declared demand by {gap:.2f} area units, " - f"beyond the maxDifference={LUCCME_MAX_DIFFERENCE} declared in the " - "LuccME script. That would indicate a genuine problem in the reference " - "rather than ordinary convergence slack." - ) - - -@skip_if_no_data -def test_quantity_error_dominates_allocation_error(): - """Most of the residual is *how much*, not *where*. - - At the official configuration the error decomposes into roughly 90% - quantity and 10% allocation: the model places deforestation in nearly the - right cells and misses on the total. If this ever inverts -- allocation - dominating -- the cause is spatial and warrants investigation, because the - convergence band does not explain it. - """ - m = run_lab1() - assert m["quantity_disagreement"] > m["allocation_disagreement"], ( - f"Allocation disagreement ({m['allocation_disagreement']:.6f}) now " - f"exceeds quantity ({m['quantity_disagreement']:.6f}). The residual is " - "spatial and is no longer explained by the convergence tolerance." - ) diff --git a/tests/test_benchmark_discriminance_lab15.py b/tests/test_benchmark_discriminance_lab15.py deleted file mode 100644 index 12e6e4d..0000000 --- a/tests/test_benchmark_discriminance_lab15.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -tests/test_benchmark_discriminance_lab15.py -=============================================== -Measures the **discriminative power** of the Lab15 benchmark. Ported -from disslucc-discrete/tests/test_benchmark_discriminance.py, adapted -from the vector substrate to disslucc's raster port. - -The question these tests answer is not "does the model reproduce TerraME?" but -"can the benchmark tell a correct CLUE-S-like implementation from one that does -not implement CLUE-S at all?". - -Reproducing the reference is necessary but not sufficient. If a trivial baseline -reaches the same parity, the benchmark is not measuring the algorithm. - -Known state (inherited from disslucc-discrete, 2026-07-27) --------------------------------------------------------------- -The naive baseline -- a static ranking by ``prob_d - prob_f``, with no CLUE-S, no -iteration and no time steps -- reproduces the TerraME output **exactly**, cell for -cell, 5914/5914. The Lab15 scenario therefore validates only the transcription of -the logistic regression coefficients, **not** the allocation algorithm. - -That is why ``test_benchmark_is_discriminative`` and -``test_trajectory_affects_result`` are marked as strict xfail, same as upstream. -If either ever XPASSes, the scenario has stopped being trivially reducible -- -that is an improvement, and the marker should be removed. -""" -from __future__ import annotations - -import pytest -from _lab15_helpers import ( - ANNUAL_DEMAND, - DEFAULT_POTENTIAL_DATA, - data_available, - load_gdf_input, - load_gdf_terrame, - run_lab15_raster, -) - -from disslucc.validation.naive_baseline import naive_allocation - -skip_if_no_data = pytest.mark.skipif(not data_available, reason="Lab15 data files not found") - - -def _reference_binary(gdf_terrame): - col = "d_out" if "d_out" in gdf_terrame.columns else "d" - return (gdf_terrame[col].values >= 0.5).astype(int) - - -# ══════════════════════════════════════════════════════════════════════════════ -# 1. Characterisation -- does the naive baseline reproduce the reference? -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -def test_naive_baseline_reproduces_terrame(): - """Records that a trivial static ranking already reproduces TerraME. - - This is NOT a requirement on the model -- it characterises a limitation of - the benchmark scenario. If it ever fails, the scenario has stopped being - trivially reducible, which would be an improvement. - """ - gdf_input = load_gdf_input() - gdf_terrame = load_gdf_terrame() - ref = _reference_binary(gdf_terrame) - - pred = naive_allocation(gdf_input, n_target=int(ref.sum()), potential_data=DEFAULT_POTENTIAL_DATA) - agreement = float((pred == ref).mean()) * 100 - - assert agreement == pytest.approx(100.0, abs=1e-9), ( - f"The naive baseline agrees with TerraME on {agreement:.4f}% of cells. " - "If this is no longer 100%, the scenario has changed -- revisit " - "test_benchmark_is_discriminative, which may have stopped being xfail." - ) - - -# ══════════════════════════════════════════════════════════════════════════════ -# 2. Discriminance -- does the benchmark tell CLUE-S-like from non-CLUE-S? -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -@pytest.mark.xfail( - strict=True, - reason=( - "Known limitation, inherited from disslucc-discrete: in the Lab15 " - "scenario every covariate is static, the elasticity of f is 0.0 and d " - "is irreversible, so allocation collapses into a static threshold. The " - "naive baseline ties with CLUE-S-like. Making the benchmark " - "discriminative requires a scenario with a dynamic covariate (for " - "example, distance to deforestation updated every step), a reversible " - "d->f transition, or multiple regions." - ), -) -def test_benchmark_is_discriminative(): - """Full raster CLUE-S-like should outperform the naive baseline. - - While the two tie, the benchmark does not constrain the allocation - algorithm -- it merely checks that the regression coefficients were - transcribed correctly. - """ - gdf_input = load_gdf_input() - gdf_terrame = load_gdf_terrame() - ref_full = _reference_binary(gdf_terrame) - - backend, rows, cols = run_lab15_raster() - pred_clue = (backend.get("d")[rows, cols] >= 0.5).astype(int) - - # Align the raster result back onto gdf_input's row order via (lin, col). - import pandas as pd - idx_pred = pd.MultiIndex.from_arrays([rows, cols], names=["lin", "col"]) - s_pred = pd.Series(pred_clue, index=idx_pred) - idx_terrame = pd.MultiIndex.from_arrays( - [gdf_terrame["lin"].astype(int).values, gdf_terrame["col"].astype(int).values], - names=["lin", "col"], - ) - aligned_pred = s_pred.reindex(idx_terrame).values - agreement_clue = float((aligned_pred == ref_full).mean()) * 100 - - pred_naive = naive_allocation(gdf_input, n_target=int(ref_full.sum()), potential_data=DEFAULT_POTENTIAL_DATA) - agreement_naive = float((pred_naive == ref_full).mean()) * 100 - - assert agreement_clue > agreement_naive, ( - f"CLUE-S-like={agreement_clue:.4f}% vs naive baseline={agreement_naive:.4f}%. " - "The benchmark cannot tell the algorithm from a trivial static ranking." - ) - - -# ══════════════════════════════════════════════════════════════════════════════ -# 3. Sensitivity -- does the demand trajectory influence the result? -# ══════════════════════════════════════════════════════════════════════════════ - -@skip_if_no_data -@pytest.mark.xfail( - strict=True, - reason=( - "Known limitation, inherited from disslucc-discrete: because d is " - "irreversible and demand is monotonic, the final state depends only on " - "the final demand, not on the path. Changing the intermediate " - "trajectory does not change the result." - ), -) -def test_trajectory_affects_result(): - """A different demand trajectory should produce a different result. - - If the final state is identical whether running 6 steps or 2 steps with an - invented intermediate demand, the benchmark validates no temporal dynamics - at all. - """ - backend_official, rows, cols = run_lab15_raster(annual_demand=ANNUAL_DEMAND[:6]) - official = (backend_official.get("d")[rows, cols] >= 0.5).astype(int) - - # Same final demand, arbitrary intermediate path. - first, last = ANNUAL_DEMAND[0], ANNUAL_DEMAND[5] - midpoint = [(a + b) / 2 for a, b in zip(first, last)] - backend_alt, rows_alt, cols_alt = run_lab15_raster(annual_demand=[first, midpoint, last]) - alternative = (backend_alt.get("d")[rows_alt, cols_alt] >= 0.5).astype(int) - - differing = int((official != alternative).sum()) - assert differing > 0, ( - "The intermediate trajectory does not affect the final result " - f"({differing} differing cells). The benchmark validates no temporal " - "dynamics." - ) diff --git a/tests/test_executor_saturation.py b/tests/test_executor_saturation.py deleted file mode 100644 index b1609fc..0000000 --- a/tests/test_executor_saturation.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -LuccSaturationExecutor through the local CLI, with the example TOML -(examples/dissmodel-configs/lucc_saturation.toml = lab03), against the lab03 -golden: the path a model like LuccME-BR is run by. - -csAC goes in as a GeoTIFF with named bands (land uses, drivers, mask, the -cells' order), written with dissmodel's save_geotiff; the CLI writes the last -step and `save_steps`; both are compared with the golden. -""" -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -import numpy as np -import pytest -from _lab03_helpers import LAND_USES, Lab -from dissmodel.geo.raster.backend import RasterBackend -from dissmodel.io import load_dataset -from dissmodel.io.raster import save_geotiff -from rasterio.transform import from_origin - -ROOT = Path(__file__).resolve().parent.parent -TOML = ROOT / "examples" / "dissmodel-configs" / "lucc_saturation.toml" -DEMAND_CSV = ROOT / "data" / "input" / "examples_demand_lab1.csv" # lab03's demand table, same values -TOL = 1e-9 - - -@pytest.fixture(scope="module") -def run_cli(tmp_path_factory): - lab = Lab("lab03") - tmp = tmp_path_factory.mktemp("saturation") - backend = RasterBackend(shape=lab.shape) - for lu in LAND_USES: - backend.set(lu, lab.initial(lu)) - for name, arr in lab.drivers(2008).items(): - backend.set(name, arr) - backend.set("mask", lab.valid.astype(np.float64)) - backend.set("order", lab.grid(np.arange(len(lab.cells)))) - names = sorted(backend.arrays) - transform = from_origin(0, lab.shape[0] * 5000.0, 5000.0, 5000.0) # any georeference: 5 km cells - cellspace = tmp / "csAC.tif" - save_geotiff( - (backend, {"crs": "EPSG:5880", "transform": transform}), - str(cellspace), - band_spec=[(n, "float64", -9999.0) for n in names], - ) - subprocess.run( - [sys.executable, "-m", "disslucc.executors.saturation", "run", "--toml", str(TOML), - "--input", str(cellspace), "--param", f"demand_csv={DEMAND_CSV}", "--output", str(tmp / "lab03.tif")], - check=True, capture_output=True, text=True, cwd=ROOT, - ) - # the CLI adds the experiment id to the name: lab03_.tif, lab03__step3.tif - outputs = {p.name: p for p in tmp.glob("lab03_*.tif")} - final = next(p for n, p in outputs.items() if "_step" not in n) - return lab, {6: final, 3: final.with_name(f"{final.stem}_step3.tif")} - - -def read(path: Path) -> dict[str, np.ndarray]: - (backend, _), _ = load_dataset(str(path), fmt="raster") - return backend.arrays - - -@pytest.mark.parametrize("step", [3, 6]) -def test_cli_run_matches_golden(run_cli, step): - lab, paths = run_cli - arrays = read(paths[step]) - ref = lab.golden_year(2008 + step) - r = ref.index.get_level_values("row").values - c = ref.index.get_level_values("col").values - for lu in LAND_USES: - err = np.abs(arrays[lu][r, c] - ref[f"{lu}_out"].values).max() - assert err < TOL, f"{2008 + step} {lu}: {err:.3e}" - - -def test_demand_csv_is_lab03s(): - """The example reuses examples_demand_lab1.csv: it holds lab03's demand table.""" - from _lab03_helpers import DEMAND - - from disslucc.components.demand import load_demand_csv - - assert load_demand_csv(DEMAND_CSV.read_text(), LAND_USES) == DEMAND diff --git a/tests/test_executor_toml.py b/tests/test_executor_toml.py new file mode 100644 index 0000000..473242f --- /dev/null +++ b/tests/test_executor_toml.py @@ -0,0 +1,121 @@ +""" +The example TOML files (examples/dissmodel-configs/) through the local CLI. + +What a modeler writes in TOML and what a script builds by hand are the same +experiment: the CLI run and the hand-built ExperimentRecord produce the same +output file (same SHA-256 in `record.artifacts["output"]`). Numerical agreement +with TerraME is not tested here; it lives in LambdaGeo/disslucc-benchmark. +""" +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +import geopandas as gpd +import numpy as np +from dissmodel.executor import ExperimentRecord +from dissmodel.executor.runner import execute_lifecycle +from dissmodel.geo.raster.backend import RasterBackend +from dissmodel.io.raster import save_geotiff +from rasterio.transform import from_origin + +from disslucc.executors import LuccContinuousExecutor + +ROOT = Path(__file__).resolve().parent.parent +CONFIGS = ROOT / "examples" / "dissmodel-configs" +INPUT = ROOT / "data" / "input" + + +def run_cli(module: str, toml: str, source: Path, demand: Path, out: Path) -> dict: + out.parent.mkdir(parents=True, exist_ok=True) + subprocess.run( + [sys.executable, "-m", f"disslucc.executors.{module}", "run", + "--toml", str(CONFIGS / toml), "--input", str(source), + "--param", f"demand_csv={demand}", "--output", str(out)], + check=True, capture_output=True, text=True, cwd=ROOT, + ) + (record_path,) = out.parent.glob("*.record.json") + return json.loads(record_path.read_text()) + + +def test_continuous_toml_equals_hand_built_record(tmp_path): + cli = run_cli("continuous", "lucc_continuous.toml", INPUT / "csAC.zip", + INPUT / "examples_demand_lab1.csv", tmp_path / "cli" / "result.tif") + + record = ExperimentRecord( + model_name=LuccContinuousExecutor.name, + source={"uri": str(INPUT / "csAC.zip")}, + parameters={ + "land_use_types": ["f", "d", "outros"], + "complementar_lu": "f", + "demand_csv": str(INPUT / "examples_demand_lab1.csv"), + "land_use_no_data": "outros", + "static": {"f": -1, "d": -1, "outros": 1}, + "cell_area": 25.0, + "n_steps": 7, + "resolution": 5000.0, + "potential_data": [ + {"const": 0.7392, "betas": { + "assentamen": -0.2193, "uc_us": 0.1754, "uc_pi": 0.09708, + "ti": 0.1207, "dist_riobr": 0.0000002388, "fertilidad": -0.1313}}, + {"const": 0.267, "betas": { + "rodovias": -0.0000009922, "assentamen": 0.2294, + "uc_us": -0.09867, "dist_riobr": -0.0000003216, "fertilidad": 0.1281}}, + {"const": 0.0}, + ], + "allocation_data": [ + {"static": -1, "min_value": 0, "max_value": 1, "min_change": 0, "max_change": 1}, + {"static": -1, "min_value": 0, "max_value": 1, "min_change": 0, "max_change": 1}, + {"static": 1, "min_value": 0, "max_value": 1, "min_change": 0, "max_change": 1}, + ], + }, + ) + hand, _ = execute_lifecycle(LuccContinuousExecutor(), record) + + assert cli["status"] == hand.status == "completed" + assert cli["source"]["checksum"] == hand.source.checksum + assert cli["artifacts"]["output"] == hand.artifacts["output"] + + +def test_discrete_toml_runs_and_is_reproducible(tmp_path): + runs = [run_cli("discrete", "lucc_discrete.toml", INPUT / "cs_moju.zip", + INPUT / "demand_moju.csv", tmp_path / f"run{i}" / "result.tif") + for i in (1, 2)] + assert all(r["status"] == "completed" for r in runs) + assert runs[0]["artifacts"]["output"] == runs[1]["artifacts"]["output"] + + +def _saturation_input(path: Path) -> None: + """csAC as a GeoTIFF with named bands: land uses, drivers, mask, cell order.""" + cells = gpd.read_file(INPUT / "csAC.zip") + rows, cols = cells["row"].astype(int).values, cells["col"].astype(int).values + shape = (rows.max() + 1, cols.max() + 1) + + def grid(values) -> np.ndarray: + a = np.zeros(shape) + a[rows, cols] = np.asarray(values, dtype=np.float64) + return a + + bands = {c: grid(cells[c].astype(float).values) for c in + ["f", "d", "outros", "uc_us", "uc_pi", "ti", "assentamen", "fertilidad", "dist_riobr"]} + bands["mask"] = grid(np.ones(len(cells))) + bands["order"] = grid(np.arange(len(cells))) + backend = RasterBackend(shape=shape) + for name, arr in bands.items(): + backend.set(name, arr) + save_geotiff( + (backend, {"crs": "EPSG:5880", "transform": from_origin(0, shape[0] * 5000.0, 5000.0, 5000.0)}), + str(path), band_spec=[(n, "float64", -9999.0) for n in sorted(backend.arrays)], + ) + + +def test_saturation_toml_runs_and_is_reproducible(tmp_path): + src = tmp_path / "csAC.tif" + _saturation_input(src) + runs = [run_cli("saturation", "lucc_saturation.toml", src, + INPUT / "examples_demand_lab1.csv", tmp_path / f"run{i}" / "result.tif") + for i in (1, 2)] + assert all(r["status"] == "completed" for r in runs) + assert runs[0]["artifacts"]["output"] == runs[1]["artifacts"]["output"] diff --git a/tests/test_goldens_per_year.py b/tests/test_goldens_per_year.py deleted file mode 100644 index e8a0761..0000000 --- a/tests/test_goldens_per_year.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -tests/test_goldens_per_year.py --------------------------------- -Year-by-year comparison against the LuccME goldens in benchmark/goldens/ -(TerraME 2.0.1 + LuccME 6244dd4, generated with LambdaGeo/terrame-docker). - -For each scenario, every simulated year is checked, not only the last one: - * the convergence-loop iteration count per year must equal TerraME's - (an integer that is very sensitive to the loop: elasticity/iter_vec updates); - * `_out` and `_pot` of every class must match (MAE per year < 1e-6; - what remains is float32 noise, ~1e-8). - -Scenarios: the package labs (lab01: maxDifference 5000, lab15: 300), whose -allocation is accepted at the first pass every year, and the variants -lab01_md1643 / lab15_md10 (terrame-docker's benchmark/references/), which iterate -8-26 and 56-67 times per year and so exercise the convergence loop. - -Continuous (CClueLike): LuccME's correctCellChange never runs. Its guard is -`if (cell.regionregionAloc == rNumber)` (AllocationCClueLike.lua:503, a typo for -regionAloc), which is always false. disslucc runs the correction by default -(`cell_correction=True`, the intended algorithm); with `cell_correction=False` it -reproduces TerraME in every year and every iteration count, which is what the -comparison tests below use. -""" -from __future__ import annotations - -import _lab1_helpers as L1 -import _lab15_helpers as L15 -import geopandas as gpd -import pytest -from _golden_helpers import ( - YearRecorder, - golden_available, - golden_iterations, - load_golden, - per_year_frame, - per_year_mae, -) -from dissmodel.core import Environment - -from disslucc import ( - AllocationClueLike, - AllocationDClueSLike, - DemandPreComputedValues, - PotentialDLogisticRegression, - PotentialLinearRegression, -) -from disslucc.components.demand import load_demand_csv - -MAE_TOL = 1e-6 - - -def run_lab1(max_difference: float, cell_correction: bool): - gdf = gpd.read_file(L1.CSAC_ZIP) - backend, rows, cols = L1.build_backend_by_rowcol(gdf) - annual_demand = load_demand_csv(L1.DEMAND_CSV.read_text(), L1.LAND_USE_TYPES) - env = Environment(end_time=L1.N_STEPS - 1) - - demand = DemandPreComputedValues(annual_demand=annual_demand, land_use_types=L1.LAND_USE_TYPES) - potential = PotentialLinearRegression( - backend=backend, demand=demand, land_use_types=L1.LAND_USE_TYPES, - land_use_no_data="outros", potential_data=L1.DEFAULT_POTENTIAL_DATA, - ) - allocation = AllocationClueLike( - backend=backend, demand=demand, potential=potential, - land_use_types=L1.LAND_USE_TYPES, - static={"f": -1, "d": -1, "outros": 1}, - complementar_lu="f", cell_area=L1.CELL_AREA, - allocation_data=L1.DEFAULT_ALLOCATION_DATA, - max_difference=max_difference, - cell_correction=cell_correction, - ) - recorder = YearRecorder(backend=backend, rows=rows, cols=cols, land_use_types=L1.LAND_USE_TYPES) - env.run() - return allocation, per_year_frame(recorder, list(range(2008, 2015))) - - -def run_lab15(max_difference: float): - gdf = L15.load_gdf_input() - backend, rows, cols = L15.build_backend_by_rowcol(gdf) - env = Environment(end_time=len(L15.ANNUAL_DEMAND) - 1) - - demand = DemandPreComputedValues(annual_demand=L15.ANNUAL_DEMAND, land_use_types=L15.LAND_USE_TYPES) - PotentialDLogisticRegression( - backend=backend, potential_data=L15.DEFAULT_POTENTIAL_DATA, land_use_types=L15.LAND_USE_TYPES, - ) - allocation = AllocationDClueSLike( - backend=backend, demand=demand, land_use_types=L15.LAND_USE_TYPES, - transition_matrix=L15.TRANSITION_MATRIX, cell_area=L15.CELL_AREA, - max_difference=max_difference, max_iteration=1000, factor_iteration=0.0001, - ) - recorder = YearRecorder(backend=backend, rows=rows, cols=cols, land_use_types=L15.LAND_USE_TYPES) - env.run() - return allocation, per_year_frame(recorder, list(range(1999, 2005))) - - -def assert_matches_golden(name: str, allocation, ours) -> None: - golden, manifest = load_golden(name) - - assert allocation.iterations_per_step == golden_iterations(manifest), ( - f"{name}: iterations per year differ from TerraME -- " - f"disslucc {allocation.iterations_per_step}, TerraME {golden_iterations(manifest)}" - ) - for column in ours.columns: - mae = per_year_mae(ours, golden, column) - worst_year = max(mae, key=lambda y: mae[y]) - assert mae[worst_year] < MAE_TOL, ( - f"{name}: {column} diverges from TerraME, first in " - f"{min(y for y, v in mae.items() if v >= MAE_TOL)} (MAE per year: {mae})" - ) - - -# ── discrete (CLUE-S-like) ──────────────────────────────────────────────────── - -@pytest.mark.skipif(not L15.data_available, reason="Lab15 input data not found") -@pytest.mark.parametrize("name, max_difference", [("lab15", 300.0), ("lab15_md10", 10.0)]) -def test_lab15_matches_terrame_every_year(name: str, max_difference: float) -> None: - if not golden_available(name): - pytest.skip(f"golden {name} not found") - allocation, ours = run_lab15(max_difference) - assert_matches_golden(name, allocation, ours) - - -# ── continuous (CLUE-like) ──────────────────────────────────────────────────── - -@pytest.mark.skipif(not L1.data_available, reason="Lab1 input data not found") -@pytest.mark.parametrize("name, max_difference", [("lab01", 5000.0), ("lab01_md1643", 1643.0)]) -def test_lab1_matches_terrame_every_year_without_cell_correction(name: str, max_difference: float) -> None: - if not golden_available(name): - pytest.skip(f"golden {name} not found") - allocation, ours = run_lab1(max_difference, cell_correction=False) - assert_matches_golden(name, allocation, ours) - - -@pytest.mark.skipif(not L1.data_available, reason="Lab1 input data not found") -def test_lab1_default_cell_correction_deviates_from_terrame() -> None: - """Characterisation, not a requirement: the default (`cell_correction=True`, - the intended algorithm) deliberately differs from TerraME, which never runs - correctCellChange. Pins the size of that deviation so a change to the - correction step, or to the default, does not go unnoticed. The final-year - numbers are the ones in docs/validation.md (MAE 0.003583).""" - if not golden_available("lab01_md1643"): - pytest.skip("golden lab01_md1643 not found") - allocation, ours = run_lab1(1643.0, cell_correction=True) - golden, _ = load_golden("lab01_md1643") - - assert allocation.iterations_per_step == [0, 0, 0, 14, 17, 16, 16] - mae = per_year_mae(ours, golden, "d_out") - assert mae[2008] == pytest.approx(0.0, abs=MAE_TOL) - assert mae[2009] > MAE_TOL # the correction acts from the first year with change - assert mae[2014] == pytest.approx(0.003583, abs=5e-7) diff --git a/tests/test_lua_differential.py b/tests/test_lua_differential.py deleted file mode 100644 index ad7603a..0000000 --- a/tests/test_lua_differential.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -The ports against the original LuccME Lua code, run as it is (lupa), on -synthetic cases built to reach what the lab03/lab06 goldens never do: - -- ``correctCellChange`` — in those labs no cell ever needs correcting; -- the saturation branch of ``computeChange`` and ``updateAllocationParameters`` - — those labs set ``changeLimiarValue = 1``; -- ``computePotential`` with log-transformed classes, isolated cells and cells - whose neighbours are all no-data — csAC has none. - -tests/lua/ holds the two Lua files unchanged (LuccME 6244dd4, luccme/lua/ of -terrame-docker v0.1.1) — provisional, see docs/decisions.md. The TerraME -functions they call are stubbed below: ``forEachCell``, ``forEachNeighbor`` (the -neighbourhoods are built here, as TerraME's ``createNeighborhood`` would: Moore -without the cell for the potential, 3 × 3 with the cell for "11x11"), ``belong``. -""" - -from __future__ import annotations - -from pathlib import Path - -import numpy as np -import pytest - -from disslucc.components.allocation.saturation import ( - compute_change, - correct_cell_change, - saturation_indicator, -) -from disslucc.components.potential.spatial_lag import spatial_lag_regression -from disslucc.schemas import SaturationAllocationSpec, SpatialLagRegressionSpec - -lupa = pytest.importorskip("lupa") - -LUA = Path(__file__).resolve().parent / "lua" -TOL = 1e-12 -STUBS = """ -print = function() end -function forEachCell(cs, f) for _, c in ipairs(cs.cells) do f(c) end end -function forEachNeighbor(cell, name, f) - if type(name) == "function" then f = name; name = "1" end - for _, n in ipairs(cell.neighborhoods[name]) do f(n, 1, cell) end -end -function belong(v, t) for _, x in ipairs(t) do if x == v then return true end end return false end -function makeDemand(dirs) - local d = {dirs = dirs} - function d:getCurrentLuDirection(i) return self.dirs[i] end - return d -end -function makeEvent(t) local e = {t = t}; function e:getTime() return self.t end; return e end -""" - - -@pytest.fixture(scope="module") -def lua(): - rt = lupa.LuaRuntime(unpack_returned_tuples=True) - rt.execute(STUBS) - for name in ("AllocationCClueLikeSaturation.lua", "PotentialCSpatialLagRegression.lua"): - rt.execute((LUA / name).read_text()) - return rt - - -def table(lua, obj): - return lua.table_from(obj, recursive=True) - - -def lua_allocation(lua, specs: list[SaturationAllocationSpec], **extra): - data = [ - { - "static": s.static, - "minValue": s.min_value, - "maxValue": s.max_value, - "minChange": s.min_change, - "maxChange": s.max_change, - "changeLimiarValue": s.change_limiar_value, - "maxChangeAboveLimiar": s.max_change_above_limiar, - } - for s in specs - ] - return lua.globals().AllocationCClueLikeSaturation(table(lua, {"allocationData": [data], **extra})) - - -class Grid: - """A small raster with holes; the cells in row-major order are LuccME's cells.""" - - def __init__(self, rng, shape=(24, 30), holes=0.15, isolated=4): - self.shape = shape - self.valid = rng.random(shape) > holes - # a few isolated cells: a valid cell with every neighbour removed - for _ in range(isolated): - r, c = rng.integers(2, shape[0] - 2), rng.integers(2, shape[1] - 2) - self.valid[r - 1 : r + 2, c - 1 : c + 2] = False - self.valid[r, c] = True - self.rows, self.cols = np.nonzero(self.valid) - - def neighbourhoods(self, lua, cells): - index = {(r, c): k for k, (r, c) in enumerate(zip(self.rows, self.cols, strict=True))} - for k, (r, c) in enumerate(zip(self.rows, self.cols, strict=True)): - moore, window = [], [] - for dr in (-1, 0, 1): - for dc in (-1, 0, 1): - j = index.get((r + dr, c + dc)) - if j is None: - continue - window.append(cells[j]) - if (dr, dc) != (0, 0): - moore.append(cells[j]) - cells[k].neighborhoods = table(lua, {"1": moore, "11x11": window}) - - -def random_specs(rng, n) -> list[SaturationAllocationSpec]: - specs = [] - for i in range(n): - lo = float(rng.choice([0.0, 0.0, 0.05, 0.2])) - hi = float(rng.choice([1.0, 1.0, 0.8, 0.6])) - specs.append( - SaturationAllocationSpec( - static=int(rng.choice([-1, -1, 0, 1])) if i else -1, - min_value=lo, - max_value=hi, - min_change=float(rng.choice([0.0, 0.01])), - max_change=float(rng.choice([1.0, 0.3, 0.1])), - change_limiar_value=float(rng.uniform(0.2, 0.8)), - max_change_above_limiar=float(rng.uniform(0.0, 0.1)), - ) - ) - return specs - - -# ── correctCellChange ────────────────────────────────────────────────────── - - -@pytest.mark.parametrize("seed", range(12)) -def test_correct_cell_change_matches_lua(lua, seed): - rng = np.random.default_rng(seed) - n_cells, n_lu = 300, int(rng.integers(3, 5)) - lus = [f"c{i}" for i in range(n_lu)] - specs = random_specs(rng, n_lu) - directions = [int(d) for d in rng.choice([-1, 0, 1], size=n_lu)] - - past = rng.dirichlet(np.ones(n_lu), size=n_cells) - step = rng.normal(0, 0.08, size=(n_cells, n_lu)) - same_way = rng.random(n_cells) < 0.3 # every class moving the same way: the "shift" branch - step[same_way] = np.abs(step[same_way]) * rng.choice([-1, 1], size=(same_way.sum(), 1)) - values = np.clip(past + step, 0, 1) - - cells = [] - for k in range(n_cells): - cell = {lu: float(values[k, i]) for i, lu in enumerate(lus)} - cell["past"] = {lu: float(past[k, i]) for i, lu in enumerate(lus)} - cell["regionAloc"] = 1 - cells.append(table(lua, cell)) - model = table(lua, {"cs": {"cells": cells}, "landUseTypes": lus}) - model.demand = lua.globals().makeDemand(table(lua, directions)) - comp = lua_allocation(lua, specs) - comp.correctCellChange(comp, model, 1) - theirs = np.array([[cells[k][lu] for lu in lus] for k in range(n_cells)]) - - stats: dict = {} - ours = correct_cell_change(values, past, specs, directions, stats) - assert np.abs(ours - theirs).max() < TOL, stats - assert stats["need"] > 0 - - -def test_correct_cell_change_cases_reach_every_branch(lua): - """The random cases above are only worth something if they reach the branches.""" - total: dict = {} - for seed in range(12): - rng = np.random.default_rng(seed) - n_lu = int(rng.integers(3, 5)) - specs = random_specs(rng, n_lu) - past = rng.dirichlet(np.ones(n_lu), size=300) - step = rng.normal(0, 0.08, size=(300, n_lu)) - same_way = rng.random(300) < 0.3 - step[same_way] = np.abs(step[same_way]) * rng.choice([-1, 1], size=(same_way.sum(), 1)) - values = np.clip(past + step, 0, 1) - correct_cell_change(values, past, specs, [int(d) for d in rng.choice([-1, 0, 1], size=n_lu)], total) - assert total["need"] > 1000 - assert total["shift"] > 100 - assert total["backp_lowered"] > 100 - assert total["several_rounds"] > 10 - - -# ── computeChange with saturation ────────────────────────────────────────── - - -@pytest.mark.parametrize("seed", range(8)) -def test_compute_change_matches_lua(lua, seed): - rng = np.random.default_rng(100 + seed) - n_cells, n_lu = 400, 3 - lus = ["f", "d", "o"] - specs = random_specs(rng, n_lu) - directions = [int(d) for d in rng.choice([-1, 1], size=n_lu)] - elasticity = [float(e) for e in rng.uniform(0.05, 1.5, size=n_lu)] - past = rng.dirichlet(np.ones(n_lu), size=n_cells) - pot = rng.normal(0, 0.3, size=(n_cells, n_lu)) - pot[rng.random((n_cells, n_lu)) < 0.05] = 0.0 - saturation = rng.random(n_cells) - - cells = [] - for k in range(n_cells): - cell = {f"{lu}_pot": float(pot[k, i]) for i, lu in enumerate(lus)} - cell.update({lu: float(past[k, i]) for i, lu in enumerate(lus)}) - cell["past"] = {lu: float(past[k, i]) for i, lu in enumerate(lus)} - cell["regionAloc"] = 1 - cell["sat"] = float(saturation[k]) - cells.append(table(lua, cell)) - model = table(lua, {"cs": {"cells": cells}, "landUseTypes": lus}) - model.demand = lua.globals().makeDemand(table(lua, directions)) - comp = lua_allocation(lua, specs, saturationIndicator="sat") - comp.elasticity = table(lua, elasticity) - comp.computeChange(comp, model, 1) - - saturated = 0 - for i, lu in enumerate(lus): - theirs = np.array([cells[k][lu] for k in range(n_cells)]) - ours = compute_change(past[:, i], pot[:, i], elasticity[i], specs[i], directions[i], saturation) - assert np.abs(ours - theirs).max() < TOL, (lu, specs[i]) - saturated += int((saturation > specs[i].change_limiar_value).sum()) - assert saturated > 0 - - -# ── updateAllocationParameters (saturation indicator) ────────────────────── - - -@pytest.mark.parametrize("seed", range(6)) -def test_saturation_indicator_matches_lua(lua, seed): - rng = np.random.default_rng(200 + seed) - grid = Grid(rng) - shape = grid.shape - comp_share = rng.random(shape) - no_data = np.where(rng.random(shape) < 0.1, 1.0, rng.random(shape) * 0.3) - protection = np.where(rng.random(shape) < 0.2, rng.random(shape) * 0.8, 0.0) - - cells = [] - for r, c in zip(grid.rows, grid.cols, strict=True): - cells.append( - table( - lua, - {"f": float(comp_share[r, c]), "nd": float(no_data[r, c]), "prot": float(protection[r, c])}, - ) - ) - grid.neighbourhoods(lua, cells) - model = table(lua, {"cs": {"cells": cells}, "landUseNoData": "nd"}) - comp = lua_allocation( - lua, - [SaturationAllocationSpec()], - saturationIndicator="sat", - attrProtection="prot", - complementarLU="f", - ) - comp.updateAllocationParameters(comp, lua.globals().makeEvent(2000), model) - - theirs = np.array([cell["sat"] for cell in cells]) - ours = saturation_indicator(comp_share, grid.valid, no_data, protection)[grid.rows, grid.cols] - assert np.abs(ours - theirs).max() < TOL - - -# ── computePotential ─────────────────────────────────────────────────────── - - -@pytest.mark.parametrize("is_log", [False, True]) -@pytest.mark.parametrize("seed", range(4)) -def test_spatial_lag_potential_matches_lua(lua, seed, is_log): - rng = np.random.default_rng(300 + seed) - grid = Grid(rng) - shape = grid.shape - share = rng.random(shape) - no_data = np.where(rng.random(shape) < 0.25, 1.0, rng.random(shape) * 0.4) # many all-no-data cells - drivers = {"x1": rng.random(shape), "x2": rng.normal(0, 1, shape)} - spec = SpatialLagRegressionSpec( - const=float(rng.normal(0, 0.2)), - ro=float(rng.uniform(0.5, 1.0)), - betas={"x1": float(rng.normal(0, 0.5)), "x2": float(rng.normal(0, 0.1))}, - is_log=is_log, - min_reg=float(rng.choice([0.0, 0.1])), - max_reg=float(rng.choice([1.0, 0.9])), - ) - newconst = spec.const + 0.1 * int(rng.integers(-2, 3)) # as after the allocation's modify - - cells = [] - for r, c in zip(grid.rows, grid.cols, strict=True): - cell = {"region": 1, "nd": float(no_data[r, c]), "past": {"u": float(share[r, c])}} - cell.update({k: float(v[r, c]) for k, v in drivers.items()}) - cells.append(table(lua, cell)) - grid.neighbourhoods(lua, cells) - model = table(lua, {"cs": {"cells": cells}, "landUseTypes": ["u"], "landUseNoData": "nd"}) - data = { - "isLog": is_log, - "const": spec.const, - "minReg": spec.min_reg, - "maxReg": spec.max_reg, - "ro": spec.ro, - "betas": spec.betas, - } - comp = lua.globals().PotentialCSpatialLagRegression(table(lua, {"potentialData": [[data]]})) - comp.potentialData[1][1].newconst = newconst - comp.computePotential(comp, model, 1, 1) - - theirs = np.array([cell["u_pot"] for cell in cells]) - _, pot = spatial_lag_regression(spec, share, drivers, grid.valid, no_data, newconst) - ours = pot[grid.rows, grid.cols] - assert np.abs(ours - theirs).max() < 1e-10 - - # the cases do contain what csAC lacks - moore_count = sum( - np.roll(np.roll(grid.valid, dr, 0), dc, 1) - for dr in (-1, 0, 1) - for dc in (-1, 0, 1) - if (dr, dc) != (0, 0) - ) - assert ((moore_count == 0) & grid.valid).any() diff --git a/tests/test_saturation_golden.py b/tests/test_saturation_golden.py deleted file mode 100644 index 881218b..0000000 --- a/tests/test_saturation_golden.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -AllocationClueLikeSaturation + PotentialSpatialLagRegression against the LuccME -goldens of lab03 and lab06: the whole model, run from 2008 on its own (no replay). - -Checked every year, 2008–2014: -- every cell's land use (_out) and potential (_pot) against the golden; -- the maximum error against the demand, and the iteration count, against the - TerraME log (terrame.log: "Maximum error: …", "Number of iterations: …"). - -The saturation branch is not reached in these labs (change_limiar_value = 1), -so it is not constrained here; everything else in the allocation is. -""" - -from __future__ import annotations - -import pytest -from _lab03_helpers import ALLOCATION, YEARS, Lab, run, terrame_log, worst_per_column - -TOL = 1e-9 - - -@pytest.fixture(scope="module", params=["lab03", "lab06"]) -def result(request): - lab = Lab(request.param) - snaps, allocation = run(lab) - return lab, snaps, allocation - - -def test_land_use_and_potential_match_golden_every_year(result): - lab, snaps, _ = result - worst = worst_per_column(lab, snaps) - bad = {col: w for col, w in worst.items() if w[1] >= TOL} - assert not bad, f"{lab.name}: {bad}" - - -def test_iterations_and_error_match_terrame_log(result): - lab, _, allocation = result - log = terrame_log(lab.name) - assert sorted(log) == YEARS - assert allocation.iterations_per_step == [log[y][0] for y in YEARS] - for year, ours in zip(YEARS, allocation.max_error_per_step, strict=True): - assert ours == pytest.approx(log[year][1], rel=1e-9), year - - -def test_what_the_goldens_do_not_reach(monkeypatch): - """In lab03 no cell ever needs correctCellChange (the classes always sum to - 1 ± 0.005 before it) and no cell is saturated (change_limiar_value = 1): the - goldens say nothing about those parts, and the cells' visiting order has no - effect. They are checked against the Lua itself in test_lua_differential.py.""" - import disslucc.components.allocation.saturation as sat - - stats: dict = {} - original = sat.correct_cell_change - monkeypatch.setattr(sat, "correct_cell_change", lambda v, p, s, d: original(v, p, s, d, stats)) - lab = Lab("lab03") - run(lab) - assert stats["need"] == 0 - assert all(s.change_limiar_value >= 1 for s in ALLOCATION) - - monkeypatch.setattr(sat, "correct_cell_change", original) - snaps, _ = run(lab, order="shuffled") - assert max(err for _, err in worst_per_column(lab, snaps).values()) < TOL diff --git a/tests/test_spatial_lag_golden.py b/tests/test_spatial_lag_golden.py deleted file mode 100644 index 26d504e..0000000 --- a/tests/test_spatial_lag_golden.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -PotentialSpatialLagRegression against the LuccME goldens of lab03 and lab06. - -Goldens (benchmark/goldens/, from LambdaGeo/terrame-docker v0.1.1): the state of every -cell at the end of every year, 2008–2014, with _out and _pot. Both labs -use DemandPreComputedValues + PotentialCSpatialLagRegression + -AllocationCClueLikeSaturation; lab06 also replaces `ti` from csAC_2009 in 2009. - -To test the potential alone, each year starts from the golden's own land use of -the year before (what LuccME's `cell.past` holds) — so an error cannot come from -the allocation. The allocation can still shift `newconst` by ±0.1 during a year -(`modify`); in these two labs it never does, so the potential must match exactly -(the golden keeps 12 decimals: tolerance 1e-10). -""" - -from __future__ import annotations - -import numpy as np -import pytest -from _lab03_helpers import DEMAND, LAND_USES, NO_DATA, YEARS, Lab, max_errors, specs -from dissmodel.core import Environment, Model -from dissmodel.geo.raster.backend import RasterBackend - -from disslucc import DemandPreComputedValues, PotentialSpatialLagRegression, SpatialLagRegressionSpec -from disslucc.components.potential.spatial_lag import spatial_lag_regression - -TOL = 1e-10 - - -@pytest.fixture(scope="module", params=["lab03", "lab06"]) -def lab(request) -> Lab: - return Lab(request.param) - - -def test_golden_is_the_expected_run(lab): - assert lab.manifest["status"] == "ok" - assert lab.manifest["years"] == [YEARS[0], YEARS[-1]] - assert lab.manifest["n_cells"] == len(lab.cells) == len(lab.golden_year(2008)) - - -def test_potential_matches_golden_every_year_and_class(lab): - errors = max_errors(lab) - worst = max(errors, key=errors.get) - assert errors[worst] < TOL, f"{lab.name}: worst {worst} = {errors[worst]:.3e}" - - -def test_clipping_is_exercised(lab): - """The golden constrains the clip branch only if some cells hit it: count the - cells whose value changes when the bounds are removed.""" - past, drivers = lab.past(2008), lab.drivers(2008) - clipped = 0 - for lu, spec in zip(LAND_USES, specs(), strict=True): - free = SpatialLagRegressionSpec( - const=spec.const, ro=spec.ro, betas=spec.betas, min_reg=-np.inf, max_reg=np.inf - ) - reg, _ = spatial_lag_regression(spec, past[lu], drivers, lab.valid, past[NO_DATA]) - reg_free, _ = spatial_lag_regression(free, past[lu], drivers, lab.valid, past[NO_DATA]) - clipped += int((np.abs(reg - reg_free)[lab.valid] > 1e-12).sum()) - assert clipped > 0 - - -def test_non_cumulative_adaptation_is_rejected(lab): - errors = max_errors(lab, cumulative=False) - assert max(v for (y, _), v in errors.items() if y >= 2010) > 1e-4 - - -def test_updates_pair_cells_by_position_not_by_id(): - lab = Lab("lab06") - by_id = max_errors(lab, pair_by="id") - assert max(v for (y, _), v in by_id.items() if y >= 2009) > 1e-2 - - -def test_component_matches_golden(lab): - """The dissmodel component (what moves to disslucc), stepped in an Environment. - - A replay model runs before it each year and puts the golden's land use of the - year before into the backend, plus the drivers of that year.""" - backend = RasterBackend(shape=lab.shape) - for lu in LAND_USES: - backend.set(lu, lab.initial(lu)) - for name, arr in lab.drivers(YEARS[0]).items(): - backend.set(name, arr) - backend.set("mask", lab.valid.astype(np.float32)) - - class Replay(Model): - def setup(self): - self.year = YEARS[0] - - def execute(self): - self.year = YEARS[0] + int(self.env.now()) - if self.year > YEARS[0]: - for lu, arr in lab.past(self.year).items(): - backend.set(lu, arr) - backend.set(lu + "_past", arr.copy()) - for name, arr in lab.drivers(self.year).items(): - backend.set(name, arr) - - got: dict[int, dict[str, np.ndarray]] = {} - - class Record(Model): - def execute(self): - year = YEARS[0] + int(self.env.now()) - got[year] = {lu: backend.get(lu + "_pot").copy() for lu in LAND_USES} - - env = Environment(end_time=len(YEARS) - 1) - demand = DemandPreComputedValues(annual_demand=DEMAND, land_use_types=LAND_USES) - Replay() - PotentialSpatialLagRegression( - backend=backend, - potential_data=[specs()], - demand=demand, - land_use_types=LAND_USES, - land_use_no_data=NO_DATA, - ) - Record() - env.run() - - assert sorted(got) == YEARS - for year in YEARS: - ref = lab.golden_year(year) - r = ref.index.get_level_values("row").values - c = ref.index.get_level_values("col").values - for lu in LAND_USES: - err = np.abs(got[year][lu][r, c] - ref[f"{lu}_pot"].values).max() - assert err < TOL, f"{lab.name} {year} {lu}: {err:.3e}" diff --git a/tests/test_validation_lab1.py b/tests/test_validation_lab1.py deleted file mode 100644 index 5447509..0000000 --- a/tests/test_validation_lab1.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -tests/test_validation_lab1.py -------------------------------- -Regression test for the raster Lab1 scenario: asserts MAE and RMSE -against the real TerraME/LUCCME reference stay within the official -0.01 tolerance (disslucc-continuous/tests/test_benchmark_validation.py's -own criterion). Turns examples/run_lab1_validation.py's printed numbers -into something CI can fail on. -""" -import pytest -from _lab1_helpers import TOLERANCE, data_available, run_lab1 - - -@pytest.mark.skipif(not data_available, reason="Lab1 data files not found") -class TestValidationLab1: - @pytest.fixture(scope="class") - @classmethod - def metrics(cls): - return run_lab1() - - def test_raster_vs_terrame_mae(self, metrics): - assert metrics["mae"] < TOLERANCE, ( - f"Raster_vs_TerraME MAE={metrics['mae']:.6f} exceeds tolerance={TOLERANCE}" - ) - - def test_raster_vs_terrame_rmse(self, metrics): - assert metrics["rmse"] < TOLERANCE, ( - f"Raster_vs_TerraME RMSE={metrics['rmse']:.6f} exceeds tolerance={TOLERANCE}" - ) diff --git a/tests/test_validation_lab15.py b/tests/test_validation_lab15.py deleted file mode 100644 index 4690557..0000000 --- a/tests/test_validation_lab15.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -tests/test_validation_lab15.py ---------------------------------- -Integration test: verifies 100% cell-level parity between disslucc's -raster CLUE-S-like port and the TerraME/LuccME reference (Lab15, -cs_moju, 1999-2004). Ported from -disslucc-discrete/tests/test_validation_lab15.py, adapted from the -vector to the raster substrate. - -Kappa is not asserted: it is deprecated across the ecosystem in favour -of the Pontius & Millones (2011) decomposition. See also -``test_benchmark_discriminance_lab15.py`` -- this scenario is -reproduced by a trivial static ranking, so it validates coefficient -transcription rather than the allocation algorithm. -""" -import pytest -from _lab15_helpers import ( - ANNUAL_DEMAND, - LAND_USE_TYPES, - data_available, - metrics_vs_terrame, - run_lab15_raster, -) - - -@pytest.mark.skipif(not data_available, reason="Lab15 data files not found -- skipping integration test") -def test_lab15_parity(): - """The raster CLUE-S-like port must achieve 100% cell-level agreement with TerraME.""" - backend, rows, cols = run_lab15_raster() - m = metrics_vs_terrame(backend, rows, cols) - - assert m["accuracy"] == pytest.approx(100.0, abs=1e-4), ( - f"Expected accuracy=100%, got {m['accuracy']:.4f}%" - ) - # Pontius & Millones (2011) -- replaces kappa as the parity criterion. - assert m["quantity_disagreement"] == pytest.approx(0.0, abs=1e-9), ( - f"Quantity disagreement={m['quantity_disagreement']:.6f}, expected 0" - ) - assert m["allocation_disagreement"] == pytest.approx(0.0, abs=1e-9), ( - f"Allocation disagreement={m['allocation_disagreement']:.6f}, expected 0" - ) - assert m["f1"] == pytest.approx(1.0, abs=1e-4), f"Expected F1=1.0, got {m['f1']:.4f}" - assert m["fp"] == 0, f"Expected FP=0, got {m['fp']}" - assert m["fn"] == 0, f"Expected FN=0, got {m['fn']}" - - # Final demand must be met within tolerance - n_d = int((backend.get("d")[rows, cols] == 1).sum()) - expected_d = int(ANNUAL_DEMAND[-1][LAND_USE_TYPES.index("d")]) - assert abs(n_d - expected_d) <= 10, ( - f"Final d allocation={n_d}, demand={expected_d}, diff={n_d - expected_d:+d}" - )