Skip to content

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

Closed
jankoslavic wants to merge 1 commit into
mainfrom
fix/get_response-frequency-alignment
Closed

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

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, and compute the FRF at every bin — this is the previously commented-out # old line:

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

get_response now reproduces get_FRF_matrix bin-for-bin, DC included.

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)
  • get_response(return_matrix=True) now equals get_FRF_matrix at every bin, including DC.
  • domain="f" and domain="t" responses agree.
  • pyLump test suite: 4 passed (test_response updated — it previously encoded the shifted grid, minus 0 Hz, in its reference).

Note on unconstrained ("free") systems at 0 Hz

Because 0 Hz is now included, get_response/get_FRF_matrix behave identically at DC — which for a free system means the impedance-inverse method (frf_method="f") raises LinAlgError there (the stiffness matrix is singular for the rigid-body mode; H(0) is theoretically infinite). The state-space method (frf_method="s") computes it fine. This matches get_FRF_matrix's existing behaviour, so the two are consistent. If you'd like the "f" method to also return something usable at 0 Hz for unconstrained systems (e.g. via a pseudo-inverse), that would be a separate change to get_FRF_matrix — happy to follow up.

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, and compute the FRF at every bin. get_response now
reproduces get_FRF_matrix bin-for-bin, including DC.

Updates test_response, which previously encoded the shifted grid in its reference.
@jankoslavic
jankoslavic force-pushed the fix/get_response-frequency-alignment branch from 23fceb7 to ae49591 Compare July 27, 2026 18:47
@jankoslavic

Copy link
Copy Markdown
Author

Superseded by #5, which contains the same reviewed fix (aligned rfftfreq grid including 0 Hz) from a clean branch.

@jankoslavic
jankoslavic deleted the fix/get_response-frequency-alignment branch July 27, 2026 18:50
@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