Tasks/Earth & Energy/Renewables & Storage

Wind Farm Layout Optimisation

Place turbines so wakes cost the least energy

windfarm_wake_layout Earth & Energy Renewables & Storage
instruction.mdthis is what the agent is given

You inherit a weak but legal wind-farm layout pipeline and must improve its solve() so that it places N wind turbines inside a site boundary to maximise annual energy production (AEP) under a directional wind rose with Gaussian wake interaction. This is a well-studied problem on which independent expert teams sharing one evaluator still finish tens of percent apart, and on which no single method is known to dominate. Your submitted solve() is re-run per instance on a set of sealed hidden instances, and the higher your mean normalised AEP, the better.

Hard Constraints

  • An instance is a self-contained JSON file: n_turbines, a boundary (either {"type": "circle", "radius": R} centred at the origin or {"type": "polygons", "polygons": [[[x, y], ...], ...]} — the feasible region is the union of the polygons), a turbine block (ci, co, rated_ws, rated_pwr, diam), a min_spacing_D factor, and a windrose: direction bins with frequencies plus either a single speed (one representative wind speed) or speeds bins with a per-direction speed_freq matrix (a full speed distribution per direction).
  • Physics (fixed, implemented by the public evaluator wflo.py): the simplified Bastankhah Gaussian wake model with a cubic power curve between cut-in and rated. wflo.py IS the definition — read it; nothing else is assumed about the physics. The single scalar objective (higher is better) is AEP in MWh.
  • Your layout must satisfy, for every turbine: inside the boundary (union of polygons, or the disc), and pairwise centre distance >= min_spacing_D * diam (2 rotor diameters on every instance). An infeasible or malformed layout scores 0 on that instance — there is no repair on the grader side.
  • Implement solve(instance_path: str, time_budget_s: float) -> list[[x, y]] in methods/main/solver.py. It is called once per instance and must return within time_budget_s (the verifier uses 60 s per instance) a list of n_turbines [x, y] coordinate pairs in metres.
  • You may only change your solver code — not the evaluator, the instances, or the scoring. Determinism is recommended (seed your RNG) so your local scores reproduce.
  • Feasibility is a cliff, not a penalty: one turbine outside the boundary or one pair closer than the minimum spacing scores 0 on that instance. Project/repair before returning, and do not assume axis-aligned or circular sites.

What You Have

  • methods/main/solver.py — the weak baseline you edit in place: deterministic farthest-point spread over a feasible candidate grid. It only spreads turbines apart and never looks at the wind rose — wake-blind by design.
  • wflo.py — the exact public evaluator used for scoring: instance loader, vectorised AEP (aep()), feasibility check (check_layout()), the deterministic weak baseline (weak_layout()) used as the normalisation denominator, and score() = 100 × AEP / AEP(weak baseline).
  • data/ — 5 visible instances: C16, C36, C64 (circular sites with 16/36/64 turbines, 3.35 MW machines and a 16-bin wind rose) plus V1, V2 (a circle-family and a polygon-family sample produced by the hidden-set generator, so the visible set spans the hidden distribution).
  • selfcheck.py — runs your current solve() on the visible instances and prints the mean normalised score. Iterate against it (QUICK=5 python selfcheck.py for fast rounds).

What You Submit

Your edited methods/main/solver.py (plus any helper modules under methods/). The verifier re-runs solve() per hidden instance and re-scores the returned layouts; only the solve(path, budget) -> list[[x, y]] contract above is relied upon.

How It Is Judged

The sealed verifier calls your solve() on each hidden instance (unseen members of the same two generator families as V1/V2: rotated/jittered wind roses, rescaled or rotated boundaries, unseen turbine counts, including a large multi-polygon site drawn from a real offshore wind zone), then recomputes

score(instance) = 100 × AEP(your layout) / AEP(weak FPS baseline)

