Ejemplo n.º 1
0
def _read_SES3D(fh, headonly=False):
    """
    Internal SES3D parsing routine.
    """
    # Import here to avoid circular imports.
    from obspy.core import AttribDict, Trace, Stream

    # Read the header.
    component = fh.readline().split()[0].lower()
    npts = int(fh.readline().split()[-1])
    delta = float(fh.readline().split()[-1])
    # Skip receiver location line.
    fh.readline()
    rec_loc = fh.readline().split()
    rec_x, rec_y, rec_z = list(map(float,
                                   [rec_loc[1], rec_loc[3], rec_loc[5]]))
    # Skip the source location line.
    fh.readline()
    src_loc = fh.readline().split()
    src_x, src_y, src_z = list(map(float,
                                   [src_loc[1], src_loc[3], src_loc[5]]))

    # Read the data.
    if headonly is False:
        data = np.array(list(map(float, fh.readlines())), dtype="float32")
    else:
        data = np.array([])

    ses3d = AttribDict()
    ses3d.receiver_latitude = rotations.colat2lat(rec_x)
    ses3d.receiver_longitude = rec_y
    ses3d.receiver_depth_in_m = rec_z
    ses3d.source_latitude = rotations.colat2lat(src_x)
    ses3d.source_longitude = src_y
    ses3d.source_depth_in_m = src_z

    header = {
        "delta": delta,
        "channel": COMPONENT_MAP[component],
        "ses3d": ses3d,
        "npts": npts
    }

    # Setup Obspy Stream/Trace structure.
    tr = Trace(data=data, header=header)

    # Small check.
    if headonly is False and npts != tr.stats.npts:
        msg = "The sample count specified in the header does not match " + \
            "the actual data count."
        warnings.warn(msg)
    return Stream(traces=[tr])
Ejemplo n.º 2
0
def _read_SES3D(fh, headonly=False):
    """
    Internal SES3D parsing routine.
    """
    # Import here to avoid circular imports.
    from obspy.core import AttribDict, Trace, Stream

    # Read the header.
    component = fh.readline().split()[0].lower()
    npts = int(fh.readline().split()[-1])
    delta = float(fh.readline().split()[-1])
    # Skip receiver location line.
    fh.readline()
    rec_loc = fh.readline().split()
    rec_x, rec_y, rec_z = map(float, [rec_loc[1], rec_loc[3], rec_loc[5]])
    # Skip the source location line.
    fh.readline()
    src_loc = fh.readline().split()
    src_x, src_y, src_z = map(float, [src_loc[1], src_loc[3], src_loc[5]])

    # Read the data.
    if headonly is False:
        data = np.array(map(float, fh.readlines()),
                        dtype="float32")
    else:
        data = np.array([])

    ses3d = AttribDict()
    ses3d.receiver_latitude = rotations.colat2lat(rec_x)
    ses3d.receiver_longitude = rec_y
    ses3d.receiver_depth_in_m = rec_z
    ses3d.source_latitude = rotations.colat2lat(src_x)
    ses3d.source_longitude = src_y
    ses3d.source_depth_in_m = src_z

    header = {
        "delta": delta,
        "channel": COMPONENT_MAP[component],
        "ses3d": ses3d,
        "npts": npts
    }

    # Setup Obspy Stream/Trace structure.
    tr = Trace(data=data, header=header)

    # Small check.
    if headonly is False and npts != tr.stats.npts:
        msg = "The sample count specified in the header does not match " + \
            "the actual data count."
        warnings.warn(msg)
    return Stream(traces=[tr])
Ejemplo n.º 3
0
 def __str__(self):
     """
     Eases interactive working.
     """
     ret_str = "Raw SES3D Model (split in %i parts)\n" % \
         (self.setup["total_cpu_count"])
     ret_str += "\tSetup:\n"
     ret_str += "\t\tLatitude: {:.2f} - {:.2f}\n".format(*[
         rotations.colat2lat(_i)
         for _i in self.setup["physical_boundaries_x"][::-1]
     ])
     ret_str += "\t\tLongitude: %.2f - %.2f\n" % \
         self.setup["physical_boundaries_y"]
     ret_str += "\t\tDepth in km: {:.2f} - {:.2f}\n".format(*[
         6371 - _i / 1000
         for _i in self.setup["physical_boundaries_z"][::-1]
     ])
     ret_str += "\t\tTotal element count: %i\n" % \
         self.setup["total_element_count"]
     ret_str += "\t\tTotal collocation point count: %i (without " \
         "duplicates: %i)\n" % (
             self.setup["total_point_count"],
             self.setup["total_point_count_without_duplicates"])
     ret_str += "\tMemory requirement per component: %.1f MB\n" % \
         ((self.setup["total_point_count_without_duplicates"] * 4) /
             (1024.0 ** 2))
     ret_str += "\tAvailable components: %s\n" % (", ".join(
         sorted(self.components.keys())))
     ret_str += "\tAvailable derived components: %s\n" % (", ".join(
         sorted(self.available_derived_components)))
     ret_str += "\tParsed components: %s" % (", ".join(
         sorted(self.parsed_components.keys())))
     return ret_str
