fullwave25 1.4.2.dev1__tar.gz → 1.4.2.dev3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.bumpversion.toml +1 -1
  2. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/PKG-INFO +8 -2
  3. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/README.md +7 -1
  4. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/__init__.py +1 -1
  5. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/beamformer/beamformer.py +12 -3
  6. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/solver.py +48 -93
  7. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/transducer.py +50 -2
  8. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/backend.py +109 -27
  9. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/backend_2d.py +203 -9
  10. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/binary.py +8 -2
  11. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/writer_2d.py +22 -2
  12. fullwave25-1.4.2.dev3/fullwave/utils/gpu_memory.py +109 -0
  13. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/relaxation_parameters.py +51 -0
  14. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/pyproject.toml +1 -1
  15. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/uv.lock +1 -1
  16. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/.gitignore +0 -0
  17. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260911_1813_dead-code.md +0 -0
  18. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_1311_commit_message_of_the_input_generation_speedup.md +0 -0
  19. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_1311_pull_request_of_the_input_generation_speedup.md +0 -0
  20. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_1405_commit_message_of_the_relaxation_memory_estimate.md +0 -0
  21. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_1405_pull_request_of_the_relaxation_memory_estimate.md +0 -0
  22. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_2152_commit_message_of_the_cuda_version_choice.md +0 -0
  23. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_2152_pull_request_of_the_cuda_version_choice.md +0 -0
  24. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_2203_commit_message_of_the_cuda_version_tests.md +0 -0
  25. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_2213_commit_message_of_the_blackwell_version_choice.md +0 -0
  26. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/20260916_2213_pull_request_of_the_blackwell_version_choice.md +0 -0
  27. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/CLAUDE.md +0 -0
  28. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260909_1844_acquire_the_reflection_matrix.py +0 -0
  29. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260909_1900_test_on_a_synthetic_acquisition.py +0 -0
  30. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260909_1913_analyze_the_reflection_matrix.py +0 -0
  31. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260909_2107_redraw_the_figures_of_an_analysis.py +0 -0
  32. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260909_2121_write_the_result_tables.py +0 -0
  33. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260910_1126_synthesize_plane_waves_from_the_multistatic_matrix.py +0 -0
  34. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260910_1137_scan_the_speed_of_sound.py +0 -0
  35. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260910_1141_compare_the_laws_with_the_truth.py +0 -0
  36. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260910_1146_evaluate_the_reverberation_filter.py +0 -0
  37. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/20260910_1208_compare_plane_waves_with_the_synthesis.py +0 -0
  38. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/figures.py +0 -0
  39. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/ground_truth.py +0 -0
  40. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/matrix_imaging.py +0 -0
  41. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/pipeline.py +0 -0
  42. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/reflection_matrix.py +0 -0
  43. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/speed_of_sound.py +0 -0
  44. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/matrix_imaging/synthetic.py +0 -0
  45. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260821_0206_band_anchor_cross_check.py +0 -0
  46. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260821_0233_delivered_power_law_end_to_end.py +0 -0
  47. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260821_0317_reword_deliver_in_the_library.py +0 -0
  48. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260821_1220_relaxation_low_frequency_rolloff.py +0 -0
  49. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260825_1937_what_fails_the_pml_golden_tests.py +0 -0
  50. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260825_2329_does_the_wrapper_read_the_record.py +0 -0
  51. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260826_0002_what_the_new_default_table_holds.py +0 -0
  52. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260828_1422_what_the_candidate_tables_hold.py +0 -0
  53. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260828_1427_what_the_deployment_needs.py +0 -0
  54. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260828_1510_where_the_unstretched_ramp_reaches.py +0 -0
  55. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260828_1517_the_blast_radius_of_the_ramp.py +0 -0
  56. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1458_the_3d_region_trimmed_kernel_against_the_reference.py +0 -0
  57. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1505_the_multi_node_kernel_against_the_multi_gpu_one.py +0 -0
  58. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1631_the_3d_multi_node_against_the_multi_gpu_one.py +0 -0
  59. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1642_the_selective_absorbing_layer.py +0 -0
  60. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1728_the_layer_against_the_taper.py +0 -0
  61. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1757_what_the_margin_outside_the_layer_does.py +0 -0
  62. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1808_the_layer_in_3d_and_on_the_multi_node_builds.py +0 -0
  63. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1832_where_the_3d_residual_sits.py +0 -0
  64. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1837_inside_the_layer_at_the_corner.py +0 -0
  65. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1840_inside_the_3d_layer_at_the_corner.py +0 -0
  66. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1912_both_absorbers_on_one_method.py +0 -0
  67. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_1922_the_layer_on_the_reflection_method.py +0 -0
  68. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2011_whole_domain_animation.py +0 -0
  69. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2032_the_example_source_kind.py +0 -0
  70. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2148_drive_scale_of_the_strong_beta_cells.py +0 -0
  71. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2159_profile_the_input_map_build.py +0 -0
  72. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2201_build_then_extend_equivalence.py +0 -0
  73. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2304_the_transmit_methods_launch_what_they_say.py +0 -0
  74. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260830_2322_the_unscaled_added_drive_follows_the_courant_number.py +0 -0
  75. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0033_the_three_dimensional_arrays.py +0 -0
  76. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0054_the_elevation_lens_of_a_linear_array.py +0 -0
  77. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0115_run_every_example.sh +0 -0
  78. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0124_move_the_notebooks_to_the_transmit_interface.py +0 -0
  79. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0137_the_air_region_at_four_mechanisms.py +0 -0
  80. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0148_the_element_records_its_own_transmit.py +0 -0
  81. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0150_the_absorbing_layer_of_a_point_source.py +0 -0
  82. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0152_the_exponential_absorbing_layer_against_its_thickness.py +0 -0
  83. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0202_the_rebuilt_exponential_absorbing_layer.py +0 -0
  84. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0216_where_the_three_dimensional_layer_leaks.py +0 -0
  85. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_0222_the_absorbing_layer_away_from_the_source.py +0 -0
  86. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1037_where_the_near_field_brightness_comes_from.py +0 -0
  87. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1040_the_aperture_transit_wedge_in_the_channel_data.py +0 -0
  88. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1043_the_transmit_in_the_recorded_trace_and_its_removal.py +0 -0
  89. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1048_the_receive_row_against_the_reference_subtraction.py +0 -0
  90. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1052_the_backing_behind_the_array_face.py +0 -0
  91. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1056_the_transmit_beam_with_the_air_backing.py +0 -0
  92. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1105_the_shipped_transducer_stack_in_the_solver.py +0 -0
  93. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1119_where_the_stack_puts_the_source_and_what_it_costs_the_depth.py +0 -0
  94. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1121_the_depth_the_image_reads_with_the_stack.py +0 -0
  95. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1123_the_depth_the_beamformer_puts_one_point_at.py +0 -0
  96. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1142_the_transmit_offset_the_beamformer_needs.py +0 -0
  97. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1145_the_depth_shift_the_offset_fix_makes.py +0 -0
  98. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1154_the_two_offset_rules_on_the_same_recording.py +0 -0
  99. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1212_what_the_pinned_release_carries.py +0 -0
  100. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1228_the_low_cut_against_the_near_field_haze.py +0 -0
  101. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1301_run_the_beamforming_notebooks.py +0 -0
  102. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1432_point_dataset_generator_at_the_local_fullwave.py +0 -0
  103. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1437_the_aperture_width_the_reference_geometry_reports.py +0 -0
  104. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1454_calibrate_the_ats539_plane_wave.py +0 -0
  105. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1508_show_the_b_mode_in_the_hdf5.py +0 -0
  106. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1534_the_comet_behind_each_wire.py +0 -0
  107. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1547_read_the_calibration_trials.py +0 -0
  108. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1605_what_the_medium_holds_in_the_near_field.py +0 -0
  109. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1607_sweep_one_generator_knob.py +0 -0
  110. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1715_assert_the_ats539_rate_chain.py +0 -0
  111. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1717_measure_every_layer_of_the_stack.py +0 -0
  112. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1720_where_the_trailing_energy_lands.py +0 -0
  113. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1725_offer_the_requested_absorption_to_the_backing.py +0 -0
  114. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1727_let_a_sweep_carry_a_switch.py +0 -0
  115. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1731_expose_the_exponential_absorbing_layer.py +0 -0
  116. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1746_point_the_solver_at_an_older_binary.py +0 -0
  117. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1756_measure_the_point_targets.py +0 -0
  118. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1802_rederive_the_near_field_bounce.py +0 -0
  119. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1810_does_the_shelf_move_with_depth.py +0 -0
  120. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1812_find_the_repeat_delay_in_the_raw_rf.py +0 -0
  121. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1937_give_each_source_row_its_own_launch_delay.py +0 -0
  122. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1943_baffle_the_element_plane.py +0 -0
  123. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_1952_take_the_cell_size_from_the_grid.py +0 -0
  124. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2058_run_the_ats539_through_the_batched_solver.py +0 -0
  125. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2105_queue_the_remaining_candidates.py +0 -0
  126. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2122_the_simulator_asks_for_a_clamped_source.py +0 -0
  127. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2126_rename_the_source_types_to_hard_and_soft.py +0 -0
  128. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2140_bind_the_source_type_to_its_entrance.py +0 -0
  129. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2225_how_redundant_are_the_absorbing_layer_files.py +0 -0
  130. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2239_skip_the_constant_absorbing_layer_maps.py +0 -0
  131. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2246_count_the_files_a_run_writes.py +0 -0
  132. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2352_run_the_map_tiers_end_to_end.py +0 -0
  133. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260831_2355_draw_the_absorbing_layer_map_cost.py +0 -0
  134. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1502_can_the_transmit_reach_the_harmonic_band.py +0 -0
  135. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1504_contrast_harmonic_imaging.py +0 -0
  136. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1507_where_each_band_puts_its_largest_sample.py +0 -0
  137. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1519_is_the_harmonic_tail_cumulative.py +0 -0
  138. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1537_does_the_cancellation_work.py +0 -0
  139. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1612_does_a_sparse_bubble_cloud_localize.py +0 -0
  140. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1619_calibrate_the_bubble_scatter_range.py +0 -0
  141. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1622_draw_the_sweep_b_modes.py +0 -0
  142. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1654_where_the_bright_region_sits.py +0 -0
  143. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1701_where_one_bubble_images.py +0 -0
  144. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1702_does_the_portable_example_reproduce_the_evaluation.py +0 -0
  145. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1753_does_the_ported_solver_still_march_the_same.py +0 -0
  146. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1755_do_the_ranks_agree_on_the_bubbles.py +0 -0
  147. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1817_does_the_air_region_reach_every_solver.py +0 -0
  148. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260901_1818_does_the_air_region_reach_every_rank.py +0 -0
  149. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260902_1317_what_the_abdomen_volume_holds.py +0 -0
  150. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260902_1319_art_wave_in_the_abdomen.py +0 -0
  151. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260902_1324_art_figures_from_the_field.py +0 -0
  152. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260902_1744_art_final_animations.py +0 -0
  153. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260904_1216_art_wire_visibility.py +0 -0
  154. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260904_2327_b_mode_of_the_abdomen.py +0 -0
  155. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260905_0038_two_against_three_dimensions.py +0 -0
  156. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260909_1934_probe_the_reverberation_filter_on_speckle.py +0 -0
  157. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260909_1939_probe_the_ideal_profile_on_speckle.py +0 -0
  158. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260909_2044_probe_the_correction_bases_on_a_flat_speckle.py +0 -0
  159. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260909_2342_measure_the_wall_delay_screen.py +0 -0
  160. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1134_find_the_first_non_finite_value_of_the_plane_wave_correction.py +0 -0
  161. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1234_print_the_summaries_of_analyses.py +0 -0
  162. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/a_maps_checks.py +0 -0
  163. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/b_effective_frequency.py +0 -0
  164. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/c_composed_steps.py +0 -0
  165. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/check_element_centers.py +0 -0
  166. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/check_plane_wave_law.py +0 -0
  167. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/d2_gate_quarter_period.py +0 -0
  168. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/d_gate_cases.py +0 -0
  169. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/e_plate_crash.py +0 -0
  170. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/f_patch_grid.py +0 -0
  171. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/g_frequency_scan.py +0 -0
  172. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/gouy_check.py +0 -0
  173. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/h_pulse_and_wires.py +0 -0
  174. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/i_phase_slope.py +0 -0
  175. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a0200910_001.sh +0 -0
  176. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a0200910_002.sh +0 -0
  177. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a0200910_003.sh +0 -0
  178. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a0200910_004.sh +0 -0
  179. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a0200910_005.sh +0 -0
  180. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_001.sh +0 -0
  181. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_002.sh +0 -0
  182. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_003.sh +0 -0
  183. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_004.sh +0 -0
  184. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_005.sh +0 -0
  185. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_006.sh +0 -0
  186. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_007.sh +0 -0
  187. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_008.sh +0 -0
  188. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_009.sh +0 -0
  189. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a16f7afd_010.sh +0 -0
  190. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a3e03828_001.sh +0 -0
  191. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a3e03828_002.sh +0 -0
  192. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a3e03828_003.sh +0 -0
  193. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a3e03828_004.sh +0 -0
  194. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a3e03828_005.sh +0 -0
  195. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_001.sh +0 -0
  196. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_002.sh +0 -0
  197. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_003.sh +0 -0
  198. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_004.sh +0 -0
  199. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_005.sh +0 -0
  200. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_006.sh +0 -0
  201. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_007.sh +0 -0
  202. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_2a8c46c5-7d0_a86c0b02_008.sh +0 -0
  203. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_47c5d913-2f0_a86c7568_001.sh +0 -0
  204. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_001.sh +0 -0
  205. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_002.sh +0 -0
  206. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_003.sh +0 -0
  207. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_004.sh +0 -0
  208. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_005.sh +0 -0
  209. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_006.sh +0 -0
  210. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_007.sh +0 -0
  211. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_008.sh +0 -0
  212. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a09af998_009.sh +0 -0
  213. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_001.sh +0 -0
  214. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_002.sh +0 -0
  215. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_003.sh +0 -0
  216. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_004.sh +0 -0
  217. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_005.sh +0 -0
  218. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_006.sh +0 -0
  219. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a3c684a5_007.sh +0 -0
  220. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_001.sh +0 -0
  221. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_002.sh +0 -0
  222. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_003.sh +0 -0
  223. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_004.sh +0 -0
  224. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_005.sh +0 -0
  225. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_006.sh +0 -0
  226. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_007.sh +0 -0
  227. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_008.sh +0 -0
  228. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_009.sh +0 -0
  229. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_010.sh +0 -0
  230. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_011.sh +0 -0
  231. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_012.sh +0 -0
  232. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_013.sh +0 -0
  233. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_014.sh +0 -0
  234. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_015.sh +0 -0
  235. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/inline_commands/wf_714887dd-b46_a99b1764_016.sh +0 -0
  236. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/instrumented_guard_run.py +0 -0
  237. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/instrumented_plane_wave_nan.py +0 -0
  238. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/j_wires_in_wall.py +0 -0
  239. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/origin_checks.py +0 -0
  240. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/origin_das.py +0 -0
  241. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/quick_checks.py +0 -0
  242. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/scan_checks.py +0 -0
  243. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/scan_checks_2.py +0 -0
  244. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/scan_checks_3.py +0 -0
  245. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/scan_checks_4.py +0 -0
  246. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1239_matrix_imaging_agent_checks/toy_plane_wave.py +0 -0
  247. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1241_check_that_the_records_load.py +0 -0
  248. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1241_locate_the_non_finite_values_of_an_analysis.py +0 -0
  249. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1241_measure_the_wire_depth_against_the_time_origin.py +0 -0
  250. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1241_print_the_time_origin_scans.py +0 -0
  251. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1241_test_the_entropy_the_antisymmetry_and_the_filter_linearity.py +0 -0
  252. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1247_time_the_phase_reversal_per_patch_against_batched.py +0 -0
  253. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1248_compare_the_synthesis_on_the_card_and_the_host.py +0 -0
  254. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1250_save_and_compare_the_local_laws_of_a_subset.py +0 -0
  255. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1254_analyze_the_2.5mhz_stage_2.sh +0 -0
  256. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1256_print_the_per_depth_curves_of_a_reverberation_test.py +0 -0
  257. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1302_print_the_durations_of_a_chain_log.py +0 -0
  258. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1314_print_the_gouy_points_of_a_speed_of_sound_scan.py +0 -0
  259. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1445_fit_the_moveout_speed_of_the_point_sources.py +0 -0
  260. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1456_fit_the_depth_offset_of_the_reference_arrivals.py +0 -0
  261. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1502_print_the_laws_and_gates_of_an_analysis.py +0 -0
  262. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1503_analyze_with_the_ramp_removal_off_or_every_law_one.py +0 -0
  263. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1504_measure_the_anti_diagonal_share_of_the_clean_clutter.py +0 -0
  264. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1508_estimate_the_speed_with_smaller_windows.py +0 -0
  265. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1553_focus_the_clutter_records_in_sub_bands.py +0 -0
  266. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1553_print_the_band_and_the_screens_of_the_records.py +0 -0
  267. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1554_predict_the_phase_speed_of_the_stencil.py +0 -0
  268. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1556_compute_the_moveout_truth_with_an_eikonal_solver.py +0 -0
  269. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1556_fit_the_height_offset_on_three_grids.py +0 -0
  270. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1600_measure_the_flat_and_the_screen_correction_on_point_targets.py +0 -0
  271. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1604_predict_the_arrivals_from_the_stencil.py +0 -0
  272. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1604_prototype_the_broadband_correction.py +0 -0
  273. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1605_scan_the_time_origin_with_other_element_planes.py +0 -0
  274. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1607_measure_the_local_far_field_structure_of_the_clutter.py +0 -0
  275. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1608_score_the_speed_scans_against_the_eikonal_truth.py +0 -0
  276. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1610_compare_the_unwrapped_law_delays_with_the_truth.py +0 -0
  277. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1611_compare_the_arrival_models_at_the_point_sources.py +0 -0
  278. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1613_compare_the_sensor_pixel_with_the_element_center.py +0 -0
  279. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1613_measure_the_order_of_the_clutter_from_the_contrast_scaling.py +0 -0
  280. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1613_scan_the_speed_of_sound_at_one_time_origin.py +0 -0
  281. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1615_locate_the_source_and_sensor_pixels_of_the_array.py +0 -0
  282. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1617_check_the_data_level_correction_and_the_pulse_band.py +0 -0
  283. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1617_test_local_and_notch_filters_on_the_clutter.py +0 -0
  284. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1621_compute_the_moveout_truth_with_a_chosen_arrival_model.py +0 -0
  285. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1622_measure_the_tilt_spread_of_the_wall_interfaces.py +0 -0
  286. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1623_tabulate_the_broadband_correction_runs.py +0 -0
  287. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1626_measure_the_ghost_depths_of_the_plate.py +0 -0
  288. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1626_score_the_product_of_the_transducer_laws.py +0 -0
  289. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1630_test_the_local_filter_with_padded_windows.py +0 -0
  290. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1632_fit_the_time_origin_and_the_speed_of_the_phantom_scans.py +0 -0
  291. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1634_fit_the_reference_speed_at_the_sensor_row.py +0 -0
  292. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1646_split_the_clutter_into_orders_and_predict_its_floor.py +0 -0
  293. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1652_tabulate_the_pulse_band_check.py +0 -0
  294. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260910_1659_check_the_plane_wave_transform_of_the_elements.py +0 -0
  295. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1103_compare_two_b_mode_roots.py +0 -0
  296. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1214_binary_cache_lookup_order.py +0 -0
  297. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1253_signal_of_old_against_new.py +0 -0
  298. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1300_read_the_numexpr_pool_limit.py +0 -0
  299. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1332_gpu_memory_of_the_relaxation_kernel.py +0 -0
  300. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1401_relaxation_regions_against_the_kernel.py +0 -0
  301. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1420_released_binary_carries_the_fix.py +0 -0
  302. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1601_stable_courant_number_of_the_stencil_table.py +0 -0
  303. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1602_fast_layer_in_a_small_3d_run.py +0 -0
  304. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1719_p4_1_pulse_energy_above_the_trace_nyquist.py +0 -0
  305. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1933_l7_4_run_at_8_ppw_stability_and_trace_sampling.py +0 -0
  306. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_1946_l7_4_trace_sampling_by_field_modulus.py +0 -0
  307. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2004_pulse_bands_of_the_two_abdomen_arrays.py +0 -0
  308. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2155_cuda_version_choice_before_and_after.py +0 -0
  309. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2155_gpu_architectures_in_cached_binaries.py +0 -0
  310. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2155_mutation_check_of_the_cuda_version_tests.py +0 -0
  311. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2155_verify_the_cuda_version_test_findings.py +0 -0
  312. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260916_2212_mutation_check_of_the_blackwell_version_choice.py +0 -0
  313. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260925_1321_selected_cuda_tag.py +0 -0
  314. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260925_1326_release_binary_bit_identity.py +0 -0
  315. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/scratch/20260925_1331_summarize_release_check.py +0 -0
  316. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/settings.local.json +0 -0
  317. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/skills/fullwave25/SKILL.md +0 -0
  318. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/convex_transducer_3d.py +0 -0
  319. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/linear_transducer_3d.py +0 -0
  320. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/matrix_transducer_3d.py +0 -0
  321. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/patch.py +0 -0
  322. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/ruff.toml +0 -0
  323. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/superseded/convex_transducer_3d.py +0 -0
  324. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/superseded/linear_transducer_3d.py +0 -0
  325. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/superseded/matrix_transducer_3d.py +0 -0
  326. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/three_dimensional/test_patch.py +0 -0
  327. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/transducer_stack/README.md +0 -0
  328. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/transducer_stack/superseded/full_synthetic_aperture_with_stack.py +0 -0
  329. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.claude/transducer_stack/superseded/plane_wave_compounding_with_stack.py +0 -0
  330. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.gitignore +0 -0
  331. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.pre-commit-config.yaml +0 -0
  332. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/.python-version +0 -0
  333. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/20260120_0844.log +0 -0
  334. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/CITATION.cff +0 -0
  335. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/CONTRIBUTING.md +0 -0
  336. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/LICENSE +0 -0
  337. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/Makefile +0 -0
  338. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/attenuation.png +0 -0
  339. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/beamformer.m +0 -0
  340. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  341. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu +0 -0
  342. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  343. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
  344. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  345. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
  346. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/docker/Dockerfile +0 -0
  347. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/docker/Dockerfile.dockerignore +0 -0
  348. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/docker/README.md +0 -0
  349. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/experiments/__init__.py +0 -0
  350. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/experiments/exp_scatterer_diameter.py +0 -0
  351. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/beamformer/__init__.py +0 -0
  352. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/constants/__init__.py +0 -0
  353. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/constants/material_properties.py +0 -0
  354. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/grid.py +0 -0
  355. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium.py +0 -0
  356. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/__init__.py +0 -0
  357. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/domain.py +0 -0
  358. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/medium_builder.py +0 -0
  359. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/__init__.py +0 -0
  360. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/data/.keep +0 -0
  361. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
  362. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
  363. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/domain_background.py +0 -0
  364. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
  365. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/domain_simple.py +0 -0
  366. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
  367. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/sensor.py +0 -0
  368. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/__init__.py +0 -0
  369. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/binary_manager.py +0 -0
  370. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/binary_markers.py +0 -0
  371. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
  372. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  373. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
  374. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
  375. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
  376. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
  377. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
  378. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
  379. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/database/relaxation_params_database_num_relax=3_20260828_1513.mat +0 -0
  380. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/database/relaxation_params_database_num_relax=3_20260828_1513_invalid_cells.json +0 -0
  381. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/database/relaxation_params_database_num_relax=4_20260828_1456.mat +0 -0
  382. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/bins/database/relaxation_params_database_num_relax=4_20260828_1456_invalid_cells.json +0 -0
  383. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/cuda_utils.py +0 -0
  384. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/input_file_writer.py +0 -0
  385. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/launcher.py +0 -0
  386. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/pml_builder.py +0 -0
  387. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/shipped_database.py +0 -0
  388. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/source_type.py +0 -0
  389. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/solver/utils.py +0 -0
  390. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/source.py +0 -0
  391. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/transmit.py +0 -0
  392. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/__init__.py +0 -0
  393. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/dat_io.py +0 -0
  394. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/dat_io_3d.py +0 -0
  395. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/errors.py +0 -0
  396. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/factory.py +0 -0
  397. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/medium_to_aexp.py +0 -0
  398. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/stencil.py +0 -0
  399. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/time_axis.py +0 -0
  400. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/ultra/writer_3d.py +0 -0
  401. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/__init__.py +0 -0
  402. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/check_functions.py +0 -0
  403. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/coordinates.py +0 -0
  404. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/memory_tempfile.py +0 -0
  405. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/numerical.py +0 -0
  406. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/plot_utils.py +0 -0
  407. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/pulse.py +0 -0
  408. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/scatterer.py +0 -0
  409. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/signal_filter.py +0 -0
  410. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/fullwave/utils/signal_process.py +0 -0
  411. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/logs/20260815_0356_sweep.log +0 -0
  412. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/ruff.log +0 -0
  413. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/ruff.toml +0 -0
  414. {fullwave25-1.4.2.dev1 → fullwave25-1.4.2.dev3}/signal_filter_example.png +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.4.2-dev1"
