Source code for pyrocko.dataset.crustdb

# http://pyrocko.org - GPLv3
#
# The Pyrocko Developers, 21st Century
# ---|P------/S----------~Lg----------
'''
Access to the USGS Global Crustal Database.

Simple queries and statistical analysis
'''

import numpy as num
import copy
import logging
from os import path

from pyrocko.guts import Object, String, Float, Int
from pyrocko.guts_array import Array

from pyrocko.cake import LayeredModel, Material
from pyrocko.plot.cake_plot import my_model_plot, xscaled, yscaled

from .crustdb_abbr import ageKey, provinceKey, referenceKey, pubYear  # noqa

logger = logging.getLogger('pyrocko.dataset.crustdb')
THICKNESS_HALFSPACE = 2

db_url = 'https://mirror.pyrocko.org/gsc20130501.txt'
km = 1e3
vel_labels = {
    'vp': '$V_P$',
    'p': '$V_P$',
    'vs': '$V_S$',
    's': '$V_S$',
}


class DatabaseError(Exception):
    pass


class ProfileEmpty(Exception):
    pass


def _getCanvas(axes):

    import matplotlib.pyplot as plt

    if axes is None:
        fig = plt.figure()
        return fig, fig.gca()
    return axes.figure, axes


def xoffset_scale(offset, scale, ax):
    from matplotlib.ticker import ScalarFormatter, AutoLocator

    class FormatVelocities(ScalarFormatter):
        @staticmethod
        def __call__(value, pos):
            return u'%.1f' % ((value-offset) * scale)

    class OffsetLocator(AutoLocator):
        def tick_values(self, vmin, vmax):
            return [v + offset for v in
                    AutoLocator.tick_values(self, vmin, vmax)]

    ax.get_xaxis().set_major_formatter(FormatVelocities())
    ax.get_xaxis().set_major_locator(OffsetLocator())


