@@ -189,11 +189,8 @@ class EikonalSolver(object):
189
189
self ._update()
190
190
191
191
192
- def trace_ray (self , *args , method = ' euler' , tolerance = 1e-2 ):
193
- if method.upper() == ' EULER' :
194
- return (self ._trace_ray_euler(* args, tolerance = tolerance))
195
- else :
196
- raise (NotImplementedError (' Only Euler integration is implemented yet' ))
192
+ # def trace_ray(self, *args, step_size=None):
193
+ # return (self._trace_ray_euler_spherical(*args))
197
194
198
195
199
196
def transfer_travel_times_from (self , old , origin , rotate = False , set_alive = False ):
@@ -238,10 +235,137 @@ class EikonalSolver(object):
238
235
vvi = return_nan_on_error(LinearInterpolator3D(old.vgrid, old.vv))
239
236
self .vv = np.apply_along_axis(vvi, - 1 , vgrid_new)
240
237
238
+ def _get_gradient (self ):
239
+ if self .coord_sys == ' cartesian' :
240
+ gg = np.moveaxis(
241
+ np.stack(
242
+ np.gradient(
243
+ self .uu,
244
+ * [
245
+ self .pgrid.node_intervals[iax]
246
+ for iax in range (self .ndim) if iax not in self .iax_null
247
+ ],
248
+ axis = [
249
+ iax
250
+ for iax in range (self .ndim)
251
+ if iax not in self .iax_null
252
+ ]
253
+ )
254
+ ),
255
+ 0 , - 1
256
+ )
257
+ for iax in self .iax_null:
258
+ gg = np.insert(gg, iax, np.zeros(self .pgrid.npts), axis = - 1 )
259
+ else :
260
+ grid = self .pgrid[...]
261
+ d0, d1, d2 = self .pgrid.node_intervals
262
+ n0, n1, n2 = self .pgrid.npts
263
+
264
+ if 0 not in self .iax_null:
265
+ g0 = np.concatenate(
266
+ [
267
+ # Second-order forward difference evaluated along the lower edge
268
+ (
269
+ (
270
+ self .uu[2 ]
271
+ - 4 * self .uu[1 ]
272
+ + 3 * self .uu[0 ]
273
+ ) / (2 * d0)
274
+ ).reshape(1 , n1, n2),
275
+ # Second order central difference evaluated in the interior
276
+ (self .uu[2 :] - self .uu[:- 2 ]) / (2 * d0),
277
+ # Second order backward difference evaluated along the upper edge
278
+ (
279
+ (
280
+ self .uu[- 3 ]
281
+ - 4 * self .uu[- 2 ]
282
+ + 3 * self .uu[- 1 ]
283
+ ) / (2 * d0)
284
+ ).reshape(1 , n1, n2)
285
+ ],
286
+ axis = 0
287
+ )
288
+ else :
289
+ g0 = np.zeros((n0, n1, n2))
241
290
242
- def _trace_ray_euler (self , start ):
291
+ if 1 not in self .iax_null:
292
+ g1 = np.concatenate(
293
+ [
294
+ # Second-order forward difference evaluated along the lower edge
295
+ (
296
+ (
297
+ self .uu[:,2 ]
298
+ - 4 * self .uu[:,1 ]
299
+ + 3 * self .uu[:,0 ]
300
+ ) / (2 * grid[:,0 ,:,0 ]* d1)
301
+ ).reshape(n0, 1 , n2),
302
+ # Second order central difference evaluated in the interior
303
+ (
304
+ self .uu[:,2 :]
305
+ - self .uu[:,:- 2 ]
306
+ ) / (2 * grid[:,1 :- 1 ,:,0 ]* d1),
307
+ # Second order backward difference evaluated along the upper edge
308
+ (
309
+ (
310
+ self .uu[:,- 3 ]
311
+ - 4 * self .uu[:,- 2 ]
312
+ + 3 * self .uu[:,- 1 ]
313
+ ) / (2 * grid[:,- 1 ,:,0 ]* d1)
314
+ ).reshape(n0, 1 , n2)
315
+ ],
316
+ axis = 1
317
+ )
318
+ else :
319
+ g1 = np.zeros((n0, n1, n2))
320
+
321
+ if 2 not in self .iax_null:
322
+ g2 = np.concatenate(
323
+ [
324
+ # Second-order forward difference evaluated along the lower edge
325
+ (
326
+ (
327
+ self .uu[:,:,2 ]
328
+ - 4 * self .uu[:,:,1 ]
329
+ + 3 * self .uu[:,:,0 ]
330
+ ) / (2 * grid[:,:,0 ,0 ]* np.sin(grid[:,:,0 ,1 ])* d2)
331
+ ).reshape(n0, n1, 1 ),
332
+
333
+ # Second order central difference evaluated in the interior
334
+ (
335
+ self .uu[:,:,2 :]
336
+ - self .uu[:,:,:- 2 ]
337
+ ) / (2 * grid[:,:,1 :- 1 ,0 ]* np.sin(grid[:,:,1 :- 1 ,1 ])* d2),
338
+ # Second order backward difference evaluated along the upper edge
339
+ (
340
+ (
341
+ self .uu[:,:,- 3 ]
342
+ - 4 * self .uu[:,:,- 2 ]
343
+ + 3 * self .uu[:,:,- 1 ]
344
+ ) / (2 * grid[:,:,- 1 ,0 ]* np.sin(grid[:,:,- 1 ,1 ])* d2)
345
+ ).reshape(n0, n1, 1 )
346
+ ],
347
+ axis = 2
348
+ )
349
+ else :
350
+ g2 = np.zeros((n0, n1, n2))
351
+ return (np.moveaxis(np.stack([g0, g1, g2]), 0 , - 1 ))
352
+
353
+ def _get_step_size (self ):
354
+ # This is will not work in spherical coordinates when rho0 = 0.
355
+ return (
356
+ self .norm[
357
+ tuple (
358
+ slice (self .norm.shape[iax])
359
+ if iax not in self .iax_null
360
+ else 0
361
+ for iax in range (self .ndim)
362
+ )
363
+ ].min() / 2
364
+ )
365
+
366
+ def trace_ray (self , start , step_size = None ):
243
367
cdef cpp_vector[_REAL_t * ] ray
244
- cdef _REAL_t step_size, gx, gy, gz , norm
368
+ cdef _REAL_t g0, g1, g2 , norm
245
369
cdef _REAL_t * point_new
246
370
cdef _REAL_t[3 ] point_last, point_2last
247
371
cdef Py_ssize_t i
@@ -251,50 +375,30 @@ class EikonalSolver(object):
251
375
point_new[0 ], point_new[1 ], point_new[2 ] = start
252
376
ray.push_back(point_new)
253
377
# step_size <-- half the smallest node_interval
254
- step_size = np.min(
255
- [
256
- self .pgrid.node_intervals[iax]
257
- for iax in range (self .ndim) if iax not in self .iax_null
258
- ]
259
- ) / 2
260
- # Create an interpolator for the gradient field
261
- gg = np.moveaxis(
262
- np.stack(
263
- np.gradient(
264
- self .uu,
265
- * [
266
- self .pgrid.node_intervals[iax]
267
- for iax in range (self .ndim) if iax not in self .iax_null
268
- ],
269
- axis = [
270
- iax
271
- for iax in range (self .ndim)
272
- if iax not in self .iax_null
273
- ]
274
- )
275
- ),
276
- 0 , - 1
277
- )
278
- for iax in self .iax_null:
279
- gg = np.insert(gg, iax, np.zeros(self .pgrid.npts), axis = - 1 )
280
- grad_x = LinearInterpolator3D(self .pgrid, gg[...,0 ].astype(DTYPE_REAL))
281
- grad_y = LinearInterpolator3D(self .pgrid, gg[...,1 ].astype(DTYPE_REAL))
282
- grad_z = LinearInterpolator3D(self .pgrid, gg[...,2 ].astype(DTYPE_REAL))
378
+ if step_size is None :
379
+ step_size = self ._get_step_size()
380
+ gg = self ._get_gradient()
381
+ grad_0 = LinearInterpolator3D(self .pgrid, gg[...,0 ].astype(DTYPE_REAL))
382
+ grad_1 = LinearInterpolator3D(self .pgrid, gg[...,1 ].astype(DTYPE_REAL))
383
+ grad_2 = LinearInterpolator3D(self .pgrid, gg[...,2 ].astype(DTYPE_REAL))
283
384
# Create an interpolator for the travel-time field
284
385
uu = LinearInterpolator3D(self .pgrid, self .uu)
285
386
point_last = ray.back()
286
387
while True :
287
- gx = grad_x.interpolate(point_last)
288
- gy = grad_y.interpolate(point_last)
289
- gz = grad_z.interpolate(point_last)
290
- norm = libc.math.sqrt(gx** 2 + gy** 2 + gz** 2 )
291
- gx /= norm
292
- gy /= norm
293
- gz /= norm
388
+ g0 = grad_0.interpolate(point_last)
389
+ g1 = grad_1.interpolate(point_last)
390
+ g2 = grad_2.interpolate(point_last)
391
+ norm = libc.math.sqrt(g0** 2 + g1** 2 + g2** 2 )
392
+ g0 /= norm
393
+ g1 /= norm
394
+ g2 /= norm
395
+ if self .coord_sys == ' spherical' :
396
+ g1 /= point_last[0 ]
397
+ g2 /= point_last[0 ] * np.sin(point_last[1 ])
294
398
point_new = < _REAL_t * > malloc(3 * sizeof(_REAL_t))
295
- point_new[0 ] = point_last[0 ] - step_size * gx
296
- point_new[1 ] = point_last[1 ] - step_size * gy
297
- point_new[2 ] = point_last[2 ] - step_size * gz
399
+ point_new[0 ] = point_last[0 ] - step_size * g0
400
+ point_new[1 ] = point_last[1 ] - step_size * g1
401
+ point_new[2 ] = point_last[2 ] - step_size * g2
298
402
point_2last = ray.back()
299
403
ray.push_back(point_new)
300
404
point_last = ray.back()
@@ -309,6 +413,76 @@ class EikonalSolver(object):
309
413
return (ray_np)
310
414
311
415
416
+ # def _trace_ray_euler_cartesian(self, start):
417
+ # cdef cpp_vector[_REAL_t *] ray
418
+ # cdef _REAL_t step_size, gx, gy, gz, norm
419
+ # cdef _REAL_t *point_new
420
+ # cdef _REAL_t[3] point_last, point_2last
421
+ # cdef Py_ssize_t i
422
+ # cdef np.ndarray[_REAL_t, ndim=2] ray_np
423
+
424
+ # point_new = <_REAL_t *> malloc(3 * sizeof(_REAL_t))
425
+ # point_new[0], point_new[1], point_new[2] = start
426
+ # ray.push_back(point_new)
427
+ # # step_size <-- half the smallest node_interval
428
+ # step_size = np.min(
429
+ # [
430
+ # self.pgrid.node_intervals[iax]
431
+ # for iax in range(self.ndim) if iax not in self.iax_null
432
+ # ]
433
+ # ) / 2
434
+ # # Create an interpolator for the gradient field
435
+ # gg = np.moveaxis(
436
+ # np.stack(
437
+ # np.gradient(
438
+ # self.uu,
439
+ # *[
440
+ # self.pgrid.node_intervals[iax]
441
+ # for iax in range(self.ndim) if iax not in self.iax_null
442
+ # ],
443
+ # axis=[
444
+ # iax
445
+ # for iax in range(self.ndim)
446
+ # if iax not in self.iax_null
447
+ # ]
448
+ # )
449
+ # ),
450
+ # 0, -1
451
+ # )
452
+ # for iax in self.iax_null:
453
+ # gg = np.insert(gg, iax, np.zeros(self.pgrid.npts), axis=-1)
454
+ # grad_x = LinearInterpolator3D(self.pgrid, gg[...,0].astype(DTYPE_REAL))
455
+ # grad_y = LinearInterpolator3D(self.pgrid, gg[...,1].astype(DTYPE_REAL))
456
+ # grad_z = LinearInterpolator3D(self.pgrid, gg[...,2].astype(DTYPE_REAL))
457
+ # # Create an interpolator for the travel-time field
458
+ # uu = LinearInterpolator3D(self.pgrid, self.uu)
459
+ # point_last = ray.back()
460
+ # while True:
461
+ # gx = grad_x.interpolate(point_last)
462
+ # gy = grad_y.interpolate(point_last)
463
+ # gz = grad_z.interpolate(point_last)
464
+ # norm = libc.math.sqrt(gx**2 + gy**2 + gz**2)
465
+ # gx /= norm
466
+ # gy /= norm
467
+ # gz /= norm
468
+ # point_new = <_REAL_t *> malloc(3 * sizeof(_REAL_t))
469
+ # point_new[0] = point_last[0] - step_size * gx
470
+ # point_new[1] = point_last[1] - step_size * gy
471
+ # point_new[2] = point_last[2] - step_size * gz
472
+ # point_2last = ray.back()
473
+ # ray.push_back(point_new)
474
+ # point_last = ray.back()
475
+ # if uu.interpolate(point_2last) <= uu.interpolate(point_last):
476
+ # break
477
+ # ray_np = np.zeros((ray.size()-1, 3), dtype=DTYPE_REAL)
478
+ # for i in range(ray.size()-1):
479
+ # ray_np[i, 0] = ray[i][0]
480
+ # ray_np[i, 1] = ray[i][1]
481
+ # ray_np[i, 2] = ray[i][2]
482
+ # free(ray[i])
483
+ # return (ray_np)
484
+
485
+
312
486
def _update (self ):
313
487
'''
314
488
Update travel-time grid.
@@ -708,6 +882,7 @@ cdef class LinearInterpolator3D(object):
708
882
)
709
883
)
710
884
idx[iax] = (point[iax] - self ._min_coords[iax]) / self ._node_intervals[iax]
885
+ # TODO:: Accounting for node_interval is likely uneccessary here.
711
886
delta[iax] = (idx[iax] % 1. ) * self ._node_intervals[iax]
712
887
i1 = < Py_ssize_t> idx[0 ]
713
888
i2 = < Py_ssize_t> idx[1 ]
0 commit comments