pyserep.frf

pyserep.frf — FRF computation (direct and modal).

class pyserep.frf.FRFResult(freqs_hz: ~numpy.ndarray, H_rom: ~typing.Dict[str, ~numpy.ndarray], H_ref: ~typing.Dict[str, ~numpy.ndarray], band_masks: ~typing.Dict[str, ~numpy.ndarray], method: str = 'direct', errors: ~typing.Dict[str, ~typing.Dict] = <factory>)[source]

Bases: object

Container for FRF results from both ROM and reference models.

freqs_hz

Evaluation frequencies (Hz). May be non-uniform if selective bands are used.

Type:

np.ndarray

H_rom

ROM FRFs. Key "f{i}_o{j}" → complex array of shape (n_freq,).

Type:

dict

H_ref

Reference FRFs (same structure as H_rom).

Type:

dict

band_masks

Boolean mask for each band, mapping band label → (n_freq,) bool array.

Type:

dict

errors

Per-pair error metrics computed at construction time. Each entry is {"max_pct": float, "rms_pct": float}.

Type:

dict

method

FRF computation method: "direct" or "modal".

Type:

str

H_ref: Dict[str, ndarray]
H_rom: Dict[str, ndarray]
band_masks: Dict[str, ndarray]
errors: Dict[str, Dict]
freqs_hz: ndarray
method: str = 'direct'
summary() str[source]

Return a formatted string summarising FRF method and per-pair errors.

pyserep.frf.compute_frf_direct(Ka: ndarray, Ma: ndarray, force_dof_indices: List[int], output_dof_indices: List[int], freq_eval: ndarray | List[float], zeta: float = 0.001, Ca: ndarray | None = None, damping_type: str = 'modal', eta: float = 0.0, verbose: bool = True) Tuple[ndarray, Dict[str, ndarray]][source]

Compute FRF of the SEREP ROM using direct impedance inversion.

At each frequency ω, solve: Z(ω) H_col = I_{force_col}

where Z(ω) = Kₐ - ω²Mₐ + jωCₐ (or Kₐ(1+jη) - ω²Mₐ for hysteretic).

Parameters:
  • Ka (np.ndarray, shape (m, m)) – Reduced stiffness matrix from SEREP.

  • Ma (np.ndarray, shape (m, m)) – Reduced mass matrix from SEREP.

  • force_dof_indices (list of int) – Indices of force (input) DOFs within the master DOF set (0-based, 0..m-1).

  • output_dof_indices (list of int) – Indices of response (output) DOFs within the master DOF set. Must be the same length as force_dof_indices.

  • freq_eval (array-like) – Evaluation frequencies in Hz.

  • zeta (float) – Uniform modal damping ratio (used only for damping_type="modal" and damping_type="rayleigh").

  • Ca (np.ndarray, shape (m, m), optional) – User-supplied damping matrix. Overrides zeta and damping_type.

  • damping_type (str) – One of: * "modal" — Ca built from modal damping ratios * "rayleigh" — Ca = α Ma + β Ka * "hysteretic"— structural damping: Ka(1+jη)−ω²Ma * "none" — undamped

  • eta (float) – Structural damping loss factor (only for damping_type="hysteretic").

  • verbose (bool)

Returns:

  • freq_eval (np.ndarray) – Evaluation frequencies in Hz.

  • H (dict) – FRF arrays keyed by "f{fi}_o{oi}". Values are complex arrays of shape (n_freq,).

Notes

This function uses LU factorisation reuse (scipy.linalg.lu_factor) only when the frequency loop is over simple viscous damping. For hysteretic or arbitrary Ca the system matrix changes with every ω so each step requires a fresh solve; but the (m × m) system is small, making direct LU fast regardless.

Examples

>>> freqs, H = compute_frf_direct(Ka, Ma, [5], [5], np.arange(1, 501))
>>> H["f5_o5"].shape
(500,)
pyserep.frf.compute_frf_direct_fullmodel(K: csc_matrix, M: csc_matrix, master_dofs: ndarray, force_dof_global: List[int], output_dof_global: List[int], freq_eval: ndarray | List[float], zeta: float = 0.001, verbose: bool = True) Tuple[ndarray, Dict[str, ndarray]][source]

Compute reference FRF directly from the full-order physical matrices.

This is the true ground truth — no modal truncation, no approximation. Uses sparse LU factorisation at each frequency step.

