Skip to content

Fix one-bin frequency shift in get_response (fixes #3) - #5

Closed
jankoslavic wants to merge 1 commit into
mainfrom
fix/get_response-freq-vector
Closed

Fix one-bin frequency shift in get_response (fixes #3)#5
jankoslavic wants to merge 1 commit into
mainfrom
fix/get_response-freq-vector

Conversation

@jankoslavic

@jankoslavic jankoslavic commented Jul 27, 2026

Copy link
Copy Markdown

Summary

Fixes #3get_response(domain="f") synthesised responses using an FRF shifted by one frequency bin relative to get_FRF_matrix.

Root cause

get_response built its frequency vector as

len_freq = exc.shape[1] // 2 + 1
freq = np.arange(1, len_freq+1, 1) * (sampling_rate / exc.shape[1])  # avoid zero frequency

i.e. [1·df, 2·df, …], starting at df. But EXC = np.fft.rfft(exc) has its bins at [0, df, 2·df, …]. The response loop then multiplies matrix[..., i] (the FRF at (i+1)·df) by EXC[:, i] (the spectrum at i·df), so the synthesised transfer function is shifted up by one bin: H_used[k] == get_FRF_matrix[k+1].

Fix

Use the aligned rfft grid, which includes the 0 Hz (rigid-body / static) term:

freq = np.fft.rfftfreq(exc.shape[1], 1/sampling_rate)

The 0 Hz FRF is kept where it is defined (grounded systems → inv(K)) and left at zero where the impedance matrix is singular (unconstrained / free systems, frf_method="f"), so get_response never fails at 0 Hz while reproducing get_FRF_matrix at every other bin:

frf_matrix = np.zeros((self.n_dof, self.n_dof, freq.shape[0]), dtype="complex128")
frf_matrix[:, :, 1:] = self.get_FRF_matrix(freq[1:], ...)
try:
    frf_matrix[:, :, :1] = self.get_FRF_matrix(freq[:1], ...)   # 0 Hz rigid-body term
except np.linalg.LinAlgError:
    pass                                                        # singular -> left at 0

The "f" and "t" domains share this handling.

Verification

before:  get_FRF_matrix vs get_response FRF, full band  max|Δ| = 2.6e-3   (one-bin shift)
after :  get_FRF_matrix vs get_response FRF, full band  max|Δ| = 0.0      (0 Hz included)
  • Grounded systems: get_response(return_matrix=True) equals get_FRF_matrix at every bin, DC included.
  • No crashes at 0 Hz for any boundary condition — both/left/right/free, both frf_methods, both domains all run and stay finite (previously free + "f" raised LinAlgError at DC).
  • domain="f" and domain="t" responses agree.
  • pyLump test suite: 4 passed. test_response now aligns its reference grid and excludes the 0 Hz bin from the comparison (by removing the time-domain mean, which zeros exactly the DC bin).

Found while adding pyLump-based cross-validation tests to pyFRF.

get_response built its frequency vector as `arange(1, len_freq+1) * df`
(starting at df to "avoid zero frequency"), offset by one bin from the rfft
bins of the excitation. Multiplying `H(freq[i])` by `rfft(exc)[i]` therefore
paired the FRF at (i+1)*df with the spectrum at i*df, shifting the synthesised
FRF by one bin relative to get_FRF_matrix.

Use the aligned grid `np.fft.rfftfreq(N, 1/fs)`, which includes the 0 Hz
(rigid-body / static) term. The 0 Hz FRF is computed where it is defined and
left at zero where the impedance matrix is singular (unconstrained systems), so
get_response never fails at 0 Hz while still reproducing get_FRF_matrix at every
other bin. The 'f' and 't' domains share the same frequency handling.

test_response is updated to align its reference grid and to exclude the 0 Hz bin
from the comparison (by removing the time-domain mean).
@jankoslavic
jankoslavic force-pushed the fix/get_response-freq-vector branch from e38c710 to c852829 Compare July 27, 2026 19:01
@jankoslavic

Copy link
Copy Markdown
Author

Closing for now — nothing actually depends on this. The pyFRF tests that surfaced it compare against get_response(return_matrix=True) (the exact FRF used to synthesise the data), so they pass with or without this change. The underlying one-bin inconsistency between get_response and get_FRF_matrix is left documented in #3 for whenever it's worth addressing.

@jankoslavic
jankoslavic deleted the fix/get_response-freq-vector branch July 27, 2026 19:10
@jankoslavic

Copy link
Copy Markdown
Author

This does not make sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_response FRF is shifted by one frequency bin relative to get_FRF_matrix

1 participant