示例#1
0
    def _verify_curves(self, gsim_name, truncation_level, ndp=3):
        """
        Implements the core verification. Initially the hazard calculation is
        run with the "Mean" GMPE (this has the mean weightings inside the
        GMPE). Then the "low", "average" and "high" cases are run separately
        and the resulting curves summed with their respective weights.

        A small degree of mismatch is found though this typcially takes
        place at very low probabilities. Curves are compared in the logarithmic
        domain (ignoring 0 values)
        """
        gsim0 = {"Active Shallow Crust": self.gsim_set[gsim_name][0]}
        # Run new weighted mean curve
        wmean_curve = calc_hazard_curves(self.sources, self.sites, self.imtls,
                                         gsim0, truncation_level)
        # Now run low, mid and high curves
        curves = {"PGA": np.zeros_like(wmean_curve["PGA"])}
        for iloc in range(1, 4):
            gsim_i = {
                "Active Shallow Crust": self.gsim_set[gsim_name][iloc][1]
            }
            wgt = self.gsim_set[gsim_name][iloc][0]
            curves["PGA"] += (
                wgt * calc_hazard_curves(self.sources, self.sites, self.imtls,
                                         gsim_i, truncation_level)["PGA"])
        # Ignore cases where values are equal to zero
        idx = wmean_curve["PGA"] > 0.0
        np.testing.assert_array_almost_equal(
            np.log(wmean_curve["PGA"][idx]), np.log(curves["PGA"][idx]), ndp)
    def test1(self):
        site1_pga_poe_expected = [0.0639157, 0.03320212, 0.02145989]
        site2_pga_poe_expected = [0.06406232, 0.02965879, 0.01864331]
        site1_pgd_poe_expected = [0.16146619, 0.1336553]
        site2_pgd_poe_expected = [0.15445961, 0.13437589]

        curves = calc_hazard_curves(
            self.sources, self.sites, self.imts,
            self.gsims, self.truncation_level)
        self.assertEqual(set(curves.dtype.fields), set(['PGA', 'PGD']))

        pga_curves = curves['PGA']
        self.assertIsInstance(pga_curves, numpy.ndarray)
        self.assertEqual(pga_curves.shape, (2, 3))  # two sites, three IMLs
        site1_pga_poe, site2_pga_poe = pga_curves
        self.assertTrue(numpy.allclose(site1_pga_poe, site1_pga_poe_expected),
                        str(site1_pga_poe))
        self.assertTrue(numpy.allclose(site2_pga_poe, site2_pga_poe_expected),
                        str(site2_pga_poe))

        pgd_curves = curves['PGD']
        self.assertIsInstance(pgd_curves, numpy.ndarray)
        self.assertEqual(pgd_curves.shape, (2, 2))  # two sites, two IMLs
        site1_pgd_poe, site2_pgd_poe = pgd_curves
        self.assertTrue(numpy.allclose(site1_pgd_poe, site1_pgd_poe_expected),
                        str(site1_pgd_poe))
        self.assertTrue(numpy.allclose(site2_pgd_poe, site2_pgd_poe_expected),
                        str(site2_pgd_poe))
示例#3
0
def reference_psha_calculation_openquake():
    """
    Sets up the reference PSHA calculation calling OpenQuake directly. All
    subsequent implementations should match this example
    """
    # Site model - 3 Sites
    site_model = SiteCollection([
        Site(Point(30.0, 30.0), 760., True, 1.0, 1.0, 1),
        Site(Point(30.25, 30.25), 760., True, 1.0, 1.0, 2),
        Site(Point(30.4, 30.4), 760., True, 1.0, 1.0, 2)])
    # Source Model Two Point Sources
    mfd_1 = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0)
    mfd_2 = TruncatedGRMFD(4.5, 7.5, 0.1, 3.5, 1.1)
    source_model = [PointSource('001', 'Point1', 'Active Shallow Crust',
                                mfd_1, 1.0, WC1994(), 1.0, PoissonTOM(50.0),
                                0.0, 30.0, Point(30.0, 30.5),
                                PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]),
                                PMF([(1.0, 10.0)])),
                    PointSource('002', 'Point2', 'Active Shallow Crust',
                                mfd_2, 1.0, WC1994(), 1.0, PoissonTOM(50.0),
                                0.0, 30.0, Point(30.0, 30.5),
                                PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]),
                                PMF([(1.0, 10.0)]))]
    imts = {'PGA': [0.01, 0.1, 0.2, 0.5, 0.8],
            'SA(0.5)': [0.01, 0.1, 0.2, 0.5, 0.8]}
    # Akkar & Bommer (2010) GMPE
    gsims = {'Active Shallow Crust': gsim.akkar_bommer_2010.AkkarBommer2010()}
    truncation_level = None
    return calc_hazard_curves(source_model, site_model, imts, gsims,
                         truncation_level)
