Esempio n. 1
0
def calculate_gmrotd(stream, percentiles, rotated=False, **kwargs):
    """
    Rotate two horizontal channels using the geometric mean.
    Args:
        stream (obspy.core.stream.Stream or list): stream of oscillators or
            list of rotation matrices.
        percentiles (list): list of percentiles (float).
            Example: [100, 50, 75] results in RotD100, RotD50, RotD75.
        rotated (bool): Wheter the stream is a rotation matrix. Used by the
                arias intensity calculation. Default is False.
    Returns:
        dictionary: Dictionary of geometric mean for each percentile.
    """
    if rotated:
        gm_percentiles = get_max(stream[0], 'gm', stream[1], percentiles)[1]
        gmrotd_dict = {}
        for idx, percent in enumerate(percentiles):
            gmrotd_dict[percent] = gm_percentiles[idx]
    else:
        horizontals = _get_horizontals(stream)
        if len(horizontals) > 2:
            raise PGMException('More than two horizontal channels.')
        elif len(horizontals) < 2:
            raise PGMException('Less than two horizontal channels.')
        osc1, osc2 = horizontals[0].data, horizontals[1].data
        if len(osc1) != len(osc2):
            raise PGMException('Horizontal channels have different lengths.')

        osc1_rot, osc2_rot = rotate(osc1, osc2, combine=False)
        gm_percentiles = get_max(osc1_rot, 'gm', osc2_rot, percentiles)[1]

        gmrotd_dict = {}
        for idx, percent in enumerate(percentiles):
            gmrotd_dict[percent] = gm_percentiles[idx]
    return gmrotd_dict
    def _get_horizontals(self):
        """
        Gets the two horizontal components.

        Returns:
            horizontal_channels: list of horizontal channels
                    (obspy.core.trac.Trace).

        Raises:
            PGMException: if there are less than or greater than two
                    horizontal channels, or if the lengths of the channels
                    are different.
        """
        horizontal_channels = []
        for trace in self.transform_data:
            # Group all of the max values from traces without
            # Z in the channel name
            if 'Z' not in trace.stats['channel'].upper():
                horizontal_channels += [trace]
        # Test the horizontals
        if len(horizontal_channels) > 2:
            raise PGMException('Rotation: More than two horizontal channels.')
        elif len(horizontal_channels) < 2:
            raise PGMException('Rotation: Less than two horizontal channels.')
        elif (len(horizontal_channels[0].data) !=
              len(horizontal_channels[1].data)):
            raise PGMException(
                'Rotation: Horizontal channels have different lengths.')
        return horizontal_channels
def calculate_greater_of_two_horizontals(stream, **kwargs):
    """Return GREATER_OF_TWO_HORIZONTALS value for given input Stream.

    NB: The input Stream should have already been "processed",
    i.e., filtered, detrended, tapered, etc.)

    Args:
        stream (Obspy Stream): Stream containing one or Traces of
            acceleration data in gals.
        kwargs (**args): Ignored by this class.

    Returns:
        float: GREATER_OF_TWO_HORIZONTALS (float).
    """
    horizontal_vals = []
    for trace in stream:
        # Group all of the max values from traces without
        # Z in the channel name
        if 'Z' not in trace.stats['channel'].upper():
            horizontal_vals += [np.abs(trace.max())]
    if len(horizontal_vals) > 2:
        raise PGMException('More than two horizontal channels.')
    elif len(horizontal_vals) < 2:
        raise PGMException('Less than two horizontal channels.')
    greater_pgm = np.max(np.asarray(horizontal_vals))
    return greater_pgm
Esempio n. 4
0
    def validate_stream(self):
        """
        Validates that the input is a StationStream, the units are either
        'vel' or 'acc', and the length of the traces are all equal.

        Railses:
            PGMException for the cases where:
                    1. The input is not a StationStream.
                    2. The units are not velocity or acceleration.
                    3. The length of the traces are not equal.
        """
        if not isinstance(self.timeseries, StationStream):
            raise PGMException("MetricsController: Input timeseries must be "
                               "a StationStream.")
        for idx, trace in enumerate(self.timeseries):
            units = trace.stats.standard.units
            trace_length = len(trace.data)
            if units.lower() != 'vel' and units.lower() != 'acc':
                raise PGMException("MetricsController: Trace units must be "
                                   "either 'vel' or 'acc'.")
            if idx == 0:
                standard_length = trace_length
            else:
                if trace_length != standard_length:
                    raise PGMException("MetricsController: Traces must all "
                                       "be the same length.")