Ejemplo n.º 4
0
 def __str__(self):
     """
     Eases interactive working.
     """
     ret_str = "Raw SES3D Model (split in %i parts)\n" % \
         (self.setup["total_cpu_count"])
     ret_str += "\tSetup:\n"
     ret_str += "\t\tLatitude: {:.2f} - {:.2f}\n".format(
         *[rotations.colat2lat(_i)
         for _i in self.setup["physical_boundaries_x"][::-1]])
     ret_str += "\t\tLongitude: %.2f - %.2f\n" % \
         self.setup["physical_boundaries_y"]
     ret_str += "\t\tDepth in km: {:.2f} - {:.2f}\n".format(
         *[6371 - _i / 1000
         for _i in self.setup["physical_boundaries_z"][::-1]])
     ret_str += "\t\tTotal element count: %i\n" % \
         self.setup["total_element_count"]
     ret_str += "\t\tTotal grid point count: %i\n" % \
         self.setup["total_point_count"]
     ret_str += "\tMemory requirement per component: %.1f MB\n" % \
         ((self.setup["total_point_count"] * 4) / (1024.0 ** 2))
     ret_str += "\tAvailable components: %s\n" % (", ".join(
         sorted(self.components.keys())))
     ret_str += "\tAvailable derived components: %s\n" % (", ".join(
         sorted(self.available_derived_components)))
     ret_str += "\tParsed components: %s" % (", ".join(
         sorted(self.parsed_components.keys())))
     return ret_str
Ejemplo n.º 5
0
    def unrotated_center(self):
        c_lng = rotations.get_center_angle(self.max_longitude,
                                           self.min_longitude)
        c_lat = rotations.colat2lat(rotations.get_center_angle(
            rotations.lat2colat(self.max_latitude),
            rotations.lat2colat(self.min_latitude)))

        Point = collections.namedtuple("CenterPoint", ["longitude",
                                                       "latitude"])
        return Point(longitude=c_lng, latitude=c_lat)
Ejemplo n.º 6
0
    def unrotated_center(self):
        c_lng = rotations.get_center_angle(self.max_longitude,
                                           self.min_longitude)
        c_lat = rotations.colat2lat(
            rotations.get_center_angle(rotations.lat2colat(self.max_latitude),
                                       rotations.lat2colat(self.min_latitude)))

        Point = collections.namedtuple("CenterPoint",
                                       ["longitude", "latitude"])
        return Point(longitude=c_lng, latitude=c_lat)
Ejemplo n.º 7
0
    def __init__(self, directory, domain, model_type="earth_model"):
        """
        The init function.

        :param directory: The directory where the earth model or kernel is
            located.
        :param model_type: Determined the type of model loaded. Currently
            two are supported:
                * earth_model - The standard SES3D model files (default)
                * kernel - The kernels. Identifies by lots of grad_* files.
                * wavefield - The raw wavefields.
        """
        self.directory = directory
        self.boxfile = os.path.join(self.directory, "boxfile")
        if not os.path.exists(self.boxfile):
            msg = "boxfile not found. Wrong directory?"
            raise ValueError(msg)

        # Read the boxfile.
        self.setup = self._read_boxfile()

        self.domain = domain
        self.model_type = model_type

        self.one_d_model = OneDimensionalModel("ak135-f")

        if model_type == "earth_model":
            # Now check what different models are available in the directory.
            # This information is also used to infer the degree of the used
            # lagrange polynomial.
            components = ["A", "B", "C", "lambda", "mu", "rhoinv", "Q"]
            self.available_derived_components = ["vp", "vsh", "vsv", "rho"]
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(
                    os.path.join(directory, "%s[0-9]*" % component))
                if len(files) != len(self.setup["subdomains"]):
                    continue
                # Check that the naming is continuous.
                all_good = True
                for _i in range(len(self.setup["subdomains"])):
                    if os.path.join(directory,
                                    "%s%i" % (component, _i)) in files:
                        continue
                    all_good = False
                    break
                if all_good is False:
                    msg = "Naming for component %s is off. It will be skipped."
                    warnings.warn(msg)
                    continue
                # They also all need to have the same size.
                if len(set([os.path.getsize(_i) for _i in files])) != 1:
                    msg = ("Component %s has the right number of model files "
                           "but they are not of equal size") % component
                    warnings.warn(msg)
                    continue
                # Sort the files by ascending number.
                files.sort(key=lambda x: int(
                    re.findall(r"\d+$", (os.path.basename(x)))[0]))
                self.components[component] = {"filenames": files}
        elif model_type == "wavefield":
            components = ["vz", "vx", "vy", "vz"]
            self.available_derived_components = []
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(os.path.join(directory,
                                               "%s_*_*" % component))
                if not files:
                    continue
                timesteps = collections.defaultdict(list)
                for filename in files:
                    timestep = int(os.path.basename(filename).split("_")[-1])
                    timesteps[timestep].append(filename)

                for timestep, filenames in timesteps.items():
                    self.components["%s %s" % (component, timestep)] = \
                        {"filenames": sorted(
                            filenames,
                            key=lambda x: int(
                                os.path.basename(x).split("_")[1]))}
        elif model_type == "kernel":
            # Now check what different models are available in the directory.
            # This information is also used to infer the degree of the used
            # lagrange polynomial.
            components = ["grad_cp", "grad_csh", "grad_csv", "grad_rho"]
            self.available_derived_components = []
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(
                    os.path.join(directory, "%s_[0-9]*" % component))
                if len(files) != len(self.setup["subdomains"]):
                    continue
                if len(set([os.path.getsize(_i) for _i in files])) != 1:
                    msg = ("Component %s has the right number of model files "
                           "but they are not of equal size") % component
                    warnings.warn(msg)
                    continue
                    # Sort the files by ascending number.
                files.sort(key=lambda x: int(
                    re.findall(r"\d+$", (os.path.basename(x)))[0]))
                self.components[component] = {"filenames": files}
        else:
            msg = "model_type '%s' not known." % model_type
            raise ValueError(msg)

        # All files for a single component have the same size. Now check that
        # all files have the same size.
        unique_filesizes = len(
            list(
                set([
                    os.path.getsize(_i["filenames"][0])
                    for _i in self.components.values()
                ])))
        if unique_filesizes != 1:
            msg = ("The different components in the folder do not have the "
                   "same number of samples")
            raise ValueError(msg)

        # Now calculate the lagrange polynomial degree. All necessary
        # information is present.
        size = os.path.getsize(
            list(self.components.values())[0]["filenames"][0])
        sd = self.setup["subdomains"][0]
        x, y, z = sd["index_x_count"], sd["index_y_count"], sd["index_z_count"]
        self.lagrange_polynomial_degree = \
            int(round(((size * 0.25) / (x * y * z)) ** (1.0 / 3.0) - 1))

        self._calculate_final_dimensions()

        # Setup the boundaries.
        self.lat_bounds = [
            rotations.colat2lat(_i)
            for _i in self.setup["physical_boundaries_x"][::-1]
        ]
        self.lng_bounds = self.setup["physical_boundaries_y"]
        self.depth_bounds = [
            6371 - _i / 1000.0 for _i in self.setup["physical_boundaries_z"]
        ]

        self.collocation_points_lngs = self._get_collocation_points_along_axis(
            self.lng_bounds[0], self.lng_bounds[1],
            self.setup["point_count_in_y"])
        self.collocation_points_lats = self._get_collocation_points_along_axis(
            self.lat_bounds[0], self.lat_bounds[1],
            self.setup["point_count_in_x"])
        self.collocation_points_depth = \
            self._get_collocation_points_along_axis(
                self.depth_bounds[1], self.depth_bounds[0],
                self.setup["point_count_in_z"])[::-1]
