Exemplo n.º 1
0
    def get_cell_dimensions(self):
        """
        Compute the area [km2] of the cells representing the surface.
        """
        lo = self.mesh.lons
        la = self.mesh.lats
        de = self.mesh.depths

        # Calculating cells dimensions
        lo0 = lo[:-1, :]
        la0 = la[:-1, :]
        de0 = de[:-1, :]
        lo1 = lo[1:, :]
        la1 = la[1:, :]
        de1 = de[1:, :]
        idx = np.logical_and(np.isfinite(lo0), np.isfinite(lo1))
        dy = np.full_like(lo0, np.nan)
        dy[idx] = distance(lo0[idx], la0[idx], de0[idx], lo1[idx], la1[idx],
                           de1[idx])

        lo0 = lo[:, 1:]
        la0 = la[:, 1:]
        de0 = de[:, 1:]
        lo1 = lo[:, :-1]
        la1 = la[:, :-1]
        de1 = de[:, :-1]
        idx = np.logical_and(np.isfinite(lo0), np.isfinite(lo1))
        dx = np.full_like(lo0, np.nan)
        dx[idx] = distance(lo0[idx], la0[idx], de0[idx], lo1[idx], la1[idx],
                           de1[idx])

        lo0 = lo[1:, 1:]
        la0 = la[1:, 1:]
        de0 = de[1:, 1:]
        lo1 = lo[:-1, :-1]
        la1 = la[:-1, :-1]
        de1 = de[:-1, :-1]
        idx = np.logical_and(np.isfinite(lo0), np.isfinite(lo1))
        dd = np.full_like(lo0, np.nan)
        dd[idx] = distance(lo0[idx], la0[idx], de0[idx], lo1[idx], la1[idx],
                           de1[idx])

        # Compute the area of the upper left triangles in each cell
        s = (dx[:-1, :] + dy[:, :-1] + dd) * 0.5
        upp = (s * (s - dx[:-1, :]) * (s - dy[:, :-1]) * (s - dd))**0.5

        # Compute the area of the lower right triangles in each cell
        s = (dx[1:, :] + dy[:, 1:] + dd) * 0.5
        low = (s * (s - dx[1:, :]) * (s - dy[:, 1:]) * (s - dd))**0.5

        # Compute the area of each cell
        area = np.full_like(dd, np.nan)
        idx = np.logical_and(np.isfinite(upp), np.isfinite(low))
        area[idx] = upp[idx] + low[idx]

        # Retain the same output of the original function which provided for
        # each cell the centroid as 3d vector in a Cartesian space, the length
        # width (size along column of points) in km and the area in km2.
        return None, None, None, area
Exemplo n.º 2
0
    def get_width(self) -> float:
        # TODO this method is provisional.  It works correctly for simple and
        # regular geometries defined using profiles parallel to the dip
        # direction
        """
        Compute the width of the kite surface.

        Defining a width for a kite surface is quite difficult. At present we
        compute it as the mean width for all the columns of the mesh defining
        the surface.
        """
        if self.width is None:
            widths = []
            for col_idx in range(self.mesh.lons.shape[1]):
                tmpa = np.nonzero(np.isfinite(self.mesh.lons[:, col_idx]))[0]
                tmpb = (tmpa[1:] - tmpa[:-1] == 1).nonzero()[0]
                idxs_low = tmpa[tmpb.astype(int)]
                tmp = distance(self.mesh.lons[idxs_low, col_idx],
                               self.mesh.lats[idxs_low, col_idx],
                               self.mesh.depths[idxs_low, col_idx],
                               self.mesh.lons[idxs_low + 1, col_idx],
                               self.mesh.lats[idxs_low + 1, col_idx],
                               self.mesh.depths[idxs_low + 1, col_idx])
                if len(tmp) > 0:
                    widths.append(np.sum(tmp))
            self.width = np.mean(np.array(widths))
        return self.width
Exemplo n.º 3
0
 def set_metadata(self):
     """
     Set the metadata for the station
     """
     stats = self.stream[0].stats
     self._starttime = stats.starttime
     self._station_code = stats.station
     if 'coordinates' not in stats:
         self._elevation = np.nan
         self._coordinates = (np.nan, np.nan)
         return
     lat = stats.coordinates.latitude
     lon = stats.coordinates.longitude
     if ('elevation' not in stats.coordinates
             or np.isnan(stats.coordinates.elevation)):
         elev = 0
     else:
         elev = stats.coordinates.elevation
     self._elevation = elev
     self._coordinates = (lat, lon)
     if self.event is not None:
         event = self.event
         dist, _, _ = gps2dist_azimuth(lat, lon, event.latitude,
                                       event.longitude)
         self._epicentral_distance = dist / M_PER_KM
         if event.depth is not None:
             self._hypocentral_distance = distance(lat, lon,
                                                   -elev / M_PER_KM,
                                                   event.latitude,
                                                   event.longitude,
                                                   event.depth / M_PER_KM)
Exemplo n.º 4
0
def distance_to_station(lat, long, depth):
    # station GPS coordinates of GS OK029 station -  Liberty Lake, Oklahoma, USA
    lat0 = 35.796570
    long0 = -97.454860
    depth0 = -0.333  # elevation: 333m
    # return distance of the event to the station
    return distance(long, lat, depth, long0, lat0, depth0)
Exemplo n.º 5
0
    def distance_to_mesh(self, mesh, with_depths=True):
        """
        Compute distance (in km) between this point and each point of ``mesh``.

        :param mesh:
            :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate
            distance to.
        :param with_depths:
            If ``True`` (by default), distance is calculated between actual
            point and the mesh, geodetic distance of projections is combined
            with vertical distance (difference of depths). If this is set
            to ``False``, only geodetic distance between projections
            is calculated.
        :returns:
            Numpy array of floats of the same shape as ``mesh`` with distance
            values in km in respective indices.
        """
        if with_depths:
            if mesh.depths is None:
                mesh_depths = numpy.zeros_like(mesh.lons)
            else:
                mesh_depths = mesh.depths
            return geodetic.distance(self.longitude, self.latitude, self.depth,
                                     mesh.lons, mesh.lats, mesh_depths)
        else:
            return geodetic.geodetic_distance(self.longitude, self.latitude,
                                              mesh.lons, mesh.lats)
 def set_metadata(self):
     """
     Set the metadata for the station
     """
     stats = self.stream[0].stats
     self._starttime = stats.starttime
     self._station_code = stats.station
     if 'coordinates' not in stats:
         self._elevation = np.nan
         self._coordinates = (np.nan, np.nan)
         return
     lat = stats.coordinates.latitude
     lon = stats.coordinates.longitude
     if 'elevation' not in stats.coordinates or np.isnan(stats.coordinates.elevation):
         elev = 0
     else:
         elev = stats.coordinates.elevation
     self._elevation = elev
     self._coordinates = (lat, lon)
     if self.event is not None:
         event = self.event
         dist, _, _ = gps2dist_azimuth(lat, lon,
                                       event.latitude,
                                       event.longitude)
         self._epicentral_distance = dist / 1000
         if event.depth is not None:
             self._hypocentral_distance = distance(lat, lon, elev / 1000,
                                                   event.latitude,
                                                   event.longitude,
                                                   event.depth / 1000)
Exemplo n.º 7
0
    def get_dip(self) -> float:
        """
        Return the fault dip as the average dip over the fault surface mesh.

        :returns:
            The average dip, in decimal degrees.
        """
        if self.dip is None:
            dips = []
            lens = []
            for col_idx in range(self.mesh.lons.shape[1]):
                hdists = distance(self.mesh.lons[:-1, col_idx],
                                  self.mesh.lats[:-1, col_idx],
                                  np.zeros_like(self.mesh.depths[1:, col_idx]),
                                  self.mesh.lons[1:, col_idx],
                                  self.mesh.lats[1:, col_idx],
                                  np.zeros_like(self.mesh.depths[1:, col_idx]))
                vdists = (self.mesh.depths[1:, col_idx] -
                          self.mesh.depths[:-1, col_idx])

                ok = np.logical_and(np.isfinite(hdists), np.isfinite(vdists))
                hdists = hdists[ok]
                vdists = vdists[ok]

                dips.append(np.mean(np.degrees(np.arctan(vdists / hdists))))
                lens.append(np.sum((hdists**2 + vdists**2)**0.5))
            lens = np.array(lens)
            self.dip = np.sum(np.array(dips) * lens / np.sum(lens))
        return self.dip
Exemplo n.º 8
0
def get_profiles_length(sps):
    """
    :parameter dict sps:
        A dictionary containing the subduction profiles
    :returns:
        A dictionary where key is the ID of the profile and value is the length
        and, a string identifying the longest profile
    """
    lengths = {}
    longest_key = None
    shortest_key = None
    longest_length = 0.
    shortest_length = 1e10
    for key in sorted(sps.keys()):
        dat = sps[key]
        total_length = 0
        for idx in range(0, len(dat) - 1):
            dst = distance(dat[idx, 0], dat[idx, 1], dat[idx, 2],
                           dat[idx + 1, 0], dat[idx + 1, 1], dat[idx + 1, 2])
            total_length += dst
        lengths[key] = total_length
        if longest_length < total_length:
            longest_length = total_length
            longest_key = key
        if shortest_length > total_length:
            shortest_length = total_length
            shortest_key = key
    return lengths, longest_key, shortest_key