Esempio n. 5
0
def calculate_fas(stream, imcs, periods, smoothing, bandwidth):
    """
    Calculate the fourier amplitude spectra.

    This process requires getting the fourier amplitude spectra, applying
    the geometric mean to the horizontal traces, smoothing and returning the
    smoothed value for each requested period.

    Args:
        stream (obspy.core.stream.Stream): streams of strong ground motion.
            Traces in stream must be in units of g.
        imcs (list): list of imcs. (Currently only the geometric mean is
            supported and this is ignored. In the future it will account for
            channels and rotated channels.)

    Returns:
        dictionary: Dictionary of fas for the geometric mean.
            Units are in cm/s. The dictionary is structured as:
            fas_dict = {
                    <period> : <value,...
            }
    """
    fas_dict = {}
    sampling_rate = None
    # check units and add channel pga
    for trace in stream:
        if trace.stats['units'] != 'g' and trace.stats['units'] != 'cm/s/s':
            raise PGMException('Invalid units for sa: %r. '
                               'Units must be g (cm/s/s)' % trace.stats['units'])
        if 'Z' not in trace.stats['channel'].upper():
            sampling_rate = trace.stats.sampling_rate
    if sampling_rate is None:
        raise PGMException('No horizontal channels')

    spec_stream = Stream()
    for trace in stream:
        nfft = len(trace.data)
        # the fft scales so the factor of 1/nfft is applied
        spectra = abs(np.fft.rfft(trace.data, n=nfft)) / nfft
        spec_trace = Trace(data=spectra, header=trace.stats)
        spec_stream.append(spec_trace)

    ## The imc is always geometric mean. However, the combined stream is
    ## required rather than the single maximum value
    gm_trace = calculate_geometric_mean(spec_stream, return_combined=True)
    freqs = np.fft.rfftfreq(nfft, 1 / trace.stats.sampling_rate)

    fas_frequencies = 1 / np.asarray(list(periods))
    smoothed_values = np.empty_like(fas_frequencies)

    if smoothing.lower() == 'konno_ohmachi':
        konno_ohmachi_smooth(gm_trace.astype(np.double), freqs,
                fas_frequencies, smoothed_values, bandwidth)
    else:
        raise PGMException('Not a valid smoothing option: %r' % smoothing)

    for idx, freq in enumerate(fas_frequencies):
        fas_dict[1/freq] = smoothed_values[idx]

    return fas_dict
def calculate_radial_transverse(stream, origin, **kwargs):
    """
    Rotate channels to radial and tranvsere components, provided with
    an event origin location.

    NB: The input Stream should have already been "processed",
    i.e., filtered, detrended, tapered, etc.)

    Args:
        stream (Obspy Stream):
            Stream containing traces of acceleration data in gals.
        origin (Obspy event origin object):
            Origin for the event containing latitude and longitude.
    """

    st_copy = stream.copy()
    st_n = st_copy.select(component='[N1]')
    st_e = st_copy.select(component='[E2]')

    # Check that we have one northing and one easting channel
    if len(st_e) != 1 or len(st_n) != 1:
        raise PGMException('Stream must have one north and one east channel.')

    # Check that the orientations are orthogonal
    if abs(st_e[0].stats.standard.horizontal_orientation -
           st_n[0].stats.standard.horizontal_orientation) not in [90, 270]:
        raise PGMException('Channels must be orthogonal.')

    # Check that the lengths of the two channels are the same
    if st_e[0].stats.npts != st_n[0].stats.npts:
        raise PGMException('East and north channels must have same length.')

    # First, rotate to North-East components if not already
    if st_n[0].stats.standard.horizontal_orientation != 0:
        az_diff = 360 - st_n[0].stats.standard.horizontal_orientation
        az_diff = np.deg2rad(az_diff)
        rotation_matrix = np.array([[np.cos(az_diff), np.sin(az_diff)],
                                   [-np.sin(az_diff), np.cos(az_diff)]])
        data = np.array([st_n[0].data, st_e[0].data])
        newdata = np.matmul(rotation_matrix, data)

        st_n[0].data = newdata[0]
        st_e[0].data = newdata[1]

    st_n[0].stats.channel = st_n[0].stats.channel[:-1] + 'N'
    st_e[0].stats.channel = st_n[0].stats.channel[:-1] + 'E'

    # Calculate back azimuth and perform rotation to radial and transverse
    baz = gps2dist_azimuth(
        st_e[0].stats.coordinates.latitude,
        st_e[0].stats.coordinates.longitude,
        origin.latitude, origin.longitude)[1]
    st_copy.rotate(method='NE->RT', back_azimuth=baz)

    channels_dict = {}
    channels_dict['R'] = abs(st_copy.select(component='R')[0].max())
    channels_dict['T'] = abs(st_copy.select(component='T')[0].max())

    return channels_dict