Ejemplo n.º 8
0
    def plot_depth_slice(self, component, depth_in_km):
        """
        Plots a depth slice.
        """
        lat_bounds = [rotations.colat2lat(_i)
            for _i in self.setup["physical_boundaries_x"][::-1]]
        lng_bounds = self.setup["physical_boundaries_y"]
        depth_bounds = [6371 - _i / 1000
            for _i in self.setup["physical_boundaries_z"]]

        data = self.parsed_components[component]

        available_depths = np.linspace(*depth_bounds, num=data.shape[2])[::-1]
        depth_index = np.argmin(np.abs(available_depths - depth_in_km))

        lon, lat = np.meshgrid(
            np.linspace(*lng_bounds, num=data.shape[1]),
            np.linspace(*lat_bounds, num=data.shape[0]))
        if self.rotation_axis and self.rotation_angle_in_degree:
            lon_shape = lon.shape
            lat_shape = lat.shape
            lon.shape = lon.size
            lat.shape = lat.size
            lat, lon = rotations.rotate_lat_lon(lat, lon, self.rotation_axis,
                self.rotation_angle_in_degree)
            lon.shape = lon_shape
            lat.shape = lat_shape

        # Get the center of the map.
        lon_0 = lon.min() + lon.ptp() / 2.0
        lat_0 = lat.min() + lat.ptp() / 2.0

        plt.figure(0)

        # Attempt to zoom into the region of interest.
        max_extend = max(lon.ptp(), lat.ptp())
        extend_used = max_extend / 180.0
        if extend_used < 0.5:
            x_buffer = 0.2 * lon.ptp()
            y_buffer = 0.2 * lat.ptp()

            m = Basemap(projection='merc', resolution="l",
                #lat_0=lat_0, lon_0=lon_0,
                llcrnrlon=lon.min() - x_buffer,
                urcrnrlon=lon.max() + x_buffer,
                llcrnrlat=lat.min() - y_buffer,
                urcrnrlat=lat.max() + y_buffer)
        else:
            m = Basemap(projection='ortho', lon_0=lon_0, lat_0=lat_0,
                resolution="c")


        m.drawcoastlines()
        m.fillcontinents("0.9", zorder=0)
        m.drawmapboundary(fill_color="white")
        m.drawparallels(np.arange(-80.0, 80.0, 10.0), labels=[1, 0, 0, 0])
        m.drawmeridians(np.arange(-170.0, 170.0, 10.0), labels=[0, 0, 0, 1])
        m.drawcountries()

        x, y = m(lon, lat)
        im = m.pcolormesh(x, y, data[::-1, :, depth_index],
            cmap=tomo_colormap)

        # Add colorbar and potentially unit.
        cm = m.colorbar(im, "right", size="3%", pad='2%')
        if component in UNIT_DICT:
            cm.set_label(UNIT_DICT[component], fontsize="x-large", rotation=0)

        plt.suptitle("Depth slice of %s at %i km" % (component,
            int(depth_in_km)), size="large")

        def _on_button_press(event):
            if event.button != 1 or not event.inaxes:
                return
            lon, lat = m(event.xdata, event.ydata, inverse=True)
            # Convert to colat to ease indexing.
            colat = rotations.lat2colat(lat)

            x_range = (self.setup["physical_boundaries_x"][1] -
                self.setup["physical_boundaries_x"][0])
            x_frac = (colat - self.setup["physical_boundaries_x"][0]) / x_range
            x_index = int(((self.setup["boundaries_x"][1] -
                self.setup["boundaries_x"][0]) * x_frac) +
                self.setup["boundaries_x"][0])
            y_range = (self.setup["physical_boundaries_y"][1] -
                self.setup["physical_boundaries_y"][0])
            y_frac = (lon - self.setup["physical_boundaries_y"][0]) / y_range
            y_index = int(((self.setup["boundaries_y"][1] -
                self.setup["boundaries_y"][0]) * y_frac) +
                self.setup["boundaries_y"][0])

            plt.figure(1, figsize=(3, 8))
            depths = available_depths
            values = data[x_index, y_index, :]
            plt.plot(values, depths)
            plt.grid()
            plt.ylim(depths[-1], depths[0])
            plt.show()
            plt.close()
            plt.figure(0)

        plt.gcf().canvas.mpl_connect('button_press_event', _on_button_press)

        plt.show()
