def test_create_oqhazardlib_complex_fault_source(self):
     """
     Tests the conversion of a point source to an instance of the :class:
     openquake.hazardlib.source.complex_fault.ComplexFaultSource
     """
     complex_edges = [
         line.Line([point.Point(11., 10., 0.), point.Point(10., 10., 0.)]),
         line.Line([point.Point(11.5, 10., 21.), point.Point(10.0, 10., 21.)])
         ]
     self.fault_source = mtkComplexFaultSource('001',
         'A Fault Source',
         trt='Active Shallow Crust',
         geometry = None,
         mag_scale_rel=None,
         rupt_aspect_ratio=1.0,
         mfd=models.TGRMFD(a_val=3., b_val=1.0, min_mag=5.0, max_mag=8.0),
         rake=0.)
     self.fault_source.create_geometry(complex_edges, 2.0)
     test_source = self.fault_source.create_oqhazardlib_source(TOM,
                                                               5.0,
                                                               True)
     self.assertIsInstance(test_source, ComplexFaultSource)
     self.assertIsInstance(test_source.mfd, TruncatedGRMFD)
     self.assertAlmostEqual(test_source.mfd.b_val, 1.0)
     self.assertIsInstance(test_source.magnitude_scaling_relationship,
                           WC1994)
예제 #2
0
    def _parse_complex(cls, src_elem, mesh_spacing):
        """
        :param src_elem:
            :class:`lxml.etree._Element` instance representing a source.
        :returns:
            Fully populated
            :class:`openquake.nrmllib.models.ComplexFaultSource` object.
        """
        # Instantiate with identifier and name
        complx = mtkComplexFaultSource(
            src_elem.get('id'), src_elem.get('name'))
        print 'Complex Fault Source - ID: %s, name: %s' % (complx.id,
                                                           complx.name)
        # Set common attributes
        cls._set_common_attrs(complx, src_elem)

        # Create the complex geometry
        complex_edges = cls._parse_complex_geometry(src_elem)
        complx.create_geometry(complex_edges, mesh_spacing)
        # Get mfd
        complx.mfd = cls._parse_mfd(src_elem)
        if _xpath(src_elem, './/nrml:rake')[0].text:
            complx.rake = float(
                _xpath(src_elem, './/nrml:rake')[0].text)
        return complx
예제 #3
0
    def test_select_within_distance(self):
        '''
        Tests the selection of earthquakes within distance of fault
        '''
        # Create fault
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        # Test case when input as list of nhlib.geo.line.Line
        self.fault_source.create_geometry(self.trace_line, mesh_spacing=2.0)
        self.assertIsInstance(self.fault_source.geometry, ComplexFaultSurface)

        # Create simple catalogue
        self.catalogue.data['longitude'] = np.arange(0., 4.1, 0.1)
        self.catalogue.data['latitude'] = np.arange(0., 4.1, 0.1)
        self.catalogue.data['depth'] = np.ones(41, dtype=float)
        self.catalogue.data['eventID'] = np.arange(0, 41, 1)
        selector0 = CatalogueSelector(self.catalogue)

        # Test when considering Joyner-Boore distance
        self.fault_source.select_catalogue(selector0, 50.)
        np.testing.assert_array_equal(
            self.fault_source.catalogue.data['eventID'], np.arange(2, 14, 1))

        # Test when considering rupture distance
        self.fault_source.select_catalogue(selector0, 50., 'rupture')
        np.testing.assert_array_equal(
            self.fault_source.catalogue.data['eventID'], np.arange(2, 12, 1))

        # The usual test to ensure error is raised when no events in catalogue
        self.catalogue = Catalogue()
        selector0 = CatalogueSelector(self.catalogue)
        with self.assertRaises(ValueError) as ver:
            self.fault_source.select_catalogue(selector0, 40.0)
        self.assertEqual(ver.exception.message,
                         'No events found in catalogue!')