Exemplo n.º 9
0
    def distance_to_mesh(self, mesh, with_depths=True):
        """
        Compute distance (in km) between this point and each point of ``mesh``.

        :param mesh:
            :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate
            distance to.
        :param with_depths:
            If ``True`` (by default), distance is calculated between actual
            point and the mesh, geodetic distance of projections is combined
            with vertical distance (difference of depths). If this is set
            to ``False``, only geodetic distance between projections
            is calculated.
        :returns:
            Numpy array of floats of the same shape as ``mesh`` with distance
            values in km in respective indices.
        """
        if with_depths:
            if mesh.depths is None:
                mesh_depths = numpy.zeros_like(mesh.lons)
            else:
                mesh_depths = mesh.depths
            return geodetic.distance(self.longitude, self.latitude, self.depth,
                                     mesh.lons, mesh.lats, mesh_depths)
        else:
            return geodetic.geodetic_distance(self.longitude, self.latitude,
                                              mesh.lons, mesh.lats)
Exemplo n.º 10
0
def distance_to_station(lat, long, depth):
    # station GPS coordinates
    lat0 = 35.796570
    long0 = -97.454860
    depth0 = -0.333
    # return distance of the event to the station
    return distance(long, lat, depth, long0, lat0, depth0)
Exemplo n.º 11
0
 def getValue(self,lat,lon,method='nearest'):
     if method == 'nearest':
         d = geodetic.distance(lon,lat,self._lons,self._lats)
         imin = d.argmin()
         return self._data[imin]
     else:
         raise NotImplementedError('Only nearest neighbor method implemented at this time.')
Exemplo n.º 12
0
    def _test(self, mlons, mlats, mdepths, slons, slats, sdepths,
              expected_mpoint_indices):
        mlons, mlats, mdepths = [numpy.array(arr, float)
                                 for arr in (mlons, mlats, mdepths)]
        slons, slats, sdepths = [numpy.array(arr, float)
                                 for arr in (slons, slats, sdepths)]
        actual_indices = geodetic.min_distance(mlons, mlats, mdepths,
                                               slons, slats, sdepths,
                                               indices=True)
        numpy.testing.assert_equal(actual_indices, expected_mpoint_indices)
        dists = geodetic.min_distance(mlons, mlats, mdepths,
                                      slons, slats, sdepths)
        expected_closest_mlons = mlons.flat[expected_mpoint_indices]
        expected_closest_mlats = mlats.flat[expected_mpoint_indices]
        expected_closest_mdepths = mdepths.flat[expected_mpoint_indices]
        expected_distances = geodetic.distance(
            expected_closest_mlons, expected_closest_mlats,
            expected_closest_mdepths,
            slons, slats, sdepths
        )
        self.assertTrue((dists == expected_distances).all())

        # testing min_geodetic_distance with the same lons and lats
        min_geod_distance = geodetic.min_geodetic_distance(mlons, mlats,
                                                           slons, slats)
        min_geo_distance2 = geodetic.min_distance(mlons, mlats, mdepths * 0,
                                                  slons, slats, sdepths * 0)
        numpy.testing.assert_almost_equal(min_geod_distance, min_geo_distance2)
Exemplo n.º 13
0
 def getValue(self, lat, lon, method='nearest'):
     if method == 'nearest':
         d = geodetic.distance(lon, lat, self._lons, self._lats)
         imin = d.argmin()
         return self._data[imin]
     else:
         raise NotImplementedError(
             'Only nearest neighbor method implemented at this time.')
Exemplo n.º 14
0
 def _test(self, filename):
     profile = _read_profile(filename)
     sampling_distance = 10.
     rprofile = _resample_profile(profile, sampling_distance)
     pro = np.array([(pnt.longitude, pnt.latitude, pnt.depth)
                     for pnt in rprofile.points])
     dsts = distance(pro[:-1, 0], pro[:-1, 1], pro[:-1, 2], pro[1:, 0],
                     pro[1:, 1], pro[1:, 2])
     np.testing.assert_allclose(dsts, sampling_distance, rtol=1)
Exemplo n.º 15
0
    def test_mesh_creation(self):
        # Create the mesh: two parallel profiles - no top alignment
        hsmpl = 4
        vsmpl = 4
        idl = False
        alg = False
        srfc = KiteSurface.from_profiles(self.prf, hsmpl, vsmpl, idl, alg)
        smsh = srfc.mesh

        #
        # Check the horizontal mesh spacing
        computed = []
        for i in range(0, smsh.lons.shape[0]):
            tmp = []
            for j in range(0, smsh.lons.shape[1] - 1):
                k = j + 1
                dst = distance(smsh.lons[i, j], smsh.lats[i, j],
                               smsh.depths[i, j], smsh.lons[i, k],
                               smsh.lats[i, k], smsh.depths[i, k])
                tmp.append(dst)
            computed.append(dst)
        computed = np.array(computed)
        self.assertTrue(np.all(abs(computed - hsmpl) / vsmpl < 0.05))

        #
        # Check the vertical mesh spacing
        computed = []
        for i in range(0, smsh.lons.shape[0] - 1):
            tmp = []
            k = i + 1
            for j in range(0, smsh.lons.shape[1]):
                dst = distance(smsh.lons[i, j], smsh.lats[i, j],
                               smsh.depths[i, j], smsh.lons[k, j],
                               smsh.lats[k, j], smsh.depths[k, j])
                tmp.append(dst)
            computed.append(dst)
        computed = np.array(computed)
        self.assertTrue(np.all(abs(computed - vsmpl) / vsmpl < 0.05))

        if PLOTTING:
            title = 'Two parallel profiles'
            ppp(self.prf, srfc, title)
Exemplo n.º 16
0
def travel_time(lat, long, depth, stationLAT, stationLONG, stationDEPTH,
                mean_velocity):
    #print("long ="+str(long)+", lat ="+str(lat)+", depth ="+str(depth)+", stationLONG ="+str(stationLONG)+", stationLAT ="+str(stationLAT));
    distance_to_station = distance(long, lat, depth, stationLONG, stationLAT,
                                   stationDEPTH)
    #distance_to_station = distance(long, lat, 0, stationLONG, stationLAT, 0)
    #distance in km
    #print("distance_to_station = "+str(distance_to_station)+" km")
    travel_time = (distance_to_station / mean_velocity) * 3600
    #print("travel_time = "+str(travel_time)+"s")
    return travel_time
Exemplo n.º 17
0
    def test_get_cell_dimensions(self):
        ksfc = self.ksfc
        _, _, _, areas = ksfc.get_cell_dimensions()
        idx = np.isfinite(areas)

        # Computing the lenght and the width of the rectangle representing the
        # rupture surface. This includes the cells which are empty
        iul = (1, 0)
        iur = (1, -1)
        slen = distance(self.lons[iul], self.lats[iul], self.deps[iul],
                        self.lons[iur], self.lats[iur], self.deps[iur])
        iul = (0, 2)
        ill = (-1, 2)
        swid = distance(self.lons[iul], self.lats[iul], self.deps[iul],
                        self.lons[ill], self.lats[ill], self.deps[ill])

        # Computing the surface area as the total area minus the area of 4
        # cells. Note that here we assume that cells have approx the same area
        expected = slen * swid / areas.size * (np.sum(idx))

        perc_diff = abs(expected - np.sum(areas[idx])) / expected * 100
        self.assertTrue(perc_diff < 0.5)
Exemplo n.º 18
0
    def test_mesh_creation(self):
        """ Create the mesh: two parallel profiles - no top alignment """
        h_sampl = 4
        v_sampl = 4
        idl = False
        alg = False
        smsh = create_from_profiles(self.profiles, h_sampl, v_sampl, idl, alg)

        # plotter(self.profiles, smsh)

        #
        # Check the horizontal mesh spacing
        computed = []
        for i in range(0, smsh.shape[0]):
            tmp = []
            for j in range(0, smsh.shape[1] - 1):
                k = j + 1
                dst = distance(smsh[i, j, 0], smsh[i, j, 1], smsh[i, j, 2],
                               smsh[i, k, 0], smsh[i, k, 1], smsh[i, k, 2])
                tmp.append(dst)
            computed.append(dst)
        computed = numpy.array(computed)
        self.assertTrue(numpy.all(abs(computed - h_sampl) / h_sampl < 0.05))
        #
        # Check the vertical mesh spacing
        computed = []
        for i in range(0, smsh.shape[0] - 1):
            tmp = []
            k = i + 1
            for j in range(0, smsh.shape[1]):
                dst = distance(smsh[i, j, 0], smsh[i, j, 1], smsh[i, j, 2],
                               smsh[k, j, 0], smsh[k, j, 1], smsh[k, j, 2])
                tmp.append(dst)
            computed.append(dst)
        computed = numpy.array(computed)
        print(numpy.amax(abs(computed - v_sampl) / v_sampl))
        self.assertTrue(numpy.all(abs(computed - v_sampl) / v_sampl < 0.05))
Exemplo n.º 19
0
 def test_v_spacing(self):
     """ Check v-spacing: two misaligned profiles - no top alignment """
     smsh = self.smsh
     computed = []
     for i in range(0, smsh.shape[0] - 1):
         tmp = []
         k = i + 1
         for j in range(0, smsh.shape[1]):
             dst = distance(smsh[i, j, 0], smsh[i, j, 1], smsh[i, j, 2],
                            smsh[k, j, 0], smsh[k, j, 1], smsh[k, j, 2])
             tmp.append(dst)
         computed.append(dst)
     computed = numpy.array(computed)
     tmp = abs(computed - self.h_sampl) / self.h_sampl
     self.assertTrue(numpy.all(tmp < 0.05))