Ejemplo n.º 9
0
def read_SES3D(file_or_file_object, *args, **kwargs):
    """
    Turns a SES3D file into a obspy.core.Stream object.

    SES3D files do not contain a starttime and thus the first first sample will
    always begin at 1970-01-01T00:00:00.

    The data will be a floating point array of the ground velocity in meters
    per second.

    Furthermore every trace will have a trace.stats.ses3d dictionary which
    contains the following six keys:
        * receiver_latitude
        * receiver_longitde
        * receiver_depth_in_m
        * source_latitude
        * source_longitude
        * source_depth_in_m

    The network, station, and location attributes of the trace will be empty,
    and the channel will be set to either 'X' (south component), 'Y' (east
    component), or 'Z' (vertical component).
    """
    # Import here to avoid circular imports.
    from obspy.core import AttribDict, Trace, Stream

    # Make sure that it is a file like object.
    if not hasattr(file_or_file_object, "read"):
        with open(file_or_file_object, "rb") as open_file:
            file_or_file_object = StringIO(open_file.read())

    # Read the header.
    component = file_or_file_object.readline().split()[0].lower()
    npts = int(file_or_file_object.readline().split()[-1])
    delta = float(file_or_file_object.readline().split()[-1])
    # Skip receiver location line.
    file_or_file_object.readline()
    rec_loc = file_or_file_object.readline().split()
    rec_x, rec_y, rec_z = map(float, [rec_loc[1], rec_loc[3], rec_loc[5]])
    # Skip the source location line.
    file_or_file_object.readline()
    src_loc = file_or_file_object.readline().split()
    src_x, src_y, src_z = map(float, [src_loc[1], src_loc[3], src_loc[5]])

    # Read the data.
    data = np.array(map(float, file_or_file_object.readlines()),
                    dtype="float32")

    # Setup Obspy Stream/Trace structure.
    tr = Trace(data=data)
    tr.stats.delta = delta
    # Map the channel attributes.
    tr.stats.channel = {
        "theta": "X",
        "phi": "Y",
        "r": "Z"}[component]
    tr.stats.ses3d = AttribDict()
    tr.stats.ses3d.receiver_latitude = rotations.colat2lat(rec_x)
    tr.stats.ses3d.receiver_longitude = rec_y
    tr.stats.ses3d.receiver_depth_in_m = rec_z
    tr.stats.ses3d.source_latitude = rotations.colat2lat(src_x)
    tr.stats.ses3d.source_longitude = src_y
    tr.stats.ses3d.source_depth_in_m = src_z
    # Small check.
    if npts != tr.stats.npts:
        msg = "The sample count specified in the header does not match " + \
            "the actual data count."
        warnings.warn(msg)
    return Stream(traces=[tr])
Ejemplo n.º 10
0
def test_RotateMomentTensor():
    """
    Tests the moment tensor rotations.
    """
    # A full rotation should not change anything.
    Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(
        1, 2, 3, 4, 5, 6, 7, 8, [9, 10, 11], 360)
    np.testing.assert_almost_equal(Mrr, 1.0, 6)
    np.testing.assert_almost_equal(Mtt, 2.0, 6)
    np.testing.assert_almost_equal(Mpp, 3.0, 6)
    np.testing.assert_almost_equal(Mrt, 4.0, 6)
    np.testing.assert_almost_equal(Mrp, 5.0, 6)
    np.testing.assert_almost_equal(Mtp, 6.0, 6)

    # The following ones are tested against a well proven Matlab script by
    # Andreas Fichtner.
    Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(
        -0.704, 0.071, 0.632, 0.226, -0.611, 3.290, rotations.colat2lat(26.08),
        -21.17, [0, 1, 0], 57.5)
    Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
        [-0.70400000, 2.04919171, -1.34619171, 0.02718681, -0.65089007,
         2.83207047]
    np.testing.assert_almost_equal(Mrr, Mrr_new, 6)
    np.testing.assert_almost_equal(Mtt, Mtt_new, 6)
    np.testing.assert_almost_equal(Mpp, Mpp_new, 6)
    np.testing.assert_almost_equal(Mrt, Mrt_new, 6)
    np.testing.assert_almost_equal(Mrp, Mrp_new, 6)
    np.testing.assert_almost_equal(Mtp, Mtp_new, 6)
    # Another example.
    Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(
        -0.818, -1.300, 2.120, 1.720, 2.290, -0.081,
        rotations.colat2lat(53.51), -9.87,
        [np.sqrt(2) / 2, -np.sqrt(2) / 2, 0], -31.34)
    Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
        [-0.81800000, -0.69772178, 1.51772178, 2.55423451, 1.29552541,
         1.30522545]
    np.testing.assert_almost_equal(Mrr, Mrr_new, 6)
    np.testing.assert_almost_equal(Mtt, Mtt_new, 6)
    np.testing.assert_almost_equal(Mpp, Mpp_new, 6)
    np.testing.assert_almost_equal(Mrt, Mrt_new, 6)
    np.testing.assert_almost_equal(Mrp, Mrp_new, 6)
    np.testing.assert_almost_equal(Mtp, Mtp_new, 6)
    # The same as before, but with a non-normalized axis. Should work just
    # as well.
    Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(
        -0.818, -1.300, 2.120, 1.720, 2.290, -0.081,
        rotations.colat2lat(53.51), -9.87, [11.12, -11.12, 0], -31.34)
    Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
        [-0.81800000, -0.69772178, 1.51772178, 2.55423451, 1.29552541,
         1.30522545]
    np.testing.assert_almost_equal(Mrr, Mrr_new, 6)
    np.testing.assert_almost_equal(Mtt, Mtt_new, 6)
    np.testing.assert_almost_equal(Mpp, Mpp_new, 6)
    np.testing.assert_almost_equal(Mrt, Mrt_new, 6)
    np.testing.assert_almost_equal(Mrp, Mrp_new, 6)
    np.testing.assert_almost_equal(Mtp, Mtp_new, 6)
    # One more.
    Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(
        0.952, -1.030, 0.076, 0.226, -0.040, -0.165,
        rotations.colat2lat(63.34), 55.80,
        [np.sqrt(3) / 3, -np.sqrt(3) / 3, -np.sqrt(3) / 3], 123.45)
    Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
        [0.95200000, -0.41458722, -0.53941278, -0.09170855, 0.21039378,
         0.57370606]
    np.testing.assert_almost_equal(Mrr, Mrr_new, 6)
    np.testing.assert_almost_equal(Mtt, Mtt_new, 6)
    np.testing.assert_almost_equal(Mpp, Mpp_new, 6)
    np.testing.assert_almost_equal(Mrt, Mrt_new, 6)
    np.testing.assert_almost_equal(Mrp, Mrp_new, 6)
    np.testing.assert_almost_equal(Mtp, Mtp_new, 6)