예제 #4
0
def parse_complex_fault_node(node, mfd_spacing=0.1, mesh_spacing=4.0):
    """
    Parses a "complexFaultSource" node and returns an instance of the :class:
    hmtk.sources.complex_fault.mtkComplexFaultSource
    """
    assert "complexFaultSource" in node.tag
    sf_taglist = get_taglist(node)
    # Get metadata
    sf_id, name, trt = (node.attrib["id"],
                        node.attrib["name"],
                        node.attrib["tectonicRegion"])
    # Process geometry
    edges = node_to_complex_fault_geometry(
        node.nodes[sf_taglist.index("complexFaultGeometry")])
    # Process scaling relation
    msr = node_to_scalerel(node.nodes[sf_taglist.index("magScaleRel")])
    # Process aspect ratio
    aspect = float_(node.nodes[sf_taglist.index("ruptAspectRatio")].text)
    # Process MFD
    mfd = node_to_mfd(node, sf_taglist)
    # Process rake
    rake = float_(node.nodes[sf_taglist.index("rake")].text)
    complex_fault = mtkComplexFaultSource(sf_id, name, trt,
                                          geometry=None,
                                          mag_scale_rel=msr,
                                          rupt_aspect_ratio=aspect,
                                          mfd=mfd,
                                          rake=rake)
    complex_fault.create_geometry(edges, mesh_spacing)
    return complex_fault
예제 #5
0
    def test_select_within_distance(self):
        """
        Tests the selection of earthquakes within distance of fault
        """
        # Create fault
        self.fault_source = mtkComplexFaultSource("101", "A complex fault")
        # Test case when input as list of nhlib.geo.line.Line
        self.fault_source.create_geometry(self.trace_line, mesh_spacing=2.0)
        self.assertIsInstance(self.fault_source.geometry, ComplexFaultSurface)

        # Create simple catalogue
        self.catalogue.data["longitude"] = np.arange(0.0, 4.1, 0.1)
        self.catalogue.data["latitude"] = np.arange(0.0, 4.1, 0.1)
        self.catalogue.data["depth"] = np.ones(41, dtype=float)
        self.catalogue.data["eventID"] = np.arange(0, 41, 1)
        selector0 = CatalogueSelector(self.catalogue)

        # Test when considering Joyner-Boore distance
        self.fault_source.select_catalogue(selector0, 50.0)
        np.testing.assert_array_equal(self.fault_source.catalogue.data["eventID"], np.arange(2, 14, 1))

        # Test when considering rupture distance
        self.fault_source.select_catalogue(selector0, 50.0, "rupture")
        np.testing.assert_array_equal(self.fault_source.catalogue.data["eventID"], np.arange(2, 12, 1))

        # The usual test to ensure error is raised when no events in catalogue
        self.catalogue = Catalogue()
        selector0 = CatalogueSelector(self.catalogue)
        with self.assertRaises(ValueError) as ver:
            self.fault_source.select_catalogue(selector0, 40.0)
        self.assertEqual(ver.exception.message, "No events found in catalogue!")
예제 #6
0
def parse_complex_fault_node(node, mfd_spacing=0.1, mesh_spacing=4.0):
    """
    Parses a "complexFaultSource" node and returns an instance of the :class:
    hmtk.sources.complex_fault.mtkComplexFaultSource
    """
    assert "complexFaultSource" in node.tag
    sf_taglist = get_taglist(node)
    # Get metadata
    sf_id, name, trt = (node.attrib["id"], node.attrib["name"],
                        node.attrib["tectonicRegion"])
    # Process geometry
    edges = node_to_complex_fault_geometry(
        node.nodes[sf_taglist.index("complexFaultGeometry")])
    # Process scaling relation
    msr = node_to_scalerel(node.nodes[sf_taglist.index("magScaleRel")])
    # Process aspect ratio
    aspect = float_(node.nodes[sf_taglist.index("ruptAspectRatio")].text)
    # Process MFD
    mfd = node_to_mfd(node, sf_taglist)
    # Process rake
    rake = float_(node.nodes[sf_taglist.index("rake")].text)
    complex_fault = mtkComplexFaultSource(sf_id,
                                          name,
                                          trt,
                                          geometry=None,
                                          mag_scale_rel=msr,
                                          rupt_aspect_ratio=aspect,
                                          mfd=mfd,
                                          rake=rake)
    complex_fault.create_geometry(edges, mesh_spacing)
    return complex_fault