WARNING: This function is computationally expensive for large systems (N > 10,000 DOFs × many frequencies). For large models, the modal reference (compute_frf_modal_reference()) with all elastic modes is a practical alternative.

Parameters:
  • K (scipy.sparse.csc_matrix) – Full stiffness matrix.

  • M (scipy.sparse.csc_matrix) – Full mass matrix.

  • master_dofs (np.ndarray of int) – Global DOF indices corresponding to the ROM master DOFs. Used to determine the force/output DOF local indices.

  • force_dof_global (list of int) – Force DOF global indices (into the N-DOF full model).

  • output_dof_global (list of int) – Response DOF global indices.

  • freq_eval (array-like) – Evaluation frequencies in Hz.

  • zeta (float) – Uniform modal damping ratio (Rayleigh-type applied to full model).

  • verbose (bool)

Returns:

  • freq_eval (np.ndarray)

  • H_ref (dict)

pyserep.frf.compute_frf_modal(phi: ndarray, freqs_hz: ndarray, mode_indices: ndarray, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet, zeta: float = 0.001, per_mode_zeta: ndarray | None = None, verbose: bool = True) Tuple[ndarray, Dict[str, ndarray]][source]

Compute FRF via modal superposition, evaluated only within band regions.

Parameters:
  • phi (np.ndarray, shape (N, n_modes)) – Full modal matrix (mass-normalised).

  • freqs_hz (np.ndarray, shape (n_modes,)) – Natural frequencies in Hz.

  • mode_indices (np.ndarray of int) – Indices of modes to include in the superposition.

  • force_dofs (list of int) – Force DOF global indices.

  • output_dofs (list of int) – Output DOF global indices.

  • band_set (FrequencyBandSet) – Defines the frequency evaluation grid.

  • zeta (float) – Uniform damping ratio (used when per_mode_zeta is None).

  • per_mode_zeta (np.ndarray, optional) – Per-mode damping ratios, shape (n_modes,). Overrides zeta.

  • verbose (bool)

Returns:

  • freq_eval (np.ndarray) – Evaluation frequencies (Hz).

  • H (dict) – FRF arrays keyed by "f{fi}_o{oi}".

pyserep.frf.compute_frf_modal_reference(phi: ndarray, freqs_hz: ndarray, rb_hz: float, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet, zeta: float = 0.001, verbose: bool = True) Dict[str, ndarray][source]

Compute reference FRF using ALL elastic modes of the full model.

This is used as the ground truth for FRF accuracy assessment.

Parameters:
  • phi (full modal matrix and frequencies)

  • freqs_hz (full modal matrix and frequencies)

  • rb_hz (float) – Rigid-body exclusion threshold in Hz.

  • force_dofs (list of int)

  • output_dofs (list of int)

  • band_set (FrequencyBandSet)

  • zeta (float)

  • verbose (bool)

Returns:

H_ref

Return type:

dict

pyserep.frf.compute_frf_pair_direct(Ka: ndarray, Ma: ndarray, phi: ndarray, freqs_hz_all: ndarray, selected_modes: ndarray, master_dofs: ndarray, force_dofs: List[int], output_dofs: List[int], band_set: FrequencyBandSet, zeta: float = 0.001, damping_type: str = 'modal', rb_hz: float = 1.0, verbose: bool = True) FRFResult[source]

Compute both ROM (direct) and reference (modal, all elastic modes) FRFs.

The reference uses all elastic modes via modal superposition — this is sufficiently accurate for large models where the direct full-model solve would be prohibitively expensive.

Parameters:
  • Ka (np.ndarray, shape (m, m)) – Reduced matrices from SEREP.

  • Ma (np.ndarray, shape (m, m)) – Reduced matrices from SEREP.

  • phi (np.ndarray, shape (N, n_modes)) – Full modal matrix.

  • freqs_hz_all (np.ndarray) – All full-model natural frequencies.

  • selected_modes (np.ndarray of int)

  • master_dofs (np.ndarray of int)

  • force_dofs (list of int) – Global DOF indices.

  • output_dofs (list of int) – Global DOF indices.

  • band_set (FrequencyBandSet)

  • zeta (see compute_frf_direct())

  • damping_type (see compute_frf_direct())

  • rb_hz (see compute_frf_direct())

  • verbose (bool)

Return type:

FRFResult