-
Notifications
You must be signed in to change notification settings - Fork 44
Fix: Stop mutation of source and sensor by kspaceFirstOrder #614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
The test file added in the previous commit for bug #600 (test_kspaceFirstOrder3D_state.py) contained a SyntaxError due to erroneous markdown backticks (```) at the end of the file. This commit removes the offending backticks to make the test file syntactically correct.
|
Closes #600. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #614 +/- ##
==========================================
+ Coverage 73.95% 74.19% +0.23%
==========================================
Files 50 50
Lines 7000 7003 +3
Branches 1338 1338
==========================================
+ Hits 5177 5196 +19
+ Misses 1280 1260 -20
- Partials 543 547 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
WalkthroughDeep copies of Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Test as Test
participant API as kspaceFirstOrder3D
participant Sim as kWaveSimulation
participant OrigSrc as source (original)
participant OrigSen as sensor (original)
Test->>API: call kspaceFirstOrder3D(source, sensor, ...)
API->>Sim: init with source, sensor, options
Note over Sim: preserve originals and operate on copies
Sim->>Sim: self.original_source = source\nself.original_sensor = sensor
Sim->>Sim: self.source = deepcopy(source)\nself.sensor = deepcopy(sensor)
Sim->>Sim: _is_binary_sensor_mask() -- (fixed pml_size check)
Sim-->>API: run using deep-copied objects
API-->>Test: return results (originals unchanged)
Test->>API: second call with same original objects (new paths)
API->>Sim: init new kWaveSimulation(...)
Sim->>Sim: repeat preserve + deepcopy steps
Sim-->>API: run using copies
API-->>Test: return results (originals still unchanged)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
✨ Finishing touches
🧪 Generate unit tests
Comment |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
kwave/kWaveSimulation.py (1)
1523-1537: Handle scalar and sequence pml_size to avoid AttributeError/TypeError.
As written, pml_size may be an int (common in options), causing len(...) to fail or pml_size.x to be missing when it’s a list/ndarray. Normalize to a Vector before use.Apply this diff to make the method robust:
- if (pml_size := self.options.pml_size) is None: + pml_size_val = self.options.pml_size + if pml_size_val is None: return False - - if len(pml_size) == 1: - # make pml_size a vector type - pml_size = Vector(np.tile(pml_size, kgrid_dim)) + # Normalize pml_size to a Vector of length kgrid_dim + arr = np.atleast_1d(np.asarray(pml_size_val)).astype(int).ravel() + if arr.size == 1: + pml_size = Vector(np.tile(int(arr.item()), kgrid_dim)) + elif arr.size == kgrid_dim: + pml_size = Vector(arr.tolist()) + else: + # Unexpected shape; cannot infer expected sensor shape + return False
🧹 Nitpick comments (4)
kwave/kWaveSimulation.py (1)
42-45: Defensive deep copies preserve caller inputs; consider memory/perf trade-offs.
This prevents mutation across runs. For large 3D arrays, deepcopy can double memory. If needed later, consider a targeted copy strategy (copy only fields that are mutated) or implementing deepcopy on kSource/kSensor to control what’s copied.tests/test_kspaceFirstOrder3D_state.py (3)
41-44: Use boolean dtype for a binary sensor mask.
This clarifies intent and avoids implicit float->bool comparisons later.- sensor_mask_array = np.zeros(N) + sensor_mask_array = np.zeros(N, dtype=bool) sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D
96-99: Prefer NumPy test helpers for clearer diffs on failure.
np.testing.assert_array_equal gives better diagnostics than array_equal.- assert np.array_equal(source.p0, original_source_p0), "source.p0 was modified after first run" - assert np.array_equal(sensor.mask, original_sensor_mask), "sensor.mask was modified after first run" + np.testing.assert_array_equal(source.p0, original_source_p0, err_msg="source.p0 was modified after first run") + np.testing.assert_array_equal(sensor.mask, original_sensor_mask, err_msg="sensor.mask was modified after first run")
124-125: Same here: use NumPy assertions for the final check.- assert np.array_equal(source.p0, original_source_p0), "source.p0 was modified after second run" - assert np.array_equal(sensor.mask, original_sensor_mask), "sensor.mask was modified after second run" + np.testing.assert_array_equal(source.p0, original_source_p0, err_msg="source.p0 was modified after second run") + np.testing.assert_array_equal(sensor.mask, original_sensor_mask, err_msg="sensor.mask was modified after second run")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
kwave/kWaveSimulation.py(3 hunks)tests/test_kspaceFirstOrder3D_state.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_kspaceFirstOrder3D_state.py (8)
kwave/data.py (1)
Vector(7-49)kwave/ksensor.py (1)
kSensor(9-81)kwave/ksource.py (1)
kSource(11-382)kwave/kspaceFirstOrder3D.py (1)
kspaceFirstOrder3D(132-376)kwave/options/simulation_execution_options.py (1)
SimulationExecutionOptions(13-308)kwave/options/simulation_options.py (1)
SimulationOptions(46-350)kwave/utils/filters.py (1)
smooth(549-601)kwave/utils/mapgen.py (1)
make_ball(505-554)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (29)
- GitHub Check: collect_references
- GitHub Check: test_install (macos-latest, 3.13, all)
- GitHub Check: test_install (ubuntu-latest, 3.13, dev)
- GitHub Check: test_install (ubuntu-latest, 3.12, all)
- GitHub Check: test_install (ubuntu-latest, 3.13, docs)
- GitHub Check: test_install (ubuntu-latest, 3.13, test)
- GitHub Check: test_install (ubuntu-latest, 3.10, all)
- GitHub Check: test_install (ubuntu-latest, 3.11, dev)
- GitHub Check: test_install (ubuntu-latest, 3.12, examples)
- GitHub Check: test_install (windows-latest, 3.12, examples)
- GitHub Check: test_install (ubuntu-latest, 3.11, test)
- GitHub Check: test_install (windows-latest, 3.12, dev)
- GitHub Check: test_install (windows-latest, 3.13, all)
- GitHub Check: test_install (windows-latest, 3.12, docs)
- GitHub Check: test_install (windows-latest, 3.12, test)
- GitHub Check: test_install (windows-latest, 3.11, docs)
- GitHub Check: test_example (macos-latest, 3.11)
- GitHub Check: test_example (macos-latest, 3.13)
- GitHub Check: test_example (macos-latest, 3.10)
- GitHub Check: test_example (ubuntu-latest, 3.11)
- GitHub Check: test_example (macos-latest, 3.12)
- GitHub Check: test_example (ubuntu-latest, 3.12)
- GitHub Check: test_example (ubuntu-latest, 3.13)
- GitHub Check: test_example (windows-latest, 3.13)
- GitHub Check: test_example (windows-latest, 3.10)
- GitHub Check: test_example (windows-latest, 3.11)
- GitHub Check: test_example (ubuntu-latest, 3.10)
- GitHub Check: test_example (windows-latest, 3.12)
- GitHub Check: collect_references
🔇 Additional comments (1)
kwave/kWaveSimulation.py (1)
3-3: Import of deepcopy is fine.
No concerns with adding this import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
kwave/kWaveSimulation.py (1)
1523-1529: len() on scalar pml_size can raise TypeError; normalize robustly.
If SimulationOptions.pml_size is an int (as in the new test), len(pml_size) will crash. Normalize scalars/sequences to Vector first.Apply:
- if (pml_size := self.options.pml_size) is None: + pml_size = self.options.pml_size + if pml_size is None: return False - if len(pml_size) == 1: - # make pml_size a vector type - pml_size = Vector(np.tile(pml_size, kgrid_dim)) + # Normalize to a Vector: accept scalars, 1-elem sequences, or full vectors + if np.isscalar(pml_size): + pml_size = Vector([int(pml_size)] * kgrid_dim) + else: + try: + seq = list(pml_size) + if len(seq) == 1: + pml_size = Vector([int(seq[0])] * kgrid_dim) + else: + pml_size = Vector(seq) + except TypeError: + pml_size = Vector([int(pml_size)] * kgrid_dim)
♻️ Duplicate comments (1)
tests/test_kspaceFirstOrder3D_state.py (1)
49-57: Pass PML size as 3-vector; scalar can break option handling.
Repeat of prior feedback: ensure option_factory/indexing logic sees a length-3 sequence.Apply:
- simulation_options = SimulationOptions( + simulation_options = SimulationOptions( save_to_disk=True, # Must be true for kspaceFirstOrder3D - pml_size=PML_size, + pml_size=[PML_size] * 3, pml_inside=False, smooth_p0=False, # p0 is already smoothed data_cast="single", - input_filename=input_filename, - output_filename=output_filename, + input_filename=str(input_filename), + output_filename=str(output_filename), )
🧹 Nitpick comments (5)
kwave/kWaveSimulation.py (1)
42-46: Defensive copies of source/sensor prevent input mutation.
Solid fix for #600. Minor: this doubles peak memory for large arrays; consider an opt-in flag (e.g., copy_inputs=True) or a custom deepcopy on kSource/kSensor to avoid copying immutable fields.tests/test_kspaceFirstOrder3D_state.py (4)
33-35: Use integer center for make_ball to avoid implicit float handling.
Ensures indices are integral across backends.Apply:
- p0_array = ball_magnitude * make_ball(N, N / 2, ball_radius) + p0_array = ball_magnitude * make_ball(N, N // 2, ball_radius)
41-43: Use boolean dtype for a binary mask.
Avoids float sums and makes intent explicit.Apply:
- sensor_mask_array = np.zeros(N) - sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D + sensor_mask_array = np.zeros(N, dtype=bool) + sensor_mask_array[0, :, :] = True # Planar sensor in 3D
61-66: Path-like for checkpoint_file — cast to str for CLI safety.
Prevents downstream subprocess arg type issues on older Python/OS combos.Apply:
- execution_options = SimulationExecutionOptions( + execution_options = SimulationExecutionOptions( is_gpu_simulation=False, # Assuming CPU for basic test - checkpoint_file=checkpoint_filename, + checkpoint_file=str(checkpoint_filename), checkpoint_timesteps=checkpoint_timesteps, verbose_level=0, # Keep test output clean )
107-110: Also cast run-2 checkpoint path to str.
Consistency with above.Apply:
- if execution_options_run2.checkpoint_file: # Only change if it exists - execution_options_run2.checkpoint_file = tmpdir / "kwave_checkpoint_run2.h5" + if execution_options_run2.checkpoint_file: # Only change if it exists + execution_options_run2.checkpoint_file = str(tmpdir / "kwave_checkpoint_run2.h5")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
kwave/kWaveSimulation.py(3 hunks)tests/test_kspaceFirstOrder3D_state.py(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: pytest
tests/test_kspaceFirstOrder3D_state.py
[error] 94-94: First call to kspaceFirstOrder3D failed: External binary 'kspaceFirstOrder-OMP' terminated with SIGABRT during test_kspaceFirstOrder3D_input_state_preservation. Command: '/Users/runner/work/k-wave-python/k-wave-python/kwave/bin/darwin/kspaceFirstOrder-OMP' -i '/var/folders/x7/ch5v91h56_zbvbd1y2f600dm0000gn/T/tmp8ruc7ke7/kwave_input.h5' -o '/var/folders/x7/ch5v91h56_zbvbd1y2f600dm0000gn/T/tmp8ruc7ke7/kwave_output.h5' -t 3 --checkpoint_timesteps 300 --checkpoint_file '/var/folders/x7/ch5v91h56_zbvbd1y2f600dm0000gn/T/tmp8ruc7ke7/kwave_checkpoint.h5' --p_raw -s 1'. The underlying error: dyld: Library not loaded: /opt/homebrew/opt/fftw/lib/libfftw3f.3.dylib (no such file).
🔇 Additional comments (1)
kwave/kWaveSimulation.py (1)
3-3: Importing deepcopy to protect inputs — good call.
This enables safe internal mutation without leaking to caller state.
Docstrings generation was requested by @waltsims. * #614 (comment) The following files were modified: * `kwave/kWaveSimulation.py` * `tests/test_kspaceFirstOrder3D_state.py`
|
Note Generated docstrings for this pull request at #639 |
The test file added in the previous commit for bug #600 (test_kspaceFirstOrder3D_state.py) contained a SyntaxError due to erroneous markdown backticks (```) at the end of the file.
This commit removes the offending backticks to make the test file syntactically correct.
Summary by CodeRabbit
Bug Fixes
Tests