예제 #7
0
 def test_create_oqhazardlib_complex_fault_source(self):
     """
     Tests the conversion of a point source to an instance of the :class:
     openquake.hazardlib.source.complex_fault.ComplexFaultSource
     """
     complex_edges = [
         line.Line([point.Point(11., 10., 0.),
                    point.Point(10., 10., 0.)]),
         line.Line(
             [point.Point(11.5, 10., 21.),
              point.Point(10.0, 10., 21.)])
     ]
     self.fault_source = mtkComplexFaultSource('001',
                                               'A Fault Source',
                                               trt='Active Shallow Crust',
                                               geometry=None,
                                               mag_scale_rel=None,
                                               rupt_aspect_ratio=1.0,
                                               mfd=models.TGRMFD(
                                                   a_val=3.,
                                                   b_val=1.0,
                                                   min_mag=5.0,
                                                   max_mag=8.0),
                                               rake=0.)
     self.fault_source.create_geometry(complex_edges, 2.0)
     test_source = self.fault_source.create_oqhazardlib_source(
         TOM, 5.0, True)
     self.assertIsInstance(test_source, ComplexFaultSource)
     self.assertIsInstance(test_source.mfd, TruncatedGRMFD)
     self.assertAlmostEqual(test_source.mfd.b_val, 1.0)
     self.assertIsInstance(test_source.magnitude_scaling_relationship,
                           WC1994)
예제 #8
0
    def test_create_complex_geometry(self):
        '''
        Tests the complex geometry creation
        '''
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        # Test case when input as list of nhlib.geo.line.Line
        self.fault_source.create_geometry(self.trace_line, mesh_spacing=2.0)
        self.assertIsInstance(self.fault_source.geometry, ComplexFaultSurface)
        # Use the dip as a simple indicator of geometrical success!
        self.assertAlmostEqual(self.fault_source.dip, 40.5398531, 2)

        # Create a second instance
        fault2 = mtkComplexFaultSource('101', 'A complex fault')
        fault2.create_geometry(self.trace_array, mesh_spacing=2.0)
        self.assertIsInstance(fault2.geometry, ComplexFaultSurface)
        # Compare it to the first
        self.assertAlmostEqual(self.fault_source.dip, fault2.dip)

        # If less than two edges are input ensure error is raised
        bad_traces = [
            line.Line([point.Point(1.0, 0.0, 3.0),
                       point.Point(1.0, 0.0, 3.0)])
        ]

        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        with self.assertRaises(ValueError) as ver:
            self.fault_source.create_geometry(bad_traces)
        self.assertEqual(ver.exception.message, 'Complex fault geometry '
                         'incorrectly defined')

        # If an edge is not defined from either a nhlib.geo.line.Line instance
        # or numpy.ndarray then ensure error is raised

        bad_traces = [
            line.Line([point.Point(1.0, 0.0, 3.0),
                       point.Point(1.0, 0.0, 3.0)])
        ]
        bad_traces.append('a bad input')

        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        with self.assertRaises(ValueError) as ver:
            self.fault_source.create_geometry(bad_traces)
        self.assertEqual(ver.exception.message, 'Unrecognised or unsupported '
                         'geometry definition')
예제 #9
0
 def test_simple_fault_instantiation(self):
     """
     Tests the core instantiation of the module
     """
     # Simple instantiation - minimual data
     self.fault_source = mtkComplexFaultSource("101", "A complex fault")
     self.assertEqual(self.fault_source.id, "101")
     self.assertEqual(self.fault_source.name, "A complex fault")
     self.assertEqual(self.fault_source.typology, "ComplexFault")
     self.assertListEqual(self.fault_source.__dict__.keys(), SOURCE_ATTRIBUTES)
예제 #10
0
 def test_simple_fault_instantiation(self):
     '''
     Tests the core instantiation of the module
     '''
     # Simple instantiation - minimual data
     self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
     self.assertEqual(self.fault_source.id, '101')
     self.assertEqual(self.fault_source.name, 'A complex fault')
     self.assertEqual(self.fault_source.typology, 'ComplexFault')
     self.assertListEqual(self.fault_source.__dict__.keys(),
                          SOURCE_ATTRIBUTES)