2
+ current_version = "1.4.2-dev3"
3
3
  parse = """(?x)
4
4
  (?P<major>0|[1-9]\\d*)\\.
5
5
  (?P<minor>0|[1-9]\\d*)\\.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fullwave25
3
- Version: 1.4.2-dev1
3
+ Version: 1.4.2-dev3
4
4
  Summary: Fullwave 2.5: Ultrasound wave propagation simulation with heterogeneous power law attenuation modelling capabilities
5
5
  Author: Masashi Sode, Gianmarco Pinton
6
6
  Maintainer: Masashi Sode
@@ -417,6 +417,8 @@ traces = solver.run() # (n_sensors, n_frames)
417
417
  # 2D only: traces = solver.run_batch(sources) # (n_transmits, n_sensors, n_frames)
418
418
  ```
419
419
 
420
+ In 2D, `run_batch` treats `max_members_per_run` as an upper bound that the free GPU memory can lower, and it keeps the requested value, with a warning, when the free memory cannot be read. The native path uses an explicit `max_members_per_run` as given.
421
+
420
422
  Supported now:
421
423
 
422
424
  - 2D grids with `run` and `run_batch`, including air cells.
@@ -430,12 +432,16 @@ Supported now:
430
432
  - Velocity sources, and sources that are not pressure sources.
431
433
  - Sparse-grid sensors, `is_static_map`, and `record_whole_domain`.
432
434
  - Media other than `fullwave.Medium` (exponential attenuation), and `alpha_power` other than 1.
433
- - `use_pml=False`, the PML settings (set the sponge width with `exponential_attenuation_pml_thickness_px`), `m_spatial_order` other than 8, `use_gpu=False`, `save_gpu_memory`, and more than one GPU.
435
+ - `use_pml=False`, `m_spatial_order` other than 8, `use_gpu=False`, and an invalid `source_type`.
434
436
  - In 2D, a binary that does not read the files for air cells, for the additive channel, or for releasing the source (with such a binary, pad `p0` to `grid.nt`).
435
437
  - In 3D, air cells with a binary that does not read `icczero.dat` and `nzero0.dat`, and additive sources with a binary that does not read `ncoords_add.dat` and `icmat_add.dat`.
436
438
 
437
439
  This list will grow as the backend does.
438
440
 
441
+ The backend runs the exponential attenuation model (frequency-independent: the same attenuation at every frequency). Set the width of its absorbing sponge, in cells, with `ultra_sponge_thickness_px`; the default is 6 wavelengths in 2D and 4 wavelengths in 3D. `exponential_attenuation_pml_thickness_px` no longer sets this width with `use_ultra=True`; a script that relied on it now gets the default width until it passes `ultra_sponge_thickness_px`.
442
+
443
+ Options that only tune the native solver are ignored with a warning, once per process: `pml_layer_thickness_px`, `n_transition_layer`, `pml_alpha_entrance`, `exponential_attenuation_pml_thickness_px` (use `ultra_sponge_thickness_px` instead), `use_gpu_pml`, `save_gpu_memory`, `write_maps_the_solver_does_not_read`, `use_isotropic_relaxation=False`, and a `cuda_device_id` that lists more than one device (the run uses the first one listed). `use_exponential_attenuation=False`, the native default, also warns that the backend runs the exponential attenuation model, whose attenuation is frequency-independent, not the relaxation model, whose attenuation is frequency-dependent. Pass `use_exponential_attenuation=True` to silence it.
444
+
439
445
  ---
440
446
 
441
447
  ## Attention
@@ -376,6 +376,8 @@ traces = solver.run() # (n_sensors, n_frames)
376
376
  # 2D only: traces = solver.run_batch(sources) # (n_transmits, n_sensors, n_frames)
377
377
  ```
378
378
 
379
+ In 2D, `run_batch` treats `max_members_per_run` as an upper bound that the free GPU memory can lower, and it keeps the requested value, with a warning, when the free memory cannot be read. The native path uses an explicit `max_members_per_run` as given.
380
+
379
381
  Supported now:
380
382
 
381
383
  - 2D grids with `run` and `run_batch`, including air cells.
@@ -389,12 +391,16 @@ Supported now:
389
391
  - Velocity sources, and sources that are not pressure sources.
390
392
  - Sparse-grid sensors, `is_static_map`, and `record_whole_domain`.
391
393
  - Media other than `fullwave.Medium` (exponential attenuation), and `alpha_power` other than 1.
392
- - `use_pml=False`, the PML settings (set the sponge width with `exponential_attenuation_pml_thickness_px`), `m_spatial_order` other than 8, `use_gpu=False`, `save_gpu_memory`, and more than one GPU.
394
+ - `use_pml=False`, `m_spatial_order` other than 8, `use_gpu=False`, and an invalid `source_type`.
393
395
  - In 2D, a binary that does not read the files for air cells, for the additive channel, or for releasing the source (with such a binary, pad `p0` to `grid.nt`).
394
396
  - In 3D, air cells with a binary that does not read `icczero.dat` and `nzero0.dat`, and additive sources with a binary that does not read `ncoords_add.dat` and `icmat_add.dat`.
395
397
 
396
398
  This list will grow as the backend does.
397
399
 
400
+ The backend runs the exponential attenuation model (frequency-independent: the same attenuation at every frequency). Set the width of its absorbing sponge, in cells, with `ultra_sponge_thickness_px`; the default is 6 wavelengths in 2D and 4 wavelengths in 3D. `exponential_attenuation_pml_thickness_px` no longer sets this width with `use_ultra=True`; a script that relied on it now gets the default width until it passes `ultra_sponge_thickness_px`.
401
+
402
+ Options that only tune the native solver are ignored with a warning, once per process: `pml_layer_thickness_px`, `n_transition_layer`, `pml_alpha_entrance`, `exponential_attenuation_pml_thickness_px` (use `ultra_sponge_thickness_px` instead), `use_gpu_pml`, `save_gpu_memory`, `write_maps_the_solver_does_not_read`, `use_isotropic_relaxation=False`, and a `cuda_device_id` that lists more than one device (the run uses the first one listed). `use_exponential_attenuation=False`, the native default, also warns that the backend runs the exponential attenuation model, whose attenuation is frequency-independent, not the relaxation model, whose attenuation is frequency-dependent. Pass `use_exponential_attenuation=True` to silence it.
403
+
398
404
  ---
399
405
 
400
406
  ## Attention
@@ -66,7 +66,7 @@ try:
66
66
  __version__ = version("fullwave")
67
67
  except PackageNotFoundError:
68
68
  # Update via bump-my-version, not manually
69
- __version__ = "1.4.2-dev1"
69
+ __version__ = "1.4.2-dev3"
70
70
 
71
71
  VERSION = __version__ # for convenience
72
72
  logger.info("Fullwave version: %s", __version__)
@@ -14,6 +14,13 @@ class Beamformer:
14
14
  For faster implementations, consider using libraries such as
15
15
  mach beamformer: https://github.com/Forest-Neurotech/mach
16
16
 
17
+ Conventions: ``axial_position_m`` is measured from the top of the grid
18
+ (row 0), not from the array. ``transducer_coordinates`` must be the
19
+ receiver positions in grid points (for example from
20
+ ``Transducer.element_id_to_receiver_center``). The time origin is the
21
+ mean receiver row, so the array depth is subtracted from the two-way
22
+ travel time.
23
+
17
24
  References
18
25
  ----------
19
26
  - Fullwave2 BMME890 implementation
@@ -46,11 +53,11 @@ class Beamformer:
46
53
  lateral_position_m : np.ndarray
47
54
  Lateral positions (m) where the beamformed image will be computed.
48
55
  axial_position_m : np.ndarray
49
- Axial positions (m) where the beamformed image will be computed.
56
+ Axial positions (m) from the top of the grid where the image is computed.
50
57
  num_elements : int
51
58
  Number of transducer elements.
52
59
  transducer_coordinates : np.ndarray
53
- Coordinates of the transducer elements (m).
60
+ Receiver coordinates [n_elements, 2] in grid points (row, column).
54
61
  f_number : float, optional
55
62
  F-number for aperture calculation, by default 1.0.
56
63
 
@@ -83,6 +90,8 @@ class Beamformer:
83
90
  if signals.ndim == 2:
84
91
  signals = signals[:, :, np.newaxis]
85
92
  n_transmit = signals.shape[2]
93
+ # idt_0 is the pulse arrival at the receivers, so time is measured from the array.
94
+ z_array = np.mean(self.transducer_coordinates[:, 0]) * self.dx
86
95
 
87
96
  hilbert_signals = hilbert(signals, axis=1)
88
97
  beamformed_image = np.zeros(
@@ -133,7 +142,7 @@ class Beamformer:
133
142
  dd = np.round(dd / (self.dt / self.dx * self.c0)).astype(int)
134
143
  dd = dd - dd.min()
135
144
 
136
- idt = idt_0 + int(2 * axial / self.c0 / self.dt)
145
+ idt = idt_0 + int(2 * (axial - z_array) / self.c0 / self.dt)
137
146
 
138
147
  # idp: 1D indices into signals flattened array
139
148
  # we convert 2D indices to 1D indices
@@ -1,11 +1,8 @@
1
1
  """solver module."""
2
2
 
3
3
  import contextlib
4
- import gc
5
4
  import logging
6
5
  import os
7
- import shutil
8
- import subprocess
9
6
  import time
10
7
  import warnings
11
8
  from collections.abc import Iterator, Sequence
@@ -30,6 +27,11 @@ from fullwave.utils import (
30
27
  check_functions,
31
28
  run_directory_in_memory,
32
29
  )
30
+ from fullwave.utils.gpu_memory import (
31
+ free_gpu_memory_bytes,
32
+ members_that_fit,
33
+ release_gpu_memory_pools,
34
+ )
33
35
  from fullwave.utils.signal_filter import apply_filter
34
36
 
35
37
  from .binary_manager import ensure_binary
@@ -74,48 +76,6 @@ def _split_evenly(members: list, largest: int) -> list[list]:
74
76
  return runs
75
77
 
76
78
 
77
- def _free_gpu_memory_bytes(device_ids: Sequence[str]) -> dict[str, int] | None:
78
- """Return the free memory of each GPU in bytes, as nvidia-smi reports it.
79
-
80
- Parameters
81
- ----------
82
- device_ids : Sequence[str]
83
- The GPU indices, as nvidia-smi counts them.
84
-
85
- Returns
86
- -------
87
- dict[str, int] | None
88
- The free bytes of each GPU, or None when nvidia-smi cannot be run or does not
89
- report every GPU.
90
-
91
- """
92
- wanted = [device.strip() for device in device_ids]
93
- nvidia_smi_path = shutil.which("nvidia-smi")
94
- if nvidia_smi_path is None:
95
- return None
96
- try:
97
- found = subprocess.run( # noqa: S603
98
- [nvidia_smi_path, "--query-gpu=index,memory.free", "--format=csv,noheader,nounits"],
99
- capture_output=True,
100
- text=True,
101
- check=True,
102
- timeout=30,
103
- ).stdout
104
- except (OSError, subprocess.SubprocessError):
105
- return None
106
- free = {}
107
- for line in found.splitlines():
108
- index, _, mebibytes = line.partition(",")
109
- if index.strip() in wanted:
110
- try:
111
- free[index.strip()] = int(float(mebibytes)) * 1024**2
112
- except ValueError:
113
- return None
114
- if set(free) != set(wanted):
115
- return None
116
- return free
117
-
118
-
119
79
  COMPATIBLE_CUDA_ARCHITECTURES = [
120
80
  "sm_50", # Maxwell: GTX 750, GTX 750 Ti
121
81
  "sm_52", # Maxwell: GTX 980, GTX 970
@@ -513,6 +473,7 @@ class Solver:
513
473
  source_type: str = HARD_RELEASED_AFTER,
514
474
  write_maps_the_solver_does_not_read: bool = False,
515
475
  use_ultra: bool = False,
476
+ ultra_sponge_thickness_px: int | None = None,
516
477
  ) -> None:
517
478
  """Initialize a Solver instance for the fullwave simulation.
