Coverage for kwave/kspaceFirstOrder.py: 17%

135 statements  

« 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 

5 

6import numpy as np 

7from numpy.fft import ifftshift 

8from scipy.io import savemat 

9 

10from kwave.kgrid import * 

11from kwave.ktransducer import * 

12from kwave.utils import dotdict 

13import math 

14import functools 

15 

16from kwave.utils import is_unix 

17 

18 

19def kspaceFirstOrderG(func): 

20 """ 

21 Decorator for the kspaceFO-GPU functions 

22 

23 Args: 

24 func: kspaceFirstOrderNDG function where 1 <= N <= 3 

25 

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 

36 

37 

38def kspaceFirstOrderC(): 

39 """ 

40 Decorator for the kspaceFO-CPU functions 

41 

42 Args: 

43 func: kspaceFirstOrderNDC function where 1 <= N <= 3 

44 

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 = '' 

53 

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 = ' & ' 

61 

62 # set system string to define domain for thread migration 

63 system_string = env_set_str + 'OMP_PLACES=cores' + sys_sep_str 

64 

65 args = dotdict() 

66 

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 

76 

77 # check for a user defined location for the binary 

78 if 'BinaryPath' in kwargs.keys(): 

79 binary_path = kwargs['BinaryPath'] 

80 

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') 

88 

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' 

96 

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 ) 

104 

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' 

112 

113 # check for a user defined location for the input and output files 

114 if 'DataPath' in kwargs.keys(): 

115 data_path = kwargs['DataPath'] 

116 

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() 

124 

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' 

136 

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) 

140 

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 

149 

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'] 

157 

158 # check for user defined number of threads 

159 if 'NumThreads' in kwargs.keys(): 

160 num_threads = kwargs['NumThreads'] 

161 

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'] 

168 

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 

179 

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 

184 

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'] 

190 

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'] 

199 

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'] 

206 

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 

212 

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 } 

222 

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 

234 

235 # check if sensor.record is given 

236 if sensor.record is not None: 

237 record = sensor.record 

238 

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}' 

259 

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' 

262 

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' 

268 

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) 

272 

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