def decluster(self, catalogue, config):
        """
        :param catalogue: Catalogue of earthquakes
        :type catalogue: Dictionary
        :param config: Configation parameters
        :type config: Dictionary

        :returns: **vcl vector** indicating cluster number, 
              **vmain_shock catalog** containing non-clustered events, 
              **flagvector** indicating which eq events belong to a cluster
        :rtype: numpy.ndarray
        """

        # Get relevent parameters
        neq = len(catalogue['magnitude'])  # Number of earthquakes
        # Get decimal year (needed for time windows)
        year_dec = decimal_year(
            catalogue['year'], catalogue['month'], catalogue['day'])
        # Get space and time windows corresponding to each event
        sw_space, sw_time = \
            time_dist_windows[config['window_opt']].\
            calc(catalogue['magnitude'])
        eqid = np.arange(0, neq, 1)  # Initial Position Identifier

        # Pre-allocate cluster index vectors
        vcl = np.zeros(neq, dtype=int)
        #print catalogue['magnitude'], year_dec
        # Sort magnitudes into descending order
        id0 = np.flipud(np.argsort(catalogue['magnitude'], kind='heapsort'))
        #m = m[id0]
        #catalog_matrix = catalog_matrix[id0, :]
        mag = catalogue['magnitude'][id0]
        longitude = catalogue['longitude'][id0]
        latitude = catalogue['latitude'][id0]
        sw_space = sw_space[id0]
        sw_time = sw_time[id0]
        year_dec = year_dec[id0]
        eqid = eqid[id0]
        flagvector = np.zeros(neq, dtype=int)
        #Begin cluster identification
        clust_index = 0
        for i in range(0, neq - 1):
            if vcl[i] == 0:
                #print vcl[i], mag, neq
                # Find Events inside both fore- and aftershock time windows
                dt = year_dec - year_dec[i]
                vsel = np.logical_and(
                    dt >= (-sw_time[i] * config['fs_time_prop']),
                    dt <= sw_time[i], 
                    flagvector == 0)
                # Of those events inside time window, find those inside distance
                # window
                vsel1 = haversine(longitude, latitude, longitude[i], 
                                  latitude[i]) <= sw_space[i]
                vsel[vsel] = vsel1
                temp_vsel = np.copy(vsel)
                temp_vsel[i] = False
                if any(temp_vsel):
                    # Allocate a cluster number
                    vcl[vsel] = clust_index + 1
                    flagvector[vsel] = 1
                    # For those events in the cluster before the main event,
                    # flagvector is equal to -1
                    temp_vsel[dt >= 0.0] = False
                    flagvector[temp_vsel] = -1
                    flagvector[i] = 0
                    clust_index += 1

        # Re-sort the catalog_matrix into original order
        id1 = np.argsort(eqid, kind='heapsort')
        eqid = eqid[id1]
        #catalog_matrix = catalog_matrix[id1, :]
        vcl = vcl[id1]
        flagvector = flagvector[id1]
        return vcl, flagvector
    def decluster(self, catalogue, config):
        '''catalogue_matrix, window_opt=TDW_GARDNERKNOPOFF, time_window=60.):

        :param catalog_matrix: eq catalog in a matrix format with these columns in
                                order: `year`, `month`, `day`, `longitude`,
                                `latitude`, `Mw`
        :type catalog_matrix: numpy.ndarray
        :keyword window_opt: method used in calculating distance and time windows
        :type window_opt: string
        :keyword time_window: Length (in days) of moving time window
        :type time_window: positive float
        :returns: **vcl vector** indicating cluster number, **vmain_shock catalog**
                  containing non-clustered events, **flagvector** indicating
                  which eq events belong to a cluster
        :rtype: numpy.ndarray
        '''

        #Convert time window from days to decimal years
        time_window = config['time_window'] / 365.

        # Pre-processing steps are the same as for Gardner & Knopoff
        # Get relevent parameters
        mag = catalogue['magnitude']
        neq = np.shape(magnitude)[0]  # Number of earthquakes
        # Get decimal year (needed for time windows)
        year_dec = decimal_year(catalogue['year'], catalogue['month'],
                                catalogue['day'])

        # Get space windows corresponding to each event
        sw_space = time_dist_windows[config['window_opt']].calc(mag)[0]

        eqid = np.arange(0, neq, 1)  # Initial Position Identifier

        # Pre-allocate cluster index vectors
        vcl = np.zeros((neq, 1), dtype=int)
        flagvector = np.zeros((neq, 1), dtype=int)
        # Sort magnitudes into descending order
        id0 = np.flipud(np.argsort(mag, kind='heapsort'))
        mag = mag[id0]
        #catalogue_matrix = catalogue_matrix[id0, :]
        longitude = catalogue['longitude'][id0]
        latitude = catalogue['latitude'][id0]
        sw_space = sw_space[id0]
        year_dec = year_dec[id0]
        eqid = eqid[id0]

        i = 0
        clust_index = 0
        while i < neq:
            if vcl[i] == 0:
                # Earthquake not allocated to cluster - perform calculation
                # Perform distance calculation
                mdist = haversine(longitude, latitude,
                                  longitude[i], latitude[i])

                # Select earthquakes inside distance window and not in cluster
                vsel = np.logical_and(mdist <= sw_space[i], vcl == 0).flatten()
                dtime = year_dec[vsel] - year_dec[i]

                nval = np.shape(dtime)[0] #Number of events inside valid window
                # Pre-allocate boolean array (all True)

                vsel1 = self._find_aftershocks(dtime, nval, time_window)
                vsel2 = self._find_foreshocks(dtime, nval, time_window, vsel1)

                temp_vsel = np.copy(vsel)
                temp_vsel[vsel] = np.logical_or(vsel1, vsel2)
                if np.shape(np.nonzero(temp_vsel)[0])[0] > 1:
                    # Contains clustered events - allocate a cluster index
                    vcl[temp_vsel] = clust_index + 1
                    # Remove mainshock from cluster
                    vsel1[0] = False
                    # Assign markers to aftershocks and foreshocks
                    temp_vsel = np.copy(vsel)
                    temp_vsel[vsel] = vsel1
                    flagvector[temp_vsel] = 1
                    vsel[vsel] = vsel2
                    flagvector[vsel] = -1
                    clust_index += 1
            i += 1

        # Now have events - re-sort array back into chronological order
        # Re-sort the data into original order
        id1 = np.argsort(eqid, kind='heapsort')
        eqid = eqid[id1]
        #catalogue_matrix = catalogue_matrix[id1, :]
        vcl = vcl[id1]
        flagvector = flagvector[id1]
        return vcl.flatten(), flagvector.flatten()