Exemplo n.º 20
0
 def test__spacing(self):
     """ Check v-spacing: two misaligned profiles - no top alignment """
     srfc = self.smsh
     smsh = srfc.mesh
     computed = []
     for i in range(0, smsh.lons.shape[0]-1):
         tmp = []
         k = i + 1
         for j in range(0, smsh.lons.shape[1]):
             dst = distance(smsh.lons[i, j], smsh.lats[i, j], smsh.depths[i, j],
                            smsh.lons[k, j], smsh.lats[k, j], smsh.depths[k, j])
             tmp.append(dst)
         computed.append(dst)
     computed = np.array(computed)
     tmp = abs(computed-self.v_sampl)/self.v_sampl
     self.assertTrue(np.all(tmp < 0.01))
Exemplo n.º 21
0
    def calcStationMetrics(self, eventid, stations=None, labels=None):
        """Calculate distance measures for each station.

        Args:
            eventid (str):
                ID of event to search for in ASDF file.
            stations (list):
                List of stations to create metrics for.
            labels (list):
                List of processing labels to create metrics for.
        """
        if not self.hasEvent(eventid):
            fmt = 'No event matching %s found in workspace.'
            raise KeyError(fmt % eventid)

        streams = self.getStreams(eventid, stations=stations, labels=labels)
        event = self.getEvent(eventid)
        for stream in streams:
            tag = stream.tag
            parts = tag.split('_')
            if len(parts) > 2:
                label = parts[-1]
                eventid = '_'.join(parts[0:-1])
            else:
                eventid, label = tag.split('_')
            elat = event.latitude
            elon = event.longitude
            edepth = event.depth_km
            slat = stream[0].stats.coordinates.latitude
            slon = stream[0].stats.coordinates.longitude
            sdep = stream[0].stats.coordinates.elevation
            epidist_m, _, _ = gps2dist_azimuth(elat, elon, slat, slon)
            hypocentral_distance = distance(elon, elat, edepth,
                                            slon, slat, -sdep / M_PER_KM)
            xmlfmt = '''<station_metrics>
            <hypocentral_distance units="km">%.1f</hypocentral_distance>
            <epicentral_distance units="km">%.1f</epicentral_distance>
            </station_metrics>
            '''
            xmlstr = xmlfmt % (hypocentral_distance, epidist_m / M_PER_KM)

            metricpath = '/'.join([
                format_netsta(stream[0].stats),
                format_nslit(stream[0].stats, stream.get_inst(), eventid)
                ])
            self.insert_aux(xmlstr, 'StationMetrics', metricpath)
Exemplo n.º 22
0
 def test_h_spacing(self):
     """ Check h-spacing: two misaligned profiles - no top alignment """
     smsh = self.smsh
     #
     # Check the horizontal mesh spacing
     computed = []
     for i in range(0, smsh.shape[0]):
         tmp = []
         for j in range(0, smsh.shape[1] - 1):
             k = j + 1
             dst = distance(smsh[i, j, 0], smsh[i, j, 1], smsh[i, j, 2],
                            smsh[i, k, 0], smsh[i, k, 1], smsh[i, k, 2])
             tmp.append(dst)
         computed.append(dst)
     computed = numpy.array(computed)
     tmp = abs(computed - self.h_sampl) / self.h_sampl
     self.assertTrue(numpy.all(tmp < 0.05))
Exemplo n.º 23
0
 def get_width(self) -> float:
     if self.width is None:
         widths = []
         for col_idx in range(self.mesh.lons.shape[1]):
             tmpa = np.nonzero(np.isfinite(self.mesh.lons[:, col_idx]))[0]
             tmpb = (tmpa[1:] - tmpa[:-1] == 1).nonzero()[0]
             idxs_low = tmpa[tmpb.astype(int)]
             tmp = distance(self.mesh.lons[idxs_low, col_idx],
                            self.mesh.lats[idxs_low, col_idx],
                            self.mesh.depths[idxs_low, col_idx],
                            self.mesh.lons[idxs_low + 1, col_idx],
                            self.mesh.lats[idxs_low + 1, col_idx],
                            self.mesh.depths[idxs_low + 1, col_idx])
             if len(tmp) > 0:
                 widths.append(np.sum(tmp))
         self.width = np.mean(np.array(widths))
     return self.width
Exemplo n.º 24
0
    def computeRepi(self, lon, lat, depth):
        """
        Method for computing epicentral distance.

        Args:
            lon (array): Numpy array of longitudes.
            lat (array): Numpy array of latitudes.
            depth (array): Numpy array of depths (km; positive down).

        Returns:
           array: Epicentral distance (km).
        """
        origin = self._origin
        oldshape = lon.shape

        repi = geodetic.distance(origin.lon, origin.lat, 0.0, lon, lat, depth)
        repi = repi.reshape(oldshape)
        return repi
Exemplo n.º 25
0
    def computeRepi(self, lon, lat, depth):
        """
        Method for computing epicentral distance.

        Args:
            lon (array): Numpy array of longitudes.
            lat (array): Numpy array of latitudes.
            depth (array): Numpy array of depths (km; positive down).

        Returns:
           array: Epicentral distance (km).
        """
        origin = self._origin
        oldshape = lon.shape

        repi = geodetic.distance(origin.lon, origin.lat, 0.0,
                                 lon, lat, depth)
        repi = repi.reshape(oldshape)
        return repi
Exemplo n.º 26
0
    def computeRhyp(self, lon, lat, depth):
        """
        Method for computing hypocentral distance.

        Args:
            lon (array): Numpy array of longitudes.
            lat (array): Numpy array of latitudes.
            depth (array): Numpy array of depths (km; positive down).

        Returns:
           array: Hypocentral distance (km).
        """
        origin = self._origin
        oldshape = lon.shape

        rhyp = geodetic.distance(origin.lon, origin.lat, origin.depth,
                                 lon, lat, depth)
        rhyp = rhyp.reshape(oldshape)
        return rhyp
Exemplo n.º 27
0
    def computeRhyp(self, lon, lat, depth):
        """
        Method for computing hypocentral distance.

        Args:
            lon (array): Numpy array of longitudes.
            lat (array): Numpy array of latitudes.
            depth (array): Numpy array of depths (km; positive down).

        Returns:
           array: Hypocentral distance (km).
        """
        origin = self._origin
        oldshape = lon.shape

        rhyp = geodetic.distance(origin.lon, origin.lat, origin.depth,
                                 lon, lat, depth)
        rhyp = rhyp.reshape(oldshape)
        return rhyp
Exemplo n.º 28
0
    def distance(self, point):
        """
        Compute the distance (in km) between this point and the given point.

        Distance is calculated using pythagoras theorem, where the
        hypotenuse is the distance and the other two sides are the
        horizontal distance (great circle distance) and vertical
        distance (depth difference between the two locations).

        :param point:
            Destination point.
        :type point:
            Instance of :class:`Point`
        :returns:
            The distance.
        :rtype:
            float
        """
        return geodetic.distance(self.longitude, self.latitude, self.depth,
                                 point.longitude, point.latitude, point.depth)
Exemplo n.º 29
0
    def distance(self, point):
        """
        Compute the distance (in km) between this point and the given point.

        Distance is calculated using pythagoras theorem, where the
        hypotenuse is the distance and the other two sides are the
        horizontal distance (great circle distance) and vertical
        distance (depth difference between the two locations).

        :param point:
            Destination point.
        :type point:
            Instance of :class:`Point`
        :returns:
            The distance.
        :rtype:
            float
        """
        return geodetic.distance(self.longitude, self.latitude, self.depth,
                                 point.longitude, point.latitude, point.depth)
Exemplo n.º 30
0
 def test_resampling_01(self):
     """
     Test trench axis resampling using a distance of 20 km
     """
     #
     # resample the trench axis - output is a numpy array
     sampling = 20.
     rtrench = self.trench.resample(sampling)
     idx = len(rtrench.axis)-2
     pts = rtrench.axis
     deps = np.zeros_like(pts[0:idx, 0])
     #
     # compute distance between consecutive points
     dsts = distance(pts[0:idx, 0], pts[0:idx, 1], deps,
                     pts[1:idx+1, 0], pts[1:idx+1, 1], deps)
     expected = np.ones_like(dsts)*sampling
     #
     # check that the spacing between points corresponds to the
     # sampling distance
     np.testing.assert_allclose(dsts, expected, rtol=1, atol=0.)
Exemplo n.º 31
0
    def closer_than(self, mesh, radius):
        """
        Check for proximity of points in the ``mesh``.

        :param mesh:
            :class:`openquake.hazardlib.geo.mesh.Mesh` instance.
        :param radius:
            Proximity measure in km.
        :returns:
            Numpy array of boolean values in the same shape as the mesh
            coordinate arrays with ``True`` on indexes of points that
            are not further than ``radius`` km from this point. Function
            :func:`~openquake.hazardlib.geo.geodetic.distance` is used to
            calculate distances to points of the mesh. Points of the mesh that
            lie exactly ``radius`` km away from this point also have
            ``True`` in their indices.
        """
        dists = geodetic.distance(self.longitude, self.latitude, self.depth,
                                  mesh.lons, mesh.lats,
                                  0 if mesh.depths is None else mesh.depths)
        return dists <= radius
Exemplo n.º 32
0
    def closer_than(self, mesh, radius):
        """
        Check for proximity of points in the ``mesh``.

        :param mesh:
            :class:`openquake.hazardlib.geo.mesh.Mesh` instance.
        :param radius:
            Proximity measure in km.
        :returns:
            Numpy array of boolean values in the same shape as the mesh
            coordinate arrays with ``True`` on indexes of points that
            are not further than ``radius`` km from this point. Function
            :func:`~openquake.hazardlib.geo.geodetic.distance` is used to
            calculate distances to points of the mesh. Points of the mesh that
            lie exactly ``radius`` km away from this point also have
            ``True`` in their indices.
        """
        dists = geodetic.distance(self.longitude, self.latitude, self.depth,
                                  mesh.lons, mesh.lats,
                                  0 if mesh.depths is None else mesh.depths)
        return dists <= radius
