[Commit history since v0.3.0](https://gitlab.windenergy.dtu.dk/TOPFARM/OptiWindNet/-/compare/v0.3.0...HEAD)

- **Automatic MILP warm starts:** `MILPRouter` builds a warm start for a fresh solve by default. `warmup=False` disables both construction and reuse; `warmup_time` sets the construction budget (default 0.2 s). 29ab4cd
- **Ringed feeder limits:** `HGSRouter.feeder_limit` counts substation connections, so it allows half as many rings as before. Odd limits raise `ValueError`. f8d0c13
- **Power units:** with `power_unit` supplied, turbine powers and cable capacities are nominal power in that unit. Without it, they must be integer inflow; existing power declarations on the location are discarded. efa6d5b
- **Cable reassignment:** assigning cables too small for the existing solution invalidates it instead of raising `ValueError`; call `optimize()` again. A solution that fits is retained and its cable types reassigned. efa6d5b
- **HiGHS options:** `'highs'` uses native `highspy` and accepts native HiGHS option names only. Use `'pyomo.highs'` for the previous backend and its appsi configuration. c92d28e
- **Buffered-location plots:** `plot_original_vs_buffered()` returns SVG by default, matching the other plot methods. Pass `ax` for Matplotlib. 6ce61fc

- **Keyword-only problem setup:** all `Solver.set_problem()` arguments after `P, A`, including `capacity` and `model_options`, must be named. e4e8783
- **Terminal demand:** integer solver demand is stored as `'inflow'`, defaulting to one; `'power'` denotes nominal physical power. Rename integer-demand attributes or declare nominal power with `loads.set_terminal_power()`. 85bfaf3 dd6d90a
- **Mixed-power solves:** sites declaring unequal power require `capacity_nominal`, including in `TerseLinks` decoders. `EWRouter`/`constructor()` reject nonunitary inflow; HGS and LKH support it only for unbalanced, single-substation radial solves. MILP rejects nonunitary ringed models and feeder limits `'minimum'`/`'min_plus1/2/3'`. b627575 17d2c20 54904b2
- **Imported power:** YAML and OSM importers, and `load_repository()`, read declared turbine power by default. Pass `read_powers=False` for turbine-count inputs; bundled Borssele and Walney Extension declare unequal powers. 05519b1
- **Persistence:** `pack_G()` and `store_G()` reject routesets with unequal power or nonunitary inflow because the storage schema has no terminal-power field. Equal-power attributes remain storable. dd6d90a
- **Enum options:** direct `make_min_length_model()` calls require enum values for `topology`, `feeder_route`, and `feeder_limit`; strings raise `TypeError`. `ModelOptions` still accepts strings. 7aeda31
- **LKH balancing:** `lkh3(balanced=True)` raises `NotImplementedError` because LKH did not enforce it. Use `hgs_cvrp(balanced=True)`. ce3df91
- **Incumbent access:** `Solver.get_incumbent_topology()` is removed. Use `get_solution()` for routed results, or `S_from_linkbits(solver.incumbent_linkbits, A)` followed by `calcload()` or `split_rings_and_calc_loads()` for an unrouted topology. 531dcf0
- **Crossing diagnostics:** `crossings.describe_crossings()` is removed; use `validate_routeset()` for geometric validation. c123ed2
- **Ring helpers:** `add_ring_to_S()` and `rings_from_S()` are private implementation details and no longer public exports. fd27af0 c4c8486
- **Baseline utilities:** `optiwindnet.baselines.utils` and its distance-matrix utilities are removed. d0c5e88
- **Deprecated imports:** relocated `interarraylib` functions and classes remain available through aliases that emit `DeprecationWarning`, scheduled for removal in v0.4.0. Import from `converting`, `loads`, `terse`, `transforming`, `validating`, or `presenting`; `fingerprint` similarly delegates to `identity`. 6bc0212 c123ed2
- **Method identities:** HGS and LKH record `balanced`, `ringed`, `repair`, and `max_retries` in `method_options`. Their stored method digests change even for settings identical to earlier runs. 7708d17
- **Warm-start validation:** infeasible starts consistently raise `OWNWarmupFailed` at the solver boundary; `MILPRouter` handles replacement or cold-solve fallback. 56e3ba6 c92d28e
- **Buffering helpers:** `buffer_border_obs()` returns only the location graph; `plot_org_buff()` is removed in favor of the standard plotting paths. 6ce61fc
- **Diagonal maps:** mesh diagonals use `utils.BiMap`; mutate the forward mapping, not its `inv` dictionary. Previously stored `bidict` mappings remain supported. bd2eff7
- **Native solver conflicts:** `solver_factory()` rejects loading incompatible native solver packages in one interpreter with `RuntimeError`. be72526

- **Mixed turbine powers:** `WindFarmNetwork` accepts nominal powers and cable capacities in an explicit unit, quantizes demands per solve with `power_rtol` (default 0.01), and exports network loads in nominal units. efa6d5b dd6d90a b627575
- **Power declarations and quantization:** `set_terminal_power()` stores exact nominal ratings; `quantize_for_capacity()` preserves which turbine combinations fit a cable. `calcload(nominal=True)` and `assign_cables()` support nominal loads and capacities. dd6d90a
- **Power import:** YAML supports turbine-model blocks or label prefixes; OSM reads `generator:output:electricity`. 05519b1
- **Demand-aware loads:** load calculation and topology conversion preserve terminal inflow and verify coverage independently of its magnitude. f1166ed 85bfaf3
- **Demand-aware MILP bounds:** flow bounds account for each terminal's inflow, tightening the model for unequal demands. 6d1b96f 17d2c20
- **Demand-aware baselines:** HGS and LKH pass supported nonunitary inflows as customer demands; LKH checks route loads when retrying capacity violations. 68bfd13 95db911
- **Exact ring counts:** balanced `hgs_cvrp(ringed=True, vehicles_exact=True)` pins the ring count, up to `T // 2`; `HGSRouter(feeder_exact=True)` exposes this through feeder counts. 7df0a6e
- **Broader MILP warm starts:** construction covers more balanced and feeder-limited models, derives counts from the model's bounds, and respects the heuristic's inflow support. 29ab4cd b6848b7 7df0a6e
- **Native HiGHS:** the `highspy` backend ranks saved improving solutions by routed length. `'pyomo.highs'` remains available, and `'pyomo.cbc'` explicitly names the existing `'cbc'` backend. c92d28e
- **CBCbox:** `'ortools.cbcbox'` builds with MathOpt and uses the CBC executable supplied by `cbcbox`, with warm-start support and DINS enabled by default. 7e3d9db b5f888b
- **Complete-graph baselines:** HGS and LKH consistently support complete solves, explicitly or through edgeless input graphs; mesh-based repair is rejected for these solves. d0c5e88
- **Canonical link bits:** candidate links have a shared order across producers; solutions carry a compact, solver-independent link representation. b781c12 56a2443
- **Solution identities:** `topology_id()` and `linkset_id()` identify topologies and their candidate-link sets; `SolutionInfo` exposes `topology_id`. 4f8e55b 2c5d8d2 1c8ee72
- **Topology reconstruction:** `S_from_linkbits()` decodes a solution against its available-links graph. 9152a5f
- **Solution summaries:** `SolutionInfo` has a column-aligned summary, and `SvgRepr` reports solution properties alongside configuration. 7140040 e125fde
- **Power-aware plots:** turbine symbols and legends distinguish ratings; `node_tag='power'` and `'load_nominal'` label nominal values. a143825
- **Tight SVG plots:** `tight=True` trims the viewBox; legends wrap to fit, and opaque backgrounds cover the final bounds. ddacdbb
- **Pre-buffer boundaries:** location plots overlay the original border and obstacles on their buffered versions. 6ce61fc

- **Warm-start recovery:** failed construction falls back to a cold MILP solve; SCIP permits repeated solves on an already-solved model. 56e3ba6
- **PathFinder robustness:** searches converge more reliably on open layouts and bend at collinear turbines instead of routing directly over them. c61a217
- **Straight feeders over turbines:** MILP models let a straight feeder pass over a turbine whose links stay on one side. a25dde4 9ccfdae
- **Ring geometry:** route decomposition covers each ring edge once, and crossing checks accept root closures and corridors shared by ring arms. c82c057
- **Validation:** topology and routeset checks cover stored loads, connectivity, capacity, orientation, and geometry, including self-intersections and branch splits. 6f1c125
- **Corridor foldbacks:** geometric validation accepts noncrossing retraces and detects crossings at shared-run exits more accurately. ad6fa98
- **Repair crossings:** conflict checks avoid false rejections, missed crossings, and `KeyError` on remapped diagonals. 822c5cb
- **Nearly straight quadrilaterals:** convexity tests reject spurious diagonals over collinear turbines. Rebuilt meshes can therefore omit links present in older stored routesets. 654360a
- **Rehooked loads:** `as_hooked_to_nearest()` clears stale loads on clone vertices before recalculation. d4bf19d
- **Solver options:** Gurobi and CPLEX instances no longer share mutable option dictionaries. 31fb1c2
- **OR-Tools HiGHS options:** options reach the correctly typed HiGHS parameter maps. 146c5e9
- **Verbose output:** HGSRouter forwards its verbose flag, OR-Tools log forwarding recognizes the backend family, and CBCbox prints output when requested. 6c4e778 56e3ba6 76a6ff1
- **Rootless SVG plots:** graphs containing only borders and obstacles can be drawn without substation-slice errors. 97be6b3

- **Multi-substation MILP models:** only each terminal's feeder to its closest substation, by contour-adjusted distance, remains available. c2c680f
- **MILP search:** flow-variable integrality is implied instead of branched on; CBC defaults are retuned for that formulation. db5a83a 9e043b0
- **Deferred topology decoding:** MILP searches retain incumbent link bits and decode topologies only when retrieving or ranking solutions. 531dcf0
- **Mesh construction:** triangulation uses CDT's NumPy array API, and contour detours use a lean A* search. 26014fe 8dfe558
- **Geometric tests:** root visibility uses a Numba kernel with Shapely fallback; quadrilateral tests avoid repeated NumPy indexing and dispatch. 9256daa c49317d
- **PathFinder inner loops:** list-based coordinates, direct adjacency access, and guarded debug calls reduce overhead. 4efefee f580ad1 a1b7c0b
- **Diagonal maps:** dict-based `BiMap` speeds map construction and crossing lookups. bd2eff7
- **Load calculation:** iterative traversal avoids graph-view overhead and recursion-depth limits. 8dd0763
- **API imports:** Matplotlib loads only when a Matplotlib plot is requested. 6ce61fc

- **Module organization:** `interarraylib` is split into `identity`, `loads`, `converting`, `transforming`, and `validating`, with text rendering in `presenting` and encodings in `terse`. Shared problem setup lives in the base `Solver`. 2c5d8d2 c123ed2 e4e8783
- **Path annotations:** YAML constructors explicitly accept `Path` as well as `str`, matching existing runtime behavior. 628faf1
- **Regression identities:** mesh and solver tests use canonical link-set and topology identities. ce5c9b6 97173ce
- **Sweep harness:** resumable A/B experiments record code state, metrics, and reconstructible solutions. bb3932d
- **Code checks:** linting, formatting, and type checking are enforced in CI, with declared tooling and stub dependencies. 9ce1a11 e63eb9b 9db83cf
- **Dependencies:** minimum versions rise to `condeltri 0.0.6` and `peewee 3.17.9`; `xxhash` supplies in-memory identities. 26014fe 76947c9 4f8e55b

- **Manual and guides:** reorganized navigation, paired API tutorials, and expanded solver, power, validation, and warm-start references. b310495 d066b24 c7e0b24
- **Notebook results:** stochastic examples specify seeds, solution summaries replace routine solver logs, and published outputs are refreshed. 6e5ccc4 9fe41cb