518
479
 
@@ -648,11 +609,17 @@ class Solver:
648
609
  Experimental. Run a user-supplied fullwave2-ultra binary (not shipped; pass it
649
610
  as ``path_fullwave_simulation_bin`` or set ``FW2U_BATCH_BIN``): ``bench_2d_batch``
650
611
  for a 2D grid, ``bench_3d_opt`` for a 3D grid.
651
- ``exponential_attenuation_pml_thickness_px`` sets the sponge width in cells.
652
- A source with ``p0_additive`` and ``source_type="soft"`` are added to the pressure
653
- through the binary's additive channel, which needs a binary that reads
654
- ``ncoords_add.dat`` (and ``icmat_add.dat`` on a 3D grid). Unsupported options raise
655
- ``UltraUnsupportedError``.
612
+ The backend runs the exponential attenuation model (frequency-independent:
613
+ the same attenuation at every frequency). A source with ``p0_additive`` and
614
+ ``source_type="soft"`` are added to the pressure through the binary's additive
615
+ channel, which needs a binary that reads ``ncoords_add.dat`` (and ``icmat_add.dat``
616
+ on a 3D grid). Options that only tune the native solver are ignored with a
617
+ warning; the README lists them.
618
+ Other unsupported options raise ``UltraUnsupportedError``.
619
+ ultra_sponge_thickness_px : int, optional
620
+ Width of the absorbing sponge of the ``use_ultra`` backend, in cells.
621
+ None, the default, gives 6 wavelengths in 2D and 4 wavelengths in 3D.
622
+ Used only with ``use_ultra=True``.
656
623
 
