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.
@@ -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.
@@ -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
+ ![northing error vs reanalysis_wd before northing](https://github.com/user-attachments/assets/aaf1e4c6-dc10-4c59-9281-1051128464af)
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
+ ![combined uplift and 90% CI](https://github.com/user-attachments/assets/a36214d1-7308-4a16-9be3-9e2230170708)
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
+ ![combined uplift and 90% CI](https://github.com/user-attachments/assets/00b17f01-54ea-4532-ab1c-16921df0b70e)
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
@@ -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
+ ![northing error vs reanalysis_wd before northing](https://github.com/user-attachments/assets/aaf1e4c6-dc10-4c59-9281-1051128464af)
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
+ ![combined uplift and 90% CI](https://github.com/user-attachments/assets/a36214d1-7308-4a16-9be3-9e2230170708)
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
+ ![combined uplift and 90% CI](https://github.com/user-attachments/assets/00b17f01-54ea-4532-ab1c-16921df0b70e)
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