[docs]class VelocityProfile(Object): uid = Int.T( optional=True, help='Unique ID of measurement') lat = Float.T( help='Latitude [deg]') lon = Float.T( help='Longitude [deg]') elevation = Float.T( default=num.nan, help='Elevation [m]') vp = Array.T( shape=(None, 1), help='P Wave velocities [m/s]') vs = Array.T( shape=(None, 1), help='S Wave velocities [m/s]') d = Array.T( shape=(None, 1), help='Interface depth, top [m]') h = Array.T( shape=(None, 1), help='Interface thickness [m]') heatflow = Float.T( optional=True, help='Heatflow [W/m^2]') geographical_location = String.T( optional=True, help='Geographic Location') geological_province = String.T( optional=True, help='Geological Province') geological_age = String.T( optional=True, help='Geological Age') measurement_method = Int.T( optional=True, help='Measurement method') publication_reference = String.T( optional=True, help='Publication Reference') publication_year__ = Int.T( help='Publication Date') def __init__(self, *args, **kwargs): Object.__init__(self, *args, **kwargs) self.h = num.abs(self.d - num.roll(self.d, -1)) self.h[-1] = 0 self.nlayers = self.h.size self.geographical_location = '%s (%s)' % ( provinceKey(self.geographical_location), self.geographical_location) self.vs[self.vs == 0] = num.nan self.vp[self.vp == 0] = num.nan self._step_vp = num.repeat(self.vp, 2) self._step_vs = num.repeat(self.vs, 2) self._step_d = num.roll(num.repeat(self.d, 2), -1) self._step_d[-1] = self._step_d[-2] + THICKNESS_HALFSPACE @property def publication_year__(self): return pubYear(self.publication_reference)
[docs] def interpolateProfile(self, depths, phase='p', stepped=True): ''' Get a continuous velocity function at arbitrary depth. :param depth: Depths to interpolate :type depth: :class:`numpy.ndarray` :param phase: P or S wave velocity, **p** or **s** :type phase: str, optional :param stepped: Use a stepped velocity function or gradient :type stepped: bool :returns: velocities at requested depths :rtype: :class:`numpy.ndarray` ''' if phase not in ['s', 'p']: raise AttributeError("Phase has to be either 'p' or 's'.") if phase == 'p': vel = self._step_vp if stepped else self.vp elif phase == 's': vel = self._step_vs if stepped else self.vs d = self._step_d if stepped else self.d if vel.size == 0: raise ProfileEmpty('Phase %s does not contain velocities' % phase) try: res = num.interp(depths, d, vel, left=num.nan, right=num.nan) except ValueError: raise ValueError('Could not interpolate velocity profile.') return res
[docs] def plot(self, axes=None): ''' Plot the velocity profile, see :class:`pyrocko.cake`. :param axes: Axes to plot into. :type axes: :class:`matplotlib.Axes` ''' import matplotlib.pyplot as plt fig, ax = _getCanvas(axes) my_model_plot(self.getLayeredModel(), axes=axes) ax.set_title('Global Crustal Database\n' 'Velocity Structure at {p.lat:.4f}N, ' ' {p.lat:.4f}E (uid {p.uid})'.format(p=self)) if axes is None: plt.show()
[docs] def getLayeredModel(self): ''' Get a layered model, see :class:`pyrocko.cake.LayeredModel`. ''' def iterLines(): for il, m in enumerate(self.iterLayers()): yield self.d[il], m, '' return LayeredModel.from_scanlines(iterLines())
[docs] def iterLayers(self): ''' Iterator yielding a :class:`pyrocko.cake.Material` for each layer. ''' for il in range(self.nlayers): yield Material(vp=self.vp[il], vs=self.vs[il])
@property def geog_loc_long(self): return provinceKey(self.geog_loc) @property def geol_age_long(self): return ageKey(self.geol_age) @property def has_s(self): return num.any(self.vp) @property def has_p(self): return num.any(self.vs)
[docs] def get_weeded(self): ''' Get weeded representation of layers used in the profile. See :func:`pyrocko.cake.get_weeded` for details. ''' weeded = num.zeros((self.nlayers, 4)) weeded[:, 0] = self.d weeded[:, 1] = self.vp weeded[:, 2] = self.vs
def _csv(self): output = '' for d in range(len(self.h)): output += ( '{p.uid}, {p.lat}, {p.lon},' ' {vp}, {vs}, {h}, {d}, {p.publication_reference}\n' ).format( p=self, vp=self.vp[d], vs=self.vs[d], h=self.h[d], d=self.d[d]) return output
[docs]class CrustDB(object): ''' CrustDB is a container for :class:`VelocityProfile` and provides functions for spatial selection, querying, processing and visualising data from the Global Crustal Database. ''' def __init__(self, database_file=None, parent=None): self.profiles = [] self._velocity_matrix_cache = {} self.data_matrix = None self.name = None self.database_file = database_file if parent is not None: pass elif database_file is not None: self._read(database_file) else: self._read(self._getRepositoryDatabase()) def __len__(self): return len(self.profiles) def __setitem__(self, key, value): if not isinstance(value, VelocityProfile): raise TypeError('Element is not a VelocityProfile') self.profiles[key] = value def __delitem__(self, key): self.profiles.remove(key) def __getitem__(self, key): return self.profiles[key] def __str__(self): rstr = 'Container contains %d velocity profiles:\n\n' % self.nprofiles return rstr @property def nprofiles(self): return len(self.profiles) def append(self, value): if not isinstance(value, VelocityProfile): raise TypeError('Element is not a VelocityProfile') self.profiles.append(value) def copy(self): return copy.deepcopy(self) def lats(self): return num.array( [p.lat for p in self.profiles]) def lons(self): return num.array( [p.lon for p in self.profiles]) def _dataMatrix(self): if self.data_matrix is not None: return self.data_matrix self.data_matrix = num.core.records.fromarrays( num.vstack([ num.concatenate([p.vp for p in self.profiles]), num.concatenate([p.vs for p in self.profiles]), num.concatenate([p.h for p in self.profiles]), num.concatenate([p.d for p in self.profiles]) ]), names='vp, vs, h, d') return self.data_matrix
[docs] def velocityMatrix(self, depth_range=(0, 60000.), ddepth=100., phase='p'): ''' Create a regular sampled velocity matrix. :param depth_range: Depth range, ``(dmin, dmax)``, defaults to ``(0, 6000.)`` :type depth_range: tuple :param ddepth: Stepping in [m], defaults to ``100.`` :type ddepth: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: Sample depths, veloctiy matrix :rtype: tuple, (sample_depth, :class:`numpy.ndarray`) ''' dmin, dmax = depth_range uid = '.'.join(map(repr, (dmin, dmax, ddepth, phase))) sdepth = num.linspace(dmin, dmax, (dmax - dmin) / ddepth) ndepth = sdepth.size if uid not in self._velocity_matrix_cache: vel_mat = num.empty((self.nprofiles, ndepth)) for ip, profile in enumerate(self.profiles): vel_mat[ip, :] = profile.interpolateProfile(sdepth, phase=phase) self._velocity_matrix_cache[uid] = num.ma.masked_invalid(vel_mat) return sdepth, self._velocity_matrix_cache[uid]
[docs] def rmsRank(self, ref_profile, depth_range=(0, 3500.), ddepth=100., phase='p'): ''' Correlates ``ref_profile`` to each profile in the database. :param ref_profile: Reference profile :type ref_profile: :class:`VelocityProfile` :param depth_range: Depth range in [m], ``(dmin, dmax)``, defaults to ``(0, 35000.)`` :type depth_range: tuple, optional :param ddepth: Stepping in [m], defaults to ``100.`` :type ddepth: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: RMS factor length of N_profiles :rtype: :class:`numpy.ndarray` ''' if not isinstance(ref_profile, VelocityProfile): raise ValueError('ref_profile is not a VelocityProfile') sdepth, vel_matrix = self.velocityMatrix(depth_range, ddepth, phase=phase) ref_vel = ref_profile.interpolateProfile(sdepth, phase=phase) rms = num.empty(self.nprofiles) for p in range(self.nprofiles): profile = vel_matrix[p, :] rms[p] = num.sqrt(profile**2 - ref_vel**2).sum() / ref_vel.size return rms
[docs] def histogram2d(self, depth_range=(0., 60000.), vel_range=None, ddepth=100., dvbin=100., ddbin=2000., phase='p'): ''' Create a 2D Histogram of all the velocity profiles. Check :func:`numpy.histogram2d` for more information. :param depth_range: Depth range in [m], ``(dmin, dmax)``, defaults to ``(0., 60000.)`` :type depth_range: tuple :param vel_range: Depth range, ``(vmin, vmax)``, defaults to ``(5500., 8500.)`` :type vel_range: tuple :param ddepth: Stepping in [km], defaults to ``100.`` :type ddepth: float :param dvbin: Bin size in velocity dimension [m/s], defaults to 100. :type dvbin: float :param dvbin: Bin size in depth dimension [m], defaults to 2000. :type dvbin: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: :func:`numpy.histogram2d` :rtype: tuple ''' sdepth, v_mat = self.velocityMatrix(depth_range, ddepth, phase=phase) d_vec = num.tile(sdepth, self.nprofiles) # Velocity and depth bins if vel_range is None: vel_range = ((v_mat.min() // 1e2) * 1e2, (v_mat.max() // 1e2) * 1e2) nvbins = int((vel_range[1] - vel_range[0]) / dvbin) ndbins = int((depth_range[1] - depth_range[0]) / ddbin) return num.histogram2d(v_mat.flatten(), d_vec, range=(vel_range, depth_range), bins=[nvbins, ndbins], normed=False)
[docs] def meanVelocity(self, depth_range=(0., 60000.), ddepth=100., phase='p'): ''' Get mean and standard deviation of velocity profile. :param depth_range: Depth range in [m], ``(dmin, dmax)``, defaults to ``(0., 60000.)`` :type depth_range: tuple :param ddepth: Stepping in [m], defaults to ``100.`` :type ddepth: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: depth vector, mean velocities, standard deviations :rtype: tuple of :class:`numpy.ndarray` ''' sdepth, v_mat = self.velocityMatrix(depth_range, ddepth, phase=phase) v_mean = num.ma.mean(v_mat, axis=0) v_std = num.ma.std(v_mat, axis=0) return sdepth, v_mean.flatten(), v_std.flatten()
[docs] def modeVelocity(self, depth_range=(0., 60000.), ddepth=100., phase='p'): ''' Get mode velocity profile and standard deviation. :param depth_range: Depth range in [m], ``(dmin, dmax)``, defaults to ``(0., 60000.)`` :type depth_range: tuple :param ddepth: Stepping in [m], defaults to ``100.`` :type ddepth: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: depth vector, mode velocity, number of counts at each depth :rtype: tuple of :class:`numpy.ndarray` ''' import scipy.stats sdepth, v_mat = self.velocityMatrix(depth_range, ddepth) v_mode, v_counts = scipy.stats.mstats.mode(v_mat, axis=0) return sdepth, v_mode.flatten(), v_counts.flatten()
[docs] def medianVelocity(self, depth_range=(0., 60000.), ddepth=100., phase='p'): ''' Median velocity profile plus std variation. :param depth_range: Depth range in [m], ``(dmin, dmax)``, defaults to ``(0., 60000.)`` :type depth_range: tuple :param ddepth: Stepping in [m], defaults to ``100.`` :type ddepth: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :returns: depth vector, median velocities, standard deviations :rtype: tuple of :class:`numpy.ndarray` ''' sdepth, v_mat = self.velocityMatrix(depth_range, ddepth, phase=phase) v_mean = num.ma.median(v_mat, axis=0) v_std = num.ma.std(v_mat, axis=0) return sdepth, v_mean.flatten(), v_std.flatten()
[docs] def plotHistogram(self, vel_range=None, bins=36, phase='vp', axes=None): ''' Plot 1D histogram of seismic velocities in the container. :param vel_range: Velocity range, defaults to (5.5, 8.5) :type vel_range: tuple, optional :param bins: bins, defaults to 30 (see :func:`numpy.histogram`) :type bins: int, optional :param phase: Property to plot out of ``['vp', 'vs']``, defaults to 'vp' :type phase: str, optional :param figure: Figure to plot in, defaults to None :type figure: :class:`matplotlib.Figure`, optional ''' import matplotlib.pyplot as plt fig, ax = _getCanvas(axes) if phase not in ['vp', 'vs']: raise AttributeError('phase has to be either vp or vs') data = self._dataMatrix()[phase] ax.hist(data, weights=self.data_matrix['h'], range=vel_range, bins=bins, color='g', alpha=.5) ax.text(.95, .95, '%d Profiles' % self.nprofiles, transform=ax.transAxes, fontsize=10, va='top', ha='right', alpha=.7) ax.set_title('Distribution of %s' % vel_labels[phase]) ax.set_xlabel('%s [km/s]' % vel_labels[phase]) ax.set_ylabel('Cumulative occurrence [N]') xscaled(1./km, ax) ax.yaxis.grid(alpha=.4) if self.name is not None: ax.set_title('%s for %s' % (ax.get_title(), self.name)) if axes is None: plt.show()
[docs] def plot(self, depth_range=(0, 60000.), ddepth=100., ddbin=2000., vel_range=None, dvbin=100., percent=False, plot_mode=True, plot_median=True, plot_mean=False, show_cbar=True, aspect=.02, phase='p', axes=None): ''' Plot a two 2D Histogram of seismic velocities. :param depth_range: Depth range, ``(dmin, dmax)``, defaults to ``(0, 60)`` :type depth_range: tuple :param vel_range: Velocity range, ``(vmin, vmax)`` :type vel_range: tuple :param ddepth: Stepping in [m], defaults to ``.1`` :type ddepth: float :param dvbin: Bin size in velocity dimension [m/s], defaults to .1 :type dvbin: float :param dvbin: Bin size in depth dimension [m], defaults to 2000. :type dvbin: float :param phase: Phase to calculate ``p`` or ``s``, defaults to ``p`` :type phase: str :param plot_mode: Plot the Mode :type plot_mode: bool :param plot_mean: Plot the Mean :type plot_mean: bool :param plot_median: Plot the Median :type plot_median: bool :param axes: Axes to plot into, defaults to None :type axes: :class:`matplotlib.Axes` ''' import matplotlib.pyplot as plt fig, ax = _getCanvas(axes) ax = fig.gca() if vel_range is not None: vmin, vmax = vel_range dmin, dmax = depth_range vfield, vedg, dedg = self.histogram2d(vel_range=vel_range, depth_range=depth_range, ddepth=ddepth, dvbin=dvbin, ddbin=ddbin, phase=phase) vfield /= (ddbin / ddepth) if percent: vfield /= vfield.sum(axis=1)[num.newaxis, :] grid_ext = [vedg[0], vedg[-1], dedg[-1], dedg[0]] histogram = ax.imshow(vfield.swapaxes(0, 1), interpolation='nearest', extent=grid_ext, aspect=aspect) if show_cbar: cticks = num.unique( num.arange(0, vfield.max(), vfield.max() // 10).round()) cbar = fig.colorbar(histogram, ticks=cticks, format='%1i', orientation='horizontal') if percent: cbar.set_label('Percent') else: cbar.set_label('Number of Profiles') if plot_mode: sdepth, vel_mode, _ = self.modeVelocity(depth_range=depth_range, ddepth=ddepth) ax.plot(vel_mode[sdepth < dmax] + ddepth/2, sdepth[sdepth < dmax], alpha=.8, color='w', label='Mode') if plot_mean: sdepth, vel_mean, _ = self.meanVelocity(depth_range=depth_range, ddepth=ddepth) ax.plot(vel_mean[sdepth < dmax] + ddepth/2, sdepth[sdepth < dmax], alpha=.8, color='w', linestyle='--', label='Mean') if plot_median: sdepth, vel_median, _ = self.medianVelocity( depth_range=depth_range, ddepth=ddepth) ax.plot(vel_median[sdepth < dmax] + ddepth/2, sdepth[sdepth < dmax], alpha=.8, color='w', linestyle=':', label='Median') ax.grid(True, which='both', color='w', linewidth=.8, alpha=.4) ax.text(.025, .025, '%d Profiles' % self.nprofiles, color='w', alpha=.7, transform=ax.transAxes, fontsize=9, va='bottom', ha='left') ax.set_title('Crustal Velocity Distribution') ax.set_xlabel('%s [km/s]' % vel_labels[phase]) ax.set_ylabel('Depth [km]') yscaled(1./km, ax) xoffset_scale(dvbin/2, 1./km, ax) ax.set_xlim(vel_range) if self.name is not None: ax.set_title('%s for %s' % (ax.get_title(), self.name)) if plot_mode or plot_mean or plot_median: leg = ax.legend(loc=1, fancybox=True, prop={'size': 10.}) leg.get_frame().set_alpha(.6) if axes is None: plt.show()
[docs] def plotVelocitySurface(self, v_max, d_min=0., d_max=6000., axes=None): ''' Plot a triangulated a depth surface exceeding velocity. ''' import matplotlib.pyplot as plt fig, ax = _getCanvas(axes) d = self.exceedVelocity(v_max, d_min, d_max) lons = self.lons()[d > 0] lats = self.lats()[d > 0] d = d[d > 0] ax.tricontourf(lats, lons, d) if axes is None: plt.show()
def plotMap(self, outfile, **kwargs): from pyrocko.plot import gmtpy lats = self.lats() lons = self.lons() s, n, w, e = (lats.min(), lats.max(), lons.min(), lons.max()) def darken(c, f=0.7): return (c[0]*f, c[1]*f, c[2]*f) gmt = gmtpy.GMT() gmt.psbasemap(B='40/20', J='M0/12', R='%f/%f/%f/%f' % (w, e, s, n)) gmt.pscoast(R=True, J=True, D='i', S='216/242/254', A=10000, W='.2p') gmt.psxy(R=True, J=True, in_columns=[lons, lats], S='c2p', G='black') gmt.save(outfile)
[docs] def exceedVelocity(self, v_max, d_min=0, d_max=60): ''' Returns the last depth ``v_max`` has not been exceeded. :param v_max: maximal velocity :type vmax: float :param dz: depth is sampled in dz steps :type dz: float :param d_max: maximum depth :type d_max: int :param d_min: minimum depth :type d_min: int :returns: Lat, Lon, Depth and uid where ``v_max`` is exceeded :rtype: list(num.array) ''' self.profile_exceed_velocity = num.empty(len(self.profiles)) self.profile_exceed_velocity[:] = num.nan for ip, profile in enumerate(self.profiles): for il in range(len(profile.d)): if profile.d[il] <= d_min\ or profile.d[il] >= d_max: continue if profile.vp[il] < v_max: continue else: self.profile_exceed_velocity[ip] = profile.d[il] break return self.profile_exceed_velocity
[docs] def selectRegion(self, west, east, south, north): ''' Select profiles within a region by geographic corner coordinates. :param west: west corner :type west: float :param east: east corner :type east: float :param south: south corner :type south: float :param north: north corner :type north: float :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() for profile in self.profiles: if profile.lon >= west and profile.lon <= east \ and profile.lat <= north and profile.lat >= south: r_container.append(profile) return r_container
[docs] def selectPolygon(self, poly): ''' Select profiles within a polygon. The algorithm is called the **Ray Casting Method** :param poly: Latitude Longitude pairs of the polygon :type param: list of :class:`numpy.ndarray` :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() for profile in self.profiles: x = profile.lon y = profile.lat inside = False p1x, p1y = poly[0] for p2x, p2y in poly: if y >= min(p1y, p2y): if y <= max(p1y, p2y): if x <= max(p1x, p2x): if p1y != p2y: xints = (y - p1y) * (p2x - p1x) / \ (p2y - p1y) + p1x if p1x == p2x or x <= xints: inside = not inside p1x, p1y = p2x, p2y if inside: r_container.append(profile) return r_container
[docs] def selectLocation(self, lat, lon, radius=10): ''' Select profiles at a geographic location within a ``radius``. :param lat: Latitude in [deg] :type lat: float :param lon: Longitude in [deg] :type lon: float :param radius: Radius in [deg] :type radius: float :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting location %f, %f (r=%f)...' % (lat, lon, radius)) for profile in self.profiles: if num.sqrt((lat - profile.lat)**2 + (lon - profile.lon)**2) <= radius: r_container.append(profile) return r_container
[docs] def selectMinLayers(self, nlayers): ''' Select profiles with more than ``nlayers``. :param nlayers: Minimum number of layers :type nlayers: int :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting minimum %d layers...' % nlayers) for profile in self.profiles: if profile.nlayers >= nlayers: r_container.append(profile) return r_container
[docs] def selectMaxLayers(self, nlayers): ''' Select profiles with more than ``nlayers``. :param nlayers: Maximum number of layers :type nlayers: int :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting maximum %d layers...' % nlayers) for profile in self.profiles: if profile.nlayers <= nlayers: r_container.append(profile) return r_container
[docs] def selectMinDepth(self, depth): ''' Select profiles describing layers deeper than ``depth``. :param depth: Minumum depth in [m] :type depth: float :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting minimum depth %f m...' % depth) for profile in self.profiles: if profile.d.max() >= depth: r_container.append(profile) return r_container
[docs] def selectMaxDepth(self, depth): ''' Select profiles describing layers shallower than ``depth``. :param depth: Maximum depth in [m] :type depth: float :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting maximum depth %f m...' % depth) for profile in self.profiles: if profile.d.max() <= depth: r_container.append(profile) return r_container
[docs] def selectVp(self): ''' Select profiles describing P wave velocity. :returns Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting profiles providing Vp...') for profile in self.profiles: if not num.all(num.isnan(profile.vp)): r_container.append(profile) return r_container
[docs] def selectVs(self): ''' Select profiles describing P wave velocity. :returns: Selected profiles :rtype: :class:`CrustDB` ''' r_container = self._emptyCopy() logger.info('Selecting profiles providing Vs...') for profile in self.profiles: if not num.all(num.isnan(profile.vs)): r_container.append(profile) return r_container
def _emptyCopy(self): r_container = CrustDB(parent=self) r_container.name = self.name return r_container
[docs] def exportCSV(self, filename=None): ''' Export as CSV file. :param filename: Export filename :type filename: str ''' with open(filename, 'w') as file: file.write('# uid, Lat, Lon, vp, vs, H, Depth, Reference\n') for profile in self.profiles: file.write(profile._csv())
[docs] def exportYAML(self, filename=None): ''' Export as YAML file. :param filename: Export filename :type filename: str ''' with open(filename, 'w') as file: for profile in self.profiles: file.write(profile.__str__())
@classmethod def readDatabase(cls, database_file): db = cls() CrustDB._read(db, database_file) return db @staticmethod def _getRepositoryDatabase(): from pyrocko import config name = path.basename(db_url) data_path = path.join(config.config().crustdb_dir, name) if not path.exists(data_path): from pyrocko import util util.download_file(db_url, data_path, None, None) return data_path def _read(self, database_file): ''' Reads in the the GSN database file and puts it in CrustDB. File format: uid lat/lon vp vs hc depth 2 29.76N 2.30 .00 2.00 .00 s 25.70 .10 .00 NAC-CO 5 U 96.31W 3.94 .00 5.30 2.00 s 33.00 MCz 39.00 61C.3 EXC 5.38 .00 12.50 7.30 c 6.92 .00 13.20 19.80 c 8.18 .00 .00 33.00 m 3 34.35N 3.00 .00 3.00 .00 s 35.00 1.60 .00 NAC-BR 4 R 117.83W 6.30 .00 16.50 3.00 38.00 MCz 55.00 63R.1 ORO 7.00 .00 18.50 19.50 7.80 .00 .00 38.00 m :param database_file: path to database file, type string ''' def get_empty_record(): meta = { 'uid': num.nan, 'geographical_location': None, 'geological_province': None, 'geological_age': None, 'elevation': num.nan, 'heatflow': num.nan, 'measurement_method': None, 'publication_reference': None } # vp, vs, h, d, lat, lon, meta return (num.zeros(128, dtype=num.float32), num.zeros(128, dtype=num.float32), num.zeros(128, dtype=num.float32), num.zeros(128, dtype=num.float32), 0., 0., meta) nlayers = [] def add_record(vp, vs, h, d, lat, lon, meta, nlayer): if nlayer == 0: return self.append(VelocityProfile( vp=vp[:nlayer] * km, vs=vs[:nlayer] * km, h=h[:nlayer] * km, d=d[:nlayer] * km, lat=lat, lon=lon, **meta)) nlayers.append(nlayer) vp, vs, h, d, lat, lon, meta = get_empty_record() ilayer = 0 with open(database_file, 'r') as database: for line, dbline in enumerate(database): if dbline.isspace(): if not len(d) == 0: add_record(vp, vs, h, d, lat, lon, meta, ilayer) if not len(vp) == len(h): raise DatabaseError( 'Inconsistent database, check line %d!\n\tDebug: ' % line, lat, lon, vp, vs, h, d, meta) vp, vs, h, d, lat, lon, meta = get_empty_record() ilayer = 0 else: try: if ilayer == 0: lat = float(dbline[8:13]) if dbline[13] == b'S': lat = -lat # Additional meta data meta['uid'] = int(dbline[0:6]) meta['elevation'] = float(dbline[52:57]) meta['heatflow'] = float(dbline[58:64]) if meta['heatflow'] == 0.: meta['heatflow'] = None meta['geographical_location'] =\ dbline[66:72].strip() meta['measurement_method'] = dbline[77] if ilayer == 1: lon = float(dbline[7:13]) if dbline[13] == b'W': lon = -lon # Additional meta data meta['geological_age'] = dbline[54:58].strip() meta['publication_reference'] =\ dbline[66:72].strip() meta['geological_province'] = dbline[74:78].strip() try: vp[ilayer] = dbline[17:21] vs[ilayer] = dbline[23:27] h[ilayer] = dbline[28:34] d[ilayer] = dbline[35:41] except ValueError: pass except ValueError: logger.warning( 'Could not interpret line %d, skipping\n%s' % (line, dbline)) while not database.readlines(): pass vp, vs, h, d, lat, lon, meta = get_empty_record() ilayer += 1 # Append last profile add_record(vp, vs, h, d, lat, lon, meta, ilayer) logger.info('Loaded %d profiles from %s' % (self.nprofiles, database_file))