예제 #11
0
 def test_simple_fault_instantiation(self):
     '''
     Tests the core instantiation of the module
     '''
     # Simple instantiation - minimual data
     self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
     self.assertEqual(self.fault_source.id, '101')
     self.assertEqual(self.fault_source.name, 'A complex fault')
     self.assertEqual(self.fault_source.typology, 'ComplexFault')
     self.assertListEqual(self.fault_source.__dict__.keys(),
                          SOURCE_ATTRIBUTES)
예제 #12
0
    def test_create_complex_geometry(self):
        '''
        Tests the complex geometry creation
        '''
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        # Test case when input as list of nhlib.geo.line.Line
        self.fault_source.create_geometry(self.trace_line, mesh_spacing = 2.0)
        self.assertIsInstance(self.fault_source.geometry, ComplexFaultSurface)
        # Use the dip as a simple indicator of geometrical success!
        self.assertAlmostEqual(self.fault_source.dip, 40.5398531, 2)

        # Create a second instance 
        fault2 = mtkComplexFaultSource('101', 'A complex fault')
        fault2.create_geometry(self.trace_array, mesh_spacing = 2.0)
        self.assertIsInstance(fault2.geometry, ComplexFaultSurface)
        # Compare it to the first
        self.assertAlmostEqual(self.fault_source.dip, fault2.dip)

        # If less than two edges are input ensure error is raised
        bad_traces = [line.Line([point.Point(1.0, 0.0, 3.0),
                                 point.Point(1.0, 0.0, 3.0)])]
        
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        with self.assertRaises(ValueError) as ver:
            self.fault_source.create_geometry(bad_traces)
        self.assertEqual(ver.exception.message, 'Complex fault geometry '
                                                'incorrectly defined')

        # If an edge is not defined from either a nhlib.geo.line.Line instance
        # or numpy.ndarray then ensure error is raised
        
        bad_traces = [line.Line([point.Point(1.0, 0.0, 3.0),
                                 point.Point(1.0, 0.0, 3.0)])]
        bad_traces.append('a bad input')

        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        with self.assertRaises(ValueError) as ver:
            self.fault_source.create_geometry(bad_traces)
        self.assertEqual(ver.exception.message, 'Unrecognised or unsupported '
                                                'geometry definition')
예제 #13
0
    def test_get_minmax_edges(self):
        '''
        Tests the private method to extract the minimum and maximum depth
        from a set of edges
        '''
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        # Test case simple edge
        self.fault_source._get_minmax_edges(self.trace_line[0])
        self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
        self.assertAlmostEqual(self.fault_source.lower_depth, 1.0)
        self.fault_source._get_minmax_edges(self.trace_line[1])
        self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
        self.assertAlmostEqual(self.fault_source.lower_depth, 45.0)

        # Check the same behaviour when input as array
        self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
        # Test case simple edge
        self.fault_source._get_minmax_edges(self.trace_array[0])
        self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
        self.assertAlmostEqual(self.fault_source.lower_depth, 1.0)
        self.fault_source._get_minmax_edges(self.trace_array[1])
        self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
        self.assertAlmostEqual(self.fault_source.lower_depth, 45.0)
예제 #14
0
    def test_create_oqnmrl_complex_fault_source(self):
        '''
        Tests the conversion of a point source to an instance of the :class:
        oqnrmllib.models.AreaSource 
        '''
        # Define a complete source
        complex_edges = [
            line.Line([point.Point(10., 10., 0.),
                       point.Point(11., 10., 0.)]),
            line.Line(
                [point.Point(10., 10., 20.),
                 point.Point(11.5, 10., 21.)])
        ]
        self.fault_source = mtkComplexFaultSource('001',
                                                  'A Fault Source',
                                                  trt='Active Shallow Crust',
                                                  geometry=None,
                                                  mag_scale_rel=None,
                                                  rupt_aspect_ratio=1.0,
                                                  mfd=models.TGRMFD(
                                                      a_val=3.,
                                                      b_val=1.0,
                                                      min_mag=5.0,
                                                      max_mag=8.0),
                                                  rake=0.)
        self.fault_source.create_geometry(complex_edges, 2.0)

        expected_geometry = models.ComplexFaultGeometry(
            top_edge_wkt='LINESTRING (10.0 10.0 0.0, 11.0 10.0 0.0)',
            bottom_edge_wkt='LINESTRING (10.0 10.0 20.0, 11.5 10.0 21.0)')

        expected_source = models.ComplexFaultSource('001',
                                                    'A Fault Source',
                                                    trt='Active Shallow Crust',
                                                    geometry=expected_geometry,
                                                    mag_scale_rel='WC1994',
                                                    rupt_aspect_ratio=1.0,
                                                    mfd=models.TGRMFD(
                                                        a_val=3.,
                                                        b_val=1.0,
                                                        min_mag=5.0,
                                                        max_mag=8.0),
                                                    rake=90.)

        test_source = self.fault_source.create_oqnrml_source(use_defaults=True)
        self.assertTrue(isinstance(test_source, models.ComplexFaultSource))
        self.assertEqual(test_source.id, expected_source.id)
        self.assertEqual(test_source.name, expected_source.name)
        self.assertAlmostEqual(test_source.mfd.b_val,
                               expected_source.mfd.b_val)