示例#4
0
    def test_source_errors(self):
        # exercise `hazard_curves_poissonian` in the case of an exception,
        # whereby we expect the source_id to be reported in the error message

        fail_source = self.FailSource(self.source2.source_id,
                                      self.source2.ruptures,
                                      self.source2.time_span)
        sources = iter([self.source1, fail_source])

        with self.assertRaises(ValueError) as ae:
            calc_hazard_curves(sources, self.sites, self.imts, self.gsims,
                               self.truncation_level)
        expected_error = (
            'An error occurred with source id=2. Error: Something bad happened'
        )
        self.assertEqual(expected_error, ae.exception.message)
 def test_hazard_curve_X(self):
     # Test the former calculator
     curves = calc_hazard_curves([self.src2],
                                 self.sites,
                                 self.imtls,
                                 self.gsim_by_trt,
                                 truncation_level=None)
     crv = curves[0][0]
     self.assertAlmostEqual(0.3, crv[0])
 def test_hazard_curve_B(self):
     # Test classical case i.e. independent sources in a list instance
     curves = calc_hazard_curves([self.src1, self.src2],
                                 self.sites,
                                 self.imtls,
                                 self.gsim_by_trt,
                                 truncation_level=None)
     crv = curves[0][0]
     npt.assert_almost_equal(numpy.array([0.58000, 0.53, 0.1347]),
                             crv, decimal=4)
    def test(self):
        d = os.path.dirname(os.path.dirname(__file__))
        source_model = os.path.join(d, 'source_model/multi-point-source.xml')
        groups = nrml.to_python(source_model, SourceConverter(
            investigation_time=50., rupture_mesh_spacing=2.))
        site = Site(Point(0.1, 0.1), 800, z1pt0=100., z2pt5=1.)
        sitecol = SiteCollection([site])
        imtls = DictArray({'PGA': [0.01, 0.02, 0.04, 0.08, 0.16]})
        gsim_by_trt = {'Stable Continental Crust': Campbell2003()}
        hcurves = calc_hazard_curves(groups, sitecol, imtls, gsim_by_trt)
        expected = [0.99999778, 0.9084039, 0.148975348,
                    0.0036909656, 2.76326e-05]
        npt.assert_almost_equal(hcurves['PGA'][0], expected)

        # splitting in point sources
        [[mps1, mps2]] = groups
        psources = list(mps1) + list(mps2)
        hcurves = calc_hazard_curves(psources, sitecol, imtls, gsim_by_trt)
        npt.assert_almost_equal(hcurves['PGA'][0], expected)
 def test_hazard_curve_A(self):
     # Test back-compatibility
     # Classical case i.e. independent sources in a list instance
     curves = calc_hazard_curves([self.src2],
                                 self.sites,
                                 self.imtls,
                                 self.gsim_by_trt,
                                 truncation_level=None)
     crv = curves[0][0]
     npt.assert_almost_equal(numpy.array([0.30000, 0.2646, 0.0625]),
                             crv, decimal=4)
 def test(self):
     source_model = os.path.join(os.path.dirname(__file__), 'nankai.xml')
     groups = nrml.to_python(source_model, SourceConverter(
         investigation_time=50., rupture_mesh_spacing=2.))
     site = Site(Point(135.68, 35.68), 400, z1pt0=100., z2pt5=1.)
     s_filter = SourceFilter(SiteCollection([site]), {})
     imtls = DictArray({'PGV': [20, 40, 80]})
     gsim_by_trt = {'Subduction Interface': SiMidorikawa1999SInter()}
     hcurves = calc_hazard_curves(groups, s_filter, imtls, gsim_by_trt)
     npt.assert_almost_equal(
         [1.1262869e-01, 3.9968668e-03, 3.1005840e-05],
         hcurves['PGV'][0])
 def test_hazard_curve_B(self):
     # Test simple calculation
     group = SourceGroup(
         TRT.ACTIVE_SHALLOW_CRUST, [self.src2], 'test', 'indep', 'indep')
     groups = [group]
     curves = calc_hazard_curves(groups,
                                 self.sites,
                                 self.imtls,
                                 self.gsim_by_trt,
                                 truncation_level=None)
     npt.assert_almost_equal(numpy.array([0.30000, 0.2646, 0.0625]),
                             curves[0][0], decimal=4)