657
624
  Raises
658
625
  ------
@@ -676,21 +643,32 @@ class Solver:
676
643
  run_on_memory=run_on_memory,
677
644
  cuda_device_id=cuda_device_id,
678
645
  verify_gpu=verify_gpu,
679
- nbdy=exponential_attenuation_pml_thickness_px,
646
+ nbdy=ultra_sponge_thickness_px,
680
647
  options={
681
648
  "use_pml": use_pml,
682
649
  "m_spatial_order": m_spatial_order,
683
- "pml_layer_thickness_px": pml_layer_thickness_px,
684
- "n_transition_layer": n_transition_layer,
685
650
  "use_gpu": use_gpu,
686
- "use_gpu_pml": use_gpu_pml,
687
- "pml_alpha_entrance": pml_alpha_entrance,
688
651
  "source_type": source_type,
689
- "save_gpu_memory": save_gpu_memory,
690
- "write_maps_the_solver_does_not_read": write_maps_the_solver_does_not_read,
652
+ "use_exponential_attenuation": use_exponential_attenuation,
653
+ "native_only_options": {
654
+ "pml_layer_thickness_px": pml_layer_thickness_px,
655
+ "n_transition_layer": n_transition_layer,
656
+ "pml_alpha_entrance": pml_alpha_entrance,
657
+ "exponential_attenuation_pml_thickness_px": (
658
+ exponential_attenuation_pml_thickness_px
659
+ ),
660
+ "use_gpu_pml": use_gpu_pml,
661
+ "save_gpu_memory": save_gpu_memory,
662
+ "write_maps_the_solver_does_not_read": (
663
+ write_maps_the_solver_does_not_read
664
+ ),
665
+ "use_isotropic_relaxation": use_isotropic_relaxation,
666
+ },
691
667
  },