def afteran_decluster(
    catalogue_matrix, window_opt=TDW_GARDNERKNOPOFF, time_window=60.):
    '''AFTERAN declustering algorithm.
    ||(Musson, 1999, "Probabilistic Seismic Hazard Maps for the North Balkan
       region", Annali di Geofisica, 42(6), 1109 - 1124) ||
    :param catalog_matrix: eq catalog in a matrix format with these columns in
                            order: `year`, `month`, `day`, `longitude`,
                            `latitude`, `Mw`
    :type catalog_matrix: numpy.ndarray
    :keyword window_opt: method used in calculating distance and time windows
    :type window_opt: string
    :keyword time_window: Length (in days) of moving time window
    :type time_window: positive float
    :returns: **vcl vector** indicating cluster number, **vmain_shock catalog**
              containing non-clustered events, **flagvector** indicating
              which eq events belong to a cluster
    :rtype: numpy.ndarray
    '''

    #Convert time window from days to decimal years
    time_window = time_window / 365.

    # Pre-processing steps are the same as for Gardner & Knopoff
    # Get relevent parameters
    mag = catalogue_matrix[:, 5]

    neq = np.shape(catalogue_matrix)[0]  # Number of earthquakes
    # Get decimal year (needed for time windows)
    year_dec = decimal_year(catalogue_matrix[:, 0], catalogue_matrix[:, 1],
                            catalogue_matrix[:, 2])

    # Get space windows corresponding to each event
    sw_space = time_dist_windows[window_opt].calc(mag)[0]

    eqid = np.arange(0, neq, 1)  # Initial Position Identifier

    # Pre-allocate cluster index vectors
    vcl = np.zeros((neq, 1), dtype=int)
    flagvector = np.zeros((neq, 1), dtype=int)
    # Sort magnitudes into descending order
    id0 = np.flipud(np.argsort(mag, kind='heapsort'))
    mag = mag[id0]
    catalogue_matrix = catalogue_matrix[id0, :]
    sw_space = sw_space[id0]
    year_dec = year_dec[id0]
    eqid = eqid[id0]

    i = 0
    clust_index = 0
    while i < neq:
        if vcl[i] == 0:
            # Earthquake not allocated to cluster - perform calculation
            # Perform distance calculation
            mdist = haversine(catalogue_matrix[:, 3], catalogue_matrix[:, 4],
                          catalogue_matrix[i, 3], catalogue_matrix[i, 4])

            # Select earthquakes inside distance window and not in cluster
            vsel = np.logical_and(mdist <= sw_space[i], vcl == 0).flatten()
            dtime = year_dec[vsel] - year_dec[i]

            nval = np.shape(dtime)[0]  # Number of events inside valid window
            # Pre-allocate boolean array (all True)

            vsel1 = _find_aftershocks(dtime, nval, time_window)
            vsel2 = _find_foreshocks(dtime, nval, time_window, vsel1)

            temp_vsel = np.copy(vsel)
            temp_vsel[vsel] = np.logical_or(vsel1, vsel2)
            if np.shape(np.nonzero(temp_vsel)[0])[0] > 1:
                # Contains clustered events - allocate a cluster index
                vcl[temp_vsel] = clust_index + 1
                # Remove mainshock from cluster
                vsel1[0] = False
                # Assign markers to aftershocks and foreshocks
                temp_vsel = np.copy(vsel)
                temp_vsel[vsel] = vsel1
                flagvector[temp_vsel] = 1
                vsel[vsel] = vsel2
                flagvector[vsel] = -1
                clust_index += 1
        i += 1

    # Now have events - re-sort array back into chronological order
    # Re-sort the data into original order
    id1 = np.argsort(eqid, kind='heapsort')
    eqid = eqid[id1]
    catalogue_matrix = catalogue_matrix[id1, :]
    vcl = vcl[id1]
    flagvector = flagvector[id1]

    # Now to produce a catalogue with aftershocks purged
    vmain_shock = catalogue_matrix[np.nonzero(flagvector == 0)[0], :]

    return vcl.flatten(), vmain_shock, flagvector.flatten()