Ejemplo n.º 11
0
    def plot_depth_slice(self, component, depth_in_km):
        """
        Plots a depth slice.
        """
        lat_bounds = [
            rotations.colat2lat(_i)
            for _i in self.setup["physical_boundaries_x"][::-1]
        ]
        lng_bounds = self.setup["physical_boundaries_y"]
        depth_bounds = [
            6371 - _i / 1000 for _i in self.setup["physical_boundaries_z"]
        ]

        data = self.parsed_components[component]

        available_depths = np.linspace(*depth_bounds, num=data.shape[2])[::-1]
        depth_index = np.argmin(np.abs(available_depths - depth_in_km))

        lon, lat = np.meshgrid(np.linspace(*lng_bounds, num=data.shape[1]),
                               np.linspace(*lat_bounds, num=data.shape[0]))
        if self.rotation_axis and self.rotation_angle_in_degree:
            lon_shape = lon.shape
            lat_shape = lat.shape
            lon.shape = lon.size
            lat.shape = lat.size
            lat, lon = rotations.rotate_lat_lon(lat, lon, self.rotation_axis,
                                                self.rotation_angle_in_degree)
            lon.shape = lon_shape
            lat.shape = lat_shape

        # Get the center of the map.
        lon_0 = lon.min() + lon.ptp() / 2.0
        lat_0 = lat.min() + lat.ptp() / 2.0

        plt.figure(0)

        # Attempt to zoom into the region of interest.
        max_extend = max(lon.ptp(), lat.ptp())
        extend_used = max_extend / 180.0
        if extend_used < 0.5:
            x_buffer = 0.2 * lon.ptp()
            y_buffer = 0.2 * lat.ptp()

            m = Basemap(
                projection='merc',
                resolution="l",
                #lat_0=lat_0, lon_0=lon_0,
                llcrnrlon=lon.min() - x_buffer,
                urcrnrlon=lon.max() + x_buffer,
                llcrnrlat=lat.min() - y_buffer,
                urcrnrlat=lat.max() + y_buffer)
        else:
            m = Basemap(projection='ortho',
                        lon_0=lon_0,
                        lat_0=lat_0,
                        resolution="c")

        m.drawcoastlines()
        m.fillcontinents("0.9", zorder=0)
        m.drawmapboundary(fill_color="white")
        m.drawparallels(np.arange(-80.0, 80.0, 10.0), labels=[1, 0, 0, 0])
        m.drawmeridians(np.arange(-170.0, 170.0, 10.0), labels=[0, 0, 0, 1])
        m.drawcountries()

        x, y = m(lon, lat)
        im = m.pcolormesh(x, y, data[::-1, :, depth_index], cmap=tomo_colormap)

        # Add colorbar and potentially unit.
        cm = m.colorbar(im, "right", size="3%", pad='2%')
        if component in UNIT_DICT:
            cm.set_label(UNIT_DICT[component], fontsize="x-large", rotation=0)

        plt.suptitle("Depth slice of %s at %i km" %
                     (component, int(depth_in_km)),
                     size="large")

        def _on_button_press(event):
            if event.button != 1 or not event.inaxes:
                return
            lon, lat = m(event.xdata, event.ydata, inverse=True)
            # Convert to colat to ease indexing.
            colat = rotations.lat2colat(lat)

            x_range = (self.setup["physical_boundaries_x"][1] -
                       self.setup["physical_boundaries_x"][0])
            x_frac = (colat - self.setup["physical_boundaries_x"][0]) / x_range
            x_index = int(((self.setup["boundaries_x"][1] -
                            self.setup["boundaries_x"][0]) * x_frac) +
                          self.setup["boundaries_x"][0])
            y_range = (self.setup["physical_boundaries_y"][1] -
                       self.setup["physical_boundaries_y"][0])
            y_frac = (lon - self.setup["physical_boundaries_y"][0]) / y_range
            y_index = int(((self.setup["boundaries_y"][1] -
                            self.setup["boundaries_y"][0]) * y_frac) +
                          self.setup["boundaries_y"][0])

            plt.figure(1, figsize=(3, 8))
            depths = available_depths
            values = data[x_index, y_index, :]
            plt.plot(values, depths)
            plt.grid()
            plt.ylim(depths[-1], depths[0])
            plt.show()
            plt.close()
            plt.figure(0)

        plt.gcf().canvas.mpl_connect('button_press_event', _on_button_press)

        plt.show()
