fullwave25 1.0.7__py3-none-any.whl

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.

Potentially problematic release.


This version of fullwave25 might be problematic. Click here for more details.

Files changed (225) hide show
  1. fullwave/__init__.py +28 -0
  2. fullwave/constants/__init__.py +5 -0
  3. fullwave/constants/material_properties.py +112 -0
  4. fullwave/grid.py +222 -0
  5. fullwave/medium.py +1042 -0
  6. fullwave/medium_builder/__init__.py +12 -0
  7. fullwave/medium_builder/domain.py +151 -0
  8. fullwave/medium_builder/medium_builder.py +198 -0
  9. fullwave/medium_builder/presets/__init__.py +8 -0
  10. fullwave/medium_builder/presets/data/.keep +0 -0
  11. fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
  12. fullwave/medium_builder/presets/domain_abdominal_wall.py +293 -0
  13. fullwave/medium_builder/presets/domain_background.py +140 -0
  14. fullwave/medium_builder/presets/domain_scatterer.py +179 -0
  15. fullwave/medium_builder/presets/domain_simple.py +92 -0
  16. fullwave/medium_builder/presets/domain_water_gel.py +1 -0
  17. fullwave/sensor.py +161 -0
  18. fullwave/solver/__init__.py +1 -0
  19. fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20251027_1437.mat +0 -0
  20. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenu +0 -0
  21. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_100_cuda129 +0 -0
  22. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_101_cuda129 +0 -0
  23. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_120_cuda129 +0 -0
  24. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda118 +0 -0
  25. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda124 +0 -0
  26. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda126 +0 -0
  27. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda129 +0 -0
  28. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda118 +0 -0
  29. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda124 +0 -0
  30. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda126 +0 -0
  31. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda129 +0 -0
  32. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda118 +0 -0
  33. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda124 +0 -0
  34. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda126 +0 -0
  35. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda129 +0 -0
  36. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda118 +0 -0
  37. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda124 +0 -0
  38. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda126 +0 -0
  39. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda129 +0 -0
  40. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda118 +0 -0
  41. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda124 +0 -0
  42. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda126 +0 -0
  43. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda129 +0 -0
  44. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda118 +0 -0
  45. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda126 +0 -0
  46. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda129 +0 -0
  47. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda118 +0 -0
  48. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda124 +0 -0
  49. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda126 +0 -0
  50. fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda129 +0 -0
  51. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_100_cuda129 +0 -0
  52. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_101_cuda129 +0 -0
  53. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_120_cuda129 +0 -0
  54. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda118 +0 -0
  55. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda124 +0 -0
  56. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda126 +0 -0
  57. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda129 +0 -0
  58. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda118 +0 -0
  59. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda124 +0 -0
  60. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda126 +0 -0
  61. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda129 +0 -0
  62. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda118 +0 -0
  63. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda124 +0 -0
  64. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda126 +0 -0
  65. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda129 +0 -0
  66. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda118 +0 -0
  67. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda124 +0 -0
  68. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda126 +0 -0
  69. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda129 +0 -0
  70. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda118 +0 -0
  71. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda124 +0 -0
  72. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda126 +0 -0
  73. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda129 +0 -0
  74. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda118 +0 -0
  75. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda124 +0 -0
  76. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda126 +0 -0
  77. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda129 +0 -0
  78. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda118 +0 -0
  79. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda124 +0 -0
  80. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda126 +0 -0
  81. fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda129 +0 -0
  82. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_100_cuda129 +0 -0
  83. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_101_cuda129 +0 -0
  84. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_120_cuda129 +0 -0
  85. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda118 +0 -0
  86. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda124 +0 -0
  87. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda126 +0 -0
  88. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda129 +0 -0
  89. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda118 +0 -0
  90. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda124 +0 -0
  91. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda126 +0 -0
  92. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda129 +0 -0
  93. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda118 +0 -0
  94. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda124 +0 -0
  95. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda126 +0 -0
  96. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda129 +0 -0
  97. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda118 +0 -0
  98. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda124 +0 -0
  99. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda126 +0 -0
  100. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda129 +0 -0
  101. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda118 +0 -0
  102. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda124 +0 -0
  103. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda126 +0 -0
  104. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda129 +0 -0
  105. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda118 +0 -0
  106. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda124 +0 -0
  107. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda126 +0 -0
  108. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda129 +0 -0
  109. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda118 +0 -0
  110. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda124 +0 -0
  111. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda126 +0 -0
  112. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda129 +0 -0
  113. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_100_cuda129 +0 -0
  114. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_101_cuda129 +0 -0
  115. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_120_cuda129 +0 -0
  116. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda118 +0 -0
  117. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda124 +0 -0
  118. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda126 +0 -0
  119. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda129 +0 -0
  120. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda118 +0 -0
  121. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda124 +0 -0
  122. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda126 +0 -0
  123. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda129 +0 -0
  124. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda118 +0 -0
  125. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda124 +0 -0
  126. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda126 +0 -0
  127. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda129 +0 -0
  128. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda118 +0 -0
  129. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda124 +0 -0
  130. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda126 +0 -0
  131. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda129 +0 -0
  132. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda118 +0 -0
  133. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda124 +0 -0
  134. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda126 +0 -0
  135. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda129 +0 -0
  136. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda118 +0 -0
  137. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda124 +0 -0
  138. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda126 +0 -0
  139. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda129 +0 -0
  140. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda118 +0 -0
  141. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda124 +0 -0
  142. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda126 +0 -0
  143. fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda129 +0 -0
  144. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_100_cuda129 +0 -0
  145. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_101_cuda129 +0 -0
  146. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_120_cuda129 +0 -0
  147. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda118 +0 -0
  148. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda124 +0 -0
  149. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda126 +0 -0
  150. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda129 +0 -0
  151. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda118 +0 -0
  152. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda124 +0 -0
  153. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda126 +0 -0
  154. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda129 +0 -0
  155. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda118 +0 -0
  156. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda124 +0 -0
  157. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda126 +0 -0
  158. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda129 +0 -0
  159. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda118 +0 -0
  160. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda124 +0 -0
  161. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda126 +0 -0
  162. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda129 +0 -0
  163. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda118 +0 -0
  164. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda124 +0 -0
  165. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda126 +0 -0
  166. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda129 +0 -0
  167. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda118 +0 -0
  168. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda124 +0 -0
  169. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda126 +0 -0
  170. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda129 +0 -0
  171. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda118 +0 -0
  172. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda124 +0 -0
  173. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda126 +0 -0
  174. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda129 +0 -0
  175. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_100_cuda129 +0 -0
  176. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_101_cuda129 +0 -0
  177. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_120_cuda129 +0 -0
  178. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda118 +0 -0
  179. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda124 +0 -0
  180. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda126 +0 -0
  181. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda129 +0 -0
  182. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda118 +0 -0
  183. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda124 +0 -0
  184. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda126 +0 -0
  185. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda129 +0 -0
  186. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda118 +0 -0
  187. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda124 +0 -0
  188. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda126 +0 -0
  189. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda129 +0 -0
  190. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda118 +0 -0
  191. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda124 +0 -0
  192. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda126 +0 -0
  193. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda129 +0 -0
  194. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda118 +0 -0
  195. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda124 +0 -0
  196. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda126 +0 -0
  197. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda129 +0 -0
  198. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda118 +0 -0
  199. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda124 +0 -0
  200. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda126 +0 -0
  201. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda129 +0 -0
  202. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda118 +0 -0
  203. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda124 +0 -0
  204. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda126 +0 -0
  205. fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda129 +0 -0
  206. fullwave/solver/cuda_utils.py +392 -0
  207. fullwave/solver/input_file_writer.py +853 -0
  208. fullwave/solver/launcher.py +134 -0
  209. fullwave/solver/pml_builder.py +1923 -0
  210. fullwave/solver/solver.py +750 -0
  211. fullwave/solver/utils.py +83 -0
  212. fullwave/source.py +173 -0
  213. fullwave/transducer.py +1003 -0
  214. fullwave/utils/__init__.py +12 -0
  215. fullwave/utils/check_functions.py +48 -0
  216. fullwave/utils/coordinates.py +155 -0
  217. fullwave/utils/memory_tempfile.py +439 -0
  218. fullwave/utils/numerical.py +111 -0
  219. fullwave/utils/plot_utils.py +1122 -0
  220. fullwave/utils/pulse.py +72 -0
  221. fullwave/utils/relaxation_parameters.py +212 -0
  222. fullwave/utils/signal_process.py +197 -0
  223. fullwave25-1.0.7.dist-info/METADATA +292 -0
  224. fullwave25-1.0.7.dist-info/RECORD +225 -0
  225. fullwave25-1.0.7.dist-info/WHEEL +4 -0