Exemplo n.º 33
0
    def get_dip(self) -> float:
        # TODO this method is provisional. It works correctly for simple and
        # regular geometries defined using profiles parallel to the dip
        # direction
        """
        Computes the fault dip as the average dip over the surface.

        :returns:
            The average dip, in decimal degrees.
        """
        if self.dip is None:
            dips = []
            lens = []
            for col_idx in range(self.mesh.lons.shape[1]):

                # For the calculation of the overall dip we use just the dip
                # values of contiguous points along a profile
                iii = np.isfinite(self.mesh.lons[1:, col_idx])
                kkk = np.isfinite(self.mesh.lons[:-1, col_idx])
                jjj = np.where(np.logical_and(kkk, iii))[0]

                zeros = np.zeros_like(self.mesh.depths[jjj, col_idx])
                hdists = distance(self.mesh.lons[jjj + 1, col_idx],
                                  self.mesh.lats[jjj + 1, col_idx], zeros,
                                  self.mesh.lons[jjj, col_idx],
                                  self.mesh.lats[jjj, col_idx], zeros)
                vdists = (self.mesh.depths[jjj + 1, col_idx] -
                          self.mesh.depths[jjj, col_idx])

                ok = np.logical_and(np.isfinite(hdists), np.isfinite(vdists))
                hdists = hdists[ok]
                vdists = vdists[ok]
                if len(vdists) > 0:
                    dips.append(np.mean(np.degrees(np.arctan(vdists /
                                                             hdists))))
                    lens.append(np.sum((hdists**2 + vdists**2)**0.5))
            lens = np.array(lens)
            self.dip = np.sum(np.array(dips) * lens / np.sum(lens))

        return self.dip
Exemplo n.º 34
0
    def test_h_spacing(self):
        """ Check v-spacing: two misaligned profiles - no top alignment """
        srfc = self.smsh
        smsh = srfc.mesh
        #
        # Check the horizontal mesh spacing
        computed = []
        for i in range(0, smsh.lons.shape[0]):
            tmp = []
            for j in range(0, smsh.lons.shape[1] - 1):
                k = j + 1
                dst = distance(smsh.lons[i, j], smsh.lats[i, j],
                               smsh.depths[i, j], smsh.lons[i, k],
                               smsh.lats[i, k], smsh.depths[i, k])
                tmp.append(dst)
            computed.append(dst)
        computed = np.array(computed)
        tmp = abs(computed - self.h_sampl) / self.h_sampl
        self.assertTrue(np.all(tmp < 0.02))

        if PLOTTING:
            ppp(self.profiles, srfc)
Exemplo n.º 35
0
    def test_interpolation_cam(self):
        """
        Test profile interpolation: CAM | maximum sampling: 30 km
        """
        #
        # read data and compute distances
        sps, dmin, dmax = read_profiles_csv(CAM_DATA_PATH)
        lengths, longest_key, shortest_key = get_profiles_length(sps)
        maximum_sampling_distance = 30.
        num_sampl = np.ceil(lengths[longest_key] / maximum_sampling_distance)
        #
        # get interpolated profiles
        ssps = get_interpolated_profiles(sps, lengths, num_sampl)
        lll = []
        for key in sorted(ssps.keys()):
            odat = sps[key]
            dat = ssps[key]

            distances = distance(dat[0:-2, 0], dat[0:-2, 1], dat[0:-2, 2],
                                 dat[1:-1, 0], dat[1:-1, 1], dat[1:-1, 2])
            expected = lengths[key] / num_sampl * np.ones_like(distances)
            np.testing.assert_allclose(distances, expected, rtol=3)
            #
            # update the list with the number of points in each profile
            lll.append(len(dat[:, 0]))
            #
            # check that the interpolated profile starts from the same point
            # of the original one
            self.assertListEqual([odat[0, 0], odat[0, 1]],
                                 [dat[0, 0], dat[0, 1]])
            #
            # check that the depth of the profiles is always increasing
            computed = np.all(np.sign(dat[:-1, 2]-dat[1:, 2]) < 0)
            self.assertTrue(computed)
        #
        # check that all the profiles have all the same length
        dff = np.diff(np.array(lll))
        zeros = np.zeros_like(dff)
        np.testing.assert_allclose(dff, zeros, rtol=2)
Exemplo n.º 36
0
 def test_edge_resampling02(self):
     """
     Test edge resampling with a resampling distance of 10 km
     """
     #
     # resampled profile
     sampling_distance = 10.
     out_line, _, _ = _resample_edge(self.edge, sampling_distance, 5)
     #
     # lists with coordinates for the resampled profile
     lo = [pnt.longitude for pnt in out_line.points]
     la = [pnt.latitude for pnt in out_line.points]
     de = [pnt.depth for pnt in out_line.points]
     #
     # lenghts of resampled segments
     dsts = []
     for i in range(0, len(out_line)-1):
         dsts.append(distance(lo[i], la[i], de[i],
                              lo[i+1], la[i+1], de[i+1]))
     #
     # testing
     expected = np.ones((len(out_line)-1))*sampling_distance
     np.testing.assert_allclose(dsts, expected, rtol=2, atol=0.)