Esempio n. 7
0
def _calculate_rotated_arias(stream, rotation):
    """Calculates Arias Intensity.
    Args:
        stream (obspy.core.stream.Stream): Stream of acceleration values
                in m/s/s.
        rotation (str): Type of rotation. gm or nongm.
    Returns:
        Ia (list): list of rotation matrices of Arias intensity values
                in m/s with respect to time.
        NIa (list): list of rotation matrices of normalized Arias intensity
                values with respect to time.
    Raises:
        PGMException: If the units are not valid. Units must be m/s/s. If two
                horizontal components are not available. If time delta is not
                the same for all horizontal traces.
    """
    horizontals = []
    for trace in stream:
        if trace.stats['units'] != 'm/s/s':
            raise PGMException('Invalid units for ARIAS: %r. '
                               'Units must be m/s/s' % trace.stats['units'])
        if trace.stats['channel'].upper().find('Z') < 0:
            horizontals += [trace.copy()]
    if len(horizontals) != 2:
        PGMException('Two horizontal channels are not available. Rotation '
                     'cannot be performed.')
    dt = horizontals[0].stats['delta']
    g = sp.g
    if rotation == 'nongm':
        rot = [rotate(horizontals[0], horizontals[1], combine=True)]
    elif rotation == 'gm':
        rot1, rot2 = rotate(horizontals[0], horizontals[1], combine=False)
        rot = [rot1, rot2]
    NIa = []
    Ia = []
    for channel in rot:
        for idx, rot_degree in enumerate(channel):
            acc2 = rot_degree
            # Calculate Arias intensity
            integration = integrate.cumtrapz(acc2 * acc2, dx=dt)
            arias_intensity = integration * np.pi / (2 * g)

            # Calculate normalized Arias intensity
            # divide arias intensity by its max value
            norm_arias_intensity = arias_intensity / np.amax(arias_intensity)

            if idx == 0:
                rotated_ia = [arias_intensity]
                rotated_nia = [norm_arias_intensity]
            else:
                rotated_ia = np.append(rotated_ia, [arias_intensity], axis=0)
                rotated_nia = np.append(rotated_nia, [norm_arias_intensity],
                                        axis=0)
        NIa += [rotated_nia]
        Ia += [rotated_ia]
    return (Ia, NIa)
Esempio n. 8
0
def calculate_sa(stream, imcs, rotation_matrix=None, origin=None):
    """
    Calculate the peak ground acceleration.

    Args:
        stream (obspy.core.stream.Stream): streams of strong ground motion.
            Traces in stream must be in units of %%g.
        imcs (list): list of imcs.
        rotation_matrix (ndarray): A rotation matrix for the rotd component.
            This is required when the rotd component is requested.
        origin (obspy.core.event.origin.Origin):
            Obspy event origin object.

    Returns:
        dictionary: Dictionary of sa for different components.
    """
    sa_dict = {}
    # check units and add channel pga
    for trace in stream:
        if trace.stats['units'] != '%%g':
            raise PGMException('Invalid units for sa: %r. '
                               'Units must be %%g' % trace.stats['units'])
    grouped_imcs = group_imcs(imcs)
    # gather imc classes
    pgm_classes = get_pgm_classes('imc')
    # store sa for imcs
    for imc in grouped_imcs:
        if 'calculate_' + imc in pgm_classes:
            sa_func = pgm_classes['calculate_' + imc]
            sa = sa_func(stream, origin=origin, percentiles=grouped_imcs[imc])
            if imc == 'rotd':
                if rotation_matrix is None:
                    raise PGMException(
                        'The rotation matrix must be included '
                        'in order to calculate the rotd component.')
                sa = sa_func(rotation_matrix,
                             origin=origin,
                             percentiles=grouped_imcs[imc],
                             rotated=True)
                for percentile in sa:
                    sa_dict[imc.upper() + str(percentile)] = sa[percentile]
            elif imc.find('rot') >= 0:
                for percentile in sa:
                    sa_dict[imc.upper() + str(percentile)] = sa[percentile]
            elif imc.find('channels') >= 0:
                for channel in sa:
                    sa_dict[channel] = sa[channel]
            elif imc.find('radial_transverse') >= 0:
                for channel in sa:
                    sa_dict[channel] = sa[channel]
            else:
                sa_dict[imc.upper()] = sa
        else:
            logging.warning('Not a valid IMC: %r. Skipping...' % imc)
    return sa_dict
