Coverage for kwave/kspaceFirstOrder.py: 17%

1 import os

2 from operator import itemgetter

3 from tempfile import gettempdir

4 from warnings import warn

5

6 import numpy as np

7 from numpy.fft import ifftshift

8 from scipy.io import savemat

9

10 from kwave.kgrid import *

11 from kwave.ktransducer import *

12 from kwave.utils import dotdict

13 import math

14 import functools

15

16 from kwave.utils import is_unix

17

18

19 def 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

38 def 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