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.
- fullwave/__init__.py +28 -0
- fullwave/constants/__init__.py +5 -0
- fullwave/constants/material_properties.py +112 -0
- fullwave/grid.py +222 -0
- fullwave/medium.py +1042 -0
- fullwave/medium_builder/__init__.py +12 -0
- fullwave/medium_builder/domain.py +151 -0
- fullwave/medium_builder/medium_builder.py +198 -0
- fullwave/medium_builder/presets/__init__.py +8 -0
- fullwave/medium_builder/presets/data/.keep +0 -0
- fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
- fullwave/medium_builder/presets/domain_abdominal_wall.py +293 -0
- fullwave/medium_builder/presets/domain_background.py +140 -0
- fullwave/medium_builder/presets/domain_scatterer.py +179 -0
- fullwave/medium_builder/presets/domain_simple.py +92 -0
- fullwave/medium_builder/presets/domain_water_gel.py +1 -0
- fullwave/sensor.py +161 -0
- fullwave/solver/__init__.py +1 -0
- fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20251027_1437.mat +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenu +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_isotropic_multi_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_isotropic_multi_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_100_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_101_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_120_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_61_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_70_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_75_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_80_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_86_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_89_cuda129 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda118 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda124 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda126 +0 -0
- fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_sm_90_cuda129 +0 -0
- fullwave/solver/cuda_utils.py +392 -0
- fullwave/solver/input_file_writer.py +853 -0
- fullwave/solver/launcher.py +134 -0
- fullwave/solver/pml_builder.py +1923 -0
- fullwave/solver/solver.py +750 -0
- fullwave/solver/utils.py +83 -0
- fullwave/source.py +173 -0
- fullwave/transducer.py +1003 -0
- fullwave/utils/__init__.py +12 -0
- fullwave/utils/check_functions.py +48 -0
- fullwave/utils/coordinates.py +155 -0
- fullwave/utils/memory_tempfile.py +439 -0
- fullwave/utils/numerical.py +111 -0
- fullwave/utils/plot_utils.py +1122 -0
- fullwave/utils/pulse.py +72 -0
- fullwave/utils/relaxation_parameters.py +212 -0
- fullwave/utils/signal_process.py +197 -0
- fullwave25-1.0.7.dist-info/METADATA +292 -0
- fullwave25-1.0.7.dist-info/RECORD +225 -0
- 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
|
+
)
|