windkit 2.1.0__tar.gz → 2.2.0rc1__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.
- {windkit-2.1.0 → windkit-2.2.0rc1}/.triage-policies.yml +41 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/CHANGELOG.md +134 -1
- {windkit-2.1.0 → windkit-2.2.0rc1}/PKG-INFO +11 -11
- {windkit-2.1.0 → windkit-2.2.0rc1}/README.md +7 -5
- {windkit-2.1.0 → windkit-2.2.0rc1}/pyproject.toml +28 -8
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/__init__.py +50 -0
- windkit-2.2.0rc1/windkit/_cli.py +95 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/_errors.py +37 -7
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/_version.py +3 -3
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/_weibull_nb.py +59 -96
- windkit-2.2.0rc1/windkit/config.py +213 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/all_vars.json +137 -7
- windkit-2.2.0rc1/windkit/import_manager.py +87 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/integrations/_earth_engine.py +46 -32
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/integrations/_planetary_computer.py +0 -31
- windkit-2.2.0rc1/windkit/io/__init__.py +36 -0
- windkit-2.2.0rc1/windkit/io/wasp/__init__.py +32 -0
- windkit-2.2.0rc1/windkit/io/wasp/_common.py +187 -0
- windkit-2.2.0rc1/windkit/io/wasp/_rvea.py +207 -0
- windkit-2.2.0rc1/windkit/io/wasp/gwc.py +507 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/wasp/lib.py +71 -13
- windkit-2.2.0rc1/windkit/io/wasp/map.py +705 -0
- windkit-2.2.0rc1/windkit/io/wasp/owc.py +165 -0
- windkit-2.2.0rc1/windkit/io/wasp/rsf.py +490 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/wasp/tab.py +42 -10
- windkit-2.2.0rc1/windkit/io/wasp/wtg.py +258 -0
- windkit-2.2.0rc1/windkit/io/wasp/wwh.py +666 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/bwc.py +24 -17
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/ltc/mcp.py +57 -46
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/_colormaps.py +4 -2
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/elevation_map.py +2 -1
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/histogram.py +4 -3
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/landcover_map.py +6 -4
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/raster_plot.py +7 -7
- windkit-2.2.0rc1/windkit/spatial/README.md +73 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/__init__.py +3 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_bbox.py +1 -1
- windkit-2.2.0rc1/windkit/spatial/_dimensions.py +235 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_interpolation.py +294 -73
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_point.py +49 -48
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_raster.py +36 -25
- windkit-2.2.0rc1/windkit/spatial/_schemas.py +63 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_struct.py +180 -84
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/spatial.py +753 -103
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/_vectormap_gml.py +1 -3
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/_vectormap_helpers.py +0 -28
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/get_map.py +1 -1
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversion/poly_to_lines.py +1 -1
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversions.py +243 -243
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/raster_map.py +289 -9
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/topography.py +219 -77
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/vector_map.py +267 -238
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/tutorial_data.py +9 -8
- windkit-2.2.0rc1/windkit/utils.py +79 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/weibull.py +133 -47
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind.py +171 -40
- windkit-2.2.0rc1/windkit/wind_climate/_correlated_fields.py +292 -0
- windkit-2.2.0rc1/windkit/wind_climate/_validation.py +89 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/binned_wind_climate.py +379 -136
- windkit-2.2.0rc1/windkit/wind_climate/extreme_wind_climate.py +419 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/generalized_wind_climate.py +133 -116
- windkit-2.2.0rc1/windkit/wind_climate/geostrophic_wind_climate.py +197 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/predicted_wind_climate.py +25 -14
- windkit-2.2.0rc1/windkit/wind_climate/stability.py +288 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/time_series_wind_climate.py +337 -90
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/weibull_wind_climate.py +245 -600
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/wind_climate.py +282 -126
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/loss.py +28 -16
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/uncertainty.py +6 -3
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/wind_turbine_generator.py +194 -221
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/wind_turbines.py +17 -21
- windkit-2.2.0rc1/windkit/workspace.py +1759 -0
- windkit-2.2.0rc1/windkit/xarray_structures/_schema.py +565 -0
- windkit-2.2.0rc1/windkit/xarray_structures/empty.py +236 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/xarray_structures/metadata.py +57 -9
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit.egg-info/SOURCES.txt +13 -2
- windkit-2.1.0/windkit/_rvea_xml.py +0 -258
- windkit-2.1.0/windkit/config.py +0 -215
- windkit-2.1.0/windkit/import_manager.py +0 -44
- windkit-2.1.0/windkit/io/__init__.py +0 -12
- windkit-2.1.0/windkit/io/wasp/__init__.py +0 -14
- windkit-2.1.0/windkit/io/wasp/_common.py +0 -31
- windkit-2.1.0/windkit/io/wasp/owc.py +0 -90
- windkit-2.1.0/windkit/spatial/README.md +0 -31
- windkit-2.1.0/windkit/spatial/_dimensions.py +0 -66
- windkit-2.1.0/windkit/utils.py +0 -34
- windkit-2.1.0/windkit/wind_climate/geostrophic_wind_climate.py +0 -148
- windkit-2.1.0/windkit/workspace.py +0 -709
- windkit-2.1.0/windkit/xarray_structures/_validate.py +0 -225
- windkit-2.1.0/windkit/xarray_structures/empty.py +0 -79
- {windkit-2.1.0 → windkit-2.2.0rc1}/LICENSE +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/setup.cfg +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/cfd.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/color_table_roughness.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/CGLS-LC100.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/CORINE.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/ESA_CCI.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/GWA4_micro.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/Globcover.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/MODIS.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/NEWA_micro_v2.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/landcover_tables/WorldCover.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/loss_tables/dtu_loss_table.csv +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/map_file_header_to_epsg.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/map_file_header_to_epsg_read.json +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/data/uncertainty_tables/dtu_uncertainty_table.csv +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/integrations/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/integrations/_gwa_map_api.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/readers.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/common.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/tswc.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/wind_turbines.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/wtg.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/translate/wwc.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/utils.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/io/windio/writers.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/ltc/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/ltc/_validation.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/ltc/regression.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/ltc/scores.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/_helpers.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/color.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/operational_curves.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/roughness_rose.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/spatial_plots.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/time_series.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/vertical_profile.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/wind_rose.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/plot/wind_turbine.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_conversions.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_crs.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_cuboid.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_latitude.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_metpy_natural_neighbors.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_utm.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_vector.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/_vertical.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/spatial/decorators.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/landcover.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversion/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversion/_polygons_constraints.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversion/helper_functions.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/topography/map_conversion/lines_to_poly.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/weng_workspace.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_climate/_synthetic_time_series.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_data.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/__init__.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/wind_farm/power_curve.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/xarray_structures/data_structures.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/xarray_structures/direction.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/xarray_structures/sector.py +0 -0
- {windkit-2.1.0 → windkit-2.2.0rc1}/windkit/xarray_structures/wsbin.py +0 -0
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
resource_rules:
|
|
2
|
+
merge_requests:
|
|
3
|
+
rules:
|
|
4
|
+
# Label merge requests as Stale after 6 months of inactivity
|
|
5
|
+
- name: Label stale merge requests
|
|
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
|
+
actions:
|
|
16
|
+
labels:
|
|
17
|
+
- Stale
|
|
18
|
+
comment: |
|
|
19
|
+
This merge request has been inactive for 6 months and has been labeled `~Stale`.
|
|
20
|
+
|
|
21
|
+
If it is still relevant, please rebase or comment to reset the
|
|
22
|
+
inactivity timer. Otherwise, it will be automatically closed in
|
|
23
|
+
3 months.
|
|
24
|
+
|
|
25
|
+
# Close merge requests 3 months after the Stale label was added
|
|
26
|
+
- name: Close stale merge requests
|
|
27
|
+
conditions:
|
|
28
|
+
state: opened
|
|
29
|
+
date:
|
|
30
|
+
attribute: updated_at
|
|
31
|
+
condition: older_than
|
|
32
|
+
interval_type: months
|
|
33
|
+
interval: 3
|
|
34
|
+
labels:
|
|
35
|
+
- Stale
|
|
36
|
+
actions:
|
|
37
|
+
status: close
|
|
38
|
+
comment: |
|
|
39
|
+
This merge request has been inactive and is being closed
|
|
40
|
+
automatically per our [stale merge request policy](CONTRIBUTING.md#stale-merge-requests).
|
|
41
|
+
|
|
42
|
+
If it is still relevant, feel free to reopen it.
|
|
2
43
|
issues:
|
|
3
44
|
rules:
|
|
4
45
|
# Label issues as Stale after 6 months of inactivity
|
|
@@ -2,7 +2,138 @@
|
|
|
2
2
|
|
|
3
3
|
All major changes are listed here.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## [2.2.0 (2026-09-29)](https://docs.wasp.dk/windkit/v2.2.0/)
|
|
6
|
+
|
|
7
|
+
windkit 2.2 contains breaking changes. Read *Upgrading from 2.1* before you upgrade.
|
|
8
|
+
|
|
9
|
+
### Highlights
|
|
10
|
+
|
|
11
|
+
* **More WAsP file formats**: `windkit.io.wasp` reads `.wtg`, `.map`, `.rsf`/`.wrg`, `.gwc` and `.wwh` files as plain dictionaries of numpy arrays, and writes `.map` and `.rsf`/`.wrg`.
|
|
12
|
+
* **Results from WAsP workspaces**: `windkit.Workspace` reads the wind climates, site effects, AEP and turbines stored in a WAsP workspace, including workspaces whose projects use different CRSs.
|
|
13
|
+
* **Radial basis function interpolation**: `interp_unstructured` and `interp_unstructured_like` gained `method="rbf"`, which, unlike `linear` and `cubic`, extrapolates beyond the source points.
|
|
14
|
+
* **Wind directions relative to grid north**: `bwc_from_tswc` rotates true-north wind directions to the grid north of a projected CRS given as `wind_dir_crs`, and `windkit.spatial.grid_convergence` computes the angle between the two.
|
|
15
|
+
* **`windkit` CLI**: `windkit configure` saves the name, email and institution that windkit stamps on datasets. windkit no longer prompts for them.
|
|
16
|
+
|
|
17
|
+
### Upgrading from 2.1
|
|
18
|
+
|
|
19
|
+
Most code runs unchanged. Check the following:
|
|
20
|
+
|
|
21
|
+
* **Direction frequencies**: `validate_bwc`, `validate_wwc`, `validate_gwc`, `validate_geowc` and the functions that check their wind climate input now raise when `wdfreq` (`geo_wv_freq` for a geostrophic wind climate) does not sum to 1 at a location. Divide frequencies in percent by 100, or normalise them with `ds["wdfreq"] = ds["wdfreq"] / ds["wdfreq"].sum("sector")`.
|
|
22
|
+
* **Net AEP**: `total_loss_factor` now compounds losses, so net AEP from a loss table with more than one loss is slightly higher than in 2.1.
|
|
23
|
+
* **Author attributes**: datasets carry `author`, `author_email` and `institution` only if you set them, with `windkit configure` or the `WINDKIT_NAME`, `WINDKIT_EMAIL` and `WINDKIT_INSTITUTION` environment variables. windkit 2.1 and earlier still ask for any field left unset.
|
|
24
|
+
* **Configuration**: `windkit.config.Settings` is removed; replace `windkit.config.CONFIG` with `windkit.config.get_config()`. windkit no longer installs `pydantic`, `pydantic-settings` or `toml`, so install them yourself if your code uses them.
|
|
25
|
+
* **Nearest points**: `nearest_points` raises a `ValueError` when its result cannot keep the target's spatial structure, for example when two targets share a source point. Pass `windkit.spatial.to_point(target)` to get point output instead.
|
|
26
|
+
* **Generated data**: the `create_*` functions that generate synthetic wind climates, site effects and time series return float32 variables, no longer add a size-one `height` or `point` dimension, and give different values for the same seed. Regenerate any stored reference output.
|
|
27
|
+
* **Zip archives**: read a GeoPackage in a zip archive whose name lacks `.gpkg` with `spatial_format="vector"` and `file_format="gpkg"`.
|
|
28
|
+
* **Errors**: import the exception classes from `windkit`, e.g. `windkit.WindkitValidationError`. Some functions now raise a different exception type; see *API Breaks*.
|
|
29
|
+
|
|
30
|
+
### New features
|
|
31
|
+
|
|
32
|
+
* `windkit.io.wasp` reads and writes more WAsP files as plain dictionaries of numpy arrays: `.wtg` (`read_wtg`, `parse_wtg_element`), `.map` (`read_map`, `write_map`, `format_map`), `.rsf`/`.wrg` (`read_rsf`, `write_rsf`, `format_rsf`), `.gwc` (`read_gwc`, `parse_gwc_element`) and `.wwh` workspaces (`read_wwh`, `parse_wwh_element`, `parse_wwh_member_element`).
|
|
33
|
+
* `windkit.Workspace` reads the results WAsP stored in a workspace: `get_wwc`, `get_site_effects`, `get_aep` and `get_owc_weibull_fit` return one point per site, and warn when WAsP marked the results as out of date.
|
|
34
|
+
* `windkit.Workspace` gained `get_wtg`, `get_mesoclimate`, `get_air_density_reference`, `get_parameters`, `get_ids` and `get_member`. The `ID` argument of the getters is optional when the workspace holds one object of that kind.
|
|
35
|
+
* `windkit.Workspace` takes each object's CRS from its own WAsP project, and its getters accept `crs` for workspaces that state none. Workspace format 4.01.0002 is supported.
|
|
36
|
+
* `ws_cdf` and `ws_freq_gt_mean` accept Weibull and time series wind climates; for these, `ws_cdf` takes `ws_bins`.
|
|
37
|
+
* `mean_wind_speed`, `mean_power_density`, `mean_ws_moment`, `ws_cdf` and `ws_freq_gt_mean` accept `bysector=True` for time series wind climates, binning directions into `n_sectors` sectors (default 12). They used to ignore it.
|
|
38
|
+
* `interp_unstructured` and `interp_unstructured_like` gained `method="rbf"`, based on scipy's `RBFInterpolator`. Unlike `linear` and `cubic`, it extrapolates beyond the source points. It interpolates horizontally only.
|
|
39
|
+
* `bwc_from_tswc`, `create_bwc`, `read_bwc` and windIO BWC import accept `wind_dir_crs`. `bwc_from_tswc` rotates true-north directions to the grid north of that CRS; the others record the frame of the existing directions.
|
|
40
|
+
* Added `windkit.spatial.grid_convergence`, which computes the grid convergence at a dataset's locations, or between two projections with `source=`.
|
|
41
|
+
* Added `windkit.fill_elevation_nodata`, which fills NaN and infinite cells in an elevation raster.
|
|
42
|
+
* `read_elevation_map`, `read_roughness_map` and `read_landcover_map` read a member of an open zip archive (`zipfile.ZipExtFile`) in the format its name gives.
|
|
43
|
+
* Added the `windkit configure` command, which saves the name, email and institution stamped on datasets, and `windkit.config.get_config()`, which returns them.
|
|
44
|
+
* `WindkitError`, `WindkitValidationError`, `WindkitFileFormatError` and `UnsupportedFileTypeError` can be imported from `windkit`.
|
|
45
|
+
* `polygons_to_lines` and the `.map` writers accept `snap_tolerance`.
|
|
46
|
+
* `nearest_points` gained `workers` for parallel queries (default 1).
|
|
47
|
+
* `create_bwc` gained `wsbin_width`, `weibull_A` and `weibull_k` (each Weibull parameter a value or a `(low, high)` range), and `create_geowc` gained `weibull_A` and `weibull_k`.
|
|
48
|
+
* `create_tswc` gained `fields`, which adds the stability variables `temp_scale` and `ustar_over_pblh`. Added `create_surface_fields`, which generates mesoscale surface fields.
|
|
49
|
+
* Added `create_gewc`, `create_pewc` and `create_v50_lut`, which generate synthetic inputs for extreme wind calculations.
|
|
50
|
+
|
|
51
|
+
### Bug fixes
|
|
52
|
+
|
|
53
|
+
* `total_loss_factor` compounds losses as `prod(1 - p_i / 100)` instead of `1 - sum(p_i) / 100`, which understated the remaining energy and could go negative. `total_loss` still reports the plain sum; `loss_table_summary` shows both.
|
|
54
|
+
* Reading a `.map` file applies the `z` scale and offset from its header, so elevations given in feet are converted to metres. A map whose header transforms `x` and `y` raises `NotImplementedError` instead of being misplaced.
|
|
55
|
+
* Writing a `.map` file in a geographic CRS no longer rounds coordinates to one decimal, which moved them by up to 11 km.
|
|
56
|
+
* Writing a `.map` file records a CRS without an EPSG code in the header instead of leaving it empty.
|
|
57
|
+
* Writing a `.map` file with NaN in `elev`, `z0_left` or `z0_right` raises a `ValueError` instead of dropping those lines.
|
|
58
|
+
* Reading a malformed `.map` file raises a `ValueError` naming the file and line instead of returning corrupted geometry.
|
|
59
|
+
* `polygons_to_lines` accepts polygons with Z coordinates.
|
|
60
|
+
* `snap_to_layer` no longer overwrites columns named `npoints`, `npoints_old` or `done`.
|
|
61
|
+
* The deprecated `PolygonMap.to_line_map` works again with `complete_polys=True` and `complete_method="new"`.
|
|
62
|
+
* `wwc_to_file` honours `use_production=True` for `.rsf`/`.wrg` files; it always wrote power density.
|
|
63
|
+
* Writing `.rsf`/`.wrg` files no longer fails for heights of 100 m or more, large coordinates or a single-sector `wdfreq` of 1. `site_elev` is rounded instead of truncated, and non-integer WRG header values keep one decimal.
|
|
64
|
+
* Reading an `.rsf`/`.wrg` file from an open file object no longer skips its first two lines.
|
|
65
|
+
* `read_lib` reads rows wrapped across lines, and raises a `ValueError` for a truncated file instead of hanging.
|
|
66
|
+
* `read_bwc` and the `windkit.io.wasp` readers read UTF-16 files without a byte-order mark, and ignore a UTF-8 byte-order mark.
|
|
67
|
+
* `windkit.read_gwc` keeps the file's description, and raises a `ValueError` naming the element for a malformed `.gwc` file instead of filling in NaN or zeros.
|
|
68
|
+
* `windkit.read_wtg` reads `.wtg` files that omit `StationaryThrustCoEfficient`, deriving it from the thrust curve. It rejects an invalid `FormatVersion`, which made a stall-regulated turbine read as pitch-regulated.
|
|
69
|
+
* A GeoPackage read from a zip archive member picks its layer as for a file path, instead of taking the first one listed.
|
|
70
|
+
* The Weibull fit functions return NaN where the moments cannot be fitted, instead of raising (without numba) or returning meaningless values (with numba).
|
|
71
|
+
* The Weibull fit functions accept numpy arrays, scalars, 0-d and integer inputs with and without numba. `fit_weibull_k_sumlogm` respects `order_m_first` and `order_m_higher`, and `weibull_fit` no longer copies the attributes of the moments onto `A` and `k`.
|
|
72
|
+
* `quantiles=True` works for the `windkit.ltc` MCP classes, which shifted every sector by one at `predict`. `wd_to_sector(quantiles=True)` accepts arrays of any shape, and puts a direction on an inner sector edge in the upper sector.
|
|
73
|
+
* `ws_freq_gt_mean` of a time series skips missing samples instead of counting them as below the mean.
|
|
74
|
+
* `shear_exponent` and `wind_veer` raise a `WindkitValidationError` for input without a vertical profile, instead of returning an empty result or an unrelated error. Duplicate heights at a location are dropped instead of giving `inf`.
|
|
75
|
+
* `interp_unstructured` and `interp_unstructured_like` raise a clear `ValueError` for an unknown `method`, and for keyword arguments with `method="natural"`.
|
|
76
|
+
* `wspd_deficit` and `wspd_deficit_sector` have units `-` instead of `m s-1`.
|
|
77
|
+
* AEP and flow map datasets describe all their variables; gross AEP was described as potential AEP.
|
|
78
|
+
* `create_bwc` no longer shifts its histogram half a bin low, which put the mean wind speed about 6 % below what `A` and `k` imply. It warns when the wind speed axis is too short for the distribution.
|
|
79
|
+
* `create_geowc` honours `wsbin_width`, and its default wind speed axis (60 bins instead of 10) covers the distribution.
|
|
80
|
+
* `create_wasp_site_effects` no longer fills `user_def_speedups` and `obstacle_speedups` with 0 or draws a `z0meso` of 0, which made downstream results NaN or degenerate. Its speedups are drawn from narrower, plausible ranges, and points of a `point` structure at the same location share their height-independent values, such as `site_elev` and `z0meso`.
|
|
81
|
+
* `windkit.plot.histogram(style="interactive")` works again.
|
|
82
|
+
* A missing optional dependency raises an `ImportError` naming the extra to install, e.g. `pip install "windkit[plots]"`.
|
|
83
|
+
|
|
84
|
+
### API Breaks
|
|
85
|
+
|
|
86
|
+
* Removed `windkit.config.Settings` and the conversion of `windkit.ini` files from before windkit 1.0.
|
|
87
|
+
* windkit no longer depends on `pydantic`, `pydantic-settings` or `toml`.
|
|
88
|
+
* Removed the `formatters` argument of `wwc_to_file` for `.rsf`/`.wrg` files. Unknown keyword arguments to the `.rsf`/`.wrg` readers and writers raise `TypeError`.
|
|
89
|
+
* `elevation_map_to_file`, `roughness_map_to_file` and `landcover_map_to_file` raise `TypeError` for `mode="a"` when writing a `.map` file.
|
|
90
|
+
* `mean_ws_moment` requires a scalar `moment`.
|
|
91
|
+
* `nearest_points` with `n_nearest > 1` requires a point target.
|
|
92
|
+
* `windkit.Workspace.catalogue` holds each object's XML element under `"Element"` instead of its XPath under `"XPATH"`.
|
|
93
|
+
* The Google Earth Engine map functions raise `ImportError` when `earthengine-api` is missing (was `ValueError`) and `RuntimeError` when Earth Engine is not authenticated. Earth Engine is initialised on first use instead of at import.
|
|
94
|
+
* `windkit.read_wtg` raises `lxml.etree.XMLSyntaxError` instead of `xml.etree.ElementTree.ParseError` for invalid XML (both subclass `SyntaxError`), and a `ValueError` naming the file for other malformed files.
|
|
95
|
+
* A `.map` file with displacement heights raises `ValueError` instead of `RuntimeError`.
|
|
96
|
+
|
|
97
|
+
### Breaking changes
|
|
98
|
+
|
|
99
|
+
* `validate_bwc`, `validate_wwc`, `validate_gwc` and `validate_geowc` reject a wind climate whose `wdfreq` (`geo_wv_freq` for a geostrophic wind climate) does not sum to 1 at each location. All-NaN locations are exempt.
|
|
100
|
+
* `combine_bwcs` raises a `ValueError` for inputs with different sectors, instead of returning an invalid dataset.
|
|
101
|
+
* `nearest_points` keeps the target's spatial structure only when the selected source points represent it exactly, and raises a `ValueError` otherwise. It warns when `dims` leaves out a spatial coordinate of the source, and raises a `ValueError` for invalid lookups.
|
|
102
|
+
* `snap_to_layer`, and so `polygons_to_lines`, raise a `ValueError` when `tolerance` is at least a ring's median vertex spacing, instead of returning invalid geometry.
|
|
103
|
+
* `create_bwc`, `create_wwc`, `create_gwc`, `create_geowc`, `create_pwc`, `create_met_fields`, `create_tswc`, `create_tswc_pair` and `create_wasp_site_effects` keep a scalar height or a single location as given, instead of adding a size-one `height` or `point` dimension. Their data variables are float32, with the spatial dimensions first.
|
|
104
|
+
* Converting between spatial structures no longer adds `height` to height-independent variables such as `elevation`, `roughness`, `z0meso` and surface fields like `T2`.
|
|
105
|
+
* `create_wasp_site_effects` gives the effective displacement `displ` the height structure of the input, and `site_effects="all"` includes `flow_sep_height`.
|
|
106
|
+
* User attribution is opt-in. windkit no longer prompts for a name, email and institution, which crashed non-interactive sessions, and no longer writes them to disk. Datasets carry `author`, `author_email` and `institution` only for fields set with `windkit configure` or the `WINDKIT_NAME`, `WINDKIT_EMAIL` and `WINDKIT_INSTITUTION` environment variables.
|
|
107
|
+
* A zip archive member passed to `read_elevation_map`, `read_roughness_map` or `read_landcover_map` is read in the format its name gives, instead of as a GeoPackage.
|
|
108
|
+
* `windkit.Workspace` places an observed or generalized wind climate at the met station that holds it, instead of at the last met station in the file. Printing a workspace shows a tree of all its objects.
|
|
109
|
+
* The wind speed statistics name their results the same way for every wind climate type: `mean_wind_speed`, `mean_power_density`, `wind_speed_moment`, `wind_speed_cdf` and `wind_speed_fgtm`. Time series results are float64.
|
|
110
|
+
|
|
111
|
+
### Deprecations
|
|
112
|
+
|
|
113
|
+
* `windkit.Workspace.get_object`, and the `west_east`, `south_north`, `height` and `description` arguments of `windkit.Workspace.get_gwc`, will be removed in windkit 3.0. Use the getter for the object, and set coordinates or `description` on the returned dataset.
|
|
114
|
+
* `windkit.config.CONFIG` will be removed in windkit 3.0; use `windkit.config.get_config()`. It now returns a frozen dataclass, so pydantic methods such as `model_dump()` are gone.
|
|
115
|
+
|
|
116
|
+
### Performance
|
|
117
|
+
|
|
118
|
+
* `polygons_to_lines` and `snap_to_layer` are several times faster, up to 70 times on maps with many small polygons. Output is unchanged, except that near-zero-area slivers get their sides assigned robustly.
|
|
119
|
+
* `nearest_points` is faster and keeps the source data lazy.
|
|
120
|
+
* `interp_unstructured` and `interp_unstructured_like` with `nearest`, `linear` or `cubic` build one interpolator for all sectors and wind speed bins instead of one per slice.
|
|
121
|
+
* Reading and writing `.map` files, and writing `.rsf`/`.wrg` files, is faster.
|
|
122
|
+
|
|
123
|
+
### Changes
|
|
124
|
+
|
|
125
|
+
* Malformed WAsP files raise `WindkitFileFormatError` naming the file. It subclasses `ValueError`, so `except ValueError` still catches it. `UnsupportedFileTypeError` now subclasses `WindkitError`.
|
|
126
|
+
* `read_owc` raises for malformed files, such as sectors with different numbers of bins or duplicate sectors, instead of zero-filling them.
|
|
127
|
+
* `windkit.read_wtg` rejects a performance table without `StationaryThrustCoEfficient` whose thrust curve is all zeros.
|
|
128
|
+
* `.map` files are read with any line ending and with automatic encoding detection, and written as UTF-8. Reading without `map_type` infers it, and a `map_type` that no `.map` file holds, such as `"speedup"`, raises.
|
|
129
|
+
* Writing a `.map` file warns when its CRS has no name in the WAsP map editor.
|
|
130
|
+
* `WindkitValidationError` lists every problem found in its `errors` attribute.
|
|
131
|
+
* `raster_plot` no longer calls `plt.show()`; display the returned figure yourself.
|
|
132
|
+
* `tswc_from_dataframe` and `get_tutorial_data` log instead of printing.
|
|
133
|
+
* `bwc_from_tswc` lists the dataset's dimensions in the order of its `wsfreq` variable.
|
|
134
|
+
* `affine>=3.0` is a declared dependency, and the `plots` extra requires `dash>=2.11`.
|
|
135
|
+
* The development environment supports `linux-aarch64` and `osx-arm64`, and needs pixi 0.68 or newer.
|
|
136
|
+
* The documentation has a version switcher and a page on how AI coding assistants are used in windkit's development.
|
|
6
137
|
|
|
7
138
|
## [2.1.0 (2026-04-22)](https://docs.wasp.dk/windkit/v2.1.0/)
|
|
8
139
|
|
|
@@ -77,6 +208,7 @@ Added support for reading and writing wind data in the [windIO format](https://i
|
|
|
77
208
|
|
|
78
209
|
### Breaking changes
|
|
79
210
|
|
|
211
|
+
* `bwc_from_tswc` now preserves singleton dimensions selected from TSWC inputs instead of stacking and unstacking through point structure; for example, `create_tswc(ds).isel(height=0)` now returns a BWC with the selected spatial dimensions rather than restoring `height: 1` and `stacked_point: 1`.
|
|
80
212
|
* 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
213
|
|
|
82
214
|
### Deprecations
|
|
@@ -87,6 +219,7 @@ Added support for reading and writing wind data in the [windIO format](https://i
|
|
|
87
219
|
### Performance
|
|
88
220
|
|
|
89
221
|
* 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.
|
|
222
|
+
* Small performance improvement in `bwc_from_tswc` due to using vectorized numpy and dropping stack/unstack pattern.
|
|
90
223
|
|
|
91
224
|
### Changes
|
|
92
225
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: windkit
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0rc1
|
|
4
4
|
Summary: WindKit provides core functionalities for working with wind resource data
|
|
5
5
|
Author: DTU Wind Energy
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -9,6 +9,7 @@ Classifier: Operating System :: OS Independent
|
|
|
9
9
|
Requires-Python: >=3.11
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
+
Requires-Dist: affine>=3.0
|
|
12
13
|
Requires-Dist: geopandas>=1.0
|
|
13
14
|
Requires-Dist: h5netcdf
|
|
14
15
|
Requires-Dist: lxml>=5.2
|
|
@@ -17,8 +18,6 @@ Requires-Dist: numpy>=2.0
|
|
|
17
18
|
Requires-Dist: packaging>=24.1
|
|
18
19
|
Requires-Dist: pandas>=2.3
|
|
19
20
|
Requires-Dist: platformdirs>=4.3
|
|
20
|
-
Requires-Dist: pydantic>=2.7
|
|
21
|
-
Requires-Dist: pydantic-settings>=2.3
|
|
22
21
|
Requires-Dist: pyproj>=3.7
|
|
23
22
|
Requires-Dist: pyyaml
|
|
24
23
|
Requires-Dist: rasterio>=1.4
|
|
@@ -26,12 +25,12 @@ Requires-Dist: requests
|
|
|
26
25
|
Requires-Dist: rioxarray>=0.17
|
|
27
26
|
Requires-Dist: scipy>=1.13
|
|
28
27
|
Requires-Dist: shapely>=2.0
|
|
29
|
-
Requires-Dist: toml
|
|
30
28
|
Requires-Dist: xarray>=2024.9
|
|
31
29
|
Provides-Extra: plots
|
|
32
30
|
Requires-Dist: matplotlib>=3.9; extra == "plots"
|
|
33
31
|
Requires-Dist: plotly>=5.21; extra == "plots"
|
|
34
32
|
Requires-Dist: kaleido>=1.0; extra == "plots"
|
|
33
|
+
Requires-Dist: dash>=2.11; extra == "plots"
|
|
35
34
|
Provides-Extra: performance
|
|
36
35
|
Requires-Dist: numba>=0.60; extra == "performance"
|
|
37
36
|
Provides-Extra: data-apis
|
|
@@ -42,8 +41,8 @@ Requires-Dist: planetary-computer>=1.0; extra == "data-apis"
|
|
|
42
41
|
Requires-Dist: pystac-client>=0.8; extra == "data-apis"
|
|
43
42
|
Requires-Dist: zarr>=3.0; extra == "data-apis"
|
|
44
43
|
Provides-Extra: dev
|
|
45
|
-
Requires-Dist: dash; extra == "dev"
|
|
46
44
|
Requires-Dist: ipython; extra == "dev"
|
|
45
|
+
Requires-Dist: jupytext>=1.16; extra == "dev"
|
|
47
46
|
Requires-Dist: nbval; extra == "dev"
|
|
48
47
|
Requires-Dist: pre-commit; extra == "dev"
|
|
49
48
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -53,7 +52,6 @@ Requires-Dist: windIO; extra == "dev"
|
|
|
53
52
|
Provides-Extra: doc
|
|
54
53
|
Requires-Dist: docutils; extra == "doc"
|
|
55
54
|
Requires-Dist: jupyter; extra == "doc"
|
|
56
|
-
Requires-Dist: jupyter-dash; extra == "doc"
|
|
57
55
|
Requires-Dist: jupyter-sphinx; extra == "doc"
|
|
58
56
|
Requires-Dist: myst-parser; extra == "doc"
|
|
59
57
|
Requires-Dist: nbsphinx; extra == "doc"
|
|
@@ -93,7 +91,7 @@ pip install windkit
|
|
|
93
91
|
|
|
94
92
|
**conda:**
|
|
95
93
|
```bash
|
|
96
|
-
conda install -c https://conda.windenergy.dtu.dk/channel/open windkit
|
|
94
|
+
conda install -c https://conda.windenergy.dtu.dk/channel/open/ -c conda-forge windkit
|
|
97
95
|
```
|
|
98
96
|
|
|
99
97
|
## Quick Start
|
|
@@ -106,7 +104,7 @@ bwc = wk.read_bwc("hovsore.tab")
|
|
|
106
104
|
|
|
107
105
|
# Calculate mean wind speed
|
|
108
106
|
mean_ws = wk.mean_wind_speed(bwc)
|
|
109
|
-
print(f"Mean wind speed: {mean_ws.
|
|
107
|
+
print(f"Mean wind speed: {mean_ws.item():.2f} m/s")
|
|
110
108
|
|
|
111
109
|
# Fit Weibull distribution and convert to Weibull wind climate
|
|
112
110
|
wwc = wk.weibull_fit(bwc)
|
|
@@ -114,11 +112,13 @@ wwc = wk.weibull_fit(bwc)
|
|
|
114
112
|
|
|
115
113
|
## Features
|
|
116
114
|
|
|
117
|
-
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .lib, .
|
|
118
|
-
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats)
|
|
119
|
-
- **WAsP Integration** - Read WAsP
|
|
115
|
+
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .tab, .owc, .lib, .gwc, .rsf, .wrg, windIO YAML)
|
|
116
|
+
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats, including WAsP .map)
|
|
117
|
+
- **WAsP Integration** - Read the inputs and results of WAsP workspaces (.wwh) and WAsP Engineering project files, with low-level readers for the WAsP file formats
|
|
120
118
|
- **Spatial Processing** - Clip, reproject, and interpolate spatial data with full CRS support
|
|
121
119
|
- **Wind Statistics** - Calculate mean wind speed, power density, Weibull parameters, and more
|
|
120
|
+
- **Long-Term Correction** - Measure-correlate-predict (MCP) methods for time series
|
|
121
|
+
- **Losses and Uncertainty** - Net AEP and exceedance values (P90) from loss and uncertainty tables
|
|
122
122
|
- **Plotting** - Interactive plots with Plotly, static plots with Matplotlib
|
|
123
123
|
- **Remote Data Access** - Download ERA5 reanalysis data and satellite-derived maps
|
|
124
124
|
|
|
@@ -22,7 +22,7 @@ pip install windkit
|
|
|
22
22
|
|
|
23
23
|
**conda:**
|
|
24
24
|
```bash
|
|
25
|
-
conda install -c https://conda.windenergy.dtu.dk/channel/open windkit
|
|
25
|
+
conda install -c https://conda.windenergy.dtu.dk/channel/open/ -c conda-forge windkit
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Quick Start
|
|
@@ -35,7 +35,7 @@ bwc = wk.read_bwc("hovsore.tab")
|
|
|
35
35
|
|
|
36
36
|
# Calculate mean wind speed
|
|
37
37
|
mean_ws = wk.mean_wind_speed(bwc)
|
|
38
|
-
print(f"Mean wind speed: {mean_ws.
|
|
38
|
+
print(f"Mean wind speed: {mean_ws.item():.2f} m/s")
|
|
39
39
|
|
|
40
40
|
# Fit Weibull distribution and convert to Weibull wind climate
|
|
41
41
|
wwc = wk.weibull_fit(bwc)
|
|
@@ -43,11 +43,13 @@ wwc = wk.weibull_fit(bwc)
|
|
|
43
43
|
|
|
44
44
|
## Features
|
|
45
45
|
|
|
46
|
-
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .lib, .
|
|
47
|
-
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats)
|
|
48
|
-
- **WAsP Integration** - Read WAsP
|
|
46
|
+
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .tab, .owc, .lib, .gwc, .rsf, .wrg, windIO YAML)
|
|
47
|
+
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats, including WAsP .map)
|
|
48
|
+
- **WAsP Integration** - Read the inputs and results of WAsP workspaces (.wwh) and WAsP Engineering project files, with low-level readers for the WAsP file formats
|
|
49
49
|
- **Spatial Processing** - Clip, reproject, and interpolate spatial data with full CRS support
|
|
50
50
|
- **Wind Statistics** - Calculate mean wind speed, power density, Weibull parameters, and more
|
|
51
|
+
- **Long-Term Correction** - Measure-correlate-predict (MCP) methods for time series
|
|
52
|
+
- **Losses and Uncertainty** - Net AEP and exceedance values (P90) from loss and uncertainty tables
|
|
51
53
|
- **Plotting** - Interactive plots with Plotly, static plots with Matplotlib
|
|
52
54
|
- **Remote Data Access** - Download ERA5 reanalysis data and satellite-derived maps
|
|
53
55
|
|
|
@@ -17,6 +17,7 @@ classifiers = [
|
|
|
17
17
|
dynamic = ["version"]
|
|
18
18
|
requires-python = ">=3.11"
|
|
19
19
|
dependencies = [
|
|
20
|
+
"affine>=3.0",
|
|
20
21
|
"geopandas>=1.0",
|
|
21
22
|
"h5netcdf",
|
|
22
23
|
"lxml>=5.2",
|
|
@@ -25,8 +26,6 @@ dependencies = [
|
|
|
25
26
|
"packaging>=24.1",
|
|
26
27
|
"pandas>=2.3",
|
|
27
28
|
"platformdirs>=4.3",
|
|
28
|
-
"pydantic>=2.7",
|
|
29
|
-
"pydantic-settings>=2.3",
|
|
30
29
|
"pyproj>=3.7",
|
|
31
30
|
"pyyaml",
|
|
32
31
|
"rasterio>=1.4",
|
|
@@ -34,7 +33,6 @@ dependencies = [
|
|
|
34
33
|
"rioxarray>=0.17",
|
|
35
34
|
"scipy>=1.13",
|
|
36
35
|
"shapely>=2.0",
|
|
37
|
-
"toml",
|
|
38
36
|
"xarray>=2024.9",
|
|
39
37
|
]
|
|
40
38
|
|
|
@@ -43,6 +41,7 @@ plots = [
|
|
|
43
41
|
"matplotlib>=3.9",
|
|
44
42
|
"plotly>=5.21",
|
|
45
43
|
"kaleido>=1.0",
|
|
44
|
+
"dash>=2.11",
|
|
46
45
|
]
|
|
47
46
|
performance = [
|
|
48
47
|
"numba>=0.60",
|
|
@@ -56,8 +55,8 @@ data_apis = [
|
|
|
56
55
|
"zarr>=3.0",
|
|
57
56
|
]
|
|
58
57
|
dev = [
|
|
59
|
-
"dash",
|
|
60
58
|
"ipython",
|
|
59
|
+
"jupytext>=1.16",
|
|
61
60
|
"nbval",
|
|
62
61
|
"pre-commit",
|
|
63
62
|
"pytest",
|
|
@@ -68,7 +67,6 @@ dev = [
|
|
|
68
67
|
doc = [
|
|
69
68
|
"docutils",
|
|
70
69
|
"jupyter",
|
|
71
|
-
"jupyter-dash",
|
|
72
70
|
"jupyter-sphinx",
|
|
73
71
|
"myst-parser",
|
|
74
72
|
"nbsphinx",
|
|
@@ -85,10 +83,15 @@ doc = [
|
|
|
85
83
|
]
|
|
86
84
|
|
|
87
85
|
[project.scripts]
|
|
86
|
+
windkit = "windkit._cli:main"
|
|
88
87
|
split_combomap = "windkit.topography.vector_map:_split_combo"
|
|
89
88
|
|
|
90
89
|
[tool.setuptools_scm]
|
|
91
90
|
write_to = "windkit/_version.py"
|
|
91
|
+
# Between releases master is the *next minor* (2.2.0.devN after v2.1.0), so
|
|
92
|
+
# a dev build can satisfy a >=2.2 pin; the default scheme would call it
|
|
93
|
+
# 2.1.1.devN. On a vX.Y.x release branch the scheme bumps the patch instead.
|
|
94
|
+
version_scheme = "release-branch-semver"
|
|
92
95
|
|
|
93
96
|
[tool.setuptools]
|
|
94
97
|
include-package-data = true
|
|
@@ -111,8 +114,25 @@ markers = [
|
|
|
111
114
|
"performance: marks tests as performance tests (run with '-m performance')",
|
|
112
115
|
]
|
|
113
116
|
|
|
114
|
-
#
|
|
115
|
-
#
|
|
117
|
+
# Third-party warnings raised inside upstream packages, not by windkit's own
|
|
118
|
+
# calls. Each entry is pinned to message, category and module so that it stops
|
|
119
|
+
# matching -- and the warning resurfaces for re-triage -- if upstream rewords
|
|
120
|
+
# or fixes it. Warnings aimed at windkit's own code are fixed at the call site,
|
|
121
|
+
# never listed here.
|
|
122
|
+
#
|
|
123
|
+
# pytest.warns re-emits the warnings it did not match, attributed to the
|
|
124
|
+
# module of warnings.WarningMessage (`warnings`, or `_py_warnings` on
|
|
125
|
+
# Python >= 3.14) rather than to the emitting module, so entries that can
|
|
126
|
+
# fire inside a pytest.warns block list those two modules as well. The cost:
|
|
127
|
+
# inside a pytest.warns block, the same message raised by windkit's own code
|
|
128
|
+
# is hidden too, so those call sites must be covered by tests outside one.
|
|
116
129
|
filterwarnings = [
|
|
117
|
-
|
|
130
|
+
# rioxarray (and rasterio) multiply Affine objects with `*`, which affine
|
|
131
|
+
# 3.0.1 deprecates in favour of `@`. rioxarray raises it from
|
|
132
|
+
# rioxarray._spatial_utils and rioxarray.rioxarray (0.17 through 0.22).
|
|
133
|
+
'ignore:Use `@` matmul instead of `\*` mul operator:PendingDeprecationWarning:(rioxarray|warnings|_py_warnings)(\.|$)',
|
|
134
|
+
'ignore:Use `@` matmul instead of `\*` mul operator:PendingDeprecationWarning:rasterio\.transform',
|
|
135
|
+
# pyogrio < 0.12 imports the deprecated shapely.geos module at import time;
|
|
136
|
+
# seen in the default-min environment only.
|
|
137
|
+
"ignore:The 'shapely.geos' module is deprecated:DeprecationWarning:pyogrio(\\.|$)",
|
|
118
138
|
]
|
|
@@ -9,6 +9,8 @@ __all__ = [
|
|
|
9
9
|
"spatial",
|
|
10
10
|
# Subpackage - LTC
|
|
11
11
|
"ltc",
|
|
12
|
+
# Subpackage - io (loaded lazily on first access)
|
|
13
|
+
"io",
|
|
12
14
|
# Wind
|
|
13
15
|
"wind_speed",
|
|
14
16
|
"wind_direction",
|
|
@@ -32,6 +34,12 @@ __all__ = [
|
|
|
32
34
|
"tswc_to_file",
|
|
33
35
|
"tswc_from_dataframe",
|
|
34
36
|
"create_tswc_pair",
|
|
37
|
+
# Extreme wind climates
|
|
38
|
+
"create_gewc",
|
|
39
|
+
"create_pewc",
|
|
40
|
+
"create_v50_lut",
|
|
41
|
+
# Mesoscale surface fields
|
|
42
|
+
"create_surface_fields",
|
|
35
43
|
# Binned wind climate
|
|
36
44
|
"validate_bwc",
|
|
37
45
|
"is_bwc",
|
|
@@ -76,6 +84,7 @@ __all__ = [
|
|
|
76
84
|
"get_landcover_table",
|
|
77
85
|
# Raster map
|
|
78
86
|
"create_raster_map",
|
|
87
|
+
"fill_elevation_nodata",
|
|
79
88
|
# Vector map
|
|
80
89
|
"create_vector_map",
|
|
81
90
|
"add_landcover_table",
|
|
@@ -147,9 +156,23 @@ __all__ = [
|
|
|
147
156
|
# tutorial_data
|
|
148
157
|
"get_tutorial_data",
|
|
149
158
|
"load_tutorial_data",
|
|
159
|
+
# Errors
|
|
160
|
+
"WindkitError",
|
|
161
|
+
"WindkitValidationError",
|
|
162
|
+
"WindkitFileFormatError",
|
|
163
|
+
"UnsupportedFileTypeError",
|
|
150
164
|
]
|
|
151
165
|
|
|
166
|
+
import importlib
|
|
167
|
+
from typing import TYPE_CHECKING
|
|
168
|
+
|
|
152
169
|
from windkit._version import version as __version__
|
|
170
|
+
from windkit._errors import (
|
|
171
|
+
WindkitError,
|
|
172
|
+
WindkitValidationError,
|
|
173
|
+
WindkitFileFormatError,
|
|
174
|
+
UnsupportedFileTypeError,
|
|
175
|
+
)
|
|
153
176
|
from windkit import spatial
|
|
154
177
|
from windkit import ltc
|
|
155
178
|
from windkit.wind import (
|
|
@@ -178,6 +201,14 @@ from windkit.wind_climate.time_series_wind_climate import (
|
|
|
178
201
|
create_tswc_pair,
|
|
179
202
|
)
|
|
180
203
|
from windkit.wind_climate import time_series_wind_climate # noqa: F401
|
|
204
|
+
from windkit.wind_climate.extreme_wind_climate import (
|
|
205
|
+
create_gewc,
|
|
206
|
+
create_pewc,
|
|
207
|
+
create_v50_lut,
|
|
208
|
+
)
|
|
209
|
+
from windkit.wind_climate import extreme_wind_climate # noqa: F401
|
|
210
|
+
from windkit.wind_climate.stability import create_surface_fields
|
|
211
|
+
from windkit.wind_climate import stability # noqa: F401
|
|
181
212
|
from windkit.wind_climate.binned_wind_climate import (
|
|
182
213
|
validate_bwc,
|
|
183
214
|
is_bwc,
|
|
@@ -264,6 +295,7 @@ from windkit.topography.get_map import _get_ee_map, get_raster_map, get_vector_m
|
|
|
264
295
|
from windkit.topography.landcover import LandCoverTable, get_landcover_table
|
|
265
296
|
from windkit.topography.raster_map import (
|
|
266
297
|
create_raster_map,
|
|
298
|
+
fill_elevation_nodata,
|
|
267
299
|
)
|
|
268
300
|
from windkit.topography.vector_map import create_vector_map, add_landcover_table
|
|
269
301
|
from windkit.topography.topography import (
|
|
@@ -311,3 +343,21 @@ from windkit.xarray_structures.wsbin import (
|
|
|
311
343
|
create_wsbin_coords,
|
|
312
344
|
)
|
|
313
345
|
from windkit.tutorial_data import get_tutorial_data, load_tutorial_data
|
|
346
|
+
|
|
347
|
+
if TYPE_CHECKING:
|
|
348
|
+
from windkit import io # noqa: F401
|
|
349
|
+
|
|
350
|
+
# Subpackages guaranteed reachable after `import windkit` via lazy import on
|
|
351
|
+
# first attribute access (PEP 562), independent of whether internal imports
|
|
352
|
+
# happen to load them as a side effect (#713).
|
|
353
|
+
_LAZY_SUBPACKAGES = ("io",)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def __getattr__(name):
|
|
357
|
+
if name in _LAZY_SUBPACKAGES:
|
|
358
|
+
return importlib.import_module(f"windkit.{name}")
|
|
359
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def __dir__():
|
|
363
|
+
return sorted(set(globals()) | set(_LAZY_SUBPACKAGES))
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Command-line interface for windkit."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from windkit import config
|
|
7
|
+
|
|
8
|
+
_LABELS = {"name": "Name", "email": "Email", "institution": "Institution"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _configure(args) -> int:
|
|
12
|
+
"""Write the user attribution file from flags, the existing file and prompts."""
|
|
13
|
+
path = config._CONFIG_FILE
|
|
14
|
+
existing = config._read_config_file(path)
|
|
15
|
+
# sys.stdin is None when stdin is closed or under pythonw
|
|
16
|
+
interactive = sys.stdin is not None and sys.stdin.isatty()
|
|
17
|
+
|
|
18
|
+
values = {}
|
|
19
|
+
missing = []
|
|
20
|
+
for name in config._FIELD_NAMES:
|
|
21
|
+
given = getattr(args, name)
|
|
22
|
+
if given is not None:
|
|
23
|
+
values[name] = config._clean(given)
|
|
24
|
+
elif interactive:
|
|
25
|
+
default = existing.get(name)
|
|
26
|
+
suffix = f" [{default}]" if default else ""
|
|
27
|
+
answer = input(f"{_LABELS[name]}{suffix}: ")
|
|
28
|
+
values[name] = config._clean(answer) or default
|
|
29
|
+
elif name in existing:
|
|
30
|
+
values[name] = existing[name]
|
|
31
|
+
else:
|
|
32
|
+
missing.append(name)
|
|
33
|
+
|
|
34
|
+
if missing:
|
|
35
|
+
flags = ", ".join(f"--{name}" for name in missing)
|
|
36
|
+
print(
|
|
37
|
+
f"windkit configure: there is no terminal to prompt for {flags}. "
|
|
38
|
+
"Pass them as flags; an empty value leaves a field unset.",
|
|
39
|
+
file=sys.stderr,
|
|
40
|
+
)
|
|
41
|
+
return 1
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
config._write_config_file(path, config.Config(**values))
|
|
45
|
+
except (UnicodeEncodeError, OSError) as e:
|
|
46
|
+
print(
|
|
47
|
+
f"windkit configure: could not write {path}: {e}. The existing "
|
|
48
|
+
"configuration is unchanged.",
|
|
49
|
+
file=sys.stderr,
|
|
50
|
+
)
|
|
51
|
+
return 1
|
|
52
|
+
config.get_config.cache_clear()
|
|
53
|
+
print(f"Saved the windkit configuration to {path}")
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main(argv: list[str] | None = None) -> int:
|
|
58
|
+
"""Run the windkit command-line interface.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
argv : list of str, optional
|
|
63
|
+
Command-line arguments, excluding the program name. Defaults to
|
|
64
|
+
``sys.argv[1:]``.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
int
|
|
69
|
+
Exit status.
|
|
70
|
+
"""
|
|
71
|
+
parser = argparse.ArgumentParser(
|
|
72
|
+
prog="windkit", description="windkit command-line interface"
|
|
73
|
+
)
|
|
74
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
75
|
+
|
|
76
|
+
configure_parser = subparsers.add_parser(
|
|
77
|
+
"configure",
|
|
78
|
+
help="Set the user attribution stamped on windkit datasets",
|
|
79
|
+
description=(
|
|
80
|
+
"Write the name, email and institution that windkit stamps on the "
|
|
81
|
+
"datasets it creates as author, author_email and institution. "
|
|
82
|
+
"Fields not given as flags are prompted for, with the current "
|
|
83
|
+
"values as defaults, and an empty answer keeps the current value. "
|
|
84
|
+
"Without a terminal, fields not given as flags keep their current "
|
|
85
|
+
'values. An empty flag value, such as --email "", leaves a field '
|
|
86
|
+
"unset. The WINDKIT_NAME, WINDKIT_EMAIL and WINDKIT_INSTITUTION "
|
|
87
|
+
"environment variables take precedence over the file."
|
|
88
|
+
),
|
|
89
|
+
)
|
|
90
|
+
for name in config._FIELD_NAMES:
|
|
91
|
+
configure_parser.add_argument(f"--{name}", help=f"{_LABELS[name]} to stamp")
|
|
92
|
+
configure_parser.set_defaults(func=_configure)
|
|
93
|
+
|
|
94
|
+
args = parser.parse_args(argv)
|
|
95
|
+
return args.func(args)
|