with its own trusted evaluator. The hidden mean is your score; higher is better, and your reward rises as it climbs. Beating the shipped reference is the goal, and there is real headroom above it — expert results on problems of this kind spread over more than ten points on this normalisation, so every extra point is a genuine climb.

Metric

mean normalised AEP score over the sealed hidden sites · higher is better

100 x AEP(returned layout) / AEP(wake-blind FPS baseline) per instance, averaged

anchorheld-outreward
Bshipped weak template (FPS)100.000.00
Rreference: ILS + greedy portfolio109.070.30
normalisation
m <= B0
B < m <= R0.3 * (l(m) - l(B)) / (l(R) - l(B))
m > R1 - 0.7/y, y = 1 + (l(m)-l(R))/(l(R)-l(B))

m = this run's held-out metric  ·  B = shipped weak template (FPS)  ·  R = reference: ILS + greedy portfolio

l(x) = log(x). Mapped once on the hidden mean, not per case. No SOTA and no upper anchor: reward tends to 1 without reaching it.

Rollouts

136 minwall clock
$45.72spend
74.6Mtokens
56versions, 36 kept
99 102 105 108 111 114 $0 $10 $20 $30 $40 cumulative spend on the run QUICK=5 visible mean score, higher is better v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 v32 v33 v34 v35 v36 v37 v38 v39 v40 v41 v42 v43 v44 v45 v46 v47 v48 v49 v50 v51 v52 v53 v54 v55
keptrevertedno scoreturning point
  1. v0Shipped wake-blind farthest-point spread, kept as the baseline snapshot1001 min · $0.22
  2. v1Exact in-solver AEP objective plus deterministic anytime hill climbingJudge every move by real wake loss inside the solver instead of by spacing alone.112.3874 min · $0.64
  3. v2Incremental row/column wake update and batched best-response scoring112.9556 min · $0.92
  4. v3Best-response batch size scaled inversely with turbine count113.4267 min · $1.10
  5. v4Rotation-equivariant exact greedy-insertion constructor for the start layoutBuild the layout turbine by turbine before polishing, rather than polishing the wake-blind spread.113.96314 min · $2.07
  6. v5Portfolio also keeps the FPS basin, 25% of refinement steps113.74716 min · $2.47
  7. v6Three cost-bounded diverse greedy starts, round-robin then focus the best113.98622 min · $3.60
  8. v716,385-point expected-power table ranks distributed-speed candidates114.11525 min · $4.06
  9. v8Circle boundary and polygon edge sampling raised 2.5x113.90526 min · $4.41
  10. v9Relocations stratified: 20% circle perimeter, 30% polygon edges114.28627 min · $4.66
  11. v10Reserved boundary fractions raised to 30% circles, 45% polygons114.10728 min · $4.88
  12. v11Half the boundary stratum drawn from the nearest perimeter neighbourhood114.2430 min · $5.34
  13. v12Refinement keeps the runner-up greedy basin alive at a fixed share114.27841 min · $8.03
  14. v13Virtual-2s constructor scale for n<=45 preserves the small-subset basin114.4841 min · $8.03
  15. v14Virtual-budget constructor limited to single-speed instances114.51842 min · $8.35
  16. v15One third of continuous local proposals projected onto the perimeter114.52146 min · $9.65
  17. v16Boundary projection share cut from 1/3 to 1/6114.45247 min · $9.90
  18. v17Sweeps ordered by production deficit plus marginal wake harm114.49250 min · $10.80
  19. v18Wake-priority order for single-speed only, random for distributed114.39950 min · $10.80
  20. v19Wake-priority order gated to single-speed farms with n>=32114.51851 min · $11.14
  21. v20Cost-consistent constructor caps; admit only starts predicted to fit114.47254 min · $11.90
  22. v21Larger constructor caps restored via 15% modelled-cost slack114.36956 min · $12.35
  23. v22Half the interior candidates replaced by Halton low-discrepancy points114.16557 min · $12.67
  24. v23Timing fix: 15% wall margin, minimum constructor cap max(200, 2n)114.32162 min · $13.83
  25. v24Polygon inside test fixed: on-edge accumulated apart from ray-cast XOR114.34463 min · $14.34
  26. v25Corrected inside test everywhere, old edge predicate for pools only114.25164 min · $14.87
  27. v26v19 constructor stop and cap schedule restored on the geometry fix114.53565 min · $15.21
  28. v27Relocation batches sampled without replacement, dropping duplicates114.60767 min · $15.73
  29. v28Unique relocation sampling limited to n<=45114.63470 min · $16.61
  30. v29Smaller coordinate batches for n>45 to raise full-sweep frequency114.45471 min · $17.13
  31. v30Intermediate fixed 200 global + 80 local proposals for n>45C64 checks only73 min · $17.91
  32. v31Three virtual-10s constructor scales for distributed-speed n<=45V2 checkpoint only78 min · $19.45
  33. v32Independent short and long cores, exact-select the better layoutRun two independent trajectories to completion and keep the better, instead of splitting one search.114.63480 min · $20.55
  34. v33Distributed-speed safety trajectory shortened from 10s to 7sV2 checkpoint only82 min · $21.19
  35. v34Constructor grid aligned to the strongest wind bin, not to the axes114.32484 min · $21.71
  36. v35Wind-aligned grid for the long core, axis grid for the safety coreV2 checkpoint only87 min · $22.70
  37. v36Distributed-speed batches cut from 280+120 to 200+100V2 checkpoint only88 min · $23.09
  38. v37Distributed-speed batches raised to 400 global + 160 localV2 checkpoint only88 min · $23.48
  39. v38Shape, finiteness, boundary and spacing checks with legal FPS fallback114.63290 min · $23.98
  40. v39A quarter of local proposals reserved for 0.002-0.05 fine scales114.45692 min · $24.80
  41. v40Fine scales derived from the coarse variates, no RNG stream shift114.51693 min · $25.40
  42. v41Fine-scale remap activated only after 65% of a trajectory114.64894 min · $25.80
  43. v42Late fine polishing restricted to single-speed trajectories114.52695 min · $26.21
  44. v43Cost-consistent constructor admission for single-speed n=30-45C36 checks only97 min · $27.18
  45. v44Independent short and long cores extended to single-speed n=30-4560s audit only100 min · $28.53
  46. v45With-replacement sampling restored, no fine remap for single-speed n<30114.517104 min · $30.02
  47. v46Fine polishing narrowed to exactly single-speed n=30-4560s audit only105 min · $30.77
  48. v47Medium single-speed safety core set to 16s after an isolated sweep60s audit only110 min · $33.76
  49. v48Both 12s and 16s safety cores plus a long core, medium single-speed60s audit only113 min · $35.32
  50. v497s and 10s safety cores plus a long core for distributed-speed60s audit only113 min · $35.32
  51. v50Only the long trajectory reseeded; proven safety cores untouched60s audit only116 min · $36.60
  52. v51Safety portfolio gated to n<=45; large sites get one full coresynthetic n=50 only122 min · $39.47
  53. v52Coordinate batches cut to 200+100 for distributed-speed n>45114.544124 min · $40.63
  54. v53Large distributed batches cut further to 150+80synthetic n=50 only126 min · $41.22
  55. v54Reserved edge proposals cut from 30% to 20% for polygon n>45synthetic n=50 only129 min · $42.41
  56. v5530s default core plus a reseeded remainder for large distributed sitessynthetic n=50 only131 min · $43.49

v0-v28 reached 114.63 on the proxy in 70 min and $16.6. The last 27 versions cost $29 more and were judged on 60 s audits, not on QUICK=5.

On the hidden set

held-out metricreward
shipped weak template (FPS)100.000.00
reference: ILS + greedy portfolio109.070.30
this run109.760.3472