示例#11
0
    def test_non_parametric_source(self):
        # non-parametric source equivalent to case 2 simple fault source
        data = test_data.SET1_CASE2_SOURCE_DATA
        ruptures = []
        for i in range(data['num_rups_dip']):
            for j in range(data['num_rups_strike']):
                lons = data['lons']
                lats = data['lats'][j]
                depths = data['depths'][i]
                mesh = RectangularMesh(lons, lats, depths)
                surf = SimpleFaultSurface(mesh)
                hypo = Point(
                    data['hypo_lons'][i, j],
                    data['hypo_lats'][i, j],
                    data['hypo_depths'][i, j]
                )
                rup = Rupture(data['mag'], data['rake'],
                              data['tectonic_region_type'], hypo, surf,
                              data['source_typology'])
                ruptures.append((rup, data['pmf']))
        npss = NonParametricSeismicSource(
            'id', 'name', data['tectonic_region_type'], ruptures
        )
        sites = SiteCollection([
            test_data.SET1_CASE1TO9_SITE1, test_data.SET1_CASE1TO9_SITE2,
            test_data.SET1_CASE1TO9_SITE3, test_data.SET1_CASE1TO9_SITE4,
            test_data.SET1_CASE1TO9_SITE5, test_data.SET1_CASE1TO9_SITE6,
            test_data.SET1_CASE1TO9_SITE7
        ])
        gsims = {const.TRT.ACTIVE_SHALLOW_CRUST: SadighEtAl1997()}
        truncation_level = 0
        imts = {str(test_data.IMT): test_data.SET1_CASE2_IMLS}

        curves = calc_hazard_curves([npss], sites, imts, gsims,
                                    truncation_level)
        s1hc, s2hc, s3hc, s4hc, s5hc, s6hc, s7hc = curves[str(test_data.IMT)]

        assert_hazard_curve_is(self, s1hc, test_data.SET1_CASE2_SITE1_POES,
                               atol=3e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s2hc, test_data.SET1_CASE2_SITE2_POES,
                               atol=2e-5, rtol=1e-5)
        assert_hazard_curve_is(self, s3hc, test_data.SET1_CASE2_SITE3_POES,
                               atol=2e-5, rtol=1e-5)
        assert_hazard_curve_is(self, s4hc, test_data.SET1_CASE2_SITE4_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s5hc, test_data.SET1_CASE2_SITE5_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s6hc, test_data.SET1_CASE2_SITE6_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s7hc, test_data.SET1_CASE2_SITE7_POES,
                               atol=2e-5, rtol=1e-5)
 def test(self):
     # mutually exclusive ruptures
     d = os.path.dirname(os.path.dirname(__file__))
     tmps = 'nonparametric-source-mutex-ruptures.xml'
     source_model = os.path.join(d, 'source_model', tmps)
     groups = nrml.to_python(source_model, SourceConverter(
         investigation_time=50., rupture_mesh_spacing=2.))
     site = Site(Point(143.5, 39.5), 800, z1pt0=100., z2pt5=1.)
     sitecol = SiteCollection([site])
     imtls = DictArray({'PGA': [0.01, 0.1, 0.2, 0.5]})
     gsim_by_trt = {'Some TRT': Campbell2003()}
     hcurves = calc_hazard_curves(groups, sitecol, imtls, gsim_by_trt)
     # expected results obtained with an ipython notebook
     expected = [4.3998728e-01, 1.1011728e-01, 7.5495312e-03, 8.5812844e-06]
     npt.assert_almost_equal(hcurves['PGA'][0], expected)
