def cartesian_square_centred_on_point(self, point, distance, **kwargs): ''' Select earthquakes from within a square centered on a point :param point: Centre point as instance of nhlib.geo.point.Point class :param distance: Distance (km) :returns: Instance of :class:`openquake.hmtk.seismicity.catalogue.Catalogue` class containing only selected events ''' point_surface = Point(point.longitude, point.latitude, 0.) # As distance is north_point = point_surface.point_at(distance, 0., 0.) east_point = point_surface.point_at(distance, 0., 90.) south_point = point_surface.point_at(distance, 0., 180.) west_point = point_surface.point_at(distance, 0., 270.) is_long = np.logical_and( self.catalogue.data['longitude'] >= west_point.longitude, self.catalogue.data['longitude'] < east_point.longitude) is_surface = np.logical_and( is_long, self.catalogue.data['latitude'] >= south_point.latitude, self.catalogue.data['latitude'] < north_point.latitude) upper_depth, lower_depth = _check_depth_limits(kwargs) is_valid = np.logical_and(is_surface, self.catalogue.data['depth'] >= upper_depth, self.catalogue.data['depth'] < lower_depth) return self.select_catalogue(is_valid)
def cartesian_square_centred_on_point(self, point, distance, **kwargs): ''' Select earthquakes from within a square centered on a point :param point: Centre point as instance of nhlib.geo.point.Point class :param distance: Distance (km) :returns: Instance of :class:`openquake.hmtk.seismicity.catalogue.Catalogue` class containing only selected events ''' point_surface = Point(point.longitude, point.latitude, 0.) # As distance is north_point = point_surface.point_at(distance, 0., 0.) east_point = point_surface.point_at(distance, 0., 90.) south_point = point_surface.point_at(distance, 0., 180.) west_point = point_surface.point_at(distance, 0., 270.) is_long = np.logical_and( self.catalogue.data['longitude'] >= west_point.longitude, self.catalogue.data['longitude'] < east_point.longitude) is_surface = np.logical_and( is_long, self.catalogue.data['latitude'] >= south_point.latitude, self.catalogue.data['latitude'] < north_point.latitude) upper_depth, lower_depth = _check_depth_limits(kwargs) is_valid = np.logical_and( is_surface, self.catalogue.data['depth'] >= upper_depth, self.catalogue.data['depth'] < lower_depth) return self.select_catalogue(is_valid)
def build_planar_surface(geometry): """ Builds the planar rupture surface from the openquake.nrmllib.models instance """ # Read geometry from wkt geom = wkt.loads(geometry.wkt) top_left = Point(geom.xy[0][0], geom.xy[1][0], geometry.upper_seismo_depth) top_right = Point(geom.xy[0][1], geom.xy[1][1], geometry.upper_seismo_depth) strike = top_left.azimuth(top_right) dip_dir = (strike + 90.) % 360. depth_diff = geometry.lower_seismo_depth - geometry.upper_seismo_depth bottom_right = top_right.point_at( depth_diff / np.tan(geometry.dip * (np.pi / 180.)), depth_diff, dip_dir) bottom_left = top_left.point_at( depth_diff / np.tan(geometry.dip * (np.pi / 180.)), depth_diff, dip_dir) return PlanarSurface(1.0, strike, geometry.dip, top_left, top_right, bottom_right, bottom_left)
def setUp(self): ''' ''' self.fault = None self.regionalisation = None self.msr = [(WC1994(), 1.0)] self.msr_sigma = [(-1.5, 0.15), (0.0, 0.7), (1.5, 0.15)] self.shear_mod = [(30.0, 0.8), (35.0, 0.2)] self.dlr = [(1.25E-5, 1.0)] self.config = [{}] self.slip = [(10.0, 1.0)] x0 = Point(30., 30., 0.) x1 = x0.point_at(30., 0., 30.) x2 = x1.point_at(30., 0., 60.) # Total length is 60 km self.trace = Line([x0, x1, x2]) self.dip = 90. self.upper_depth = 0. self.lower_depth = 20. self.simple_fault = SimpleFaultGeometry(self.trace, self.dip, self.upper_depth, self.lower_depth) # Creates a trace ~60 km long made of 3 points upper_edge = Line([x0, x1, x2]) lower_edge = Line([x0.point_at(40., 20., 130.), x1.point_at(42., 25., 130.), x2.point_at(41., 22., 130.)]) self.complex_fault = ComplexFaultGeometry([upper_edge, lower_edge], 2.0)
def _rup_to_point(distance, surface, origin, azimuth, distance_type='rjb', iter_stop=1E-5, maxiter=1000): """ """ pt0 = origin pt1 = origin.point_at(distance, 0., azimuth) r_diff = np.inf iterval = 0 while (np.fabs(r_diff) >= iter_stop) and (iterval <= maxiter): pt1mesh = Mesh(np.array([pt1.longitude]), np.array([pt1.latitude]), None) if distance_type == 'rjb': r_diff = distance - surface.get_joyner_boore_distance(pt1mesh) elif distance_type == 'rrup': r_diff = distance - surface.get_min_distance(pt1mesh) else: raise ValueError('Distance type must be rrup or rjb!') pt0 = Point(pt1.longitude, pt1.latitude) if r_diff > 0.: pt1 = pt0.point_at(r_diff, 0., azimuth) else: pt1 = pt0.point_at(r_diff, 0., (azimuth + 180.) % 360.) return pt1
def setUp(self): ''' Creates a complex fault typology ''' x0 = Point(30., 30., 0.) x1 = x0.point_at(30., 0., 30.) x2 = x1.point_at(30., 0., 60.) upper_edge = Line([x0, x1, x2]) lower_edge = Line([x0.point_at(40., 20., 130.), x1.point_at(42., 25., 130.), x2.point_at(41., 22., 130.)]) self.edges = [upper_edge, lower_edge] self.fault = None
def _rup_to_point(distance, surface, origin, azimuth, distance_type='rjb', iter_stop=1E-3, maxiter=1000): """ """ pt0 = origin pt1 = origin.point_at(distance, 0., azimuth) print pt0, pt1 r_diff = np.inf dip = surface.dip sin_dip = np.sin(np.radians(dip)) dist_sin_dip = distance / sin_dip #max_surf_dist = surface.width / np.cos(np.radians(dip)) iterval = 0 while (np.fabs(r_diff) >= iter_stop) and (iterval <= maxiter): pt1mesh = Mesh(np.array([pt1.longitude]), np.array([pt1.latitude]), None) if distance_type == 'rjb' or np.fabs(dip - 90.0) < 1.0E-3: r_diff = (distance - surface.get_joyner_boore_distance(pt1mesh)).flatten() pt0 = Point(pt1.longitude, pt1.latitude) if r_diff > 0.: pt1 = pt0.point_at(r_diff, 0., azimuth) else: pt1 = pt0.point_at(np.fabs(r_diff), 0., (azimuth + 180.) % 360.) elif distance_type == 'rrup': rrup = surface.get_min_distance(pt1mesh).flatten() if azimuth >= 0.0 and azimuth <= 180.0: # On hanging wall r_diff = dist_sin_dip - (rrup / sin_dip) else: # On foot wall r_diff = distance - rrup pt0 = Point(pt1.longitude, pt1.latitude) #print azimuth, (azimuth + 180.0) % 360, rrup, r_diff, np.fabs(r_diff) if r_diff > 0.: pt1 = pt0.point_at(r_diff, 0., azimuth) else: pt1 = pt0.point_at(np.fabs(r_diff), 0., (azimuth + 180.) % 360.) else: raise ValueError('Distance type must be rrup or rjb!') iterval += 1 return pt1
def check_surface_validity(cls, edges): """ Check validity of the surface. Project edge points to vertical plane anchored to surface upper left edge and with strike equal to top edge strike. Check that resulting polygon is valid. This method doesn't have to be called by hands before creating the surface object, because it is called from :meth:`from_fault_data`. """ # extract coordinates of surface boundary (as defined from edges) full_boundary = [] left_boundary = [] right_boundary = [] for i in range(1, len(edges) - 1): left_boundary.append(edges[i].points[0]) right_boundary.append(edges[i].points[-1]) full_boundary.extend(edges[0].points) full_boundary.extend(right_boundary) full_boundary.extend(edges[-1].points[::-1]) full_boundary.extend(left_boundary[::-1]) lons = [p.longitude for p in full_boundary] lats = [p.latitude for p in full_boundary] depths = [p.depth for p in full_boundary] # define reference plane. Corner points are separated by an arbitrary # distance of 10 km. The mesh spacing is set to 2 km. Both corner # distance and mesh spacing values do not affect the algorithm results. ul = edges[0].points[0] strike = ul.azimuth(edges[0].points[-1]) dist = 10. mesh_spacing = 2. ur = ul.point_at(dist, 0, strike) bl = Point(ul.longitude, ul.latitude, ul.depth + dist) br = bl.point_at(dist, 0, strike) # project surface boundary to reference plane and check for # validity. ref_plane = PlanarSurface.from_corner_points( mesh_spacing, ul, ur, br, bl ) _, xx, yy = ref_plane._project(lons, lats, depths) coords = [(x, y) for x, y in zip(xx, yy)] p = shapely.geometry.Polygon(coords) if not p.is_valid: raise ValueError('Edges points are not in the right order')
def check_surface_validity(cls, edges): """ Check validity of the surface. Project edge points to vertical plane anchored to surface upper left edge and with strike equal to top edge strike. Check that resulting polygon is valid. This method doesn't have to be called by hands before creating the surface object, because it is called from :meth:`from_fault_data`. """ # extract coordinates of surface boundary (as defined from edges) full_boundary = [] left_boundary = [] right_boundary = [] for i in range(1, len(edges) - 1): left_boundary.append(edges[i].points[0]) right_boundary.append(edges[i].points[-1]) full_boundary.extend(edges[0].points) full_boundary.extend(right_boundary) full_boundary.extend(edges[-1].points[::-1]) full_boundary.extend(left_boundary[::-1]) lons = [p.longitude for p in full_boundary] lats = [p.latitude for p in full_boundary] depths = [p.depth for p in full_boundary] # define reference plane. Corner points are separated by an arbitrary # distance of 10 km. The mesh spacing is set to 2 km. Both corner # distance and mesh spacing values do not affect the algorithm results. ul = edges[0].points[0] strike = ul.azimuth(edges[0].points[-1]) dist = 10. mesh_spacing = 2. ur = ul.point_at(dist, 0, strike) bl = Point(ul.longitude, ul.latitude, ul.depth + dist) br = bl.point_at(dist, 0, strike) # project surface boundary to reference plane and check for # validity. ref_plane = PlanarSurface.from_corner_points(mesh_spacing, ul, ur, br, bl) _, xx, yy = ref_plane._project(lons, lats, depths) coords = [(x, y) for x, y in zip(xx, yy)] p = shapely.geometry.Polygon(coords) if not p.is_valid: raise ValueError('Edges points are not in the right order')
def setUp(self): ''' Create a simple fault of known length and downdip width ''' # Creates a trace ~60 km long made of 3 points x0 = Point(30., 30., 0.) x1 = x0.point_at(30., 0., 30.) x2 = x1.point_at(30., 0., 60.) # Total length is 60 km self.trace = Line([x0, x1, x2]) self.dip = 90. # Simple Vertical Strike-Slip fault # Total downdip width = 20. km self.upper_depth = 0. self.lower_depth = 20. self.fault = None
def test_build_fault_model(self): # Tests the constuction of a fault model with two faults (1 simple, # 1 complex) each with two mfd rates - should produce four sources self.model = mtkActiveFaultModel('001', 'A Fault Model', faults=[]) x0 = Point(30., 30., 0.) x1 = x0.point_at(30., 0., 30.) x2 = x1.point_at(30., 0., 60.) # Total length is 60 km trace = Line([x0, x1, x2]) simple_fault = SimpleFaultGeometry(trace, 90., 0., 20.) # Creates a trace ~60 km long made of 3 points upper_edge = Line([x0, x1, x2]) lower_edge = Line([ x0.point_at(40., 20., 130.), x1.point_at(42., 25., 130.), x2.point_at(41., 22., 130.) ]) complex_fault = ComplexFaultGeometry([upper_edge, lower_edge], 2.0) config = [{ 'MFD_spacing': 0.1, 'Maximum_Magnitude': 7.0, 'Maximum_Uncertainty': None, 'Model_Name': 'Characteristic', 'Model_Weight': 0.5, 'Sigma': 0.1, 'Lower_Bound': -1., 'Upper_Bound': 1. }, { 'MFD_spacing': 0.1, 'Maximum_Magnitude': 7.5, 'Maximum_Uncertainty': None, 'Model_Name': 'Characteristic', 'Model_Weight': 0.5, 'Sigma': 0.1, 'Lower_Bound': -1., 'Upper_Bound': 1. }] fault1 = mtkActiveFault('001', 'Simple Fault 1', simple_fault, [(10.0, 1.0)], -90., None, aspect_ratio=1.0, scale_rel=[(WC1994(), 1.0)], shear_modulus=[(30.0, 1.0)], disp_length_ratio=[(1E-5, 1.0)]) fault1.generate_config_set(config) fault2 = mtkActiveFault('002', 'Complex Fault 1', complex_fault, [(10.0, 1.0)], -90., None, aspect_ratio=1.0, scale_rel=[(WC1994(), 1.0)], shear_modulus=[(30.0, 1.0)], disp_length_ratio=[(1E-5, 1.0)]) fault2.generate_config_set(config) self.model.faults = [fault1, fault2] # Generate source model self.model.build_fault_model() self.assertEqual(len(self.model.source_model.sources), 4) # First source should be an instance of a mtkSimpleFaultSource model1 = self.model.source_model.sources[0] self.assertTrue(isinstance(model1, mtkSimpleFaultSource)) self.assertEqual(model1.id, '001_1') self.assertAlmostEqual(model1.mfd.min_mag, 6.9) np.testing.assert_array_almost_equal( np.log10(np.array(model1.mfd.occurrence_rates)), np.array([-2.95320041, -2.54583708, -2.953200413])) # Second source should be an instance of a mtkSimpleFaultSource model2 = self.model.source_model.sources[1] self.assertTrue(isinstance(model2, mtkSimpleFaultSource)) self.assertEqual(model2.id, '001_2') self.assertAlmostEqual(model2.mfd.min_mag, 7.4) np.testing.assert_array_almost_equal( np.log10(np.array(model2.mfd.occurrence_rates)), np.array([-3.70320041, -3.29583708, -3.70320041])) # Third source should be an instance of a mtkComplexFaultSource model3 = self.model.source_model.sources[2] self.assertTrue(isinstance(model3, mtkComplexFaultSource)) self.assertEqual(model3.id, '002_1') self.assertAlmostEqual(model3.mfd.min_mag, 6.9) np.testing.assert_array_almost_equal( np.log10(np.array(model3.mfd.occurrence_rates)), np.array([-2.59033387, -2.18297054, -2.59033387])) # Fourth source should be an instance of a mtkComplexFaultSource model4 = self.model.source_model.sources[3] self.assertTrue(isinstance(model4, mtkComplexFaultSource)) self.assertEqual(model4.id, '002_2') self.assertAlmostEqual(model4.mfd.min_mag, 7.4) np.testing.assert_array_almost_equal( np.log10(np.array(model4.mfd.occurrence_rates)), np.array([-3.34033387, -2.93297054, -3.34033387]))
def test_build_fault_model(self): # Tests the constuction of a fault model with two faults (1 simple, # 1 complex) each with two mfd rates - should produce four sources self.model = mtkActiveFaultModel('001', 'A Fault Model', faults=[]) x0 = Point(30., 30., 0.) x1 = x0.point_at(30., 0., 30.) x2 = x1.point_at(30., 0., 60.) # Total length is 60 km trace = Line([x0, x1, x2]) simple_fault = SimpleFaultGeometry(trace, 90., 0., 20.) # Creates a trace ~60 km long made of 3 points upper_edge = Line([x0, x1, x2]) lower_edge = Line( [x0.point_at(40., 20., 130.), x1.point_at(42., 25., 130.), x2.point_at(41., 22., 130.)]) complex_fault = ComplexFaultGeometry([upper_edge, lower_edge], 2.0) config = [{'MFD_spacing': 0.1, 'Maximum_Magnitude': 7.0, 'Maximum_Uncertainty': None, 'Model_Name': 'Characteristic', 'Model_Weight': 0.5, 'Sigma': 0.1, 'Lower_Bound': -1., 'Upper_Bound': 1.}, {'MFD_spacing': 0.1, 'Maximum_Magnitude': 7.5, 'Maximum_Uncertainty': None, 'Model_Name': 'Characteristic', 'Model_Weight': 0.5, 'Sigma': 0.1, 'Lower_Bound': -1., 'Upper_Bound': 1.}] fault1 = mtkActiveFault('001', 'Simple Fault 1', simple_fault, [(10.0, 1.0)], -90., None, aspect_ratio=1.0, scale_rel=[(WC1994(), 1.0)], shear_modulus=[(30.0, 1.0)], disp_length_ratio=[(1E-5, 1.0)]) fault1.generate_config_set(config) fault2 = mtkActiveFault('002', 'Complex Fault 1', complex_fault, [(10.0, 1.0)], -90., None, aspect_ratio=1.0, scale_rel=[(WC1994(), 1.0)], shear_modulus=[(30.0, 1.0)], disp_length_ratio=[(1E-5, 1.0)]) fault2.generate_config_set(config) self.model.faults = [fault1, fault2] # Generate source model self.model.build_fault_model() self.assertEqual(len(self.model.source_model.sources), 4) # First source should be an instance of a mtkSimpleFaultSource model1 = self.model.source_model.sources[0] self.assertTrue(isinstance(model1, mtkSimpleFaultSource)) self.assertEqual(model1.id, '001_1') self.assertAlmostEqual(model1.mfd.min_mag, 6.9) np.testing.assert_array_almost_equal( np.log10(np.array(model1.mfd.occurrence_rates)), np.array([-2.95320041, -2.54583708, -2.953200413])) # Second source should be an instance of a mtkSimpleFaultSource model2 = self.model.source_model.sources[1] self.assertTrue(isinstance(model2, mtkSimpleFaultSource)) self.assertEqual(model2.id, '001_2') self.assertAlmostEqual(model2.mfd.min_mag, 7.4) np.testing.assert_array_almost_equal( np.log10(np.array(model2.mfd.occurrence_rates)), np.array([-3.70320041, -3.29583708, -3.70320041])) # Third source should be an instance of a mtkComplexFaultSource model3 = self.model.source_model.sources[2] self.assertTrue(isinstance(model3, mtkComplexFaultSource)) self.assertEqual(model3.id, '002_1') self.assertAlmostEqual(model3.mfd.min_mag, 6.9) np.testing.assert_array_almost_equal( np.log10(np.array(model3.mfd.occurrence_rates)), np.array([-2.59033387, -2.18297054, -2.59033387])) # Fourth source should be an instance of a mtkComplexFaultSource model4 = self.model.source_model.sources[3] self.assertTrue(isinstance(model4, mtkComplexFaultSource)) self.assertEqual(model4.id, '002_2') self.assertAlmostEqual(model4.mfd.min_mag, 7.4) np.testing.assert_array_almost_equal( np.log10(np.array(model4.mfd.occurrence_rates)), np.array([-3.34033387, -2.93297054, -3.34033387]))