Repository navigation
Fix the row order of H4 parameter files in MKZ_Param_Multi_Layer - #116
Merged
Merged
Conversation
Test functions that ran the same code with different inputs and expected results are now one test with `pytest.mark.parametrize`, in 15 of the 22 test files. Each case keeps its own test ID (named after the original test or its comment), so the same cases still run and are reported separately: 108 test functions become 40 parametrized tests with 109 cases (the old invalid-unit-names test in test_class_ground_motion.py had two checks, which are now two cases). - Where the cases call different methods of the same kind (e.g., the four ground-motion filters, `get_HH_x_param()`/`get_H4_x_param()`, or `get_f0_BH()`/`get_f0_RO()`), the unbound method is the parameter - Boolean parameters are keyword-only (FBT001), as in CONTRIBUTING.md - The expected-failure cases that matched against an empty string (`match=''`, which pytest warns always passes) now use no `match` - The HH case of the multi-layer list-operations test now checks `n_layer` after deleting a layer, like the MKZ case did, instead of checking `len()` twice Tests with different assertions or setup are left as they are, and so are the sequential checks inside one test function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1a5vwxyeL4atk9tYUUEoQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1a5vwxyeL4atk9tYUUEoQ
- Name the combined HH/H4 and HH/MKZ tests after both models, e.g., `test_HH_x_or_H4_x_fit_single_layer` and `test_HH_MKZ_param_multi_layer__can_initiate_object_from_file`, and `test_f0` as `test_f0_BH_or_f0_RO` - Name the cases of `test_construct_curves`, `test_param_serialize`, and `test_serialize_to_2D_array` after the original test functions (e.g., `from_HH_G_parameters`) - Move a comment in test_helper_hh_model.py into the `pytest.param()` that it describes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1a5vwxyeL4atk9tYUUEoQ
- Leave out optional arguments in the cases that used to leave them out, so that their defaults are tested again: `dt` in the ground motion loading tests, and `OCR_upper_limit` in `test_calc_OCR`. The cases now pass a `kwargs` dict, like `test_class_svm.py` does - Pass callables (`lambda gm: gm.pga_in_g`, and lambdas that call `get_curve_matrix()` with the keyword argument) instead of attribute and keyword names as strings - Combine the differential-evolution and DEAP multi-layer fitting tests into `test_HH_x_or_H4_x_fit_multi_layer`. The DEAP cases still pass when DEAP is not installed, as before; an `ImportError` in the differential-evolution cases is no longer swallowed - Share one `multi_layer_case` fixture among the 4 HH/MKZ multi-layer parameter tests, instead of repeating the case list. The list operations test now deletes layer 3 in both cases (it was 6 for MKZ) - Rename `test_param_multi_layer__test_list_operations` to `test_HH_MKZ_param_multi_layer__list_operations`, and `test_calc_damping_from_stress_strain__case_3` to `..._initial_damping_almost_0`, after what it tests (its siblings' IDs are no longer numbered) - Pass a copy, not a view, of the module-level `DATA_5_COLUMNS` array in the `incorrect_number_of_columns` case Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016MPgzyHE9jy5vP7heBxZhA
- `MKZ_Param_Multi_Layer()` now reads files and 2D arrays with `from_files=True`, i.e., in the format of the H4 parameter files (gamma_ref, 0, s, beta), as its docstring says. Before, it read them as (gamma_ref, s, beta, Gmax), so each layer got s = 0 and flat curves. Gmax (not in the files) is set to 1.0, as documented now. - `MKZ_Param_Multi_Layer.serialize_to_2D_array()` (and so `save_txt()`) and `Multiple_Damping_Curves.get_all_H4_x_params(save_txt=True)` now write the same format (`to_files=True`), so a saved file loads back with the same parameters. - Tests: fix the expected parameters of layer 1 of H4_G_IWTH04.txt (they had s = 0), check the curve values in `test_construct_curves`, and add round-trip tests for `save_txt()` and `get_all_H4_x_params(save_txt=True)`. 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 was referenced Oct 11, 2026
#74 was squash-merged into main, so main's files are identical to the branch's old base. Every conflict is resolved with this branch's version. 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
Co-authored-by: jsh9 <jsh9@users.noreply.github.com>
Co-authored-by: jsh9 <jsh9@users.noreply.github.com>
Co-authored-by: jsh9 <jsh9@users.noreply.github.com>
Co-authored-by: jsh9 <jsh9@users.noreply.github.com>
- MKZ_Param_Multi_Layer() now raises a ValueError when the 2nd row ("b")
of a file or 2D array is not 0, naming the layers. MATLAB SeismoSoil's
H4 fitters always write 0 there, and H4 files written by older versions
of PySeismoSoil (rows gamma_ref, s, beta, Gmax) would otherwise load
wrongly without an error.
- The rows are deserialized once, after reading the file or the array.
- New parametrized test for files and 2D arrays, in the old format and
with one bad layer.
- CHANGELOG: the #79 entry mentions the new error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7fK8T2pPLf3tPCnMCKL1T
Follow-ups from the review of #116: - `mkz.deserialize_array_to_params()` now validates its input: a wrong number of values raises a `ValueError` with a message (instead of a bare `assert`). With `from_files=True`, NaN/infinite values (such as from a trailing delimiter) and a nonzero "b" raise a `ValueError`; b within 1e-8 of 0 counts as 0. The b check moved here from `MKZ_Param_Multi_Layer.__init__()`, so every caller gets it. The message says how to convert H4 files written by 0.7.1 or earlier. - `MKZ_Param_Multi_Layer.__init__()`: the file branch loads a 2D array and falls through to the array branch, which prefixes errors with the layer number. This replaces the `list_of_param_array = None` placeholder. - `HH_Param_Multi_Layer` and `MKZ_Param_Multi_Layer` read files with `ndmin=2`, so one-layer files (one column) load (#86). - `mkz.serialize_params_to_array(to_files=True)` warns when it drops a Gmax other than 1.0. - `Param_Multi_Layer.serialize_to_2D_array()` calls a per-layer hook, `_serialize_layer()`, which `MKZ_Param_Multi_Layer` overrides, instead of the subclass copying the loop. - `save_txt()` and `fit_all_damping_curves()` share one precision, `hlp.PARAM_FILE_PRECISION` ('%.6g'; `save_txt()` used '%.5g'). - Tests: the single-layer tests use the `_HH/_H4_param_from_column` helpers; new tests cover the cases above. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FSqLqxJej1HeeayXnFVrvV
`MKZ_Param_Multi_Layer.serialize_to_2D_array()` (and so `save_txt()`) warned once per layer whose Gmax is not 1.0, and the warning pointed at a line inside class_parameters.py instead of the user's call. - `MKZ_Param_Multi_Layer.serialize_to_2D_array()` now issues one `UserWarning` that names those layers (0-based), then serializes each layer with Gmax = 1.0 so that the per-layer helper does not warn again - New `hlp.find_stack_level()` counts the frames inside the package, so the warning points at the first line outside it, whether the user calls `save_txt()` or `serialize_to_2D_array()`. `mkz.serialize_params_to_array(to_files=True)` uses it too - The warning text is shared as `mkz.H4_FILE_GMAX_WARNING` - Tests: one warning naming layers [0, 2], at this test file, for both methods; the helper's warning also points at the caller - CHANGELOG: the #79 entry says the warning is issued once and names the layers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC
Demo_05 said `MKZ_Param_Multi_Layer` behaves identically to `HH_Param_Multi_Layer`. Its files, `serialize_to_2D_array()` and `save_txt()` now use the H4 file layout (gamma_ref, 0, s, beta), which has no Gmax, so the note now says so. All notebooks were re-run (Python 3.12, as recorded on main). Apart from that note, only timings, object addresses, folder names, progress bars and image bytes changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC
The Lint job failed because the format-docstring pre-commit hook rewraps one paragraph of this docstring. No other change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC
- Rename `hlp.find_stack_level()` to `hlp._find_stack_level()`. It is an internal helper, and the API docs list every public function of helper_generic. Its callers use `# noqa: SLF001`, like the other cross-module private helpers (e.g., `_process_fig_ax_objects()`) - The Gmax warning of `MKZ_Param_Multi_Layer.serialize_to_2D_array()` now ends with "Layers whose Gmax is not 1.0 (0-based): [0, 2]." instead of "Layers with such a Gmax (0-based): [0, 2]", whose "such a Gmax" referred back past a whole sentence. When every layer is affected (for example, Gmax from a Vs profile), it ends with "Gmax is not 1.0 in every layer." instead of listing every layer number - Tests: the new ending, and the every-layer case for 1 and 3 layers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC
`MKZ_Param_Multi_Layer.serialize_to_2D_array()` now returns the H4 file layout (gamma_ref, 0, s, beta) instead of gamma_ref, s, beta, Gmax, and it warns when it drops a Gmax other than 1.0. The CHANGELOG described only the file reading and writing, so code that reads the rows of its result by position could break without notice. The new "Changed" entry says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC
This was referenced Oct 14, 2026
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 #79. Fixes #86. One of the four P0 fixes from #114.
Problem
H4 parameter files (
H4_G_*.txt,H4_x_*.txt) store each layer asgamma_ref, b (always 0), s, beta. MATLAB SeismoSoil writes this layout, and its H4 kernel reads it (see "Where the layout comes from" below).MKZ_Param_Multi_Layer()read files and 2D arrays withoutfrom_files=True, so every layer gots = 0,beta= the file'ss, andGmax= the file'sbeta. Withs = 0, G/Gmax is a constant (0.5296 for every strain in layer 1 ofH4_G_IWTH04.txt), and damping is about 0.save_txt()andget_all_H4_x_params(save_txt=True)wrotegamma_ref, s, beta, Gmaxinstead of the file layout.Reading and writing were wrong in the same way, so a file written by the library loaded back unchanged. That is why the existing round-trip tests passed.
Separately,
HH_Param_Multi_Layer()andMKZ_Param_Multi_Layer()could not load a one-layer parameter file (#86):np.genfromtxt()returns a 1D array for a file with one column.Changes
PySeismoSoil/class_parameters.py:MKZ_Param_Multi_Layer.__init__()reads files and 2D arrays withfrom_files=True. The class docstring said a 2D array has the same format as a file, but its table did not name the rows. It now does. The file branch loads the file into a 2D array and falls through to the 2D-array branch.ValueErrorwhose message starts with the first bad layer (Layer #j (0-based): ...): its 2nd row ("b") is not 0, it has NaN (for example, from an empty cell or a trailing delimiter), or it does not have 4 rows (for example, an HH file). MATLAB's H4 fitters always write 0 in the 2nd row (see "Where the layout comes from"), so their files load. H4 files written by PySeismoSoil 0.7.1 or earlier havesthere, so they raise this error instead of loading wrongly, and the message says how to convert them. The checks themselves are inmkz.deserialize_array_to_params()(below), so they also apply to direct callers.HH_Param_Multi_LayerandMKZ_Param_Multi_Layerread files withnp.genfromtxt(..., ndmin=2), so one-layer files load (HH_Param_Multi_Layer() and MKZ_Param_Multi_Layer() cannot load a one-layer parameter file, including one written by save_txt() #86).Param_Multi_Layer.serialize_to_2D_array()calls a per-layer hook,_serialize_layer()(default:Parameter.serialize()).MKZ_Param_Multi_Layeroverrides the hook to write the file layout (to_files=True), sosave_txt()does too.MKZ_Param_Multi_Layer.serialize_to_2D_array()(and sosave_txt()) issues oneUserWarninginstead of one warning per layer. It ends with "Layers whose Gmax is not 1.0 (0-based): [0, 2]." or, when every layer is affected (for example, Gmax from a Vs profile), "Gmax is not 1.0 in every layer." It then calls the base method, which serializes each layer with Gmax = 1.0, so the per-layer helper does not warn again.save_txt()now defaults tohlp.PARAM_FILE_PRECISION('%.6g', was'%.5g'), the same as the files that the curve-fitting methods write.PySeismoSoil/helper_mkz_model.py:deserialize_array_to_params()raises aValueErrorwith a message when the array does not have 4 values (it was a bareassert, whichpython -Oskips). Withfrom_files=True, it also raises aValueErrorfor NaN or infinite values, and for a "b" that is not withinH4_FILE_B_TOLERANCE(1e-8) of 0.serialize_params_to_array(to_files=True)issues aUserWarningwhen Gmax is not 1.0, because the file layout cannot store it. The warning text is shared withMKZ_Param_Multi_LayerasH4_FILE_GMAX_WARNING.PySeismoSoil/helper_generic.py:PARAM_FILE_PRECISION = '%.6g'._find_stack_level(). It counts the frames inside the package, so that a warning points at the first line outside it: the user'ssave_txt()orserialize_to_2D_array()call, not a line inclass_parameters.py. Both Gmax warnings use it, with# noqa: SLF001like the other cross-module private helpers (e.g.,_process_fig_ax_objects()). It is private so that the API docs do not list it.PySeismoSoil/class_curves.py:get_all_H4_x_params()serializes withfunctools.partial(mkz.serialize_params_to_array, to_files=True).PySeismoSoil/helper_site_response.py:fit_all_damping_curves()writes withhlp.PARAM_FILE_PRECISION, and itsfunc_serializedocstring mentionsto_files=Truefor H4 files.CHANGELOG.md: "Fixed" entries for MKZ_Param_Multi_Layer() reads H4 parameter files in the wrong row order, so s is 0 and the G/Gmax curves are flat #79 (including that the Gmax warning is issued once and names the layers, and a one-line recipe to convert old H4 files) and HH_Param_Multi_Layer() and MKZ_Param_Multi_Layer() cannot load a one-layer parameter file, including one written by save_txt() #86, and "Changed" entries for thesave_txt()precision, theValueErrorindeserialize_array_to_params(), and the new row layout ofMKZ_Param_Multi_Layer.serialize_to_2D_array()(code that reads its rows by position needs to change).examples/Demo_05_Parameters.ipynb: it saidMKZ_Param_Multi_Layerbehaves identically toHH_Param_Multi_Layer. It now says that its files,serialize_to_2D_array(), andsave_txt()use the H4 file layout, which has no Gmax.main, as CONTRIBUTING.md requires. No notebook loads H4 files, buildsMKZ_Param_Multi_Layerfrom a file or array, or callssave_txt(), so no printed results changed. Apart from the Demo_05 note, only timings, folder names, object addresses, progress bars, and image bytes changed. The later commits (6bf6beb, 4dfa939, 3da325f) change no notebook output: no notebook triggers the Gmax warning.Where the layout comes from
PySeismoSoil. The docstrings of
serialize_params_to_array()anddeserialize_array_to_params()inhelper_mkz_model.pydescribe both orders. The first says "the text files recognizable by MATLAB and Fortran functions have the convention of "gamma_ref, 0.0, s, beta"". The library's own MKZ fit already uses this layout.fit_MKZ(), a port of MATLAB'sfitMKZ.mwith the same bounds, returns one row per layer:The HH calibration (
helper_hh_calibration.py) reads it that way:The git history agrees. 4b80e2a (2019-02-15) added both orders:
gamma_ref, s, beta, Gmaxfor in-memory serialization (kept byMKZ_Param.serialize(), which this PR does not change) andgamma_ref, 0, s, betafor files. The firstget_all_H4_x_params()wrote its file withto_files=True; the refactor in 862f996 (2019-03-02) dropped that flag. 2838e75 (2019-04-11) addedH4_G_IWTH04.txt(2nd row all 0) as the test input ofMKZ_Param_Multi_Layer, and 72ebd9e (same day) added a test thatserialize_to_2D_array()equals that file. That test passed only because reading and writing used the same wrong order.MATLAB SeismoSoil (Caltech-geoquake/SeismoSoil).
tests/files/H4_G_IWTH04.txtandH4_x_IWTH04.txtare byte-for-byte copies of itsSample_input_files/. The GUI writes the fitted matrices to the H4 files as they are (SeismoSoil_Preparation_H2_H4_Curve_Fitting.m):runNLH4FromGUI.mwrites the imported matrices for the kernel as they are, then runsNLH4.exeornlH4.m:MATLAB source of the functions that SeismoSoil ships as compiled
.pfiles (not public).fitMKZ.mmakes the H4_G parameters:gaH4x.mmakes the H4_x parameters. Its model has a strain-dependent reference strain,x(1)+gamma*x(2), but it uses only the first column ofLB/UB, which fixesb(row 2) at 0.gaH4G.mhas the same bounds and the same output line.nlH4.m, the MATLAB version of the H4 kernel, reads rows 1, 3 and 4 asgamma_ref,sandbeta, and does the same forH4_x.dat. It reads row 2 intob_val, but no calculation uses it. Gmax comes from the Vs profile, not from the file.The sample files fit these bounds. Row 3 is 0.886–0.889 (
H4_G) and 0.610–0.686 (H4_x), inside the bounds fors(0.6–0.999). Row 4 is 0.47–1.79 and 0.48–1.41, inside the bounds forbeta(0.2–1.8).mainreads these files withs = 0, below the lower bound.Tests
These fail on the old library code (
main) and pass with the fix:test_mkz_param_multi_layer__test_contents_of_list_elements: the old benchmark[0.000856, 0, 0.88832, 1.7492]encodeds = 0. The test now expects layer 1 ofH4_G_IWTH04.txtread in its file layout:gamma_ref=0.00085636, s=0.88832, beta=1.7492, Gmax=1.0.test_mkz_param_multi_layer__save_txt_and_load_back(new):serialize_to_2D_array()and thesave_txt()file have the rowsgamma_ref, 0, s, beta, both warn that a Gmax other than 1.0 is not saved, and both load back with the same parameters (Gmax = 1.0).test_mkz_param_multi_layer__one_Gmax_warning_at_the_caller[serialize_to_2D_array|save_txt](new): with Gmax other than 1.0 in layers 0 and 2 of 3, each method issues exactly one warning, which ends with "Layers whose Gmax is not 1.0 (0-based): [0, 2]." and points at the test file (the caller).test_mkz_param_multi_layer__Gmax_warning_if_every_layer_is_affected[1|3](new): when every layer (1 of 1, or 3 of 3) has a Gmax other than 1.0, there is one warning, which ends with "Gmax is not 1.0 in every layer." instead of listing the layers.test_construct_curves[from_H4_G_parameters]/[from_H4_x_parameters]: these checked only shapes. They now also compare the values of one layer with the curve from that layer's column read withfrom_files=True. The HH cases get the same check.test_mkz_param_multi_layer__invalid_H4_parameters_raise_error[file|2D_array × old_format|one_layer_with_nonzero_b|NaN|too_few_rows|HH_parameters](new): each raises theValueError, and the message names the bad layer.test_mkz_param_multi_layer__trailing_delimiter_raises_error(new): a file with a trailing tab on each line raises the NaN error for the extra column, not the "b" error.test_HH_MKZ_param_multi_layer__one_layer_file_loads_back[HH|MKZ](new, HH_Param_Multi_Layer() and MKZ_Param_Multi_Layer() cannot load a one-layer parameter file, including one written by save_txt() #86): a one-layer object saved withsave_txt()loads back from the file.tests/test_class_curves.py::test_get_all_H4_x_params__saved_file_loads_back(new): the saved file has the file layout, loads back viaMKZ_Param_Multi_Layer(), and is byte-for-byte the same as the filesave_txt()writes (same layout and precision).tests/test_helper_mkz_model.py:test_deserialize_array_to_params__failure[incorrect_number_of_parameters]now expects aValueErrorwith a message instead ofAssertionError.test_serialize_params_to_array__to_files_warns_if_Gmax_is_not_1(new): it also checks that the warning points at the caller.test_deserialize_array_to_params__from_files_failure[old_layout|NaN|incorrect_number_of_parameters](new).These check that nothing else changed, and pass on both:
test_mkz_param_multi_layer__save_txt_without_warning_if_Gmax_is_1,test_mkz_param_multi_layer__b_close_to_0_is_accepted,test_serialize_params_to_array__to_files, andtest_deserialize_array_to_params__from_files[b_is_0|b_is_close_to_0].The single-layer H4 and HH tests now build their parameters with the
_H4_param_from_column()/_HH_param_from_column()helpers.Testing
Run locally on 4dfa939, the last commit that changes code (3da325f changes only
CHANGELOG.md):python -m pytest tests: 269 passed. Against the library code of 6bf6beb (the head before 4dfa939), the 2 cases oftest_mkz_param_multi_layer__one_Gmax_warning_at_the_callerand the 2 cases oftest_mkz_param_multi_layer__Gmax_warning_if_every_layer_is_affectedfail.tests/test_class_parameters.py,tests/test_helper_mkz_model.py, andtests/test_class_curves.pypass (75 passed).pydoclint PySeismoSoil: pass.pre-commit run --all-files: pass, also on 3da325f.main'ssave_txt()into the new layout, andMKZ_Param_Multi_Layer()then loads the original parameters. Without the conversion, the file raises theValueError, whose message explains the conversion.Notes for review
get_all_H4_x_params(save_txt=True)orMKZ_Param_Multi_Layer.save_txt()in 0.7.1 or earlier are in the old layout (gamma_ref, s, beta, Gmax). They now raise aValueErrorinstead of loading wrongly. The message says how to convert them, and the CHANGELOG has a one-line recipe. The Gmax they stored is 1.0 forget_all_H4_x_params()(the H4_x fit always sets it), so converting them loses nothing. Files in the H4 layout, such as the ones intests/files/and those made by MATLAB SeismoSoil, now load correctly.save_txt()precision: the default is now 6 significant digits forHH_Param_Multi_Layertoo.mkz.deserialize_array_to_params()no longer usesassertfor its length check.mkz.serialize_params_to_array()and both HH functions still do.Nonlinear_SimulationacceptsMKZ_Param_Multi_LayerforG_param/xi_paramaccording to its docstring. But it writesserialize_to_2D_array()intoHH_G.dat/HH_x.datand runs the HH kernel (PySeismoSoil ships onlyNLHH), which reads 9 HH parameters per layer. So MKZ parameters do not work there with either layout: the kernel fails to readHH_G.dat, andrun()then fails with an unrelatedValueError(Nonlinear_Simulation accepts MKZ_Param_Multi_Layer, as documented, but run() crashes because the NLHH kernel reads only HH parameters #124).HH_Param_Multi_Layer()does not reject NaN parameters (from a trailing delimiter or an empty cell), unlikeMKZ_Param_Multi_Layer()here (HH_Param_Multi_Layer() accepts NaN parameters, so a trailing delimiter adds a layer of NaN and an empty cell gives NaN curves, without an error #125).mainis merged into this branch. Make the filters zero-phase again and usepadlen#117 and Apply the transfer function at the right FFT bins in amplify_motion() #118 change other library files. All three re-run the notebooks and add an entry to the "Fixed" group inCHANGELOG.md(this PR also adds a "Changed" group), so after one of them merges, the others needmainmerged in again (keep both entries, then re-run the notebooks).🤖 Generated with Claude Code
https://claude.ai/code/session_01W7fK8T2pPLf3tPCnMCKL1T
https://claude.ai/code/session_01FSqLqxJej1HeeayXnFVrvV
https://claude.ai/code/session_018XWkqdYHdQvARgx5RDCmcC