def write_gmm_data_file(model_name, mag, dist, result_type,
                        periods, file_out,
                        component_type="AVERAGE_HORIZONTAL",):
    """
    Create a file of input and output parameters for the sommerville GMM.

    params:
      model_name: The ground motion model, as a string.
      mag: dictionary, key - the mag column name, values, the mag vectors,
           as a list
      dist: dictionary, key - the distance column name, value,
            the distance vectors, as a list.
      result_type: MEAN or TOTAL_STDDEV
      periods: A list of periods requiring SA values.
               The first value has to be 0.0.

       Mag, distance and periods will be iterated over to give a single SA for
       each combination.
       file_out: The file name and location of the produced data file.
    """
    assert periods[0] == 0.0
    handle = open(file_out, 'wb')
    writer = csv.writer(handle, delimiter=',', quoting=csv.QUOTE_NONE)

    # write title
    title = [mag[0], dist[0], 'result_type', 'component_type'] + periods[1:] + \
        ['pga']
    writer.writerow(title)

    # prepare the coefficients
    model = Ground_motion_specification(model_name)
    coeff = model.calc_coefficient(periods)
    coeff = reshape(coeff, (coeff.shape[0], 1, 1, coeff.shape[1]))
    sigma_coeff = model.calc_sigma_coefficient(periods)
    sigma_coeff = reshape(sigma_coeff, (sigma_coeff.shape[0], 1, 1,
                                        sigma_coeff.shape[1]))

    # Iterate
    for magi in mag[1]:
        for disti in dist[1]:
            dist_args = {'mag': array([[[magi]]]),
                         dist[0]: array([[[disti]]]),
                         'coefficient': coeff,
                         'sigma_coefficient': sigma_coeff}
            log_mean, log_sigma = model.distribution(**dist_args)
            sa_mod = list(log_mean.reshape(-1))
            sa_mod = [math.exp(x) for x in sa_mod]
            sigma_mod = list(log_sigma.reshape(-1))
            if result_type == 'MEAN':
                row = [magi, disti, result_type, component_type] + \
                    sa_mod[1:] + \
                    [sa_mod[0]]
            else:
                row = [magi, disti, result_type, component_type] + \
                    sigma_mod[1:] + \
                    [sigma_mod[0]]

            writer.writerow(row)
    handle.close()
Ejemplo n.º 2
0
def write_gmm_data_file_depth(
    model_name, mag, dist, depth, result_type, periods, file_out, component_type="AVERAGE_HORIZONTAL"
):
    """
    Create a file of input and output parameters for the sommerville GMM.

    params:
      model_name: The ground motion model, as a string.
      mag: dictionary, key - the mag column name, values, the mag vectors,
           as a list
      dist: dictionary, key - the distance column name, value,
            the distance vectors, as a list.
      depth: depth in km.
      result_type: MEAN or TOTAL_STDDEV
      periods: A list of periods requiring SA values.
               The first value has to be 0.0.

       Mag, distance and periods will be iterated over to give a single SA for
       each combination.
       file_out: The file name and location of the produced data file.
    """
    assert periods[0] == 0.0
    handle = open(file_out, "wb")
    writer = csv.writer(handle, delimiter=",", quoting=csv.QUOTE_NONE)

    # write title
    title = [depth[0], mag[0], dist[0], "result_type", "component_type"] + periods[1:] + ["pga"]
    writer.writerow(title)

    # prepare the coefficients
    model = Ground_motion_specification(model_name)
    coeff = model.calc_coefficient(periods)
    coeff = reshape(coeff, (coeff.shape[0], 1, 1, coeff.shape[1]))
    sigma_coeff = model.calc_sigma_coefficient(periods)
    sigma_coeff = reshape(sigma_coeff, (sigma_coeff.shape[0], 1, 1, sigma_coeff.shape[1]))

    # Iterate
    for depi in depth[1]:
        for magi in mag[1]:
            for disti in dist[1]:
                dist_args = {
                    "mag": array([[[magi]]]),
                    dist[0]: array([[[disti]]]),
                    "depth": array([[[depi]]]),
                    "coefficient": coeff,
                    "sigma_coefficient": sigma_coeff,
                }
                log_mean, log_sigma = model.distribution(**dist_args)
                sa_mod = list(log_mean.reshape(-1))
                sa_mod = [math.exp(x) for x in sa_mod]
                sigma_mod = list(log_sigma.reshape(-1))
                if result_type == "MEAN":
                    row = [depi, magi, disti, result_type, component_type] + sa_mod[1:] + [sa_mod[0]]
                else:
                    row = [depi, magi, disti, result_type, component_type] + sigma_mod[1:] + [sigma_mod[0]]

                writer.writerow(row)
    handle.close()