Ejemplo n.º 12
0
    def __init__(self, directory, domain, model_type="earth_model"):
        """
        The init function.

        :param directory: The directory where the earth model or kernel is
            located.
        :param model_type: Determined the type of model loaded. Currently
            two are supported:
                * earth_model - The standard SES3D model files (default)
                * kernel - The kernels. Identifies by lots of grad_* files.
                * wavefield - The raw wavefields.
        """
        self.directory = directory
        self.boxfile = os.path.join(self.directory, "boxfile")
        if not os.path.exists(self.boxfile):
            msg = "boxfile not found. Wrong directory?"
            raise ValueError(msg)

        # Read the boxfile.
        self.setup = self._read_boxfile()

        self.domain = domain
        self.model_type = model_type

        self.one_d_model = OneDimensionalModel("ak135-f")

        if model_type == "earth_model":
            # Now check what different models are available in the directory.
            # This information is also used to infer the degree of the used
            # lagrange polynomial.
            components = ["A", "B", "C", "lambda", "mu", "rhoinv", "Q"]
            self.available_derived_components = ["vp", "vsh", "vsv", "rho"]
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(
                    os.path.join(directory, "%s[0-9]*" % component))
                if len(files) != len(self.setup["subdomains"]):
                    continue
                # Check that the naming is continuous.
                all_good = True
                for _i in xrange(len(self.setup["subdomains"])):
                    if os.path.join(directory,
                                    "%s%i" % (component, _i)) in files:
                        continue
                    all_good = False
                    break
                if all_good is False:
                    msg = "Naming for component %s is off. It will be skipped."
                    warnings.warn(msg)
                    continue
                # They also all need to have the same size.
                if len(set([os.path.getsize(_i) for _i in files])) != 1:
                    msg = ("Component %s has the right number of model files "
                           "but they are not of equal size") % component
                    warnings.warn(msg)
                    continue
                # Sort the files by ascending number.
                files.sort(key=lambda x: int(re.findall(r"\d+$",
                                             (os.path.basename(x)))[0]))
                self.components[component] = {"filenames": files}
        elif model_type == "wavefield":
            components = ["vz", "vx", "vy", "vz"]
            self.available_derived_components = []
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(os.path.join(directory, "%s_*_*" %
                                  component))
                if not files:
                    continue
                timesteps = collections.defaultdict(list)
                for filename in files:
                    timestep = int(os.path.basename(filename).split("_")[-1])
                    timesteps[timestep].append(filename)

                for timestep, filenames in timesteps.iteritems():
                    self.components["%s %s" % (component, timestep)] = \
                        {"filenames": sorted(
                            filenames,
                            key=lambda x: int(
                                os.path.basename(x).split("_")[1]))}
        elif model_type == "kernel":
            # Now check what different models are available in the directory.
            # This information is also used to infer the degree of the used
            # lagrange polynomial.
            components = ["grad_cp", "grad_csh", "grad_csv", "grad_rho"]
            self.available_derived_components = []
            self.components = {}
            self.parsed_components = {}
            for component in components:
                files = glob.glob(
                    os.path.join(directory, "%s_[0-9]*" % component))
                if len(files) != len(self.setup["subdomains"]):
                    continue
                if len(set([os.path.getsize(_i) for _i in files])) != 1:
                    msg = ("Component %s has the right number of model files "
                           "but they are not of equal size") % component
                    warnings.warn(msg)
                    continue
                    # Sort the files by ascending number.
                files.sort(key=lambda x: int(
                    re.findall(r"\d+$",
                               (os.path.basename(x)))[0]))
                self.components[component] = {"filenames": files}
        else:
            msg = "model_type '%s' not known." % model_type
            raise ValueError(msg)

        # All files for a single component have the same size. Now check that
        # all files have the same size.
        unique_filesizes = len(list(set([
            os.path.getsize(_i["filenames"][0])
            for _i in self.components.itervalues()])))
        if unique_filesizes != 1:
            msg = ("The different components in the folder do not have the "
                   "same number of samples")
            raise ValueError(msg)

        # Now calculate the lagrange polynomial degree. All necessary
        # information is present.
        size = os.path.getsize(self.components.values()[0]["filenames"][0])
        sd = self.setup["subdomains"][0]
        x, y, z = sd["index_x_count"], sd["index_y_count"], sd["index_z_count"]
        self.lagrange_polynomial_degree = \
            int(round(((size * 0.25) / (x * y * z)) ** (1.0 / 3.0) - 1))

        self._calculate_final_dimensions()

        # Setup the boundaries.
        self.lat_bounds = [
            rotations.colat2lat(_i)
            for _i in self.setup["physical_boundaries_x"][::-1]]
        self.lng_bounds = self.setup["physical_boundaries_y"]
        self.depth_bounds = [
            6371 - _i / 1000.0 for _i in self.setup["physical_boundaries_z"]]

        self.collocation_points_lngs = self._get_collocation_points_along_axis(
            self.lng_bounds[0], self.lng_bounds[1],
            self.setup["point_count_in_y"])
        self.collocation_points_lats = self._get_collocation_points_along_axis(
            self.lat_bounds[0], self.lat_bounds[1],
            self.setup["point_count_in_x"])
        self.collocation_points_depth = \
            self._get_collocation_points_along_axis(
                self.depth_bounds[1], self.depth_bounds[0],
                self.setup["point_count_in_z"])[::-1]
