Skip to content

wind-up v1 - #100

Draft
aclerc wants to merge 38 commits into
mainfrom
v1
Draft

wind-up v1#100
aclerc wants to merge 38 commits into
mainfrom
v1

Conversation

@aclerc

@aclerc aclerc commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

v0 is a single-method tool: it measures turbine-upgrade uplift with a binned power-curve, test-vs-reference method. v1 turns wind-up into a platform in which alternative uplift methods are pluggable and objectively benchmarked on synthetic datasets with known ground truth. The driving goals are accurate and precise results from short campaigns and richer conditional information about how an upgrade performs (uplift by wind speed, uplift in wakes vs free-stream, day vs night, by direction/stability, etc.).

Metrics:

  • Campaign uplift P50 bias and spread
  • Conditional uplift distribution: can measure per bin bias and spread of uplift distributions (vs wind speed, wind direction, TI, etc)
  • Uncertainty quantification: harder to judge but can verify campaign uplift actual quantiles vs those implied by the reported uncertainty (eg verify that ground truth uplift is below P95 exactly 5% of the time)
  • AEP uplift P50 bias and spread
    • A 'ground truth' AEP can be calculated if the source data has sufficient years. HOT has 5 clean years (2016-2020) so an AEP comparison based on those 5 years can be done. In this case the method estimates the AEP based on what it has learned about the uplift (conditional distribution) and the weather data it has access to (ERA5). However this is fairly tricky because even if the uplift is a very simple function of the turbine's conditions (eg just wind speed) the relationship between ERA5 data and the turbine's wind speed will usually be complicated (eg directional effect like wakes)
    • In some cases the AEP calculation will be done with a different tool (example FLORIS for wake steering, or pre-construction yield modelling could be used). In those cases wind-up needs to report out whatever these tools need. The vs wind speed conditional uplift distribution can be used to make a new power curve for pre-construction modelling, for example. FLORIS modelling would need more complex things like Pp (can be derived from uplift vs steer command) and perhaps wake model parameters.

Design considerations:

  • Robustness to reference turbine changes. Reference turbine performance can change in time which introduces risk of bias. Reference turbine wind speed is even more likely to change in time (eg due to anemometer calibration change)

aclerc and others added 30 commits June 19, 2026 13:59
* implement most of issue 1

* update cp_surface

* correct period meaning

* update docstrings

* finish issue 1

* minor comment updates

* remove turbine_key

mirrors change in resgroup/hill-of-towie-open-source-analysis#53

* address PR comments

* Update hill_of_towie.py
* issue 2 initial effort

* Simplify docstring

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* add example_hot_study.py

* address PR comments

* improve kwargs enforcement

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* issue 3 WIP

* Update example_v0_study.py

* fix md has different turbines than cfg.asset.wtgs

* write out tdf

* add missing save_plots guards

* fix lint

* remove HOT details from era5.py

* fix format

* fix test_explicit_mplbackend_is_respected

* address PR comments
* update issue 4

* WIP

* cache per year, turbine

* format

* improve naive plots

* improve naive plots

* improve issue 4 descr

* rename example prepost

* rename example prepost

* rename example prepost

* run fast methods first

* fix TypeError

* address PR comment

* address PR comment
* refactor out scadadf

* improve e2e tests

* address PR comments

* fix test flake
* update issue text

* RLearner WIP

* RLearner WIP

* RLearner tested against naive

* update gitignore

* add overnight scripts

* fix typo

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* improve overnight scripts

* add findings.md

* address PR comments

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* add inspect_prepost_hard_case.py

* add diagnostics modules

* mandatory availability filter

* fix naive plot bug

* fix docstring

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix docstring

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* update findings.md

* address PR comments

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* power_model

* update docstrings

* improve histograms

* address PR comments
* add study_power_model_compare.py

* add study_power_model_compare baseline

* uplift by condition WIP

* update power_model baseline results

* add by condition diagnostic plots

* add by condition diagnostic plots

* update findings.md

* plan issues 6-8

* address PR comment
* add prediction clip

* add condition dependent comparison

* add _select_profiles

* add --accept-candidate

* update baseline

* address pr comments
* add ERA5 future work

* issue 8 substantially complete

* address PR comments

* accept new conditional uplift method

* update issue 8 text

* address PR comments
* issues 9-11

* address PR comments

* explore feature removal

* improvements from feature removal

* address PR comments
* issue 12

* address PR comments
* issue 13 complete

* further investigate half life

* address PR comments

* skip rlearner e2e tests

* address PR comments
* issue 14 WIP

* add 1 and 2 months to baseline

* accept matched count 50

* issue 14 full benchmark

* address PR comments
* issue 15 WIP

* re-number issues

* re-number issues

* fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* prune opt-in behaviour

* update docstring
* update findings.md

* add power conditional WIP

* add power conditional WIP

* improve conditional diagnostic plots

* add power plot

* rerun study_power_model_compare

* address PR comment
aclerc and others added 8 commits July 14, 2026 14:24
* Update naive_ratio.py

* Update method.py

* make benchmarking importable

* use shared build_toggle_df

* fix log bug

* address PR comments
* make columns required

* address PR comments
* add ToggleSpecialistMethod

* Update test_naive_ratio.py

* Update toggle_specialist.py

* complete issue 17

* feat: add campaign_weeks grid + toggle-methods regression harness

Adds a weeks-based campaign-length grid alongside the existing months grid,
and a study script that regression-tests the two toggle-capable methods
(toggle_specialist, power_model) on Hill of Towie.