692
668
  )
693
669
  return
670
+ if ultra_sponge_thickness_px is not None:
671
+ backend.warn_sponge_without_ultra()
694
672
  if exponential_attenuation_pml_thickness_px is not None and not use_exponential_attenuation:
695
673
  error_msg = (
696
674
  "exponential_attenuation_pml_thickness_px belongs to the exponential "
@@ -968,31 +946,6 @@ class Solver:
968
946
  )
969
947
  logger.warning(message)
970
948
 
971
- @staticmethod
972
- def _release_gpu_memory_pools() -> None:
973
- """Release all CuPy GPU memory pool blocks back to CUDA.
974
-
975
- Call ``gc.collect()`` first so that Python releases references to
976
- CuPy arrays, then drain both the device and pinned memory pools.
977
- This prevents stale allocations from causing memory pressure when
978
- subsequent operations allocate large GPU arrays.
979
-
980
- The release is best effort. It does nothing when CuPy is absent, and
981
- it does nothing when CuPy is present without a usable CUDA driver,
982
- which is the case on a machine with no GPU.
983
- """
984
- gc.collect()
985
- try:
986
- import cupy as cp # noqa: PLC0415
987
- except ImportError:
988
- return
989
-
990
- try:
991
- cp.get_default_memory_pool().free_all_blocks()
992
- cp.get_default_pinned_memory_pool().free_all_blocks()
993
- except cp.cuda.runtime.CUDARuntimeError as error:
994
- logger.debug("GPU memory pools were not released: %s", error)
995
-
996
949
  @staticmethod