Esempio n. 9
0
    def __init__(
        self,
        reduction_data,
        bandwidth=None,
        percentile=None,
        period=None,
        smoothing=None,
        interval=[5, 95],
    ):
        """
        Args:
            reduction_data (obspy.core.stream.Stream or numpy.ndarray):
                Intensity measurement component.
            bandwidth (float):
                Bandwidth for the smoothing operation.
            percentile (float):
                Percentile for rotation calculations.
            period (float):
                Period for smoothing (Fourier amplitude spectra) calculations.
            smoothing (string):
                Smoothing type.
            interval (list):
                List of length 2 with the quantiles (0-1) for duration interval
                calculation.

        Raises:
            PGMException: if the bandwidth, period, or smoothing values
            are None.
        """
        super().__init__(
            reduction_data, bandwidth=None, percentile=None, period=None, smoothing=None
        )
        if period is None:
            raise PGMException(
                "Smooth_Select: The period value must "
                "be defined and of type float or int."
            )
        if bandwidth is None:
            raise PGMException(
                "Smooth_Select: The bandwidth value must "
                "be defined and of type float or int."
            )
        if smoothing is None:
            raise PGMException(
                "Smooth_Select: The smoothing value must "
                "be defined and of type string."
            )
        self.period = period
        self.smoothing = smoothing
        self.bandwidth = bandwidth
        self.result = self.get_pick()
Esempio n. 10
0
    def __init__(self, reduction_data, bandwidth=None, percentile=None,
                 period=None, smoothing=None):
        """
        Args:
            reduction_data (obspy.core.stream.Stream or numpy.ndarray):
                Intensity measurement component.
            percentile (float):
                Percentile for rotation calculations.
            period (float):
                Period for smoothing (Fourier amplitude spectra) calculations.
                Default is None.
            smoothing (string):
                Smoothing type. Default is None.
            bandwidth (float):
                Bandwidth for the smoothing operation. Default is None.

        Raises:
            PGMException: if the percentile value is None.
        """
        super().__init__(reduction_data, bandwidth=None, percentile=None,
                         period=None, smoothing=None)
        if percentile is None:
            raise PGMException('Percentile: The percentile value must '
                               'be defined and of type float or int.')
        self.percentile = percentile
        self.result = self.get_percentile()
Esempio n. 11
0
    def _get_horizontals(self):
        """
        Gets the two horizontal components.

        Returns:
            horizontal_channels: list of horizontal channels
                    (obspy.core.trac.Trace or float).

        Raises:
            PGMException: if there are less than or greater than two
                    horizontal channels, or if the length of the traces are
                    not equal.
        """
        horizontal_channels = []
        if isinstance(self.combination_data, (StationStream, Stream)):
            for trace in self.combination_data:
                # Group all of the max values from traces without
                # Z in the channel name
                if 'Z' not in trace.stats['channel'].upper():
                    horizontal_channels += [trace]
            ## Test the horizontals
            if len(horizontal_channels) > 2:
                raise PGMException(
                    'Combination: More than two horizontal channels.')
            elif len(horizontal_channels) < 2:
                raise PGMException(
                    'Combination: Less than two horizontal channels.')
            elif len(horizontal_channels[0].data) != len(
                    horizontal_channels[1].data):
                raise PGMException(
                    'Combination: Horizontal channels have different lengths.')
        elif isinstance(self.combination_data, dict):
            for channel_key in self.combination_data:
                # Group all of the max values from traces without
                # Z in the channel name
                if 'Z' not in channel_key:
                    horizontal_channels += [self.combination_data[channel_key]]
            if len(horizontal_channels) > 2:
                raise PGMException(
                    'Combination: More than two horizontal channels.')
            elif len(horizontal_channels) < 2:
                raise PGMException(
                    'Combination: Less than two horizontal channels.')
        else:
            raise Exception('Combination: Invalid input data type')
        return horizontal_channels