示例#13
0
 def calculate_hazard(self, num_workers=DEFAULT_WORKERS,
         num_src_workers=1):
     """
     Calculates the hazard
     :param int num_workers:
         Number of workers for parallel calculation
     :param int num_src_workers:
         Number of elements per worker
     """
     return hazard_curve.calc_hazard_curves(self.source_model,
                                            self.sites,
                                            self.imts,
                                            self.gmpes,
                                            self.truncation_level,
                                            self.src_filter,
                                            self.rup_filter)
 def test_hazard_curve(self):
     # Classical PSHA with cluster source
     curves = calc_hazard_curves(self.sg,
                                 self.sites,
                                 self.imtls,
                                 self.gsim_by_trt,
                                 truncation_level=None)
     crv = curves[0][0]
     # Expected results computed with a notebook using the original USGS
     # formulation as described in Appendix F of Petersen et al. (2008).
     # The rates of exceedance were converted a posteriori into
     # probabilities.
     rates = np.array([1.00000000e-03, 9.98565030e-04, 8.42605169e-04,
                       4.61559062e-04, 1.10100503e-06])
     expected = 1 - np.exp(-rates)
     np.testing.assert_almost_equal(crv, expected)
示例#15
0
    def test_case_11(self):
        hypocenter_probability = (
            Decimal(1) / len(test_data.SET1_CASE11_HYPOCENTERS)
        )
        hypocenter_pmf = PMF([
            (hypocenter_probability, hypocenter)
            for hypocenter in test_data.SET1_CASE11_HYPOCENTERS
        ])
        # apart from hypocenter pmf repeats case 10
        sources = [AreaSource(
            source_id='area', name='area',
            tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
            mfd=test_data.SET1_CASE11_MFD,
            nodal_plane_distribution=PMF([(1, NodalPlane(0.0, 90.0, 0.0))]),
            hypocenter_distribution=hypocenter_pmf,
            upper_seismogenic_depth=0.0,
            lower_seismogenic_depth=10.0,
            magnitude_scaling_relationship=PointMSR(),
            rupture_aspect_ratio=test_data.SET1_RUPTURE_ASPECT_RATIO,
            temporal_occurrence_model=PoissonTOM(1.),
            polygon=test_data.SET1_CASE11_SOURCE_POLYGON,
            area_discretization=10.0,
            rupture_mesh_spacing=10.0
        )]
        sites = SiteCollection([
            test_data.SET1_CASE11_SITE1, test_data.SET1_CASE11_SITE2,
            test_data.SET1_CASE11_SITE3, test_data.SET1_CASE11_SITE4
        ])
        gsims = {const.TRT.ACTIVE_SHALLOW_CRUST: SadighEtAl1997()}
        truncation_level = 0
        imts = {str(test_data.IMT): test_data.SET1_CASE11_IMLS}

        curves = calc_hazard_curves(
            sources, sites, imts, gsims, truncation_level)
        s1hc, s2hc, s3hc, s4hc = curves[str(test_data.IMT)]

        assert_hazard_curve_is(self, s1hc, test_data.SET1_CASE11_SITE1_POES,
                               atol=1e-4, rtol=1e-1)
        assert_hazard_curve_is(self, s2hc, test_data.SET1_CASE11_SITE2_POES,
                               atol=1e-4, rtol=1e-1)
        assert_hazard_curve_is(self, s3hc, test_data.SET1_CASE11_SITE3_POES,
                               atol=1e-4, rtol=1e-1)
        assert_hazard_curve_is(self, s4hc, test_data.SET1_CASE11_SITE4_POES,
                               atol=1e-4, rtol=1e-1)