Exemplo n.º 37
0
def _computeGC2(rupture, lon, lat, depth):
    """
    Method for computing GC2 from a ShakeMap Rupture instance.

    Args:
        rupture (Rupture): ShakeMap rupture object.
        lon (array): Numpy array of site longitudes.
        lat (array): Numpy array of site latitudes.
        depth (array): Numpy array of site depths.

    Returns:
        dict: Dictionary of GC2 distances. Keys include "T", "U", "rx"
            "ry", "ry0".
    """

    quadlist = rupture.getQuadrilaterals()
    quadgc2 = copy.deepcopy(quadlist)

    oldshape = lon.shape

    if len(oldshape) == 2:
        newshape = (oldshape[0] * oldshape[1], 1)
    else:
        newshape = (oldshape[0], 1)

    # -------------------------------------------------------------------------
    # Define a projection that spans sites and rupture
    # -------------------------------------------------------------------------

    all_lat = np.append(lat, rupture.lats)
    all_lon = np.append(lon, rupture.lons)

    west = np.nanmin(all_lon)
    east = np.nanmax(all_lon)
    south = np.nanmin(all_lat)
    north = np.nanmax(all_lat)
    proj = get_orthographic_projection(west, east, north, south)

    totweight = np.zeros(newshape, dtype=lon.dtype)
    GC2T = np.zeros(newshape, dtype=lon.dtype)
    GC2U = np.zeros(newshape, dtype=lon.dtype)

    # -------------------------------------------------------------------------
    # First sort out strike discordance and nominal strike prior to
    # starting the loop if there is more than one group/trace.
    # -------------------------------------------------------------------------
    group_ind = rupture._getGroupIndex()

    # Need group_ind as numpy array for sensible indexing...
    group_ind_np = np.array(group_ind)
    uind = np.unique(group_ind_np)
    n_groups = len(uind)

    if n_groups > 1:
        # ---------------------------------------------------------------------
        # The first thing we need to worry about is finding the coordinate
        # shift. U's origin is "selected from the two endpoints most
        # distant from each other."
        # ---------------------------------------------------------------------

        # Need to get index of first and last quad
        # for each segment
        iq0 = np.zeros(n_groups, dtype='int16')
        iq1 = np.zeros(n_groups, dtype='int16')
        for k in uind:
            ii = [i for i, j in enumerate(group_ind) if j == uind[k]]
            iq0[k] = int(np.min(ii))
            iq1[k] = int(np.max(ii))

        # ---------------------------------------------------------------------
        # This is an iterator for each possible combination of traces
        # including trace orientations (i.e., flipped).
        # ---------------------------------------------------------------------

        it_seg = it.product(it.combinations(uind, 2),
                            it.product([0, 1], [0, 1]))

        # Placeholder for the trace pair/orientation that gives the
        # largest distance.
        dist_save = 0

        for k in it_seg:
            s0ind = k[0][0]
            s1ind = k[0][1]
            p0ind = k[1][0]
            p1ind = k[1][1]
            if p0ind == 0:
                P0 = quadlist[iq0[s0ind]][0]
            else:
                P0 = quadlist[iq1[s0ind]][1]
            if p1ind == 0:
                P1 = quadlist[iq1[s1ind]][0]
            else:
                P1 = quadlist[iq0[s1ind]][1]

            dist = geodetic.distance(P0.longitude, P0.latitude, 0.0,
                                     P1.longitude, P1.latitude, 0.0)
            if dist > dist_save:
                dist_save = dist
                A0 = P0
                A1 = P1

        # ---------------------------------------------------------------------
        # A0 and A1 are the furthest two segment endpoints, but we still
        # need to sort out which one is the "origin".
        # ---------------------------------------------------------------------

        # This goofy while-loop is to adjust the side of the rupture where the
        # origin is located
        dummy = -1
        while dummy < 0:
            A0.depth = 0
            A1.depth = 0
            p_origin = Vector.fromPoint(A0)
            a0 = Vector.fromPoint(A0)
            a1 = Vector.fromPoint(A1)
            ahat = (a1 - a0).norm()

            # Loop over traces
            e_j = np.zeros(n_groups)
            b_prime = [None] * n_groups
            for j in range(n_groups):
                P0 = quadlist[iq0[j]][0]
                P1 = quadlist[iq1[j]][1]
                P0.depth = 0
                P1.depth = 0
                p0 = Vector.fromPoint(P0)
                p1 = Vector.fromPoint(P1)
                b_prime[j] = p1 - p0
                e_j[j] = ahat.dot(b_prime[j])
            E = np.sum(e_j)

            # List of discordancy
            dc = [np.sign(a) * np.sign(E) for a in e_j]
            b = Vector(0, 0, 0)
            for j in range(n_groups):
                b.x = b.x + b_prime[j].x * dc[j]
                b.y = b.y + b_prime[j].y * dc[j]
                b.z = b.z + b_prime[j].z * dc[j]
            bhat = b.norm()
            dummy = bhat.dot(ahat)
            if dummy < 0:
                tmpA0 = copy.deepcopy(A0)
                tmpA1 = copy.deepcopy(A1)
                A0 = tmpA1
                A1 = tmpA0

        # ---------------------------------------------------------------------
        # To fix discordancy, need to flip quads and rearrange
        # the order of quadgc2
        # ---------------------------------------------------------------------

        # 1) flip quads
        for i in range(len(quadgc2)):
            if dc[group_ind[i]] < 0:
                quadgc2[i] = reverse_quad(quadgc2[i])

        # 2) rearrange quadlist order
        qind = np.arange(len(quadgc2))
        for i in range(n_groups):
            qsel = qind[group_ind_np == uind[i]]
            if dc[i] < 0:
                qrev = qsel[::-1]
                qind[group_ind_np == uind[i]] = qrev

        quadgc2old = copy.deepcopy(quadgc2)
        for i in range(len(qind)):
            quadgc2[i] = quadgc2old[qind[i]]

        # End of if-statement for adjusting group discordancy

    s_i = 0.0
    l_i = np.zeros(len(quadgc2))

    for i in range(len(quadgc2)):
        G0, G1, G2, G3 = quadgc2[i]

        # Compute u_i and t_i for this quad
        t_i = __calc_t_i(G0, G1, lat, lon, proj)
        u_i = __calc_u_i(G0, G1, lat, lon, proj)

        # Quad length (top edge)
        l_i[i] = get_quad_length(quadgc2[i])

        # ---------------------------------------------------------------------
        # Weight of segment, three cases
        # ---------------------------------------------------------------------

        # Case 3: t_i == 0 and 0 <= u_i <= l_i
        w_i = np.zeros_like(t_i)

        # Case 1:
        ix = t_i != 0
        w_i[ix] = (1.0 / t_i[ix]) * (np.arctan((l_i[i] -
                  u_i[ix]) / t_i[ix]) - np.arctan(-u_i[ix] / t_i[ix]))

        # Case 2:
        ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
        w_i[ix] = 1 / (u_i[ix] - l_i[i]) - 1 / u_i[ix]

        totweight = totweight + w_i
        GC2T = GC2T + w_i * t_i

        if n_groups == 1:
            GC2U = GC2U + w_i * (u_i + s_i)
        else:
            if i == 0:
                qind = np.array(range(len(quadgc2)))
                l_kj = 0
                s_ij_1 = 0
            else:
                l_kj = l_i[(group_ind_np == group_ind_np[i]) & (qind < i)]
                s_ij_1 = np.sum(l_kj)

            # First endpoint in the current 'group' (or 'trace' in GC2 terms)
            p1 = Vector.fromPoint(quadgc2[iq0[group_ind[i]]][0])
            s_ij_2 = (p1 - p_origin).dot(np.sign(E) * ahat) / 1000.0

            # Above is GC2N, for GC2T use:
            # s_ij_2 = (p1 - p_origin).dot(bhat) / 1000.0

            s_ij = s_ij_1 + s_ij_2
            GC2U = GC2U + w_i * (u_i + s_ij)

        s_i = s_i + l_i[i]

    GC2T = GC2T / totweight
    GC2U = GC2U / totweight

    # Dictionary for holding the distances
    distdict = dict()

    distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
    distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)

    # Take care of Rx
    Rx = copy.deepcopy(GC2T)  # preserve sign (no absolute value)
    Rx = Rx.reshape(oldshape)
    distdict['rx'] = Rx

    # Ry
    Ry = GC2U - s_i / 2.0
    Ry = Ry.reshape(oldshape)
    distdict['ry'] = Ry

    # Ry0
    Ry0 = np.zeros_like(GC2U)
    ix = GC2U < 0
    Ry0[ix] = np.abs(GC2U[ix])
    if n_groups > 1:
        s_i = s_ij + l_i[-1]
    ix = GC2U > s_i
    Ry0[ix] = GC2U[ix] - s_i
    Ry0 = Ry0.reshape(oldshape)
    distdict['ry0'] = Ry0

    return distdict
Exemplo n.º 38
0
 def test(self):
     p1 = (0, 0, 10)
     p2 = (0.5, -0.3, 5)
     distance = geodetic.distance(*(p1 + p2))
     self.assertAlmostEqual(distance, 65.0295143)