Esempio n. 12
0
 def __init__(self,
              imts,
              imcs,
              timeseries,
              bandwidth=None,
              damping=None,
              event=None,
              smooth_type=None):
     """
     Args:
         imts (list):
             Intensity measurement types (string) to calculate.
         imcs (list):
             Intensity measurement components (string) to
             calculate. timeseries (StationStream): Stream of the
             timeseries data.
         event (ScalarEvent):
             Defines the focal time, geographic location, and magnitude of
             an earthquake hypocenter. Default is None.
         damping (float):
             Damping for the oscillator calculation.
         bandwidth (float):
             Bandwidth for the smoothing calculation.
         smoothing (string):
             Currently not used, as konno_ohmachi is the only smoothing
             type.
     """
     if not isinstance(imts, (list, np.ndarray)):
         imts = [imts]
     if not isinstance(imcs, (list, np.ndarray)):
         imcs = [imcs]
     self.imts = set(np.sort([imt.lower() for imt in imts]))
     self.imcs = set(np.sort([imc.lower() for imc in imcs]))
     if 'radial_transverse' in self.imcs and event is None:
         raise PGMException('MetricsController: Event is required for '
                            'radial_transverse imc')
     self.timeseries = timeseries
     self.validate_stream()
     self.event = event
     self.config = get_config()
     self.damping = damping
     self.smooth_type = smooth_type
     self.bandwidth = bandwidth
     if damping is None:
         self.damping = self.config['metrics']['sa']['damping']
     if smooth_type is None:
         self.smooth_type = self.config['metrics']['fas']['smoothing']
     if bandwidth is None:
         self.bandwidth = self.config['metrics']['fas']['bandwidth']
     self._available_imts, self._available_imcs = gather_pgms()
     self._step_sets = self.get_steps()
     imtstr = '_'.join(imts)
     if '_sa' in imtstr or imtstr.startswith('sa'):
         self._times = self._get_horizontal_time()
     else:
         self._times = None
     self.pgms = self.execute_steps()
Esempio n. 13
0
def _gm_combine(stream):
    horizontals = []
    for trace in stream:
        # Group all of the max values from traces without
        # Z in the channel name
        if 'Z' not in trace.stats['channel'].upper():
            horizontals += [trace]
    if len(horizontals) > 2:
        raise PGMException('More than two horizontal channels.')
    elif len(horizontals) < 2:
        raise PGMException('Less than two horizontal channels.')

    if len(horizontals[0].data) != len(horizontals[1].data):
        raise PGMException('Horizontal channels are not the same length.')
    if horizontals[0].stats.sampling_rate != horizontals[1].stats.sampling_rate:
        raise PGMException(
            'Horizontal channels have different sampling rates.')

    geometric_means = np.sqrt(
        np.mean([np.abs(trace.data)**2 for trace in horizontals], axis=0))
    return geometric_means
Esempio n. 14
0
def calculate_rotd(stream, percentiles, rotated=False, **kwargs):
    """
    Rotate two horizontal channels and combine to get the spectral response.

    Args:
        stream (obspy.core.stream.Stream): stream of oscillators.
        percentiles (list): list of percentiles (float).
            Example: [100, 50, 75] results in RotD100, RotD50, RotD75.
        rotated (bool): Wheter the stream is a rotation matrix. Used by
            the arias intensity calculation. Default is False.

    Returns:
        dictionary: Dictionary of oienation indeendent nongeometric mean
        measures for each percentile.
    """
    if rotated:
        rot_percentiles = get_max(stream[0], 'max', percentiles=percentiles)[1]
        rotd_dict = {}
        for idx, percent in enumerate(percentiles):
            rotd_dict[percent] = rot_percentiles[idx]
    else:
        horizontals = _get_horizontals(stream)
        if len(horizontals) > 2:
            raise PGMException('More than two horizontal channels.')
        elif len(horizontals) < 2:
            raise PGMException('Less than two horizontal channels.')
        osc1, osc2 = horizontals[0].data, horizontals[1].data
        if len(osc1) != len(osc2):
            raise PGMException('Horizontal channels have different lengths.')

        rot = rotate(osc1, osc2, combine=True)
        rot_percentiles = get_max(rot, 'max', None, percentiles)[1]

        rotd_dict = {}
        for idx, percent in enumerate(percentiles):
            rotd_dict[percent] = rot_percentiles[idx]
    return rotd_dict
