Repository navigation
Conversation
- `_get_freq_interval()` built its frequency array as `np.linspace(df, fmax, num=half_n)`, so element k was (k + 1) * df, but `amplify_motion()` uses element k as the frequency of FFT bin k (bin 0 is 0 Hz). The transfer function was applied one bin too high. It now uses `np.arange(half_n) * df` (#78) - `fmax` is now the last element of that array (the highest FFT bin, i.e., the Nyquist frequency for an even length), instead of one bin above it. So with `extrap_tf=False`, a transfer function that reaches the Nyquist frequency no longer makes `amplify_motion()` downsample the motion - The amplification and phase plots of `amplify_motion()` skip 0 Hz, which a logarithmic axis cannot show; update the comments on the 0 Hz value - Add tests: a cosine at an FFT bin through a frequency-dependent transfer function (amplify and deconvolve), a transfer function up to the Nyquist frequency without `extrap_tf`, and the frequency array itself - `sim.linear()` and `linear_site_resp()` now agree to a correlation of 0.99999 (elastic) and 0.99986 (rigid), so raise the thresholds of the tests that compare them to 0.999 and fix the comment that blamed `sim.linear()` for the difference Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7fK8T2pPLf3tPCnMCKL1T
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7fK8T2pPLf3tPCnMCKL1T
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #78. One of the four P0 fixes from #114, based on #74's branch (
2026-10-10-parametrize-tests).Problem
_get_freq_interval()built its frequency array asnp.linspace(df, fmax, num=half_n), so element k was (k + 1)·df.amplify_motion()uses element k as the frequency of FFT bin k, where bin 0 is 0 Hz. So the transfer function was applied one bin too high.linear_site_resp(),Ground_Motion.amplify(),deconvolve(),amplify_by_tf()andSite_Effect_Adjustment.run().linear_site_resp()had correlation 0.99791 (elastic) and 0.96969 (rigid). With this fix it is 1.00000 and 0.99992.Changes
All in
PySeismoSoil/helper_site_response.py:_get_freq_interval():f_array = np.arange(half_n) * df, so element k is the frequency of FFT bin k. This is the fix proposed in the issue.fmax(beyond the issue): it is now(half_n - 1) * df, the last element off_array(the Nyquist frequency when n is even). Before, it was one bin above that.extrap_tf=False,amplify_motion()halves the input until the transfer function reachesfmax. With the old value, a transfer function covering exactly 0–50 Hz for dt = 0.01 s still halved the output's sampling rate, although the docstring says that is enough.extrap_tf=True, the extrapolated point now falls exactly on the last bin.tf_ss[0] = np.real(tf_ss[0])is still right. The comments there and "Note (1)" are updated to describe hownp.interp()fills 0 Hz.show_figplot: it now skips the 0 Hz point, which the logarithmic frequency axes cannot show.CHANGELOG.md: a "Fixed" entry.Tests
11 new or changed tests, all of which fail on the old code:
tests/test_helper_site_response.py:test_amplify_motion__cosine_at_an_FFT_bin[amplify|deconvolve × even|odd length]: a cosine at FFT bin 10 through |TF| = 1 + f (zero phase) comes out multiplied (or divided) by |TF| at its frequency. Old code: 2.1 instead of 2.0.test_amplify_motion__tf_up_to_the_Nyquist_frequency[even|odd]: withextrap_tf=False, a 0–50 Hz transfer function causes no downsampling. Old code:(500, 2) == (1000, 2)fails.test_get_freq_interval[even|odd]:f_arrayequalsnp.fft.rfftfreq(n, dt), andfmaxis its last value.tests/test_helper_simulations.py::test_linear[elastic|rigid]:sim.linear()for the difference is corrected.tests/test_class_simulation.py::test_linear: the threshold goes from 0.99 to 0.999 (0.998177 before, 0.999991 now).No benchmark values changed. The deconvolution round-trip tests passed before and still pass, because the shift cancels out there.
Testing
CI runs only on PRs into
main, so it does not run here. These were run locally on Python 3.13:python -m pytest tests: 249 passed.pre-commit run -aandpydoclint PySeismoSoil: pass.tox -e run-notebooks: all 14 notebooks ran without errors.Notes for review
Two related off-by-one problems were found while fixing this, and are not changed here:
helper_signal_processing.fourier_transform()labels FFT bin 0 (0 Hz) asdf(freq_array = np.arange(1, ...) / (N * dt)), so every frequency label is one bin too high. It feedsGround_Motion.get_Fourier_spectrum(),calc_transfer_function(),compare_two_accel()and the GoF scores.helper_simulations.linear()evaluates bin k at k·df + df/15, which is why it reaches only 0.99999 rather than exactly 1 against the exact solution.The other P0 fixes (#77 in #117; #79 in #116; #60 in #115) are in separate PRs on the same base and change different library code. All of them re-run the notebooks and add a "Fixed" group to
CHANGELOG.md, so whichever merges second will conflict there. To resolve: keep both entries and re-run the notebooks.Because the base is not
main, merging this PR will not close #78 automatically.🤖 Generated with Claude Code
https://claude.ai/code/session_01W7fK8T2pPLf3tPCnMCKL1T
Generated by Claude Code