示例#16
0
    def test_case_5(self):
        # only mfd differs from case 2
        sources = [SimpleFaultSource(source_id='fault1', name='fault1',
            tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
            mfd=test_data.SET1_CASE5_MFD,
            rupture_mesh_spacing=1.0,
            magnitude_scaling_relationship=PeerMSR(),
            rupture_aspect_ratio=test_data.SET1_RUPTURE_ASPECT_RATIO,
            temporal_occurrence_model=PoissonTOM(1.),
            upper_seismogenic_depth=test_data.SET1_CASE1TO9_UPPER_SEISMOGENIC_DEPTH,
            lower_seismogenic_depth=test_data.SET1_CASE1TO9_LOWER_SEISMOGENIC_DEPTH,
            fault_trace=test_data.SET1_CASE1TO9_FAULT_TRACE,
            dip=test_data.SET1_CASE1TO9_DIP,
            rake=test_data.SET1_CASE1TO9_RAKE
        )]
        sites = SiteCollection([
            test_data.SET1_CASE1TO9_SITE1, test_data.SET1_CASE1TO9_SITE2,
            test_data.SET1_CASE1TO9_SITE3, test_data.SET1_CASE1TO9_SITE4,
            test_data.SET1_CASE1TO9_SITE5, test_data.SET1_CASE1TO9_SITE6,
            test_data.SET1_CASE1TO9_SITE7
        ])
        gsims = {const.TRT.ACTIVE_SHALLOW_CRUST: SadighEtAl1997()}
        truncation_level = 0
        imts = {str(test_data.IMT): test_data.SET1_CASE5_IMLS}

        curves = calc_hazard_curves(
            sources, sites, imts, gsims, truncation_level)
        s1hc, s2hc, s3hc, s4hc, s5hc, s6hc, s7hc = curves[str(test_data.IMT)]

        assert_hazard_curve_is(self, s1hc, test_data.SET1_CASE5_SITE1_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s2hc, test_data.SET1_CASE5_SITE2_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s3hc, test_data.SET1_CASE5_SITE3_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s4hc, test_data.SET1_CASE5_SITE4_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s5hc, test_data.SET1_CASE5_SITE5_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s6hc, test_data.SET1_CASE5_SITE6_POES,
                               atol=1e-3, rtol=1e-5)
        assert_hazard_curve_is(self, s7hc, test_data.SET1_CASE5_SITE7_POES,
                               atol=1e-3, rtol=1e-5)