The weeks grid is additive: StudyConfig takes exactly one of campaign_months /
campaign_weeks, and leaderboard/plot_campaign_curves take a length_col
defaulting to campaign_months. Every existing months-based call is therefore
bit-identical, so the committed power_model baseline stays valid. months-only
accessors (CampaignWindow.months, StudyConfig.max_activity_months) raise on a
weeks study rather than silently reporting weeks as months.

study_toggle_methods_compare.py scores a placebo plus a symmetric +/-2% Cp pair
over 1/2/4/8 weeks -- the small-signal, short-campaign regime a real toggle
campaign lives in. It reports raw deltas against a committed benchmark: ground
truth is deterministic in (config, seed), so an unchanged method must diff to
exactly 0.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* add validate_conditions

* Create study_toggle_methods_compare_baseline.json

Recorded on f6b509b's method code. The stamp reads 04f36d6-dirty because validate_conditions (a pure, unwired addition) landed mid-run and test files were uncommitted; neither method's source differed.

* enforce kwargs

* add binning to toggle specialist

* Update density.py

* update vocab

* update study_toggle_methods_compare_baseline.json

* address PR comments

* Update study_toggle_methods_compare.py

* Update study_toggle_methods_compare_baseline.json

* V1 ts uncertainty (#127)

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* toggle specialist uncertainty WIP

* clean up baseline loose end

* address PR comments

* add labeled_rows

* make power bins symmetric

* address PR comments

* format

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* exclude_row WIP

* address PR comments
* WIP

* WIP

* WIP

* address PR comments

* address further PR comments

* add inspect_wake_steering_case.py

* create issues_campaigns.md
…133)

Drop the rlearner package entirely, carrying forward only the shared piece
power_model still needs.

- Relocate make_outcome_model (+ its _COMMON params and lazy lightgbm import)
  verbatim from rlearner/nuisance.py into power_model/fitting.py; repoint
  power_model/method.py and inspect_era5_matching_importance.py. Estimator
  construction is byte-identical, so power_model behaviour is unchanged.
- Carry over the factory's direct unit test into test_power_model_fitting.py
  (TestMakeOutcomeModel) so relocating it does not drop its unit coverage.
- Delete the rlearner package, its seven tests, and the rlearner-only
  inspect_prepost_feature_ablation.py. Rename the "rlearner" fixture labels in
  test_plots.py (they were plain strings, no import).
- Drop the now-stale rlearner references in era5_sync.py and method.py docstrings.

Acceptance: poe all-fast green; power_model reads UNCHANGED against both
committed benchmarks (study_toggle_methods_compare max delta 0.06 pp;
study_power_model_compare 0 material moves), i.e. identical to within LightGBM's
same-machine noise floor.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…nt drivers (#134)

Post-C7 housekeeping ahead of the campaigns tranche.

- Reword all "R-learner" references left behind by the rlearner package
  deletion (C7) to describe current behaviour. Notably the overnight studies
  advertised "oracle + naive + R-learner + v0" but now run power_model.
- Strip finding-number citations (F1/F5/F13/F30/...) from source and tests per
  the CLAUDE.md rule; the rationale stays in docs/v1/findings.md.
- Move spent one-off / manual inspection drivers into
  benchmarking/baselines/old/: migrate_toggle_baseline_v2_to_v3,
  inspect_era5_matching_importance, inspect_short_campaigns, inspect_naive,
  inspect_v0_run (no tests, not imported by the active tree).

poe all-fast green; no scored-method or benchmark behaviour changes.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* W0: src/ layout — rename legacy wind_up to wind_up_v0, add v1 wind_up skeleton

Adopt a src/ layout so the v1 package can claim the `wind_up` import name while the
legacy tool is retained as `wind_up_v0` (distribution stays `res-wind-up`; only import
names change).

- move legacy `wind_up/` to `src/wind_up_v0/`; add a minimal `src/wind_up/` v1 skeleton
  (docstring + __version__ + py.typed) for W1 to fill
- repoint every importer (v0_binned baseline, benchmarking, tests, examples, and the
  smarteole notebook) to `wind_up_v0`
- constants.py PROJECTROOT_DIR parents[1] -> parents[2] (repo root is one level deeper)
- pyproject packaging (where=["src","."]), coverage source, ruff paths, CODEOWNERS
- document Git LFS as a prerequisite; gitignore the smarteole example download

Behaviour-preserving: the v0 output-schema key "wind_up_version" is unchanged, poe lint
is green (mypy 120 files), 819 tests pass, and the smarteole/wedowind example plots and
numbers are byte/pixel-identical before and after.

benchmarking/ stays packaged temporarily (imported by a separate project); dropping it
from the release artifact and deleting the legacy config/input_data/cache root folders
are deferred to W2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* update v1 README

* W0 review: fix 3.10 mypy, repoint README to wind_up_v0, harden baseline guards

Address PR #135 review feedback:

- benchmarking/synthetic/upgrades.py: pin shape-agnostic npt.NDArray[np.float64]
  annotations on cp_ratio/ws_factor/nacelle_delta and cast the _is_waked argument to
  float64, so mypy passes under the numpy resolved on Python 3.10. This lint failure
  predates W0 (it came in with the wake-steering work and the v1 branch was already
  red on 3.10); the fix is type-only, no runtime change.
- README.md: repoint the quick-start imports from `wind_up` to `wind_up_v0`. The
  `wind_up` package is now the empty v1 skeleton, so the old snippets raised
  ModuleNotFoundError.
- src/wind_up_v0/py.typed: PEP 561 marker for the retained, fully typed legacy API.
- test_naive_ratio / test_toggle_specialist: the independence guards now reject both
  `wind_up` and `wind_up_v0`, so an "independent" baseline cannot quietly import either.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant