windkit 2.0.0__tar.gz → 2.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.
Files changed (138) hide show
  1. windkit-2.1.0/.triage-policies.yml +47 -0
  2. {windkit-2.0.0 → windkit-2.1.0}/CHANGELOG.md +124 -6
  3. {windkit-2.0.0 → windkit-2.1.0}/PKG-INFO +56 -34
  4. windkit-2.1.0/pyproject.toml +118 -0
  5. windkit-2.1.0/setup.cfg +4 -0
  6. {windkit-2.0.0 → windkit-2.1.0}/windkit/__init__.py +12 -3
  7. {windkit-2.0.0 → windkit-2.1.0}/windkit/_errors.py +1 -1
  8. windkit-2.1.0/windkit/_version.py +24 -0
  9. {windkit-2.0.0 → windkit-2.1.0}/windkit/cfd.py +5 -3
  10. windkit-2.1.0/windkit/integrations/_earth_engine.py +337 -0
  11. {windkit-2.0.0 → windkit-2.1.0}/windkit/integrations/_gwa_map_api.py +46 -4
  12. {windkit-2.0.0 → windkit-2.1.0}/windkit/integrations/_planetary_computer.py +5 -5
  13. windkit-2.1.0/windkit/io/__init__.py +12 -0
  14. windkit-2.1.0/windkit/io/wasp/__init__.py +14 -0
  15. windkit-2.1.0/windkit/io/wasp/_common.py +31 -0
  16. windkit-2.1.0/windkit/io/wasp/lib.py +198 -0
  17. windkit-2.1.0/windkit/io/wasp/owc.py +90 -0
  18. windkit-2.1.0/windkit/io/wasp/tab.py +149 -0
  19. windkit-2.1.0/windkit/io/windio/__init__.py +86 -0
  20. windkit-2.1.0/windkit/io/windio/readers.py +410 -0
  21. windkit-2.1.0/windkit/io/windio/translate/__init__.py +49 -0
  22. windkit-2.1.0/windkit/io/windio/translate/bwc.py +515 -0
  23. windkit-2.1.0/windkit/io/windio/translate/common.py +1298 -0
  24. windkit-2.1.0/windkit/io/windio/translate/tswc.py +339 -0
  25. windkit-2.1.0/windkit/io/windio/translate/wind_turbines.py +338 -0
  26. windkit-2.1.0/windkit/io/windio/translate/wtg.py +399 -0
  27. windkit-2.1.0/windkit/io/windio/translate/wwc.py +411 -0
  28. windkit-2.1.0/windkit/io/windio/utils.py +385 -0
  29. windkit-2.1.0/windkit/io/windio/writers.py +360 -0
  30. {windkit-2.0.0 → windkit-2.1.0}/windkit/ltc/__init__.py +1 -1
  31. {windkit-2.0.0 → windkit-2.1.0}/windkit/ltc/_validation.py +2 -2
  32. {windkit-2.0.0 → windkit-2.1.0}/windkit/ltc/mcp.py +122 -54
  33. {windkit-2.0.0 → windkit-2.1.0}/windkit/ltc/regression.py +39 -24
  34. {windkit-2.0.0 → windkit-2.1.0}/windkit/ltc/scores.py +6 -11
  35. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/__init__.py +2 -1
  36. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/_colormaps.py +1 -1
  37. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/_helpers.py +1 -1
  38. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/histogram.py +246 -35
  39. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/operational_curves.py +1 -1
  40. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/raster_plot.py +1 -1
  41. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/roughness_rose.py +1 -1
  42. windkit-2.1.0/windkit/plot/spatial_plots.py +264 -0
  43. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/time_series.py +1 -1
  44. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/vertical_profile.py +1 -1
  45. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/wind_rose.py +1 -1
  46. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/__init__.py +2 -1
  47. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_bbox.py +21 -4
  48. windkit-2.1.0/windkit/spatial/_conversions.py +42 -0
  49. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_crs.py +23 -5
  50. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_dimensions.py +1 -1
  51. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_interpolation.py +144 -15
  52. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_point.py +194 -86
  53. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_raster.py +16 -2
  54. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_struct.py +138 -1
  55. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_utm.py +1 -1
  56. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_vector.py +1 -1
  57. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_vertical.py +1 -1
  58. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/spatial.py +90 -29
  59. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/_vectormap_gml.py +1 -1
  60. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/_vectormap_helpers.py +1 -1
  61. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/get_map.py +17 -6
  62. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/landcover.py +1 -1
  63. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversion/lines_to_poly.py +6 -1
  64. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversion/poly_to_lines.py +8 -1
  65. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversions.py +3 -0
  66. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/raster_map.py +122 -19
  67. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/topography.py +1 -1
  68. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/vector_map.py +60 -12
  69. windkit-2.1.0/windkit/tutorial_data.py +313 -0
  70. {windkit-2.0.0 → windkit-2.1.0}/windkit/utils.py +1 -1
  71. {windkit-2.0.0 → windkit-2.1.0}/windkit/weibull.py +16 -5
  72. {windkit-2.0.0 → windkit-2.1.0}/windkit/weng_workspace.py +1 -1
  73. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind.py +27 -19
  74. windkit-2.1.0/windkit/wind_climate/_synthetic_time_series.py +279 -0
  75. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/binned_wind_climate.py +179 -183
  76. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/generalized_wind_climate.py +112 -241
  77. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/geostrophic_wind_climate.py +5 -35
  78. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/predicted_wind_climate.py +3 -1
  79. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/time_series_wind_climate.py +485 -38
  80. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/weibull_wind_climate.py +106 -13
  81. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/wind_climate.py +117 -141
  82. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_data.py +1 -1
  83. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/loss.py +1 -1
  84. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/uncertainty.py +1 -1
  85. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/wind_turbine_generator.py +87 -32
  86. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/wind_turbines.py +176 -0
  87. {windkit-2.0.0 → windkit-2.1.0}/windkit/workspace.py +1 -1
  88. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/_validate.py +1 -1
  89. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/data_structures.py +1 -1
  90. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/direction.py +1 -1
  91. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/empty.py +8 -8
  92. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/metadata.py +1 -1
  93. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/sector.py +1 -1
  94. {windkit-2.0.0 → windkit-2.1.0}/windkit/xarray_structures/wsbin.py +1 -1
  95. {windkit-2.0.0 → windkit-2.1.0}/windkit.egg-info/SOURCES.txt +21 -2
  96. windkit-2.0.0/PERFORMANCE_CI_PLAN.md +0 -294
  97. windkit-2.0.0/pyproject.toml +0 -15
  98. windkit-2.0.0/setup.cfg +0 -66
  99. windkit-2.0.0/windkit/_version.py +0 -34
  100. windkit-2.0.0/windkit/integrations/_earth_engine.py +0 -241
  101. windkit-2.0.0/windkit/tutorial_data.py +0 -188
  102. {windkit-2.0.0 → windkit-2.1.0}/LICENSE +0 -0
  103. {windkit-2.0.0 → windkit-2.1.0}/README.md +0 -0
  104. {windkit-2.0.0 → windkit-2.1.0}/windkit/_rvea_xml.py +0 -0
  105. {windkit-2.0.0 → windkit-2.1.0}/windkit/_weibull_nb.py +0 -0
  106. {windkit-2.0.0 → windkit-2.1.0}/windkit/config.py +0 -0
  107. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/all_vars.json +0 -0
  108. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/color_table_roughness.json +0 -0
  109. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/CGLS-LC100.json +0 -0
  110. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/CORINE.json +0 -0
  111. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/ESA_CCI.json +0 -0
  112. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/GWA4_micro.json +0 -0
  113. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/Globcover.json +0 -0
  114. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/MODIS.json +0 -0
  115. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/NEWA_micro_v2.json +0 -0
  116. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/landcover_tables/WorldCover.json +0 -0
  117. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/loss_tables/dtu_loss_table.csv +0 -0
  118. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/map_file_header_to_epsg.json +0 -0
  119. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/map_file_header_to_epsg_read.json +0 -0
  120. {windkit-2.0.0 → windkit-2.1.0}/windkit/data/uncertainty_tables/dtu_uncertainty_table.csv +0 -0
  121. {windkit-2.0.0 → windkit-2.1.0}/windkit/import_manager.py +0 -0
  122. {windkit-2.0.0 → windkit-2.1.0}/windkit/integrations/__init__.py +0 -0
  123. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/color.py +0 -0
  124. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/elevation_map.py +0 -0
  125. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/landcover_map.py +0 -0
  126. {windkit-2.0.0 → windkit-2.1.0}/windkit/plot/wind_turbine.py +0 -0
  127. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/README.md +0 -0
  128. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_cuboid.py +0 -0
  129. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_latitude.py +0 -0
  130. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/_metpy_natural_neighbors.py +0 -0
  131. {windkit-2.0.0 → windkit-2.1.0}/windkit/spatial/decorators.py +0 -0
  132. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/__init__.py +0 -0
  133. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversion/__init__.py +0 -0
  134. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversion/_polygons_constraints.py +0 -0
  135. {windkit-2.0.0 → windkit-2.1.0}/windkit/topography/map_conversion/helper_functions.py +0 -0
  136. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_climate/__init__.py +0 -0
  137. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/__init__.py +0 -0
  138. {windkit-2.0.0 → windkit-2.1.0}/windkit/wind_farm/power_curve.py +0 -0