class Ground_motion_calculator(object):
    """Ground_motion_calculator instances are used to calculate the ground
    motion given a ground motion specifiction.

    Attributes:
      GM_spec: instance of Ground_motion_specification
      coefficient:   ground motion coefficient for the given periods
      sigma_coefficient:  ground motion sigma coefficient for the given periods
      periods:  the periods
    """
    def __init__(self, ground_motion_model_name, periods):
        """
        Args:
        ground_motion_model_name: A string, naming the ground motion model
        periods: The periods that will be used for this simulation.  Used
          to calculate coefficient and sigma_coefficient.

        RESIZING NOTES
        Adding lots of extra dimensions.

        'distance' had dimension [site]*[events]
        'magnitude' had dimension [events]
        'coefficient' had dimension [number of coefficients]*[Period]
        'sigma_coefficient' had dimension [number of coefficients]*[Period]

        Now 'distance' and 'magnitude' have dimension:
        [bonus dimension!]*[site]*[events]

        once 'coefficient' and 'sigma_coefficient are unpacked (ie
        c1,c2,c4,c6,c7,c10=c), they have dimension:
        [bonus dimension!]*[site]*[events]

        Note that some of these dimensions are degenerate (newaxises),
        such as [site] for 'magnitude'.

        newaxis is used to broadcast arrays into higher dimensions:
        a=array([1,2,3])
        b=array([0,1])
        a=a[...,newaxis]
        print a
        >[[1]
        > [2]
        > [3]]

        # if a is added to a 1D array of length n; a will act as:
        #    [[1, 1, ... (n times],
        #     [2, 2, ... (n times],
        #     [3, 3, ... (n times]]
        # (this is just broadcasting rules)

        a+b
        >array([[1, 2],
        >       [2, 3],
        >       [3, 4]])

        Note that all [bonus dimensions] are degenerate.
        They are there because once the distribution is sampled,
        I want it to maintain the same number of dimension. I don't
        want it to add an extra dimension for the spawnings. If that
        happens, then it is hard to address futher samplings (from
        soil) or multi-models in a uniform manner.

        so ground_motion_from_toro =[gmd_torro]
        sampled ground_motion_from_toro = [gmd1,gmd2,gmd3, ... (n samples)]
        Uniform behaviour.
        """

        self.GM_spec = Ground_motion_specification(ground_motion_model_name)

        periods = asarray(periods)
        # calc the coefficient and sigma_coefficient for the input periods
        coefficient = self.GM_spec.calc_coefficient(periods)
        sigma_coefficient = self.GM_spec.calc_sigma_coefficient(periods)

        # Adding extra dimensions.
        self.coefficient = coefficient[:, newaxis, newaxis, :]
        self.sigma_coefficient = sigma_coefficient[:, newaxis, newaxis, :]

    def distribution_function(self,
                              dist_object,
                              dist_types,
                              mag_dict,
                              periods=None,
                              depth=None,
                              depth_to_top=None,
                              fault_type=None,
                              Vs30=None,
                              mag_type=None,
                              Z25=None,
                              dip=None,
                              width=None,
                              event_activity=None):
        """
        dist_object must give distance info if dist_object.distance(dist_type)
        is called.  The distance info must be an array.

        Returns:
          log_mean - dimensions are
          log_sigma

        FIXME: Why should we let depth be None?
        """

        # dist_type and mag_type are attributes of self.GM_spec
        # we shouldn't pass them around.

        distances = {}
        for dist_type in dist_types:
            distances[dist_type] = dist_object.distance(dist_type)

        mag = mag_dict[mag_type]

        if depth is not None:
            depth = asarray(depth)

        (mag, depth, depth_to_top, fault_type, dip,
         width) = self.resize_mag_depth(mag, depth, depth_to_top, fault_type,
                                        dip, width)
        for dist_type in dist_types:
            distances[dist_type] = self.resize_dist(distances[dist_type],
                                                    mag.size)

        # This is calling the distribution functions described in the
        # ground_motion_interface module.
        # We add the new 'dist_object' parameter to cater to models that
        # require more than one distance.  Once all existing models use
        # the new parameter we can remove the 'distance' parameter.
        distribution_args = {
            'mag': mag,
            'coefficient': self.coefficient,
            'sigma_coefficient': self.sigma_coefficient,
            'depth': depth,
            'depth_to_top': depth_to_top,
            'fault_type': fault_type,
            'Vs30': Vs30,
            'Z25': Z25,
            'dip': dip,
            'width': width,
            'periods': periods
        }
        for dist_type in dist_types:
            distribution_args[dist_type] = distances[dist_type]

        (log_mean, log_sigma) = self.GM_spec.distribution(**distribution_args)

        # FIXME when will this fail?  Maybe let it fail then?
        # If it does not fail here it fails in analysis.py"
        #, line 427, in main
        # assert isfinite(bedrock_SA).all()
        # example of log_mean when this failed log_mean [[[ NaN  NaN  NaN]]]
        # An Mw of 0.0 caused it.
        # this is a good place to catch this error
        assert isfinite(log_mean).all()
        assert isfinite(log_sigma).all()

        return (log_mean, log_sigma)

    def resize_mag_depth(self, mag, depth, depth_to_top, fault_type, dip,
                         width):
        """
        Warning, Toro_1997_midcontinent_distribution assumes
        that this occurs.  So if resizing is changed,
        Toro_1997_midcontinent_distribution needs to be changed as well
        """

        if mag.size == 1:
            mag = mag.reshape([1])
            if depth is not None:
                depth = depth.reshape([1])  # Don't know if we have to do this
            if depth_to_top is not None:
                depth_to_top = depth_to_top.reshape([1])
            if fault_type is not None:
                fault_type = fault_type.reshape([1])

        # resize depth, depth_to_top, etc
        if depth is not None:
            depth = depth[newaxis, :, newaxis]
            # collapsed arrays are a bad idea...

        if depth_to_top is not None:
            depth_to_top = array(depth_to_top)[newaxis, :, newaxis]

        if fault_type is not None:
            fault_type = array(fault_type)[newaxis, :, newaxis]

        if dip is not None:
            dip = array(dip)[newaxis, :, newaxis]

        if width is not None:
            width = array(width)[newaxis, :, newaxis]

        assert len(mag.shape) == 1

        mag = mag[newaxis, :, newaxis]

        return (mag, depth, depth_to_top, fault_type, dip, width)

    def resize_dist(self, dist, mag_size):
        """
        Warning, Toro_1997_midcontinent_distribution assumes
        that this occurs.  So if resizing is changed,
        Toro_1997_midcontinent_distribution needs to be changed as well
        """
        # [4.5,5.5,6.0] => site * mag * T
        assert len(dist.shape) < 3
        if not len(dist.shape) == 2:
            # if distances is collapsed
            if dist.size == 1:
                # if distances is size 1
                dist = dist.reshape((1, 1))
            else:
                assert len(dist.shape) == 1
                if mag_size > 1:
                    assert dist.size == mag_size
                    # therefore distance is 1 site * n magnitudes
                    dist = dist.reshape((1, mag_size))
                else:
                    # therefore distance is n site * 1 magnitudes
                    dist = dist.reshape((dist.size, 1))
        # collapsed arrays are a bad idea...

        dist = dist[:, :, newaxis]
        # [[30.0,35.0],[45.0,20.0]]=> [site*mag] * T

        return dist