997
950
  def _check_input(
998
951
  grid: fullwave.Grid,
@@ -1420,10 +1373,10 @@ class Solver:
1420
1373
  "Input data generation completed in %s. Skipping simulation execution.",
1421
1374
  simulation_dir,
1422
1375
  )
1423
- self._release_gpu_memory_pools()
1376
+ release_gpu_memory_pools()
1424
1377
  return simulation_dir
1425
1378
 
1426
- self._release_gpu_memory_pools()
1379
+ release_gpu_memory_pools()
1427
1380
 
1428
1381
  if members is not None:
1429
1382
  return self._run_members(
@@ -1933,9 +1886,8 @@ class Solver:
1933
1886
  def _members_that_fit(self, sensor: fullwave.Sensor) -> int | None:
1934
1887
  """Return how many transmits the GPU memory estimate fits into the free memory.
1935
1888
 
1936
- The estimate of one GPU grows by the same amount for each transmit. The count
1937
- keeps 10 % of the free memory and 512 MiB free for the CUDA context and for
1938
- what the estimate does not count, and takes the smallest count over the GPUs.
1889
+ The estimate of one GPU grows by the same amount for each transmit. The count is the
1890
+ smallest over the GPUs.
1939
1891
 
1940
1892
  Parameters
1941
1893
  ----------
@@ -1948,18 +1900,21 @@ class Solver:
1948
1900
  The count, or None when the free memory cannot be read.
1949
1901
 
1950
1902
  """
1951
- free = _free_gpu_memory_bytes(self.fullwave_launcher.cuda_device_id.split(","))
1903
+ free = free_gpu_memory_bytes(self.fullwave_launcher.cuda_device_id.split(","))
1952
1904
  if free is None:
1953
1905
  return None
1954
1906
  one = self._gpu_memory_bytes(sensor, n_members=1)
1955
1907
  two = self._gpu_memory_bytes(sensor, n_members=2)
1956
- reserve = 512 * 1024**2
1957
1908
  counts = []
1958
1909
  for (device, one_bytes, *_), (_, two_bytes, *_) in zip(one, two, strict=True):
1959
- per_member = max(two_bytes - one_bytes, 1)
1960
- shared = one_bytes - per_member
1961
- usable = 0.9 * free.get(device, 0) - reserve - shared
1962
- counts.append(int(usable // per_member))
1910
+ per_member_bytes = max(two_bytes - one_bytes, 1)
1911
+ counts.append(
1912
+ members_that_fit(
1913
+ free_bytes=free.get(device, 0),
1914
+ shared_bytes=one_bytes - per_member_bytes,
1915
+ per_member_bytes=per_member_bytes,
1916
+ )
1917
+ )
1963
1918
  return min(counts)
1964
1919
 
1965
1920
  def _estimate_gpu_memory(
@@ -352,6 +352,9 @@ class TransducerGeometry:
352
352
  ) -> tuple[NDArray[np.int64], NDArray[np.int64], NDArray[np.int64], NDArray[np.int64]]:
353
353
  """Build flat coordinate arrays for source and sensor pixels.
354
354
 
355
+ For a flat array the sensor row is the first row in front of the element
356
+ (``position_px[0] + element_layer_px``), outside the source cells.
357
+
355
358
  Returns
356
359
  -------
357
360
  source_coords : NDArray[np.int64], shape [N_src, ndim]
@@ -404,7 +407,7 @@ class TransducerGeometry:
404
407
  source_ids_parts.append(np.full(len(src), element_index + 1))
405
408
 
406
409
  # Single center pixel for sensor output
407
- sx = element_pos_x + self.element_layer_px - 1
410
+ sx = self._flat_sensor_row(element_pos_x=element_pos_x)
408
411
  sy = element_pos_y + self.element_width_px - 1
409
412
  sz = element_pos_z + self.element_height_px // 2 - 1
410
413
  sensor_coords_parts.append(np.array([[sx, sy, sz]]))
@@ -432,7 +435,7 @@ class TransducerGeometry:
432
435
  source_coords_parts.append(src)
433
436
  source_ids_parts.append(np.full(len(src), element_index + 1))
434
437
 
435
- sx = element_pos_x + self.element_layer_px - 1
438
+ sx = self._flat_sensor_row(element_pos_x=element_pos_x)
436
439
  if self.average_surface_signals:
437
440
  out_ys = np.arange(element_pos_y, element_pos_y + self.element_width_px)
438
441
  out_xs = np.full(len(out_ys), sx)
@@ -452,6 +455,39 @@ class TransducerGeometry:
452
455
  sensor_ids = np.concatenate(sensor_ids_parts, axis=0).astype(np.int64)
453
456
  return source_coords, source_ids, sensor_coords, sensor_ids
454
457
 
458
+ def _flat_sensor_row(self, *, element_pos_x: int) -> int:
459
+ """Return the sensor row of a flat-array element.
460
+
461
+ The row lies in front of the source cells because a hard source keeps a
462
+ static pressure after the drive, which a receiver inside it would record.
463
+
464
+ Parameters
465
+ ----------
466
+ element_pos_x : int
467
+ First (back) source row of the element.
468
+
469
+ Returns
470
+ -------
471
+ int
472
+ The sensor row, ``element_pos_x + element_layer_px``.
473
+
474
+ Raises
475
+ ------
476
+ ValueError
477
+ If the sensor row is outside the grid.
478
+
479
+ """
480
+ sensor_row = int(element_pos_x + self.element_layer_px)
481
+ if sensor_row >= self.stored_grid_size[0]:
482
+ error_msg = (
483
+ f"The sensor row {sensor_row} (one row in front of the element) is "
484
+ f"outside the grid, which has {self.stored_grid_size[0]} rows in x. "
485
+ "Move the transducer back or enlarge the grid."
486
+ )
487
+ logger.error(error_msg)
488
+ raise ValueError(error_msg)
489
+ return sensor_row
490
+
455
491
  def _arc_element_coords(
456
492
  self,
457
493
  ) -> tuple[NDArray[np.int64], NDArray[np.int64], NDArray[np.int64], NDArray[np.int64]]:
@@ -1728,6 +1764,18 @@ class Transducer:
1728
1764
  out[eid] = np.rint(coords[sel].mean(axis=0)).astype(np.int64)
1729
1765
  return out
1730
1766
 
1767
+ @property
1768
+ def element_id_to_receiver_center(self) -> dict[int, NDArray[np.int64]]:
1769
+ """Return the mapping from element IDs to the mean receiver coordinate in grid points."""
1770
+ ndim = 3 if self.is_3d else 2
1771
+ out: dict[int, NDArray[np.int64]] = {}
1772
+ for eid, coords in self.element_id_to_element_surface.items():
1773
+ if len(coords) == 0:
1774
+ out[eid] = np.full(ndim, -1, dtype=np.int64)
1775
+ else:
1776
+ out[eid] = np.rint(coords.mean(axis=0)).astype(np.int64)
1777
+ return out
1778
+
1731
1779
  @property
1732
1780
  def sensor(self) -> fullwave.sensor.Sensor:
1733
1781
  """Return the Sensor object with active sensor coordinates.
@@ -2,10 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import inspect
5
6
  import time
6
7
  import warnings
7
8
  from pathlib import Path
8
- from typing import TYPE_CHECKING
9
+ from typing import TYPE_CHECKING, NamedTuple
9
10
 
10
11
  import numpy as np
11
12
 
@@ -36,15 +37,43 @@ if TYPE_CHECKING:
36
37
  CFL_LIMIT_3D = 0.30
37
38
  SPEED_OPTIMIZED_PPW = 6
38
39
 
40
+ PACKAGE_DIR = Path(__file__).parents[1]
41
+
39
42
  _shown_warnings: set[str] = set()
40
43
 
41
44
 
45
+ def _stacklevel_of_first_caller_outside_package() -> int:
46
+ """Return the ``warnings.warn`` stacklevel of the first frame outside the fullwave package."""
47
+ frame = inspect.currentframe()
48
+ stacklevel = 0
49
+ while frame is not None and Path(frame.f_code.co_filename).is_relative_to(PACKAGE_DIR):
50
+ frame = frame.f_back
51
+ stacklevel += 1
52
+ return stacklevel
53
+
54
+
42
55
  def _warn_once(*, name: str, message: str, category: type[Warning]) -> None:
43
- """Warn with ``message`` the first time ``name`` is seen in this process."""
56
+ """Warn with ``message`` the first time ``name`` is seen in this process.
57
+
58
+ The warning points at the line of the user's code, however deep the call is.
59
+ """
44
60
  if name in _shown_warnings:
45
61
  return
46
62
  _shown_warnings.add(name)
47
- warnings.warn(message, category, stacklevel=5)
63
+ warnings.warn(message, category, stacklevel=_stacklevel_of_first_caller_outside_package())
64
+
65
+
66
+ def warn_sponge_without_ultra() -> None:
67
+ """Warn once per process that ``ultra_sponge_thickness_px`` needs ``use_ultra=True``."""
68
+ _warn_once(
69
+ name="sponge_without_ultra",
70
+ message=(
71
+ "ultra_sponge_thickness_px is ignored without use_ultra=True. The native "
72
+ "absorber is sized by pml_layer_thickness_px (relaxation model) or "
73
+ "exponential_attenuation_pml_thickness_px (exponential model)."
74
+ ),
75
+ category=UserWarning,
76
+ )
48
77
 
49
78
 
50
79
  def warn_ppw_above_speed_optimum(*, ppw: int) -> None:
@@ -72,6 +101,25 @@ def warn_experimental() -> None:
72
101
  )
73
102
 
74
103
 
104
+ def warn_ignored(*, option: str, detail: str = "") -> None:
105
+ """Warn once per process that ``use_ultra=True`` ignores ``option``."""
106
+ suffix = f"; {detail}" if detail else ""
107
+ _warn_once(
108
+ name=f"ignored:{option}",
109
+ message=f"{option} is ignored with use_ultra=True{suffix}.",
110
+ category=UserWarning,
111
+ )
112
+
113
+
114
+ def warn_one_device_only(*, device: int) -> None:
115
+ """Warn once per process that ``use_ultra=True`` runs on one GPU, the first one listed."""
116
+ _warn_once(
117
+ name="one_device_only",
118
+ message=f"cuda_device_id lists several GPUs; use_ultra=True runs on GPU {device} only.",
119
+ category=UserWarning,
120
+ )
121
+
122
+
75
123
  def refuse(message: str) -> None:
76
124
  """Raise ``UltraUnsupportedError`` with ``message``."""
77
125
  raise UltraUnsupportedError(message)
@@ -122,9 +170,12 @@ def single_device(cuda_device_id: str | int | list | None) -> int:
122
170
  devices = list(cuda_device_id)
123
171
  else:
124
172
  devices = [cuda_device_id]
125
- if len(devices) != 1:
126
- refuse("use_ultra=True runs on one GPU; give one cuda_device_id")
127
- return int(devices[0])
173
+ if not devices:
174
+ refuse("cuda_device_id lists no device")
175
+ device = int(devices[0])
176
+ if len(devices) > 1:
177
+ warn_one_device_only(device=device)
178
+ return device
128
179
 
129
180
 
130
181
  def work_directory(
@@ -377,39 +428,70 @@ class UltraBackend3D:
377
428
  )
378
429
 
379
430
 
431
+ SPONGE_HINT = "set the sponge width with ultra_sponge_thickness_px"
432
+
433
+
434
+ class NativeOnlyOption(NamedTuple):
435
+ """An option of the native solver that ``use_ultra=True`` ignores."""
436
+
437
+ solver_default: object
438
+ hint: str = ""
439
+
440
+
441
+ NATIVE_ONLY_OPTIONS: dict[str, NativeOnlyOption] = {
442
+ "pml_layer_thickness_px": NativeOnlyOption(solver_default=None, hint=SPONGE_HINT),
443
+ "n_transition_layer": NativeOnlyOption(solver_default=None, hint=SPONGE_HINT),
444
+ "pml_alpha_entrance": NativeOnlyOption(solver_default=None, hint=SPONGE_HINT),
445
+ "exponential_attenuation_pml_thickness_px": NativeOnlyOption(
446
+ solver_default=None, hint=SPONGE_HINT
447
+ ),
448
+ "use_gpu_pml": NativeOnlyOption(solver_default=False),
449
+ "save_gpu_memory": NativeOnlyOption(solver_default=False),
450
+ "write_maps_the_solver_does_not_read": NativeOnlyOption(solver_default=False),
451
+ "use_isotropic_relaxation": NativeOnlyOption(solver_default=True),
452
+ }
453
+
454
+
455
+ def warn_native_only_options(*, options: dict[str, object]) -> None:
456
+ """Warn once per process for each native-only option set to a non-default value."""
457
+ for name, option in NATIVE_ONLY_OPTIONS.items():
458
+ if options[name] != option.solver_default:
459
+ warn_ignored(option=name, detail=option.hint)
460
+
461
+
462
+ def warn_relaxation_model_requested(*, use_exponential_attenuation: bool) -> None:
463
+ """Warn once per process when the relaxation model is requested, as the native default does."""
464
+ if use_exponential_attenuation:
465
+ return
466
+ _warn_once(
467
+ name="attenuation_model",
468
+ message=(
469
+ "use_ultra=True runs the exponential attenuation model, whose attenuation is "
470
+ "frequency-independent (the same at every frequency), not the relaxation model that "
471
+ "use_exponential_attenuation=False requests, whose attenuation is "
472
+ "frequency-dependent."
473
+ ),
474
+ category=UserWarning,
475
+ )
476
+
477
+
380
478
  def check_solver_options(
381
479
  *,
382
480
  use_pml: bool,
383
481
  m_spatial_order: int,
384
- pml_layer_thickness_px: int | None,
385
- n_transition_layer: int | None,
386
482
  use_gpu: bool,
387
- use_gpu_pml: bool,
388
- pml_alpha_entrance: float | None,
389
483
  source_type: str,
390
- save_gpu_memory: bool,
391
- write_maps_the_solver_does_not_read: bool,
484
+ native_only_options: dict[str, object],
485
+ use_exponential_attenuation: bool,
392
486
  ) -> None:
393
- """Refuse the ``Solver`` options that ``use_ultra=True`` cannot honor."""
487
+ """Refuse the ``Solver`` options that ``use_ultra=True`` cannot honor; warn on the rest."""
394
488
  if not use_pml:
395
489
  refuse("use_pml=False is not supported by use_ultra=True")
396
- if (
397
- pml_layer_thickness_px is not None
398
- or n_transition_layer is not None
399
- or use_gpu_pml
400
- or pml_alpha_entrance is not None
401
- ):
402
- refuse(
403
- "PML settings do not apply to use_ultra=True; "
404
- "set the sponge with exponential_attenuation_pml_thickness_px"
405
- )
406
490
  if m_spatial_order != 8:
407
491
  refuse("use_ultra=True supports m_spatial_order=8 only")
408
492
  if not use_gpu:
409
493
  refuse("use_ultra=True needs the GPU")
410
494
  if source_type not in SOURCE_TYPES:
411
495
  refuse(f"source_type must be one of {SOURCE_TYPES}, not {source_type!r}")
412
- if save_gpu_memory:
413
- refuse("save_gpu_memory is not supported by use_ultra=True")
414
- if write_maps_the_solver_does_not_read:
415
- refuse("write_maps_the_solver_does_not_read does not apply to use_ultra=True")
496
+ warn_native_only_options(options=native_only_options)
497
+ warn_relaxation_model_requested(use_exponential_attenuation=use_exponential_attenuation)