Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vane/io/openfast_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ def _unique_index(names: list[str], requested: str) -> int:
return matches[0]


def _require_zero(values: np.ndarray, atol: float, label: str) -> None:
def _require_zero(values: npt.NDArray[np.float64], atol: float, label: str) -> None:
if not np.all(np.abs(values) <= atol):
maximum = float(np.max(np.abs(values)))
msg = f"{label} is not zero within atol={atol:g} (max={maximum:g})"
Expand Down
7 changes: 5 additions & 2 deletions src/vane/modal/averaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def interval_weights(azimuths: npt.ArrayLike) -> npt.NDArray[np.float64]:
)
raise ValueError(msg)
sorted_weights = 0.5 * (np.roll(gaps, 1) + gaps) / _TWO_PI
weights = np.empty_like(sorted_weights)
weights: npt.NDArray[np.float64] = np.empty_like(sorted_weights)
weights[order] = sorted_weights
return weights

Expand Down Expand Up @@ -957,7 +957,10 @@ def azimuth_tangent(
stacklevel=2,
)
vector = basis.right[:, index] / basis.right[position, index]
return np.asarray(vector.real, dtype=np.float64)
tangent = np.array(vector.real, dtype=np.float64)
# z / z need not round to exactly one on every platform.
tangent[position] = 1.0
return tangent