Ejemplo n.º 4
0
class Ground_motion_calculator(object):

    """Ground_motion_calculator instances are used to calculate the ground
    motion given a ground motion specifiction.

    Attributes:
      GM_spec: instance of Ground_motion_specification
      coefficient:   ground motion coefficient for the given periods
      sigma_coefficient:  ground motion sigma coefficient for the given periods
      periods:  the periods
    """

    def __init__(self, ground_motion_model_name, periods):
        """
        Args:
        ground_motion_model_name: A string, naming the ground motion model
        periods: The periods that will be used for this simulation.  Used
          to calculate coefficient and sigma_coefficient.

        RESIZING NOTES
        Adding lots of extra dimensions.

        'distance' had dimension [site]*[events]
        'magnitude' had dimension [events]
        'coefficient' had dimension [number of coefficients]*[Period]
        'sigma_coefficient' had dimension [number of coefficients]*[Period]

        Now 'distance' and 'magnitude' have dimension:
        [bonus dimension!]*[site]*[events]

        once 'coefficient' and 'sigma_coefficient are unpacked (ie
        c1,c2,c4,c6,c7,c10=c), they have dimension:
        [bonus dimension!]*[site]*[events]

        Note that some of these dimensions are degenerate (newaxises),
        such as [site] for 'magnitude'.

        newaxis is used to broadcast arrays into higher dimensions:
        a=array([1,2,3])
        b=array([0,1])
        a=a[...,newaxis]
        print a
        >[[1]
        > [2]
        > [3]]

        # if a is added to a 1D array of length n; a will act as:
        #    [[1, 1, ... (n times],
        #     [2, 2, ... (n times],
        #     [3, 3, ... (n times]]
        # (this is just broadcasting rules)

        a+b
        >array([[1, 2],
        >       [2, 3],
        >       [3, 4]])

        Note that all [bonus dimensions] are degenerate.
        They are there because once the distribution is sampled,
        I want it to maintain the same number of dimension. I don't
        want it to add an extra dimension for the spawnings. If that
        happens, then it is hard to address futher samplings (from
        soil) or multi-models in a uniform manner.

        so ground_motion_from_toro =[gmd_torro]
        sampled ground_motion_from_toro = [gmd1,gmd2,gmd3, ... (n samples)]
        Uniform behaviour.
        """

        self.GM_spec = Ground_motion_specification(ground_motion_model_name)

        periods = asarray(periods)
        # calc the coefficient and sigma_coefficient for the input periods
        coefficient = self.GM_spec.calc_coefficient(periods)
        sigma_coefficient = self.GM_spec.calc_sigma_coefficient(periods)

        # Adding extra dimensions.
        self.coefficient = coefficient[:, newaxis, newaxis, :]
        self.sigma_coefficient = sigma_coefficient[:, newaxis, newaxis, :]

    def distribution_function(self, dist_object, dist_types, mag_dict,
                              periods=None, depth=None, depth_to_top=None,
                              fault_type=None, Vs30=None, mag_type=None,
                              Z25=None, dip=None, width=None,
                              event_activity=None):
        """
        dist_object must give distance info if dist_object.distance(dist_type)
        is called.  The distance info must be an array.

        Returns:
          log_mean - dimensions are
          log_sigma

        FIXME: Why should we let depth be None?
        """

        # dist_type and mag_type are attributes of self.GM_spec
        # we shouldn't pass them around.

        distances = {}
        for dist_type in dist_types:
            distances[dist_type] = dist_object.distance(dist_type)

        mag = mag_dict[mag_type]

        if depth is not None:
            depth = asarray(depth)

        (mag, depth, depth_to_top, fault_type,
         dip, width) = self.resize_mag_depth(mag, depth, depth_to_top,
                                             fault_type, dip, width)
        for dist_type in dist_types:
            distances[dist_type] = self.resize_dist(distances[dist_type],
                                                    mag.size)

        # This is calling the distribution functions described in the
        # ground_motion_interface module.
        # We add the new 'dist_object' parameter to cater to models that
        # require more than one distance.  Once all existing models use
        # the new parameter we can remove the 'distance' parameter.
        distribution_args = {'mag': mag,
                             'coefficient': self.coefficient,
                             'sigma_coefficient': self.sigma_coefficient,
                             'depth': depth,
                             'depth_to_top': depth_to_top,
                             'fault_type': fault_type,
                             'Vs30': Vs30,
                             'Z25': Z25,
                             'dip': dip,
                             'width': width,
                             'periods': periods}
        for dist_type in dist_types:
            distribution_args[dist_type] = distances[dist_type]

        (log_mean, log_sigma) = self.GM_spec.distribution(**distribution_args)

        # FIXME when will this fail?  Maybe let it fail then?
        # If it does not fail here it fails in analysis.py"
        #, line 427, in main
        # assert isfinite(bedrock_SA).all()
        # example of log_mean when this failed log_mean [[[ NaN  NaN  NaN]]]
        # An Mw of 0.0 caused it.
        # this is a good place to catch this error
        assert isfinite(log_mean).all()
        assert isfinite(log_sigma).all()

        return (log_mean, log_sigma)

    def resize_mag_depth(self, mag, depth, depth_to_top,
                         fault_type, dip, width):
        """
        Warning, Toro_1997_midcontinent_distribution assumes
        that this occurs.  So if resizing is changed,
        Toro_1997_midcontinent_distribution needs to be changed as well
        """

        if mag.size == 1:
            mag = mag.reshape([1])
            if depth is not None:
                depth = depth.reshape([1])  # Don't know if we have to do this
            if depth_to_top is not None:
                depth_to_top = depth_to_top.reshape([1])
            if fault_type is not None:
                fault_type = fault_type.reshape([1])

        # resize depth, depth_to_top, etc
        if depth is not None:
            depth = depth[newaxis, :, newaxis]
            # collapsed arrays are a bad idea...

        if depth_to_top is not None:
            depth_to_top = array(depth_to_top)[newaxis, :, newaxis]

        if fault_type is not None:
            fault_type = array(fault_type)[newaxis, :, newaxis]

        if dip is not None:
            dip = array(dip)[newaxis, :, newaxis]

        if width is not None:
            width = array(width)[newaxis, :, newaxis]

        assert len(mag.shape) == 1

        mag = mag[newaxis, :, newaxis]

        return (mag, depth, depth_to_top, fault_type, dip, width)

    def resize_dist(self, dist, mag_size):
        """
        Warning, Toro_1997_midcontinent_distribution assumes
        that this occurs.  So if resizing is changed,
        Toro_1997_midcontinent_distribution needs to be changed as well
        """
        # [4.5,5.5,6.0] => site * mag * T
        assert len(dist.shape) < 3
        if not len(dist.shape) == 2:
            # if distances is collapsed
            if dist.size == 1:
                # if distances is size 1
                dist = dist.reshape((1, 1))
            else:
                assert len(dist.shape) == 1
                if mag_size > 1:
                    assert dist.size == mag_size
                    # therefore distance is 1 site * n magnitudes
                    dist = dist.reshape((1, mag_size))
                else:
                    # therefore distance is n site * 1 magnitudes
                    dist = dist.reshape((dist.size, 1))
        # collapsed arrays are a bad idea...

        dist = dist[:, :, newaxis]
        # [[30.0,35.0],[45.0,20.0]]=> [site*mag] * T

        return dist