Ejemplo n.º 13
0
def read_SES3D(file_or_file_object, *args, **kwargs):
    """
    Turns a SES3D file into a obspy.core.Stream object.

    SES3D files do not contain a starttime and thus the first first sample will
    always begin at 1970-01-01T00:00:00.

    The data will be a floating point array of the ground velocity in meters
    per second.

    Furthermore every trace will have a trace.stats.ses3d dictionary which
    contains the following six keys:
        * receiver_latitude
        * receiver_longitde
        * receiver_depth_in_m
        * source_latitude
        * source_longitude
        * source_depth_in_m

    The network, station, and location attributes of the trace will be empty,
    and the channel will be set to either 'X' (south component), 'Y' (east
    component), or 'Z' (vertical component).
    """
    # Make sure that it is a file like object.
    if not hasattr(file_or_file_object, "read"):
        with open(file_or_file_object, "rb") as open_file:
            file_or_file_object = StringIO(open_file.read())

    # Read the header.
    component = file_or_file_object.readline().split()[0].lower()
    npts = int(file_or_file_object.readline().split()[-1])
    delta = float(file_or_file_object.readline().split()[-1])
    # Skip receiver location line.
    file_or_file_object.readline()
    rec_loc = file_or_file_object.readline().split()
    rec_x, rec_y, rec_z = map(float, [rec_loc[1], rec_loc[3], rec_loc[5]])
    # Skip the source location line.
    file_or_file_object.readline()
    src_loc = file_or_file_object.readline().split()
    src_x, src_y, src_z = map(float, [src_loc[1], src_loc[3], src_loc[5]])

    # Read the data.
    data = np.array(map(float, file_or_file_object.readlines()),
                    dtype="float32")

    # Setup Obspy Stream/Trace structure.
    tr = Trace(data=data)
    tr.stats.delta = delta
    # Map the channel attributes.
    tr.stats.channel = {"theta": "X", "phi": "Y", "r": "Z"}[component]
    tr.stats.ses3d = AttribDict()
    tr.stats.ses3d.receiver_latitude = rotations.colat2lat(rec_x)
    tr.stats.ses3d.receiver_longitude = rec_y
    tr.stats.ses3d.receiver_depth_in_m = rec_z
    tr.stats.ses3d.source_latitude = rotations.colat2lat(src_x)
    tr.stats.ses3d.source_longitude = src_y
    tr.stats.ses3d.source_depth_in_m = src_z
    # Small check.
    if npts != tr.stats.npts:
        msg = "The sample count specified in the header does not match " + \
            "the actual data count."
        warnings.warn(msg)
    return Stream(traces=[tr])
Ejemplo n.º 14
0
    def test_RotateMomentTensor(self):
        """
        Tests the moment tensor rotations.
        """
        # A full rotation should not change anything.
        Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor(1, 2, 3,
            4, 5, 6, 7, 8, [9, 10, 11], 360)
        self.assertAlmostEqual(Mrr, 1.0, 6)
        self.assertAlmostEqual(Mtt, 2.0, 6)
        self.assertAlmostEqual(Mpp, 3.0, 6)
        self.assertAlmostEqual(Mrt, 4.0, 6)
        self.assertAlmostEqual(Mrp, 5.0, 6)
        self.assertAlmostEqual(Mtp, 6.0, 6)

        # The following ones are tested against a well proven Matlab script by
        # Andreas Fichtner.
        Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor( \
            -0.704, 0.071, 0.632, 0.226, -0.611, 3.290,
            rotations.colat2lat(26.08), -21.17,
            [0, 1, 0], 57.5)
        Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
            [-0.70400000, 2.04919171, -1.34619171, 0.02718681, -0.65089007,
            2.83207047]
        self.assertAlmostEqual(Mrr, Mrr_new, 6)
        self.assertAlmostEqual(Mtt, Mtt_new, 6)
        self.assertAlmostEqual(Mpp, Mpp_new, 6)
        self.assertAlmostEqual(Mrt, Mrt_new, 6)
        self.assertAlmostEqual(Mrp, Mrp_new, 6)
        self.assertAlmostEqual(Mtp, Mtp_new, 6)
        # Another example.
        Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor( \
            -0.818, -1.300, 2.120, 1.720, 2.290, -0.081,
            rotations.colat2lat(53.51), -9.87,
            [np.sqrt(2) / 2, -np.sqrt(2) / 2, 0], -31.34)
        Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
            [-0.81800000, -0.69772178, 1.51772178, 2.55423451, 1.29552541,
            1.30522545]
        self.assertAlmostEqual(Mrr, Mrr_new, 6)
        self.assertAlmostEqual(Mtt, Mtt_new, 6)
        self.assertAlmostEqual(Mpp, Mpp_new, 6)
        self.assertAlmostEqual(Mrt, Mrt_new, 6)
        self.assertAlmostEqual(Mrp, Mrp_new, 6)
        self.assertAlmostEqual(Mtp, Mtp_new, 6)
        # The same as before, but with a non-normalized axis. Should work just
        # as well.
        Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor( \
            -0.818, -1.300, 2.120, 1.720, 2.290, -0.081,
            rotations.colat2lat(53.51), -9.87,
            [11.12, -11.12, 0], -31.34)
        Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
            [-0.81800000, -0.69772178, 1.51772178, 2.55423451, 1.29552541,
            1.30522545]
        self.assertAlmostEqual(Mrr, Mrr_new, 6)
        self.assertAlmostEqual(Mtt, Mtt_new, 6)
        self.assertAlmostEqual(Mpp, Mpp_new, 6)
        self.assertAlmostEqual(Mrt, Mrt_new, 6)
        self.assertAlmostEqual(Mrp, Mrp_new, 6)
        self.assertAlmostEqual(Mtp, Mtp_new, 6)
        # One more.
        Mrr, Mtt, Mpp, Mrt, Mrp, Mtp = rotations.rotate_moment_tensor( \
            0.952, -1.030, 0.076, 0.226, -0.040, -0.165,
            rotations.colat2lat(63.34), 55.80,
            [np.sqrt(3) / 3, -np.sqrt(3) / 3, -np.sqrt(3) / 3], 123.45)
        Mrr_new, Mtt_new, Mpp_new, Mrt_new, Mrp_new, Mtp_new = \
            [0.95200000, -0.41458722, -0.53941278, -0.09170855, 0.21039378,
            0.57370606]
        self.assertAlmostEqual(Mrr, Mrr_new, 6)
        self.assertAlmostEqual(Mtt, Mtt_new, 6)
        self.assertAlmostEqual(Mpp, Mpp_new, 6)
        self.assertAlmostEqual(Mrt, Mrt_new, 6)
        self.assertAlmostEqual(Mrp, Mrp_new, 6)
        self.assertAlmostEqual(Mtp, Mtp_new, 6)