示例#17
0
def example_calc(apply):
    sitecol = SiteCollection([
        Site(Point(30.0, 30.0), 760., True, 1.0, 1.0),
        Site(Point(30.25, 30.25), 760., True, 1.0, 1.0),
        Site(Point(30.4, 30.4), 760., True, 1.0, 1.0)])
    mfd_1 = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0)
    mfd_2 = TruncatedGRMFD(4.5, 7.5, 0.1, 3.5, 1.1)
    sources = [PointSource('001', 'Point1', 'Active Shallow Crust',
                           mfd_1, 1.0, WC1994(), 1.0, PoissonTOM(50.0),
                           0.0, 30.0, Point(30.0, 30.5),
                           PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]),
                           PMF([(1.0, 10.0)])),
               PointSource('002', 'Point2', 'Active Shallow Crust',
                           mfd_2, 1.0, WC1994(), 1.0, PoissonTOM(50.0),
                           0.0, 30.0, Point(30.0, 30.5),
                           PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]),
                           PMF([(1.0, 10.0)]))]
    imtls = {'PGA': [0.01, 0.1, 0.2, 0.5, 0.8],
             'SA(0.5)': [0.01, 0.1, 0.2, 0.5, 0.8]}
    gsims = {'Active Shallow Crust': akkar_bommer_2010.AkkarBommer2010()}
    return calc_hazard_curves(sources, sitecol, imtls, gsims, apply=apply)
    def test_point_sources(self):
        sources = [
            openquake.hazardlib.source.PointSource(
                source_id='point1', name='point1',
                tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
                mfd=openquake.hazardlib.mfd.EvenlyDiscretizedMFD(
                    min_mag=4, bin_width=1, occurrence_rates=[5]
                ),
                nodal_plane_distribution=openquake.hazardlib.pmf.PMF([
                    (1, openquake.hazardlib.geo.NodalPlane(strike=0.0,
                                                           dip=90.0,
                                                           rake=0.0))
                ]),
                hypocenter_distribution=openquake.hazardlib.pmf.PMF([(1, 10)]),
                upper_seismogenic_depth=0.0,
                lower_seismogenic_depth=10.0,
                magnitude_scaling_relationship=
                openquake.hazardlib.scalerel.PeerMSR(),
                rupture_aspect_ratio=2,
                temporal_occurrence_model=PoissonTOM(1.),
                rupture_mesh_spacing=1.0,
                location=Point(10, 10)
            ),
            openquake.hazardlib.source.PointSource(
                source_id='point2', name='point2',
                tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
                mfd=openquake.hazardlib.mfd.EvenlyDiscretizedMFD(
                    min_mag=4, bin_width=2, occurrence_rates=[5, 6, 7]
                ),
                nodal_plane_distribution=openquake.hazardlib.pmf.PMF([
                    (1, openquake.hazardlib.geo.NodalPlane(strike=0,
                                                           dip=90,
                                                           rake=0.0)),
                ]),
                hypocenter_distribution=openquake.hazardlib.pmf.PMF([(1, 10)]),
                upper_seismogenic_depth=0.0,
                lower_seismogenic_depth=10.0,
                magnitude_scaling_relationship=
                openquake.hazardlib.scalerel.PeerMSR(),
                rupture_aspect_ratio=2,
                temporal_occurrence_model=PoissonTOM(1.),
                rupture_mesh_spacing=1.0,
                location=Point(10, 11)
            ),
        ]
        sites = [openquake.hazardlib.site.Site(Point(11, 10), 1, True, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 16), 2, True, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 10.6), 3, True, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 10.7), 4, True, 2, 3)]
        sitecol = openquake.hazardlib.site.SiteCollection(sites)

        from openquake.hazardlib.gsim.sadigh_1997 import SadighEtAl1997
        gsims = {const.TRT.ACTIVE_SHALLOW_CRUST: SadighEtAl1997()}
        truncation_level = 1
        imts = {'PGA': [0.1, 0.5, 1.3]}

        from openquake.hazardlib.calc import filters
        source_site_filter = self.SitesCounterSourceFilter(
            filters.source_site_distance_filter(30)
        )
        rupture_site_filter = self.SitesCounterRuptureFilter(
            filters.rupture_site_distance_filter(30)
        )
        calc_hazard_curves(
            sources, sitecol, imts, gsims, truncation_level,
            source_site_filter=source_site_filter,
            rupture_site_filter=rupture_site_filter
        )
        # there are two sources and four sites. The first source contains only
        # one rupture, the second source contains three ruptures.
        #
        # the first source has 'maximum projection radius' of 0.707 km
        # the second source has 'maximum projection radius' of 500.0 km
        #
        # the epicentral distances for source 1 are: [ 109.50558394,
        # 667.16955987,   66.71695599,   77.83644865]
        # the epicentral distances for source 2 are: [ 155.9412148 ,
        # 555.97463322,   44.47797066,   33.35847799]
        #
        # Considering that the source site filtering distance is set to 30 km,
        # for source 1, all sites have epicentral distance larger than
        # 0.707 + 30 km. This means that source 1 ('point 1') is not considered
        # in the calculation because too far.
        # for source 2, the 1st, 3rd and 4th sites have epicentral distances
        # smaller than 500.0 + 30 km. This means that source 2 ('point 2') is
        # considered in the calculation for site 1, 3, and 4.
        #
        # JB distances for rupture 1 in source 2 are: [ 155.43860273,
        #  555.26752644,   43.77086388,   32.65137121]
        # JB distances for rupture 2 in source 2 are: [ 150.98882575,
        #  548.90356541,   37.40690285,   26.28741018]
        # JB distances for rupture 3 in source 2 are: [ 109.50545819,
        # 55.97463322,    0.        ,    0.        ]
        #
        # Considering that the rupture site filtering distance is set to 30 km,
        # rupture 1 (magnitude 4) is not considered because too far, rupture 2
        # (magnitude 6) affect only the 4th site, rupture 3 (magnitude 8)
        # affect the 3rd and 4th sites.
        self.assertEqual(source_site_filter.counts,
                         [('point2', [1, 3, 4])])
        self.assertEqual(rupture_site_filter.counts,
                         [(6, [4]), (8, [3, 4])])
    def test_point_sources(self):
        sources = [
            openquake.hazardlib.source.PointSource(
                source_id='point1', name='point1',
                tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
                mfd=openquake.hazardlib.mfd.EvenlyDiscretizedMFD(
                    min_mag=4, bin_width=1, occurrence_rates=[5]
                ),
                nodal_plane_distribution=openquake.hazardlib.pmf.PMF([
                    (1, openquake.hazardlib.geo.NodalPlane(strike=0.0,
                                                           dip=90.0,
                                                           rake=0.0))
                ]),
                hypocenter_distribution=openquake.hazardlib.pmf.PMF([(1, 10)]),
                upper_seismogenic_depth=0.0,
                lower_seismogenic_depth=10.0,
                magnitude_scaling_relationship=
                openquake.hazardlib.scalerel.PeerMSR(),
                rupture_aspect_ratio=2,
                temporal_occurrence_model=PoissonTOM(1.),
                rupture_mesh_spacing=1.0,
                location=Point(10, 10)
            ),
            openquake.hazardlib.source.PointSource(
                source_id='point2', name='point2',
                tectonic_region_type=const.TRT.ACTIVE_SHALLOW_CRUST,
                mfd=openquake.hazardlib.mfd.EvenlyDiscretizedMFD(
                    min_mag=4, bin_width=2, occurrence_rates=[5, 6, 7]
                ),
                nodal_plane_distribution=openquake.hazardlib.pmf.PMF([
                    (1, openquake.hazardlib.geo.NodalPlane(strike=0,
                                                           dip=90,
                                                           rake=0.0)),
                ]),
                hypocenter_distribution=openquake.hazardlib.pmf.PMF([(1, 10)]),
                upper_seismogenic_depth=0.0,
                lower_seismogenic_depth=10.0,
                magnitude_scaling_relationship=
                openquake.hazardlib.scalerel.PeerMSR(),
                rupture_aspect_ratio=2,
                temporal_occurrence_model=PoissonTOM(1.),
                rupture_mesh_spacing=1.0,
                location=Point(10, 11)
            ),
        ]
        sites = [openquake.hazardlib.site.Site(Point(11, 10), 1, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 16), 2, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 10.6, 1), 3, 2, 3),
                 openquake.hazardlib.site.Site(Point(10, 10.7, -1), 4, 2, 3)]
        sitecol = openquake.hazardlib.site.SiteCollection(sites)
        gsims = {const.TRT.ACTIVE_SHALLOW_CRUST: SadighEtAl1997()}
        truncation_level = 1
        imts = {'PGA': [0.1, 0.5, 1.3]}
        s_filter = SourceFilter(sitecol, {const.TRT.ACTIVE_SHALLOW_CRUST: 30})
        result = calc_hazard_curves(
            sources, s_filter, imts, gsims, truncation_level)['PGA']
        # there are two sources and four sites. The first source contains only
        # one rupture, the second source contains three ruptures.
        #
        # the first source has 'maximum projection radius' of 0.707 km
        # the second source has 'maximum projection radius' of 500.0 km
        #
        # the epicentral distances for source 1 are: [ 109.50558394,
        # 667.16955987,   66.71695599,   77.83644865]
        # the epicentral distances for source 2 are: [ 155.9412148 ,
        # 555.97463322,   44.47797066,   33.35847799]
        #
        # Considering that the source site filtering distance is set to 30 km,
        # for source 1, all sites have epicentral distance larger than
        # 0.707 + 30 km. This means that source 1 ('point 1') is not considered
        # in the calculation because too far.
        # for source 2, the 1st, 3rd and 4th sites have epicentral distances
        # smaller than 500.0 + 30 km. This means that source 2 ('point 2') is
        # considered in the calculation for site 1, 3, and 4.
        #
        # JB distances for rupture 1 in source 2 are: [ 155.43860273,
        #  555.26752644,   43.77086388,   32.65137121]
        # JB distances for rupture 2 in source 2 are: [ 150.98882575,
        #  548.90356541,   37.40690285,   26.28741018]
        # JB distances for rupture 3 in source 2 are: [ 109.50545819,
        # 55.97463322,    0.        ,    0.        ]
        #
        # Considering that the rupture site filtering distance is set to 30 km,
        # rupture 1 (magnitude 4) is not considered because too far, rupture 2
        # (magnitude 6) affect only the 4th site, rupture 3 (magnitude 8)
        # affect the 3rd and 4th sites.

        self.assertEqual(result.shape, (4, 3))  # 4 sites, 3 levels
        numpy.testing.assert_allclose(result[0], 0)  # no contrib to site 1
        numpy.testing.assert_allclose(result[1], 0)  # no contrib to site 2

        # test that depths are kept after filtering (sites 3 and 4 remain)
        s_filter = SourceFilter(sitecol, {'default': 100})
        numpy.testing.assert_array_equal(
            s_filter.get_close_sites(sources[0]).depths, ([1, -1]))