def pin_azimuth(
Expand Down
2 changes: 1 addition & 1 deletion src/vane/modal/averaging_error_estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1571,7 +1571,7 @@ def azimuth_count_table(
basis = eigen_basis(model.mean)
labels = _resolve_branches(basis, branches)
tails: dict[str, AliasTail] = {}
scale = max(float(np.linalg.norm(model.mean)), np.finfo(np.float64).tiny)
scale = max(float(np.linalg.norm(model.mean)), float(np.finfo(np.float64).tiny))
if extrapolate:
for label, j in labels.items():
orders, sensitivity = _sensitivities(model, basis, j, b)
Expand Down
5 changes: 4 additions & 1 deletion src/vane/modal/floquet.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ def _fourier_coefficients(
coeffs: dict[int, npt.NDArray[np.complex128]] = {}
for n in range(-n_max, n_max + 1):
phase = np.exp(-1j * n * angles)
coeffs[n] = np.tensordot(phase, matrices, axes=(0, 0)) / n_azimuth
coeffs[n] = np.asarray(
np.tensordot(phase, matrices, axes=(0, 0)) / n_azimuth,
dtype=np.complex128,
)
return coeffs


Expand Down
4 changes: 3 additions & 1 deletion src/vane/modal/identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,9 @@ def _best_path(
parent: list[npt.NDArray[np.int_]] = []
for op in range(n_op):
start = np.where(used[op], _NEG_INF, 0.0)
link_parent = np.full(used[op].shape[0], -1, dtype=np.int_)
link_parent: npt.NDArray[np.int_] = np.full(
used[op].shape[0], -1, dtype=np.int_
)
if op > 0 and best[op - 1].size and start.size:
candidate = best[op - 1][:, np.newaxis] + affinities[op - 1]
from_prev = candidate.max(axis=0) if candidate.size else start
Expand Down
6 changes: 4 additions & 2 deletions src/vane/modal/labeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ def _number_tower_modes(
"""
frequencies = np.asarray(solution.natural_frequencies_hz, dtype=np.float64)
eigenvalues = np.asarray(solution.eigenvalues, dtype=np.complex128)
flags = np.zeros(len(labels), dtype=np.bool_)
flags: npt.NDArray[np.bool_] = np.zeros(len(labels), dtype=np.bool_)
if np.shape(solution.is_degenerate) == flags.shape:
flags = np.asarray(solution.is_degenerate, dtype=np.bool_)
if frequencies.shape != flags.shape or eigenvalues.shape != flags.shape:
Expand Down Expand Up @@ -1201,7 +1201,9 @@ def label_solution(
mbc_coordinates = solution.dof_mbc_coordinates
# A degenerate mode's eigenvector is an arbitrary rotation within its subspace,
# so its multi-blade (whirl) type is not defined; it gets no suffix.
degenerate = np.zeros(solution.mode_shapes.shape[1], dtype=np.bool_)
degenerate: npt.NDArray[np.bool_] = np.zeros(
solution.mode_shapes.shape[1], dtype=np.bool_
)
if np.shape(solution.is_degenerate) == degenerate.shape:
degenerate = np.asarray(solution.is_degenerate, dtype=np.bool_)
columns = [
Expand Down
6 changes: 4 additions & 2 deletions src/vane/run/acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,9 @@ def _component_indices(
return tuple(indices)


def _values(files: Sequence[LinFile], indices: tuple[int, ...]) -> np.ndarray:
def _values(
files: Sequence[LinFile], indices: tuple[int, ...]
) -> npt.NDArray[np.float64]:
"""Stack the selected state operating-point values by azimuth."""
if not indices:
return np.empty((len(files), 0), dtype=np.float64)
Expand All @@ -622,7 +624,7 @@ def _values(files: Sequence[LinFile], indices: tuple[int, ...]) -> np.ndarray:


def _vector_metrics(
values: np.ndarray,
values: npt.NDArray[np.float64],
weights: npt.NDArray[np.float64],
) -> tuple[tuple[float, ...] | None, float | None, float | None]:
"""Return component mean, norm of that mean, and maximum instantaneous norm."""
Expand Down
6 changes: 3 additions & 3 deletions src/vane/sysid/kalman_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class FilterResult:

def _as_available(
available: npt.NDArray[np.bool_] | None, n_outputs: int
) -> np.ndarray:
) -> npt.NDArray[np.bool_]:
"""Return a boolean output mask of length ``n_outputs`` (all-True if ``None``)."""
if available is None:
return np.ones(n_outputs, dtype=bool)
Expand Down Expand Up @@ -242,7 +242,7 @@ def __init__(
self.state_names = list(model.state_names)
n = self.a.shape[0]
if initial_state is None:
self.x = np.zeros(n, dtype=np.float64)
self.x: npt.NDArray[np.float64] = np.zeros(n, dtype=np.float64)
else:
self.x = np.asarray(initial_state, dtype=np.float64).ravel()
if self.x.shape != (n,):
Expand Down Expand Up @@ -648,7 +648,7 @@ def __init__(
)
raise ValueError(msg)
if initial_state is None:
self.x = np.zeros(n, dtype=np.float64)
self.x: npt.NDArray[np.float64] = np.zeros(n, dtype=np.float64)
else:
self.x = np.asarray(initial_state, dtype=np.float64).ravel()
if self.x.shape != (n,):
Expand Down
2 changes: 1 addition & 1 deletion src/vane/sysid/observability.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def observability_scores(
np.trace(_gramian_from_information(a, info_i, system.dt, horizon))
)

per_mode = np.empty(0, dtype=np.float64)
per_mode: npt.NDArray[np.float64] = np.empty(0, dtype=np.float64)
if mode_states is not None:
information = _output_information(c, measurement_noise)
gramian = _gramian_from_information(a, information, system.dt, horizon)
Expand Down
4 changes: 2 additions & 2 deletions src/vane/sysid/periodic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, cast

import numpy as np

Expand Down Expand Up @@ -131,7 +131,7 @@ def discretized(self, dt: float) -> PeriodicStateSpace:


def _shape(matrix: npt.NDArray[np.float64] | None) -> tuple[int, int] | None:
return None if matrix is None else matrix.shape
return None if matrix is None else cast("tuple[int, int]", matrix.shape)


def _blend(
Expand Down
3 changes: 2 additions & 1 deletion src/vane/sysid/physical_lpv.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ def _dropped_coupling(
chosen = set(x_idx)
dropped = [j for j in range(a.shape[0]) if j not in chosen]
a_ratio, a_cols = _relative_block_norms(a[list(x_idx), :], dropped)
c_ratio, c_cols = 0.0, np.zeros(len(dropped), dtype=np.float64)
c_ratio = 0.0
c_cols: npt.NDArray[np.float64] = np.zeros(len(dropped), dtype=np.float64)
if c is not None and y_idx:
c_ratio, c_cols = _relative_block_norms(c[list(y_idx), :], dropped)
shares = np.maximum(a_cols, c_cols)
Expand Down
4 changes: 2 additions & 2 deletions src/vane/sysid/rom_family.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ def build_rom_family(
if prev_residues is None:
# Reference: track the top-n_modes (modal_reduction ranks by residue).
order = list(range(n_modes))
mac_row = np.ones(n_modes, dtype=np.float64)
mac_row: npt.NDArray[np.float64] = np.ones(n_modes, dtype=np.float64)
else:
# Match ALL previously tracked modes jointly against the FULL candidate
# set by output-residue MAC with the frequency tie-breaker, so a tracked
Expand Down Expand Up @@ -710,7 +710,7 @@ def _transfer_eligible(
n_modes = system.n_states // 2
c_modal = c[:, 0::2] - 1j * c[:, 1::2] # (n_outputs, n_modes)
b_modal = b[0::2] + 1j * b[1::2] # (n_modes, n_inputs)
scores = np.zeros(n_modes, dtype=np.float64)
scores: npt.NDArray[np.float64] = np.zeros(n_modes, dtype=np.float64)
for output, input_channel in pairs:
row = _resolve_output(system, output)
column = _resolve_input(system, input_channel)
Expand Down
17 changes: 10 additions & 7 deletions src/vane/sysid/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import json
import os
from dataclasses import fields, is_dataclass
from typing import Any
from typing import TYPE_CHECKING, Any

import numpy as np

Expand All @@ -32,6 +32,9 @@
from vane.sysid.rom_family import RomFamily
from vane.sysid.state_space import StateSpace

if TYPE_CHECKING:
import numpy.typing as npt

__all__ = [
"LPV_FAMILY_SCHEMA_VERSION",
"load_lpv_family",
Expand Down Expand Up @@ -170,7 +173,7 @@ def save_result(result: _Result, path: str | os.PathLike[str]) -> None:
if type(result) not in (FilterResult, ScheduledFilterResult):
msg = f"save_result does not handle {name}"
raise TypeError(msg)
arrays: dict[str, np.ndarray] = {}
arrays: dict[str, npt.NDArray[Any]] = {}
meta_fields: dict[str, Any] = {}
for field in fields(result):
value = getattr(result, field.name)
Expand All @@ -186,7 +189,7 @@ def save_result(result: _Result, path: str | os.PathLike[str]) -> None:
}
# Store the metadata as a 0-d string array so the archive is a single, pickle-free
# npz (the arrays share the same file under their field names).
payload: dict[str, np.ndarray] = {"__meta__": np.asarray(json.dumps(meta))}
payload: dict[str, npt.NDArray[Any]] = {"__meta__": np.asarray(json.dumps(meta))}
payload.update(arrays)
# mypy reads a splatted dict as a possible ``allow_pickle=`` (bool) keyword; none of
# our keys is that reserved name, so the arrays are all stored as named arrays. The
Expand Down Expand Up @@ -381,7 +384,7 @@ def save_rom_family(family: RomFamily, path: str | os.PathLike[str]) -> None:
"""
_validate_rom_family(family)
systems_meta: list[dict[str, Any]] = []
payload: dict[str, np.ndarray] = {
payload: dict[str, npt.NDArray[Any]] = {
"operating_points": family.operating_points,
"frequencies_hz": family.frequencies_hz,
"damping_ratios": family.damping_ratios,
Expand Down Expand Up @@ -459,7 +462,7 @@ def load_rom_family(path: str | os.PathLike[str]) -> RomFamily:
raise ValueError(msg)
prefix = f"system_{index:04d}_"
required.add(prefix + "a")
matrices: dict[str, np.ndarray | None] = {}
matrices: dict[str, npt.NDArray[Any] | None] = {}
for name in ("b", "c", "d"):
present = presence[name]
if not isinstance(present, bool):
Expand Down Expand Up @@ -635,7 +638,7 @@ def save_lpv_family(family: LPVFamily, path: str | os.PathLike[str]) -> None:
layout (matrix shapes, names and sample time).
"""
_validate_lpv_family(family)
payload: dict[str, np.ndarray] = {
payload: dict[str, npt.NDArray[Any]] = {
"scheduling_points": family.scheduling_points,
"dropped_coupling_a": family.dropped_coupling_a,
"dropped_coupling_c": family.dropped_coupling_c,
Expand Down Expand Up @@ -719,7 +722,7 @@ def _load_lpv_system(
msg = f"LPV-family system {index} has invalid matrix metadata"
raise ValueError(msg)
prefix = f"system_{index:04d}_"
matrices: dict[str, np.ndarray | None] = {}
matrices: dict[str, npt.NDArray[Any] | None] = {}
for name in ("a", "b", "c", "d"):
present = True if name == "a" else presence[name]
if not isinstance(present, bool):
Expand Down
4 changes: 3 additions & 1 deletion src/vane/sysid/spectral_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ def frequency_response(
b = system.b[:, inputs]
c = system.c[outputs]
if system.d is None:
d = np.zeros((len(outputs), len(inputs)), dtype=np.float64)
d: npt.NDArray[np.float64] = np.zeros(
(len(outputs), len(inputs)), dtype=np.float64
)
else:
d = system.d[np.ix_(outputs, inputs)]
identity = np.eye(system.n_states)
Expand Down
8 changes: 4 additions & 4 deletions src/vane/viz/_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def plot_style() -> Iterator[None]:
... fig, ax = plt.subplots()
... lines = ax.plot([0.0, 1.0], [0.0, 1.0])
"""
with mpl.rc_context(rc=style_rc()):
with mpl.rc_context(rc=cast("dict[Any, Any]", style_rc())):
yield


Expand Down Expand Up @@ -316,7 +316,7 @@ def apply_style(target: Figure | Axes | go.Figure | None = None) -> None:
If ``target`` is none of the supported types.
"""
if target is None:
mpl.rcParams.update(style_rc())
mpl.rcParams.update(cast("dict[Any, Any]", style_rc()))
elif isinstance(target, go.Figure):
target.update_layout(template=resolve_template(None))
elif isinstance(target, Figure):
Expand Down Expand Up @@ -352,7 +352,7 @@ def static_style(
else:
stack.enter_context(matplotlib.style.context(style))
if size is not None:
stack.enter_context(mpl.rc_context(rc=size.rc()))
stack.enter_context(mpl.rc_context(rc=cast("dict[Any, Any]", size.rc())))
yield


Expand Down Expand Up @@ -521,7 +521,7 @@ def operating_range_label(parameter_name: str) -> str:

def rc_points(key: str) -> float:
"""Return a font-size ``rcParams`` entry in points (``"medium"`` is resolved)."""
return float(FontProperties(size=mpl.rcParams[key]).get_size())
return float(FontProperties(size=mpl.rcParams[cast("Any", key)]).get_size())


def text_color(color: str, shade: float = 0.8) -> str:
Expand Down
2 changes: 1 addition & 1 deletion src/vane/viz/qoi_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _heatmap_data(
if min_normalized <= 0.0:
# The documented escape hatch: keep every mode, including any that are
# completely dormant (zero contribution to every load).
keep = np.arange(matrix.shape[1])
keep: npt.NDArray[np.intp] = np.arange(matrix.shape[1])
else:
keep = np.flatnonzero(matrix.max(axis=0) > min_normalized)
if keep.size == 0:
Expand Down
3 changes: 3 additions & 0 deletions tests/test_run/test_auto_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,10 @@ def test_shared_servodyn_allowed_without_override(
"""Without an override ServoDyn is only read, so it may stay shared."""
from vane.run import auto_sweep as auto_module
from vane.run import driver as driver_module
from vane.run import floating_auto as floating_module

# The deck names a ``.dll``; make it a host library on every test platform.
monkeypatch.setattr(floating_module, "_host_library_suffix", lambda: ".dll")
shared = tmp_path / "shared"
shared.mkdir()
servo = _SERVO_DLL.replace(
Expand Down
Loading