def __init__(self, source_id, name, tectonic_region_type, mfd, rupture_mesh_spacing, magnitude_scaling_relationship, rupture_aspect_ratio, # simple fault specific parameters upper_seismogenic_depth, lower_seismogenic_depth, fault_trace, dip, rake): super(SimpleFaultSource, self).__init__( source_id, name, tectonic_region_type, mfd, rupture_mesh_spacing, magnitude_scaling_relationship, rupture_aspect_ratio ) NodalPlane.check_rake(rake) SimpleFaultSurface.check_fault_data( fault_trace, upper_seismogenic_depth, lower_seismogenic_depth, dip, rupture_mesh_spacing ) self.fault_trace = fault_trace self.upper_seismogenic_depth = upper_seismogenic_depth self.lower_seismogenic_depth = lower_seismogenic_depth self.dip = dip self.rake = rake min_mag = self.mfd.get_min_mag() cols_rows = self._get_rupture_dimensions(float('inf'), float('inf'), min_mag) if 1 in cols_rows: raise ValueError('mesh spacing %s is too low to represent ' 'ruptures of magnitude %s' % (rupture_mesh_spacing, min_mag))
def test_upper_seismo_depth_range(self): self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, -0.1, 10.0, 90.0, 1.0) SimpleFaultSurface.check_fault_data(self.fault_trace, 0.0, 1.0, 90.0, 1.0) SimpleFaultSurface.check_fault_data(self.fault_trace, 1.0, 1.1, 90.0, 1.0)
def test_mesh_spacing_range(self): SimpleFaultSurface.check_fault_data(self.fault_trace, 0.0, 1.0, 90.0, 1.0) self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, 0.0, 1.0, 90.0, 0.0) self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, 0.0, 1.0, 90.0, -1.0)
def test_dip_inside_range(self): self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, 0.0, 1.0, 0.0, 1.0) self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, 0.0, 1.0, -0.1, 1.0) self.assertRaises(ValueError, SimpleFaultSurface.check_fault_data, self.fault_trace, 0.0, 1.0, 90.1, 1.0) SimpleFaultSurface.check_fault_data(self.fault_trace, 0.0, 1.0, 0.1, 1.0) SimpleFaultSurface.check_fault_data(self.fault_trace, 0.0, 1.0, 90.0, 1.0)
def test_get_strike_1(self): p1 = Point(0.0, 0.0) p2 = Point(0.0635916966572, 0.0635916574897) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2]), 1.0, 6.0, 89.9, 1.0) self.assertAlmostEquals(45.0, surface.get_strike(), delta=1e-4)
def test_inclined_non_planar_surface(self): p1 = Point(0.1, 0.1, 0.0) p2 = Point(0.1, 0.117986432118, 0.0) p3 = Point(0.117986470254, 0.117986426305, 0.0) p4 = Point(0.117986470254, 0.0999999941864, 0.0) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3, p4]), 2.0, 4.0, 30.0, 1.0) self.assertAlmostEqual(surface.get_width(), 4.0, places=2)
def test_get_dip_2(self): p1 = Point(0.0, 0.0) p2 = Point(0.0635916966572, 0.0635916574897) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2]), 1.0, 6.0, 30.0, 1.0) self.assertAlmostEquals(30.0, surface.get_dip(), 1)
def test_get_dip_1(self): p1 = Point(0.0, 0.0) p2 = Point(0.0635916966572, 0.0635916574897) p3 = Point(0.0860747816618, 0.102533437776) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3]), 1.0, 6.0, 90.0, 1.0) self.assertAlmostEquals(90.0, surface.get_dip(), delta=1e-6)
def test_get_mesh_5(self): p1 = Point(179.9, 0.0) p2 = Point(180.0, 0.0) p3 = Point(-179.9, 0.0) fault = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3]), 1.0, 6.0, 90.0, 1.0) self.assert_mesh_is(fault, test_data.TEST_5_MESH)
def test_dip_90_three_points(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(1, -20), Point(1, -20.2), Point(2, -19.7)]), dip=90, upper_seismogenic_depth=30, lower_seismogenic_depth=50, ) elons = [1, 1, 2] elats = [-20.2, -20., -19.7] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def test_get_mesh_4(self): p1 = Point(0.0, 0.0, 0.0) p2 = Point(0.0, 0.0359728811759, 0.0) p3 = Point(0.0190775080917, 0.0550503815182, 0.0) p4 = Point(0.03974514139, 0.0723925718856, 0.0) fault = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3, p4]), 0.0, 4.0, 90.0, 1.0) self.assert_mesh_is(fault, test_data.TEST_4_MESH)
def test_get_mesh_1(self): p1 = Point(0.0, 0.0, 0.0) p2 = Point(0.0, 0.0359728811759, 0.0) p3 = Point(0.0190775080917, 0.0550503815182, 0.0) p4 = Point(0.03974514139, 0.0723925718856, 0.0) fault = SimpleFaultSurface.from_fault_data( Line([p1, p2, p3, p4]), 0.0, 4.2426406871192848, 45.0, 1.0) self.assert_mesh_is(fault, test_data.TEST_1_MESH)
def test_dip_90_self_intersection(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(1, -2), Point(2, -1.9), Point(3, -2.1), Point(4, -2)]), dip=90, upper_seismogenic_depth=10, lower_seismogenic_depth=20, ) elons = [3., 1., 2., 4.] elats = [-2.1, -2., -1.9, -2.] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def __init__( self, identifier, name, tectonic_region, aspect_ratio, fault_trace, dip, upper_depth, lower_depth, mesh_spacing=1.0, ): """Instantiate class with just the basic attributes :param identifier: Integer ID code for the source :param name: Source Name (string) :param tectonic_region: Tectonic Region Type (String) :param aspect_ratio: Ratio of along-strike length to down-dip width (float) :param fault_trace: Surface trace of the fault [Long., Lat] as np.ndarray :param dip: Dip of fault (degrees) (Float between 0 and 90) :param upper_depth: Upper seismogenic depth (km) (Non-negative Float) :param lower_depth: Lower Seismogenic depth (km) (Non-negative Float) :param mesh_spacing: Spacing of mesh (km) (Non-negative Float) """ self.typology = "SimpleFault" self.source_id = identifier self.name = name self.tectonic_region_type = tectonic_region self.aspect_ratio = aspect_ratio self.mfd = None self.msr = None if upper_depth < 0.0: raise ValueError("Upper Depth Must be Non Negative") if lower_depth < 0.0 or (lower_depth < upper_depth): raise ValueError("Lower Depth must be Non-Negative and > Upper depth") self.trace = [] if np.shape(fault_trace)[1] < 3: depth = np.zeros(np.shape(fault_trace)[0], dtype=float) else: depth = fault_trace[:, 2] for iloc, node in enumerate(fault_trace): self.trace.append(Point(node[0], node[1], depth[iloc])) self.trace = Line(self.trace) self.geometry = SimpleFaultSurface.from_fault_data( self.trace, self.upper_depth, self.lower_depth, self.dip, mesh_spacing )
def test_dip_90_two_points(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(2, 2), Point(1, 1)]), dip=90, upper_seismogenic_depth=10, lower_seismogenic_depth=20, ) elons = [1.00003181, 0.99996821, 0.99996819, 1.99996819, 2.00003182, 2.00003181] elats = [0.99996822, 0.99996819, 1.00003178, 2.0000318, 2.0000318, 1.9999682] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def test_three_points(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(10, -20), Point(11, -20.2), Point(12, -19.7)]), dip=30, upper_seismogenic_depth=25.3, lower_seismogenic_depth=53.6, ) elons = [11.13560807, 10.1354272, 10.06374285, 12.06361991, 12.13515987] elats = [-21.02520738, -20.82520794, -20.3895235, -20.08952368, -20.52520878] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def test_get_mesh_2(self): p1 = Point(0.0, 0.0, 0.0) p2 = Point(0.0, 0.0359728811759, 0.0) p3 = Point(0.0190775080917, 0.0550503815182, 0.0) p4 = Point(0.03974514139, 0.0723925718856, 0.0) fault = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3, p4]), 2.12132034356, 4.2426406871192848, 45.0, 1.0) self.assert_mesh_is(fault, test_data.TEST_2_MESH)
def __init__( self, source_id, name, tectonic_region_type, mfd, rupture_mesh_spacing, magnitude_scaling_relationship, rupture_aspect_ratio, # simple fault specific parameters upper_seismogenic_depth, lower_seismogenic_depth, fault_trace, dip, rake): super(SimpleFaultSource, self).__init__(source_id, name, tectonic_region_type, mfd, rupture_mesh_spacing, magnitude_scaling_relationship, rupture_aspect_ratio) NodalPlane.check_rake(rake) SimpleFaultSurface.check_fault_data(fault_trace, upper_seismogenic_depth, lower_seismogenic_depth, dip, rupture_mesh_spacing) self.fault_trace = fault_trace self.upper_seismogenic_depth = upper_seismogenic_depth self.lower_seismogenic_depth = lower_seismogenic_depth self.dip = dip self.rake = rake min_mag = self.mfd.get_min_mag() cols_rows = self._get_rupture_dimensions(float('inf'), float('inf'), min_mag) if 1 in cols_rows: raise ValueError('mesh spacing %s is too low to represent ' 'ruptures of magnitude %s' % (rupture_mesh_spacing, min_mag))
def iter_ruptures(self, temporal_occurrence_model): """ See :meth:`nhlib.source.base.SeismicSource.iter_ruptures`. Generates a ruptures using the "floating" algorithm: for all the magnitude values of assigned MFD calculates the rupture size with respect to MSR and aspect ratio and then places ruptures of that size on the surface of the whole fault source. The occurrence rate of each of those ruptures is the magnitude occurrence rate divided by the number of ruptures that can be placed in a fault. """ whole_fault_surface = SimpleFaultSurface.from_fault_data( self.fault_trace, self.upper_seismogenic_depth, self.lower_seismogenic_depth, self.dip, self.rupture_mesh_spacing) whole_fault_mesh = whole_fault_surface.get_mesh() mesh_rows, mesh_cols = whole_fault_mesh.shape fault_length = float((mesh_cols - 1) * self.rupture_mesh_spacing) fault_width = float((mesh_rows - 1) * self.rupture_mesh_spacing) for (mag, mag_occ_rate) in self.get_annual_occurrence_rates(): rup_cols, rup_rows = self._get_rupture_dimensions( fault_length, fault_width, mag) num_rup_along_length = mesh_cols - rup_cols + 1 num_rup_along_width = mesh_rows - rup_rows + 1 num_rup = num_rup_along_length * num_rup_along_width occurrence_rate = mag_occ_rate / float(num_rup) for first_row in xrange(num_rup_along_width): for first_col in xrange(num_rup_along_length): mesh = whole_fault_mesh[first_row:first_row + rup_rows, first_col:first_col + rup_cols] hypocenter = mesh.get_middle_point() surface = SimpleFaultSurface(mesh) yield ProbabilisticRupture(mag, self.rake, self.tectonic_region_type, hypocenter, surface, type(self), occurrence_rate, temporal_occurrence_model)
def test_dip_90_two_points(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(2, 2), Point(1, 1)]), dip=90, upper_seismogenic_depth=10, lower_seismogenic_depth=20, ) elons = [ 1.00003181, 0.99996821, 0.99996819, 1.99996819, 2.00003182, 2.00003181 ] elats = [ 0.99996822, 0.99996819, 1.00003178, 2.0000318, 2.0000318, 1.9999682 ] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def _make_source(self, *args, **kwargs): source = super(ComplexFaultSourceSimpleGeometryIterRupturesTestCase, self)._make_source(*args, **kwargs) surface = SimpleFaultSurface.from_fault_data( source.fault_trace, source.upper_seismogenic_depth, source.lower_seismogenic_depth, source.dip, source.rupture_mesh_spacing) mesh = surface.get_mesh() top_edge = Line(list(mesh[0:1])) bottom_edge = Line(list(mesh[-1:])) return ComplexFaultSource(source.source_id, source.name, source.tectonic_region_type, source.mfd, source.rupture_mesh_spacing, source.magnitude_scaling_relationship, source.rupture_aspect_ratio, [top_edge, bottom_edge], source.rake)
def test_three_points(self): polygon = SimpleFaultSurface.surface_projection_from_fault_data( Line([Point(10, -20), Point(11, -20.2), Point(12, -19.7)]), dip=30, upper_seismogenic_depth=25.3, lower_seismogenic_depth=53.6, ) elons = [ 11.13560807, 10.1354272, 10.06374285, 12.06361991, 12.13515987 ] elats = [ -21.02520738, -20.82520794, -20.3895235, -20.08952368, -20.52520878 ] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def get_rupture_enclosing_polygon(self, dilation=0): """ Uses :meth:`nhlib.geo.surface.simple_fault.SimpleFaultSurface.surface_projection_from_fault_data` for getting the fault's surface projection and then calls its :meth:`~nhlib.geo.polygon.Polygon.dilate` method passing in ``dilation`` parameter. See :meth:`superclass method <nhlib.source.base.SeismicSource.get_rupture_enclosing_polygon>` for parameter and return value definition. """ polygon = SimpleFaultSurface.surface_projection_from_fault_data( self.fault_trace, self.upper_seismogenic_depth, self.lower_seismogenic_depth, self.dip) if dilation: return polygon.dilate(dilation) else: return polygon
def get_rupture_enclosing_polygon(self, dilation=0): """ Uses :meth:`nhlib.geo.surface.simple_fault.SimpleFaultSurface.surface_projection_from_fault_data` for getting the fault's surface projection and then calls its :meth:`~nhlib.geo.polygon.Polygon.dilate` method passing in ``dilation`` parameter. See :meth:`superclass method <nhlib.source.base.SeismicSource.get_rupture_enclosing_polygon>` for parameter and return value definition. """ polygon = SimpleFaultSurface.surface_projection_from_fault_data( self.fault_trace, self.upper_seismogenic_depth, self.lower_seismogenic_depth, self.dip ) if dilation: return polygon.dilate(dilation) else: return polygon
def iter_ruptures(self, temporal_occurrence_model): """ See :meth:`nhlib.source.base.SeismicSource.iter_ruptures`. Generates a ruptures using the "floating" algorithm: for all the magnitude values of assigned MFD calculates the rupture size with respect to MSR and aspect ratio and then places ruptures of that size on the surface of the whole fault source. The occurrence rate of each of those ruptures is the magnitude occurrence rate divided by the number of ruptures that can be placed in a fault. """ whole_fault_surface = SimpleFaultSurface.from_fault_data( self.fault_trace, self.upper_seismogenic_depth, self.lower_seismogenic_depth, self.dip, self.rupture_mesh_spacing ) whole_fault_mesh = whole_fault_surface.get_mesh() mesh_rows, mesh_cols = whole_fault_mesh.shape fault_length = float((mesh_cols - 1) * self.rupture_mesh_spacing) fault_width = float((mesh_rows - 1) * self.rupture_mesh_spacing) for (mag, mag_occ_rate) in self.get_annual_occurrence_rates(): rup_cols, rup_rows = self._get_rupture_dimensions( fault_length, fault_width, mag ) num_rup_along_length = mesh_cols - rup_cols + 1 num_rup_along_width = mesh_rows - rup_rows + 1 num_rup = num_rup_along_length * num_rup_along_width occurrence_rate = mag_occ_rate / float(num_rup) for first_row in xrange(num_rup_along_width): for first_col in xrange(num_rup_along_length): mesh = whole_fault_mesh[first_row : first_row + rup_rows, first_col : first_col + rup_cols] hypocenter = mesh.get_middle_point() surface = SimpleFaultSurface(mesh) yield ProbabilisticRupture( mag, self.rake, self.tectonic_region_type, hypocenter, surface, type(self), occurrence_rate, temporal_occurrence_model )
def test_get_dip_5(self): p1 = Point(0.0, 0.0) p2 = Point(0.0, 0.0899322029395) p3 = Point(0.0899323137217, 0.0899320921571) p4 = Point(0.0899323137217, -1.10782376538e-07) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3, p4]), 0.0, 10.0, 45.0, 1.0) # fault contains three segments. the one in the middle is inclined # with dip of 45 degrees. other two are purely vertical (they are # perpendicular to the middle one). the middle segment is rectangle # and the side ones are parallelograms. area of those parallelograms # is area of middle segment times sine of their acute angle. mid_area = 1.0 mid_dip = pi / 4 # 45 degree side_area = sin(mid_dip) * mid_area side_dip = pi / 2 # 90 degree expected_dip = degrees( atan2((mid_area * sin(mid_dip) + 2 * (side_area * sin(side_dip))) / 3.0, (mid_area * cos(mid_dip) + 2 * (side_area * cos(side_dip))) / 3.0)) self.assertAlmostEquals(surface.get_dip(), expected_dip, delta=1e-3)
def test_get_dip_5(self): p1 = Point(0.0, 0.0) p2 = Point(0.0, 0.0899322029395) p3 = Point(0.0899323137217, 0.0899320921571) p4 = Point(0.0899323137217, -1.10782376538e-07) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2, p3, p4]), 0.0, 10.0, 45.0, 1.0) # fault contains three segments. the one in the middle is inclined # with dip of 45 degrees. other two are purely vertical (they are # perpendicular to the middle one). the middle segment is rectangle # and the side ones are parallelograms. area of those parallelograms # is area of middle segment times sine of their acute angle. mid_area = 1.0 mid_dip = pi / 4 # 45 degree side_area = sin(mid_dip) * mid_area side_dip = pi / 2 # 90 degree expected_dip = degrees(atan2( (mid_area * sin(mid_dip) + 2 * (side_area * sin(side_dip))) / 3.0, (mid_area * cos(mid_dip) + 2 * (side_area * cos(side_dip))) / 3.0 )) self.assertAlmostEquals(surface.get_dip(), expected_dip, delta=1e-3)
def test_vertical_planar_surface(self): p1 = Point(0.1, 0.1, 0.0) p2 = Point(0.1, 0.126979648178, 0.0) surface = SimpleFaultSurface.from_fault_data(Line([p1, p2]), 2.0, 4.0, 90.0, 1.0) self.assertAlmostEqual(surface.get_width(), 2.0)
def test_get_strike_along_meridian(self): line = Line([Point(0, 0), Point(1e-5, 1e-3), Point(0, 2e-3)]) surface = SimpleFaultSurface.from_fault_data(line, 1.0, 6.0, 89.9, 0.1) self.assertAlmostEquals(0, surface.get_strike(), delta=6e-2)