Skip to content

Conversation

@faridyagubbayli
Copy link
Collaborator

@faridyagubbayli faridyagubbayli commented Oct 30, 2025

I removed the duplicate cart_interp option and kept cartesian_interp to make the API clear and consistent, then switched the executor return from a loose dotdict to a typed SimulationResult. This cuts confusion around options, improves IDE help and readability, and makes it safer and simpler to work with outputs from the k‑Wave binaries.

Changes

  • kwave/options/simulation_options.py: removed cart_interp, kept cartesian_interp, cleaned docs/validation.
  • tests/test_tvsp_3D_simulation.py, tests/test_ivp_3D_simulation.py: switched to cartesian_interp.
  • kwave/data.py: added SimulationResult dataclass (+ from_dotdict).
  • kwave/executor.py: return SimulationResult from run_simulation.
  • kwave/kspaceFirstOrder2D.py, kwave/kspaceFirstOrder3D.py, kwave/kspaceFirstOrderAS.py: updated return annotations/imports.
  • tests/test_executor.py: assertions now expect SimulationResult.

Compatibility

  • Non-breaking at call sites; return type is now structured. Consumers relying on dict-like access should migrate to attributes.

Tests

  • Updated unit tests pass locally; basic import/creation of SimulationResult verified.

Summary by CodeRabbit

  • Breaking Changes

    • SimulationOptions parameter renamed from cart_interp to cartesian_interp (update call sites).
  • New Features

    • Simulation solvers now return a structured SimulationResult object consolidating grid info and arrays for pressure, velocity, and intensity.
  • Tests

    • Tests updated to expect and validate the new structured SimulationResult outputs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Walkthrough

Adds a new public data type SimulationResult for kWave simulation outputs, updates simulation entry points and executor to return it, removes the cart_interp option in favor of cartesian_interp, and updates tests to expect SimulationResult instances.

Changes

Cohort / File(s) Summary
New Data Structure
kwave/data.py
Adds SimulationResult class with grid metadata and optional per-field arrays, plus from_dotdict() constructor and dictionary-like methods (__getitem__, __contains__) for compatibility.
Executor & Return Types
kwave/executor.py, kwave/kspaceFirstOrder2D.py, kwave/kspaceFirstOrder3D.py, kwave/kspaceFirstOrderAS.py
Imports SimulationResult; updates function/method return annotations to Optional[SimulationResult]; executor.run_simulation now returns SimulationResult.from_dotdict(sensor_data).
Options Cleanup
kwave/options/simulation_options.py
Removes the public SimulationOptions.cart_interp field (retains cartesian_interp).
Tests — executor
tests/test_executor.py
Replaces mock dict usage and updates assertions to expect SimulationResult instances; adjusts cropping-related mocks and checks.
Tests — simulation usage
tests/test_ivp_3D_simulation.py, tests/test_tvsp_3D_simulation.py
Updates test code to pass cartesian_interp instead of removed cart_interp.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Executor
  participant NativeSim as "Native Simulation (C/engine)"
  participant SimResult as "SimulationResult.from_dotdict"

  Caller->>Executor: run_simulation(input, output, options)
  Executor->>NativeSim: invoke simulation with files/options
  NativeSim-->>Executor: sensor_data (dict)
  Note right of Executor: previously returned raw dict/dotdict
  Executor->>SimResult: SimulationResult.from_dotdict(sensor_data)
  SimResult-->>Executor: SimulationResult instance
  Executor-->>Caller: SimulationResult
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Areas needing extra attention:
    • SimulationResult.from_dotdict() mapping completeness (all sensor keys, shapes, None handling)
    • Backward-compatibility of __getitem__ / __contains__ behavior vs. previous dotdict access patterns
    • Consistency of updated return annotations across 2D/3D/AS modules and any callers
    • Tests that mock / inspect sensor arrays (cropping behavior) to ensure mocks reflect new type semantics
    • Removal of cart_interp: check for any remaining references across codebase or docs

Poem

🐰 I stitched the bytes into a tidy chest,
Fields aligned, no arrays in unrest.
from_dotdict hummed a gentle tune,
Old dict ways still hop in the moon—
A neat result, hugged by a rabbit's zest. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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 pull request title "Consolidate cartesian interp option and introduce structured SimulationResult return" accurately reflects the two major changes in the changeset. It correctly identifies the removal of the duplicate cart_interp option in favor of cartesian_interp in SimulationOptions, and it clearly describes the introduction of the new SimulationResult dataclass that replaces the previous loose dotdict return type across multiple execution functions. The title is concise, specific, and would enable a teammate scanning the repository history to understand the primary purpose of the changes without needing to inspect the details.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/minor-code-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64699ce and 088aa86.

