def min(self, source_depths, receiver_depths, distances, method='linear'):
'''
Returns the minimum (minima) travel time(s) for the point(s) identified by
(source_depths, receiver_depths, distances) by building a grid and
interpolating on the points identified by each
P[i] = (source_depths[i], receiver_depths[i], distances[i])
```
if the source file has been built with
receiver depths == [0]. It uses a 2d linear interpolation on a grid. It uses
scipy griddata
:param source_depths: numeric or numpy array of length n: the source depth(s), in km
:param receiver_depths: numeric or numpy array of length n: the receiver depth(s), in km.
For most applications, this value can be set to zero
:param distances: numeric or numpy array of length n: the distance(s), in degrees
:param method: forwarded to `scipy.griddata` function
:return: a numpy array of length n denoting the minimum travel time(s) of this model for
each P
'''
# Handle the case some arguments are scalars and some arrays:
source_depths, receiver_depths, distances = \
np.broadcast_arrays(source_depths, receiver_depths, distances)
# handle the case all arguments scalars. See
# https://stackoverflow.com/questions/29318459/python-function-that-handles-scalar-or-arrays
allscalars = all(_.ndim == 0 for _ in (source_depths, receiver_depths, distances))
if source_depths.ndim == 0:
source_depths = source_depths[None] # Makes x 1D
if receiver_depths.ndim == 0:
receiver_depths = receiver_depths[None] # Makes x 1D
if distances.ndim == 0:
distances = distances[None] # Makes x 1D
# correct source depths and receiver depths
source_depths[source_depths < 0] = 0
receiver_depths[receiver_depths < 0] = 0
# correct distances to be compatible with obpsy traveltimes calculations:
distances = distances % 360
# set values symmetric to 180 degrees if greater than 180:
_mask = distances > 180
if _mask.any(): # does this speeds up (allocate mask array once)? FIXME: check
distances[_mask] = 360 - distances[_mask]
# set source depths to nan if out of bounds. This prevent method = 'nearest'
# to return non nan values and be consistent with 'linear' and 'cubic'
if self._unique_receiver_depth:
# get values without receiver depth dimension:
values = np.hstack((self._km2deg(source_depths).reshape(-1, 1),
distances.reshape(-1, 1)))
else:
values = np.hstack((self._km2deg(source_depths).reshape(-1, 1),
self._km2deg(receiver_depths).reshape(-1, 1),
distances.reshape(-1, 1)))
ret = griddata(self._gridpts, self._gridvals, values,
method=method, rescale=False, fill_value=np.nan)
# ret is almost likely a float, so we can set now nans for out of boun values
# we cannot do it before on any input array cause int arrays do not support nan assignement
ret[(source_depths > self._sourcedepth_bounds_km[1]) |
(receiver_depths > self._receiverdepth_bounds_km[1])] = np.nan
# return scalar if inputs are scalar, array oitherwise
return np.squeeze(ret) if allscalars else ret
```