hot-open 0.2.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.
- hot_open-0.2.0/.gitignore +172 -0
- hot_open-0.2.0/LICENSE.txt +26 -0
- hot_open-0.2.0/PKG-INFO +235 -0
- hot_open-0.2.0/README.md +208 -0
- hot_open-0.2.0/docs/fastlog_resampling.md +110 -0
- hot_open-0.2.0/pyproject.toml +157 -0
- hot_open-0.2.0/src/hot_open/__init__.py +3 -0
- hot_open-0.2.0/src/hot_open/circular_math.py +180 -0
- hot_open-0.2.0/src/hot_open/era5_helpers.py +142 -0
- hot_open-0.2.0/src/hot_open/fastlog_helpers.py +1059 -0
- hot_open-0.2.0/src/hot_open/lidar_helpers.py +365 -0
- hot_open-0.2.0/src/hot_open/parquet_io.py +45 -0
- hot_open-0.2.0/src/hot_open/scada_helpers.py +195 -0
- hot_open-0.2.0/src/hot_open/settings.py +102 -0
- hot_open-0.2.0/src/hot_open/sourcing_data.py +396 -0
- hot_open-0.2.0/src/hot_open/unpack.py +81 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
116
|
+
.pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
121
|
+
__pypackages__/
|
|
122
|
+
|
|
123
|
+
# Celery stuff
|
|
124
|
+
celerybeat-schedule
|
|
125
|
+
celerybeat.pid
|
|
126
|
+
|
|
127
|
+
# SageMath parsed files
|
|
128
|
+
*.sage.py
|
|
129
|
+
|
|
130
|
+
# Environments
|
|
131
|
+
.env
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# PyCharm
|
|
164
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
165
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
166
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
167
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
168
|
+
.idea/
|
|
169
|
+
|
|
170
|
+
# PyPI configuration file
|
|
171
|
+
.pypirc
|
|
172
|
+
CLAUDE.md
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, RENEWABLE ENERGY SYSTEMS LIMITED, all rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted
|
|
6
|
+
provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
|
9
|
+
and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of
|
|
12
|
+
conditions and the following disclaimer in the documentation and/or other materials provided
|
|
13
|
+
with the distribution.
|
|
14
|
+
|
|
15
|
+
* Neither the name of the copyright holder nor the names of its contributors may be used to
|
|
16
|
+
endorse or promote products derived from this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
19
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
20
|
+
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
|
|
21
|
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
22
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
23
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
24
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
25
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
POSSIBILITY OF SUCH DAMAGE.
|
hot_open-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: hot_open
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Project for open analysis of the Hill of Towie SCADA open source dataset
|
|
5
|
+
Project-URL: Homepage, https://github.com/resgroup/hill-of-towie-open-source-analysis
|
|
6
|
+
Project-URL: Dataset, https://zenodo.org/records/22662930
|
|
7
|
+
Author-email: Alex Clerc <alex.clerc@res-group.com>
|
|
8
|
+
License-Expression: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE.txt
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
|
+
Requires-Dist: openmeteo-requests>=1.4.0
|
|
17
|
+
Requires-Dist: pandas>=2.3.3
|
|
18
|
+
Requires-Dist: pyarrow>=15.0
|
|
19
|
+
Requires-Dist: python-dotenv>=1.1.0
|
|
20
|
+
Requires-Dist: requests-cache>=1.2.1
|
|
21
|
+
Requires-Dist: requests>=2.32.3
|
|
22
|
+
Requires-Dist: res-wind-up>=0.4.10
|
|
23
|
+
Requires-Dist: retry-requests>=2.0.0
|
|
24
|
+
Requires-Dist: scipy>=1.11
|
|
25
|
+
Requires-Dist: tqdm>=4.68.1
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Hill of Towie - Open Source Dataset
|
|
29
|
+
|
|
30
|
+
Analysis of the Hill of Towie wind farm open dataset: https://zenodo.org/records/22662930
|
|
31
|
+
|
|
32
|
+
This repo contains analysis and helper functions to:
|
|
33
|
+
- download the [open dataset from Zenodo](https://zenodo.org/records/22662930) which includes 10-minute SCADA, ~1Hz fastlog collected by [Anemo](https://www.res-group.com/digital-solutions/anemo/), controller telemetry and LiDAR data.
|
|
34
|
+
- estimate energy uplift thanks to upgrades using the [wind-up](https://github.com/resgroup/wind-up) library
|
|
35
|
+
- ad-hoc analysis of the [Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) trial, including wake-steering event finding and LiDAR analysis.
|
|
36
|
+
|
|
37
|
+
**New to the dataset?** Field definitions, reference frames and known data quality issues are
|
|
38
|
+
documented in `Hill_of_Towie_data_dictionary.md` in the
|
|
39
|
+
[Zenodo data pack](https://zenodo.org/records/22662930).
|
|
40
|
+
|
|
41
|
+
**Working with the fastlog?** How it is resampled to a regular timebase, which aggregates are
|
|
42
|
+
available, and how to reuse the chunked cache on your own data source is described in
|
|
43
|
+
[docs/fastlog_resampling.md](docs/fastlog_resampling.md).
|
|
44
|
+
|
|
45
|
+
The repository currently covers three upgrade validation campaigns:
|
|
46
|
+
- the [Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) wake steering and collective yaw control trial, analysis first published in 2026 (`scripts/wfc_analysis_2026`)
|
|
47
|
+
- the earlier [AeroUp](https://www.res-group.com/digital-solutions/aeroup/) and [TuneUp](https://www.res-group.com/digital-solutions/tuneup/) turbine upgrades, analysis first published in 2025 (`scripts/uplift_analysis_2025`)
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
Analysis scripts in this repo download the input data from Zenodo automatically on first
|
|
52
|
+
use — no manual data staging is required. See `scripts/wfc_analysis_2026/inspect_data.py`
|
|
53
|
+
for a short example of loading the open dataset into pandas dataframes. An example plot
|
|
54
|
+
this script makes is shown below; this plot compares standard SCADA 10-min data with the
|
|
55
|
+
controller fastlog data available thanks to [Anemo](https://www.res-group.com/digital-solutions/anemo/).
|
|
56
|
+
|
|
57
|
+
<img width="2018" height="1471" alt="10min_vs_fastlog_T03" src="https://github.com/user-attachments/assets/b77b9aeb-70e8-4120-bed7-069e9b938f1f" />
|
|
58
|
+
|
|
59
|
+
## Dynamic Yaw analysis
|
|
60
|
+
|
|
61
|
+
A presentation explaining Dynamic Yaw and the analysis in this repo is available [here](https://www.res-group.com/wp-content/uploads/2026/06/Alex-Clerc-wake-steering-WindEurope-Tech-2026-v2.pdf).
|
|
62
|
+
|
|
63
|
+
The folder `scripts/wfc_analysis_2026` analyses the energy uplift from the
|
|
64
|
+
[Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) **(DY)** controller trial at Hill of Towie. DY combines two control features which both provide energy uplift in different ways:
|
|
65
|
+
|
|
66
|
+
- **Wake Steering (WS)** — selected upwind turbines steer to
|
|
67
|
+
deflect their wake away from a downwind partner. The controller applies a non-zero
|
|
68
|
+
wake-steering offset only when the wind direction falls inside one of the pre-defined
|
|
69
|
+
steering windows in `controller_config/wake-steering-lookup.csv`.
|
|
70
|
+
- **Collective Control (CC)** — every controlled turbine gets an improved wind direction signal based on pooled information from all turbines. This helps the turbine ignore turbulence when deciding whether to yaw or not, which can reduce yaw activity and increase energy yield. This is also essential for steering turbines to mitigate sensor bias during the steer.
|
|
71
|
+
|
|
72
|
+
DY is switched on and off by a synthetic toggle generated from UTC time (50-minute
|
|
73
|
+
on/off period, see `hot_dy_toggle_df()` in `uplift_ws.py`). The toggle pattern
|
|
74
|
+
lets wind-up estimate uplift from interleaved on/off pairs rather than a single
|
|
75
|
+
before/after split.
|
|
76
|
+
|
|
77
|
+
The driver script is `scripts/wfc_analysis_2026/total_uplift.py`. It runs the WS
|
|
78
|
+
and CC analyses end-to-end, generates many plots including all the plots shown below, and combines the two
|
|
79
|
+
results into a long-term AEP figure via `compute_lt_uplift()`.
|
|
80
|
+
|
|
81
|
+
### Site layout
|
|
82
|
+
|
|
83
|
+
The bubble plot
|
|
84
|
+
below shows the layout and ground elevation of each turbine — the west cluster sits
|
|
85
|
+
on the higher, exposed hilltop while the east cluster sits on a second smaller hill:
|
|
86
|
+
|
|
87
|
+
<img width="1440" height="922" alt="Hill of Towie turbine elevation" src="https://github.com/user-attachments/assets/21a8424a-3022-4c66-aa94-60d0933418ab" />
|
|
88
|
+
|
|
89
|
+
### Wake Steering result
|
|
90
|
+
|
|
91
|
+
`scripts/wfc_analysis_2026/uplift_ws.py` runs one wind-up analysis per
|
|
92
|
+
steering window. Each window uses a wind direction filter that matches the
|
|
93
|
+
upwind turbine's steering range. The P50 wake-steering uplift across the
|
|
94
|
+
qualifying steering events is **+1.32% (σ = 0.54%)**.
|
|
95
|
+
|
|
96
|
+
The plot below shows the per-turbine WS uplift across the west of the farm:
|
|
97
|
+
|
|
98
|
+
<img width="1440" height="1529" alt="Hill of Towie WS test turbine uplift" src="https://github.com/user-attachments/assets/a8e45437-53b2-478f-9d05-f11deb7e3363" />
|
|
99
|
+
|
|
100
|
+
### Collective Control result
|
|
101
|
+
|
|
102
|
+
`scripts/wfc_analysis_2026/uplift_cc.py` runs wind-up on the 10-min
|
|
103
|
+
periods that the per-steer analysis did *not* consume, isolating the contribution of
|
|
104
|
+
CC to uplift. T07 is excluded from the CC test set because it was not actively controlled during the campaign.
|
|
105
|
+
|
|
106
|
+
Across the 13 CC test turbines the P50 uplift is **+0.56% (σ = 0.47%)** with an
|
|
107
|
+
average yaw-activity reduction of **−6.0%**. The headline test-vs-reference bar
|
|
108
|
+
chart shows ~0% uplift on the four references (three reference turbines and the ZX300 LiDAR) as expected, while the test
|
|
109
|
+
group is positive:
|
|
110
|
+
|
|
111
|
+
<img width="1440" height="922" alt="Hill of Towie CC test turbine uplift" src="https://github.com/user-attachments/assets/dd28ddc9-f577-43ad-b7d3-a6c13dcb31f4" />
|
|
112
|
+
|
|
113
|
+
<img width="640" height="480" alt="combined uplift and 90pct CI" src="https://github.com/user-attachments/assets/2a43daad-0b95-45a3-b7d4-c1db871b96dd" />
|
|
114
|
+
|
|
115
|
+
CC also changes how much each turbine yaws. The bubble plot below shows the
|
|
116
|
+
per-turbine yaw activity change: blue means the turbine yawed less under CC:
|
|
117
|
+
|
|
118
|
+
<img width="1440" height="922" alt="Hill of Towie CC test turbine yaw activity change" src="https://github.com/user-attachments/assets/86a32515-5de6-4ffa-81ee-642d9760a787" />
|
|
119
|
+
|
|
120
|
+
### Combined long-term AEP
|
|
121
|
+
|
|
122
|
+
`compute_lt_uplift()` in `total_uplift.py` blends the WS and CC results into a
|
|
123
|
+
long-term AEP figure using FLORIS-derived fractions of time and energy that the
|
|
124
|
+
long-term steering schedule will spend in each mode. The combination gives:
|
|
125
|
+
|
|
126
|
+
- **AEP uplift P50: +0.7%**, σ = 0.4%, P95 = 0.0%
|
|
127
|
+
- Long-term yaw-activity change: **−2.4%** wind farm average, with an individual
|
|
128
|
+
turbine maximum of **+8.1%** on T03 (the most actively-steered turbine).
|
|
129
|
+
|
|
130
|
+
### `scripts/wfc_analysis_2026` also includes:
|
|
131
|
+
|
|
132
|
+
- `inspect_data.py` — quick sanity plots of all data sources together.
|
|
133
|
+
- `zx300_wake_steers.py`, `zxtm_wake_steers.py` — wake steer plots with data from the
|
|
134
|
+
on-site ZX300 (near T11) and ZXTM (on T07) LiDARs.
|
|
135
|
+
- `z300_wake_steer_animation.py`, `zxtm_wake_steer_animation.py` — matplotlib
|
|
136
|
+
animations of LiDAR and turbine data during steering events.
|
|
137
|
+
|
|
138
|
+
The output of `zxtm_wake_steer_animation.py` is shown below (note wind is coming from SSW, bottom left of the middle plot):
|
|
139
|
+
<img width="1400" height="1200" alt="zxtm_wake_steer_animation" src="https://github.com/user-attachments/assets/b3b5fdb2-b258-4f86-9ae9-c57d6c14d44b" />
|
|
140
|
+
|
|
141
|
+
## Earlier upgrade analysis (published in 2025)
|
|
142
|
+
|
|
143
|
+
The folder `scripts/uplift_analysis_2025` uses [wind-up](https://github.com/resgroup/wind-up) to run two analyses of energy uplift
|
|
144
|
+
after turbine upgrades.
|
|
145
|
+
|
|
146
|
+
Note any analysis can be run without running the others, e.g. you do not need to run `northing.py` before `aero_up.py`.
|
|
147
|
+
|
|
148
|
+
The script `scripts/uplift_analysis_2025/northing.py` calculates the northing corrections saved to
|
|
149
|
+
`scripts/uplift_analysis_2025/wind_up_config/northing/optimized_northing_corrections.yaml` and used in subsequent analyses. The plot
|
|
150
|
+
below shows the circular difference of ERA5 wind direction to each turbine's yaw direction before the northing
|
|
151
|
+
correction. The turbine north calibrations are apparently often wrong, which means the northing correction is quite
|
|
152
|
+
important.
|
|
153
|
+
|
|
154
|
+

|
|
155
|
+
|
|
156
|
+
### AeroUp
|
|
157
|
+
|
|
158
|
+
The script `scripts/uplift_analysis_2025/aero_up.py` analyses the energy uplift thanks
|
|
159
|
+
to [AeroUp](https://www.res-group.com/digital-solutions/aeroup/) for T13.
|
|
160
|
+
The result is a P50 uplift of 4.3% with a 90% confidence interval of 3.3% to 5.3%. This is visualized in the plot below
|
|
161
|
+
along with the uplift results for the three selected reference turbines, which are near 0% uplift as expected:
|
|
162
|
+
|
|
163
|
+

|
|
164
|
+
|
|
165
|
+
### TuneUp
|
|
166
|
+
|
|
167
|
+
The script `scripts/uplift_analysis_2025/tune_up.py` analyses the energy uplift thanks
|
|
168
|
+
to [TuneUp](https://www.res-group.com/digital-solutions/tuneup/) for nine test turbines. The result is a P50 uplift of
|
|
169
|
+
1.1% with a 90% confidence interval of 0.2% to 2.0%. This is visualized in the plot below along with the uplift result for
|
|
170
|
+
the ten unchanged reference turbines, which is near 0% uplift as expected:
|
|
171
|
+
|
|
172
|
+

|
|
173
|
+
|
|
174
|
+
## Code for WeDoWind challenge
|
|
175
|
+
|
|
176
|
+
`scripts/wedowind_challenge_2025` contains code used for the
|
|
177
|
+
[WeDoWind 2025 power prediction kaggle competition](https://www.kaggle.com/competitions/hill-of-towie-wind-turbine-power-prediction),
|
|
178
|
+
which used the Hill of Towie open dataset.
|
|
179
|
+
`solution_using_wind_up.py` needs a few extra packages that are not in the locked environment;
|
|
180
|
+
see its docstring.
|
|
181
|
+
|
|
182
|
+
## Using `hot_open` in another project
|
|
183
|
+
|
|
184
|
+
The helper package is published on [PyPI](https://pypi.org/project/hot-open/):
|
|
185
|
+
|
|
186
|
+
```commandline
|
|
187
|
+
uv add hot-open
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Releases follow [semantic versioning](https://semver.org/); while the version is `0.x`, a minor
|
|
191
|
+
version bump may contain breaking changes. Release notes are on the
|
|
192
|
+
[GitHub releases page](https://github.com/resgroup/hill-of-towie-open-source-analysis/releases).
|
|
193
|
+
The analysis scripts under `scripts/` are not part of the package.
|
|
194
|
+
|
|
195
|
+
## Python environment
|
|
196
|
+
|
|
197
|
+
The environment can be created and managed using [uv](https://docs.astral.sh/uv/). To create the environment:
|
|
198
|
+
|
|
199
|
+
```commandline
|
|
200
|
+
uv sync
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
To run formatting and linting:
|
|
204
|
+
|
|
205
|
+
```commandline
|
|
206
|
+
uv run poe all
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Configuration
|
|
210
|
+
|
|
211
|
+
All filesystem paths used by the scripts and helpers are controlled by environment
|
|
212
|
+
variables. Each variable is optional — if unset, the default location under your
|
|
213
|
+
home directory is used and created on first access. Values can either be exported
|
|
214
|
+
in your shell or written to a `.env` file at the repo root (auto-loaded via
|
|
215
|
+
`python-dotenv`).
|
|
216
|
+
|
|
217
|
+
| Variable | Default | Used for |
|
|
218
|
+
| --- | --- | --- |
|
|
219
|
+
| `HOT_OPEN_DATA_DIR` | `~/temp/hill-of-towie-open-source-analysis/data` | Input data: Zenodo downloads land here (SCADA zips, LiDAR data, fastlog data, metadata CSV). |
|
|
220
|
+
| `HOT_OPEN_OUTPUT_DIR` | `~/temp/hill-of-towie-open-source-analysis/output` | Per-script output directory (one subfolder per script, named after the script's filename). |
|
|
221
|
+
| `HOT_OPEN_CACHE_DIR` | `~/temp/hill-of-towie-open-source-analysis/cache` | Parquet caches for unpacked SCADA, fastlog-by-day, ERA5, and wind-up intermediates. Safe to delete to force a rebuild. |
|
|
222
|
+
| `HOT_OPEN_FILESTORE_DIR` | `<HOT_OPEN_DATA_DIR>/turbine_fastlog/Filestore` | Fastlog tree (`FL/<park>/<device>/<date>/`). When set explicitly, `load_hot_fl_data()` skips the Zenodo download of `turbine_fastlog.zip`. |
|
|
223
|
+
| `WINDUP_OUTPUT_DIR` | `~/temp/hill-of-towie-open-source-analysis/windup_output` | Root directory wind-up analyses write their per-assessment output into. |
|
|
224
|
+
|
|
225
|
+
Example `.env`:
|
|
226
|
+
|
|
227
|
+
```dotenv
|
|
228
|
+
HOT_OPEN_DATA_DIR=D:/hot_open/data
|
|
229
|
+
HOT_OPEN_CACHE_DIR=D:/hot_open/cache
|
|
230
|
+
WINDUP_OUTPUT_DIR=D:/hot_open/windup_output
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Contact
|
|
234
|
+
|
|
235
|
+
Alex.Clerc@res-group.com
|
hot_open-0.2.0/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Hill of Towie - Open Source Dataset
|
|
2
|
+
|
|
3
|
+
Analysis of the Hill of Towie wind farm open dataset: https://zenodo.org/records/22662930
|
|
4
|
+
|
|
5
|
+
This repo contains analysis and helper functions to:
|
|
6
|
+
- download the [open dataset from Zenodo](https://zenodo.org/records/22662930) which includes 10-minute SCADA, ~1Hz fastlog collected by [Anemo](https://www.res-group.com/digital-solutions/anemo/), controller telemetry and LiDAR data.
|
|
7
|
+
- estimate energy uplift thanks to upgrades using the [wind-up](https://github.com/resgroup/wind-up) library
|
|
8
|
+
- ad-hoc analysis of the [Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) trial, including wake-steering event finding and LiDAR analysis.
|
|
9
|
+
|
|
10
|
+
**New to the dataset?** Field definitions, reference frames and known data quality issues are
|
|
11
|
+
documented in `Hill_of_Towie_data_dictionary.md` in the
|
|
12
|
+
[Zenodo data pack](https://zenodo.org/records/22662930).
|
|
13
|
+
|
|
14
|
+
**Working with the fastlog?** How it is resampled to a regular timebase, which aggregates are
|
|
15
|
+
available, and how to reuse the chunked cache on your own data source is described in
|
|
16
|
+
[docs/fastlog_resampling.md](docs/fastlog_resampling.md).
|
|
17
|
+
|
|
18
|
+
The repository currently covers three upgrade validation campaigns:
|
|
19
|
+
- the [Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) wake steering and collective yaw control trial, analysis first published in 2026 (`scripts/wfc_analysis_2026`)
|
|
20
|
+
- the earlier [AeroUp](https://www.res-group.com/digital-solutions/aeroup/) and [TuneUp](https://www.res-group.com/digital-solutions/tuneup/) turbine upgrades, analysis first published in 2025 (`scripts/uplift_analysis_2025`)
|
|
21
|
+
|
|
22
|
+
## Quickstart
|
|
23
|
+
|
|
24
|
+
Analysis scripts in this repo download the input data from Zenodo automatically on first
|
|
25
|
+
use — no manual data staging is required. See `scripts/wfc_analysis_2026/inspect_data.py`
|
|
26
|
+
for a short example of loading the open dataset into pandas dataframes. An example plot
|
|
27
|
+
this script makes is shown below; this plot compares standard SCADA 10-min data with the
|
|
28
|
+
controller fastlog data available thanks to [Anemo](https://www.res-group.com/digital-solutions/anemo/).
|
|
29
|
+
|
|
30
|
+
<img width="2018" height="1471" alt="10min_vs_fastlog_T03" src="https://github.com/user-attachments/assets/b77b9aeb-70e8-4120-bed7-069e9b938f1f" />
|
|
31
|
+
|
|
32
|
+
## Dynamic Yaw analysis
|
|
33
|
+
|
|
34
|
+
A presentation explaining Dynamic Yaw and the analysis in this repo is available [here](https://www.res-group.com/wp-content/uploads/2026/06/Alex-Clerc-wake-steering-WindEurope-Tech-2026-v2.pdf).
|
|
35
|
+
|
|
36
|
+
The folder `scripts/wfc_analysis_2026` analyses the energy uplift from the
|
|
37
|
+
[Dynamic Yaw](https://www.res-group.com/digital-solutions/dynamic-yaw/) **(DY)** controller trial at Hill of Towie. DY combines two control features which both provide energy uplift in different ways:
|
|
38
|
+
|
|
39
|
+
- **Wake Steering (WS)** — selected upwind turbines steer to
|
|
40
|
+
deflect their wake away from a downwind partner. The controller applies a non-zero
|
|
41
|
+
wake-steering offset only when the wind direction falls inside one of the pre-defined
|
|
42
|
+
steering windows in `controller_config/wake-steering-lookup.csv`.
|
|
43
|
+
- **Collective Control (CC)** — every controlled turbine gets an improved wind direction signal based on pooled information from all turbines. This helps the turbine ignore turbulence when deciding whether to yaw or not, which can reduce yaw activity and increase energy yield. This is also essential for steering turbines to mitigate sensor bias during the steer.
|
|
44
|
+
|
|
45
|
+
DY is switched on and off by a synthetic toggle generated from UTC time (50-minute
|
|
46
|
+
on/off period, see `hot_dy_toggle_df()` in `uplift_ws.py`). The toggle pattern
|
|
47
|
+
lets wind-up estimate uplift from interleaved on/off pairs rather than a single
|
|
48
|
+
before/after split.
|
|
49
|
+
|
|
50
|
+
The driver script is `scripts/wfc_analysis_2026/total_uplift.py`. It runs the WS
|
|
51
|
+
and CC analyses end-to-end, generates many plots including all the plots shown below, and combines the two
|
|
52
|
+
results into a long-term AEP figure via `compute_lt_uplift()`.
|
|
53
|
+
|
|
54
|
+
### Site layout
|
|
55
|
+
|
|
56
|
+
The bubble plot
|
|
57
|
+
below shows the layout and ground elevation of each turbine — the west cluster sits
|
|
58
|
+
on the higher, exposed hilltop while the east cluster sits on a second smaller hill:
|
|
59
|
+
|
|
60
|
+
<img width="1440" height="922" alt="Hill of Towie turbine elevation" src="https://github.com/user-attachments/assets/21a8424a-3022-4c66-aa94-60d0933418ab" />
|
|
61
|
+
|
|
62
|
+
### Wake Steering result
|
|
63
|
+
|
|
64
|
+
`scripts/wfc_analysis_2026/uplift_ws.py` runs one wind-up analysis per
|
|
65
|
+
steering window. Each window uses a wind direction filter that matches the
|
|
66
|
+
upwind turbine's steering range. The P50 wake-steering uplift across the
|
|
67
|
+
qualifying steering events is **+1.32% (σ = 0.54%)**.
|
|
68
|
+
|
|
69
|
+
The plot below shows the per-turbine WS uplift across the west of the farm:
|
|
70
|
+
|
|
71
|
+
<img width="1440" height="1529" alt="Hill of Towie WS test turbine uplift" src="https://github.com/user-attachments/assets/a8e45437-53b2-478f-9d05-f11deb7e3363" />
|
|
72
|
+
|
|
73
|
+
### Collective Control result
|
|
74
|
+
|
|
75
|
+
`scripts/wfc_analysis_2026/uplift_cc.py` runs wind-up on the 10-min
|
|
76
|
+
periods that the per-steer analysis did *not* consume, isolating the contribution of
|
|
77
|
+
CC to uplift. T07 is excluded from the CC test set because it was not actively controlled during the campaign.
|
|
78
|
+
|
|
79
|
+
Across the 13 CC test turbines the P50 uplift is **+0.56% (σ = 0.47%)** with an
|
|
80
|
+
average yaw-activity reduction of **−6.0%**. The headline test-vs-reference bar
|
|
81
|
+
chart shows ~0% uplift on the four references (three reference turbines and the ZX300 LiDAR) as expected, while the test
|
|
82
|
+
group is positive:
|
|
83
|
+
|
|
84
|
+
<img width="1440" height="922" alt="Hill of Towie CC test turbine uplift" src="https://github.com/user-attachments/assets/dd28ddc9-f577-43ad-b7d3-a6c13dcb31f4" />
|
|
85
|
+
|
|
86
|
+
<img width="640" height="480" alt="combined uplift and 90pct CI" src="https://github.com/user-attachments/assets/2a43daad-0b95-45a3-b7d4-c1db871b96dd" />
|
|
87
|
+
|
|
88
|
+
CC also changes how much each turbine yaws. The bubble plot below shows the
|
|
89
|
+
per-turbine yaw activity change: blue means the turbine yawed less under CC:
|
|
90
|
+
|
|
91
|
+
<img width="1440" height="922" alt="Hill of Towie CC test turbine yaw activity change" src="https://github.com/user-attachments/assets/86a32515-5de6-4ffa-81ee-642d9760a787" />
|
|
92
|
+
|
|
93
|
+
### Combined long-term AEP
|
|
94
|
+
|
|
95
|
+
`compute_lt_uplift()` in `total_uplift.py` blends the WS and CC results into a
|
|
96
|
+
long-term AEP figure using FLORIS-derived fractions of time and energy that the
|
|
97
|
+
long-term steering schedule will spend in each mode. The combination gives:
|
|
98
|
+
|
|
99
|
+
- **AEP uplift P50: +0.7%**, σ = 0.4%, P95 = 0.0%
|
|
100
|
+
- Long-term yaw-activity change: **−2.4%** wind farm average, with an individual
|
|
101
|
+
turbine maximum of **+8.1%** on T03 (the most actively-steered turbine).
|
|
102
|
+
|
|
103
|
+
### `scripts/wfc_analysis_2026` also includes:
|
|
104
|
+
|
|
105
|
+
- `inspect_data.py` — quick sanity plots of all data sources together.
|
|
106
|
+
- `zx300_wake_steers.py`, `zxtm_wake_steers.py` — wake steer plots with data from the
|
|
107
|
+
on-site ZX300 (near T11) and ZXTM (on T07) LiDARs.
|
|
108
|
+
- `z300_wake_steer_animation.py`, `zxtm_wake_steer_animation.py` — matplotlib
|
|
109
|
+
animations of LiDAR and turbine data during steering events.
|
|
110
|
+
|
|
111
|
+
The output of `zxtm_wake_steer_animation.py` is shown below (note wind is coming from SSW, bottom left of the middle plot):
|
|
112
|
+
<img width="1400" height="1200" alt="zxtm_wake_steer_animation" src="https://github.com/user-attachments/assets/b3b5fdb2-b258-4f86-9ae9-c57d6c14d44b" />
|
|
113
|
+
|
|
114
|
+
## Earlier upgrade analysis (published in 2025)
|
|
115
|
+
|
|
116
|
+
The folder `scripts/uplift_analysis_2025` uses [wind-up](https://github.com/resgroup/wind-up) to run two analyses of energy uplift
|
|
117
|
+
after turbine upgrades.
|
|
118
|
+
|
|
119
|
+
Note any analysis can be run without running the others, e.g. you do not need to run `northing.py` before `aero_up.py`.
|
|
120
|
+
|
|
121
|
+
The script `scripts/uplift_analysis_2025/northing.py` calculates the northing corrections saved to
|
|
122
|
+
`scripts/uplift_analysis_2025/wind_up_config/northing/optimized_northing_corrections.yaml` and used in subsequent analyses. The plot
|
|
123
|
+
below shows the circular difference of ERA5 wind direction to each turbine's yaw direction before the northing
|
|
124
|
+
correction. The turbine north calibrations are apparently often wrong, which means the northing correction is quite
|
|
125
|
+
important.
|
|
126
|
+
|
|
127
|
+

|
|
128
|
+
|
|
129
|
+
### AeroUp
|
|
130
|
+
|
|
131
|
+
The script `scripts/uplift_analysis_2025/aero_up.py` analyses the energy uplift thanks
|
|
132
|
+
to [AeroUp](https://www.res-group.com/digital-solutions/aeroup/) for T13.
|
|
133
|
+
The result is a P50 uplift of 4.3% with a 90% confidence interval of 3.3% to 5.3%. This is visualized in the plot below
|
|
134
|
+
along with the uplift results for the three selected reference turbines, which are near 0% uplift as expected:
|
|
135
|
+
|
|
136
|
+

|
|
137
|
+
|
|
138
|
+
### TuneUp
|
|
139
|
+
|
|
140
|
+
The script `scripts/uplift_analysis_2025/tune_up.py` analyses the energy uplift thanks
|
|
141
|
+
to [TuneUp](https://www.res-group.com/digital-solutions/tuneup/) for nine test turbines. The result is a P50 uplift of
|
|
142
|
+
1.1% with a 90% confidence interval of 0.2% to 2.0%. This is visualized in the plot below along with the uplift result for
|
|
143
|
+
the ten unchanged reference turbines, which is near 0% uplift as expected:
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
## Code for WeDoWind challenge
|
|
148
|
+
|
|
149
|
+
`scripts/wedowind_challenge_2025` contains code used for the
|
|
150
|
+
[WeDoWind 2025 power prediction kaggle competition](https://www.kaggle.com/competitions/hill-of-towie-wind-turbine-power-prediction),
|
|
151
|
+
which used the Hill of Towie open dataset.
|
|
152
|
+
`solution_using_wind_up.py` needs a few extra packages that are not in the locked environment;
|
|
153
|
+
see its docstring.
|
|
154
|
+
|
|
155
|
+
## Using `hot_open` in another project
|
|
156
|
+
|
|
157
|
+
The helper package is published on [PyPI](https://pypi.org/project/hot-open/):
|
|
158
|
+
|
|
159
|
+
```commandline
|
|
160
|
+
uv add hot-open
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Releases follow [semantic versioning](https://semver.org/); while the version is `0.x`, a minor
|
|
164
|
+
version bump may contain breaking changes. Release notes are on the
|
|
165
|
+
[GitHub releases page](https://github.com/resgroup/hill-of-towie-open-source-analysis/releases).
|
|
166
|
+
The analysis scripts under `scripts/` are not part of the package.
|
|
167
|
+
|
|
168
|
+
## Python environment
|
|
169
|
+
|
|
170
|
+
The environment can be created and managed using [uv](https://docs.astral.sh/uv/). To create the environment:
|
|
171
|
+
|
|
172
|
+
```commandline
|
|
173
|
+
uv sync
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
To run formatting and linting:
|
|
177
|
+
|
|
178
|
+
```commandline
|
|
179
|
+
uv run poe all
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Configuration
|
|
183
|
+
|
|
184
|
+
All filesystem paths used by the scripts and helpers are controlled by environment
|
|
185
|
+
variables. Each variable is optional — if unset, the default location under your
|
|
186
|
+
home directory is used and created on first access. Values can either be exported
|
|
187
|
+
in your shell or written to a `.env` file at the repo root (auto-loaded via
|
|
188
|
+
`python-dotenv`).
|
|
189
|
+
|
|
190
|
+
| Variable | Default | Used for |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| `HOT_OPEN_DATA_DIR` | `~/temp/hill-of-towie-open-source-analysis/data` | Input data: Zenodo downloads land here (SCADA zips, LiDAR data, fastlog data, metadata CSV). |
|
|
193
|
+
| `HOT_OPEN_OUTPUT_DIR` | `~/temp/hill-of-towie-open-source-analysis/output` | Per-script output directory (one subfolder per script, named after the script's filename). |
|
|
194
|
+
| `HOT_OPEN_CACHE_DIR` | `~/temp/hill-of-towie-open-source-analysis/cache` | Parquet caches for unpacked SCADA, fastlog-by-day, ERA5, and wind-up intermediates. Safe to delete to force a rebuild. |
|
|
195
|
+
| `HOT_OPEN_FILESTORE_DIR` | `<HOT_OPEN_DATA_DIR>/turbine_fastlog/Filestore` | Fastlog tree (`FL/<park>/<device>/<date>/`). When set explicitly, `load_hot_fl_data()` skips the Zenodo download of `turbine_fastlog.zip`. |
|
|
196
|
+
| `WINDUP_OUTPUT_DIR` | `~/temp/hill-of-towie-open-source-analysis/windup_output` | Root directory wind-up analyses write their per-assessment output into. |
|
|
197
|
+
|
|
198
|
+
Example `.env`:
|
|
199
|
+
|
|
200
|
+
```dotenv
|
|
201
|
+
HOT_OPEN_DATA_DIR=D:/hot_open/data
|
|
202
|
+
HOT_OPEN_CACHE_DIR=D:/hot_open/cache
|
|
203
|
+
WINDUP_OUTPUT_DIR=D:/hot_open/windup_output
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Contact
|
|
207
|
+
|
|
208
|
+
Alex.Clerc@res-group.com
|