Ejemplo n.º 15
0
    def plot_depth_slice(self, component, depth_in_km):
        """
        Plots a depth slice.

        :param component: The component to plot.
        :type component: basestring
        :param depth_in_km: The depth in km to plot. If the exact depth does
             not exists, the nearest neighbour will be plotted.
        :type depth_in_km: integer or float
        """
        lat_bounds = [rotations.colat2lat(_i)
                      for _i in self.setup["physical_boundaries_x"][::-1]]
        lng_bounds = self.setup["physical_boundaries_y"]
        depth_bounds = [6371 - _i / 1000.0
                        for _i in self.setup["physical_boundaries_z"]]

        data = self.parsed_components[component]

        available_depths = np.linspace(*depth_bounds, num=data.shape[2])
        depth_index = np.argmin(np.abs(available_depths - depth_in_km))
        actual_depth = available_depths[depth_index]

        lngs = self._get_collocation_points_along_axis(
            lng_bounds[0], lng_bounds[1], data.shape[1])
        lats = self._get_collocation_points_along_axis(
            lat_bounds[0], lat_bounds[1], data.shape[0])

        lon, lat = np.meshgrid(lngs, lats)
        if self.rotation_axis and self.rotation_angle_in_degree:
            lon_shape = lon.shape
            lat_shape = lat.shape
            lon.shape = lon.size
            lat.shape = lat.size
            lat, lon = rotations.rotate_lat_lon(lat, lon, self.rotation_axis,
                                                self.rotation_angle_in_degree)
            lon.shape = lon_shape
            lat.shape = lat_shape

        # Get the center of the map.
        lon_0 = lon.min() + lon.ptp() / 2.0
        lat_0 = lat.min() + lat.ptp() / 2.0

        plt.figure(0)

        # Attempt to zoom into the region of interest.
        max_extend = max(lon.ptp(), lat.ptp())
        extend_used = max_extend / 180.0
        if extend_used < 0.5:
            # Calculate approximate width and height in meters.
            width = lon.ptp()
            height = lat.ptp()
            width *= 110000 * 1.1
            height *= 110000 * 1.1
            # Lambert azimuthal equal area projection. Equal area projections
            # are useful for interpreting features and this particular one also
            # does not distort features a lot on regional scales.
            m = Basemap(projection='laea', resolution="l",
                        width=width, height=height,
                        lat_0=lat_0, lon_0=lon_0)
        else:
            m = Basemap(projection='ortho', lon_0=lon_0, lat_0=lat_0,
                        resolution="c")

        m.drawcoastlines()
        m.fillcontinents("0.9", zorder=0)
        m.drawmapboundary(fill_color="white")
        m.drawparallels(np.arange(-80.0, 80.0, 10.0), labels=[1, 0, 0, 0])
        m.drawmeridians(np.arange(-170.0, 170.0, 10.0), labels=[0, 0, 0, 1])
        m.drawcountries()

        x, y = m(lon, lat)
        depth_data = data[::-1, :, depth_index]
        vmin, vmax = depth_data.min(), depth_data.max()
        vmedian = np.median(depth_data)
        offset = max(abs(vmax - vmedian), abs(vmedian - vmin))

        if vmax - vmin == 0:
            offset = 0.01

        vmin = vmedian - offset
        vmax = vmedian + offset

        im = m.pcolormesh(x, y, depth_data, cmap=tomo_colormap, vmin=vmin,
                          vmax=vmax)

        # Add colorbar and potentially unit.
        cm = m.colorbar(im, "right", size="3%", pad='2%')
        if component in UNIT_DICT:
            cm.set_label(UNIT_DICT[component], fontsize="x-large", rotation=0)

        plt.suptitle("Depth slice of %s at %i km" % (
            component, int(round(actual_depth))), size="large")

        border = rotations.get_border_latlng_list(
            rotations.colat2lat(self.setup["physical_boundaries_x"][0]),
            rotations.colat2lat(self.setup["physical_boundaries_x"][1]),
            self.setup["physical_boundaries_y"][0],
            self.setup["physical_boundaries_y"][1],
            rotation_axis=self.rotation_axis,
            rotation_angle_in_degree=self.rotation_angle_in_degree)
        border = np.array(border)
        lats = border[:, 0]
        lngs = border[:, 1]
        lngs, lats = m(lngs, lats)
        m.plot(lngs, lats, color="black", lw=2, path_effects=[
            PathEffects.withStroke(linewidth=4, foreground="white")])

        def _on_button_press(event):
            if event.button != 1 or not event.inaxes:
                return
            lon, lat = m(event.xdata, event.ydata, inverse=True)
            # Convert to colat to ease indexing.
            colat = rotations.lat2colat(lat)

            x_range = (self.setup["physical_boundaries_x"][1] -
                       self.setup["physical_boundaries_x"][0])
            x_frac = (colat - self.setup["physical_boundaries_x"][0]) / x_range
            x_index = int(((self.setup["boundaries_x"][1] -
                            self.setup["boundaries_x"][0]) * x_frac) +
                          self.setup["boundaries_x"][0])
            y_range = (self.setup["physical_boundaries_y"][1] -
                       self.setup["physical_boundaries_y"][0])
            y_frac = (lon - self.setup["physical_boundaries_y"][0]) / y_range
            y_index = int(((self.setup["boundaries_y"][1] -
                            self.setup["boundaries_y"][0]) * y_frac) +
                          self.setup["boundaries_y"][0])

            plt.figure(1, figsize=(3, 8))
            depths = available_depths
            values = data[x_index, y_index, :]
            plt.plot(values, depths)
            plt.grid()
            plt.ylim(depths[-1], depths[0])
            plt.show()
            plt.close()
            plt.figure(0)

        plt.gcf().canvas.mpl_connect('button_press_event', _on_button_press)

        plt.show()