예제 #15
0
 def test_get_minmax_edges(self):
     '''
     Tests the private method to extract the minimum and maximum depth
     from a set of edges
     '''
     self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
     # Test case simple edge
     self.fault_source._get_minmax_edges(self.trace_line[0])
     self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
     self.assertAlmostEqual(self.fault_source.lower_depth, 1.0)
     self.fault_source._get_minmax_edges(self.trace_line[1])
     self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
     self.assertAlmostEqual(self.fault_source.lower_depth, 45.0)
     
     # Check the same behaviour when input as array
     self.fault_source = mtkComplexFaultSource('101', 'A complex fault')
     # Test case simple edge
     self.fault_source._get_minmax_edges(self.trace_array[0])
     self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
     self.assertAlmostEqual(self.fault_source.lower_depth, 1.0)
     self.fault_source._get_minmax_edges(self.trace_array[1])
     self.assertAlmostEqual(self.fault_source.upper_depth, 0.9)
     self.assertAlmostEqual(self.fault_source.lower_depth, 45.0)
예제 #16
0
    def generate_fault_source_model(self):
        '''
        Creates a resulting hmtk fault source set.

        :returns:
            source_model - list of instances of either the
              :class:`hmtk.sources.simple_fault_source.mtkSimpleFaultSource`
              or
              :class:`hmtk.sources.complex_fault_source.mtkComplexFaultSource`
            model_weight - Corresponding weights for each source model
        '''
        source_model = []
        model_weight = []
        for iloc in range(0, self.get_number_mfd_models()):
            model_mfd = EvenlyDiscretizedMFD(
                self.mfd[0][iloc].min_mag,
                self.mfd[0][iloc].bin_width,
                self.mfd[0][iloc].occur_rates.tolist())

            if isinstance(self.geometry, ComplexFaultGeometry):
                # Complex fault class
                source = mtkComplexFaultSource(
                    self.id,
                    self.name,
                    self.trt,
                    self.geometry.surface,
                    self.mfd[2][iloc],
                    self.rupt_aspect_ratio,
                    model_mfd,
                    self.rake)
                source.fault_edges = self.geometry.trace
            else:
                # Simple Fault source
                source = mtkSimpleFaultSource(
                    self.id,
                    self.name,
                    self.trt,
                    self.geometry.surface,
                    self.geometry.dip,
                    self.geometry.upper_depth,
                    self.geometry.lower_depth,
                    self.mfd[2][iloc],
                    self.rupt_aspect_ratio,
                    model_mfd,
                    self.rake)
                source.fault_trace = self.geometry.trace
            source_model.append(source)
            model_weight.append(self.mfd[1][iloc])
        return source_model, model_weight
