windkit 1.0.2__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-1.0.2 → windkit-2.0.0}/CHANGELOG.md +41 -2
- 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-1.0.2 → windkit-2.0.0}/pyproject.toml +1 -0
- {windkit-1.0.2 → windkit-2.0.0}/setup.cfg +24 -23
- {windkit-1.0.2 → windkit-2.0.0}/windkit/__init__.py +12 -2
- {windkit-1.0.2 → windkit-2.0.0}/windkit/_version.py +16 -3
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/__init__.py +1 -1
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/_colormaps.py +17 -16
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/elevation_map.py +2 -10
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/landcover_map.py +5 -13
- windkit-2.0.0/windkit/plot/time_series.py +254 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_bbox.py +1 -5
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_interpolation.py +27 -27
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_point.py +25 -30
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/spatial.py +1 -1
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversions.py +10 -7
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/vector_map.py +6 -2
- {windkit-1.0.2 → windkit-2.0.0}/windkit/tutorial_data.py +4 -1
- {windkit-1.0.2 → windkit-2.0.0}/windkit/weibull.py +30 -15
- windkit-2.0.0/windkit/wind.py +968 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/binned_wind_climate.py +19 -5
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/generalized_wind_climate.py +0 -149
- windkit-2.0.0/windkit/wind_climate/geostrophic_wind_climate.py +178 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/time_series_wind_climate.py +22 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/wind_climate.py +48 -4
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_data.py +2 -2
- windkit-2.0.0/windkit/wind_farm/power_curve.py +124 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_farm/uncertainty.py +4 -2
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_farm/wind_turbine_generator.py +41 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/metadata.py +13 -7
- {windkit-1.0.2 → windkit-2.0.0}/windkit.egg-info/SOURCES.txt +3 -0
- windkit-1.0.2/PKG-INFO +0 -93
- windkit-1.0.2/README.md +0 -45
- windkit-1.0.2/windkit/wind.py +0 -469
- windkit-1.0.2/windkit/wind_climate/geostrophic_wind_climate.py +0 -75
- {windkit-1.0.2 → windkit-2.0.0}/LICENSE +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/_errors.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/_rvea_xml.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/_weibull_nb.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/cfd.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/config.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/all_vars.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/color_table_roughness.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/CGLS-LC100.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/CORINE.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/ESA_CCI.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/GWA4_micro.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/Globcover.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/MODIS.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/NEWA_micro_v2.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/landcover_tables/WorldCover.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/loss_tables/dtu_loss_table.csv +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/map_file_header_to_epsg.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/map_file_header_to_epsg_read.json +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/data/uncertainty_tables/dtu_uncertainty_table.csv +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/import_manager.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/integrations/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/integrations/_earth_engine.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/integrations/_gwa_map_api.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/integrations/_planetary_computer.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/ltc/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/ltc/_validation.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/ltc/mcp.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/ltc/regression.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/ltc/scores.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/_helpers.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/color.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/histogram.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/operational_curves.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/raster_plot.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/roughness_rose.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/vertical_profile.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/wind_rose.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/plot/wind_turbine.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/README.md +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_crs.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_cuboid.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_dimensions.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_latitude.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_metpy_natural_neighbors.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_raster.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_struct.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_utm.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_vector.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/_vertical.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/spatial/decorators.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/_vectormap_gml.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/_vectormap_helpers.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/get_map.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/landcover.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversion/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversion/_polygons_constraints.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversion/helper_functions.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversion/lines_to_poly.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/map_conversion/poly_to_lines.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/raster_map.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/topography/topography.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/utils.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/weng_workspace.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/predicted_wind_climate.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_climate/weibull_wind_climate.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_farm/__init__.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_farm/loss.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/wind_farm/wind_turbines.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/workspace.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/_validate.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/data_structures.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/direction.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/empty.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/sector.py +0 -0
- {windkit-1.0.2 → windkit-2.0.0}/windkit/xarray_structures/wsbin.py +0 -0
|
@@ -2,7 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
All major changes are listed here.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 2.0.0 (2025-07-21)
|
|
6
|
+
|
|
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/)
|
|
6
45
|
|
|
7
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``.
|
|
8
47
|
|
|
@@ -277,7 +316,7 @@ as required in the `stacked_point` structure.
|
|
|
277
316
|
* Switched configuration file from ini to toml, old file will be automatically migrated
|
|
278
317
|
|
|
279
318
|
|
|
280
|
-
## 0.8.0 (2024-06-10)
|
|
319
|
+
## [0.8.0 (2024-06-10)](https://docs.wasp.dk/windkit/v0.8.0/)
|
|
281
320
|
|
|
282
321
|
### New features
|
|
283
322
|
|
|
@@ -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
|
windkit-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: windkit
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: WindKit provides core functionalities for working with wind resource data
|
|
5
|
+
Home-page: https://docs.wasp.dk/windkit/latest
|
|
6
|
+
Author: DTU Wind Energy
|
|
7
|
+
License: BSD-3-Clause
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: lxml>=5.0
|
|
14
|
+
Requires-Dist: netcdf4>=1.6
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
|
+
Requires-Dist: packaging>=23
|
|
17
|
+
Requires-Dist: pandas>=2.1
|
|
18
|
+
Requires-Dist: pyproj>=3.6
|
|
19
|
+
Requires-Dist: scipy>=1.12
|
|
20
|
+
Requires-Dist: shapely>=2
|
|
21
|
+
Requires-Dist: xarray>=2023.9
|
|
22
|
+
Requires-Dist: rioxarray>=0.17
|
|
23
|
+
Requires-Dist: rasterio>=1.3
|
|
24
|
+
Requires-Dist: geopandas>=1.0
|
|
25
|
+
Requires-Dist: h5netcdf>=1.3
|
|
26
|
+
Requires-Dist: h5py>=3.9
|
|
27
|
+
Requires-Dist: matplotlib>=3.8
|
|
28
|
+
Requires-Dist: pydantic-settings>=2.3
|
|
29
|
+
Requires-Dist: pydantic>=2.7
|
|
30
|
+
Requires-Dist: platformdirs>=4.3.8
|
|
31
|
+
Requires-Dist: plotly>=5
|
|
32
|
+
Requires-Dist: kaleido>=0.2.1
|
|
33
|
+
Requires-Dist: requests>=2.31
|
|
34
|
+
Requires-Dist: toml>=0.10
|
|
35
|
+
Provides-Extra: plots
|
|
36
|
+
Requires-Dist: matplotlib>=3.6; extra == "plots"
|
|
37
|
+
Requires-Dist: plotly>=5.12; extra == "plots"
|
|
38
|
+
Requires-Dist: kaleido>=0.2.1; extra == "plots"
|
|
39
|
+
Provides-Extra: data-apis
|
|
40
|
+
Requires-Dist: ee; extra == "data-apis"
|
|
41
|
+
Requires-Dist: pystac_client; extra == "data-apis"
|
|
42
|
+
Requires-Dist: planetary_computer; extra == "data-apis"
|
|
43
|
+
Requires-Dist: fsspec; extra == "data-apis"
|
|
44
|
+
Requires-Dist: adlfs; extra == "data-apis"
|
|
45
|
+
Requires-Dist: zarr; extra == "data-apis"
|
|
46
|
+
Provides-Extra: performance
|
|
47
|
+
Requires-Dist: numba; extra == "performance"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# WindKit
|
|
51
|
+
|
|
52
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/pipelines)
|
|
53
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/pipelines)
|
|
54
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/releases)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
[](https://www.python.org/downloads/)
|
|
57
|
+
[](https://docs.wasp.dk/windkit)
|
|
58
|
+
|
|
59
|
+
[](https://pypi.org/project/windkit/)
|
|
60
|
+
[](https://pypi.org/project/windkit/)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
WindKit provides core functionalities for working with wind resource data, built on top of [xarray](https://xarray.dev/) and [geopandas](https://geopandas.org/).
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
**pip:**
|
|
68
|
+
```bash
|
|
69
|
+
pip install windkit
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**conda:**
|
|
73
|
+
```bash
|
|
74
|
+
conda install -c https://conda.windenergy.dtu.dk/channel/open windkit
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import windkit as wk
|
|
81
|
+
|
|
82
|
+
# Read a binned wind climate
|
|
83
|
+
bwc = wk.read_bwc("hovsore.tab")
|
|
84
|
+
|
|
85
|
+
# Calculate mean wind speed
|
|
86
|
+
mean_ws = wk.mean_wind_speed(bwc)
|
|
87
|
+
print(f"Mean wind speed: {mean_ws.values:.2f} m/s")
|
|
88
|
+
|
|
89
|
+
# Fit Weibull distribution and convert to Weibull wind climate
|
|
90
|
+
wwc = wk.weibull_fit(bwc)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Features
|
|
94
|
+
|
|
95
|
+
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .lib, .tab, .rsf, .wrg)
|
|
96
|
+
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats)
|
|
97
|
+
- **WAsP Integration** - Read WAsP Workspace files and WAsP Engineering project files
|
|
98
|
+
- **Spatial Processing** - Clip, reproject, and interpolate spatial data with full CRS support
|
|
99
|
+
- **Wind Statistics** - Calculate mean wind speed, power density, Weibull parameters, and more
|
|
100
|
+
- **Plotting** - Interactive plots with Plotly, static plots with Matplotlib
|
|
101
|
+
- **Remote Data Access** - Download ERA5 reanalysis data and satellite-derived maps
|
|
102
|
+
|
|
103
|
+
## Documentation
|
|
104
|
+
|
|
105
|
+
Full documentation: [docs.wasp.dk/windkit](https://docs.wasp.dk/windkit)
|
|
106
|
+
|
|
107
|
+
Community forum: [WAsP Python tools](https://www.wasptechnical.dk/forum/forum/19-wasp-python-tools/)
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
BSD-3-Clause - see [LICENSE](LICENSE) for details.
|
windkit-2.0.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# WindKit
|
|
2
|
+
|
|
3
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/pipelines)
|
|
4
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/pipelines)
|
|
5
|
+
[](https://gitlab-internal.windenergy.dtu.dk/ram/software/pywasp/windkit/-/releases)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://docs.wasp.dk/windkit)
|
|
9
|
+
|
|
10
|
+
[](https://pypi.org/project/windkit/)
|
|
11
|
+
[](https://pypi.org/project/windkit/)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
WindKit provides core functionalities for working with wind resource data, built on top of [xarray](https://xarray.dev/) and [geopandas](https://geopandas.org/).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
**pip:**
|
|
19
|
+
```bash
|
|
20
|
+
pip install windkit
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**conda:**
|
|
24
|
+
```bash
|
|
25
|
+
conda install -c https://conda.windenergy.dtu.dk/channel/open windkit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import windkit as wk
|
|
32
|
+
|
|
33
|
+
# Read a binned wind climate
|
|
34
|
+
bwc = wk.read_bwc("hovsore.tab")
|
|
35
|
+
|
|
36
|
+
# Calculate mean wind speed
|
|
37
|
+
mean_ws = wk.mean_wind_speed(bwc)
|
|
38
|
+
print(f"Mean wind speed: {mean_ws.values:.2f} m/s")
|
|
39
|
+
|
|
40
|
+
# Fit Weibull distribution and convert to Weibull wind climate
|
|
41
|
+
wwc = wk.weibull_fit(bwc)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Wind Climate I/O** - Read/write binned, Weibull, generalized, and time-series wind climates (NetCDF, .lib, .tab, .rsf, .wrg)
|
|
47
|
+
- **Map I/O** - Read/write elevation, roughness, and landcover maps (raster and vector formats)
|
|
48
|
+
- **WAsP Integration** - Read WAsP Workspace files and WAsP Engineering project files
|
|
49
|
+
- **Spatial Processing** - Clip, reproject, and interpolate spatial data with full CRS support
|
|
50
|
+
- **Wind Statistics** - Calculate mean wind speed, power density, Weibull parameters, and more
|
|
51
|
+
- **Plotting** - Interactive plots with Plotly, static plots with Matplotlib
|
|
52
|
+
- **Remote Data Access** - Download ERA5 reanalysis data and satellite-derived maps
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
Full documentation: [docs.wasp.dk/windkit](https://docs.wasp.dk/windkit)
|
|
57
|
+
|
|
58
|
+
Community forum: [WAsP Python tools](https://www.wasptechnical.dk/forum/forum/19-wasp-python-tools/)
|
|
59
|
+
|
|
60
|
+
## Contributing
|
|
61
|
+
|
|
62
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
BSD-3-Clause - see [LICENSE](LICENSE) for details.
|
|
@@ -9,34 +9,35 @@ license_files = LICENSE
|
|
|
9
9
|
classifiers =
|
|
10
10
|
Programming Language :: Python :: 3
|
|
11
11
|
Operating System :: OS Independent
|
|
12
|
-
url = https://docs
|
|
12
|
+
url = https://docs.wasp.dk/windkit/latest
|
|
13
13
|
|
|
14
14
|
[options]
|
|
15
15
|
packages = find_namespace:
|
|
16
|
-
python_requires = >=3.11
|
|
16
|
+
python_requires = >=3.11
|
|
17
17
|
include_package_data = True
|
|
18
18
|
install_requires =
|
|
19
|
-
lxml >=5.0
|
|
20
|
-
netcdf4 >=1.6
|
|
21
|
-
numpy >=1.26
|
|
19
|
+
lxml >=5.0
|
|
20
|
+
netcdf4 >=1.6
|
|
21
|
+
numpy >=1.26
|
|
22
22
|
packaging >=23
|
|
23
|
-
pandas >=2.1
|
|
24
|
-
pyproj >=3.6
|
|
25
|
-
scipy >=1.12
|
|
26
|
-
shapely >=2
|
|
27
|
-
xarray >=2023.9
|
|
28
|
-
rioxarray >=0.17
|
|
29
|
-
rasterio >=1.3
|
|
30
|
-
geopandas >=1.0
|
|
31
|
-
h5netcdf >=1.3
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
pydantic >=2.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
pandas >=2.1
|
|
24
|
+
pyproj >=3.6
|
|
25
|
+
scipy >=1.12
|
|
26
|
+
shapely >=2
|
|
27
|
+
xarray >=2023.9
|
|
28
|
+
rioxarray >=0.17
|
|
29
|
+
rasterio >=1.3
|
|
30
|
+
geopandas >=1.0
|
|
31
|
+
h5netcdf >=1.3
|
|
32
|
+
h5py >=3.9
|
|
33
|
+
matplotlib >=3.8
|
|
34
|
+
pydantic-settings >=2.3
|
|
35
|
+
pydantic >=2.7
|
|
36
|
+
platformdirs >=4.3.8
|
|
37
|
+
plotly >=5
|
|
38
|
+
kaleido >=0.2.1
|
|
39
|
+
requests >=2.31
|
|
40
|
+
toml >=0.10
|
|
40
41
|
|
|
41
42
|
[options.package_data]
|
|
42
43
|
windkit.data =
|
|
@@ -51,7 +52,7 @@ windkit.data.uncertainty_tables =
|
|
|
51
52
|
*.csv
|
|
52
53
|
|
|
53
54
|
[options.extras_require]
|
|
54
|
-
plots = matplotlib>=3.6; plotly>=5.12; kaleido>=0.2
|
|
55
|
+
plots = matplotlib>=3.6; plotly>=5.12; kaleido>=0.2.1
|
|
55
56
|
data_apis = ee; pystac_client; planetary_computer; fsspec; adlfs; zarr
|
|
56
57
|
performance = numba
|
|
57
58
|
|
|
@@ -20,6 +20,10 @@ __all__ = [
|
|
|
20
20
|
"vinterp_wind_speed",
|
|
21
21
|
"rotor_equivalent_wind_speed",
|
|
22
22
|
"tswc_resample",
|
|
23
|
+
"shear_extrapolate",
|
|
24
|
+
"shear_exponent",
|
|
25
|
+
"veer_extrapolate",
|
|
26
|
+
"wind_veer",
|
|
23
27
|
# Time Series Wind Climate
|
|
24
28
|
"validate_tswc",
|
|
25
29
|
"is_tswc",
|
|
@@ -50,10 +54,10 @@ __all__ = [
|
|
|
50
54
|
"create_gwc",
|
|
51
55
|
"read_gwc",
|
|
52
56
|
"gwc_to_file",
|
|
53
|
-
"spatial_interpolate_gwc",
|
|
54
57
|
# Geostrophic wind climate
|
|
55
58
|
"validate_geowc",
|
|
56
59
|
"is_geowc",
|
|
60
|
+
"create_geowc",
|
|
57
61
|
# Predicted Wind Climate
|
|
58
62
|
"create_pwc",
|
|
59
63
|
# Wind Climate
|
|
@@ -64,6 +68,7 @@ __all__ = [
|
|
|
64
68
|
"mean_wind_speed",
|
|
65
69
|
"mean_power_density",
|
|
66
70
|
"get_cross_predictions",
|
|
71
|
+
"extrapolate_to_height",
|
|
67
72
|
# Landcover
|
|
68
73
|
"LandCoverTable",
|
|
69
74
|
"get_landcover_table",
|
|
@@ -153,6 +158,10 @@ from windkit.wind import (
|
|
|
153
158
|
vinterp_wind_speed,
|
|
154
159
|
rotor_equivalent_wind_speed,
|
|
155
160
|
tswc_resample,
|
|
161
|
+
shear_extrapolate,
|
|
162
|
+
shear_exponent,
|
|
163
|
+
veer_extrapolate,
|
|
164
|
+
wind_veer,
|
|
156
165
|
)
|
|
157
166
|
from windkit.wind_climate.time_series_wind_climate import (
|
|
158
167
|
validate_tswc,
|
|
@@ -190,13 +199,13 @@ from windkit.wind_climate.generalized_wind_climate import (
|
|
|
190
199
|
create_gwc,
|
|
191
200
|
read_gwc,
|
|
192
201
|
gwc_to_file,
|
|
193
|
-
spatial_interpolate_gwc,
|
|
194
202
|
_reproject_gwc,
|
|
195
203
|
)
|
|
196
204
|
from windkit.wind_climate import generalized_wind_climate # noqa: F401
|
|
197
205
|
from windkit.wind_climate.geostrophic_wind_climate import (
|
|
198
206
|
validate_geowc,
|
|
199
207
|
is_geowc,
|
|
208
|
+
create_geowc,
|
|
200
209
|
)
|
|
201
210
|
from windkit.wind_climate import geostrophic_wind_climate # noqa: F401
|
|
202
211
|
from windkit.wind_climate.predicted_wind_climate import create_pwc
|
|
@@ -209,6 +218,7 @@ from windkit.wind_climate.wind_climate import (
|
|
|
209
218
|
mean_wind_speed,
|
|
210
219
|
mean_power_density,
|
|
211
220
|
get_cross_predictions,
|
|
221
|
+
extrapolate_to_height,
|
|
212
222
|
)
|
|
213
223
|
from windkit.cfd import read_cfdres
|
|
214
224
|
from windkit.wind_farm.wind_turbine_generator import (
|