diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..eeb86ce --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,25 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install package and dev dependencies + run: pip install -e ".[dev]" + + - name: Run tests + run: pytest tests/ -v diff --git a/.gitignore b/.gitignore index 8392d2f..cc0a93f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,11 @@ examples/local_output.* # Benchmark benchmark/results/ benchmark/data/output/ +benchmark/simulacao_*.record.json +benchmark/simulacao_*.tif/ +benchmark/profiling_*.md + +# Examples outputs +examples/experiment_record.json map_frames \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4259482 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 LambdaGeo research group, UFMA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0e2d97a..066bd64 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > **Continuous Spatial Library for Land Use Change Modeling** โ€” A Python implementation of continuous LUCC modeling components (LUCCME-like), built on top of [DissModel](https://github.com/LambdaGeo/dissmodel) -[![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg)](LICENSE) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Python](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org) [![LambdaGeo](https://img.shields.io/badge/LambdaGeo-Research-green.svg)](https://github.com/LambdaGeo) @@ -62,7 +62,7 @@ python lab1_raster.py run \ python lab1_raster.py validate --input data/input/csAC.zip # Run the Benchmark suite (Vector vs Raster vs TerraME/LUCCME comparison) -python -m disslucc_continuous.infra.executors.lucc_benchmark_executor run \ +python -m disslucc_continuous.executors.lucc_benchmark_executor run \ --input examples/data/input/csAC.zip \ --output ./benchmark/ \ --param demand_csv=examples/data/input/examples_demand_lab1.csv \ @@ -206,7 +206,7 @@ Infrastructure Layer (ModelExecutor) The `LUCCBenchmarkExecutor` is a meta-executor that runs vector and raster substrates in a single pass and compares both against a TerraME/LUCCME reference result. It generates a Markdown report and scatter plots โ€” the primary tool for validating numerical equivalence before publishing results. ```bash -python -m disslucc_continuous.infra.executors.lucc_benchmark_executor run \ +python -m disslucc_continuous.executors.lucc_benchmark_executor run \ --input examples/data/input/csAC.zip \ --output ./benchmark/ \ --param demand_csv=examples/data/input/examples_demand_lab1.csv \ @@ -244,8 +244,8 @@ class MyLUCCExecutor(ModelExecutor): def run(self, record: ExperimentRecord): from dissmodel.core import Environment from disslucc_continuous import DemandPreComputedValues, load_demand_csv - from disslucc_continuous.vector.potential.linear import PotentialLinearRegression - from disslucc_continuous.vector.allocation.clue import AllocationClueLike + from disslucc_continuous.components.potential.vector.linear import PotentialLinearRegression + from disslucc_continuous.components.allocation.vector.clue import AllocationClueLike params = record.parameters gdf = self.load(record) @@ -351,15 +351,13 @@ max_change = 1 ## ๐Ÿ“ฆ Installation ```bash -# Via pip -pip install disslucc_continuousc-continuous - -# From source -git clone https://github.com/LambdaGeo/DisSLUCC.git -cd DisSLUCC +git clone https://github.com/DisSModel/disslucc-continuous.git +cd disslucc-continuous pip install -e . ``` +> **Note:** PyPI publication is planned once the package reaches a stable API (tracked alongside the JOSS submission). + **Dependencies:** `dissmodel`, `geopandas`, `shapely`, `pandas`, `numpy`, `rasterio`, `matplotlib` --- @@ -378,11 +376,10 @@ DisSLUCC-Continuous/ โ”‚ โ”‚ โ””โ”€โ”€ allocation/ โ”‚ โ”‚ โ”œโ”€โ”€ raster/ โ”‚ โ”‚ โ””โ”€โ”€ vector/ -โ”‚ โ”œโ”€โ”€ infra/ # Infrastructure Layer (Executors) -โ”‚ โ”‚ โ””โ”€โ”€ executors/ -โ”‚ โ”‚ โ”œโ”€โ”€ clue_like_raster_executor.py -โ”‚ โ”‚ โ”œโ”€โ”€ clue_like_vector_executor.py -โ”‚ โ”‚ โ””โ”€โ”€ lucc_benchmark_executor.py +โ”‚ โ”œโ”€โ”€ executors/ # Infrastructure Layer (Executors) +โ”‚ โ”‚ โ”œโ”€โ”€ clue_like_raster_executor.py +โ”‚ โ”‚ โ”œโ”€โ”€ clue_like_vector_executor.py +โ”‚ โ”‚ โ””โ”€โ”€ lucc_benchmark_executor.py โ”‚ โ””โ”€โ”€ common/ # Common Layer (Schemas and Protocols) โ”‚ โ”œโ”€โ”€ schemas.py # RegressionSpec, AllocationSpec โ”‚ โ””โ”€โ”€ protocols.py # Component interfaces @@ -407,6 +404,29 @@ DisSLUCC-Continuous/ --- +## ๐Ÿงช Testing & Validation + +The primary validation strategy is numerical comparison against TerraME/LUCCME reference outputs via the `lucc_benchmark` executor, which runs both Vector and Raster substrates in a single pass and reports MAE, RMSE, and match% for each comparison pair: + +| Comparison | What it checks | +|---|---| +| `Vector_vs_TerraME` | Python vector model vs original LUCCME/TerraME result | +| `Raster_vs_TerraME` | Python raster model vs original LUCCME/TerraME result | +| `Vector_vs_Raster` | Internal consistency between substrates | + +Run the full test suite (benchmark validation + unit tests): + +```bash +pytest tests/ -v +``` + +The benchmark test (`tests/test_benchmark_validation.py`) uses: +- **Input**: `examples/data/input/csAC.zip` + `examples/data/input/examples_demand_lab1.csv` +- **Reference**: `benchmark/data/LUCCME_Lab1_2014.zip` +- **Assertion**: MAE and RMSE below `tolerance=0.01` for `Vector_vs_TerraME` and `Raster_vs_TerraME` + +--- + ## ๐Ÿ“š References - **LUCCME**: Carneiro et al. (2013). *Environmental Modelling & Software*, 46, 104โ€“117. [http://luccme.ccst.inpe.br](http://luccme.ccst.inpe.br) @@ -430,7 +450,7 @@ To register a new model in the platform, open a PR in [dissmodel-configs](https: ## ๐Ÿ“„ License -Distributed under the **GPL-3.0 License**. Developed by the **[LambdaGeo](https://github.com/LambdaGeo)** research group. +Distributed under the **MIT License**. Developed by the **[LambdaGeo](https://github.com/LambdaGeo)** research group. --- diff --git a/benchmark/profiling_064d4f68.md b/benchmark/profiling_064d4f68.md deleted file mode 100644 index 7f978b8..0000000 --- a/benchmark/profiling_064d4f68.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_validation - -**Experiment ID:** `064d4f6894e147feb25a2fcc923e3453` -**Date:** `2026-05-02T23:41:35.184125` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 0.385 | 16.2% | -| **Run** | 1.983 | 83.7% | -| **Save** | 0.002 | 0.1% | -| **Total** | **2.370** | **100%** | diff --git a/benchmark/profiling_12af2d48.md b/benchmark/profiling_12af2d48.md deleted file mode 100644 index a378388..0000000 --- a/benchmark/profiling_12af2d48.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_validation - -**Experiment ID:** `12af2d48e07e4769bd51c293dd8ae44a` -**Date:** `2026-05-02T23:43:47.497426` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 0.516 | 22.4% | -| **Run** | 1.782 | 77.5% | -| **Save** | 0.001 | 0.0% | -| **Total** | **2.299** | **100%** | diff --git a/benchmark/profiling_52b82914.md b/benchmark/profiling_52b82914.md deleted file mode 100644 index 05d7f5e..0000000 --- a/benchmark/profiling_52b82914.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_validation - -**Experiment ID:** `52b82914dcff47ad9da7e314843361d3` -**Date:** `2026-04-14T17:02:05.554902` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 0.371 | 17.4% | -| **Run** | 1.760 | 82.6% | -| **Save** | 0.001 | 0.0% | -| **Total** | **2.132** | **100%** | diff --git a/benchmark/simulacao_064d4f68.record.json b/benchmark/simulacao_064d4f68.record.json deleted file mode 100644 index 7c71a81..0000000 --- a/benchmark/simulacao_064d4f68.record.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "experiment_id": "064d4f6894e147feb25a2fcc923e3453", - "created_at": "2026-05-02T23:41:35.184125", - "model_name": "local", - "model_commit": "local-cli", - "code_version": "dev", - "resolved_spec": {}, - "source": { - "type": "local", - "uri": "examples/data/input/csAC.zip", - "collection": "", - "version": "", - "checksum": "dc659e611cb082bc40a2acbac8ec816cbaff66bf5c97f4940abe8c7c3c91a6eb" - }, - "input_format": "auto", - "column_map": {}, - "band_map": {}, - "parameters": { - "demand_csv": "examples/data/input/examples_demand_lab1.csv", - "terrame_reference": "benchmark/data/LUCCME_Lab1_2014.zip", - "n_steps": 6, - "tolerance": 0.01 - }, - "output_path": "benchmark/simulacao_064d4f68.tif", - "artifacts": { - "plot": "e93594a5682ed2cedda1f3f2183e3168b2385a8b8f42a5b58fc49bbb25a8bef8", - "report": "0a8c74df300e2b8d46cd70d9e528abedf005de37cc11d5e9a139cab8101b19a1", - "profiling": "2f876c7d75ef7068c97255aaa09bb2a7e0da96ee18aed32edbe0c7f3b6a04cfd" - }, - "metrics": { - "Vector_vs_TerraME": { - "match_pct": 97.09461515059324, - "mae": 0.0022764970910315783, - "rmse": 0.0038817988763166435, - "max_err": 0.0156034736177963, - "n_cells": 6574 - }, - "Raster_vs_TerraME": { - "match_pct": 97.09461515059324, - "mae": 0.0022764990324372907, - "rmse": 0.0038817986262381678, - "max_err": 0.015603571145812282, - "n_cells": 6574 - }, - "Vector_vs_Raster": { - "match_pct": 100.0, - "mae": 7.864424227577985e-9, - "rmse": 1.323676637014114e-8, - "max_err": 1.1453525662792619e-7, - "n_cells": 6574 - }, - "time_validate_sec": 0.0, - "time_load_sec": 0.385, - "time_run_sec": 1.983, - "time_save_sec": 0.002, - "time_total_sec": 2.37 - }, - "status": "completed", - "logs": [ - "Loaded Shapefile: 6574 cells", - "Loaded TerraME Ref: 6574 cells", - "Running Vector Model (6 steps)...", - "Vector done: 122.5 ms/step", - "Running Raster Model (6 steps)...", - "Raster done: 31.6 ms/step", - "Calculating metrics...", - "Generating artifacts...", - "Saved artifacts to benchmark/simulacao_064d4f68.tif", - "Saved profiling artifact โ†’ benchmark/profiling_064d4f68.md" - ] -} \ No newline at end of file diff --git a/benchmark/simulacao_064d4f68.tif/report.md b/benchmark/simulacao_064d4f68.tif/report.md deleted file mode 100644 index 07a74d0..0000000 --- a/benchmark/simulacao_064d4f68.tif/report.md +++ /dev/null @@ -1,18 +0,0 @@ -# Lab1 Validation Report - -**Steps:** 6 | **Tolerance:** 0.01 - -## Runtime - -| Substrate | ms/step | Speedup | -|---|---|---| -| Vector | 122.5 | 1ร— | -| Raster | 31.6 | 3.9ร— | - -## Accuracy โ€” `d` - -| Comparison | Match % | MAE | RMSE | Max err | N cells | -|---|---|---|---|---|---| -| Vector vs TerraME | 97.09% | 0.002276 | 0.003882 | 0.015603 | 6574 | -| Raster vs TerraME | 97.09% | 0.002276 | 0.003882 | 0.015604 | 6574 | -| Vector vs Raster | 100.00% | 0.000000 | 0.000000 | 0.000000 | 6574 | diff --git a/benchmark/simulacao_064d4f68.tif/scatter.png b/benchmark/simulacao_064d4f68.tif/scatter.png deleted file mode 100644 index cbdb697..0000000 Binary files a/benchmark/simulacao_064d4f68.tif/scatter.png and /dev/null differ diff --git a/benchmark/simulacao_12af2d48.record.json b/benchmark/simulacao_12af2d48.record.json deleted file mode 100644 index 6fbdbf0..0000000 --- a/benchmark/simulacao_12af2d48.record.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "experiment_id": "12af2d48e07e4769bd51c293dd8ae44a", - "created_at": "2026-05-02T23:43:47.497426", - "model_name": "local", - "model_commit": "local-cli", - "code_version": "dev", - "resolved_spec": {}, - "source": { - "type": "local", - "uri": "examples/data/input/csAC.zip", - "collection": "", - "version": "", - "checksum": "dc659e611cb082bc40a2acbac8ec816cbaff66bf5c97f4940abe8c7c3c91a6eb" - }, - "input_format": "auto", - "column_map": {}, - "band_map": {}, - "parameters": { - "demand_csv": "examples/data/input/examples_demand_lab1.csv", - "terrame_reference": "benchmark/data/LUCCME_Lab1_2014.zip", - "n_steps": 6, - "tolerance": 0.01 - }, - "output_path": "benchmark/simulacao_12af2d48.tif", - "artifacts": { - "plot": "e93594a5682ed2cedda1f3f2183e3168b2385a8b8f42a5b58fc49bbb25a8bef8", - "report": "4d74524db848d0598ec1c209e770a751e0f610606f3580fd38549f49ca6f8700", - "profiling": "2e29c7bb237bed5ec04d7213cacf2d20f53e65eae4d9899d0cf0816b3a05c12f" - }, - "metrics": { - "Vector_vs_TerraME": { - "match_pct": 97.09461515059324, - "mae": 0.0022764970910315783, - "rmse": 0.0038817988763166435, - "max_err": 0.0156034736177963, - "n_cells": 6574 - }, - "Raster_vs_TerraME": { - "match_pct": 97.09461515059324, - "mae": 0.0022764990324372907, - "rmse": 0.0038817986262381678, - "max_err": 0.015603571145812282, - "n_cells": 6574 - }, - "Vector_vs_Raster": { - "match_pct": 100.0, - "mae": 7.864424227577985e-9, - "rmse": 1.323676637014114e-8, - "max_err": 1.1453525662792619e-7, - "n_cells": 6574 - }, - "time_validate_sec": 0.0, - "time_load_sec": 0.516, - "time_run_sec": 1.782, - "time_save_sec": 0.001, - "time_total_sec": 2.299 - }, - "status": "completed", - "logs": [ - "Loaded Shapefile: 6574 cells", - "Loaded TerraME Ref: 6574 cells", - "Running Vector Model (6 steps)...", - "Vector done: 157.5 ms/step", - "Running Raster Model (6 steps)...", - "Raster done: 39.2 ms/step", - "Calculating metrics...", - "Generating artifacts...", - "Saved artifacts to benchmark/simulacao_12af2d48.tif", - "Saved profiling artifact โ†’ benchmark/profiling_12af2d48.md" - ] -} \ No newline at end of file diff --git a/benchmark/simulacao_12af2d48.tif/report.md b/benchmark/simulacao_12af2d48.tif/report.md deleted file mode 100644 index 4fa2499..0000000 --- a/benchmark/simulacao_12af2d48.tif/report.md +++ /dev/null @@ -1,18 +0,0 @@ -# Lab1 Validation Report - -**Steps:** 6 | **Tolerance:** 0.01 - -## Runtime - -| Substrate | ms/step | Speedup | -|---|---|---| -| Vector | 157.5 | 1ร— | -| Raster | 39.2 | 4.0ร— | - -## Accuracy โ€” `d` - -| Comparison | Match % | MAE | RMSE | Max err | N cells | -|---|---|---|---|---|---| -| Vector vs TerraME | 97.09% | 0.002276 | 0.003882 | 0.015603 | 6574 | -| Raster vs TerraME | 97.09% | 0.002276 | 0.003882 | 0.015604 | 6574 | -| Vector vs Raster | 100.00% | 0.000000 | 0.000000 | 0.000000 | 6574 | diff --git a/benchmark/simulacao_12af2d48.tif/scatter.png b/benchmark/simulacao_12af2d48.tif/scatter.png deleted file mode 100644 index cbdb697..0000000 Binary files a/benchmark/simulacao_12af2d48.tif/scatter.png and /dev/null differ diff --git a/benchmark/simulacao_52b82914.record.json b/benchmark/simulacao_52b82914.record.json deleted file mode 100644 index 19062f1..0000000 --- a/benchmark/simulacao_52b82914.record.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "experiment_id": "52b82914dcff47ad9da7e314843361d3", - "created_at": "2026-04-14T17:02:05.554902", - "model_name": "local", - "model_commit": "local-cli", - "code_version": "dev", - "resolved_spec": {}, - "source": { - "type": "local", - "uri": "examples/data/input/csAC.zip", - "collection": "", - "version": "", - "checksum": "dc659e611cb082bc40a2acbac8ec816cbaff66bf5c97f4940abe8c7c3c91a6eb" - }, - "input_format": "auto", - "column_map": {}, - "band_map": {}, - "parameters": { - "demand_csv": "examples/data/input/examples_demand_lab1.csv", - "terrame_reference": "benchmark/data/LUCCME_Lab1_2014.zip", - "n_steps": 6, - "tolerance": 0.01 - }, - "output_path": "benchmark/simulacao_52b82914.tif", - "artifacts": { - "plot": "3afa25dfdab331530a7e16210148b236f5929510c03163b1d0514597aa1bb35b", - "report": "d2a7700cb7aced289c400110f1c0d429e0c96a168f0f426f543f5a7cff0093a1", - "profiling": "79f72a3d0f9fb0ffc96e84d3dd307fb05a695b514ba1ad79e2c385a1b9e385b6" - }, - "metrics": { - "Vector_vs_TerraME": { - "match_pct": 87.37450562823244, - "mae": 0.0035832315039763117, - "rmse": 0.006188164136224939, - "max_err": 0.027355465233104365, - "n_cells": 6574 - }, - "Raster_vs_TerraME": { - "match_pct": 87.37450562823244, - "mae": 0.0035832335223444727, - "rmse": 0.006188163534159373, - "max_err": 0.027355464344323344, - "n_cells": 6574 - }, - "Vector_vs_Raster": { - "match_pct": 100.0, - "mae": 8.264914064301092e-9, - "rmse": 1.3897950852649964e-8, - "max_err": 1.1173078400678049e-7, - "n_cells": 6574 - }, - "time_validate_sec": 0.0, - "time_load_sec": 0.371, - "time_run_sec": 1.76, - "time_save_sec": 0.001, - "time_total_sec": 2.132 - }, - "status": "completed", - "logs": [ - "Loaded Shapefile: 6574 cells", - "Loaded TerraME Ref: 6574 cells", - "Running Vector Model (6 steps)...", - "Vector done: 143.2 ms/step", - "Running Raster Model (6 steps)...", - "Raster done: 39.9 ms/step", - "Calculating metrics...", - "Generating artifacts...", - "Saved artifacts to benchmark/simulacao_52b82914.tif", - "Saved profiling artifact โ†’ benchmark/profiling_52b82914.md" - ] -} \ No newline at end of file diff --git a/benchmark/simulacao_52b82914.tif/report.md b/benchmark/simulacao_52b82914.tif/report.md deleted file mode 100644 index 443f396..0000000 --- a/benchmark/simulacao_52b82914.tif/report.md +++ /dev/null @@ -1,18 +0,0 @@ -# Lab1 Validation Report - -**Steps:** 6 | **Tolerance:** 0.01 - -## Runtime - -| Substrate | ms/step | Speedup | -|---|---|---| -| Vector | 143.2 | 1ร— | -| Raster | 39.9 | 3.6ร— | - -## Accuracy โ€” `d` - -| Comparison | Match % | MAE | RMSE | Max err | N cells | -|---|---|---|---|---|---| -| Vector vs TerraME | 87.37% | 0.003583 | 0.006188 | 0.027355 | 6574 | -| Raster vs TerraME | 87.37% | 0.003583 | 0.006188 | 0.027355 | 6574 | -| Vector vs Raster | 100.00% | 0.000000 | 0.000000 | 0.000000 | 6574 | diff --git a/benchmark/simulacao_52b82914.tif/scatter.png b/benchmark/simulacao_52b82914.tif/scatter.png deleted file mode 100644 index a9437bc..0000000 Binary files a/benchmark/simulacao_52b82914.tif/scatter.png and /dev/null differ diff --git a/examples/experiment_record.json b/examples/experiment_record.json deleted file mode 100644 index 5cd9483..0000000 --- a/examples/experiment_record.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "experiment_id": "0c11177b767045c8a722dbc1ba4f24f0", - "created_at": "2026-04-14T17:02:20.195314", - "model_name": "local", - "model_commit": "local-cli", - "code_version": "dev", - "resolved_spec": { - "model": { - "parameters": { - "resolution": 5000.0, - "n_steps": 7, - "demand_csv": "data/input/examples_demand_lab1.csv", - "land_use_types": [ - "f", - "d", - "outros" - ], - "land_use_no_data": "outros", - "complementar_lu": "f", - "cell_area": 25.0 - }, - "driver_columns": { - "cols": [ - "assentamen", - "uc_us", - "uc_pi", - "ti", - "dist_riobr", - "fertilidad", - "rodovias" - ] - }, - "static": { - "f": -1, - "d": -1, - "outros": 1 - }, - "potential": [ - { - "lu": "f", - "const": 0.7392, - "betas": { - "assentamen": -0.2193, - "uc_us": 0.1754, - "uc_pi": 0.09708, - "ti": 0.1207, - "dist_riobr": 2.388e-7, - "fertilidad": -0.1313 - } - }, - { - "lu": "d", - "const": 0.267, - "betas": { - "rodovias": -9.922e-7, - "assentamen": 0.2294, - "uc_us": -0.09867, - "dist_riobr": -3.216e-7, - "fertilidad": 0.1281 - } - }, - { - "lu": "outros", - "const": 0.0 - } - ], - "allocation": [ - { - "lu": "f", - "static": -1, - "min_value": 0, - "max_value": 1, - "min_change": 0, - "max_change": 1 - }, - { - "lu": "d", - "static": -1, - "min_value": 0, - "max_value": 1, - "min_change": 0, - "max_change": 1 - }, - { - "lu": "outros", - "static": 1, - "min_value": 0, - "max_value": 1, - "min_change": 0, - "max_change": 1 - } - ] - } - }, - "source": { - "type": "local", - "uri": "data/input/csAC.zip", - "collection": "", - "version": "", - "checksum": "dc659e611cb082bc40a2acbac8ec816cbaff66bf5c97f4940abe8c7c3c91a6eb" - }, - "input_format": "auto", - "column_map": {}, - "band_map": {}, - "parameters": { - "resolution": 5000.0, - "n_steps": 7, - "demand_csv": "data/input/examples_demand_lab1.csv", - "land_use_types": [ - "f", - "d", - "outros" - ], - "land_use_no_data": "outros", - "complementar_lu": "f", - "cell_area": 25.0 - }, - "output_path": "local_output.tif", - "artifacts": { - "output": "1059f63656be4f149ae34ce5b351ca97d69a6627d90ed1756af146d5b0eca573", - "profiling": "a4769ef3b45e6c9cea578a3e679733ed5fb83a655fd556b5174bdff01b10eccf" - }, - "metrics": { - "time_validate_sec": 0.0, - "time_load_sec": 5.691, - "time_run_sec": 0.373, - "time_save_sec": 0.015, - "time_total_sec": 6.079 - }, - "status": "completed", - "logs": [ - "Rasterized: shape=(89, 165) valid=6,574 cells", - "Running 7 steps...", - "Simulation complete", - "Saved to local_output.tif", - "Saved profiling artifact โ†’ ./profiling_0c11177b.md" - ] -} \ No newline at end of file diff --git a/examples/local_output.gpkg b/examples/local_output.gpkg deleted file mode 100644 index f8fa986..0000000 Binary files a/examples/local_output.gpkg and /dev/null differ diff --git a/examples/local_output.tif b/examples/local_output.tif deleted file mode 100644 index afe25c5..0000000 Binary files a/examples/local_output.tif and /dev/null differ diff --git a/examples/profiling_0c11177b.md b/examples/profiling_0c11177b.md deleted file mode 100644 index 92a3592..0000000 --- a/examples/profiling_0c11177b.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_raster - -**Experiment ID:** `0c11177b767045c8a722dbc1ba4f24f0` -**Date:** `2026-04-14T17:02:20.195314` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 5.691 | 93.6% | -| **Run** | 0.373 | 6.1% | -| **Save** | 0.015 | 0.2% | -| **Total** | **6.079** | **100%** | diff --git a/examples/profiling_4eadf928.md b/examples/profiling_4eadf928.md deleted file mode 100644 index aa4b9d4..0000000 --- a/examples/profiling_4eadf928.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_vector - -**Experiment ID:** `4eadf928546843d8ad0c49e29eeb4758` -**Date:** `2026-04-14T16:37:54.194973` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 0.256 | 5.4% | -| **Run** | 1.237 | 26.1% | -| **Save** | 3.253 | 68.5% | -| **Total** | **4.746** | **100%** | diff --git a/examples/profiling_737852f1.md b/examples/profiling_737852f1.md deleted file mode 100644 index 8e856b6..0000000 --- a/examples/profiling_737852f1.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_vector - -**Experiment ID:** `737852f104004c90a165216f50946980` -**Date:** `2026-04-14T11:09:29.774480` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 0.236 | 2.4% | -| **Run** | 8.882 | 91.9% | -| **Save** | 0.544 | 5.6% | -| **Total** | **9.663** | **100%** | diff --git a/examples/profiling_be6b17fc.md b/examples/profiling_be6b17fc.md deleted file mode 100644 index d103f99..0000000 --- a/examples/profiling_be6b17fc.md +++ /dev/null @@ -1,14 +0,0 @@ -# Profiling Report: lucc_raster - -**Experiment ID:** `be6b17fc687244a7a689c7efe3a404cd` -**Date:** `2026-04-14T16:34:57.053934` - -## Execution Times - -| Phase | Time (seconds) | % of Total | -|---|---|---| -| **Validate** | 0.000 | 0.0% | -| **Load** | 19.670 | 95.4% | -| **Run** | 0.880 | 4.3% | -| **Save** | 0.073 | 0.4% | -| **Total** | **20.623** | **100%** | diff --git a/pyproject.toml b/pyproject.toml index ae5835e..0237d0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,10 +7,10 @@ name = "disslucc-continuous" version = "0.2.0" description = "Continuous spatial LUCC models for DisSModel โ€” CLUE-like allocation with linear regression potential" readme = "README.md" -authors = [{ name = "Your Name", email = "your.email@ufma.br" }] +authors = [{ name = "Sergio Souza Costa", email = "sergio@ufma.br" }] license = { text = "MIT" } dependencies = [ - "dissmodel @ git+https://github.com/LambdaGeo/dissmodel.git@develop", + "dissmodel>=0.6.0,<0.7.0", "pandas>=1.5.0", "numpy>=1.23.0", "geopandas>=0.12.0", @@ -39,6 +39,6 @@ select = ["E", "F", "I"] ignore = ["E701", "E241", "E203", "E501"] [project.entry-points."dissmodel.executors"] -lucc_raster = "disslucc_continuous.infra.executors.clue_like_raster_executor:LUCCRasterExecutor" -lucc_vector = "disslucc_continuous.infra.executors.clue_like_vector_executor:LUCCVectorExecutor" -lucc_validation = "disslucc_continuous.infra.executors.lucc_benchmark_executor:LuccBenchmarkExecutor" \ No newline at end of file +lucc_raster = "disslucc_continuous.executors.clue_like_raster_executor:LUCCRasterExecutor" +lucc_vector = "disslucc_continuous.executors.clue_like_vector_executor:LUCCVectorExecutor" +lucc_benchmark = "disslucc_continuous.executors.lucc_benchmark_executor:LUCCBenchmarkExecutor" \ No newline at end of file diff --git a/src/disslucc_continuous/__init__.py b/src/disslucc_continuous/__init__.py index 817ad2b..588b9a8 100644 --- a/src/disslucc_continuous/__init__.py +++ b/src/disslucc_continuous/__init__.py @@ -13,19 +13,25 @@ from .components.allocation.raster import AllocationClueLike as AllocationRaster from .components.allocation.vector import AllocationClueLike as AllocationVector +# Aliases matching README examples +PotentialLinearRegression = PotentialVector +AllocationClueLike = AllocationVector + # Re-exporting from schemas from .schemas.schemas import RegressionSpec, AllocationSpec from .schemas.protocols import DemandProtocol, PotentialProtocol # Registra os executors no ExecutorRegistry via __init_subclass__ -from .infra.executors import LUCCRasterExecutor, LUCCVectorExecutor, LuccBenchmarkExecutor # noqa: F401 +from .executors import LUCCRasterExecutor, LUCCVectorExecutor, LUCCBenchmarkExecutor, LuccBenchmarkExecutor # noqa: F401 __all__ = [ "DemandPreComputedValues", "PotentialRaster", "PotentialVector", + "PotentialLinearRegression", "AllocationRaster", "AllocationVector", + "AllocationClueLike", "RegressionSpec", "AllocationSpec", "DemandProtocol", @@ -34,5 +40,6 @@ # Executors expostos para quem precisar instanciar diretamente "LUCCRasterExecutor", "LUCCVectorExecutor", + "LUCCBenchmarkExecutor", "LuccBenchmarkExecutor", ] diff --git a/src/disslucc_continuous/common/utils.py b/src/disslucc_continuous/common/utils.py index dfad0b8..3df1a37 100644 --- a/src/disslucc_continuous/common/utils.py +++ b/src/disslucc_continuous/common/utils.py @@ -1,6 +1,7 @@ - -# dissmodel/executor/utils.py +# TODO: duplicated across disslucc-continuous, disslucc-discrete and +# brmangue-dissmodel. Should be promoted to dissmodel.executor.utils +# (tracked separately). def default_output_uri(experiment_id: str, ext: str) -> str: """ diff --git a/src/disslucc_continuous/infra/executors/__init__.py b/src/disslucc_continuous/executors/__init__.py similarity index 68% rename from src/disslucc_continuous/infra/executors/__init__.py rename to src/disslucc_continuous/executors/__init__.py index 99c991c..2eed0b2 100644 --- a/src/disslucc_continuous/infra/executors/__init__.py +++ b/src/disslucc_continuous/executors/__init__.py @@ -1,12 +1,11 @@ -# src/disslucc_continuous/infra/executors/__init__.py - from .clue_like_raster_executor import LUCCRasterExecutor from .clue_like_vector_executor import LUCCVectorExecutor -from .lucc_benchmark_executor import LuccBenchmarkExecutor +from .lucc_benchmark_executor import LUCCBenchmarkExecutor, LuccBenchmarkExecutor __all__ = [ "LUCCRasterExecutor", "LUCCVectorExecutor", + "LUCCBenchmarkExecutor", "LuccBenchmarkExecutor", "EXECUTOR_REGISTRY", ] @@ -14,5 +13,5 @@ EXECUTOR_REGISTRY = { LUCCRasterExecutor.name: LUCCRasterExecutor, # "lucc_raster" LUCCVectorExecutor.name: LUCCVectorExecutor, # "lucc_vector" - LuccBenchmarkExecutor.name: LuccBenchmarkExecutor, # "lucc_validation" + LUCCBenchmarkExecutor.name: LUCCBenchmarkExecutor, # "lucc_benchmark" } \ No newline at end of file diff --git a/src/disslucc_continuous/infra/executors/clue_like_raster_executor.py b/src/disslucc_continuous/executors/clue_like_raster_executor.py similarity index 100% rename from src/disslucc_continuous/infra/executors/clue_like_raster_executor.py rename to src/disslucc_continuous/executors/clue_like_raster_executor.py diff --git a/src/disslucc_continuous/infra/executors/clue_like_vector_executor.py b/src/disslucc_continuous/executors/clue_like_vector_executor.py similarity index 100% rename from src/disslucc_continuous/infra/executors/clue_like_vector_executor.py rename to src/disslucc_continuous/executors/clue_like_vector_executor.py diff --git a/src/disslucc_continuous/infra/executors/lucc_benchmark_executor.py b/src/disslucc_continuous/executors/lucc_benchmark_executor.py similarity index 91% rename from src/disslucc_continuous/infra/executors/lucc_benchmark_executor.py rename to src/disslucc_continuous/executors/lucc_benchmark_executor.py index 7f80548..bc81e2f 100644 --- a/src/disslucc_continuous/infra/executors/lucc_benchmark_executor.py +++ b/src/disslucc_continuous/executors/lucc_benchmark_executor.py @@ -1,11 +1,11 @@ -# src/dissluc/executor/lucc_validation_executor.py from __future__ import annotations import io +import os import time -import struct import zipfile import pathlib +import tempfile import numpy as np import pandas as pd @@ -31,7 +31,7 @@ from disslucc_continuous.components.allocation.raster import AllocationClueLike as RasAllocation from disslucc_continuous.schemas.schemas import RegressionSpec, AllocationSpec -# โ”€โ”€ Defaults do Lab1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# โ”€โ”€ Lab1 defaults โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ LAND_USE_TYPES = ["f", "d", "outros"] LAND_USE_NO_DATA = "outros" @@ -56,7 +56,7 @@ ]] -class LuccBenchmarkExecutor(ModelExecutor): +class LUCCBenchmarkExecutor(ModelExecutor): """ Validation executor for CLUE-like LUCC modeling (Lab1). Compares Vector execution, Raster execution, and a TerraME reference dataset. @@ -69,7 +69,7 @@ class LuccBenchmarkExecutor(ModelExecutor): - terrame_reference (str): Path to TerraME ZIP file """ - name = "lucc_validation" + name = "lucc_benchmark" # โ”€โ”€ public contract โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -144,8 +144,6 @@ def run(self, data: tuple[gpd.GeoDataFrame, pd.DataFrame], record: ExperimentRec record.add_log(f"Running Raster Model ({n_steps} steps)...") backend, rows, cols = _build_mock_raster(gdf_orig) env_ras = Environment(start_time=1, end_time=n_steps) - - demand = DemandPreComputedValues( annual_demand = load_demand_csv(read_text(demand_csv), LAND_USE_TYPES), @@ -220,7 +218,7 @@ def run(self, data: tuple[gpd.GeoDataFrame, pd.DataFrame], record: ExperimentRec def save(self, result: dict, record: ExperimentRecord) -> ExperimentRecord: base_uri = ( record.output_path - or f"{settings.default_output_base}/experiments/{record.experiment_id}/lucc_validation" + or f"{settings.default_output_base}/experiments/{record.experiment_id}/lucc_benchmark" ) record.add_artifact( @@ -295,36 +293,21 @@ def _build_mock_raster(gdf: gpd.GeoDataFrame) -> tuple[RasterBackend, np.ndarray def _load_terrame(path: pathlib.Path) -> pd.DataFrame: + # Extract DBF from ZIP and read via geopandas (avoids manual struct parsing) with zipfile.ZipFile(path) as z: - dbf = z.read("Lab1_2014.dbf") - - nrecords = struct.unpack_from("