crc-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crc_sdk-0.1.0/LICENSE +18 -0
- crc_sdk-0.1.0/PKG-INFO +374 -0
- crc_sdk-0.1.0/README.md +340 -0
- crc_sdk-0.1.0/crc_sdk/__init__.py +31 -0
- crc_sdk-0.1.0/crc_sdk/connectors/__init__.py +35 -0
- crc_sdk-0.1.0/crc_sdk/connectors/adapters.py +285 -0
- crc_sdk-0.1.0/crc_sdk/connectors/duckdb/__init__.py +35 -0
- crc_sdk-0.1.0/crc_sdk/connectors/duckdb/connection.py +366 -0
- crc_sdk-0.1.0/crc_sdk/connectors/duckdb/geotiff.py +617 -0
- crc_sdk-0.1.0/crc_sdk/connectors/duckdb/zarr.py +461 -0
- crc_sdk-0.1.0/crc_sdk/connectors/parquet.py +323 -0
- crc_sdk-0.1.0/crc_sdk/connectors/protocols.py +13 -0
- crc_sdk-0.1.0/crc_sdk/core/__init__.py +95 -0
- crc_sdk-0.1.0/crc_sdk/fitting/__init__.py +31 -0
- crc_sdk-0.1.0/crc_sdk/fitting/workflows.py +4 -0
- crc_sdk-0.1.0/crc_sdk/geometry/__init__.py +117 -0
- crc_sdk-0.1.0/crc_sdk/geometry/admin.py +462 -0
- crc_sdk-0.1.0/crc_sdk/geometry/coverage.py +1201 -0
- crc_sdk-0.1.0/crc_sdk/geometry/formats.py +91 -0
- crc_sdk-0.1.0/crc_sdk/geometry/h3.py +418 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/__init__.py +49 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/_build.py +185 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/_geojson_sql.py +221 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/_process.py +195 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/archive.py +153 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/binaries.py +38 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/budget.py +243 -0
- crc_sdk-0.1.0/crc_sdk/geometry/pmtiles/presets.py +231 -0
- crc_sdk-0.1.0/crc_sdk/geometry/vector.py +288 -0
- crc_sdk-0.1.0/crc_sdk/impacts/__init__.py +25 -0
- crc_sdk-0.1.0/crc_sdk/impacts/custom.py +5 -0
- crc_sdk-0.1.0/crc_sdk/providers/__init__.py +21 -0
- crc_sdk-0.1.0/crc_sdk/providers/local.py +36 -0
- crc_sdk-0.1.0/crc_sdk/providers/metadata.py +1 -0
- crc_sdk-0.1.0/crc_sdk/providers/os_climate.py +270 -0
- crc_sdk-0.1.0/crc_sdk/providers/protocol.py +22 -0
- crc_sdk-0.1.0/crc_sdk/py.typed +1 -0
- crc_sdk-0.1.0/crc_sdk/schema/__init__.py +17 -0
- crc_sdk-0.1.0/crc_sdk/schema/hazard_field.py +41 -0
- crc_sdk-0.1.0/crc_sdk/types/__init__.py +20 -0
- crc_sdk-0.1.0/crc_sdk/types/dataset.py +75 -0
- crc_sdk-0.1.0/crc_sdk/types/geometry.py +12 -0
- crc_sdk-0.1.0/crc_sdk/types/hazard.py +87 -0
- crc_sdk-0.1.0/crc_sdk/types/storage.py +11 -0
- crc_sdk-0.1.0/crc_sdk/workflows/__init__.py +55 -0
- crc_sdk-0.1.0/crc_sdk/workflows/_portfolio.py +407 -0
- crc_sdk-0.1.0/crc_sdk/workflows/distributions.py +317 -0
- crc_sdk-0.1.0/crc_sdk/workflows/portfolio.py +386 -0
- crc_sdk-0.1.0/crc_sdk/workflows/tiling.py +322 -0
- crc_sdk-0.1.0/crc_sdk.egg-info/PKG-INFO +374 -0
- crc_sdk-0.1.0/crc_sdk.egg-info/SOURCES.txt +74 -0
- crc_sdk-0.1.0/crc_sdk.egg-info/dependency_links.txt +1 -0
- crc_sdk-0.1.0/crc_sdk.egg-info/requires.txt +24 -0
- crc_sdk-0.1.0/crc_sdk.egg-info/top_level.txt +1 -0
- crc_sdk-0.1.0/pyproject.toml +68 -0
- crc_sdk-0.1.0/setup.cfg +4 -0
- crc_sdk-0.1.0/tests/test_admin_lookup.py +206 -0
- crc_sdk-0.1.0/tests/test_core.py +16 -0
- crc_sdk-0.1.0/tests/test_coverage_sql.py +463 -0
- crc_sdk-0.1.0/tests/test_distribution_workflows.py +745 -0
- crc_sdk-0.1.0/tests/test_duckdb_secrets.py +54 -0
- crc_sdk-0.1.0/tests/test_geometry_h3.py +259 -0
- crc_sdk-0.1.0/tests/test_geotiff.py +285 -0
- crc_sdk-0.1.0/tests/test_imports.py +24 -0
- crc_sdk-0.1.0/tests/test_os_climate.py +370 -0
- crc_sdk-0.1.0/tests/test_parquet.py +229 -0
- crc_sdk-0.1.0/tests/test_pmtiles_archive.py +245 -0
- crc_sdk-0.1.0/tests/test_pmtiles_binaries.py +43 -0
- crc_sdk-0.1.0/tests/test_pmtiles_budget.py +173 -0
- crc_sdk-0.1.0/tests/test_pmtiles_geojson_sql.py +242 -0
- crc_sdk-0.1.0/tests/test_pmtiles_presets.py +188 -0
- crc_sdk-0.1.0/tests/test_runtime_resources.py +90 -0
- crc_sdk-0.1.0/tests/test_schema.py +24 -0
- crc_sdk-0.1.0/tests/test_types.py +141 -0
- crc_sdk-0.1.0/tests/test_vector_polyfill.py +172 -0
- crc_sdk-0.1.0/tests/test_workflows_tiling.py +789 -0
crc_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
CRC SDK
|
|
2
|
+
Copyright (C) 2026 Riskthinking.AI
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it
|
|
5
|
+
under the terms of the GNU Affero General Public License as published by
|
|
6
|
+
the Free Software Foundation, either version 3 of the License, or (at your
|
|
7
|
+
option) any later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
11
|
+
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
12
|
+
License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
The complete license text is available at:
|
|
18
|
+
<https://www.gnu.org/licenses/agpl-3.0.txt>
|
crc_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: crc-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Climate Risk Commons data access and workflows
|
|
5
|
+
Author: Riskthinking.AI
|
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Typing :: Typed
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: crc-framework<0.3,>=0.2
|
|
14
|
+
Requires-Dist: pydantic<3,>=2
|
|
15
|
+
Requires-Dist: duckdb<2,>=1
|
|
16
|
+
Requires-Dist: pyarrow>=14
|
|
17
|
+
Requires-Dist: psutil>=5
|
|
18
|
+
Requires-Dist: fsspec>=2024
|
|
19
|
+
Requires-Dist: s3fs>=2024
|
|
20
|
+
Requires-Dist: gcsfs>=2024
|
|
21
|
+
Provides-Extra: zarr
|
|
22
|
+
Requires-Dist: zarr<3,>=2.18; extra == "zarr"
|
|
23
|
+
Provides-Extra: raster
|
|
24
|
+
Requires-Dist: rasterio>=1.4; extra == "raster"
|
|
25
|
+
Provides-Extra: geometry
|
|
26
|
+
Requires-Dist: h3>=4; extra == "geometry"
|
|
27
|
+
Requires-Dist: h3ronpy>=0.19; extra == "geometry"
|
|
28
|
+
Requires-Dist: shapely>=2; extra == "geometry"
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == "test"
|
|
31
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == "test"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# CRC SDK
|
|
36
|
+
|
|
37
|
+
CRC SDK is the higher-level Python interface for Climate Risk Commons data
|
|
38
|
+
access, storage providers, geometry utilities, and analytical workflows.
|
|
39
|
+
Numerical distributions, curve fitting, impact transforms, and risk metrics are
|
|
40
|
+
provided by the versioned
|
|
41
|
+
[`crc-framework`](https://pypi.org/project/crc-framework/) dependency.
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
uv is the recommended tool for managing the development environment:
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
uv sync --all-extras
|
|
49
|
+
|
|
50
|
+
uv run pytest
|
|
51
|
+
uv run mypy
|
|
52
|
+
uv run ruff check .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or simply:
|
|
56
|
+
|
|
57
|
+
```shell
|
|
58
|
+
python -m venv .venv
|
|
59
|
+
.venv/bin/python -m pip install -e ".[zarr,raster,geometry,test]"
|
|
60
|
+
.venv/bin/python -m pytest
|
|
61
|
+
.venv/bin/python -m mypy
|
|
62
|
+
.venv/bin/python -m ruff check .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Dependencies
|
|
66
|
+
|
|
67
|
+
DuckDB, Arrow (`pyarrow`), `psutil` (resource detection), and remote-storage
|
|
68
|
+
transport (`fsspec`, `s3fs`, `gcsfs`) are baseline dependencies — every
|
|
69
|
+
connector and workflow in this SDK is built on that stack, so gating it
|
|
70
|
+
behind an extra would just move the same install onto every real caller.
|
|
71
|
+
|
|
72
|
+
Everything else is a specific data-format adapter or a pure-geometry
|
|
73
|
+
dependency, opted into only by the callers that need it:
|
|
74
|
+
|
|
75
|
+
| Extra | Adds | Used by |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `zarr` | `zarr` | `OSClimateProvider`/`ZarrRaster` (OS-Climate Zarr raster ingest) |
|
|
78
|
+
| `raster` | `rasterio` | `GeoTiffRaster` (GeoTIFF/COG ingest, streamed via GDAL VSI) |
|
|
79
|
+
| `geometry` | `h3`, `h3ronpy`, `shapely` | `H3Indexer`, `intersecting_cells`, `cell_polygon`, other abstract H3/geometry math, vectorized batch H3 ops on Arrow data (`polyfill_wkb`, `expand_polygon_candidates`, raster-to-H3 sampling) |
|
|
80
|
+
| `test` | `mypy`, `pytest`, `ruff` | Development only |
|
|
81
|
+
|
|
82
|
+
Every function that needs an extra-gated dependency imports it lazily and
|
|
83
|
+
raises a clear `ImportError` naming the extra to install if it's missing —
|
|
84
|
+
importing `crc_sdk` (or any of its subpackages) itself never requires more
|
|
85
|
+
than the baseline dependencies.
|
|
86
|
+
|
|
87
|
+
**OS-level dependency (not a pip extra):** `tippecanoe` and `tile-join`
|
|
88
|
+
(https://github.com/felt/tippecanoe) must be present on `PATH` for
|
|
89
|
+
`crc_sdk.geometry.pmtiles` — they're assumed to already be installed on the
|
|
90
|
+
runtime image, not `pip install`-able, so there's no extra for them. Verify
|
|
91
|
+
availability with `require_tippecanoe()`/`require_tile_join()`, which raise a
|
|
92
|
+
friendly, actionable error (with install instructions) if either is missing.
|
|
93
|
+
|
|
94
|
+
## Package boundaries
|
|
95
|
+
|
|
96
|
+
- `crc_sdk.core`, `crc_sdk.fitting`, and `crc_sdk.impacts` expose the stable
|
|
97
|
+
public API of `crc_framework`.
|
|
98
|
+
- `crc_sdk.connectors` handles external formats and query engines: DuckDB
|
|
99
|
+
connection helpers (`DuckDBConnection`, `RuntimeResources`, streaming
|
|
100
|
+
Parquet writes), OS-Climate Zarr ingest (`zarr` extra), and GeoTIFF/COG
|
|
101
|
+
ingest (`GeoTiffRaster`, `raster` extra) — the latter streams directly
|
|
102
|
+
from local paths or `gs://`/`s3://`/`http(s)://` URIs via GDAL's own
|
|
103
|
+
range-request support, with no local download by default.
|
|
104
|
+
- `crc_sdk.providers` describes storage and dataset discovery.
|
|
105
|
+
- `crc_sdk.geometry` contains geometry conversion, DuckDB-native H3 polyfill
|
|
106
|
+
(`H3Indexer`), Arrow batch polyfill (`polyfill_wkb`, `geometry`
|
|
107
|
+
extra for h3ronpy), raster-to-H3 sampling primitives
|
|
108
|
+
(`pixel_grid_resolution`, `sample_grid_to_h3`), exploded coverage writers
|
|
109
|
+
(`write_exploded_coverage`), optional nested lookup derivation
|
|
110
|
+
(`LookupCatalog`, `write_lookup_contract`, `write_partitioned_lookup`), and
|
|
111
|
+
PMTiles generation (`crc_sdk.geometry.pmtiles` — also reachable flattened
|
|
112
|
+
as `crc_sdk.geometry.PMTilesBuild`, etc.): `PMTilesBuild` streams a
|
|
113
|
+
GeoParquet source (a single file, or a Hive-partitioned dataset glob) into
|
|
114
|
+
one `.pmtiles` archive in one tiling pass, building the GeoParquet ->
|
|
115
|
+
GeoJSON bridge itself in DuckDB `spatial`-extension SQL
|
|
116
|
+
(`ST_AsGeoJSON`/`ST_ReducePrecision`/`ST_Transform`) rather than shelling
|
|
117
|
+
out to an external converter, streamed via the same Arrow-batched-reader
|
|
118
|
+
pattern used elsewhere in this SDK. `tippecanoe_threads`/`duckdb_threads`
|
|
119
|
+
default to every detected core (no conservative per-thread cap, unlike
|
|
120
|
+
DuckDB's own GEOS-throttled default) since tippecanoe's tile-building has
|
|
121
|
+
no documented per-thread memory ceiling. A pre-flight budget check raises
|
|
122
|
+
a clear, actionable error if a source is estimated to exceed available
|
|
123
|
+
scratch disk, rather than silently degrading into a slower multi-batch
|
|
124
|
+
fallback — provisioning more disk or narrowing the run's scope is left to
|
|
125
|
+
the caller.
|
|
126
|
+
- `crc_sdk.schema` defines columnar data contracts.
|
|
127
|
+
- `crc_sdk.types` contains SDK-owned Pydantic configuration and metadata.
|
|
128
|
+
- `crc_sdk.workflows` coordinates data access and computation.
|
|
129
|
+
|
|
130
|
+
DuckDB resource limits are detected when requested
|
|
131
|
+
(`RuntimeResources.detect` / `DuckDBConnection.for_analytics`) and relayed
|
|
132
|
+
through the connection `config` mapping. Thread count is
|
|
133
|
+
`min(cpus, usable_RAM / GiB_per_thread)` with usable RAM ≈ 60% of detected
|
|
134
|
+
memory and a default of ~2.5 GiB/thread (GEOS spatial work often slows when
|
|
135
|
+
over-threaded). `memory_limit` and `max_temp_directory_size` remain hard
|
|
136
|
+
process caps. Override with `CRC_DUCKDB_THREADS`, `CRC_DUCKDB_MEMORY`, and/or
|
|
137
|
+
`CRC_DUCKDB_BYTES_PER_THREAD_GIB`, or pass an explicit `config` dict. Set
|
|
138
|
+
`CRC_DUCKDB_PROFILE=1` to enable detailed query profiling around
|
|
139
|
+
enrich/coverage stages in h3geo.
|
|
140
|
+
|
|
141
|
+
Constructors with no natural caller-supplied directory of their own
|
|
142
|
+
(`OSClimateProvider`, `ZarrRaster`, `H3Indexer`) build a resource-tuned
|
|
143
|
+
connection by default — via `DuckDBConnection.for_analytics` — instead of a
|
|
144
|
+
bare, untuned one, so this scales out of the box with no configuration.
|
|
145
|
+
Passing an explicit `connection`/`con` always wins and skips this entirely.
|
|
146
|
+
Otherwise the spill/temp directory defaults to a stable location under the
|
|
147
|
+
system temp directory (`default_work_dir()`, not a fresh one per call), and
|
|
148
|
+
can be set per-call via each constructor's own `work_dir` parameter, or
|
|
149
|
+
globally via `CRC_DUCKDB_WORK_DIR`.
|
|
150
|
+
|
|
151
|
+
Private/authenticated remote sources (a non-public GCS/S3 bucket) are
|
|
152
|
+
configured the same idiomatic-DuckDB way as everything else here: raw
|
|
153
|
+
`CREATE OR REPLACE SECRET` SQL, passed as `setup_sql=(...)` to
|
|
154
|
+
`DuckDBConnection`/`DuckDBConnection.for_analytics` to have it run
|
|
155
|
+
automatically on `.connect()`, right after extensions load. There is
|
|
156
|
+
deliberately no secret-builder type in the SDK — DuckDB's own secret DDL
|
|
157
|
+
(https://duckdb.org/docs/configuration/secrets_manager) is already the
|
|
158
|
+
documented interface, and a caller's own connection-setup module is a more
|
|
159
|
+
natural home for its specific credentials than a generic wrapper trying to
|
|
160
|
+
track every provider/type DuckDB supports. `sql_quote`/`sql_identifier` are
|
|
161
|
+
exported for safely building that SQL; the one DuckDB quirk worth knowing is
|
|
162
|
+
that `PROVIDER` is a bare keyword (`config`, `credential_chain`, ...), not a
|
|
163
|
+
quoted string literal, unlike every other secret option:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import os
|
|
167
|
+
from crc_sdk.connectors.duckdb import DuckDBConnection, sql_quote
|
|
168
|
+
|
|
169
|
+
setup_sql = []
|
|
170
|
+
key_id, secret = os.getenv("GCS_ACCESS_KEY"), os.getenv("GCS_ACCESS_SECRET")
|
|
171
|
+
if key_id and secret:
|
|
172
|
+
setup_sql.append(
|
|
173
|
+
f"CREATE OR REPLACE SECRET gcs (TYPE GCS, KEY_ID {sql_quote(key_id)}, "
|
|
174
|
+
f"SECRET {sql_quote(secret)})"
|
|
175
|
+
)
|
|
176
|
+
con = DuckDBConnection.for_analytics(work_dir, setup_sql=setup_sql).connect()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Canonical hazard datasets
|
|
180
|
+
|
|
181
|
+
The SDK internalizes fitted hazards as one versioned Arrow/Parquet contract.
|
|
182
|
+
Rows contain a canonical unsigned H3 `cell_index`, stable `source_id`, optional
|
|
183
|
+
source WKB, scenario dimensions, and the parameters needed to reconstruct
|
|
184
|
+
either a `crc_framework.FittedDistribution` or `HurdleDistribution`.
|
|
185
|
+
`curve_shape` is nullable because Gumbel families do not use a shape parameter;
|
|
186
|
+
atom probability and location are present only when `curve_kind` is `hurdle`.
|
|
187
|
+
|
|
188
|
+
The logical row key is
|
|
189
|
+
`(hazard_name, horizon, pathway, cell_index, source_id)`. `cell_index` is the
|
|
190
|
+
spatial join key, not a globally unique identifier. Canonical files are sorted
|
|
191
|
+
by that row key for predicate pruning and merge joins.
|
|
192
|
+
|
|
193
|
+
Dataset-wide facts are stored once as a complete JSON payload under the
|
|
194
|
+
`crc.hazard.metadata` Parquet key: schema version, one uncompacted H3
|
|
195
|
+
resolution, non-exceedance probability convention, value unit and semantics,
|
|
196
|
+
WKB CRS, producer, source provenance, and creation version.
|
|
197
|
+
|
|
198
|
+
Each dataset is one self-describing Parquet file, expanded by H3 cell for
|
|
199
|
+
spatial joins. The caller chooses its full destination path and filename.
|
|
200
|
+
Writes use DuckDB, and an optional configured DuckDB connection allows the same
|
|
201
|
+
API to use its local or cloud filesystems, extensions, secrets, and settings.
|
|
202
|
+
Source knots and fit diagnostics are transient ingest inputs, not a second
|
|
203
|
+
persisted data contract.
|
|
204
|
+
|
|
205
|
+
External connectors remain source-format readers. Ingest adapters perform the
|
|
206
|
+
explicit conversion:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
external raster/table -> source curves and geometry -> selected family fit
|
|
210
|
+
-> conservative intersecting H3 cells -> canonical Arrow -> Parquet
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Boundary candidate generation uses H3 overlap coverage, not center polyfill.
|
|
214
|
+
This makes the integer join a conservative superset before an exact
|
|
215
|
+
`ST_Contains(source_geometry, asset_point)` refinement. Resolution estimates
|
|
216
|
+
report measured coverage error and expanded row count, while ingest policy
|
|
217
|
+
selects and records the dataset resolution.
|
|
218
|
+
|
|
219
|
+
OS-Climate return-period rasters can be canonicalized with
|
|
220
|
+
`OSClimateIngestPolicy` and `canonicalize_os_climate`. The caller must choose
|
|
221
|
+
the distribution family and, for zero-heavy hazards, provide an explicit
|
|
222
|
+
`HurdleFitPolicy`; the SDK does not infer an exact point mass from sparse
|
|
223
|
+
knots. Plain curves use `fit_quantiles`, while hurdle curves use
|
|
224
|
+
`fit_hurdle_quantiles`. `LocalProvider` queries persisted hazard rows through
|
|
225
|
+
`HazardQuery`.
|
|
226
|
+
|
|
227
|
+
### Evaluating asset portfolios at return periods
|
|
228
|
+
|
|
229
|
+
Canonical curve parameters can be evaluated for a portfolio without returning
|
|
230
|
+
to the external source format or refitting the data. The workflow joins every
|
|
231
|
+
asset to its canonical curve and writes one row per asset, hazard, horizon, and
|
|
232
|
+
pathway, with one value column per requested return period:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
import pyarrow as pa
|
|
236
|
+
|
|
237
|
+
from crc_sdk.workflows import HazardDataset
|
|
238
|
+
|
|
239
|
+
assets = pa.table(
|
|
240
|
+
{
|
|
241
|
+
"asset_id": ["warehouse-a", "warehouse-b"],
|
|
242
|
+
"longitude": [6.9603, 7.5010],
|
|
243
|
+
"latitude": [50.9375, 51.0030],
|
|
244
|
+
"sector": ["logistics", "manufacturing"],
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
result = (
|
|
249
|
+
HazardDataset.local("flood.parquet")
|
|
250
|
+
.for_assets(assets)
|
|
251
|
+
.select(horizons=[2050], pathways=["ssp585"])
|
|
252
|
+
.return_periods([25, 50, 100, 250, 500, 1000])
|
|
253
|
+
.write_parquet("portfolio-flood.parquet")
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The resulting value columns are `value_rp25`, `value_rp50`, `value_rp100`,
|
|
258
|
+
`value_rp250`, `value_rp500`, and `value_rp1000`. For upper-tail hazards, each
|
|
259
|
+
return period `RP` is evaluated at non-exceedance probability `1 - 1/RP`.
|
|
260
|
+
Value unit, value semantics, and the complete return-period/probability/column
|
|
261
|
+
mapping are stored under `crc.hazard.evaluation` in Parquet metadata.
|
|
262
|
+
|
|
263
|
+
An impact function can replace the sampled hazard values with event-aligned
|
|
264
|
+
impact values before writing:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
import numpy as np
|
|
268
|
+
|
|
269
|
+
impact_result = (
|
|
270
|
+
HazardDataset.local("flood.parquet")
|
|
271
|
+
.for_assets(assets)
|
|
272
|
+
.return_periods([25, 100, 250])
|
|
273
|
+
.impact(
|
|
274
|
+
lambda depth: np.clip(depth / 2.0, 0.0, 1.0),
|
|
275
|
+
name="depth_damage_ratio",
|
|
276
|
+
value_unit="fraction",
|
|
277
|
+
value_semantics="damage ratio",
|
|
278
|
+
)
|
|
279
|
+
.write_parquet(
|
|
280
|
+
"portfolio-impact.parquet",
|
|
281
|
+
execution=ExecutionOptions(max_workers=1),
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The SDK first samples each hazard return period and then calls
|
|
287
|
+
`impact.evaluate(...)` on that row's value vector. Therefore
|
|
288
|
+
`value_rp100 = impact(hazard_rp100)`: the return period continues to identify
|
|
289
|
+
the source hazard event. This differs intentionally from transforming a full
|
|
290
|
+
distribution and then taking an impact quantile, which can reorder decreasing
|
|
291
|
+
or non-monotonic impacts. Use the distribution interface in `crc-framework`
|
|
292
|
+
for that risk-analysis interpretation.
|
|
293
|
+
|
|
294
|
+
Built-in and registry-backed framework impacts use the same fluent method:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from crc_sdk.impacts import PiecewiseLinearImpact, impacts
|
|
298
|
+
from crc_sdk.workflows import ImpactContextColumns
|
|
299
|
+
|
|
300
|
+
damage_curve = PiecewiseLinearImpact(
|
|
301
|
+
exposure=[0.0, 0.2, 1.0, 2.0],
|
|
302
|
+
impact=[0.0, 0.0, 0.25, 1.0],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
request = request.impact(
|
|
306
|
+
damage_curve,
|
|
307
|
+
name="flood_damage_ratio",
|
|
308
|
+
value_unit="fraction",
|
|
309
|
+
value_semantics="damage ratio",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
registry_request = request.impact(
|
|
313
|
+
impacts.for_factor("inundation"),
|
|
314
|
+
context=ImpactContextColumns(
|
|
315
|
+
country="country",
|
|
316
|
+
continent="continent",
|
|
317
|
+
building_type="building_type",
|
|
318
|
+
historic_mean="historic_mean",
|
|
319
|
+
),
|
|
320
|
+
name="inundation_impact",
|
|
321
|
+
value_unit="fraction",
|
|
322
|
+
value_semantics="damage ratio",
|
|
323
|
+
)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The generated H3 `cell_index` is always supplied to the framework impact
|
|
327
|
+
context. Configured context columns are read from each asset, including when
|
|
328
|
+
they are not retained as output passthrough columns. Stored registry context
|
|
329
|
+
provides fallback values for fields without an asset value. Impact metadata
|
|
330
|
+
records the event-aligned interpretation, source hazard units and semantics,
|
|
331
|
+
output units and semantics, function name/type, and context-column mapping.
|
|
332
|
+
|
|
333
|
+
Framework impact objects and top-level Python callables can run in the existing
|
|
334
|
+
process pool. Lambdas and closures are not picklable, so they run serially when
|
|
335
|
+
the worker count is implicit; explicitly requesting more than one worker for
|
|
336
|
+
one raises an error.
|
|
337
|
+
|
|
338
|
+
Point assets are converted to the H3 resolution recorded by the canonical
|
|
339
|
+
dataset. The H3 join is refined with `ST_Covers(source_geometry, asset_point)`
|
|
340
|
+
when source WKB is present; rows without WKB retain cell-level precision.
|
|
341
|
+
`source_id` and `spatial_match` (`exact_geometry` or `h3_cell`) remain in the
|
|
342
|
+
output. Multiple source curves for one asset/hazard/horizon/pathway raise
|
|
343
|
+
instead of being silently selected or aggregated, and missing asset/scenario
|
|
344
|
+
matches raise rather than being dropped from the output.
|
|
345
|
+
|
|
346
|
+
When assets already contain canonical H3 indexes, use
|
|
347
|
+
`cell_index_column="cell_index"` instead of longitude/latitude columns. This
|
|
348
|
+
avoids point conversion and exact source-geometry refinement:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
(
|
|
352
|
+
HazardDataset.local("flood.parquet")
|
|
353
|
+
.for_assets(assets_with_cells)
|
|
354
|
+
.return_periods([25, 50, 100, 250, 500, 1000])
|
|
355
|
+
.write_parquet("portfolio-flood.parquet")
|
|
356
|
+
)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Arrow tables can be registered directly as shown above. A `Path` reads an
|
|
360
|
+
asset Parquet file, while a string is treated as caller-supplied DuckDB SQL.
|
|
361
|
+
Output evaluation is streamed in bounded Arrow batches to compressed Parquet.
|
|
362
|
+
For already selected canonical rows, `distribution_from_hazard_row` remains
|
|
363
|
+
available as the low-level curve reconstruction utility.
|
|
364
|
+
|
|
365
|
+
The common column names `asset_id`, `longitude`/`latitude`, and `cell_index`
|
|
366
|
+
are inferred. Use `AssetPortfolio`, `PointColumns`, or `CellColumn` only for a
|
|
367
|
+
nonstandard asset schema. Worker, batch, and connection controls are grouped
|
|
368
|
+
under `ExecutionOptions` on `write_parquet`, keeping execution tuning out of
|
|
369
|
+
the normal workflow.
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
CRC SDK is licensed under the GNU Affero General Public License, version 3 or
|
|
374
|
+
later.
|