Changelog
The changelog follows Keep a Changelog and the project adheres to Semantic Versioning. It is maintained at the repository root and reproduced below.
Changelog
All notable changes to VANE will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Changed
Public-story consistency: the README no longer labels implemented features as “planned”, describes the classifier’s uncertainty as indicative (not calibrated), and the 3-D mode-shape plot as a static phasor plot (not animated); the API contract documents the study/provenance entry points and uses “0.y.z alpha” stability wording; and the release checklist and security-scan policy are clarified. The Ruff target is aligned to
py310(the supported floor).The documentation’s Theory page is rewritten to a full mathematical treatment: linearized LTP dynamics, the multi-blade-coordinate transform and azimuth averaging (with the Coriolis/centrifugal term), eigenanalysis and Wilkinson conditioning, mode correlation (MAC/MACXP) and participation, the blade collective/regressive/progressive taxonomy and Coleman split, the global-path tracking objective, the azimuth-spread perturbation and unified confidence, and the state-space export — with numbered equations and a reference bibliography.
Study provenance is now audit-grade.
discover_operating_pointsreturnsDiscoveredOperatingPointobjects that retain the<case>grouping name (and behave as sequences of their files), and each entry inProvenance.operating_pointsrecords the case name, the full list of sampled azimuths, the averaged rotor speed and wind speed, the operating-parameter value, and that point’s own hashed source files. The manifest also records anenvironmentblock (Python, platform, and scientific-dependency versions).run_studyvalidatesparameter_namebefore doing any work.The learned classifier’s documentation no longer describes its probabilities as “calibrated”: because the default model is trained on synthetic priors, they are indicative until trained and calibrated on measured data.
The test suite is warning-clean: the intentional
ExperimentalWarningfrom the AI layer is filtered (by message, so coverage is unaffected) and remains asserted intest_experimental.py. The documentation build no longer suppresses alldocutilswarnings — the NumPy dtype reference targets (np.bool_,np.int_) are defined instead, so genuine reference issues are still surfaced under-W.
[0.1.0] - 2026-06-09
First public release: automated OpenFAST linearization post-processing with robust, uncertainty-aware mode identification, provenance-tracked study orchestration, and a digital-twin-ready state-space export.
Changed
Packaging hygiene: the source distribution now carries the full evidence base —
tests/,docs/,examples/, and the license/changelog/community files — via a newMANIFEST.in, so the validation claims can be reproduced from the released artifact. Project metadata uses the SPDXlicense = "Apache-2.0"withlicense-files, dropping the deprecated license table and classifier (build is now deprecation-warning-free).Resonance detection is now damping-aware: each
ResonanceCrossingreports the resonating mode’s interpolateddamping_ratioand aResonanceSeverity(HIGH/MEDIUM/LOW, since a lightly damped crossing is more dangerous than a well-damped one), andfind_resonancesaccepts anoperating_rangeto restrict crossings to a rotor-speed operating window.The
identify_modesdocstring now states the tracker’s optimality caveat precisely: each path is the globally optimal single continuity path, but the extraction is sequential, not a simultaneous globally optimal multi-track assignment (ambiguous hotspots are flagged).The learned AI layer is now explicitly experimental: constructing a
ModeClassifier(directly, or throughtrain_default_classifier, which builds one) emits avane.ai.ExperimentalWarning, because the default model is trained on synthetic priors rather than measured data. A model card documents its training data, intended use, and out-of-scope use; the rule-based label and deterministic blade taxonomy remain the primary, validated identification path.Documentation now matches the implementation: the README,
docs/index.md, and thevane.modal.macdocstring describe the deterministic global mode tracker (identify_modes, optimal continuity paths) instead of “Hungarian-algorithm mode tracking” (Hungarian remains a pairwisematch_modesbuilding block), and drop the deferred graph-/sequence-based classifiers. CI lint now installs from the package metadata (pip install -e ".[dev]") instead of a hand-picked package list.mbc3_transformnow validates that the azimuth files form one coherent operating point: duplicate azimuths (which bias the azimuth average) and a rotor speed that drifts across the sweep (which makes the averaged non-rotating model physically meaningless) are rejected with a clearValueError.Input validation is now enforced at the lower-level APIs, not only in
ModalPipeline:identify_modesrejects afrequency_weight,mac_threshold, orambiguity_marginoutside[0, 1](NaN included);excitation_frequenciesandfind_resonancesreject non-positive, fractional, non-finite, or duplicate harmonics (an empty set remains a valid no-op); andModalPipeline.runrejects non-finite or duplicate operating-parameter values, which would otherwise make the Campbell diagram and resonance interpolation ambiguous.read_lin_filenow validates each parsed file at its trust boundary: a non-finite operating-point scalar (rotor speed, azimuth, wind speed, simulation time) and a state-space matrix whose shape disagrees with the channel-table dimensions (A=(n_x,n_x),B=(n_x,n_u),C=(n_y,n_x),D=(n_y,n_u)) now fail loudly with a clearLinFileFormatErrorinstead of surfacing as an opaque error deep in the analysis. (Overflow markers in matrix data remain tolerated asnan.)
Added
Study orchestration with reproducibility provenance (
vane.study):discover_operating_pointsgroups a directory’s.linfiles into operating points,run_studyruns the pipeline and records aProvenancemanifest (source files with SHA-256 content hashes, per-operating-point azimuth coverage, tuning thresholds, harmonics, and the library version), andStudyResult.write_bundleserializes a reproducibility package (provenance.json+campbell.csv). The OpenFAST examples use the shared discovery helper instead of duplicating ad-hoc grouping logic.Data-free quickstart example (
examples/synthetic_quickstart.py): builds a system with prescribed modes, extracts and labels them, attaches a unified confidence, writes a CSV, and reports recovery accuracy and noise robustness — runnable without any OpenFAST data.Tabular export layer (
vane.export):modes_tableflattens a modal solution (optionally with physical labels, unified confidence, and azimuth spread) into a per-modepandas.DataFrame;campbell_tableflattens a Campbell diagram into a long-format table of tracked modes versus the operating parameter; andwrite_tablewrites either to CSV, JSON, or Excel inferred from the file suffix (Excel via the optionalopenpyxl). Lets downstream tools consume the results directly.Ground-truth accuracy and robustness benchmark (
vane.benchmark):synthetic_systembuilds a classically damped system with prescribed natural frequencies, damping ratios, and mode shapes (exact known answer);score_recoveryscores an extracted solution against it (frequency/damping error and MAC, matched by Hungarian assignment); androbustness_curvemeasures error growth under increasing additive noise. The extraction recovers clean systems to machine precision and degrades gracefully — quantitative evidence neither reference tool provides.Unified mode uncertainty quantification (
vane.modal.uncertainty): a novel azimuth-spread estimate (azimuth_spread, requiringmbc3_transform(retain_per_azimuth=True)) measures each averaged mode’s first-order eigenvalue perturbation across the per-azimuth matrices — ~0 for an isotropic rotor, growing with anisotropy — andunified_mode_confidencefuses it with the eigenvalue conditioning, degeneracy, and tracking confidence into one calibrated per-mode confidence that feeds the Kalman covariance export. No uncertainty quantification exists in the reference tools.Global deterministic mode tracker (
vane.modal.identify_modes): mode lines are now extracted as the globally optimal continuity path through the whole sweep (successive optimal-path dynamic programming over the MAC / frequency-continuity affinity) instead of a greedy adjacent match. Deterministic — no random restarts or preset cluster counts — and eachModeTrackcarries aconfidence(its weakest-link MAC) and anis_ambiguousflag (raised at crossing/veering hotspots where a mode had two near-equally correlated continuations).Deterministic blade multi-blade taxonomy (
vane.modal.labeler): blade modes are sub-classified as collective / regressive / progressive from the MBC multi-blade-coordinate structure (each transformed triplet is[collective, cosine, sine]) and the phase-invariant whirl signIm(q_s * conj(q_c))— template-free and general. The MBC transform now annotates each state’s multi-blade coordinate (MBCResult.mbc_coordinates), carried through toModalSolution.dof_mbc_coordinatesand surfaced asModeLabel.multiblade.Robust eigen-core (
vane.modal.eigensolver): via a left/right-eigenvector analysis, each mode now reports its eigenvalue condition number (numerical sensitivity; 1 = perfectly conditioned, large = fragile/non-normal) and a near-degeneracy flag (shape defined only up to a subspace rotation), and the solution reports the unstable and over-damped eigenvalue counts. Numbers the rivals do not provide.High-level pipeline (
vane.ModalPipeline):ModalPipeline.runexecutes the full workflow over a set of operating points (MBC3 → modes → cross-operating-point tracking → Campbell diagram → resonance detection), against rotor speed or wind speed.PipelineResultexposes every intermediate product and astate_spaceexport;ModalPipelineandPipelineResultare re-exported from the package root.examples/show end-to-end use for the NREL 5 MW land turbine and the OC3 spar and OC4 semi-submersible floating cases (including a UQ-informed Kalman model export).
Visualization layer (
vane.viz):plot_campbellrenders an interactive Campbell diagram withnPexcitation lines and resonance markers;plot_dampingplots damping versus the operating parameter.plot_mode_shape(Matplotlib) shows the signed participation of a mode’s most active DOFs;plot_mode_3d(Plotly) renders the mode’s complex components as 3-D phasors.
System-identification layer (
vane.sysid):StateSpace/state_space_from_mbcexport a validated continuous-time LTI system from the averaged MBC matrices, with zero-order-hold discretization and a stability check.modal_state_spacerealizes a real, block-diagonal modal state-space model from the identified modes.covariances_from_confidence/initialize_covariancesbuild the KalmanQ,R,P0matrices, propagating classification uncertainty intoP0.build_kalman_modelassembles a dimension-checked, optionally discretized Kalman-ready model.
Campbell layer (
vane.campbell):build_campbell/campbell_from_solutionsassemble aCampbellDiagramof natural frequency and damping versus an operating parameter (rotor speed or wind speed) from the identified mode tracks.excitation_frequenciesandfind_resonancescompute thenPexcitation lines and flag mode/nPresonance crossings by interpolation.
AI layer (
vane.ai), torch-free core:solution_features/mode_feature_vectorreduce each mode to a scale-independent feature vector (frequency, damping, per-category participation fractions).ModeClassifieris a Gaussian-process mode classifier (scikit-learn) that predicts a DOF category with a calibrated confidence for uncertainty quantification, withsave/load.train_default_classifier/synthetic_training_setbootstrap the classifier on synthetic labeled modes until measured data is available.ensemble_solution/fuse_labelfuse the rule-based and learned labels, reinforcing agreement and discounting conflicts.
Mode identification:
vane.config.classify_dofmaps an OpenFAST state description onto a physical DOF category, module, and blade index using the embeddedDOF_*index token.vane.modal.label_mode/label_modes/label_solutionassign a physical label to each mode from its dominant participation, with a confidence that flags ambiguous modes.vane.modal.identify_modeslinks modes across operating points into labeled tracks (Campbell-diagram lines) via a MAC- and frequency-continuity-based Hungarian assignment, tracking through frequency crossings by mode shape.
Modal layer (
vane.modal):compute_modes/modes_from_mbcperform the eigenvalue analysis of the azimuth-averaged state matrix, returning natural frequencies, damping ratios, damped frequencies, and phase-normalized complex mode shapes, with conjugate-pair collapse and rigid-body-mode counting.compute_mac,compute_macxp(pole-weighted), andmatch_modes(Hungarian assignment with an optional frequency penalty) provide mode correlation and cross-operating-point matching.compute_participationquantifies per-degree-of-freedom participation from the mode shapes, with dominant-component normalization and a phase-based sign.
I/O layer (
vane.io):read_lin_fileparses modern OpenFAST (ModGlue).linlinearization files into a typedLinFile(state/input/output operating-point tables plus theA,B,C,Dmatrices), handling orientation rows, overflow markers, and absent matrix blocks; legacy-format files are rejected with a clear error.read_fst_fileandread_elastodyn_geometryextract the linearization configuration, active-module switches, referenced sub-files, and rotor/tower geometry from a primary.fstdeck and its ElastoDyn input.mbc3_transformapplies the multi-blade-coordinate (MBC3) transform to theA,B,C, andDmatrices and azimuth-averages them into the non-rotating frame, with automatic blade-triplet detection.
Test fixtures for synthetic modern
.lingeneration and optional integration against a local OpenFASTr-testclone.Initial repository scaffolding:
src-layout package skeleton, subpackage stubs forio,modal,ai,campbell,viz,sysid,config, andutils.Build, lint, type-check, and test configuration:
pyproject.toml,.ruff.toml,mypy --strictsettings, andpytestdefaults with coverage gate (≥ 85%).GitHub Actions CI workflow covering lint, type check, multi-version tests (Python 3.10–3.12), and an automated PR review summary comment.
Pre-commit hooks for ruff, formatting, basic file hygiene, and mypy.
Pull request template and contributor checklist aligned with the project workflow.
Tests skeleton with
conftest.pyand a smoke test for package import and version exposure.