Exemplo n.º 39
0
def get_distance(methods, lat, lon, dep, source,
                 use_median_distance=True):
    """
    Calculate distance using any one of a number of distance measures.
    One of quadlist OR hypo must be specified. The following table gives
    the allowed distance strings and a description of each. 

    +--------+----------------------------------------------------------+
    | String | Description                                              |
    +========+==========================================================+
    | repi   | Distance to epicenter.                                   |
    +--------+----------------------------------------------------------+
    | rhypo  | Distance to hypocenter.                                  |
    +--------+----------------------------------------------------------+
    | rjb    | Joyner-Boore distance; this is closest distance to the   |
    |        | surface projection of the rupture plane.                 |
    +--------+----------------------------------------------------------+
    | rrup   | Rupture distance; closest distance to the rupture plane. |
    +--------+----------------------------------------------------------+
    | rx     | Strike-normal distance; same as GC2 coordiante T.        |
    +--------+----------------------------------------------------------+
    | ry     | Strike-parallel distance; same as GC2 coordiante U, but  |
    |        | with a shift in origin definition. See Spudich and Chiou |
    |        | (2015) http://dx.doi.org/10.3133/ofr20151028.            |
    +--------+----------------------------------------------------------+
    | ry0    | Horizontal distance off the end of the rupture measured  |
    |        | parallel to strike. Can only be zero or positive. We     |
    |        | compute this as a function of GC2 coordinate U.          |
    +--------+----------------------------------------------------------+
    | U      | GC2 coordinate U.                                        |
    +--------+----------------------------------------------------------+
    | T      | GC2 coordinate T.                                        |
    +--------+----------------------------------------------------------+

    :param methods:
        List of strings (or just a string) of distances to compute.
    :param lat:
       A numpy array of latitudes.
    :param lon:
       A numpy array of longidues.
    :param dep:
       A numpy array of depths (km).
    :param source:
       source instance.
    :param use_median_distance:
        Boolean; only used if GMPE requests fault distances and not fault is
        availalbe. Default is True, meaning that point-source distances are
        adjusted based on magnitude to get the median fault distance.
    :returns:
       dictionary of numpy arrays of distances, size of lon.shape
       IMPORTANT: If a finite fault is not supplied, and the distance
       measures requested include rx, ry, ry0, U, or T, then zeros will
       be returned; if rjb is requested, repi will be returned; if rrup
       is requested, rhypo will be returned.
    """
    fault = source.getFault()
    hypo = source.getHypo()
    if fault is not None:
        quadlist = fault.getQuadrilaterals()
        # Need a copy for GC2 since order of verticies/quads needs to be modivied.
        quadgc2 = copy.deepcopy(quadlist)
    else:
        quadlist = None

    # Dictionary for holding the distances
    distdict = dict()

    if not isinstance(methods, list):
        methods = [methods]

    methods_available = set(get_distance_measures())
    if not set(methods).issubset(methods_available):
        raise NotImplementedError(
            'One or more requested distance method is not '
            'valid or is not implemented yet')

    if (lat.shape == lon.shape) and (lat.shape == dep.shape):
        pass
    else:
        raise ShakeMapException('lat, lon, and dep must have the same shape.')

    oldshape = lon.shape

    if len(oldshape) == 2:
        newshape = (oldshape[0] * oldshape[1], 1)
    else:
        newshape = (oldshape[0], 1)

    if ('rrup' in methods) or ('rjb' in methods):
        x, y, z = latlon2ecef(lat, lon, dep)
        x.shape = newshape
        y.shape = newshape
        z.shape = newshape
        sites_ecef = np.hstack((x, y, z))

    # Define a projection that spands sites and fault
    if fault is None:
        all_lat = lat
        all_lon = lon
    else:
        all_lat = np.append(lat, fault.getLats())
        all_lon = np.append(lon, fault.getLons())

    west = np.nanmin(all_lon)
    east = np.nanmax(all_lon)
    south = np.nanmin(all_lat)
    north = np.nanmax(all_lat)
    proj = get_orthographic_projection(west, east, north, south)

    # ---------------------------------------------
    # Distances that do not require loop over quads
    # ---------------------------------------------

    if ('repi' in methods) or \
       (('rjb' in methods) and (quadlist is None)) or \
       (('rrup' in methods) and (quadlist is None)) or \
       (('ry0' in methods) and (quadlist is None)) or \
       (('rx' in methods) and (quadlist is None)) or \
       (('T' in methods) and (quadlist is None)) or \
       (('U' in methods) and (quadlist is None)):
        # I don't think this error check makes sense any more because hypo
        # is assigned above with source.getHypo() that constructs it from
        # source._event_dict entries. 
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        repidist = geodetic.distance(hypo.longitude, hypo.latitude, 0.0,
                                     lon, lat, dep)
        repidist = repidist.reshape(oldshape)
        distdict['repi'] = repidist

    if ('rhypo' in methods) or \
       (('rrup' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        rhypodist = geodetic.distance(
            hypo.longitude, hypo.latitude, hypo.depth, lon, lat, dep)
        rhypodist = rhypodist.reshape(oldshape)
        distdict['rhypo'] = rhypodist

    # --------------------------------------------------------
    # Loop over quadlist for those distances that require loop
    # --------------------------------------------------------
    if 'rrup' in methods:
        minrrup = np.ones(newshape, dtype=lon.dtype) * 1e16
    if 'rjb' in methods:
        minrjb = np.ones(newshape, dtype=lon.dtype) * 1e16
    if ('rx' in methods) or ('ry' in methods) or \
       ('ry0' in methods) or ('U' in methods) or ('T' in methods):
        totweight = np.zeros(newshape, dtype=lon.dtype)
        GC2T = np.zeros(newshape, dtype=lon.dtype)
        GC2U = np.zeros(newshape, dtype=lon.dtype)

        if quadlist is not None:
            #-----------------------------------------------------------------
            # For these distances, we need to sort out strike discordance and
            # nominal strike prior to starting the loop if there is more than
            # one segment.
            #-----------------------------------------------------------------

            segind = fault._getSegmentIndex()
            segindnp = np.array(segind)
            uind = np.unique(segind)
            nseg = len(uind)

            #-------------------------------------------------------------------
            # The first thing we need to worry about is finding the coordinate
            # shift. U's origin is " selected from the two endpoints most
            # distant from each other." 
            #-------------------------------------------------------------------

            if nseg > 1:
                # Need to get index of first and last quad
                # for each segment
                iq0 = np.zeros(nseg, dtype='int16')
                iq1 = np.zeros(nseg, dtype='int16')
                for k in uind:
                    ii = [i for i, j in enumerate(segind) if j == uind[k]]
                    iq0[k] = int(np.min(ii))
                    iq1[k] = int(np.max(ii))

                #---------------------------------------------------------------
                # This is an iterator for each possible combination of segments
                # including segment orientations (i.e., flipped). 
                #---------------------------------------------------------------

                it_seg = it.product(it.combinations(uind, 2),
                                    it.product([0, 1], [0, 1]))

                # Placeholder for the segment pair/orientation that gives the
                # largest distance. 
                dist_save = 0

                for k in it_seg:
                    s0ind = k[0][0]
                    s1ind = k[0][1]
                    p0ind = k[1][0]
                    p1ind = k[1][1]
                    if p0ind == 0:
                        P0 = quadlist[iq0[s0ind]][0]
                    else:
                        P0 = quadlist[iq1[s0ind]][1]
                    if p1ind == 0:
                        P1 = quadlist[iq1[s1ind]][0]
                    else:
                        P1 = quadlist[iq0[s1ind]][1]

                    dist = geodetic.distance(P0.longitude, P0.latitude, 0.0,
                                             P1.longitude, P1.latitude, 0.0)
                    if dist > dist_save:
                        dist_save = dist
                        A0 = P0
                        A1 = P1

                #---------------------------------------------------------------
                # A0 and A1 are the furthest two segment endpoints, but we still
                # need to sort out which one is the "origin".
                #---------------------------------------------------------------

                # Goofy while-loop is to adjust the side of the fault where the
                # origin is located
                dummy = -1
                while dummy < 0:
                    A0.depth = 0
                    A1.depth = 0
                    p_origin = Vector.fromPoint(A0)
                    a0 = Vector.fromPoint(A0)
                    a1 = Vector.fromPoint(A1)
                    ahat = (a1 - a0).norm()

                    # Loop over traces
                    e_j = np.zeros(nseg)
                    b_prime = [None] * nseg
                    for j in range(nseg):
                        P0 = quadlist[iq0[j]][0]
                        P1 = quadlist[iq1[j]][1]
                        P0.depth = 0
                        P1.depth = 0
                        p0 = Vector.fromPoint(P0)
                        p1 = Vector.fromPoint(P1)
                        b_prime[j] = p1 - p0
                        e_j[j] = ahat.dot(b_prime[j])
                    E = np.sum(e_j)

                    # List of discordancy
                    dc = [np.sign(a) * np.sign(E) for a in e_j]
                    b = Vector(0, 0, 0)
                    for j in range(nseg):
                        b.x = b.x + b_prime[j].x * dc[j]
                        b.y = b.y + b_prime[j].y * dc[j]
                        b.z = b.z + b_prime[j].z * dc[j]
                    bhat = b.norm()
                    dummy = bhat.dot(ahat)
                    if dummy < 0:
                        tmpA0 = copy.copy(A0)
                        tmpA1 = copy.copy(A1)
                        A0 = tmpA1
                        A1 = tmpA0

                # To fix discordancy, need to flip quads and rearrange
                # the order of quadgc2

                # 1) flip quads
                for i in range(len(quadgc2)):
                    if dc[segind[i]] < 0: #***************U*UUUUUUUSDFUSDfkjjhakjsdhfljkhn
                        quadgc2[i] = reverse_quad(quadgc2[i])

                # 2) rearrange quadlist to remove discordancy
                qind = np.arange(len(quadgc2))
                segnp = np.array(segind)
                for i in range(nseg):
                    qsel = qind[segnp == uind[i]]
                    if dc[i] < 0:
                        qrev = qsel[::-1]
                        qind[segnp == uind[i]] = qrev

                quadgc2old = copy.deepcopy(quadgc2)
                for i in range(len(qind)):
                    quadgc2[i] = quadgc2old[qind[i]]


    if quadlist is not None:
        # Length of prior segments
        s_i = 0.0
        l_i = np.zeros(len(quadlist))
        for i in range(len(quadlist)):
            P0, P1, P2, P3 = quadlist[i]
            G0, G1, G2, G3 = quadgc2[i]

            if 'rrup' in methods:
                rrupdist = _calc_rupture_distance(P0, P1, P2, P3, sites_ecef)
                minrrup = np.minimum(minrrup, rrupdist)

            if 'rjb' in methods:
                S0 = copy.deepcopy(P0)
                S1 = copy.deepcopy(P1)
                S2 = copy.deepcopy(P2)
                S3 = copy.deepcopy(P3)
                S0.depth = 0.0
                S1.depth = 0.0
                S2.depth = 0.0
                S3.depth = 0.0
                rjbdist = _calc_rupture_distance(S0, S1, S2, S3, sites_ecef)
                minrjb = np.minimum(minrjb, rjbdist)

            if ('rx' in methods) or ('ry' in methods) or \
               ('ry0' in methods) or ('U' in methods) or ('T' in methods):
                # Rx, Ry, and Ry0 are all computed if one is requested since
                # they all require similar information for the weights. This
                # isn't necessary for a single segment fault though.
                # Note, we are basing these calculations on GC2 coordinates U
                # and T as described in:
                # Spudich and Chiou (2015)
                # http://dx.doi.org/10.3133/ofr20151028.

                # Compute u_i and t_i for this quad
                t_i = __calc_t_i(G0, G1, lat, lon, proj)
                u_i = __calc_u_i(G0, G1, lat, lon, proj)

                # Quad length
                l_i[i] = get_quad_length(quadlist[i])

                # Weight of segment, three cases
                # Case 3: t_i == 0 and 0 <= u_i <= l_i
                w_i = np.zeros_like(t_i)
                # Case 1:
                ix = t_i != 0
                w_i[ix] = (1.0 / t_i[ix]) * (np.arctan((l_i[i] -
                    u_i[ix]) / t_i[ix]) - np.arctan(-u_i[ix] / t_i[ix]))
                # Case 2:
                ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
                w_i[ix] = 1 / (u_i[ix] - l_i[i]) - 1 / u_i[ix]

                totweight = totweight + w_i
                GC2T = GC2T + w_i * t_i
                if nseg == 1:
                    GC2U = GC2U + w_i * (u_i + s_i)
                else:
                    if i == 0:
                        qind = np.array(range(len(quadlist)))
                        l_kj = 0
                        s_ij_1 = 0
                    else:
                        l_kj = l_i[(segindnp == segindnp[i]) & (qind < i)]
                        s_ij_1 = np.sum(l_kj)

                    p1 = Vector.fromPoint(quadgc2[iq0[segind[i]]][0])
                    s_ij_2 = (p1 - p_origin).dot(np.sign(E)*ahat) / 1000.0
                    # Above is GC2N, for GC2T use:
#                    s_ij_2 = (p1 - p_origin).dot(bhat) / 1000.0


                    s_ij = s_ij_1 + s_ij_2
                    GC2U = GC2U + w_i * (u_i + s_ij)
                s_i = s_i + l_i[i]

        # Collect distances from loop into the distance dict
        if 'rjb' in methods:
            minrjb = minrjb.reshape(oldshape)
            distdict['rjb'] = minrjb

        if ('rx' in methods) or ('ry' in methods) or \
           ('ry0' in methods) or ('U' in methods) or ('T' in methods):
            # Normalize by sum of quad weights
            GC2T = GC2T / totweight
            GC2U = GC2U / totweight
            distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
            distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)

            # Take care of Rx
            Rx = copy.deepcopy(GC2T)  # preserve sign (no absolute value)
            Rx = Rx.reshape(oldshape)
            distdict['rx'] = Rx

            # Ry
            Ry = GC2U - s_i / 2.0
            Ry = Ry.reshape(oldshape)
            distdict['ry'] = Ry

            # Ry0
            Ry0 = np.zeros_like(GC2U)
            ix = GC2U < 0
            Ry0[ix] = np.abs(GC2U[ix])
            if nseg > 1:
                s_i = s_ij + l_i[-1]
            ix = GC2U > s_i
            Ry0[ix] = GC2U[ix] - s_i
            Ry0 = Ry0.reshape(oldshape)
            distdict['ry0'] = Ry0

        if 'rrup' in methods:
            minrrup = minrrup.reshape(oldshape)
            distdict['rrup'] = minrrup

    else:
        if 'rjb' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rjb with median rjb given M and repi.')
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                mech = source.getEventParam('mech')
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rjb_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rjb_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall(
                            'R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rjb_ratios_tbl['Repi_km']))
                repi2rjb_grid = repi2rjb_ratios_tbl.values[:, 1:]
                repi2rjb_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjb_grid, kx=1, ky=1)

                def repi2rjb_tbl(repi, M):
                    ratio = repi2rjb_obj.ev(np.log(repi), M)
                    rjb = repi * ratio
                    return rjb
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rjb_hat = repi2rjb_tbl(repis, mags)
                distdict['rjb'] = rjb_hat
                # -------------------
                # Additional Variance
                # -------------------
                repi2rjbvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rjbvar_grid = repi2rjbvar_ratios_tbl.values[:, 1:]
                repi2rjbvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjbvar_grid, kx=1, ky=1)
                rjbvar = repi2rjbvar_obj.ev(np.log(repis), mags)
                distdict['rjbvar'] = rjbvar
            else:
                warnings.warn('No fault; Replacing rjb with repi')
                distdict['rjb'] = distdict['repi'].copy()
        if 'rrup' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rrup with median rrup given M and repi.')
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                rake = source._event_dict.get('rake')
                mech = rake_to_mech(rake)
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rrup_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rrup_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall(
                            'R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rrup_ratios_tbl['Repi_km']))
                repi2rrup_grid = repi2rrup_ratios_tbl.values[:, 1:]
                repi2rrup_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrup_grid, kx=1, ky=1)

                def repi2rrup_tbl(repi, M):
                    ratio = repi2rrup_obj.ev(np.log(repi), M)
                    rrup = repi * ratio
                    return rrup
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rrup_hat = repi2rrup_tbl(repis, mags)
                distdict['rrup'] = rrup_hat

                # -------------------
                # Additional Variance
                # -------------------
                repi2rrupvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rrupvar_grid = repi2rrupvar_ratios_tbl.values[:, 1:]
                repi2rrupvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrupvar_grid, kx=1, ky=1)
                rrupvar = repi2rrupvar_obj.ev(np.log(repis), mags)
                distdict['rrupvar'] = rrupvar
            else:
                warnings.warn('No fault; Replacing rrup with rhypo')
                distdict['rrup'] = distdict['rhypo'].copy()
        if 'rx' in methods:
            warnings.warn('No fault; Setting Rx to zero.')
            distdict['rx'] = np.zeros_like(distdict['repi'])
        if 'ry0' in methods:
            warnings.warn('No fault; Setting ry0 to zero')
            distdict['ry0'] = np.zeros_like(distdict['repi'])
        if 'ry' in methods:
            warnings.warn('No fault; Setting ry to zero')
            distdict['ry'] = np.zeros_like(distdict['repi'])
        if 'U' in methods:
            warnings.warn('No fault; Setting U to zero')
            distdict['U'] = np.zeros_like(distdict['repi'])
        if 'T' in methods:
            warnings.warn('No fault; Setting T to zero')
            distdict['T'] = np.zeros_like(distdict['repi'])

    return distdict