📒 Files selected for processing (9)
  • kwave/data.py (2 hunks)
  • kwave/executor.py (3 hunks)
  • kwave/kspaceFirstOrder2D.py (3 hunks)
  • kwave/kspaceFirstOrder3D.py (4 hunks)
  • kwave/kspaceFirstOrderAS.py (5 hunks)
  • kwave/options/simulation_options.py (0 hunks)
  • tests/test_executor.py (3 hunks)
  • tests/test_ivp_3D_simulation.py (2 hunks)
  • tests/test_tvsp_3D_simulation.py (2 hunks)
💤 Files with no reviewable changes (1)
  • kwave/options/simulation_options.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-11T16:12:04.876Z
Learnt from: waltsims
PR: waltsims/k-wave-python#625
File: docs/get_started/first_simulation.rst:147-154
Timestamp: 2025-09-11T16:12:04.876Z
Learning: In k-Wave Python, kspaceFirstOrder2D function takes separate simulation_options and execution_options parameters, not a single options dict. Use SimulationOptions and SimulationExecutionOptions classes from kwave.options, with snake_case attributes like pml_inside instead of MATLAB-style PMLInside.

Applied to files:

  • tests/test_ivp_3D_simulation.py
  • tests/test_tvsp_3D_simulation.py
  • kwave/kspaceFirstOrder2D.py
  • kwave/executor.py
  • kwave/kspaceFirstOrder3D.py
  • kwave/kspaceFirstOrderAS.py
🧬 Code graph analysis (7)
tests/test_ivp_3D_simulation.py (1)
kwave/kWaveSimulation.py (1)
  • kWaveSimulation (34-1552)
kwave/data.py (1)
kwave/kgrid.py (3)
  • Nx (157-161)
  • Ny (164-168)
  • Nz (171-175)
kwave/kspaceFirstOrder2D.py (1)
kwave/data.py (1)
  • SimulationResult (125-236)
kwave/executor.py (1)
kwave/data.py (2)
  • SimulationResult (125-236)
  • from_dotdict (196-236)
kwave/kspaceFirstOrder3D.py (1)
kwave/data.py (1)
  • SimulationResult (125-236)
tests/test_executor.py (1)
kwave/data.py (1)
  • SimulationResult (125-236)
kwave/kspaceFirstOrderAS.py (1)
kwave/data.py (1)
  • SimulationResult (125-236)
🪛 GitHub Actions: pytest
tests/test_executor.py

[error] 221-221: TestExecutor.test_sensor_data_cropping_with_pml_inside: AssertionError - expected sensor_data['p_final'] to match mock values; actual value did not equal expected (1 != 20).


[error] 192-192: TestExecutor.test_sensor_data_cropping_with_pml_outside: getitem on p_final was not called as expected; cropping did not occur.

🔇 Additional comments (9)
tests/test_tvsp_3D_simulation.py (1)

75-75: LGTM! Parameter renamed to use the consolidated API.

The change from cart_interp to cartesian_interp correctly reflects the API consolidation mentioned in the PR objectives.

kwave/executor.py (1)

9-9: LGTM! Clean integration of SimulationResult return type.

The changes correctly:

  • Import the new SimulationResult type
  • Annotate the return type (non-Optional is correct since there are no early returns in this method)
  • Wrap the dotdict in SimulationResult after PML cropping

The implementation preserves the existing cropping logic while providing a structured return type with backward compatibility.

Also applies to: 36-36, 65-65

tests/test_ivp_3D_simulation.py (1)

73-73: LGTM! Parameter renamed to match consolidated API.

Consistent with the broader API cleanup replacing cart_interp with cartesian_interp.

kwave/data.py (1)

124-236: LGTM! Well-structured dataclass with good backward compatibility.

The SimulationResult implementation is clean and well-designed:

  • Clear separation between required grid metadata and optional sensor data fields
  • Backward-compatible dict-like access via __getitem__ and __contains__
  • Safe construction from dotdict with sensible defaults
  • Appropriate type hints throughout

The from_dotdict classmethod properly handles missing keys and type conversions, ensuring robust construction from HDF5-loaded data.

kwave/kspaceFirstOrderAS.py (3)

2-2: LGTM! Return types correctly annotated as Optional[SimulationResult].

The Optional return type is appropriate because of the early return at line 366 when save_to_disk_exit=True. The imports are correctly added to support the new type annotations.

Also applies to: 7-7, 34-34, 93-93


162-162: Improved warning message clarity.

The warning message appears to have been improved (possibly fixing a typo or improving wording). Without seeing the old version, this looks like a reasonable refinement.


300-300: Assertion message consolidated to single line.

Good cleanup - keeping assertion messages as single strings improves readability and maintainability.

kwave/kspaceFirstOrder2D.py (1)

1-1: LGTM! Return type annotations correctly added.

The Optional[SimulationResult] return type is appropriate for both functions due to the early return path at line 359 when save_to_disk_exit=True. This provides better type safety and IDE support.

Also applies to: 6-6, 89-89, 149-149

kwave/kspaceFirstOrder3D.py (1)

1-1: LGTM! Return type annotations consistently applied across all 3D simulation functions.

