Skip to content

Conversation

@waltsims
Copy link
Owner

@waltsims waltsims commented May 25, 2025

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

    • Prevents mutation of user-provided simulation inputs by operating on internal copies, preserving originals across runs.
    • Fixes sensor-mask handling tied to PML size evaluation, improving configuration accuracy.
  • Tests

    • Adds an end-to-end 3D simulation test that verifies input state remains unchanged across multiple runs with different I/O and checkpoint settings.

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.
@waltsims waltsims changed the title Fix: Correct SyntaxError in test_kspaceFirstOrder3D_state.py Fix: Stop mutation of source and sensor by kspaceFirstOrder May 26, 2025
@waltsims waltsims requested a review from faridyagubbayli May 26, 2025 04:46
@waltsims
Copy link
Owner Author

Closes #600.

@codecov
Copy link

codecov bot commented May 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.19%. Comparing base (e1311b3) to head (791a65c).

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     
Flag Coverage Δ
3.10 74.19% <100.00%> (+0.23%) ⬆️
3.11 74.19% <100.00%> (+0.23%) ⬆️
3.12 74.19% <100.00%> (+0.23%) ⬆️
3.13 74.19% <100.00%> (+0.23%) ⬆️
ubuntu-latest 74.16% <100.00%> (+0.23%) ⬆️
windows-latest 74.18% <100.00%> (+0.23%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Walkthrough

Deep copies of source and sensor are created in kWaveSimulation and the originals are retained on new public attributes original_source and original_sensor. A walrus-condition bug in _is_binary_sensor_mask was fixed. A new test asserts input state is preserved across consecutive runs.

Changes

Cohort / File(s) Summary of changes
Simulation input preservation & mask check fix
kwave/kWaveSimulation.py
Added public fields original_source and original_sensor. Store self.source and self.sensor as deepcopy of inputs while keeping originals. Fixed walrus operator usage in _is_binary_sensor_mask to properly assign and check pml_size.
State preservation test
tests/test_kspaceFirstOrder3D_state.py
New test module that constructs a 3D simulation and asserts source.p0 and sensor.mask remain unchanged across two kspaceFirstOrder3D runs with different I/O/checkpoint paths. Adds helper to build simulation parameters and execution options.

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)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix: Stop mutation of source and sensor by kspaceFirstOrder" is a concise, single-line description that directly matches the PR's main change — preventing in-place mutation of source and sensor objects (the code now deepcopies inputs and preserves originals) and the accompanying test that verifies state preservation. It clearly communicates the primary intent of the changeset and is specific enough for reviewers scanning history.

Poem

I nibble the code like clover leaves,
Tuck source and sensor in cozy sleeves.
Copies to run, originals to keep,
No sneaky mutations while rabbits leap.
A tiny mask fix, then off I hop—
Two runs later, still tip-top! 🐇✨

✨ Finishing touches
  • 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-test-syntax-bug-600

Comment @coderabbitai help to get the list of available commands and usage tips.

@waltsims
Copy link
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 51b2041 and e3e76d4.

📒 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 51b2041 and e3e76d4.

📒 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.

coderabbitai bot added a commit that referenced this pull request Sep 11, 2025
Docstrings generation was requested by @waltsims.

* #614 (comment)

The following files were modified:

* `kwave/kWaveSimulation.py`
* `tests/test_kspaceFirstOrder3D_state.py`
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Note

Generated docstrings for this pull request at #639

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.

2 participants