pyserep.selection
pyserep.selection — frequency bands, mode selection, DOF selection.
- class pyserep.selection.FrequencyBand(f_min: float, f_max: float, label: str | None = None, n_points: int | None = None)[source]
Bases:
objectA single contiguous analysis band [f_min, f_max] Hz.
- Parameters:
Examples
>>> band = FrequencyBand(0, 100, label="LowBand") >>> band.contains(50) True >>> band.span 100.0
- expanded(alpha: float) FrequencyBand[source]
Return a new band with f_max scaled by alpha (MS1 safety factor).
- class pyserep.selection.FrequencyBandSet(bands: Sequence[FrequencyBand], n_points_per_band: int = 2000)[source]
Bases:
objectContainer for a collection of FrequencyBand objects.
Manages the multi-band frequency grid, mode relevance checks, and band-weighted Modal Participation Factor computation.
- Parameters:
bands (sequence of FrequencyBand)
n_points_per_band (int) – Default frequency evaluation points per band.
Examples
>>> bset = FrequencyBandSet([FrequencyBand(0, 100), FrequencyBand(400, 500)]) >>> bset.n_bands 2 >>> len(bset.frequency_grid()) 4000
- band_weighted_mpf(phi_f: ndarray, phi_o: ndarray, omega_n: ndarray, band: FrequencyBand) ndarray[source]
Band-weighted Modal Participation Factor for all modes.
C_i = |phi_f_i * phi_o_i| * max_ω_in_band(1 / |ωᵢ² − ω²|)
- Parameters:
phi_f ((n_modes,))
phi_o ((n_modes,))
omega_n ((n_modes,))
band (FrequencyBand)
- Return type:
(n_modes,) band-weighted MPF
- property bands: List[FrequencyBand]
Return a copy of the band list, sorted by f_min.
- frequency_grid() ndarray[source]
Build the evaluation frequency array (Hz) as the union of all bands.
- Return type:
np.ndarray, sorted and deduplicated.
- frequency_mask(freqs_hz: ndarray) ndarray[source]
Boolean mask: True where freqs_hz falls inside any band.
- pyserep.selection.compare_dof_selectors(phi: ndarray, selected_modes: ndarray, verbose: bool = True) dict[source]
Run all four DOF selectors and return a comparison table.
- pyserep.selection.mac_filter(phi: ndarray, candidate_indices: ndarray, freqs_hz: ndarray, force_dofs: List[int], output_dofs: List[int], mac_threshold: float = 0.9, verbose: bool = True) ndarray[source]
Remove spatially redundant modes from candidate_indices.
Two modes are considered duplicates if their MAC exceeds mac_threshold. The mode with the lower participation score is discarded.
- pyserep.selection.ms1_frequency_range(freqs_hz: ndarray, band_set: FrequencyBandSet, rb_hz: float = 1.0, ms1_alpha: float = 1.5, verbose: bool = True) ndarray[source]
MS1: Retain modes whose natural frequency is relevant to at least one band.
- A mode at frequency f passes MS1 if:
f > rb_hz (not rigid-body)
f ≤ alpha × max(band.f_max) for at least one band
- pyserep.selection.ms2_participation_factor(phi: ndarray, freqs_hz: ndarray, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet, rb_hz: float = 1.0, threshold_pct: float = 1.0, verbose: bool = True) ndarray[source]
MS2: Retain modes with significant band-weighted Modal Participation Factor.
- For each band B and DOF pair (f, o), a mode passes MS2 if:
C_i_B / C_dom_B ≥ threshold_pct / 100
where C_dom_B is the dominant elastic mode’s MPF within band B.
- pyserep.selection.ms3_spatial_amplitude(phi: ndarray, freqs_hz: ndarray, target_dofs: List[int], rb_hz: float = 1.0, threshold_pct: float = 5.0, verbose: bool = True) ndarray[source]
MS3: Retain modes with significant spatial amplitude at target DOFs.
This catches modes near nodal lines that MS2 might miss.
- pyserep.selection.ms4_conditioning_check(phi: ndarray, mode_indices: ndarray, master_dofs: ndarray, verbose: bool = True) float[source]
MS4: Compute κ(Φₐ) to verify the master DOF set is well conditioned.
- pyserep.selection.select_dofs_eid(phi: ndarray, selected_modes: ndarray, n_master: int | None = None, candidate_dofs: ndarray | None = None, ke_prescreen_frac: float = 0.5, required_dofs: ndarray | None = None, verbose: bool = True) Tuple[ndarray, float][source]
DS4: Select master DOFs using the Effective Independence (EID) method.
The algorithm iteratively removes the DOF with the smallest diagonal entry of the EI matrix:
E = Φₛ (ΦₛᵀΦₛ)⁻¹ Φₛᵀ
which corresponds to the DOF contributing the least to the linear independence of the mode shape partition. The Fisher Information Matrix det(ΦₛᵀΦₛ) is thereby maximised.
- Parameters:
phi (np.ndarray, shape (N, n_all_modes)) – Full modal matrix (mass-normalised).
selected_modes (np.ndarray of int) – Mode indices from the mode selection pipeline.
n_master (int, optional) – Number of master DOFs. Default:
len(selected_modes)(enforces the exact-SEREP rule a = m).candidate_dofs (np.ndarray of int, optional) – Pre-screened candidate DOF set. If None, a kinetic-energy pre-screen retains the top ke_prescreen_frac fraction.
ke_prescreen_frac (float) – Fraction to retain in the KE pre-screen (0 < f ≤ 1).
verbose (bool)
- Returns:
master_dofs (np.ndarray of int) – Selected master DOF indices.
kappa (float) – Condition number κ(Φₐ) of the final master partition.
Examples
>>> dofs, kappa = select_dofs_eid(phi, selected_modes) >>> kappa < 100 True
- pyserep.selection.select_dofs_kinetic(phi: ndarray, selected_modes: ndarray, n_master: int | None = None, verbose: bool = True) Tuple[ndarray, float][source]
DS1: Select master DOFs by total kinetic energy across selected modes.
Score for DOF j: KE_j = Σᵢ |φᵢ(j)|²
Selects the top-n_master DOFs by descending KE score. Fast (O(N·m)) but typically produces κ ~ 10¹⁰–10¹⁵, which is unsuitable for SEREP unless used as a pre-screen for DS4.
- Parameters:
phi (see
select_dofs_eid())selected_modes (see
select_dofs_eid())n_master (int, optional)
- Return type:
- pyserep.selection.select_dofs_modal_disp(phi: ndarray, selected_modes: ndarray, n_master: int | None = None, verbose: bool = True) Tuple[ndarray, float][source]
DS2: Select master DOFs by maximum modal displacement magnitude.
Score for DOF j: S_j = max_i |φᵢ(j)|
Retains DOFs where at least one mode has large amplitude.
- Parameters:
phi (see
select_dofs_eid())selected_modes (see
select_dofs_eid())n_master (int, optional)
- Return type:
- pyserep.selection.select_dofs_svd(phi: ndarray, selected_modes: ndarray, n_master: int | None = None, verbose: bool = True) Tuple[ndarray, float][source]
DS3: Select master DOFs via QR factorisation with column pivoting of Φᵀ.
The column permutation from
scipy.linalg.qr(Φᵀ, pivoting=True)directly gives the DOF ordering from most to least informative.This is equivalent to maximising the diagonal entries of R and gives κ values typically in the range 10³–10⁶ — better than DS1/DS2 but still inferior to DS4 for SEREP.
- Parameters:
phi (see
select_dofs_eid())selected_modes (see
select_dofs_eid())n_master (int, optional)
- Return type:
- pyserep.selection.select_modes(phi: ndarray, freqs_hz: ndarray, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet | None = None, f_max: float = 500.0, f_min: float = 0.1, rb_hz: float = 1.0, ms1_alpha: float = 1.5, ms2_threshold: float = 1.0, ms3_threshold: float = 5.0, mac_threshold: float = 0.9, verbose: bool = True) ndarray[source]
Run the complete mode selection pipeline.
Convenience wrapper that accepts either a
FrequencyBandSetor plainf_min/f_maxscalars for single-band analysis.- Parameters:
phi (np.ndarray, shape (N, n_modes)) – Mass-normalised modal matrix.
freqs_hz (np.ndarray, shape (n_modes,)) – Natural frequencies in Hz.
band_set (FrequencyBandSet, optional) – Multi-band specification. If None, a single band
[f_min, f_max]is created.f_max (float) – Upper frequency limit (Hz) when band_set is None.
f_min (float) – Lower frequency limit (Hz) when band_set is None.
rb_hz (float) – Rigid-body exclusion threshold (Hz).
ms1_alpha (float) – MS1 safety factor: retains modes up to
alpha × max(f_max).ms2_threshold (float) – MS2 band-weighted MPF threshold (% of dominant mode).
ms3_threshold (float) – MS3 spatial amplitude threshold (% of global peak).
mac_threshold (float) – MAC duplicate removal threshold.
verbose (bool)
- Returns:
Selected mode indices, sorted ascending.
- Return type:
np.ndarray of int
- pyserep.selection.select_modes_pipeline(phi: ndarray, freqs_hz: ndarray, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet, rb_hz: float = 1.0, ms1_alpha: float = 1.5, ms2_threshold: float = 1.0, ms3_threshold: float = 5.0, mac_threshold: float = 0.9, verbose: bool = True) ndarray[source]
Run the complete mode selection pipeline.
S_final = (MS1 filtered by MAC) ∪ MS2 ∪ MS3
- Parameters:
- Return type:
np.ndarray of int — final selected mode indices, sorted ascending