The Optional[SimulationResult] return type is correctly applied to all three functions (GPU, CPU wrapper, and main function) to account for the early return at line 371 when save_to_disk_exit=True.

Also applies to: 5-5, 29-29, 83-83, 140-140

@codecov
Copy link

codecov bot commented Oct 30, 2025

Codecov Report

❌ Patch coverage is 93.61702% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.05%. Comparing base (64699ce) to head (4daf678).

Files with missing lines Patch % Lines
kwave/data.py 91.89% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #647      +/-   ##
==========================================
+ Coverage   73.95%   74.05%   +0.09%     
==========================================
  Files          50       50              
  Lines        7000     7038      +38     
  Branches     1338     1339       +1     
==========================================
+ Hits         5177     5212      +35     
- Misses       1280     1282       +2     
- Partials      543      544       +1     
Flag Coverage Δ
3.10 74.05% <93.61%> (+0.09%) ⬆️
3.11 74.05% <93.61%> (+0.09%) ⬆️
3.12 74.05% <93.61%> (+0.09%) ⬆️
3.13 74.05% <93.61%> (+0.09%) ⬆️
macos-latest 74.02% <93.61%> (+0.09%) ⬆️
ubuntu-latest 74.02% <93.61%> (+0.09%) ⬆️
windows-latest 74.04% <93.61%> (+0.09%) ⬆️

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.

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: 0

🧹 Nitpick comments (2)
tests/test_executor.py (2)

177-209: Cropping test correctly updated for SimulationResult.

The test logic is sound:

  • Tracking mocks verify that _crop_pml is called with correct slice operations
  • Field assertions confirm non-cropped data is preserved
  • SimulationResult instance check validates the return type

The test effectively verifies the control flow and cropping behavior.

Optional: Consider strengthening verification of cropped values.

Currently, the test verifies that cropping functions were called but doesn't verify the final cropped array shapes or values in the SimulationResult. You could enhance this by:

# Configure mocks to return arrays with realistic shapes
cropped_2d = np.zeros((16, 16))  # After cropping 20x20 with PML=2
cropped_3d = np.zeros((16, 16, 16))
original_two_d_output.__getitem__.return_value = cropped_2d
original_three_d_output.__getitem__.return_value = cropped_3d

# Then verify the shapes in the result
self.assertEqual(sensor_data["p_final"].shape, (16, 16))
self.assertEqual(sensor_data["p_max_all"].shape, (16, 16, 16))

This would provide stronger verification that the cropping logic produces the expected output, not just that it was invoked.


211-243: Correct verification that cropping is skipped when pml_inside is True.

The test properly verifies that when pml_inside=True, the cropping functions are not called and all fields remain unchanged in the SimulationResult.

Optional: Same enhancement opportunity as the pml_outside test.

Consider configuring mocks with realistic array shapes and verifying the final array dimensions in the SimulationResult to strengthen the test's verification of data correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 088aa86 and 4daf678.

📒 Files selected for processing (1)
  • tests/test_executor.py (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_executor.py (2)
kwave/data.py (1)
  • SimulationResult (125-236)
kwave/executor.py (2)
  • Executor (14-144)
  • run_simulation (36-65)
⏰ 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). (5)
  • GitHub Check: test (macos-latest, 3.11)
  • GitHub Check: test (macos-latest, 3.12)
  • GitHub Check: test (macos-latest, 3.10)
  • GitHub Check: test (macos-latest, 3.13)
  • GitHub Check: test (macos-latest, 3.12)
🔇 Additional comments (3)
tests/test_executor.py (3)

12-12: LGTM: Import updated for SimulationResult.

The import correctly reflects the updated return type from the executor.


66-67: Good fix: Using real dict for SimulationResult compatibility.

This change is necessary because SimulationResult.from_dotdict expects a real dictionary with working .get() method semantics, which MagicMock doesn't provide correctly.


111-111: LGTM: Assertions correctly verify SimulationResult return type.

The updated assertions properly check that run_simulation returns SimulationResult instances instead of dotdict.

Also applies to: 131-131



@dataclass
class SimulationResult:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@djps djps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think to increase utility of this, you need more data: specifically the spatial and temporal resolution.

For 2D simulations is Nz (and dz) 1 or None?

If you want to take a data set and compute some key metrics you would need more information as well, such as the frequency (which is needed for the mechanical index). I think it should be an aim to have data from which all key metrics can be derived without needing input files.

@faridyagubbayli
Copy link
Collaborator Author

@djps @waltsims could you please summarize the discussion outcomes from our meeting today? I didn't catch everything, unfortunately. How do we want to proceed with this PR?

Comment on lines -206 to -208
* cart_interp: Interpolation mode used to extract the pressure when a Cartesian sensor mask is given.
If set to 'nearest', duplicated data points are discarded and sensor_data
will be returned with fewer points than specified by sensor.mask (default = 'linear').
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we document Cartesian interp with the old docstring?

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.

4 participants