Coverage for kwave/kspaceFirstOrder.py: 17%
135 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
1import os
2from operator import itemgetter
3from tempfile import gettempdir
4from warnings import warn
6import numpy as np
7from numpy.fft import ifftshift
8from scipy.io import savemat
10from kwave.kgrid import *
11from kwave.ktransducer import *
12from kwave.utils import dotdict
13import math
14import functools
16from kwave.utils import is_unix
19def kspaceFirstOrderG(func):
20 """
21 Decorator for the kspaceFO-GPU functions
23 Args:
24 func: kspaceFirstOrderNDG function where 1 <= N <= 3
26 Returns:
27 Function wrapper
28 """
29 @functools.wraps(func)
30 def wrapper(**kwargs):
31 # Check for the binary name input. If not defined, set the default name of the GPU binary
32 if 'BinaryName' not in kwargs.keys():
33 kwargs['BinaryName'] = 'kspaceFirstOrder-CUDA' if is_unix() else 'kspaceFirstOrder-CUDA.exe'
34 return func(**kwargs)
35 return wrapper
38def kspaceFirstOrderC():
39 """
40 Decorator for the kspaceFO-CPU functions
42 Args:
43 func: kspaceFirstOrderNDC function where 1 <= N <= 3
45 Returns:
46 Function wrapper
47 """
48 def decorator(func):
49 @functools.wraps(func)
50 def wrapper(**kwargs):
51 # set empty options string
52 options_string = ''
54 # set OS string for setting environment variables
55 if is_unix():
56 env_set_str = ''
57 sys_sep_str = ' '
58 else:
59 env_set_str = 'set '
60 sys_sep_str = ' & '
62 # set system string to define domain for thread migration
63 system_string = env_set_str + 'OMP_PLACES=cores' + sys_sep_str
65 args = dotdict()
67 # check for user input on axisymmetric code
68 if 'Axisymmetric' in kwargs.keys():
69 args.axisymmetric = kwargs['Axisymmetric']
70 # check option is true or false
71 assert isinstance(args.axisymmetric, bool), "Axisymmetric argument must be bool"
72 del kwargs['Axisymmetric']
73 else:
74 # set axisymmetric to false
75 args.axisymmetric = False
77 # check for a user defined location for the binary
78 if 'BinaryPath' in kwargs.keys():
79 binary_path = kwargs['BinaryPath']
81 # check for a trailing slash
82 if not binary_path.endswith(os.path.sep):
83 binary_path = binary_path + os.path.sep
84 del kwargs['BinaryPath']
85 else:
86 # set default path from environment variable
87 binary_path = os.getenv('KWAVE_BINARY_PATH')
89 # check for a user defined name for the binary
90 if 'BinaryName' in kwargs.keys():
91 binary_name = kwargs['BinaryName']
92 del kwargs['BinaryName']
93 else:
94 # set default name for the binary
95 binary_name = 'kspaceFirstOrder-OMP' if is_unix() else 'kspaceFirstOrder-OMP.exe'
97 # check the binary exists and is in the correct place before doing anything else
98 if not os.path.exists(f'{binary_path}{binary_name}'):
99 warn(f'''
100 The binary file {binary_name} could not be found in {binary_path}.
101 To use the C++ code, the C++ binaries for your operating system must be downloaded
102 from www.k-wave.org/download.php and placed in the binaries folder.'''
103 )
105 # check for a user defined name for the MATLAB function to call
106 if 'FunctionName' in kwargs.keys():
107 kwave_function_name = kwargs['FunctionName']
108 del kwargs['FunctionName']
109 else:
110 # set default name for the k-Wave MATLAB function to call
111 kwave_function_name = 'kspaceFirstOrder3D'
113 # check for a user defined location for the input and output files
114 if 'DataPath' in kwargs.keys():
115 data_path = kwargs['DataPath']
117 # check for a trailing slash
118 if not data_path.endswith(os.path.sep):
119 data_path = data_path + os.path.sep
120 del kwargs['DataPath']
121 else:
122 # set default path
123 data_path = gettempdir()
125 # check for a user defined name for the input and output files
126 if 'DataName' in kwargs.keys():
127 name_prefix = kwargs['DataName']
128 input_filename = f'{name_prefix}_input.h5'
129 output_filename = f'{name_prefix}_output.h5'
130 del kwargs['DataName']
131 else:
132 # set the filename inputs to store data in the default temp directory
133 date_string = get_date_string()
134 input_filename = 'kwave_input_data' + date_string + '.h5'
135 output_filename = 'kwave_output_data' + date_string + '.h5'
137 # add pathname to input and output filenames
138 input_filename = os.path.join(data_path, input_filename)
139 output_filename = os.path.join(data_path, output_filename)
141 # check for delete data input
142 if 'DeleteData' in kwargs.keys():
143 delete_data = kwargs['DeleteData']
144 assert isinstance(delete_data, bool), 'DeleteData argument must be bool'
145 del kwargs['DeleteData']
146 else:
147 # set data to be deleted
148 delete_data = True
150 # check for GPU device flag
151 if 'DeviceNum' in kwargs.keys():
152 # force to be positive integer or zero
153 device_num = int(abs(kwargs['DeviceNum']))
154 # add the value of the parameter to the input options
155 options_string = options_string + ' -g ' + str(device_num)
156 del kwargs['DeviceNum']
158 # check for user defined number of threads
159 if 'NumThreads' in kwargs.keys():
160 num_threads = kwargs['NumThreads']
162 if num_threads != 'all':
163 # check value
164 isinstance(num_threads, int) and num_threads > 0 and num_threads != float('inf')
165 # add the value of the parameter to the input options
166 options_string = options_string + ' -t ' + str(num_threads)
167 del kwargs['NumThreads']
169 # check for user defined thread binding option
170 if 'ThreadBinding' in kwargs.keys():
171 thread_binding = kwargs['ThreadBinding']
172 # check value
173 assert isinstance(thread_binding, int) and 0 <= thread_binding <= 1
174 # read the parameters and update the system options
175 if thread_binding == 0:
176 system_string = system_string + ' ' + env_set_str + 'OMP_PROC_BIND=SPREAD' + sys_sep_str
177 elif thread_binding == 1:
178 system_string = system_string + ' ' + env_set_str + 'OMP_PROC_BIND=CLOSE' + sys_sep_str
180 del kwargs['ThreadBinding']
181 else:
182 # set to round robin over places
183 system_string = system_string + ' ' + env_set_str + 'OMP_PROC_BIND=SPREAD' + sys_sep_str
185 # check for user input for system string
186 if 'SystemCall' in kwargs.keys():
187 # read the value of the parameter and add to the system options
188 system_string = system_string + ' ' + kwargs['SystemCall'] + sys_sep_str
189 del kwargs['SystemCall']
191 # check for user defined number of threads
192 if 'VerboseLevel' in kwargs.keys():
193 verbose_level = kwargs['VerboseLevel']
194 # check value
195 assert isinstance(verbose_level, int) and 0 <= verbose_level <= 2
196 # add the value of the parameter to the input options
197 options_string = options_string + ' --verbose ' + str(verbose_level)
198 del kwargs['VerboseLevel']
200 # assign pseudonyms for input structures
201 kgrid, source, sensor, medium = itemgetter('kgrid', 'source', 'sensor', 'medium')(kwargs)
202 del kwargs['kgrid']
203 del kwargs['source']
204 del kwargs['sensor']
205 del kwargs['medium']
207 # check if the sensor mask is defined as cuboid corners
208 if sensor.mask is not None and sensor.mask.shape[0] == (2 * kgrid.dim):
209 args.cuboid_corners = True
210 else:
211 args.cuboid_corners = False
213 # check if performing time reversal, and replace inputs to explicitly use a
214 # source with a dirichlet boundary condition
215 if sensor.time_reversal_boundary_data is not None:
216 # define a new source structure
217 source = {
218 'p_mask': sensor.p_mask,
219 'p': np.flip(sensor.time_reversal_boundary_data, 2),
220 'p_mode': 'dirichlet'
221 }
223 # define a new sensor structure
224 Nx, Ny, Nz = kgrid.Nx, kgrid.Ny, kgrid.Nz
225 sensor = kSensor(
226 mask=np.ones((Nx, Ny, max(1, Nz))),
227 record=['p_final']
228 )
229 # set time reversal flag
230 args.time_rev = True
231 else:
232 # set time reversal flag
233 args.time_rev = False
235 # check if sensor.record is given
236 if sensor.record is not None:
237 record = sensor.record
239 # set the options string to record the required output fields
240 record_options_map = {
241 'p': 'p_raw',
242 'p_max': 'p_max',
243 'p_min': 'p_min',
244 'p_rms': 'p_rms',
245 'p_max_all': 'p_max_all',
246 'p_min_all': 'p_min_all',
247 'p_final': 'p_final',
248 'u': 'u_raw',
249 'u_max': 'u_max',
250 'u_min': 'u_min',
251 'u_rms': 'u_rms',
252 'u_max_all': 'u_max_all',
253 'u_min_all': 'u_min_all',
254 'u_final': 'u_final'
255 }
256 for k, v in record_options_map.items():
257 if k in record:
258 options_string = options_string + f' --{v}'
260 if 'u_non_staggered' in record or 'I_avg' in record or 'I' in record:
261 options_string = options_string + ' --u_non_staggered_raw'
263 if ('I_avg' in record or 'I' in record) and ('p' not in record):
264 options_string = options_string + ' --p_raw'
265 else:
266 # if sensor.record is not given, record the raw time series of p
267 options_string = options_string + ' --p_raw'
269 # check if sensor.record_start_imdex is given
270 if sensor.record_start_index is not None:
271 options_string = options_string + ' -s ' + str(sensor.record_start_index)
273 # append the save to disk parameter
274 # farid | modified behaviour here!
275 # Originally, kspaceFO-nD GPU and CPU version would ALWAYS add 'SaveToDisk' option to kwargs
276 # And kspaceFO-nD would not add this option
277 # In our case all examples use CPU version. So some usages already pass 'SaveToDisk' as kwarg
278 # For them, we don't override 'SaveToDisk' options
279 # (originally it would have and it was required to be changed using DataPath & DataName options)
280 if 'SaveToDisk' not in kwargs:
281 kwargs['SaveToDisk'] = input_filename
282 kwargs['SaveToDiskExit'] = True
283 res = func(kgrid=kgrid, medium=medium, source=source, sensor=sensor, **args, **kwargs)
284 return res
285 return wrapper
286 return decorator