예제 #17
0
    def test_create_oqnmrl_complex_fault_source(self):
        """
        Tests the conversion of a point source to an instance of the :class:
        oqnrmllib.models.AreaSource
        """
        # Define a complete source
        complex_edges = [
            line.Line([point.Point(10.0, 10.0, 0.0), point.Point(11.0, 10.0, 0.0)]),
            line.Line([point.Point(10.0, 10.0, 20.0), point.Point(11.5, 10.0, 21.0)]),
        ]
        self.fault_source = mtkComplexFaultSource(
            "001",
            "A Fault Source",
            trt="Active Shallow Crust",
            geometry=None,
            mag_scale_rel=None,
            rupt_aspect_ratio=1.0,
            mfd=models.TGRMFD(a_val=3.0, b_val=1.0, min_mag=5.0, max_mag=8.0),
            rake=0.0,
        )
        self.fault_source.create_geometry(complex_edges, 2.0)

        expected_geometry = models.ComplexFaultGeometry(
            top_edge_wkt="LINESTRING (10.0 10.0 0.0, 11.0 10.0 0.0)",
            bottom_edge_wkt="LINESTRING (10.0 10.0 20.0, 11.5 10.0 21.0)",
        )

        expected_source = models.ComplexFaultSource(
            "001",
            "A Fault Source",
            trt="Active Shallow Crust",
            geometry=expected_geometry,
            mag_scale_rel="WC1994",
            rupt_aspect_ratio=1.0,
            mfd=models.TGRMFD(a_val=3.0, b_val=1.0, min_mag=5.0, max_mag=8.0),
            rake=90.0,
        )

        test_source = self.fault_source.create_oqnrml_source(use_defaults=True)
        self.assertTrue(isinstance(test_source, models.ComplexFaultSource))
        self.assertEqual(test_source.id, expected_source.id)
        self.assertEqual(test_source.name, expected_source.name)
        self.assertAlmostEqual(test_source.mfd.b_val, expected_source.mfd.b_val)
    def test_create_oqnmrl_complex_fault_source(self):
        '''
        Tests the conversion of a point source to an instance of the :class:
        oqnrmllib.models.AreaSource
        '''
        complex_edges = [
            line.Line([point.Point(11., 10., 0.), point.Point(10., 10., 0.)]),
            line.Line([point.Point(11.5, 10., 21.), point.Point(10.0, 10., 21.)])
            ]
        self.fault_source = mtkComplexFaultSource('001',
            'A Fault Source',
            trt='Active Shallow Crust',
            geometry = None,
            mag_scale_rel=None,
            rupt_aspect_ratio=1.0,
            mfd=models.TGRMFD(a_val=3., b_val=1.0, min_mag=5.0, max_mag=8.0),
            rake=0.)
        self.fault_source.create_geometry(complex_edges, 2.0)

        expected_geometry=models.ComplexFaultGeometry(
            top_edge_wkt='LINESTRING (10.0 10.0 0.0, 11.0 10.0 0.0)',
            bottom_edge_wkt='LINESTRING (10.0 10.0 20.0, 11.5 10.0 21.0)')

        expected_source = models.ComplexFaultSource(
            '001',
            'A Fault Source',
            trt='Active Shallow Crust',
            geometry=expected_geometry,
            mag_scale_rel='WC1994',
            rupt_aspect_ratio=1.0,
            mfd=models.TGRMFD(a_val=3., b_val=1.0, min_mag=5.0, max_mag=8.0),
            rake=90.)

        test_source = self.fault_source.create_oqnrml_source(use_defaults=True)
        self.assertTrue(isinstance(test_source, models.ComplexFaultSource))
        self.assertEqual(test_source.id, expected_source.id)
        self.assertEqual(test_source.name, expected_source.name)
        self.assertAlmostEqual(test_source.mfd.b_val,
                               expected_source.mfd.b_val)
예제 #19
0
    def _parse_complex(cls, src_elem, mesh_spacing):
        """
        :param src_elem:
            :class:`lxml.etree._Element` instance representing a source.
        :returns:
            Fully populated
            :class:`openquake.nrmllib.models.ComplexFaultSource` object.
        """
        # Instantiate with identifier and name
        complx = mtkComplexFaultSource(src_elem.get('id'),
                                       src_elem.get('name'))
        print 'Complex Fault Source - ID: %s, name: %s' % (complx.id,
                                                           complx.name)
        # Set common attributes
        cls._set_common_attrs(complx, src_elem)

        # Create the complex geometry
        complex_edges = cls._parse_complex_geometry(src_elem)
        complx.create_geometry(complex_edges, mesh_spacing)
        # Get mfd
        complx.mfd = cls._parse_mfd(src_elem)
        if _xpath(src_elem, './/nrml:rake')[0].text:
            complx.rake = float(_xpath(src_elem, './/nrml:rake')[0].text)
        return complx