Esempio n. 15
0
    def _get_horizontal_time(self):
        """
        Get the 'times' array for a horizontal channel. This is required for
        spectral accelerations where a rotation (rotd) is requested.

        Returns:
            numpy.ndarray: Array of times for a horizontal channel.

        Raises:
            PGMException: if there are no horizontal channels.
        """
        for trace in self.timeseries:
            if 'Z' not in trace.stats['channel'].upper():
                times = trace.times()
                return times
        raise PGMException(
            'MetricsController: At least one horizontal '
            'channel is required for calculations of SA, ROTD, GMROTD, GM.')
Esempio n. 16
0
def calculate_pga(stream, imcs, origin=None):
    """
    Calculate the peak ground acceleration.

    Args:
        stream (obspy.core.stream.Stream): streams of strong ground motion.
            Traces in stream must be in units of %%g.
        imcs (list): list of imcs.
        origin (obspy.core.event.origin.Origin):
            Obspy event origin object.

    Returns:
        dictionary: Dictionary of pga for different components.
    """
    pga_dict = {}
    # check units and add channel pga
    for trace in stream:
        if trace.stats['units'] != '%%g':
            raise PGMException('Invalid units for PGA: %r. '
                               'Units must be %%g' % trace.stats['units'])
    # sort imcs
    grouped_imcs = group_imcs(imcs)
    # gather imc classes
    pgm_classes = get_pgm_classes('imc')
    # store pga for imcs
    for imc in grouped_imcs:
        if 'calculate_' + imc in pgm_classes:
            pga_func = pgm_classes['calculate_' + imc]
            pga = pga_func(stream, origin=origin,
                           percentiles=grouped_imcs[imc])
            if imc.find('rot') >= 0:
                for percentile in pga:
                    pga_dict[imc.upper() + str(percentile)] = pga[percentile]
            elif imc.find('channels') >= 0:
                for channel in pga:
                    pga_dict[channel] = pga[channel]
            elif imc.find('radial_transverse') >= 0:
                for channel in pga:
                    pga_dict[channel] = pga[channel]
            else:
                pga_dict[imc.upper()] = pga
        else:
            logging.warning('Not a valid IMC: %r. Skipping...' % imc)
    return pga_dict
Esempio n. 17
0
    def _get_horizontal_time(self):
        """
        Get the 'times' array for a horizontal channel. This is required for
        spectral accelerations where a rotation (rotd) is requested.

        Returns:
            numpy.ndarray: Array of times for a horizontal channel.

        Raises:
            PGMException: if there are no horizontal channels.
        """
        for trace in self.timeseries:
            if "Z" not in trace.stats["channel"].upper():
                # times = trace.times()
                times = np.linspace(
                    0.0, trace.stats.endtime - trace.stats.starttime,
                    trace.stats.npts)
                return times
        raise PGMException(
            "MetricsController: At least one horizontal "
            "channel is required for calculations of SA, ROTD, GMROTD, GM.")
    def __init__(
        self,
        reduction_data,
        bandwidth=None,
        percentile=None,
        period=None,
        smoothing=None,
        interval=[5, 95],
    ):
        """
        Args:
            reduction_data (StationStream or ndarray):
                Intensity measurement component.
            bandwidth (float):
                Bandwidth for the smoothing operation. Default is None.
            percentile (float):
                Percentile for rotation calculations.
            period (float):
                Period for smoothing (Fourier amplitude spectra) calculations.
                Default is None.
            smoothing (string):
                Smoothing type. Default is None.
            interval (list):
                List of length 2 with the quantiles (0-1) for duration interval
                calculation.

        Raises:
            PGMException: if the percentile value is None.
        """
        super().__init__(reduction_data,
                         bandwidth=None,
                         percentile=None,
                         period=None,
                         smoothing=None)
        if percentile is None:
            raise PGMException("Percentile: The percentile value must "
                               "be defined and of type float or int.")
        self.period = period
        self.percentile = percentile
        self.result = self.get_percentile()