fullwave/transducer.py ADDED
@@ -0,0 +1,1003 @@
1
+ """Transducer class for Fullwave.
2
+
3
+ adapted and modified from k-wave-python
4
+ https://github.com/waltsims/k-wave-python/blob/4590a9445ebf8cdd2b719e32ee792d3752f2f55a/kwave/ktransducer.py
5
+ """
6
+
7
+ import logging
8
+ from functools import cached_property
9
+ from pathlib import Path
10
+
11
+ import numpy as np
12
+ from numpy.typing import NDArray
13
+
14
+ import fullwave
15
+ from fullwave.grid import Grid
16
+ from fullwave.utils import check_functions
17
+ from fullwave.utils.coordinates import make_circle_idx, map_to_coordinates, map_to_coords_with_sort
18
+
19
+ logger = logging.getLogger("__main__." + __name__)
20
+
21
+
22
+ def _make_pos_int(val: float | tuple[float] | tuple[int]) -> NDArray[np.int64]:
23
+ """Force value to be a positive integer.
24
+
25
+ Returns:
26
+ NDArray[np.int64]: Array with positive integers.
27
+
28
+ """
29
+ return np.array(val).astype(int).clip(min=0)
30
+
31
+
32
+ class TransducerGeometry:
33
+ """base transducer class."""
34
+
35
+ def __init__(
36
+ self,
37
+ grid: Grid,
38
+ number_elements: int = 128,
39
+ element_width_m: float | None = None,
40
+ element_height_m: float | None = None,
41
+ element_spacing_m: float | None = None,
42
+ position_m: tuple[float, float] | tuple[float, float, float] | None = None,
43
+ element_layer_m: float | None = None,
44
+ radius: float = float("inf"),
45
+ element_width_px: int | None = None,
46
+ element_height_px: int | None = None,
47
+ element_spacing_px: int | None = None,
48
+ element_layer_px: int | None = None,
49
+ position_px: tuple[int, int] | tuple[int, int, int] | None = None,
50
+ *,
51
+ validate_input: bool = True,
52
+ zero_offset: float = 0.0124,
53
+ ) -> None:
54
+ """Initialize base transducer class.
55
+
56
+ Parameters
57
+ ----------
58
+ grid: Grid
59
+ Grid object
60
+ number_elements:
61
+ the total number of transducer elements
62
+ element_width_m:
63
+ the width of each element in m
64
+ element_height_m:
65
+ the height of each element in m. only used for 3D simulations.
66
+ element_spacing_m:
67
+ the spacing (kerf width) between the transducer elements in m
68
+ position_m:
69
+ the position of the corner of the transducer in m
70
+ element_layer_m:
71
+ the thickness of the transducer elements in m
72
+ radius:
73
+ the radius of curvature of the transducer [m]
74
+ element_width_px:
75
+ the width of each transducer element in pixels
76
+ element_height_px:
77
+ the height of each transducer element in pixels.
78
+ element_spacing_px:
79
+ the spacing (kerf width) in pixels between transducer elements
80
+ element_layer_px:
81
+ the thickness of each transducer element in pixels
82
+ position_px:
83
+ the position of the transducer in pixels
84
+ validate_input: bool, optional
85
+ Flag indicating whether to validate the input data.
86
+ default is True.
87
+ zero_offset: float
88
+ The zero offset for the convex transducer position in meters.
89
+ default is 0.0124 m. This value is only used for convex transducers (radius < inf).
90
+
91
+ Raises
92
+ ------
93
+ ValueError
94
+ If neither pixel nor meter dimensions are provided
95
+ or if the transducer exceeds grid bounds.
96
+
97
+ """
98
+ if validate_input:
99
+ check_functions.check_instance(grid, Grid)
100
+ self.grid = grid
101
+ self.is_3d = grid.is_3d
102
+ (
103
+ self.element_width_px,
104
+ self.element_width_m,
105
+ self.element_height_px,
106
+ self.element_height_m,
107
+ self.element_spacing_px,
108
+ self.element_spacing_m,
109
+ self.element_layer_px,
110
+ self.element_layer_m,
111
+ ) = self._init_dimensions(
112
+ grid,
113
+ element_width_px,
114
+ element_width_m,
115
+ element_height_px,
116
+ element_height_m,
117
+ element_spacing_px,
118
+ element_spacing_m,
119
+ element_layer_px,
120
+ element_layer_m,
121
+ )
122
+ self.element_width_px = _make_pos_int(self.element_width_px)
123
+ self.element_spacing_px = _make_pos_int(self.element_spacing_px)
124
+
125
+ self.stored_grid_size = (
126
+ [
127
+ grid.nx,
128
+ grid.ny,
129
+ grid.nz,
130
+ ]
131
+ if self.is_3d
132
+ else [
133
+ grid.nx,
134
+ grid.ny,
135
+ ]
136
+ )
137
+ # size of the grid in which the transducer is defined
138
+ self.grid_spacing = (
139
+ [
140
+ grid.dx,
141
+ grid.dy,
142
+ grid.dz,
143
+ ]
144
+ if self.is_3d
145
+ else [
146
+ grid.dx,
147
+ grid.dy,
148
+ ]
149
+ )
150
+ # corresponding grid spacing
151
+
152
+ self.number_elements = _make_pos_int(number_elements)
153
+
154
+ self.position_px, self.position_m = self._init_positions(position_px, position_m)
155
+
156
+ self.radius = radius
157
+ self.zero_offset = zero_offset
158
+
159
+ # check the transducer fits into the grid
160
+ if (
161
+ self.position_px[1]
162
+ + self.number_elements * self.element_width_px
163
+ + (self.number_elements - 1) * self.element_spacing_px
164
+ ) > self.stored_grid_size[1] and self.radius == float("inf"):
165
+ error_msg = (
166
+ "The defined transducer is too large or"
167
+ "positioned outside the grid in the y-direction:\n"
168
+ f"position_px: {self.position_px[1]}, "
169
+ f"number_elements: {self.number_elements}, "
170
+ f"element_width_px: {self.element_width_px}, "
171
+ f"element_spacing_px: {self.element_spacing_px}, "
172
+ f"ny: {self.stored_grid_size[1]}, "
173
+ f"transducer_width_px: {self.transducer_width_px}, "
174
+ )
175
+ raise ValueError(error_msg)
176
+ # if (self.position_px[2] + self.element_length_px) > self.stored_grid_size[2]:
177
+ # logger.info(self.position_px[2])
178
+ # logger.info(self.element_length_px)
179
+ # logger.info(self.stored_grid_size[2])
180
+ # error_msg = (
181
+ # "The defined transducer is too large or"
182
+ # " positioned outside the grid in the z-direction"
183
+ # )
184
+ # raise ValueError(
185
+ # error_msg,
186
+ # )
187
+ if self.position_px[0] > self.stored_grid_size[0]:
188
+ error_msg = "The defined transducer is positioned outside the grid in the x-direction"
189
+ raise ValueError(error_msg)
190
+
191
+ # create the transducer mask
192
+ self.indexed_element_mask_input, self.indexed_element_mask_output = (
193
+ self._create_element_mask()
194
+ )
195
+ self.element_mask_input = self.indexed_element_mask_input > 0
196
+ self.element_mask_output = self.indexed_element_mask_output > 0
197
+
198
+ def _init_dimensions( # noqa: C901, PLR0912
199
+ self,
200
+ grid: Grid,
201
+ element_width_px: int | None,
202
+ element_width_m: float | None,
203
+ element_height_px: int | None,
204
+ element_height_m: float | None,
205
+ element_spacing_px: int | None,
206
+ element_spacing_m: float | None,
207
+ element_layer_px: int | None,
208
+ element_layer_m: float | None,
209
+ ) -> tuple[int, float, int | None, float | None, int, float, int, float]:
210
+ # Initialize element dimensions by converting between meters and pixels.
211
+ if element_width_px is None and element_width_m is not None:
212
+ element_width_px = round(element_width_m / grid.dy)
213
+ element_width_px = max(1, element_width_px)
214
+ self.use_px_in_width = False
215
+ elif element_width_px is not None and element_width_m is None:
216
+ element_width_m = element_width_px * grid.dy
217
+ self.use_px_in_width = True
218
+ else:
219
+ error_msg = "Either element_width_px or element_width_m must be provided"
220
+ raise ValueError(error_msg)
221
+
222
+ if self.is_3d is True and element_height_px is None and element_height_m is not None:
223
+ element_height_px = round(element_height_m / grid.dz)
224
+ element_height_px = max(1, element_height_px)
225
+ self.use_px_in_width = False
226
+ elif self.is_3d is True and element_height_px is not None and element_height_m is None:
227
+ element_height_m = element_height_px * grid.dz
228
+ self.use_px_in_width = True
229
+ elif self.is_3d is True and (element_height_px is None and element_height_m is None):
230
+ error_msg = "Either element_height_px or element_height_m must be provided"
231
+ raise ValueError(error_msg)
232
+ elif self.is_3d is False and (
233
+ element_height_px is not None or element_height_m is not None
234
+ ):
235
+ warning_msg = (
236
+ "element_height_px and element_height_m are provided, "
237
+ "but the transducer is not 3D. "
238
+ "Ignoring element_height_px and element_height_m."
239
+ )
240
+ logger.warning(warning_msg)
241
+ else:
242
+ element_height_px = 0
243
+ element_height_m = 0.0
244
+
245
+ if element_spacing_px is None and element_spacing_m is not None:
246
+ element_spacing_px = round(element_spacing_m / grid.dy)
247
+ element_spacing_px = max(0, element_spacing_px)
248
+ self.use_px_in_space = False
249
+ elif element_spacing_px is not None and element_spacing_m is None:
250
+ element_spacing_m = element_spacing_px * grid.dy
251
+ self.use_px_in_space = True
252
+ else:
253
+ error_msg = "Either element_spacing_px or element_spacing_m must be provided"
254
+ raise ValueError(error_msg)
255
+
256
+ if element_layer_px is None and element_layer_m is not None:
257
+ element_layer_px = round(element_layer_m / grid.dy)
258
+ element_layer_px = max(1, element_layer_px)
259
+ elif element_layer_px is not None and element_layer_m is None:
260
+ element_layer_m = element_layer_px * grid.dy
261
+ else:
262
+ error_msg = "Either element_layer_px or element_layer_m must be provided"
263
+ raise ValueError(error_msg)
264
+
265
+ return (
266
+ element_width_px,
267
+ max(0, element_width_m),
268
+ element_height_px if self.is_3d else None,
269
+ max(0, element_height_m) if self.is_3d else None,
270
+ element_spacing_px,
271
+ max(0, element_spacing_m),
272
+ element_layer_px,
273
+ max(0, element_layer_m),
274
+ )
275
+
276
+ def _init_positions(self, position_px: int, position_m: float) -> tuple[int, float]:
277
+ if position_px is None and position_m is None:
278
+ position_px = (1, 1, 1) if self.is_3d else (1, 1)
279
+ position_px = _make_pos_int(position_px)
280
+ position_m = [
281
+ pos * grid_spacing
282
+ for pos, grid_spacing in zip(position_px, self.grid_spacing, strict=False)
283
+ ]
284
+ elif position_px is not None and position_m is None:
285
+ position_m = [
286
+ pos * grid_spacing
287
+ for pos, grid_spacing in zip(position_px, self.grid_spacing, strict=False)
288
+ ]
289
+ position_px = _make_pos_int(position_px)
290
+ elif position_px is None and position_m is not None:
291
+ position_px = [
292
+ round(pos / grid_spacing)
293
+ for pos, grid_spacing in zip(position_m, self.grid_spacing, strict=False)
294
+ ]
295
+ position_px = _make_pos_int(position_px)
296
+ else:
297
+ error_msg = "Either position_px or position_m must be provided"
298
+ raise ValueError(error_msg)
299
+ if self.is_3d:
300
+ assert len(position_px) == 3, "position_px must have 3 elements for 3D transducer"
301
+ assert len(position_m) == 3, "position_m must have 3 elements for 3D transducer"
302
+ return position_px, position_m
303
+
304
+ def _create_element_mask(self) -> tuple[NDArray[np.int64], ...]:
305
+ indexed_element_mask_input = np.zeros(self.stored_grid_size, dtype=int)
306
+ indexed_element_mask_output = np.zeros(self.stored_grid_size, dtype=int)
307
+ if self.radius == float("inf"):
308
+ if self.is_3d:
309
+ for element_index in range(self.number_elements):
310
+ element_pos_x = self.position_px[0]
311
+ element_pos_y = round(
312
+ (
313
+ self.position_m[1]
314
+ + (self.element_width_m + self.element_spacing_m) * element_index
315
+ )
316
+ / self.grid_spacing[1],
317
+ )
318
+ element_pos_z = round(self.position_m[2] / self.grid_spacing[2])
319
+ if self.use_px_in_space or self.use_px_in_width:
320
+ element_pos_y = (
321
+ self.position_px[1]
322
+ + (self.element_width_px + self.element_spacing_px) * element_index
323
+ )
324
+ indexed_element_mask_input[
325
+ element_pos_x : element_pos_x + self.element_layer_px,
326
+ element_pos_y : element_pos_y + self.element_width_px,
327
+ element_pos_z : element_pos_z + self.element_height_px,
328
+ ] = element_index + 1
329
+ indexed_element_mask_output[
330
+ element_pos_x + self.element_layer_px - 1,
331
+ element_pos_y + self.element_width_px - 1,
332
+ element_pos_z + self.element_height_px // 2 - 1,
333
+ ] = element_index + 1
334
+ else:
335
+ for element_index in range(self.number_elements):
336
+ element_pos_x = self.position_px[0]
337
+ element_pos_y = round(
338
+ (
339
+ self.position_m[1]
340
+ + (self.element_width_m + self.element_spacing_m) * element_index
341
+ )
342
+ / self.grid_spacing[1],
343
+ )
344
+ if self.use_px_in_space or self.use_px_in_width:
345
+ element_pos_y = (
346
+ self.position_px[1]
347
+ + (self.element_width_px + self.element_spacing_px) * element_index
348
+ )
349
+ indexed_element_mask_input[
350
+ element_pos_x : element_pos_x + self.element_layer_px,
351
+ element_pos_y : element_pos_y + self.element_width_px,
352
+ ] = element_index + 1
353
+ indexed_element_mask_output[
354
+ element_pos_x + self.element_layer_px - 1,
355
+ element_pos_y + self.element_width_px // 2 - 1,
356
+ ] = element_index + 1
357
+ elif self.is_3d:
358
+ error_msg = "3D convex transducers are not implemented yet."
359
+ raise NotImplementedError(error_msg)
360
+ else:
361
+ radius_px = round(self.radius / self.grid.dx)
362
+ d_theta = np.arctan2(self.element_spacing_m / self.grid.dy, radius_px)
363
+ theta_list = self._define_theta_at_center(
364
+ d_theta=d_theta,
365
+ num_elements=self.number_elements,
366
+ )
367
+ center = np.array(
368
+ [
369
+ self.zero_offset / self.grid.dx - radius_px,
370
+ self.grid.ny // 2,
371
+ ],
372
+ )
373
+ in_map = self._calculate_inmap(
374
+ center=center,
375
+ radius=radius_px,
376
+ )
377
+ out_map = self._calculate_outmap(
378
+ center=center,
379
+ radius=radius_px,
380
+ )
381
+
382
+ in_coords = map_to_coords_with_sort(in_map)
383
+ out_coords = map_to_coords_with_sort(out_map)
384
+ in_coords, out_coords = self._assign_transducer_num_to_input(
385
+ in_coords=in_coords,
386
+ out_coords=out_coords,
387
+ center=center,
388
+ number_elements=self.number_elements,
389
+ d_theta=d_theta,
390
+ theta_list=theta_list,
391
+ )
392
+ indexed_element_mask_input = self._coords_to_index_map(
393
+ in_coords,
394
+ grid_shape=self.stored_grid_size,
395
+ )
396
+ indexed_element_mask_output = self._coords_to_index_map(
397
+ out_coords,
398
+ grid_shape=self.stored_grid_size,
399
+ )
400
+
401
+ return indexed_element_mask_input, indexed_element_mask_output
402
+
403
+ @staticmethod
404
+ def _coords_to_index_map(
405
+ coords: NDArray[np.float64],
406
+ grid_shape: NDArray[np.float64],
407
+ ) -> NDArray[np.int64]:
408
+ indexed_element_mask = np.zeros(grid_shape, dtype=int)
409
+ for coord in coords:
410
+ x = coord[0]
411
+ y = coord[1]
412
+ index = coord[3]
413
+ indexed_element_mask[x.astype(int), y.astype(int)] = index
414
+ return indexed_element_mask
415
+
416
+ def _assign_transducer_num_to_input(
417
+ self,
418
+ in_coords: NDArray[np.float64],
419
+ out_coords: NDArray[np.float64],
420
+ center: NDArray[np.float64],
421
+ number_elements: int,
422
+ d_theta: float,
423
+ theta_list: NDArray[np.float64],
424
+ ) -> tuple[NDArray[np.float64], ...]:
425
+ # Assign which transducer number is assigned to each input.
426
+ thetas_in = np.arctan2(in_coords[:, 1] - center[1], in_coords[:, 0] - center[0])
427
+ thetas_out = np.arctan2(out_coords[:, 1] - center[1], out_coords[:, 0] - center[0])
428
+
429
+ # out_coords2 = np.zeros((number_elements, 2))
430
+ # in_coords2 = np.zeros((number_elements, 2))
431
+
432
+ in_coords = np.append(in_coords, np.zeros((in_coords.shape[0], 2)), axis=1)
433
+ in_coords[:, 2] = 0
434
+ in_coords[:, 3] = 0
435
+
436
+ out_coords = np.append(out_coords, np.zeros((out_coords.shape[0], 2)), axis=1)
437
+ out_coords[:, 2] = 0
438
+ out_coords[:, 3] = 0
439
+
440
+ for tt in range(number_elements):
441
+ # find which incoords are assigned to tt
442
+ less_than_max = thetas_in < (theta_list[tt] + d_theta / 2)
443
+ greater_than_min = thetas_in > (theta_list[tt] - d_theta / 2)
444
+ id_theta = np.where(np.logical_and(less_than_max, greater_than_min))[0]
445
+ in_coords[id_theta, 3] = tt + 1
446
+ # in_coords2[tt, 0] = np.mean(in_coords[id_theta, 0])
447
+ # in_coords2[tt, 1] = np.mean(in_coords[id_theta, 1])
448
+
449
+ # find which outcoords are assigned to tt
450
+ less_than_max = thetas_out < (theta_list[tt] + d_theta / 2)
451
+ greater_than_min = thetas_out > (theta_list[tt] - d_theta / 2)
452
+ id_theta = np.where(np.logical_and(less_than_max, greater_than_min))[0]
453
+ out_coords[id_theta, 3] = tt + 1
454
+ # out_coords2[tt, 0] = np.mean(out_coords[id_theta, 0])
455
+ # out_coords2[tt, 1] = np.mean(out_coords[id_theta, 1])
456
+ return in_coords, out_coords
457
+
458
+ @staticmethod
459
+ def _define_theta_at_center(d_theta: float, num_elements: int) -> NDArray[np.float64]:
460
+ thetas = d_theta * (np.arange((-(num_elements - 1) / 2), ((num_elements - 1) / 2) + 1))
461
+ for n in np.arange(num_elements):
462
+ thetas[n] = (n + 1) * d_theta
463
+
464
+ return thetas - np.mean(thetas)
465
+
466
+ def _calculate_inmap(self, center: NDArray[np.float64], radius: float) -> np.ndarray:
467
+ # Make a circle that defines the transducer surface
468
+ in_map = np.zeros((self.grid.nx, self.grid.ny))
469
+ in_map[make_circle_idx(in_map.shape, center, radius)] = 1
470
+ output_map = np.zeros((self.grid.nx, self.grid.ny))
471
+
472
+ # make outcoords from iccoords
473
+ # Grab the coords on edge of the circle - larger circle for outcoords
474
+ for i in range(self.grid.ny):
475
+ # find inmap coords
476
+ j = np.where(in_map[:, i] == 1)[0]
477
+ if j.shape[0] == 0:
478
+ continue
479
+ j = j[-1]
480
+
481
+ output_map[j - self.element_layer_px : j, i] = 1
482
+
483
+ return output_map
484
+
485
+ def _calculate_outmap(self, center: NDArray[np.float64], radius: float) -> np.ndarray:
486
+ # Make a circle that defines the transducer surface
487
+ out_map = np.zeros((self.grid.nx, self.grid.ny))
488
+ out_map[make_circle_idx(out_map.shape, center, radius)] = 1
489
+ output_map = np.zeros((self.grid.nx, self.grid.ny))
490
+
491
+ # make outcoords from iccoords
492
+ # Grab the coords on edge of the circle - larger circle for outcoords
493
+ for i in range(self.grid.ny):
494
+ # find inmap coords
495
+ j = np.where(out_map[:, i] == 1)[0]
496
+ if j.shape[0] == 0:
497
+ continue
498
+ j = j[-1]
499
+
500
+ output_map[j - 1, i] = 1
501
+
502
+ return output_map
503
+
504
+ @cached_property
505
+ def indexed_element_mask_input_px(self) -> NDArray[np.int64]:
506
+ """Return the pixel wise indexed element mask."""
507
+ out_map = np.zeros_like(self.element_mask_input, dtype=int)
508
+ coordinates = map_to_coordinates(self.element_mask_input).T
509
+ index = 1
510
+ for i in range(len(coordinates)):
511
+ x = coordinates[i][0]
512
+ y = coordinates[i][1]
513
+ out_map[x.astype(int), y.astype(int)] = index
514
+ index += 1
515
+ return out_map
516
+
517
+ @property
518
+ def element_pitch_m(self) -> float:
519
+ """Compute the pitch of the transducer elements in the y-direction."""
520
+ return self.element_spacing_m + self.element_width_m
521
+
522
+ @property
523
+ def element_pitch_px(self) -> int:
524
+ """Compute the pitch of the transducer elements in the y-direction."""
525
+ return round(self.element_pitch_m / self.grid_spacing[1])
526
+
527
+ @property
528
+ def transducer_width_m(self) -> float:
529
+ """Total width of the transducer in meter.
530
+
531
+ Returns
532
+ -------
533
+ int
534
+ Total width of the transducer in meter
535
+
536
+ """
537
+ return float(
538
+ self.number_elements * self.element_width_m
539
+ + (self.number_elements - 1) * self.element_spacing_m,
540
+ )
541
+
542
+ @property
543
+ def transducer_width_px(self) -> int:
544
+ """Total width of the transducer in grid points.
545
+
546
+ Returns
547
+ -------
548
+ int
549
+ Total width of the transducer in grid points.
550
+
551
+ """
552
+ return int(
553
+ self.number_elements * self.element_width_px
554
+ + (self.number_elements - 1) * self.element_spacing_px,
555
+ )
556
+
557
+ @property
558
+ def n_sources(self) -> NDArray[np.int64]:
559
+ """Return the number of source elements."""
560
+ return self.element_mask_input.sum()
561
+
562
+ @property
563
+ def n_sources_per_element(self) -> NDArray[np.int64]:
564
+ """Return the number of source elements."""
565
+ return self.element_mask_input.sum() // self.number_elements
566
+
567
+ def __str__(self) -> str:
568
+ """Return string representation of the TransducerGeometry.
569
+
570
+ Returns
571
+ -------
572
+ str
573
+ String representation of the TransducerGeometry.
574
+
575
+ """
576
+ return (
577
+ f"TransducerGeometry:\n"
578
+ f" Number of elements: {self.number_elements}\n"
579
+ f" Element width (m): {self.element_width_m}\n"
580
+ f" Element height (m): {self.element_height_m}\n"
581
+ f" Element spacing (m): {self.element_spacing_m}\n"
582
+ f" Element layer (m): {self.element_layer_m}\n"
583
+ f" Position (m): {self.position_m}\n"
584
+ f" Radius (m): {self.radius}\n"
585
+ f" Element width (px): {self.element_width_px}\n"
586
+ f" Element height (px): {self.element_height_px}\n"
587
+ f" Element spacing (px): {self.element_spacing_px}\n"
588
+ f" Element layer (px): {self.element_layer_px}\n"
589
+ f" Position (px): {self.position_px}\n"
590
+ )
591
+
592
+
593
+ class Transducer:
594
+ """General transducer class.
595
+
596
+ it connects transducer geometry with fullwave Source and Sensor implementations.
597
+ """
598
+
599
+ def __init__(
600
+ self,
601
+ transducer_geometry: TransducerGeometry,
602
+ grid: Grid,
603
+ input_signal: NDArray[np.float64] | None = None,
604
+ active_source_elements: tuple[bool] | None = None,
605
+ active_sensor_elements: tuple[bool] | None = None,
606
+ *,
607
+ validate_input: bool = True,
608
+ sampling_modulus_time: int = 1,
609
+ ) -> None:
610
+ """Initialize the GeneralTransducer with the provided geometry, grid, and input signal.
611
+
612
+ Parameters
613
+ ----------
614
+ transducer_geometry: TransducerGeometry
615
+ TransducerGeometry object. it defines the geometry of the transducer.
616
+ grid: Grid
617
+ Grid object. it defines the spatial and temporal grid.
618
+ input_signal: NDArray
619
+ source signal emmited by the transducer elements. it has shape (number_elements, nt)
620
+ active_source_elements: tuple[bool] | None
621
+ boolean array that defines which elements are active sources.
622
+ if None, all elements are active.
623
+ active_sensor_elements: tuple[bool] | None
624
+ boolean array that defines which elements are active sensors.
625
+ if None, all elements are active.
626
+ validate_input: bool, optional
627
+ Flag indicating whether to validate the input data.
628
+ default is True.
629
+ sampling_modulus_time: int
630
+ Sampling modulus in time. Default is 1 (record at every time step).
631
+ Changing this value to n will record the pressure every n time steps.
632
+ It reduces the size of the output data.
633
+
634
+ """
635
+ if validate_input:
636
+ check_functions.check_instance(transducer_geometry, TransducerGeometry)
637
+ check_functions.check_instance(grid, Grid)
638
+
639
+ self.transducer_geometry = transducer_geometry
640
+ self.grid = grid
641
+ self.is_3d = grid.is_3d
642
+
643
+ if active_source_elements is None:
644
+ active_source_elements = np.ones(transducer_geometry.number_elements, dtype=bool)
645
+ self.active_source_elements = np.array(active_source_elements)
646
+
647
+ if active_sensor_elements is None:
648
+ active_sensor_elements = np.ones(transducer_geometry.number_elements, dtype=bool)
649
+ self.active_sensor_elements = active_sensor_elements
650
+
651
+ self.sampling_modulus_time = sampling_modulus_time
652
+
653
+ if input_signal is not None:
654
+ self._check_signal(input_signal)
655
+ self._signal: NDArray[np.float64] | None = input_signal
656
+ else:
657
+ self._signal = None
658
+
659
+ def _check_signal(self, signal: NDArray[np.float64]) -> None:
660
+ if signal.shape[1] != self.grid.nt:
661
+ error_msg = "Input signal has the wrong number of time points"
662
+ raise ValueError(error_msg)
663
+ if (signal == 0).all():
664
+ error_msg = "Input signal is all zeros"
665
+ raise ValueError(error_msg)
666
+ if signal.shape[0] != self.source_mask.sum():
667
+ error_msg = "Input signal has the wrong number of elements"
668
+ raise ValueError(error_msg)
669
+
670
+ @property
671
+ def signal(self) -> NDArray[np.float64]:
672
+ """Return the input signal.
673
+
674
+ Raises
675
+ ------
676
+ ValueError
677
+ If the signal is not set.
678
+
679
+ """
680
+ if self._signal is None:
681
+ error_msg = "Input signal is not set. use set_signal() to set the signal."
682
+ raise ValueError(error_msg)
683
+ return self._signal
684
+
685
+ @signal.setter
686
+ def signal(self, value: NDArray[np.float64]) -> None:
687
+ self._check_signal(value)
688
+ self._signal = value
689
+
690
+ def set_signal(self, value: NDArray[np.float64]) -> None:
691
+ """Set the input signal.
692
+
693
+ This method is used to set the input signal for the transducer.
694
+
695
+ Parameters
696
+ ----------
697
+ value : NDArray[np.float64]
698
+ The input signal to be set.
699
+
700
+ """
701
+ self.signal = value
702
+
703
+ @property
704
+ def sensor_mask(self) -> NDArray[np.bool]:
705
+ """Return the sensor mask indicating active sensor elements from the transducer geometry."""
706
+ active_ids = np.where(self.active_sensor_elements)[0] + 1
707
+ return np.isin(self.transducer_geometry.indexed_element_mask_output, active_ids)
708
+
709
+ @property
710
+ def source_mask(self) -> NDArray[np.bool]:
711
+ """Return the source mask indicating active source elements from the transducer geometry."""
712
+ active_ids = np.where(self.active_source_elements)[0] + 1
713
+ return np.isin(self.transducer_geometry.indexed_element_mask_input, active_ids)
714
+
715
+ @property
716
+ def dict_source_index_to_location(self) -> dict[int, NDArray[np.int64]]:
717
+ """Return the dictionary mapping source elements to their locations."""
718
+ # get the coordinates of the active source elements
719
+ coords = map_to_coordinates(self.source_mask, is_3d=self.is_3d, sort=True).T
720
+ # create a dictionary mapping source elements to their coordinates
721
+ return {i: coords[i - 1] for i in range(1, self.transducer_geometry.n_sources + 1)}
722
+
723
+ @property
724
+ def element_id_to_element_center(self) -> dict[int, NDArray[np.int64]]:
725
+ """Return the dictionary mapping source elements to their center coordinates."""
726
+ out_dict = {}
727
+ for i in range(1, self.transducer_geometry.number_elements + 1):
728
+ indexed_element_mask = np.stack(
729
+ np.where(
730
+ self.transducer_geometry.indexed_element_mask_input == i,
731
+ ),
732
+ )
733
+ center = np.round(indexed_element_mask.mean(axis=1))
734
+ out_dict[i] = center
735
+ return out_dict
736
+
737
+ @property
738
+ def sensor(self) -> fullwave.sensor.Sensor:
739
+ """Return the Sensor object with the sensor mask.
740
+
741
+ this property is used in the fullwave simulation run.
742
+ """
743
+ return fullwave.sensor.Sensor(
744
+ self.sensor_mask,
745
+ sampling_modulus_time=self.sampling_modulus_time,
746
+ )
747
+
748
+ @property
749
+ def source(self) -> fullwave.source.Source:
750
+ """Return the Source object with the sensor mask and signal.
751
+
752
+ this property is used in the fullwave simulation run.
753
+
754
+ Raises
755
+ ------
756
+ ValueError
757
+ If the signal is not set.
758
+
759
+ """
760
+ # check if the signal is set
761
+ if self._signal is None:
762
+ error_msg = "Input signal is not set. use set_signal() to set the signal."
763
+ raise ValueError(error_msg)
764
+ return fullwave.source.Source(self.signal, self.source_mask)
765
+
766
+ @property
767
+ def n_sources(self) -> NDArray[np.int64]:
768
+ """Return the number of source elements."""
769
+ return self.transducer_geometry.n_sources
770
+
771
+ @property
772
+ def tranducer_surface(self) -> NDArray[np.int64]:
773
+ """Return the coordinates of the transducer surface."""
774
+ return map_to_coordinates(self.sensor_mask == 1)[0]
775
+
776
+ @property
777
+ def tranducer_mask(self) -> NDArray[np.bool]:
778
+ """Return the coordinates of the transducer mask."""
779
+ mask = np.zeros(self.transducer_geometry.stored_grid_size, dtype=bool)
780
+ tranducer_surface = self.tranducer_surface
781
+ for i in range(len(tranducer_surface)):
782
+ mask[: tranducer_surface[i].astype(int), i] = 1
783
+ return mask
784
+
785
+ def plot_source_mask(
786
+ self,
787
+ export_path: Path | str | None = Path("./temp/temp.png"),
788
+ dpi: int = 300,
789
+ *,
790
+ show: bool = False,
791
+ ) -> None:
792
+ """Plot everything.
793
+
794
+ it plots whole transducer geometry including the inactive/active source and sensor elements.
795
+ """
796
+ import matplotlib.pyplot as plt
797
+
798
+ fig, ax = plt.subplots(1, 1, figsize=(10, 10))
799
+ plot_mask = np.zeros(self.transducer_geometry.stored_grid_size)
800
+ plot_mask[self.transducer_geometry.element_mask_input] = 1
801
+ # plot_mask[np.roll(self.source_mask, 10, axis=0)] = 2
802
+ # plot_mask[np.roll(self.sensor_mask, 5, axis=0)] = 3
803
+ plot_mask[self.source_mask] = 2
804
+ # plot_mask[self.sensor_mask + 1] = 3
805
+ pcm = ax.imshow(plot_mask, cmap="turbo")
806
+ ax.set_title("Source Mask layout")
807
+ ax.set_xlabel("y")
808
+ ax.set_ylabel("x")
809
+ ax.set_aspect("equal")
810
+ ax.set_xlim(0 - 10, self.grid.ny + 10)
811
+ ax.set_ylim(0 - 10, self.grid.nx + 10)
812
+ ax.invert_yaxis()
813
+ cbar = fig.colorbar(
814
+ pcm,
815
+ ax=ax,
816
+ label="Element Type",
817
+ # orientation="horizontal",
818
+ )
819
+ cbar.set_ticks(
820
+ ticks=[0, 1, 2],
821
+ labels=["background", "inactive", "active"],
822
+ )
823
+ if export_path is not None:
824
+ plt.savefig(export_path, dpi=dpi)
825
+ if show:
826
+ plt.show()
827
+ plt.close()
828
+
829
+ def plot_sensor_mask(
830
+ self,
831
+ export_path: Path | str | None = Path("./temp/temp.png"),
832
+ dpi: int = 300,
833
+ *,
834
+ show: bool = False,
835
+ ) -> None:
836
+ """Plot everything.
837
+
838
+ it plots whole transducer geometry including the inactive/active source and sensor elements.
839
+ """
840
+ import matplotlib.pyplot as plt
841
+
842
+ fig, ax = plt.subplots(1, 1, figsize=(10, 10))
843
+ plot_mask = np.zeros(self.transducer_geometry.stored_grid_size)
844
+ plot_mask[self.transducer_geometry.element_mask_input] = 1
845
+ # plot_mask[np.roll(self.source_mask, 10, axis=0)] = 2
846
+ # plot_mask[np.roll(self.sensor_mask, 5, axis=0)] = 3
847
+ plot_mask[self.sensor_mask] = 2
848
+ # plot_mask[self.sensor_mask + 1] = 3
849
+ pcm = ax.imshow(plot_mask, cmap="turbo")
850
+ ax.set_title("Sensor Mask layout")
851
+ ax.set_xlabel("y")
852
+ ax.set_ylabel("x")
853
+ ax.set_aspect("equal")
854
+ ax.set_xlim(0 - 10, self.grid.ny + 10)
855
+ ax.set_ylim(0 - 10, self.grid.nx + 10)
856
+ ax.invert_yaxis()
857
+ cbar = fig.colorbar(
858
+ pcm,
859
+ ax=ax,
860
+ label="Element Type",
861
+ # orientation="horizontal",
862
+ )
863
+ cbar.set_ticks(
864
+ ticks=[0, 1, 2],
865
+ labels=["background", "inactive", "active"],
866
+ )
867
+ if export_path is not None:
868
+ plt.savefig(export_path, dpi=dpi)
869
+ if show:
870
+ plt.show()
871
+ plt.close()
872
+
873
+ def print_info(self) -> None:
874
+ """Print information about the Transducer object."""
875
+ print(str(self))
876
+
877
+ def summary(self) -> None:
878
+ """Alias for print_info."""
879
+ self.print_info()
880
+
881
+ def __str__(self) -> str:
882
+ """Return a string representation of the Transducer object.
883
+
884
+ Returns
885
+ -------
886
+ str
887
+ A string representation of the Transducer object.
888
+
889
+ """
890
+ return (
891
+ f"Transducer with {self.transducer_geometry.number_elements} elements\n"
892
+ f"Element width (m): {self.transducer_geometry.element_width_m}\n"
893
+ f"Element spacing (m): {self.transducer_geometry.element_spacing_m}\n"
894
+ f"Transducer width (m): {self.transducer_geometry.transducer_width_m}\n"
895
+ f"Position (m): {self.transducer_geometry.position_m}\n"
896
+ f"Active source elements: {self.active_source_elements}\n"
897
+ f"Active sensor elements: {self.active_sensor_elements}\n"
898
+ f"Input signal shape: {self._signal.shape if self._signal is not None else None}\n"
899
+ )
900
+
901
+ def __repr__(self) -> str:
902
+ """Return a string representation of the Transducer object.
903
+
904
+ Returns
905
+ -------
906
+ str
907
+ A string representation of the Transducer object.
908
+
909
+ """
910
+ return self.__str__()
911
+
912
+
913
+ class LinearTransducer(Transducer):
914
+ """Linear transducer class.
915
+
916
+ it implements a linear array transducer for fullwave simulations.
917
+ """
918
+
919
+ def __init__(
920
+ self,
921
+ grid: Grid,
922
+ position_m: tuple[float, float] | tuple[float, float, float],
923
+ active_source_elements: tuple[bool] | None = None,
924
+ active_sensor_elements: tuple[bool] | None = None,
925
+ ) -> None:
926
+ """Initialize a LinearTransducer instance.
927
+
928
+ Parameters
929
+ ----------
930
+ grid : Grid
931
+ Grid object defining the spatial and temporal grid.
932
+ position_m : tuple[float, float] | tuple[float, float, float])
933
+ Position of the transducer in meters.
934
+ input_signal : (NDArray[np.float64])
935
+ Input signal emitted by the transducer.
936
+ active_source_elements : (tuple[bool] | None)
937
+ Flags indicating active source elements.
938
+ active_sensor_elements : (tuple[bool] | None)
939
+ Flags indicating active sensor elements.
940
+
941
+ """
942
+ transducer_geometry = TransducerGeometry(
943
+ grid=grid,
944
+ number_elements=128,
945
+ element_width_m=1.459375e-4, # 1.459375e-4 [m] = 0.1459375 [mm]
946
+ element_spacing_m=1.459375e-4, # 1.459375e-4 [m] = 0.1459375 [mm]
947
+ position_m=position_m,
948
+ )
949
+ input_signal = np.ones((transducer_geometry.number_elements, grid.nt))
950
+ super().__init__(
951
+ transducer_geometry=transducer_geometry,
952
+ grid=grid,
953
+ input_signal=input_signal,
954
+ active_source_elements=active_source_elements,
955
+ active_sensor_elements=active_sensor_elements,
956
+ )
957
+
958
+
959
+ def make_p4_1c_trasnducer(
960
+ grid: Grid,
961
+ position_m: tuple[float, float] | None = (0.0, 0.0),
962
+ position_px: tuple[int, int] | None = None,
963
+ ) -> Transducer:
964
+ """Create a P4.1C transducer.
965
+
966
+ Parameters
967
+ ----------
968
+ Args:
969
+ grid : Grid
970
+ Grid object defining the spatial and temporal grid.
971
+ position_m : tuple[float, float] | tuple[float, float, float])
972
+ Position of the transducer in meters.
973
+ position_px : tuple[int, int] | None
974
+ Position of the transducer in pixels. If None, it will be calculated from position_m.
975
+
976
+ Returns
977
+ -------
978
+ Transducer
979
+ A Transducer object representing the P4.1C transducer.
980
+
981
+ """
982
+ transducer_width_m = 27e-3
983
+ element_layer_px = 4
984
+ transducer_geometry = fullwave.TransducerGeometry(
985
+ grid,
986
+ number_elements=64,
987
+ # -
988
+ element_width_m=transducer_width_m / 64 * 0.8,
989
+ # -
990
+ element_spacing_m=transducer_width_m / 64 * 0.2,
991
+ # -
992
+ element_layer_px=element_layer_px,
993
+ # -
994
+ # [axial, lateral]
995
+ position_m=position_m,
996
+ position_px=position_px,
997
+ # -
998
+ radius=float("inf"),
999
+ )
1000
+ return fullwave.Transducer(
1001
+ transducer_geometry=transducer_geometry,
1002
+ grid=grid,
1003
+ )