Climate-impact crop suitability — historical vs SSP scenarios¶
This notebook walks through the TerraFlow climate-impact pipeline introduced in
v0.5.0 (issue #138). The pipeline runs the historical suitability score and a
scenario × rule fan-out (e.g. growing-degree-days, frost days, SPEI) across one or
more climate scenarios in a single terraflow run call.
Outputs land under <output_dir>/runs/<fingerprint>/:
features.parquet— historical per-cell suitability (unchanged from v0.4.x).climate_features.parquet— one column per<rule>__<scenario>pair, cell-indexed; merge withfeatures.parquetoncell_id.
The example below uses a tiny synthetic raster + station time-series so the notebook executes in under 30 seconds. Swap the file paths for a real CDL TIF + bias-corrected CMIP6 station CSV to reproduce the demo at full scale.
1. Build a tiny synthetic dataset¶
9 stations × 4 years × 2 scenario windows. The raster covers a 6 × 6-cell patch in western Kansas (WGS84).
import textwrap
from pathlib import Path
import numpy as np
import pandas as pd
import rasterio
from rasterio.transform import from_origin
from rasterio.crs import CRS
work = Path('_climate_impact_demo'); work.mkdir(exist_ok=True)
raster = work / 'cdl.tif'
climate_csv = work / 'climate.csv'
ts_csv = work / 'timeseries.csv'
out = work / 'outputs'
transform = from_origin(-100.5, 40.0, 0.1, 0.1)
arr = np.random.default_rng(0).integers(1, 200, size=(6, 6), dtype=np.uint8)
with rasterio.open(raster, 'w', driver='GTiff', width=6, height=6, count=1,
dtype='uint8', crs=CRS.from_epsg(4326), transform=transform) as dst:
dst.write(arr, 1)
pd.DataFrame({'lat':[39.8,39.6,39.4],'lon':[-100.4,-100.2,-100.0],
'mean_temp':[15.0,17.0,19.0],'total_rain':[120.0,140.0,160.0]}
).to_csv(climate_csv, index=False)
stations = [('S1',39.8,-100.4),('S2',39.6,-100.2),('S3',39.4,-100.0)]
rows = []
rng = np.random.default_rng(7)
for yr_min, yr_max, t_off in [(1991,1992,0.0), (2041,2042,3.0)]:
dates = pd.date_range(f'{yr_min}-01-01', f'{yr_max}-12-31', freq='D')
doy = dates.dayofyear.to_numpy()
seasonal = 12 + 12 * np.sin(2*np.pi*(doy-105)/365.0)
for sid, lat, lon in stations:
rows.append(pd.DataFrame({
'station_id': sid, 'lat': lat, 'lon': lon,
'date': dates,
'temperature_c': np.round(seasonal + t_off + (lon+100)*0.3 + rng.normal(0,1.2,len(dates)), 2),
'precipitation_mm': np.round(rng.gamma(1.4, 1.3, len(dates)), 2),
}))
pd.concat(rows, ignore_index=True).to_csv(ts_csv, index=False)
print(f'raster: {raster}\nclimate: {climate_csv}\ntimeseries: {ts_csv}')
raster: _climate_impact_demo/cdl.tif climate: _climate_impact_demo/climate.csv timeseries: _climate_impact_demo/timeseries.csv
2. Define the climate-impact config¶
Three signals worth flagging:
climate.timeseries_csvis the new input — required whentemporal_aggregationsis set.temporal_aggregationsdeclares the rules to compute per station.scenariosdeclares the year windows; the pipeline filters the time-series per scenario before aggregating.
cfg_text = textwrap.dedent(f'''
raster_path: "{raster.resolve()}"
climate_csv: "{climate_csv.resolve()}"
output_dir: "{out.resolve()}"
roi: {{ type: bbox, xmin: -100.5, ymin: 39.4, xmax: -99.9, ymax: 40.0 }}
model_params: {{ v_min: 0, v_max: 255, t_min: 0, t_max: 40, r_min: 0, r_max: 300,
w_v: 0.4, w_t: 0.3, w_r: 0.3 }}
climate:
strategy: spatial
interpolation_method: linear
timeseries_csv: "{ts_csv.resolve()}"
temporal_aggregations:
- {{ kind: annual_mean }}
- {{ kind: growing_degree_days, base_temp_c: 10.0 }}
- {{ kind: frost_days, threshold_c: 0.0 }}
scenarios:
- {{ name: historical, period: [1991, 1992] }}
- {{ name: ssp245, period: [2041, 2042] }}
''')
cfg_path = work / 'cfg.yml'
cfg_path.write_text(cfg_text)
print(cfg_text)
raster_path: "/Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/cdl.tif"
climate_csv: "/Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/climate.csv"
output_dir: "/Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/outputs"
roi: { type: bbox, xmin: -100.5, ymin: 39.4, xmax: -99.9, ymax: 40.0 }
model_params: { v_min: 0, v_max: 255, t_min: 0, t_max: 40, r_min: 0, r_max: 300,
w_v: 0.4, w_t: 0.3, w_r: 0.3 }
climate:
strategy: spatial
interpolation_method: linear
timeseries_csv: "/Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/timeseries.csv"
temporal_aggregations:
- { kind: annual_mean }
- { kind: growing_degree_days, base_temp_c: 10.0 }
- { kind: frost_days, threshold_c: 0.0 }
scenarios:
- { name: historical, period: [1991, 1992] }
- { name: ssp245, period: [2041, 2042] }
3. Run the pipeline¶
run_pipeline auto-detects the climate-impact configuration and writes the
extra climate_features.parquet artifact alongside the standard one.
from terraflow.pipeline import run_pipeline
df = run_pipeline(cfg_path)
run_dir = Path(df.attrs['run_dir'])
print('run_dir:', run_dir)
print('artifacts:', sorted(p.name for p in run_dir.iterdir()))
INFO:terraflow:Loaded config from _climate_impact_demo/cfg.yml
INFO:terraflow:Computed run fingerprint 6C2vpEl2jkZEjY-u65AV4WPqfCMmtxfVfSVilTci-Xo (config=fd070cec6614d42b57c40f3f4c59c04d96a79c15a5e24df67428202d0371fb36, inputs=3)
INFO:terraflow:DataCatalog built: raster=cdl.tif (CRS EPSG:4326, shape (6, 6)), climate=climate.csv (3 rows, vars=['mean_temp', 'total_rain'])
INFO:terraflow:Loaded raster from /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/cdl.tif
INFO:terraflow:Loaded climate CSV from /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/climate.csv with 3 rows
INFO:terraflow:Climate variables: ['mean_temp', 'total_rain']
INFO:terraflow:Climate CSV validated successfully: 3 valid records
INFO:terraflow:Loaded raster: /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/cdl.tif (CRS: EPSG:4326)
INFO:terraflow:Loaded climate data: /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/climate.csv
INFO:terraflow:Clipped raster to ROI
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['mean_temp', 'total_rain']
INFO:terraflow:Initialized climate interpolator with strategy='spatial', method='linear'
INFO:terraflow:Sampled 36 cells from 36 valid cells in ROI
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow:Interpolated climate for 36 cells using strategy='spatial'
INFO:terraflow:Closed raster dataset
INFO:terraflow:Climate-impact features: 3 rules × 2 scenarios → 6 derived columns
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['annual_mean__historical']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['growing_degree_days__base10__historical']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['frost_days__t0__historical']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['annual_mean__ssp245']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['growing_degree_days__base10__ssp245']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow.climate:ClimateInterpolator initialised: strategy='spatial', interpolation_method='linear', variogram_mode='standard', records=3, variables=['frost_days__t0__ssp245']
INFO:terraflow.climate:Linear interpolation failed (QH6154 Qhull precision error: Initial simplex is flat (facet 1 is coplanar with the interior point)
While executing: | qhull d Qbb Qt Q12 Qz Qc
Options selected for Qhull 2020.2.r 2020/08/31:
run-id 2096181653 delaunay Qbbound-last Qtriangulate Q12-allow-wide
Qz-infinity-point Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood _max-width 0.4 Error-roundoff 1.4e-13 _one-merge 9.8e-13
Visible-distance 2.8e-13 U-max-coplanar 2.8e-13 Width-outside 5.6e-13
_wide-facet 1.7e-12 _maxoutside 1.1e-12
The input to qhull appears to be less than 3 dimensional, or a
computation has overflowed.
Qhull could not construct a clearly convex simplex from points:
- p1(v4): 40 -1e+02 4.4
- p3(v3): 40 -1e+02 1e+02
- p0(v2): 40 -1e+02 8.8
- p2(v1): 39 -1e+02 -2.9e-14
The center point is coplanar with a facet, or a vertex is coplanar
with a neighboring facet. The maximum round off error for
computing distances is 1.4e-13. The center point, facets and distances
to the center point are as follows:
center point 39.6 -100.2 28.39
facet p3 p0 p2 distance= -4.5e-15
facet p1 p0 p2 distance= -9.6e-12
facet p1 p3 p2 distance= -5e-16
facet p1 p3 p0 distance= 6e-15
These points either have a maximum or minimum x-coordinate, or
they maximize the determinant for k coordinates. Trial points
are first selected from points that maximize a coordinate.
The min and max coordinates for each dimension are:
0: 39.4 39.8 difference= 0.4
1: -100.4 -2.225e-308 difference= 100.4
2: -2.917e-14 100.4 difference= 100.4
If the input should be full dimensional, you have several options that
may determine an initial simplex:
- use 'QJ' to joggle the input and make it full dimensional
- use 'QbB' to scale the points to the unit cube
- use 'QR0' to randomly rotate the input for different maximum points
- use 'Qs' to search all points for the initial simplex
- use 'En' to specify a maximum roundoff error less than 1.4e-13.
- trace execution with 'T3' to see the determinant for each point.
If the input is lower dimensional:
- use 'QJ' to joggle the input and make it full dimensional
- use 'Qbk:0Bk:0' to delete coordinate k from the input. You should
pick the coordinate with the least range. The hull will have the
correct topology.
- determine the flat containing the points, rotate the points
into a coordinate plane, and delete the other coordinates.
- add one or more points to make the input full dimensional.
), falling back to nearest-neighbor
INFO:terraflow:Wrote climate_features.parquet (6 columns) → /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/outputs/runs/6C2vpEl2jkZEjY-u65AV4WPqfCMmtxfVfSVilTci-Xo/climate_features.parquet
INFO:terraflow:Artifacts written to /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/outputs/runs/6C2vpEl2jkZEjY-u65AV4WPqfCMmtxfVfSVilTci-Xo (fingerprint=6C2vpEl2jkZEjY-u65AV4WPqfCMmtxfVfSVilTci-Xo, cells=36, total=0.10s)
run_dir: /Users/marupilla/workspace/projects/oss/AgroTerraFlow/docs/notebooks/_climate_impact_demo/outputs/runs/6C2vpEl2jkZEjY-u65AV4WPqfCMmtxfVfSVilTci-Xo artifacts: ['climate_features.parquet', 'features.parquet', 'manifest.json', 'report.json', 'results.csv']
4. Inspect the climate-impact output¶
Columns are named <rule_label>__<scenario_name>. The shape is (n_cells, n_rules × n_scenarios + 1).
cf = pd.read_parquet(run_dir / 'climate_features.parquet')
merged = df.merge(cf, on='cell_id')
merged[[c for c in merged.columns if '__' in c or c in ('cell_id','score','label')]].head()
| cell_id | score | label | annual_mean__historical | growing_degree_days__base10__historical | frost_days__t0__historical | annual_mean__ssp245 | growing_degree_days__base10__ssp245 | frost_days__t0__ssp245 | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0.570245 | medium | 11.917756 | 3533.65 | 43.0 | 14.972849 | 4865.62 | 1.0 |
| 1 | 1 | 0.521618 | medium | 11.917756 | 3533.65 | 43.0 | 14.972849 | 4865.62 | 1.0 |
| 2 | 2 | 0.349069 | medium | 11.917756 | 3533.65 | 43.0 | 14.972849 | 4865.62 | 1.0 |
| 3 | 3 | 0.554559 | medium | 11.917756 | 3533.65 | 43.0 | 14.972849 | 4865.62 | 1.0 |
| 4 | 4 | 0.573873 | medium | 11.966402 | 3548.69 | 51.0 | 14.952274 | 4851.92 | 1.0 |
5. Compare historical vs future¶
Subtracting paired scenario columns reveals the climate-shift signal at cell resolution — without re-running the suitability score.
deltas = pd.DataFrame({'cell_id': cf['cell_id']})
for rule_base in ['annual_mean', 'growing_degree_days__base10', 'frost_days__0p0']:
hist = f'{rule_base}__historical'
fut = f'{rule_base}__ssp245'
if hist in cf.columns and fut in cf.columns:
deltas[f'delta_{rule_base}'] = cf[fut] - cf[hist]
deltas.describe().round(2)
| cell_id | delta_annual_mean | delta_growing_degree_days__base10 | |
|---|---|---|---|
| count | 36.00 | 36.00 | 36.00 |
| mean | 17.50 | 3.04 | 1321.46 |
| std | 10.54 | 0.02 | 12.69 |
| min | 0.00 | 2.99 | 1303.23 |
| 25% | 8.75 | 3.03 | 1308.02 |
| 50% | 17.50 | 3.06 | 1331.97 |
| 75% | 26.25 | 3.06 | 1331.97 |
| max | 35.00 | 3.06 | 1331.97 |
What changed vs v0.4.x¶
terraflowno longer ships GeoAI inference (terraflow geoai) or H3 export (terraflow export --format h3). Both were thin wrappers that didn't earn Methods-section citations.- Validation narrowed to spatial block CV — Cohen's κ + Moran's I removed
(call
sklearn.metrics/esda.Morandirectly onfeatures.parquet). - New
climate_features.parquetsibling artifact — the historicalfeatures.parquetcontract is unchanged.
See docs/migration-v0.4-to-v0.5.md for the full diff and examples/ demo_config_climate_impact.yml for the full-scale demo (Kansas wheat,
historical + SSP2-4.5 + SSP5-8.5).