Esempio n. 19
0
def _calculate_channel_arias(stream):
    """Calculates Arias Intensity.
    Args:
        stream (obspy.core.stream.Stream): Stream of acceleration values
                in m/s/s.
    Returns:
        Ia (obspy.core.stream.Stream): Stream of Arias intensity values
                in m/s with respect to time.
        NIa (obspy.core.stream.Stream): Stream of normalized Arias intensity
                values with respect to time.
    Raises:
        PGMException: If the units are not valid. Units must be m/s/s.
    """
    Ia = Stream()
    NIa = Stream()
    for trace in stream:
        if trace.stats['units'] != 'm/s/s':
            raise PGMException('Invalid units for ARIAS: %r. '
                               'Units must be m/s/s' % trace.stats['units'])
        dt = trace.stats['delta']
        g = sp.g
        acc2 = trace.data

        # Calculate Arias intensity
        integration = integrate.cumtrapz(acc2 * acc2, dx=dt)
        arias_intensity = integration * np.pi / (2 * g)

        # Calculate normalized Arias intensity
        # divide arias intensity by its max value
        norm_arias_intensity = arias_intensity / np.amax(arias_intensity)
        stats_out = trace.stats.copy()
        stats_out['units'] = 'm/s'
        trace_ia = Trace(data=arias_intensity, header=stats_out)
        trace_nia = Trace(data=norm_arias_intensity, header=stats_out)
        Ia.append(trace_ia)
        NIa.append(trace_nia)
    return (Ia, NIa)
    def get_max(self, tr1, pick_peak, tr2=None, percentiles=50):
        """
        Finds the maximum from traces and either picks the geometric mean,
        arithmetic mean, or maximum of the two. The two input can either be
        1D traces, or 2D arrays.

        For the 2D array case, the number of rows must be the number of traces,
        and the numer of columns is the number of samples in the trace.
        The following assumptions are made regarding the 2D array:
            1) The rows in each matrix are the same component at different
               rotation at different angles.
            2) If tr2 is provided, the orientation of the trace in each row is
               orthogonal to the analagous row in tr1
            3) The traces that constitute tr1 and tr2 are both
               horizontal components.

        Args:
            tr1 (obspy.core.trace.Trace or 2D array):
                Trace 1, either 1D trace or 2D matrix of rotated components.
            tr2 (obspy.core.trace.Trace or 2D array):
                Trace 2, either 1D trace or or 2D matrix of rotated components.
                Default is None.
            pick_peak (str):
                The choice for either geometric mean, arithmetic or maximum.
                 The valid strings are:
                    - "gm" for geometric mean
                    - "am" for arithmetic mean
                    - "max" for maximum
            percentiles (list): percentile(s) to return the requested values.
                Default is 50.

        Returns:
            If 1D input:
                Returns a singular,  scalar value for the requested pick_peak.
            If 2D input:
                Returns a list of the maximum values, as well as the singular
                value at the requested percentile.
        """

        # Check if valid trace dimensions were provided, and determine if we're
        # working with 1D or 2D traces. Trace 1 and Trace 2 must have the same
        # dimension (either both 1D or 2D).
        if tr2 is None:
            if (len(tr1.shape) == 1):
                input_dim = '1D'
            elif (len(tr1.shape) == 2):
                input_dim = '2D'
            else:
                raise PGMException('Trace one must be either 1D or 2D.')
        else:
            if (len(tr1.shape) != len(tr2.shape)):
                raise PGMException('Traces must have the same dimensions.')
            elif (len(tr1.shape) == 1):
                input_dim = '1D'
            elif (len(tr1.shape) == 2):
                input_dim = '2D'
            else:
                raise PGMException('Traces must be either 1D or 2D.')

        # Set the axis from which to pull the maximums based on
        # the input dimension.
        if input_dim == '1D':
            axis = 0
        else:
            axis = 1

        # Geometric mean
        if (pick_peak.lower() == 'gm'):
            if tr2 is None:
                raise PGMException('Two traces must be provided to find mean.')
            tr1_max = np.amax(tr1, axis)
            tr2_max = np.amax(tr2, axis)
            geo_means = np.sqrt(tr1_max * tr2_max)

            if (input_dim == '1D'):
                return geo_means
            else:
                return geo_means, np.percentile(geo_means, percentiles)

        # Arithmetic mean
        elif (pick_peak.lower() == 'am'):
            if tr2 is None:
                raise PGMException('Two traces must be provided to find mean.')
            tr1_max = np.amax(tr1, axis)
            tr2_max = np.amax(tr2, axis)
            arith_means = 0.5 * (tr1_max + tr2_max)

            if (input_dim == '1D'):
                return arith_means
            else:
                return arith_means, np.percentile(arith_means, percentiles)

        # Maximum
        elif (pick_peak.lower() == 'max'):
            if tr2 is not None:
                tr1_max = np.amax(np.abs(tr1), axis)
                tr2_max = np.amax(np.abs(tr2), axis)

                # Maximum of two horizontals
                if (input_dim == '1D'):
                    return np.amax([tr1_max, tr2_max])
                else:
                    maximums = []
                    for idx, val in enumerate(tr1_max):
                        max_val = np.max([val, tr2_max[idx]])
                        maximums.append(max_val)
                    return maximums, np.percentile(maximums, percentiles)
            else:
                maximums = np.amax(np.abs(tr1), axis)
                if (input_dim == '1D'):
                    return maximums
                else:
                    return maximums, np.percentile(maximums, percentiles)
        else:
            raise PGMException('Not a valid pick for the peak.')
    def __init__(self,
                 imts,
                 imcs,
                 timeseries,
                 bandwidth=None,
                 damping=None,
                 event=None,
                 smooth_type=None,
                 allow_nans=None):
        """Initialize MetricsController class.

        Args:
            imts (list):
                Intensity measurement types (string) to calculate.
            imcs (list):
                Intensity measurement components (string) to
                calculate.
            timeseries (StationStream):
                Stream of the timeseries data.
            bandwidth (float):
                Bandwidth for the smoothing calculation.
            damping (float):
                Damping for the oscillator calculation.
            event (ScalarEvent):
                Defines the focal time, geographic location, and magnitude of
                an earthquake hypocenter. Default is None.
            smooth_type (string):
                Currently not used, as konno_ohmachi is the only smoothing
                type.
            allow_nans (bool):
                Should nans be allowed in the smoothed spectra. If False, then
                the number of points in the FFT will be computed to ensure
                that nans will not result in the smoothed spectra.

        Raises:
            PGMException: Requires an event for radial_transfer imcs.
        """
        if not isinstance(imts, (list, np.ndarray)):
            imts = [imts]
        if not isinstance(imcs, (list, np.ndarray)):
            imcs = [imcs]
        self.imts = set(np.sort([imt.lower() for imt in imts]))
        self.clean_imts()
        self.imcs = set(np.sort([imc.lower() for imc in imcs]))
        if 'radial_transverse' in self.imcs and event is None:
            raise PGMException('MetricsController: Event is required for '
                               'radial_transverse imc')

        self.channel_dict = {}  # dictionary to serve as translator between
        self.timeseries = timeseries
        self.validate_stream()
        self.event = event
        self.config = get_config()
        self.damping = damping
        self.smooth_type = smooth_type
        self.bandwidth = bandwidth
        self.allow_nans = allow_nans
        if damping is None:
            self.damping = self.config['metrics']['sa']['damping']
        if smooth_type is None:
            self.smooth_type = self.config['metrics']['fas']['smoothing']
        if bandwidth is None:
            self.bandwidth = self.config['metrics']['fas']['bandwidth']
        if allow_nans is None:
            self.allow_nans = self.config['metrics']['fas']['allow_nans']
        self._available_imts, self._available_imcs = gather_pgms()
        self._step_sets = self.get_steps()
        imtstr = '_'.join(imts)
        if '_sa' in imtstr or imtstr.startswith('sa'):
            self._times = self._get_horizontal_time()
        else:
            self._times = None
        self.max_period = self._get_max_period()
        self.pgms = self.execute_steps()