Exemplo n.º 40
0
 def test_topo(self):
     p1 = (0, 0, 1)
     p2 = (0, 0, -1)
     distance = geodetic.distance(*(p1 + p2))
     self.assertAlmostEqual(distance, 2.0)
Exemplo n.º 41
0
 def test_topo2(self):
     p1 = (0, 0, 3)
     p2 = (0.5, -0.3, -2)
     distance = geodetic.distance(*(p1 + p2))
     self.assertAlmostEqual(distance, 65.0295143)
Exemplo n.º 42
0
def get_distance(methods, lat, lon, dep, source, 
                 use_median_distance = True):
    """
    Calculate distance using any one of a number of distance measures. 
    One of quadlist OR hypo must be specified.
    :param methods:
       List of strings (or just a string) of distances to compute; 
       can include: 'repi', 'rhypo', 'rjb', 'rrup', 'rx', 'ry', 'ry0'
         repi:  Distance to epicenter. 
         rhypo: Distance to hypocenter. 
         rjb:   Joyner-Boore distance; this is closest distance to the 
                surface projection of the rupture plane. 
         rrup:  Rupture distance; closest distance to the rupture plane. 
         rx:    Strike-normal distance; same as GC2 coordiante T. 
         ry:    Strike-parallel distance; same as GC2 coordiante U, but 
                with a shift in origin definition. See Spudich and Chiou
                (2015) http://dx.doi.org/10.3133/ofr20151028. 
         ry0:   Horizontal distance off the end of the rupture measured
                parallel to strike. Can only be zero or positive. We
                compute this as a function of GC2 coordinate U. 
         U:     GC2 coordinate U. 
         T:     GC2 coordinate T. 

    :param lat:
       A numpy array of latitudes.
    :param lon:
       A numpy array of longidues.
    :param dep:
       A numpy array of depths (km).
    :param source:
       source instance. 
    https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/geo/point.py
    :param use_median_distance: 
        Boolean; only used if GMPE requests fault distances and not fault is 
        availalbe. Default is True, meaning that point-source distances are 
        adjusted based on magnitude to get the median fault distance. 
    :returns:
       dictionary of numpy array of distances, size of lon.shape
    :raises ShakeMapException:
       if a fault distance method is called without quadlist
    :raises NotImplementedError:
       for unknown distance measures or ones not yet implemented.
    """
    fault = source.getFault()
    hypo = source.getHypo()
    if fault is not None:
        quadlist = fault.getQuadrilaterals()
    else:
        quadlist = None
    
    # Dictionary for holding the distances
    distdict = dict()
    
    if not isinstance(methods, list):
        methods = [methods]
    
    methods_available = set(['repi', 'rhypo', 'rjb', 'rrup', 'rx', 'ry', 'ry0', 'U', 'T'])
    if not set(methods).issubset(methods_available):
        raise NotImplementedError('One or more requested distance method is not '\
                                  'valid or is not implemented yet')

    if (lat.shape == lon.shape) and (lat.shape == dep.shape):
        pass
    else:
        raise ShakeMapException('lat, lon, and dep must have the same shape.')
    
    oldshape = lon.shape
    
    if len(oldshape) == 2:
        newshape = (oldshape[0]*oldshape[1],1)
    else:
        newshape = (oldshape[0],1)
    
    if ('rrup' in methods) or ('rjb' in methods):
        x, y, z = latlon2ecef(lat, lon, dep)
        x.shape = newshape
        y.shape = newshape
        z.shape = newshape
        sites_ecef = np.hstack((x, y, z))
    
    # ---------------------------------------------
    # Distances that do not require loop over quads
    # ---------------------------------------------
    
    if ('repi' in methods) or \
       (('rjb' in methods) and (quadlist is None)) or \
       (('rrup' in methods) and (quadlist is None)) or \
       (('ry0' in methods) and (quadlist is None)) or \
       (('rx' in methods) and (quadlist is None)) or \
       (('T' in methods) and (quadlist is None)) or \
       (('U' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '\
                                    'without a point object')
        repidist = geodetic.distance(hypo.longitude, hypo.latitude, 0.0,
                                     lon, lat, dep)
        repidist = repidist.reshape(oldshape)
        distdict['repi'] = repidist
    
    if ('rhypo' in methods) or \
       (('rrup' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '\
                                    'without a point object')
        rhypodist = geodetic.distance(hypo.longitude, hypo.latitude, hypo.depth,
                                      lon, lat, dep)
        rhypodist = rhypodist.reshape(oldshape)
        distdict['rhypo'] = rhypodist

    # --------------------------------------------------------
    # Loop over quadlist for those distances that require loop    
    # --------------------------------------------------------
    if 'rrup' in methods:
        minrrup = np.ones(newshape, dtype = lon.dtype)*1e16
    if 'rjb' in methods:
        minrjb = np.ones(newshape, dtype = lon.dtype)*1e16
    if ('rx' in methods) or ('ry' in methods) or \
       ('ry0' in methods) or ('U' in methods) or ('T' in methods):
        totweight = np.zeros(newshape, dtype = lon.dtype)
        GC2T = np.zeros(newshape, dtype = lon.dtype)
        GC2U = np.zeros(newshape, dtype = lon.dtype)
        
        if quadlist is not None: 
            # For these distances, we need to sort out strike discordance and nominal
            # strike prior to starting the loop if there are more than one segments
            segind = fault.getSegmentIndex()
            segindnp = np.array(segind)
            uind = np.unique(segind)
            nseg = len(uind)
            if nseg > 1: 
                quadlist = fault.getQuadrilaterals()
                # Need to get index of first and last quad
                # for each segment
                iq0 = np.zeros(nseg, dtype = 'int16')
                iq1 = np.zeros(nseg, dtype = 'int16')
                for k in uind:
                    ii = [i for i, j in enumerate(segind) if j == uind[k]]
                    iq0[k] = int(np.min(ii))
                    iq1[k] = int(np.max(ii))
                
                it_seg = it.product(it.combinations(uind, 2),
                                    it.product([0, 1], [0, 1]))
                dist_save = 0
                for k in it_seg:
                    s0ind = k[0][0]
                    s1ind = k[0][1]
                    p0ind = k[1][0]
                    p1ind = k[1][1]
                    if p0ind == 0:
                        P0 = quadlist[iq0[s0ind]][0]
                    else:
                        P0 = quadlist[iq1[s0ind]][1]
                    if p1ind == 0:
                        P1 = quadlist[iq1[s1ind]][0]
                    else:
                        P1 = quadlist[iq0[s1ind]][1]
                    
                    dist = geodetic.distance(P0.longitude, P0.latitude, 0.0, 
                                             P1.longitude, P1.latitude, 0.0)
                    if dist > dist_save:
                        dist_save = dist
                        A0 = P0
                        A1 = P1
                
                A0.depth = 0
                A1.depth = 0
                p_origin = Vector.fromPoint(A0)
                a0 = Vector.fromPoint(A0)
                a1 = Vector.fromPoint(A1)
                ahat = (a1 - a0).norm()
                # Loop over traces
                e_j = np.zeros(nseg)
                b_prime = [None]*nseg
                for j in range(nseg):
                    P0 = quadlist[iq0[j]][0]
                    P1 = quadlist[iq1[j]][1]
                    P0.depth = 0
                    P1.depth = 0
                    p0 = Vector.fromPoint(P0)
                    p1 = Vector.fromPoint(P1)
                    b_prime[j] = p1 - p0
                    e_j[j] = ahat.dot(b_prime[j])
                E = np.sum(e_j)
                # List of discordancy
                dc = [np.sign(a) * np.sign(E) for a in e_j]
                b = Vector(0, 0, 0)
                for j in range(nseg):
                    b.x = b.x + b_prime[j].x*dc[j]
                    b.y = b.y + b_prime[j].y*dc[j]
                    b.z = b.z + b_prime[j].z*dc[j]
                    bhat = b.norm()
    
    
    if quadlist is not None: 
        # Length of prior segments
        s_i = 0.0
        l_i = np.zeros(len(quadlist))
        for i in range(len(quadlist)):
            P0, P1, P2, P3 = quadlist[i]
            
            if 'rrup' in methods:
                rrupdist = calc_rupture_distance(P0, P1, P2, P3, sites_ecef)
                minrrup = np.minimum(minrrup, rrupdist)
            
            if 'rjb' in methods:
                S0 = copy.deepcopy(P0)
                S1 = copy.deepcopy(P1)
                S2 = copy.deepcopy(P2)
                S3 = copy.deepcopy(P3)
                S0.depth = 0.0
                S1.depth = 0.0
                S2.depth = 0.0
                S3.depth = 0.0
                rjbdist = calc_rupture_distance(S0, S1, S2, S3, sites_ecef)
                minrjb = np.minimum(minrjb, rjbdist)
            
            if ('rx' in methods) or ('ry' in methods) or \
               ('ry0' in methods) or ('U' in methods) or ('T' in methods):
                # Rx, Ry, and Ry0 are all computed if one is requested since
                # they all require similar information for the weights. This
                # isn't necessary for a single segment fault though.
                # Note, we are basing these calculations on GC2 coordinates U
                # and T as described in:
                # Spudich and Chiou (2015) http://dx.doi.org/10.3133/ofr20151028.
                
                # Compute u_i and t_i for this segment
                t_i = calc_t_i(P0, P1, lat, lon)
                u_i = calc_u_i(P0, P1, lat, lon)
                
                # Quad length
                l_i[i] = get_quad_length(quadlist[i])
                
                # Weight of segment, three cases
                # Case 3: t_i == 0 and 0 <= u_i <= l_i
                w_i = np.zeros_like(t_i)
                # Case 1:
                ix = t_i != 0
                w_i[ix] = (1.0/t_i[ix])*(np.arctan((l_i[i] - u_i[ix])/t_i[ix]) -
                                         np.arctan(-u_i[ix]/t_i[ix]))
                # Case 2:
                ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
                w_i[ix] = 1/(u_i[ix] - l_i[i]) - 1/u_i[ix]
                
                totweight = totweight + w_i
                GC2T = GC2T + w_i*t_i
                if nseg == 1:
                    GC2U = GC2U + w_i*(u_i + s_i)
                else:
                    if i == 0:
                        qind = np.array(range(len(quadlist)))
                        l_kj = 0
                        s_ij_1 = 0
                    else:
                        l_kj = l_i[(segindnp == segindnp[i]) & (qind < i) ]
                        s_ij_1 = np.sum(l_kj)
                    
                    p1 = Vector.fromPoint(quadlist[iq0[segind[i]]][0])
                    s_ij_2 = ((p1 - p_origin)*dc[segind[i]]).dot(bhat)/1000.0
                    s_ij = s_ij_1 + s_ij_2
                    GC2U = GC2U + w_i*(u_i + s_ij)
                s_i = s_i + l_i[i]
                
        # Collect distances from loop into the distance dict
        if 'rjb' in methods:
            minrjb = minrjb.reshape(oldshape)
            distdict['rjb'] = minrjb
        
        if ('rx' in methods) or ('ry' in methods) or \
           ('ry0' in methods) or ('U' in methods) or ('T' in methods):
            # Normalize by sum of quad weights
            GC2T = GC2T/totweight
            GC2U = GC2U/totweight
            distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
            distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)
            
            # Take care of Rx
            Rx = copy.deepcopy(GC2T) # preserve sign (no absolute value)
            Rx = Rx.reshape(oldshape)
            distdict['rx'] = Rx
            
            # Ry
            Ry = GC2U - s_i/2.0
            Ry = Ry.reshape(oldshape)
            distdict['ry'] = Ry
            
            # Ry0
            Ry0 = np.zeros_like(GC2U)
            ix = GC2U < 0
            Ry0[ix] = np.abs(GC2U[ix])
            if nseg > 1:
                s_i = s_ij + l_i[-1]
            ix = GC2U > s_i
            Ry0[ix] = GC2U[ix] - s_i
            Ry0 = Ry0.reshape(oldshape)
            distdict['ry0'] = Ry0
            
        if 'rrup' in methods:
            minrrup = minrrup.reshape(oldshape)
            distdict['rrup'] = minrrup
    
    else:
        if 'rjb' in methods:
            if use_median_distance:
                warnings.warn('No fault; Replacing rjb with median rjb given M and repi.')
                cdir, tmp = os.path.split(__file__)
                
                # -------------------
                # Sort out file names
                # -------------------
                mech = source.getEventParam('mech')
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Var.csv")
                else:
                    warnings.warn('Unsupported tectonic region; using coefficients for unknown'\
                                  'tectonic region.')
                    rf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                    
                # -----------------
                # Start with ratios
                # -----------------
                repi2rjb_ratios_tbl = pd.read_csv(rf, comment = '#')
                r2rrt_cols = repi2rjb_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rjb_ratios_tbl['Repi_km']))
                repi2rjb_grid = repi2rjb_ratios_tbl.values[:, 1:]
                repi2rjb_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjb_grid, kx = 1, ky = 1)
                def repi2rjb_tbl(repi, M):
                    ratio = repi2rjb_obj.ev(np.log(repi), M)
                    rjb = repi * ratio
                    return rjb
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rjb_hat = repi2rjb_tbl(repis, mags)
                distdict['rjb'] = rjb_hat
                # -------------------
                # Additional Variance
                # -------------------
                repi2rjbvar_ratios_tbl = pd.read_csv(vf, comment = '#')
                repi2rjbvar_grid = repi2rjbvar_ratios_tbl.values[:, 1:]
                repi2rjbvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjbvar_grid, kx = 1, ky = 1)
                rjbvar = repi2rjbvar_obj.ev(np.log(repis), mags)
                distdict['rjbvar'] = rjbvar
            else:
                warnings.warn('No fault; Replacing rjb with repi')
                distdict['rjb'] = distdict['repi']
        if 'rrup' in methods:
            if use_median_distance:
                warnings.warn('No fault; Replacing rrup with median rrup given M and repi.')
                cdir, tmp = os.path.split(__file__)
                
                # -------------------
                # Sort out file names
                # -------------------
                rake = source._event_dict.get('rake')
                mech = rake_to_mech(rake)
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Var.csv")
                else:
                    warnings.warn('Unsupported tectonic region; using coefficients for unknown'\
                                  'tectonic region.')
                    rf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                
                # -----------------
                # Start with ratios
                # -----------------
                repi2rrup_ratios_tbl = pd.read_csv(rf, comment = '#')
                r2rrt_cols = repi2rrup_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rrup_ratios_tbl['Repi_km']))
                repi2rrup_grid = repi2rrup_ratios_tbl.values[:, 1:]
                repi2rrup_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrup_grid, kx = 1, ky = 1)
                def repi2rrup_tbl(repi, M):
                    ratio = repi2rrup_obj.ev(np.log(repi), M)
                    rrup = repi * ratio
                    return rrup
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rrup_hat = repi2rrup_tbl(repis, mags)
                distdict['rrup'] = rrup_hat
                
                # -------------------
                # Additional Variance
                # -------------------
                repi2rrupvar_ratios_tbl = pd.read_csv(vf, comment = '#')
                repi2rrupvar_grid = repi2rrupvar_ratios_tbl.values[:, 1:]
                repi2rrupvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrupvar_grid, kx = 1, ky = 1)
                rrupvar = repi2rrupvar_obj.ev(np.log(repis), mags)
                distdict['rrupvar'] = rrupvar
            else:
                warnings.warn('No fault; Replacing rrup with rhypo')
                distdict['rrup'] = distdict['rhypo']
        if 'rx' in methods:
            warnings.warn('No fault; Setting Rx to zero.')
            distdict['rx'] = np.zeros_like(distdict['repi'])
        if 'ry0' in methods:
            warnings.warn('No fault; Replacing ry0 with repi')
            distdict['ry0'] = distdict['repi']
        if 'ry' in methods:
            warnings.warn('No fault; Replacing ry with repi')
            distdict['ry'] = distdict['repi']
    
    return distdict