windkit 0.8.0__tar.gz → 2.0.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.
- {windkit-0.8.0 → windkit-2.0.0}/CHANGELOG.md +319 -7
- windkit-2.0.0/PERFORMANCE_CI_PLAN.md +294 -0
- windkit-2.0.0/PKG-INFO +115 -0
- windkit-2.0.0/README.md +66 -0
- {windkit-0.8.0 → windkit-2.0.0}/pyproject.toml +4 -8
- windkit-2.0.0/setup.cfg +66 -0
- windkit-2.0.0/windkit/__init__.py +304 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/_errors.py +7 -1
- windkit-2.0.0/windkit/_version.py +34 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/_weibull_nb.py +0 -1
- {windkit-0.8.0 → windkit-2.0.0}/windkit/cfd.py +11 -6
- windkit-2.0.0/windkit/config.py +215 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/data}/all_vars.json +43 -13
- windkit-2.0.0/windkit/data/color_table_roughness.json +92 -0
- windkit-2.0.0/windkit/data/landcover_tables/GWA4_micro.json +156 -0
- windkit-2.0.0/windkit/data/landcover_tables/NEWA_micro_v2.json +1 -0
- windkit-2.0.0/windkit/data/loss_tables/dtu_loss_table.csv +16 -0
- windkit-2.0.0/windkit/data/map_file_header_to_epsg_read.json +937 -0
- windkit-2.0.0/windkit/data/uncertainty_tables/dtu_uncertainty_table.csv +35 -0
- windkit-2.0.0/windkit/import_manager.py +44 -0
- windkit-2.0.0/windkit/integrations/__init__.py +0 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/integrations}/_earth_engine.py +4 -13
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/integrations}/_gwa_map_api.py +41 -24
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/integrations}/_planetary_computer.py +12 -18
- {windkit-0.8.0 → windkit-2.0.0}/windkit/ltc/__init__.py +6 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/ltc/_validation.py +25 -27
- {windkit-0.8.0 → windkit-2.0.0}/windkit/ltc/mcp.py +12 -10
- {windkit-0.8.0 → windkit-2.0.0}/windkit/ltc/scores.py +21 -22
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/__init__.py +3 -1
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/_colormaps.py +27 -8
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/_helpers.py +32 -54
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/color.py +9 -12
- windkit-2.0.0/windkit/plot/elevation_map.py +47 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/histogram.py +34 -23
- windkit-2.0.0/windkit/plot/landcover_map.py +62 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/operational_curves.py +13 -22
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/raster_plot.py +6 -10
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/roughness_rose.py +14 -20
- windkit-2.0.0/windkit/plot/time_series.py +254 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/vertical_profile.py +3 -10
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/wind_rose.py +7 -16
- {windkit-0.8.0 → windkit-2.0.0}/windkit/plot/wind_turbine.py +2 -1
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/__init__.py +88 -33
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_bbox.py +13 -20
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_crs.py +41 -21
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_cuboid.py +2 -2
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_interpolation.py +44 -194
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_latitude.py +6 -3
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_metpy_natural_neighbors.py +22 -12
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_point.py +35 -31
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_raster.py +24 -25
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_struct.py +14 -6
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_utm.py +3 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_vector.py +2 -3
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_vertical.py +1 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/decorators.py +8 -4
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/spatial.py +366 -120
- windkit-2.0.0/windkit/topography/__init__.py +0 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/_vectormap_gml.py +23 -17
- windkit-2.0.0/windkit/topography/_vectormap_helpers.py +554 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/get_map.py +51 -46
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/landcover.py +50 -46
- windkit-2.0.0/windkit/topography/map_conversion/__init__.py +3 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/map_conversion/_polygons_constraints.py +1 -11
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/map_conversion/helper_functions.py +1 -7
- windkit-0.8.0/windkit/map_conversion/lines2poly.py → windkit-2.0.0/windkit/topography/map_conversion/lines_to_poly.py +23 -24
- windkit-0.8.0/windkit/map_conversion/poly2lines.py → windkit-2.0.0/windkit/topography/map_conversion/poly_to_lines.py +25 -20
- windkit-2.0.0/windkit/topography/map_conversions.py +902 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/raster_map.py +193 -94
- windkit-2.0.0/windkit/topography/topography.py +481 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/topography}/vector_map.py +372 -175
- windkit-2.0.0/windkit/tutorial_data.py +188 -0
- windkit-2.0.0/windkit/utils.py +34 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/weibull.py +42 -15
- windkit-2.0.0/windkit/weng_workspace.py +256 -0
- windkit-2.0.0/windkit/wind.py +968 -0
- windkit-2.0.0/windkit/wind_climate/__init__.py +0 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_climate}/binned_wind_climate.py +332 -257
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_climate}/generalized_wind_climate.py +245 -89
- windkit-2.0.0/windkit/wind_climate/geostrophic_wind_climate.py +178 -0
- windkit-2.0.0/windkit/wind_climate/predicted_wind_climate.py +97 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_climate}/time_series_wind_climate.py +213 -50
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_climate}/weibull_wind_climate.py +289 -187
- windkit-2.0.0/windkit/wind_climate/wind_climate.py +663 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/wind_data.py +3 -1
- windkit-2.0.0/windkit/wind_farm/__init__.py +0 -0
- windkit-2.0.0/windkit/wind_farm/loss.py +185 -0
- windkit-2.0.0/windkit/wind_farm/power_curve.py +124 -0
- windkit-2.0.0/windkit/wind_farm/uncertainty.py +335 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_farm}/wind_turbine_generator.py +71 -244
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/wind_farm}/wind_turbines.py +33 -28
- {windkit-0.8.0 → windkit-2.0.0}/windkit/workspace.py +120 -41
- windkit-2.0.0/windkit/xarray_structures/_validate.py +225 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/xarray_structures}/data_structures.py +8 -6
- windkit-2.0.0/windkit/xarray_structures/direction.py +52 -0
- windkit-2.0.0/windkit/xarray_structures/empty.py +79 -0
- windkit-2.0.0/windkit/xarray_structures/metadata.py +525 -0
- windkit-2.0.0/windkit/xarray_structures/sector.py +93 -0
- windkit-2.0.0/windkit/xarray_structures/wsbin.py +95 -0
- windkit-2.0.0/windkit.egg-info/SOURCES.txt +110 -0
- windkit-0.8.0/.conda_env/dev_unix.yml +0 -44
- windkit-0.8.0/.conda_env/dev_win.yml +0 -42
- windkit-0.8.0/.conda_env/prod_unix.yml +0 -14
- windkit-0.8.0/.gitignore +0 -41
- windkit-0.8.0/.gitlab/merge_request_templates/default.md +0 -27
- windkit-0.8.0/.gitlab/merge_request_templates/release.md +0 -43
- windkit-0.8.0/.gitlab-ci.yml +0 -132
- windkit-0.8.0/.pre-commit-config.yaml +0 -36
- windkit-0.8.0/.pylintrc +0 -34
- windkit-0.8.0/MANIFEST.in +0 -8
- windkit-0.8.0/PKG-INFO +0 -70
- windkit-0.8.0/README-Internal.md +0 -66
- windkit-0.8.0/README.md +0 -37
- windkit-0.8.0/docs/.dockerignore +0 -6
- windkit-0.8.0/docs/.gitignore +0 -1
- windkit-0.8.0/docs/Dockerfile +0 -4
- windkit-0.8.0/docs/Makefile +0 -184
- windkit-0.8.0/docs/docker-compose.yml +0 -19
- windkit-0.8.0/docs/legacy/dev/dev-git.rst +0 -35
- windkit-0.8.0/docs/legacy/dev/dev-intro.rst +0 -45
- windkit-0.8.0/docs/legacy/dev/dev-pytest.rst +0 -40
- windkit-0.8.0/docs/legacy/dev/dev-sphinx.rst +0 -28
- windkit-0.8.0/docs/nginx.conf +0 -17
- windkit-0.8.0/docs/source/WAsP.bib +0 -187
- windkit-0.8.0/docs/source/_templates/autosummary/class.rst +0 -33
- windkit-0.8.0/docs/source/_templates/autosummary/module.rst +0 -39
- windkit-0.8.0/docs/source/conf.py +0 -315
- windkit-0.8.0/docs/source/development_roadmap.rst +0 -7
- windkit-0.8.0/docs/source/getting_started.rst +0 -13
- windkit-0.8.0/docs/source/index.rst +0 -59
- windkit-0.8.0/docs/source/installation.rst +0 -89
- windkit-0.8.0/docs/source/io/io.rst +0 -164
- windkit-0.8.0/docs/source/map_conversion.rst +0 -10
- windkit-0.8.0/docs/source/map_conversion_examples_nb.nblink +0 -3
- windkit-0.8.0/docs/source/plotting.rst +0 -14
- windkit-0.8.0/docs/source/plotting_examples.rst +0 -10
- windkit-0.8.0/docs/source/plotting_examples_nb.nblink +0 -3
- windkit-0.8.0/docs/source/plotting_user_guide.rst +0 -129
- windkit-0.8.0/docs/source/release_notes.rst +0 -8
- windkit-0.8.0/docs/source/topographic_data.rst +0 -76
- windkit-0.8.0/docs/source/wind_climate_objects.rst +0 -210
- windkit-0.8.0/docs/source/wind_turbines.rst +0 -138
- windkit-0.8.0/docs/source/windkit.rst +0 -57
- windkit-0.8.0/examples/ERA5_Sujawal_2010_2018_80m.nc +0 -0
- windkit-0.8.0/examples/long_term_correction.ipynb +0 -547
- windkit-0.8.0/pixi.lock +0 -31168
- windkit-0.8.0/pixi.toml +0 -63
- windkit-0.8.0/recipe/recipe.yaml +0 -54
- windkit-0.8.0/setup.cfg +0 -49
- windkit-0.8.0/windkit/__init__.py +0 -40
- windkit-0.8.0/windkit/_validate.py +0 -87
- windkit-0.8.0/windkit/_vectormap_helpers.py +0 -184
- windkit-0.8.0/windkit/_version.py +0 -16
- windkit-0.8.0/windkit/config.py +0 -5
- windkit-0.8.0/windkit/elevation_map.py +0 -40
- windkit-0.8.0/windkit/empty.py +0 -761
- windkit-0.8.0/windkit/geospatial_imports.py +0 -52
- windkit-0.8.0/windkit/geostrophic_wind_climate.py +0 -59
- windkit-0.8.0/windkit/map_conversion/__init__.py +0 -3
- windkit-0.8.0/windkit/metadata.py +0 -495
- windkit-0.8.0/windkit/plot/landcover_map.py +0 -74
- windkit-0.8.0/windkit/roughness_map.py +0 -41
- windkit-0.8.0/windkit/sector.py +0 -211
- windkit-0.8.0/windkit/wind.py +0 -451
- windkit-0.8.0/windkit/wind_climate.py +0 -290
- windkit-0.8.0/windkit.egg-info/SOURCES.txt +0 -134
- {windkit-0.8.0 → windkit-2.0.0}/LICENSE +0 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/_rvea_xml.py +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/CGLS-LC100.json +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/CORINE.json +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/ESA_CCI.json +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/Globcover.json +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/MODIS.json +0 -0
- {windkit-0.8.0/windkit/landcovertables → windkit-2.0.0/windkit/data/landcover_tables}/WorldCover.json +0 -0
- {windkit-0.8.0/windkit → windkit-2.0.0/windkit/data}/map_file_header_to_epsg.json +0 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/ltc/regression.py +0 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/README.md +0 -0
- {windkit-0.8.0 → windkit-2.0.0}/windkit/spatial/_dimensions.py +0 -0
|
@@ -2,9 +2,321 @@
|
|
|
2
2
|
|
|
3
3
|
All major changes are listed here.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 2.0.0 (2025-07-21)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Highlights
|
|
8
|
+
|
|
9
|
+
### Performance improvements
|
|
10
|
+
* Significantly reduced import time by lazy loading some packages and data. Should be approx 1/3 of previous import time.
|
|
11
|
+
|
|
12
|
+
### Sheer and veer functionality
|
|
13
|
+
|
|
14
|
+
* Added new function `wk.shear_extrapolate` to extrapolate wind speeds to different heights using a power law shear profile.
|
|
15
|
+
* Added new function `wk.shear_exponent` to calculate a shear exponent from wind speeds
|
|
16
|
+
* Added guidance about `wk.shear_exponent` into shear extrapolation example.
|
|
17
|
+
|
|
18
|
+
* Added new function `wk.veer_extrapolate` to extrapolate wind direction linearly to different heights, accounting for circular discontinuities.
|
|
19
|
+
* Added new function `wk.wind_veer` to calculate wind veer from wind direction
|
|
20
|
+
* Added guidance about `wk.wind_veer` into shear extrapolation example.
|
|
21
|
+
|
|
22
|
+
### Support for PyWAsP's Geostrophic wind climate objects
|
|
23
|
+
|
|
24
|
+
* new function `create_geowc`, which creates a dummy geostrophic wind climate
|
|
25
|
+
* 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
|
+
|
|
28
|
+
### New features
|
|
29
|
+
* 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
|
+
* `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
|
+
* `wk.roughness_map_to_file` now allows users to write to a `.map` as long all displacement heights in the map are zero.
|
|
33
|
+
* Bugfix in `read_bwc`: it now opens `bwc` stored in netcdf correctly.
|
|
34
|
+
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.
|
|
36
|
+
|
|
37
|
+
### Changes
|
|
38
|
+
* Changed default value of `use_bounds` to `True` in `wk.spatial.BBox.reproject` method.
|
|
39
|
+
|
|
40
|
+
### Breaking changes
|
|
41
|
+
|
|
42
|
+
* `spatial_interpolate_gwc` has been removed. This functionality is now only available in PyWAsP through its `interpolate_gwc` function.
|
|
43
|
+
|
|
44
|
+
## [1.0.2 (2025-07-21)](https://docs.wasp.dk/windkit/v1.0.2/)
|
|
45
|
+
|
|
46
|
+
WindKit 1.0.2 is a patch release to ensure consistent config and app data paths on windows. Both the config and app data paths are now set by default to ``C:\\Users\\<user>\\AppData\\Roaming\\DTU Wind Energy\\windkit``.
|
|
47
|
+
|
|
48
|
+
## 1.0.1 (2025-07-15)
|
|
49
|
+
|
|
50
|
+
WindKit 1.0.1 is a patch release that changes the config migration that shipped with 1.0.0. The changed ensures that the legacy `.ini` configuration files are migrated to the new `.toml` format without automatically removing the old file. This ensures that users are not constantly prompted to update their configuration file if they use different versions of WindKit in different environments.
|
|
51
|
+
|
|
52
|
+
## 1.0.0 (2025-06-26)
|
|
53
|
+
|
|
54
|
+
This is a major release that introduces a significantly cleaner and more intuitive API. We have removed many functions from the public namespace, renamed others for consistency, and introduced powerful new features. While this version includes many breaking changes, the result is a more streamlined and user-friendly library. We strongly recommend all users to carefully read these release notes and update their code.
|
|
55
|
+
|
|
56
|
+
### Highlights
|
|
57
|
+
|
|
58
|
+
#### Simplified and Consistent API
|
|
59
|
+
The `windkit` namespace has been significantly cleaned up to make the API more predictable and easier to learn. Many internal or redundant functions have been removed from the public API, and others have been renamed for better clarity and consistency.
|
|
60
|
+
* Functions for reading and writing different objects in windkit are now grouped by type, such as `read_tswc`, `read_bwc`, `read_wwc`, and `read_gwc` for reading wind climate datasets, and `bwc_to_file`, `wwc_to_file`, and `gwc_to_file` for writing them.
|
|
61
|
+
* Functions for creating empty datasets are now prefixed with `create_*` (e.g., `create_bwc` instead of `empty_bwc`).
|
|
62
|
+
* Functions for creating wind climate objects from other data types now follow a `[climate]_from_[source]` pattern (e.g., `bwc_from_tswc` instead of `bwc_from_timeseries`).
|
|
63
|
+
* Validation functions are now consistently named `validate_*` (e.g., `validate_bwc` instead of `bwc_validate`).
|
|
64
|
+
|
|
65
|
+
#### Revamped Map Reading and Writing
|
|
66
|
+
The API for handling spatial maps has been completely overhauled. The generic `read_raster_map`, `read_vector_map`, `raster_map_to_file`, and `vector_map_to_file` functions have been replaced by specific, format-aware functions for each map type:
|
|
67
|
+
* `read_elevation_map` and `elevation_map_to_file`
|
|
68
|
+
* `read_roughness_map` and `roughness_map_to_file`
|
|
69
|
+
* `read_landcover_map` and `landcover_map_to_file`
|
|
70
|
+
|
|
71
|
+
These new functions automatically infer the file format from the suffix (e.g., `.map`, `.tif`, `.gpkg`) and provide a more robust and intuitive interface for working with elevation, roughness, and landcover data.
|
|
72
|
+
|
|
73
|
+
#### Polygon Representation for Landcover and Roughness Maps
|
|
74
|
+
A major improvement is the new default support for polygon-based landcover and roughness maps. The functions `read_landcover_map` and `read_roughness_map` now return `geopandas.GeoDataFrame` objects with polygons when reading vector data, which is a more standard and powerful representation for GIS data. This enables more robust handling and validation of maps. For backward compatibility, you can still work with the legacy line-based format by setting `polygons=False`.
|
|
75
|
+
|
|
76
|
+
#### Unified API for Wind Climate Statistics
|
|
77
|
+
Calculating statistics from wind climate objects is now much simpler. A new set of generic functions can operate on any wind climate type (BWC, WWC, etc.):
|
|
78
|
+
* `wk.mean_wind_speed`
|
|
79
|
+
* `wk.mean_power_density`
|
|
80
|
+
* `wk.ws_cdf` (Cumulative Distribution Function)
|
|
81
|
+
* `wk.ws_freq_gt_mean`
|
|
82
|
+
* `wk.mean_ws_moment`
|
|
83
|
+
|
|
84
|
+
This unified API replaces the climate-specific functions (e.g., `bwc_mean_windspeed`), simplifying your code and making it more versatile.
|
|
85
|
+
|
|
86
|
+
#### New Loss and Uncertainty Modeling
|
|
87
|
+
WindKit now includes tools to support loss and uncertainty analysis, designed to integrate seamlessly with PyWAsP. You can now create, validate, and summarize standardized loss and uncertainty tables using:
|
|
88
|
+
* `get_loss_table`, `total_loss`, `loss_summary`
|
|
89
|
+
* `get_uncertainty_table`, `total_uncertainty`, `uncertainty_summary`
|
|
90
|
+
|
|
91
|
+
This provides a structured framework for AEP calculations.
|
|
92
|
+
|
|
93
|
+
### Breaking Changes and Migration Guide
|
|
94
|
+
Due to the extensive API changes, you will need to update your existing code. Here is a guide to the most common changes:
|
|
95
|
+
|
|
96
|
+
#### General Renaming and Removals
|
|
97
|
+
* **Namespace Cleanup**: Many functions like `bwc_validate_wrapper`, `update_history`, and various `spatial` utilities have been removed from the public `windkit` namespace.
|
|
98
|
+
* **Function Renaming**:
|
|
99
|
+
* `windkit.read_timeseries_from_pandas` is now `windkit.tswc_from_dataframe`.
|
|
100
|
+
* `windkit.bwc_from_timeseries` is now `windkit.bwc_from_tswc`.
|
|
101
|
+
* `windkit.read_timeseries_from_csv` is now `windkit.read_tswc`.
|
|
102
|
+
* All `windkit.empty_*` functions are now `windkit.create_*` (e.g., `empty_bwc` -> `create_bwc`).
|
|
103
|
+
* `lines2poly` is now `lines_to_poly`, and `poly2lines` is now `poly_to_lines`.
|
|
104
|
+
* `add_crs` is now `set_crs`.
|
|
105
|
+
* **Argument Renaming**: Several function arguments have been renamed for consistency. For example, `nsec` is now `n_sectors` and `nwsbins` is now `n_wsbins`. Check the function signatures if you encounter errors.
|
|
106
|
+
* **Removed Classes**: The `windkit.WindTurbines` class has been removed. Use the functions in the `windkit.wind_turbines` module to work with `xarray.Dataset` representations of turbines.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
#### Wind Climate I/O
|
|
110
|
+
Wind climate reading and writing has been standardized to one reader and one writer for each type:
|
|
111
|
+
* Use `read_tswc` to read time series wind climate datasets.
|
|
112
|
+
* Use `read_bwc` and `bwc_to_file` for Binned Wind Climate datasets.
|
|
113
|
+
* Use `read_wwc` and `wwc_to_file` for Weibull Wind Climate datasets.
|
|
114
|
+
* Use `read_gwc` and `gwc_to_file` for Generalized Wind Climate datasets.
|
|
115
|
+
|
|
116
|
+
#### Configuration File and Environment Variables
|
|
117
|
+
The configuration file format has been switched from INI to TOML. Existing `.ini` configuration files will be migrated automatically.
|
|
118
|
+
|
|
119
|
+
Additionally, user details can now be configured using case-insensitive environment variables, which is ideal for containerized or CI/CD environments:
|
|
120
|
+
* `WINDKIT_NAME`
|
|
121
|
+
* `WINDKIT_EMAIL`
|
|
122
|
+
* `WINDKIT_INSTITUTION`
|
|
123
|
+
|
|
124
|
+
If no configuration is found (either via file or environment variables), you will be prompted for the information upon initialization.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
### Full list of changes
|
|
129
|
+
|
|
130
|
+
#### Bug Fixes
|
|
131
|
+
* `clip_with_margin` now returns a `ValueError` when the bbox crosses the dateline.
|
|
132
|
+
* Fixed an issue where `read_raster_map` was checking for too high roughness values twice.
|
|
133
|
+
* Duplicate heights passed to `create_dataset` with `struct=stacked_point` are now correctly removed.
|
|
134
|
+
* `gdf_to_ds` now correctly handles unique points to ensure round-tripping with `ds_to_gdf` works as expected.
|
|
135
|
+
* Fixed a bug where `get_vector_map` for CORINE data could fail with a missing column error.
|
|
136
|
+
* `to_stacked_point` now correctly retains the input order of coordinates.
|
|
137
|
+
* `nearest_points` now raises an error on duplicate points to be compatible with newer xarray versions.
|
|
138
|
+
* Fixed a bug in `wk.spatial.add_crs` where custom projection information could be overwritten by a detected EPSG code.
|
|
139
|
+
* Fixed `wk.spatial.interp_unstructured_like` to correctly broadcast single-point sources.
|
|
140
|
+
* Fixed a bug in `nearest_points` when `keep_duplicates=False` and `return_distance=True`.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
#### API Breaks
|
|
144
|
+
* `windkit.read_timeseries_from_pandas` renamed as `windkit.tswc_from_dataframe`
|
|
145
|
+
* `windkit.bwc_from_timeseries` renamed as `windkit.bwc_from_tswc`
|
|
146
|
+
* `windkit.read_timeseries_from_csv` renamed as `windkit.read_tswc`
|
|
147
|
+
* all `windkit.empty_*` functions renamed to `windkit.create_*`
|
|
148
|
+
* renamed `lines2poly` to `lines_to_poly` and `poly2lines` to `poly_to_lines`
|
|
149
|
+
* removed `wk.lib_string` from public namespace
|
|
150
|
+
* `windkit.spatial.interp_unstructured_like` and `windkit.spatial.interp_structured_like` will not interpolate over 1D variables by default
|
|
151
|
+
* Removed `read_raster_map`,`raster_map_to_file`, and `read_vector_map` and `vector_map_to_file` (made private)
|
|
152
|
+
* Added `read_elevation_map` and `elevation_map_to_file` (works for vector and raster)
|
|
153
|
+
* Added `read_roughness_map` and `roughness_map_to_file` (works for vector and raster)
|
|
154
|
+
* Added `read_landcover_map`, and `landcover_map_to_file` (works for vector and raster)
|
|
155
|
+
* Old .gpkg files with change lines in landcover_map_to_file
|
|
156
|
+
and roughness_map_to_file can be written by using polygons=False
|
|
157
|
+
* File formats for the readers and writers described above are now inferred from the filename's suffix, e.g., `.map` for map files.
|
|
158
|
+
* The functions `read_landcover_map` and `read_roughness_map` by default return polygons instead of changes lines. You can set the argument `polygons=False` to open the file as old fashioned change lines. You can try this if your file is not being opened correctly, but you are responsible for checking it's correctness, because there is less error checking for these types of files.
|
|
159
|
+
* Added `roughness_to_landcover` to convert roughness maps to landcover maps (vector or raster)
|
|
160
|
+
* Added `landcover_to_roughness` to convert landcover maps to roughness maps (vector or raster)
|
|
161
|
+
* Renamed `add_crs` to `set_crs` and and deprecated `add_crs` (which calls `set_crs`)
|
|
162
|
+
* `get_vector_map` now returns landcover data as polygons by default.
|
|
163
|
+
* Removed `wk.bwc_ws_moment` from public namespace, use `wk.mean_ws_moment` instead
|
|
164
|
+
* Removed `wk.bwc_mean_windspeed` from public namespace, use `wk.mean_wind_speed` instead
|
|
165
|
+
* Removed `wk.bwc_mean_windspeed3` from public namespace, use `wk.mean_wind_speed3` instead
|
|
166
|
+
* Removed `wk.bwc_power_density` from public namespace, use `wk.mean_power_density` instead
|
|
167
|
+
* Removed `wk.bwc_ws_cdf` from public namespace, use `wk.ws_cdf` instead
|
|
168
|
+
* Removed `wk.bwc_ws_freq_gt_mean` from public namespace, use `wk.ws_freq_gt_mean` instead
|
|
169
|
+
* Removed `wk.bwc_to_tabfile` from public namespace, use `wk.bwc_to_file` instead
|
|
170
|
+
* Removed `wk.bwc_validate_wrapper` from public namespace
|
|
171
|
+
* Removed `wk.create_time_attributes` from public namespace
|
|
172
|
+
* Removed `wk.count_to_ws_freq_by_sector` from public namespace
|
|
173
|
+
* Removed `wk.read_rsffile` from public namespace use `wk.read_wwc` instead
|
|
174
|
+
* Removed `wk.read_wrgfile` from public namespace, use `wk.read_wwc` instead
|
|
175
|
+
* Removed `wk.read_grdfile` from public namespace, use `wk.read_wwc` instead
|
|
176
|
+
* Removed `wk.read_pwcfile` from public namespace, use `wk.read_wwc` instead
|
|
177
|
+
* Removed `wk.to_rsffile` from public namespace, use `wk.wwc_to_file` instead
|
|
178
|
+
* Removed `wk.to_wrgfile` from public namespace, use `wk.wwc_to_file` instead
|
|
179
|
+
* Removed `wk.wwc_validate_wrapper` from public namespace
|
|
180
|
+
* Removed `wk.wwc_validate_structure`
|
|
181
|
+
* Removed `wk.wwc_validate_structure_wrapper`
|
|
182
|
+
* Removed `wk.wwc_mean_windspeed` from public namespace, use `wk.mean_wind_speed` instead
|
|
183
|
+
* Removed `wk.wwc_power_density` from public namespace, use `wk.mean_power_density` instead
|
|
184
|
+
* Removed `wk.to_libfile` from public namespace, use `wk.gwc_to_file` instead
|
|
185
|
+
* Removed `wk.gwc_validate_wrapper` from public namespace
|
|
186
|
+
* Removed `wk.wtg_validate_wrapper` from public namespace
|
|
187
|
+
* Removed `wk.windturbines_validate_wrapper` from public namespace
|
|
188
|
+
* Modified `wk.read_tswc` to be a generic tswc reader (not just for CSV)
|
|
189
|
+
* Renamed `wk.is_windturbines` to `wk.is_wind_turbines`
|
|
190
|
+
* Renamed `wk.windturbines_validate` to `wk.validate_windturbines`
|
|
191
|
+
* Renamed `wk.wtg_validate` to `wk.validate_wtg`
|
|
192
|
+
* Renamed `wk.gwc_validate` to `wk.validate_gwc`
|
|
193
|
+
* Renamed `wk.wwc_validate` to `validate_wwc`
|
|
194
|
+
* Renamed `wk.bwc_validate` to `wk.validate_bwc`
|
|
195
|
+
* Removed `wk.get_map`
|
|
196
|
+
* Removed `wk.spatial.interpolate_to_grid`
|
|
197
|
+
* Removed `wk.WindTurbines`
|
|
198
|
+
* Removed `wk.Workspace.get_vector_map`
|
|
199
|
+
* Added `wk.Workspace.get_elevation_map`
|
|
200
|
+
* Added `wk.Workspace.get_roughness_map`
|
|
201
|
+
* Removed `wk.bwc_from_counts` (from public namespace)
|
|
202
|
+
* Removed `wk.create_wv_count` (from public namespace)
|
|
203
|
+
* Removed `wk.update_history` (from public namespace)
|
|
204
|
+
* Removed `wk.update_coords_attrs` (from public namespace)
|
|
205
|
+
* Removed `wk.update_var_attrs` (from public namespace)
|
|
206
|
+
* Removed `wk.ALL_VARS_META` (from public namespace)
|
|
207
|
+
* Removed `wk.spatial.spatial_stack` (from public namespace)
|
|
208
|
+
* Removed `wk.spatial.spatial_unstack` (from public namespace)
|
|
209
|
+
* Removed `wk.spatial.stack_then_unstack` (from public namespace)
|
|
210
|
+
* Removed `wk.spatial.get_latitude` (from public namespace)
|
|
211
|
+
* Removed `wk.spatial.is_vertical` (from public namespace)
|
|
212
|
+
* Removed `wk.import_optional_dependency` (from public namespace)
|
|
213
|
+
* Removed `wk.reproject_gwc` (from public namespace)
|
|
214
|
+
* Removed `wk.get_ee_map` (from public namespace)
|
|
215
|
+
* Removed `wk.create_coords`
|
|
216
|
+
* Renamed `wk.create_ws_bin_coords` to `wk.create_wsbin_coords` and changed the arguments to `bins`, `width` and `start`. `bins` is an integer number of bins or a list of bin edges. `width` is the width of the bins, and `start` is the starting point of the first bin. `width` and `start` are only used if `bins` is an integer.
|
|
217
|
+
* Removed `wk.create_ws_bin_coords_from_values`
|
|
218
|
+
* Changed `wk.create_sector_coords` arguments to `bins` and `start`. `bins` is an integer number of bins or a list of bin edges. `start` is the starting point of the first bin. By default, `start` is -360/n_bins/2.
|
|
219
|
+
* Removed `wk.create_sector_coords_from_edges`
|
|
220
|
+
* Removed `wk.create_direction_coords` (from public namespace)
|
|
221
|
+
* `wk.create_vector_map` now has bbox as a first positional argument and argument `dist` is removed
|
|
222
|
+
* Renamed `wk.get_land_cover_table` to `wk.get_landcover_table`
|
|
223
|
+
|
|
224
|
+
#### New features
|
|
225
|
+
* `wk.read_wtg` can read wind turbine generators files in netCDF format.
|
|
226
|
+
* `wk.mean_ws_moment` to calculate the mean of a wind speed moment of any wind climate
|
|
227
|
+
* `wk.ws_cdf` to calculate the cumulative distribution function of a wind speed of any wind climate
|
|
228
|
+
* `wk.ws_freq_gt_mean` to calculate the fraction of probability mass above the mean of a wind speed of any wind climate
|
|
229
|
+
* `wk.mean_wind_speed` to calculate the mean wind speed of any wind climate
|
|
230
|
+
* `wk.mean_wind_speed3` to calculate the mean wind speed cubed of any wind climate
|
|
231
|
+
* `wk.mean_power_density` to calculate the mean power density of any wind climate
|
|
232
|
+
* All of the above calculates the the variable of all wind climates (not all functions are supports all climates yet).
|
|
233
|
+
* Added `read_wwc` to read a weibull wind climate dataset from a file
|
|
234
|
+
* Added `read_mfwwc` to read a multi-filey weibull wind climate dataset
|
|
235
|
+
* Added `wwc_to_file` to write a weibull wind climate dataset to a file
|
|
236
|
+
* Added `read_gwc` to read a generalized wind climate dataset from a file
|
|
237
|
+
* Added `gwc_to_file` to write a generalized wind climate dataset to a file
|
|
238
|
+
* Added `bwc_to_file` to write a binned wind climate dataset to a file
|
|
239
|
+
* New function `add_projected_wrf_coordinates` that is useful to process `WRF` output data to be used with `windkit`.
|
|
240
|
+
* Existing function `windkit.read_wtg` can optionally read the wind turbine manufacturer name from a `.wtg` file if exists.
|
|
241
|
+
* New function `windkit.metadata.update_coords_attrs` to update attributes of a dataset coordinates.
|
|
242
|
+
* New function `create_baro` to create a baro file with dummy values
|
|
243
|
+
* New function `create_stab` to create a stab file with dummy values
|
|
244
|
+
* New function `windkit.snap_to_layer` that transform a vector map with polyons with gaps, open areas and inconsistent vertices into a valid vector map to run `pywasp`.
|
|
245
|
+
* New example notebook tutorial for `windkit.get_era5` to get ERA5 data from Microsoft Planetary Computer
|
|
246
|
+
* `windkit.raster_map_to_file` allows extra arguments that are forwarded to `rasterio.open`. Useful to create e.g. compressed geoTIFF files with the "compress" option.
|
|
247
|
+
* New class `windkit.WengWorkspace` to read WAsP Engineering proyect files in XML format and converting its content to `xarray.Dataset` and `xarray.DataArray` instances such as extreme wind climate datasets, location datasets and raster map data arrays.
|
|
248
|
+
* Enable roughness maps to be written to `.map` files
|
|
249
|
+
* Added kw-argument `return_lctable` to functions reading landcover maps, set to false by default, which means only the landcover map is returned
|
|
250
|
+
* Added kw-argument `convert_to_landcover` to functions reading roughness maps to optionally convert the map to landcover, returning both the map and the table (two outputs)
|
|
251
|
+
* It is now allowed to use `wk.landcover_map_to_file` to write a landcover map to a `.map` file as long as the displacements are all zero.
|
|
252
|
+
* `lines_to_polygons` is a more robust version of `lines_to_poly` and now tries to make the polygons valid if they are not. It is also much faster.
|
|
253
|
+
* `polygons_to_lines` is a more robust version of `poly_to_lines` and is much faster.
|
|
254
|
+
* Added function `windkit.plot.landcover_map`, which can plot a landcover map with some nicer color schemes.
|
|
255
|
+
* Added function `windkit.plot.elevation_map`, which can plot an elevation map with some nicer color schemes.
|
|
256
|
+
* Added function `windkit.add_landcover_table` to add roughness and other information from a wk.LandCoverTable to a geopandas dataframe with only an `id` column
|
|
257
|
+
* in `get_raster_map`, an option to download from GWA4 landcover `GWA4_landcover` and `GWA4_elevation` has been added
|
|
258
|
+
* Added function `windkit.get_tutorial_data` to download tutorial data from the windkit zenodo repository. The data is downloaded to a local cache directory, which is created if it does not exist.
|
|
259
|
+
* Configuration can now be defined using case-insensitive environment variables `windkit_name`, `windkit_email` and `windkit_institution`. If these are not set, and the configuration file is not found, you will be prompted on initialization.
|
|
260
|
+
|
|
261
|
+
#### Added functionality to work with losses
|
|
262
|
+
- Defined the data structure of loss tables: a pandas DataFrame with required columns: "loss_name" and "loss_percentage". It can also have optional columns "loss_category", "loss_upper_bound", "loss_lower_bound", "loss_default", and "description"
|
|
263
|
+
- Added function `get_loss_table` to obtain a loss tables (DataFrame) from known name. Currently, only "dtu_default" exists.
|
|
264
|
+
- Added function `validate_loss_table` to validate a loss table is formatted correctly
|
|
265
|
+
- Added function `total_loss` to calculate the total loss in percentage from a loss table
|
|
266
|
+
- Added function `loss_summary` to print a formatted summary of the loss table
|
|
267
|
+
- Added function `total_loss_factor` to calculate the loss factor (the factor that can be directly multiplied to subtract the loss from an AEP number)
|
|
268
|
+
|
|
269
|
+
#### Added functionality to work with uncertainties
|
|
270
|
+
- Defined the data structure of uncertainty tables: a pandas DataFrame with required columns: "uncertainty_kind", "uncertainty_name" and "uncertainty_percentage". It can also have optional columns "uncertainty_category", "uncertainty_upper_bound", "uncertainty_lower_bound", "uncertainty_default", and "description"
|
|
271
|
+
- Added function `get_uncertainty_table` to obtain an uncertainty table (DataFrame) from known name. Currently, only "dtu_default" exists.
|
|
272
|
+
- Added function `validate_uncertainty_table` to validate an uncertainty table is formatted correctly
|
|
273
|
+
- Added function `total_uncertainty` to calculate the total uncertainty in percentage from an uncertainty table. Following the GUM guideline on adding uncertainties.
|
|
274
|
+
- Added function `uncertainty_summary` to print a formatted summary of the uncertainty table
|
|
275
|
+
- Added function `total_uncertainty_factor` to calculate the uncertainty factor (the factor that can be directly multiplied to add the uncertainty to an AEP estiamtion)
|
|
276
|
+
|
|
277
|
+
#### Bug fixes
|
|
278
|
+
* `clip_with_margin` now returns a `ValueError` when the bbox crosses the dateline
|
|
279
|
+
* `read_raster_map` was checking for too high roughness twice, one is enough
|
|
280
|
+
* If duplicate heights are passed to `create_dataset` with `struct=stacked_point` it will remove the duplicates
|
|
281
|
+
as required in the `stacked_point` structure.
|
|
282
|
+
* in `gdf_to_ds` we must create the target dataset using unique (`west_east`,`south_north`) points for `stacked_point` and unique `west_east` and `south_north` for cuboids to be able to round trip `ds_to_gdf` and `gdf_to_ds`.
|
|
283
|
+
* get_vector_map(bb, "CORINE", source="dtu") could sometimes fail with "Exception: Missing required column: id", this has been fixed
|
|
284
|
+
* read_vector_map also reads .gpkg files where a geometry column is missing on the landcovertable layer
|
|
285
|
+
* to_stacked_point now retains input order of west_east/south_north coordinates
|
|
286
|
+
* Nearest_points will now raise an error if there are duplicate points resulting from the nearest neighbour lookup if the data is in stacked_point or cuboid format. This is required because xarray >2024.02 does not allow duplicates in MultiIndexes anymore. A new keyword argument keep_duplicates controls the behaviour, the default being True, i.e. when possible behaviour is the same as before.
|
|
287
|
+
* In the function `wk.spatial.add_crs` assigning a projection with more detailed info given in a proj4 string or custom CRS was overwritten when an EPSG was found for the projection. This is now fixed and the full custom CRS is added.
|
|
288
|
+
* Fixed `wk.spatial.interp_unstructured_like` to broadcast single-point sources correctly to target.
|
|
289
|
+
* Fix bug in `nearest_points` when `keep_duplicates=False` and `return_distance=True`: it threw an error that the arrays were not the same size.
|
|
290
|
+
|
|
291
|
+
#### Breaking changes
|
|
292
|
+
* `empty_wasp_site_factors` renamed to `empty_wasp_site_effects`
|
|
293
|
+
* in `empty_z0meso` argument `site_factors` was renamed renamed to `site_effects`
|
|
294
|
+
* in `empty_pwc` argument `site_factors` was renamed renamed to `site_effects`
|
|
295
|
+
- Argument `nwsbins`, `nws`, `n_bins` has been renamed to `n_wsbins` or `wsbins` (in case of array-like argument) in the following functions:
|
|
296
|
+
- `bwc_from_tswc`
|
|
297
|
+
- `create_ws_bin_coords`
|
|
298
|
+
- `create_ws_bin_coords_from_values`
|
|
299
|
+
- Argument `nsec`, `nsecs`, `nwd`, `nbins` has been renamed to `n_sectors` in the following functions:
|
|
300
|
+
- `create_sector_coords`
|
|
301
|
+
- `create_direction_coords`
|
|
302
|
+
- `create_sector_coords_from_edges`
|
|
303
|
+
- `wd_to_sector`
|
|
304
|
+
- `bwc_from_tswc`
|
|
305
|
+
- Argument `num_args` has been renamed to `n_args` in the following functions:
|
|
306
|
+
- `ltc_validate`
|
|
307
|
+
- Argument `ws_bin_width`has been renamed to `wsbin_width` in the following functions:
|
|
308
|
+
- `create_ws_bin_coords`
|
|
309
|
+
- `bwc_from_tswc`
|
|
310
|
+
* Metadata variable name `aep_deficit` has been renamed to `potential_aep_deficit`
|
|
311
|
+
|
|
312
|
+
* Functions `read_raster_map` and `raster_map_to_file` were removed in favour of the new functions `read_elevation_raster`, `write_elevation_raster`, `read_landcover_raster`, and `write_landcover_raster`.
|
|
313
|
+
* Functions `read_vector_map` and `vector_map_to_file` were removed in favour of the new functions `read_elevation_vector`, `write_elevation_vector`, `read_landcover_vector`, and `write_landcover_vector`.
|
|
314
|
+
* Changed most instances where "header" is used to "description"
|
|
315
|
+
* `get_vector_map` returns polygons instead of lines.
|
|
316
|
+
* Switched configuration file from ini to toml, old file will be automatically migrated
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
## [0.8.0 (2024-06-10)](https://docs.wasp.dk/windkit/v0.8.0/)
|
|
8
320
|
|
|
9
321
|
### New features
|
|
10
322
|
|
|
@@ -107,8 +419,8 @@ Additionally a few high level routines were added for interpolation specific fie
|
|
|
107
419
|
#### Improvements to the map_conversion routines
|
|
108
420
|
|
|
109
421
|
* Added `windkit.map_conversion.find_duplicate_lines` to identify duplicated lines in a GeoDataFrame, even if the line is reversed.
|
|
110
|
-
* `windkit.
|
|
111
|
-
* `windkit.
|
|
422
|
+
* `windkit.poly_to_lines` now correctly sets the CRS to match the input data. Before it was `None`.
|
|
423
|
+
* `windkit.poly_to_lines` and `windkit.lines2poly` now have an absolute tolerance argument, `atol`, which can be used to identify nearby points, which should be joined.
|
|
112
424
|
|
|
113
425
|
#### Access to data from Microsoft's Planetary Computer platform
|
|
114
426
|
|
|
@@ -168,7 +480,7 @@ Additionally a few high level routines were added for interpolation specific fie
|
|
|
168
480
|
* `windkit.spatial.interpolate_to_grid` deprecated in favor of `windkit.spatial.interp_structured_like` and `windkit.spatial.interp_unstructured_like`
|
|
169
481
|
|
|
170
482
|
### Breaking changes
|
|
171
|
-
* `windkit.
|
|
483
|
+
* `windkit.poly_to_lines` no longer has a `closed_map` argument. Instead, when `background_lc_id` is set, all empty space is filled with the provided id. This allows for maps that are no longer rectangular, e.g. due to reprojection, to be properly converted to line maps.
|
|
172
484
|
* Removed accessors, which were previously registered for several wind climate data structures. You will need to use the functions instead.
|
|
173
485
|
|
|
174
486
|
## 0.6.3 (2023-07-18)
|
|
@@ -291,7 +603,7 @@ Several new features in this release including support for Wind Turbines, the ab
|
|
|
291
603
|
the following:
|
|
292
604
|
- `LineMap` and `PolyMap` classes: These classes enable conversion and nice plotting
|
|
293
605
|
of their types.
|
|
294
|
-
- `lines2poly` and `
|
|
606
|
+
- `lines2poly` and `poly_to_lines` helper functions that perform the conversion from
|
|
295
607
|
lines to polygons and from polygons to line respectively, using the library
|
|
296
608
|
`geopandas.GeoDataFrame`.
|
|
297
609
|
* New module `windkit.plot.landcover_map` to plot the `geopandas.GeoDataFrame` polygons
|
|
@@ -303,7 +615,7 @@ Several new features in this release including support for Wind Turbines, the ab
|
|
|
303
615
|
#### New spatial functionality
|
|
304
616
|
|
|
305
617
|
* New function `windkit.spatial.interpolate_to_grid` to resample data to a target grid.
|
|
306
|
-
* New function `windkit.spatial.
|
|
618
|
+
* New function `windkit.spatial._get_latitude` that returns data-array of latitude values in wgs84.
|
|
307
619
|
* New function `windkit.spatial.count_spatial_points` counts how many points are in a windkit dataset or dataarray.
|
|
308
620
|
|
|
309
621
|
#### Enhancements to `windkit.get_map.get_ee_map`
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Performance Regression CI Job - Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This plan outlines the implementation of a CI job to catch performance regressions in windkit. The job will test import timing, function execution benchmarks, and memory usage.
|
|
6
|
+
|
|
7
|
+
## Goals
|
|
8
|
+
|
|
9
|
+
1. **Catch regressions** in import time (target: < 1 second)
|
|
10
|
+
2. **Benchmark critical functions** and track performance over time
|
|
11
|
+
3. **Fail CI** when performance degrades beyond acceptable thresholds
|
|
12
|
+
4. **Provide actionable reports** showing what regressed and by how much
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Implementation Components
|
|
17
|
+
|
|
18
|
+
### 1. Performance Test Module
|
|
19
|
+
|
|
20
|
+
**File: `test/test_performance.py`**
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
"""Performance regression tests for windkit."""
|
|
24
|
+
|
|
25
|
+
import sys
|
|
26
|
+
import time
|
|
27
|
+
import pytest
|
|
28
|
+
|
|
29
|
+
# Thresholds (in seconds)
|
|
30
|
+
IMPORT_TIME_THRESHOLD = 1.0 # Maximum acceptable import time
|
|
31
|
+
IMPORT_TIME_WARNING = 0.8 # Warning threshold
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TestImportPerformance:
|
|
35
|
+
"""Tests for import time performance."""
|
|
36
|
+
|
|
37
|
+
def test_import_time_under_threshold(self):
|
|
38
|
+
"""Verify windkit imports in under 1 second."""
|
|
39
|
+
# Ensure windkit is not already imported
|
|
40
|
+
modules_to_remove = [k for k in sys.modules if k.startswith('windkit')]
|
|
41
|
+
for mod in modules_to_remove:
|
|
42
|
+
del sys.modules[mod]
|
|
43
|
+
|
|
44
|
+
start = time.perf_counter()
|
|
45
|
+
import windkit # noqa: F401
|
|
46
|
+
elapsed = time.perf_counter() - start
|
|
47
|
+
|
|
48
|
+
assert elapsed < IMPORT_TIME_THRESHOLD, (
|
|
49
|
+
f"Import time {elapsed:.3f}s exceeds threshold {IMPORT_TIME_THRESHOLD}s"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def test_lazy_imports_not_loaded(self):
|
|
53
|
+
"""Verify heavy optional dependencies are not loaded on import."""
|
|
54
|
+
modules_to_remove = [k for k in sys.modules if k.startswith('windkit')]
|
|
55
|
+
for mod in modules_to_remove:
|
|
56
|
+
del sys.modules[mod]
|
|
57
|
+
|
|
58
|
+
import windkit # noqa: F401
|
|
59
|
+
|
|
60
|
+
# These should NOT be loaded on import
|
|
61
|
+
lazy_modules = [
|
|
62
|
+
'matplotlib',
|
|
63
|
+
'numba',
|
|
64
|
+
'scipy.stats',
|
|
65
|
+
'pystac_client',
|
|
66
|
+
'pydantic_settings',
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
for mod in lazy_modules:
|
|
70
|
+
assert mod not in sys.modules, (
|
|
71
|
+
f"Module '{mod}' should not be loaded on 'import windkit'"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@pytest.mark.slow
|
|
76
|
+
class TestFunctionBenchmarks:
|
|
77
|
+
"""Benchmark tests for critical functions."""
|
|
78
|
+
|
|
79
|
+
def test_create_bwc_performance(self, benchmark_output_locs):
|
|
80
|
+
"""Benchmark create_bwc function."""
|
|
81
|
+
import windkit as wk
|
|
82
|
+
|
|
83
|
+
start = time.perf_counter()
|
|
84
|
+
wk.create_bwc(benchmark_output_locs, n_sectors=12, n_wsbins=30)
|
|
85
|
+
elapsed = time.perf_counter() - start
|
|
86
|
+
|
|
87
|
+
# Log timing for tracking
|
|
88
|
+
print(f"create_bwc: {elapsed:.4f}s")
|
|
89
|
+
|
|
90
|
+
# Threshold TBD based on baseline measurements
|
|
91
|
+
assert elapsed < 5.0, f"create_bwc took {elapsed:.3f}s, exceeds threshold"
|
|
92
|
+
|
|
93
|
+
def test_spatial_operations_performance(self, benchmark_dataset):
|
|
94
|
+
"""Benchmark spatial operations."""
|
|
95
|
+
import windkit as wk
|
|
96
|
+
|
|
97
|
+
start = time.perf_counter()
|
|
98
|
+
wk.spatial.to_point(benchmark_dataset)
|
|
99
|
+
elapsed = time.perf_counter() - start
|
|
100
|
+
|
|
101
|
+
print(f"to_point: {elapsed:.4f}s")
|
|
102
|
+
assert elapsed < 1.0, f"to_point took {elapsed:.3f}s, exceeds threshold"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 2. Benchmark Fixtures
|
|
106
|
+
|
|
107
|
+
**File: `test/fixtures/fixture_benchmarks.py`**
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
"""Fixtures for performance benchmarks."""
|
|
111
|
+
|
|
112
|
+
import pytest
|
|
113
|
+
import numpy as np
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.fixture(scope="module")
|
|
117
|
+
def benchmark_output_locs():
|
|
118
|
+
"""Create a standard dataset for benchmarking."""
|
|
119
|
+
import windkit as wk
|
|
120
|
+
|
|
121
|
+
return wk.spatial.create_point(
|
|
122
|
+
x=[0, 1, 2],
|
|
123
|
+
y=[0, 1, 2],
|
|
124
|
+
z=[10, 20, 30],
|
|
125
|
+
crs=4326
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@pytest.fixture(scope="module")
|
|
130
|
+
def benchmark_dataset():
|
|
131
|
+
"""Create a larger dataset for spatial benchmarks."""
|
|
132
|
+
import windkit as wk
|
|
133
|
+
|
|
134
|
+
# Create a 100x100 grid for meaningful benchmarks
|
|
135
|
+
x = np.linspace(0, 100, 50)
|
|
136
|
+
y = np.linspace(0, 100, 50)
|
|
137
|
+
|
|
138
|
+
return wk.spatial.create_raster(
|
|
139
|
+
x=x,
|
|
140
|
+
y=y,
|
|
141
|
+
crs=32632
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3. pytest-benchmark Integration (Optional Enhancement)
|
|
146
|
+
|
|
147
|
+
**Add to `pixi.toml` feature.dev.dependencies:**
|
|
148
|
+
|
|
149
|
+
```toml
|
|
150
|
+
[feature.dev.dependencies]
|
|
151
|
+
pytest-benchmark = "*"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Enhanced benchmark test using pytest-benchmark:**
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
def test_import_time_benchmark(benchmark):
|
|
158
|
+
"""Benchmark import time with pytest-benchmark."""
|
|
159
|
+
def import_windkit():
|
|
160
|
+
modules = [k for k in sys.modules if k.startswith('windkit')]
|
|
161
|
+
for mod in modules:
|
|
162
|
+
del sys.modules[mod]
|
|
163
|
+
import windkit
|
|
164
|
+
return windkit
|
|
165
|
+
|
|
166
|
+
result = benchmark(import_windkit)
|
|
167
|
+
assert result is not None
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 4. CI Job Configuration
|
|
171
|
+
|
|
172
|
+
**Add to `.gitlab-ci.yml`:**
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
# Performance regression tests
|
|
176
|
+
performance_tests:
|
|
177
|
+
extends:
|
|
178
|
+
- .pixi_pytest
|
|
179
|
+
stage: test
|
|
180
|
+
before_script:
|
|
181
|
+
- mkdir -p ~/.config/windkit/
|
|
182
|
+
- cp .conda_env/windkit.ini ~/.config/windkit/windkit.ini
|
|
183
|
+
variables:
|
|
184
|
+
PIXI_ENV: "default"
|
|
185
|
+
script:
|
|
186
|
+
- pixi run -e $PIXI_ENV test-performance
|
|
187
|
+
artifacts:
|
|
188
|
+
when: always
|
|
189
|
+
paths:
|
|
190
|
+
- performance_results/
|
|
191
|
+
reports:
|
|
192
|
+
junit: performance_junit.xml
|
|
193
|
+
expire_in: 1 month
|
|
194
|
+
rules:
|
|
195
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
196
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
197
|
+
allow_failure: false # Fail the pipeline on performance regression
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 5. Pixi Task Configuration
|
|
201
|
+
|
|
202
|
+
**Add to `pixi.toml`:**
|
|
203
|
+
|
|
204
|
+
```toml
|
|
205
|
+
[feature.dev.tasks]
|
|
206
|
+
# ... existing tasks ...
|
|
207
|
+
|
|
208
|
+
# Performance tests
|
|
209
|
+
test-performance = {cmd="cd test; pytest test_performance.py -v --tb=short --junitxml=../performance_junit.xml", depends-on = ["install_tests"]}
|
|
210
|
+
|
|
211
|
+
# Performance tests with benchmarks (if pytest-benchmark is added)
|
|
212
|
+
test-benchmark = {cmd="cd test; pytest test_performance.py -v --benchmark-only --benchmark-json=../performance_results/benchmark.json", depends-on = ["install_tests"]}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Implementation Phases
|
|
218
|
+
|
|
219
|
+
### Phase 1: Basic Import Time Testing (Minimal)
|
|
220
|
+
- [ ] Create `test/test_performance.py` with import time tests
|
|
221
|
+
- [ ] Add `performance` marker to `pyproject.toml`
|
|
222
|
+
- [ ] Add `test-performance` task to `pixi.toml`
|
|
223
|
+
- [ ] Verify tests pass locally
|
|
224
|
+
|
|
225
|
+
### Phase 2: CI Integration
|
|
226
|
+
- [ ] Add `performance_tests` job to `.gitlab-ci.yml`
|
|
227
|
+
- [ ] Configure artifacts for performance results
|
|
228
|
+
- [ ] Test in MR pipeline
|
|
229
|
+
|
|
230
|
+
### Phase 3: Function Benchmarks (Optional)
|
|
231
|
+
- [ ] Add `pytest-benchmark` dependency
|
|
232
|
+
- [ ] Create benchmark fixtures
|
|
233
|
+
- [ ] Add benchmark tests for critical functions
|
|
234
|
+
- [ ] Store benchmark history for trend analysis
|
|
235
|
+
|
|
236
|
+
### Phase 4: Advanced Monitoring (Future)
|
|
237
|
+
- [ ] Memory profiling with `pytest-memray` or `memory_profiler`
|
|
238
|
+
- [ ] Benchmark comparison against main branch
|
|
239
|
+
- [ ] Performance dashboard/visualization
|
|
240
|
+
- [ ] Slack/email alerts on regression
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Thresholds and Metrics
|
|
245
|
+
|
|
246
|
+
| Metric | Threshold | Warning | Action on Failure |
|
|
247
|
+
|--------|-----------|---------|-------------------|
|
|
248
|
+
| Import time | < 1.0s | < 0.8s | Block merge |
|
|
249
|
+
| Lazy modules loaded | 0 | - | Block merge |
|
|
250
|
+
| create_bwc | < 5.0s | < 3.0s | Warning |
|
|
251
|
+
| Spatial operations | < 1.0s | < 0.5s | Warning |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## File Changes Summary
|
|
256
|
+
|
|
257
|
+
| File | Change |
|
|
258
|
+
|------|--------|
|
|
259
|
+
| `test/test_performance.py` | New file - performance tests |
|
|
260
|
+
| `test/fixtures/fixture_benchmarks.py` | New file - benchmark fixtures |
|
|
261
|
+
| `test/conftest.py` | Import benchmark fixtures |
|
|
262
|
+
| `pixi.toml` | Add test-performance task |
|
|
263
|
+
| `.gitlab-ci.yml` | Add performance_tests job |
|
|
264
|
+
| `pyproject.toml` | Add performance marker |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Questions to Consider
|
|
269
|
+
|
|
270
|
+
1. **Should performance tests run on every MR or only on main?**
|
|
271
|
+
- Recommendation: Run on every MR to catch regressions before merge
|
|
272
|
+
|
|
273
|
+
2. **Should we use pytest-benchmark for detailed tracking?**
|
|
274
|
+
- Recommendation: Start simple, add later if needed
|
|
275
|
+
|
|
276
|
+
3. **What functions should be benchmarked?**
|
|
277
|
+
- Import time (critical)
|
|
278
|
+
- create_bwc, create_tswc (commonly used)
|
|
279
|
+
- Spatial transformations (performance-sensitive)
|
|
280
|
+
- File I/O operations (user-facing)
|
|
281
|
+
|
|
282
|
+
4. **How to handle flaky timing tests?**
|
|
283
|
+
- Use generous thresholds (2x expected time)
|
|
284
|
+
- Run multiple iterations and take median
|
|
285
|
+
- Mark as `allow_failure` initially, tighten later
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Next Steps
|
|
290
|
+
|
|
291
|
+
1. Review and approve this plan
|
|
292
|
+
2. Implement Phase 1 (basic import time testing)
|
|
293
|
+
3. Test locally and in CI
|
|
294
|
+
4. Iterate based on results
|