@@ -0,0 +1,47 @@
1
+ resource_rules:
2
+ issues:
3
+ rules:
4
+ # Label issues as Stale after 6 months of inactivity
5
+ - name: Label stale issues
6
+ conditions:
7
+ state: opened
8
+ date:
9
+ attribute: updated_at
10
+ condition: older_than
11
+ interval_type: months
12
+ interval: 6
13
+ forbidden_labels:
14
+ - Stale
15
+ - "Status: Blocked"
16
+ - Meta-issue
17
+ actions:
18
+ labels:
19
+ - Stale
20
+ comment: |
21
+ This issue has been inactive for 6 months and has been labeled `~Stale`.
22
+
23
+ If this issue is still relevant, please comment or update it to reset
24
+ the inactivity timer. Otherwise, it will be automatically closed in
25
+ 3 months.
26
+
27
+ # Close issues 3 months after the Stale label was added
28
+ - name: Close stale issues
29
+ conditions:
30
+ state: opened
31
+ date:
32
+ attribute: updated_at
33
+ condition: older_than
34
+ interval_type: months
35
+ interval: 3
36
+ labels:
37
+ - Stale
38
+ forbidden_labels:
39
+ - "Status: Blocked"
40
+ - Meta-issue
41
+ actions:
42
+ status: close
43
+ comment: |
44
+ This issue has been inactive and is being closed
45
+ automatically per our [stale issue policy](CONTRIBUTING.md#stale-issue-policy).
46
+
47
+ If this issue is still relevant, feel free to reopen it.
@@ -2,14 +2,124 @@
2
2
 
3
3
  All major changes are listed here.
4
4
 
5
- ## 2.0.0 (2025-07-21)
5
+ ## Unreleased
6
+
7
+ ## [2.1.0 (2026-04-22)](https://docs.wasp.dk/windkit/v2.1.0/)
8
+
9
+ ### Highlights
10
+
11
+ * **LinRegMCP with residual noise** — new `LinRegMCP.predict_with_noise()` samples Gaussian residuals per sector to recover realistic wind-speed distributions that preserve variance. `MCPRegressor.predict(return_std=True)` exposes the per-timestep residual standard deviation, and `LinearRegression.residual_std` is computed and stored during `fit()`.
12
+ * **New `windkit.io.wasp` low-level subpackage** — read and write `.tab`, `.lib`, and `.owc` / `.omwc` files as plain dicts with numpy arrays, without xarray overhead. `read_tab` / `write_tab` / `format_tab`, `read_lib` / `write_lib` / `format_lib`, `read_owc` / `parse_owc_element`.
13
+ * **Correlated synthetic TSWC data** — `create_tswc(not_empty=True)` now generates temporally and spatially correlated wind data using an AR(1) / Weibull model (one year at 10-minute frequency by default). New `create_tswc_pair` creates correlated TSWC pairs with configurable speed bias, directional bias, and target correlation.
14
+ * **windIO format support (experimental)** — new `windkit.io.windio` subpackage adds readers and writers for the IEA [windIO format](https://ieawindsystems.github.io/windIO/), covering BWC / WWC / TSWC wind climates, wind turbines / WTGs, and full plant composition. Top-level readers and writers (`read_wwc`, `read_bwc`, `read_tswc`, `read_wind_turbines`, `read_wtg`, and their `*_to_file` counterparts) now accept YAML. See *New features* below for the full API.
15
+ * **Dependency minimum bumps (breaking)** — minimum supported versions raised: numpy 2.0, xarray 2024.9, pandas 2.3, scipy 1.13, geopandas 1.0, rasterio 1.4, pyproj 3.7, lxml 5.2, netcdf4 1.7. Users on older versions must upgrade.
16
+
17
+ ### New features
18
+
19
+ * New `windkit.plot.spatial_structure_2d` and `windkit.plot.spatial_structure_3d` functions for visualising the geometry of WindKit spatial objects (`point`, `stacked_point`, `raster`, `cuboid`). Both functions accept an optional `ax` argument and support subsampling for large datasets. The 3D function also draws vertical tower lines for multi-height structures (`stacked_point`, `cuboid`), which can be toggled via `show_tower_lines`.
20
+ * `create_tswc` now generates temporally and spatially correlated synthetic wind data using an AR(1)/Weibull model when `not_empty=True`, replacing the previous uncorrelated random fill. The default date range is now one year at 10-min frequency (previously ten years).
21
+ * New `create_tswc_pair` function for generating correlated TSWC pairs with configurable speed bias, directional bias, and target correlation.
22
+ * New low-level `windkit.io.wasp` subpackage for reading and writing WAsP file formats without xarray overhead. Initial support for `.tab` files via `read_tab`, `write_tab`, and `format_tab`. These return plain dicts with numpy arrays and use normalized (0-1) frequency values.
23
+ * Added `.lib` file support to `windkit.io.wasp` via `read_lib`, `write_lib`, and `format_lib`. These return plain dicts with numpy arrays and use normalized (0-1) direction frequency values.
24
+ * Added `.owc`/`.omwc` file support to `windkit.io.wasp` via `read_owc` and `parse_owc_element`. These parse WAsP observed wind climate XML files and return plain dicts with numpy arrays.
25
+ * New `style="combined"` option for `histogram()` that shows the marginal (all-sector average) wind speed distribution as a stepped line, per-sector conditional distributions as faint stepped lines, an optional emergent Weibull curve, and a metrics annotation box with mean wind speed, power density, and Weibull parameters.
26
+ * Added `vrt` to the raster map supported file formats.
27
+ * `create_geowc` now accepts a `wsbin_width` argument to control the wind-speed bin width on the returned geostrophic wind climate.
28
+ * New `LinRegMCP.predict_with_noise()` method for predicting with Gaussian noise sampled from the per-sector residual standard deviation, recovering realistic wind speed distributions that preserve variance.
29
+ * `MCPRegressor.predict()` now accepts `return_std=True` to optionally return per-timestep residual standard deviations alongside predictions.
30
+ * `LinearRegression.residual_std` is now computed and stored during `fit()`, providing the per-sector residual standard deviation.
31
+
32
+ #### windIO format support (experimental)
33
+
34
+ Added support for reading and writing wind data in the [windIO format](https://ieawindsystems.github.io/windIO/), an open YAML-based data exchange standard developed by the IEA Wind Systems Engineering community. Only the Plant schema is supported.
35
+
36
+ * New module `windkit.io.windio` with:
37
+ * **Readers**: `read_plant`, `read_site`, `read_energy_resource`, `read_wind_resource`, `read_wind_farm`, `read_layout`, `read_turbine`
38
+ * **Writers**: `write_plant`, `write_turbine` (with optional `modular=True` for split files)
39
+ * `export_site` — convert windkit wind climate datasets (WWC/BWC/TSWC) to windIO site format and write to disk (netCDF or YAML)
40
+ * **Translation functions**:
41
+ * `wwc_from_windio` / `wwc_to_windio` — Weibull wind climate
42
+ * `bwc_from_windio` / `bwc_to_windio` — Binned wind climate
43
+ * `tswc_from_windio` / `tswc_to_windio` — Time series wind climate
44
+ * `wind_turbines_from_windio` / `wind_turbines_to_windio` — turbine layouts
45
+ * `wtg_from_windio` / `wtg_to_windio` — WTG power/thrust curves
46
+ * `compose_plant` — combine wind climate, turbines, and WTGs into a complete plant structure
47
+ * `resolve_includes` — expand `!include` directives in windIO files
48
+ * Top-level readers (`read_wwc`, `read_bwc`, `read_tswc`, `read_wind_turbines`, `read_wtg`) now accept YAML files.
49
+ * Top-level writers (`wwc_to_file`, `bwc_to_file`, `tswc_to_file`) now support YAML export.
50
+ * New function `tswc_to_file` for writing time series wind climates.
51
+ * All reader/writer functions support a `level` parameter for partial file operations (e.g., `level="wind_resource"` to read/write only the wind resource portion).
52
+ * Multi-height wind climate support.
53
+ * IEA reference turbine compatibility (tested with IEA-15MW, IEA-22MW).
54
+ * All `*_from_windio` translation functions now automatically detect and resolve `!include` markers, so callers can pass unresolved windIO dicts directly without calling `resolve_includes` first.
55
+
56
+ ### Bug fixes
57
+
58
+ * `interp_structured_like`, `interp_unstructured_like`, `fit_weibull_wasp_m1_m3_fgtm`, `fit_weibull_wasp_m1_m3`, and `fit_weibull_k_sumlogm` now preserve the dtype of the input data. Previously, all these functions unconditionally upcast results to float64.
59
+ * Fixed `interp_unstructured_like` returning `NaN` near the dateline in geographic CRSs by wrapping longitudes around the target region before building the unstructured interpolator. Longitude wrapping only activates when CRS metadata is present.
60
+ * Fixed `clip_with_margin` to handle reversed coordinate bounds (e.g., ERA5/CFSR data with south_north going from 90 to -90).
61
+ * Fixed typo "marging" to "margin" in `clip_with_margin` warning message.
62
+ * Fixed `BBox.from_ds` and `BBox.reproject_to_utm` raising `ValueError` for single-point datasets where `minx == maxx` or `miny == maxy`. Zero-size bounding boxes are now allowed (by relaxing validation in `BBox.from_cornerpts`).
63
+ * Fixed crash in `nearest_points` (and other spatial operations) when called on a dataset that was previously subset from a stacked-point structure. Internal MultiIndex metadata left from the original stacking would be out of sync with the reduced dataset size, causing an error during spatial conversion.
64
+ * Reprojecting is now loss-less for raster datasets, due to original coordinates being kept internally.
65
+ * Fixed raster map saving with NumPy>=2.0 by passing numpy arrays to rasterio.
66
+ * Fixed `set_crs` crash for CRS types that cannot be converted to WKT1_GDAL (e.g. rotated lat-lon) — now emits a warning instead.
67
+ * Fixed `ValueError` in LTC MCP caused by incorrect unpacking of `wd_to_sector` return values, which had made all MCP functionality (`LinRegMCP`, `VarRatMCP`) non-functional.
68
+ * Fixed mutable default argument (`model_kws={}`) in `MCPRegressor.__init__()`.
69
+ * Fixed `LinearRegression.fit()` not returning `self`.
70
+ * Fixed `wasserstein_distance()` not extracting `.values.flatten()` like other score functions.
71
+ * Fixed `np.empty_like` in `MCPRegressor.predict()` leaving uninitialized values (now uses NaN).
72
+ * Fixed overly broad `except Exception` in `validate_dataset_ltc()` to catch only `WindkitValidationError`.
73
+ * Fixed incorrect docstrings claiming `quantiles` defaults to `True` in `LinRegMCP` / `VarRatMCP`.
74
+ * Fixed sector wrap-around bug in `MCPBase._wd_to_sector()` where wind directions near 0°/360° were assigned to invalid sector index -1.
75
+ * Fixed bug in `tswc_resample` which used vector averages for wind speed. Now (correctly) uses average magnitudes instead.
76
+ * Fixed `create_tswc_pair` where the second dataset silently ignored the `speed_tau` argument.
77
+
78
+ ### Breaking changes
79
+
80
+ * Minimum supported dependency versions raised: numpy 2.0, xarray 2024.9, pandas 2.3, scipy 1.13, geopandas 1.0, rasterio 1.4, pyproj 3.7, lxml 5.2, netcdf4 1.7. Users on older versions must upgrade.
81
+
82
+ ### Deprecations
83
+
84
+ * Writing multi-point datasets to multiple `.tab` files via `bwc_to_file` is deprecated and will be removed in windkit 3.0. Write single-point datasets instead.
85
+ * Writing multi-point datasets to multiple `.lib` files via `gwc_to_file` is deprecated and will be removed in windkit 3.0. Write single-point datasets instead.
86
+
87
+ ### Performance
88
+
89
+ * Preserve MultiIndex when converting between point and stacked_point/raster structures, avoiding unnecessary re-stacking and leading to small performance improvements for large datasets in many windkit and pywasp functions.
90
+
91
+ ### Changes
92
+
93
+ * Added explicit `compat`, `join`, and `data_vars` parameters to all `xr.merge` and `xr.concat` calls to suppress xarray FutureWarnings.
94
+ * Improved `_read_ts_windpro_txt` to parse date format from the windpro file header instead of hard-coding.
95
+ * Improved `create_raster_map` to generate synthetic 2D raster maps for elevation, roughness and landcover.
96
+ * Improved vector map reading in `_read_vector_map`.
97
+ * Added a `layer` argument and automatic layer detection for vector map files (`.gpkg`, `.zip`) when `layer=None`.
98
+ * Improved download retry logic for `get_tutorial_data`.
99
+ * Changed `read_wtg` to read multiple suggested heights as a new variable `suggested_heights` if present in WTG files.
100
+ * Wind climate dispatch (`mean_wind_speed`, `mean_power_density`, `mean_ws_moment`, `ws_cdf`, `ws_freq_gt_mean`) now uses capability-based detection instead of strict type-identity checks. Datasets that have the right fields but don't pass every validator check (e.g. missing `sector_ceil` / `sector_floor`) now work correctly.
101
+ * Generalized wind climate (GWC) datasets now dispatch to the Weibull handler for `mean_wind_speed`, `mean_power_density`, and `mean_ws_moment`, since they contain Weibull parameters. Previously these operations raised `NotImplementedError`.
102
+ * The `bysector` keyword is now forwarded consistently to all wind climate types, including geostrophic wind climate, where it was previously silently ignored.
103
+ * Standardized deprecation warnings to include the `since` and `removal` version in every message (e.g. "deprecated since windkit 2.1, removed in windkit 3.0").
104
+ * Give informative error in `get_vector_map` when the requested area is too large and when using `source="dtu"`.
105
+
106
+
107
+ ## [2.0.1 (2026-01-29)](https://docs.wasp.dk/windkit/v2.0.1/)
6
108
 
7
109
  ### Highlights
8
110
 
9
- ### Performance improvements
111
+ #### Performance improvements
10
112
  * Significantly reduced import time by lazy loading some packages and data. Should be approx 1/3 of previous import time.
11
113
 
12
- ### Sheer and veer functionality
114
+ #### Documentation
115
+ * Restructured user guide into focused, object-based documentation pages:
116
+ - Wind climates section with dedicated pages for TSWC, BWC, WWC, and GWC including I/O formats and usage examples
117
+ - Topography section with elevation, roughness, and landcover map documentation including format support tables
118
+ - Spatial section explaining point, stacked_point, raster, and cuboid structures with operation examples
119
+ - Wind farms section documenting wind turbine datasets and WTG objects
120
+ - New overview section with data structures, schemas, and tutorial data documentation
121
+
122
+ #### Sheer and veer functionality
13
123
 
14
124
  * Added new function `wk.shear_extrapolate` to extrapolate wind speeds to different heights using a power law shear profile.
15
125
  * Added new function `wk.shear_exponent` to calculate a shear exponent from wind speeds
@@ -23,16 +133,24 @@ All major changes are listed here.
23
133
 
24
134
  * new function `create_geowc`, which creates a dummy geostrophic wind climate
25
135
  * functions `mean_wind_speed`, `mean_power_density`, `ws_freq_gt_mean`, `ws_cdf` and `mean_ws_moment` are now also working when a geostrophic wind climate is passed.
26
- * bug fix to interpolation, which would create invalid spatial structures when a dimension was in exclude dims but not present in the target structure (e.g. when doing interpolation from `stacked_point` to `point`).
27
136
 
28
137
  ### New features
29
138
  * Added new function `wk.extrapolate_to_height` to extrapolate wind climate objects to different heights. Currently, only works for time-series wind climates (TSWC).
30
- * Fixed bug where a point dataset with several horizontal locations would be converted to a single horizontal location when using the function `to_stacked_point`
31
139
  * `wk.roughness_map_to_file` now allows to write to a old fashioned `.map` file for the special case where these is a single polygon and the external roughness length is not specified (None). For this case we assume that the external roughness length is the same as the roughness length inside the polygon.
32
140
  * `wk.roughness_map_to_file` now allows users to write to a `.map` as long all displacement heights in the map are zero.
141
+ * For `wv_count` objects (thus also geostrophic wind climates) if all of the bins have nan's, the result of function `mean_wind_speed`, `mean_power_density`, `ws_freq_gt_mean` and `mean_ws_moment` operating on such a function is also nan, whereas before it would be 0.
142
+ * Added new function `wk.load_tutorial_data` to download and load tutorial data directly into memory as windkit-compatible objects (xarray.Dataset and geopandas.GeoDataFrame).
143
+ * Changed `wk.get_tutorial_data` to return a `Path` to the extracted tutorial data folder, allowing users to load files manually using windkit readers or generic loaders. The function now includes detailed docstrings describing all available files for each dataset, including .nc, .omwc, .wtg, .map, and .csv.
144
+ * Added `wk.read_wind_turbines` and `wk.wind_turbines_to_file` functions to read and write wind turbine datasets in various formats. Both only support CSV for now. The CSV should follow the windkit wind turbine format specification.
145
+
146
+
147
+ ### Bug fixes
148
+ * Fixed pixel misalignment when downloading raster maps from Google Earth Engine. Downloads now use `crs_transform` instead of `scale` to preserve exact pixel alignment with source datasets (e.g., Globcover, SRTM, WorldCover). This fixes half-pixel offsets (~150m for Globcover) and ~12-15m offsets for elevation data.
33
149
  * Bugfix in `read_bwc`: it now opens `bwc` stored in netcdf correctly.
34
150
  but some of this functionality was only available in pywasp, so it has been moved there.
35
- * For `wv_count` objects (thus also geostrophic wind climates) if all of the bins have nan's, the result of function `mean_wind_speed`, `mean_power_density`, `ws_freq_gt_mean` and `mean_ws_moment` operating on such a function is also nan, whereas before it would be 0.
151
+ * Fixed bug where a point dataset with several horizontal locations would be converted to a single horizontal location when using the function `to_stacked_point`
152
+ * bug fix to interpolation, which would create invalid spatial structures when a dimension was in exclude dims but not present in the target structure (e.g. when doing interpolation from `stacked_point` to `point`).
153
+
36
154
 
37
155
  ### Changes
38
156
  * Changed default value of `use_bounds` to `True` in `wk.spatial.BBox.reproject` method.
@@ -1,50 +1,72 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: windkit
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: WindKit provides core functionalities for working with wind resource data
5
- Home-page: https://docs.wasp.dk/windkit/latest
6
5
  Author: DTU Wind Energy
7
- License: BSD-3-Clause
6
+ License-Expression: BSD-3-Clause
8
7
  Classifier: Programming Language :: Python :: 3
9
8
  Classifier: Operating System :: OS Independent
10
9
  Requires-Python: >=3.11
11
10
  Description-Content-Type: text/markdown
12
11
  License-File: LICENSE
13
- Requires-Dist: lxml>=5.0
14
- Requires-Dist: netcdf4>=1.6
15
- Requires-Dist: numpy>=1.26
16
- Requires-Dist: packaging>=23
17
- Requires-Dist: pandas>=2.1
18
- Requires-Dist: pyproj>=3.6
19
- Requires-Dist: scipy>=1.12
20
- Requires-Dist: shapely>=2
21
- Requires-Dist: xarray>=2023.9
22
- Requires-Dist: rioxarray>=0.17
23
- Requires-Dist: rasterio>=1.3
24
12
  Requires-Dist: geopandas>=1.0
25
- Requires-Dist: h5netcdf>=1.3
26
- Requires-Dist: h5py>=3.9
27
- Requires-Dist: matplotlib>=3.8
28
- Requires-Dist: pydantic-settings>=2.3
13
+ Requires-Dist: h5netcdf
14
+ Requires-Dist: lxml>=5.2
15
+ Requires-Dist: netcdf4>=1.7
16
+ Requires-Dist: numpy>=2.0
17
+ Requires-Dist: packaging>=24.1
18
+ Requires-Dist: pandas>=2.3
19
+ Requires-Dist: platformdirs>=4.3
29
20
  Requires-Dist: pydantic>=2.7
30
- Requires-Dist: platformdirs>=4.3.8
31
- Requires-Dist: plotly>=5
32
- Requires-Dist: kaleido>=0.2.1
33
- Requires-Dist: requests>=2.31
34
- Requires-Dist: toml>=0.10
21
+ Requires-Dist: pydantic-settings>=2.3
22
+ Requires-Dist: pyproj>=3.7
23
+ Requires-Dist: pyyaml
24
+ Requires-Dist: rasterio>=1.4
25
+ Requires-Dist: requests
26
+ Requires-Dist: rioxarray>=0.17
27
+ Requires-Dist: scipy>=1.13
28
+ Requires-Dist: shapely>=2.0
29
+ Requires-Dist: toml
30
+ Requires-Dist: xarray>=2024.9
35
31
  Provides-Extra: plots
36
- Requires-Dist: matplotlib>=3.6; extra == "plots"
37
- Requires-Dist: plotly>=5.12; extra == "plots"
38
- Requires-Dist: kaleido>=0.2.1; extra == "plots"
39
- Provides-Extra: data-apis
40
- Requires-Dist: ee; extra == "data-apis"
41
- Requires-Dist: pystac_client; extra == "data-apis"
42
- Requires-Dist: planetary_computer; extra == "data-apis"
43
- Requires-Dist: fsspec; extra == "data-apis"
44
- Requires-Dist: adlfs; extra == "data-apis"
45
- Requires-Dist: zarr; extra == "data-apis"
32
+ Requires-Dist: matplotlib>=3.9; extra == "plots"
33
+ Requires-Dist: plotly>=5.21; extra == "plots"
34
+ Requires-Dist: kaleido>=1.0; extra == "plots"
46
35
  Provides-Extra: performance
47
- Requires-Dist: numba; extra == "performance"
36
+ Requires-Dist: numba>=0.60; extra == "performance"
37
+ Provides-Extra: data-apis
38
+ Requires-Dist: adlfs>=2024.4; extra == "data-apis"
39
+ Requires-Dist: earthengine-api>=1.0; extra == "data-apis"
40
+ Requires-Dist: fsspec>=2024.5; extra == "data-apis"
41
+ Requires-Dist: planetary-computer>=1.0; extra == "data-apis"
42
+ Requires-Dist: pystac-client>=0.8; extra == "data-apis"
43
+ Requires-Dist: zarr>=3.0; extra == "data-apis"
44
+ Provides-Extra: dev
45
+ Requires-Dist: dash; extra == "dev"
46
+ Requires-Dist: ipython; extra == "dev"
47
+ Requires-Dist: nbval; extra == "dev"
48
+ Requires-Dist: pre-commit; extra == "dev"
49
+ Requires-Dist: pytest; extra == "dev"
50
+ Requires-Dist: pytest-cov; extra == "dev"
51
+ Requires-Dist: pytest-xdist; extra == "dev"
52
+ Requires-Dist: windIO; extra == "dev"
53
+ Provides-Extra: doc
54
+ Requires-Dist: docutils; extra == "doc"
55
+ Requires-Dist: jupyter; extra == "doc"
56
+ Requires-Dist: jupyter-dash; extra == "doc"
57
+ Requires-Dist: jupyter-sphinx; extra == "doc"
58
+ Requires-Dist: myst-parser; extra == "doc"
59
+ Requires-Dist: nbsphinx; extra == "doc"
60
+ Requires-Dist: nbsphinx-link; extra == "doc"
61
+ Requires-Dist: pydata-sphinx-theme; extra == "doc"
62
+ Requires-Dist: recommonmark; extra == "doc"
63
+ Requires-Dist: sphinx; extra == "doc"
64
+ Requires-Dist: sphinx-argparse; extra == "doc"
65
+ Requires-Dist: sphinx-copybutton; extra == "doc"
66
+ Requires-Dist: sphinx-design; extra == "doc"
67
+ Requires-Dist: sphinx-gallery; extra == "doc"
68
+ Requires-Dist: sphinxcontrib-bibtex; extra == "doc"
69
+ Requires-Dist: sphinxext-opengraph; extra == "doc"
48
70
  Dynamic: license-file
49
71
 
50
72
  # WindKit
@@ -0,0 +1,118 @@
1
+ [build-system]
2
+ requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "windkit"
7
+ description = "WindKit provides core functionalities for working with wind resource data"
8
+ readme = "README.md"
9
+ license = "BSD-3-Clause"
10
+ authors = [
11
+ {name = "DTU Wind Energy"}
12
+ ]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ dynamic = ["version"]
18
+ requires-python = ">=3.11"
19
+ dependencies = [
20
+ "geopandas>=1.0",
21
+ "h5netcdf",
22
+ "lxml>=5.2",
23
+ "netcdf4>=1.7",
24
+ "numpy>=2.0",
25
+ "packaging>=24.1",
26
+ "pandas>=2.3",
27
+ "platformdirs>=4.3",
28
+ "pydantic>=2.7",
29
+ "pydantic-settings>=2.3",
30
+ "pyproj>=3.7",
31
+ "pyyaml",
32
+ "rasterio>=1.4",
33
+ "requests",
34
+ "rioxarray>=0.17",
35
+ "scipy>=1.13",
36
+ "shapely>=2.0",
37
+ "toml",
38
+ "xarray>=2024.9",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ plots = [
43
+ "matplotlib>=3.9",
44
+ "plotly>=5.21",
45
+ "kaleido>=1.0",
46
+ ]
47
+ performance = [
48
+ "numba>=0.60",
49
+ ]
50
+ data_apis = [
51
+ "adlfs>=2024.4",
52
+ "earthengine-api>=1.0",
53
+ "fsspec>=2024.5",
54
+ "planetary-computer>=1.0",
55
+ "pystac-client>=0.8",
56
+ "zarr>=3.0",
57
+ ]
58
+ dev = [
59
+ "dash",
60
+ "ipython",
61
+ "nbval",
62
+ "pre-commit",
63
+ "pytest",
64
+ "pytest-cov",
65
+ "pytest-xdist",
66
+ "windIO",
67
+ ]
68
+ doc = [
69
+ "docutils",
70
+ "jupyter",
71
+ "jupyter-dash",
72
+ "jupyter-sphinx",
73
+ "myst-parser",
74
+ "nbsphinx",
75
+ "nbsphinx-link",
76
+ "pydata-sphinx-theme",
77
+ "recommonmark",
78
+ "sphinx",
79
+ "sphinx-argparse",
80
+ "sphinx-copybutton",
81
+ "sphinx-design",
82
+ "sphinx-gallery",
83
+ "sphinxcontrib-bibtex",
84
+ "sphinxext-opengraph",
85
+ ]
86
+
87
+ [project.scripts]
88
+ split_combomap = "windkit.topography.vector_map:_split_combo"
89
+
90
+ [tool.setuptools_scm]
91
+ write_to = "windkit/_version.py"
92
+
93
+ [tool.setuptools]
94
+ include-package-data = true
95
+
96
+ [tool.setuptools.packages.find]
97
+ namespaces = true
98
+
99
+ [tool.setuptools.package-data]
100
+ "windkit.data" = ["*.json"]
101
+ "windkit.data.landcover_tables" = ["*.json"]
102
+ "windkit.data.loss_tables" = ["*.csv"]
103
+ "windkit.data.uncertainty_tables" = ["*.csv"]
104
+
105
+ [tool.pytest.ini_options]
106
+ minversion = "6.0"
107
+ norecursedirs = "dist build .tox"
108
+ testpaths = ["test",]
109
+ markers = [
110
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
111
+ "performance: marks tests as performance tests (run with '-m performance')",
112
+ ]
113
+
114
+ # Added warning filter for warnings that show up for min dependencies.
115
+ # These do not produce any errors and once packages are upgraded, these are not emitted
116
+ filterwarnings = [
117
+ "ignore:The 'shapely.geos' module is deprecated:DeprecationWarning:geopandas._compat",
118
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,4 +1,4 @@
1
- # (c) 2022 DTU Wind Energy
1
+ # (c) 2026 DTU Wind Energy
2
2
  """
3
3
  WindKit provides an API for working with wind resource assessment related file formats.
4
4
  """
@@ -29,7 +29,9 @@ __all__ = [
29
29
  "is_tswc",
30
30
  "create_tswc",
31
31
  "read_tswc",
32
+ "tswc_to_file",
32
33
  "tswc_from_dataframe",
34
+ "create_tswc_pair",
33
35
  # Binned wind climate
34
36
  "validate_bwc",
35
37
  "is_bwc",
@@ -105,6 +107,8 @@ __all__ = [
105
107
  "create_wind_turbines_from_dataframe",
106
108
  "create_wind_turbines_from_arrays",
107
109
  "wind_turbines_to_geodataframe",
110
+ "read_wind_turbines",
111
+ "wind_turbines_to_file",
108
112
  # workspace
109
113
  "Workspace",
110
114
  # weng_workspace
@@ -118,7 +122,7 @@ __all__ = [
118
122
  "polygons_to_lines",
119
123
  "snap_to_layer",
120
124
  "check_dead_ends",
121
- "check_lines_cross"
125
+ "check_lines_cross",
122
126
  # get_map
123
127
  "get_raster_map",
124
128
  "get_vector_map",
@@ -142,6 +146,7 @@ __all__ = [
142
146
  "total_uncertainty_factor",
143
147
  # tutorial_data
144
148
  "get_tutorial_data",
149
+ "load_tutorial_data",
145
150
  ]
146
151
 
147
152
  from windkit._version import version as __version__
@@ -168,7 +173,9 @@ from windkit.wind_climate.time_series_wind_climate import (
168
173
  is_tswc,
169
174
  create_tswc,
170
175
  read_tswc,
176
+ tswc_to_file,
171
177
  tswc_from_dataframe,
178
+ create_tswc_pair,
172
179
  )
173
180
  from windkit.wind_climate import time_series_wind_climate # noqa: F401
174
181
  from windkit.wind_climate.binned_wind_climate import (
@@ -238,6 +245,8 @@ from windkit.wind_farm.wind_turbines import (
238
245
  create_wind_turbines_from_dataframe,
239
246
  create_wind_turbines_from_arrays,
240
247
  wind_turbines_to_geodataframe,
248
+ read_wind_turbines,
249
+ wind_turbines_to_file,
241
250
  )
242
251
  from windkit.workspace import Workspace
243
252
  from windkit.weng_workspace import WengWorkspace
@@ -301,4 +310,4 @@ from windkit.xarray_structures.direction import (
301
310
  from windkit.xarray_structures.wsbin import (
302
311
  create_wsbin_coords,
303
312
  )
304
- from windkit.tutorial_data import get_tutorial_data
313
+ from windkit.tutorial_data import get_tutorial_data, load_tutorial_data
@@ -1,4 +1,4 @@
1
- # (c) 2022 DTU Wind Energy
1
+ # (c) 2026 DTU Wind Energy
2
2
  """
3
3
  Package exceptions message writing
4
4
  """
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '2.1.0'
22
+ __version_tuple__ = version_tuple = (2, 1, 0)
23
+
24
+ __commit_id__ = commit_id = 'g0a6bf992d'
@@ -1,4 +1,4 @@
1
- # (c) 2022 DTU Wind Energy
1
+ # (c) 2026 DTU Wind Energy
2
2
  """
3
3
  Processes WAsP CFD volume results for use in PyWAsP
4
4
  """
@@ -124,13 +124,15 @@ def read_cfdres(path, crs):
124
124
  array = array.expand_dims(["height"])
125
125
  sector_list.append(array)
126
126
  # combine all data arrays into a dataset
127
- ds_sector = xr.merge(sector_list)
127
+ ds_sector = xr.merge(sector_list, compat="no_conflicts", join="outer")
128
128
  # load mesoscale roughness length
129
129
  ds_sector.coords["sector"] = sec_dir
130
130
  ds_sector = ds_sector.expand_dims(["sector"])
131
131
  all_arrays.append(ds_sector)
132
132
 
133
- ds_cfd = xr.concat(all_arrays, dim="sector")
133
+ ds_cfd = xr.concat(
134
+ all_arrays, dim="sector", data_vars="all", compat="equals", join="outer"
135
+ )
134
136
  ds_cfd["z0meso"] = (("sector",), z0meso)
135
137
  # make sure all the meta data for sectors are there
136
138
  ds_cfd = ds_cfd.assign_coords(