def test_generate_fault_source_model_complex(self): ''' Tests the function to turn fault model into mtkSimpleFault or mtkComplexFault ''' self.fault = mtkActiveFault( '001', 'A Fault', self.complex_fault, [(5.0, 1.0)], 0., None, aseismic=0., msr_sigma=[(0.0, 1.0)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(30., 1.0)], disp_length_ratio=[(1.25E-5, 1.0)]) # Define simple placeholder MFD rec_models = [IncrementalMFD(5.0, 0.1, 1.0 * np.ones(10)), IncrementalMFD(5.0, 0.1, 2.0 * np.ones(10))] self.fault.mfd = (rec_models, [0.5, 0.5], [WC1994(), WC1994()]) # Run model source_model, weights = self.fault.generate_fault_source_model() self.assertEqual(len(source_model), 2) self.assertTrue(isinstance(source_model[0], mtkComplexFaultSource)) for iloc, model in enumerate(source_model): self.assertEqual(model.id, '001') self.assertTrue(isinstance(model.mfd, EvenlyDiscretizedMFD)) np.testing.assert_array_almost_equal(model.mfd.occurrence_rates, rec_models[iloc].occur_rates) self.assertAlmostEqual(weights[iloc], 0.5)
def test_generate_config_set_with_bad_weights(self): ''' Tests that a valueError is raised when the config weights do not sum to 1.0 ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 1.0)], 0., None) bad_config = [{'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 0.5, 'Type': 'First', 'b_value': [0.8, 0.05]}, {'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Maximum_Magnitude_Uncertainty': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'YoungsCoppersmithExponential', 'Model_Weight': 0.3, 'b_value': [0.8, 0.05]}] with self.assertRaises(ValueError) as ae: self.fault.generate_config_set(bad_config) self.assertEqual(ae.exception.message, 'MFD config weights do not sum to 1.0 for fault 001')
def _build_mock_recurrence_branches(self): ''' Given the mock branches return information necessary to define a collapse model ''' # Build test data mags = COLLAPSE_DATA[0, :-1] weights = COLLAPSE_DATA[1:-1, -1] rates = COLLAPSE_DATA[1:-1:, :-1] expected_rate = COLLAPSE_DATA[-1, :-1] test_fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5.0, 1.0)], 0., None) test_fault.mfd_models = [] for (iloc, weight) in enumerate(weights): idx = rates[iloc, :] > 0 model = RecurrenceBranch(None, None, None, None, None, weight=weight) model.recurrence = IncrementalMFD(np.min(rates[iloc, idx]), 0.1, rates[iloc, idx]) model.magnitudes = mags[idx] test_fault.mfd_models.append(model) return test_fault, expected_rate, np.min(mags), np.max(mags), weights
def test_select_catalogue_rrup(self): """ Tests catalogue selection with Joyner-Boore distance """ self.fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, msr_sigma=[(-1.5, 0.15), (0., 0.7), (1.5, 0.15)]) cat1 = Catalogue() cat1.data = {"eventID": ["001", "002", "003", "004"], "longitude": np.array([30.1, 30.1, 30.5, 31.5]), "latitude": np.array([30.0, 30.25, 30.4, 30.5]), "depth": np.array([5.0, 250.0, 10.0, 10.0])} selector = CatalogueSelector(cat1) # Select within 50 km of the fault self.fault.select_catalogue(selector, 50.0, distance_metric="rupture") np.testing.assert_array_almost_equal( self.fault.catalogue.data["longitude"], np.array([30.1, 30.5])) np.testing.assert_array_almost_equal( self.fault.catalogue.data["latitude"], np.array([30.0, 30.4])) np.testing.assert_array_almost_equal( self.fault.catalogue.data["depth"], np.array([5.0, 10.0]))
def test_generate_branching_index(self): ''' Simple test to check that a correct branching index is raised Slip - 2 values MSR - 1 value Shear Modulus - 2 value DLR - 1 value MSR_Sigma - 3 Values Config - 1 value ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, msr_sigma=[(-1.5, 0.15), (0., 0.7), (1.5, 0.15)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(28., 0.5), (30., 0.5)], disp_length_ratio=[(1.25E-5, 1.0)]) # Set with only one config - no data input self.fault.generate_config_set({}) expected_result = np.array( [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 2, 0], [0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 2, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 2, 0], [1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 2, 0]], dtype=int) branch_index, number_branches = \ self.fault._generate_branching_index() np.testing.assert_array_equal(branch_index, expected_result) self.assertEqual(number_branches, 12)
def test_generate_recurrence_models_bad_input(self): ''' Tests to ensure correct errors are raised when input is bad ''' self.fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, msr_sigma=[(-1.5, 0.15), (0., 0.7), (1.5, 0.15)]) # Test 1 - Non-iterable config bad_config = None with self.assertRaises(ValueError) as ae: self.fault.generate_recurrence_models(bad_config) self.assertEqual(ae.exception.message, 'MFD configuration missing or incorrectly formatted') # Test 2 - Collapse is required but no msr set! with self.assertRaises(ValueError) as ae: self.fault.generate_recurrence_models(collapse=True) self.assertEqual(ae.exception.message, 'Collapsing logic tree branches requires input ' 'of a single msr for rendering sources')
def test_generate_config_set_as_dict(self): ''' Tests the function to generate a configuration tuple list from a list of multiple config dicts ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 1.0)], 0., None) good_config = [{'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 0.7, 'Model_Type': 'First', 'b_value': [0.8, 0.05]}, {'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Maximum_Magnitude_Uncertainty': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'YoungsCoppersmithExp', 'Model_Weight': 0.3, 'b_value': [0.8, 0.05]}] self.fault.generate_config_set(good_config) self.assertTrue(isinstance(self.fault.config, list)) self.assertEqual(len(self.fault.config), 2) self.assertDictEqual(self.fault.config[0][0], good_config[0]) self.assertDictEqual(self.fault.config[1][0], good_config[1]) self.assertAlmostEqual(self.fault.config[0][1], 0.7) self.assertAlmostEqual(self.fault.config[1][1], 0.3)
def test_mtk_active_fault_not_bad_input_geometry(self): ''' Tests the instantiation with a bad geometry input - should raise error ''' with self.assertRaises(IOError) as ioe: self.fault = mtkActiveFault('001', 'A Fault', 'Nonsense', self.slip, 0., 'Active Shallow Crust') self.assertEqual(ioe.exception.message, 'Geometry must be instance ' 'of hmtk.faults.fault_geometries.BaseFaultGeometry')
def test_generate_config_set_bad_input(self): ''' Tests that valueError is raised when the config is not input as either a list or dict ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 1.0)], 0., None) with self.assertRaises(ValueError) as ae: self.fault.generate_config_set(None) self.assertEqual(ae.exception.message, 'MFD config must be input as dictionary or list!')
def test_mtk_active_fault_not_bad_input_slip(self): ''' Tests the instantiation with slip wieghts not equal to 1.0 - should raise value error ''' with self.assertRaises(ValueError) as ae: self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5.0, 0.5), (7.0, 0.4)], 0., 'Active Shallow Crust') self.assertEqual(ae.exception.message, 'Slip rate weightings must sum to 1.0')
def test_mtk_active_fault_instantiation(self): ''' Tests core instantiation of mtkActiveFault Class ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, self.slip, 0., 'Active Shallow Crust', aseismic=0.5) self.assertListEqual([(0., 1.0)], self.fault.msr_sigma) self.assertAlmostEqual(self.fault.area, 1200.) self.assertListEqual([(5.0, 1.0)], self.fault.slip)
def read_file(self, mesh_spacing = 1.0): ''' Reads the file and returns an instance of the FaultSource class :param float mesh_spacing: Fault mesh spacing (km) ''' # Process the tectonic regionalisation tectonic_reg = self.process_tectonic_regionalisation() model = mtkActiveFaultModel(self.data['Fault_Model_ID'], self.data['Fault_Model_Name']) for fault in self.data['Fault_Model']: fault_geometry = self.read_fault_geometry(fault['Fault_Geometry'], mesh_spacing) if fault['Shear_Modulus']: fault['Shear_Modulus'] = weight_list_to_tuple( fault['Shear_Modulus'], '%s Shear Modulus' % fault['ID']) if fault['Displacement_Length_Ratio']: fault['Displacement_Length_Ratio'] = weight_list_to_tuple( fault['Displacement_Length_Ratio'], '%s Displacement to Length Ratio' % fault['ID']) fault_source = mtkActiveFault( fault['ID'], fault['Fault_Name'], fault_geometry, weight_list_to_tuple(fault['Slip'], '%s - Slip' % fault['ID']), float(fault['Rake']), fault['Tectonic_Region'], float(fault['Aseismic']), weight_list_to_tuple( fault['Scaling_Relation_Sigma'], '%s Scaling_Relation_Sigma' % fault['ID']), neotectonic_fault=None, scale_rel=get_scaling_relation_tuple( fault['Magnitude_Scaling_Relation']), aspect_ratio=fault['Aspect_Ratio'], shear_modulus=fault['Shear_Modulus'], disp_length_ratio=fault['Displacement_Length_Ratio']) if tectonic_reg: fault_source.get_tectonic_regionalisation( tectonic_reg, fault['Tectonic_Region']) assert isinstance(fault['MFD_Model'], list) fault_source.generate_config_set(fault['MFD_Model']) model.faults.append(fault_source) return model, tectonic_reg
def test_recurrence_collapse_branches(self): ''' Tests the recurrence model generated by collapsing branches ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, aseismic=0., msr_sigma=[(0.0, 1.0)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(30., 1.0)], disp_length_ratio=[(1.25E-5, 1.0)]) mfd_config = [{ 'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 0.7, 'Model_Type': 'First', 'b_value': [0.8, 0.05] }, { 'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Maximum_Magnitude_Uncertainty': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'YoungsCoppersmithExponential', 'Model_Weight': 0.3, 'b_value': [0.8, 0.05] }] # Enumerated branches should have four models self.fault.generate_recurrence_models(collapse=True, rendered_msr=WC1994(), config=mfd_config) expected_rates = 0. expected_weights = np.array([0.35, 0.15, 0.35, 0.15]) for iloc in range(0, 4): expected_rates = expected_rates + (expected_weights[iloc] * 10.**FAULT_RATE_DATA[iloc, :]) np.testing.assert_array_almost_equal( np.log10(self.fault.mfd[0][0].occur_rates), np.log10(expected_rates))
def test_recurrence_collapse_branches(self): ''' Tests the recurrence model generated by collapsing branches ''' self.fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, aseismic=0., msr_sigma=[(0.0, 1.0)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(30., 1.0)], disp_length_ratio=[(1.25E-5, 1.0)]) mfd_config = [{'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 0.7, 'Model_Type': 'First', 'b_value': [0.8, 0.05]}, {'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Maximum_Magnitude_Uncertainty': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'YoungsCoppersmithExponential', 'Model_Weight': 0.3, 'b_value': [0.8, 0.05]}] # Enumerated branches should have four models self.fault.generate_recurrence_models(collapse=True, rendered_msr=WC1994(), config=mfd_config) expected_rates = 0. expected_weights = np.array([0.35, 0.15, 0.35, 0.15]) for iloc in range(0, 4): expected_rates = expected_rates + (expected_weights[iloc] * 10. ** FAULT_RATE_DATA[iloc, :]) np.testing.assert_array_almost_equal( np.log10(self.fault.mfd[0][0].occur_rates), np.log10(expected_rates))
def test_generate_recurrence_models_no_collapse(self): ''' Tests the generate recurrence models option without collapsing branches: simple example with two slip rates and two mfd configurations ''' self.fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, aseismic=0., msr_sigma=[(0.0, 1.0)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(30., 1.0)], disp_length_ratio=[(1.25E-5, 1.0)]) mfd_config = [{'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 0.7, 'Model_Type': 'First', 'b_value': [0.8, 0.05]}, {'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Maximum_Magnitude_Uncertainty': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'YoungsCoppersmithExponential', 'Model_Weight': 0.3, 'b_value': [0.8, 0.05]}] # Enumerates branches should have four models self.fault.generate_recurrence_models(config=mfd_config) mfds = self.fault.mfd[0] weights = self.fault.mfd[1] np.testing.assert_array_almost_equal( weights, np.array([0.35, 0.15, 0.35, 0.15])) for (iloc, occur) in enumerate(mfds): np.testing.assert_array_almost_equal( np.log10(occur.occur_rates), FAULT_RATE_DATA[iloc, :])
def test_generate_config_set_as_dict(self): ''' Tests the function to generate a configuration tuple list from a single config dict ''' self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, [(5., 1.0)], 0., None) good_config = {'MFD_spacing': 0.1, 'Maximum_Magnitude': None, 'Minimum_Magnitude': 5.0, 'Model_Name': 'AndersonLucoArbitrary', 'Model_Weight': 1.0, 'Model_Type': 'First', 'b_value': [0.8, 0.05]} self.fault.generate_config_set(good_config) self.assertTrue(isinstance(self.fault.config, list)) self.assertDictEqual(self.fault.config[0][0], good_config) self.assertAlmostEqual(self.fault.config[0][1], 1.0)
def test_get_tectonic_regionalisation_missing_case(self): ''' Test case when no region is defined - should raise error ''' # Set up regionalistion region_dict = [{'Code': '001', 'Name': 'Active Shallow Crust'}] tect_reg = TectonicRegionalisation() tect_reg.populate_regions(region_dict) self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, self.slip, 0., None) with self.assertRaises(ValueError) as ae: self.fault.get_tectonic_regionalisation(tect_reg, None) self.assertEqual(ae.exception.message, 'Tectonic region classification missing or ' 'not defined in regionalisation')
def test_get_tectonic_regionalisation(self): ''' Tests the retreival of tectonic regionalisation information ''' # Set up regionalistion region_dict = [{'Code': '001', 'Name': 'Active Shallow Crust'}] tect_reg = TectonicRegionalisation() tect_reg.populate_regions(region_dict) # Test successful case self.fault = mtkActiveFault('001', 'A Fault', self.simple_fault, self.slip, 0., None) self.fault.get_tectonic_regionalisation(tect_reg, 'Active Shallow Crust') self.assertEqual(self.fault.trt, 'Active Shallow Crust') # Should take default values self.assertListEqual(self.fault.shear_modulus, [(30., 1.0)]) self.assertListEqual(self.fault.disp_length_ratio, [(1.25E-5, 1.0)]) self.assertTrue(isinstance(self.fault.msr[0][0], WC1994)) self.assertAlmostEqual(self.fault.msr[0][1], 1.0)
def test_generate_branching_index(self): ''' Simple test to check that a correct branching index is raised Slip - 2 values MSR - 1 value Shear Modulus - 2 value DLR - 1 value MSR_Sigma - 3 Values Config - 1 value ''' self.fault = mtkActiveFault( '001', 'A Fault', self.simple_fault, [(5., 0.5), (7., 0.5)], 0., None, msr_sigma=[(-1.5, 0.15), (0., 0.7), (1.5, 0.15)], neotectonic_fault=None, scale_rel=[(WC1994(), 1.0)], aspect_ratio=1.0, shear_modulus=[(28., 0.5), (30., 0.5)], disp_length_ratio=[(1.25E-5, 1.0)]) # Set with only one config - no data input self.fault.generate_config_set({}) expected_result = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 2, 0], [0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 2, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 2, 0], [1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 2, 0]], dtype=int) branch_index, number_branches = \ self.fault._generate_branching_index() np.testing.assert_array_equal(branch_index, expected_result) self.assertEqual(number_branches, 12)
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]))